From 6107a2d2d5850afbe94173b57cb0cc5bd75d0a3e Mon Sep 17 00:00:00 2001 From: medariox <dariovizz@hotmail.it> Date: Tue, 12 Jan 2016 15:03:48 +0100 Subject: [PATCH] Added guessit2, rebulk, regex --- lib/guessit2/__init__.py | 8 + lib/guessit2/__main__.py | 158 + lib/guessit2/__version__.py | 7 + lib/guessit2/api.py | 89 + lib/guessit2/backports.py | 27 + lib/guessit2/jsonutils.py | 32 + lib/guessit2/options.py | 97 + lib/guessit2/rules/__init__.py | 76 + lib/guessit2/rules/common/__init__.py | 13 + lib/guessit2/rules/common/comparators.py | 70 + lib/guessit2/rules/common/date.py | 78 + lib/guessit2/rules/common/formatters.py | 75 + lib/guessit2/rules/common/numeral.py | 167 + lib/guessit2/rules/common/validators.py | 30 + lib/guessit2/rules/common/words.py | 61 + lib/guessit2/rules/markers/__init__.py | 5 + lib/guessit2/rules/markers/groups.py | 51 + lib/guessit2/rules/markers/path.py | 45 + lib/guessit2/rules/processors.py | 199 + lib/guessit2/rules/properties/__init__.py | 5 + lib/guessit2/rules/properties/audio_codec.py | 148 + lib/guessit2/rules/properties/bonus.py | 52 + lib/guessit2/rules/properties/cds.py | 35 + lib/guessit2/rules/properties/container.py | 53 + lib/guessit2/rules/properties/country.py | 108 + lib/guessit2/rules/properties/crc.py | 87 + lib/guessit2/rules/properties/date.py | 74 + lib/guessit2/rules/properties/edition.py | 33 + .../rules/properties/episode_title.py | 198 + lib/guessit2/rules/properties/episodes.py | 366 + lib/guessit2/rules/properties/film.py | 44 + lib/guessit2/rules/properties/format.py | 68 + lib/guessit2/rules/properties/language.py | 249 + lib/guessit2/rules/properties/mimetype.py | 43 + lib/guessit2/rules/properties/other.py | 159 + lib/guessit2/rules/properties/part.py | 29 + .../rules/properties/release_group.py | 166 + lib/guessit2/rules/properties/screen_size.py | 79 + lib/guessit2/rules/properties/title.py | 340 + lib/guessit2/rules/properties/type.py | 77 + lib/guessit2/rules/properties/video_codec.py | 67 + lib/guessit2/rules/properties/website.py | 39 + lib/guessit2/test/__init__.py | 3 + lib/guessit2/test/episodes.yml | 1708 ++ lib/guessit2/test/movies.yml | 788 + lib/guessit2/test/rules/__init__.py | 3 + lib/guessit2/test/rules/audio_codec.yml | 77 + lib/guessit2/test/rules/bonus.yml | 9 + lib/guessit2/test/rules/cds.yml | 5 + lib/guessit2/test/rules/country.yml | 10 + lib/guessit2/test/rules/date.yml | 50 + lib/guessit2/test/rules/edition.yml | 25 + lib/guessit2/test/rules/episodes.yml | 119 + lib/guessit2/test/rules/film.yml | 9 + lib/guessit2/test/rules/format.yml | 112 + lib/guessit2/test/rules/language.yml | 26 + lib/guessit2/test/rules/other.yml | 137 + lib/guessit2/test/rules/part.yml | 18 + lib/guessit2/test/rules/processors.yml | 8 + lib/guessit2/test/rules/release_group.yml | 33 + lib/guessit2/test/rules/screen_size.yml | 65 + lib/guessit2/test/rules/title.yml | 32 + lib/guessit2/test/rules/video_codec.yml | 54 + lib/guessit2/test/rules/website.yml | 15 + lib/guessit2/test/test-input-file.txt | 2 + lib/guessit2/test/test_api.py | 31 + lib/guessit2/test/test_benchmark.py | 53 + lib/guessit2/test/test_main.py | 72 + lib/guessit2/test/test_yml.py | 274 + lib/guessit2/test/various.yml | 545 + lib/guessit2/tlds-alpha-by-domain.txt | 341 + lib/guessit2/yamlutils.py | 71 + lib/rebulk/__init__.py | 10 + lib/rebulk/__version__.py | 7 + lib/rebulk/debug.py | 56 + lib/rebulk/formatters.py | 23 + lib/rebulk/introspector.py | 126 + lib/rebulk/loose.py | 194 + lib/rebulk/match.py | 781 + lib/rebulk/pattern.py | 415 + lib/rebulk/processors.py | 92 + lib/rebulk/rebulk.py | 281 + lib/rebulk/rules.py | 378 + lib/rebulk/test/__init__.py | 3 + lib/rebulk/test/default_rules_module.py | 80 + lib/rebulk/test/rebulk_rules_module.py | 38 + lib/rebulk/test/rules_module.py | 54 + lib/rebulk/test/test_debug.py | 83 + lib/rebulk/test/test_introspector.py | 138 + lib/rebulk/test/test_loose.py | 83 + lib/rebulk/test/test_match.py | 571 + lib/rebulk/test/test_pattern.py | 757 + lib/rebulk/test/test_processors.py | 215 + lib/rebulk/test/test_rebulk.py | 408 + lib/rebulk/test/test_rules.py | 197 + lib/rebulk/test/test_toposort.py | 111 + lib/rebulk/test/test_validators.py | 68 + lib/rebulk/toposort.py | 84 + lib/rebulk/utils.py | 133 + lib/rebulk/validators.py | 70 + lib/regex/__init__.py | 703 + lib/regex/_regex.c | 24497 ++++++++++++++++ lib/regex/_regex.h | 243 + lib/regex/_regex.so | Bin 0 -> 1189938 bytes lib/regex/_regex_core.py | 4317 +++ lib/regex/_regex_unicode.c | 14258 +++++++++ lib/regex/_regex_unicode.h | 226 + lib/regex/test_regex.py | 3585 +++ 108 files changed, 61787 insertions(+) create mode 100644 lib/guessit2/__init__.py create mode 100644 lib/guessit2/__main__.py create mode 100644 lib/guessit2/__version__.py create mode 100644 lib/guessit2/api.py create mode 100644 lib/guessit2/backports.py create mode 100644 lib/guessit2/jsonutils.py create mode 100644 lib/guessit2/options.py create mode 100644 lib/guessit2/rules/__init__.py create mode 100644 lib/guessit2/rules/common/__init__.py create mode 100644 lib/guessit2/rules/common/comparators.py create mode 100644 lib/guessit2/rules/common/date.py create mode 100644 lib/guessit2/rules/common/formatters.py create mode 100644 lib/guessit2/rules/common/numeral.py create mode 100644 lib/guessit2/rules/common/validators.py create mode 100644 lib/guessit2/rules/common/words.py create mode 100644 lib/guessit2/rules/markers/__init__.py create mode 100644 lib/guessit2/rules/markers/groups.py create mode 100644 lib/guessit2/rules/markers/path.py create mode 100644 lib/guessit2/rules/processors.py create mode 100644 lib/guessit2/rules/properties/__init__.py create mode 100644 lib/guessit2/rules/properties/audio_codec.py create mode 100644 lib/guessit2/rules/properties/bonus.py create mode 100644 lib/guessit2/rules/properties/cds.py create mode 100644 lib/guessit2/rules/properties/container.py create mode 100644 lib/guessit2/rules/properties/country.py create mode 100644 lib/guessit2/rules/properties/crc.py create mode 100644 lib/guessit2/rules/properties/date.py create mode 100644 lib/guessit2/rules/properties/edition.py create mode 100644 lib/guessit2/rules/properties/episode_title.py create mode 100644 lib/guessit2/rules/properties/episodes.py create mode 100644 lib/guessit2/rules/properties/film.py create mode 100644 lib/guessit2/rules/properties/format.py create mode 100644 lib/guessit2/rules/properties/language.py create mode 100644 lib/guessit2/rules/properties/mimetype.py create mode 100644 lib/guessit2/rules/properties/other.py create mode 100644 lib/guessit2/rules/properties/part.py create mode 100644 lib/guessit2/rules/properties/release_group.py create mode 100644 lib/guessit2/rules/properties/screen_size.py create mode 100644 lib/guessit2/rules/properties/title.py create mode 100644 lib/guessit2/rules/properties/type.py create mode 100644 lib/guessit2/rules/properties/video_codec.py create mode 100644 lib/guessit2/rules/properties/website.py create mode 100644 lib/guessit2/test/__init__.py create mode 100644 lib/guessit2/test/episodes.yml create mode 100644 lib/guessit2/test/movies.yml create mode 100644 lib/guessit2/test/rules/__init__.py create mode 100644 lib/guessit2/test/rules/audio_codec.yml create mode 100644 lib/guessit2/test/rules/bonus.yml create mode 100644 lib/guessit2/test/rules/cds.yml create mode 100644 lib/guessit2/test/rules/country.yml create mode 100644 lib/guessit2/test/rules/date.yml create mode 100644 lib/guessit2/test/rules/edition.yml create mode 100644 lib/guessit2/test/rules/episodes.yml create mode 100644 lib/guessit2/test/rules/film.yml create mode 100644 lib/guessit2/test/rules/format.yml create mode 100644 lib/guessit2/test/rules/language.yml create mode 100644 lib/guessit2/test/rules/other.yml create mode 100644 lib/guessit2/test/rules/part.yml create mode 100644 lib/guessit2/test/rules/processors.yml create mode 100644 lib/guessit2/test/rules/release_group.yml create mode 100644 lib/guessit2/test/rules/screen_size.yml create mode 100644 lib/guessit2/test/rules/title.yml create mode 100644 lib/guessit2/test/rules/video_codec.yml create mode 100644 lib/guessit2/test/rules/website.yml create mode 100644 lib/guessit2/test/test-input-file.txt create mode 100644 lib/guessit2/test/test_api.py create mode 100644 lib/guessit2/test/test_benchmark.py create mode 100644 lib/guessit2/test/test_main.py create mode 100644 lib/guessit2/test/test_yml.py create mode 100644 lib/guessit2/test/various.yml create mode 100644 lib/guessit2/tlds-alpha-by-domain.txt create mode 100644 lib/guessit2/yamlutils.py create mode 100644 lib/rebulk/__init__.py create mode 100644 lib/rebulk/__version__.py create mode 100644 lib/rebulk/debug.py create mode 100644 lib/rebulk/formatters.py create mode 100644 lib/rebulk/introspector.py create mode 100644 lib/rebulk/loose.py create mode 100644 lib/rebulk/match.py create mode 100644 lib/rebulk/pattern.py create mode 100644 lib/rebulk/processors.py create mode 100644 lib/rebulk/rebulk.py create mode 100644 lib/rebulk/rules.py create mode 100644 lib/rebulk/test/__init__.py create mode 100644 lib/rebulk/test/default_rules_module.py create mode 100644 lib/rebulk/test/rebulk_rules_module.py create mode 100644 lib/rebulk/test/rules_module.py create mode 100644 lib/rebulk/test/test_debug.py create mode 100644 lib/rebulk/test/test_introspector.py create mode 100644 lib/rebulk/test/test_loose.py create mode 100644 lib/rebulk/test/test_match.py create mode 100644 lib/rebulk/test/test_pattern.py create mode 100644 lib/rebulk/test/test_processors.py create mode 100644 lib/rebulk/test/test_rebulk.py create mode 100644 lib/rebulk/test/test_rules.py create mode 100644 lib/rebulk/test/test_toposort.py create mode 100644 lib/rebulk/test/test_validators.py create mode 100644 lib/rebulk/toposort.py create mode 100644 lib/rebulk/utils.py create mode 100644 lib/rebulk/validators.py create mode 100644 lib/regex/__init__.py create mode 100644 lib/regex/_regex.c create mode 100644 lib/regex/_regex.h create mode 100644 lib/regex/_regex.so create mode 100644 lib/regex/_regex_core.py create mode 100644 lib/regex/_regex_unicode.c create mode 100644 lib/regex/_regex_unicode.h create mode 100644 lib/regex/test_regex.py diff --git a/lib/guessit2/__init__.py b/lib/guessit2/__init__.py new file mode 100644 index 000000000..8b5c841b8 --- /dev/null +++ b/lib/guessit2/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Extracts as much information as possible from a video file. +""" +from .api import guessit, GuessItApi + +from .__version__ import __version__ diff --git a/lib/guessit2/__main__.py b/lib/guessit2/__main__.py new file mode 100644 index 000000000..0ba11b422 --- /dev/null +++ b/lib/guessit2/__main__.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Entry point module +""" +# pragma: no cover +from __future__ import print_function, unicode_literals + +import os +import logging +import json +import sys +from io import open #pylint:disable=redefined-builtin + +import six +from guessit.jsonutils import GuessitEncoder + +from guessit.__version__ import __version__ +from guessit.options import argument_parser +from guessit import api + + +def guess_filename(filename, options): + """ + Guess a single filename using given options + """ + if not options.yaml and not options.json and not options.show_property: + print('For:', filename) + + cmd_options = vars(options) + cmd_options['implicit'] = True # Force implicit option in CLI + + guess = api.guessit(filename, vars(options)) + + if options.show_property: + print(guess.get(options.show_property, '')) + return + + if options.json: + print(json.dumps(guess, cls=GuessitEncoder, ensure_ascii=False)) + elif options.yaml: + import yaml + from guessit import yamlutils + + ystr = yaml.dump({filename: dict(guess)}, Dumper=yamlutils.CustomDumper, default_flow_style=False, + allow_unicode=True) + i = 0 + for yline in ystr.splitlines(): + if i == 0: + print("? " + yline[:-1]) + elif i == 1: + print(":" + yline[1:]) + else: + print(yline) + i += 1 + else: + print('GuessIt found:', json.dumps(guess, cls=GuessitEncoder, indent=4, ensure_ascii=False)) + + +def display_properties(options): + """ + Display properties + """ + properties = api.properties(options) + + if options.json: + if options.values: + print(json.dumps(properties, cls=GuessitEncoder, ensure_ascii=False)) + else: + print(json.dumps(list(properties.keys()), cls=GuessitEncoder, ensure_ascii=False)) + elif options.yaml: + import yaml + from guessit import yamlutils + if options.values: + print(yaml.dump(properties, Dumper=yamlutils.CustomDumper, default_flow_style=False, allow_unicode=True)) + else: + print(yaml.dump(list(properties.keys()), Dumper=yamlutils.CustomDumper, default_flow_style=False, + allow_unicode=True)) + else: + print('GuessIt properties:') + + properties_list = list(sorted(properties.keys())) + for property_name in properties_list: + property_values = properties.get(property_name) + print(2 * ' ' + '[+] %s' % (property_name,)) + if property_values and options.values: + for property_value in property_values: + print(4 * ' ' + '[!] %s' % (property_value,)) + + +def main(args=None): # pylint:disable=too-many-branches + """ + Main function for entry point + """ + if six.PY2 and os.name == 'nt': # pragma: no cover + # see http://bugs.python.org/issue2128 + import locale + + for i, j in enumerate(sys.argv): + sys.argv[i] = j.decode(locale.getpreferredencoding()) + + if args is None: # pragma: no cover + options = argument_parser.parse_args() + else: + options = argument_parser.parse_args(args) + if options.verbose: + logging.basicConfig(stream=sys.stdout, format='%(message)s') + logging.getLogger().setLevel(logging.DEBUG) + + help_required = True + + if options.version: + print('+-------------------------------------------------------+') + print('+ GuessIt ' + __version__ + (28 - len(__version__)) * ' ' + '+') + print('+-------------------------------------------------------+') + print('| Please report any bug or feature request at |') + print('| https://github.com/wackou/guessit/issues. |') + print('+-------------------------------------------------------+') + help_required = False + + if options.yaml: + try: + import yaml # pylint:disable=unused-variable + except ImportError: # pragma: no cover + options.yaml = False + print('PyYAML is not installed. \'--yaml\' option will be ignored ...', file=sys.stderr) + + if options.properties or options.values: + display_properties(options) + help_required = False + + filenames = [] + if options.filename: + for filename in options.filename: + if not isinstance(filename, six.text_type): # pragma: no cover + encoding = sys.getfilesystemencoding() + filename = filename.decode(encoding) + filenames.append(filename) + if options.input_file: + input_file = open(options.input_file, 'r', encoding='utf-8') + try: + filenames.extend([line.strip() for line in input_file.readlines()]) + finally: + input_file.close() + + filenames = list(filter(lambda f: f, filenames)) + + if filenames: + for filename in filenames: + help_required = False + guess_filename(filename, options) + + if help_required: # pragma: no cover + argument_parser.print_help() + + +if __name__ == '__main__': # pragma: no cover + main() diff --git a/lib/guessit2/__version__.py b/lib/guessit2/__version__.py new file mode 100644 index 000000000..c6db86db2 --- /dev/null +++ b/lib/guessit2/__version__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Version module +""" +# pragma: no cover +__version__ = '2.0rc5.dev0' diff --git a/lib/guessit2/api.py b/lib/guessit2/api.py new file mode 100644 index 000000000..3a4959ad1 --- /dev/null +++ b/lib/guessit2/api.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +API functions that can be used by external software +""" +from __future__ import unicode_literals +try: + from collections import OrderedDict +except ImportError: # pragma: no-cover + from ordereddict import OrderedDict # pylint:disable=import-error + +import six + +from rebulk.introspector import introspect + +from .rules import rebulk_builder +from .options import parse_options + + +def guessit(string, options=None): + """ + Retrieves all matches from string as a dict + :param string: the filename or release name + :type string: str + :param options: the filename or release name + :type options: str|dict + :return: + :rtype: + """ + return default_api.guessit(string, options) + + +def properties(options=None): + """ + Retrieves all properties with possible values that can be guessed + :param options: + :type options: + :return: + :rtype: + """ + return default_api.properties(options) + + +class GuessItApi(object): + """ + An api class that can be configured with custom Rebulk configuration. + """ + + def __init__(self, rebulk): + """ + :param rebulk: Rebulk instance to use. + :type rebulk: Rebulk + :return: + :rtype: + """ + self.rebulk = rebulk + + def guessit(self, string, options=None): + """ + Retrieves all matches from string as a dict + :param string: the filename or release name + :type string: str + :param options: the filename or release name + :type options: str|dict + :return: + :rtype: + """ + if not isinstance(string, six.text_type): + raise TypeError("guessit input must be %s." % six.text_type.__name__) + options = parse_options(options) + return self.rebulk.matches(string, options).to_dict(options.get('advanced', False), + options.get('implicit', False)) + + def properties(self, options=None): + """ + Grab properties and values that can be generated. + :param options: + :type options: + :return: + :rtype: + """ + unordered = introspect(self.rebulk, options).properties + ordered = OrderedDict() + for k in sorted(unordered.keys(), key=six.text_type): + ordered[k] = list(sorted(unordered[k], key=six.text_type)) + return ordered + + +default_api = GuessItApi(rebulk_builder()) diff --git a/lib/guessit2/backports.py b/lib/guessit2/backports.py new file mode 100644 index 000000000..3e94e27ad --- /dev/null +++ b/lib/guessit2/backports.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Backports +""" +# pragma: no-cover +# pylint: disabled + +def cmp_to_key(mycmp): + """functools.cmp_to_key backport""" + class KeyClass(object): + """Key class""" + def __init__(self, obj, *args): # pylint: disable=unused-argument + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + def __ne__(self, other): + return mycmp(self.obj, other.obj) != 0 + return KeyClass diff --git a/lib/guessit2/jsonutils.py b/lib/guessit2/jsonutils.py new file mode 100644 index 000000000..54bf79431 --- /dev/null +++ b/lib/guessit2/jsonutils.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +JSON Utils +""" +import json +try: + from collections import OrderedDict +except ImportError: # pragma: no-cover + from ordereddict import OrderedDict # pylint:disable=import-error + +from rebulk.match import Match + + +class GuessitEncoder(json.JSONEncoder): + """ + JSON Encoder for guessit response + """ + + def default(self, o): # pylint:disable=method-hidden + if isinstance(o, Match): + ret = OrderedDict() + ret['value'] = o.value + if o.raw: + ret['raw'] = o.raw + ret['start'] = o.start + ret['end'] = o.end + return ret + elif hasattr(o, 'name'): # Babelfish languages/countries long name + return o.name + else: # pragma: no cover + return str(o) diff --git a/lib/guessit2/options.py b/lib/guessit2/options.py new file mode 100644 index 000000000..2cdc02cea --- /dev/null +++ b/lib/guessit2/options.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Options +""" +from __future__ import unicode_literals + +import sys +from argparse import ArgumentParser +import shlex + +import six + + +if six.PY2: + StrOptType = lambda s: unicode(s, sys.stdin.encoding) # pylint:disable=undefined-variable +else: + StrOptType = str # pylint:disable=redefined-variable-type + + +def build_argument_parser(): + """ + Builds the argument parser + :return: the argument parser + :rtype: ArgumentParser + """ + opts = ArgumentParser() + opts.add_argument(dest='filename', help='Filename or release name to guess', nargs='*') + + naming_opts = opts.add_argument_group("Naming") + naming_opts.add_argument('-t', '--type', dest='type', default=None, + help='The suggested file type: movie, episode. If undefined, type will be guessed.') + naming_opts.add_argument('-n', '--name-only', dest='name_only', action='store_true', default=False, + help='Parse files as name only, considering "/" and "\\" like other separators.') + naming_opts.add_argument('-Y', '--date-year-first', action='store_true', dest='date_year_first', default=None, + help='If short date is found, consider the first digits as the year.') + naming_opts.add_argument('-D', '--date-day-first', action='store_true', dest='date_day_first', default=None, + help='If short date is found, consider the second digits as the day.') + naming_opts.add_argument('-L', '--allowed-languages', action='append', dest='allowed_languages', + help='Allowed language (can be used multiple times)') + naming_opts.add_argument('-C', '--allowed-countries', action='append', dest='allowed_countries', + help='Allowed country (can be used multiple times)') + naming_opts.add_argument('-E', '--episode-prefer-number', action='store_true', dest='episode_prefer_number', + default=False, + help='Guess "serie.213.avi" as the episode 213. Without this option, ' + 'it will be guessed as season 2, episode 13') + naming_opts.add_argument('-T', '--expected-title', action='append', dest='expected_title', type=StrOptType, + help='Expected title to parse (can be used multiple times)') + naming_opts.add_argument('-G', '--expected-group', action='append', dest='expected_group', type=StrOptType, + help='Expected release group (can be used multiple times)') + + input_opts = opts.add_argument_group("Input") + input_opts.add_argument('-f', '--input-file', dest='input_file', default=False, + help='Read filenames from an input text file. File should use UTF-8 charset.') + + output_opts = opts.add_argument_group("Output") + output_opts.add_argument('-v', '--verbose', action='store_true', dest='verbose', default=False, + help='Display debug output') + output_opts.add_argument('-P', '--show-property', dest='show_property', default=None, + help='Display the value of a single property (title, series, video_codec, year, ...)') + output_opts.add_argument('-a', '--advanced', dest='advanced', action='store_true', default=False, + help='Display advanced information for filename guesses, as json output') + output_opts.add_argument('-j', '--json', dest='json', action='store_true', default=False, + help='Display information for filename guesses as json output') + output_opts.add_argument('-y', '--yaml', dest='yaml', action='store_true', default=False, + help='Display information for filename guesses as yaml output') + + + + information_opts = opts.add_argument_group("Information") + information_opts.add_argument('-p', '--properties', dest='properties', action='store_true', default=False, + help='Display properties that can be guessed.') + information_opts.add_argument('-V', '--values', dest='values', action='store_true', default=False, + help='Display property values that can be guessed.') + information_opts.add_argument('--version', dest='version', action='store_true', default=False, + help='Display the guessit version.') + + return opts + + +def parse_options(options): + """ + Parse given option string + :param options: + :type options: + :return: + :rtype: + """ + if isinstance(options, six.string_types): + args = shlex.split(options) + options = vars(argument_parser.parse_args(args)) + if options is None: + options = {} + return options + + +argument_parser = build_argument_parser() diff --git a/lib/guessit2/rules/__init__.py b/lib/guessit2/rules/__init__.py new file mode 100644 index 000000000..8e2716cc0 --- /dev/null +++ b/lib/guessit2/rules/__init__.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Rebulk object default builder +""" +from __future__ import unicode_literals + +from rebulk import Rebulk + +from .markers.path import path +from .markers.groups import groups + +from .properties.episodes import episodes +from .properties.container import container +from .properties.format import format_ +from .properties.video_codec import video_codec +from .properties.audio_codec import audio_codec +from .properties.screen_size import screen_size +from .properties.website import website +from .properties.date import date +from .properties.title import title +from .properties.episode_title import episode_title +from .properties.language import language +from .properties.country import country +from .properties.release_group import release_group +from .properties.other import other +from .properties.edition import edition +from .properties.cds import cds +from .properties.bonus import bonus +from .properties.film import film +from .properties.part import part +from .properties.crc import crc +from .properties.mimetype import mimetype +from .properties.type import type_ + +from .processors import processors + + +def rebulk_builder(): + """ + Default builder for main Rebulk object used by api. + :return: Main Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk() + + rebulk.rebulk(path()) + rebulk.rebulk(groups()) + + rebulk.rebulk(episodes()) + rebulk.rebulk(container()) + rebulk.rebulk(format_()) + rebulk.rebulk(video_codec()) + rebulk.rebulk(audio_codec()) + rebulk.rebulk(screen_size()) + rebulk.rebulk(website()) + rebulk.rebulk(date()) + rebulk.rebulk(title()) + rebulk.rebulk(episode_title()) + rebulk.rebulk(language()) + rebulk.rebulk(country()) + rebulk.rebulk(release_group()) + rebulk.rebulk(other()) + rebulk.rebulk(edition()) + rebulk.rebulk(cds()) + rebulk.rebulk(bonus()) + rebulk.rebulk(film()) + rebulk.rebulk(part()) + rebulk.rebulk(crc()) + + rebulk.rebulk(processors()) + + rebulk.rebulk(mimetype()) + rebulk.rebulk(type_()) + + return rebulk diff --git a/lib/guessit2/rules/common/__init__.py b/lib/guessit2/rules/common/__init__.py new file mode 100644 index 000000000..a30f08798 --- /dev/null +++ b/lib/guessit2/rules/common/__init__.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Common module +""" +from __future__ import unicode_literals + +seps = r' [](){}+*|=§-_~#/\.,;:' # list of tags/words separators + +title_seps = r'-+/\|' # separators for title + +dash = (r'-', r'[\W_]') # abbreviation used by many rebulk objects. +alt_dash = (r'@', r'[\W_]') # abbreviation used by many rebulk objects. diff --git a/lib/guessit2/rules/common/comparators.py b/lib/guessit2/rules/common/comparators.py new file mode 100644 index 000000000..f27a407fe --- /dev/null +++ b/lib/guessit2/rules/common/comparators.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Comparators +""" +from __future__ import unicode_literals + +try: + from functools import cmp_to_key +except ImportError: + from ...backports import cmp_to_key + + +def marker_comparator_predicate(match): + """ + Match predicate used in comparator + """ + return not match.private and \ + match.name not in ['proper_count', 'title', 'episode_title', 'alternativeTitle'] and \ + not (match.name == 'container' and 'extension' in match.tags) + + +def marker_weight(matches, marker): + """ + Compute the comparator weight of a marker + :param matches: + :param marker: + :return: + """ + return len(set(match.name for match in matches.range(*marker.span, predicate=marker_comparator_predicate))) + + +def marker_comparator(matches, markers): + """ + Builds a comparator that returns markers sorted from the most valuable to the less. + + Take the parts where matches count is higher, then when length is higher, then when position is at left. + + :param matches: + :type matches: + :return: + :rtype: + """ + def comparator(marker1, marker2): + """ + The actual comparator function. + """ + matches_count = marker_weight(matches, marker2) - marker_weight(matches, marker1) + if matches_count: + return matches_count + len_diff = len(marker2) - len(marker1) + if len_diff: + return len_diff + return markers.index(marker2) - markers.index(marker1) + + return comparator + + +def marker_sorted(markers, matches): + """ + Sort markers from matches, from the most valuable to the less. + + :param fileparts: + :type fileparts: + :param matches: + :type matches: + :return: + :rtype: + """ + return sorted(markers, key=cmp_to_key(marker_comparator(matches, markers))) diff --git a/lib/guessit2/rules/common/date.py b/lib/guessit2/rules/common/date.py new file mode 100644 index 000000000..56b0987f9 --- /dev/null +++ b/lib/guessit2/rules/common/date.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Date +""" +from __future__ import unicode_literals + +import regex as re + +from dateutil import parser + +_dsep = r'[-/ \.]' +_dsep_bis = r'[-/ \.x]' + +date_regexps = [ + re.compile(r'%s((\d{8}))%s' % (_dsep, _dsep), re.IGNORECASE), + re.compile(r'%s((\d{6}))%s' % (_dsep, _dsep), re.IGNORECASE), + re.compile(r'(?:^|[^\d])((\d{2})%s(\d{1,2})%s(\d{1,2}))(?:$|[^\d])' % (_dsep, _dsep), re.IGNORECASE), + re.compile(r'(?:^|[^\d])((\d{1,2})%s(\d{1,2})%s(\d{2}))(?:$|[^\d])' % (_dsep, _dsep), re.IGNORECASE), + re.compile(r'(?:^|[^\d])((\d{4})%s(\d{1,2})%s(\d{1,2}))(?:$|[^\d])' % (_dsep_bis, _dsep), re.IGNORECASE), + re.compile(r'(?:^|[^\d])((\d{1,2})%s(\d{1,2})%s(\d{4}))(?:$|[^\d])' % (_dsep, _dsep_bis), re.IGNORECASE), + re.compile(r'(?:^|[^\d])((\d{1,2}(?:st|nd|rd|th)?%s(?:[a-z]{3,10})%s\d{4}))(?:$|[^\d])' % (_dsep, _dsep), + re.IGNORECASE)] + + +def valid_year(year): + """Check if number is a valid year""" + return 1920 <= year < 2030 + + +def search_date(string, year_first=None, day_first=True): + """Looks for date patterns, and if found return the date and group span. + + Assumes there are sentinels at the beginning and end of the string that + always allow matching a non-digit delimiting the date. + + Year can be defined on two digit only. It will return the nearest possible + date from today. + + >>> search_date(' This happened on 2002-04-22. ') + (18, 28, datetime.date(2002, 4, 22)) + + >>> search_date(' And this on 17-06-1998. ') + (13, 23, datetime.date(1998, 6, 17)) + + >>> search_date(' no date in here ') + """ + start, end = None, None + match = None + for date_re in date_regexps: + search_match = date_re.search(string) + if search_match and (match is None or search_match.end() - search_match.start() > len(match)): + start, end = search_match.start(1), search_match.end(1) + match = '-'.join(search_match.groups()[1:]) + + if match is None: + return + + # If day_first/year_first is undefined, parse is made using both possible values. + yearfirst_opts = [False, True] + if year_first is not None: + yearfirst_opts = [year_first] + + dayfirst_opts = [True, False] + if day_first is not None: + dayfirst_opts = [day_first] + + kwargs_list = ({'dayfirst': d, 'yearfirst': y} for d in dayfirst_opts for y in yearfirst_opts) + for kwargs in kwargs_list: + try: + date = parser.parse(match, **kwargs) + except (ValueError, TypeError): # pragma: no cover + # see https://bugs.launchpad.net/dateutil/+bug/1247643 + date = None + + # check date plausibility + if date and valid_year(date.year): # pylint:disable=no-member + return start, end, date.date() # pylint:disable=no-member diff --git a/lib/guessit2/rules/common/formatters.py b/lib/guessit2/rules/common/formatters.py new file mode 100644 index 000000000..ca70811f8 --- /dev/null +++ b/lib/guessit2/rules/common/formatters.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Formatters +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk.formatters import formatters + +from . import seps + + +_excluded_clean_chars = ',:;-/\\' +clean_chars = "" +for sep in seps: + if sep not in _excluded_clean_chars: + clean_chars += sep + + +def cleanup(input_string): + """ + Removes and strip separators from input_string (but keep ',;' characters) + :param input_string: + :type input_string: + :return: + :rtype: + """ + for char in clean_chars: + input_string = input_string.replace(char, ' ') + return re.sub(' +', ' ', strip(input_string)) + + +def strip(input_string): + """ + Strip separators from input_string + :param input_string: + :type input_string: + :return: + :rtype: + """ + return input_string.strip(seps) + + +def raw_cleanup(raw): + """ + Cleanup a raw value to perform raw comparison + :param raw: + :type raw: + :return: + :rtype: + """ + return formatters(cleanup, strip)(raw.lower()) + + +def reorder_title(title, articles=('the',), separators=(',', ', ')): + """ + Reorder the title + :param title: + :type title: + :param articles: + :type articles: + :param separators: + :type separators: + :return: + :rtype: + """ + ltitle = title.lower() + for article in articles: + for separator in separators: + suffix = separator + article + if ltitle[-len(suffix):] == suffix: + return title[-len(suffix) + len(separator):] + ' ' + title[:-len(suffix)] + return title diff --git a/lib/guessit2/rules/common/numeral.py b/lib/guessit2/rules/common/numeral.py new file mode 100644 index 000000000..8b868ea66 --- /dev/null +++ b/lib/guessit2/rules/common/numeral.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +parse numeral from various formats +""" +from __future__ import unicode_literals + +import regex as re + +digital_numeral = r'\d{1,4}' + +roman_numeral = r'(?=[MCDLXVI]+)M{0,4}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3})' + +english_word_numeral_list = [ + 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', + 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty' +] + +french_word_numeral_list = [ + 'zéro', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', + 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf', 'vingt' +] + +french_alt_word_numeral_list = [ + 'zero', 'une', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', + 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dixsept', 'dixhuit', 'dixneuf', 'vingt' +] + + +def __build_word_numeral(*args): + """ + Build word numeral regexp from list. + + :param args: + :type args: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + re_ = None + for word_list in args: + for word in word_list: + if not re_: + re_ = r'(?:(?=\w+)' + else: + re_ += '|' + re_ += word + re_ += ')' + return re_ + + +word_numeral = __build_word_numeral(english_word_numeral_list, french_word_numeral_list, french_alt_word_numeral_list) + +numeral = '(?:' + digital_numeral + '|' + roman_numeral + '|' + word_numeral + ')' + +__romanNumeralMap = ( + ('M', 1000), + ('CM', 900), + ('D', 500), + ('CD', 400), + ('C', 100), + ('XC', 90), + ('L', 50), + ('XL', 40), + ('X', 10), + ('IX', 9), + ('V', 5), + ('IV', 4), + ('I', 1) +) + +__romanNumeralPattern = re.compile('^' + roman_numeral + '$') + + +def __parse_roman(value): + """ + convert Roman numeral to integer + + :param value: Value to parse + :type value: string + :return: + :rtype: + """ + if not __romanNumeralPattern.search(value): + raise ValueError('Invalid Roman numeral: %s' % value) + + result = 0 + index = 0 + for num, integer in __romanNumeralMap: + while value[index:index + len(num)] == num: + result += integer + index += len(num) + return result + + +def __parse_word(value): + """ + Convert Word numeral to integer + + :param value: Value to parse + :type value: string + :return: + :rtype: + """ + for word_list in [english_word_numeral_list, french_word_numeral_list, french_alt_word_numeral_list]: + try: + return word_list.index(value.lower()) + except ValueError: + pass + raise ValueError # pragma: no cover + + +_clean_re = re.compile(r'[^\d]*(\d+)[^\d]*') + + +def parse_numeral(value, int_enabled=True, roman_enabled=True, word_enabled=True, clean=True): + """ + Parse a numeric value into integer. + + :param value: Value to parse. Can be an integer, roman numeral or word. + :type value: string + :param int_enabled: + :type int_enabled: + :param roman_enabled: + :type roman_enabled: + :param word_enabled: + :type word_enabled: + :param clean: + :type clean: + :return: Numeric value, or None if value can't be parsed + :rtype: int + """ + # pylint: disable=too-many-branches + if int_enabled: + try: + if clean: + match = _clean_re.match(value) + if match: + clean_value = match.group(1) + return int(clean_value) + return int(value) + except ValueError: + pass + if roman_enabled: + try: + if clean: + for word in value.split(): + try: + return __parse_roman(word.upper()) + except ValueError: + pass + return __parse_roman(value) + except ValueError: + pass + if word_enabled: + try: + if clean: + for word in value.split(): + try: + return __parse_word(word) + except ValueError: # pragma: no cover + pass + return __parse_word(value) # pragma: no cover + except ValueError: # pragma: no cover + pass + raise ValueError('Invalid numeral: ' + value) # pragma: no cover diff --git a/lib/guessit2/rules/common/validators.py b/lib/guessit2/rules/common/validators.py new file mode 100644 index 000000000..f25dd0008 --- /dev/null +++ b/lib/guessit2/rules/common/validators.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Validators +""" +from __future__ import unicode_literals + +from functools import partial + +from rebulk.validators import chars_before, chars_after, chars_surround +from . import seps + +seps_before = partial(chars_before, seps) +seps_after = partial(chars_after, seps) +seps_surround = partial(chars_surround, seps) + + +def int_coercable(string): + """ + Check if string can be coerced to int + :param string: + :type string: + :return: + :rtype: + """ + try: + int(string) + return True + except ValueError: + return False diff --git a/lib/guessit2/rules/common/words.py b/lib/guessit2/rules/common/words.py new file mode 100644 index 000000000..1a56117ee --- /dev/null +++ b/lib/guessit2/rules/common/words.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Words utils +""" +from __future__ import unicode_literals + +import regex as re + +_words_rexp = re.compile(r'\w+', re.UNICODE) + + +def iter_words(string): + """ + Iterate on all words in a string + :param string: + :type string: + :return: + :rtype: iterable[str] + """ + return _words_rexp.finditer(string.replace('_', ' ')) + +# list of common words which could be interpreted as properties, but which +# are far too common to be able to say they represent a property in the +# middle of a string (where they most likely carry their commmon meaning) +COMMON_WORDS = frozenset([ + # english words + 'is', 'it', 'am', 'mad', 'men', 'man', 'run', 'sin', 'st', 'to', + 'no', 'non', 'war', 'min', 'new', 'car', 'day', 'bad', 'bat', 'fan', + 'fry', 'cop', 'zen', 'gay', 'fat', 'one', 'cherokee', 'got', 'an', 'as', + 'cat', 'her', 'be', 'hat', 'sun', 'may', 'my', 'mr', 'rum', 'pi', 'bb', + 'bt', 'tv', 'aw', 'by', 'md', 'mp', 'cd', 'lt', 'gt', 'in', 'ad', 'ice', + 'ay', 'at', 'star', 'so', 'he', + # french words + 'bas', 'de', 'le', 'son', 'ne', 'ca', 'ce', 'et', 'que', + 'mal', 'est', 'vol', 'or', 'mon', 'se', 'je', 'tu', 'me', + 'ne', 'ma', 'va', 'au', + # japanese words, + 'wa', 'ga', 'ao', + # spanish words + 'la', 'el', 'del', 'por', 'mar', 'al', + # other + 'ind', 'arw', 'ts', 'ii', 'bin', 'chan', 'ss', 'san', 'oss', 'iii', + 'vi', 'ben', 'da', 'lt', 'ch', 'sr', 'ps', 'cx', 'vo', + # new from babelfish + 'mkv', 'avi', 'dmd', 'the', 'dis', 'cut', 'stv', 'des', 'dia', 'and', + 'cab', 'sub', 'mia', 'rim', 'las', 'une', 'par', 'srt', 'ano', 'toy', + 'job', 'gag', 'reel', 'www', 'for', 'ayu', 'csi', 'ren', 'moi', 'sur', + 'fer', 'fun', 'two', 'big', 'psy', 'air', + # movie title + 'brazil', + # release groups + 'bs', # Bosnian + 'kz', + # countries + 'gt', 'lt', 'im', + # part/pt + 'pt', + # screener + 'scr' +]) diff --git a/lib/guessit2/rules/markers/__init__.py b/lib/guessit2/rules/markers/__init__.py new file mode 100644 index 000000000..6a48a13b3 --- /dev/null +++ b/lib/guessit2/rules/markers/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Markers +""" diff --git a/lib/guessit2/rules/markers/groups.py b/lib/guessit2/rules/markers/groups.py new file mode 100644 index 000000000..15104b869 --- /dev/null +++ b/lib/guessit2/rules/markers/groups.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Groups markers (...), [...] and {...} +""" +from __future__ import unicode_literals + +from rebulk import Rebulk + + +def groups(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk() + rebulk.defaults(name="group", marker=True) + + starting = '([{' + ending = ')]}' + + def mark_groups(input_string): + """ + Functional pattern to mark groups (...), [...] and {...}. + + :param input_string: + :return: + """ + openings = ([], [], []) + i = 0 + + ret = [] + for char in input_string: + start_type = starting.find(char) + if start_type > -1: + openings[start_type].append(i) + + i += 1 + + end_type = ending.find(char) + if end_type > -1: + try: + start_index = openings[end_type].pop() + ret.append((start_index, i)) + except IndexError: + pass + return ret + + rebulk.functional(mark_groups) + return rebulk diff --git a/lib/guessit2/rules/markers/path.py b/lib/guessit2/rules/markers/path.py new file mode 100644 index 000000000..841419634 --- /dev/null +++ b/lib/guessit2/rules/markers/path.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Path markers +""" +from __future__ import unicode_literals + +from rebulk import Rebulk + +from rebulk.utils import find_all + + +def path(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk() + rebulk.defaults(name="path", marker=True) + + def mark_path(input_string, context): + """ + Functional pattern to mark path elements. + + :param input_string: + :return: + """ + ret = [] + if context.get('name_only', False): + ret.append((0, len(input_string))) + else: + indices = list(find_all(input_string, '/')) + indices += list(find_all(input_string, r'\\')) + indices += [-1, len(input_string)] + + indices.sort() + + for i in range(0, len(indices) - 1): + ret.append((indices[i] + 1, indices[i + 1])) + + return ret + + rebulk.functional(mark_path) + return rebulk diff --git a/lib/guessit2/rules/processors.py b/lib/guessit2/rules/processors.py new file mode 100644 index 000000000..cce451ba1 --- /dev/null +++ b/lib/guessit2/rules/processors.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Processors +""" +from __future__ import unicode_literals + +from collections import defaultdict +import copy + +import six + +from rebulk import Rebulk, Rule, CustomRule, POST_PROCESS, PRE_PROCESS, AppendMatch, RemoveMatch +from guessit2.rules.common.words import iter_words +from .common.formatters import cleanup +from .common.comparators import marker_sorted +from .common.date import valid_year + + +class EnlargeGroupMatches(CustomRule): + """ + Enlarge matches that are starting and/or ending group to include brackets in their span. + :param matches: + :type matches: + :return: + :rtype: + """ + priority = PRE_PROCESS + + def when(self, matches, context): + starting = [] + ending = [] + + for group in matches.markers.named('group'): + for match in matches.starting(group.start + 1): + starting.append(match) + + for match in matches.ending(group.end - 1): + ending.append(match) + + if starting or ending: + return starting, ending + + def then(self, matches, when_response, context): + starting, ending = when_response + for match in starting: + matches.remove(match) + match.start -= 1 + match.raw_start += 1 + matches.append(match) + + for match in ending: + matches.remove(match) + match.end += 1 + match.raw_end -= 1 + matches.append(match) + + +class EquivalentHoles(Rule): + """ + Creates equivalent matches for holes that have same values than existing (case insensitive) + """ + priority = POST_PROCESS + consequence = AppendMatch + + def when(self, matches, context): + new_matches = [] + + for filepath in marker_sorted(matches.markers.named('path'), matches): + holes = matches.holes(start=filepath.start, end=filepath.end, formatter=cleanup) + for name in matches.names: + for hole in list(holes): + for current_match in matches.named(name): + if isinstance(current_match.value, six.string_types) and \ + hole.value.lower() == current_match.value.lower(): + if 'equivalent-ignore' in current_match.tags: + continue + new_value = _preferred_string(hole.value, current_match.value) + if hole.value != new_value: + hole.value = new_value + if current_match.value != new_value: + current_match.value = new_value + hole.name = name + hole.tags = ['equivalent'] + new_matches.append(hole) + if hole in holes: + holes.remove(hole) + + return new_matches + + +class RemoveAmbiguous(Rule): + """ + If multiple match are found with same name and different values, keep the one in the most valuable filepart. + Also keep others match with same name and values than those kept ones. + """ + priority = POST_PROCESS + consequence = RemoveMatch + + def when(self, matches, context): + fileparts = marker_sorted(matches.markers.named('path'), matches) + + previous_fileparts_names = set() + values = defaultdict(list) + + to_remove = [] + for filepart in fileparts: + filepart_matches = matches.range(filepart.start, filepart.end) + + filepart_names = set() + for match in filepart_matches: + filepart_names.add(match.name) + if match.name in previous_fileparts_names: + if match.value not in values[match.name]: + to_remove.append(match) + else: + if match.value not in values[match.name]: + values[match.name].append(match.value) + + previous_fileparts_names.update(filepart_names) + + return to_remove + + +def _preferred_string(value1, value2): # pylint:disable=too-many-return-statements + """ + Retrieves preferred title from both values. + :param value1: + :type value1: str + :param value2: + :type value2: str + :return: The preferred title + :rtype: str + """ + if value1 == value2: + return value1 + if value1.istitle() and not value2.istitle(): + return value1 + if not value1.isupper() and value2.isupper(): + return value1 + if not value1.isupper() and value1[0].isupper() and not value2[0].isupper(): + return value1 + if _count_title_words(value1) > _count_title_words(value2): + return value1 + return value2 + + +def _count_title_words(value): + """ + Count only many words are titles in value. + :param value: + :type value: + :return: + :rtype: + """ + ret = 0 + for word in iter_words(value): + if word.group(0).istitle(): + ret += 1 + return ret + +class SeasonYear(Rule): + """ + If a season is a valid year and no year was found, create an match with year. + """ + priority = POST_PROCESS + consequence = AppendMatch + + def when(self, matches, context): + ret = [] + if not matches.named('year'): + for season in matches.named('season'): + if valid_year(season.value): + year = copy.copy(season) + year.name = 'year' + ret.append(year) + return ret + + +class Processors(CustomRule): + """ + Empty rule for ordering post_processing properly. + """ + priority = POST_PROCESS + + def when(self, matches, context): + pass + + def then(self, matches, when_response, context): # pragma: no cover + pass + + +def processors(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + return Rebulk().rules(EnlargeGroupMatches, EquivalentHoles, RemoveAmbiguous, SeasonYear, Processors) diff --git a/lib/guessit2/rules/properties/__init__.py b/lib/guessit2/rules/properties/__init__.py new file mode 100644 index 000000000..e0a24eaf0 --- /dev/null +++ b/lib/guessit2/rules/properties/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Properties +""" diff --git a/lib/guessit2/rules/properties/audio_codec.py b/lib/guessit2/rules/properties/audio_codec.py new file mode 100644 index 000000000..85ebff3e8 --- /dev/null +++ b/lib/guessit2/rules/properties/audio_codec.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +audio_codec, audio_profile and audio_channels property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk, Rule, RemoveMatch +from ..common import dash +from ..common.validators import seps_before, seps_after + +audio_properties = ['audio_codec', 'audio_profile', 'audio_channels'] + + +def audio_codec(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) + rebulk.defaults(name="audio_codec") + + rebulk.regex("MP3", "LAME", r"LAME(?:\d)+-?(?:\d)+", value="MP3") + rebulk.regex("DolbyDigital", "DD", value="DolbyDigital") + rebulk.regex("AAC", value="AAC") + rebulk.regex("AC3", value="AC3") + rebulk.regex("Flac", value="FLAC") + rebulk.regex("DTS", value="DTS") + rebulk.regex("True-?HD", value="TrueHD") + + rebulk.defaults(name="audio_profile") + rebulk.string("HD", value="HD", tags="DTS") + rebulk.regex("HD-?MA", value="HDMA", tags="DTS") + rebulk.string("HE", value="HE", tags="AAC") + rebulk.string("LC", value="LC", tags="AAC") + rebulk.string("HQ", value="HQ", tags="AC3") + + rebulk.defaults(name="audio_channels") + rebulk.regex(r'(7[\W_]1)(?:[^\d]|$)', value='7.1', children=True) + rebulk.regex(r'(5[\W_]1)(?:[^\d]|$)', value='5.1', children=True) + rebulk.regex(r'(2[\W_]0)(?:[^\d]|$)', value='2.0', children=True) + rebulk.string('7ch', '8ch', value='7.1') + rebulk.string('5ch', '6ch', value='5.1') + rebulk.string('2ch', 'stereo', value='2.0') + rebulk.string('1ch', 'mono', value='1.0') + + rebulk.rules(DtsRule, AacRule, Ac3Rule, AudioValidatorRule, HqConflictRule) + + return rebulk + + +class AudioValidatorRule(Rule): + """ + Remove audio properties if not surrounded by separators and not next each others + """ + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + + audio_list = matches.range(predicate=lambda match: match.name in audio_properties) + for audio in audio_list: + if not seps_before(audio): + valid_before = matches.range(audio.start - 1, audio.start, + lambda match: match.name in audio_properties) + if not valid_before: + ret.append(audio) + continue + if not seps_after(audio): + valid_after = matches.range(audio.end, audio.end + 1, + lambda match: match.name in audio_properties) + if not valid_after: + ret.append(audio) + continue + + return ret + + +class AudioProfileRule(Rule): + """ + Abstract rule to validate audio profiles + """ + priority = 64 + dependency = AudioValidatorRule + consequence = RemoveMatch + + def __init__(self, codec): + super(AudioProfileRule, self).__init__() + self.codec = codec + + def when(self, matches, context): + profile_list = matches.named('audio_profile', lambda match: self.codec in match.tags) + ret = [] + for profile in profile_list: + codec = matches.previous(profile, lambda match: match.name == 'audio_codec' and match.value == self.codec) + if not codec: + codec = matches.next(profile, lambda match: match.name == 'audio_codec' and match.value == self.codec) + if not codec: + ret.append(profile) + return ret + + +class DtsRule(AudioProfileRule): + """ + Rule to validate DTS profile + """ + + def __init__(self): + super(DtsRule, self).__init__("DTS") + + +class AacRule(AudioProfileRule): + """ + Rule to validate AAC profile + """ + + def __init__(self): + super(AacRule, self).__init__("AAC") + + +class Ac3Rule(AudioProfileRule): + """ + Rule to validate AC3 profile + """ + + def __init__(self): + super(Ac3Rule, self).__init__("AC3") + + +class HqConflictRule(Rule): + """ + Solve conflict between HQ from other property and from audio_profile. + """ + + dependency = [DtsRule, AacRule, Ac3Rule] + consequence = RemoveMatch + + def when(self, matches, context): + hq_audio = matches.named('audio_profile', lambda match: match.value == 'HQ') + hq_audio_spans = [match.span for match in hq_audio] + hq_other = matches.named('other', lambda match: match.span in hq_audio_spans) + + if hq_other: + return hq_other diff --git a/lib/guessit2/rules/properties/bonus.py b/lib/guessit2/rules/properties/bonus.py new file mode 100644 index 000000000..416de2b2c --- /dev/null +++ b/lib/guessit2/rules/properties/bonus.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +bonus property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk, AppendMatch, Rule + +from .title import TitleFromPosition +from ..common.formatters import cleanup +from ..common.validators import seps_surround + + +def bonus(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE) + + rebulk.regex(r'x(\d+)', name='bonus', private_parent=True, children=True, formatter=int, + validator={'__parent__': lambda match: seps_surround}, + conflict_solver=lambda match, conflicting: match + if conflicting.name in ['video_codec', 'episode'] and 'bonus-conflict' not in conflicting.tags + else '__default__') + + rebulk.rules(BonusTitleRule) + + return rebulk + + +class BonusTitleRule(Rule): + """ + Find bonus title after bonus. + """ + dependency = TitleFromPosition + consequence = AppendMatch + + properties = {'bonus_title': [None]} + + def when(self, matches, context): + bonus_number = matches.named('bonus', lambda match: not match.private, index=0) + if bonus_number: + filepath = matches.markers.at_match(bonus_number, lambda marker: marker.name == 'path', 0) + hole = matches.holes(bonus_number.end, filepath.end + 1, formatter=cleanup, index=0) + if hole and hole.value: + hole.name = 'bonus_title' + return hole diff --git a/lib/guessit2/rules/properties/cds.py b/lib/guessit2/rules/properties/cds.py new file mode 100644 index 000000000..e2d39b3ff --- /dev/null +++ b/lib/guessit2/rules/properties/cds.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +cd and cd_count properties +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk +from ..common import dash + + +def cds(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) + + rebulk.regex(r'cd-?(?P<cd>\d+)(?:-?of-?(?P<cd_count>\d+))?', + validator={'cd': lambda match: match.value > 0, 'cd_count': lambda match: match.value > 0}, + formatter={'cd': int, 'cd_count': int}, + children=True, + private_parent=True, + properties={'cd': [None], 'cd_count': [None]}) + rebulk.regex(r'(?P<cd_count>\d+)-?cds?', + validator={'cd': lambda match: match.value > 0, 'cd_count': lambda match: match.value > 0}, + formatter={'cd_count': int}, + children=True, + private_parent=True, + properties={'cd': [None], 'cd_count': [None]}) + + return rebulk diff --git a/lib/guessit2/rules/properties/container.py b/lib/guessit2/rules/properties/container.py new file mode 100644 index 000000000..564b32f99 --- /dev/null +++ b/lib/guessit2/rules/properties/container.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +container property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk +from ..common.validators import seps_surround + + +def container(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE).string_defaults(ignore_case=True) + rebulk.defaults(name='container', + formatter=lambda value: value[1:], + tags=['extension'], + conflict_solver=lambda match, other: other + if other.name in ['format', 'video_codec'] or + other.name == 'container' and 'extension' not in other.tags + else '__default__') + + subtitles = ['srt', 'idx', 'sub', 'ssa', 'ass'] + info = ['nfo'] + videos = ['3g2', '3gp', '3gp2', 'asf', 'avi', 'divx', 'flv', 'm4v', 'mk2', + 'mka', 'mkv', 'mov', 'mp4', 'mp4a', 'mpeg', 'mpg', 'ogg', 'ogm', + 'ogv', 'qt', 'ra', 'ram', 'rm', 'ts', 'wav', 'webm', 'wma', 'wmv', + 'iso', 'vob'] + torrent = ['torrent'] + + rebulk.regex(r'\.\L<exts>$', exts=subtitles, tags=['extension', 'subtitle']) + rebulk.regex(r'\.\L<exts>$', exts=info, tags=['extension', 'info']) + rebulk.regex(r'\.\L<exts>$', exts=videos, tags=['extension', 'video']) + rebulk.regex(r'\.\L<exts>$', exts=torrent, tags=['extension', 'torrent']) + + rebulk.defaults(name='container', + validator=seps_surround, + conflict_solver=lambda match, other: match + if other.name in ['format', + 'video_codec'] or other.name == 'container' and 'extension' in other.tags + else '__default__') + + rebulk.string(*[sub for sub in subtitles if sub not in ['sub']], tags=['subtitle']) + rebulk.string(*videos, tags=['video']) + rebulk.string(*torrent, tags=['torrent']) + + return rebulk diff --git a/lib/guessit2/rules/properties/country.py b/lib/guessit2/rules/properties/country.py new file mode 100644 index 000000000..4cda291ea --- /dev/null +++ b/lib/guessit2/rules/properties/country.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +country property +""" +# pylint: disable=no-member +from __future__ import unicode_literals + +import babelfish + +from rebulk import Rebulk +from ..common.words import COMMON_WORDS, iter_words + + +def country(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().defaults(name='country') + + rebulk.functional(find_countries, + # Prefer language and any other property over country if not US or GB. + conflict_solver=lambda match, other: match + if other.name != 'language' or match.value not in [babelfish.Country('US'), + babelfish.Country('GB')] + else other, + properties={'country': [None]}) + + return rebulk + + +COUNTRIES_SYN = {'ES': ['españa'], + 'GB': ['UK'], + 'BR': ['brazilian', 'bra'], + # FIXME: this one is a bit of a stretch, not sure how to do it properly, though... + 'MX': ['Latinoamérica', 'latin america']} + + +class GuessitCountryConverter(babelfish.CountryReverseConverter): # pylint: disable=missing-docstring + def __init__(self): + self.guessit_exceptions = {} + + for alpha2, synlist in COUNTRIES_SYN.items(): + for syn in synlist: + self.guessit_exceptions[syn.lower()] = alpha2 + + @property + def codes(self): # pylint: disable=missing-docstring + return (babelfish.country_converters['name'].codes | + frozenset(babelfish.COUNTRIES.values()) | + frozenset(self.guessit_exceptions.keys())) + + def convert(self, alpha2): + if alpha2 == 'GB': + return 'UK' + return str(babelfish.Country(alpha2)) + + def reverse(self, name): + # exceptions come first, as they need to override a potential match + # with any of the other guessers + try: + return self.guessit_exceptions[name.lower()] + except KeyError: + pass + + try: + return babelfish.Country(name.upper()).alpha2 + except ValueError: + pass + + for conv in [babelfish.Country.fromname]: + try: + return conv(name).alpha2 + except babelfish.CountryReverseError: + pass + + raise babelfish.CountryReverseError(name) + + +babelfish.country_converters['guessit'] = GuessitCountryConverter() + + +def is_valid_country(country_object, context=None): + """ + Check if country is valid. + """ + if context and context.get('allowed_countries'): + allowed_countries = context.get('allowed_countries') + return country_object.name.lower() in allowed_countries or country_object.alpha2.lower() in allowed_countries + else: + return country_object.name.lower() not in COMMON_WORDS and country_object.alpha2.lower() not in COMMON_WORDS + + +def find_countries(string, context=None): + """ + Find countries in given string. + """ + ret = [] + for word_match in iter_words(string.strip().lower()): + try: + country_object = babelfish.Country.fromguessit(word_match.group()) + if is_valid_country(country_object, context): + ret.append((word_match.start(), word_match.end(), {'value': country_object})) + except babelfish.Error: + continue + return ret diff --git a/lib/guessit2/rules/properties/crc.py b/lib/guessit2/rules/properties/crc.py new file mode 100644 index 000000000..62275ca32 --- /dev/null +++ b/lib/guessit2/rules/properties/crc.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +crc and uuid properties +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk +from ..common.validators import seps_surround + + +def crc(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE) + rebulk.defaults(validator=seps_surround) + + rebulk.regex('(?:[a-fA-F]|[0-9]){8}', name='crc32', + conflict_solver=lambda match, other: match + if other.name in ['episode', 'season'] + else '__default__') + + rebulk.functional(guess_idnumber, name='uuid', + conflict_solver=lambda match, other: match + if other.name in ['episode', 'season'] + else '__default__') + return rebulk + + +_DIGIT = 0 +_LETTER = 1 +_OTHER = 2 + +_idnum = re.compile(r'(?P<uuid>[a-zA-Z0-9-]{20,})') # 1.0, (0, 0)) + + +def guess_idnumber(string): + """ + Guess id number function + :param string: + :type string: + :return: + :rtype: + """ + # pylint:disable=invalid-name + ret = [] + + matches = list(_idnum.finditer(string)) + for match in matches: + result = match.groupdict() + switch_count = 0 + switch_letter_count = 0 + letter_count = 0 + last_letter = None + + last = _LETTER + for c in result['uuid']: + if c in '0123456789': + ci = _DIGIT + elif c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': + ci = _LETTER + if c != last_letter: + switch_letter_count += 1 + last_letter = c + letter_count += 1 + else: + ci = _OTHER + + if ci != last: + switch_count += 1 + + last = ci + + # only return the result as probable if we alternate often between + # char type (more likely for hash values than for common words) + switch_ratio = float(switch_count) / len(result['uuid']) + letters_ratio = (float(switch_letter_count) / letter_count) if letter_count > 0 else 1 + + if switch_ratio > 0.4 and letters_ratio > 0.4: + ret.append(match.span()) + + return ret diff --git a/lib/guessit2/rules/properties/date.py b/lib/guessit2/rules/properties/date.py new file mode 100644 index 000000000..ff9c24851 --- /dev/null +++ b/lib/guessit2/rules/properties/date.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +date and year properties +""" +from __future__ import unicode_literals + +from rebulk import Rebulk, RemoveMatch, Rule + +from ..common.date import search_date, valid_year +from ..common.validators import seps_surround + + +def date(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().defaults(validator=seps_surround) + + rebulk.regex(r"\d{4}", name="year", formatter=int, + validator=lambda match: seps_surround(match) and valid_year(match.value)) + + def date_functional(string, context): + """ + Search for date in the string and retrieves match + + :param string: + :return: + """ + + ret = search_date(string, context.get('date_year_first'), context.get('date_day_first')) + if ret: + return ret[0], ret[1], {'value': ret[2]} + + rebulk.functional(date_functional, name="date", properties={'date': [None]}, + conflict_solver=lambda match, other: other + if other.name in ['episode', 'season'] + else '__default__') + + rebulk.rules(KeepMarkedYearInFilepart) + + return rebulk + + +class KeepMarkedYearInFilepart(Rule): + """ + Keep first years marked with [](){} in filepart, or if no year is marked, ensure it won't override titles. + """ + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + if len(matches.named('year')) > 1: + for filepart in matches.markers.named('path'): + years = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year') + if len(years) > 1: + group_years = [] + ungroup_years = [] + for year in years: + if matches.markers.at_match(year, lambda marker: marker.name == 'group'): + group_years.append(year) + else: + ungroup_years.append(year) + if group_years and ungroup_years: + ret.extend(ungroup_years) + ret.extend(group_years[1:]) # Keep the first year in marker. + elif not group_years: + ret.append(ungroup_years[0]) # Keep first year for title. + if len(ungroup_years) > 2: + ret.extend(ungroup_years[2:]) + return ret diff --git a/lib/guessit2/rules/properties/edition.py b/lib/guessit2/rules/properties/edition.py new file mode 100644 index 000000000..93021a5f8 --- /dev/null +++ b/lib/guessit2/rules/properties/edition.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +edition property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk +from ..common import dash +from ..common.validators import seps_surround + + +def edition(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) + rebulk.defaults(name='edition', validator=seps_surround) + + rebulk.regex('collector', 'collector-edition', 'edition-collector', value='Collector Edition') + rebulk.regex('special-edition', 'edition-special', value='Special Edition', + conflict_solver=lambda match, other: other + if other.name == 'episode_details' and other.value == 'Special' + else '__default__') + rebulk.regex('criterion-edition', 'edition-criterion', value='Criterion Edition') + rebulk.regex('deluxe', 'deluxe-edition', 'edition-deluxe', value='Deluxe Edition') + rebulk.regex('director\'?s?-cut', 'director\'?s?-cut-edition', 'edition-director\'?s?-cut', value='Director\'s cut') + + return rebulk diff --git a/lib/guessit2/rules/properties/episode_title.py b/lib/guessit2/rules/properties/episode_title.py new file mode 100644 index 000000000..fc92d7b97 --- /dev/null +++ b/lib/guessit2/rules/properties/episode_title.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Episode title +""" +from __future__ import unicode_literals + +from collections import defaultdict + +from rebulk import Rebulk, Rule, AppendMatch, RenameMatch +from ..common import seps, title_seps +from ..properties.title import TitleFromPosition, TitleBaseRule +from ..common.formatters import cleanup + + +def episode_title(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().rules(EpisodeTitleFromPosition, + AlternativeTitleReplace, + TitleToEpisodeTitle, + Filepart3EpisodeTitle, + Filepart2EpisodeTitle) + return rebulk + + +class TitleToEpisodeTitle(Rule): + """ + If multiple different title are found, convert the one following episode number to episode_title. + """ + dependency = TitleFromPosition + + def when(self, matches, context): + titles = matches.named('title') + + if len(titles) < 2: + return + + title_groups = defaultdict(list) + for title in titles: + title_groups[title.value].append(title) + + episode_titles = [] + main_titles = [] + for title in titles: + if matches.previous(title, lambda match: match.name == 'episode'): + episode_titles.append(title) + else: + main_titles.append(title) + + if episode_titles: + return episode_titles + + def then(self, matches, when_response, context): + for title in when_response: + matches.remove(title) + title.name = 'episode_title' + matches.append(title) + + +class EpisodeTitleFromPosition(TitleBaseRule): + """ + Add episode title match in existing matches + Must run after TitleFromPosition rule. + """ + dependency = TitleToEpisodeTitle + + def hole_filter(self, hole, matches): + episode = matches.previous(hole, + lambda previous: any(name in previous.names + for name in ['episode', 'episode_details', + 'episode_count', 'season', 'season_count', + 'date', 'title', 'year']), + 0) + + crc32 = matches.named('crc32') + + return episode or crc32 + + def filepart_filter(self, filepart, matches): + # Filepart where title was found. + if matches.range(filepart.start, filepart.end, lambda match: match.name == 'title'): + return True + return False + + def should_remove(self, match, matches, filepart, hole, context): + if match.name == 'episode_details': + return False + return super(EpisodeTitleFromPosition, self).should_remove(match, matches, filepart, hole, context) + + def __init__(self): + super(EpisodeTitleFromPosition, self).__init__('episode_title', ['title']) + + def when(self, matches, context): + if matches.named('episode_title'): + return + return super(EpisodeTitleFromPosition, self).when(matches, context) + + +class AlternativeTitleReplace(Rule): + """ + If alternateTitle was found and title is next to episode, season or date, replace it with episode_title. + """ + dependency = EpisodeTitleFromPosition + consequence = RenameMatch + + def when(self, matches, context): + if matches.named('episode_title'): + return + + alternative_title = matches.range(predicate=lambda match: match.name == 'alternativeTitle', index=0) + if alternative_title: + main_title = matches.chain_before(alternative_title.start, seps=seps, + predicate=lambda match: 'title' in match.tags, index=0) + if main_title: + episode = matches.previous(main_title, + lambda previous: any(name in previous.names + for name in ['episode', 'episode_details', + 'episode_count', 'season', + 'season_count', + 'date', 'title', 'year']), + 0) + + crc32 = matches.named('crc32') + + if episode or crc32: + return alternative_title + + def then(self, matches, when_response, context): + matches.remove(when_response) + when_response.name = 'episode_title' + matches.append(when_response) + + +class Filepart3EpisodeTitle(Rule): + """ + If we have at least 3 filepart structured like this: + + Serie name/SO1/E01-episode_title.mkv + AAAAAAAAAA/BBB/CCCCCCCCCCCCCCCCCCCC + + If CCCC contains episode and BBB contains seasonNumber + Then title is to be found in AAAA. + """ + consequence = AppendMatch('title') + + def when(self, matches, context): + fileparts = matches.markers.named('path') + if len(fileparts) < 3: + return + + filename = fileparts[-1] + directory = fileparts[-2] + subdirectory = fileparts[-3] + + episode_number = matches.range(filename.start, filename.end, lambda match: match.name == 'episode', 0) + if episode_number: + season = matches.range(directory.start, directory.end, lambda match: match.name == 'season', 0) + + if season: + hole = matches.holes(subdirectory.start, subdirectory.end, + formatter=cleanup, seps=title_seps, predicate=lambda match: match.value, + index=0) + if hole: + return hole + + +class Filepart2EpisodeTitle(Rule): + """ + If we have at least 2 filepart structured like this: + + Serie name SO1/E01-episode_title.mkv + AAAAAAAAAAAAA/BBBBBBBBBBBBBBBBBBBBB + + If BBBB contains episode and AAA contains a hole followed by seasonNumber + Then title is to be found in AAAA. + """ + consequence = AppendMatch('title') + + def when(self, matches, context): + fileparts = matches.markers.named('path') + if len(fileparts) < 2: + return + + filename = fileparts[-1] + directory = fileparts[-2] + + episode_number = matches.range(filename.start, filename.end, lambda match: match.name == 'episode', 0) + if episode_number: + season = matches.range(directory.start, directory.end, lambda match: match.name == 'season', 0) + if season: + hole = matches.holes(directory.start, directory.end, formatter=cleanup, seps=title_seps, + predicate=lambda match: match.value, index=0) + if hole: + return hole diff --git a/lib/guessit2/rules/properties/episodes.py b/lib/guessit2/rules/properties/episodes.py new file mode 100644 index 000000000..87995f0de --- /dev/null +++ b/lib/guessit2/rules/properties/episodes.py @@ -0,0 +1,366 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +episode, season, episode_count, season_count and episode_details properties +""" +from __future__ import unicode_literals + +from collections import defaultdict +import copy + +import regex as re + +from rebulk import Rebulk, RemoveMatch, Rule, AppendMatch, RenameMatch +from .title import TitleFromPosition +from ..common.validators import seps_surround +from ..common import dash, alt_dash +from ..common.numeral import numeral, parse_numeral + + +def episodes(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk() + rebulk.regex_defaults(flags=re.IGNORECASE).string_defaults(ignore_case=True) + rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator']) + + # 01x02, 01x02x03x04 + rebulk.regex(r'(?P<season>\d+)@?x@?(?P<episode>\d+)' + + r'(?:(?P<episodeSeparator>x|-|\+|&)(?P<episode>\d+))*', + # S01E02, S01x02, S01E02E03, S01Ex02, S01xE02, SO1Ex02Ex03 + r'S(?P<season>\d+)@?(?:xE|Ex|E|x)@?(?P<episode>\d+)' + + r'(?:(?P<episodeSeparator>xE|Ex|E|x|-|\+|&)(?P<episode>\d+))*', + # S01 + r'S(?P<season>\d+)' + + r'(?:(?P<seasonSeparator>S|-|\+|&)(?P<season>\d+))*', + formatter={'season': int, 'episode': int}, + tags=['SxxExx'], + abbreviations=[alt_dash], + children=True, + private_parent=True, + conflict_solver=lambda match, other: match + if match.name in ['season', 'episode'] and other.name in + ['screen_size', 'video_codec', 'audio_codec', 'audio_channels', 'container', 'date'] + else '__default__') + + # episode_details property + for episode_detail in ('Special', 'Bonus', 'Omake', 'Ova', 'Oav', 'Pilot', 'Unaired'): + rebulk.string(episode_detail, value=episode_detail, name='episode_details') + rebulk.regex(r'Extras?', name='episode_details', value='Extras') + + rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator'], + validate_all=True, validator={'__parent__': seps_surround}, children=True, private_parent=True) + + season_words = ['season', 'saison', 'serie', 'seasons', 'saisons', 'series'] + episode_words = ['episode', 'episodes', 'ep'] + of_words = ['of', 'sur'] + all_words = ['All'] + + rebulk.regex(r'\L<season_words>@?(?P<season>' + numeral + ')' + + r'(?:@?\L<of_words>@?(?P<count>' + numeral + '))?' + + r'(?:@?(?P<seasonSeparator>-)@?(?P<season>\d+))*' + + r'(?:@?(?P<seasonSeparator>\+|&)@?(?P<season>\d+))*', + of_words=of_words, + season_words=season_words, # Season 1, # Season one + abbreviations=[alt_dash], formatter={'season': parse_numeral, 'count': parse_numeral}) + + rebulk.regex(r'\L<episode_words>-?(?P<episode>\d+)' + + r'(?:v(?P<version>\d+))?' + + r'(?:-?\L<of_words>?-?(?P<count>\d+))?', + of_words=of_words, + episode_words=episode_words, # Episode 4 + abbreviations=[dash], formatter=int, + disabled=lambda context: context.get('type') == 'episode') + + rebulk.regex(r'\L<episode_words>-?(?P<episode>' + numeral + ')' + + r'(?:v(?P<version>\d+))?' + + r'(?:-?\L<of_words>?-?(?P<count>\d+))?', + of_words=of_words, + episode_words=episode_words, # Episode 4 + abbreviations=[dash], formatter={'episode': parse_numeral, 'version': int, 'count': int}, + disabled=lambda context: context.get('type') != 'episode') + + rebulk.regex(r'S?(?P<season>\d+)-?(?:xE|Ex|E|x)-?(?P<other>\L<all_words>)', + tags=['SxxExx'], + all_words=all_words, + abbreviations=[dash], + validator=None, + formatter={'season': int, 'other': lambda match: 'Complete'}) + + rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator'], validate_all=True, + validator={'__parent__': seps_surround}, children=True, private_parent=True) + + # 12, 13 + rebulk.regex(r'(?P<episode>\d{2})' + + r'(?:v(?P<version>\d+))?' + + r'(?:(?P<episodeSeparator>[x-])(?P<episode>\d{2}))*', + tags=['bonus-conflict', 'weak-movie'], formatter={'episode': int, 'version': int}) + + # 012, 013 + rebulk.regex(r'0(?P<episode>\d{1,2})' + + r'(?:v(?P<version>\d+))?' + + r'(?:(?P<episodeSeparator>[x-])0(?P<episode>\d{1,2}))*', + tags=['bonus-conflict', 'weak-movie'], formatter={'episode': int, 'version': int}) + + # 112, 113 + rebulk.regex(r'(?P<episode>\d{3,4})' + + r'(?:v(?P<version>\d+))?' + + r'(?:(?P<episodeSeparator>[x-])(?P<episode>\d{3,4}))*', + tags=['bonus-conflict', 'weak-movie'], formatter={'episode': int, 'version': int}, + disabled=lambda context: not context.get('episode_prefer_number', False)) + + # 1, 2, 3 + rebulk.regex(r'(?P<episode>\d)' + + r'(?:v(?P<version>\d+))?' + + r'(?:(?P<episodeSeparator>[x-])(?P<episode>\d{1,2}))*', + tags=['bonus-conflict', 'weak-movie'], formatter={'episode': int, 'version': int}, + disabled=lambda context: context.get('type') != 'episode') + + # e112, e113 + rebulk.regex(r'e(?P<episode>\d{1,4})' + + r'(?:v(?P<version>\d+))?' + + r'(?:(?P<episodeSeparator>e|x|-)(?P<episode>\d{1,4}))*', + formatter={'episode': int, 'version': int}) + + # ep 112, ep113, ep112, ep113 + rebulk.regex(r'ep-?(?P<episode>\d{1,4})' + + r'(?:v(?P<version>\d+))?' + + r'(?:(?P<episodeSeparator>ep|e|x|-)(?P<episode>\d{1,4}))*', + abbreviations=[dash], + formatter={'episode': int, 'version': int}) + + # 102, 0102 + rebulk.regex(r'(?P<season>\d{1,2})(?P<episode>\d{2})' + + r'(?:v(?P<version>\d+))?' + + r'(?:(?P<episodeSeparator>x|-)(?P<episode>\d{2}))*', + tags=['bonus-conflict', 'weak-movie', 'weak-duplicate'], + formatter={'season': int, 'episode': int, 'version': int}, + conflict_solver=lambda match, other: match if other.name == 'year' else '__default__', + disabled=lambda context: context.get('episode_prefer_number', False)) + + rebulk.regex(r'v(?P<version>\d+)', children=True, private_parent=True, formatter=int) + + rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator']) + + # detached of X count (season/episode) + rebulk.regex(r'(?P<episode>\d+)?-?\L<of_words>-?(?P<count>\d+)-?\L<episode_words>?', of_words=of_words, + episode_words=episode_words, abbreviations=[dash], children=True, private_parent=True, formatter=int) + + rebulk.regex(r'Minisodes?', name='episode_format', value="Minisode") + + # Harcoded movie to disable weak season/episodes + rebulk.regex('OSS-?117', + abbreviations=[dash], name="hardcoded-movies", marker=True, + conflict_solver=lambda match, other: None) + + rebulk.rules(EpisodeNumberSeparatorRange, SeasonSeparatorRange, RemoveWeakIfMovie, RemoveWeakIfSxxExx, + RemoveWeakDuplicate, EpisodeDetailValidator, RemoveDetachedEpisodeNumber, VersionValidator, + CountValidator, EpisodeSingleDigitValidator) + + return rebulk + + +class CountValidator(Rule): + """ + Validate count property and rename it + """ + priority = 64 + consequence = [RemoveMatch, RenameMatch('episode_count'), RenameMatch('season_count')] + + properties = {'episode_count': [None], 'season_count': [None]} + + def when(self, matches, context): + to_remove = [] + episode_count = [] + season_count = [] + + for count in matches.named('count'): + previous = matches.previous(count, lambda match: match.name in ['episode', 'season'], 0) + if previous: + if previous.name == 'episode': + episode_count.append(count) + elif previous.name == 'season': + season_count.append(count) + else: + to_remove.append(count) + return to_remove, episode_count, season_count + + +class EpisodeNumberSeparatorRange(Rule): + """ + Remove separator matches and create matches for episoderNumber range. + """ + priority = 128 + consequence = [RemoveMatch, AppendMatch] + + def when(self, matches, context): + to_remove = [] + to_append = [] + for separator in matches.named('episodeSeparator'): + previous_match = matches.previous(separator, lambda match: match.name == 'episode', 0) + next_match = matches.next(separator, lambda match: match.name == 'episode', 0) + + if previous_match and next_match and separator.value == '-': + for episode_number in range(previous_match.value + 1, next_match.value): + match = copy.copy(separator) + match.private = False + match.name = 'episode' + match.value = episode_number + to_append.append(match) + to_remove.append(separator) + return to_remove, to_append + + +class SeasonSeparatorRange(Rule): + """ + Remove separator matches and create matches for season range. + """ + priority = 128 + consequence = [RemoveMatch, AppendMatch] + + def when(self, matches, context): + to_remove = [] + to_append = [] + for separator in matches.named('seasonSeparator'): + previous_match = matches.previous(separator, lambda match: match.name == 'season', 0) + next_match = matches.next(separator, lambda match: match.name == 'season', 0) + + if previous_match and next_match and separator.value == '-': + for episode_number in range(previous_match.value + 1, next_match.value): + match = copy.copy(separator) + match.private = False + match.name = 'season' + match.value = episode_number + to_append.append(match) + to_remove.append(separator) + return to_remove, to_append + + +class RemoveWeakIfMovie(Rule): + """ + Remove weak-movie tagged matches if it seems to be a movie. + """ + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + if matches.named('year') or matches.markers.named('hardcoded-movies'): + return matches.tagged('weak-movie') + + +class RemoveWeakIfSxxExx(Rule): + """ + Remove weak-movie tagged matches if SxxExx pattern is matched. + """ + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + if matches.tagged('SxxExx', lambda match: not match.private): + return matches.tagged('weak-movie') + + +class RemoveWeakDuplicate(Rule): + """ + Remove weak-duplicate tagged matches if duplicate patterns, for example The 100.109 + """ + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + to_remove = [] + for filepart in matches.markers.named('path'): + patterns = defaultdict(list) + for match in reversed(matches.range(filepart.start, filepart.end, + predicate=lambda match: 'weak-duplicate' in match.tags)): + if match.pattern in patterns[match.name]: + to_remove.append(match) + else: + patterns[match.name].append(match.pattern) + return to_remove + + +class EpisodeDetailValidator(Rule): + """ + Validate episode_details if they are detached or next to season or episode. + """ + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for detail in matches.named('episode_details'): + if not seps_surround(detail) \ + and not matches.previous(detail, lambda match: match.name in ['season', 'episode']) \ + and not matches.next(detail, lambda match: match.name in ['season', 'episode']): + ret.append(detail) + return ret + + +class RemoveDetachedEpisodeNumber(Rule): + """ + If multiple episode are found, remove those that are not detached from a range and less than 10. + + Fairy Tail 2 - 16-20, 2 should be removed. + """ + priority = 64 + consequence = RemoveMatch + dependency = [RemoveWeakIfSxxExx, RemoveWeakDuplicate] + + def when(self, matches, context): + ret = [] + + episode_numbers = [] + episode_values = set() + for match in matches.named('episode', lambda match: not match.private and 'weak-movie' in match.tags): + if match.value not in episode_values: + episode_numbers.append(match) + episode_values.add(match.value) + + episode_numbers = list(sorted(episode_numbers, key=lambda match: match.value)) + if len(episode_numbers) > 1 and \ + episode_numbers[0].value < 10 and \ + episode_numbers[1].value - episode_numbers[0].value != 1: + parent = episode_numbers[0] + while parent: # TODO: Add a feature in rebulk to avoid this ... + ret.append(parent) + parent = parent.parent + return ret + + +class VersionValidator(Rule): + """ + Validate version if previous match is episode or if surrounded by separators. + """ + priority = 64 + dependency = [RemoveWeakIfMovie, RemoveWeakIfSxxExx] + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for version in matches.named('version'): + episode_number = matches.previous(version, lambda match: match.name == 'episode', 0) + if not episode_number and not seps_surround(version.initiator): + ret.append(version) + return ret + + +class EpisodeSingleDigitValidator(Rule): + """ + Remove single digit episode when inside a group that doesn't own title. + """ + dependency = [TitleFromPosition] + + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for episode in matches.named('episode', lambda match: len(match.initiator) == 1): + group = matches.markers.at_match(episode, lambda marker: marker.name == 'group', index=0) + if group: + if not matches.range(*group.span, predicate=lambda match: match.name == 'title'): + ret.append(episode) + return ret diff --git a/lib/guessit2/rules/properties/film.py b/lib/guessit2/rules/properties/film.py new file mode 100644 index 000000000..5c6e3ab5b --- /dev/null +++ b/lib/guessit2/rules/properties/film.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +film property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk, AppendMatch, Rule +from ..common.formatters import cleanup + + +def film(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE) + + rebulk.regex(r'f(\d+)', name='film', private_parent=True, children=True, formatter=int) + + rebulk.rules(FilmTitleRule) + + return rebulk + + +class FilmTitleRule(Rule): + """ + Rule to find out film_title (hole after film property + """ + consequence = AppendMatch + + properties = {'film_title': [None]} + + def when(self, matches, context): + bonus_number = matches.named('film', lambda match: not match.private, index=0) + if bonus_number: + filepath = matches.markers.at_match(bonus_number, lambda marker: marker.name == 'path', 0) + hole = matches.holes(filepath.start, bonus_number.start + 1, formatter=cleanup, index=0) + if hole and hole.value: + hole.name = 'film_title' + return hole diff --git a/lib/guessit2/rules/properties/format.py b/lib/guessit2/rules/properties/format.py new file mode 100644 index 000000000..d250a8b88 --- /dev/null +++ b/lib/guessit2/rules/properties/format.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +format property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk, RemoveMatch, Rule +from ..common import dash +from ..common.validators import seps_before, seps_after + + +def format_(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) + rebulk.defaults(name="format") + + rebulk.regex("VHS", "VHS-?Rip", value="VHS") + rebulk.regex("CAM", "CAM-?Rip", "HD-?CAM", value="Cam") + rebulk.regex("TELESYNC", "TS", "HD-?TS", value="Telesync") + rebulk.regex("WORKPRINT", "WP", value="Workprint") + rebulk.regex("TELECINE", "TC", value="Telecine") + rebulk.regex("PPV", "PPV-?Rip", value="PPV") # Pay Per View + rebulk.regex("SD-?TV", "SD-?TV-?Rip", "Rip-?SD-?TV", "TV-?Rip", + "Rip-?TV", value="TV") # TV is too common to allow matching + rebulk.regex("DVB-?Rip", "DVB", "PD-?TV", value="DVB") + rebulk.regex("DVD", "DVD-?Rip", "VIDEO-?TS", "DVD-?R(?:$|(?!E))", # "DVD-?R(?:$|^E)" => DVD-Real ... + "DVD-?9", "DVD-?5", value="DVD") + + rebulk.regex("HD-?TV", "TV-?RIP-?HD", "HD-?TV-?RIP", "HD-?RIP", value="HDTV") + rebulk.regex("VOD", "VOD-?Rip", value="VOD") + rebulk.regex("WEB-?Rip", value="WEBRip") + rebulk.regex("WEB-?DL", "WEB-?HD", "WEB", value="WEB-DL") + rebulk.regex("HD-?DVD-?Rip", "HD-?DVD", value="HD-DVD") + rebulk.regex("Blu-?ray(?:-?Rip)?", "B[DR]", "B[DR]-?Rip", "BD[59]", "BD25", "BD50", value="BluRay") + + rebulk.rules(ValidateFormat) + + return rebulk + + +class ValidateFormat(Rule): + """ + Validate format with screener property or separated. + """ + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for format_match in matches.named('format'): + if not seps_before(format_match) and \ + not matches.range(format_match.start - 1, format_match.start - 2, + lambda match: match.name == 'other' and match.value == 'Screener'): + ret.append(format_match) + continue + if not seps_after(format_match) and \ + not matches.range(format_match.end, format_match.end + 1, + lambda match: match.name == 'other' and match.value == 'Screener'): + ret.append(format_match) + continue + return ret diff --git a/lib/guessit2/rules/properties/language.py b/lib/guessit2/rules/properties/language.py new file mode 100644 index 000000000..c42b2e116 --- /dev/null +++ b/lib/guessit2/rules/properties/language.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +language and subtitle_language properties +""" +# pylint: disable=no-member +from __future__ import unicode_literals + +import copy + +import regex as re +import babelfish + +from rebulk import Rebulk, Rule, RemoveMatch, RenameMatch +from ..common.words import iter_words, COMMON_WORDS +from ..common.validators import seps_surround + + +def language(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk() + + rebulk.string(*subtitle_prefixes, name="subtitle_language.prefix", ignore_case=True, private=True, + validator=seps_surround) + rebulk.string(*subtitle_suffixes, name="subtitle_language.suffix", ignore_case=True, private=True, + validator=seps_surround) + rebulk.functional(find_languages, properties={'language': [None]}) + rebulk.rules(SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, SubtitleExtensionRule) + + return rebulk + + +COMMON_WORDS_STRICT = frozenset(['brazil']) + +UNDETERMINED = babelfish.Language('und') + +SYN = {('und', None): ['unknown', 'inconnu', 'unk', 'un'], + ('ell', None): ['gr', 'greek'], + ('spa', None): ['esp', 'español'], + ('fra', None): ['français', 'vf', 'vff', 'vfi', 'vfq'], + ('swe', None): ['se'], + ('por', 'BR'): ['po', 'pb', 'pob', 'br', 'brazilian'], + ('cat', None): ['català'], + ('ces', None): ['cz'], + ('ukr', None): ['ua'], + ('zho', None): ['cn'], + ('jpn', None): ['jp'], + ('hrv', None): ['scr'], + ('mul', None): ['multi', 'dl']} # http://scenelingo.wordpress.com/2009/03/24/what-does-dl-mean/ + + +class GuessitConverter(babelfish.LanguageReverseConverter): # pylint: disable=missing-docstring + _with_country_regexp = re.compile(r'(.*)\((.*)\)') + _with_country_regexp2 = re.compile(r'(.*)-(.*)') + + def __init__(self): + self.guessit_exceptions = {} + for (alpha3, country), synlist in SYN.items(): + for syn in synlist: + self.guessit_exceptions[syn.lower()] = (alpha3, country, None) + + @property + def codes(self): # pylint: disable=missing-docstring + return (babelfish.language_converters['alpha3b'].codes | + babelfish.language_converters['alpha2'].codes | + babelfish.language_converters['name'].codes | + babelfish.language_converters['opensubtitles'].codes | + babelfish.country_converters['name'].codes | + frozenset(self.guessit_exceptions.keys())) + + def convert(self, alpha3, country=None, script=None): + return str(babelfish.Language(alpha3, country, script)) + + def reverse(self, name): + with_country = (GuessitConverter._with_country_regexp.match(name) or + GuessitConverter._with_country_regexp2.match(name)) + + name = name.lower() + if with_country: + lang = babelfish.Language.fromguessit(with_country.group(1).strip()) + lang.country = babelfish.Country.fromguessit(with_country.group(2).strip()) + return lang.alpha3, lang.country.alpha2 if lang.country else None, lang.script or None + + # exceptions come first, as they need to override a potential match + # with any of the other guessers + try: + return self.guessit_exceptions[name] + except KeyError: + pass + + for conv in [babelfish.Language, + babelfish.Language.fromalpha3b, + babelfish.Language.fromalpha2, + babelfish.Language.fromname, + babelfish.Language.fromopensubtitles]: + try: + reverse = conv(name) + return reverse.alpha3, reverse.country, reverse.script + except (ValueError, babelfish.LanguageReverseError): + pass + + raise babelfish.LanguageReverseError(name) + + +babelfish.language_converters['guessit'] = GuessitConverter() + +subtitle_prefixes = ['sub', 'subs', 'st', 'vost', 'subforced', 'fansub', 'hardsub'] +subtitle_suffixes = ['subforced', 'fansub', 'hardsub', 'sub', 'subs'] +lang_prefixes = ['true'] + +all_lang_prefixes_suffixes = subtitle_prefixes + subtitle_suffixes + lang_prefixes + + +def find_languages(string, context=None): + """Find languages in the string + + :return: list of tuple (property, Language, lang_word, word) + """ + allowed_languages = context.get('allowed_languages') + common_words = COMMON_WORDS_STRICT if allowed_languages else COMMON_WORDS + + matches = [] + for word_match in iter_words(string): + word = word_match.group() + start, end = word_match.span() + + lang_word = word.lower() + key = 'language' + for prefix in subtitle_prefixes: + if lang_word.startswith(prefix): + lang_word = lang_word[len(prefix):] + key = 'subtitle_language' + for suffix in subtitle_suffixes: + if lang_word.endswith(suffix): + lang_word = lang_word[:len(suffix) - 1] + key = 'subtitle_language' + for prefix in lang_prefixes: + if lang_word.startswith(prefix): + lang_word = lang_word[len(prefix):] + if lang_word not in common_words and word.lower() not in common_words: + try: + lang = babelfish.Language.fromguessit(lang_word) + match = (start, end, {'name': key, 'value': lang}) + if allowed_languages: + if lang.name.lower() in allowed_languages \ + or lang.alpha2.lower() in allowed_languages \ + or lang.alpha3.lower() in allowed_languages: + matches.append(match) + # Keep language with alpha2 equivalent. Others are probably + # uncommon languages. + elif lang == 'mul' or hasattr(lang, 'alpha2'): + matches.append(match) + except babelfish.Error: + pass + return matches + + +class SubtitlePrefixLanguageRule(Rule): + """ + Convert language guess as subtitle_language if previous match is a subtitle language prefix + """ + consequence = RemoveMatch + + properties = {'subtitle_language': [None]} + + def when(self, matches, context): + to_rename = [] + to_remove = matches.named('subtitle_language.prefix') + for lang in matches.named('language'): + prefix = matches.previous(lang, lambda match: match.name == 'subtitle_language.prefix', 0) + if not prefix: + group_marker = matches.markers.at_match(lang, lambda marker: marker.name == 'group', 0) + if group_marker: + # Find prefix if placed just before the group + prefix = matches.previous(group_marker, lambda match: match.name == 'subtitle_language.prefix', + 0) + if not prefix: + # Find prefix if placed before in the group + prefix = matches.range(group_marker.start, lang.start, + lambda match: match.name == 'subtitle_language.prefix', 0) + if prefix: + to_rename.append((prefix, lang)) + if prefix in to_remove: + to_remove.remove(prefix) + return to_rename, to_remove + + def then(self, matches, when_response, context): + to_rename, to_remove = when_response + super(SubtitlePrefixLanguageRule, self).then(matches, to_remove, context) + for prefix, match in to_rename: + # Remove suffix equivalent of prefix. + suffix = copy.copy(prefix) + suffix.name = 'subtitle_language.suffix' + if suffix in matches: + matches.remove(suffix) + matches.remove(match) + match.name = 'subtitle_language' + matches.append(match) + + +class SubtitleSuffixLanguageRule(Rule): + """ + Convert language guess as subtitle_language if next match is a subtitle language suffix + """ + dependency = SubtitlePrefixLanguageRule + consequence = RemoveMatch + + properties = {'subtitle_language': [None]} + + def when(self, matches, context): + to_append = [] + to_remove = matches.named('subtitle_language.suffix') + for lang in matches.named('language'): + suffix = matches.next(lang, lambda match: match.name == 'subtitle_language.suffix', 0) + if suffix: + to_append.append(lang) + if suffix in to_remove: + to_remove.remove(suffix) + return to_append, to_remove + + def then(self, matches, when_response, context): + to_rename, to_remove = when_response + super(SubtitleSuffixLanguageRule, self).then(matches, to_remove, context) + for match in to_rename: + matches.remove(match) + match.name = 'subtitle_language' + matches.append(match) + + +class SubtitleExtensionRule(Rule): + """ + Convert language guess as subtitle_language if next match is a subtitle extension + """ + consequence = RenameMatch('subtitle_language') + + properties = {'subtitle_language': [None]} + + def when(self, matches, context): + subtitle_extension = matches.named('container', + lambda match: 'extension' in match.tags and 'subtitle' in match.tags, + 0) + if subtitle_extension: + subtitle_lang = matches.previous(subtitle_extension, lambda match: match.name == 'language', 0) + if subtitle_lang: + return subtitle_lang diff --git a/lib/guessit2/rules/properties/mimetype.py b/lib/guessit2/rules/properties/mimetype.py new file mode 100644 index 000000000..8e21ca32e --- /dev/null +++ b/lib/guessit2/rules/properties/mimetype.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +mimetype property +""" +from __future__ import unicode_literals + +import mimetypes + +from rebulk import Rebulk, CustomRule, POST_PROCESS +from rebulk.match import Match + +from ...rules.processors import Processors + + +def mimetype(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + return Rebulk().rules(Mimetype) + + +class Mimetype(CustomRule): + """ + Mimetype post processor + :param matches: + :type matches: + :return: + :rtype: + """ + priority = POST_PROCESS + + dependency = Processors + + def when(self, matches, context): + mime, _ = mimetypes.guess_type(matches.input_string, strict=False) + return mime + + def then(self, matches, when_response, context): + mime = when_response + matches.append(Match(len(matches.input_string), len(matches.input_string), name='mimetype', value=mime)) diff --git a/lib/guessit2/rules/properties/other.py b/lib/guessit2/rules/properties/other.py new file mode 100644 index 000000000..38e69f767 --- /dev/null +++ b/lib/guessit2/rules/properties/other.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +other property +""" +from __future__ import unicode_literals + +import copy + +import regex as re + +from rebulk import Rebulk, Rule, RemoveMatch, POST_PROCESS, AppendMatch +from ..common import dash +from ..common import seps +from ..common.validators import seps_surround +from guessit2.rules.common.formatters import raw_cleanup + + +def other(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) + rebulk.defaults(name="other", validator=seps_surround) + + rebulk.regex('Audio-?Fix', 'Audio-?Fixed', value='AudioFix') + rebulk.regex('Sync-?Fix', 'Sync-?Fixed', value='SyncFix') + rebulk.regex('Dual-?Audio', value='DualAudio') + rebulk.regex('ws', 'wide-?screen', value='WideScreen') + rebulk.string('Netflix', 'NF', value='Netflix') + + rebulk.string('Real', 'Fix', value='Proper', tags=['has-neighbor-before', 'has-neighbor-after']) + rebulk.string('Proper', 'Repack', 'Rerip', value='Proper') + rebulk.string('Fansub', value='Fansub', tags='has-neighbor') + rebulk.string('Fastsub', value='Fastsub', tags='has-neighbor') + + rebulk.regex('(?:Seasons?-)?Complete', value='Complete', tags=['release-group-prefix'], + validator=lambda match: seps_surround(match) and match.raw.lower().strip(seps) != "complete") + rebulk.string('R5', 'RC', value='R5') + rebulk.regex('Pre-?Air', value='Preair') + + for value in ( + 'Screener', 'Remux', '3D', 'HD', 'mHD', 'HDLight', 'HQ', 'DDC', 'HR', 'PAL', 'SECAM', 'NTSC', 'CC', 'LD', + 'MD'): + rebulk.string(value, value=value) + + for value in ('Limited', 'Complete', 'Classic', 'Unrated', 'LiNE', 'Bonus', 'Trailer', 'FINAL'): + rebulk.string(value, value=value, tags=['has-neighbor', 'release-group-prefix']) + + rebulk.string('VO', 'OV', value='OV', tags='has-neighbor') + + rebulk.regex('Scr(?:eener)?', value='Screener', validator=None, tags='other.validate.screener') + + rebulk.rules(ValidateHasNeighbor, ValidateHasNeighborAfter, ValidateHasNeighborBefore, ValidateScreenerRule, + ProperCountRule) + + return rebulk + + +class ProperCountRule(Rule): + """ + Add proper_count property + """ + priority = POST_PROCESS + + consequence = AppendMatch + + properties = {'proper_count': [None]} + + def when(self, matches, context): + propers = matches.named('other', lambda match: match.value == 'Proper') + if propers: + raws = {} # Count distinct raw values + for proper in propers: + raws[raw_cleanup(proper.raw)] = proper + proper_count_match = copy.copy(propers[-1]) + proper_count_match.name = 'proper_count' + proper_count_match.value = len(raws) + return proper_count_match + + +class ValidateHasNeighbor(Rule): + """ + Validate tag has-neighbor + """ + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for to_check in matches.range(predicate=lambda match: 'has-neighbor' in match.tags): + previous_match = matches.previous(to_check, index=0) + previous_group = matches.markers.previous(to_check, lambda marker: marker.name == 'group', 0) + if previous_group and (not previous_match or previous_group.end > previous_match.end): + previous_match = previous_group + if previous_match and not matches.input_string[previous_match.end:to_check.start].strip(seps): + break + next_match = matches.next(to_check, index=0) + next_group = matches.markers.next(to_check, lambda marker: marker.name == 'group', 0) + if next_group and (not next_match or next_group.start < next_match.start): + next_match = next_group + if next_match and not matches.input_string[to_check.end:next_match.start].strip(seps): + break + ret.append(to_check) + return ret + + +class ValidateHasNeighborBefore(Rule): + """ + Validate tag has-neighbor-before that previous match exists. + """ + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for to_check in matches.range(predicate=lambda match: 'has-neighbor-before' in match.tags): + next_match = matches.next(to_check, index=0) + next_group = matches.markers.next(to_check, lambda marker: marker.name == 'group', 0) + if next_group and (not next_match or next_group.start < next_match.start): + next_match = next_group + if next_match and not matches.input_string[to_check.end:next_match.start].strip(seps): + break + ret.append(to_check) + return ret + + +class ValidateHasNeighborAfter(Rule): + """ + Validate tag has-neighbor-after that next match exists. + """ + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for to_check in matches.range(predicate=lambda match: 'has-neighbor-after' in match.tags): + previous_match = matches.previous(to_check, index=0) + previous_group = matches.markers.previous(to_check, lambda marker: marker.name == 'group', 0) + if previous_group and (not previous_match or previous_group.end > previous_match.end): + previous_match = previous_group + if previous_match and not matches.input_string[previous_match.end:to_check.start].strip(seps): + break + ret.append(to_check) + return ret + + +class ValidateScreenerRule(Rule): + """ + Validate tag other.validate.screener + """ + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for screener in matches.named('other', lambda match: 'other.validate.screener' in match.tags): + format_match = matches.previous(screener, lambda match: match.name == 'format', 0) + if not format_match or matches.input_string[format_match.end:screener.start].strip(seps): + ret.append(screener) + return ret diff --git a/lib/guessit2/rules/properties/part.py b/lib/guessit2/rules/properties/part.py new file mode 100644 index 000000000..483b86edb --- /dev/null +++ b/lib/guessit2/rules/properties/part.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +part property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk +from ..common import dash +from ..common.validators import seps_surround +from ..common.numeral import numeral, parse_numeral + + +def part(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], validator={'__parent__': seps_surround}) + + prefixes = ['pt', 'part'] + + rebulk.regex(r'\L<prefixes>-?(' + numeral + r')', prefixes=prefixes, + name='part', validate_all=True, private_parent=True, children=True, formatter=parse_numeral) + + return rebulk diff --git a/lib/guessit2/rules/properties/release_group.py b/lib/guessit2/rules/properties/release_group.py new file mode 100644 index 000000000..0802f490a --- /dev/null +++ b/lib/guessit2/rules/properties/release_group.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +release_group property +""" +from __future__ import unicode_literals + +import copy + +import regex as re + +from rebulk import Rebulk, Rule, AppendMatch +from ..common.validators import int_coercable +from ..properties.title import TitleFromPosition +from ..common.formatters import cleanup +from ..common import seps, dash +from ..common.comparators import marker_sorted + + +def release_group(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + return Rebulk().rules(SceneReleaseGroup, AnimeReleaseGroup, ExpectedReleaseGroup) + + +forbidden_groupnames = ['rip', 'by', 'for', 'par', 'pour', 'bonus'] + +groupname_seps = ''.join([c for c in seps if c not in '[]{}()']) + + +def clean_groupname(string): + """ + Removes and strip separators from input_string + :param input_string: + :type input_string: + :return: + :rtype: + """ + string = string.strip(groupname_seps) + for forbidden in forbidden_groupnames: + if string.lower().startswith(forbidden): + string = string[len(forbidden):] + string = string.strip(groupname_seps) + if string.lower().endswith(forbidden): + string = string[:len(forbidden)] + string = string.strip(groupname_seps) + return string + + +_scene_previous_names = ['video_codec', 'format', 'video_api', 'audio_codec', 'audio_profile', 'video_profile', + 'audio_channels', 'screen_size'] + +_scene_previous_tags = ['release-group-prefix'] + + +class ExpectedReleaseGroup(Rule): + """ + Add release_group match from expected_group option + """ + consequence = AppendMatch + + properties = {'release_group': [None]} + + def enabled(self, context): + return context.get('expected_group') + + def when(self, matches, context): + expected_rebulk = Rebulk().defaults(name='release_group') + + for expected_group in context.get('expected_group'): + if expected_group.startswith('re:'): + expected_group = expected_group[3:] + expected_group = expected_group.replace(' ', '-') + expected_rebulk.regex(expected_group, abbreviations=[dash], flags=re.IGNORECASE) + else: + expected_rebulk.string(expected_group, ignore_case=True) + + matches = expected_rebulk.matches(matches.input_string, context) + return matches + + +class SceneReleaseGroup(Rule): + """ + Add release_group match in existing matches (scene format). + + Something.XViD-ReleaseGroup.mkv + """ + dependency = [TitleFromPosition, ExpectedReleaseGroup] + consequence = AppendMatch + + properties = {'release_group': [None]} + + def when(self, matches, context): + # If a release_group is found before, ignore this kind of release_group rule. + if matches.named('release_group'): + return + + ret = [] + + for filepart in marker_sorted(matches.markers.named('path'), matches): + start, end = filepart.span + + last_hole = matches.holes(start, end + 1, formatter=clean_groupname, + predicate=lambda hole: cleanup(hole.value), index=-1) + + if last_hole: + previous_match = matches.previous(last_hole, lambda match: not match.private, index=0) + if previous_match and (previous_match.name in _scene_previous_names or + any(tag in previous_match.tags for tag in _scene_previous_tags)) and \ + not matches.input_string[previous_match.end:last_hole.start].strip(seps) \ + and not int_coercable(last_hole.value.strip(seps)): + + last_hole.name = 'release_group' + last_hole.tags = ['scene'] + + # if hole is insed a group marker with same value, remove [](){} ... + group = matches.markers.at_match(last_hole, lambda marker: marker.name == 'group', 0) + if group: + group.formatter = clean_groupname + if group.value == last_hole.value: + last_hole.start = group.start + 1 + last_hole.end = group.end - 1 + last_hole.tags = ['anime'] + + ret.append(last_hole) + return ret + + +class AnimeReleaseGroup(Rule): + """ + Add release_group match in existing matches (anime format) + ...[ReleaseGroup] Something.mkv + """ + dependency = [SceneReleaseGroup, TitleFromPosition] + consequence = AppendMatch + + properties = {'release_group': [None]} + + def when(self, matches, context): + ret = [] + + # If a release_group is found before, ignore this kind of release_group rule. + if matches.named('release_group'): + return ret + + for filepart in marker_sorted(matches.markers.named('path'), matches): + + # pylint:disable=bad-continuation + empty_group_marker = matches.markers \ + .range(filepart.start, filepart.end, lambda marker: marker.name == 'group' + and not matches.range(marker.start, marker.end) + and not int_coercable(marker.value.strip(seps)), + 0) + + if empty_group_marker: + group = copy.copy(empty_group_marker) + group.marker = False + group.raw_start += 1 + group.raw_end -= 1 + group.tags = ['anime'] + group.name = 'release_group' + ret.append(group) + return ret diff --git a/lib/guessit2/rules/properties/screen_size.py b/lib/guessit2/rules/properties/screen_size.py new file mode 100644 index 000000000..1ccc6572e --- /dev/null +++ b/lib/guessit2/rules/properties/screen_size.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +screen_size property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk, Rule, RemoveMatch +from ..common.validators import seps_surround +from guessit2.rules.common import dash + + +def screen_size(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + def conflict_solver(match, other): + """ + Conflict solver for most screen_size. + """ + if other.name == 'screen_size': + if 'resolution' in other.tags: + # The chtouile to solve conflict in "720 x 432" string matching both 720p pattern + int_value = _digits_re.findall(match.raw)[-1] + if other.value.startswith(int_value): + return match + return other + return '__default__' + + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE) + rebulk.defaults(name="screen_size", validator=seps_surround, conflict_solver=conflict_solver) + + rebulk.regex(r"(?:\d{3,}(?:x|\*))?360(?:i|p?x?)", value="360p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?368(?:i|p?x?)", value="368p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?480(?:i|p?x?)", value="480p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?576(?:i|p?x?)", value="576p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?720(?:i|p?(?:50|60)?x?)", value="720p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?720(?:p(?:50|60)?x?)", value="720p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?720hd", value="720p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?900(?:i|p?x?)", value="900p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?1080i", value="1080i") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?1080p?x?", value="1080p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?1080(?:p(?:50|60)?x?)", value="1080p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?1080hd", value="1080p") + rebulk.regex(r"(?:\d{3,}(?:x|\*))?2160(?:i|p?x?)", value="4K") + + _digits_re = re.compile(r'\d+') + + rebulk.defaults(name="screen_size", validator=seps_surround) + rebulk.regex(r'\d{3,}-?(?:x|\*)-?\d{3,}', + formatter=lambda value: 'x'.join(_digits_re.findall(value)), + abbreviations=[dash], + tags=['resolution'], + conflict_solver=lambda match, other: '__default__' if other.name == 'screen_size' else other) + + rebulk.rules(ScreenSizeOnlyOne) + + return rebulk + + +class ScreenSizeOnlyOne(Rule): + """ + Keep a single screen_size pet filepath part. + """ + consequence = RemoveMatch + + def when(self, matches, context): + to_remove = [] + for filepart in matches.markers.named('path'): + screensize = list(reversed(matches.range(filepart.start, filepart.end, + lambda match: match.name == 'screen_size'))) + if len(screensize) > 1: + to_remove.extend(screensize[1:]) + + return to_remove diff --git a/lib/guessit2/rules/properties/title.py b/lib/guessit2/rules/properties/title.py new file mode 100644 index 000000000..25efea85c --- /dev/null +++ b/lib/guessit2/rules/properties/title.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +title property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk, Rule, AppendMatch, RemoveMatch, AppendTags +from rebulk.formatters import formatters +from rebulk.pattern import RePattern +from rebulk.utils import find_all + +from .film import FilmTitleRule +from .language import SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, SubtitleExtensionRule +from ..common.formatters import cleanup, reorder_title +from ..common.comparators import marker_sorted +from ..common import seps, title_seps, dash + + +def title(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().rules(TitleFromPosition, PreferTitleWithYear) + + def expected_title(input_string, context): + """ + Expected title functional pattern. + :param input_string: + :type input_string: + :param context: + :type context: + :return: + :rtype: + """ + ret = [] + for search in context.get('expected_title'): + if search.startswith('re:'): + search = search[3:] + search = search.replace(' ', '-') + matches = RePattern(search, abbreviations=[dash], flags=re.IGNORECASE).matches(input_string, context) + for match in matches: + ret.append(match.span) + else: + for start in find_all(input_string, search, ignore_case=True): + ret.append((start, start+len(search))) + return ret + + rebulk.functional(expected_title, name='title', tags=['expected'], + conflict_solver=lambda match, other: other, + disabled=lambda context: not context.get('expected_title')) + + return rebulk + + +class TitleBaseRule(Rule): + """ + Add title match in existing matches + """ + # pylint:disable=no-self-use,unused-argument + consequence = [AppendMatch, RemoveMatch] + + def __init__(self, match_name, match_tags=None, alternative_match_name=None): + super(TitleBaseRule, self).__init__() + self.match_name = match_name + self.match_tags = match_tags + self.alternative_match_name = alternative_match_name + + def hole_filter(self, hole, matches): + """ + Filter holes for titles. + :param hole: + :type hole: + :param matches: + :type matches: + :return: + :rtype: + """ + return True + + def filepart_filter(self, filepart, matches): + """ + Filter filepart for titles. + :param filepart: + :type filepart: + :param matches: + :type matches: + :return: + :rtype: + """ + return True + + def holes_process(self, holes, matches): + """ + process holes + :param holes: + :type holes: + :param matches: + :type matches: + :return: + :rtype: + """ + cropped_holes = [] + for hole in holes: + group_markers = matches.markers.named('group') + cropped_holes.extend(hole.crop(group_markers)) + return cropped_holes + + def is_ignored(self, match): + """ + Ignore matches when scanning for title (hole) + """ + return match.name in ['language', 'country', 'episode_details'] + + def should_keep(self, match, to_keep, matches, filepart, hole, starting): + """ + Check if this match should be accepted when ending or starting a hole. + :param match: + :type match: + :param to_keep: + :type to_keep: list[Match] + :param matches: + :type matches: Matches + :param hole: the filepart match + :type hole: Match + :param hole: the hole match + :type hole: Match + :param starting: true if match is starting the hole + :type starting: bool + :return: + :rtype: + """ + # Keep language if other languages exists in the filepart. + if match.name in ['language', 'country']: + outside_matches = filepart.crop(hole) + other_languages = [] + for outside in outside_matches: + other_languages.extend(matches.range(outside.start, outside.end, + lambda c_match: c_match.name == match.name and + c_match not in to_keep)) + + if not other_languages: + return True + + return False + + def should_remove(self, match, matches, filepart, hole, context): + """ + Check if this match should be removed after beeing ignored. + :param match: + :param matches: + :param filepart: + :param hole: + :return: + """ + if context.get('type') == 'episode' and match.name == 'episode_details': + return False + return True + + def check_titles_in_filepart(self, filepart, matches, context): + """ + Find title in filepart (ignoring language) + """ + # pylint:disable=too-many-locals,too-many-branches,too-many-statements + start, end = filepart.span + + holes = matches.holes(start, end + 1, formatter=formatters(cleanup, reorder_title), + ignore=self.is_ignored, + predicate=lambda hole: hole.value) + + holes = self.holes_process(holes, matches) + + for hole in holes: + # pylint:disable=cell-var-from-loop + if not hole or (self.hole_filter and not self.hole_filter(hole, matches)): + continue + + to_remove = [] + to_keep = [] + + ignored_matches = matches.range(hole.start, hole.end, self.is_ignored) + + if ignored_matches: + for ignored_match in reversed(ignored_matches): + # pylint:disable=undefined-loop-variable + trailing = matches.chain_before(hole.end, seps, predicate=lambda match: match == ignored_match) + if trailing: + should_keep = self.should_keep(ignored_match, to_keep, matches, filepart, hole, False) + if should_keep: + # pylint:disable=unpacking-non-sequence + try: + append, crop = should_keep + except TypeError: + append, crop = should_keep, should_keep + if append: + to_keep.append(ignored_match) + if crop: + hole.end = ignored_match.start + + for ignored_match in ignored_matches: + if ignored_match not in to_keep: + starting = matches.chain_after(hole.start, seps, + predicate=lambda match: match == ignored_match) + if starting: + should_keep = self.should_keep(ignored_match, to_keep, matches, filepart, hole, True) + if should_keep: + # pylint:disable=unpacking-non-sequence + try: + append, crop = should_keep + except TypeError: + append, crop = should_keep, should_keep + if append: + to_keep.append(ignored_match) + if crop: + hole.start = ignored_match.end + + for match in ignored_matches: + if self.should_remove(match, matches, filepart, hole, context): + to_remove.append(match) + for keep_match in to_keep: + to_remove.remove(keep_match) + + if hole and hole.value: + hole.name = self.match_name + hole.tags = self.match_tags + if self.alternative_match_name: + # Split and keep values that can be a title + titles = hole.split(title_seps, lambda match: match.value) + for title_match in list(titles[1:]): + previous_title = titles[titles.index(title_match) - 1] + separator = matches.input_string[previous_title.end:title_match.start] + if len(separator) == 1 and separator == '-' \ + and previous_title.raw[-1] not in seps \ + and title_match.raw[0] not in seps: + titles[titles.index(title_match) - 1].end = title_match.end + titles.remove(title_match) + else: + title_match.name = self.alternative_match_name + + else: + titles = [hole] + return titles, to_remove + + def when(self, matches, context): + if matches.named(self.match_name, lambda match: 'expected' in match.tags): + return + + fileparts = [filepart for filepart in list(marker_sorted(matches.markers.named('path'), matches)) + if not self.filepart_filter or self.filepart_filter(filepart, matches)] + + to_remove = [] + + # Priorize fileparts containing the year + years_fileparts = [] + for filepart in fileparts: + year_match = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year', 0) + if year_match: + years_fileparts.append(filepart) + + ret = [] + for filepart in fileparts: + try: + years_fileparts.remove(filepart) + except ValueError: + pass + titles = self.check_titles_in_filepart(filepart, matches, context) + if titles: + titles, to_remove_c = titles + ret.extend(titles) + to_remove.extend(to_remove_c) + break + + # Add title match in all fileparts containing the year. + for filepart in years_fileparts: + titles = self.check_titles_in_filepart(filepart, matches, context) + if titles: + # pylint:disable=unbalanced-tuple-unpacking + titles, to_remove_c = titles + ret.extend(titles) + to_remove.extend(to_remove_c) + + return ret, to_remove + + +class TitleFromPosition(TitleBaseRule): + """ + Add title match in existing matches + """ + dependency = [FilmTitleRule, SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, SubtitleExtensionRule] + + properties = {'title': [None]} + + def __init__(self): + super(TitleFromPosition, self).__init__('title', ['title'], 'alternativeTitle') + + +class PreferTitleWithYear(Rule): + """ + Prefer title where filepart contains year. + """ + dependency = TitleFromPosition + consequence = [RemoveMatch, AppendTags(['equivalent-ignore'])] + + properties = {'title': [None]} + + def when(self, matches, context): + with_year_in_group = [] + with_year = [] + titles = matches.named('title') + + for title_match in titles: + filepart = matches.markers.at_match(title_match, lambda marker: marker.name == 'path', 0) + if filepart: + year_match = matches.range(filepart.start, filepart.end, lambda match: match.name == 'year', 0) + if year_match: + group = matches.markers.at_match(year_match, lambda group: group.name == 'group') + if group: + with_year_in_group.append(title_match) + else: + with_year.append(title_match) + + to_tag = [] + if with_year_in_group: + title_values = set([title_match.value for title_match in with_year_in_group]) + to_tag.extend(with_year_in_group) + elif with_year: + title_values = set([title_match.value for title_match in with_year]) + to_tag.extend(with_year) + else: + title_values = set([title_match.value for title_match in titles]) + + to_remove = [] + for title_match in titles: + if title_match.value not in title_values: + to_remove.append(title_match) + return to_remove, to_tag diff --git a/lib/guessit2/rules/properties/type.py b/lib/guessit2/rules/properties/type.py new file mode 100644 index 000000000..70043e091 --- /dev/null +++ b/lib/guessit2/rules/properties/type.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +type property +""" +from __future__ import unicode_literals + +from rebulk import CustomRule, Rebulk, POST_PROCESS +from rebulk.match import Match + +from ...rules.processors import Processors + + +def _type(matches, value): + """ + Define type match with given value. + :param matches: + :param value: + :return: + """ + matches.append(Match(len(matches.input_string), len(matches.input_string), name='type', value=value)) + + +def type_(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + return Rebulk().rules(TypeProcessor) + + +class TypeProcessor(CustomRule): + """ + Post processor to find file type based on all others found matches. + """ + priority = POST_PROCESS + + dependency = Processors + + properties = {'type': ['episode', 'movie']} + + def when(self, matches, context): # pylint:disable=too-many-return-statements + option_type = context.get('type', None) + if option_type: + return option_type + + episode = matches.named('episode') + season = matches.named('season') + episode_details = matches.named('episode_details') + + if episode or season or episode_details: + return 'episode' + + film = matches.named('film') + if film: + return 'movie' + + year = matches.named('year') + date = matches.named('date') + + if date and not year: + return 'episode' + + bonus = matches.named('bonus') + if bonus and not year: + return 'episode' + + crc32 = matches.named('crc32') + anime_release_group = matches.named('release_group', lambda match: 'anime' in match.tags) + if crc32 and anime_release_group: + return 'episode' + + return 'movie' + + def then(self, matches, when_response, context): + _type(matches, when_response) diff --git a/lib/guessit2/rules/properties/video_codec.py b/lib/guessit2/rules/properties/video_codec.py new file mode 100644 index 000000000..41ad1cf5e --- /dev/null +++ b/lib/guessit2/rules/properties/video_codec.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +video_codec and video_profile property +""" +from __future__ import unicode_literals + +import regex as re + +from rebulk import Rebulk, Rule, RemoveMatch +from ..common import dash +from ..common.validators import seps_surround + + +def video_codec(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) + rebulk.defaults(name="video_codec", validator=seps_surround) + + rebulk.regex(r"Rv\d{2}", value="Real") + rebulk.regex("Mpeg2", value="Mpeg2") + rebulk.regex("DVDivX", "DivX", value="DivX") + rebulk.regex("XviD", value="XviD") + rebulk.regex("[hx]-?264(?:-?AVC)?", "MPEG-?4(?:-?AVC)", value="h264") + rebulk.regex("[hx]-?265(?:-?HEVC)?", "HEVC", value="h265") + + # http://blog.mediacoderhq.com/h264-profiles-and-levels/ + # http://fr.wikipedia.org/wiki/H.264 + rebulk.defaults(name="video_profile", validator=seps_surround) + + rebulk.regex('10.?bit', 'Hi10P', value='10bit') + rebulk.regex('8.?bit', value='8bit') + + rebulk.string('BP', value='BP', tags='video_profile.rule') + rebulk.string('XP', 'EP', value='XP', tags='video_profile.rule') + rebulk.string('MP', value='MP', tags='video_profile.rule') + rebulk.string('HP', 'HiP', value='HP', tags='video_profile.rule') + rebulk.regex('Hi422P', value='Hi422P', tags='video_profile.rule') + rebulk.regex('Hi444PP', value='Hi444PP', tags='video_profile.rule') + + rebulk.string('DXVA', value='DXVA', name='video_api') + + rebulk.rules(VideoProfileRule) + + return rebulk + + +class VideoProfileRule(Rule): + """ + Rule to validate video_profile + """ + consequence = RemoveMatch + + def when(self, matches, context): + profile_list = matches.named('video_profile', lambda match: 'video_profile.rule' in match.tags) + ret = [] + for profile in profile_list: + codec = matches.previous(profile, lambda match: match.name == 'video_codec') + if not codec: + codec = matches.next(profile, lambda match: match.name == 'video_codec') + if not codec: + ret.append(profile) + return ret diff --git a/lib/guessit2/rules/properties/website.py b/lib/guessit2/rules/properties/website.py new file mode 100644 index 000000000..8040ad73d --- /dev/null +++ b/lib/guessit2/rules/properties/website.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Website property. +""" +from __future__ import unicode_literals + +from pkg_resources import resource_stream # @UnresolvedImport +import regex as re + +from rebulk import Rebulk + + +def website(): + """ + Builder for rebulk object. + :return: Created Rebulk object + :rtype: Rebulk + """ + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE) + rebulk.defaults(name="website") + + tlds = [l.strip().decode('utf-8') + for l in resource_stream('guessit', 'tlds-alpha-by-domain.txt').readlines() + if b'--' not in l][1:] # All registered domain extension + + safe_tlds = ['com', 'org', 'net'] # For sure a website extension + safe_subdomains = ['www'] # For sure a website subdomain + safe_prefix = ['co', 'com', 'org', 'net'] # Those words before a tlds are sure + + rebulk.regex(r'(?:[^a-z0-9]|^)((?:\L<safe_subdomains>\.)+(?:[a-z-]+\.)+(?:\L<tlds>))(?:[^a-z0-9]|$)', + safe_subdomains=safe_subdomains, tlds=tlds, children=True) + rebulk.regex(r'(?:[^a-z0-9]|^)((?:\L<safe_subdomains>\.)*[a-z-]+\.(?:\L<safe_tlds>))(?:[^a-z0-9]|$)', + safe_subdomains=safe_subdomains, safe_tlds=safe_tlds, children=True) + rebulk.regex( + r'(?:[^a-z0-9]|^)((?:\L<safe_subdomains>\.)*[a-z-]+\.(?:\L<safe_prefix>\.)+(?:\L<tlds>))(?:[^a-z0-9]|$)', + safe_subdomains=safe_subdomains, safe_prefix=safe_prefix, tlds=tlds, children=True) + + return rebulk diff --git a/lib/guessit2/test/__init__.py b/lib/guessit2/test/__init__.py new file mode 100644 index 000000000..e5be370e4 --- /dev/null +++ b/lib/guessit2/test/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name diff --git a/lib/guessit2/test/episodes.yml b/lib/guessit2/test/episodes.yml new file mode 100644 index 000000000..2173a258d --- /dev/null +++ b/lib/guessit2/test/episodes.yml @@ -0,0 +1,1708 @@ +? __default__ +: type: episode + +? Series/Californication/Season 2/Californication.2x05.Vaginatown.HDTV.XviD-0TV.avi +: title: Californication + season: 2 + episode: 5 + episode_title: Vaginatown + format: HDTV + video_codec: XviD + release_group: 0TV + container: avi + +? Series/dexter/Dexter.5x02.Hello,.Bandit.ENG.-.sub.FR.HDTV.XviD-AlFleNi-TeaM.[tvu.org.ru].avi +: title: Dexter + season: 5 + episode: 2 + episode_title: Hello, Bandit + language: English + subtitle_language: French + format: HDTV + video_codec: XviD + release_group: AlFleNi-TeaM + website: tvu.org.ru + container: avi + +? Series/Treme/Treme.1x03.Right.Place,.Wrong.Time.HDTV.XviD-NoTV.avi +: title: Treme + season: 1 + episode: 3 + episode_title: Right Place, Wrong Time + format: HDTV + video_codec: XviD + release_group: NoTV + +? Series/Duckman/Duckman - S1E13 Joking The Chicken (unedited).avi +: title: Duckman + season: 1 + episode: 13 + episode_title: Joking The Chicken + +? Series/Simpsons/Saison 12 Français/Simpsons,.The.12x08.A.Bas.Le.Sergent.Skinner.FR.avi +: title: The Simpsons + season: 12 + episode: 8 + episode_title: A Bas Le Sergent Skinner + language: French + +? Series/Duckman/Duckman - 101 (01) - 20021107 - I, Duckman.avi +: title: Duckman + season: 1 + episode: 1 + episode_title: I, Duckman + date: 2002-11-07 + +? Series/Simpsons/Saison 12 Français/Simpsons,.The.12x08.A.Bas.Le.Sergent.Skinner.FR.avi +: title: The Simpsons + season: 12 + episode: 8 + episode_title: A Bas Le Sergent Skinner + language: French + +? Series/Futurama/Season 3 (mkv)/[™] Futurama - S03E22 - Le chef de fer à 30% ( 30 Percent Iron Chef ).mkv +: title: Futurama + season: 3 + episode: 22 + episode_title: Le chef de fer à 30% + +? Series/The Office/Season 6/The Office - S06xE01.avi +: title: The Office + season: 6 + episode: 1 + +? series/The Office/Season 4/The Office [401] Fun Run.avi +: title: The Office + season: 4 + episode: 1 + episode_title: Fun Run + +? Series/Mad Men Season 1 Complete/Mad.Men.S01E01.avi +: title: Mad Men + season: 1 + episode: 1 + other: Complete + +? series/Psych/Psych S02 Season 2 Complete English DVD/Psych.S02E02.65.Million.Years.Off.avi +: title: Psych + season: 2 + episode: 2 + episode_title: 65 Million Years Off + language: english + format: DVD + other: Complete + +? series/Psych/Psych S02 Season 2 Complete English DVD/Psych.S02E03.Psy.Vs.Psy.Français.srt +: title: Psych + season: 2 + episode: 3 + episode_title: Psy Vs Psy + format: DVD + language: English + subtitle_language: French + other: Complete + +? Series/Pure Laine/Pure.Laine.1x01.Toutes.Couleurs.Unies.FR.(Québec).DVB-Kceb.[tvu.org.ru].avi +: title: Pure Laine + season: 1 + episode: 1 + episode_title: Toutes Couleurs Unies + format: DVB + release_group: Kceb + language: french + website: tvu.org.ru + +? Series/Pure Laine/2x05 - Pure Laine - Je Me Souviens.avi +: title: Pure Laine + season: 2 + episode: 5 + episode_title: Je Me Souviens + +? Series/Tout sur moi/Tout sur moi - S02E02 - Ménage à trois (14-01-2008) [Rip by Ampli].avi +: title: Tout sur moi + season: 2 + episode: 2 + episode_title: Ménage à trois + date: 2008-01-14 + +? The.Mentalist.2x21.18-5-4.ENG.-.sub.FR.HDTV.XviD-AlFleNi-TeaM.[tvu.org.ru].avi +: title: The Mentalist + season: 2 + episode: 21 + episode_title: 18-5-4 + language: english + subtitle_language: french + format: HDTV + video_codec: XviD + release_group: AlFleNi-TeaM + website: tvu.org.ru + +? series/__ Incomplete __/Dr Slump (Catalan)/Dr._Slump_-_003_DVB-Rip_Catalan_by_kelf.avi +: title: Dr Slump + episode: 3 + format: DVB + language: catalan + +# Disabling this test because it just doesn't looks like a serie ... +#? series/Ren and Stimpy - Black_hole_[DivX].avi +#: title: Ren and Stimpy +# episode_title: Black hole +# video_codec: DivX + +# Disabling this test because it just doesn't looks like a serie ... +# ? Series/Walt Disney/Donald.Duck.-.Good.Scouts.[www.bigernie.jump.to].avi +#: title: Donald Duck +# episode_title: Good Scouts +# website: www.bigernie.jump.to + +? Series/Neverwhere/Neverwhere.05.Down.Street.[tvu.org.ru].avi +: title: Neverwhere + episode: 5 + episode_title: Down Street + website: tvu.org.ru + +? Series/South Park/Season 4/South.Park.4x07.Cherokee.Hair.Tampons.DVDRip.[tvu.org.ru].avi +: title: South Park + season: 4 + episode: 7 + episode_title: Cherokee Hair Tampons + format: DVD + website: tvu.org.ru + +? Series/Kaamelott/Kaamelott - Livre V - Ep 23 - Le Forfait.avi +: title: Kaamelott + alternativeTitle: Livre V + episode: 23 + episode_title: Le Forfait + +? Series/Duckman/Duckman - 110 (10) - 20021218 - Cellar Beware.avi +: title: Duckman + season: 1 + episode: 10 + date: 2002-12-18 + episode_title: Cellar Beware + +# Removing this test because it doesn't look like a series +# ? Series/Ren & Stimpy/Ren And Stimpy - Onward & Upward-Adult Party Cartoon.avi +# : title: Ren And Stimpy +# episode_title: Onward & Upward-Adult Party Cartoon + +? Series/Breaking Bad/Minisodes/Breaking.Bad.(Minisodes).01.Good.Cop.Bad.Cop.WEBRip.XviD.avi +: title: Breaking Bad + episode_format: Minisode + episode: 1 + episode_title: Good Cop Bad Cop + format: WEBRip + video_codec: XviD + +? Series/My Name Is Earl/My.Name.Is.Earl.S01Extras.-.Bad.Karma.DVDRip.XviD.avi +: title: My Name Is Earl + season: 1 + episode_title: Extras - Bad Karma + format: DVD + episode_details: Extras + video_codec: XviD + +? series/Freaks And Geeks/Season 1/Episode 4 - Kim Kelly Is My Friend-eng(1).srt +: title: Freaks And Geeks + season: 1 + episode: 4 + episode_title: Kim Kelly Is My Friend + subtitle_language: English # This is really a subtitle_language, despite guessit 1.x assert for language. + +? /mnt/series/The Big Bang Theory/S01/The.Big.Bang.Theory.S01E01.mkv +: title: The Big Bang Theory + season: 1 + episode: 1 + +? /media/Parks_and_Recreation-s03-e01.mkv +: title: Parks and Recreation + season: 3 + episode: 1 + +? /media/Parks_and_Recreation-s03-e02-Flu_Season.mkv +: title: Parks and Recreation + season: 3 + episode_title: Flu Season + episode: 2 + +? /media/Parks_and_Recreation-s03-x01.mkv +: title: Parks and Recreation + season: 3 + episode: 1 + +? /media/Parks_and_Recreation-s03-x02-Gag_Reel.mkv +: title: Parks and Recreation + season: 3 + episode: 2 + episode_title: Gag Reel + +? /media/Band_of_Brothers-e01-Currahee.mkv +: title: Band of Brothers + episode: 1 + episode_title: Currahee + +? /media/Band_of_Brothers-x02-We_Stand_Alone_Together.mkv +: title: Band of Brothers + bonus: 2 + bonus_title: We Stand Alone Together + +? /TV Shows/Mad.M-5x9.mkv +: title: Mad M + season: 5 + episode: 9 + +? /TV Shows/new.girl.117.hdtv-lol.mp4 +: title: new girl + season: 1 + episode: 17 + format: HDTV + release_group: lol + +? Kaamelott - 5x44x45x46x47x48x49x50.avi +: title: Kaamelott + season: 5 + episode: [44, 45, 46, 47, 48, 49, 50] + +? Example S01E01-02.avi +? Example S01E01E02.avi +: title: Example + season: 1 + episode: [1, 2] + +? Series/Baccano!/Baccano!_-_T1_-_Trailer_-_[Ayu](dae8173e).mkv +: title: Baccano! + other: Trailer + release_group: Ayu + episode_title: T1 + crc32: dae8173e + +? Series/Doctor Who (2005)/Season 06/Doctor Who (2005) - S06E01 - The Impossible Astronaut (1).avi +: title: Doctor Who + year: 2005 + season: 6 + episode: 1 + episode_title: The Impossible Astronaut + +? The Sopranos - [05x07] - In Camelot.mp4 +: title: The Sopranos + season: 5 + episode: 7 + episode_title: In Camelot + +? The.Office.(US).1x03.Health.Care.HDTV.XviD-LOL.avi +: title: The Office + country: US + season: 1 + episode: 3 + episode_title: Health Care + format: HDTV + video_codec: XviD + release_group: LOL + +? /Volumes/data-1/Series/Futurama/Season 3/Futurama_-_S03_DVD_Bonus_-_Deleted_Scenes_Part_3.ogm +: title: Futurama + season: 3 + part: 3 + other: Bonus + episode_title: Deleted Scenes + format: DVD + +? Ben.and.Kate.S01E02.720p.HDTV.X264-DIMENSION.mkv +: title: Ben and Kate + season: 1 + episode: 2 + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: DIMENSION + +? /volume1/TV Series/Drawn Together/Season 1/Drawn Together 1x04 Requiem for a Reality Show.avi +: title: Drawn Together + season: 1 + episode: 4 + episode_title: Requiem for a Reality Show + +? Sons.of.Anarchy.S05E06.720p.WEB.DL.DD5.1.H.264-CtrlHD.mkv +: title: Sons of Anarchy + season: 5 + episode: 6 + screen_size: 720p + format: WEB-DL + audio_channels: "5.1" + audio_codec: DolbyDigital + video_codec: h264 + release_group: CtrlHD + +? /media/bdc64bfe-e36f-4af8-b550-e6fd2dfaa507/TV_Shows/Doctor Who (2005)/Saison 6/Doctor Who (2005) - S06E13 - The Wedding of River Song.mkv +: title: Doctor Who + season: 6 + episode: 13 + year: 2005 + episode_title: The Wedding of River Song + uuid: bdc64bfe-e36f-4af8-b550-e6fd2dfaa507 + +? /mnt/videos/tvshows/Doctor Who/Season 06/E13 - The Wedding of River Song.mkv +: title: Doctor Who + season: 6 + episode: 13 + episode_title: The Wedding of River Song + +? The.Simpsons.S24E03.Adventures.in.Baby-Getting.720p.WEB-DL.DD5.1.H.264-CtrlHD.mkv +: title: The Simpsons + season: 24 + episode: 3 + episode_title: Adventures in Baby-Getting + screen_size: 720p + format: WEB-DL + audio_channels: "5.1" + audio_codec: DolbyDigital + video_codec: h264 + release_group: CtrlHD + +? /home/disaster/Videos/TV/Merlin/merlin_2008.5x02.arthurs_bane_part_two.repack.720p_hdtv_x264-fov.mkv +: title: merlin + season: 5 + episode: 2 + part: 2 + episode_title: arthurs bane + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: fov + year: 2008 + other: Proper + proper_count: 1 + +? "Da Vinci's Demons - 1x04 - The Magician.mkv" +: title: "Da Vinci's Demons" + season: 1 + episode: 4 + episode_title: The Magician + +? CSI.S013E18.Sheltered.720p.WEB-DL.DD5.1.H.264.mkv +: title: CSI + season: 13 + episode: 18 + episode_title: Sheltered + screen_size: 720p + format: WEB-DL + audio_channels: "5.1" + audio_codec: DolbyDigital + video_codec: h264 + +? Game of Thrones S03E06 1080i HDTV DD5.1 MPEG2-TrollHD.ts +: title: Game of Thrones + season: 3 + episode: 6 + screen_size: 1080i + format: HDTV + audio_channels: "5.1" + audio_codec: DolbyDigital + video_codec: Mpeg2 + release_group: TrollHD + +? gossip.girl.s01e18.hdtv.xvid-2hd.eng.srt +: title: gossip girl + season: 1 + episode: 18 + format: HDTV + video_codec: XviD + release_group: 2hd + subtitle_language: english + +? Wheels.S03E01E02.720p.HDTV.x264-IMMERSE.mkv +: title: Wheels + season: 3 + episode: [1, 2] + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: IMMERSE + +? Wheels.S03E01-02.720p.HDTV.x264-IMMERSE.mkv +: title: Wheels + season: 3 + episode: [1, 2] + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: IMMERSE + +? Wheels.S03E01-E02.720p.HDTV.x264-IMMERSE.mkv +: title: Wheels + season: 3 + episode: [1, 2] + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: IMMERSE + +? Wheels.S03E01-04.720p.HDTV.x264-IMMERSE.mkv +: title: Wheels + season: 3 + episode: [1, 2, 3, 4] + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: IMMERSE + +? Marvels.Agents.of.S.H.I.E.L.D.S01E06.720p.HDTV.X264-DIMENSION.mkv +: title: Marvels Agents of S H I E L D + season: 1 + episode: 6 + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: DIMENSION + +? Marvels.Agents.of.S.H.I.E.L.D..S01E06.720p.HDTV.X264-DIMENSION.mkv +: title: Marvels Agents of S H I E L D + season: 1 + episode: 6 + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: DIMENSION + +? Series/Friday Night Lights/Season 1/Friday Night Lights S01E19 - Ch-Ch-Ch-Ch-Changes.avi +: title: Friday Night Lights + season: 1 + episode: 19 + episode_title: Ch-Ch-Ch-Ch-Changes + +? Dexter Saison VII FRENCH.BDRip.XviD-MiND.nfo +: title: Dexter + season: 7 + video_codec: XviD + language: French + format: BluRay + release_group: MiND + +? Dexter Saison sept FRENCH.BDRip.XviD-MiND.nfo +: title: Dexter + season: 7 + video_codec: XviD + language: French + format: BluRay + release_group: MiND + +? "Pokémon S16 - E29 - 1280*720 HDTV VF.mkv" +: title: Pokémon + format: HDTV + language: French + season: 16 + episode: 29 + screen_size: 720p + +? One.Piece.E576.VOSTFR.720p.HDTV.x264-MARINE-FORD.mkv +: episode: 576 + video_codec: h264 + format: HDTV + title: One Piece + release_group: MARINE-FORD + subtitle_language: French + screen_size: 720p + +? Dexter.S08E12.FINAL.MULTi.1080p.BluRay.x264-MiND.mkv +: video_codec: h264 + episode: 12 + season: 8 + format: BluRay + title: Dexter + other: FINAL + language: Multiple languages + release_group: MiND + screen_size: 1080p + +? One Piece - E623 VOSTFR HD [www.manga-ddl-free.com].mkv +: website: www.manga-ddl-free.com + episode: 623 + subtitle_language: French + title: One Piece + other: HD + +? Falling Skies Saison 1.HDLight.720p.x264.VFF.mkv +: language: French + screen_size: 720p + season: 1 + title: Falling Skies + video_codec: h264 + other: HDLight + +? Sleepy.Hollow.S01E09.720p.WEB-DL.DD5.1.H.264-BP.mkv +: episode: 9 + video_codec: h264 + format: WEB-DL + title: Sleepy Hollow + audio_channels: "5.1" + screen_size: 720p + season: 1 + video_profile: BP + audio_codec: DolbyDigital + +? Sleepy.Hollow.S01E09.720p.WEB-DL.DD5.1.H.264-BS.mkv +: episode: 9 + video_codec: h264 + format: WEB-DL + title: Sleepy Hollow + audio_channels: "5.1" + screen_size: 720p + season: 1 + release_group: BS + audio_codec: DolbyDigital + +? Battlestar.Galactica.S00.Pilot.FRENCH.DVDRip.XviD-NOTAG.avi +: title: Battlestar Galactica + season: 0 + episode_details: Pilot + episode_title: Pilot + language: French + format: DVD + video_codec: XviD + release_group: NOTAG + +? The Big Bang Theory S00E00 Unaired Pilot VOSTFR TVRip XviD-VioCs +: title: The Big Bang Theory + season: 0 + episode: 0 + subtitle_language: French + format: TV + video_codec: XviD + release_group: VioCs + episode_details: [Unaired, Pilot] + +? The Big Bang Theory S01E00 PROPER Unaired Pilot TVRip XviD-GIGGITY +: title: The Big Bang Theory + season: 1 + episode: 0 + format: TV + video_codec: XviD + release_group: GIGGITY + other: Proper + proper_count: 1 + episode_details: [Unaired, Pilot] + +? Pawn.Stars.S2014E18.720p.HDTV.x264-KILLERS +: title: Pawn Stars + season: 2014 + year: 2014 + episode: 18 + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: KILLERS + +? 2.Broke.Girls.S03E10.480p.HDTV.x264-mSD.mkv +: title: 2 Broke Girls + season: 3 + episode: 10 + screen_size: 480p + format: HDTV + video_codec: h264 + release_group: mSD + +? House.of.Cards.2013.S02E03.1080p.NF.WEBRip.DD5.1.x264-NTb.mkv +: title: House of Cards + year: 2013 + season: 2 + episode: 3 + screen_size: 1080p + other: Netflix + format: WEBRip + audio_channels: "5.1" + audio_codec: DolbyDigital + video_codec: h264 + release_group: NTb + +? the.100.109.hdtv-lol.mp4 +: title: the 100 + season: 1 + episode: 9 + format: HDTV + release_group: lol + +? Criminal.Minds.5x03.Reckoner.ENG.-.sub.FR.HDTV.XviD-STi.[tvu.org.ru].avi +: title: Criminal Minds + language: English + subtitle_language: French + season: 5 + episode: 3 + video_codec: XviD + format: HDTV + website: tvu.org.ru + release_group: STi + episode_title: Reckoner + +? 03-Criminal.Minds.avi +: title: Criminal Minds + episode: 3 + +? '[Evil-Saizen]_Laughing_Salesman_14_[DVD][1C98686A].mkv' +: crc32: 1C98686A + episode: 14 + format: DVD + release_group: Evil-Saizen + title: Laughing Salesman + +? '[Kaylith] Zankyou no Terror - 04 [480p][B4D4514E].mp4' +: crc32: B4D4514E + episode: 4 + release_group: Kaylith + screen_size: 480p + title: Zankyou no Terror + +? '[PuyaSubs!] Seirei Tsukai no Blade Dance - 05 [720p][32DD560E].mkv' +: crc32: 32DD560E + episode: 5 + release_group: PuyaSubs! + screen_size: 720p + title: Seirei Tsukai no Blade Dance + +? '[Doremi].Happiness.Charge.Precure.27.[1280x720].[DC91581A].mkv' +: crc32: DC91581A + episode: 27 + release_group: Doremi + screen_size: 720p + title: Happiness Charge Precure + +? "[Daisei] Free!:Iwatobi Swim Club - 01 ~ (BD 720p 10-bit AAC) [99E8E009].mkv" +: audio_codec: AAC + crc32: 99E8E009 + episode: 1 + format: BluRay + release_group: Daisei + screen_size: 720p + title: Free!:Iwatobi Swim Club + video_profile: 10bit + +? '[Tsundere] Boku wa Tomodachi ga Sukunai - 03 [BDRip h264 1920x1080 10bit FLAC][AF0C22CC].mkv' +: audio_codec: FLAC + crc32: AF0C22CC + episode: 3 + format: BluRay + release_group: Tsundere + screen_size: 1080p + title: Boku wa Tomodachi ga Sukunai + video_codec: h264 + video_profile: 10bit + +? '[t.3.3.d]_Mikakunin_de_Shinkoukei_-_12_[720p][5DDC1352].mkv' +: crc32: 5DDC1352 + episode: 12 + screen_size: 720p + title: Mikakunin de Shinkoukei + release_group: t.3.3.d + +? '[Anime-Koi] Sabagebu! - 06 [h264-720p][ABB3728A].mkv' +: crc32: ABB3728A + episode: 6 + release_group: Anime-Koi + screen_size: 720p + title: Sabagebu! + video_codec: h264 + +? '[aprm-Diogo4D] [BD][1080p] Nagi no Asukara 08 [4D102B7C].mkv' +: crc32: 4D102B7C + episode: 8 + format: BluRay + release_group: aprm-Diogo4D + screen_size: 1080p + title: Nagi no Asukara + +? '[Akindo-SSK] Zankyou no Terror - 05 [720P][Sub_ITA][F5CCE87C].mkv' +: crc32: F5CCE87C + episode: 5 + release_group: Akindo-SSK + screen_size: 720p + title: Zankyou no Terror + subtitle_language: it + +? Naruto Shippuden Episode 366 VOSTFR.avi +: episode: 366 + title: Naruto Shippuden + subtitle_language: fr + +? Naruto Shippuden Episode 366v2 VOSTFR.avi +: episode: 366 + version: 2 + title: Naruto Shippuden + subtitle_language: fr + +? '[HorribleSubs] Ao Haru Ride - 06 [480p].mkv' +: episode: 6 + release_group: HorribleSubs + screen_size: 480p + title: Ao Haru Ride + +? '[DeadFish] Tari Tari - 01 [BD][720p][AAC].mp4' +: audio_codec: AAC + episode: 1 + format: BluRay + release_group: DeadFish + screen_size: 720p + title: Tari Tari + +? '[NoobSubs] Sword Art Online II 06 (720p 8bit AAC).mp4' +: audio_codec: AAC + episode: 6 + release_group: NoobSubs + screen_size: 720p + title: Sword Art Online II + video_profile: 8bit + +? '[DeadFish] 01 - Tari Tari [BD][720p][AAC].mp4' +: audio_codec: AAC + episode: 1 + format: BluRay + release_group: DeadFish + screen_size: 720p + title: Tari Tari + +? '[NoobSubs] 06 Sword Art Online II (720p 8bit AAC).mp4' +: audio_codec: AAC + episode: 6 + release_group: NoobSubs + screen_size: 720p + title: Sword Art Online II + video_profile: 8bit + +? '[DeadFish] 12 - Tari Tari [BD][720p][AAC].mp4' +: audio_codec: AAC + episode: 12 + format: BluRay + release_group: DeadFish + screen_size: 720p + title: Tari Tari + +? Something.Season.2.1of4.Ep.Title.HDTV.torrent +: episode_count: 4 + episode: 1 + format: HDTV + season: 2 + title: Something + episode_title: Title + container: torrent + +? Something.Season.2of5.3of9.Ep.Title.HDTV.torrent +: episode_count: 9 + episode: 3 + format: HDTV + season: 2 + season_count: 5 + title: Something + episode_title: Title + container: torrent + +? Something.Other.Season.3of5.Complete.HDTV.torrent +: format: HDTV + other: Complete + season: 3 + season_count: 5 + title: Something Other + container: torrent + +? Something.Other.Season.1-3.avi +: season: [1, 2, 3] + title: Something Other + +? Something.Other.Season.1&3.avi +: season: [1, 3] + title: Something Other + +? Something.Other.Season.1&3-1to12ep.avi +: season: [1, 3] + title: Something Other + +? W2Test.123.HDTV.XViD-FlexGet +: episode: 23 + season: 1 + format: HDTV + release_group: FlexGet + title: W2Test + video_codec: XviD + +? W2Test.123.HDTV.XViD-FlexGet +: options: --episode-prefer-number + episode: 123 + format: HDTV + release_group: FlexGet + title: W2Test + video_codec: XviD + +? FooBar.0307.PDTV-FlexGet +: episode: 7 + format: DVB + release_group: FlexGet + season: 3 + title: FooBar + +? FooBar.0307.PDTV-FlexGet +? FooBar.307.PDTV-FlexGet +: options: --episode-prefer-number + episode: 307 + format: DVB + release_group: FlexGet + title: FooBar + +? FooBar.07.PDTV-FlexGet +: options: --episode-prefer-number + episode: 7 + format: DVB + release_group: FlexGet + title: FooBar + +? FooBar.7.PDTV-FlexGet +: options: --episode-prefer-number + episode: 7 + format: DVB + release_group: FlexGet + title: FooBar + +? FooBar.0307.PDTV-FlexGet +: episode: 7 + format: DVB + release_group: FlexGet + season: 3 + title: FooBar + +? FooBar.307.PDTV-FlexGet +: episode: 7 + format: DVB + release_group: FlexGet + season: 3 + title: FooBar + +? FooBar.07.PDTV-FlexGet +: episode: 7 + format: DVB + release_group: FlexGet + title: FooBar + +? FooBar.07v4.PDTV-FlexGet +: episode: 7 + version: 4 + format: DVB + release_group: FlexGet + title: FooBar + +? FooBar.7.PDTV-FlexGet +: format: DVB + release_group: FlexGet + title: FooBar 7 + type: movie + +? FooBar.7.PDTV-FlexGet +: options: -t episode + episode: 7 + format: DVB + release_group: FlexGet + title: FooBar + +? FooBar.7v3.PDTV-FlexGet +: options: -t episode + episode: 7 + version: 3 + format: DVB + release_group: FlexGet + title: FooBar + +? Test.S02E01.hdtv.real.proper +: episode: 1 + format: HDTV + other: Proper + proper_count: 2 + season: 2 + title: Test + +? Real.Test.S02E01.hdtv.proper +: episode: 1 + format: HDTV + other: Proper + proper_count: 1 + season: 2 + title: Real Test + +? Test.Real.S02E01.hdtv.proper +: episode: 1 + format: HDTV + other: Proper + proper_count: 1 + season: 2 + title: Test Real + +? Test.S02E01.hdtv.proper +: episode: 1 + format: HDTV + other: Proper + proper_count: 1 + season: 2 + title: Test + +? Test.S02E01.hdtv.real.repack.proper +: episode: 1 + format: HDTV + other: Proper + proper_count: 3 + season: 2 + title: Test + +? Date.Show.03-29-2012.HDTV.XViD-FlexGet +: date: 2012-03-29 + format: HDTV + release_group: FlexGet + title: Date Show + video_codec: XviD + +? Something.1x5.Season.Complete-FlexGet +: episode: 5 + other: Complete + season: 1 + title: Something + release_group: FlexGet + +? Something Seasons 1 & 2 - Complete +: other: Complete + season: + - 1 + - 2 + title: Something + +? Something Seasons 4 Complete +: other: Complete + season: 4 + title: Something + +? Something.1xAll.Season.Complete-FlexGet +: other: Complete + season: 1 + title: Something + release_group: FlexGet + +? Something.1xAll-FlexGet +: other: Complete + season: 1 + title: Something + episode_title: FlexGet # 1.x guess this as release_group, but it's better to guess it as episode_title + +? FlexGet.US.S2013E14.Title.Here.720p.HDTV.AAC5.1.x264-NOGRP +: audio_channels: '5.1' + audio_codec: AAC + country: US + episode: 14 + format: HDTV + release_group: NOGRP + screen_size: 720p + season: 2013 + title: FlexGet + episode_title: Title Here + video_codec: h264 + year: 2013 + +? FlexGet.14.of.21.Title.Here.720p.HDTV.AAC5.1.x264-NOGRP +: audio_channels: '5.1' + audio_codec: AAC + episode_count: 21 + episode: 14 + format: HDTV + release_group: NOGRP + screen_size: 720p + title: FlexGet + episode_title: Title Here + video_codec: h264 + +? FlexGet.Series.2013.14.of.21.Title.Here.720p.HDTV.AAC5.1.x264-NOGRP +: audio_channels: '5.1' + audio_codec: AAC + episode_count: 21 + episode: 14 + format: HDTV + release_group: NOGRP + screen_size: 720p + season: 2013 + title: FlexGet + episode_title: Title Here + video_codec: h264 + year: 2013 + +? Something.S04E05E09 +: episode: # 1.x guessit this as a range from 5 to 9. But not sure if it should ... + - 5 + - 9 + season: 4 + title: Something + +? FooBar 360 1080i +: options: --episode-prefer-number + episode: 360 + screen_size: 1080i + title: FooBar + +? FooBar 360 1080i +: episode: 60 + season: 3 + screen_size: 1080i + title: FooBar + +? FooBar 360 +: screen_size: 360p + title: FooBar + +? BarFood christmas special HDTV +: options: --expected-title BarFood + format: HDTV + title: BarFood + episode_title: christmas special + episode_details: Special + +? Something.2008x12.13-FlexGet +: title: Something + date: 2008-12-13 + episode_title: FlexGet + +? '[Ignored] Test 12' +: episode: 12 + release_group: Ignored + title: Test + +? '[FlexGet] Test 12' +: episode: 12 + release_group: FlexGet + title: Test + +? Test.13.HDTV-Ignored +: episode: 13 + format: HDTV + release_group: Ignored + title: Test + +? Test.13.HDTV-Ignored +: options: --expected-series test + episode: 13 + format: HDTV + release_group: Ignored + title: Test + +? Test.13.HDTV-Ignored +: title: Test + episode: 13 + format: HDTV + release_group: Ignored + +? Test.13.HDTV-Ignored +: episode: 13 + format: HDTV + release_group: Ignored + title: Test + +? Test.13.HDTV-FlexGet +: episode: 13 + format: HDTV + release_group: FlexGet + title: Test + +? Test.14.HDTV-Name +: episode: 14 + format: HDTV + release_group: Name + title: Test + +? Real.Time.With.Bill.Maher.2014.10.31.HDTV.XviD-AFG.avi +: date: 2014-10-31 + format: HDTV + release_group: AFG + title: Real Time With Bill Maher + video_codec: XviD + +? Arrow.S03E21.Al.Sah-Him.1080p.WEB-DL.DD5.1.H.264-BS.mkv +: title: Arrow + season: 3 + episode: 21 + episode_title: Al Sah-Him + screen_size: 1080p + audio_codec: DolbyDigital + audio_channels: "5.1" + video_codec: h264 + release_group: BS + format: WEB-DL + +? How to Make It in America - S02E06 - I'm Sorry, Who's Yosi?.mkv +: title: How to Make It in America + season: 2 + episode: 6 + episode_title: I'm Sorry, Who's Yosi? + +? 24.S05E07.FRENCH.DVDRip.XviD-FiXi0N.avi +: episode: 7 + format: DVD + language: fr + season: 5 + title: '24' + video_codec: XviD + release_group: FiXi0N + +? 12.Monkeys.S01E12.FRENCH.BDRip.x264-VENUE.mkv +: episode: 12 + format: BluRay + language: fr + release_group: VENUE + season: 1 + title: 12 Monkeys + video_codec: h264 + +? The.Daily.Show.2015.07.01.Kirsten.Gillibrand.Extended.720p.CC.WEBRip.AAC2.0.x264-BTW.mkv +: audio_channels: '2.0' + audio_codec: AAC + date: 2015-07-01 + format: WEBRip + other: CC + release_group: BTW + screen_size: 720p + title: The Daily Show + episode_title: Kirsten Gillibrand Extended + video_codec: h264 + +? The.Daily.Show.2015.07.02.Sarah.Vowell.CC.WEBRip.AAC2.0.x264-BTW.mkv +: audio_channels: '2.0' + audio_codec: AAC + date: 2015-07-02 + format: WEBRip + other: CC + release_group: BTW + title: The Daily Show + episode_title: Sarah Vowell + video_codec: h264 + +? 90.Day.Fiance.S02E07.I.Have.To.Tell.You.Something.720p.HDTV.x264-W4F +: episode: 7 + format: HDTV + screen_size: 720p + season: 2 + title: 90 Day Fiance + episode_title: I Have To Tell You Something + release_group: W4F + +? Doctor.Who.2005.S04E06.FRENCH.LD.DVDRip.XviD-TRACKS.avi +: episode: 6 + format: DVD + language: fr + release_group: TRACKS + season: 4 + title: Doctor Who + other: LD + video_codec: XviD + year: 2005 + +? Astro.Le.Petit.Robot.S01E01+02.FRENCH.DVDRiP.X264.INT-BOOLZ.mkv +: episode: [1, 2] + format: DVD + language: fr + release_group: INT-BOOLZ + season: 1 + title: Astro Le Petit Robot + video_codec: h264 + +? Annika.Bengtzon.2012.E01.Le.Testament.De.Nobel.FRENCH.DVDRiP.XViD-STVFRV.avi +: episode: 1 + format: DVD + language: fr + release_group: STVFRV + title: Annika Bengtzon + episode_title: Le Testament De Nobel + video_codec: XviD + year: 2012 + +? Dead.Set.02.FRENCH.LD.DVDRip.XviD-EPZ.avi +: episode: 2 + format: DVD + language: fr + other: LD + release_group: EPZ + title: Dead Set + video_codec: XviD + +? Phineas and Ferb S01E00 & S01E01 & S01E02 +: episode: [0, 1, 2] + season: 1 + title: Phineas and Ferb + +? Show.Name.S01E02.S01E03.HDTV.XViD.Etc-Group +: episode: [2, 3] + format: HDTV + release_group: Etc-Group + season: 1 + title: Show Name + video_codec: XviD + +? Show Name - S01E02 - S01E03 - S01E04 - Ep Name +: episode: [2, 3, 4] + season: 1 + title: Show Name + episode_title: Ep Name + +? Show.Name.1x02.1x03.HDTV.XViD.Etc-Group +: episode: [2, 3] + format: HDTV + release_group: Etc-Group + season: 1 + title: Show Name + video_codec: XviD + +? Show Name - 1x02 - 1x03 - 1x04 - Ep Name +: episode: [2, 3, 4] + season: 1 + title: Show Name + episode_title: Ep Name + +? Show.Name.S01E02.HDTV.XViD.Etc-Group +: episode: 2 + format: HDTV + release_group: Etc-Group + season: 1 + title: Show Name + video_codec: XviD + +? Show Name - S01E02 - My Ep Name +: episode: 2 + season: 1 + title: Show Name + episode_title: My Ep Name + +? Show Name - S01.E03 - My Ep Name +: episode: 3 + season: 1 + title: Show Name + episode_title: My Ep Name + +? Show.Name.S01E02E03.HDTV.XViD.Etc-Group +: episode: [2, 3] + format: HDTV + release_group: Etc-Group + season: 1 + title: Show Name + video_codec: XviD + +? Show Name - S01E02-03 - My Ep Name +: episode: [2, 3] + season: 1 + title: Show Name + episode_title: My Ep Name + +? Show.Name.S01.E02.E03 +: episode: [2, 3] + season: 1 + title: Show Name + +? Show_Name.1x02.HDTV_XViD_Etc-Group +: episode: 2 + format: HDTV + release_group: Etc-Group + season: 1 + title: Show Name + video_codec: XviD + +? Show Name - 1x02 - My Ep Name +: episode: 2 + season: 1 + title: Show Name + episode_title: My Ep Name + +? Show_Name.1x02x03x04.HDTV_XViD_Etc-Group +: episode: [2, 3, 4] + format: HDTV + release_group: Etc-Group + season: 1 + title: Show Name + video_codec: XviD + +? Show Name - 1x02-03-04 - My Ep Name +: episode: [2, 3, 4] + season: 1 + title: Show Name + episode_title: My Ep Name + +# 1x guess this as episode 100 but 101 as episode 1 season 1. +? Show.Name.100.Event.2010.11.23.HDTV.XViD.Etc-Group +: date: 2010-11-23 + season: 1 + episode: 0 + format: HDTV + release_group: Etc-Group + title: Show Name + episode_title: Event + video_codec: XviD + +? Show.Name.101.Event.2010.11.23.HDTV.XViD.Etc-Group +: date: 2010-11-23 + season: 1 + episode: 1 + format: HDTV + release_group: Etc-Group + title: Show Name + episode_title: Event + video_codec: XviD + +? Show.Name.2010.11.23.HDTV.XViD.Etc-Group +: date: 2010-11-23 + format: HDTV + release_group: Etc-Group + title: Show Name + +? Show Name - 2010-11-23 - Ep Name +: date: 2010-11-23 + title: Show Name + episode_title: Ep Name + +? Show Name Season 1 Episode 2 Ep Name +: episode: 2 + season: 1 + title: Show Name + episode_title: Ep Name + +? Show.Name.S01.HDTV.XViD.Etc-Group +: format: HDTV + release_group: Etc-Group + season: 1 + title: Show Name + video_codec: XviD + +? Show.Name.E02-03 +: episode: [2, 3] + title: Show Name + +? Show.Name.E02.2010 +: episode: 2 + year: 2010 + title: Show Name + +? Show.Name.E23.Test +: episode: 23 + title: Show Name + episode_title: Test + +? Show.Name.Part.3.HDTV.XViD.Etc-Group +: part: 3 + title: Show Name + format: HDTV + video_codec: XviD + release_group: Etc-Group + type: movie + # Fallback to movie type because we can't tell it's a series ... + +? Show.Name.Part.1.and.Part.2.Blah-Group +: part: [1, 2] + title: Show Name + type: movie + # Fallback to movie type because we can't tell it's a series ... + +? Show Name - 01 - Ep Name +: episode: 1 + title: Show Name + episode_title: Ep Name + +? 01 - Ep Name +: episode: 1 + title: Ep Name + +? Show.Name.102.HDTV.XViD.Etc-Group +: episode: 2 + format: HDTV + release_group: Etc-Group + season: 1 + title: Show Name + video_codec: XviD + +? '[HorribleSubs] Maria the Virgin Witch - 01 [720p].mkv' +: episode: 1 + release_group: HorribleSubs + screen_size: 720p + title: Maria the Virgin Witch + +? '[ISLAND]One_Piece_679_[VOSTFR]_[V1]_[8bit]_[720p]_[EB7838FC].mp4' +: options: -E + crc32: EB7838FC + episode: 679 + release_group: ISLAND + screen_size: 720p + title: One Piece + subtitle_language: fr + video_profile: 8bit + version: 1 + +? '[ISLAND]One_Piece_679_[VOSTFR]_[8bit]_[720p]_[EB7838FC].mp4' +: options: -E + crc32: EB7838FC + episode: 679 + release_group: ISLAND + screen_size: 720p + title: One Piece + subtitle_language: fr + video_profile: 8bit + +? '[Kaerizaki-Fansub]_One_Piece_679_[VOSTFR][HD_1280x720].mp4' +: options: -E + episode: 679 + other: HD + release_group: Kaerizaki-Fansub + screen_size: 720p + title: One Piece + subtitle_language: fr + +? '[Kaerizaki-Fansub]_One_Piece_679_[VOSTFR][FANSUB][HD_1280x720].mp4' +: options: -E + episode: 679 + other: + - Fansub + - HD + release_group: Kaerizaki-Fansub + screen_size: 720p + title: One Piece + subtitle_language: fr + +? '[Kaerizaki-Fansub]_One_Piece_681_[VOSTFR][HD_1280x720]_V2.mp4' +: options: -E + episode: 681 + other: HD + release_group: Kaerizaki-Fansub + screen_size: 720p + title: One Piece + subtitle_language: fr + version: 2 + +? '[Kaerizaki-Fansub] High School DxD New 04 VOSTFR HD (1280x720) V2.mp4' +: options: -E + episode: 4 + other: HD + release_group: Kaerizaki-Fansub + screen_size: 720p + title: High School DxD New + subtitle_language: fr + version: 2 + +? '[Kaerizaki-Fansub] One Piece 603 VOSTFR PS VITA (960x544) V2.mp4' +: options: -E + episode: 603 + release_group: Kaerizaki-Fansub + screen_size: 960x544 + title: One Piece + subtitle_language: fr + version: 2 + +? '[Group Name] Show Name.13' +: episode: 13 + release_group: Group Name + title: Show Name + +? '[Group Name] Show Name - 13' +: episode: 13 + release_group: Group Name + title: Show Name + +? '[Group Name] Show Name 13' +: episode: 13 + release_group: Group Name + title: Show Name + +# [Group Name] Show Name.13-14 +# [Group Name] Show Name - 13-14 +# Show Name 13-14 + +? '[Stratos-Subs]_Infinite_Stratos_-_12_(1280x720_H.264_AAC)_[379759DB]' +: audio_codec: AAC + crc32: 379759DB + episode: 12 + release_group: Stratos-Subs + screen_size: 720p + title: Infinite Stratos + video_codec: h264 + +# [ShinBunBu-Subs] Bleach - 02-03 (CX 1280x720 x264 AAC) + +? '[SGKK] Bleach 312v1 [720p/MKV]' +: options: -E # guessit 1.x for episode only when version is guessed, but it's doesn't make it consistent. + episode: 312 + release_group: SGKK + screen_size: 720p + title: Bleach + version: 1 + +? '[Ayako]_Infinite_Stratos_-_IS_-_07_[H264][720p][EB7838FC]' +: crc32: EB7838FC + episode: 7 + release_group: Ayako + screen_size: 720p + title: Infinite Stratos + video_codec: h264 + +? '[Ayako] Infinite Stratos - IS - 07v2 [H264][720p][44419534]' +: crc32: '44419534' + episode: 7 + release_group: Ayako + screen_size: 720p + title: Infinite Stratos + video_codec: h264 + version: 2 + +? '[Ayako-Shikkaku] Oniichan no Koto Nanka Zenzen Suki Janain Dakara ne - 10 [LQ][h264][720p] [8853B21C]' +: crc32: 8853B21C + episode: 10 + release_group: Ayako-Shikkaku + screen_size: 720p + title: Oniichan no Koto Nanka Zenzen Suki Janain Dakara ne + video_codec: h264 + +# TODO: Add support for absolute episodes +? Bleach - s16e03-04 - 313-314 +? Bleach.s16e03-04.313-314 +? Bleach.s16e03-04.313-314 +? Bleach - s16e03-04 - 313-314 +? Bleach.s16e03-04.313-314 +? Bleach s16e03e04 313-314 +: episode: [3, 4] + season: 16 + title: Bleach + +? Bleach - 313-314 +: options: -E + episode: [313, 314] + title: Bleach + +? '[ShinBunBu-Subs] Bleach - 02-03 (CX 1280x720 x264 AAC)' +: audio_codec: AAC + episode: [2, 3] + release_group: ShinBunBu-Subs + screen_size: 720p + title: Bleach + video_codec: h264 + +? 003. Show Name - Ep Name.avi +: episode: 3 + title: Show Name + episode_title: Ep Name + +? 003-004. Show Name - Ep Name.avi +: episode: [3, 4] + title: Show Name + episode_title: Ep Name + +? One Piece - 102 +: episode: 2 + season: 1 + title: One Piece + +? "[ACX]_Wolf's_Spirit_001.mkv" +: episode: 1 + release_group: ACX + title: "Wolf's Spirit" + +? Project.Runway.S14E00.and.S14E01.(Eng.Subs).SDTV.x264-[2Maverick].mp4 +: episode: [0, 1] + format: TV + release_group: 2Maverick + season: 14 + title: Project Runway + subtitle_language: en + video_codec: h264 + +? '[Hatsuyuki-Kaitou]_Fairy_Tail_2_-_16-20_[720p][10bit].torrent' +: episode: [16, 17, 18, 19, 20] + release_group: Hatsuyuki-Kaitou + screen_size: 720p + title: Fairy Tail 2 + video_profile: 10bit + +? '[Hatsuyuki-Kaitou]_Fairy_Tail_2_-_16-20_(191-195)_[720p][10bit].torrent' +: options: -E + episode: [16, 17, 18, 19, 20, 191, 192, 193, 194, 195] + release_group: Hatsuyuki-Kaitou + screen_size: 720p + title: Fairy Tail 2 + +? "Looney Tunes 1940x01 Porky's Last Stand.mkv" +: episode: 1 + season: 1940 + title: Looney Tunes + episode_title: Porky's Last Stand + year: 1940 + +? The.Good.Wife.S06E01.E10.720p.WEB-DL.DD5.1.H.264-CtrlHD/The.Good.Wife.S06E09.Trust.Issues.720p.WEB-DL.DD5.1.H.264-CtrlHD.mkv +: audio_channels: '5.1' + audio_codec: DolbyDigital + episode: 9 + format: WEB-DL + release_group: CtrlHD + screen_size: 720p + season: 6 + title: The Good Wife + episode_title: Trust Issues + video_codec: h264 + +? Fear the Walking Dead - 01x02 - So Close, Yet So Far.REPACK-KILLERS.French.C.updated.Addic7ed.com.mkv +: episode: 2 + language: fr + other: Proper + proper_count: 1 + season: 1 + title: Fear the Walking Dead + episode_title: So Close, Yet So Far + +? Fear the Walking Dead - 01x02 - En Close, Yet En Far.REPACK-KILLERS.French.C.updated.Addic7ed.com.mkv +: episode: 2 + language: fr + other: Proper + proper_count: 1 + season: 1 + title: Fear the Walking Dead + episode_title: En Close, Yet En Far + +? /av/unsorted/The.Daily.Show.2015.07.22.Jake.Gyllenhaal.720p.HDTV.x264-BATV.mkv +: date: 2015-07-22 + format: HDTV + release_group: BATV + screen_size: 720p + title: The Daily Show + episode_title: Jake Gyllenhaal + video_codec: h264 + +? "[7.1.7.8.5] Foo Bar - 11 (H.264) [5235532D].mkv" +: options: -E + episode: 11 + +? my 720p show S01E02 +: options: -T "my 720p show" + title: my 720p show + season: 1 + episode: 2 + +? my 720p show S01E02 720p +: options: -T "my 720p show" + title: my 720p show + season: 1 + episode: 2 + screen_size: 720p + +? -my 720p show S01E02 +: options: -T "re:my \d+p show" + screen_size: 720p + +? Show S01E02 +: options: -T "The Show" + title: Show + season: 1 + episode: 2 + +? Foo's & Bars (2009) S01E01 720p XviD-2HD[AOEU] +: episode: 1 + release_group: 2HD[AOEU] + screen_size: 720p + season: 1 + title: Foo's & Bars + type: episode + video_codec: XviD + year: 2009 + +? Date.Series.10-11-2008.XViD +: date: 2008-11-10 + title: Date + type: episode + video_codec: XviD diff --git a/lib/guessit2/test/movies.yml b/lib/guessit2/test/movies.yml new file mode 100644 index 000000000..5167622c8 --- /dev/null +++ b/lib/guessit2/test/movies.yml @@ -0,0 +1,788 @@ +? __default__ +: type: movie + +? Movies/Fear and Loathing in Las Vegas (1998)/Fear.and.Loathing.in.Las.Vegas.720p.HDDVD.DTS.x264-ESiR.mkv +: title: Fear and Loathing in Las Vegas + year: 1998 + screen_size: 720p + format: HD-DVD + audio_codec: DTS + video_codec: h264 + container: mkv + release_group: ESiR + +? Movies/El Dia de la Bestia (1995)/El.dia.de.la.bestia.DVDrip.Spanish.DivX.by.Artik[SEDG].avi +: title: El Dia de la Bestia + year: 1995 + format: DVD + language: spanish + video_codec: DivX + release_group: Artik[SEDG] + container: avi + +? Movies/Dark City (1998)/Dark.City.(1998).DC.BDRip.720p.DTS.X264-CHD.mkv +: title: Dark City + year: 1998 + format: BluRay + screen_size: 720p + audio_codec: DTS + video_codec: h264 + release_group: CHD + +? Movies/Sin City (BluRay) (2005)/Sin.City.2005.BDRip.720p.x264.AC3-SEPTiC.mkv +: title: Sin City + year: 2005 + format: BluRay + screen_size: 720p + video_codec: h264 + audio_codec: AC3 + release_group: SEPTiC + +? Movies/Borat (2006)/Borat.(2006).R5.PROPER.REPACK.DVDRip.XviD-PUKKA.avi +: title: Borat + year: 2006 + proper_count: 2 + format: DVD + other: [ R5, Proper ] + video_codec: XviD + release_group: PUKKA + +? "[XCT].Le.Prestige.(The.Prestige).DVDRip.[x264.HP.He-Aac.{Fr-Eng}.St{Fr-Eng}.Chaps].mkv" +: title: Le Prestige + format: DVD + video_codec: h264 + video_profile: HP + audio_codec: AAC + audio_profile: HE + language: [ french, english ] + subtitle_language: [ french, english ] + release_group: XCT + +? Battle Royale (2000)/Battle.Royale.(Batoru.Rowaiaru).(2000).(Special.Edition).CD1of2.DVDRiP.XviD-[ZeaL].avi +: title: Battle Royale + year: 2000 + edition: Special Edition + cd: 1 + cd_count: 2 + format: DVD + video_codec: XviD + release_group: ZeaL + +? Movies/Brazil (1985)/Brazil_Criterion_Edition_(1985).CD2.avi +: title: Brazil + edition: Criterion Edition + year: 1985 + cd: 2 + +? Movies/Persepolis (2007)/[XCT] Persepolis [H264+Aac-128(Fr-Eng)+ST(Fr-Eng)+Ind].mkv +: title: Persepolis + year: 2007 + video_codec: h264 + audio_codec: AAC + language: [ French, English ] + subtitle_language: [ French, English ] + release_group: XCT + +? Movies/Toy Story (1995)/Toy Story [HDTV 720p English-Spanish].mkv +: title: Toy Story + year: 1995 + format: HDTV + screen_size: 720p + language: [ english, spanish ] + +? Movies/Office Space (1999)/Office.Space.[Dual-DVDRip].[Spanish-English].[XviD-AC3-AC3].[by.Oswald].avi +: title: Office Space + year: 1999 + format: DVD + language: [ english, spanish ] + video_codec: XviD + audio_codec: AC3 + +? Movies/Wild Zero (2000)/Wild.Zero.DVDivX-EPiC.avi +: title: Wild Zero + year: 2000 + video_codec: DivX + release_group: EPiC + +? movies/Baraka_Edition_Collector.avi +: title: Baraka + edition: Collector Edition + +? Movies/Blade Runner (1982)/Blade.Runner.(1982).(Director's.Cut).CD1.DVDRip.XviD.AC3-WAF.avi +: title: Blade Runner + year: 1982 + edition: Director's cut + cd: 1 + format: DVD + video_codec: XviD + audio_codec: AC3 + release_group: WAF + +? movies/American.The.Bill.Hicks.Story.2009.DVDRip.XviD-EPiSODE.[UsaBit.com]/UsaBit.com_esd-americanbh.avi +: title: American The Bill Hicks Story + year: 2009 + format: DVD + video_codec: XviD + release_group: EPiSODE + website: UsaBit.com + +? movies/Charlie.And.Boots.DVDRip.XviD-TheWretched/wthd-cab.avi +: title: Charlie And Boots + format: DVD + video_codec: XviD + release_group: TheWretched + +? movies/Steig Larsson Millenium Trilogy (2009) BRrip 720 AAC x264/(1)The Girl With The Dragon Tattoo (2009) BRrip 720 AAC x264.mkv +: title: The Girl With The Dragon Tattoo + #film_title: Steig Larsson Millenium Trilogy + #film: 1 + year: 2009 + format: BluRay + audio_codec: AAC + video_codec: h264 + screen_size: 720p + +? movies/Greenberg.REPACK.LiMiTED.DVDRip.XviD-ARROW/arw-repack-greenberg.dvdrip.xvid.avi +: title: Greenberg + format: DVD + video_codec: XviD + release_group: ARROW + other: ['Proper', 'Limited'] + proper_count: 1 + +? Movies/Fr - Paris 2054, Renaissance (2005) - De Christian Volckman - (Film Divx Science Fiction Fantastique Thriller Policier N&B).avi +: title: Paris 2054, Renaissance + year: 2005 + language: french + video_codec: DivX + +? Movies/[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi +: title: Avida + year: 2006 + language: french + format: DVD + video_codec: XviD + release_group: PROD + +? Movies/Alice in Wonderland DVDRip.XviD-DiAMOND/dmd-aw.avi +: title: Alice in Wonderland + format: DVD + video_codec: XviD + release_group: DiAMOND + +? Movies/Ne.Le.Dis.A.Personne.Fr 2 cd/personnea_mp.avi +: title: Ne Le Dis A Personne + language: french + cd_count: 2 + +? Movies/Bunker Palace Hôtel (Enki Bilal) (1989)/Enki Bilal - Bunker Palace Hotel (Fr Vhs Rip).avi +: title: Bunker Palace Hôtel + year: 1989 + language: french + format: VHS + +? Movies/21 (2008)/21.(2008).DVDRip.x264.AC3-FtS.[sharethefiles.com].mkv +: title: "21" + year: 2008 + format: DVD + video_codec: h264 + audio_codec: AC3 + release_group: FtS + website: sharethefiles.com + +? Movies/9 (2009)/9.2009.Blu-ray.DTS.720p.x264.HDBRiSe.[sharethefiles.com].mkv +: title: "9" + year: 2009 + format: BluRay + audio_codec: DTS + screen_size: 720p + video_codec: h264 + release_group: HDBRiSe + website: sharethefiles.com + +? Movies/Mamma.Mia.2008.DVDRip.AC3.XviD-CrazyTeam/Mamma.Mia.2008.DVDRip.AC3.XviD-CrazyTeam.avi +: title: Mamma Mia + year: 2008 + format: DVD + audio_codec: AC3 + video_codec: XviD + release_group: CrazyTeam + +? Movies/M.A.S.H. (1970)/MASH.(1970).[Divx.5.02][Dual-Subtitulos][DVDRip].ogm +: title: MASH + year: 1970 + video_codec: DivX + format: DVD + +? Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv +: title: The Doors + year: 1991 + date: 2008-03-09 + format: BluRay + screen_size: 720p + audio_codec: AC3 + video_codec: h264 + release_group: HiS@SiLUHD + language: english + website: sharethefiles.com + +? Movies/The Doors (1991)/08.03.09.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv +: options: --date-year-first + title: The Doors + year: 1991 + date: 2008-03-09 + format: BluRay + screen_size: 720p + audio_codec: AC3 + video_codec: h264 + release_group: HiS@SiLUHD + language: english + website: sharethefiles.com + +? Movies/Ratatouille/video_ts-ratatouille.srt +: title: Ratatouille + format: DVD + +# Removing this one because 001 is guessed as an episode number. +# ? Movies/001 __ A classer/Fantomas se déchaine - Louis de Funès.avi +# : title: Fantomas se déchaine + +? Movies/Comme une Image (2004)/Comme.Une.Image.FRENCH.DVDRiP.XViD-NTK.par-www.divx-overnet.com.avi +: title: Comme une Image + year: 2004 + language: french + format: DVD + video_codec: XviD + release_group: NTK + website: www.divx-overnet.com + +? Movies/Fantastic Mr Fox/Fantastic.Mr.Fox.2009.DVDRip.{x264+LC-AAC.5.1}{Fr-Eng}{Sub.Fr-Eng}-™.[sharethefiles.com].mkv +: title: Fantastic Mr Fox + year: 2009 + format: DVD + video_codec: h264 + audio_codec: AAC + audio_profile: LC + audio_channels: "5.1" + language: [ french, english ] + subtitle_language: [ french, english ] + website: sharethefiles.com + +? Movies/Somewhere.2010.DVDRip.XviD-iLG/i-smwhr.avi +: title: Somewhere + year: 2010 + format: DVD + video_codec: XviD + release_group: iLG + +? Movies/Moon_(2009).mkv +: title: Moon + year: 2009 + +? Movies/Moon_(2009)-x02-Making_Of.mkv +: title: Moon + year: 2009 + bonus: 2 + bonus_title: Making Of + +? movies/James_Bond-f17-Goldeneye.mkv +: title: Goldeneye + film_title: James Bond + film: 17 + + +? /movies/James_Bond-f21-Casino_Royale.mkv +: title: Casino Royale + film_title: James Bond + film: 21 + +? /movies/James_Bond-f21-Casino_Royale-x01-Becoming_Bond.mkv +: title: Casino Royale + film_title: James Bond + film: 21 + bonus: 1 + bonus_title: Becoming Bond + +? /movies/James_Bond-f21-Casino_Royale-x02-Stunts.mkv +: title: Casino Royale + film_title: James Bond + film: 21 + bonus: 2 + bonus_title: Stunts + +? OSS_117--Cairo,_Nest_of_Spies.mkv +: title: OSS 117 +# TODO: Implement subTitle for movies. + +? The Godfather Part 3.mkv +? The Godfather Part III.mkv +: title: The Godfather + part: 3 + +? Foobar Part VI.mkv +: title: Foobar + part: 6 + +? The_Insider-(1999)-x02-60_Minutes_Interview-1996.mp4 +: title: The Insider + year: 1999 + bonus: 2 + bonus_title: 60 Minutes Interview-1996 + +? Rush.._Beyond_The_Lighted_Stage-x09-Between_Sun_and_Moon-2002_Hartford.mkv +: title: Rush Beyond The Lighted Stage + bonus: 9 + bonus_title: Between Sun and Moon + year: 2002 + +? /public/uTorrent/Downloads Finished/Movies/Indiana.Jones.and.the.Temple.of.Doom.1984.HDTV.720p.x264.AC3.5.1-REDµX/Indiana.Jones.and.the.Temple.of.Doom.1984.HDTV.720p.x264.AC3.5.1-REDµX.mkv +: title: Indiana Jones and the Temple of Doom + year: 1984 + format: HDTV + screen_size: 720p + video_codec: h264 + audio_codec: AC3 + audio_channels: "5.1" + release_group: REDµX + +? The.Director’s.Notebook.2006.Blu-Ray.x264.DXVA.720p.AC3-de[42].mkv +: title: The Director’s Notebook + year: 2006 + format: BluRay + video_codec: h264 + video_api: DXVA + screen_size: 720p + audio_codec: AC3 + release_group: de[42] + + +? Movies/Cosmopolis.2012.LiMiTED.720p.BluRay.x264-AN0NYM0US[bb]/ano-cosmo.720p.mkv +: title: Cosmopolis + year: 2012 + screen_size: 720p + video_codec: h264 + release_group: AN0NYM0US[bb] + format: BluRay + other: Limited + +? movies/La Science des Rêves (2006)/La.Science.Des.Reves.FRENCH.DVDRip.XviD-MP-AceBot.avi +: title: La Science des Rêves + year: 2006 + format: DVD + video_codec: XviD + video_profile: MP + release_group: AceBot + language: French + +? The_Italian_Job.mkv +: title: The Italian Job + +? The.Rum.Diary.2011.1080p.BluRay.DTS.x264.D-Z0N3.mkv +: title: The Rum Diary + year: 2011 + screen_size: 1080p + format: BluRay + video_codec: h264 + audio_codec: DTS + release_group: D-Z0N3 + +? Life.Of.Pi.2012.1080p.BluRay.DTS.x264.D-Z0N3.mkv +: title: Life Of Pi + year: 2012 + screen_size: 1080p + format: BluRay + video_codec: h264 + audio_codec: DTS + release_group: D-Z0N3 + +? The.Kings.Speech.2010.1080p.BluRay.DTS.x264.D Z0N3.mkv +: title: The Kings Speech + year: 2010 + screen_size: 1080p + format: BluRay + audio_codec: DTS + video_codec: h264 + release_group: D Z0N3 + +? Street.Kings.2008.BluRay.1080p.DTS.x264.dxva EuReKA.mkv +: title: Street Kings + year: 2008 + format: BluRay + screen_size: 1080p + audio_codec: DTS + video_codec: h264 + video_api: DXVA + release_group: EuReKA + +? 2001.A.Space.Odyssey.1968.HDDVD.1080p.DTS.x264.dxva EuReKA.mkv +: title: 2001 A Space Odyssey + year: 1968 + format: HD-DVD + screen_size: 1080p + audio_codec: DTS + video_codec: h264 + video_api: DXVA + release_group: EuReKA + +? 2012.2009.720p.BluRay.x264.DTS WiKi.mkv +: title: "2012" + year: 2009 + screen_size: 720p + format: BluRay + video_codec: h264 + audio_codec: DTS + release_group: WiKi + +? /share/Download/movie/Dead Man Down (2013) BRRiP XViD DD5_1 Custom NLSubs =-_lt Q_o_Q gt-=_/XD607ebb-BRc59935-5155473f-1c5f49/XD607ebb-BRc59935-5155473f-1c5f49.avi +: title: Dead Man Down + year: 2013 + format: BluRay + video_codec: XviD + audio_channels: "5.1" + audio_codec: DolbyDigital + uuid: XD607ebb-BRc59935-5155473f-1c5f49 + +? Pacific.Rim.3D.2013.COMPLETE.BLURAY-PCH.avi +: title: Pacific Rim + year: 2013 + format: BluRay + other: + - Complete + - 3D + release_group: PCH + +? Immersion.French.2011.STV.READNFO.QC.FRENCH.ENGLISH.NTSC.DVDR.nfo +: title: Immersion French + year: 2011 + language: + - French + - English + format: DVD + other: NTSC + +? Immersion.French.2011.STV.READNFO.QC.FRENCH.NTSC.DVDR.nfo +: title: Immersion French + year: 2011 + language: French + format: DVD + other: NTSC + +? Immersion.French.2011.STV.READNFO.QC.NTSC.DVDR.nfo +: title: Immersion + language: French + year: 2011 + format: DVD + other: NTSC + +? French.Immersion.2011.STV.READNFO.QC.ENGLISH.NTSC.DVDR.nfo +: title: French Immersion + year: 2011 + language: ENGLISH + format: DVD + other: NTSC + +? Howl's_Moving_Castle_(2004)_[720p,HDTV,x264,DTS]-FlexGet.avi +: video_codec: h264 + format: HDTV + title: Howl's Moving Castle + screen_size: 720p + year: 2004 + audio_codec: DTS + release_group: FlexGet + +? Pirates de langkasuka.2008.FRENCH.1920X1080.h264.AVC.AsiaRa.mkv +: screen_size: 1080p + year: 2008 + language: French + video_codec: h264 + title: Pirates de langkasuka + release_group: AsiaRa + +? Masala (2013) Telugu Movie HD DVDScr XviD - Exclusive.avi +: year: 2013 + video_codec: XviD + title: Masala + format: HD-DVD + other: Screener + language: Telugu + release_group: Exclusive + +? Django Unchained 2012 DVDSCR X264 AAC-P2P.nfo +: year: 2012 + other: Screener + video_codec: h264 + title: Django Unchained + audio_codec: AAC + format: DVD + release_group: P2P + +? Ejecutiva.En.Apuros(2009).BLURAY.SCR.Xvid.Spanish.LanzamientosD.nfo +: year: 2009 + other: Screener + format: BluRay + video_codec: XviD + language: Spanish + title: Ejecutiva En Apuros + +? Die.Schluempfe.2.German.DL.1080p.BluRay.x264-EXQUiSiTE.mkv +: title: Die Schluempfe 2 + format: BluRay + language: + - Multiple languages + - German + video_codec: h264 + release_group: EXQUiSiTE + screen_size: 1080p + +? Rocky 1976 French SubForced BRRip x264 AC3-FUNKY.mkv +: title: Rocky + year: 1976 + subtitle_language: French + format: BluRay + video_codec: h264 + audio_codec: AC3 + release_group: FUNKY + +? REDLINE (BD 1080p H264 10bit FLAC) [3xR].mkv +: title: REDLINE + format: BluRay + video_codec: h264 + video_profile: 10bit + audio_codec: FLAC + screen_size: 1080p + +? The.Lizzie.McGuire.Movie.(2003).HR.DVDRiP.avi +: title: The Lizzie McGuire Movie + year: 2003 + format: DVD + other: HR + +? Hua.Mulan.BRRIP.MP4.x264.720p-HR.avi +: title: Hua Mulan + video_codec: h264 + format: BluRay + screen_size: 720p + other: HR + +? Dr.Seuss.The.Lorax.2012.DVDRip.LiNE.XviD.AC3.HQ.Hive-CM8.mp4 +: video_codec: XviD + title: Dr Seuss The Lorax + format: DVD + other: LiNE + year: 2012 + audio_codec: AC3 + audio_profile: HQ + release_group: Hive-CM8 + +? "Star Wars: Episode IV - A New Hope (2004) Special Edition.MKV" +: title: "Star Wars: Episode IV" + alternativeTitle: A New Hope + year: 2004 + edition: Special Edition + +? Dr.LiNE.The.Lorax.2012.DVDRip.LiNE.XviD.AC3.HQ.Hive-CM8.mp4 +: video_codec: XviD + title: Dr LiNE The Lorax + format: DVD + other: LiNE + year: 2012 + audio_codec: AC3 + audio_profile: HQ + release_group: Hive-CM8 + +? Dr.LiNE.The.Lorax.2012.DVDRip.XviD.AC3.HQ.Hive-CM8.mp4 +: video_codec: XviD + title: Dr LiNE The Lorax + format: DVD + year: 2012 + audio_codec: AC3 + audio_profile: HQ + release_group: Hive-CM8 + +? Perfect Child-2007-TRUEFRENCH-TVRip.Xvid-h@mster.avi +: release_group: h@mster + title: Perfect Child + video_codec: XviD + language: French + format: TV + year: 2007 + +? entre.ciel.et.terre.(1994).dvdrip.h264.aac-psypeon.avi +: audio_codec: AAC + format: DVD + release_group: psypeon + title: entre ciel et terre + video_codec: h264 + year: 1994 + +? Yves.Saint.Laurent.2013.FRENCH.DVDSCR.MD.XviD-ViVARiUM.avi +: format: DVD + language: French + other: + - MD + - Screener + release_group: ViVARiUM + title: Yves Saint Laurent + video_codec: XviD + year: 2013 + +? Echec et Mort - Hard to Kill - Steven Seagal Multi 1080p BluRay x264 CCATS.avi +: format: BluRay + language: Multiple languages + release_group: CCATS + screen_size: 1080p + title: Echec et Mort + alternativeTitle: + - Hard to Kill + - Steven Seagal + video_codec: h264 + +? Paparazzi - Timsit/Lindon (MKV 1080p tvripHD) +: options: -n + title: Paparazzi + alternativeTitle: + - Timsit + - Lindon + screen_size: 1080p + format: HDTV + +? some.movie.720p.bluray.x264-mind +: title: some movie + screen_size: 720p + video_codec: h264 + release_group: mind + format: BluRay + +? Dr LiNE The Lorax 720p h264 BluRay +: title: Dr LiNE The Lorax + screen_size: 720p + video_codec: h264 + format: BluRay + +#TODO: Camelcase implementation +#? BeatdownFrenchDVDRip.mkv +#: options: -c +# title: Beatdown +# language: French +# format: DVD + +#? YvesSaintLaurent2013FrenchDVDScrXvid.avi +#: options: -c +# format: DVD +# language: French +# other: Screener +# title: Yves saint laurent +# video_codec: XviD +# year: 2013 + + +? Elle.s.en.va.720p.mkv +: screen_size: 720p + title: Elle s en va + +? FooBar.7.PDTV-FlexGet +: format: DVB + release_group: FlexGet + title: FooBar 7 + +? h265 - HEVC Riddick Unrated Director Cut French 1080p DTS.mkv +: audio_codec: DTS + edition: Director's cut + language: fr + screen_size: 1080p + title: Riddick + other: Unrated + video_codec: h265 + +? "[h265 - HEVC] Riddick Unrated Director Cut French [1080p DTS].mkv" +: audio_codec: DTS + edition: Director's cut + language: fr + screen_size: 1080p + title: Riddick + other: Unrated + video_codec: h265 + +? Barbecue-2014-French-mHD-1080p +: language: fr + other: mHD + screen_size: 1080p + title: Barbecue + year: 2014 + +? Underworld Quadrilogie VO+VFF+VFQ 1080p HDlight.x264~Tonyk~Monde Infernal +: language: fr + other: + - HDLight + - OV + screen_size: 1080p + title: Underworld Quadrilogie + video_codec: h264 + +? A Bout Portant (The Killers).PAL.Multi.DVD-R-KZ +: format: DVD + language: mul + release_group: KZ + title: A Bout Portant + +? "Mise à Sac (Alain Cavalier, 1967) [Vhs.Rip.Vff]" +: format: VHS + language: fr + title: "Mise à Sac" + year: 1967 + +? A Bout Portant (The Killers).PAL.Multi.DVD-R-KZ +: format: DVD + other: PAL + language: mul + release_group: KZ + title: A Bout Portant + +? Youth.In.Revolt.(Be.Bad).2009.MULTI.1080p.LAME3*92-MEDIOZZ +: audio_codec: MP3 + language: mul + release_group: MEDIOZZ + screen_size: 1080p + title: Youth In Revolt + year: 2009 + +? La Defense Lincoln (The Lincoln Lawyer) 2011 [DVDRIP][Vostfr] +: format: DVD + subtitle_language: fr + title: La Defense Lincoln + year: 2011 + +? '[h265 - HEVC] Fight Club French 1080p DTS.' +: audio_codec: DTS + language: fr + screen_size: 1080p + title: Fight Club + video_codec: h265 + +? Love Gourou (Mike Myers) - FR +: language: fr + title: Love Gourou + +? '[h265 - hevc] transformers 2 1080p french ac3 6ch.' +: audio_channels: '5.1' + audio_codec: AC3 + language: fr + screen_size: 1080p + title: transformers 2 + video_codec: h265 + +? 1.Angry.Man.1957.mkv +: title: 1 Angry Man + year: 1957 + +? 12.Angry.Men.1957.mkv +: title: 12 Angry Men + year: 1957 + +? 123.Angry.Men.1957.mkv +: title: 123 Angry Men + year: 1957 + +? "Looney Tunes 1444x866 Porky's Last Stand.mkv" +: screen_size: 1444x866 + title: Looney Tunes diff --git a/lib/guessit2/test/rules/__init__.py b/lib/guessit2/test/rules/__init__.py new file mode 100644 index 000000000..e5be370e4 --- /dev/null +++ b/lib/guessit2/test/rules/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name diff --git a/lib/guessit2/test/rules/audio_codec.yml b/lib/guessit2/test/rules/audio_codec.yml new file mode 100644 index 000000000..afbfae04b --- /dev/null +++ b/lib/guessit2/test/rules/audio_codec.yml @@ -0,0 +1,77 @@ +# Multiple input strings having same expected results can be chained. +# Use $ marker to check inputs that should not match results. + + +? +MP3 +? +lame +? +lame3.12 +? +lame3.100 +: audio_codec: MP3 + +? +DolbyDigital +? +DD +? -Dolby Digital +: audio_codec: DolbyDigital + +? +AAC +: audio_codec: AAC + +? +AC3 +: audio_codec: AC3 + +? +Flac +: audio_codec: FLAC + +? +DTS +: audio_codec: DTS + +? +True-HD +? +trueHD +: audio_codec: TrueHD + +? +DTS-HD +: audio_codec: DTS + audio_profile: HD + +? +DTS-HDma +: audio_codec: DTS + audio_profile: HDMA + +? +AC3-hq +: audio_codec: AC3 + audio_profile: HQ + +? +AAC-HE +: audio_codec: AAC + audio_profile: HE + +? +AAC-LC +: audio_codec: AAC + audio_profile: LC + +? +AAC2.0 +: audio_codec: AAC + audio_channels: '2.0' + +? +7.1 +? +7ch +? +8ch +: audio_channels: '7.1' + +? +5.1 +? +5ch +? +6ch +: audio_channels: '5.1' + +? +2ch +? +2.0 +? +stereo +: audio_channels: '2.0' + +? +1ch +? +mono +: audio_channels: '1.0' + +? DD5.1 +: audio_codec: DolbyDigital + audio_channels: '5.1' diff --git a/lib/guessit2/test/rules/bonus.yml b/lib/guessit2/test/rules/bonus.yml new file mode 100644 index 000000000..6ef6f5b25 --- /dev/null +++ b/lib/guessit2/test/rules/bonus.yml @@ -0,0 +1,9 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? Movie Title-x01-Other Title.mkv +? Movie Title-x01-Other Title +? directory/Movie Title-x01-Other Title/file.mkv +: title: Movie Title + bonus_title: Other Title + bonus: 1 + diff --git a/lib/guessit2/test/rules/cds.yml b/lib/guessit2/test/rules/cds.yml new file mode 100644 index 000000000..8bb4e98c6 --- /dev/null +++ b/lib/guessit2/test/rules/cds.yml @@ -0,0 +1,5 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? cd 1of3 +: cd: 1 + cd_count: 3 diff --git a/lib/guessit2/test/rules/country.yml b/lib/guessit2/test/rules/country.yml new file mode 100644 index 000000000..f2da1b205 --- /dev/null +++ b/lib/guessit2/test/rules/country.yml @@ -0,0 +1,10 @@ +# Multiple input strings having same expected results can be chained. +# Use $ marker to check inputs that should not match results. +? Us.this.is.title +? this.is.title.US +: country: US + title: this is title + +? This.is.us.title +: title: This is us title + diff --git a/lib/guessit2/test/rules/date.yml b/lib/guessit2/test/rules/date.yml new file mode 100644 index 000000000..d7379f03c --- /dev/null +++ b/lib/guessit2/test/rules/date.yml @@ -0,0 +1,50 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? +09.03.08 +? +09.03.2008 +? +2008.03.09 +: date: 2008-03-09 + +? +31.01.15 +? +31.01.2015 +? +15.01.31 +? +2015.01.31 +: date: 2015-01-31 + +? +01.02.03 +: date: 2003-02-01 + +? +01.02.03 +: options: --date-year-first + date: 2001-02-03 + +? +01.02.03 +: options: --date-day-first + date: 2003-02-01 + +? 1919 +? 2030 +: !!map {} + +? 2029 +: year: 2029 + +? (1920) +: year: 1920 + +? 2012 +: year: 2012 + +? 2011 2013 (2012) (2015) # first marked year is guessed. +: title: "2011 2013" + year: 2012 + +? 2012 2009 S01E02 2015 # If no year is marked, the second one is guessed. +: title: "2012" + year: 2009 + episode_title: "2015" + +? Something 2 mar 2013) +: title: Something + date: 2013-03-02 + type: episode diff --git a/lib/guessit2/test/rules/edition.yml b/lib/guessit2/test/rules/edition.yml new file mode 100644 index 000000000..bc35b85e6 --- /dev/null +++ b/lib/guessit2/test/rules/edition.yml @@ -0,0 +1,25 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? Director's cut +? Edition Director's cut +: edition: Director's cut + +? Collector +? Collector Edition +? Edition Collector +: edition: Collector Edition + +? Special Edition +? Edition Special +? -Special +: edition: Special Edition + +? Criterion Edition +? Edition Criterion +? -Criterion +: edition: Criterion Edition + +? Deluxe +? Deluxe Edition +? Edition Deluxe +: edition: Deluxe Edition diff --git a/lib/guessit2/test/rules/episodes.yml b/lib/guessit2/test/rules/episodes.yml new file mode 100644 index 000000000..61eea5766 --- /dev/null +++ b/lib/guessit2/test/rules/episodes.yml @@ -0,0 +1,119 @@ +# Multiple input strings having same expected results can be chained. +# Use $ marker to check inputs that should not match results. +? +2x5 +? +2X5 +? +02x05 +? +2X05 +? +02x5 +? S02E05 +? s02e05 +? s02e5 +? s2e05 +? -s03e05 +? -s02e06 +? -3x05 +? -2x06 +: season: 2 + episode: 5 + +? "+0102" +? "+102" +: season: 1 + episode: 2 + +? "0102 S03E04" +? "S03E04 102" +: season: 3 + episode: 4 + +? +serie Saison 2 other +? +serie Season 2 other +? +serie Saisons 2 other +? +serie Seasons 2 other +? +serie Serie 2 other +? +serie Series 2 other +? +serie Season Two other +? +serie Season II other +: season: 2 + +? Some Series.S02E01.Episode.title.mkv +? Some Series/Season 02/E01-Episode title.mkv +? Some Series/Season 02/Some Series-E01-Episode title.mkv +? Some Dummy Directory/Season 02/Some Series-E01-Episode title.mkv +? -Some Dummy Directory/Season 02/E01-Episode title.mkv +? Some Series/Unsafe Season 02/Some Series-E01-Episode title.mkv +? -Some Series/Unsafe Season 02/E01-Episode title.mkv +? Some Series/Season 02/E01-Episode title.mkv +? Some Series/ Season 02/E01-Episode title.mkv +? Some Dummy Directory/Some Series S02/E01-Episode title.mkv +? Some Dummy Directory/S02 Some Series/E01-Episode title.mkv +: title: Some Series + episode_title: Episode title + season: 2 + episode: 1 + +? Some Series.S02E01.mkv +? Some Series/Season 02/E01.mkv +? Some Series/Season 02/Some Series-E01.mkv +? Some Dummy Directory/Season 02/Some Series-E01.mkv +? -Some Dummy Directory/Season 02/E01.mkv +? Some Series/Unsafe Season 02/Some Series-E01.mkv +? -Some Series/Unsafe Season 02/E01.mkv +? Some Series/Season 02/E01.mkv +? Some Series/ Season 02/E01.mkv +? Some Dummy Directory/Some Series S02/E01-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.mkv +: title: Some Series + season: 2 + episode: 1 + +? Some Series S03E01E02 +: title: Some Series + season: 3 + episode: [1, 2] + +? Some Series S01S02S03 +? Some Series S01-02-03 +? Some Series S01 S02 S03 +? -Some Series S01 02 03 +: title: Some Series + season: [1, 2, 3] + +? Some Series E01E02E03 +? Some Series E01-02-03 +? Some Series E01-03 +? Some Series E01 E02 E03 +? Some Series E01 02 03 +: title: Some Series + episode: [1, 2, 3] + +? Some Series E01E02E04 +? Some Series E01 E02 E04 +? Some Series E01 02 04 +: title: Some Series + episode: [1, 2, 4] + +? Some Series E01-02-04 +? Some Series E01-04 +? Some Series E01-04 +: title: Some Series + episode: [1, 2, 3, 4] + +? Some Series E01-02-E04 +: title: Some Series + episode: [1, 2, 4] + +? Episode 3 +? -Episode III +: episode: 3 + +? Episode 3 +? Episode III +: options: -t episode + episode: 3 + +? -A very special movie +: episode_details: Special + +? A very special episode +: options: -t episode + episode_details: Special diff --git a/lib/guessit2/test/rules/film.yml b/lib/guessit2/test/rules/film.yml new file mode 100644 index 000000000..1f7743318 --- /dev/null +++ b/lib/guessit2/test/rules/film.yml @@ -0,0 +1,9 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? Film Title-f01-Series Title.mkv +? Film Title-f01-Series Title +? directory/Film Title-f01-Series Title/file.mkv +: title: Series Title + film_title: Film Title + film: 1 + diff --git a/lib/guessit2/test/rules/format.yml b/lib/guessit2/test/rules/format.yml new file mode 100644 index 000000000..cf3dea921 --- /dev/null +++ b/lib/guessit2/test/rules/format.yml @@ -0,0 +1,112 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? +VHS +? +VHSRip +? +VHS-Rip +? +VhS_rip +? +VHS.RIP +? -VHSAnythingElse +? -SomeVHS stuff +? -VH +? -VHx +? -VHxRip +: format: VHS + +? +Cam +? +CamRip +? +CaM Rip +? +Cam_Rip +? +cam.rip +: format: Cam + +? +Telesync +? +TS +? +HD TS +? -Hd.Ts # ts file extension +? -HD.TS # ts file extension +? +Hd-Ts +: format: Telesync + +? +Workprint +? +workPrint +? +WorkPrint +? +WP +? -Work Print +: format: Workprint + +? +Telecine +? +teleCine +? +TC +? -Tele Cine +: format: Telecine + +? +PPV +? +ppv-rip +: format: PPV + +? -TV +? +SDTV +? +SDTVRIP +? +Rip sd tv +? +TvRip +? +Rip TV +: format: TV + +? +DVB +? +DVB-Rip +? +DvBRiP +? +pdTV +? +Pd Tv +: format: DVB + +? +DVD +? +DVD-RIP +? +video ts +? +DVDR +? +DVD 9 +? +dvd 5 +? -dvd ts +: format: DVD + -format: ts + +? +HDTV +? +tv rip hd +? +HDtv Rip +? +HdRip +: format: HDTV + +? +VOD +? +VodRip +? +vod rip +: format: VOD + +? +webrip +? +Web Rip +: format: WEBRip + +? +webdl +? +Web DL +? +webHD +? +WEB hd +? +web +: format: WEB-DL + +? +HDDVD +? +hd dvd +? +hdDvdRip +: format: HD-DVD + +? +BluRay +? +BluRay rip +? +BD +? +BR +? +BDRip +? +BR rip +? +BD5 +? +BD9 +? +BD25 +? +bd50 +: format: BluRay + +? XVID.NTSC.DVDR.nfo +: format: DVD diff --git a/lib/guessit2/test/rules/language.yml b/lib/guessit2/test/rules/language.yml new file mode 100644 index 000000000..7871898b6 --- /dev/null +++ b/lib/guessit2/test/rules/language.yml @@ -0,0 +1,26 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? +English +? .ENG. +: language: English + +? +French +: language: French + +? +SubFrench +? +SubFr +? +STFr +? ST.FR +: subtitle_language: French + +? +ENG.-.sub.FR +? ENG.-.FR Sub +? +ENG.-.SubFR +? +ENG.-.FRSUB +: language: English + subtitle_language: French + +? "{Fr-Eng}.St{Fr-Eng}" +? "Le.Prestige[x264.{Fr-Eng}.St{Fr-Eng}.Chaps].mkv" +: language: [French, English] + subtitle_language: [French, English] \ No newline at end of file diff --git a/lib/guessit2/test/rules/other.yml b/lib/guessit2/test/rules/other.yml new file mode 100644 index 000000000..cce8cbd05 --- /dev/null +++ b/lib/guessit2/test/rules/other.yml @@ -0,0 +1,137 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? +DVDSCR +? +DVDScreener +? +DVD-SCR +? +DVD Screener +? +DVD AnythingElse Screener +? -DVD AnythingElse SCR +: other: Screener + +? +AudioFix +? +AudioFixed +? +Audio Fix +? +Audio Fixed +: other: AudioFix + +? +SyncFix +? +SyncFixed +? +Sync Fix +? +Sync Fixed +: other: SyncFix + +? +DualAudio +? +Dual Audio +: other: DualAudio + +? +ws +? +WideScreen +? +Wide Screen +: other: WideScreen + +? +NF +? +Netflix +: other: Netflix + +# Fix and Real must be surround by others properties to be matched. +? DVD.Real.XViD +? DVD.fix.XViD +? -DVD.Real +? -DVD.Fix +? -Real.XViD +? -Fix.XViD +: other: Proper + proper_count: 1 + +? -DVD.BlablaBla.Fix.Blablabla.XVID +? -DVD.BlablaBla.Fix.XVID +? -DVD.Fix.Blablabla.XVID +: other: Proper + proper_count: 1 + + +? DVD.Real.PROPER.REPACK +: other: Proper + proper_count: 3 + + +? Proper +? +Repack +? +Rerip +: other: Proper + proper_count: 1 + +? XViD.Fansub +: other: Fansub + +? XViD.Fastsub +: other: Fastsub + +? +Season Complete +? -Complete +: other: Complete + +? R5 +? RC +: other: R5 + +? PreAir +? Pre Air +: other: Preair + +? Screener +: other: Screener + +? Remux +: other: Remux + +? 3D +: other: 3D + +? HD +: other: HD + +? mHD # ?? +: other: mHD + +? HDLight +: other: HDLight + +? HQ +: other: HQ + +? ddc +: other: DDC + +? hr +: other: HR + +? PAL +: other: PAL + +? SECAM +: other: SECAM + +? NTSC +: other: NTSC + +? CC +: other: CC + +? LD +: other: LD + +? MD +: other: MD + +? -The complete movie +: other: Complete + +? +The complete movie +: title: The complete movie + +? +AC3-HQ +: audio_profile: HQ + +? Other-HQ +: other: HQ diff --git a/lib/guessit2/test/rules/part.yml b/lib/guessit2/test/rules/part.yml new file mode 100644 index 000000000..72f3d98a8 --- /dev/null +++ b/lib/guessit2/test/rules/part.yml @@ -0,0 +1,18 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? Filename Part 3.mkv +? Filename Part III.mkv +? Filename Part Three.mkv +? Filename Part Trois.mkv +: title: Filename + part: 3 + +? Part 3 +? Part III +? Part Three +? Part Trois +? Part3 +: part: 3 + +? -Something.Apt.1 +: part: 1 \ No newline at end of file diff --git a/lib/guessit2/test/rules/processors.yml b/lib/guessit2/test/rules/processors.yml new file mode 100644 index 000000000..ee906b2c3 --- /dev/null +++ b/lib/guessit2/test/rules/processors.yml @@ -0,0 +1,8 @@ +# Multiple input strings having same expected results can be chained. +# Use $ marker to check inputs that should not match results. + +# Prefer information for last path. +? Some movie (2000)/Some movie (2001).mkv +? Some movie (2001)/Some movie.mkv +: year: 2001 + container: mkv diff --git a/lib/guessit2/test/rules/release_group.yml b/lib/guessit2/test/rules/release_group.yml new file mode 100644 index 000000000..8f1d9e931 --- /dev/null +++ b/lib/guessit2/test/rules/release_group.yml @@ -0,0 +1,33 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? Some.Title.XViD-ReleaseGroup +? Some.Title.XViD-ReleaseGroup.mkv +: release_group: ReleaseGroup + +? Some.Title.XViD-by.Artik[SEDG].avi +: release_group: Artik[SEDG] + +? "[ABC] Some.Title.XViD.avi" +? some/folder/[ABC]Some.Title.avi +: release_group: ABC + +? Some.Title.XViD-S2E02.NoReleaseGroup.avi +: release_group: !!null + +? Test.S01E01-FooBar-Group +: options: -G group -G xxxx + episode: 1 + episode_title: FooBar + release_group: Group + season: 1 + title: Test + type: episode + +? Test.S01E01-FooBar-Group +: options: -G re:gr.?up -G xxxx + episode: 1 + episode_title: FooBar + release_group: Group + season: 1 + title: Test + type: episode diff --git a/lib/guessit2/test/rules/screen_size.yml b/lib/guessit2/test/rules/screen_size.yml new file mode 100644 index 000000000..b7de201c3 --- /dev/null +++ b/lib/guessit2/test/rules/screen_size.yml @@ -0,0 +1,65 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? +360p +? +360px +? +360i +? "+360" +? +500x360 +: screen_size: 360p + +? +368p +? +368px +? +368i +? "+368" +? +500x368 +: screen_size: 368p + +? +480p +? +480px +? +480i +? "+480" +? +500x480 +: screen_size: 480p + +? +576p +? +576px +? +576i +? "+576" +? +500x576 +: screen_size: 576p + +? +720p +? +720px +? +720i +? "+720" +? +500x720 +: screen_size: 720p + +? +900p +? +900px +? +900i +? "+900" +? +500x900 +: screen_size: 900p + +? +1080p +? +1080px +? -1080i +? "+1080" +? +500x1080 +: screen_size: 1080p + +? +1080i +? -1080p +: screen_size: 1080i + +? +2160p +? +2160px +? +2160i +? "+2160" +? +4096x2160 +: screen_size: 4K + +? Test.File.720hd.bluray +? Test.File.720p50 +: screen_size: 720p diff --git a/lib/guessit2/test/rules/title.yml b/lib/guessit2/test/rules/title.yml new file mode 100644 index 000000000..fffaf8a25 --- /dev/null +++ b/lib/guessit2/test/rules/title.yml @@ -0,0 +1,32 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? Title Only +? -Title XViD 720p Only +? sub/folder/Title Only +? -sub/folder/Title XViD 720p Only +? Title Only.mkv +? Title Only.avi +: title: Title Only + +? Title Only/title_only.mkv +: title: Title Only + +? title_only.mkv +: title: title only + +? Some Title/some.title.mkv +? some.title/Some.Title.mkv +: title: Some Title + +? SOME TITLE/Some.title.mkv +? Some.title/SOME TITLE.mkv +: title: Some title + +? some title/Some.title.mkv +? Some.title/some title.mkv +: title: Some title + +? Some other title/Some.Other.title.mkv +? Some.Other title/Some other title.mkv +: title: Some Other title + diff --git a/lib/guessit2/test/rules/video_codec.yml b/lib/guessit2/test/rules/video_codec.yml new file mode 100644 index 000000000..d195eaafe --- /dev/null +++ b/lib/guessit2/test/rules/video_codec.yml @@ -0,0 +1,54 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? rv10 +? rv13 +? RV20 +? Rv30 +? rv40 +? -xrv40 +: video_codec: Real + +? mpeg2 +? MPEG2 +? -mpeg +? -mpeg 2 # Not sure if we should ignore this one ... +? -xmpeg2 +? -mpeg2x +: video_codec: Mpeg2 + +? DivX +? -div X +? divx +? dvdivx +? DVDivX +: video_codec: DivX + +? XviD +? xvid +? -x vid +: video_codec: XviD + +? h264 +? x264 +? h.264 +? x.264 +? mpeg4-AVC +? -MPEG-4 +? -mpeg4 +? -mpeg +? -h 265 +? -x265 +: video_codec: h264 + +? h265 +? x265 +? h.265 +? x.265 +? hevc +? -h 264 +? -x264 +: video_codec: h265 + +? h265-HP +: video_codec: h265 + video_profile: HP \ No newline at end of file diff --git a/lib/guessit2/test/rules/website.yml b/lib/guessit2/test/rules/website.yml new file mode 100644 index 000000000..552386576 --- /dev/null +++ b/lib/guessit2/test/rules/website.yml @@ -0,0 +1,15 @@ +# Multiple input strings having same expected results can be chained. +# Use - marker to check inputs that should not match results. +? +tvu.org.ru +? -tvu.unsafe.ru +: website: tvu.org.ru + +? +www.nimp.na +? -somewww.nimp.na +? -www.nimp.nawouak +? -nimp.na +: website: www.nimp.na + +? +wawa.co.uk +? -wawa.uk +: website: wawa.co.uk diff --git a/lib/guessit2/test/test-input-file.txt b/lib/guessit2/test/test-input-file.txt new file mode 100644 index 000000000..656bc9317 --- /dev/null +++ b/lib/guessit2/test/test-input-file.txt @@ -0,0 +1,2 @@ +Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv +SecondFile.avi \ No newline at end of file diff --git a/lib/guessit2/test/test_api.py b/lib/guessit2/test/test_api.py new file mode 100644 index 000000000..05ed9a431 --- /dev/null +++ b/lib/guessit2/test/test_api.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name + +import os + +import pytest + +from ..api import guessit, properties + +__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + + +def test_default(): + ret = guessit(u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') + assert ret and 'title' in ret + + +def test_unicode(): + ret = guessit(u'[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi') + assert ret and 'title' in ret + + +def test_main_non_unicode(): + with pytest.raises(TypeError): + guessit(b'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') + + +def test_properties(): + props = properties() + assert 'video_codec' in props.keys() diff --git a/lib/guessit2/test/test_benchmark.py b/lib/guessit2/test/test_benchmark.py new file mode 100644 index 000000000..7638fe2d6 --- /dev/null +++ b/lib/guessit2/test/test_benchmark.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use,pointless-statement,missing-docstring,invalid-name,line-too-long +import time + +import pytest + +from ..api import guessit + + +def case1(): + return guessit(u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') + + +def case2(): + return guessit(u'Movies/Fantastic Mr Fox/Fantastic.Mr.Fox.2009.DVDRip.{x264+LC-AAC.5.1}{Fr-Eng}{Sub.Fr-Eng}-™.[sharethefiles.com].mkv') + + +def case3(): + return guessit(u'Series/dexter/Dexter.5x02.Hello,.Bandit.ENG.-.sub.FR.HDTV.XviD-AlFleNi-TeaM.[tvu.org.ru].avi') + + +def case4(): + return guessit(u'Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv') + + +@pytest.mark.benchmark( + group="Performance Tests", + min_time=1, + max_time=2, + min_rounds=5, + timer=time.time, + disable_gc=True, + warmup=False +) +@pytest.mark.skipif(True, reason="Disabled") +class TestBenchmark(object): + def test_case1(self, benchmark): + ret = benchmark(case1) + assert ret + + def test_case2(self, benchmark): + ret = benchmark(case2) + assert ret + + def test_case3(self, benchmark): + ret = benchmark(case3) + assert ret + + def test_case4(self, benchmark): + ret = benchmark(case4) + assert ret + diff --git a/lib/guessit2/test/test_main.py b/lib/guessit2/test/test_main.py new file mode 100644 index 000000000..5c76064a6 --- /dev/null +++ b/lib/guessit2/test/test_main.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name + +import os + +import pytest + +from ..__main__ import main + +__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + + +def test_main_no_args(): + main([]) + + +def test_main(): + main([u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv']) + + +def test_main_unicode(): + main([u'[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi']) + + +def test_main_non_unicode(): + main(['Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv']) + + +def test_main_verbose(): + main([u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '--verbose']) + + +def test_main_yaml(): + main([u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '--yaml']) + + +def test_main_json(): + main([u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '--json']) + + +def test_main_show_property(): + main([u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '-P', 'title']) + + +def test_main_advanced(): + main([u'Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv', '-a']) + + +def test_main_input(): + main(['--input', os.path.join(__location__, 'test-input-file.txt')]) + + +def test_main_properties(): + main(['-p']) + main(['-p', '--json']) + main(['-p', '--yaml']) + + +def test_main_values(): + main(['-V']) + main(['-V', '--json']) + main(['-V', '--yaml']) + + +def test_main_help(): + with pytest.raises(SystemExit): + main(['--help']) + + +def test_main_version(): + main(['--version']) diff --git a/lib/guessit2/test/test_yml.py b/lib/guessit2/test/test_yml.py new file mode 100644 index 000000000..ccd78e15d --- /dev/null +++ b/lib/guessit2/test/test_yml.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name +import logging + +# io.open supports encoding= in python 2.7 +from io import open # pylint: disable=redefined-builtin +import os +import yaml + +import six + +import regex as re + +import babelfish +import pytest + +from rebulk.utils import is_iterable + +from guessit.options import parse_options +from ..yamlutils import OrderedDictYAMLLoader +from .. import guessit + + +logger = logging.getLogger(__name__) + +__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + +filename_predicate = None +string_predicate = None + + +# filename_predicate = lambda filename: 'episode_title' in filename +# string_predicate = lambda string: '-DVD.BlablaBla.Fix.Blablabla.XVID' in string + + + +class EntryResult(object): + def __init__(self, string, negates=False): + self.string = string + self.negates = negates + self.valid = [] + self.missing = [] + self.different = [] + self.extra = [] + self.others = [] + + @property + def ok(self): + if self.negates: + return self.missing or self.different + return not self.missing and not self.different and not self.extra and not self.others + + @property + def warning(self): + if self.negates: + return False + return not self.missing and not self.different and self.extra + + @property + def error(self): + if self.negates: + return not self.missing and not self.different and not self.others + return self.missing or self.different or self.others + + def __repr__(self): + if self.ok: + return self.string + ': OK!' + elif self.warning: + return '%s%s: WARNING! (valid=%i, extra=%i)' % ('-' if self.negates else '', self.string, len(self.valid), + len(self.extra)) + elif self.error: + return '%s%s: ERROR! (valid=%i, missing=%i, different=%i, extra=%i, others=%i)' % \ + ('-' if self.negates else '', self.string, len(self.valid), len(self.missing), len(self.different), + len(self.extra), len(self.others)) + else: + return '%s%s: UNKOWN! (valid=%i, missing=%i, different=%i, extra=%i, others=%i)' % \ + ('-' if self.negates else '', self.string, len(self.valid), len(self.missing), len(self.different), + len(self.extra), len(self.others)) + + @property + def details(self): + ret = [] + if self.valid: + ret.append('valid=' + str(len(self.valid))) + for valid in self.valid: + ret.append(' ' * 4 + str(valid)) + if self.missing: + ret.append('missing=' + str(len(self.missing))) + for missing in self.missing: + ret.append(' ' * 4 + str(missing)) + if self.different: + ret.append('different=' + str(len(self.different))) + for different in self.different: + ret.append(' ' * 4 + str(different)) + if self.extra: + ret.append('extra=' + str(len(self.extra))) + for extra in self.extra: + ret.append(' ' * 4 + str(extra)) + if self.others: + ret.append('others=' + str(len(self.others))) + for other in self.others: + ret.append(' ' * 4 + str(other)) + return ret + + +class Results(list): + def assert_ok(self): + errors = [entry for entry in self if entry.error] + assert not errors + + +def files_and_ids(predicate=None): + files = [] + ids = [] + + for (dirpath, _, filenames) in os.walk(__location__): + if dirpath == __location__: + dirpath_rel = '' + else: + dirpath_rel = os.path.relpath(dirpath, __location__) + for filename in filenames: + name, ext = os.path.splitext(filename) + filepath = os.path.join(dirpath_rel, filename) + if ext == '.yml' and (not predicate or predicate(filepath)): + files.append(filepath) + ids.append(os.path.join(dirpath_rel, name)) + + return files, ids + + +class TestYml(object): + """ + Run tests from yaml files. + Multiple input strings having same expected results can be chained. + Use $ marker to check inputs that should not match results. + """ + + options_re = re.compile(r'^([ \+-]+)(.*)') + + files, ids = files_and_ids(filename_predicate) + + @staticmethod + def set_default(expected, default): + if default: + for k, v in default.items(): + if k not in expected: + expected[k] = v + + @pytest.mark.parametrize('filename', files, ids=ids) + def test(self, filename, caplog): + caplog.setLevel(logging.INFO) + with open(os.path.join(__location__, filename), 'r', encoding='utf-8') as infile: + data = yaml.load(infile, OrderedDictYAMLLoader) + entries = Results() + + last_expected = None + for string, expected in reversed(list(data.items())): + if expected is None: + data[string] = last_expected + else: + last_expected = expected + + default = None + try: + default = data['__default__'] + del data['__default__'] + except KeyError: + pass + + for string, expected in data.items(): + TestYml.set_default(expected, default) + if not isinstance(string, six.text_type): + string = six.text_type(string) + if not string_predicate or string_predicate(string): # pylint: disable=not-callable + entry = self.check(string, expected) + if entry.ok: + logger.debug(u'[' + filename + '] ' + six.text_type(entry)) + elif entry.warning: + logger.warning(u'[' + filename + '] ' + six.text_type(entry)) + elif entry.error: + logger.error(u'[' + filename + '] ' + six.text_type(entry)) + for line in entry.details: + logger.error(u'[' + filename + '] ' + ' ' * 4 + line) + entries.append(entry) + entries.assert_ok() + + def check(self, string, expected): + negates, global_, string = self.parse_token_options(string) + + options = expected.get('options') + if options is None: + options = {} + if not isinstance(options, dict): + options = parse_options(options) + if 'implicit' not in options: + options['implicit'] = True + try: + result = guessit(string, options) + except Exception as exc: + logger.error('[' + string + '] Exception: ' + str(exc)) + raise exc + + entry = EntryResult(string, negates) + + if global_: + self.check_global(string, result, entry) + + self.check_expected(result, expected, entry) + + return entry + + def parse_token_options(self, string): + matches = self.options_re.search(string) + negates = False + global_ = False + if matches: + string = matches.group(2) + for opt in matches.group(1): + if '-' in opt: + negates = True + if '+' in opt: + global_ = True + return negates, global_, string + + def check_global(self, string, result, entry): + global_span = [] + for result_matches in result.matches.values(): + for result_match in result_matches: + if not global_span: + global_span = list(result_match.span) + else: + if global_span[0] > result_match.span[0]: + global_span[0] = result_match.span[0] + if global_span[1] < result_match.span[1]: + global_span[1] = result_match.span[1] + if global_span and global_span[1] - global_span[0] < len(string): + entry.others.append("Match is not global") + + def is_same(self, value, expected): + values = set(value) if is_iterable(value) else set((value,)) + expecteds = set(expected) if is_iterable(expected) else set((expected,)) + if len(values) != len(expecteds): + return False + if isinstance(next(iter(values)), babelfish.Language): + # pylint: disable=no-member + expecteds = set([babelfish.Language.fromguessit(expected) for expected in expecteds]) + elif isinstance(next(iter(values)), babelfish.Country): + # pylint: disable=no-member + expecteds = set([babelfish.Country.fromguessit(expected) for expected in expecteds]) + return values == expecteds + + def check_expected(self, result, expected, entry): + if expected: + for expected_key, expected_value in expected.items(): + if expected_key and expected_key != 'options' and expected_value is not None: + negates_key, _, result_key = self.parse_token_options(expected_key) + if result_key in result.keys(): + if not self.is_same(result[result_key], expected_value): + if negates_key: + entry.valid.append((expected_key, expected_value)) + else: + entry.different.append((expected_key, expected_value, result[expected_key])) + else: + if negates_key: + entry.different.append((expected_key, expected_value, result[expected_key])) + else: + entry.valid.append((expected_key, expected_value)) + elif not negates_key: + entry.missing.append((expected_key, expected_value)) + + for result_key, result_value in result.items(): + if result_key not in expected.keys(): + entry.extra.append((result_key, result_value)) diff --git a/lib/guessit2/test/various.yml b/lib/guessit2/test/various.yml new file mode 100644 index 000000000..1d22a4eb9 --- /dev/null +++ b/lib/guessit2/test/various.yml @@ -0,0 +1,545 @@ +? Movies/Fear and Loathing in Las Vegas (1998)/Fear.and.Loathing.in.Las.Vegas.720p.HDDVD.DTS.x264-ESiR.mkv +: type: movie + title: Fear and Loathing in Las Vegas + year: 1998 + screen_size: 720p + format: HD-DVD + audio_codec: DTS + video_codec: h264 + release_group: ESiR + +? Series/Duckman/Duckman - 101 (01) - 20021107 - I, Duckman.avi +: type: episode + title: Duckman + season: 1 + episode: 1 + episode_title: I, Duckman + date: 2002-11-07 + +? Series/Neverwhere/Neverwhere.05.Down.Street.[tvu.org.ru].avi +: type: episode + title: Neverwhere + episode: 5 + episode_title: Down Street + website: tvu.org.ru + +? Neverwhere.05.Down.Street.[tvu.org.ru].avi +: type: episode + title: Neverwhere + episode: 5 + episode_title: Down Street + website: tvu.org.ru + +? Series/Breaking Bad/Minisodes/Breaking.Bad.(Minisodes).01.Good.Cop.Bad.Cop.WEBRip.XviD.avi +: type: episode + title: Breaking Bad + episode_format: Minisode + episode: 1 + episode_title: Good Cop Bad Cop + format: WEBRip + video_codec: XviD + +? Series/Kaamelott/Kaamelott - Livre V - Ep 23 - Le Forfait.avi +: type: episode + title: Kaamelott + episode: 23 + episode_title: Le Forfait + +? Movies/The Doors (1991)/09.03.08.The.Doors.(1991).BDRip.720p.AC3.X264-HiS@SiLUHD-English.[sharethefiles.com].mkv +: type: movie + title: The Doors + year: 1991 + date: 2008-03-09 + format: BluRay + screen_size: 720p + audio_codec: AC3 + video_codec: h264 + release_group: HiS@SiLUHD + language: english + website: sharethefiles.com + +? Movies/M.A.S.H. (1970)/MASH.(1970).[Divx.5.02][Dual-Subtitulos][DVDRip].ogm +: type: movie + title: MASH + year: 1970 + video_codec: DivX + format: DVD + +? the.mentalist.501.hdtv-lol.mp4 +: type: episode + title: the mentalist + season: 5 + episode: 1 + format: HDTV + release_group: lol + +? the.simpsons.2401.hdtv-lol.mp4 +: type: episode + title: the simpsons + season: 24 + episode: 1 + format: HDTV + release_group: lol + +? Homeland.S02E01.HDTV.x264-EVOLVE.mp4 +: type: episode + title: Homeland + season: 2 + episode: 1 + format: HDTV + video_codec: h264 + release_group: EVOLVE + +? /media/Band_of_Brothers-e01-Currahee.mkv +: type: episode + title: Band of Brothers + episode: 1 + episode_title: Currahee + +? /media/Band_of_Brothers-x02-We_Stand_Alone_Together.mkv +: type: episode + title: Band of Brothers + bonus: 2 + bonus_title: We Stand Alone Together + +? /movies/James_Bond-f21-Casino_Royale-x02-Stunts.mkv +: type: movie + title: Casino Royale + film_title: James Bond + film: 21 + bonus: 2 + bonus_title: Stunts + +? /TV Shows/new.girl.117.hdtv-lol.mp4 +: type: episode + title: new girl + season: 1 + episode: 17 + format: HDTV + release_group: lol + +? The.Office.(US).1x03.Health.Care.HDTV.XviD-LOL.avi +: type: episode + title: The Office + country: US + season: 1 + episode: 3 + episode_title: Health Care + format: HDTV + video_codec: XviD + release_group: LOL + +? The_Insider-(1999)-x02-60_Minutes_Interview-1996.mp4 +: type: movie + title: The Insider + year: 1999 + bonus: 2 + bonus_title: 60 Minutes Interview-1996 + +? OSS_117--Cairo,_Nest_of_Spies.mkv +: type: movie + title: OSS 117 + alternativeTitle: Cairo, Nest of Spies + +? Rush.._Beyond_The_Lighted_Stage-x09-Between_Sun_and_Moon-2002_Hartford.mkv +: type: movie + title: Rush Beyond The Lighted Stage + bonus: 9 + bonus_title: Between Sun and Moon + year: 2002 + +? House.Hunters.International.S56E06.720p.hdtv.x264.mp4 +: type: episode + title: House Hunters International + season: 56 + episode: 6 + screen_size: 720p + format: HDTV + video_codec: h264 + +? White.House.Down.2013.1080p.BluRay.DTS-HD.MA.5.1.x264-PublicHD.mkv +: type: movie + title: White House Down + year: 2013 + screen_size: 1080p + format: BluRay + audio_codec: DTS + audio_profile: HDMA + video_codec: h264 + release_group: PublicHD + audio_channels: "5.1" + +? White.House.Down.2013.1080p.BluRay.DTSHD.MA.5.1.x264-PublicHD.mkv +: type: movie + title: White House Down + year: 2013 + screen_size: 1080p + format: BluRay + audio_codec: DTS + audio_profile: HDMA + video_codec: h264 + release_group: PublicHD + audio_channels: "5.1" + +? Hostages.S01E01.Pilot.for.Air.720p.WEB-DL.DD5.1.H.264-NTb.nfo +: type: episode + title: Hostages + episode_title: Pilot for Air + season: 1 + episode: 1 + screen_size: 720p + format: WEB-DL + audio_channels: "5.1" + video_codec: h264 + audio_codec: DolbyDigital + release_group: NTb + +? Despicable.Me.2.2013.1080p.BluRay.x264-VeDeTT.nfo +: type: movie + title: Despicable Me 2 + year: 2013 + screen_size: 1080p + format: BluRay + video_codec: h264 + release_group: VeDeTT + +? Le Cinquieme Commando 1971 SUBFORCED FRENCH DVDRiP XViD AC3 Bandix.mkv +: type: movie + audio_codec: AC3 + format: DVD + release_group: Bandix + subtitle_language: French + title: Le Cinquieme Commando + video_codec: XviD + year: 1971 + +? Le Seigneur des Anneaux - La Communauté de l'Anneau - Version Longue - BDRip.mkv +: type: movie + format: BluRay + title: Le Seigneur des Anneaux + +? La petite bande (Michel Deville - 1983) VF PAL MP4 x264 AAC.mkv +: type: movie + audio_codec: AAC + language: French + title: La petite bande + video_codec: h264 + year: 1983 + other: PAL + +? Retour de Flammes (Gregor Schnitzler 2003) FULL DVD.iso +: type: movie + format: DVD + title: Retour de Flammes + type: movie + year: 2003 + +? A.Common.Title.Special.2014.avi +: type: movie + year: 2014 + title: A Common Title Special + +? A.Common.Title.2014.Special.avi +: type: episode + year: 2014 + title: A Common Title + episode_title: Special + episode_details: Special + +? A.Common.Title.2014.Special.Edition.avi +: type: movie + year: 2014 + title: A Common Title + edition: Special Edition + +? Downton.Abbey.2013.Christmas.Special.HDTV.x264-FoV.mp4 +: type: episode + year: 2013 + title: Downton Abbey + episode_title: Christmas Special + video_codec: h264 + release_group: FoV + format: HDTV + episode_details: Special + +? Doctor_Who_2013_Christmas_Special.The_Time_of_The_Doctor.HD +: type: episode + title: Doctor Who + other: HD + episode_details: Special + episode_title: Christmas Special The Time of The Doctor + year: 2013 + +? Doctor Who 2005 50th Anniversary Special The Day of the Doctor 3.avi +: type: episode + title: Doctor Who + episode_details: Special + episode_title: 50th Anniversary Special The Day of the Doctor 3 + year: 2005 + +? Robot Chicken S06-Born Again Virgin Christmas Special HDTV x264.avi +: type: episode + title: Robot Chicken + format: HDTV + season: 6 + episode_title: Born Again Virgin Christmas Special + video_codec: h264 + episode_details: Special + +? Wicked.Tuna.S03E00.Head.To.Tail.Special.HDTV.x264-YesTV +: type: episode + title: Wicked Tuna + episode_title: Head To Tail Special + release_group: YesTV + season: 3 + episode: 0 + video_codec: h264 + format: HDTV + episode_details: Special + +? The.Voice.UK.S03E12.HDTV.x264-C4TV +: episode: 12 + video_codec: h264 + format: HDTV + title: The Voice + release_group: C4TV + season: 3 + country: United Kingdom + type: episode + +? /tmp/star.trek.9/star.trek.9.mkv +: type: movie + title: star trek 9 + +? star.trek.9.mkv +: type: movie + title: star trek 9 + +? FlexGet.S01E02.TheName.HDTV.xvid +: episode: 2 + format: HDTV + season: 1 + title: FlexGet + episode_title: TheName + type: episode + video_codec: XviD + +? FlexGet.S01E02.TheName.HDTV.xvid +: episode: 2 + format: HDTV + season: 1 + title: FlexGet + episode_title: TheName + type: episode + video_codec: XviD + +? some.series.S03E14.Title.Here.720p +: episode: 14 + screen_size: 720p + season: 3 + title: some series + episode_title: Title Here + type: episode + +? '[the.group] Some.Series.S03E15.Title.Two.720p' +: episode: 15 + release_group: the.group + screen_size: 720p + season: 3 + title: Some Series + episode_title: Title Two + type: episode + +? 'HD 720p: Some series.S03E16.Title.Three' +: episode: 16 + other: HD + screen_size: 720p + season: 3 + title: Some series + episode_title: Title Three + type: episode + +? Something.Season.2.1of4.Ep.Title.HDTV.torrent +: episode_count: 4 + episode: 1 + format: HDTV + season: 2 + title: Something + episode_title: Title + type: episode + container: torrent + +? Show-A (US) - Episode Title S02E09 hdtv +: country: US + episode: 9 + format: HDTV + season: 2 + title: Show-A + type: episode + +? Jack's.Show.S03E01.blah.1080p +: episode: 1 + screen_size: 1080p + season: 3 + title: Jack's Show + episode_title: blah + type: episode + +? FlexGet.epic +: title: FlexGet epic + type: movie + +? FlexGet.Apt.1 +: title: FlexGet Apt 1 + type: movie + +? FlexGet.aptitude +: title: FlexGet aptitude + type: movie + +? FlexGet.Step1 +: title: FlexGet Step1 + type: movie + +? Movies/El Bosque Animado (1987)/El.Bosque.Animado.[Jose.Luis.Cuerda.1987].[Xvid-Dvdrip-720 * 432].avi +: format: DVD + screen_size: 720x432 + title: El Bosque Animado + video_codec: XviD + year: 1987 + type: movie + +? Movies/El Bosque Animado (1987)/El.Bosque.Animado.[Jose.Luis.Cuerda.1987].[Xvid-Dvdrip-720x432].avi +: format: DVD + screen_size: 720x432 + title: El Bosque Animado + video_codec: XviD + year: 1987 + type: movie + +? 2009.shoot.fruit.chan.multi.dvd9.pal +: format: DVD + language: mul + other: PAL + title: shoot fruit chan + type: movie + year: 2009 + +? 2009.shoot.fruit.chan.multi.dvd5.pal +: format: DVD + language: mul + other: PAL + title: shoot fruit chan + type: movie + year: 2009 + +? The.Flash.2014.S01E01.PREAIR.WEBRip.XviD-EVO.avi +: episode: 1 + format: WEBRip + other: Preair + release_group: EVO + season: 1 + title: The Flash + type: episode + video_codec: XviD + year: 2014 + +? Ice.Lake.Rebels.S01E06.Ice.Lake.Games.720p.HDTV.x264-DHD +: episode: 6 + format: HDTV + release_group: DHD + screen_size: 720p + season: 1 + title: Ice Lake Rebels + episode_title: Ice Lake Games + type: episode + video_codec: h264 + +? The League - S06E10 - Epi Sexy.mkv +: episode: 10 + season: 6 + title: The League + episode_title: Epi Sexy + type: episode + +? Stay (2005) [1080p]/Stay.2005.1080p.BluRay.x264.YIFY.mp4 +: format: BluRay + release_group: YIFY + screen_size: 1080p + title: Stay + type: movie + video_codec: h264 + year: 2005 + +? /media/live/A/Anger.Management.S02E82.720p.HDTV.X264-DIMENSION.mkv +: format: HDTV + release_group: DIMENSION + screen_size: 720p + title: Anger Management + type: episode + season: 2 + episode: 82 + video_codec: h264 + +? "[Figmentos] Monster 34 - At the End of Darkness [781219F1].mkv" +: type: episode + release_group: Figmentos + title: Monster + episode: 34 + episode_title: At the End of Darkness + crc32: 781219F1 + +? Game.of.Thrones.S05E07.720p.HDTV-KILLERS.mkv +: type: episode + episode: 7 + format: HDTV + release_group: KILLERS + screen_size: 720p + season: 5 + title: Game of Thrones + +? Game.of.Thrones.S05E07.HDTV.720p-KILLERS.mkv +: type: episode + episode: 7 + format: HDTV + release_group: KILLERS + screen_size: 720p + season: 5 + title: Game of Thrones + +? Parks and Recreation - [04x12] - Ad Campaign.avi +: type: episode + title: Parks and Recreation + season: 4 + episode: 12 + episode_title: Ad Campaign + +? Star Trek Into Darkness (2013)/star.trek.into.darkness.2013.720p.web-dl.h264-publichd.mkv +: type: movie + title: Star Trek Into Darkness + year: 2013 + screen_size: 720p + format: WEB-DL + video_codec: h264 + release_group: publichd + +? /var/medias/series/The Originals/Season 02/The.Originals.S02E15.720p.HDTV.X264-DIMENSION.mkv +: type: episode + title: The Originals + season: 2 + episode: 15 + screen_size: 720p + format: HDTV + video_codec: h264 + release_group: DIMENSION + +? Test.S01E01E07-FooBar-Group.avi +: container: avi + episode: + - 1 + - 7 + episode_title: FooBar-Group # Make sure it doesn't conflict with uuid + mimetype: video/x-msvideo + season: 1 + title: Test + type: episode diff --git a/lib/guessit2/tlds-alpha-by-domain.txt b/lib/guessit2/tlds-alpha-by-domain.txt new file mode 100644 index 000000000..280c794c5 --- /dev/null +++ b/lib/guessit2/tlds-alpha-by-domain.txt @@ -0,0 +1,341 @@ +# Version 2013112900, Last Updated Fri Nov 29 07:07:01 2013 UTC +AC +AD +AE +AERO +AF +AG +AI +AL +AM +AN +AO +AQ +AR +ARPA +AS +ASIA +AT +AU +AW +AX +AZ +BA +BB +BD +BE +BF +BG +BH +BI +BIKE +BIZ +BJ +BM +BN +BO +BR +BS +BT +BV +BW +BY +BZ +CA +CAMERA +CAT +CC +CD +CF +CG +CH +CI +CK +CL +CLOTHING +CM +CN +CO +COM +CONSTRUCTION +CONTRACTORS +COOP +CR +CU +CV +CW +CX +CY +CZ +DE +DIAMONDS +DIRECTORY +DJ +DK +DM +DO +DZ +EC +EDU +EE +EG +ENTERPRISES +EQUIPMENT +ER +ES +ESTATE +ET +EU +FI +FJ +FK +FM +FO +FR +GA +GALLERY +GB +GD +GE +GF +GG +GH +GI +GL +GM +GN +GOV +GP +GQ +GR +GRAPHICS +GS +GT +GU +GURU +GW +GY +HK +HM +HN +HOLDINGS +HR +HT +HU +ID +IE +IL +IM +IN +INFO +INT +IO +IQ +IR +IS +IT +JE +JM +JO +JOBS +JP +KE +KG +KH +KI +KITCHEN +KM +KN +KP +KR +KW +KY +KZ +LA +LAND +LB +LC +LI +LIGHTING +LK +LR +LS +LT +LU +LV +LY +MA +MC +MD +ME +MG +MH +MIL +MK +ML +MM +MN +MO +MOBI +MP +MQ +MR +MS +MT +MU +MUSEUM +MV +MW +MX +MY +MZ +NA +NAME +NC +NE +NET +NF +NG +NI +NL +NO +NP +NR +NU +NZ +OM +ORG +PA +PE +PF +PG +PH +PHOTOGRAPHY +PK +PL +PLUMBING +PM +PN +POST +PR +PRO +PS +PT +PW +PY +QA +RE +RO +RS +RU +RW +SA +SB +SC +SD +SE +SEXY +SG +SH +SI +SINGLES +SJ +SK +SL +SM +SN +SO +SR +ST +SU +SV +SX +SY +SZ +TATTOO +TC +TD +TECHNOLOGY +TEL +TF +TG +TH +TIPS +TJ +TK +TL +TM +TN +TO +TODAY +TP +TR +TRAVEL +TT +TV +TW +TZ +UA +UG +UK +US +UY +UZ +VA +VC +VE +VENTURES +VG +VI +VN +VOYAGE +VU +WF +WS +XN--3E0B707E +XN--45BRJ9C +XN--80AO21A +XN--80ASEHDB +XN--80ASWG +XN--90A3AC +XN--CLCHC0EA0B2G2A9GCD +XN--FIQS8S +XN--FIQZ9S +XN--FPCRJ9C3D +XN--FZC2C9E2C +XN--GECRJ9C +XN--H2BRJ9C +XN--J1AMH +XN--J6W193G +XN--KPRW13D +XN--KPRY57D +XN--L1ACC +XN--LGBBAT1AD8J +XN--MGB9AWBF +XN--MGBA3A4F16A +XN--MGBAAM7A8H +XN--MGBAYH7GPA +XN--MGBBH1A71E +XN--MGBC0A9AZCG +XN--MGBERP4A5D4AR +XN--MGBX4CD0AB +XN--NGBC5AZD +XN--O3CW4H +XN--OGBPF8FL +XN--P1AI +XN--PGBS0DH +XN--Q9JYB4C +XN--S9BRJ9C +XN--UNUP4Y +XN--WGBH1C +XN--WGBL6A +XN--XKC2AL3HYE2A +XN--XKC2DL3A5EE0H +XN--YFRO4I67O +XN--YGBI2AMMX +XXX +YE +YT +ZA +ZM +ZW diff --git a/lib/guessit2/yamlutils.py b/lib/guessit2/yamlutils.py new file mode 100644 index 000000000..2824575da --- /dev/null +++ b/lib/guessit2/yamlutils.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Options +""" +try: + from collections import OrderedDict +except ImportError: # pragma: no-cover + from ordereddict import OrderedDict # pylint:disable=import-error +import babelfish + +import yaml + + +class OrderedDictYAMLLoader(yaml.Loader): + """ + A YAML loader that loads mappings into ordered dictionaries. + From https://gist.github.com/enaeseth/844388 + """ + + def __init__(self, *args, **kwargs): + yaml.Loader.__init__(self, *args, **kwargs) + + self.add_constructor(u'tag:yaml.org,2002:map', type(self).construct_yaml_map) + self.add_constructor(u'tag:yaml.org,2002:omap', type(self).construct_yaml_map) + + def construct_yaml_map(self, node): + data = OrderedDict() + yield data + value = self.construct_mapping(node) + data.update(value) + + def construct_mapping(self, node, deep=False): + if isinstance(node, yaml.MappingNode): + self.flatten_mapping(node) + else: # pragma: no cover + raise yaml.constructor.ConstructorError(None, None, + 'expected a mapping node, but found %s' % node.id, node.start_mark) + + mapping = OrderedDict() + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + try: + hash(key) + except TypeError as exc: # pragma: no cover + raise yaml.constructor.ConstructorError('while constructing a mapping', + node.start_mark, 'found unacceptable key (%s)' + % exc, key_node.start_mark) + value = self.construct_object(value_node, deep=deep) + mapping[key] = value + return mapping + + +class CustomDumper(yaml.SafeDumper): + """ + Custom YAML Dumper. + """ + pass + + +def default_representer(dumper, data): + """Default representer""" + return dumper.represent_str(str(data)) +CustomDumper.add_representer(babelfish.Language, default_representer) +CustomDumper.add_representer(babelfish.Country, default_representer) + + +def ordered_dict_representer(dumper, data): + """OrderedDict representer""" + return dumper.represent_dict(data) +CustomDumper.add_representer(OrderedDict, ordered_dict_representer) diff --git a/lib/rebulk/__init__.py b/lib/rebulk/__init__.py new file mode 100644 index 000000000..93d5e4774 --- /dev/null +++ b/lib/rebulk/__init__.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Define simple search patterns in bulk to perform advanced matching on any string. +""" +# pylint:disable=import-self +from .rebulk import Rebulk +from .rules import Rule, CustomRule, AppendMatch, RemoveMatch, RenameMatch, AppendTags, RemoveTags +from .processors import ConflictSolver, PrivateRemover, POST_PROCESS, PRE_PROCESS +from .pattern import REGEX_AVAILABLE diff --git a/lib/rebulk/__version__.py b/lib/rebulk/__version__.py new file mode 100644 index 000000000..59489449b --- /dev/null +++ b/lib/rebulk/__version__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Version module +""" +# pragma: no cover +__version__ = '0.6.5.dev0' diff --git a/lib/rebulk/debug.py b/lib/rebulk/debug.py new file mode 100644 index 000000000..2384b26ef --- /dev/null +++ b/lib/rebulk/debug.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Debug tools. + +Can be configured by changing values of those variable. + +DEBUG = False +Enable this variable to activate debug features (like defined_at parameters). It can slow down Rebulk + +LOG_LEVEL = 0 +Default log level of generated rebulk logs. +""" + +import inspect +import logging +import os +from collections import namedtuple + + +DEBUG = False +LOG_LEVEL = logging.DEBUG + + +class Frame(namedtuple('Frame', ['lineno', 'package', 'name', 'filename'])): + """ + Stack frame representation. + """ + __slots__ = () + + def __repr__(self): + return "%s#L%s" % (os.path.basename(self.filename), self.lineno) + + +def defined_at(): + """ + Get definition location of a pattern or a match (outside of rebulk package). + :return: + :rtype: + """ + if DEBUG: + frame = inspect.currentframe() + while frame: + try: + if frame.f_globals['__package__'] != __package__: + break + except KeyError: # pragma:no cover + # If package is missing, consider we are in. Workaround for python 3.3. + break + frame = frame.f_back + ret = Frame(frame.f_lineno, + frame.f_globals.get('__package__'), + frame.f_globals.get('__name__'), + frame.f_code.co_filename) + del frame + return ret diff --git a/lib/rebulk/formatters.py b/lib/rebulk/formatters.py new file mode 100644 index 000000000..470469426 --- /dev/null +++ b/lib/rebulk/formatters.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Formatter functions to use in patterns. + +All those function have last argument as match.value (str). +""" + + +def formatters(*chained_formatters): + """ + Chain formatter functions. + :param chained_formatters: + :type chained_formatters: + :return: + :rtype: + """ + def formatters_chain(input_string): # pylint:disable=missing-docstring + for chained_formatter in chained_formatters: + input_string = chained_formatter(input_string) + return input_string + + return formatters_chain diff --git a/lib/rebulk/introspector.py b/lib/rebulk/introspector.py new file mode 100644 index 000000000..64b9836f0 --- /dev/null +++ b/lib/rebulk/introspector.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Introspect rebulk object to retrieve capabilities. +""" +from abc import ABCMeta, abstractproperty +from collections import defaultdict + +import six +from .pattern import StringPattern, RePattern, FunctionalPattern +from .utils import extend_safe + + +@six.add_metaclass(ABCMeta) +class Description(object): + """ + Abstract class for a description. + """ + @abstractproperty + def properties(self): # pragma: no cover + """ + Properties of described object. + :return: all properties that described object can generate grouped by name. + :rtype: dict + """ + pass + + +class PatternDescription(Description): + """ + Description of a pattern. + """ + def __init__(self, pattern): # pylint:disable=too-many-branches + self.pattern = pattern + self._properties = defaultdict(list) + + if pattern.properties: + for key, values in pattern.properties.items(): + extend_safe(self._properties[key], values) + elif 'value' in pattern.match_options: + self._properties[pattern.name].append(pattern.match_options['value']) + elif isinstance(pattern, StringPattern): + extend_safe(self._properties[pattern.name], pattern.patterns) + elif isinstance(pattern, RePattern): + if pattern.name and pattern.name not in pattern.private_names: + extend_safe(self._properties[pattern.name], [None]) + if not pattern.private_children: + for regex_pattern in pattern.patterns: + for group_name, values in regex_pattern.groupindex.items(): + if group_name not in pattern.private_names: + extend_safe(self._properties[group_name], [None]) + elif isinstance(pattern, FunctionalPattern): + if pattern.name and pattern.name not in pattern.private_names: + extend_safe(self._properties[pattern.name], [None]) + + + @property + def properties(self): + """ + Properties for this rule. + :return: + :rtype: dict + """ + return self._properties + + +class RuleDescription(Description): + """ + Description of a rule. + """ + def __init__(self, rule): + self.rule = rule + + self._properties = defaultdict(list) + + if rule.properties: + for key, values in rule.properties.items(): + extend_safe(self._properties[key], values) + + @property + def properties(self): + """ + Properties for this rule. + :return: + :rtype: dict + """ + return self._properties + + +class Introspection(Description): + """ + Introspection results. + """ + def __init__(self, rebulk, context=None): + self.patterns = [PatternDescription(pattern) for pattern in rebulk.effective_patterns(context) + if not pattern.private and not pattern.marker] + self.rules = [RuleDescription(rule) for rule in rebulk.effective_rules(context)] + + @property + def properties(self): + """ + Properties for Introspection results. + :return: + :rtype: + """ + properties = defaultdict(list) + for pattern in self.patterns: + for key, values in pattern.properties.items(): + extend_safe(properties[key], values) + for rule in self.rules: + for key, values in rule.properties.items(): + extend_safe(properties[key], values) + return properties + + +def introspect(rebulk, context=None): + """ + Introspect a Rebulk instance to grab defined objects and properties that can be generated. + :param rebulk: + :type rebulk: Rebulk + :param context: + :type context: + :return: Introspection instance + :rtype: Introspection + """ + return Introspection(rebulk, context) diff --git a/lib/rebulk/loose.py b/lib/rebulk/loose.py new file mode 100644 index 000000000..79e1a1f12 --- /dev/null +++ b/lib/rebulk/loose.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Various utilities functions +""" +import inspect +import sys +from .utils import is_iterable + +if sys.version_info < (3, 4, 0): # pragma: no cover + def _constructor(class_): + """ + Retrieves constructor from given class + + :param class_: + :type class_: class + :return: constructor from given class + :rtype: callable + """ + return class_.__init__ +else: # pragma: no cover + def _constructor(class_): + """ + Retrieves constructor from given class + + :param class_: + :type class_: class + :return: constructor from given class + :rtype: callable + """ + return class_ + + +def call(function, *args, **kwargs): + """ + Call a function or constructor with given args and kwargs after removing args and kwargs that doesn't match + function or constructor signature + + :param function: Function or constructor to call + :type function: callable + :param args: + :type args: + :param kwargs: + :type kwargs: + :return: sale vakye as default function call + :rtype: object + """ + func = constructor_args if inspect.isclass(function) else function_args + call_args, call_kwargs = func(function, *args, **kwargs) + return function(*call_args, **call_kwargs) + + +def function_args(callable_, *args, **kwargs): + """ + Return (args, kwargs) matching the function signature + + :param callable: callable to inspect + :type callable: callable + :param args: + :type args: + :param kwargs: + :type kwargs: + :return: (args, kwargs) matching the function signature + :rtype: tuple + """ + argspec = inspect.getargspec(callable_) # pylint:disable=deprecated-method + return argspec_args(argspec, False, *args, **kwargs) + + +def constructor_args(class_, *args, **kwargs): + """ + Return (args, kwargs) matching the function signature + + :param callable: callable to inspect + :type callable: Callable + :param args: + :type args: + :param kwargs: + :type kwargs: + :return: (args, kwargs) matching the function signature + :rtype: tuple + """ + argspec = inspect.getargspec(_constructor(class_)) # pylint:disable=deprecated-method + return argspec_args(argspec, True, *args, **kwargs) + + +def argspec_args(argspec, constructor, *args, **kwargs): + """ + Return (args, kwargs) matching the argspec object + + :param argspec: argspec to use + :type argspec: argspec + :param constructor: is it a constructor ? + :type constructor: bool + :param args: + :type args: + :param kwargs: + :type kwargs: + :return: (args, kwargs) matching the function signature + :rtype: tuple + """ + if argspec.keywords: + call_kwarg = kwargs + else: + call_kwarg = dict((k, kwargs[k]) for k in kwargs if k in argspec.args) # Python 2.6 dict comprehension + if argspec.varargs: + call_args = args + else: + call_args = args[:len(argspec.args) - (1 if constructor else 0)] + return call_args, call_kwarg + + +def ensure_list(param): + """ + Retrieves a list from given parameter. + + :param param: + :type param: + :return: + :rtype: + """ + if not param: + param = [] + elif not is_iterable(param): + param = [param] + return param + + +def ensure_dict(param, default_value, default_key=None): + """ + Retrieves a dict and a default value from given parameter. + + if parameter is not a dict, it will be promoted as the default value. + + :param param: + :type param: + :param default_value: + :type default_value: + :param default_key: + :type default_key: + :return: + :rtype: + """ + if not param: + param = default_value + if not isinstance(param, dict): + if param: + default_value = param + return {default_key: param}, default_value + return param, default_value + + +def filter_index(collection, predicate=None, index=None): + """ + Filter collection with predicate function and index. + + If index is not found, returns None. + :param collection: + :type collection: collection supporting iteration and slicing + :param predicate: function to filter the collection with + :type predicate: function + :param index: position of a single element to retrieve + :type index: int + :return: filtered list, or single element of filtered list if index is defined + :rtype: list or object + """ + if index is None and isinstance(predicate, int): + index = predicate + predicate = None + if predicate: + collection = collection.__class__(filter(predicate, collection)) + if index is not None: + try: + collection = collection[index] + except IndexError: + collection = None + return collection + + +def set_defaults(defaults, kwargs): + """ + Set defaults from defaults dict to kwargs dict + :param defaults: + :type defaults: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + for key, value in defaults.items(): + if key not in kwargs: + kwargs[key] = value + elif isinstance(value, list) and isinstance(kwargs[key], list): + kwargs[key] = list(value) + kwargs[key] diff --git a/lib/rebulk/match.py b/lib/rebulk/match.py new file mode 100644 index 000000000..86034eb25 --- /dev/null +++ b/lib/rebulk/match.py @@ -0,0 +1,781 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Classes and functions related to matches +""" +from collections import defaultdict, MutableSequence +import copy +try: + from collections import OrderedDict # pylint:disable=ungrouped-imports +except ImportError: # pragma: no cover + from ordereddict import OrderedDict # pylint:disable=import-error +import six + +from .loose import ensure_list, filter_index +from .utils import is_iterable +from .debug import defined_at + + +class MatchesDict(OrderedDict): + """ + A custom dict with matches property. + """ + def __init__(self): + super(MatchesDict, self).__init__() + self.matches = defaultdict(list) + self.values_list = defaultdict(list) + + +class _BaseMatches(MutableSequence): + """ + A custom list[Match] that automatically maintains name, tag, start and end lookup structures. + """ + _base = list + _base_add = _base.append + _base_remove = _base.remove + + def __init__(self, matches=None, input_string=None): + self.input_string = input_string + self._max_end = 0 + self._delegate = [] + self._name_dict = defaultdict(_BaseMatches._base) + self._tag_dict = defaultdict(_BaseMatches._base) + self._start_dict = defaultdict(_BaseMatches._base) + self._end_dict = defaultdict(_BaseMatches._base) + self._index_dict = defaultdict(_BaseMatches._base) + if matches: + self.extend(matches) + + def _add_match(self, match): + """ + Add a match + :param match: + :type match: Match + """ + if match.name: + _BaseMatches._base_add(self._name_dict[match.name], (match)) + for tag in match.tags: + _BaseMatches._base_add(self._tag_dict[tag], match) + _BaseMatches._base_add(self._start_dict[match.start], match) + _BaseMatches._base_add(self._end_dict[match.end], match) + for index in range(*match.span): + _BaseMatches._base_add(self._index_dict[index], match) + if match.end > self._max_end: + self._max_end = match.end + + def _remove_match(self, match): + """ + Remove a match + :param match: + :type match: Match + """ + if match.name: + _BaseMatches._base_remove(self._name_dict[match.name], match) + for tag in match.tags: + _BaseMatches._base_remove(self._tag_dict[tag], match) + _BaseMatches._base_remove(self._start_dict[match.start], match) + _BaseMatches._base_remove(self._end_dict[match.end], match) + for index in range(*match.span): + _BaseMatches._base_remove(self._index_dict[index], match) + if match.end >= self._max_end and not self._end_dict[match.end]: + self._max_end = max(self._end_dict.keys()) + + def previous(self, match, predicate=None, index=None): + """ + Retrieves the nearest previous matches. + :param match: + :type match: + :param predicate: + :type predicate: + :param index: + :type index: int + :return: + :rtype: + """ + current = match.start + while current > -1: + previous_matches = self.ending(current) + if previous_matches: + return filter_index(previous_matches, predicate, index) + current -= 1 + return filter_index(_BaseMatches._base(), predicate, index) + + def next(self, match, predicate=None, index=None): + """ + Retrieves the nearest next matches. + :param match: + :type match: + :param predicate: + :type predicate: + :param index: + :type index: int + :return: + :rtype: + """ + current = match.start + 1 + while current <= self._max_end: + next_matches = self.starting(current) + if next_matches: + return filter_index(next_matches, predicate, index) + current += 1 + return filter_index(_BaseMatches._base(), predicate, index) + + def named(self, name, predicate=None, index=None): + """ + Retrieves a set of Match objects that have the given name. + :param name: + :type name: str + :param predicate: + :type predicate: + :param index: + :type index: int + :return: set of matches + :rtype: set[Match] + """ + return filter_index(_BaseMatches._base(self._name_dict[name]), predicate, index) + + def tagged(self, tag, predicate=None, index=None): + """ + Retrieves a set of Match objects that have the given tag defined. + :param tag: + :type tag: str + :param predicate: + :type predicate: + :param index: + :type index: int + :return: set of matches + :rtype: set[Match] + """ + return filter_index(_BaseMatches._base(self._tag_dict[tag]), predicate, index) + + def starting(self, start, predicate=None, index=None): + """ + Retrieves a set of Match objects that starts at given index. + :param start: the starting index + :type start: int + :param predicate: + :type predicate: + :param index: + :type index: int + :return: set of matches + :rtype: set[Match] + """ + return filter_index(_BaseMatches._base(self._start_dict[start]), predicate, index) + + def ending(self, end, predicate=None, index=None): + """ + Retrieves a set of Match objects that ends at given index. + :param end: the ending index + :type end: int + :param predicate: + :type predicate: + :return: set of matches + :rtype: set[Match] + """ + return filter_index(_BaseMatches._base(self._end_dict[end]), predicate, index) + + def range(self, start=0, end=None, predicate=None, index=None): + """ + Retrieves a set of Match objects that are available in given range, sorted from start to end. + :param start: the starting index + :type start: int + :param end: the ending index + :type end: int + :param predicate: + :type predicate: + :param index: + :type index: int + :return: set of matches + :rtype: set[Match] + """ + if end is None: + end = self.max_end + else: + end = min(self.max_end, end) + ret = _BaseMatches._base() + for match in sorted(self): + if match.start < end and match.end > start: + ret.append(match) + return filter_index(ret, predicate, index) + + def chain_before(self, position, seps, start=0, predicate=None, index=None): + """ + Retrieves a list of chained matches, before position, matching predicate and separated by characters from seps + only. + :param position: + :type position: + :param seps: + :type seps: + :param start: + :type start: + :param predicate: + :type predicate: + :param index: + :type index: + :return: + :rtype: + """ + if hasattr(position, 'start'): + position = position.start + + chain = _BaseMatches._base() + position = min(self.max_end, position) + + for i in reversed(range(start, position)): + index_matches = self.at_index(i) + filtered_matches = [index_match for index_match in index_matches if not predicate or predicate(index_match)] + if filtered_matches: + for chain_match in filtered_matches: + if chain_match not in chain: + chain.append(chain_match) + elif self.input_string[i] not in seps: + break + + return filter_index(chain, predicate, index) + + def chain_after(self, position, seps, end=None, predicate=None, index=None): + """ + Retrieves a list of chained matches, after position, matching predicate and separated by characters from seps + only. + :param position: + :type position: + :param seps: + :type seps: + :param end: + :type end: + :param predicate: + :type predicate: + :param index: + :type index: + :return: + :rtype: + """ + if hasattr(position, 'end'): + position = position.end + chain = _BaseMatches._base() + + if end is None: + end = self.max_end + else: + end = min(self.max_end, end) + + for i in range(position, end): + index_matches = self.at_index(i) + filtered_matches = [index_match for index_match in index_matches if not predicate or predicate(index_match)] + if filtered_matches: + for chain_match in filtered_matches: + if chain_match not in chain: + chain.append(chain_match) + elif self.input_string[i] not in seps: + break + + return filter_index(chain, predicate, index) + + @property + def max_end(self): + """ + Retrieves the maximum index. + :return: + """ + return max(len(self.input_string), self._max_end) if self.input_string else self._max_end + + def _hole_start(self, position, ignore=None): + """ + Retrieves the start of hole index from position. + :param position: + :type position: + :param ignore: + :type ignore: + :return: + :rtype: + """ + for lindex in reversed(range(0, position)): + for starting in self.starting(lindex): + if not ignore or not ignore(starting): + return lindex + return 0 + + def _hole_end(self, position, ignore=None): + """ + Retrieves the end of hole index from position. + :param position: + :type position: + :param ignore: + :type ignore: + :return: + :rtype: + """ + for rindex in range(position, self.max_end): + for starting in self.starting(rindex): + if not ignore or not ignore(starting): + return rindex + return self.max_end + + def holes(self, start=0, end=None, formatter=None, ignore=None, seps=None, predicate=None, index=None): # pylint: disable=too-many-branches,too-many-locals + """ + Retrieves a set of Match objects that are not defined in given range. + :param start: + :type start: + :param end: + :type end: + :param formatter: + :type formatter: + :param ignore: + :type ignore: + :param seps: + :type seps: + :param predicate: + :type predicate: + :param index: + :type index: + :return: + :rtype: + """ + assert self.input_string if seps else True, "input_string must be defined when using seps parameter" + if end is None: + end = self.max_end + else: + end = min(self.max_end, end) + ret = _BaseMatches._base() + hole = False + rindex = start + + loop_start = self._hole_start(start, ignore) + + for rindex in range(loop_start, end): + current = [] + for at_index in self.at_index(rindex): + if not ignore or not ignore(at_index): + current.append(at_index) + + if seps and hole and self.input_string and self.input_string[rindex] in seps: + hole = False + ret[-1].end = rindex + else: + if not current and not hole: + # Open a new hole match + hole = True + ret.append(Match(max(rindex, start), None, input_string=self.input_string, formatter=formatter)) + elif current and hole: + # Close current hole match + hole = False + ret[-1].end = rindex + + if ret and hole: + # go the the next starting element ... + ret[-1].end = min(self._hole_end(rindex, ignore), end) + return filter_index(ret, predicate, index) + + def conflicting(self, match, predicate=None, index=None): + """ + Retrieves a list of ``Match`` objects that conflicts with given match. + :param match: + :type match: + :param predicate: + :type predicate: + :param index: + :type index: + :return: + :rtype: + """ + ret = _BaseMatches._base() + + for i in range(*match.span): + for at_match in self.at_index(i): + if at_match not in ret: + ret.append(at_match) + + ret.remove(match) + + return filter_index(ret, predicate, index) + + def at_match(self, match, predicate=None, index=None): + """ + Retrieves a list of matches from given match. + """ + return self.at_span(match.span, predicate, index) + + def at_span(self, span, predicate=None, index=None): + """ + Retrieves a list of matches from given (start, end) tuple. + """ + starting = self._index_dict[span[0]] + ending = self._index_dict[span[1] - 1] + + merged = list(starting) + for marker in ending: + if marker not in merged: + merged.append(marker) + + return filter_index(merged, predicate, index) + + def at_index(self, pos, predicate=None, index=None): + """ + Retrieves a list of matches from given position + """ + return filter_index(self._index_dict[pos], predicate, index) + + @property + def names(self): + """ + Retrieve all names. + :return: + """ + return self._name_dict.keys() + + @property + def tags(self): + """ + Retrieve all tags. + :return: + """ + return self._tag_dict.keys() + + def to_dict(self, details=False, implicit=False): + """ + Converts matches to a dict object. + :param details if True, values will be complete Match object, else it will be only string Match.value property + :type details: bool + :param implicit if True, multiple values will be set as a list in the dict. Else, only the first value + will be kept. + :type implicit: bool + :return: + :rtype: dict + """ + ret = MatchesDict() + for match in sorted(self): + value = match if details else match.value + ret.matches[match.name].append(match) + if value not in ret.values_list[match.name]: + ret.values_list[match.name].append(value) + if match.name in ret.keys(): + if implicit: + if not isinstance(ret[match.name], list): + if ret[match.name] == value: + continue + ret[match.name] = [ret[match.name]] + else: + if value in ret[match.name]: + continue + ret[match.name].append(value) + else: + ret[match.name] = value + return ret + + if six.PY2: # pragma: no cover + def clear(self): + """ + Python 3 backport + """ + del self[:] + + def __len__(self): + return len(self._delegate) + + def __getitem__(self, index): + ret = self._delegate[index] + if isinstance(ret, list): + return Matches(ret) + return ret + + def __setitem__(self, index, match): + self._delegate[index] = match + if isinstance(index, slice): + for match_item in match: + self._add_match(match_item) + return + self._add_match(match) + + def __delitem__(self, index): + match = self._delegate[index] + del self._delegate[index] + if isinstance(match, list): + # if index is a slice, we has a match list + for match_item in match: + self._remove_match(match_item) + else: + self._remove_match(match) + + def __repr__(self): + return self._delegate.__repr__() + + def insert(self, index, match): + self._delegate.insert(index, match) + self._add_match(match) + + +class Matches(_BaseMatches): + """ + A custom list[Match] contains matches list. + """ + def __init__(self, matches=None, input_string=None): + self.markers = Markers(input_string=input_string) + super(Matches, self).__init__(matches=matches, input_string=input_string) + + def _add_match(self, match): + assert not match.marker, "A marker match should not be added to <Matches> object" + super(Matches, self)._add_match(match) + + +class Markers(_BaseMatches): + """ + A custom list[Match] containing markers list. + """ + def __init__(self, matches=None, input_string=None): + super(Markers, self).__init__(matches=None, input_string=input_string) + + def _add_match(self, match): + assert match.marker, "A non-marker match should not be added to <Markers> object" + super(Markers, self)._add_match(match) + + +class Match(object): + """ + Object storing values related to a single match + """ + def __init__(self, start, end, value=None, name=None, tags=None, marker=None, parent=None, private=None, + pattern=None, input_string=None, formatter=None, conflict_solver=None): + self.start = start + self.end = end + self.name = name + self._value = value + self.tags = ensure_list(tags) + self.marker = marker + self.parent = parent + self.input_string = input_string + self.formatter = formatter + self.pattern = pattern + self.private = private + self.conflict_solver = conflict_solver + self.children = [] + self._raw_start = None + self._raw_end = None + self.defined_at = pattern.defined_at if pattern else defined_at() + + @property + def span(self): + """ + 2-tuple with start and end indices of the match + """ + return self.start, self.end + + @property + def value(self): + """ + Get the value of the match, using formatter if defined. + :return: + :rtype: + """ + if self._value: + return self._value + if self.formatter: + return self.formatter(self.raw) + return self.raw + + @value.setter + def value(self, value): + """ + Set the value (hardcode) + :param value: + :type value: + :return: + :rtype: + """ + self._value = value # pylint: disable=attribute-defined-outside-init + + @property + def names(self): + """ + Get all names of children + :return: + :rtype: + """ + if not self.children: + return set([self.name]) + else: + ret = set() + for child in self.children: + for name in child.names: + ret.add(name) + return ret + + @property + def raw_start(self): + """ + start index of raw value + :return: + :rtype: + """ + if self._raw_start is None: + return self.start + return self._raw_start + + @raw_start.setter + def raw_start(self, value): + """ + Set start index of raw value + :return: + :rtype: + """ + self._raw_start = value + + @property + def raw_end(self): + """ + end index of raw value + :return: + :rtype: + """ + if self._raw_end is None: + return self.end + return self._raw_end + + @raw_end.setter + def raw_end(self, value): + """ + Set end index of raw value + :return: + :rtype: + """ + self._raw_end = value + + @property + def raw(self): + """ + Get the raw value of the match, without using hardcoded value nor formatter. + :return: + :rtype: + """ + if self.input_string: + return self.input_string[self.raw_start:self.raw_end] + return None + + @property + def initiator(self): + """ + Retrieve the initiator parent of a match + :param match: + :type match: + :return: + :rtype: + """ + match = self + while match.parent: + match = match.parent + return match + + def crop(self, crops, predicate=None, index=None): + """ + crop the match with given Match objects or spans tuples + :param crops: + :type crops: list or object + :return: a list of Match objects + :rtype: list[Match] + """ + if not is_iterable(crops) or len(crops) == 2 and isinstance(crops[0], int): + crops = [crops] + initial = copy.deepcopy(self) + ret = [initial] + for crop in crops: + if hasattr(crop, 'span'): + start, end = crop.span + else: + start, end = crop + for current in list(ret): + if start <= current.start and end >= current.end: + # self is included in crop, remove current ... + ret.remove(current) + elif start >= current.start and end <= current.end: + # crop is included in self, split current ... + right = copy.deepcopy(current) + current.end = start + if len(current) <= 0: + ret.remove(current) + right.start = end + if len(right) > 0: + ret.append(right) + elif end <= current.end and end > current.start: + current.start = end + elif start >= current.start and start < current.end: + current.end = start + return filter_index(ret, predicate, index) + + def split(self, seps, predicate=None, index=None): + """ + Split this match in multiple matches using given separators. + :param seps: + :type seps: string containing separator characters + :return: list of new Match objects + :rtype: list + """ + split_match = copy.deepcopy(self) + current_match = split_match + ret = [] + + for i in range(0, len(self.raw)): + if self.raw[i] in seps: + if not split_match: + split_match = copy.deepcopy(current_match) + current_match.end = self.start + i + + else: + if split_match: + split_match.start = self.start + i + current_match = split_match + ret.append(split_match) + split_match = None + + return filter_index(ret, predicate, index) + + def __len__(self): + return self.end - self.start + + def __hash__(self): + return hash(Match) + hash(self.start) + hash(self.end) + hash(self.value) + + def __eq__(self, other): + if isinstance(other, Match): + return self.span == other.span and self.value == other.value and self.name == other.name and \ + self.parent == other.parent + return NotImplemented + + def __ne__(self, other): + if isinstance(other, Match): + return self.span != other.span or self.value != other.value or self.name != other.name or \ + self.parent != other.parent + return NotImplemented + + def __lt__(self, other): + if isinstance(other, Match): + return self.span < other.span + return NotImplemented + + def __gt__(self, other): + if isinstance(other, Match): + return self.span > other.span + return NotImplemented + + def __le__(self, other): + if isinstance(other, Match): + return self.span <= other.span + return NotImplemented + + def __ge__(self, other): + if isinstance(other, Match): + return self.span >= other.span + return NotImplemented + + def __repr__(self): + flags = "" + name = "" + tags = "" + defined = "" + if self.private: + flags += '+private' + if self.name: + name = "+name=" + self.name + if self.tags: + tags = "+tags=" + six.text_type(self.tags) + if self.defined_at: + defined += "@" + six.text_type(self.defined_at) + return "<%s:%s%s%s%s%s>" % (self.value, self.span, flags, name, tags, defined) diff --git a/lib/rebulk/pattern.py b/lib/rebulk/pattern.py new file mode 100644 index 000000000..e789cd60d --- /dev/null +++ b/lib/rebulk/pattern.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Abstract pattern class definition along with various implementations (regexp, string, functional) +""" +# pylint: disable=super-init-not-called,wrong-import-position + +from abc import ABCMeta, abstractmethod, abstractproperty +REGEX_AVAILABLE = None +try: + import regex as re + REGEX_AVAILABLE = True +except ImportError: # pragma: no cover + import re #pylint:disable=wrong-import-order + REGEX_AVAILABLE = False + +import six + +from .match import Match +from .utils import find_all, is_iterable +from .loose import call, ensure_list, ensure_dict +from . import debug + + +@six.add_metaclass(ABCMeta) +class Pattern(object): + """ + Definition of a particular pattern to search for. + """ + + def __init__(self, name=None, tags=None, formatter=None, validator=None, children=False, every=False, + private_parent=False, private_children=False, private=False, private_names=None, marker=False, + format_all=False, validate_all=False, disabled=lambda context: False, log_level=None, properties=None): + """ + :param name: Name of this pattern + :type name: str + :param tags: List of tags related to this pattern + :type tags: list[str] + :param formatter: dict (name, func) of formatter to use with this pattern. name is the match name to support, + and func a function(input_string) that returns the formatted string. A single formatter function can also be + passed as a shortcut for {None: formatter}. The returned formatted string with be set in Match.value property. + :type formatter: dict[str, func] || func + :param validator: dict (name, func) of validator to use with this pattern. name is the match name to support, + and func a function(match) that returns the a boolean. A single validator function can also be + passed as a shortcut for {None: validator}. If return value is False, match will be ignored. + :param children: generates children instead of parent + :type children: bool + :param every: generates both parent and children. + :type every: bool + :param private: flag this pattern as beeing private. + :type private: bool + :param private_parent: force return of parent and flag parent matches as private. + :type private_parent: bool + :param private_children: force return of children and flag children matches as private. + :type private_children: bool + :param private_names: force return of named matches as private. + :type private_names: bool + :param marker: flag this pattern as beeing a marker. + :type private: bool + :param format_all if True, pattern will format every match in the hierarchy (even match not yield). + :type format_all: bool + :param validate_all if True, pattern will validate every match in the hierarchy (even match not yield). + :type validate_all: bool + :param disabled: if True, this pattern is disabled. Can also be a function(context). + :type disabled: bool|function + :param log_lvl: Log level associated to this pattern + :type log_lvl: int + """ + # pylint:disable=too-many-locals + self.name = name + self.tags = ensure_list(tags) + self.formatters, self._default_formatter = ensure_dict(formatter, lambda x: x) + self.validators, self._default_validator = ensure_dict(validator, lambda match: True) + self.every = every + self.children = children + self.private = private + self.private_names = private_names if private_names else [] + self.private_parent = private_parent + self.private_children = private_children + self.marker = marker + self.format_all = format_all + self.validate_all = validate_all + if not callable(disabled): + self.disabled = lambda context: disabled + else: + self.disabled = disabled + self._log_level = log_level + self._properties = properties + self.defined_at = debug.defined_at() + + @property + def log_level(self): + """ + Log level for this pattern. + :return: + :rtype: + """ + return self._log_level if self._log_level is not None else debug.LOG_LEVEL + + def _yield_children(self, match): + """ + Does this mat + :param match: + :type match: + :return: + :rtype: + """ + return match.children and (self.children or self.every) + + def _yield_parent(self): + """ + Does this mat + :param match: + :type match: + :return: + :rtype: + """ + return not self.children or self.every + + def _match_parent(self, match, yield_parent): + """ + Handle a parent match + :param match: + :type match: + :param yield_parent: + :type yield_parent: + :return: + :rtype: + """ + if yield_parent or self.format_all: + match.formatter = self.formatters.get(match.name, + self.formatters.get('__parent__', self._default_formatter)) + if yield_parent or self.validate_all: + validator = self.validators.get(match.name, self.validators.get('__parent__', self._default_validator)) + if not validator(match): + return False + return True + + def _match_child(self, child, yield_children): + """ + Handle a children match + :param child: + :type child: + :param yield_children: + :type yield_children: + :return: + :rtype: + """ + if yield_children or self.format_all: + child.formatter = self.formatters.get(child.name, + self.formatters.get('__children__', self._default_formatter)) + if yield_children or self.validate_all: + validator = self.validators.get(child.name, self.validators.get('__children__', self._default_validator)) + if not validator(child): + return False + return True + + def matches(self, input_string, context=None): + """ + Computes all matches for a given input + + :param input_string: the string to parse + :type input_string: str + :param context: the context + :type context: dict + :return: matches based on input_string for this pattern + :rtype: iterator[Match] + """ + + ret = [] + for pattern in self.patterns: + yield_parent = self._yield_parent() + for match in self._match(pattern, input_string, context): + yield_children = self._yield_children(match) + if not self._match_parent(match, yield_parent): + continue + validated = True + for child in match.children: + if not self._match_child(child, yield_children): + validated = False + break + if validated: + if self.private_parent: + match.private = True + if self.private_children: + for child in match.children: + child.private = True + if yield_parent or self.private_parent: + ret.append(match) + if yield_children or self.private_children: + for child in match.children: + ret.append(child) + self._matches_privatize(ret) + return ret + + def _matches_privatize(self, matches): + """ + Mark matches included in private_names with private flag. + :param matches: + :type matches: + :return: + :rtype: + """ + if self.private_names: + for child in matches: + if child.name in self.private_names: + child.private = True + + @abstractproperty + def patterns(self): # pragma: no cover + """ + List of base patterns defined + + :return: A list of base patterns + :rtype: list + """ + pass + + @property + def properties(self): + """ + Properties names and values that can ben retrieved by this pattern. + :return: + :rtype: + """ + if self._properties: + return self._properties + return {} + + @abstractproperty + def match_options(self): # pragma: no cover + """ + dict of default options for generated Match objects + + :return: **options to pass to Match constructor + :rtype: dict + """ + pass + + @abstractmethod + def _match(self, pattern, input_string, context=None): # pragma: no cover + """ + Computes all matches for a given pattern and input + + :param pattern: the pattern to use + :param input_string: the string to parse + :type input_string: str + :param context: the context + :type context: dict + :return: matches based on input_string for this pattern + :rtype: iterator[Match] + """ + pass + + def __repr__(self): + defined = "" + if self.defined_at: + defined = "@" + six.text_type(self.defined_at) + return "<%s%s:%s>" % (self.__class__.__name__, defined, self.patterns) + + +class StringPattern(Pattern): + """ + Definition of one or many strings to search for. + """ + + def __init__(self, *patterns, **kwargs): + call(super(StringPattern, self).__init__, **kwargs) + self._patterns = patterns + self._kwargs = kwargs + self._match_kwargs = _filter_match_kwargs(kwargs) + + @property + def patterns(self): + return self._patterns + + @property + def match_options(self): + return self._match_kwargs + + def _match(self, pattern, input_string, context=None): + for index in call(find_all, input_string, pattern, **self._kwargs): + yield call(Match, index, index + len(pattern), pattern=self, input_string=input_string, + **self._match_kwargs) + + +class RePattern(Pattern): + """ + Definition of one or many regular expression pattern to search for. + """ + + def __init__(self, *patterns, **kwargs): + call(super(RePattern, self).__init__, **kwargs) + self.repeated_captures = REGEX_AVAILABLE + if 'repeated_captures' in kwargs: + self.repeated_captures = kwargs.get('repeated_captures') + if self.repeated_captures and not REGEX_AVAILABLE: # pragma: no cover + raise NotImplementedError("repeated_capture is available only with regex module.") + self.abbreviations = kwargs.get('abbreviations', []) + self._kwargs = kwargs + self._match_kwargs = _filter_match_kwargs(kwargs) + self._children_match_kwargs = _filter_match_kwargs(kwargs, children=True) + self._patterns = [] + for pattern in patterns: + if isinstance(pattern, six.string_types): + if self.abbreviations and pattern: + for key, replacement in self.abbreviations: + pattern = pattern.replace(key, replacement) + pattern = call(re.compile, pattern, **self._kwargs) + elif isinstance(pattern, dict): + if self.abbreviations and 'pattern' in pattern: + for key, replacement in self.abbreviations: + pattern['pattern'] = pattern['pattern'].replace(key, replacement) + pattern = re.compile(**pattern) + elif hasattr(pattern, '__iter__'): + pattern = re.compile(*pattern) + self._patterns.append(pattern) + + @property + def patterns(self): + return self._patterns + + @property + def match_options(self): + return self._match_kwargs + + def _match(self, pattern, input_string, context=None): + names = dict((v, k) for k, v in pattern.groupindex.items()) + for match_object in pattern.finditer(input_string): + start = match_object.start() + end = match_object.end() + main_match = call(Match, start, end, pattern=self, input_string=input_string, **self._match_kwargs) + + if pattern.groups: + for i in range(1, pattern.groups + 1): + name = names.get(i, main_match.name) + if self.repeated_captures: + for start, end in match_object.spans(i): + child_match = call(Match, start, end, name=name, parent=main_match, pattern=self, + input_string=input_string, **self._children_match_kwargs) + main_match.children.append(child_match) + else: + start, end = match_object.span(i) + child_match = call(Match, start, end, name=name, parent=main_match, pattern=self, + input_string=input_string, **self._children_match_kwargs) + main_match.children.append(child_match) + + yield main_match + + +class FunctionalPattern(Pattern): + """ + Definition of one or many functional pattern to search for. + """ + + def __init__(self, *patterns, **kwargs): + call(super(FunctionalPattern, self).__init__, **kwargs) + self._patterns = patterns + self._kwargs = kwargs + self._match_kwargs = _filter_match_kwargs(kwargs) + + @property + def patterns(self): + return self._patterns + + @property + def match_options(self): + return self._match_kwargs + + def _match(self, pattern, input_string, context=None): + ret = call(pattern, input_string, context, **self._kwargs) + if ret: + if not is_iterable(ret) or isinstance(ret, dict) \ + or (is_iterable(ret) and hasattr(ret, '__getitem__') and isinstance(ret[0], int)): + args_iterable = [ret] + else: + args_iterable = ret + for args in args_iterable: + if isinstance(args, dict): + options = args + options.pop('input_string', None) + options.pop('pattern', None) + if self._match_kwargs: + options = self._match_kwargs.copy() + options.update(args) + yield call(Match, pattern=self, input_string=input_string, **options) + else: + kwargs = self._match_kwargs + if isinstance(args[-1], dict): + kwargs = dict(kwargs) + kwargs.update(args[-1]) + args = args[:-1] + yield call(Match, *args, pattern=self, input_string=input_string, **kwargs) + + +def _filter_match_kwargs(kwargs, children=False): + """ + Filters out kwargs for Match construction + + :param kwargs: + :type kwargs: dict + :param children: + :type children: Flag to filter children matches + :return: A filtered dict + :rtype: dict + """ + kwargs = kwargs.copy() + for key in ('pattern', 'start', 'end', 'parent', 'formatter'): + if key in kwargs: + del kwargs[key] + if children: + for key in ('name',): + if key in kwargs: + del kwargs[key] + return kwargs diff --git a/lib/rebulk/processors.py b/lib/rebulk/processors.py new file mode 100644 index 000000000..b0a69fc8e --- /dev/null +++ b/lib/rebulk/processors.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Processor functions +""" +from logging import getLogger + +from .utils import IdentitySet + +from .rules import Rule, RemoveMatch + +log = getLogger(__name__).log + +DEFAULT = '__default__' + +POST_PROCESS = -2048 +PRE_PROCESS = 2048 + + +def _default_conflict_solver(match, conflicting_match): + """ + Default conflict solver for matches, shorter matches if they conflicts with longer ones + + :param conflicting_match: + :type conflicting_match: + :param match: + :type match: + :return: + :rtype: + """ + if len(conflicting_match.initiator) < len(match.initiator): + return conflicting_match + elif len(match.initiator) < len(conflicting_match.initiator): + return match + return None + + +class ConflictSolver(Rule): + """ + Remove conflicting matches. + """ + priority = PRE_PROCESS + + consequence = RemoveMatch + + @property + def default_conflict_solver(self): # pylint:disable=no-self-use + """ + Default conflict solver to use. + """ + return _default_conflict_solver + + def when(self, matches, context): + to_remove_matches = IdentitySet() + for match in filter(lambda match: not match.private, matches): + conflicting_matches = matches.conflicting(match) + + if conflicting_matches: + # keep the match only if it's the longest + for conflicting_match in filter(lambda match: not match.private, conflicting_matches): + reverse = False + conflict_solvers = [(self.default_conflict_solver, False)] + + if match.conflict_solver: + conflict_solvers.append((match.conflict_solver, False)) + if conflicting_match.conflict_solver: + conflict_solvers.append((conflicting_match.conflict_solver, True)) + + for conflict_solver, reverse in reversed(conflict_solvers): + if reverse: + to_remove = conflict_solver(conflicting_match, match) + else: + to_remove = conflict_solver(match, conflicting_match) + if to_remove == DEFAULT: + continue + if to_remove and to_remove not in to_remove_matches: + to_remove_matches.add(to_remove) + break + return to_remove_matches + + +class PrivateRemover(Rule): + """ + Removes private matches rule. + """ + priority = POST_PROCESS + + consequence = RemoveMatch + + def when(self, matches, context): + return [match for match in matches if match.private] + diff --git a/lib/rebulk/rebulk.py b/lib/rebulk/rebulk.py new file mode 100644 index 000000000..dde3699d6 --- /dev/null +++ b/lib/rebulk/rebulk.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Entry point functions and classes for Rebulk +""" +from logging import getLogger + +from .match import Matches + +from .pattern import RePattern, StringPattern, FunctionalPattern + +from .processors import ConflictSolver, PrivateRemover +from .loose import set_defaults +from .utils import extend_safe +from .rules import Rules + +log = getLogger(__name__).log + + +class Rebulk(object): + r""" + Regular expression, string and function based patterns are declared in a ``Rebulk`` object. It use a fluent API to + chain ``string``, ``regex``, and ``functional`` methods to define various patterns types. + + .. code-block:: python + + >>> from rebulk import Rebulk + >>> bulk = Rebulk().string('brown').regex(r'qu\w+').functional(lambda s: (20, 25)) + + When ``Rebulk`` object is fully configured, you can call ``matches`` method with an input string to retrieve all + ``Match`` objects found by registered pattern. + + .. code-block:: python + + >>> bulk.matches("The quick brown fox jumps over the lazy dog") + [<brown:(10, 15)>, <quick:(4, 9)>, <jumps:(20, 25)>] + + If multiple ``Match`` objects are found at the same position, only the longer one is kept. + + .. code-block:: python + + >>> bulk = Rebulk().string('lakers').string('la') + >>> bulk.matches("the lakers are from la") + [<lakers:(4, 10)>, <la:(20, 22)>] + """ + # pylint:disable=protected-access + + def __init__(self, disabled=lambda context: False, default_rules=True): + """ + Creates a new Rebulk object. + :param disabled: if True, this pattern is disabled. Can also be a function(context). + :type disabled: bool|function + :param default_rules: use default rules + :type default_rules: + :return: + :rtype: + """ + if not callable(disabled): + self.disabled = lambda context: disabled + else: + self.disabled = disabled + self._patterns = [] + self._rules = Rules() + if default_rules: + self.rules(ConflictSolver, PrivateRemover) + self._defaults = {} + self._regex_defaults = {} + self._string_defaults = {} + self._functional_defaults = {} + self._rebulks = [] + + def pattern(self, *pattern): + """ + Add patterns objects + + :param pattern: + :type pattern: rebulk.pattern.Pattern + :return: self + :rtype: Rebulk + """ + self._patterns.extend(pattern) + return self + + def defaults(self, **kwargs): + """ + Define default keyword arguments for all patterns + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + self._defaults = kwargs + return self + + def regex_defaults(self, **kwargs): + """ + Define default keyword arguments for functional patterns. + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + self._regex_defaults = kwargs + return self + + def regex(self, *pattern, **kwargs): + """ + Add re pattern + + :param pattern: + :type pattern: + :return: self + :rtype: Rebulk + """ + set_defaults(self._regex_defaults, kwargs) + set_defaults(self._defaults, kwargs) + self.pattern(RePattern(*pattern, **kwargs)) + return self + + def string_defaults(self, **kwargs): + """ + Define default keyword arguments for string patterns. + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + self._string_defaults = kwargs + return self + + def string(self, *pattern, **kwargs): + """ + Add string pattern + + :param pattern: + :type pattern: + :return: self + :rtype: Rebulk + """ + set_defaults(self._string_defaults, kwargs) + set_defaults(self._defaults, kwargs) + self.pattern(StringPattern(*pattern, **kwargs)) + return self + + def functional_defaults(self, **kwargs): + """ + Define default keyword arguments for functional patterns. + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + self._functional_defaults = kwargs + return self + + def functional(self, *pattern, **kwargs): + """ + Add functional pattern + + :param pattern: + :type pattern: + :return: self + :rtype: Rebulk + """ + set_defaults(self._functional_defaults, kwargs) + set_defaults(self._defaults, kwargs) + self.pattern(FunctionalPattern(*pattern, **kwargs)) + return self + + def rules(self, *rules): + """ + Add rules as a module, class or instance. + :param rules: + :type rules: list[Rule] + :return: + """ + self._rules.load(*rules) + return self + + def rebulk(self, *rebulks): + """ + Add a children rebulk object + :param rebulks: + :type rebulks: Rebulk + :return: + """ + self._rebulks.extend(rebulks) + return self + + def matches(self, string, context=None): + """ + Search for all matches with current configuration against input_string + :param string: string to search into + :type string: str + :param context: context to use + :type context: dict + :return: A custom list of matches + :rtype: Matches + """ + matches = Matches(input_string=string) + if context is None: + context = {} + + self._matches_patterns(matches, context) + + self._execute_rules(matches, context) + + return matches + + def effective_rules(self, context=None): + """ + Get effective rules for this rebulk object and its children. + :param context: + :type context: + :return: + :rtype: + """ + rules = Rules() + rules.extend(self._rules) + for rebulk in self._rebulks: + if not rebulk.disabled(context): + extend_safe(rules, rebulk._rules) + return rules + + def _execute_rules(self, matches, context): + """ + Execute rules for this rebulk and children. + :param matches: + :type matches: + :param context: + :type context: + :return: + :rtype: + """ + if not self.disabled(context): + rules = self.effective_rules(context) + rules.execute_all_rules(matches, context) + + def effective_patterns(self, context=None): + """ + Get effective patterns for this rebulk object and its children. + :param context: + :type context: + :return: + :rtype: + """ + patterns = list(self._patterns) + for rebulk in self._rebulks: + if not rebulk.disabled(context): + extend_safe(patterns, rebulk._patterns) + return patterns + + def _matches_patterns(self, matches, context): + """ + Search for all matches with current paterns agains input_string + :param matches: matches list + :type matches: Matches + :param context: context to use + :type context: dict + :return: + :rtype: + """ + if not self.disabled(context): + patterns = self.effective_patterns(context) + for pattern in patterns: + if not pattern.disabled(context): + pattern_matches = pattern.matches(matches.input_string, context) + if pattern_matches: + log(pattern.log_level, "Pattern has %s match(es). (%s)", len(pattern_matches), pattern) + else: + pass + # log(pattern.log_level, "Pattern doesn't match. (%s)" % (pattern,)) + for match in pattern_matches: + if match.marker: + log(pattern.log_level, "Marker found. (%s)", match) + matches.markers.append(match) + else: + log(pattern.log_level, "Match found. (%s)", match) + matches.append(match) + else: + log(pattern.log_level, "Pattern is disabled. (%s)", pattern) diff --git a/lib/rebulk/rules.py b/lib/rebulk/rules.py new file mode 100644 index 000000000..c318cef93 --- /dev/null +++ b/lib/rebulk/rules.py @@ -0,0 +1,378 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Abstract rule class definition and rule engine implementation +""" +from abc import ABCMeta, abstractmethod +import inspect +from itertools import groupby +from logging import getLogger + +import six +from .utils import is_iterable + +from .toposort import toposort + +from . import debug + +log = getLogger(__name__).log + + +@six.add_metaclass(ABCMeta) +class Consequence(object): + """ + Definition of a consequence to apply. + """ + @abstractmethod + def then(self, matches, when_response, context): # pragma: no cover + """ + Action implementation. + + :param matches: + :type matches: rebulk.match.Matches + :param context: + :type context: + :param when_response: return object from when call. + :type when_response: object + :return: True if the action was runned, False if it wasn't. + :rtype: bool + """ + pass + + +@six.add_metaclass(ABCMeta) +class Condition(object): + """ + Definition of a condition to check. + """ + @abstractmethod + def when(self, matches, context): # pragma: no cover + """ + Condition implementation. + + :param matches: + :type matches: rebulk.match.Matches + :param context: + :type context: + :return: truthy if rule should be triggered and execute then action, falsy if it should not. + :rtype: object + """ + pass + + +@six.add_metaclass(ABCMeta) +class CustomRule(Condition, Consequence): + """ + Definition of a rule to apply + """ + # pylint: disable=no-self-use, unused-argument, abstract-method + priority = 0 + name = None + dependency = None + properties = {} + + def __init__(self, log_level=None): + self.defined_at = debug.defined_at() + if log_level is None and not hasattr(self, 'log_level'): + self.log_level = debug.LOG_LEVEL + + def enabled(self, context): + """ + Disable rule. + + :param context: + :type context: + :return: True if rule is enabled, False if disabled + :rtype: bool + """ + return True + + def __lt__(self, other): + return self.priority > other.priority + + def __repr__(self): + defined = "" + if self.defined_at: + defined = "@" + six.text_type(self.defined_at) + return "<%s%s>" % (self.name if self.name else self.__class__.__name__, defined) + + def __eq__(self, other): + return self.__class__ == other.__class__ + + def __hash__(self): + return hash(self.__class__) + + +class Rule(CustomRule): + """ + Definition of a rule to apply + """ + # pylint:disable=abstract-method + consequence = None + + def then(self, matches, when_response, context): + assert self.consequence + if is_iterable(self.consequence): + if not is_iterable(when_response): + when_response = [when_response] + iterator = iter(when_response) + for cons in self.consequence: #pylint: disable=not-an-iterable + if inspect.isclass(cons): + cons = cons() + cons.then(matches, next(iterator), context) + else: + cons = self.consequence + if inspect.isclass(cons): + cons = cons() # pylint:disable=not-callable + cons.then(matches, when_response, context) + + +class RemoveMatch(Consequence): # pylint: disable=abstract-method + """ + Remove matches returned by then + """ + def then(self, matches, when_response, context): + if is_iterable(when_response): + ret = [] + when_response = list(when_response) + for match in when_response: + if match in matches: + matches.remove(match) + ret.append(match) + return ret + else: + if when_response in matches: + matches.remove(when_response) + return when_response + + +class AppendMatch(Consequence): # pylint: disable=abstract-method + """ + Append matches returned by then + """ + def __init__(self, match_name=None): + self.match_name = match_name + + def then(self, matches, when_response, context): + if is_iterable(when_response): + ret = [] + when_response = list(when_response) + for match in when_response: + if match not in matches: + if self.match_name: + match.name = self.match_name + matches.append(match) + ret.append(match) + return ret + else: + if self.match_name: + when_response.name = self.match_name + if when_response not in matches: + matches.append(when_response) + return when_response + + +class RenameMatch(Consequence): # pylint: disable=abstract-method + """ + Rename matches returned by then + """ + def __init__(self, match_name): + self.match_name = match_name + self.remove = RemoveMatch() + self.append = AppendMatch() + + def then(self, matches, when_response, context): + removed = self.remove.then(matches, when_response, context) + if is_iterable(removed): + removed = list(removed) + for match in removed: + match.name = self.match_name + elif removed: + removed.name = self.match_name + if removed: + self.append.then(matches, removed, context) + + +class AppendTags(Consequence): # pylint: disable=abstract-method + """ + Add tags to returned matches + """ + def __init__(self, tags): + self.tags = tags + self.remove = RemoveMatch() + self.append = AppendMatch() + + def then(self, matches, when_response, context): + removed = self.remove.then(matches, when_response, context) + if is_iterable(removed): + removed = list(removed) + for match in removed: + match.tags.extend(self.tags) + elif removed: + removed.tags.extend(self.tags) # pylint: disable=no-member + if removed: + self.append.then(matches, removed, context) + + +class RemoveTags(Consequence): # pylint: disable=abstract-method + """ + Remove tags from returned matches + """ + def __init__(self, tags): + self.tags = tags + self.remove = RemoveMatch() + self.append = AppendMatch() + + def then(self, matches, when_response, context): + removed = self.remove.then(matches, when_response, context) + if is_iterable(removed): + removed = list(removed) + for match in removed: + for tag in self.tags: + if tag in match.tags: + match.tags.remove(tag) + elif removed: + for tag in self.tags: + if tag in removed.tags: # pylint: disable=no-member + removed.tags.remove(tag) # pylint: disable=no-member + if removed: + self.append.then(matches, removed, context) + + +class Rules(list): + """ + list of rules ready to execute. + """ + + def __init__(self, *rules): + super(Rules, self).__init__() + self.load(*rules) + + def load(self, *rules): + """ + Load rules from a Rule module, class or instance + + :param rules: + :type rules: + :return: + :rtype: + """ + for rule in rules: + if inspect.ismodule(rule): + self.load_module(rule) + elif inspect.isclass(rule): + self.load_class(rule) + else: + self.append(rule) + + def load_module(self, module): + """ + Load a rules module + + :param module: + :type module: + :return: + :rtype: + """ + # pylint: disable=unused-variable + for name, obj in inspect.getmembers(module, + lambda member: hasattr(member, '__module__') + and member.__module__ == module.__name__ + and inspect.isclass): + self.load_class(obj) + + def load_class(self, class_): + """ + Load a Rule class. + + :param class_: + :type class_: + :return: + :rtype: + """ + self.append(class_()) + + def execute_all_rules(self, matches, context): + """ + Execute all rules from this rules list. All when condition with same priority will be performed before + calling then actions. + + :param matches: + :type matches: + :param context: + :type context: + :return: + :rtype: + """ + ret = [] + for priority, priority_rules in groupby(sorted(self), lambda rule: rule.priority): + sorted_rules = toposort_rules(list(priority_rules)) # Group by dependency graph toposort + for rules_group in sorted_rules: + rules_group = list(sorted(rules_group, key=self.index)) # Sort rules group based on initial ordering. + group_log_level = None + for rule in rules_group: + if group_log_level is None or group_log_level < rule.log_level: + group_log_level = rule.log_level + log(group_log_level, "%s independent rule(s) at priority %s.", len(rules_group), priority) + for rule in rules_group: + when_response = execute_rule(rule, matches, context) + if when_response is not None: + ret.append((rule, when_response)) + + return ret + + +def execute_rule(rule, matches, context): + """ + Execute the given rule. + :param rule: + :type rule: + :param matches: + :type matches: + :param context: + :type context: + :return: + :rtype: + """ + if rule.enabled(context): + log(rule.log_level, "Checking rule condition: %s", rule) + when_response = rule.when(matches, context) + if when_response: + log(rule.log_level, "Rule was triggered: %s", when_response) + log(rule.log_level, "Running rule consequence: %s %s", rule, when_response) + rule.then(matches, when_response, context) + return when_response + else: + log(rule.log_level, "Rule is disabled: %s", rule) + +def toposort_rules(rules): + """ + Sort given rules using toposort with dependency parameter. + :param rules: + :type rules: + :return: + :rtype: + """ + graph = {} + class_dict = {} + for rule in rules: + if rule.__class__ in class_dict: + raise ValueError("Duplicate class rules are not allowed: %s" % rule.__class__) + class_dict[rule.__class__] = rule + for rule in rules: + if not is_iterable(rule.dependency) and rule.dependency: + rule_dependencies = [rule.dependency] + else: + rule_dependencies = rule.dependency + dependencies = set() + if rule_dependencies: + for dependency in rule_dependencies: + if inspect.isclass(dependency): + dependency = class_dict.get(dependency) + if dependency: + dependencies.add(dependency) + graph[rule] = dependencies + return toposort(graph) + + + diff --git a/lib/rebulk/test/__init__.py b/lib/rebulk/test/__init__.py new file mode 100644 index 000000000..0ab48c94b --- /dev/null +++ b/lib/rebulk/test/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring diff --git a/lib/rebulk/test/default_rules_module.py b/lib/rebulk/test/default_rules_module.py new file mode 100644 index 000000000..533752fc6 --- /dev/null +++ b/lib/rebulk/test/default_rules_module.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name +from ..match import Match +from ..rules import Rule, RemoveMatch, AppendMatch, RenameMatch, AppendTags, RemoveTags + + +class RuleRemove0(Rule): + consequence = RemoveMatch + def when(self, matches, context): + return matches[0] + + +class RuleAppend0(Rule): + consequence = AppendMatch() + def when(self, matches, context): + return Match(5, 10) + +class RuleRename0(Rule): + consequence = [RenameMatch('renamed')] + def when(self, matches, context): + return [Match(5, 10, name="original")] + +class RuleRemove1(Rule): + consequence = [RemoveMatch()] + def when(self, matches, context): + return [matches[0]] + +class RuleAppend1(Rule): + consequence = [AppendMatch] + def when(self, matches, context): + return [Match(5, 10)] + +class RuleRename1(Rule): + consequence = RenameMatch('renamed') + def when(self, matches, context): + return [Match(5, 10, name="original")] + +class RuleAppend2(Rule): + consequence = [AppendMatch('renamed')] + properties = {'renamed': [None]} + def when(self, matches, context): + return [Match(5, 10)] + +class RuleRename2(Rule): + consequence = RenameMatch('renamed') + def when(self, matches, context): + return Match(5, 10, name="original") + +class RuleAppend3(Rule): + consequence = AppendMatch('renamed') + properties = {'renamed': [None]} + def when(self, matches, context): + return [Match(5, 10)] + +class RuleRename3(Rule): + consequence = [RenameMatch('renamed')] + def when(self, matches, context): + return Match(5, 10, name="original") + +class RuleAppendTags0(Rule): + consequence = AppendTags(['new-tag']) + def when(self, matches, context): + return matches.named('tags', 0) + +class RuleRemoveTags0(Rule): + consequence = RemoveTags(['new-tag']) + def when(self, matches, context): + return matches.named('tags', 0) + +class RuleAppendTags1(Rule): + consequence = AppendTags(['new-tag']) + def when(self, matches, context): + return matches.named('tags') + +class RuleRemoveTags1(Rule): + consequence = RemoveTags(['new-tag']) + def when(self, matches, context): + return matches.named('tags') + diff --git a/lib/rebulk/test/rebulk_rules_module.py b/lib/rebulk/test/rebulk_rules_module.py new file mode 100644 index 000000000..0bd5ef33a --- /dev/null +++ b/lib/rebulk/test/rebulk_rules_module.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name +from rebulk.rules import Rule, RemoveMatch, CustomRule + + +class RemoveAllButLastYear(Rule): + consequence = RemoveMatch + def when(self, matches, context): + entries = matches.named('year') + return entries[:-1] + + +class PrefixedSuffixedYear(CustomRule): + def when(self, matches, context): + toRemove = [] + years = matches.named('year') + for year in years: + if not matches.previous(year, lambda p: p.name == 'yearPrefix') and \ + not matches.next(year, lambda n: n.name == 'yearSuffix'): + toRemove.append(year) + return toRemove + + def then(self, matches, when_response, context): + for to_remove in when_response: + matches.remove(to_remove) + + +class PrefixedSuffixedYearNoLambda(Rule): + consequence = RemoveMatch + def when(self, matches, context): + toRemove = [] + years = matches.named('year') + for year in years: + if not [m for m in matches.previous(year) if m.name == 'yearPrefix'] and \ + not [m for m in matches.next(year) if m.name == 'yearSuffix']: + toRemove.append(year) + return toRemove diff --git a/lib/rebulk/test/rules_module.py b/lib/rebulk/test/rules_module.py new file mode 100644 index 000000000..887b81da8 --- /dev/null +++ b/lib/rebulk/test/rules_module.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name +from ..match import Match +from ..rules import Rule + + +class Rule3(Rule): + def when(self, matches, context): + return context.get('when') + + def then(self, matches, when_response, context): + assert when_response in [True, False] + matches.append(Match(3, 4)) + + +class Rule2(Rule): + dependency = Rule3 + + def when(self, matches, context): + return True + + def then(self, matches, when_response, context): + assert when_response + matches.append(Match(3, 4)) + + +class Rule1(Rule): + dependency = Rule2 + + def when(self, matches, context): + return True + + def then(self, matches, when_response, context): + assert when_response + matches.clear() + + +class Rule0(Rule): + dependency = Rule1 + + def when(self, matches, context): + return True + + def then(self, matches, when_response, context): + assert when_response + matches.append(Match(3, 4)) + + +class Rule1Disabled(Rule1): + name = "Disabled Rule1" + + def enabled(self, context): + return False diff --git a/lib/rebulk/test/test_debug.py b/lib/rebulk/test/test_debug.py new file mode 100644 index 000000000..a35f95fdf --- /dev/null +++ b/lib/rebulk/test/test_debug.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, protected-access, invalid-name + +from ..pattern import StringPattern +from ..rebulk import Rebulk +from ..match import Match +from .. import debug +from .default_rules_module import RuleRemove0 + + +class TestDebug(object): + + + #request.addfinalizer(disable_debug) + + + + debug.DEBUG = True + pattern = StringPattern(1, 3, value="es") + + match = Match(1, 3, value="es") + rule = RuleRemove0() + + input_string = "This is a debug test" + rebulk = Rebulk().string("debug") \ + .string("is") + + matches = rebulk.matches(input_string) + debug.DEBUG = False + + @classmethod + def setup_class(cls): + debug.DEBUG = True + + @classmethod + def teardown_class(cls): + debug.DEBUG = False + + def test_pattern(self): + assert self.pattern.defined_at.lineno == 20 + assert self.pattern.defined_at.name == 'rebulk.test.test_debug' + assert self.pattern.defined_at.filename.endswith('test_debug.py') + + assert str(self.pattern.defined_at) == 'test_debug.py#L20' + assert repr(self.pattern) == '<StringPattern@test_debug.py#L20:(1, 3)>' + + def test_match(self): + assert self.match.defined_at.lineno == 22 + assert self.match.defined_at.name == 'rebulk.test.test_debug' + assert self.match.defined_at.filename.endswith('test_debug.py') + + assert str(self.match.defined_at) == 'test_debug.py#L22' + + def test_rule(self): + assert self.rule.defined_at.lineno == 23 + assert self.rule.defined_at.name == 'rebulk.test.test_debug' + assert self.rule.defined_at.filename.endswith('test_debug.py') + + assert str(self.rule.defined_at) == 'test_debug.py#L23' + assert repr(self.rule) == '<RuleRemove0@test_debug.py#L23>' + + def test_rebulk(self): + """ + This test fails on travis CI, can't find out why there's 1 line offset ... + """ + assert self.rebulk._patterns[0].defined_at.lineno in [26, 27] + assert self.rebulk._patterns[0].defined_at.name == 'rebulk.test.test_debug' + assert self.rebulk._patterns[0].defined_at.filename.endswith('test_debug.py') + + assert str(self.rebulk._patterns[0].defined_at) in ['test_debug.py#L26', 'test_debug.py#L27'] + + assert self.rebulk._patterns[1].defined_at.lineno in [27, 28] + assert self.rebulk._patterns[1].defined_at.name == 'rebulk.test.test_debug' + assert self.rebulk._patterns[1].defined_at.filename.endswith('test_debug.py') + + assert str(self.rebulk._patterns[1].defined_at) in ['test_debug.py#L27', 'test_debug.py#L28'] + + assert self.matches[0].defined_at == self.rebulk._patterns[0].defined_at + assert self.matches[1].defined_at == self.rebulk._patterns[1].defined_at + + def test_repr(self): + str(self.matches) diff --git a/lib/rebulk/test/test_introspector.py b/lib/rebulk/test/test_introspector.py new file mode 100644 index 000000000..24c0c5001 --- /dev/null +++ b/lib/rebulk/test/test_introspector.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Introspector tests +""" +# pylint: disable=no-self-use,pointless-statement,missing-docstring,protected-access,invalid-name +from ..rebulk import Rebulk +from .. import introspector +from .default_rules_module import RuleAppend2, RuleAppend3 + + +def test_string_introspector(): + rebulk = Rebulk().string('One', 'Two', 'Three', name='first').string('1', '2', '3', name='second') + + introspected = introspector.introspect(rebulk, None) + + assert len(introspected.patterns) == 2 + + first_properties = introspected.patterns[0].properties + assert len(first_properties) == 1 + first_properties['first'] == ['One', 'Two', 'Three'] + + second_properties = introspected.patterns[1].properties + assert len(second_properties) == 1 + second_properties['second'] == ['1', '2', '3'] + + properties = introspected.properties + assert len(properties) == 2 + assert properties['first'] == first_properties['first'] + assert properties['second'] == second_properties['second'] + + +def test_string_properties(): + rebulk = Rebulk()\ + .string('One', 'Two', 'Three', name='first', properties={'custom': ['One']})\ + .string('1', '2', '3', name='second', properties={'custom': [1]}) + + introspected = introspector.introspect(rebulk, None) + + assert len(introspected.patterns) == 2 + assert len(introspected.rules) == 2 + + first_properties = introspected.patterns[0].properties + assert len(first_properties) == 1 + first_properties['custom'] == ['One'] + + second_properties = introspected.patterns[1].properties + assert len(second_properties) == 1 + second_properties['custom'] == [1] + + properties = introspected.properties + assert len(properties) == 1 + assert properties['custom'] == ['One', 1] + + +def test_various_pattern(): + rebulk = Rebulk()\ + .regex('One', 'Two', 'Three', name='first', value="string") \ + .string('1', '2', '3', name='second', value="digit") \ + .string('4', '5', '6', name='third') \ + .string('private', private=True) \ + .functional(lambda string: (0, 5), name='func', value='test') \ + .regex('One', 'Two', 'Three', name='regex_name') \ + .regex('(?P<one>One)(?P<two>Two)(?P<three>Three)') \ + .functional(lambda string: (6, 10), name='func2') \ + .string('7', name='third') + + introspected = introspector.introspect(rebulk, None) + + assert len(introspected.patterns) == 8 + assert len(introspected.rules) == 2 + + first_properties = introspected.patterns[0].properties + assert len(first_properties) == 1 + first_properties['first'] == ['string'] + + second_properties = introspected.patterns[1].properties + assert len(second_properties) == 1 + second_properties['second'] == ['digit'] + + third_properties = introspected.patterns[2].properties + assert len(third_properties) == 1 + third_properties['third'] == ['4', '5', '6'] + + func_properties = introspected.patterns[3].properties + assert len(func_properties) == 1 + func_properties['func'] == ['test'] + + regex_name_properties = introspected.patterns[4].properties + assert len(regex_name_properties) == 1 + regex_name_properties['regex_name'] == [None] + + regex_groups_properties = introspected.patterns[5].properties + assert len(regex_groups_properties) == 3 + regex_groups_properties['one'] == [None] + regex_groups_properties['two'] == [None] + regex_groups_properties['three'] == [None] + + func2_properties = introspected.patterns[6].properties + assert len(func2_properties) == 1 + func2_properties['func2'] == [None] + + append_third_properties = introspected.patterns[7].properties + assert len(append_third_properties) == 1 + append_third_properties['third'] == [None] + + properties = introspected.properties + assert len(properties) == 9 + assert properties['first'] == first_properties['first'] + assert properties['second'] == second_properties['second'] + assert properties['third'] == third_properties['third'] + append_third_properties['third'] + assert properties['func'] == func_properties['func'] + assert properties['regex_name'] == regex_name_properties['regex_name'] + assert properties['one'] == regex_groups_properties['one'] + assert properties['two'] == regex_groups_properties['two'] + assert properties['three'] == regex_groups_properties['three'] + assert properties['func2'] == func2_properties['func2'] + + +def test_rule_properties(): + rebulk = Rebulk(default_rules=False).rules(RuleAppend2, RuleAppend3) + + introspected = introspector.introspect(rebulk, None) + + assert len(introspected.rules) == 2 + assert len(introspected.patterns) == 0 + + rule_properties = introspected.rules[0].properties + assert len(rule_properties) == 1 + assert rule_properties['renamed'] == [None] + + rule_properties = introspected.rules[1].properties + assert len(rule_properties) == 1 + assert rule_properties['renamed'] == [None] + + properties = introspected.properties + assert len(properties) == 1 + assert properties['renamed'] == [None] diff --git a/lib/rebulk/test/test_loose.py b/lib/rebulk/test/test_loose.py new file mode 100644 index 000000000..bc0c6bca1 --- /dev/null +++ b/lib/rebulk/test/test_loose.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name + +from ..loose import call + + +def test_loose_function(): + + def func(v1, v2, v3=3, v4=4): + return v1 + v2 + v3 + v4 + + assert call(func, 1, 2) == func(1, 2) + assert call(func, 1, 2, 3, 5) == func(1, 2, 3, 5) + assert call(func, 1, 2, v3=4, v4=5) == func(1, 2, v3=4, v4=5) + assert call(func, 1, 2, 3, 4, 5) == func(1, 2, 3, 4) + assert call(func, 1, 2, 3, 4, more=5) == func(1, 2, 3, 4) + + +def test_loose_varargs_function(): + def func(v1, v2, *args): + return v1 + v2 + args[0] if len(args) > 0 else 3 + args[1] if len(args) > 1 else 4 + + assert call(func, 1, 2) == func(1, 2) + assert call(func, 1, 2, 3, 5) == func(1, 2, 3, 5) + assert call(func, 1, 2, 3, 4, 5) == func(1, 2, 3, 4) + + +def test_loose_kwargs_function(): + def func(v1, v2, **kwargs): + return v1 + v2 + kwargs.get('v3', 3) + kwargs.get('v4', 4) + + assert call(func, v1=1, v2=2) == func(v1=1, v2=2) + assert call(func, v1=1, v2=2, v3=3, v4=5) == func(v1=1, v2=2, v3=3, v4=5) + + +def test_loose_class(): + class Dummy(object): + def __init__(self, v1, v2, v3=3, v4=4): + self.v1 = v1 + self.v2 = v2 + self.v3 = v3 + self.v4 = v4 + + def call(self): + return self.v1 + self.v2 + self.v3 + self.v4 + + assert call(Dummy, 1, 2).call() == Dummy(1, 2).call() + assert call(Dummy, 1, 2, 3, 5).call() == Dummy(1, 2, 3, 5).call() + assert call(Dummy, 1, 2, v3=4, v4=5).call() == Dummy(1, 2, v3=4, v4=5).call() + assert call(Dummy, 1, 2, 3, 4, 5).call() == Dummy(1, 2, 3, 4).call() + assert call(Dummy, 1, 2, 3, 4, more=5).call() == Dummy(1, 2, 3, 4).call() + + +def test_loose_varargs_class(): + class Dummy(object): + def __init__(self, v1, v2, *args): + self.v1 = v1 + self.v2 = v2 + self.v3 = args[0] if len(args) > 0 else 3 + self.v4 = args[1] if len(args) > 1 else 4 + + def call(self): + return self.v1 + self.v2 + self.v3 + self.v4 + + assert call(Dummy, 1, 2).call() == Dummy(1, 2).call() + assert call(Dummy, 1, 2, 3, 5).call() == Dummy(1, 2, 3, 5).call() + assert call(Dummy, 1, 2, 3, 4, 5).call() == Dummy(1, 2, 3, 4).call() + + +def test_loose_kwargs_class(): + class Dummy(object): + def __init__(self, v1, v2, **kwargs): + self.v1 = v1 + self.v2 = v2 + self.v3 = kwargs.get('v3', 3) + self.v4 = kwargs.get('v4', 4) + + def call(self): + return self.v1 + self.v2 + self.v3 + self.v4 + + assert call(Dummy, v1=1, v2=2).call() == Dummy(v1=1, v2=2).call() + assert call(Dummy, v1=1, v2=2, v3=3, v4=5).call() == Dummy(v1=1, v2=2, v3=3, v4=5).call() diff --git a/lib/rebulk/test/test_match.py b/lib/rebulk/test/test_match.py new file mode 100644 index 000000000..c87311d1a --- /dev/null +++ b/lib/rebulk/test/test_match.py @@ -0,0 +1,571 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, unneeded-not + +import pytest +import six + +from ..match import Match, Matches +from ..pattern import StringPattern, RePattern +from ..formatters import formatters + + +class TestMatchClass(object): + def test_repr(self): + match1 = Match(1, 3, value="es") + + assert repr(match1) == '<es:(1, 3)>' + + match2 = Match(0, 4, value="test", private=True, name="abc", tags=['one', 'two']) + + assert repr(match2) == '<test:(0, 4)+private+name=abc+tags=[\'one\', \'two\']>' + + def test_names(self): + parent = Match(0, 10, name="test") + parent.children.append(Match(0, 10, name="child1", parent=parent)) + parent.children.append(Match(0, 10, name="child2", parent=parent)) + + assert set(parent.names) == set(["child1", "child2"]) + + def test_equality(self): + match1 = Match(1, 3, value="es") + match2 = Match(1, 3, value="es") + + other = object() + + assert hash(match1) == hash(match2) + assert hash(match1) != hash(other) + + assert match1 == match2 + assert not match1 == other + + def test_inequality(self): + match1 = Match(0, 2, value="te") + match2 = Match(2, 4, value="st") + match3 = Match(0, 2, value="other") + + other = object() + + assert hash(match1) != hash(match2) + assert hash(match1) != hash(match3) + + assert match1 != other + assert match1 != match2 + assert match1 != match3 + + def test_length(self): + match1 = Match(0, 4, value="test") + match2 = Match(0, 2, value="spanIsUsed") + + assert len(match1) == 4 + assert len(match2) == 2 + + def test_compare(self): + match1 = Match(0, 2, value="te") + match2 = Match(2, 4, value="st") + + other = object() + + assert match1 < match2 + assert match1 <= match2 + + assert match2 > match1 + assert match2 >= match1 + + if six.PY3: + with pytest.raises(TypeError): + match1 < other + + with pytest.raises(TypeError): + match1 <= other + + with pytest.raises(TypeError): + match1 > other + + with pytest.raises(TypeError): + match1 >= other + else: + assert match1 < other + assert match1 <= other + assert not match1 > other + assert not match1 >= other + + def test_value(self): + match1 = Match(1, 3) + match1.value = "test" + + assert match1.value == "test" + + +class TestMatchesClass(object): + match1 = Match(0, 2, value="te", name="start") + match2 = Match(2, 3, value="s", tags="tag1") + match3 = Match(3, 4, value="t", tags=["tag1", "tag2"]) + match4 = Match(2, 4, value="st", name="end") + + def test_tag(self): + matches = Matches() + matches.append(self.match1) + matches.append(self.match2) + matches.append(self.match3) + matches.append(self.match4) + + assert "start" in matches.names + assert "end" in matches.names + + assert "tag1" in matches.tags + assert "tag2" in matches.tags + + tag1 = matches.tagged("tag1") + assert len(tag1) == 2 + assert tag1[0] == self.match2 + assert tag1[1] == self.match3 + + tag2 = matches.tagged("tag2") + assert len(tag2) == 1 + assert tag2[0] == self.match3 + + start = matches.named("start") + assert len(start) == 1 + assert start[0] == self.match1 + + end = matches.named("end") + assert len(end) == 1 + assert end[0] == self.match4 + + def test_base(self): + matches = Matches() + matches.append(self.match1) + + assert len(matches) == 1 + assert repr(matches) == repr([self.match1]) + assert list(matches.starting(0)) == [self.match1] + assert list(matches.ending(2)) == [self.match1] + + matches.append(self.match2) + matches.append(self.match3) + matches.append(self.match4) + + assert len(matches) == 4 + assert list(matches.starting(2)) == [self.match2, self.match4] + assert list(matches.starting(3)) == [self.match3] + assert list(matches.ending(3)) == [self.match2] + assert list(matches.ending(4)) == [self.match3, self.match4] + assert list(matches.range()) == [self.match1, self.match2, self.match4, self.match3] + assert list(matches.range(0)) == [self.match1, self.match2, self.match4, self.match3] + assert list(matches.range(0, 3)) == [self.match1, self.match2, self.match4] + assert list(matches.range(2, 3)) == [self.match2, self.match4] + assert list(matches.range(3, 4)) == [self.match4, self.match3] + + matches.remove(self.match1) + assert len(matches) == 3 + assert len(matches.starting(0)) == 0 + assert len(matches.ending(2)) == 0 + + matches.clear() + + assert len(matches) == 0 + assert len(matches.starting(0)) == 0 + assert len(matches.starting(2)) == 0 + assert len(matches.starting(3)) == 0 + assert len(matches.ending(2)) == 0 + assert len(matches.ending(3)) == 0 + assert len(matches.ending(4)) == 0 + + def test_get_slices(self): + matches = Matches() + matches.append(self.match1) + matches.append(self.match2) + matches.append(self.match3) + matches.append(self.match4) + + slice_matches = matches[1:3] + + assert isinstance(slice_matches, Matches) + + assert len(slice_matches) == 2 + assert slice_matches[0] == self.match2 + assert slice_matches[1] == self.match3 + + def test_remove_slices(self): + matches = Matches() + matches.append(self.match1) + matches.append(self.match2) + matches.append(self.match3) + matches.append(self.match4) + + del matches[1:3] + + assert len(matches) == 2 + assert matches[0] == self.match1 + assert matches[1] == self.match4 + + def test_set_slices(self): + matches = Matches() + matches.append(self.match1) + matches.append(self.match2) + matches.append(self.match3) + matches.append(self.match4) + + matches[1:3] = self.match1, self.match4 + + assert len(matches) == 4 + assert matches[0] == self.match1 + assert matches[1] == self.match1 + assert matches[2] == self.match4 + assert matches[3] == self.match4 + + def test_set_index(self): + matches = Matches() + matches.append(self.match1) + matches.append(self.match2) + matches.append(self.match3) + + matches[1] = self.match4 + + assert len(matches) == 3 + assert matches[0] == self.match1 + assert matches[1] == self.match4 + assert matches[2] == self.match3 + + def test_constructor(self): + matches = Matches([self.match1, self.match2, self.match3, self.match4]) + + assert len(matches) == 4 + assert list(matches.starting(0)) == [self.match1] + assert list(matches.ending(2)) == [self.match1] + assert list(matches.starting(2)) == [self.match2, self.match4] + assert list(matches.starting(3)) == [self.match3] + assert list(matches.ending(3)) == [self.match2] + assert list(matches.ending(4)) == [self.match3, self.match4] + + def test_constructor_kwargs(self): + matches = Matches([self.match1, self.match2, self.match3, self.match4], input_string="test") + + assert len(matches) == 4 + assert matches.input_string == "test" + assert list(matches.starting(0)) == [self.match1] + assert list(matches.ending(2)) == [self.match1] + assert list(matches.starting(2)) == [self.match2, self.match4] + assert list(matches.starting(3)) == [self.match3] + assert list(matches.ending(3)) == [self.match2] + assert list(matches.ending(4)) == [self.match3, self.match4] + + def test_crop(self): + input_string = "abcdefghijklmnopqrstuvwxyz" + + match1 = Match(1, 10, input_string=input_string) + match2 = Match(0, 2, input_string=input_string) + match3 = Match(8, 15, input_string=input_string) + + ret = match1.crop([match2, match3.span]) + + assert len(ret) == 1 + + assert ret[0].span == (2, 8) + assert ret[0].value == "cdefgh" + + ret = match1.crop((1, 10)) + assert len(ret) == 0 + + ret = match1.crop((1, 3)) + assert len(ret) == 1 + assert ret[0].span == (3, 10) + + ret = match1.crop((7, 10)) + assert len(ret) == 1 + assert ret[0].span == (1, 7) + + ret = match1.crop((0, 12)) + assert len(ret) == 0 + + ret = match1.crop((4, 6)) + assert len(ret) == 2 + + assert ret[0].span == (1, 4) + assert ret[1].span == (6, 10) + + ret = match1.crop([(3, 5), (7, 9)]) + assert len(ret) == 3 + + assert ret[0].span == (1, 3) + assert ret[1].span == (5, 7) + assert ret[2].span == (9, 10) + + def test_split(self): + input_string = "123 +word1 - word2 + word3 456" + match = Match(3, len(input_string) - 3, input_string=input_string) + splitted = match.split(" -+") + + assert len(splitted) == 3 + assert [split.value for split in splitted] == ["word1", "word2", "word3"] + + +class TestMaches(object): + def test_names(self): + input_string = "One Two Three" + + matches = Matches() + + matches.extend(StringPattern("One", name="1-str", tags=["One", "str"]).matches(input_string)) + matches.extend(RePattern("One", name="1-re", tags=["One", "re"]).matches(input_string)) + matches.extend(StringPattern("Two", name="2-str", tags=["Two", "str"]).matches(input_string)) + matches.extend(RePattern("Two", name="2-re", tags=["Two", "re"]).matches(input_string)) + matches.extend(StringPattern("Three", name="3-str", tags=["Three", "str"]).matches(input_string)) + matches.extend(RePattern("Three", name="3-re", tags=["Three", "re"]).matches(input_string)) + + assert set(matches.names) == set(["1-str", "1-re", "2-str", "2-re", "3-str", "3-re"]) + + def test_filters(self): + input_string = "One Two Three" + + matches = Matches() + + matches.extend(StringPattern("One", name="1-str", tags=["One", "str"]).matches(input_string)) + matches.extend(RePattern("One", name="1-re", tags=["One", "re"]).matches(input_string)) + matches.extend(StringPattern("Two", name="2-str", tags=["Two", "str"]).matches(input_string)) + matches.extend(RePattern("Two", name="2-re", tags=["Two", "re"]).matches(input_string)) + matches.extend(StringPattern("Three", name="3-str", tags=["Three", "str"]).matches(input_string)) + matches.extend(RePattern("Three", name="3-re", tags=["Three", "re"]).matches(input_string)) + + selection = matches.starting(0) + assert len(selection) == 2 + + selection = matches.starting(0, lambda m: "str" in m.tags) + assert len(selection) == 1 + assert selection[0].pattern.name == "1-str" + + selection = matches.ending(7, predicate=lambda m: "str" in m.tags) + assert len(selection) == 1 + assert selection[0].pattern.name == "2-str" + + selection = matches.previous(matches.named("2-str")[0]) + assert len(selection) == 2 + assert selection[0].pattern.name == "1-str" + assert selection[1].pattern.name == "1-re" + + selection = matches.previous(matches.named("2-str", 0), lambda m: "str" in m.tags) + assert len(selection) == 1 + assert selection[0].pattern.name == "1-str" + + selection = matches.next(matches.named("2-str", 0)) + assert len(selection) == 2 + assert selection[0].pattern.name == "3-str" + assert selection[1].pattern.name == "3-re" + + selection = matches.next(matches.named("2-str", 0), index=0, predicate=lambda m: "re" in m.tags) + assert selection is not None + assert selection.pattern.name == "3-re" + + selection = matches.next(matches.named("2-str", index=0), lambda m: "re" in m.tags) + assert len(selection) == 1 + assert selection[0].pattern.name == "3-re" + + selection = matches.named("2-str", lambda m: "re" in m.tags) + assert len(selection) == 0 + + selection = matches.named("2-re", lambda m: "re" in m.tags, 0) + assert selection is not None + assert selection.name == "2-re" # pylint:disable=no-member + + selection = matches.named("2-re", lambda m: "re" in m.tags) + assert len(selection) == 1 + assert selection[0].name == "2-re" + + selection = matches.named("2-re", lambda m: "re" in m.tags, index=1000) + assert selection is None + + def test_raw(self): + input_string = "0123456789" + + match = Match(0, 10, input_string=input_string, formatter=lambda s: s*2) + + assert match.value == match.raw * 2 + assert match.raw == input_string + + match.raw_end = 9 + match.raw_start = 1 + + assert match.value == match.raw * 2 + assert match.raw == input_string[1:9] + + match.raw_end = None + match.raw_start = None + + assert match.value == match.raw * 2 + assert match.raw == input_string + + + def test_formatter_chain(self): + input_string = "100" + + match = Match(0, 3, input_string=input_string, formatter=formatters(int, lambda s: s*2, lambda s: s+10)) + + assert match.raw == input_string + assert match.value == 100 * 2 + 10 + + + def test_to_dict(self): + input_string = "One Two Two Three" + + matches = Matches() + + matches.extend(StringPattern("One", name="1", tags=["One", "str"]).matches(input_string)) + matches.extend(RePattern("One", name="1", tags=["One", "re"]).matches(input_string)) + matches.extend(StringPattern("Two", name="2", tags=["Two", "str"]).matches(input_string)) + matches.extend(RePattern("Two", name="2", tags=["Two", "re"]).matches(input_string)) + matches.extend(RePattern("Two", name="2", tags=["Two", "reBis"]).matches(input_string)) + matches.extend(StringPattern("Three", name="3", tags=["Three", "str"]).matches(input_string)) + matches.extend(RePattern("Three", name="3bis", tags=["Three", "re"]).matches(input_string)) + matches.extend(RePattern(r"(\w+)", name="words").matches(input_string)) + + kvalues = matches.to_dict() + assert kvalues == {"1": "One", + "2": "Two", + "3": "Three", + "3bis": "Three", + "words": "One"} + assert kvalues.values_list["words"] == ["One", "Two", "Three"] + + kvalues = matches.to_dict(details=True, implicit=True) + assert kvalues["1"].value == "One" + + assert len(kvalues["2"]) == 2 + assert kvalues["2"][0].value == "Two" + assert kvalues["2"][1].value == "Two" + + assert kvalues["3"].value == "Three" + assert kvalues["3bis"].value == "Three" + + assert len(kvalues["words"]) == 4 + assert kvalues["words"][0].value == "One" + assert kvalues["words"][1].value == "Two" + assert kvalues["words"][2].value == "Two" + assert kvalues["words"][3].value == "Three" + + kvalues = matches.to_dict(details=True) + assert kvalues["1"].value == "One" + + assert len(kvalues.values_list["2"]) == 2 + assert kvalues.values_list["2"][0].value == "Two" + assert kvalues.values_list["2"][1].value == "Two" + + assert kvalues["3"].value == "Three" + assert kvalues["3bis"].value == "Three" + + assert len(kvalues.values_list["words"]) == 4 + assert kvalues.values_list["words"][0].value == "One" + assert kvalues.values_list["words"][1].value == "Two" + assert kvalues.values_list["words"][2].value == "Two" + assert kvalues.values_list["words"][3].value == "Three" + + def test_chains(self): + input_string = "wordX 10 20 30 40 wordA, wordB, wordC 70 80 wordX" + + matches = Matches(input_string=input_string) + + matches.extend(RePattern(r"\d+", name="digit").matches(input_string)) + matches.extend(RePattern("[a-zA-Z]+", name="word").matches(input_string)) + + assert len(matches) == 11 + + a_start = input_string.find('wordA') + + b_start = input_string.find('wordB') + b_end = b_start + len('wordB') + + c_start = input_string.find('wordC') + c_end = c_start + len('wordC') + + chain_before = matches.chain_before(b_start, " ,", predicate=lambda match: match.name == "word") + assert len(chain_before) == 1 + assert chain_before[0].value == 'wordA' + + chain_before = matches.chain_before(Match(b_start, b_start), " ,", predicate=lambda match: match.name == "word") + assert len(chain_before) == 1 + assert chain_before[0].value == 'wordA' + + chain_before = matches.chain_before(b_start, " ,", predicate=lambda match: match.name == "digit") + assert len(chain_before) == 0 + + chain_before = matches.chain_before(a_start, " ,", predicate=lambda match: match.name == "digit") + assert len(chain_before) == 4 + assert [match.value for match in chain_before] == ["40", "30", "20", "10"] + + chain_after = matches.chain_after(b_end, " ,", predicate=lambda match: match.name == "word") + assert len(chain_after) == 1 + assert chain_after[0].value == 'wordC' + + chain_after = matches.chain_after(Match(b_end, b_end), " ,", predicate=lambda match: match.name == "word") + assert len(chain_after) == 1 + assert chain_after[0].value == 'wordC' + + chain_after = matches.chain_after(b_end, " ,", predicate=lambda match: match.name == "digit") + assert len(chain_after) == 0 + + chain_after = matches.chain_after(c_end, " ,", predicate=lambda match: match.name == "digit") + assert len(chain_after) == 2 + assert [match.value for match in chain_after] == ["70", "80"] + + chain_after = matches.chain_after(c_end, " ,", end=10000, predicate=lambda match: match.name == "digit") + assert len(chain_after) == 2 + assert [match.value for match in chain_after] == ["70", "80"] + + def test_holes(self): + input_string = '1'*10+'2'*10+'3'*10+'4'*10+'5'*10+'6'*10+'7'*10 + + hole1 = Match(0, 10, input_string=input_string) + hole2 = Match(20, 30, input_string=input_string) + hole3 = Match(30, 40, input_string=input_string) + hole4 = Match(60, 70, input_string=input_string) + + matches = Matches([hole1, hole2], input_string=input_string) + matches.append(hole3) + matches.append(hole4) + + holes = list(matches.holes()) + assert len(holes) == 2 + assert holes[0].span == (10, 20) + assert holes[0].value == '2'*10 + assert holes[1].span == (40, 60) + assert holes[1].value == '5' * 10 + '6' * 10 + + holes = list(matches.holes(5, 15)) + assert len(holes) == 1 + assert holes[0].span == (10, 15) + assert holes[0].value == '2'*5 + + holes = list(matches.holes(5, 15, formatter=lambda value: "formatted")) + assert len(holes) == 1 + assert holes[0].span == (10, 15) + assert holes[0].value == "formatted" + + holes = list(matches.holes(5, 15, predicate=lambda hole: False)) + assert len(holes) == 0 + + def test_holes_empty(self): + input_string = "Test hole on empty matches" + matches = Matches(input_string=input_string) + holes = matches.holes() + assert len(holes) == 1 + assert holes[0].value == input_string + + def test_holes_seps(self): + input_string = "Test hole - with many separators + included" + match = StringPattern("many").matches(input_string) + + matches = Matches(match, input_string) + holes = matches.holes() + + assert len(holes) == 2 + + holes = matches.holes(seps="-+") + + assert len(holes) == 4 + assert [hole.value for hole in holes] == ["Test hole ", " with ", " separators ", " included"] + + + + + + diff --git a/lib/rebulk/test/test_pattern.py b/lib/rebulk/test/test_pattern.py new file mode 100644 index 000000000..0316aabaf --- /dev/null +++ b/lib/rebulk/test/test_pattern.py @@ -0,0 +1,757 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, unbalanced-tuple-unpacking + +import re +import pytest + +from ..pattern import StringPattern, RePattern, FunctionalPattern, REGEX_AVAILABLE +from ..match import Match + +class TestStringPattern(object): + """ + Tests for StringPattern matching + """ + + input_string = "An Abyssinian fly playing a Celtic violin was annoyed by trashy flags on " \ + "which were the Hebrew letter qoph." + + def test_single(self): + pattern = StringPattern("Celtic") + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (28, 34) + assert matches[0].value == "Celtic" + + def test_repr(self): + pattern = StringPattern("Celtic") + + assert repr(pattern) == '<StringPattern:(\'Celtic\',)>' + + def test_ignore_case(self): + pattern = StringPattern("celtic", ignore_case=False) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + pattern = StringPattern("celtic", ignore_case=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert matches[0].value == "Celtic" + + def test_private_names(self): + pattern = StringPattern("celtic", name="test", private_names=["test"], ignore_case=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert matches[0].private + + def test_no_match(self): + pattern = StringPattern("Python") + + matches = list(pattern.matches(self.input_string)) + assert not matches + + def test_multiple_patterns(self): + pattern = StringPattern("playing", "annoyed", "Hebrew") + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 3 + + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (18, 25) + assert matches[0].value == "playing" + + assert isinstance(matches[1], Match) + assert matches[1].pattern == pattern + assert matches[1].span == (46, 53) + assert matches[1].value == "annoyed" + + assert isinstance(matches[2], Match) + assert matches[2].pattern == pattern + assert matches[2].span == (88, 94) + assert matches[2].value == "Hebrew" + + def test_start_end_kwargs(self): + pattern = StringPattern("Abyssinian", start=20, end=40) + matches = list(pattern.matches(self.input_string)) + + assert len(matches) == 0 + + def test_matches_kwargs(self): + pattern = StringPattern("Abyssinian", name="test", value="AB") + matches = list(pattern.matches(self.input_string)) + + assert len(matches) == 1 + assert matches[0].name == "test" + assert matches[0].value == "AB" + + +class TestRePattern(object): + """ + Tests for RePattern matching + """ + + input_string = "An Abyssinian fly playing a Celtic violin was annoyed by trashy flags on " \ + "which were the Hebrew letter qoph." + + def test_single_compiled(self): + pattern = RePattern(re.compile("Celt.?c")) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (28, 34) + assert matches[0].value == "Celtic" + + def test_single_string(self): + pattern = RePattern("Celt.?c") + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (28, 34) + assert matches[0].value == "Celtic" + + def test_single_kwargs(self): + pattern = RePattern({"pattern": "celt.?c", "flags": re.IGNORECASE}) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (28, 34) + assert matches[0].value == "Celtic" + + def test_single_vargs(self): + pattern = RePattern(("celt.?c", re.IGNORECASE)) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (28, 34) + assert matches[0].value == "Celtic" + + def test_no_match(self): + pattern = RePattern("abc.?def") + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + def test_shortcuts(self): + pattern = RePattern("Celtic-violin", abbreviations=[("-", r"[\W_]+")]) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + pattern = RePattern({"pattern": "celtic-violin", "flags": re.IGNORECASE}, abbreviations=[("-", r"[\W_]+")]) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + def test_multiple_patterns(self): + pattern = RePattern("pla.?ing", "ann.?yed", "Heb.?ew") + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 3 + + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (18, 25) + assert matches[0].value == "playing" + + assert isinstance(matches[1], Match) + assert matches[1].pattern == pattern + assert matches[1].span == (46, 53) + assert matches[1].value == "annoyed" + + assert isinstance(matches[2], Match) + assert matches[2].pattern == pattern + assert matches[2].span == (88, 94) + assert matches[2].value == "Hebrew" + + def test_unnamed_groups(self): + pattern = RePattern(r"(Celt.?c)\s+(\w+)") + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + parent = matches[0] + + assert isinstance(parent, Match) + assert parent.pattern == pattern + assert parent.span == (28, 41) + assert parent.name is None + assert parent.value == "Celtic violin" + + assert len(parent.children) == 2 + + group1, group2 = parent.children + + assert isinstance(group1, Match) + assert group1.pattern == pattern + assert group1.span == (28, 34) + assert group1.name is None + assert group1.value == "Celtic" + assert group1.parent == parent + + assert isinstance(group2, Match) + assert group2.pattern == pattern + assert group2.span == (35, 41) + assert group2.name is None + assert group2.value == "violin" + assert group2.parent == parent + + def test_named_groups(self): + pattern = RePattern(r"(?P<param1>Celt.?c)\s+(?P<param2>\w+)") + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + parent = matches[0] + + assert isinstance(parent, Match) + assert parent.pattern == pattern + assert parent.span == (28, 41) + assert parent.name is None + assert parent.value == "Celtic violin" + + assert len(parent.children) == 2 + group1, group2 = parent.children + + assert isinstance(group1, Match) + assert group1.pattern == pattern + assert group1.span == (28, 34) + assert group1.name == "param1" + assert group1.value == "Celtic" + assert group1.parent == parent + + assert isinstance(group2, Match) + assert group2.pattern == pattern + assert group2.span == (35, 41) + assert group2.name == "param2" + assert group2.value == "violin" + assert group2.parent == parent + + def test_children(self): + pattern = RePattern(r"(?P<param1>Celt.?c)\s+(?P<param2>\w+)", children=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 2 + group1, group2 = matches + + assert isinstance(group1, Match) + assert group1.pattern == pattern + assert group1.span == (28, 34) + assert group1.name == "param1" + assert group1.value == "Celtic" + + assert isinstance(group2, Match) + assert group2.pattern == pattern + assert group2.span == (35, 41) + assert group2.name == "param2" + assert group2.value == "violin" + + def test_children_parent_private(self): + pattern = RePattern(r"(?P<param1>Celt.?c)\s+(?P<param2>\w+)", children=True, private_parent=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 3 + parent, group1, group2 = matches + + assert isinstance(group1, Match) + assert parent.private + assert parent.pattern == pattern + assert parent.span == (28, 41) + assert parent.name is None + assert parent.value == "Celtic violin" + + assert isinstance(group1, Match) + assert not group1.private + assert group1.pattern == pattern + assert group1.span == (28, 34) + assert group1.name == "param1" + assert group1.value == "Celtic" + + assert isinstance(group2, Match) + assert not group2.private + assert group2.pattern == pattern + assert group2.span == (35, 41) + assert group2.name == "param2" + assert group2.value == "violin" + + def test_parent_children_private(self): + pattern = RePattern(r"(?P<param1>Celt.?c)\s+(?P<param2>\w+)", private_children=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 3 + parent, group1, group2 = matches + + assert isinstance(group1, Match) + assert not parent.private + assert parent.pattern == pattern + assert parent.span == (28, 41) + assert parent.name is None + assert parent.value == "Celtic violin" + + assert isinstance(group1, Match) + assert group1.private + assert group1.pattern == pattern + assert group1.span == (28, 34) + assert group1.name == "param1" + assert group1.value == "Celtic" + + assert isinstance(group2, Match) + assert group2.private + assert group2.pattern == pattern + assert group2.span == (35, 41) + assert group2.name == "param2" + assert group2.value == "violin" + + def test_every(self): + pattern = RePattern(r"(?P<param1>Celt.?c)\s+(?P<param2>\w+)", every=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 3 + parent, group1, group2 = matches + + assert isinstance(group1, Match) + assert not parent.private + assert parent.pattern == pattern + assert parent.span == (28, 41) + assert parent.name is None + assert parent.value == "Celtic violin" + + assert isinstance(group1, Match) + assert not group1.private + assert group1.pattern == pattern + assert group1.span == (28, 34) + assert group1.name == "param1" + assert group1.value == "Celtic" + + assert isinstance(group2, Match) + assert not group2.private + assert group2.pattern == pattern + assert group2.span == (35, 41) + assert group2.name == "param2" + assert group2.value == "violin" + + def test_matches_kwargs(self): + pattern = RePattern("He.rew", name="test", value="HE") + matches = list(pattern.matches(self.input_string)) + + assert len(matches) == 1 + assert matches[0].name == "test" + assert matches[0].value == "HE" + + pattern = RePattern("H(e.)(rew)", name="test", value="HE") + matches = list(pattern.matches(self.input_string)) + + assert len(matches) == 1 + assert matches[0].name == "test" + assert matches[0].value == "HE" + + children = matches[0].children + assert len(children) == 2 + assert children[0].name is "test" + assert children[0].value == "HE" + + assert children[1].name is "test" + assert children[1].value == "HE" + + pattern = RePattern("H(?P<first>e.)(?P<second>rew)", name="test", value="HE") + matches = list(pattern.matches(self.input_string)) + + assert len(matches) == 1 + assert matches[0].name == "test" + assert matches[0].value == "HE" + + children = matches[0].children + assert len(children) == 2 + assert children[0].name == "first" + assert children[0].value == "HE" + + assert children[1].name == "second" + assert children[1].value == "HE" + + +class TestFunctionalPattern(object): + """ + Tests for FunctionalPattern matching + """ + + input_string = "An Abyssinian fly playing a Celtic violin was annoyed by trashy flags on " \ + "which were the Hebrew letter qoph." + + def test_single_vargs(self): + def func(input_string): + i = input_string.find("fly") + if i > -1: + return i, i + len("fly"), "fly", "functional" + + pattern = FunctionalPattern(func) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (14, 17) + assert matches[0].name == "functional" + assert matches[0].value == "fly" + + def test_single_kwargs(self): + def func(input_string): + i = input_string.find("fly") + if i > -1: + return {"start": i, "end": i + len("fly"), "name": "functional"} + + pattern = FunctionalPattern(func) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (14, 17) + assert matches[0].name == "functional" + assert matches[0].value == "fly" + + def test_multiple_objects(self): + def func(input_string): + i = input_string.find("fly") + matches = [] + if i > -1: + matches.append((i, i + len("fly"), {'name': "functional"})) + i = input_string.find("annoyed") + if i > -1: + matches.append((i, i + len("annoyed"))) + i = input_string.find("Hebrew") + if i > -1: + matches.append({"start": i, "end": i + len("Hebrew")}) + return matches + + pattern = FunctionalPattern(func) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 3 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (14, 17) + assert matches[0].name == "functional" + assert matches[0].value == "fly" + + assert isinstance(matches[1], Match) + assert matches[1].pattern == pattern + assert matches[1].span == (46, 53) + assert matches[1].value == "annoyed" + + assert isinstance(matches[2], Match) + assert matches[2].pattern == pattern + assert matches[2].span == (88, 94) + assert matches[2].value == "Hebrew" + + def test_multiple_generator(self): + def func(input_string): + i = input_string.find("fly") + if i > -1: + yield (i, i + len("fly"), {'name': "functional"}) + i = input_string.find("annoyed") + if i > -1: + yield (i, i + len("annoyed")) + i = input_string.find("Hebrew") + if i > -1: + yield (i, {"end": i + len("Hebrew")}) + + pattern = FunctionalPattern(func) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 3 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (14, 17) + assert matches[0].name == "functional" + assert matches[0].value == "fly" + + assert isinstance(matches[1], Match) + assert matches[1].pattern == pattern + assert matches[1].span == (46, 53) + assert matches[1].value == "annoyed" + + assert isinstance(matches[2], Match) + assert matches[2].pattern == pattern + assert matches[2].span == (88, 94) + assert matches[2].value == "Hebrew" + + def test_no_match(self): + pattern = FunctionalPattern(lambda x: None) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + def test_multiple_patterns(self): + def playing(input_string): + i = input_string.find("playing") + if i > -1: + return i, i + len("playing") + + def annoyed(input_string): + i = input_string.find("annoyed") + if i > -1: + return i, i + len("annoyed") + + def hebrew(input_string): + i = input_string.find("Hebrew") + if i > -1: + return i, i + len("Hebrew") + + pattern = FunctionalPattern(playing, annoyed, hebrew) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 3 + + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (18, 25) + assert matches[0].value == "playing" + + assert isinstance(matches[1], Match) + assert matches[1].pattern == pattern + assert matches[1].span == (46, 53) + assert matches[1].value == "annoyed" + + assert isinstance(matches[2], Match) + assert matches[2].pattern == pattern + assert matches[2].span == (88, 94) + assert matches[2].value == "Hebrew" + + def test_matches_kwargs(self): + def playing(input_string): + i = input_string.find("playing") + if i > -1: + return i, i + len("playing") + + pattern = FunctionalPattern(playing, name="test", value="PLAY") + matches = list(pattern.matches(self.input_string)) + + assert len(matches) == 1 + assert matches[0].name == "test" + assert matches[0].value == "PLAY" + + +class TestFormatter(object): + """ + Tests for formatter option + """ + + input_string = "This string contains 1849 a number" + + def test_single_string(self): + pattern = StringPattern("1849", name="dummy", formatter=lambda x: int(x) / 2) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (21, 25) + assert matches[0].value == 1849 / 2 + + def test_single_re_no_group(self): + pattern = RePattern(r"\d+", formatter=lambda x: int(x) * 2) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (21, 25) + assert matches[0].value == 1849 * 2 + + def test_single_re_named_groups(self): + pattern = RePattern(r"(?P<strParam>cont.?ins)\s+(?P<intParam>\d+)", + formatter={'intParam': lambda x: int(x) * 2, + 'strParam': lambda x: "really " + x}, format_all=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + parent = matches[0] + assert len(parent.children) == 2 + + group1, group2 = parent.children + + assert isinstance(group1, Match) + assert group1.pattern == pattern + assert group1.span == (12, 20) + assert group1.value == "really contains" + + assert isinstance(group2, Match) + assert group2.pattern == pattern + assert group2.span == (21, 25) + assert group2.value == 1849 * 2 + + def test_repeated_captures_option(self): + pattern = RePattern(r"\[(\d+)\](?:-(\d+))*") + + matches = list(pattern.matches("[02]-03-04-05-06")) + assert len(matches) == 1 + + match = matches[0] + if REGEX_AVAILABLE: + assert len(match.children) == 5 + assert [child.value for child in match.children] == ["02", "03", "04", "05", "06"] + else: + assert len(match.children) == 2 + assert [child.value for child in match.children] == ["02", "06"] + + with pytest.raises(NotImplementedError): + RePattern(r"\[(\d+)\](?:-(\d+))*", repeated_captures=True) + + pattern = RePattern(r"\[(\d+)\](?:-(\d+))*", repeated_captures=False) + + matches = list(pattern.matches("[02]-03-04-05-06")) + assert len(matches) == 1 + + match = matches[0] + assert len(match.children) == 2 + assert [child.value for child in match.children] == ["02", "06"] + + def test_single_functional(self): + def digit(input_string): + i = input_string.find("1849") + if i > -1: + return i, i + len("1849") + + pattern = FunctionalPattern(digit, formatter=lambda x: int(x) * 3) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + assert isinstance(matches[0], Match) + assert matches[0].pattern == pattern + assert matches[0].span == (21, 25) + assert matches[0].value == 1849 * 3 + + +class TestValidator(object): + """ + Tests for validator option + """ + + input_string = "This string contains 1849 a number" + + @staticmethod + def true_validator(match): + return int(match.value) < 1850 + + @staticmethod + def false_validator(match): + return int(match.value) >= 1850 + + def test_single_string(self): + pattern = StringPattern("1849", name="dummy", validator=self.false_validator) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + pattern = StringPattern("1849", validator=self.true_validator) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + def test_single_re_no_group(self): + pattern = RePattern(r"\d+", validator=self.false_validator) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + pattern = RePattern(r"\d+", validator=self.true_validator) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + def test_single_re_named_groups(self): + pattern = RePattern(r"(?P<strParam>cont.?ins)\s+(?P<intParam>\d+)", + validator={'intParam': self.false_validator}, validate_all=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + pattern = RePattern(r"(?P<strParam>cont.?ins)\s+(?P<intParam>\d+)", + validator={'intParam': self.true_validator}, validate_all=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + def test_validate_all(self): + pattern = RePattern(r"contains (?P<intParam>\d+)", formatter=int, validator=lambda match: match.value < 100, + children=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + pattern = RePattern(r"contains (?P<intParam>\d+)", formatter=int, validator=lambda match: match.value > 100, + children=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + def invalid_func(match): + if match.name == 'intParam': + return True + else: + return match.value.startswith('abc') + + pattern = RePattern(r"contains (?P<intParam>\d+)", formatter=int, validator=invalid_func, validate_all=True, + children=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + def func(match): + if match.name == 'intParam': + return True + else: + return match.value.startswith('contains') + + pattern = RePattern(r"contains (?P<intParam>\d+)", formatter=int, validator=func, validate_all=True, + children=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + + def test_format_all(self): + pattern = RePattern(r"contains (?P<intParam>\d+)", formatter=int, + children=True) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + for match in matches: + assert match.value is not None + + with pytest.raises(ValueError): + pattern = RePattern(r"contains (?P<intParam>\d+)", formatter=int, format_all=True) + matches = list(pattern.matches(self.input_string)) + for match in matches: + assert match.value is not None + + def test_single_functional(self): + def digit(input_string): + i = input_string.find("1849") + if i > -1: + return i, i + len("1849") + + pattern = FunctionalPattern(digit, validator=self.false_validator) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 0 + + pattern = FunctionalPattern(digit, validator=self.true_validator) + + matches = list(pattern.matches(self.input_string)) + assert len(matches) == 1 + diff --git a/lib/rebulk/test/test_processors.py b/lib/rebulk/test/test_processors.py new file mode 100644 index 000000000..099cc47dc --- /dev/null +++ b/lib/rebulk/test/test_processors.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, no-member + +from ..pattern import StringPattern, RePattern +from ..processors import ConflictSolver +from ..rules import execute_rule +from rebulk.match import Matches + + +def test_conflict_1(): + input_string = "abcdefghijklmnopqrstuvwxyz" + + pattern = StringPattern("ijklmn", "kl", "abcdef", "ab", "ef", "yz") + matches = Matches(pattern.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + + values = [x.value for x in matches] + + assert values == ["ijklmn", "abcdef", "yz"] + + +def test_conflict_2(): + input_string = "abcdefghijklmnopqrstuvwxyz" + + pattern = StringPattern("ijklmn", "jklmnopqrst") + matches = Matches(pattern.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + + values = [x.value for x in matches] + + assert values == ["jklmnopqrst"] + + +def test_conflict_3(): + input_string = "abcdefghijklmnopqrstuvwxyz" + + pattern = StringPattern("ijklmnopqrst", "jklmnopqrst") + matches = Matches(pattern.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + + values = [x.value for x in matches] + + assert values == ["ijklmnopqrst"] + + +def test_conflict_4(): + input_string = "123456789" + + pattern = StringPattern("123", "456789") + matches = Matches(pattern.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + + values = [x.value for x in matches] + assert values == ["123", "456789"] + + +def test_conflict_5(): + input_string = "123456789" + + pattern = StringPattern("123456", "789") + matches = Matches(pattern.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + + values = [x.value for x in matches] + assert values == ["123456", "789"] + + +def test_prefer_longer_parent(): + input_string = "xxx.1x02.xxx" + + re1 = RePattern("([0-9]+)x([0-9]+)", name='prefer', children=True, formatter=int) + re2 = RePattern("x([0-9]+)", name='skip', children=True) + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 2 + assert matches[0].value == 1 + assert matches[1].value == 2 + + +def test_conflict_solver_1(): + input_string = "123456789" + + re1 = StringPattern("2345678", conflict_solver=lambda match, conflicting: '__default__') + re2 = StringPattern("34567") + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 1 + assert matches[0].value == "2345678" + + +def test_conflict_solver_2(): + input_string = "123456789" + + re1 = StringPattern("2345678", conflict_solver=lambda match, conflicting: '__default__') + re2 = StringPattern("34567", conflict_solver=lambda match, conflicting: conflicting) + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 1 + assert matches[0].value == "34567" + + +def test_conflict_solver_3(): + input_string = "123456789" + + re1 = StringPattern("2345678", conflict_solver=lambda match, conflicting: match) + re2 = StringPattern("34567") + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 1 + assert matches[0].value == "34567" + + +def test_conflict_solver_4(): + input_string = "123456789" + + re1 = StringPattern("2345678") + re2 = StringPattern("34567", conflict_solver=lambda match, conflicting: conflicting) + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 1 + assert matches[0].value == "34567" + + +def test_conflict_solver_5(): + input_string = "123456789" + + re1 = StringPattern("2345678", conflict_solver=lambda match, conflicting: conflicting) + re2 = StringPattern("34567") + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 1 + assert matches[0].value == "2345678" + + +def test_conflict_solver_6(): + input_string = "123456789" + + re1 = StringPattern("2345678") + re2 = StringPattern("34567", conflict_solver=lambda match, conflicting: conflicting) + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 1 + assert matches[0].value == "34567" + + +def test_conflict_solver_7(): + input_string = "102" + + re1 = StringPattern("102") + re2 = StringPattern("02") + + matches = Matches(re2.matches(input_string)) + matches.extend(re1.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 1 + assert matches[0].value == "102" + + +def test_unresolved(): + input_string = "123456789" + + re1 = StringPattern("23456") + re2 = StringPattern("34567") + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 2 + + re1 = StringPattern("34567") + re2 = StringPattern("2345678", conflict_solver=lambda match, conflicting: None) + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 2 + + re1 = StringPattern("34567", conflict_solver=lambda match, conflicting: None) + re2 = StringPattern("2345678") + + matches = Matches(re1.matches(input_string)) + matches.extend(re2.matches(input_string)) + + execute_rule(ConflictSolver(), matches, None) + assert len(matches) == 2 diff --git a/lib/rebulk/test/test_rebulk.py b/lib/rebulk/test/test_rebulk.py new file mode 100644 index 000000000..a29361ca9 --- /dev/null +++ b/lib/rebulk/test/test_rebulk.py @@ -0,0 +1,408 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, no-member + +from ..rebulk import Rebulk +from ..rules import Rule +import rebulk.test.rebulk_rules_module as rm + + +def test_rebulk_simple(): + rebulk = Rebulk() + + rebulk.string("quick") + rebulk.regex("f.x") + + def func(input_string): + i = input_string.find("over") + if i > -1: + return i, i + len("over") + + rebulk.functional(func) + + input_string = "The quick brown fox jumps over the lazy dog" + + matches = rebulk.matches(input_string) + assert len(matches) == 3 + + assert matches[0].value == "quick" + assert matches[1].value == "fox" + assert matches[2].value == "over" + + +def test_rebulk_composition(): + rebulk = Rebulk() + + rebulk.string("quick") + rebulk.rebulk(Rebulk().regex("f.x")) + + rebulk.rebulk(Rebulk(disabled=lambda context: True).functional(lambda string: None)) + + input_string = "The quick brown fox jumps over the lazy dog" + + matches = rebulk.matches(input_string) + assert len(matches) == 2 + + assert matches[0].value == "quick" + assert matches[1].value == "fox" + + +def test_rebulk_context(): + rebulk = Rebulk() + + context = {'nostring': True, 'word': 'lazy'} + + rebulk.string("quick", disabled=lambda context: context.get('nostring', False)) + rebulk.regex("f.x", disabled=lambda context: context.get('noregex', False)) + + def func(input_string, context): + word = context.get('word', 'over') + i = input_string.find(word) + if i > -1: + return i, i + len(word) + + rebulk.functional(func) + + input_string = "The quick brown fox jumps over the lazy dog" + + matches = rebulk.matches(input_string, context) + assert len(matches) == 2 + + assert matches[0].value == "fox" + assert matches[1].value == "lazy" + + +def test_rebulk_prefer_longer(): + input_string = "The quick brown fox jumps over the lazy dog" + + matches = Rebulk().string("quick").string("own").regex("br.{2}n").matches(input_string) + + assert len(matches) == 2 + + assert matches[0].value == "quick" + assert matches[1].value == "brown" + + +def test_rebulk_defaults(): + input_string = "The quick brown fox jumps over the lazy dog" + + def func(input_string): + i = input_string.find("fox") + if i > -1: + return i, i + len("fox") + + matches = Rebulk()\ + .string_defaults(name="string", tags=["a", "b"])\ + .regex_defaults(name="regex") \ + .functional_defaults(name="functional") \ + .string("quick", tags=["c"])\ + .functional(func)\ + .regex("br.{2}n") \ + .matches(input_string) + assert matches[0].name == "string" + assert matches[0].tags == ["a", "b", "c"] + assert matches[1].name == "functional" + assert matches[2].name == "regex" + + matches = Rebulk() \ + .defaults(name="default", tags=["0"])\ + .string_defaults(name="string", tags=["a", "b"]) \ + .functional_defaults(name="functional", tags=["1"]) \ + .string("quick", tags=["c"]) \ + .functional(func) \ + .regex("br.{2}n") \ + .matches(input_string) + assert matches[0].name == "string" + assert matches[0].tags == ["0", "a", "b", "c"] + assert matches[1].name == "functional" + assert matches[1].tags == ["0", "1"] + assert matches[2].name == "default" + assert matches[2].tags == ["0"] + + +def test_rebulk_rebulk(): + input_string = "The quick brown fox jumps over the lazy dog" + + base = Rebulk().string("quick") + child = Rebulk().string("own").regex("br.{2}n") + + matches = base.rebulk(child).matches(input_string) + + assert len(matches) == 2 + + assert matches[0].value == "quick" + assert matches[1].value == "brown" + + +def test_rebulk_no_default(): + input_string = "The quick brown fox jumps over the lazy dog" + + matches = Rebulk(default_rules=False).string("quick").string("own").regex("br.{2}n").matches(input_string) + + assert len(matches) == 3 + + assert matches[0].value == "quick" + assert matches[1].value == "own" + assert matches[2].value == "brown" + + +def test_rebulk_tags_names(): + rebulk = Rebulk() + + rebulk.string("quick", name="str", tags=["first", "other"]) + rebulk.regex("f.x", tags="other") + + def func(input_string): + i = input_string.find("over") + if i > -1: + return i, i + len("over"), {'tags': ['custom']} + + rebulk.functional(func, name="fn") + + def func2(input_string): + i = input_string.find("lazy") + if i > -1: + return {'start': i, 'end': i + len("lazy"), 'tags': ['custom']} + + rebulk.functional(func2, name="fn") + + input_string = "The quick brown fox jumps over the lazy dog" + + matches = rebulk.matches(input_string) + assert len(matches) == 4 + + assert len(matches.named("str")) == 1 + assert len(matches.named("fn")) == 2 + assert len(matches.named("false")) == 0 + assert len(matches.tagged("false")) == 0 + assert len(matches.tagged("first")) == 1 + assert len(matches.tagged("other")) == 2 + assert len(matches.tagged("custom")) == 2 + + +def test_rebulk_rules_1(): + rebulk = Rebulk() + + rebulk.regex(r'\d{4}', name="year") + rebulk.rules(rm.RemoveAllButLastYear) + + matches = rebulk.matches("1984 keep only last 1968 entry 1982 case") + assert len(matches) == 1 + assert matches[0].value == "1982" + + +def test_rebulk_rules_2(): + rebulk = Rebulk() + + rebulk.regex(r'\d{4}', name="year") + rebulk.string(r'year', name="yearPrefix", private=True) + rebulk.string(r'keep', name="yearSuffix", private=True) + rebulk.rules(rm.PrefixedSuffixedYear) + + matches = rebulk.matches("Keep suffix 1984 keep prefixed year 1968 and remove the rest 1982") + assert len(matches) == 2 + assert matches[0].value == "1984" + assert matches[1].value == "1968" + + +def test_rebulk_rules_3(): + rebulk = Rebulk() + + rebulk.regex(r'\d{4}', name="year") + rebulk.string(r'year', name="yearPrefix", private=True) + rebulk.string(r'keep', name="yearSuffix", private=True) + rebulk.rules(rm.PrefixedSuffixedYearNoLambda) + + matches = rebulk.matches("Keep suffix 1984 keep prefixed year 1968 and remove the rest 1982") + assert len(matches) == 2 + assert matches[0].value == "1984" + assert matches[1].value == "1968" + + +def test_rebulk_rules_4(): + class FirstOnlyRule(Rule): + def when(self, matches, context): + grabbed = matches.named("grabbed", 0) + if grabbed and matches.previous(grabbed): + return grabbed + + def then(self, matches, when_response, context): + matches.remove(when_response) + + rebulk = Rebulk() + + rebulk.regex("This match (.*?)grabbed", name="grabbed") + rebulk.regex("if it's (.*?)first match", private=True) + + rebulk.rules(FirstOnlyRule) + + matches = rebulk.matches("This match is grabbed only if it's the first match") + assert len(matches) == 1 + assert matches[0].value == "This match is grabbed" + + matches = rebulk.matches("if it's NOT the first match, This match is NOT grabbed") + assert len(matches) == 0 + + +class TestMarkers(object): + def test_one_marker(self): + class MarkerRule(Rule): + def when(self, matches, context): + word_match = matches.named("word", 0) + marker = matches.markers.at_match(word_match, lambda marker: marker.name == "mark1", 0) + if not marker: + return word_match + + def then(self, matches, when_response, context): + matches.remove(when_response) + + rebulk = Rebulk().regex(r'\(.*?\)', marker=True, name="mark1") \ + .regex(r'\[.*?\]', marker=True, name="mark2") \ + .string("word", name="word") \ + .rules(MarkerRule) + + matches = rebulk.matches("grab (word) only if it's in parenthesis") + + assert len(matches) == 1 + assert matches[0].value == "word" + + matches = rebulk.matches("don't grab [word] if it's in braket") + assert len(matches) == 0 + + matches = rebulk.matches("don't grab word at all") + assert len(matches) == 0 + + def test_multiple_marker(self): + class MarkerRule(Rule): + def when(self, matches, context): + word_match = matches.named("word", 0) + marker = matches.markers.at_match(word_match, + lambda marker: marker.name == "mark1" or marker.name == "mark2") + if len(marker) < 2: + return word_match + + def then(self, matches, when_response, context): + matches.remove(when_response) + + rebulk = Rebulk().regex(r'\(.*?\)', marker=True, name="mark1") \ + .regex(r'\[.*?\]', marker=True, name="mark2") \ + .regex("w.*?d", name="word") \ + .rules(MarkerRule) + + matches = rebulk.matches("[grab (word) only] if it's in parenthesis and brakets") + + assert len(matches) == 1 + assert matches[0].value == "word" + + matches = rebulk.matches("[don't grab](word)[if brakets are outside]") + assert len(matches) == 0 + + matches = rebulk.matches("(grab w[or)d even] if it's partially in parenthesis and brakets") + assert len(matches) == 1 + assert matches[0].value == "w[or)d" + + def test_at_index_marker(self): + class MarkerRule(Rule): + def when(self, matches, context): + word_match = matches.named("word", 0) + marker = matches.markers.at_index(word_match.start, + lambda marker: marker.name == "mark1", 0) + if not marker: + return word_match + + def then(self, matches, when_response, context): + matches.remove(when_response) + + rebulk = Rebulk().regex(r'\(.*?\)', marker=True, name="mark1") \ + .regex("w.*?d", name="word") \ + .rules(MarkerRule) + + matches = rebulk.matches("gr(ab wo)rd only if starting of match is inside parenthesis") + + assert len(matches) == 1 + assert matches[0].value == "wo)rd" + + matches = rebulk.matches("don't grab wo(rd if starting of match is not inside parenthesis") + + assert len(matches) == 0 + + def test_remove_marker(self): + class MarkerRule(Rule): + def when(self, matches, context): + marker = matches.markers.named("mark1", 0) + if marker: + return marker + + def then(self, matches, when_response, context): + matches.markers.remove(when_response) + + rebulk = Rebulk().regex(r'\(.*?\)', marker=True, name="mark1") \ + .regex("w.*?d", name="word") \ + .rules(MarkerRule) + + matches = rebulk.matches("grab word event (if it's not) inside parenthesis") + + assert len(matches) == 1 + assert matches[0].value == "word" + + assert not matches.markers + + +class TestUnicode(object): + def test_rebulk_simple(self): + input_string = u"敏捷的棕色狐狸跳過懶狗" + + rebulk = Rebulk() + + rebulk.string(u"敏") + rebulk.regex(u"捷") + + def func(input_string): + i = input_string.find(u"的") + if i > -1: + return i, i + len(u"的") + + rebulk.functional(func) + + matches = rebulk.matches(input_string) + assert len(matches) == 3 + + assert matches[0].value == u"敏" + assert matches[1].value == u"捷" + assert matches[2].value == u"的" + + +class TestImmutable(object): + def test_starting(self): + input_string = "The quick brown fox jumps over the lazy dog" + matches = Rebulk().string("quick").string("over").string("fox").matches(input_string) + + for i in range(0, len(input_string)): + starting = matches.starting(i) + for match in list(starting): + starting.remove(match) + + assert len(matches) == 3 + + def test_ending(self): + input_string = "The quick brown fox jumps over the lazy dog" + matches = Rebulk().string("quick").string("over").string("fox").matches(input_string) + + for i in range(0, len(input_string)): + starting = matches.ending(i) + for match in list(starting): + starting.remove(match) + + assert len(matches) == 3 + + def test_named(self): + input_string = "The quick brown fox jumps over the lazy dog" + matches = Rebulk().defaults(name='test').string("quick").string("over").string("fox").matches(input_string) + + named = matches.named('test') + for match in list(named): + named.remove(match) + + assert len(named) == 0 + assert len(matches) == 3 + diff --git a/lib/rebulk/test/test_rules.py b/lib/rebulk/test/test_rules.py new file mode 100644 index 000000000..3d53b591e --- /dev/null +++ b/lib/rebulk/test/test_rules.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name, no-member +import pytest +from rebulk.test.default_rules_module import RuleRemove0, RuleAppend0, RuleRename0, RuleAppend1, RuleRemove1, \ + RuleRename1, RuleAppend2, RuleRename2, RuleAppend3, RuleRename3, RuleAppendTags0, RuleRemoveTags0, \ + RuleAppendTags1, RuleRemoveTags1 + +from ..rules import Rules +from ..match import Matches, Match + +from .rules_module import Rule1, Rule2, Rule3, Rule0, Rule1Disabled +import rebulk.test.rules_module as rm + + +def test_rule_priority(): + matches = Matches([Match(1, 2)]) + + rules = Rules(Rule1, Rule2()) + + rules.execute_all_rules(matches, {}) + assert len(matches) == 0 + matches = Matches([Match(1, 2)]) + + rules = Rules(Rule1(), Rule0) + + rules.execute_all_rules(matches, {}) + assert len(matches) == 1 + assert matches[0] == Match(3, 4) + + +def test_rules_duplicates(): + matches = Matches([Match(1, 2)]) + + rules = Rules(Rule1, Rule1) + + with pytest.raises(ValueError): + rules.execute_all_rules(matches, {}) + + +def test_rule_disabled(): + matches = Matches([Match(1, 2)]) + + rules = Rules(Rule1Disabled(), Rule2()) + + rules.execute_all_rules(matches, {}) + assert len(matches) == 2 + assert matches[0] == Match(1, 2) + assert matches[1] == Match(3, 4) + + +def test_rule_when(): + matches = Matches([Match(1, 2)]) + + rules = Rules(Rule3()) + + rules.execute_all_rules(matches, {'when': False}) + assert len(matches) == 1 + assert matches[0] == Match(1, 2) + + matches = Matches([Match(1, 2)]) + + rules.execute_all_rules(matches, {'when': True}) + assert len(matches) == 2 + assert matches[0] == Match(1, 2) + assert matches[1] == Match(3, 4) + + +class TestDefaultRules(object): + def test_remove(self): + rules = Rules(RuleRemove0) + + matches = Matches([Match(1, 2)]) + rules.execute_all_rules(matches, {}) + + assert len(matches) == 0 + + rules = Rules(RuleRemove1) + + matches = Matches([Match(1, 2)]) + rules.execute_all_rules(matches, {}) + + assert len(matches) == 0 + + def test_append(self): + rules = Rules(RuleAppend0) + + matches = Matches([Match(1, 2)]) + rules.execute_all_rules(matches, {}) + + assert len(matches) == 2 + + rules = Rules(RuleAppend1) + + matches = Matches([Match(1, 2)]) + rules.execute_all_rules(matches, {}) + + assert len(matches) == 2 + + rules = Rules(RuleAppend2) + + matches = Matches([Match(1, 2)]) + rules.execute_all_rules(matches, {}) + + assert len(matches) == 2 + assert len(matches.named('renamed')) == 1 + + rules = Rules(RuleAppend3) + + matches = Matches([Match(1, 2)]) + rules.execute_all_rules(matches, {}) + + assert len(matches) == 2 + assert len(matches.named('renamed')) == 1 + + def test_rename(self): + rules = Rules(RuleRename0) + + matches = Matches([Match(1, 2, name='original')]) + rules.execute_all_rules(matches, {}) + + assert len(matches.named('original')) == 1 + assert len(matches.named('renamed')) == 0 + + rules = Rules(RuleRename1) + + matches = Matches([Match(5, 10, name='original')]) + rules.execute_all_rules(matches, {}) + + assert len(matches.named('original')) == 0 + assert len(matches.named('renamed')) == 1 + + rules = Rules(RuleRename2) + + matches = Matches([Match(5, 10, name='original')]) + rules.execute_all_rules(matches, {}) + + assert len(matches.named('original')) == 0 + assert len(matches.named('renamed')) == 1 + + rules = Rules(RuleRename3) + + matches = Matches([Match(5, 10, name='original')]) + rules.execute_all_rules(matches, {}) + + assert len(matches.named('original')) == 0 + assert len(matches.named('renamed')) == 1 + + def test_append_tags(self): + rules = Rules(RuleAppendTags0) + + matches = Matches([Match(1, 2, name='tags', tags=['other'])]) + rules.execute_all_rules(matches, {}) + + assert len(matches.named('tags')) == 1 + assert matches.named('tags', index=0).tags == ['other', 'new-tag'] + + rules = Rules(RuleAppendTags1) + + matches = Matches([Match(1, 2, name='tags', tags=['other'])]) + rules.execute_all_rules(matches, {}) + + assert len(matches.named('tags')) == 1 + assert matches.named('tags', index=0).tags == ['other', 'new-tag'] + + def test_remove_tags(self): + rules = Rules(RuleRemoveTags0) + + matches = Matches([Match(1, 2, name='tags', tags=['other', 'new-tag'])]) + rules.execute_all_rules(matches, {}) + + assert len(matches.named('tags')) == 1 + assert matches.named('tags', index=0).tags == ['other'] + + rules = Rules(RuleRemoveTags1) + + matches = Matches([Match(1, 2, name='tags', tags=['other', 'new-tag'])]) + rules.execute_all_rules(matches, {}) + + assert len(matches.named('tags')) == 1 + assert matches.named('tags', index=0).tags == ['other'] + + +def test_rule_module(): + rules = Rules(rm) + + matches = Matches([Match(1, 2)]) + rules.execute_all_rules(matches, {}) + + assert len(matches) == 1 + + +def test_rule_repr(): + assert str(Rule0()) == "<Rule0>" + assert str(Rule1()) == "<Rule1>" + assert str(Rule2()) == "<Rule2>" + assert str(Rule1Disabled()) == "<Disabled Rule1>" diff --git a/lib/rebulk/test/test_toposort.py b/lib/rebulk/test/test_toposort.py new file mode 100644 index 000000000..76ea60313 --- /dev/null +++ b/lib/rebulk/test/test_toposort.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright 2014 True Blade Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Original: +# - https://bitbucket.org/ericvsmith/toposort (1.4) +# Modifications: +# - port to pytest +# pylint: skip-file + +import pytest +from ..toposort import toposort, toposort_flatten, CyclicDependency + + +class TestCase(object): + def test_simple(self): + results = list(toposort({2: set([11]), 9: set([11, 8]), 10: set([11, 3]), 11: set([7, 5]), 8: set([7, 3])})) + expected = [set([3, 5, 7]), set([8, 11]), set([2, 9, 10])] + assert results == expected + + # make sure self dependencies are ignored + results = list(toposort({2: set([2, 11]), 9: set([11, 8]), 10: set([10, 11, 3]), 11: set([7, 5]), 8: set([7, 3])})) + expected = [set([3, 5, 7]), set([8, 11]), set([2, 9, 10])] + assert results == expected + + assert list(toposort({1: set()})) == [set([1])] + assert list(toposort({1: set([1])})) == [set([1])] + + def test_no_dependencies(self): + assert list(toposort({1: set([2]), 3: set([4]), 5: set([6])})) == [set([2, 4, 6]), set([1, 3, 5])] + assert list(toposort({1: set(), 3: set(), 5: set()})) == [set([1, 3, 5])] + + def test_empty(self): + assert list(toposort({})) == [] + + def test_strings(self): + results = list(toposort({'2': set(['11']), '9': set(['11', '8']), '10': set(['11', '3']), '11': set(['7', '5']), '8': set(['7', '3'])})) + expected = [set(['3', '5', '7']), set(['8', '11']), set(['2', '9', '10'])] + assert results == expected + + def test_objects(self): + o2 = object() + o3 = object() + o5 = object() + o7 = object() + o8 = object() + o9 = object() + o10 = object() + o11 = object() + results = list(toposort({o2: set([o11]), o9: set([o11, o8]), o10: set([o11, o3]), o11: set([o7, o5]), o8: set([o7, o3, o8])})) + expected = [set([o3, o5, o7]), set([o8, o11]), set([o2, o9, o10])] + assert results == expected + + def test_cycle(self): + # a simple, 2 element cycle + with pytest.raises(CyclicDependency): + list(toposort({1: set([2]), 2: set([1])})) + + # an indirect cycle + with pytest.raises(CyclicDependency): + list(toposort({1: set([2]), 2: set([3]), 3: set([1])})) + + def test_input_not_modified(self): + data = {2: set([11]), + 9: set([11, 8]), + 10: set([11, 3]), + 11: set([7, 5]), + 8: set([7, 3, 8]), # includes something self-referential + } + orig = data.copy() + results = list(toposort(data)) + assert data == orig + + def test_input_not_modified_when_cycle_error(self): + data = {1: set([2]), + 2: set([1]), + 3: set([4]), + } + orig = data.copy() + with pytest.raises(CyclicDependency): + list(toposort(data)) + assert data == orig + + +class TestCaseAll(object): + def test_sort_flatten(self): + data = {2: set([11]), + 9: set([11, 8]), + 10: set([11, 3]), + 11: set([7, 5]), + 8: set([7, 3, 8]), # includes something self-referential + } + expected = [set([3, 5, 7]), set([8, 11]), set([2, 9, 10])] + assert list(toposort(data)) == expected + + # now check the sorted results + results = [] + for item in expected: + results.extend(sorted(item)) + assert toposort_flatten(data) == results + + # and the unsorted results. break the results up into groups to compare them + actual = toposort_flatten(data, False) + results = [set([i for i in actual[0:3]]), set([i for i in actual[3:5]]), set([i for i in actual[5:8]])] + assert results == expected diff --git a/lib/rebulk/test/test_validators.py b/lib/rebulk/test/test_validators.py new file mode 100644 index 000000000..ef5c756d8 --- /dev/null +++ b/lib/rebulk/test/test_validators.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name + +from functools import partial + +from rebulk.pattern import StringPattern + +from ..validators import chars_before, chars_after, chars_surround, validators + +chars = ' _.' +left = partial(chars_before, chars) +right = partial(chars_after, chars) +surrounding = partial(chars_surround, chars) + + +def test_left_chars(): + matches = list(StringPattern("word", validator=left).matches("xxxwordxxx")) + assert len(matches) == 0 + + matches = list(StringPattern("word", validator=left).matches("xxx_wordxxx")) + assert len(matches) == 1 + + matches = list(StringPattern("word", validator=left).matches("wordxxx")) + assert len(matches) == 1 + + +def test_right_chars(): + matches = list(StringPattern("word", validator=right).matches("xxxwordxxx")) + assert len(matches) == 0 + + matches = list(StringPattern("word", validator=right).matches("xxxword.xxx")) + assert len(matches) == 1 + + matches = list(StringPattern("word", validator=right).matches("xxxword")) + assert len(matches) == 1 + + +def test_surrounding_chars(): + matches = list(StringPattern("word", validator=surrounding).matches("xxxword xxx")) + assert len(matches) == 0 + + matches = list(StringPattern("word", validator=surrounding).matches("xxx.wordxxx")) + assert len(matches) == 0 + + matches = list(StringPattern("word", validator=surrounding).matches("xxx word_xxx")) + assert len(matches) == 1 + + matches = list(StringPattern("word", validator=surrounding).matches("word")) + assert len(matches) == 1 + + +def test_chain(): + matches = list(StringPattern("word", validator=validators(left, right)).matches("xxxword xxx")) + assert len(matches) == 0 + + matches = list(StringPattern("word", validator=validators(left, right)).matches("xxx.wordxxx")) + assert len(matches) == 0 + + matches = list(StringPattern("word", validator=validators(left, right)).matches("xxx word_xxx")) + assert len(matches) == 1 + + matches = list(StringPattern("word", validator=validators(left, right)).matches("word")) + assert len(matches) == 1 + + + + diff --git a/lib/rebulk/toposort.py b/lib/rebulk/toposort.py new file mode 100644 index 000000000..2bcba9ae6 --- /dev/null +++ b/lib/rebulk/toposort.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright 2014 True Blade Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Original: +# - https://bitbucket.org/ericvsmith/toposort (1.4) +# Modifications: +# - merged Pull request #2 for CyclicDependency error +# - import reduce as original name +# - support python 2.6 dict comprehension + +# pylint: skip-file +from functools import reduce + + +class CyclicDependency(ValueError): + def __init__(self, cyclic): + s = 'Cyclic dependencies exist among these items: {0}'.format(', '.join(repr(x) for x in cyclic.items())) + super(CyclicDependency, self).__init__(s) + self.cyclic = cyclic + + +def toposort(data): + """ + Dependencies are expressed as a dictionary whose keys are items + and whose values are a set of dependent items. Output is a list of + sets in topological order. The first set consists of items with no + dependences, each subsequent set consists of items that depend upon + items in the preceeding sets. + :param data: + :type data: + :return: + :rtype: + """ + + # Special case empty input. + if len(data) == 0: + return + + # Copy the input so as to leave it unmodified. + data = data.copy() + + # Ignore self dependencies. + for k, v in data.items(): + v.discard(k) + # Find all items that don't depend on anything. + extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys()) + # Add empty dependences where needed. + data.update(dict((item, set()) for item in extra_items_in_deps)) + while True: + ordered = set(item for item, dep in data.items() if len(dep) == 0) + if not ordered: + break + yield ordered + data = dict((item, (dep - ordered)) + for item, dep in data.items() + if item not in ordered) + if len(data) != 0: + raise CyclicDependency(data) + + +def toposort_flatten(data, sort=True): + """ + Returns a single list of dependencies. For any set returned by + toposort(), those items are sorted and appended to the result (just to + make the results deterministic). + :param data: + :type data: + :param sort: + :type sort: + :return: Single list of dependencies. + :rtype: list + """ + + result = [] + for d in toposort(data): + result.extend((sorted if sort else list)(d)) + return result diff --git a/lib/rebulk/utils.py b/lib/rebulk/utils.py new file mode 100644 index 000000000..6e8ffb23a --- /dev/null +++ b/lib/rebulk/utils.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Various utilities functions +""" +from types import GeneratorType + +from collections import MutableSet + + +def find_all(string, sub, start=None, end=None, ignore_case=False): + """ + Return all indices in string s where substring sub is + found, such that sub is contained in the slice s[start:end]. + + >>> list(find_all('The quick brown fox jumps over the lazy dog', 'fox')) + [16] + + >>> list(find_all('The quick brown fox jumps over the lazy dog', 'mountain')) + [] + + >>> list(find_all('The quick brown fox jumps over the lazy dog', 'The')) + [0] + + >>> list(find_all( + ... 'Carved symbols in a mountain hollow on the bank of an inlet irritated an eccentric person', + ... 'an')) + [44, 51, 70] + + >>> list(find_all( + ... 'Carved symbols in a mountain hollow on the bank of an inlet irritated an eccentric person', + ... 'an', + ... 50, + ... 60)) + [51] + + :param string: the input string + :type string: str + :param sub: the substring + :type sub: str + :return: all indices in the input string + :rtype: __generator[str] + """ + if ignore_case: + sub = sub.lower() + string = string.lower() + while True: + start = string.find(sub, start, end) + if start == -1: + return + yield start + start += len(sub) + + +def is_iterable(obj): + """ + Are we being asked to look up a list of things, instead of a single thing? + We check for the `__iter__` attribute so that this can cover types that + don't have to be known by this module, such as NumPy arrays. + + Strings, however, should be considered as atomic values to look up, not + iterables. + + We don't need to check for the Python 2 `unicode` type, because it doesn't + have an `__iter__` attribute anyway. + """ + return hasattr(obj, '__iter__') and not isinstance(obj, str) or isinstance(obj, GeneratorType) + + +def extend_safe(target, source): + """ + Extends source list to target list only if elements doesn't exists in target list. + :param target: + :type target: list + :param source: + :type source: list + """ + for elt in source: + if elt not in target: + target.append(elt) + + +class _Ref(object): + """ + Reference for IdentitySet + """ + def __init__(self, value): + self.value = value + + def __eq__(self, other): + return self.value is other.value + + def __hash__(self): + return id(self.value) + + +class IdentitySet(MutableSet): # pragma: no cover + """ + Set based on identity + """ + def __init__(self, items=None): + if items is None: + items = [] + self.refs = set(map(_Ref, items)) + + def __contains__(self, elem): + return _Ref(elem) in self.refs + + def __iter__(self): + return (ref.value for ref in self.refs) + + def __len__(self): + return len(self.refs) + + def add(self, elem): + self.refs.add(_Ref(elem)) + + def discard(self, elem): + self.refs.discard(_Ref(elem)) + + def update(self, iterable): + """ + Update set with iterable + :param iterable: + :type iterable: + :return: + :rtype: + """ + for elem in iterable: + self.add(elem) + + def __repr__(self): # pragma: no cover + return "%s(%s)" % (type(self).__name__, list(self)) diff --git a/lib/rebulk/validators.py b/lib/rebulk/validators.py new file mode 100644 index 000000000..7d5e6303b --- /dev/null +++ b/lib/rebulk/validators.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Validator functions to use in patterns. + +All those function have last argument as match, so it's possible to use functools.partial to bind previous arguments. +""" + + +def chars_before(chars, match): + """ + Validate the match if left character is in a given sequence. + + :param chars: + :type chars: + :param match: + :type match: + :return: + :rtype: + """ + if match.start <= 0: + return True + return match.input_string[match.start - 1] in chars + + +def chars_after(chars, match): + """ + Validate the match if left character is in a given sequence. + + :param chars: + :type chars: + :param match: + :type match: + :return: + :rtype: + """ + if match.end >= len(match.input_string): + return True + return match.input_string[match.end] in chars + + +def chars_surround(chars, match): + """ + Validate the match if surrounding characters are in a given sequence. + + :param chars: + :type chars: + :param match: + :type match: + :return: + :rtype: + """ + return chars_before(chars, match) and chars_after(chars, match) + + +def validators(*chained_validators): + """ + Creates a validator chain from several validator functions. + + :param chained_validators: + :type chained_validators: + :return: + :rtype: + """ + def validator_chain(match): # pylint:disable=missing-docstring + for chained_validator in chained_validators: + if not chained_validator(match): + return False + return True + return validator_chain diff --git a/lib/regex/__init__.py b/lib/regex/__init__.py new file mode 100644 index 000000000..098b02f2a --- /dev/null +++ b/lib/regex/__init__.py @@ -0,0 +1,703 @@ +# +# Secret Labs' Regular Expression Engine +# +# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. +# +# This version of the SRE library can be redistributed under CNRI's +# Python 1.6 license. For any other use, please contact Secret Labs +# AB (info@pythonware.com). +# +# Portions of this engine have been developed in cooperation with +# CNRI. Hewlett-Packard provided funding for 1.6 integration and +# other compatibility work. +# +# 2010-01-16 mrab Python front-end re-written and extended + +r"""Support for regular expressions (RE). + +This module provides regular expression matching operations similar to those +found in Perl. It supports both 8-bit and Unicode strings; both the pattern and +the strings being processed can contain null bytes and characters outside the +US ASCII range. + +Regular expressions can contain both special and ordinary characters. Most +ordinary characters, like "A", "a", or "0", are the simplest regular +expressions; they simply match themselves. You can concatenate ordinary +characters, so last matches the string 'last'. + +There are a few differences between the old (legacy) behaviour and the new +(enhanced) behaviour, which are indicated by VERSION0 or VERSION1. + +The special characters are: + "." Matches any character except a newline. + "^" Matches the start of the string. + "$" Matches the end of the string or just before the + newline at the end of the string. + "*" Matches 0 or more (greedy) repetitions of the preceding + RE. Greedy means that it will match as many repetitions + as possible. + "+" Matches 1 or more (greedy) repetitions of the preceding + RE. + "?" Matches 0 or 1 (greedy) of the preceding RE. + *?,+?,?? Non-greedy versions of the previous three special + characters. + *+,++,?+ Possessive versions of the previous three special + characters. + {m,n} Matches from m to n repetitions of the preceding RE. + {m,n}? Non-greedy version of the above. + {m,n}+ Possessive version of the above. + {...} Fuzzy matching constraints. + "\\" Either escapes special characters or signals a special + sequence. + [...] Indicates a set of characters. A "^" as the first + character indicates a complementing set. + "|" A|B, creates an RE that will match either A or B. + (...) Matches the RE inside the parentheses. The contents are + captured and can be retrieved or matched later in the + string. + (?flags-flags) VERSION1: Sets/clears the flags for the remainder of + the group or pattern; VERSION0: Sets the flags for the + entire pattern. + (?:...) Non-capturing version of regular parentheses. + (?>...) Atomic non-capturing version of regular parentheses. + (?flags-flags:...) Non-capturing version of regular parentheses with local + flags. + (?P<name>...) The substring matched by the group is accessible by + name. + (?<name>...) The substring matched by the group is accessible by + name. + (?P=name) Matches the text matched earlier by the group named + name. + (?#...) A comment; ignored. + (?=...) Matches if ... matches next, but doesn't consume the + string. + (?!...) Matches if ... doesn't match next. + (?<=...) Matches if preceded by .... + (?<!...) Matches if not preceded by .... + (?(id)yes|no) Matches yes pattern if group id matched, the (optional) + no pattern otherwise. + (?(DEFINE)...) If there's no group called "DEFINE", then ... will be + ignored, but any group definitions will be available. + (?|...|...) (?|A|B), creates an RE that will match either A or B, + but reuses capture group numbers across the + alternatives. + (*FAIL) Forces matching to fail, which means immediate + backtracking. + (*F) Abbreviation for (*FAIL). + (*PRUNE) Discards the current backtracking information. Its + effect doesn't extend outside an atomic group or a + lookaround. + (*SKIP) Similar to (*PRUNE), except that it also sets where in + the text the next attempt at matching the entire + pattern will start. Its effect doesn't extend outside + an atomic group or a lookaround. + +The fuzzy matching constraints are: "i" to permit insertions, "d" to permit +deletions, "s" to permit substitutions, "e" to permit any of these. Limits are +optional with "<=" and "<". If any type of error is provided then any type not +provided is not permitted. + +A cost equation may be provided. + +Examples: + (?:fuzzy){i<=2} + (?:fuzzy){i<=1,s<=2,d<=1,1i+1s+1d<3} + +VERSION1: Set operators are supported, and a set can include nested sets. The +set operators, in order of increasing precedence, are: + || Set union ("x||y" means "x or y"). + ~~ (double tilde) Symmetric set difference ("x~~y" means "x or y, but not + both"). + && Set intersection ("x&&y" means "x and y"). + -- (double dash) Set difference ("x--y" means "x but not y"). + +Implicit union, ie, simple juxtaposition like in [ab], has the highest +precedence. + +VERSION0 and VERSION1: +The special sequences consist of "\\" and a character from the list below. If +the ordinary character is not on the list, then the resulting RE will match the +second character. + \number Matches the contents of the group of the same number if + number is no more than 2 digits, otherwise the character + with the 3-digit octal code. + \a Matches the bell character. + \A Matches only at the start of the string. + \b Matches the empty string, but only at the start or end of a + word. + \B Matches the empty string, but not at the start or end of a + word. + \d Matches any decimal digit; equivalent to the set [0-9] when + matching a bytestring or a Unicode string with the ASCII + flag, or the whole range of Unicode digits when matching a + Unicode string. + \D Matches any non-digit character; equivalent to [^\d]. + \f Matches the formfeed character. + \g<name> Matches the text matched by the group named name. + \G Matches the empty string, but only at the position where + the search started. + \K Keeps only what follows for the entire match. + \L<name> Named list. The list is provided as a keyword argument. + \m Matches the empty string, but only at the start of a word. + \M Matches the empty string, but only at the end of a word. + \n Matches the newline character. + \N{name} Matches the named character. + \p{name=value} Matches the character if its property has the specified + value. + \P{name=value} Matches the character if its property hasn't the specified + value. + \r Matches the carriage-return character. + \s Matches any whitespace character; equivalent to + [ \t\n\r\f\v]. + \S Matches any non-whitespace character; equivalent to [^\s]. + \t Matches the tab character. + \uXXXX Matches the Unicode codepoint with 4-digit hex code XXXX. + \UXXXXXXXX Matches the Unicode codepoint with 8-digit hex code + XXXXXXXX. + \v Matches the vertical tab character. + \w Matches any alphanumeric character; equivalent to + [a-zA-Z0-9_] when matching a bytestring or a Unicode string + with the ASCII flag, or the whole range of Unicode + alphanumeric characters (letters plus digits plus + underscore) when matching a Unicode string. With LOCALE, it + will match the set [0-9_] plus characters defined as + letters for the current locale. + \W Matches the complement of \w; equivalent to [^\w]. + \xXX Matches the character with 2-digit hex code XX. + \X Matches a grapheme. + \Z Matches only at the end of the string. + \\ Matches a literal backslash. + +This module exports the following functions: + match Match a regular expression pattern at the beginning of a string. + fullmatch Match a regular expression pattern against all of a string. + search Search a string for the presence of a pattern. + sub Substitute occurrences of a pattern found in a string using a + template string. + subf Substitute occurrences of a pattern found in a string using a + format string. + subn Same as sub, but also return the number of substitutions made. + subfn Same as subf, but also return the number of substitutions made. + split Split a string by the occurrences of a pattern. VERSION1: will + split at zero-width match; VERSION0: won't split at zero-width + match. + splititer Return an iterator yielding the parts of a split string. + findall Find all occurrences of a pattern in a string. + finditer Return an iterator yielding a match object for each match. + compile Compile a pattern into a Pattern object. + purge Clear the regular expression cache. + escape Backslash all non-alphanumerics or special characters in a + string. + +Most of the functions support a concurrent parameter: if True, the GIL will be +released during matching, allowing other Python threads to run concurrently. If +the string changes during matching, the behaviour is undefined. This parameter +is not needed when working on the builtin (immutable) string classes. + +Some of the functions in this module take flags as optional parameters. Most of +these flags can also be set within an RE: + A a ASCII Make \w, \W, \b, \B, \d, and \D match the + corresponding ASCII character categories. Default + when matching a bytestring. + B b BESTMATCH Find the best fuzzy match (default is first). + D DEBUG Print the parsed pattern. + E e ENHANCEMATCH Attempt to improve the fit after finding the first + fuzzy match. + F f FULLCASE Use full case-folding when performing + case-insensitive matching in Unicode. + I i IGNORECASE Perform case-insensitive matching. + L L LOCALE Make \w, \W, \b, \B, \d, and \D dependent on the + current locale. (One byte per character only.) + M m MULTILINE "^" matches the beginning of lines (after a newline) + as well as the string. "$" matches the end of lines + (before a newline) as well as the end of the string. + P p POSIX Perform POSIX-standard matching (leftmost longest). + R r REVERSE Searches backwards. + S s DOTALL "." matches any character at all, including the + newline. + U u UNICODE Make \w, \W, \b, \B, \d, and \D dependent on the + Unicode locale. Default when matching a Unicode + string. + V0 V0 VERSION0 Turn on the old legacy behaviour. + V1 V1 VERSION1 Turn on the new enhanced behaviour. This flag + includes the FULLCASE flag. + W w WORD Make \b and \B work with default Unicode word breaks + and make ".", "^" and "$" work with Unicode line + breaks. + X x VERBOSE Ignore whitespace and comments for nicer looking REs. + +This module also defines an exception 'error'. + +""" + +# Public symbols. +__all__ = ["compile", "escape", "findall", "finditer", "fullmatch", "match", + "purge", "search", "split", "splititer", "sub", "subf", "subfn", "subn", + "template", "Scanner", "A", "ASCII", "B", "BESTMATCH", "D", "DEBUG", "E", + "ENHANCEMATCH", "S", "DOTALL", "F", "FULLCASE", "I", "IGNORECASE", "L", + "LOCALE", "M", "MULTILINE", "P", "POSIX", "R", "REVERSE", "T", "TEMPLATE", + "U", "UNICODE", "V0", "VERSION0", "V1", "VERSION1", "X", "VERBOSE", "W", + "WORD", "error", "Regex"] + +__version__ = "2.4.85" + +# -------------------------------------------------------------------- +# Public interface. + +def match(pattern, string, flags=0, pos=None, endpos=None, partial=False, + concurrent=None, **kwargs): + """Try to apply the pattern at the start of the string, returning a match + object, or None if no match was found.""" + return _compile(pattern, flags, kwargs).match(string, pos, endpos, + concurrent, partial) + +def fullmatch(pattern, string, flags=0, pos=None, endpos=None, partial=False, + concurrent=None, **kwargs): + """Try to apply the pattern against all of the string, returning a match + object, or None if no match was found.""" + return _compile(pattern, flags, kwargs).fullmatch(string, pos, endpos, + concurrent, partial) + +def search(pattern, string, flags=0, pos=None, endpos=None, partial=False, + concurrent=None, **kwargs): + """Search through string looking for a match to the pattern, returning a + match object, or None if no match was found.""" + return _compile(pattern, flags, kwargs).search(string, pos, endpos, + concurrent, partial) + +def sub(pattern, repl, string, count=0, flags=0, pos=None, endpos=None, + concurrent=None, **kwargs): + """Return the string obtained by replacing the leftmost (or rightmost with a + reverse pattern) non-overlapping occurrences of the pattern in string by the + replacement repl. repl can be either a string or a callable; if a string, + backslash escapes in it are processed; if a callable, it's passed the match + object and must return a replacement string to be used.""" + return _compile(pattern, flags, kwargs).sub(repl, string, count, pos, + endpos, concurrent) + +def subf(pattern, format, string, count=0, flags=0, pos=None, endpos=None, + concurrent=None, **kwargs): + """Return the string obtained by replacing the leftmost (or rightmost with a + reverse pattern) non-overlapping occurrences of the pattern in string by the + replacement format. format can be either a string or a callable; if a string, + it's treated as a format string; if a callable, it's passed the match object + and must return a replacement string to be used.""" + return _compile(pattern, flags, kwargs).subf(format, string, count, pos, + endpos, concurrent) + +def subn(pattern, repl, string, count=0, flags=0, pos=None, endpos=None, + concurrent=None, **kwargs): + """Return a 2-tuple containing (new_string, number). new_string is the string + obtained by replacing the leftmost (or rightmost with a reverse pattern) + non-overlapping occurrences of the pattern in the source string by the + replacement repl. number is the number of substitutions that were made. repl + can be either a string or a callable; if a string, backslash escapes in it + are processed; if a callable, it's passed the match object and must return a + replacement string to be used.""" + return _compile(pattern, flags, kwargs).subn(repl, string, count, pos, + endpos, concurrent) + +def subfn(pattern, format, string, count=0, flags=0, pos=None, endpos=None, + concurrent=None, **kwargs): + """Return a 2-tuple containing (new_string, number). new_string is the string + obtained by replacing the leftmost (or rightmost with a reverse pattern) + non-overlapping occurrences of the pattern in the source string by the + replacement format. number is the number of substitutions that were made. format + can be either a string or a callable; if a string, it's treated as a format + string; if a callable, it's passed the match object and must return a + replacement string to be used.""" + return _compile(pattern, flags, kwargs).subfn(format, string, count, pos, + endpos, concurrent) + +def split(pattern, string, maxsplit=0, flags=0, concurrent=None, **kwargs): + """Split the source string by the occurrences of the pattern, returning a + list containing the resulting substrings. If capturing parentheses are used + in pattern, then the text of all groups in the pattern are also returned as + part of the resulting list. If maxsplit is nonzero, at most maxsplit splits + occur, and the remainder of the string is returned as the final element of + the list.""" + return _compile(pattern, flags, kwargs).split(string, maxsplit, concurrent) + +def splititer(pattern, string, maxsplit=0, flags=0, concurrent=None, **kwargs): + "Return an iterator yielding the parts of a split string." + return _compile(pattern, flags, kwargs).splititer(string, maxsplit, + concurrent) + +def findall(pattern, string, flags=0, pos=None, endpos=None, overlapped=False, + concurrent=None, **kwargs): + """Return a list of all matches in the string. The matches may be overlapped + if overlapped is True. If one or more groups are present in the pattern, + return a list of groups; this will be a list of tuples if the pattern has + more than one group. Empty matches are included in the result.""" + return _compile(pattern, flags, kwargs).findall(string, pos, endpos, + overlapped, concurrent) + +def finditer(pattern, string, flags=0, pos=None, endpos=None, overlapped=False, + partial=False, concurrent=None, **kwargs): + """Return an iterator over all matches in the string. The matches may be + overlapped if overlapped is True. For each match, the iterator returns a + match object. Empty matches are included in the result.""" + return _compile(pattern, flags, kwargs).finditer(string, pos, endpos, + overlapped, concurrent, partial) + +def compile(pattern, flags=0, **kwargs): + "Compile a regular expression pattern, returning a pattern object." + return _compile(pattern, flags, kwargs) + +def purge(): + "Clear the regular expression cache" + _cache.clear() + _locale_sensitive.clear() + +def template(pattern, flags=0): + "Compile a template pattern, returning a pattern object." + return _compile(pattern, flags | TEMPLATE) + +def escape(pattern, special_only=False): + "Escape all non-alphanumeric characters or special characters in pattern." + s = [] + if special_only: + for c in pattern: + if c in _METACHARS: + s.append("\\") + s.append(c) + elif c == "\x00": + s.append("\\000") + else: + s.append(c) + else: + for c in pattern: + if c in _ALNUM: + s.append(c) + elif c == "\x00": + s.append("\\000") + else: + s.append("\\") + s.append(c) + + return pattern[ : 0].join(s) + +# -------------------------------------------------------------------- +# Internals. + +import _regex_core +import _regex +from threading import RLock as _RLock +from locale import getlocale as _getlocale +from _regex_core import * +from _regex_core import (_ALL_VERSIONS, _ALL_ENCODINGS, _FirstSetError, + _UnscopedFlagSet, _check_group_features, _compile_firstset, + _compile_replacement, _flatten_code, _fold_case, _get_required_string, + _parse_pattern, _shrink_cache) +from _regex_core import (ALNUM as _ALNUM, Info as _Info, OP as _OP, Source as + _Source, Fuzzy as _Fuzzy) + +# Version 0 is the old behaviour, compatible with the original 're' module. +# Version 1 is the new behaviour, which differs slightly. + +DEFAULT_VERSION = VERSION0 + +_METACHARS = frozenset("()[]{}?*+|^$\\.") + +_regex_core.DEFAULT_VERSION = DEFAULT_VERSION + +# Caches for the patterns and replacements. +_cache = {} +_cache_lock = _RLock() +_named_args = {} +_replacement_cache = {} +_locale_sensitive = {} + +# Maximum size of the cache. +_MAXCACHE = 500 +_MAXREPCACHE = 500 + +def _compile(pattern, flags=0, kwargs={}): + "Compiles a regular expression to a PatternObject." + + # We won't bother to cache the pattern if we're debugging. + debugging = (flags & DEBUG) != 0 + + # What locale is this pattern using? + locale_key = (type(pattern), pattern) + if _locale_sensitive.get(locale_key, True) or (flags & LOCALE) != 0: + # This pattern is, or might be, locale-sensitive. + pattern_locale = _getlocale()[1] + else: + # This pattern is definitely not locale-sensitive. + pattern_locale = None + + if not debugging: + try: + # Do we know what keyword arguments are needed? + args_key = pattern, type(pattern), flags + args_needed = _named_args[args_key] + + # Are we being provided with its required keyword arguments? + args_supplied = set() + if args_needed: + for k, v in args_needed: + try: + args_supplied.add((k, frozenset(kwargs[k]))) + except KeyError: + raise error("missing named list: {!r}".format(k)) + + args_supplied = frozenset(args_supplied) + + # Have we already seen this regular expression and named list? + pattern_key = (pattern, type(pattern), flags, args_supplied, + DEFAULT_VERSION, pattern_locale) + return _cache[pattern_key] + except KeyError: + # It's a new pattern, or new named list for a known pattern. + pass + + # Guess the encoding from the class of the pattern string. + if isinstance(pattern, unicode): + guess_encoding = UNICODE + elif isinstance(pattern, str): + guess_encoding = ASCII + elif isinstance(pattern, _pattern_type): + if flags: + raise ValueError("cannot process flags argument with a compiled pattern") + + return pattern + else: + raise TypeError("first argument must be a string or compiled pattern") + + # Set the default version in the core code in case it has been changed. + _regex_core.DEFAULT_VERSION = DEFAULT_VERSION + + global_flags = flags + + while True: + caught_exception = None + try: + source = _Source(pattern) + info = _Info(global_flags, source.char_type, kwargs) + info.guess_encoding = guess_encoding + source.ignore_space = bool(info.flags & VERBOSE) + parsed = _parse_pattern(source, info) + break + except _UnscopedFlagSet: + # Remember the global flags for the next attempt. + global_flags = info.global_flags + except error, e: + caught_exception = e + + if caught_exception: + raise error(caught_exception.msg, caught_exception.pattern, + caught_exception.pos) + + if not source.at_end(): + raise error("unbalanced parenthesis", pattern, source.pos) + + # Check the global flags for conflicts. + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + if version not in (0, VERSION0, VERSION1): + raise ValueError("VERSION0 and VERSION1 flags are mutually incompatible") + + if (info.flags & _ALL_ENCODINGS) not in (0, ASCII, LOCALE, UNICODE): + raise ValueError("ASCII, LOCALE and UNICODE flags are mutually incompatible") + + if not (info.flags & _ALL_ENCODINGS): + if isinstance(pattern, unicode): + info.flags |= UNICODE + else: + info.flags |= ASCII + + reverse = bool(info.flags & REVERSE) + fuzzy = isinstance(parsed, _Fuzzy) + + # Remember whether this pattern as an inline locale flag. + _locale_sensitive[locale_key] = info.inline_locale + + # Fix the group references. + caught_exception = None + try: + parsed.fix_groups(pattern, reverse, False) + except error, e: + caught_exception = e + + if caught_exception: + raise error(caught_exception.msg, caught_exception.pattern, + caught_exception.pos) + + # Should we print the parsed pattern? + if flags & DEBUG: + parsed.dump(indent=0, reverse=reverse) + + # Optimise the parsed pattern. + parsed = parsed.optimise(info) + parsed = parsed.pack_characters(info) + + # Get the required string. + req_offset, req_chars, req_flags = _get_required_string(parsed, info.flags) + + # Build the named lists. + named_lists = {} + named_list_indexes = [None] * len(info.named_lists_used) + args_needed = set() + for key, index in info.named_lists_used.items(): + name, case_flags = key + values = frozenset(kwargs[name]) + if case_flags: + items = frozenset(_fold_case(info, v) for v in values) + else: + items = values + named_lists[name] = values + named_list_indexes[index] = items + args_needed.add((name, values)) + + # Check the features of the groups. + _check_group_features(info, parsed) + + # Compile the parsed pattern. The result is a list of tuples. + code = parsed.compile(reverse) + + # Is there a group call to the pattern as a whole? + key = (0, reverse, fuzzy) + ref = info.call_refs.get(key) + if ref is not None: + code = [(_OP.CALL_REF, ref)] + code + [(_OP.END, )] + + # Add the final 'success' opcode. + code += [(_OP.SUCCESS, )] + + # Compile the additional copies of the groups that we need. + for group, rev, fuz in info.additional_groups: + code += group.compile(rev, fuz) + + # Flatten the code into a list of ints. + code = _flatten_code(code) + + if not parsed.has_simple_start(): + # Get the first set, if possible. + try: + fs_code = _compile_firstset(info, parsed.get_firstset(reverse)) + fs_code = _flatten_code(fs_code) + code = fs_code + code + except _FirstSetError: + pass + + # The named capture groups. + index_group = dict((v, n) for n, v in info.group_index.items()) + + # Create the PatternObject. + # + # Local flags like IGNORECASE affect the code generation, but aren't needed + # by the PatternObject itself. Conversely, global flags like LOCALE _don't_ + # affect the code generation but _are_ needed by the PatternObject. + compiled_pattern = _regex.compile(pattern, info.flags | version, code, + info.group_index, index_group, named_lists, named_list_indexes, + req_offset, req_chars, req_flags, info.group_count) + + # Do we need to reduce the size of the cache? + if len(_cache) >= _MAXCACHE: + _cache_lock.acquire() + try: + _shrink_cache(_cache, _named_args, _locale_sensitive, _MAXCACHE) + finally: + _cache_lock.release() + + if not debugging: + if (info.flags & LOCALE) == 0: + pattern_locale = None + + args_needed = frozenset(args_needed) + + # Store this regular expression and named list. + pattern_key = (pattern, type(pattern), flags, args_needed, + DEFAULT_VERSION, pattern_locale) + _cache[pattern_key] = compiled_pattern + + # Store what keyword arguments are needed. + _named_args[args_key] = args_needed + + return compiled_pattern + +def _compile_replacement_helper(pattern, template): + "Compiles a replacement template." + # This function is called by the _regex module. + + # Have we seen this before? + key = pattern.pattern, pattern.flags, template + compiled = _replacement_cache.get(key) + if compiled is not None: + return compiled + + if len(_replacement_cache) >= _MAXREPCACHE: + _replacement_cache.clear() + + is_unicode = isinstance(template, unicode) + source = _Source(template) + if is_unicode: + def make_string(char_codes): + return u"".join(unichr(c) for c in char_codes) + else: + def make_string(char_codes): + return "".join(chr(c) for c in char_codes) + + compiled = [] + literal = [] + while True: + ch = source.get() + if not ch: + break + if ch == "\\": + # '_compile_replacement' will return either an int group reference + # or a string literal. It returns items (plural) in order to handle + # a 2-character literal (an invalid escape sequence). + is_group, items = _compile_replacement(source, pattern, is_unicode) + if is_group: + # It's a group, so first flush the literal. + if literal: + compiled.append(make_string(literal)) + literal = [] + compiled.extend(items) + else: + literal.extend(items) + else: + literal.append(ord(ch)) + + # Flush the literal. + if literal: + compiled.append(make_string(literal)) + + _replacement_cache[key] = compiled + + return compiled + +# We define _pattern_type here after all the support objects have been defined. +_pattern_type = type(_compile("", 0, {})) + +# We'll define an alias for the 'compile' function so that the repr of a +# pattern object is eval-able. +Regex = compile + +# Register myself for pickling. +import copy_reg as _copy_reg + +def _pickle(p): + return _compile, (p.pattern, p.flags) + +_copy_reg.pickle(_pattern_type, _pickle, _compile) + +if not hasattr(str, "format"): + # Strings don't have the .format method (below Python 2.6). + while True: + _start = __doc__.find(" subf") + if _start < 0: + break + + _end = __doc__.find("\n", _start) + 1 + while __doc__.startswith(" ", _end): + _end = __doc__.find("\n", _end) + 1 + + __doc__ = __doc__[ : _start] + __doc__[_end : ] + + __all__ = [_name for _name in __all__ if not _name.startswith("subf")] + + del _start, _end + + del subf, subfn diff --git a/lib/regex/_regex.c b/lib/regex/_regex.c new file mode 100644 index 000000000..e9602102a --- /dev/null +++ b/lib/regex/_regex.c @@ -0,0 +1,24497 @@ +/* Secret Labs' Regular Expression Engine + * + * regular expression matching engine + * + * partial history: + * 1999-10-24 fl created (based on existing template matcher code) + * 2000-03-06 fl first alpha, sort of + * 2000-08-01 fl fixes for 1.6b1 + * 2000-08-07 fl use PyOS_CheckStack() if available + * 2000-09-20 fl added expand method + * 2001-03-20 fl lots of fixes for 2.1b2 + * 2001-04-15 fl export copyright as Python attribute, not global + * 2001-04-28 fl added __copy__ methods (work in progress) + * 2001-05-14 fl fixes for 1.5.2 compatibility + * 2001-07-01 fl added BIGCHARSET support (from Martin von Loewis) + * 2001-10-18 fl fixed group reset issue (from Matthew Mueller) + * 2001-10-20 fl added split primitive; reenable unicode for 1.6/2.0/2.1 + * 2001-10-21 fl added sub/subn primitive + * 2001-10-24 fl added finditer primitive (for 2.2 only) + * 2001-12-07 fl fixed memory leak in sub/subn (Guido van Rossum) + * 2002-11-09 fl fixed empty sub/subn return type + * 2003-04-18 mvl fully support 4-byte codes + * 2003-10-17 gn implemented non recursive scheme + * 2009-07-26 mrab completely re-designed matcher code + * 2011-11-18 mrab added support for PEP 393 strings + * + * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. + * + * This version of the SRE library can be redistributed under CNRI's + * Python 1.6 license. For any other use, please contact Secret Labs + * AB (info@pythonware.com). + * + * Portions of this engine have been developed in cooperation with + * CNRI. Hewlett-Packard provided funding for 1.6 integration and + * other compatibility work. + */ + +/* #define VERBOSE */ + +#if defined(VERBOSE) +#define TRACE(X) printf X; +#else +#define TRACE(X) +#endif + +#include "Python.h" +#include "structmember.h" /* offsetof */ +#include <ctype.h> +#include "_regex.h" +#include "pyport.h" +#include "pythread.h" + +#if PY_VERSION_HEX < 0x02060000 +#if SIZEOF_SIZE_T == SIZEOF_LONG_LONG +#define T_PYSSIZET T_LONGLONG +#elif SIZEOF_SIZE_T == SIZEOF_LONG +#define T_PYSSIZET T_LONG +#else +#error size_t is the same size as neither LONG nor LONGLONG +#endif + +#endif +typedef unsigned char Py_UCS1; +typedef unsigned short Py_UCS2; + +typedef RE_UINT32 RE_CODE; + +/* Properties in the General Category. */ +#define RE_PROP_GC_CN ((RE_PROP_GC << 16) | RE_PROP_CN) +#define RE_PROP_GC_LU ((RE_PROP_GC << 16) | RE_PROP_LU) +#define RE_PROP_GC_LL ((RE_PROP_GC << 16) | RE_PROP_LL) +#define RE_PROP_GC_LT ((RE_PROP_GC << 16) | RE_PROP_LT) +#define RE_PROP_GC_P ((RE_PROP_GC << 16) | RE_PROP_P) + +/* Unlimited repeat count. */ +#define RE_UNLIMITED (~(RE_CODE)0) + +/* The status of a . */ +typedef RE_UINT32 RE_STATUS_T; + +/* Whether to match concurrently, i.e. release the GIL while matching. */ +#define RE_CONC_NO 0 +#define RE_CONC_YES 1 +#define RE_CONC_DEFAULT 2 + +/* The side that could truncate in a partial match. + * + * The values RE_PARTIAL_LEFT and RE_PARTIAL_RIGHT are also used as array + * indexes, so they need to be 0 and 1. + */ +#define RE_PARTIAL_NONE -1 +#define RE_PARTIAL_LEFT 0 +#define RE_PARTIAL_RIGHT 1 + +/* Flags for the kind of 'sub' call: 'sub', 'subn', 'subf', 'subfn'. */ +#define RE_SUB 0x0 +#define RE_SUBN 0x1 +#if PY_VERSION_HEX >= 0x02060000 +#define RE_SUBF 0x2 +#endif + +/* The name of this module, minus the leading underscore. */ +#define RE_MODULE "regex" + +/* Error codes. */ +#define RE_ERROR_SUCCESS 1 /* Successful match. */ +#define RE_ERROR_FAILURE 0 /* Unsuccessful match. */ +#define RE_ERROR_ILLEGAL -1 /* Illegal code. */ +#define RE_ERROR_INTERNAL -2 /* Internal error. */ +#define RE_ERROR_CONCURRENT -3 /* "concurrent" invalid. */ +#define RE_ERROR_MEMORY -4 /* Out of memory. */ +#define RE_ERROR_INTERRUPTED -5 /* Signal handler raised exception. */ +#define RE_ERROR_REPLACEMENT -6 /* Invalid replacement string. */ +#define RE_ERROR_INVALID_GROUP_REF -7 /* Invalid group reference. */ +#define RE_ERROR_GROUP_INDEX_TYPE -8 /* Group index type error. */ +#define RE_ERROR_NO_SUCH_GROUP -9 /* No such group. */ +#define RE_ERROR_INDEX -10 /* String index. */ +#define RE_ERROR_BACKTRACKING -11 /* Too much backtracking. */ +#define RE_ERROR_NOT_STRING -12 /* Not a string. */ +#define RE_ERROR_NOT_UNICODE -13 /* Not a Unicode string. */ +#define RE_ERROR_PARTIAL -15 /* Partial match. */ + +/* The number of backtrack entries per allocated block. */ +#define RE_BACKTRACK_BLOCK_SIZE 64 + +/* The maximum number of backtrack entries to allocate. */ +#define RE_MAX_BACKTRACK_ALLOC (1024 * 1024) + +/* The number of atomic entries per allocated block. */ +#define RE_ATOMIC_BLOCK_SIZE 64 + +/* The initial maximum capacity of the guard block. */ +#define RE_INIT_GUARDS_BLOCK_SIZE 16 + +/* The initial maximum capacity of the node list. */ +#define RE_INIT_NODE_LIST_SIZE 16 + +/* The size increment for various allocation lists. */ +#define RE_LIST_SIZE_INC 16 + +/* The initial maximum capacity of the capture groups. */ +#define RE_INIT_CAPTURE_SIZE 16 + +/* Node bitflags. */ +#define RE_POSITIVE_OP 0x1 +#define RE_ZEROWIDTH_OP 0x2 +#define RE_FUZZY_OP 0x4 +#define RE_REVERSE_OP 0x8 +#define RE_REQUIRED_OP 0x10 + +/* Guards against further matching can occur at the start of the body and the + * tail of a repeat containing a repeat. + */ +#define RE_STATUS_BODY 0x1 +#define RE_STATUS_TAIL 0x2 + +/* Whether a guard is added depends on whether there's a repeat in the body of + * the repeat or a group reference in the body or tail of the repeat. + */ +#define RE_STATUS_NEITHER 0x0 +#define RE_STATUS_REPEAT 0x4 +#define RE_STATUS_LIMITED 0x8 +#define RE_STATUS_REF 0x10 +#define RE_STATUS_VISITED_AG 0x20 +#define RE_STATUS_VISITED_REP 0x40 + +/* Whether a string node has been initialised for fast searching. */ +#define RE_STATUS_FAST_INIT 0x80 + +/* Whether a node us being used. (Additional nodes may be created while the + * pattern is being built. + */ +#define RE_STATUS_USED 0x100 + +/* Whether a node is a string node. */ +#define RE_STATUS_STRING 0x200 + +/* Whether a repeat node is within another repeat. */ +#define RE_STATUS_INNER 0x400 + +/* Various flags stored in a node status member. */ +#define RE_STATUS_SHIFT 11 + +#define RE_STATUS_FUZZY (RE_FUZZY_OP << RE_STATUS_SHIFT) +#define RE_STATUS_REVERSE (RE_REVERSE_OP << RE_STATUS_SHIFT) +#define RE_STATUS_REQUIRED (RE_REQUIRED_OP << RE_STATUS_SHIFT) +#define RE_STATUS_HAS_GROUPS 0x10000 +#define RE_STATUS_HAS_REPEATS 0x20000 + +/* The different error types for fuzzy matching. */ +#define RE_FUZZY_SUB 0 +#define RE_FUZZY_INS 1 +#define RE_FUZZY_DEL 2 +#define RE_FUZZY_ERR 3 +#define RE_FUZZY_COUNT 3 + +/* The various values in a FUZZY node. */ +#define RE_FUZZY_VAL_MAX_BASE 1 +#define RE_FUZZY_VAL_MAX_SUB (RE_FUZZY_VAL_MAX_BASE + RE_FUZZY_SUB) +#define RE_FUZZY_VAL_MAX_INS (RE_FUZZY_VAL_MAX_BASE + RE_FUZZY_INS) +#define RE_FUZZY_VAL_MAX_DEL (RE_FUZZY_VAL_MAX_BASE + RE_FUZZY_DEL) +#define RE_FUZZY_VAL_MAX_ERR (RE_FUZZY_VAL_MAX_BASE + RE_FUZZY_ERR) + +#define RE_FUZZY_VAL_COST_BASE 5 +#define RE_FUZZY_VAL_SUB_COST (RE_FUZZY_VAL_COST_BASE + RE_FUZZY_SUB) +#define RE_FUZZY_VAL_INS_COST (RE_FUZZY_VAL_COST_BASE + RE_FUZZY_INS) +#define RE_FUZZY_VAL_DEL_COST (RE_FUZZY_VAL_COST_BASE + RE_FUZZY_DEL) +#define RE_FUZZY_VAL_MAX_COST (RE_FUZZY_VAL_COST_BASE + RE_FUZZY_ERR) + +/* The various values in an END_FUZZY node. */ +#define RE_FUZZY_VAL_MIN_BASE 1 +#define RE_FUZZY_VAL_MIN_SUB (RE_FUZZY_VAL_MIN_BASE + RE_FUZZY_SUB) +#define RE_FUZZY_VAL_MIN_INS (RE_FUZZY_VAL_MIN_BASE + RE_FUZZY_INS) +#define RE_FUZZY_VAL_MIN_DEL (RE_FUZZY_VAL_MIN_BASE + RE_FUZZY_DEL) +#define RE_FUZZY_VAL_MIN_ERR (RE_FUZZY_VAL_MIN_BASE + RE_FUZZY_ERR) + +/* The maximum number of errors when trying to improve a fuzzy match. */ +#define RE_MAX_ERRORS 10 + +/* The flags which will be set for full Unicode case folding. */ +#define RE_FULL_CASE_FOLDING (RE_FLAG_UNICODE | RE_FLAG_FULLCASE | RE_FLAG_IGNORECASE) + +/* The shortest string prefix for which we'll use a fast string search. */ +#define RE_MIN_FAST_LENGTH 5 + +static char copyright[] = + " RE 2.3.0 Copyright (c) 1997-2002 by Secret Labs AB "; + +/* The exception to raise on error. */ +static PyObject* error_exception; + +/* The dictionary of Unicode properties. */ +static PyObject* property_dict; + +typedef struct RE_State* RE_StatePtr; + +/* Bit-flags for the common character properties supported by locale-sensitive + * matching. + */ +#define RE_LOCALE_ALNUM 0x001 +#define RE_LOCALE_ALPHA 0x002 +#define RE_LOCALE_CNTRL 0x004 +#define RE_LOCALE_DIGIT 0x008 +#define RE_LOCALE_GRAPH 0x010 +#define RE_LOCALE_LOWER 0x020 +#define RE_LOCALE_PRINT 0x040 +#define RE_LOCALE_PUNCT 0x080 +#define RE_LOCALE_SPACE 0x100 +#define RE_LOCALE_UPPER 0x200 + +/* Info about the current locale. + * + * Used by patterns that are locale-sensitive. + */ +typedef struct RE_LocaleInfo { + unsigned short properties[0x100]; + unsigned char uppercase[0x100]; + unsigned char lowercase[0x100]; +} RE_LocaleInfo; + +/* Handlers for ASCII, locale and Unicode. */ +typedef struct RE_EncodingTable { + BOOL (*has_property)(RE_LocaleInfo* locale_info, RE_CODE property, Py_UCS4 + ch); + BOOL (*at_boundary)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*at_word_start)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*at_word_end)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*at_default_boundary)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*at_default_word_start)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*at_default_word_end)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*at_grapheme_boundary)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*is_line_sep)(Py_UCS4 ch); + BOOL (*at_line_start)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*at_line_end)(RE_StatePtr state, Py_ssize_t text_pos); + BOOL (*possible_turkic)(RE_LocaleInfo* locale_info, Py_UCS4 ch); + int (*all_cases)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + codepoints); + Py_UCS4 (*simple_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch); + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + folded); + int (*all_turkic_i)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + cases); +} RE_EncodingTable; + +/* Position within the regex and text. */ +typedef struct RE_Position { + struct RE_Node* node; + Py_ssize_t text_pos; +} RE_Position; + +/* Info about fuzzy matching. */ +typedef struct RE_FuzzyInfo { + struct RE_Node* node; + size_t counts[RE_FUZZY_COUNT + 1]; /* Add 1 for total errors. */ + size_t total_cost; +} RE_FuzzyInfo; + +/* Storage for backtrack data. */ +typedef struct RE_BacktrackData { + union { + struct { + size_t capture_change; + BOOL too_few_errors; + } atomic; + struct { + RE_Position position; + } branch; + struct { + RE_FuzzyInfo fuzzy_info; + Py_ssize_t text_pos; + RE_CODE index; + } fuzzy; + struct { + RE_Position position; + size_t count; + struct RE_Node* fuzzy_node; + BOOL too_few_errors; + } fuzzy_insert; + struct { + RE_Position position; + RE_INT8 fuzzy_type; + RE_INT8 step; + } fuzzy_item; + struct { + RE_Position position; + Py_ssize_t string_pos; + RE_INT8 fuzzy_type; + RE_INT8 folded_pos; + RE_INT8 folded_len; + RE_INT8 gfolded_pos; + RE_INT8 gfolded_len; + RE_INT8 step; + } fuzzy_string; + struct { + Py_ssize_t text_pos; + Py_ssize_t current_capture; + RE_CODE private_index; + RE_CODE public_index; + BOOL capture; + } group; + struct { + struct RE_Node* node; + size_t capture_change; + } group_call; + struct { + Py_ssize_t match_pos; + } keep; + struct { + struct RE_Node* node; + size_t capture_change; + BOOL too_few_errors; + BOOL inside; + } lookaround; + struct { + RE_Position position; + Py_ssize_t text_pos; + size_t count; + Py_ssize_t start; + size_t capture_change; + RE_CODE index; + } repeat; + }; + RE_UINT8 op; +} RE_BacktrackData; + +/* Storage for backtrack data is allocated in blocks for speed. */ +typedef struct RE_BacktrackBlock { + RE_BacktrackData items[RE_BACKTRACK_BLOCK_SIZE]; + struct RE_BacktrackBlock* previous; + struct RE_BacktrackBlock* next; + size_t capacity; + size_t count; +} RE_BacktrackBlock; + +/* Storage for atomic data. */ +typedef struct RE_AtomicData { + RE_BacktrackBlock* current_backtrack_block; + size_t backtrack_count; + struct RE_Node* node; + RE_BacktrackData* backtrack; + struct RE_SavedGroups* saved_groups; + struct RE_SavedRepeats* saved_repeats; + Py_ssize_t slice_start; + Py_ssize_t slice_end; + Py_ssize_t text_pos; + BOOL is_lookaround; + BOOL has_groups; + BOOL has_repeats; +} RE_AtomicData; + +/* Storage for atomic data is allocated in blocks for speed. */ +typedef struct RE_AtomicBlock { + RE_AtomicData items[RE_ATOMIC_BLOCK_SIZE]; + struct RE_AtomicBlock* previous; + struct RE_AtomicBlock* next; + size_t capacity; + size_t count; +} RE_AtomicBlock; + +/* Storage for saved groups. */ +typedef struct RE_SavedGroups { + struct RE_SavedGroups* previous; + struct RE_SavedGroups* next; + struct RE_GroupSpan* spans; + size_t* counts; +} RE_SavedGroups; + +/* Storage for info around a recursive by 'basic'match'. */ +typedef struct RE_Info { + RE_BacktrackBlock* current_backtrack_block; + size_t backtrack_count; + RE_SavedGroups* current_saved_groups; + struct RE_GroupCallFrame* current_group_call_frame; + BOOL must_advance; +} RE_Info; + +/* Storage for the next node. */ +typedef struct RE_NextNode { + struct RE_Node* node; + struct RE_Node* test; + struct RE_Node* match_next; + Py_ssize_t match_step; +} RE_NextNode; + +/* A pattern node. */ +typedef struct RE_Node { + RE_NextNode next_1; + union { + struct { + RE_NextNode next_2; + } nonstring; + struct { + /* Used only if (node->status & RE_STATUS_STRING) is true. */ + Py_ssize_t* bad_character_offset; + Py_ssize_t* good_suffix_offset; + } string; + }; + Py_ssize_t step; + size_t value_count; + RE_CODE* values; + RE_STATUS_T status; + RE_UINT8 op; + BOOL match; +} RE_Node; + +/* Info about a group's span. */ +typedef struct RE_GroupSpan { + Py_ssize_t start; + Py_ssize_t end; +} RE_GroupSpan; + +/* Span of a guard (inclusive range). */ +typedef struct RE_GuardSpan { + Py_ssize_t low; + Py_ssize_t high; + BOOL protect; +} RE_GuardSpan; + +/* Spans guarded against further matching. */ +typedef struct RE_GuardList { + size_t capacity; + size_t count; + RE_GuardSpan* spans; + Py_ssize_t last_text_pos; + size_t last_low; +} RE_GuardList; + +/* Info about a group. */ +typedef struct RE_GroupData { + RE_GroupSpan span; + size_t capture_count; + size_t capture_capacity; + Py_ssize_t current_capture; + RE_GroupSpan* captures; +} RE_GroupData; + +/* Info about a repeat. */ +typedef struct RE_RepeatData { + RE_GuardList body_guard_list; + RE_GuardList tail_guard_list; + size_t count; + Py_ssize_t start; + size_t capture_change; +} RE_RepeatData; + +/* Storage for saved repeats. */ +typedef struct RE_SavedRepeats { + struct RE_SavedRepeats* previous; + struct RE_SavedRepeats* next; + RE_RepeatData* repeats; +} RE_SavedRepeats; + +/* Guards for fuzzy sections. */ +typedef struct RE_FuzzyGuards { + RE_GuardList body_guard_list; + RE_GuardList tail_guard_list; +} RE_FuzzyGuards; + +/* Info about a capture group. */ +typedef struct RE_GroupInfo { + Py_ssize_t end_index; + RE_Node* node; + BOOL referenced; + BOOL has_name; +} RE_GroupInfo; + +/* Info about a call_ref. */ +typedef struct RE_CallRefInfo { + RE_Node* node; + BOOL defined; + BOOL used; +} RE_CallRefInfo; + +/* Info about a repeat. */ +typedef struct RE_RepeatInfo { + RE_STATUS_T status; +} RE_RepeatInfo; + +/* Stack frame for a group call. */ +typedef struct RE_GroupCallFrame { + struct RE_GroupCallFrame* previous; + struct RE_GroupCallFrame* next; + RE_Node* node; + RE_GroupData* groups; + RE_RepeatData* repeats; +} RE_GroupCallFrame; + +/* Info about a string argument. */ +typedef struct RE_StringInfo { +#if PY_VERSION_HEX >= 0x02060000 + Py_buffer view; /* View of the string if it's a buffer object. */ +#endif + void* characters; /* Pointer to the characters of the string. */ + Py_ssize_t length; /* Length of the string. */ + Py_ssize_t charsize; /* Size of the characters in the string. */ + BOOL is_unicode; /* Whether the string is Unicode. */ + BOOL should_release; /* Whether the buffer should be released. */ +} RE_StringInfo; + +/* Info about where the next match was found, starting from a certain search + * position. This is used when a pattern starts with a BRANCH. + */ +#define MAX_SEARCH_POSITIONS 7 + +/* Info about a search position. */ +typedef struct { + Py_ssize_t start_pos; + Py_ssize_t match_pos; +} RE_SearchPosition; + +/* The state object used during matching. */ +typedef struct RE_State { + struct PatternObject* pattern; /* Parent PatternObject. */ + /* Info about the string being matched. */ + PyObject* string; +#if PY_VERSION_HEX >= 0x02060000 + Py_buffer view; /* View of the string if it's a buffer object. */ +#endif + Py_ssize_t charsize; + void* text; + Py_ssize_t text_length; + /* The slice of the string being searched. */ + Py_ssize_t slice_start; + Py_ssize_t slice_end; + /* Info about the capture groups. */ + RE_GroupData* groups; + Py_ssize_t lastindex; + Py_ssize_t lastgroup; + /* Info about the repeats. */ + RE_RepeatData* repeats; + Py_ssize_t search_anchor; /* Where the last match finished. */ + Py_ssize_t match_pos; /* The start position of the match. */ + Py_ssize_t text_pos; /* The current position of the match. */ + Py_ssize_t final_newline; /* The index of newline at end of string, or -1. */ + Py_ssize_t final_line_sep; /* The index of line separator at end of string, or -1. */ + /* Storage for backtrack info. */ + RE_BacktrackBlock backtrack_block; + RE_BacktrackBlock* current_backtrack_block; + Py_ssize_t backtrack_allocated; + RE_BacktrackData* backtrack; + RE_AtomicBlock* current_atomic_block; + /* Storage for saved capture groups. */ + RE_SavedGroups* first_saved_groups; + RE_SavedGroups* current_saved_groups; + RE_SavedRepeats* first_saved_repeats; + RE_SavedRepeats* current_saved_repeats; + /* Info about the best POSIX match (leftmost longest). */ + Py_ssize_t best_match_pos; + Py_ssize_t best_text_pos; + RE_GroupData* best_match_groups; + /* Miscellaneous. */ + Py_ssize_t min_width; /* The minimum width of the string to match (assuming it's not a fuzzy pattern). */ + RE_EncodingTable* encoding; /* The 'encoding' of the string being searched. */ + RE_LocaleInfo* locale_info; /* Info about the locale, if needed. */ + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + void (*set_char_at)(void* text, Py_ssize_t pos, Py_UCS4 ch); + void* (*point_to)(void* text, Py_ssize_t pos); + PyThread_type_lock lock; /* A lock for accessing the state across threads. */ + RE_FuzzyInfo fuzzy_info; /* Info about fuzzy matching. */ + size_t total_fuzzy_counts[RE_FUZZY_COUNT]; /* Totals for fuzzy matching. */ + size_t best_fuzzy_counts[RE_FUZZY_COUNT]; /* Best totals for fuzzy matching. */ + RE_FuzzyGuards* fuzzy_guards; /* The guards for a fuzzy match. */ + size_t total_errors; /* The total number of errors of a fuzzy match. */ + size_t max_errors; /* The maximum permitted number of errors. */ + size_t fewest_errors; /* The fewest errors so far of an enhanced fuzzy match. */ + /* The group call stack. */ + RE_GroupCallFrame* first_group_call_frame; + RE_GroupCallFrame* current_group_call_frame; + RE_GuardList* group_call_guard_list; + RE_SearchPosition search_positions[MAX_SEARCH_POSITIONS]; /* Where the search matches next. */ + size_t capture_change; /* Incremented every time a captive group changes. */ + Py_ssize_t req_pos; /* The position where the required string matched. */ + Py_ssize_t req_end; /* The end position where the required string matched. */ + int partial_side; /* The side that could truncate in a partial match. */ + RE_UINT16 iterations; /* The number of iterations the matching engine has performed since checking for KeyboardInterrupt. */ + BOOL is_unicode; /* Whether the string to be matched is Unicode. */ + BOOL should_release; /* Whether the buffer should be released. */ + BOOL overlapped; /* Whether the matches can be overlapped. */ + BOOL reverse; /* Whether it's a reverse pattern. */ + BOOL visible_captures; /* Whether the 'captures' method will be visible. */ + BOOL version_0; /* Whether to perform version_0 behaviour (same as re module). */ + BOOL must_advance; /* Whether the end of the match must advance past its start. */ + BOOL is_multithreaded; /* Whether to release the GIL while matching. */ + BOOL too_few_errors; /* Whether there were too few fuzzy errors. */ + BOOL match_all; /* Whether to match all of the string ('fullmatch'). */ + BOOL found_match; /* Whether a POSIX match has been found. */ +} RE_State; + +/* Storage for the regex state and thread state. + * + * Scanner objects can sometimes be shared across threads, which means that + * their RE_State structs are also shared. This isn't safe when the GIL is + * released, so in such instances we have a lock (mutex) in the RE_State struct + * to protect it during matching. We also need a thread-safe place to store the + * thread state when releasing the GIL. + */ +typedef struct RE_SafeState { + RE_State* re_state; + PyThreadState* thread_state; +} RE_SafeState; + +/* The PatternObject created from a regular expression. */ +typedef struct PatternObject { + PyObject_HEAD + PyObject* pattern; /* Pattern source (or None). */ + Py_ssize_t flags; /* Flags used when compiling pattern source. */ + PyObject* weakreflist; /* List of weak references */ + /* Nodes into which the regular expression is compiled. */ + RE_Node* start_node; + RE_Node* start_test; + size_t true_group_count; /* The true number of capture groups. */ + size_t public_group_count; /* The number of public capture groups. */ + size_t repeat_count; /* The number of repeats. */ + Py_ssize_t group_end_index; /* The number of group closures. */ + PyObject* groupindex; + PyObject* indexgroup; + PyObject* named_lists; + size_t named_lists_count; + PyObject** partial_named_lists[2]; + PyObject* named_list_indexes; + /* Storage for the pattern nodes. */ + size_t node_capacity; + size_t node_count; + RE_Node** node_list; + /* Info about the capture groups. */ + size_t group_info_capacity; + RE_GroupInfo* group_info; + /* Info about the call_refs. */ + size_t call_ref_info_capacity; + size_t call_ref_info_count; + RE_CallRefInfo* call_ref_info; + Py_ssize_t pattern_call_ref; + /* Info about the repeats. */ + size_t repeat_info_capacity; + RE_RepeatInfo* repeat_info; + Py_ssize_t min_width; /* The minimum width of the string to match (assuming it isn't a fuzzy pattern). */ + RE_EncodingTable* encoding; /* Encoding handlers. */ + RE_LocaleInfo* locale_info; /* Info about the locale, if needed. */ + RE_GroupData* groups_storage; + RE_RepeatData* repeats_storage; + size_t fuzzy_count; /* The number of fuzzy sections. */ + Py_ssize_t req_offset; /* The offset to the required string. */ + RE_Node* req_string; /* The required string. */ + BOOL is_fuzzy; /* Whether it's a fuzzy pattern. */ + BOOL do_search_start; /* Whether to do an initial search. */ + BOOL recursive; /* Whether the entire pattern is recursive. */ +} PatternObject; + +/* The MatchObject created when a match is found. */ +typedef struct MatchObject { + PyObject_HEAD + PyObject* string; /* Link to the target string or NULL if detached. */ + PyObject* substring; /* Link to (a substring of) the target string. */ + Py_ssize_t substring_offset; /* Offset into the target string. */ + PatternObject* pattern; /* Link to the regex (pattern) object. */ + Py_ssize_t pos; /* Start of current slice. */ + Py_ssize_t endpos; /* End of current slice. */ + Py_ssize_t match_start; /* Start of matched slice. */ + Py_ssize_t match_end; /* End of matched slice. */ + Py_ssize_t lastindex; /* Last group seen by the engine (-1 if none). */ + Py_ssize_t lastgroup; /* Last named group seen by the engine (-1 if none). */ + size_t group_count; /* The number of groups. */ + RE_GroupData* groups; /* The capture groups. */ + PyObject* regs; + size_t fuzzy_counts[RE_FUZZY_COUNT]; + BOOL partial; /* Whether it's a partial match. */ +} MatchObject; + +/* The ScannerObject. */ +typedef struct ScannerObject { + PyObject_HEAD + PatternObject* pattern; + RE_State state; + int status; +} ScannerObject; + +/* The SplitterObject. */ +typedef struct SplitterObject { + PyObject_HEAD + PatternObject* pattern; + RE_State state; + Py_ssize_t maxsplit; + Py_ssize_t last_pos; + Py_ssize_t split_count; + Py_ssize_t index; + int status; +} SplitterObject; +#if PY_VERSION_HEX >= 0x02060000 + +/* The CaptureObject. */ +typedef struct CaptureObject { + PyObject_HEAD + Py_ssize_t group_index; + MatchObject** match_indirect; +} CaptureObject; +#endif + +/* Info used when compiling a pattern to nodes. */ +typedef struct RE_CompileArgs { + RE_CODE* code; /* The start of the compiled pattern. */ + RE_CODE* end_code; /* The end of the compiled pattern. */ + PatternObject* pattern; /* The pattern object. */ + Py_ssize_t min_width; /* The minimum width of the string to match (assuming it isn't a fuzzy pattern). */ + RE_Node* start; /* The start node. */ + RE_Node* end; /* The end node. */ + size_t repeat_depth; /* The nesting depth of the repeat. */ + BOOL forward; /* Whether it's a forward (not reverse) pattern. */ + BOOL visible_captures; /* Whether all of the captures will be visible. */ + BOOL has_captures; /* Whether the pattern has capture groups. */ + BOOL is_fuzzy; /* Whether the pattern (or some part of it) is fuzzy. */ + BOOL within_fuzzy; /* Whether the subpattern is within a fuzzy section. */ + BOOL has_groups; /* Whether the subpattern contains captures. */ + BOOL has_repeats; /* Whether the subpattern contains repeats. */ +} RE_CompileArgs; + +/* The string slices which will be concatenated to make the result string of + * the 'sub' method. + * + * This allows us to avoid creating a list of slices if there of fewer than 2 + * of them. Empty strings aren't recorded, so if 'list' and 'item' are both + * NULL then the result is an empty string. + */ +typedef struct JoinInfo { + PyObject* list; /* The list of slices if there are more than 2 of them. */ + PyObject* item; /* The slice if there is only 1 of them. */ + BOOL reversed; /* Whether the slices have been found in reverse order. */ + BOOL is_unicode; /* Whether the string is Unicode. */ +} JoinInfo; + +/* Info about fuzzy matching. */ +typedef struct { + RE_Node* new_node; + Py_ssize_t new_text_pos; + Py_ssize_t limit; + Py_ssize_t new_string_pos; + int step; + int new_folded_pos; + int folded_len; + int new_gfolded_pos; + int new_group_pos; + int fuzzy_type; + BOOL permit_insertion; +} RE_FuzzyData; + +typedef struct RE_BestEntry { + Py_ssize_t match_pos; + Py_ssize_t text_pos; +} RE_BestEntry; + +typedef struct RE_BestList { + size_t capacity; + size_t count; + RE_BestEntry* entries; +} RE_BestList; + +/* Function types for getting info from a MatchObject. */ +typedef PyObject* (*RE_GetByIndexFunc)(MatchObject* self, Py_ssize_t index); + +/* Returns the magnitude of a 'Py_ssize_t' value. */ +Py_LOCAL_INLINE(Py_ssize_t) abs_ssize_t(Py_ssize_t x) { + return x >= 0 ? x : -x; +} + +/* Returns the minimum of 2 'Py_ssize_t' values. */ +Py_LOCAL_INLINE(Py_ssize_t) min_ssize_t(Py_ssize_t x, Py_ssize_t y) { + return x <= y ? x : y; +} + +/* Returns the maximum of 2 'Py_ssize_t' values. */ +Py_LOCAL_INLINE(Py_ssize_t) max_ssize_t(Py_ssize_t x, Py_ssize_t y) { + return x >= y ? x : y; +} + +/* Returns the minimum of 2 'size_t' values. */ +Py_LOCAL_INLINE(size_t) min_size_t(size_t x, size_t y) { + return x <= y ? x : y; +} + +/* Returns the maximum of 2 'size_t' values. */ +Py_LOCAL_INLINE(size_t) max_size_t(size_t x, size_t y) { + return x >= y ? x : y; +} + +/* Returns the 'maximum' of 2 RE_STATUS_T values. */ +Py_LOCAL_INLINE(RE_STATUS_T) max_status_2(RE_STATUS_T x, RE_STATUS_T y) { + return x >= y ? x : y; +} + +/* Returns the 'maximum' of 3 RE_STATUS_T values. */ +Py_LOCAL_INLINE(RE_STATUS_T) max_status_3(RE_STATUS_T x, RE_STATUS_T y, + RE_STATUS_T z) { + return max_status_2(x, max_status_2(y, z)); +} + +/* Returns the 'maximum' of 4 RE_STATUS_T values. */ +Py_LOCAL_INLINE(RE_STATUS_T) max_status_4(RE_STATUS_T w, RE_STATUS_T x, + RE_STATUS_T y, RE_STATUS_T z) { + return max_status_2(max_status_2(w, x), max_status_2(y, z)); +} + +/* Gets a character at a position assuming 1 byte per character. */ +static Py_UCS4 bytes1_char_at(void* text, Py_ssize_t pos) { + return *((Py_UCS1*)text + pos); +} + +/* Sets a character at a position assuming 1 byte per character. */ +static void bytes1_set_char_at(void* text, Py_ssize_t pos, Py_UCS4 ch) { + *((Py_UCS1*)text + pos) = (Py_UCS1)ch; +} + +/* Gets a pointer to a position assuming 1 byte per character. */ +static void* bytes1_point_to(void* text, Py_ssize_t pos) { + return (Py_UCS1*)text + pos; +} + +/* Gets a character at a position assuming 2 bytes per character. */ +static Py_UCS4 bytes2_char_at(void* text, Py_ssize_t pos) { + return *((Py_UCS2*)text + pos); +} + +/* Sets a character at a position assuming 2 bytes per character. */ +static void bytes2_set_char_at(void* text, Py_ssize_t pos, Py_UCS4 ch) { + *((Py_UCS2*)text + pos) = (Py_UCS2)ch; +} + +/* Gets a pointer to a position assuming 2 bytes per character. */ +static void* bytes2_point_to(void* text, Py_ssize_t pos) { + return (Py_UCS2*)text + pos; +} + +/* Gets a character at a position assuming 4 bytes per character. */ +static Py_UCS4 bytes4_char_at(void* text, Py_ssize_t pos) { + return *((Py_UCS4*)text + pos); +} + +/* Sets a character at a position assuming 4 bytes per character. */ +static void bytes4_set_char_at(void* text, Py_ssize_t pos, Py_UCS4 ch) { + *((Py_UCS4*)text + pos) = (Py_UCS4)ch; +} + +/* Gets a pointer to a position assuming 4 bytes per character. */ +static void* bytes4_point_to(void* text, Py_ssize_t pos) { + return (Py_UCS4*)text + pos; +} + +/* Default for whether a position is on a word boundary. */ +static BOOL at_boundary_always(RE_State* state, Py_ssize_t text_pos) { + return TRUE; +} + +/* Converts a BOOL to success/failure. */ +Py_LOCAL_INLINE(int) bool_as_status(BOOL value) { + return value ? RE_ERROR_SUCCESS : RE_ERROR_FAILURE; +} + +/* ASCII-specific. */ + +Py_LOCAL_INLINE(BOOL) unicode_has_property(RE_CODE property, Py_UCS4 ch); + +/* Checks whether a character has a property. */ +Py_LOCAL_INLINE(BOOL) ascii_has_property(RE_CODE property, Py_UCS4 ch) { + if (ch > RE_ASCII_MAX) { + /* Outside the ASCII range. */ + RE_UINT32 value; + + value = property & 0xFFFF; + + return value == 0; + } + + return unicode_has_property(property, ch); +} + +/* Wrapper for calling 'ascii_has_property' via a pointer. */ +static BOOL ascii_has_property_wrapper(RE_LocaleInfo* locale_info, RE_CODE + property, Py_UCS4 ch) { + return ascii_has_property(property, ch); +} + +/* Checks whether there's a word character to the left. */ +Py_LOCAL_INLINE(BOOL) ascii_word_left(RE_State* state, Py_ssize_t text_pos) { + return text_pos > 0 && ascii_has_property(RE_PROP_WORD, + state->char_at(state->text, text_pos - 1)); +} + +/* Checks whether there's a word character to the right. */ +Py_LOCAL_INLINE(BOOL) ascii_word_right(RE_State* state, Py_ssize_t text_pos) { + return text_pos < state->text_length && ascii_has_property(RE_PROP_WORD, + state->char_at(state->text, text_pos)); +} + +/* Checks whether a position is on a word boundary. */ +static BOOL ascii_at_boundary(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = ascii_word_left(state, text_pos); + right = ascii_word_right(state, text_pos); + + return left != right; +} + +/* Checks whether a position is at the start of a word. */ +static BOOL ascii_at_word_start(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = ascii_word_left(state, text_pos); + right = ascii_word_right(state, text_pos); + + return !left && right; +} + +/* Checks whether a position is at the end of a word. */ +static BOOL ascii_at_word_end(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = ascii_word_left(state, text_pos); + right = ascii_word_right(state, text_pos); + + return left && !right; +} + +/* Checks whether a character is a line separator. */ +static BOOL ascii_is_line_sep(Py_UCS4 ch) { + return 0x0A <= ch && ch <= 0x0D; +} + +/* Checks whether a position is at the start of a line. */ +static BOOL ascii_at_line_start(RE_State* state, Py_ssize_t text_pos) { + Py_UCS4 ch; + + if (text_pos <= 0) + return TRUE; + + ch = state->char_at(state->text, text_pos - 1); + + if (ch == 0x0D) { + if (text_pos >= state->text_length) + return TRUE; + + /* No line break inside CRLF. */ + return state->char_at(state->text, text_pos) != 0x0A; + } + + return 0x0A <= ch && ch <= 0x0D; +} + +/* Checks whether a position is at the end of a line. */ +static BOOL ascii_at_line_end(RE_State* state, Py_ssize_t text_pos) { + Py_UCS4 ch; + + if (text_pos >= state->text_length) + return TRUE; + + ch = state->char_at(state->text, text_pos); + + if (ch == 0x0A) { + if (text_pos <= 0) + return TRUE; + + /* No line break inside CRLF. */ + return state->char_at(state->text, text_pos - 1) != 0x0D; + } + + return 0x0A <= ch && ch <= 0x0D; +} + +/* Checks whether a character could be Turkic (variants of I/i). For ASCII, it + * won't be. + */ +static BOOL ascii_possible_turkic(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return FALSE; +} + +/* Gets all the cases of a character. */ +static int ascii_all_cases(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + codepoints) { + int count; + + count = 0; + + codepoints[count++] = ch; + + if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z')) + /* It's a letter, so add the other case. */ + codepoints[count++] = ch ^ 0x20; + + return count; +} + +/* Returns a character with its case folded. */ +static Py_UCS4 ascii_simple_case_fold(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + if ('A' <= ch && ch <= 'Z') + /* Uppercase folds to lowercase. */ + return ch ^ 0x20; + + return ch; +} + +/* Returns a character with its case folded. */ +static int ascii_full_case_fold(RE_LocaleInfo* locale_info, Py_UCS4 ch, + Py_UCS4* folded) { + if ('A' <= ch && ch <= 'Z') + /* Uppercase folds to lowercase. */ + folded[0] = ch ^ 0x20; + else + folded[0] = ch; + + return 1; +} + +/* Gets all the case variants of Turkic 'I'. The given character will be listed + * first. + */ +static int ascii_all_turkic_i(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + cases) { + int count; + + count = 0; + + cases[count++] = ch; + + if (ch != 'I') + cases[count++] = 'I'; + + if (ch != 'i') + cases[count++] = 'i'; + + return count; +} + +/* The handlers for ASCII characters. */ +static RE_EncodingTable ascii_encoding = { + ascii_has_property_wrapper, + ascii_at_boundary, + ascii_at_word_start, + ascii_at_word_end, + ascii_at_boundary, /* No special "default word boundary" for ASCII. */ + ascii_at_word_start, /* No special "default start of word" for ASCII. */ + ascii_at_word_end, /* No special "default end of a word" for ASCII. */ + at_boundary_always, /* No special "grapheme boundary" for ASCII. */ + ascii_is_line_sep, + ascii_at_line_start, + ascii_at_line_end, + ascii_possible_turkic, + ascii_all_cases, + ascii_simple_case_fold, + ascii_full_case_fold, + ascii_all_turkic_i, +}; + +/* Locale-specific. */ + +/* Checks whether a character has the 'alnum' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_isalnum(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_ALNUM) != 0; +} + +/* Checks whether a character has the 'alpha' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_isalpha(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_ALPHA) != 0; +} + +/* Checks whether a character has the 'cntrl' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_iscntrl(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_CNTRL) != 0; +} + +/* Checks whether a character has the 'digit' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_isdigit(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_DIGIT) != 0; +} + +/* Checks whether a character has the 'graph' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_isgraph(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_GRAPH) != 0; +} + +/* Checks whether a character has the 'lower' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_islower(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_LOWER) != 0; +} + +/* Checks whether a character has the 'print' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_isprint(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_PRINT) != 0; +} + +/* Checks whether a character has the 'punct' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_ispunct(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_PUNCT) != 0; +} + +/* Checks whether a character has the 'space' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_isspace(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_SPACE) != 0; +} + +/* Checks whether a character has the 'upper' property in the given locale. */ +Py_LOCAL_INLINE(BOOL) locale_isupper(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch <= RE_LOCALE_MAX && (locale_info->properties[ch] & + RE_LOCALE_UPPER) != 0; +} + +/* Converts a character to lowercase in the given locale. */ +Py_LOCAL_INLINE(Py_UCS4) locale_tolower(RE_LocaleInfo* locale_info, Py_UCS4 ch) + { + return ch <= RE_LOCALE_MAX ? locale_info->lowercase[ch] : ch; +} + +/* Converts a character to uppercase in the given locale. */ +Py_LOCAL_INLINE(Py_UCS4) locale_toupper(RE_LocaleInfo* locale_info, Py_UCS4 ch) + { + return ch <= RE_LOCALE_MAX ? locale_info->uppercase[ch] : ch; +} + +/* Checks whether a character has a property. */ +Py_LOCAL_INLINE(BOOL) locale_has_property(RE_LocaleInfo* locale_info, RE_CODE + property, Py_UCS4 ch) { + RE_UINT32 value; + RE_UINT32 v; + + value = property & 0xFFFF; + + if (ch > RE_LOCALE_MAX) + /* Outside the locale range. */ + return value == 0; + + switch (property >> 16) { + case RE_PROP_ALNUM >> 16: + v = locale_isalnum(locale_info, ch) != 0; + break; + case RE_PROP_ALPHA >> 16: + v = locale_isalpha(locale_info, ch) != 0; + break; + case RE_PROP_ANY >> 16: + v = 1; + break; + case RE_PROP_ASCII >> 16: + v = ch <= RE_ASCII_MAX; + break; + case RE_PROP_BLANK >> 16: + v = ch == '\t' || ch == ' '; + break; + case RE_PROP_GC: + switch (property) { + case RE_PROP_ASSIGNED: + v = ch <= RE_LOCALE_MAX; + break; + case RE_PROP_CASEDLETTER: + v = locale_isalpha(locale_info, ch) ? value : 0xFFFF; + break; + case RE_PROP_CNTRL: + v = locale_iscntrl(locale_info, ch) ? value : 0xFFFF; + break; + case RE_PROP_DIGIT: + v = locale_isdigit(locale_info, ch) ? value : 0xFFFF; + break; + case RE_PROP_GC_CN: + v = ch > RE_LOCALE_MAX; + break; + case RE_PROP_GC_LL: + v = locale_islower(locale_info, ch) ? value : 0xFFFF; + break; + case RE_PROP_GC_LU: + v = locale_isupper(locale_info, ch) ? value : 0xFFFF; + break; + case RE_PROP_GC_P: + v = locale_ispunct(locale_info, ch) ? value : 0xFFFF; + break; + default: + v = 0xFFFF; + break; + } + break; + case RE_PROP_GRAPH >> 16: + v = locale_isgraph(locale_info, ch) != 0; + break; + case RE_PROP_LOWER >> 16: + v = locale_islower(locale_info, ch) != 0; + break; + case RE_PROP_POSIX_ALNUM >> 16: + v = re_get_posix_alnum(ch) != 0; + break; + case RE_PROP_POSIX_DIGIT >> 16: + v = re_get_posix_digit(ch) != 0; + break; + case RE_PROP_POSIX_PUNCT >> 16: + v = re_get_posix_punct(ch) != 0; + break; + case RE_PROP_POSIX_XDIGIT >> 16: + v = re_get_posix_xdigit(ch) != 0; + break; + case RE_PROP_PRINT >> 16: + v = locale_isprint(locale_info, ch) != 0; + break; + case RE_PROP_SPACE >> 16: + v = locale_isspace(locale_info, ch) != 0; + break; + case RE_PROP_UPPER >> 16: + v = locale_isupper(locale_info, ch) != 0; + break; + case RE_PROP_WORD >> 16: + v = ch == '_' || locale_isalnum(locale_info, ch) != 0; + break; + case RE_PROP_XDIGIT >> 16: + v = re_get_hex_digit(ch) != 0; + break; + default: + v = 0; + break; + } + + return v == value; +} + +/* Wrapper for calling 'locale_has_property' via a pointer. */ +static BOOL locale_has_property_wrapper(RE_LocaleInfo* locale_info, RE_CODE + property, Py_UCS4 ch) { + return locale_has_property(locale_info, property, ch); +} + +/* Checks whether there's a word character to the left. */ +Py_LOCAL_INLINE(BOOL) locale_word_left(RE_State* state, Py_ssize_t text_pos) { + return text_pos > 0 && locale_has_property(state->locale_info, + RE_PROP_WORD, state->char_at(state->text, text_pos - 1)); +} + +/* Checks whether there's a word character to the right. */ +Py_LOCAL_INLINE(BOOL) locale_word_right(RE_State* state, Py_ssize_t text_pos) { + return text_pos < state->text_length && + locale_has_property(state->locale_info, RE_PROP_WORD, + state->char_at(state->text, text_pos)); +} + +/* Checks whether a position is on a word boundary. */ +static BOOL locale_at_boundary(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = locale_word_left(state, text_pos); + right = locale_word_right(state, text_pos); + + return left != right; +} + +/* Checks whether a position is at the start of a word. */ +static BOOL locale_at_word_start(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = locale_word_left(state, text_pos); + right = locale_word_right(state, text_pos); + + return !left && right; +} + +/* Checks whether a position is at the end of a word. */ +static BOOL locale_at_word_end(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = locale_word_left(state, text_pos); + right = locale_word_right(state, text_pos); + + return left && !right; +} + +/* Checks whether a character could be Turkic (variants of I/i). */ +static BOOL locale_possible_turkic(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return locale_toupper(locale_info, ch) == 'I' || + locale_tolower(locale_info, ch) == 'i'; +} + +/* Gets all the cases of a character. */ +static int locale_all_cases(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + codepoints) { + int count; + Py_UCS4 other; + + count = 0; + + codepoints[count++] = ch; + + other = locale_toupper(locale_info, ch); + if (other != ch) + codepoints[count++] = other; + + other = locale_tolower(locale_info, ch); + if (other != ch) + codepoints[count++] = other; + + return count; +} + +/* Returns a character with its case folded. */ +static Py_UCS4 locale_simple_case_fold(RE_LocaleInfo* locale_info, Py_UCS4 ch) + { + return locale_tolower(locale_info, ch); +} + +/* Returns a character with its case folded. */ +static int locale_full_case_fold(RE_LocaleInfo* locale_info, Py_UCS4 ch, + Py_UCS4* folded) { + folded[0] = locale_tolower(locale_info, ch); + + return 1; +} + +/* Gets all the case variants of Turkic 'I'. The given character will be listed + * first. + */ +static int locale_all_turkic_i(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + cases) { + int count; + Py_UCS4 other; + + count = 0; + + cases[count++] = ch; + + if (ch != 'I') + cases[count++] = 'I'; + + if (ch != 'i') + cases[count++] = 'i'; + + /* Uppercase 'i' will be either dotted (Turkic) or dotless (non-Turkic). */ + other = locale_toupper(locale_info, 'i'); + if (other != ch && other != 'I') + cases[count++] = other; + + /* Lowercase 'I' will be either dotless (Turkic) or dotted (non-Turkic). */ + other = locale_tolower(locale_info, 'I'); + if (other != ch && other != 'i') + cases[count++] = other; + + return count; +} + +/* The handlers for locale characters. */ +static RE_EncodingTable locale_encoding = { + locale_has_property_wrapper, + locale_at_boundary, + locale_at_word_start, + locale_at_word_end, + locale_at_boundary, /* No special "default word boundary" for locale. */ + locale_at_word_start, /* No special "default start of a word" for locale. */ + locale_at_word_end, /* No special "default end of a word" for locale. */ + at_boundary_always, /* No special "grapheme boundary" for locale. */ + ascii_is_line_sep, /* Assume locale line separators are same as ASCII. */ + ascii_at_line_start, /* Assume locale line separators are same as ASCII. */ + ascii_at_line_end, /* Assume locale line separators are same as ASCII. */ + locale_possible_turkic, + locale_all_cases, + locale_simple_case_fold, + locale_full_case_fold, + locale_all_turkic_i, +}; + +/* Unicode-specific. */ + +/* Checks whether a Unicode character has a property. */ +Py_LOCAL_INLINE(BOOL) unicode_has_property(RE_CODE property, Py_UCS4 ch) { + RE_UINT32 prop; + RE_UINT32 value; + RE_UINT32 v; + + prop = property >> 16; + if (prop >= sizeof(re_get_property) / sizeof(re_get_property[0])) + return FALSE; + + value = property & 0xFFFF; + v = re_get_property[prop](ch); + + if (v == value) + return TRUE; + + if (prop == RE_PROP_GC) { + switch (value) { + case RE_PROP_ASSIGNED: + return v != RE_PROP_CN; + case RE_PROP_C: + return (RE_PROP_C_MASK & (1 << v)) != 0; + case RE_PROP_CASEDLETTER: + return v == RE_PROP_LU || v == RE_PROP_LL || v == RE_PROP_LT; + case RE_PROP_L: + return (RE_PROP_L_MASK & (1 << v)) != 0; + case RE_PROP_M: + return (RE_PROP_M_MASK & (1 << v)) != 0; + case RE_PROP_N: + return (RE_PROP_N_MASK & (1 << v)) != 0; + case RE_PROP_P: + return (RE_PROP_P_MASK & (1 << v)) != 0; + case RE_PROP_S: + return (RE_PROP_S_MASK & (1 << v)) != 0; + case RE_PROP_Z: + return (RE_PROP_Z_MASK & (1 << v)) != 0; + } + } + + return FALSE; +} + +/* Wrapper for calling 'unicode_has_property' via a pointer. */ +static BOOL unicode_has_property_wrapper(RE_LocaleInfo* locale_info, RE_CODE + property, Py_UCS4 ch) { + return unicode_has_property(property, ch); +} + +/* Checks whether there's a word character to the left. */ +Py_LOCAL_INLINE(BOOL) unicode_word_left(RE_State* state, Py_ssize_t text_pos) { + return text_pos > 0 && unicode_has_property(RE_PROP_WORD, + state->char_at(state->text, text_pos - 1)); +} + +/* Checks whether there's a word character to the right. */ +Py_LOCAL_INLINE(BOOL) unicode_word_right(RE_State* state, Py_ssize_t text_pos) + { + return text_pos < state->text_length && unicode_has_property(RE_PROP_WORD, + state->char_at(state->text, text_pos)); +} + +/* Checks whether a position is on a word boundary. */ +static BOOL unicode_at_boundary(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = unicode_word_left(state, text_pos); + right = unicode_word_right(state, text_pos); + + return left != right; +} + +/* Checks whether a position is at the start of a word. */ +static BOOL unicode_at_word_start(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = unicode_word_left(state, text_pos); + right = unicode_word_right(state, text_pos); + + return !left && right; +} + +/* Checks whether a position is at the end of a word. */ +static BOOL unicode_at_word_end(RE_State* state, Py_ssize_t text_pos) { + BOOL left; + BOOL right; + + left = unicode_word_left(state, text_pos); + right = unicode_word_right(state, text_pos); + + return left && !right; +} + +/* Checks whether a character is a Unicode vowel. + * + * Only a limited number are treated as vowels. + */ +Py_LOCAL_INLINE(BOOL) is_unicode_vowel(Py_UCS4 ch) { + switch (Py_UNICODE_TOLOWER((Py_UNICODE)ch)) { + case 'a': case 0xE0: case 0xE1: case 0xE2: + case 'e': case 0xE8: case 0xE9: case 0xEA: + case 'i': case 0xEC: case 0xED: case 0xEE: + case 'o': case 0xF2: case 0xF3: case 0xF4: + case 'u': case 0xF9: case 0xFA: case 0xFB: + return TRUE; + default: + return FALSE; + } +} + +/* Checks whether a position is on a default word boundary. + * + * The rules are defined here: + * http://www.unicode.org/reports/tr29/#Default_Word_Boundaries + */ +static BOOL unicode_at_default_boundary(RE_State* state, Py_ssize_t text_pos) { + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + int prop; + int prop_m1; + Py_ssize_t pos_m1; + Py_ssize_t pos_m2; + int prop_m2; + Py_ssize_t pos_p0; + int prop_p0; + Py_ssize_t pos_p1; + int prop_p1; + + /* Break at the start and end of the text. */ + /* WB1 */ + if (text_pos <= 0) + return TRUE; + + /* WB2 */ + if (text_pos >= state->text_length) + return TRUE; + + char_at = state->char_at; + + prop = (int)re_get_word_break(char_at(state->text, text_pos)); + prop_m1 = (int)re_get_word_break(char_at(state->text, text_pos - 1)); + + /* Don't break within CRLF. */ + /* WB3 */ + if (prop_m1 == RE_BREAK_CR && prop == RE_BREAK_LF) + return FALSE; + + /* Otherwise break before and after Newlines (including CR and LF). */ + /* WB3a and WB3b */ + if (prop_m1 == RE_BREAK_NEWLINE || prop_m1 == RE_BREAK_CR || prop_m1 == + RE_BREAK_LF || prop == RE_BREAK_NEWLINE || prop == RE_BREAK_CR || prop == + RE_BREAK_LF) + return TRUE; + + /* WB4 */ + /* Get the property of the previous character, ignoring Format and Extend + * characters. + */ + pos_m1 = text_pos - 1; + prop_m1 = RE_BREAK_OTHER; + while (pos_m1 >= 0) { + prop_m1 = (int)re_get_word_break(char_at(state->text, pos_m1)); + if (prop_m1 != RE_BREAK_EXTEND && prop_m1 != RE_BREAK_FORMAT) + break; + + --pos_m1; + } + + /* Get the property of the preceding character, ignoring Format and Extend + * characters. + */ + pos_m2 = pos_m1 - 1; + prop_m2 = RE_BREAK_OTHER; + while (pos_m2 >= 0) { + prop_m2 = (int)re_get_word_break(char_at(state->text, pos_m2)); + if (prop_m2 != RE_BREAK_EXTEND && prop_m2 != RE_BREAK_FORMAT) + break; + + --pos_m2; + } + + /* Get the property of the next character, ignoring Format and Extend + * characters. + */ + pos_p0 = text_pos; + prop_p0 = prop; + while (pos_p0 < state->text_length) { + prop_p0 = (int)re_get_word_break(char_at(state->text, pos_p0)); + if (prop_p0 != RE_BREAK_EXTEND && prop_p0 != RE_BREAK_FORMAT) + break; + + ++pos_p0; + } + + /* Get the property of the following character, ignoring Format and Extend + * characters. + */ + pos_p1 = pos_p0 + 1; + prop_p1 = RE_BREAK_OTHER; + while (pos_p1 < state->text_length) { + prop_p1 = (int)re_get_word_break(char_at(state->text, pos_p1)); + if (prop_p1 != RE_BREAK_EXTEND && prop_p1 != RE_BREAK_FORMAT) + break; + + ++pos_p1; + } + + /* Don't break between most letters. */ + /* WB5 */ + if ((prop_m1 == RE_BREAK_ALETTER || prop_m1 == RE_BREAK_HEBREWLETTER) && + (prop_p0 == RE_BREAK_ALETTER || prop_p0 == RE_BREAK_HEBREWLETTER)) + return FALSE; + + /* Break between apostrophe and vowels (French, Italian). */ + /* WB5a */ + if (pos_m1 >= 0 && char_at(state->text, pos_m1) == '\'' && + is_unicode_vowel(char_at(state->text, text_pos))) + return TRUE; + + /* Don't break letters across certain punctuation. */ + /* WB6 */ + if ((prop_m1 == RE_BREAK_ALETTER || prop_m1 == RE_BREAK_HEBREWLETTER) && + (prop_p0 == RE_BREAK_MIDLETTER || prop_p0 == RE_BREAK_MIDNUMLET || + prop_p0 == RE_BREAK_SINGLEQUOTE) && (prop_p1 == RE_BREAK_ALETTER || + prop_p1 == RE_BREAK_HEBREWLETTER)) + return FALSE; + /* WB7 */ + if ((prop_m2 == RE_BREAK_ALETTER || prop_m2 == RE_BREAK_HEBREWLETTER) && + (prop_m1 == RE_BREAK_MIDLETTER || prop_m1 == RE_BREAK_MIDNUMLET || + prop_m1 == RE_BREAK_SINGLEQUOTE) && (prop_p0 == RE_BREAK_ALETTER || + prop_p0 == RE_BREAK_HEBREWLETTER)) + return FALSE; + /* WB7a */ + if (prop_m1 == RE_BREAK_HEBREWLETTER && prop_p0 == RE_BREAK_SINGLEQUOTE) + return FALSE; + /* WB7b */ + if (prop_m1 == RE_BREAK_HEBREWLETTER && prop_p0 == RE_BREAK_DOUBLEQUOTE && + prop_p1 == RE_BREAK_HEBREWLETTER) + return FALSE; + /* WB7c */ + if (prop_m2 == RE_BREAK_HEBREWLETTER && prop_m1 == RE_BREAK_DOUBLEQUOTE && + prop_p0 == RE_BREAK_HEBREWLETTER) + return FALSE; + + /* Don't break within sequences of digits, or digits adjacent to letters + * ("3a", or "A3"). + */ + /* WB8 */ + if (prop_m1 == RE_BREAK_NUMERIC && prop_p0 == RE_BREAK_NUMERIC) + return FALSE; + /* WB9 */ + if ((prop_m1 == RE_BREAK_ALETTER || prop_m1 == RE_BREAK_HEBREWLETTER) && + prop_p0 == RE_BREAK_NUMERIC) + return FALSE; + /* WB10 */ + if (prop_m1 == RE_BREAK_NUMERIC && (prop_p0 == RE_BREAK_ALETTER || prop_p0 + == RE_BREAK_HEBREWLETTER)) + return FALSE; + + /* Don't break within sequences, such as "3.2" or "3,456.789". */ + /* WB11 */ + if (prop_m2 == RE_BREAK_NUMERIC && (prop_m1 == RE_BREAK_MIDNUM || prop_m1 + == RE_BREAK_MIDNUMLET || prop_m1 == RE_BREAK_SINGLEQUOTE) && prop_p0 == + RE_BREAK_NUMERIC) + return FALSE; + /* WB12 */ + if (prop_m1 == RE_BREAK_NUMERIC && (prop_p0 == RE_BREAK_MIDNUM || prop_p0 + == RE_BREAK_MIDNUMLET || prop_p0 == RE_BREAK_SINGLEQUOTE) && prop_p1 == + RE_BREAK_NUMERIC) + return FALSE; + + /* Don't break between Katakana. */ + /* WB13 */ + if (prop_m1 == RE_BREAK_KATAKANA && prop_p0 == RE_BREAK_KATAKANA) + return FALSE; + + /* Don't break from extenders. */ + /* WB13a */ + if ((prop_m1 == RE_BREAK_ALETTER || prop_m1 == RE_BREAK_HEBREWLETTER || + prop_m1 == RE_BREAK_NUMERIC || prop_m1 == RE_BREAK_KATAKANA || prop_m1 == + RE_BREAK_EXTENDNUMLET) && prop_p0 == RE_BREAK_EXTENDNUMLET) + return FALSE; + /* WB13b */ + if (prop_m1 == RE_BREAK_EXTENDNUMLET && (prop_p0 == RE_BREAK_ALETTER || + prop_p0 == RE_BREAK_HEBREWLETTER || prop_p0 == RE_BREAK_NUMERIC || + prop_p0 == RE_BREAK_KATAKANA)) + return FALSE; + + /* Don't break between regional indicator symbols. */ + /* WB13c */ + if (prop_m1 == RE_BREAK_REGIONALINDICATOR && prop_p0 == + RE_BREAK_REGIONALINDICATOR) + return FALSE; + + /* Otherwise, break everywhere (including around ideographs). */ + /* WB14 */ + return TRUE; +} + +/* Checks whether a position is at the start/end of a word. */ +Py_LOCAL_INLINE(BOOL) unicode_at_default_word_start_or_end(RE_State* state, + Py_ssize_t text_pos, BOOL at_start) { + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + BOOL before; + BOOL after; + Py_UCS4 char_0; + Py_UCS4 char_m1; + int prop; + int prop_m1; + Py_ssize_t pos_m1; + Py_ssize_t pos_p1; + int prop_p1; + Py_UCS4 char_p1; + Py_ssize_t pos_m2; + int prop_m2; + Py_UCS4 char_m2; + + char_at = state->char_at; + + /* At the start or end of the text. */ + if (text_pos <= 0 || text_pos >= state->text_length) { + before = unicode_word_left(state, text_pos); + after = unicode_word_right(state, text_pos); + + return before != at_start && after == at_start; + } + + char_0 = char_at(state->text, text_pos); + char_m1 = char_at(state->text, text_pos - 1); + prop = (int)re_get_word_break(char_0); + prop_m1 = (int)re_get_word_break(char_m1); + + /* No break within CRLF. */ + if (prop_m1 == RE_BREAK_CR && prop == RE_BREAK_LF) + return FALSE; + + /* Break before and after Newlines (including CR and LF). */ + if (prop_m1 == RE_BREAK_NEWLINE || prop_m1 == RE_BREAK_CR || prop_m1 == + RE_BREAK_LF || prop == RE_BREAK_NEWLINE || prop == RE_BREAK_CR || prop == + RE_BREAK_LF) { + before = unicode_has_property(RE_PROP_WORD, char_m1); + after = unicode_has_property(RE_PROP_WORD, char_0); + + return before != at_start && after == at_start; + } + + /* No break just before Format or Extend characters. */ + if (prop == RE_BREAK_EXTEND || prop == RE_BREAK_FORMAT) + return FALSE; + + /* Get the property of the previous character. */ + pos_m1 = text_pos - 1; + prop_m1 = RE_BREAK_OTHER; + while (pos_m1 >= 0) { + char_m1 = char_at(state->text, pos_m1); + prop_m1 = (int)re_get_word_break(char_m1); + if (prop_m1 != RE_BREAK_EXTEND && prop_m1 != RE_BREAK_FORMAT) + break; + + --pos_m1; + } + + /* No break between most letters. */ + if (prop_m1 == RE_BREAK_ALETTER && prop == RE_BREAK_ALETTER) + return FALSE; + + if (pos_m1 >= 0 && char_m1 == '\'' && is_unicode_vowel(char_0)) + return TRUE; + + pos_p1 = text_pos + 1; + prop_p1 = RE_BREAK_OTHER; + while (pos_p1 < state->text_length) { + char_p1 = char_at(state->text, pos_p1); + prop_p1 = (int)re_get_word_break(char_p1); + if (prop_p1 != RE_BREAK_EXTEND && prop_p1 != RE_BREAK_FORMAT) + break; + + ++pos_p1; + } + + /* No break letters across certain punctuation. */ + if (prop_m1 == RE_BREAK_ALETTER && (prop == RE_BREAK_MIDLETTER || prop == + RE_BREAK_MIDNUMLET) && prop_p1 == RE_BREAK_ALETTER) + return FALSE; + + pos_m2 = pos_m1 - 1; + prop_m2 = RE_BREAK_OTHER; + while (pos_m2 >= 0) { + char_m2 = char_at(state->text, pos_m2); + prop_m2 = (int)re_get_word_break(char_m2); + if (prop_m2 != RE_BREAK_EXTEND && prop_m1 != RE_BREAK_FORMAT) + break; + + --pos_m2; + } + + if (prop_m2 == RE_BREAK_ALETTER && (prop_m1 == RE_BREAK_MIDLETTER || + prop_m1 == RE_BREAK_MIDNUMLET) && prop == RE_BREAK_ALETTER) + return FALSE; + + /* No break within sequences of digits, or digits adjacent to letters + * ("3a", or "A3"). + */ + if ((prop_m1 == RE_BREAK_NUMERIC || prop_m1 == RE_BREAK_ALETTER) && prop == + RE_BREAK_NUMERIC) + return FALSE; + + if (prop_m1 == RE_BREAK_NUMERIC && prop == RE_BREAK_ALETTER) + return FALSE; + + /* No break within sequences, such as "3.2" or "3,456.789". */ + if (prop_m2 == RE_BREAK_NUMERIC && (prop_m1 == RE_BREAK_MIDNUM || prop_m1 + == RE_BREAK_MIDNUMLET) && prop == RE_BREAK_NUMERIC) + return FALSE; + + if (prop_m1 == RE_BREAK_NUMERIC && (prop == RE_BREAK_MIDNUM || prop == + RE_BREAK_MIDNUMLET) && prop_p1 == RE_BREAK_NUMERIC) + return FALSE; + + /* No break between Katakana. */ + if (prop_m1 == RE_BREAK_KATAKANA && prop == RE_BREAK_KATAKANA) + return FALSE; + + /* No break from extenders. */ + if ((prop_m1 == RE_BREAK_ALETTER || prop_m1 == RE_BREAK_NUMERIC || prop_m1 + == RE_BREAK_KATAKANA || prop_m1 == RE_BREAK_EXTENDNUMLET) && prop == + RE_BREAK_EXTENDNUMLET) + return FALSE; + + if (prop_m1 == RE_BREAK_EXTENDNUMLET && (prop == RE_BREAK_ALETTER || prop + == RE_BREAK_NUMERIC || prop == RE_BREAK_KATAKANA)) + return FALSE; + + /* Otherwise, break everywhere (including around ideographs). */ + before = unicode_has_property(RE_PROP_WORD, char_m1); + after = unicode_has_property(RE_PROP_WORD, char_0); + + return before != at_start && after == at_start; +} + +/* Checks whether a position is at the start of a word. */ +static BOOL unicode_at_default_word_start(RE_State* state, Py_ssize_t text_pos) + { + return unicode_at_default_word_start_or_end(state, text_pos, TRUE); +} + +/* Checks whether a position is at the end of a word. */ +static BOOL unicode_at_default_word_end(RE_State* state, Py_ssize_t text_pos) { + return unicode_at_default_word_start_or_end(state, text_pos, FALSE); +} + +/* Checks whether a position is on a grapheme boundary. + * + * The rules are defined here: + * http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries + */ +static BOOL unicode_at_grapheme_boundary(RE_State* state, Py_ssize_t text_pos) + { + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + int prop; + int prop_m1; + + /* Break at the start and end of the text. */ + /* GB1 */ + if (text_pos <= 0) + return TRUE; + + /* GB2 */ + if (text_pos >= state->text_length) + return TRUE; + + char_at = state->char_at; + + prop = (int)re_get_grapheme_cluster_break(char_at(state->text, text_pos)); + prop_m1 = (int)re_get_grapheme_cluster_break(char_at(state->text, text_pos + - 1)); + + /* Don't break within CRLF. */ + /* GB3 */ + if (prop_m1 == RE_GBREAK_CR && prop == RE_GBREAK_LF) + return FALSE; + + /* Otherwise break before and after controls (including CR and LF). */ + /* GB4 and GB5 */ + if (prop_m1 == RE_GBREAK_CONTROL || prop_m1 == RE_GBREAK_CR || prop_m1 == + RE_GBREAK_LF || prop == RE_GBREAK_CONTROL || prop == RE_GBREAK_CR || prop + == RE_GBREAK_LF) + return TRUE; + + /* Don't break Hangul syllable sequences. */ + /* GB6 */ + if (prop_m1 == RE_GBREAK_L && (prop == RE_GBREAK_L || prop == RE_GBREAK_V + || prop == RE_GBREAK_LV || prop == RE_GBREAK_LVT)) + return FALSE; + /* GB7 */ + if ((prop_m1 == RE_GBREAK_LV || prop_m1 == RE_GBREAK_V) && (prop == + RE_GBREAK_V || prop == RE_GBREAK_T)) + return FALSE; + /* GB8 */ + if ((prop_m1 == RE_GBREAK_LVT || prop_m1 == RE_GBREAK_T) && (prop == + RE_GBREAK_T)) + return FALSE; + + /* Don't break between regional indicator symbols. */ + /* GB8a */ + if (prop_m1 == RE_GBREAK_REGIONALINDICATOR && prop == + RE_GBREAK_REGIONALINDICATOR) + return FALSE; + + /* Don't break just before Extend characters. */ + /* GB9 */ + if (prop == RE_GBREAK_EXTEND) + return FALSE; + + /* Don't break before SpacingMarks, or after Prepend characters. */ + /* GB9a */ + if (prop == RE_GBREAK_SPACINGMARK) + return FALSE; + + /* GB9b */ + if (prop_m1 == RE_GBREAK_PREPEND) + return FALSE; + + /* Otherwise, break everywhere. */ + /* GB10 */ + return TRUE; +} + +/* Checks whether a character is a line separator. */ +static BOOL unicode_is_line_sep(Py_UCS4 ch) { + return (0x0A <= ch && ch <= 0x0D) || ch == 0x85 || ch == 0x2028 || ch == + 0x2029; +} + +/* Checks whether a position is at the start of a line. */ +static BOOL unicode_at_line_start(RE_State* state, Py_ssize_t text_pos) { + Py_UCS4 ch; + + if (text_pos <= 0) + return TRUE; + + ch = state->char_at(state->text, text_pos - 1); + + if (ch == 0x0D) { + if (text_pos >= state->text_length) + return TRUE; + + /* No line break inside CRLF. */ + return state->char_at(state->text, text_pos) != 0x0A; + } + + return (0x0A <= ch && ch <= 0x0D) || ch == 0x85 || ch == 0x2028 || ch == + 0x2029; +} + +/* Checks whether a position is at the end of a line. */ +static BOOL unicode_at_line_end(RE_State* state, Py_ssize_t text_pos) { + Py_UCS4 ch; + + if (text_pos >= state->text_length) + return TRUE; + + ch = state->char_at(state->text, text_pos); + + if (ch == 0x0A) { + if (text_pos <= 0) + return TRUE; + + /* No line break inside CRLF. */ + return state->char_at(state->text, text_pos - 1) != 0x0D; + } + + return (0x0A <= ch && ch <= 0x0D) || ch == 0x85 || ch == 0x2028 || ch == + 0x2029; +} + +/* Checks whether a character could be Turkic (variants of I/i). */ +static BOOL unicode_possible_turkic(RE_LocaleInfo* locale_info, Py_UCS4 ch) { + return ch == 'I' || ch == 'i' || ch == 0x0130 || ch == 0x0131; +} + +/* Gets all the cases of a character. */ +static int unicode_all_cases(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + codepoints) { + return re_get_all_cases(ch, codepoints); +} + +/* Returns a character with its case folded, unless it could be Turkic + * (variants of I/i). + */ +static Py_UCS4 unicode_simple_case_fold(RE_LocaleInfo* locale_info, Py_UCS4 ch) + { + /* Is it a possible Turkic character? If so, pass it through unchanged. */ + if (ch == 'I' || ch == 'i' || ch == 0x0130 || ch == 0x0131) + return ch; + + return (Py_UCS4)re_get_simple_case_folding(ch); +} + +/* Returns a character with its case folded, unless it could be Turkic + * (variants of I/i). + */ +static int unicode_full_case_fold(RE_LocaleInfo* locale_info, Py_UCS4 ch, + Py_UCS4* folded) { + /* Is it a possible Turkic character? If so, pass it through unchanged. */ + if (ch == 'I' || ch == 'i' || ch == 0x0130 || ch == 0x0131) { + folded[0] = ch; + return 1; + } + + return re_get_full_case_folding(ch, folded); +} + +/* Gets all the case variants of Turkic 'I'. */ +static int unicode_all_turkic_i(RE_LocaleInfo* locale_info, Py_UCS4 ch, + Py_UCS4* cases) { + int count; + + count = 0; + + cases[count++] = ch; + + if (ch != 'I') + cases[count++] = 'I'; + + if (ch != 'i') + cases[count++] = 'i'; + + if (ch != 0x130) + cases[count++] = 0x130; + + if (ch != 0x131) + cases[count++] = 0x131; + + return count; + +} + +/* The handlers for Unicode characters. */ +static RE_EncodingTable unicode_encoding = { + unicode_has_property_wrapper, + unicode_at_boundary, + unicode_at_word_start, + unicode_at_word_end, + unicode_at_default_boundary, + unicode_at_default_word_start, + unicode_at_default_word_end, + unicode_at_grapheme_boundary, + unicode_is_line_sep, + unicode_at_line_start, + unicode_at_line_end, + unicode_possible_turkic, + unicode_all_cases, + unicode_simple_case_fold, + unicode_full_case_fold, + unicode_all_turkic_i, +}; + +Py_LOCAL_INLINE(PyObject*) get_object(char* module_name, char* object_name); + +/* Sets the error message. */ +Py_LOCAL_INLINE(void) set_error(int status, PyObject* object) { + TRACE(("<<set_error>>\n")) + + if (!error_exception) + error_exception = get_object("_" RE_MODULE "_core", "error"); + + switch (status) { + case RE_ERROR_BACKTRACKING: + PyErr_SetString(error_exception, "too much backtracking"); + break; + case RE_ERROR_CONCURRENT: + PyErr_SetString(PyExc_ValueError, "concurrent not int or None"); + break; + case RE_ERROR_GROUP_INDEX_TYPE: + if (object) + PyErr_Format(PyExc_TypeError, + "group indices must be integers or strings, not %.200s", + object->ob_type->tp_name); + else + PyErr_Format(PyExc_TypeError, + "group indices must be integers or strings"); + break; + case RE_ERROR_ILLEGAL: + PyErr_SetString(PyExc_RuntimeError, "invalid RE code"); + break; + case RE_ERROR_INDEX: + PyErr_SetString(PyExc_TypeError, "string indices must be integers"); + break; + case RE_ERROR_INTERRUPTED: + /* An exception has already been raised, so let it fly. */ + break; + case RE_ERROR_INVALID_GROUP_REF: + PyErr_SetString(error_exception, "invalid group reference"); + break; + case RE_ERROR_MEMORY: + PyErr_NoMemory(); + break; + case RE_ERROR_NOT_STRING: + PyErr_Format(PyExc_TypeError, "expected string instance, %.200s found", + object->ob_type->tp_name); + break; + case RE_ERROR_NOT_UNICODE: + PyErr_Format(PyExc_TypeError, "expected unicode instance, not %.200s", + object->ob_type->tp_name); + break; + case RE_ERROR_NO_SUCH_GROUP: + PyErr_SetString(PyExc_IndexError, "no such group"); + break; + case RE_ERROR_REPLACEMENT: + PyErr_SetString(error_exception, "invalid replacement"); + break; + default: + /* Other error codes indicate compiler/engine bugs. */ + PyErr_SetString(PyExc_RuntimeError, + "internal error in regular expression engine"); + break; + } +} + +/* Allocates memory. + * + * Sets the Python error handler and returns NULL if the allocation fails. + */ +Py_LOCAL_INLINE(void*) re_alloc(size_t size) { + void* new_ptr; + + new_ptr = PyMem_Malloc(size); + if (!new_ptr) + set_error(RE_ERROR_MEMORY, NULL); + + return new_ptr; +} + +/* Reallocates memory. + * + * Sets the Python error handler and returns NULL if the reallocation fails. + */ +Py_LOCAL_INLINE(void*) re_realloc(void* ptr, size_t size) { + void* new_ptr; + + new_ptr = PyMem_Realloc(ptr, size); + if (!new_ptr) + set_error(RE_ERROR_MEMORY, NULL); + + return new_ptr; +} + +/* Deallocates memory. */ +Py_LOCAL_INLINE(void) re_dealloc(void* ptr) { + PyMem_Free(ptr); +} + +/* Releases the GIL if multithreading is enabled. */ +Py_LOCAL_INLINE(void) release_GIL(RE_SafeState* safe_state) { + if (safe_state->re_state->is_multithreaded) + safe_state->thread_state = PyEval_SaveThread(); +} + +/* Acquires the GIL if multithreading is enabled. */ +Py_LOCAL_INLINE(void) acquire_GIL(RE_SafeState* safe_state) { + if (safe_state->re_state->is_multithreaded) + PyEval_RestoreThread(safe_state->thread_state); +} + +/* Allocates memory, holding the GIL during the allocation. + * + * Sets the Python error handler and returns NULL if the allocation fails. + */ +Py_LOCAL_INLINE(void*) safe_alloc(RE_SafeState* safe_state, size_t size) { + void* new_ptr; + + acquire_GIL(safe_state); + + new_ptr = re_alloc(size); + + release_GIL(safe_state); + + return new_ptr; +} + +/* Reallocates memory, holding the GIL during the reallocation. + * + * Sets the Python error handler and returns NULL if the reallocation fails. + */ +Py_LOCAL_INLINE(void*) safe_realloc(RE_SafeState* safe_state, void* ptr, size_t + size) { + void* new_ptr; + + acquire_GIL(safe_state); + + new_ptr = re_realloc(ptr, size); + + release_GIL(safe_state); + + return new_ptr; +} + +/* Deallocates memory, holding the GIL during the deallocation. */ +Py_LOCAL_INLINE(void) safe_dealloc(RE_SafeState* safe_state, void* ptr) { + acquire_GIL(safe_state); + + re_dealloc(ptr); + + release_GIL(safe_state); +} + +/* Checks for KeyboardInterrupt, holding the GIL during the check. */ +Py_LOCAL_INLINE(BOOL) safe_check_signals(RE_SafeState* safe_state) { + BOOL result; + + acquire_GIL(safe_state); + + result = (BOOL)PyErr_CheckSignals(); + + release_GIL(safe_state); + + return result; +} + +/* Checks whether a character is in a range. */ +Py_LOCAL_INLINE(BOOL) in_range(Py_UCS4 lower, Py_UCS4 upper, Py_UCS4 ch) { + return lower <= ch && ch <= upper; +} + +/* Checks whether a character is in a range, ignoring case. */ +Py_LOCAL_INLINE(BOOL) in_range_ign(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, Py_UCS4 lower, Py_UCS4 upper, Py_UCS4 ch) { + int count; + Py_UCS4 cases[RE_MAX_CASES]; + int i; + + count = encoding->all_cases(locale_info, ch, cases); + + for (i = 0; i < count; i++) { + if (in_range(lower, upper, cases[i])) + return TRUE; + } + + return FALSE; +} + +/* Checks whether 2 characters are the same. */ +Py_LOCAL_INLINE(BOOL) same_char(Py_UCS4 ch1, Py_UCS4 ch2) { + return ch1 == ch2; +} + +/* Wrapper for calling 'same_char' via a pointer. */ +static BOOL same_char_wrapper(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, Py_UCS4 ch1, Py_UCS4 ch2) { + return same_char(ch1, ch2); +} + +/* Checks whether 2 characters are the same, ignoring case. */ +Py_LOCAL_INLINE(BOOL) same_char_ign(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, Py_UCS4 ch1, Py_UCS4 ch2) { + int count; + Py_UCS4 cases[RE_MAX_CASES]; + int i; + + if (ch1 == ch2) + return TRUE; + + count = encoding->all_cases(locale_info, ch1, cases); + + for (i = 1; i < count; i++) { + if (cases[i] == ch2) + return TRUE; + } + + return FALSE; +} + +/* Wrapper for calling 'same_char' via a pointer. */ +static BOOL same_char_ign_wrapper(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, Py_UCS4 ch1, Py_UCS4 ch2) { + return same_char_ign(encoding, locale_info, ch1, ch2); +} + +/* Checks whether a character is anything except a newline. */ +Py_LOCAL_INLINE(BOOL) matches_ANY(RE_EncodingTable* encoding, RE_Node* node, + Py_UCS4 ch) { + return ch != '\n'; +} + +/* Checks whether a character is anything except a line separator. */ +Py_LOCAL_INLINE(BOOL) matches_ANY_U(RE_EncodingTable* encoding, RE_Node* node, + Py_UCS4 ch) { + return !encoding->is_line_sep(ch); +} + +/* Checks whether 2 characters are the same. */ +Py_LOCAL_INLINE(BOOL) matches_CHARACTER(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch) { + return same_char(node->values[0], ch); +} + +/* Checks whether 2 characters are the same, ignoring case. */ +Py_LOCAL_INLINE(BOOL) matches_CHARACTER_IGN(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch) { + return same_char_ign(encoding, locale_info, node->values[0], ch); +} + +/* Checks whether a character has a property. */ +Py_LOCAL_INLINE(BOOL) matches_PROPERTY(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch) { + return encoding->has_property(locale_info, node->values[0], ch); +} + +/* Checks whether a character has a property, ignoring case. */ +Py_LOCAL_INLINE(BOOL) matches_PROPERTY_IGN(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch) { + RE_UINT32 property; + RE_UINT32 prop; + + property = node->values[0]; + prop = property >> 16; + + /* We need to do special handling of case-sensitive properties according to + * the 'encoding'. + */ + if (encoding == &unicode_encoding) { + /* We are working with Unicode. */ + if (property == RE_PROP_GC_LU || property == RE_PROP_GC_LL || property + == RE_PROP_GC_LT) { + RE_UINT32 value; + + value = re_get_general_category(ch); + + return value == RE_PROP_LU || value == RE_PROP_LL || value == + RE_PROP_LT; + } else if (prop == RE_PROP_UPPERCASE || prop == RE_PROP_LOWERCASE) + return (BOOL)re_get_cased(ch); + + /* The property is case-insensitive. */ + return unicode_has_property(property, ch); + } else if (encoding == &ascii_encoding) { + /* We are working with ASCII. */ + if (property == RE_PROP_GC_LU || property == RE_PROP_GC_LL || property + == RE_PROP_GC_LT) { + RE_UINT32 value; + + value = re_get_general_category(ch); + + return value == RE_PROP_LU || value == RE_PROP_LL || value == + RE_PROP_LT; + } else if (prop == RE_PROP_UPPERCASE || prop == RE_PROP_LOWERCASE) + return (BOOL)re_get_cased(ch); + + /* The property is case-insensitive. */ + return ascii_has_property(property, ch); + } else { + /* We are working with Locale. */ + if (property == RE_PROP_GC_LU || property == RE_PROP_GC_LL || property + == RE_PROP_GC_LT) + return locale_isupper(locale_info, ch) || + locale_islower(locale_info, ch); + else if (prop == RE_PROP_UPPERCASE || prop == RE_PROP_LOWERCASE) + return locale_isupper(locale_info, ch) || + locale_islower(locale_info, ch); + + /* The property is case-insensitive. */ + return locale_has_property(locale_info, property, ch); + } +} + +/* Checks whether a character is in a range. */ +Py_LOCAL_INLINE(BOOL) matches_RANGE(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, RE_Node* node, Py_UCS4 ch) { + return in_range(node->values[0], node->values[1], ch); +} + +/* Checks whether a character is in a range, ignoring case. */ +Py_LOCAL_INLINE(BOOL) matches_RANGE_IGN(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch) { + return in_range_ign(encoding, locale_info, node->values[0], + node->values[1], ch); +} + +Py_LOCAL_INLINE(BOOL) in_set_diff(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, RE_Node* node, Py_UCS4 ch); +Py_LOCAL_INLINE(BOOL) in_set_inter(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, RE_Node* node, Py_UCS4 ch); +Py_LOCAL_INLINE(BOOL) in_set_sym_diff(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch); +Py_LOCAL_INLINE(BOOL) in_set_union(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, RE_Node* node, Py_UCS4 ch); + +/* Checks whether a character matches a set member. */ +Py_LOCAL_INLINE(BOOL) matches_member(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, RE_Node* member, Py_UCS4 ch) { + switch (member->op) { + case RE_OP_CHARACTER: + /* values are: char_code */ + TRACE(("%s %d %d\n", re_op_text[member->op], member->match, + member->values[0])) + return ch == member->values[0]; + case RE_OP_PROPERTY: + /* values are: property */ + TRACE(("%s %d %d\n", re_op_text[member->op], member->match, + member->values[0])) + return encoding->has_property(locale_info, member->values[0], ch); + case RE_OP_RANGE: + /* values are: lower, upper */ + TRACE(("%s %d %d %d\n", re_op_text[member->op], member->match, + member->values[0], member->values[1])) + return in_range(member->values[0], member->values[1], ch); + case RE_OP_SET_DIFF: + TRACE(("%s\n", re_op_text[member->op])) + return in_set_diff(encoding, locale_info, member, ch); + case RE_OP_SET_INTER: + TRACE(("%s\n", re_op_text[member->op])) + return in_set_inter(encoding, locale_info, member, ch); + case RE_OP_SET_SYM_DIFF: + TRACE(("%s\n", re_op_text[member->op])) + return in_set_sym_diff(encoding, locale_info, member, ch); + case RE_OP_SET_UNION: + TRACE(("%s\n", re_op_text[member->op])) + return in_set_union(encoding, locale_info, member, ch); + case RE_OP_STRING: + { + /* values are: char_code, char_code, ... */ + size_t i; + TRACE(("%s %d %d\n", re_op_text[member->op], member->match, + member->value_count)) + + for (i = 0; i < member->value_count; i++) { + if (ch == member->values[i]) + return TRUE; + } + return FALSE; + } + default: + return FALSE; + } +} + +/* Checks whether a character matches a set member, ignoring case. */ +Py_LOCAL_INLINE(BOOL) matches_member_ign(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* member, int case_count, Py_UCS4* cases) + { + int i; + + for (i = 0; i < case_count; i++) { + switch (member->op) { + case RE_OP_CHARACTER: + /* values are: char_code */ + TRACE(("%s %d %d\n", re_op_text[member->op], member->match, + member->values[0])) + if (cases[i] == member->values[0]) + return TRUE; + break; + case RE_OP_PROPERTY: + /* values are: property */ + TRACE(("%s %d %d\n", re_op_text[member->op], member->match, + member->values[0])) + if (encoding->has_property(locale_info, member->values[0], + cases[i])) + return TRUE; + break; + case RE_OP_RANGE: + /* values are: lower, upper */ + TRACE(("%s %d %d %d\n", re_op_text[member->op], member->match, + member->values[0], member->values[1])) + if (in_range(member->values[0], member->values[1], cases[i])) + return TRUE; + break; + case RE_OP_SET_DIFF: + TRACE(("%s\n", re_op_text[member->op])) + if (in_set_diff(encoding, locale_info, member, cases[i])) + return TRUE; + break; + case RE_OP_SET_INTER: + TRACE(("%s\n", re_op_text[member->op])) + if (in_set_inter(encoding, locale_info, member, cases[i])) + return TRUE; + break; + case RE_OP_SET_SYM_DIFF: + TRACE(("%s\n", re_op_text[member->op])) + if (in_set_sym_diff(encoding, locale_info, member, cases[i])) + return TRUE; + break; + case RE_OP_SET_UNION: + TRACE(("%s\n", re_op_text[member->op])) + if (in_set_union(encoding, locale_info, member, cases[i])) + return TRUE; + break; + case RE_OP_STRING: + { + size_t j; + TRACE(("%s %d %d\n", re_op_text[member->op], member->match, + member->value_count)) + + for (j = 0; j < member->value_count; j++) { + if (cases[i] == member->values[j]) + return TRUE; + } + break; + } + default: + return TRUE; + } + } + + return FALSE; +} + +/* Checks whether a character is in a set difference. */ +Py_LOCAL_INLINE(BOOL) in_set_diff(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, RE_Node* node, Py_UCS4 ch) { + RE_Node* member; + + member = node->nonstring.next_2.node; + + if (matches_member(encoding, locale_info, member, ch) != member->match) + return FALSE; + + member = member->next_1.node; + + while (member) { + if (matches_member(encoding, locale_info, member, ch) == member->match) + return FALSE; + + member = member->next_1.node; + } + + return TRUE; +} + +/* Checks whether a character is in a set difference, ignoring case. */ +Py_LOCAL_INLINE(BOOL) in_set_diff_ign(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, int case_count, Py_UCS4* cases) { + RE_Node* member; + + member = node->nonstring.next_2.node; + + if (matches_member_ign(encoding, locale_info, member, case_count, cases) != + member->match) + return FALSE; + + member = member->next_1.node; + + while (member) { + if (matches_member_ign(encoding, locale_info, member, case_count, + cases) == member->match) + return FALSE; + + member = member->next_1.node; + } + + return TRUE; +} + +/* Checks whether a character is in a set intersection. */ +Py_LOCAL_INLINE(BOOL) in_set_inter(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, RE_Node* node, Py_UCS4 ch) { + RE_Node* member; + + member = node->nonstring.next_2.node; + + while (member) { + if (matches_member(encoding, locale_info, member, ch) != member->match) + return FALSE; + + member = member->next_1.node; + } + + return TRUE; +} + +/* Checks whether a character is in a set intersection, ignoring case. */ +Py_LOCAL_INLINE(BOOL) in_set_inter_ign(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, int case_count, Py_UCS4* cases) { + RE_Node* member; + + member = node->nonstring.next_2.node; + + while (member) { + if (matches_member_ign(encoding, locale_info, member, case_count, + cases) != member->match) + return FALSE; + + member = member->next_1.node; + } + + return TRUE; +} + +/* Checks whether a character is in a set symmetric difference. */ +Py_LOCAL_INLINE(BOOL) in_set_sym_diff(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch) { + RE_Node* member; + BOOL result; + + member = node->nonstring.next_2.node; + + result = FALSE; + + while (member) { + if (matches_member(encoding, locale_info, member, ch) == member->match) + result = !result; + + member = member->next_1.node; + } + + return result; +} + +/* Checks whether a character is in a set symmetric difference, ignoring case. + */ +Py_LOCAL_INLINE(BOOL) in_set_sym_diff_ign(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, int case_count, Py_UCS4* cases) { + RE_Node* member; + BOOL result; + + member = node->nonstring.next_2.node; + + result = FALSE; + + while (member) { + if (matches_member_ign(encoding, locale_info, member, case_count, + cases) == member->match) + result = !result; + + member = member->next_1.node; + } + + return result; +} + +/* Checks whether a character is in a set union. */ +Py_LOCAL_INLINE(BOOL) in_set_union(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, RE_Node* node, Py_UCS4 ch) { + RE_Node* member; + + member = node->nonstring.next_2.node; + + while (member) { + if (matches_member(encoding, locale_info, member, ch) == member->match) + return TRUE; + + member = member->next_1.node; + } + + return FALSE; +} + +/* Checks whether a character is in a set union, ignoring case. */ +Py_LOCAL_INLINE(BOOL) in_set_union_ign(RE_EncodingTable* encoding, + RE_LocaleInfo* locale_info, RE_Node* node, int case_count, Py_UCS4* cases) { + RE_Node* member; + + member = node->nonstring.next_2.node; + + while (member) { + if (matches_member_ign(encoding, locale_info, member, case_count, + cases) == member->match) + return TRUE; + + member = member->next_1.node; + } + + return FALSE; +} + +/* Checks whether a character is in a set. */ +Py_LOCAL_INLINE(BOOL) matches_SET(RE_EncodingTable* encoding, +RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch) { + switch (node->op) { + case RE_OP_SET_DIFF: + case RE_OP_SET_DIFF_REV: + return in_set_diff(encoding, locale_info, node, ch); + case RE_OP_SET_INTER: + case RE_OP_SET_INTER_REV: + return in_set_inter(encoding, locale_info, node, ch); + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_SYM_DIFF_REV: + return in_set_sym_diff(encoding, locale_info, node, ch); + case RE_OP_SET_UNION: + case RE_OP_SET_UNION_REV: + return in_set_union(encoding, locale_info, node, ch); + } + + return FALSE; +} + +/* Checks whether a character is in a set, ignoring case. */ +Py_LOCAL_INLINE(BOOL) matches_SET_IGN(RE_EncodingTable* encoding, +RE_LocaleInfo* locale_info, RE_Node* node, Py_UCS4 ch) { + Py_UCS4 cases[RE_MAX_CASES]; + int case_count; + + case_count = encoding->all_cases(locale_info, ch, cases); + + switch (node->op) { + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_DIFF_IGN_REV: + return in_set_diff_ign(encoding, locale_info, node, case_count, cases); + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_INTER_IGN_REV: + return in_set_inter_ign(encoding, locale_info, node, case_count, + cases); + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_SYM_DIFF_IGN_REV: + return in_set_sym_diff_ign(encoding, locale_info, node, case_count, + cases); + case RE_OP_SET_UNION_IGN: + case RE_OP_SET_UNION_IGN_REV: + return in_set_union_ign(encoding, locale_info, node, case_count, + cases); + } + + return FALSE; +} + +/* Resets a guard list. */ +Py_LOCAL_INLINE(void) reset_guard_list(RE_GuardList* guard_list) { + guard_list->count = 0; + guard_list->last_text_pos = -1; +} + +/* Clears the groups. */ +Py_LOCAL_INLINE(void) clear_groups(RE_State* state) { + size_t i; + + for (i = 0; i < state->pattern->true_group_count; i++) { + RE_GroupData* group; + + group = &state->groups[i]; + group->span.start = -1; + group->span.end = -1; + group->capture_count = 0; + group->current_capture = -1; + } +} + +/* Initialises the state for a match. */ +Py_LOCAL_INLINE(void) init_match(RE_State* state) { + RE_AtomicBlock* current; + size_t i; + + /* Reset the backtrack. */ + state->current_backtrack_block = &state->backtrack_block; + state->current_backtrack_block->count = 0; + state->current_saved_groups = state->first_saved_groups; + state->backtrack = NULL; + state->search_anchor = state->text_pos; + state->match_pos = state->text_pos; + + /* Reset the atomic stack. */ + current = state->current_atomic_block; + if (current) { + while (current->previous) + current = current->previous; + + state->current_atomic_block = current; + state->current_atomic_block->count = 0; + } + + /* Reset the guards for the repeats. */ + for (i = 0; i < state->pattern->repeat_count; i++) { + reset_guard_list(&state->repeats[i].body_guard_list); + reset_guard_list(&state->repeats[i].tail_guard_list); + } + + /* Reset the guards for the fuzzy sections. */ + for (i = 0; i < state->pattern->fuzzy_count; i++) { + reset_guard_list(&state->fuzzy_guards[i].body_guard_list); + reset_guard_list(&state->fuzzy_guards[i].tail_guard_list); + } + + /* Clear the groups. */ + clear_groups(state); + + /* Reset the guards for the group calls. */ + for (i = 0; i < state->pattern->call_ref_info_count; i++) + reset_guard_list(&state->group_call_guard_list[i]); + + /* Clear the counts and cost for matching. */ + if (state->pattern->is_fuzzy) { + memset(state->fuzzy_info.counts, 0, sizeof(state->fuzzy_info.counts)); + memset(state->total_fuzzy_counts, 0, + sizeof(state->total_fuzzy_counts)); + } + + state->fuzzy_info.total_cost = 0; + state->total_errors = 0; + state->too_few_errors = FALSE; + state->found_match = FALSE; + state->capture_change = 0; + state->iterations = 0; +} + +/* Adds a new backtrack entry. */ +Py_LOCAL_INLINE(BOOL) add_backtrack(RE_SafeState* safe_state, RE_UINT8 op) { + RE_State* state; + RE_BacktrackBlock* current; + + state = safe_state->re_state; + + current = state->current_backtrack_block; + if (current->count >= current->capacity) { + if (!current->next) { + RE_BacktrackBlock* next; + + /* Is there too much backtracking? */ + if (state->backtrack_allocated >= RE_MAX_BACKTRACK_ALLOC) + return FALSE; + + next = (RE_BacktrackBlock*)safe_alloc(safe_state, + sizeof(RE_BacktrackBlock)); + if (!next) + return FALSE; + + next->previous = current; + next->next = NULL; + next->capacity = RE_BACKTRACK_BLOCK_SIZE; + current->next = next; + + state->backtrack_allocated += RE_BACKTRACK_BLOCK_SIZE; + } + + current = current->next; + current->count = 0; + state->current_backtrack_block = current; + } + + state->backtrack = ¤t->items[current->count++]; + state->backtrack->op = op; + + return TRUE; +} + +/* Gets the last backtrack entry. + * + * It'll never be called when there are _no_ entries. + */ +Py_LOCAL_INLINE(RE_BacktrackData*) last_backtrack(RE_State* state) { + RE_BacktrackBlock* current; + + current = state->current_backtrack_block; + state->backtrack = ¤t->items[current->count - 1]; + + return state->backtrack; +} + +/* Discards the last backtrack entry. + * + * It'll never be called to discard the _only_ entry. + */ +Py_LOCAL_INLINE(void) discard_backtrack(RE_State* state) { + RE_BacktrackBlock* current; + + current = state->current_backtrack_block; + --current->count; + if (current->count == 0 && current->previous) + state->current_backtrack_block = current->previous; +} + +/* Pushes a new empty entry onto the atomic stack. */ +Py_LOCAL_INLINE(RE_AtomicData*) push_atomic(RE_SafeState* safe_state) { + RE_State* state; + RE_AtomicBlock* current; + + state = safe_state->re_state; + + current = state->current_atomic_block; + if (!current || current->count >= current->capacity) { + /* The current block is full. */ + if (current && current->next) + /* Advance to the next block. */ + current = current->next; + else { + /* Add a new block. */ + RE_AtomicBlock* next; + + next = (RE_AtomicBlock*)safe_alloc(safe_state, + sizeof(RE_AtomicBlock)); + if (!next) + return NULL; + + next->previous = current; + next->next = NULL; + next->capacity = RE_ATOMIC_BLOCK_SIZE; + if (current) + /* The current block is the last one. */ + current->next = next; + else + /* The new block is the first one. */ + state->current_atomic_block = next; + current = next; + } + + current->count = 0; + } + + return ¤t->items[current->count++]; +} + +/* Pops the top entry from the atomic stack. */ +Py_LOCAL_INLINE(RE_AtomicData*) pop_atomic(RE_SafeState* safe_state) { + RE_State* state; + RE_AtomicBlock* current; + RE_AtomicData* atomic; + + state = safe_state->re_state; + + current = state->current_atomic_block; + atomic = ¤t->items[--current->count]; + if (current->count == 0 && current->previous) + state->current_atomic_block = current->previous; + + return atomic; +} + +/* Gets the top entry from the atomic stack. */ +Py_LOCAL_INLINE(RE_AtomicData*) top_atomic(RE_SafeState* safe_state) { + RE_State* state; + RE_AtomicBlock* current; + + state = safe_state->re_state; + + current = state->current_atomic_block; + return ¤t->items[current->count - 1]; +} + +/* Copies a repeat guard list. */ +Py_LOCAL_INLINE(BOOL) copy_guard_data(RE_SafeState* safe_state, RE_GuardList* + dst, RE_GuardList* src) { + if (dst->capacity < src->count) { + RE_GuardSpan* new_spans; + + if (!safe_state) + return FALSE; + + dst->capacity = src->count; + new_spans = (RE_GuardSpan*)safe_realloc(safe_state, dst->spans, + dst->capacity * sizeof(RE_GuardSpan)); + if (!new_spans) + return FALSE; + + dst->spans = new_spans; + } + + dst->count = src->count; + memmove(dst->spans, src->spans, dst->count * sizeof(RE_GuardSpan)); + + dst->last_text_pos = -1; + + return TRUE; +} + +/* Copies a repeat. */ +Py_LOCAL_INLINE(BOOL) copy_repeat_data(RE_SafeState* safe_state, RE_RepeatData* + dst, RE_RepeatData* src) { + if (!copy_guard_data(safe_state, &dst->body_guard_list, + &src->body_guard_list) || !copy_guard_data(safe_state, + &dst->tail_guard_list, &src->tail_guard_list)) { + safe_dealloc(safe_state, dst->body_guard_list.spans); + safe_dealloc(safe_state, dst->tail_guard_list.spans); + + return FALSE; + } + + dst->count = src->count; + dst->start = src->start; + dst->capture_change = src->capture_change; + + return TRUE; +} + +/* Pushes a return node onto the group call stack. */ +Py_LOCAL_INLINE(BOOL) push_group_return(RE_SafeState* safe_state, RE_Node* + return_node) { + RE_State* state; + PatternObject* pattern; + RE_GroupCallFrame* frame; + + state = safe_state->re_state; + pattern = state->pattern; + + if (state->current_group_call_frame && + state->current_group_call_frame->next) + /* Advance to the next allocated frame. */ + frame = state->current_group_call_frame->next; + else if (!state->current_group_call_frame && state->first_group_call_frame) + /* Advance to the first allocated frame. */ + frame = state->first_group_call_frame; + else { + /* Create a new frame. */ + frame = (RE_GroupCallFrame*)safe_alloc(safe_state, + sizeof(RE_GroupCallFrame)); + if (!frame) + return FALSE; + + frame->groups = (RE_GroupData*)safe_alloc(safe_state, + pattern->true_group_count * sizeof(RE_GroupData)); + frame->repeats = (RE_RepeatData*)safe_alloc(safe_state, + pattern->repeat_count * sizeof(RE_RepeatData)); + if (!frame->groups || !frame->repeats) { + safe_dealloc(safe_state, frame->groups); + safe_dealloc(safe_state, frame->repeats); + safe_dealloc(safe_state, frame); + + return FALSE; + } + + memset(frame->groups, 0, pattern->true_group_count * + sizeof(RE_GroupData)); + memset(frame->repeats, 0, pattern->repeat_count * + sizeof(RE_RepeatData)); + + frame->previous = state->current_group_call_frame; + frame->next = NULL; + + if (frame->previous) + frame->previous->next = frame; + else + state->first_group_call_frame = frame; + } + + frame->node = return_node; + + /* Push the groups and guards. */ + if (return_node) { + size_t g; + size_t r; + + for (g = 0; g < pattern->true_group_count; g++) { + frame->groups[g].span = state->groups[g].span; + frame->groups[g].current_capture = + state->groups[g].current_capture; + } + + for (r = 0; r < pattern->repeat_count; r++) { + if (!copy_repeat_data(safe_state, &frame->repeats[r], + &state->repeats[r])) + return FALSE; + } + } + + state->current_group_call_frame = frame; + + return TRUE; +} + +/* Pops a return node from the group call stack. */ +Py_LOCAL_INLINE(RE_Node*) pop_group_return(RE_State* state) { + RE_GroupCallFrame* frame; + + frame = state->current_group_call_frame; + + /* Pop the groups and repeats. */ + if (frame->node) { + PatternObject* pattern; + size_t g; + size_t r; + + pattern = state->pattern; + + for (g = 0; g < pattern->true_group_count; g++) { + state->groups[g].span = frame->groups[g].span; + state->groups[g].current_capture = + frame->groups[g].current_capture; + } + + for (r = 0; r < pattern->repeat_count; r++) + copy_repeat_data(NULL, &state->repeats[r], &frame->repeats[r]); + } + + /* Withdraw to previous frame. */ + state->current_group_call_frame = frame->previous; + + return frame->node; +} + +/* Returns the return node from the top of the group call stack. */ +Py_LOCAL_INLINE(RE_Node*) top_group_return(RE_State* state) { + RE_GroupCallFrame* frame; + + frame = state->current_group_call_frame; + + return frame->node; +} + +/* Checks whether a node matches only 1 character. */ +Py_LOCAL_INLINE(BOOL) node_matches_one_character(RE_Node* node) { + switch (node->op) { + case RE_OP_ANY: + case RE_OP_ANY_ALL: + case RE_OP_ANY_ALL_REV: + case RE_OP_ANY_REV: + case RE_OP_ANY_U: + case RE_OP_ANY_U_REV: + case RE_OP_CHARACTER: + case RE_OP_CHARACTER_IGN: + case RE_OP_CHARACTER_IGN_REV: + case RE_OP_CHARACTER_REV: + case RE_OP_PROPERTY: + case RE_OP_PROPERTY_IGN: + case RE_OP_PROPERTY_IGN_REV: + case RE_OP_PROPERTY_REV: + case RE_OP_RANGE: + case RE_OP_RANGE_IGN: + case RE_OP_RANGE_IGN_REV: + case RE_OP_RANGE_REV: + case RE_OP_SET_DIFF: + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_DIFF_IGN_REV: + case RE_OP_SET_DIFF_REV: + case RE_OP_SET_INTER: + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION: + case RE_OP_SET_UNION_IGN: + case RE_OP_SET_UNION_IGN_REV: + case RE_OP_SET_UNION_REV: + return TRUE; + default: + return FALSE; + } +} + +/* Checks whether the node is a firstset. */ +Py_LOCAL_INLINE(BOOL) is_firstset(RE_Node* node) { + if (node->step != 0) + return FALSE; + + return node_matches_one_character(node); +} + +/* Locates the start node for testing ahead. */ +Py_LOCAL_INLINE(RE_Node*) locate_test_start(RE_Node* node) { + for (;;) { + switch (node->op) { + case RE_OP_BOUNDARY: + switch (node->next_1.node->op) { + case RE_OP_STRING: + case RE_OP_STRING_FLD: + case RE_OP_STRING_FLD_REV: + case RE_OP_STRING_IGN: + case RE_OP_STRING_IGN_REV: + case RE_OP_STRING_REV: + return node->next_1.node; + default: + return node; + } + case RE_OP_CALL_REF: + case RE_OP_END_GROUP: + case RE_OP_START_GROUP: + node = node->next_1.node; + break; + case RE_OP_GREEDY_REPEAT: + case RE_OP_LAZY_REPEAT: + if (node->values[1] == 0) + return node; + node = node->next_1.node; + break; + case RE_OP_GREEDY_REPEAT_ONE: + case RE_OP_LAZY_REPEAT_ONE: + if (node->values[1] == 0) + return node; + return node->nonstring.next_2.node; + case RE_OP_LOOKAROUND: + node = node->nonstring.next_2.node; + break; + default: + if (is_firstset(node)) { + switch (node->next_1.node->op) { + case RE_OP_END_OF_STRING: + case RE_OP_START_OF_STRING: + return node->next_1.node; + } + } + + return node; + } + } +} + +/* Checks whether a character matches any of a set of case characters. */ +Py_LOCAL_INLINE(BOOL) any_case(Py_UCS4 ch, int case_count, Py_UCS4* cases) { + int i; + + for (i = 0; i < case_count; i++) { + if (ch == cases[i]) + return TRUE; + } + + return FALSE; +} + +/* Matches many ANYs, up to a limit. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_ANY(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + + text = state->text; + encoding = state->encoding; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && matches_ANY(encoding, node, text_ptr[0]) + == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && matches_ANY(encoding, node, text_ptr[0]) + == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && matches_ANY(encoding, node, text_ptr[0]) + == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many ANYs, up to a limit, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_ANY_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + + text = state->text; + encoding = state->encoding; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && matches_ANY(encoding, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && matches_ANY(encoding, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && matches_ANY(encoding, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many ANY_Us, up to a limit. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_ANY_U(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + + text = state->text; + encoding = state->encoding; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && matches_ANY_U(encoding, node, + text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && matches_ANY_U(encoding, node, + text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && matches_ANY_U(encoding, node, + text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many ANY_Us, up to a limit, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_ANY_U_REV(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + + text = state->text; + encoding = state->encoding; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && matches_ANY_U(encoding, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && matches_ANY_U(encoding, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && matches_ANY_U(encoding, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many CHARACTERs, up to a limit. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_CHARACTER(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + Py_UCS4 ch; + + text = state->text; + match = node->match == match; + ch = node->values[0]; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && (text_ptr[0] == ch) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && (text_ptr[0] == ch) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && (text_ptr[0] == ch) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many CHARACTERs, up to a limit, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_CHARACTER_IGN(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + Py_UCS4 cases[RE_MAX_CASES]; + int case_count; + + text = state->text; + match = node->match == match; + case_count = state->encoding->all_cases(state->locale_info, + node->values[0], cases); + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && any_case(text_ptr[0], case_count, cases) + == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && any_case(text_ptr[0], case_count, cases) + == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && any_case(text_ptr[0], case_count, cases) + == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many CHARACTERs, up to a limit, backwards, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_CHARACTER_IGN_REV(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + Py_UCS4 cases[RE_MAX_CASES]; + int case_count; + + text = state->text; + match = node->match == match; + case_count = state->encoding->all_cases(state->locale_info, + node->values[0], cases); + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && any_case(text_ptr[-1], case_count, + cases) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && any_case(text_ptr[-1], case_count, + cases) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && any_case(text_ptr[-1], case_count, + cases) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many CHARACTERs, up to a limit, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_CHARACTER_REV(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + Py_UCS4 ch; + + text = state->text; + match = node->match == match; + ch = node->values[0]; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && (text_ptr[-1] == ch) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && (text_ptr[-1] == ch) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && (text_ptr[-1] == ch) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many PROPERTYs, up to a limit. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_PROPERTY(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && matches_PROPERTY(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && matches_PROPERTY(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && matches_PROPERTY(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many PROPERTYs, up to a limit, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_PROPERTY_IGN(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && matches_PROPERTY_IGN(encoding, + locale_info, node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && matches_PROPERTY_IGN(encoding, + locale_info, node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && matches_PROPERTY_IGN(encoding, + locale_info, node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many PROPERTYs, up to a limit, backwards, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_PROPERTY_IGN_REV(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && matches_PROPERTY_IGN(encoding, + locale_info, node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && matches_PROPERTY_IGN(encoding, + locale_info, node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && matches_PROPERTY_IGN(encoding, + locale_info, node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many PROPERTYs, up to a limit, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_PROPERTY_REV(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && matches_PROPERTY(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && matches_PROPERTY(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && matches_PROPERTY(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many RANGEs, up to a limit. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_RANGE(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && matches_RANGE(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && matches_RANGE(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && matches_RANGE(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many RANGEs, up to a limit, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_RANGE_IGN(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && matches_RANGE_IGN(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && matches_RANGE_IGN(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && matches_RANGE_IGN(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many RANGEs, up to a limit, backwards, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_RANGE_IGN_REV(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && matches_RANGE_IGN(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && matches_RANGE_IGN(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && matches_RANGE_IGN(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many RANGEs, up to a limit, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_RANGE_REV(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && matches_RANGE(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && matches_RANGE(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && matches_RANGE(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many SETs, up to a limit. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_SET(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && matches_SET(encoding, locale_info, node, + text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && matches_SET(encoding, locale_info, node, + text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && matches_SET(encoding, locale_info, node, + text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many SETs, up to a limit, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_SET_IGN(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr < limit_ptr && matches_SET_IGN(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr < limit_ptr && matches_SET_IGN(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr < limit_ptr && matches_SET_IGN(encoding, locale_info, + node, text_ptr[0]) == match) + ++text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many SETs, up to a limit, backwards, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_SET_IGN_REV(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && matches_SET_IGN(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && matches_SET_IGN(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && matches_SET_IGN(encoding, locale_info, + node, text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Matches many SETs, up to a limit, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) match_many_SET_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit, BOOL match) { + void* text; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + + text = state->text; + match = node->match == match; + encoding = state->encoding; + locale_info = state->locale_info; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr > limit_ptr && matches_SET(encoding, locale_info, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS1*)text; + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr > limit_ptr && matches_SET(encoding, locale_info, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS2*)text; + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr > limit_ptr && matches_SET(encoding, locale_info, node, + text_ptr[-1]) == match) + --text_ptr; + + text_pos = text_ptr - (Py_UCS4*)text; + break; + } + } + + return text_pos; +} + +/* Counts a repeated character pattern. */ +Py_LOCAL_INLINE(size_t) count_one(RE_State* state, RE_Node* node, Py_ssize_t + text_pos, size_t max_count, BOOL* is_partial) { + size_t count; + + *is_partial = FALSE; + + if (max_count < 1) + return 0; + + switch (node->op) { + case RE_OP_ANY: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_ANY(state, node, text_pos, text_pos + + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_ANY_ALL: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_ANY_ALL_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_ANY_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_ANY_REV(state, node, text_pos, + text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_ANY_U: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_ANY_U(state, node, text_pos, text_pos + + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_ANY_U_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_ANY_U_REV(state, node, text_pos, + text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_CHARACTER: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_CHARACTER(state, node, text_pos, text_pos + + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_CHARACTER_IGN: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_CHARACTER_IGN(state, node, text_pos, + text_pos + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_CHARACTER_IGN_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_CHARACTER_IGN_REV(state, node, + text_pos, text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_CHARACTER_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_CHARACTER_REV(state, node, + text_pos, text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_PROPERTY: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_PROPERTY(state, node, text_pos, text_pos + + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_PROPERTY_IGN: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_PROPERTY_IGN(state, node, text_pos, + text_pos + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_PROPERTY_IGN_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_PROPERTY_IGN_REV(state, node, + text_pos, text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_PROPERTY_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_PROPERTY_REV(state, node, + text_pos, text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_RANGE: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_RANGE(state, node, text_pos, text_pos + + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_RANGE_IGN: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_RANGE_IGN(state, node, text_pos, text_pos + + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_RANGE_IGN_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_RANGE_IGN_REV(state, node, + text_pos, text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_RANGE_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_RANGE_REV(state, node, text_pos, + text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_SET_DIFF: + case RE_OP_SET_INTER: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_UNION: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_SET(state, node, text_pos, text_pos + + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_UNION_IGN: + count = min_size_t((size_t)(state->slice_end - text_pos), max_count); + + count = (size_t)(match_many_SET_IGN(state, node, text_pos, text_pos + + (Py_ssize_t)count, TRUE) - text_pos); + + *is_partial = count == (size_t)(state->text_length - text_pos) && count + < max_count && state->partial_side == RE_PARTIAL_RIGHT; + + return count; + case RE_OP_SET_DIFF_IGN_REV: + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_UNION_IGN_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_SET_IGN_REV(state, node, + text_pos, text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + case RE_OP_SET_DIFF_REV: + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION_REV: + count = min_size_t((size_t)(text_pos - state->slice_start), max_count); + + count = (size_t)(text_pos - match_many_SET_REV(state, node, text_pos, + text_pos - (Py_ssize_t)count, TRUE)); + + *is_partial = count == (size_t)(text_pos) && count < max_count && + state->partial_side == RE_PARTIAL_LEFT; + + return count; + } + + return 0; +} + +/* Performs a simple string search. */ +Py_LOCAL_INLINE(Py_ssize_t) simple_string_search(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL* is_partial) { + Py_ssize_t length; + RE_CODE* values; + Py_UCS4 check_char; + + length = (Py_ssize_t)node->value_count; + values = node->values; + check_char = values[0]; + + *is_partial = FALSE; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text = (Py_UCS1*)state->text; + Py_UCS1* text_ptr = text + text_pos; + Py_UCS1* limit_ptr = text + limit; + + while (text_ptr < limit_ptr) { + if (text_ptr[0] == check_char) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr + s_pos >= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_RIGHT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char(text_ptr[s_pos], values[s_pos])) + break; + + ++s_pos; + } + } + + ++text_ptr; + } + text_pos = text_ptr - text; + break; + } + case 2: + { + Py_UCS2* text = (Py_UCS2*)state->text; + Py_UCS2* text_ptr = text + text_pos; + Py_UCS2* limit_ptr = text + limit; + + while (text_ptr < limit_ptr) { + if (text_ptr[0] == check_char) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr + s_pos >= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_RIGHT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char(text_ptr[s_pos], values[s_pos])) + break; + + ++s_pos; + } + } + + ++text_ptr; + } + text_pos = text_ptr - text; + break; + } + case 4: + { + Py_UCS4* text = (Py_UCS4*)state->text; + Py_UCS4* text_ptr = text + text_pos; + Py_UCS4* limit_ptr = text + limit; + + while (text_ptr < limit_ptr) { + if (text_ptr[0] == check_char) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr + s_pos >= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_RIGHT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char(text_ptr[s_pos], values[s_pos])) + break; + + ++s_pos; + } + } + + ++text_ptr; + } + text_pos = text_ptr - text; + break; + } + } + + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_RIGHT) { + /* Partial match. */ + *is_partial = TRUE; + return text_pos; + } + + return -1; +} + +/* Performs a simple string search, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) simple_string_search_ign(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL* is_partial) { + Py_ssize_t length; + RE_CODE* values; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + Py_UCS4 cases[RE_MAX_CASES]; + int case_count; + + length = (Py_ssize_t)node->value_count; + values = node->values; + encoding = state->encoding; + locale_info = state->locale_info; + case_count = encoding->all_cases(locale_info, values[0], cases); + + *is_partial = FALSE; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text = (Py_UCS1*)state->text; + Py_UCS1* text_ptr = text + text_pos; + Py_UCS1* limit_ptr = text + limit; + + while (text_ptr < limit_ptr) { + if (any_case(text_ptr[0], case_count, cases)) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr + s_pos >= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_RIGHT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char_ign(encoding, locale_info, text_ptr[s_pos], + values[s_pos])) + break; + + ++s_pos; + } + } + + ++text_ptr; + } + text_pos = text_ptr - text; + break; + } + case 2: + { + Py_UCS2* text = (Py_UCS2*)state->text; + Py_UCS2* text_ptr = text + text_pos; + Py_UCS2* limit_ptr = text + limit; + + while (text_ptr < limit_ptr) { + if (any_case(text_ptr[0], case_count, cases)) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr + s_pos >= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_RIGHT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char_ign(encoding, locale_info, text_ptr[s_pos], + values[s_pos])) + break; + + ++s_pos; + } + } + + ++text_ptr; + } + text_pos = text_ptr - text; + break; + } + case 4: + { + Py_UCS4* text = (Py_UCS4*)state->text; + Py_UCS4* text_ptr = text + text_pos; + Py_UCS4* limit_ptr = text + limit; + + while (text_ptr < limit_ptr) { + if (any_case(text_ptr[0], case_count, cases)) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr + s_pos >= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_RIGHT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char_ign(encoding, locale_info, text_ptr[s_pos], + values[s_pos])) + break; + + ++s_pos; + } + } + + ++text_ptr; + } + text_pos = text_ptr - text; + break; + } + } + + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_RIGHT) { + /* Partial match. */ + *is_partial = TRUE; + return text_pos; + } + + return -1; +} + +/* Performs a simple string search, backwards, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) simple_string_search_ign_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL* is_partial) { + Py_ssize_t length; + RE_CODE* values; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + Py_UCS4 cases[RE_MAX_CASES]; + int case_count; + + length = (Py_ssize_t)node->value_count; + values = node->values; + encoding = state->encoding; + locale_info = state->locale_info; + case_count = encoding->all_cases(locale_info, values[length - 1], cases); + + *is_partial = FALSE; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text = (Py_UCS1*)state->text; + Py_UCS1* text_ptr = text + text_pos; + Py_UCS1* limit_ptr = text + limit; + + while (text_ptr > limit_ptr) { + if (any_case(text_ptr[-1], case_count, cases)) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr - s_pos <= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_LEFT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char_ign(encoding, locale_info, text_ptr[- s_pos + - 1], values[length - s_pos - 1])) + break; + + ++s_pos; + } + } + + --text_ptr; + } + text_pos = text_ptr - text; + break; + } + case 2: + { + Py_UCS2* text = (Py_UCS2*)state->text; + Py_UCS2* text_ptr = text + text_pos; + Py_UCS2* limit_ptr = text + limit; + + while (text_ptr > limit_ptr) { + if (any_case(text_ptr[-1], case_count, cases)) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr - s_pos <= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_LEFT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char_ign(encoding, locale_info, text_ptr[- s_pos + - 1], values[length - s_pos - 1])) + break; + + ++s_pos; + } + } + + --text_ptr; + } + text_pos = text_ptr - text; + break; + } + case 4: + { + Py_UCS4* text = (Py_UCS4*)state->text; + Py_UCS4* text_ptr = text + text_pos; + Py_UCS4* limit_ptr = text + limit; + + while (text_ptr > limit_ptr) { + if (any_case(text_ptr[-1], case_count, cases)) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr - s_pos <= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_LEFT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char_ign(encoding, locale_info, text_ptr[- s_pos + - 1], values[length - s_pos - 1])) + break; + + ++s_pos; + } + } + + --text_ptr; + } + text_pos = text_ptr - text; + break; + } + } + + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_LEFT) { + /* Partial match. */ + *is_partial = TRUE; + return text_pos; + } + + return -1; +} + +/* Performs a simple string search, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) simple_string_search_rev(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL* is_partial) { + Py_ssize_t length; + RE_CODE* values; + Py_UCS4 check_char; + + length = (Py_ssize_t)node->value_count; + values = node->values; + check_char = values[length - 1]; + + *is_partial = FALSE; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text = (Py_UCS1*)state->text; + Py_UCS1* text_ptr = text + text_pos; + Py_UCS1* limit_ptr = text + limit; + + while (text_ptr > limit_ptr) { + if (text_ptr[-1] == check_char) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr - s_pos <= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_LEFT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char(text_ptr[- s_pos - 1], values[length - s_pos + - 1])) + break; + + ++s_pos; + } + } + + --text_ptr; + } + text_pos = text_ptr - text; + break; + } + case 2: + { + Py_UCS2* text = (Py_UCS2*)state->text; + Py_UCS2* text_ptr = text + text_pos; + Py_UCS2* limit_ptr = text + limit; + + while (text_ptr > limit_ptr) { + if (text_ptr[-1] == check_char) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr - s_pos <= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_LEFT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char(text_ptr[- s_pos - 1], values[length - s_pos + - 1])) + break; + + ++s_pos; + } + } + + --text_ptr; + } + text_pos = text_ptr - text; + break; + } + case 4: + { + Py_UCS4* text = (Py_UCS4*)state->text; + Py_UCS4* text_ptr = text + text_pos; + Py_UCS4* limit_ptr = text + limit; + + while (text_ptr > limit_ptr) { + if (text_ptr[-1] == check_char) { + Py_ssize_t s_pos; + + s_pos = 1; + + for (;;) { + if (s_pos >= length) + /* End of search string. */ + return text_ptr - text; + + if (text_ptr - s_pos <= limit_ptr) { + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_LEFT) { + /* Partial match. */ + *is_partial = TRUE; + return text_ptr - text; + } + + return -1; + + } + + if (!same_char(text_ptr[- s_pos - 1], values[length - s_pos + - 1])) + break; + + ++s_pos; + } + } + + --text_ptr; + } + text_pos = text_ptr - text; + break; + } + } + + /* Off the end of the text. */ + if (state->partial_side == RE_PARTIAL_LEFT) { + /* Partial match. */ + *is_partial = TRUE; + return text_pos; + } + + return -1; +} + +/* Performs a Boyer-Moore fast string search. */ +Py_LOCAL_INLINE(Py_ssize_t) fast_string_search(RE_State* state, RE_Node* node, + Py_ssize_t text_pos, Py_ssize_t limit) { + void* text; + Py_ssize_t length; + RE_CODE* values; + Py_ssize_t* bad_character_offset; + Py_ssize_t* good_suffix_offset; + Py_ssize_t last_pos; + Py_UCS4 check_char; + + text = state->text; + length = (Py_ssize_t)node->value_count; + values = node->values; + good_suffix_offset = node->string.good_suffix_offset; + bad_character_offset = node->string.bad_character_offset; + last_pos = length - 1; + check_char = values[last_pos]; + limit -= length; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr <= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[last_pos]; + if (ch == check_char) { + Py_ssize_t pos; + + pos = last_pos - 1; + while (pos >= 0 && same_char(text_ptr[pos], values[pos])) + --pos; + + if (pos < 0) + return text_ptr - (Py_UCS1*)text; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr <= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[last_pos]; + if (ch == check_char) { + Py_ssize_t pos; + + pos = last_pos - 1; + while (pos >= 0 && same_char(text_ptr[pos], values[pos])) + --pos; + + if (pos < 0) + return text_ptr - (Py_UCS2*)text; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr <= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[last_pos]; + if (ch == check_char) { + Py_ssize_t pos; + + pos = last_pos - 1; + while (pos >= 0 && same_char(text_ptr[pos], values[pos])) + --pos; + + if (pos < 0) + return text_ptr - (Py_UCS4*)text; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + } + + return -1; +} + +/* Performs a Boyer-Moore fast string search, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) fast_string_search_ign(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit) { + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + void* text; + Py_ssize_t length; + RE_CODE* values; + Py_ssize_t* bad_character_offset; + Py_ssize_t* good_suffix_offset; + Py_ssize_t last_pos; + Py_UCS4 cases[RE_MAX_CASES]; + int case_count; + + encoding = state->encoding; + locale_info = state->locale_info; + text = state->text; + length = (Py_ssize_t)node->value_count; + values = node->values; + good_suffix_offset = node->string.good_suffix_offset; + bad_character_offset = node->string.bad_character_offset; + last_pos = length - 1; + case_count = encoding->all_cases(locale_info, values[last_pos], cases); + limit -= length; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr <= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[last_pos]; + if (any_case(ch, case_count, cases)) { + Py_ssize_t pos; + + pos = last_pos - 1; + while (pos >= 0 && same_char_ign(encoding, locale_info, + text_ptr[pos], values[pos])) + --pos; + + if (pos < 0) + return text_ptr - (Py_UCS1*)text; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr <= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[last_pos]; + if (any_case(ch, case_count, cases)) { + Py_ssize_t pos; + + pos = last_pos - 1; + while (pos >= 0 && same_char_ign(encoding, locale_info, + text_ptr[pos], values[pos])) + --pos; + + if (pos < 0) + return text_ptr - (Py_UCS2*)text; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr <= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[last_pos]; + if (any_case(ch, case_count, cases)) { + Py_ssize_t pos; + + pos = last_pos - 1; + while (pos >= 0 && same_char_ign(encoding, locale_info, + text_ptr[pos], values[pos])) + --pos; + + if (pos < 0) + return text_ptr - (Py_UCS4*)text; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + } + + return -1; +} + +/* Performs a Boyer-Moore fast string search, backwards, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) fast_string_search_ign_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit) { + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + void* text; + Py_ssize_t length; + RE_CODE* values; + Py_ssize_t* bad_character_offset; + Py_ssize_t* good_suffix_offset; + Py_UCS4 cases[RE_MAX_CASES]; + int case_count; + + encoding = state->encoding; + locale_info = state->locale_info; + text = state->text; + length = (Py_ssize_t)node->value_count; + values = node->values; + good_suffix_offset = node->string.good_suffix_offset; + bad_character_offset = node->string.bad_character_offset; + case_count = encoding->all_cases(locale_info, values[0], cases); + text_pos -= length; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr >= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[0]; + if (any_case(ch, case_count, cases)) { + Py_ssize_t pos; + + pos = 1; + while (pos < length && same_char_ign(encoding, locale_info, + text_ptr[pos], values[pos])) + ++pos; + + if (pos >= length) + return text_ptr - (Py_UCS1*)text + length; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr >= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[0]; + if (any_case(ch, case_count, cases)) { + Py_ssize_t pos; + + pos = 1; + while (pos < length && same_char_ign(encoding, locale_info, + text_ptr[pos], values[pos])) + ++pos; + + if (pos >= length) + return text_ptr - (Py_UCS2*)text + length; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr >= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[0]; + if (any_case(ch, case_count, cases)) { + Py_ssize_t pos; + + pos = 1; + while (pos < length && same_char_ign(encoding, locale_info, + text_ptr[pos], values[pos])) + ++pos; + + if (pos >= length) + return text_ptr - (Py_UCS4*)text + length; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + } + + return -1; +} + +/* Performs a Boyer-Moore fast string search, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) fast_string_search_rev(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit) { + void* text; + Py_ssize_t length; + RE_CODE* values; + Py_ssize_t* bad_character_offset; + Py_ssize_t* good_suffix_offset; + Py_UCS4 check_char; + + text = state->text; + length = (Py_ssize_t)node->value_count; + values = node->values; + good_suffix_offset = node->string.good_suffix_offset; + bad_character_offset = node->string.bad_character_offset; + check_char = values[0]; + text_pos -= length; + + switch (state->charsize) { + case 1: + { + Py_UCS1* text_ptr; + Py_UCS1* limit_ptr; + + text_ptr = (Py_UCS1*)text + text_pos; + limit_ptr = (Py_UCS1*)text + limit; + + while (text_ptr >= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[0]; + if (ch == check_char) { + Py_ssize_t pos; + + pos = 1; + while (pos < length && same_char(text_ptr[pos], values[pos])) + ++pos; + + if (pos >= length) + return text_ptr - (Py_UCS1*)text + length; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + case 2: + { + Py_UCS2* text_ptr; + Py_UCS2* limit_ptr; + + text_ptr = (Py_UCS2*)text + text_pos; + limit_ptr = (Py_UCS2*)text + limit; + + while (text_ptr >= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[0]; + if (ch == check_char) { + Py_ssize_t pos; + + pos = 1; + while (pos < length && same_char(text_ptr[pos], values[pos])) + ++pos; + + if (pos >= length) + return text_ptr - (Py_UCS2*)text + length; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + case 4: + { + Py_UCS4* text_ptr; + Py_UCS4* limit_ptr; + + text_ptr = (Py_UCS4*)text + text_pos; + limit_ptr = (Py_UCS4*)text + limit; + + while (text_ptr >= limit_ptr) { + Py_UCS4 ch; + + ch = text_ptr[0]; + if (ch == check_char) { + Py_ssize_t pos; + + pos = 1; + while (pos < length && same_char(text_ptr[pos], values[pos])) + ++pos; + + if (pos >= length) + return text_ptr - (Py_UCS4*)text + length; + + text_ptr += good_suffix_offset[pos]; + } else + text_ptr += bad_character_offset[ch & 0xFF]; + } + break; + } + } + + return -1; +} + +/* Builds the tables for a Boyer-Moore fast string search. */ +Py_LOCAL_INLINE(BOOL) build_fast_tables(RE_State* state, RE_Node* node, BOOL + ignore) { + Py_ssize_t length; + RE_CODE* values; + Py_ssize_t* bad; + Py_ssize_t* good; + Py_UCS4 ch; + Py_ssize_t last_pos; + Py_ssize_t pos; + BOOL (*is_same_char)(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, Py_UCS4 ch1, Py_UCS4 ch2); + Py_ssize_t suffix_len; + BOOL saved_start; + Py_ssize_t s; + Py_ssize_t i; + Py_ssize_t s_start; + Py_UCS4 codepoints[RE_MAX_CASES]; + + length = (Py_ssize_t)node->value_count; + + if (length < RE_MIN_FAST_LENGTH) + return TRUE; + + values = node->values; + bad = (Py_ssize_t*)re_alloc(256 * sizeof(bad[0])); + good = (Py_ssize_t*)re_alloc((size_t)length * sizeof(good[0])); + + if (!bad || !good) { + re_dealloc(bad); + re_dealloc(good); + + return FALSE; + } + + for (ch = 0; ch < 0x100; ch++) + bad[ch] = length; + + last_pos = length - 1; + + for (pos = 0; pos < last_pos; pos++) { + Py_ssize_t offset; + + offset = last_pos - pos; + ch = values[pos]; + if (ignore) { + int count; + int i; + + count = state->encoding->all_cases(state->locale_info, ch, + codepoints); + + for (i = 0; i < count; i++) + bad[codepoints[i] & 0xFF] = offset; + } else + bad[ch & 0xFF] = offset; + } + + is_same_char = ignore ? same_char_ign_wrapper : same_char_wrapper; + + suffix_len = 2; + pos = length - suffix_len; + saved_start = FALSE; + s = pos - 1; + i = suffix_len - 1; + s_start = s; + + while (pos >= 0) { + /* Look for another occurrence of the suffix. */ + while (i > 0) { + /* Have we dropped off the end of the string? */ + if (s + i < 0) + break; + + if (is_same_char(state->encoding, state->locale_info, values[s + + i], values[pos + i])) + /* It still matches. */ + --i; + else { + /* Start again further along. */ + --s; + i = suffix_len - 1; + } + } + + if (s >= 0 && is_same_char(state->encoding, state->locale_info, + values[s], values[pos])) { + /* We haven't dropped off the end of the string, and the suffix has + * matched this far, so this is a good starting point for the next + * iteration. + */ + --s; + if (!saved_start) { + s_start = s; + saved_start = TRUE; + } + } else { + /* Calculate the suffix offset. */ + good[pos] = pos - s; + + /* Extend the suffix and start searching for _this_ one. */ + --pos; + ++suffix_len; + + /* Where's a good place to start searching? */ + if (saved_start) { + s = s_start; + saved_start = FALSE; + } else + --s; + + /* Can we short-circuit the searching? */ + if (s < 0) + break; + } + + i = suffix_len - 1; + } + + /* Fill-in any remaining entries. */ + while (pos >= 0) { + good[pos] = pos - s; + --pos; + --s; + } + + node->string.bad_character_offset = bad; + node->string.good_suffix_offset = good; + + return TRUE; +} + +/* Builds the tables for a Boyer-Moore fast string search, backwards. */ +Py_LOCAL_INLINE(BOOL) build_fast_tables_rev(RE_State* state, RE_Node* node, + BOOL ignore) { + Py_ssize_t length; + RE_CODE* values; + Py_ssize_t* bad; + Py_ssize_t* good; + Py_UCS4 ch; + Py_ssize_t last_pos; + Py_ssize_t pos; + BOOL (*is_same_char)(RE_EncodingTable* encoding, RE_LocaleInfo* + locale_info, Py_UCS4 ch1, Py_UCS4 ch2); + Py_ssize_t suffix_len; + BOOL saved_start; + Py_ssize_t s; + Py_ssize_t i; + Py_ssize_t s_start; + Py_UCS4 codepoints[RE_MAX_CASES]; + + length = (Py_ssize_t)node->value_count; + + if (length < RE_MIN_FAST_LENGTH) + return TRUE; + + values = node->values; + bad = (Py_ssize_t*)re_alloc(256 * sizeof(bad[0])); + good = (Py_ssize_t*)re_alloc((size_t)length * sizeof(good[0])); + + if (!bad || !good) { + re_dealloc(bad); + re_dealloc(good); + + return FALSE; + } + + for (ch = 0; ch < 0x100; ch++) + bad[ch] = -length; + + last_pos = length - 1; + + for (pos = last_pos; pos > 0; pos--) { + Py_ssize_t offset; + + offset = -pos; + ch = values[pos]; + if (ignore) { + int count; + int i; + + count = state->encoding->all_cases(state->locale_info, ch, + codepoints); + + for (i = 0; i < count; i++) + bad[codepoints[i] & 0xFF] = offset; + } else + bad[ch & 0xFF] = offset; + } + + is_same_char = ignore ? same_char_ign_wrapper : same_char_wrapper; + + suffix_len = 2; + pos = suffix_len - 1; + saved_start = FALSE; + s = pos + 1; + i = suffix_len - 1; + s_start = s; + + while (pos < length) { + /* Look for another occurrence of the suffix. */ + while (i > 0) { + /* Have we dropped off the end of the string? */ + if (s - i >= length) + break; + + if (is_same_char(state->encoding, state->locale_info, values[s - + i], values[pos - i])) + /* It still matches. */ + --i; + else { + /* Start again further along. */ + ++s; + i = suffix_len - 1; + } + } + + if (s < length && is_same_char(state->encoding, state->locale_info, + values[s], values[pos])) { + /* We haven't dropped off the end of the string, and the suffix has + * matched this far, so this is a good starting point for the next + * iteration. + */ + ++s; + if (!saved_start) { + s_start = s; + saved_start = TRUE; + } + } else { + /* Calculate the suffix offset. */ + good[pos] = pos - s; + + /* Extend the suffix and start searching for _this_ one. */ + ++pos; + ++suffix_len; + + /* Where's a good place to start searching? */ + if (saved_start) { + s = s_start; + saved_start = FALSE; + } else + ++s; + + /* Can we short-circuit the searching? */ + if (s >= length) + break; + } + + i = suffix_len - 1; + } + + /* Fill-in any remaining entries. */ + while (pos < length) { + good[pos] = pos - s; + ++pos; + ++s; + } + + node->string.bad_character_offset = bad; + node->string.good_suffix_offset = good; + + return TRUE; +} + +/* Performs a string search. */ +Py_LOCAL_INLINE(Py_ssize_t) string_search(RE_SafeState* safe_state, RE_Node* + node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL* is_partial) { + RE_State* state; + Py_ssize_t found_pos; + + state = safe_state->re_state; + + *is_partial = FALSE; + + /* Has the node been initialised for fast searching, if necessary? */ + if (!(node->status & RE_STATUS_FAST_INIT)) { + /* Ideally the pattern should immutable and shareable across threads. + * Internally, however, it isn't. For safety we need to hold the GIL. + */ + acquire_GIL(safe_state); + + /* Double-check because of multithreading. */ + if (!(node->status & RE_STATUS_FAST_INIT)) { + build_fast_tables(state, node, FALSE); + node->status |= RE_STATUS_FAST_INIT; + } + + release_GIL(safe_state); + } + + if (node->string.bad_character_offset) { + /* Start with a fast search. This will find the string if it's complete + * (i.e. not truncated). + */ + found_pos = fast_string_search(state, node, text_pos, limit); + if (found_pos < 0 && state->partial_side == RE_PARTIAL_RIGHT) + /* We didn't find the string, but it could've been truncated, so + * try again, starting close to the end. + */ + found_pos = simple_string_search(state, node, limit - + (Py_ssize_t)(node->value_count - 1), limit, is_partial); + } else + found_pos = simple_string_search(state, node, text_pos, limit, + is_partial); + + return found_pos; +} + +/* Performs a string search, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) string_search_fld(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, Py_ssize_t* new_pos, + BOOL* is_partial) { + RE_State* state; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + folded); + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + void* text; + RE_CODE* values; + Py_ssize_t start_pos; + int f_pos; + int folded_len; + Py_ssize_t length; + Py_ssize_t s_pos; + Py_UCS4 folded[RE_MAX_FOLDED]; + + state = safe_state->re_state; + encoding = state->encoding; + locale_info = state->locale_info; + full_case_fold = encoding->full_case_fold; + char_at = state->char_at; + text = state->text; + + values = node->values; + start_pos = text_pos; + f_pos = 0; + folded_len = 0; + length = (Py_ssize_t)node->value_count; + s_pos = 0; + + *is_partial = FALSE; + + while (s_pos < length || f_pos < folded_len) { + if (f_pos >= folded_len) { + /* Fetch and casefold another character. */ + if (text_pos >= limit) { + if (text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) { + *is_partial = TRUE; + return start_pos; + } + + return -1; + } + + folded_len = full_case_fold(locale_info, char_at(text, text_pos), + folded); + f_pos = 0; + } + + if (s_pos < length && same_char_ign(encoding, locale_info, + values[s_pos], folded[f_pos])) { + ++s_pos; + ++f_pos; + + if (f_pos >= folded_len) + ++text_pos; + } else { + ++start_pos; + text_pos = start_pos; + f_pos = 0; + folded_len = 0; + s_pos = 0; + } + } + + /* We found the string. */ + if (new_pos) + *new_pos = text_pos; + + return start_pos; +} + +/* Performs a string search, backwards, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) string_search_fld_rev(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, Py_ssize_t* new_pos, + BOOL* is_partial) { + RE_State* state; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + folded); + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + void* text; + RE_CODE* values; + Py_ssize_t start_pos; + int f_pos; + int folded_len; + Py_ssize_t length; + Py_ssize_t s_pos; + Py_UCS4 folded[RE_MAX_FOLDED]; + + state = safe_state->re_state; + encoding = state->encoding; + locale_info = state->locale_info; + full_case_fold = encoding->full_case_fold; + char_at = state->char_at; + text = state->text; + + values = node->values; + start_pos = text_pos; + f_pos = 0; + folded_len = 0; + length = (Py_ssize_t)node->value_count; + s_pos = 0; + + *is_partial = FALSE; + + while (s_pos < length || f_pos < folded_len) { + if (f_pos >= folded_len) { + /* Fetch and casefold another character. */ + if (text_pos <= limit) { + if (text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) { + *is_partial = TRUE; + return start_pos; + } + + return -1; + } + + folded_len = full_case_fold(locale_info, char_at(text, text_pos - + 1), folded); + f_pos = 0; + } + + if (s_pos < length && same_char_ign(encoding, locale_info, + values[length - s_pos - 1], folded[folded_len - f_pos - 1])) { + ++s_pos; + ++f_pos; + + if (f_pos >= folded_len) + --text_pos; + } else { + --start_pos; + text_pos = start_pos; + f_pos = 0; + folded_len = 0; + s_pos = 0; + } + } + + /* We found the string. */ + if (new_pos) + *new_pos = text_pos; + + return start_pos; +} + +/* Performs a string search, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) string_search_ign(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL* is_partial) { + RE_State* state; + Py_ssize_t found_pos; + + state = safe_state->re_state; + + *is_partial = FALSE; + + /* Has the node been initialised for fast searching, if necessary? */ + if (!(node->status & RE_STATUS_FAST_INIT)) { + /* Ideally the pattern should immutable and shareable across threads. + * Internally, however, it isn't. For safety we need to hold the GIL. + */ + acquire_GIL(safe_state); + + /* Double-check because of multithreading. */ + if (!(node->status & RE_STATUS_FAST_INIT)) { + build_fast_tables(state, node, TRUE); + node->status |= RE_STATUS_FAST_INIT; + } + + release_GIL(safe_state); + } + + if (node->string.bad_character_offset) { + /* Start with a fast search. This will find the string if it's complete + * (i.e. not truncated). + */ + found_pos = fast_string_search_ign(state, node, text_pos, limit); + if (found_pos < 0 && state->partial_side == RE_PARTIAL_RIGHT) + /* We didn't find the string, but it could've been truncated, so + * try again, starting close to the end. + */ + found_pos = simple_string_search_ign(state, node, limit - + (Py_ssize_t)(node->value_count - 1), limit, is_partial); + } else + found_pos = simple_string_search_ign(state, node, text_pos, limit, + is_partial); + + return found_pos; +} + +/* Performs a string search, backwards, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) string_search_ign_rev(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL* is_partial) { + RE_State* state; + Py_ssize_t found_pos; + + state = safe_state->re_state; + + *is_partial = FALSE; + + /* Has the node been initialised for fast searching, if necessary? */ + if (!(node->status & RE_STATUS_FAST_INIT)) { + /* Ideally the pattern should immutable and shareable across threads. + * Internally, however, it isn't. For safety we need to hold the GIL. + */ + acquire_GIL(safe_state); + + /* Double-check because of multithreading. */ + if (!(node->status & RE_STATUS_FAST_INIT)) { + build_fast_tables_rev(state, node, TRUE); + node->status |= RE_STATUS_FAST_INIT; + } + + release_GIL(safe_state); + } + + if (node->string.bad_character_offset) { + /* Start with a fast search. This will find the string if it's complete + * (i.e. not truncated). + */ + found_pos = fast_string_search_ign_rev(state, node, text_pos, limit); + if (found_pos < 0 && state->partial_side == RE_PARTIAL_LEFT) + /* We didn't find the string, but it could've been truncated, so + * try again, starting close to the end. + */ + found_pos = simple_string_search_ign_rev(state, node, limit + + (Py_ssize_t)(node->value_count - 1), limit, is_partial); + } else + found_pos = simple_string_search_ign_rev(state, node, text_pos, limit, + is_partial); + + return found_pos; +} + +/* Performs a string search, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) string_search_rev(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL* is_partial) { + RE_State* state; + Py_ssize_t found_pos; + + state = safe_state->re_state; + + *is_partial = FALSE; + + /* Has the node been initialised for fast searching, if necessary? */ + if (!(node->status & RE_STATUS_FAST_INIT)) { + /* Ideally the pattern should immutable and shareable across threads. + * Internally, however, it isn't. For safety we need to hold the GIL. + */ + acquire_GIL(safe_state); + + /* Double-check because of multithreading. */ + if (!(node->status & RE_STATUS_FAST_INIT)) { + build_fast_tables_rev(state, node, FALSE); + node->status |= RE_STATUS_FAST_INIT; + } + + release_GIL(safe_state); + } + + if (node->string.bad_character_offset) { + /* Start with a fast search. This will find the string if it's complete + * (i.e. not truncated). + */ + found_pos = fast_string_search_rev(state, node, text_pos, limit); + if (found_pos < 0 && state->partial_side == RE_PARTIAL_LEFT) + /* We didn't find the string, but it could've been truncated, so + * try again, starting close to the end. + */ + found_pos = simple_string_search_rev(state, node, limit + + (Py_ssize_t)(node->value_count - 1), limit, is_partial); + } else + found_pos = simple_string_search_rev(state, node, text_pos, limit, + is_partial); + + return found_pos; +} + +/* Returns how many characters there could be before full case-folding. */ +Py_LOCAL_INLINE(Py_ssize_t) possible_unfolded_length(Py_ssize_t length) { + if (length == 0) + return 0; + + if (length < RE_MAX_FOLDED) + return 1; + + return length / RE_MAX_FOLDED; +} + +/* Checks whether there's any character except a newline at a position. */ +Py_LOCAL_INLINE(int) try_match_ANY(RE_State* state, RE_Node* node, Py_ssize_t + text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_ANY(state->encoding, node, state->char_at(state->text, + text_pos))); +} + +/* Checks whether there's any character at all at a position. */ +Py_LOCAL_INLINE(int) try_match_ANY_ALL(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end); +} + +/* Checks whether there's any character at all at a position, backwards. */ +Py_LOCAL_INLINE(int) try_match_ANY_ALL_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start); +} + +/* Checks whether there's any character except a newline at a position, + * backwards. + */ +Py_LOCAL_INLINE(int) try_match_ANY_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_ANY(state->encoding, node, state->char_at(state->text, text_pos - + 1))); +} + +/* Checks whether there's any character except a line separator at a position. + */ +Py_LOCAL_INLINE(int) try_match_ANY_U(RE_State* state, RE_Node* node, Py_ssize_t + text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_ANY_U(state->encoding, node, state->char_at(state->text, + text_pos))); +} + +/* Checks whether there's any character except a line separator at a position, + * backwards. + */ +Py_LOCAL_INLINE(int) try_match_ANY_U_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_ANY_U(state->encoding, node, state->char_at(state->text, text_pos + - 1))); +} + +/* Checks whether a position is on a word boundary. */ +Py_LOCAL_INLINE(int) try_match_BOUNDARY(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_boundary(state, text_pos) == + node->match); +} + +/* Checks whether there's a character at a position. */ +Py_LOCAL_INLINE(int) try_match_CHARACTER(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_CHARACTER(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos)) == node->match); +} + +/* Checks whether there's a character at a position, ignoring case. */ +Py_LOCAL_INLINE(int) try_match_CHARACTER_IGN(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_CHARACTER_IGN(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos)) == node->match); +} + +/* Checks whether there's a character at a position, ignoring case, backwards. + */ +Py_LOCAL_INLINE(int) try_match_CHARACTER_IGN_REV(RE_State* state, RE_Node* + node, Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_CHARACTER_IGN(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos - 1)) == node->match); +} + +/* Checks whether there's a character at a position, backwards. */ +Py_LOCAL_INLINE(int) try_match_CHARACTER_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_CHARACTER(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos - 1)) == node->match); +} + +/* Checks whether a position is on a default word boundary. */ +Py_LOCAL_INLINE(int) try_match_DEFAULT_BOUNDARY(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_default_boundary(state, text_pos) + == node->match); +} + +/* Checks whether a position is at the default end of a word. */ +Py_LOCAL_INLINE(int) try_match_DEFAULT_END_OF_WORD(RE_State* state, RE_Node* + node, Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_default_word_end(state, + text_pos)); +} + +/* Checks whether a position is at the default start of a word. */ +Py_LOCAL_INLINE(int) try_match_DEFAULT_START_OF_WORD(RE_State* state, RE_Node* + node, Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_default_word_start(state, + text_pos)); +} + +/* Checks whether a position is at the end of a line. */ +Py_LOCAL_INLINE(int) try_match_END_OF_LINE(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(text_pos >= state->slice_end || + state->char_at(state->text, text_pos) == '\n'); +} + +/* Checks whether a position is at the end of a line. */ +Py_LOCAL_INLINE(int) try_match_END_OF_LINE_U(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_line_end(state, text_pos)); +} + +/* Checks whether a position is at the end of the string. */ +Py_LOCAL_INLINE(int) try_match_END_OF_STRING(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(text_pos >= state->text_length); +} + +/* Checks whether a position is at the end of a line or the string. */ +Py_LOCAL_INLINE(int) try_match_END_OF_STRING_LINE(RE_State* state, RE_Node* + node, Py_ssize_t text_pos) { + return bool_as_status(text_pos >= state->text_length || text_pos == + state->final_newline); +} + +/* Checks whether a position is at the end of the string. */ +Py_LOCAL_INLINE(int) try_match_END_OF_STRING_LINE_U(RE_State* state, RE_Node* + node, Py_ssize_t text_pos) { + return bool_as_status(text_pos >= state->text_length || text_pos == + state->final_line_sep); +} + +/* Checks whether a position is at the end of a word. */ +Py_LOCAL_INLINE(int) try_match_END_OF_WORD(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_word_end(state, text_pos)); +} + +/* Checks whether a position is on a grapheme boundary. */ +Py_LOCAL_INLINE(int) try_match_GRAPHEME_BOUNDARY(RE_State* state, RE_Node* + node, Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_grapheme_boundary(state, + text_pos)); +} + +/* Checks whether there's a character with a certain property at a position. */ +Py_LOCAL_INLINE(int) try_match_PROPERTY(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_PROPERTY(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos)) == node->match); +} + +/* Checks whether there's a character with a certain property at a position, + * ignoring case. + */ +Py_LOCAL_INLINE(int) try_match_PROPERTY_IGN(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_PROPERTY_IGN(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos)) == node->match); +} + +/* Checks whether there's a character with a certain property at a position, + * ignoring case, backwards. + */ +Py_LOCAL_INLINE(int) try_match_PROPERTY_IGN_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_PROPERTY_IGN(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos - 1)) == node->match); +} + +/* Checks whether there's a character with a certain property at a position, + * backwards. + */ +Py_LOCAL_INLINE(int) try_match_PROPERTY_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_PROPERTY(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos - 1)) == node->match); +} + +/* Checks whether there's a character in a certain range at a position. */ +Py_LOCAL_INLINE(int) try_match_RANGE(RE_State* state, RE_Node* node, Py_ssize_t + text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_RANGE(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos)) == node->match); +} + +/* Checks whether there's a character in a certain range at a position, + * ignoring case. + */ +Py_LOCAL_INLINE(int) try_match_RANGE_IGN(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_RANGE_IGN(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos)) == node->match); +} + +/* Checks whether there's a character in a certain range at a position, + * ignoring case, backwards. + */ +Py_LOCAL_INLINE(int) try_match_RANGE_IGN_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_RANGE_IGN(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos - 1)) == node->match); +} + +/* Checks whether there's a character in a certain range at a position, + * backwards. + */ +Py_LOCAL_INLINE(int) try_match_RANGE_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_RANGE(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos - 1)) == node->match); +} + +/* Checks whether a position is at the search anchor. */ +Py_LOCAL_INLINE(int) try_match_SEARCH_ANCHOR(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(text_pos == state->search_anchor); +} + +/* Checks whether there's a character in a certain set at a position. */ +Py_LOCAL_INLINE(int) try_match_SET(RE_State* state, RE_Node* node, Py_ssize_t + text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_SET(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos)) == node->match); +} + +/* Checks whether there's a character in a certain set at a position, ignoring + * case. + */ +Py_LOCAL_INLINE(int) try_match_SET_IGN(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos < state->slice_end && + matches_SET_IGN(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos)) == node->match); +} + +/* Checks whether there's a character in a certain set at a position, ignoring + * case, backwards. + */ +Py_LOCAL_INLINE(int) try_match_SET_IGN_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_SET_IGN(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos - 1)) == node->match); +} + +/* Checks whether there's a character in a certain set at a position, + * backwards. + */ +Py_LOCAL_INLINE(int) try_match_SET_REV(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + if (text_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + return bool_as_status(text_pos > state->slice_start && + matches_SET(state->encoding, state->locale_info, node, + state->char_at(state->text, text_pos - 1)) == node->match); +} + +/* Checks whether a position is at the start of a line. */ +Py_LOCAL_INLINE(int) try_match_START_OF_LINE(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(text_pos <= 0 || state->char_at(state->text, text_pos + - 1) == '\n'); +} + +/* Checks whether a position is at the start of a line. */ +Py_LOCAL_INLINE(int) try_match_START_OF_LINE_U(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_line_start(state, text_pos)); +} + +/* Checks whether a position is at the start of the string. */ +Py_LOCAL_INLINE(int) try_match_START_OF_STRING(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(text_pos <= 0); +} + +/* Checks whether a position is at the start of a word. */ +Py_LOCAL_INLINE(int) try_match_START_OF_WORD(RE_State* state, RE_Node* node, + Py_ssize_t text_pos) { + return bool_as_status(state->encoding->at_word_start(state, text_pos)); +} + +/* Checks whether there's a certain string at a position. */ +Py_LOCAL_INLINE(int) try_match_STRING(RE_State* state, RE_NextNode* next, + RE_Node* node, Py_ssize_t text_pos, RE_Position* next_position) { + Py_ssize_t length; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + RE_CODE* values; + Py_ssize_t s_pos; + + length = (Py_ssize_t)node->value_count; + char_at = state->char_at; + values = node->values; + + for (s_pos = 0; s_pos < length; s_pos++) { + if (text_pos + s_pos >= state->slice_end) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + if (!same_char(char_at(state->text, text_pos + s_pos), values[s_pos])) + return RE_ERROR_FAILURE; + } + + next_position->node = next->match_next; + next_position->text_pos = text_pos + next->match_step; + + return RE_ERROR_SUCCESS; +} + +/* Checks whether there's a certain string at a position, ignoring case. */ +Py_LOCAL_INLINE(int) try_match_STRING_FLD(RE_State* state, RE_NextNode* next, + RE_Node* node, Py_ssize_t text_pos, RE_Position* next_position) { + Py_ssize_t length; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + folded); + Py_ssize_t s_pos; + RE_CODE* values; + int folded_len; + int f_pos; + Py_ssize_t start_pos; + Py_UCS4 folded[RE_MAX_FOLDED]; + + length = (Py_ssize_t)node->value_count; + char_at = state->char_at; + encoding = state->encoding; + locale_info = state->locale_info; + full_case_fold = encoding->full_case_fold; + + s_pos = 0; + values = node->values; + folded_len = 0; + f_pos = 0; + start_pos = text_pos; + + while (s_pos < length) { + if (f_pos >= folded_len) { + /* Fetch and casefold another character. */ + if (text_pos >= state->slice_end) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + folded_len = full_case_fold(locale_info, char_at(state->text, + text_pos), folded); + f_pos = 0; + } + + if (!same_char_ign(encoding, locale_info, folded[f_pos], + values[s_pos])) + return RE_ERROR_FAILURE; + + ++s_pos; + ++f_pos; + + if (f_pos >= folded_len) + ++text_pos; + } + + if (f_pos < folded_len) + return RE_ERROR_FAILURE; + + next_position->node = next->match_next; + if (next->match_step == 0) + next_position->text_pos = start_pos; + else + next_position->text_pos = text_pos; + + return RE_ERROR_SUCCESS; +} + +/* Checks whether there's a certain string at a position, ignoring case, + * backwards. + */ +Py_LOCAL_INLINE(int) try_match_STRING_FLD_REV(RE_State* state, RE_NextNode* + next, RE_Node* node, Py_ssize_t text_pos, RE_Position* next_position) { + Py_ssize_t length; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + folded); + Py_ssize_t s_pos; + RE_CODE* values; + int folded_len; + int f_pos; + Py_ssize_t start_pos; + Py_UCS4 folded[RE_MAX_FOLDED]; + + length = (Py_ssize_t)node->value_count; + char_at = state->char_at; + encoding = state->encoding; + locale_info = state->locale_info; + full_case_fold = encoding->full_case_fold; + + s_pos = 0; + values = node->values; + folded_len = 0; + f_pos = 0; + start_pos = text_pos; + + while (s_pos < length) { + if (f_pos >= folded_len) { + /* Fetch and casefold another character. */ + if (text_pos <= state->slice_start) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + folded_len = full_case_fold(locale_info, char_at(state->text, + text_pos - 1), folded); + f_pos = 0; + } + + if (!same_char_ign(encoding, locale_info, folded[folded_len - f_pos - + 1], values[length - s_pos - 1])) + return RE_ERROR_FAILURE; + + ++s_pos; + ++f_pos; + + if (f_pos >= folded_len) + --text_pos; + } + + if (f_pos < folded_len) + return RE_ERROR_FAILURE; + + next_position->node = next->match_next; + if (next->match_step == 0) + next_position->text_pos = start_pos; + else + next_position->text_pos = text_pos; + + return RE_ERROR_SUCCESS; +} + +/* Checks whether there's a certain string at a position, ignoring case. */ +Py_LOCAL_INLINE(int) try_match_STRING_IGN(RE_State* state, RE_NextNode* next, + RE_Node* node, Py_ssize_t text_pos, RE_Position* next_position) { + Py_ssize_t length; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + RE_CODE* values; + Py_ssize_t s_pos; + + length = (Py_ssize_t)node->value_count; + char_at = state->char_at; + encoding = state->encoding; + locale_info = state->locale_info; + values = node->values; + + for (s_pos = 0; s_pos < length; s_pos++) { + if (text_pos + s_pos >= state->slice_end) { + if (state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + if (!same_char_ign(encoding, locale_info, char_at(state->text, text_pos + + s_pos), values[s_pos])) + return RE_ERROR_FAILURE; + } + + next_position->node = next->match_next; + next_position->text_pos = text_pos + next->match_step; + + return RE_ERROR_SUCCESS; +} + +/* Checks whether there's a certain string at a position, ignoring case, + * backwards. + */ +Py_LOCAL_INLINE(int) try_match_STRING_IGN_REV(RE_State* state, RE_NextNode* + next, RE_Node* node, Py_ssize_t text_pos, RE_Position* next_position) { + Py_ssize_t length; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + RE_CODE* values; + Py_ssize_t s_pos; + + length = (Py_ssize_t)node->value_count; + char_at = state->char_at; + encoding = state->encoding; + locale_info = state->locale_info; + values = node->values; + + for (s_pos = 0; s_pos < length; s_pos++) { + if (text_pos - s_pos <= state->slice_start) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + if (!same_char_ign(encoding, locale_info, char_at(state->text, text_pos + - s_pos - 1), values[length - s_pos - 1])) + return RE_ERROR_FAILURE; + } + + next_position->node = next->match_next; + next_position->text_pos = text_pos + next->match_step; + + return RE_ERROR_SUCCESS; +} + +/* Checks whether there's a certain string at a position, backwards. */ +Py_LOCAL_INLINE(int) try_match_STRING_REV(RE_State* state, RE_NextNode* next, + RE_Node* node, Py_ssize_t text_pos, RE_Position* next_position) { + Py_ssize_t length; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + RE_CODE* values; + Py_ssize_t s_pos; + + length = (Py_ssize_t)node->value_count; + char_at = state->char_at; + values = node->values; + + for (s_pos = 0; s_pos < length; s_pos++) { + if (text_pos - s_pos <= state->slice_start) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + + if (!same_char(char_at(state->text, text_pos - s_pos - 1), + values[length - s_pos - 1])) + return RE_ERROR_FAILURE; + } + + next_position->node = next->match_next; + next_position->text_pos = text_pos + next->match_step; + + return RE_ERROR_SUCCESS; +} + +/* Tries a match at the current text position. + * + * Returns the next node and text position if the match succeeds. + */ +Py_LOCAL_INLINE(int) try_match(RE_State* state, RE_NextNode* next, Py_ssize_t + text_pos, RE_Position* next_position) { + RE_Node* test; + int status; + + test = next->test; + + if (test->status & RE_STATUS_FUZZY) { + next_position->node = next->node; + next_position->text_pos = text_pos; + return RE_ERROR_SUCCESS; + } + + switch (test->op) { + case RE_OP_ANY: + status = try_match_ANY(state, test, text_pos); + break; + case RE_OP_ANY_ALL: + status = try_match_ANY_ALL(state, test, text_pos); + break; + case RE_OP_ANY_ALL_REV: + status = try_match_ANY_ALL_REV(state, test, text_pos); + break; + case RE_OP_ANY_REV: + status = try_match_ANY_REV(state, test, text_pos); + break; + case RE_OP_ANY_U: + status = try_match_ANY_U(state, test, text_pos); + break; + case RE_OP_ANY_U_REV: + status = try_match_ANY_U_REV(state, test, text_pos); + break; + case RE_OP_BOUNDARY: + status = try_match_BOUNDARY(state, test, text_pos); + break; + case RE_OP_BRANCH: + status = try_match(state, &test->next_1, text_pos, next_position); + if (status == RE_ERROR_FAILURE) + status = try_match(state, &test->nonstring.next_2, text_pos, + next_position); + break; + case RE_OP_CHARACTER: + status = try_match_CHARACTER(state, test, text_pos); + break; + case RE_OP_CHARACTER_IGN: + status = try_match_CHARACTER_IGN(state, test, text_pos); + break; + case RE_OP_CHARACTER_IGN_REV: + status = try_match_CHARACTER_IGN_REV(state, test, text_pos); + break; + case RE_OP_CHARACTER_REV: + status = try_match_CHARACTER_REV(state, test, text_pos); + break; + case RE_OP_DEFAULT_BOUNDARY: + status = try_match_DEFAULT_BOUNDARY(state, test, text_pos); + break; + case RE_OP_DEFAULT_END_OF_WORD: + status = try_match_DEFAULT_END_OF_WORD(state, test, text_pos); + break; + case RE_OP_DEFAULT_START_OF_WORD: + status = try_match_DEFAULT_START_OF_WORD(state, test, text_pos); + break; + case RE_OP_END_OF_LINE: + status = try_match_END_OF_LINE(state, test, text_pos); + break; + case RE_OP_END_OF_LINE_U: + status = try_match_END_OF_LINE_U(state, test, text_pos); + break; + case RE_OP_END_OF_STRING: + status = try_match_END_OF_STRING(state, test, text_pos); + break; + case RE_OP_END_OF_STRING_LINE: + status = try_match_END_OF_STRING_LINE(state, test, text_pos); + break; + case RE_OP_END_OF_STRING_LINE_U: + status = try_match_END_OF_STRING_LINE_U(state, test, text_pos); + break; + case RE_OP_END_OF_WORD: + status = try_match_END_OF_WORD(state, test, text_pos); + break; + case RE_OP_GRAPHEME_BOUNDARY: + status = try_match_GRAPHEME_BOUNDARY(state, test, text_pos); + break; + case RE_OP_PROPERTY: + status = try_match_PROPERTY(state, test, text_pos); + break; + case RE_OP_PROPERTY_IGN: + status = try_match_PROPERTY_IGN(state, test, text_pos); + break; + case RE_OP_PROPERTY_IGN_REV: + status = try_match_PROPERTY_IGN_REV(state, test, text_pos); + break; + case RE_OP_PROPERTY_REV: + status = try_match_PROPERTY_REV(state, test, text_pos); + break; + case RE_OP_RANGE: + status = try_match_RANGE(state, test, text_pos); + break; + case RE_OP_RANGE_IGN: + status = try_match_RANGE_IGN(state, test, text_pos); + break; + case RE_OP_RANGE_IGN_REV: + status = try_match_RANGE_IGN_REV(state, test, text_pos); + break; + case RE_OP_RANGE_REV: + status = try_match_RANGE_REV(state, test, text_pos); + break; + case RE_OP_SEARCH_ANCHOR: + status = try_match_SEARCH_ANCHOR(state, test, text_pos); + break; + case RE_OP_SET_DIFF: + case RE_OP_SET_INTER: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_UNION: + status = try_match_SET(state, test, text_pos); + break; + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_UNION_IGN: + status = try_match_SET_IGN(state, test, text_pos); + break; + case RE_OP_SET_DIFF_IGN_REV: + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_UNION_IGN_REV: + status = try_match_SET_IGN_REV(state, test, text_pos); + break; + case RE_OP_SET_DIFF_REV: + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION_REV: + status = try_match_SET_REV(state, test, text_pos); + break; + case RE_OP_START_OF_LINE: + status = try_match_START_OF_LINE(state, test, text_pos); + break; + case RE_OP_START_OF_LINE_U: + status = try_match_START_OF_LINE_U(state, test, text_pos); + break; + case RE_OP_START_OF_STRING: + status = try_match_START_OF_STRING(state, test, text_pos); + break; + case RE_OP_START_OF_WORD: + status = try_match_START_OF_WORD(state, test, text_pos); + break; + case RE_OP_STRING: + return try_match_STRING(state, next, test, text_pos, next_position); + case RE_OP_STRING_FLD: + return try_match_STRING_FLD(state, next, test, text_pos, + next_position); + case RE_OP_STRING_FLD_REV: + return try_match_STRING_FLD_REV(state, next, test, text_pos, + next_position); + case RE_OP_STRING_IGN: + return try_match_STRING_IGN(state, next, test, text_pos, + next_position); + case RE_OP_STRING_IGN_REV: + return try_match_STRING_IGN_REV(state, next, test, text_pos, + next_position); + case RE_OP_STRING_REV: + return try_match_STRING_REV(state, next, test, text_pos, + next_position); + default: + next_position->node = next->node; + next_position->text_pos = text_pos; + return RE_ERROR_SUCCESS; + } + + if (status != RE_ERROR_SUCCESS) + return status; + + next_position->node = next->match_next; + next_position->text_pos = text_pos + next->match_step; + + return RE_ERROR_SUCCESS; +} + +/* Searches for a word boundary. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_BOUNDARY(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_boundary)(RE_State* state, Py_ssize_t text_pos); + + at_boundary = state->encoding->at_boundary; + + *is_partial = FALSE; + + for (;;) { + if (at_boundary(state, text_pos) == node->match) + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for a word boundary, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_BOUNDARY_rev(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_boundary)(RE_State* state, Py_ssize_t text_pos); + + at_boundary = state->encoding->at_boundary; + + *is_partial = FALSE; + + for (;;) { + if (at_boundary(state, text_pos) == node->match) + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for a default word boundary. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_DEFAULT_BOUNDARY(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_default_boundary)(RE_State* state, Py_ssize_t text_pos); + + at_default_boundary = state->encoding->at_default_boundary; + + *is_partial = FALSE; + + for (;;) { + if (at_default_boundary(state, text_pos) == node->match) + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for a default word boundary, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_DEFAULT_BOUNDARY_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_default_boundary)(RE_State* state, Py_ssize_t text_pos); + + at_default_boundary = state->encoding->at_default_boundary; + + *is_partial = FALSE; + + for (;;) { + if (at_default_boundary(state, text_pos) == node->match) + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for the default end of a word. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_DEFAULT_END_OF_WORD(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_default_word_end)(RE_State* state, Py_ssize_t text_pos); + + at_default_word_end = state->encoding->at_default_word_end; + + *is_partial = FALSE; + + for (;;) { + if (at_default_word_end(state, text_pos) == node->match) + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for the default end of a word, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_DEFAULT_END_OF_WORD_rev(RE_State* + state, RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_default_word_end)(RE_State* state, Py_ssize_t text_pos); + + at_default_word_end = state->encoding->at_default_word_end; + + *is_partial = FALSE; + + for (;;) { + if (at_default_word_end(state, text_pos) == node->match) + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for the default start of a word. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_DEFAULT_START_OF_WORD(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_default_word_start)(RE_State* state, Py_ssize_t text_pos); + + at_default_word_start = state->encoding->at_default_word_start; + + *is_partial = FALSE; + + for (;;) { + if (at_default_word_start(state, text_pos) == node->match) + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for the default start of a word, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_DEFAULT_START_OF_WORD_rev(RE_State* + state, RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_default_word_start)(RE_State* state, Py_ssize_t text_pos); + + at_default_word_start = state->encoding->at_default_word_start; + + *is_partial = FALSE; + + for (;;) { + if (at_default_word_start(state, text_pos) == node->match) + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for the end of line. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_END_OF_LINE(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + for (;;) { + if (text_pos >= state->text_length || state->char_at(state->text, + text_pos) == '\n') + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for the end of line, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_END_OF_LINE_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + for (;;) { + if (text_pos >= state->text_length || state->char_at(state->text, + text_pos) == '\n') + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for the end of the string. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_END_OF_STRING(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + if (state->slice_end >= state->text_length) + return state->text_length; + + return -1; +} + +/* Searches for the end of the string, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_END_OF_STRING_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + if (text_pos >= state->text_length) + return text_pos; + + return -1; +} + +/* Searches for the end of the string or line. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_END_OF_STRING_LINE(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + if (text_pos <= state->final_newline) + text_pos = state->final_newline; + else if (text_pos <= state->text_length) + text_pos = state->text_length; + + if (text_pos > state->slice_end) + return -1; + + if (text_pos >= state->text_length) + return text_pos; + + return text_pos; +} + +/* Searches for the end of the string or line, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_END_OF_STRING_LINE_rev(RE_State* + state, RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + if (text_pos >= state->text_length) + text_pos = state->text_length; + else if (text_pos >= state->final_newline) + text_pos = state->final_newline; + else + return -1; + + if (text_pos < state->slice_start) + return -1; + + if (text_pos <= 0) + return text_pos; + + return text_pos; +} + +/* Searches for the end of a word. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_END_OF_WORD(RE_State* state, RE_Node* + node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_word_end)(RE_State* state, Py_ssize_t text_pos); + + at_word_end = state->encoding->at_word_end; + + *is_partial = FALSE; + + for (;;) { + if (at_word_end(state, text_pos) == node->match) + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for the end of a word, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_END_OF_WORD_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_word_end)(RE_State* state, Py_ssize_t text_pos); + + at_word_end = state->encoding->at_word_end; + + *is_partial = FALSE; + + for (;;) { + if (at_word_end(state, text_pos) == node->match) + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for a grapheme boundary. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_GRAPHEME_BOUNDARY(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_grapheme_boundary)(RE_State* state, Py_ssize_t text_pos); + + at_grapheme_boundary = state->encoding->at_grapheme_boundary; + + *is_partial = FALSE; + + for (;;) { + if (at_grapheme_boundary(state, text_pos) == node->match) + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for a grapheme boundary, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_GRAPHEME_BOUNDARY_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_grapheme_boundary)(RE_State* state, Py_ssize_t text_pos); + + at_grapheme_boundary = state->encoding->at_grapheme_boundary; + + *is_partial = FALSE; + + for (;;) { + if (at_grapheme_boundary(state, text_pos) == node->match) + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for the start of line. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_START_OF_LINE(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + for (;;) { + if (text_pos <= 0 || state->char_at(state->text, text_pos - 1) == '\n') + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for the start of line, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_START_OF_LINE_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + for (;;) { + if (text_pos <= 0 || state->char_at(state->text, text_pos - 1) == '\n') + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for the start of the string. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_START_OF_STRING(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + if (text_pos <= 0) + return text_pos; + + return -1; +} + +/* Searches for the start of the string, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_START_OF_STRING_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + *is_partial = FALSE; + + if (state->slice_start <= 0) + return 0; + + return -1; +} + +/* Searches for the start of a word. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_START_OF_WORD(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_word_start)(RE_State* state, Py_ssize_t text_pos); + + at_word_start = state->encoding->at_word_start; + + *is_partial = FALSE; + + for (;;) { + if (at_word_start(state, text_pos) == node->match) + return text_pos; + + if (text_pos >= state->slice_end) + return -1; + + ++text_pos; + } +} + +/* Searches for the start of a word, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_START_OF_WORD_rev(RE_State* state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + BOOL (*at_word_start)(RE_State* state, Py_ssize_t text_pos); + + at_word_start = state->encoding->at_word_start; + + *is_partial = FALSE; + + for (;;) { + if (at_word_start(state, text_pos) == node->match) + return text_pos; + + if (text_pos <= state->slice_start) + return -1; + + --text_pos; + } +} + +/* Searches for a string. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_STRING(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + RE_State* state; + + state = safe_state->re_state; + + *is_partial = FALSE; + + if ((node->status & RE_STATUS_REQUIRED) && text_pos == state->req_pos) + return text_pos; + + return string_search(safe_state, node, text_pos, state->slice_end, + is_partial); +} + +/* Searches for a string, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_STRING_FLD(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, Py_ssize_t* new_pos, BOOL* is_partial) { + RE_State* state; + + state = safe_state->re_state; + + *is_partial = FALSE; + + if ((node->status & RE_STATUS_REQUIRED) && text_pos == state->req_pos) { + *new_pos = state->req_end; + return text_pos; + } + + return string_search_fld(safe_state, node, text_pos, state->slice_end, + new_pos, is_partial); +} + +/* Searches for a string, ignoring case, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_STRING_FLD_REV(RE_SafeState* + safe_state, RE_Node* node, Py_ssize_t text_pos, Py_ssize_t* new_pos, BOOL* + is_partial) { + RE_State* state; + + state = safe_state->re_state; + + *is_partial = FALSE; + + if ((node->status & RE_STATUS_REQUIRED) && text_pos == state->req_pos) { + *new_pos = state->req_end; + return text_pos; + } + + return string_search_fld_rev(safe_state, node, text_pos, + state->slice_start, new_pos, is_partial); +} + +/* Searches for a string, ignoring case. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_STRING_IGN(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + RE_State* state; + + state = safe_state->re_state; + + *is_partial = FALSE; + + if ((node->status & RE_STATUS_REQUIRED) && text_pos == state->req_pos) + return text_pos; + + return string_search_ign(safe_state, node, text_pos, state->slice_end, + is_partial); +} + +/* Searches for a string, ignoring case, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_STRING_IGN_REV(RE_SafeState* + safe_state, RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + RE_State* state; + + state = safe_state->re_state; + + *is_partial = FALSE; + + if ((node->status & RE_STATUS_REQUIRED) && text_pos == state->req_pos) + return text_pos; + + return string_search_ign_rev(safe_state, node, text_pos, + state->slice_start, is_partial); +} + +/* Searches for a string, backwards. */ +Py_LOCAL_INLINE(Py_ssize_t) search_start_STRING_REV(RE_SafeState* safe_state, + RE_Node* node, Py_ssize_t text_pos, BOOL* is_partial) { + RE_State* state; + + state = safe_state->re_state; + + *is_partial = FALSE; + + if ((node->status & RE_STATUS_REQUIRED) && text_pos == state->req_pos) + return text_pos; + + return string_search_rev(safe_state, node, text_pos, state->slice_start, + is_partial); +} + +/* Searches for the start of a match. */ +Py_LOCAL_INLINE(int) search_start(RE_SafeState* safe_state, RE_NextNode* next, + RE_Position* new_position, int search_index) { + RE_State* state; + Py_ssize_t start_pos; + RE_Node* test; + RE_Node* node; + RE_SearchPosition* info; + Py_ssize_t text_pos; + + state = safe_state->re_state; + + start_pos = state->text_pos; + TRACE(("<<search_start>> at %d\n", start_pos)) + + test = next->test; + node = next->node; + + if (state->reverse) { + if (start_pos < state->slice_start) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = state->slice_start; + return RE_ERROR_PARTIAL; + } + + return RE_ERROR_FAILURE; + } + } else { + if (start_pos > state->slice_end) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = state->slice_end; + return RE_ERROR_PARTIAL; + } + } + } + + if (test->status & RE_STATUS_FUZZY) { + /* Don't call 'search_start' again. */ + state->pattern->do_search_start = FALSE; + + state->match_pos = start_pos; + new_position->node = node; + new_position->text_pos = start_pos; + + return RE_ERROR_SUCCESS; + } + +again: + if (!state->pattern->is_fuzzy && state->partial_side == RE_PARTIAL_NONE) { + if (state->reverse) { + if (start_pos - state->min_width < state->slice_start) + return RE_ERROR_FAILURE; + } else { + if (start_pos + state->min_width > state->slice_end) + return RE_ERROR_FAILURE; + } + } + + if (search_index < MAX_SEARCH_POSITIONS) { + info = &state->search_positions[search_index]; + if (state->reverse) { + if (info->start_pos >= 0 && info->start_pos >= start_pos && + start_pos >= info->match_pos) { + state->match_pos = info->match_pos; + + new_position->text_pos = state->match_pos; + new_position->node = node; + + return RE_ERROR_SUCCESS; + } + } else { + if (info->start_pos >= 0 && info->start_pos <= start_pos && + start_pos <= info->match_pos) { + state->match_pos = info->match_pos; + + new_position->text_pos = state->match_pos; + new_position->node = node; + + return RE_ERROR_SUCCESS; + } + } + } else + info = NULL; + + switch (test->op) { + case RE_OP_ANY: + start_pos = match_many_ANY(state, test, start_pos, state->slice_end, + FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return RE_ERROR_FAILURE; + break; + case RE_OP_ANY_ALL: + case RE_OP_ANY_ALL_REV: + break; + case RE_OP_ANY_REV: + start_pos = match_many_ANY_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return RE_ERROR_FAILURE; + break; + case RE_OP_ANY_U: + start_pos = match_many_ANY_U(state, test, start_pos, state->slice_end, + FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return RE_ERROR_FAILURE; + break; + case RE_OP_ANY_U_REV: + start_pos = match_many_ANY_U_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return RE_ERROR_FAILURE; + break; + case RE_OP_BOUNDARY: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_BOUNDARY_rev(state, test, start_pos, + &is_partial); + else + start_pos = search_start_BOUNDARY(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_CHARACTER: + start_pos = match_many_CHARACTER(state, test, start_pos, + state->slice_end, FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return RE_ERROR_FAILURE; + break; + case RE_OP_CHARACTER_IGN: + start_pos = match_many_CHARACTER_IGN(state, test, start_pos, + state->slice_end, FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return RE_ERROR_FAILURE; + break; + case RE_OP_CHARACTER_IGN_REV: + start_pos = match_many_CHARACTER_IGN_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return RE_ERROR_FAILURE; + break; + case RE_OP_CHARACTER_REV: + start_pos = match_many_CHARACTER_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return RE_ERROR_FAILURE; + break; + case RE_OP_DEFAULT_BOUNDARY: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_DEFAULT_BOUNDARY_rev(state, test, + start_pos, &is_partial); + else + start_pos = search_start_DEFAULT_BOUNDARY(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_DEFAULT_END_OF_WORD: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_DEFAULT_END_OF_WORD_rev(state, test, + start_pos, &is_partial); + else + start_pos = search_start_DEFAULT_END_OF_WORD(state, test, + start_pos, &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_DEFAULT_START_OF_WORD: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_DEFAULT_START_OF_WORD_rev(state, test, + start_pos, &is_partial); + else + start_pos = search_start_DEFAULT_START_OF_WORD(state, test, + start_pos, &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_END_OF_LINE: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_END_OF_LINE_rev(state, test, start_pos, + &is_partial); + else + start_pos = search_start_END_OF_LINE(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_END_OF_STRING: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_END_OF_STRING_rev(state, test, start_pos, + &is_partial); + else + start_pos = search_start_END_OF_STRING(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_END_OF_STRING_LINE: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_END_OF_STRING_LINE_rev(state, test, + start_pos, &is_partial); + else + start_pos = search_start_END_OF_STRING_LINE(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_END_OF_WORD: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_END_OF_WORD_rev(state, test, start_pos, + &is_partial); + else + start_pos = search_start_END_OF_WORD(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_GRAPHEME_BOUNDARY: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_GRAPHEME_BOUNDARY_rev(state, test, + start_pos, &is_partial); + else + start_pos = search_start_GRAPHEME_BOUNDARY(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_PROPERTY: + start_pos = match_many_PROPERTY(state, test, start_pos, + state->slice_end, FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return RE_ERROR_FAILURE; + break; + case RE_OP_PROPERTY_IGN: + start_pos = match_many_PROPERTY_IGN(state, test, start_pos, + state->slice_end, FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return RE_ERROR_FAILURE; + break; + case RE_OP_PROPERTY_IGN_REV: + start_pos = match_many_PROPERTY_IGN_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return RE_ERROR_FAILURE; + break; + case RE_OP_PROPERTY_REV: + start_pos = match_many_PROPERTY_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return RE_ERROR_FAILURE; + break; + case RE_OP_RANGE: + start_pos = match_many_RANGE(state, test, start_pos, state->slice_end, + FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return RE_ERROR_FAILURE; + break; + case RE_OP_RANGE_IGN: + start_pos = match_many_RANGE_IGN(state, test, start_pos, + state->slice_end, FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return RE_ERROR_FAILURE; + break; + case RE_OP_RANGE_IGN_REV: + start_pos = match_many_RANGE_IGN_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return RE_ERROR_FAILURE; + break; + case RE_OP_RANGE_REV: + start_pos = match_many_RANGE_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return RE_ERROR_FAILURE; + break; + case RE_OP_SEARCH_ANCHOR: + if (state->reverse) { + if (start_pos < state->search_anchor) + return RE_ERROR_FAILURE; + } else { + if (start_pos > state->search_anchor) + return RE_ERROR_FAILURE; + } + + start_pos = state->search_anchor; + break; + case RE_OP_SET_DIFF: + case RE_OP_SET_INTER: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_UNION: + start_pos = match_many_SET(state, test, start_pos, state->slice_end, + FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return FALSE; + break; + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_UNION_IGN: + start_pos = match_many_SET_IGN(state, test, start_pos, + state->slice_end, FALSE); + + if (start_pos >= state->text_length) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos >= state->slice_end) + return FALSE; + break; + case RE_OP_SET_DIFF_IGN_REV: + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_UNION_IGN_REV: + start_pos = match_many_SET_IGN_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return FALSE; + break; + case RE_OP_SET_DIFF_REV: + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION_REV: + start_pos = match_many_SET_REV(state, test, start_pos, + state->slice_start, FALSE); + + if (start_pos <= 0) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + } + + if (start_pos <= state->slice_start) + return FALSE; + break; + case RE_OP_START_OF_LINE: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_START_OF_LINE_rev(state, test, start_pos, + &is_partial); + else + start_pos = search_start_START_OF_LINE(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_START_OF_STRING: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_START_OF_STRING_rev(state, test, + start_pos, &is_partial); + else + start_pos = search_start_START_OF_STRING(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_START_OF_WORD: + { + BOOL is_partial; + + if (state->reverse) + start_pos = search_start_START_OF_WORD_rev(state, test, start_pos, + &is_partial); + else + start_pos = search_start_START_OF_WORD(state, test, start_pos, + &is_partial); + + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_STRING: + { + BOOL is_partial; + + start_pos = search_start_STRING(safe_state, test, start_pos, + &is_partial); + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_STRING_FLD: + { + Py_ssize_t new_pos; + BOOL is_partial; + + start_pos = search_start_STRING_FLD(safe_state, test, start_pos, + &new_pos, &is_partial); + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + + /* Can we look further ahead? */ + if (test == node) { + if (test->next_1.node) { + int status; + + status = try_match(state, &test->next_1, new_pos, + new_position); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) { + ++start_pos; + + if (start_pos >= state->slice_end) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = state->slice_start; + return RE_ERROR_PARTIAL; + } + + return RE_ERROR_FAILURE; + } + + goto again; + } + } + + /* It's a possible match. */ + state->match_pos = start_pos; + + if (info) { + info->start_pos = state->text_pos; + info->match_pos = state->match_pos; + } + + return RE_ERROR_SUCCESS; + } + break; + } + case RE_OP_STRING_FLD_REV: + { + Py_ssize_t new_pos; + BOOL is_partial; + + start_pos = search_start_STRING_FLD_REV(safe_state, test, start_pos, + &new_pos, &is_partial); + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + + /* Can we look further ahead? */ + if (test == node) { + if (test->next_1.node) { + int status; + + status = try_match(state, &test->next_1, new_pos, + new_position); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) { + --start_pos; + + if (start_pos <= state->slice_start) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = state->slice_start; + return RE_ERROR_PARTIAL; + } + + return RE_ERROR_FAILURE; + } + + goto again; + } + } + + /* It's a possible match. */ + state->match_pos = start_pos; + + if (info) { + info->start_pos = state->text_pos; + info->match_pos = state->match_pos; + } + + return RE_ERROR_SUCCESS; + } + break; + } + case RE_OP_STRING_IGN: + { + BOOL is_partial; + + start_pos = search_start_STRING_IGN(safe_state, test, start_pos, + &is_partial); + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_STRING_IGN_REV: + { + BOOL is_partial; + + start_pos = search_start_STRING_IGN_REV(safe_state, test, start_pos, + &is_partial); + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + case RE_OP_STRING_REV: + { + BOOL is_partial; + + start_pos = search_start_STRING_REV(safe_state, test, start_pos, + &is_partial); + if (start_pos < 0) + return RE_ERROR_FAILURE; + + if (is_partial) { + new_position->text_pos = start_pos; + return RE_ERROR_PARTIAL; + } + break; + } + default: + /* Don't call 'search_start' again. */ + state->pattern->do_search_start = FALSE; + + state->match_pos = start_pos; + new_position->node = node; + new_position->text_pos = start_pos; + return RE_ERROR_SUCCESS; + } + + /* Can we look further ahead? */ + if (test == node) { + text_pos = start_pos + test->step; + + if (test->next_1.node) { + int status; + + status = try_match(state, &test->next_1, text_pos, new_position); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) { + if (state->reverse) { + --start_pos; + + if (start_pos < state->slice_start) { + if (state->partial_side == RE_PARTIAL_LEFT) { + new_position->text_pos = state->slice_start; + return RE_ERROR_PARTIAL; + } + + return RE_ERROR_FAILURE; + } + } else { + ++start_pos; + + if (start_pos > state->slice_end) { + if (state->partial_side == RE_PARTIAL_RIGHT) { + new_position->text_pos = state->slice_end; + return RE_ERROR_PARTIAL; + } + + return RE_ERROR_FAILURE; + } + } + + goto again; + } + } + } else { + new_position->node = node; + new_position->text_pos = start_pos; + } + + /* It's a possible match. */ + state->match_pos = start_pos; + + if (info) { + info->start_pos = state->text_pos; + info->match_pos = state->match_pos; + } + + return RE_ERROR_SUCCESS; +} + +/* Saves a capture group. */ +Py_LOCAL_INLINE(BOOL) save_capture(RE_SafeState* safe_state, size_t + private_index, size_t public_index) { + RE_State* state; + RE_GroupData* private_group; + RE_GroupData* public_group; + + state = safe_state->re_state; + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + private_group = &state->groups[private_index - 1]; + public_group = &state->groups[public_index - 1]; + + /* Will the repeated captures ever be visible? */ + if (!state->visible_captures) { + public_group->captures[0] = private_group->span; + public_group->capture_count = 1; + + return TRUE; + } + + if (public_group->capture_count >= public_group->capture_capacity) { + size_t new_capacity; + RE_GroupSpan* new_captures; + + new_capacity = public_group->capture_capacity * 2; + new_capacity = max_size_t(new_capacity, RE_INIT_CAPTURE_SIZE); + new_captures = (RE_GroupSpan*)safe_realloc(safe_state, + public_group->captures, new_capacity * sizeof(RE_GroupSpan)); + if (!new_captures) + return FALSE; + + public_group->captures = new_captures; + public_group->capture_capacity = new_capacity; + } + + public_group->captures[public_group->capture_count++] = + private_group->span; + + return TRUE; +} + +/* Unsaves a capture group. */ +Py_LOCAL_INLINE(void) unsave_capture(RE_State* state, size_t private_index, + size_t public_index) { + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + if (state->groups[public_index - 1].capture_count > 0) + --state->groups[public_index - 1].capture_count; +} + +/* Pushes the groups for backtracking. */ +Py_LOCAL_INLINE(BOOL) push_groups(RE_SafeState* safe_state) { + RE_State* state; + size_t group_count; + RE_SavedGroups* current; + size_t g; + + state = safe_state->re_state; + + group_count = state->pattern->true_group_count; + if (group_count == 0) + return TRUE; + + current = state->current_saved_groups; + + if (current && current->next) + current = current->next; + else if (!current && state->first_saved_groups) + current = state->first_saved_groups; + else { + RE_SavedGroups* new_block; + + new_block = (RE_SavedGroups*)safe_alloc(safe_state, + sizeof(RE_SavedGroups)); + if (!new_block) + return FALSE; + + new_block->spans = (RE_GroupSpan*)safe_alloc(safe_state, group_count * + sizeof(RE_GroupSpan)); + new_block->counts = (size_t*)safe_alloc(safe_state, group_count * + sizeof(Py_ssize_t)); + if (!new_block->spans || !new_block->counts) { + safe_dealloc(safe_state, new_block->spans); + safe_dealloc(safe_state, new_block->counts); + safe_dealloc(safe_state, new_block); + return FALSE; + } + + new_block->previous = current; + new_block->next = NULL; + + if (new_block->previous) + new_block->previous->next = new_block; + else + state->first_saved_groups = new_block; + + current = new_block; + } + + for (g = 0; g < group_count; g++) { + current->spans[g] = state->groups[g].span; + current->counts[g] = state->groups[g].capture_count; + } + + state->current_saved_groups = current; + + return TRUE; +} + +/* Pops the groups for backtracking. */ +Py_LOCAL_INLINE(void) pop_groups(RE_State* state) { + size_t group_count; + RE_SavedGroups* current; + size_t g; + + group_count = state->pattern->true_group_count; + if (group_count == 0) + return; + + current = state->current_saved_groups; + + for (g = 0; g < group_count; g++) { + state->groups[g].span = current->spans[g]; + state->groups[g].capture_count = current->counts[g]; + } + + state->current_saved_groups = current->previous; +} + +/* Drops the groups for backtracking. */ +Py_LOCAL_INLINE(void) drop_groups(RE_State* state) { + if (state->pattern->true_group_count != 0) + state->current_saved_groups = state->current_saved_groups->previous; +} + +/* Pushes the repeats for backtracking. */ +Py_LOCAL_INLINE(BOOL) push_repeats(RE_SafeState* safe_state) { + RE_State* state; + PatternObject* pattern; + size_t repeat_count; + RE_SavedRepeats* current; + size_t r; + + state = safe_state->re_state; + pattern = state->pattern; + + repeat_count = pattern->repeat_count; + if (repeat_count == 0) + return TRUE; + + current = state->current_saved_repeats; + + if (current && current->next) + current = current->next; + else if (!current && state->first_saved_repeats) + current = state->first_saved_repeats; + else { + RE_SavedRepeats* new_block; + + new_block = (RE_SavedRepeats*)safe_alloc(safe_state, + sizeof(RE_SavedRepeats)); + if (!new_block) + return FALSE; + + new_block->repeats = (RE_RepeatData*)safe_alloc(safe_state, + repeat_count * sizeof(RE_RepeatData)); + if (!new_block->repeats) { + safe_dealloc(safe_state, new_block); + return FALSE; + } + + memset(new_block->repeats, 0, repeat_count * sizeof(RE_RepeatData)); + + new_block->previous = current; + new_block->next = NULL; + + if (new_block->previous) + new_block->previous->next = new_block; + else + state->first_saved_repeats = new_block; + + current = new_block; + } + + for (r = 0; r < repeat_count; r++) { + if (!copy_repeat_data(safe_state, ¤t->repeats[r], + &state->repeats[r])) + return FALSE; + } + + state->current_saved_repeats = current; + + return TRUE; +} + +/* Pops the repeats for backtracking. */ +Py_LOCAL_INLINE(void) pop_repeats(RE_State* state) { + PatternObject* pattern; + size_t repeat_count; + RE_SavedRepeats* current; + size_t r; + + pattern = state->pattern; + + repeat_count = pattern->repeat_count; + if (repeat_count == 0) + return; + + current = state->current_saved_repeats; + + for (r = 0; r < repeat_count; r++) + copy_repeat_data(NULL, &state->repeats[r], ¤t->repeats[r]); + + state->current_saved_repeats = current->previous; +} + +/* Drops the repeats for backtracking. */ +Py_LOCAL_INLINE(void) drop_repeats(RE_State* state) { + PatternObject* pattern; + size_t repeat_count; + RE_SavedRepeats* current; + + pattern = state->pattern; + + repeat_count = pattern->repeat_count; + if (repeat_count == 0) + return; + + current = state->current_saved_repeats; + state->current_saved_repeats = current->previous; +} + +/* Inserts a new span in a guard list. */ +Py_LOCAL_INLINE(BOOL) insert_guard_span(RE_SafeState* safe_state, RE_GuardList* + guard_list, size_t index) { + size_t n; + + if (guard_list->count >= guard_list->capacity) { + size_t new_capacity; + RE_GuardSpan* new_spans; + + new_capacity = guard_list->capacity * 2; + if (new_capacity == 0) + new_capacity = RE_INIT_GUARDS_BLOCK_SIZE; + new_spans = (RE_GuardSpan*)safe_realloc(safe_state, guard_list->spans, + new_capacity * sizeof(RE_GuardSpan)); + if (!new_spans) + return FALSE; + + guard_list->capacity = new_capacity; + guard_list->spans = new_spans; + } + + n = guard_list->count - index; + if (n > 0) + memmove(guard_list->spans + index + 1, guard_list->spans + index, n * + sizeof(RE_GuardSpan)); + ++guard_list->count; + + return TRUE; +} + +/* Deletes a span in a guard list. */ +Py_LOCAL_INLINE(void) delete_guard_span(RE_GuardList* guard_list, size_t index) + { + size_t n; + + n = guard_list->count - index - 1; + if (n > 0) + memmove(guard_list->spans + index, guard_list->spans + index + 1, n * + sizeof(RE_GuardSpan)); + --guard_list->count; +} + +/* Checks whether a position is guarded against further matching. */ +Py_LOCAL_INLINE(BOOL) is_guarded(RE_GuardList* guard_list, Py_ssize_t text_pos) + { + size_t low; + size_t high; + + /* Is this position in the guard list? */ + if (guard_list->count == 0 || text_pos < guard_list->spans[0].low) + guard_list->last_low = 0; + else if (text_pos > guard_list->spans[guard_list->count - 1].high) + guard_list->last_low = guard_list->count; + else { + low = 0; + high = guard_list->count; + while (low < high) { + size_t mid; + RE_GuardSpan* span; + + mid = (low + high) / 2; + span = &guard_list->spans[mid]; + if (text_pos < span->low) + high = mid; + else if (text_pos > span->high) + low = mid + 1; + else + return span->protect; + } + + guard_list->last_low = low; + } + + guard_list->last_text_pos = text_pos; + + return FALSE; +} + +/* Guards a position against further matching. */ +Py_LOCAL_INLINE(BOOL) guard(RE_SafeState* safe_state, RE_GuardList* guard_list, + Py_ssize_t text_pos, BOOL protect) { + size_t low; + size_t high; + + /* Where should be new position be added? */ + if (text_pos == guard_list->last_text_pos) + low = guard_list->last_low; + else { + low = 0; + high = guard_list->count; + while (low < high) { + size_t mid; + RE_GuardSpan* span; + + mid = (low + high) / 2; + span = &guard_list->spans[mid]; + if (text_pos < span->low) + high = mid; + else if (text_pos > span->high) + low = mid + 1; + else + return TRUE; + } + } + + /* Add the position to the guard list. */ + if (low > 0 && guard_list->spans[low - 1].high + 1 == text_pos && + guard_list->spans[low - 1].protect == protect) { + /* The new position is just above this span. */ + if (low < guard_list->count && guard_list->spans[low].low - 1 == + text_pos && guard_list->spans[low].protect == protect) { + /* The new position joins 2 spans */ + guard_list->spans[low - 1].high = guard_list->spans[low].high; + delete_guard_span(guard_list, low); + } else + /* Extend the span. */ + guard_list->spans[low - 1].high = text_pos; + } else if (low < guard_list->count && guard_list->spans[low].low - 1 == + text_pos && guard_list->spans[low].protect == protect) + /* The new position is just below this span. */ + /* Extend the span. */ + guard_list->spans[low].low = text_pos; + else { + /* Insert a new span. */ + if (!insert_guard_span(safe_state, guard_list, low)) + return FALSE; + guard_list->spans[low].low = text_pos; + guard_list->spans[low].high = text_pos; + guard_list->spans[low].protect = protect; + } + + guard_list->last_text_pos = -1; + + return TRUE; +} + +/* Guards a position against further matching for a repeat. */ +Py_LOCAL_INLINE(BOOL) guard_repeat(RE_SafeState* safe_state, size_t index, + Py_ssize_t text_pos, RE_STATUS_T guard_type, BOOL protect) { + RE_State* state; + RE_GuardList* guard_list; + + state = safe_state->re_state; + + /* Is a guard active here? */ + if (!(state->pattern->repeat_info[index].status & guard_type)) + return TRUE; + + /* Which guard list? */ + if (guard_type & RE_STATUS_BODY) + guard_list = &state->repeats[index].body_guard_list; + else + guard_list = &state->repeats[index].tail_guard_list; + + return guard(safe_state, guard_list, text_pos, protect); +} + +/* Guards a range of positions against further matching for a repeat. */ +Py_LOCAL_INLINE(BOOL) guard_repeat_range(RE_SafeState* safe_state, size_t + index, Py_ssize_t lo_pos, Py_ssize_t hi_pos, RE_STATUS_T guard_type, BOOL + protect) { + RE_State* state; + RE_GuardList* guard_list; + Py_ssize_t pos; + + state = safe_state->re_state; + + /* Is a guard active here? */ + if (!(state->pattern->repeat_info[index].status & guard_type)) + return TRUE; + + /* Which guard list? */ + if (guard_type & RE_STATUS_BODY) + guard_list = &state->repeats[index].body_guard_list; + else + guard_list = &state->repeats[index].tail_guard_list; + + for (pos = lo_pos; pos <= hi_pos; pos++) { + if (!guard(safe_state, guard_list, pos, protect)) + return FALSE; + } + + return TRUE; +} + +/* Checks whether a position is guarded against further matching for a repeat. + */ +Py_LOCAL_INLINE(BOOL) is_repeat_guarded(RE_SafeState* safe_state, size_t index, + Py_ssize_t text_pos, RE_STATUS_T guard_type) { + RE_State* state; + RE_GuardList* guard_list; + + state = safe_state->re_state; + + /* Is a guard active here? */ + if (!(state->pattern->repeat_info[index].status & guard_type)) + return FALSE; + + /* Which guard list? */ + if (guard_type == RE_STATUS_BODY) + guard_list = &state->repeats[index].body_guard_list; + else + guard_list = &state->repeats[index].tail_guard_list; + + return is_guarded(guard_list, text_pos); +} + +/* Builds a Unicode string. */ +Py_LOCAL_INLINE(PyObject*) build_unicode_value(void* buffer, Py_ssize_t len, + Py_ssize_t buffer_charsize) { + return PyUnicode_FromUnicode(buffer, len); +} + +/* Builds a bytestring. Returns NULL if any member is too wide. */ +Py_LOCAL_INLINE(PyObject*) build_bytes_value(void* buffer, Py_ssize_t len, + Py_ssize_t buffer_charsize) { + Py_UCS1* byte_buffer; + Py_ssize_t i; + PyObject* result; + + if (buffer_charsize == 1) + return Py_BuildValue("s#", buffer, len); + + byte_buffer = re_alloc((size_t)len); + if (!byte_buffer) + return NULL; + + for (i = 0; i < len; i++) { + Py_UCS2 c = ((Py_UCS2*)buffer)[i]; + if (c > 0xFF) + goto too_wide; + + byte_buffer[i] = (Py_UCS1)c; + } + + result = Py_BuildValue("s#", byte_buffer, len); + + re_dealloc(byte_buffer); + + return result; + +too_wide: + re_dealloc(byte_buffer); + + return NULL; +} + +/* Looks for a string in a string set. */ +Py_LOCAL_INLINE(int) string_set_contains(RE_State* state, PyObject* string_set, + Py_ssize_t first, Py_ssize_t last) { + PyObject* string; + int status; + + if (state->is_unicode) + string = build_unicode_value(state->point_to(state->text, first), last + - first, state->charsize); + else + string = build_bytes_value(state->point_to(state->text, first), last - + first, state->charsize); + if (!string) + return RE_ERROR_INTERNAL; + + status = PySet_Contains(string_set, string); + Py_DECREF(string); + + return status; +} + +/* Looks for a string in a string set, ignoring case. */ +Py_LOCAL_INLINE(int) string_set_contains_ign(RE_State* state, PyObject* + string_set, void* buffer, Py_ssize_t index, Py_ssize_t len, Py_ssize_t + buffer_charsize) { + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + void (*set_char_at)(void* text, Py_ssize_t pos, Py_UCS4 ch); + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + BOOL (*possible_turkic)(RE_LocaleInfo* locale_info, Py_UCS4 ch); + Py_UCS4 codepoints[4]; + + switch (buffer_charsize) { + case 1: + char_at = bytes1_char_at; + set_char_at = bytes1_set_char_at; + break; + case 2: + char_at = bytes2_char_at; + set_char_at = bytes2_set_char_at; + break; + case 4: + char_at = bytes4_char_at; + set_char_at = bytes4_set_char_at; + break; + default: + char_at = bytes1_char_at; + set_char_at = bytes1_set_char_at; + break; + } + + encoding = state->encoding; + locale_info = state->locale_info; + possible_turkic = encoding->possible_turkic; + + /* Look for a possible Turkic 'I'. */ + while (index < len && !possible_turkic(locale_info, char_at(buffer, + index))) + ++index; + + if (index < len) { + /* Possible Turkic 'I'. */ + int count; + int i; + + /* Try all the alternatives to the 'I'. */ + count = encoding->all_turkic_i(locale_info, char_at(buffer, index), + codepoints); + + for (i = 0; i < count; i++) { + int status; + + set_char_at(buffer, index, codepoints[i]); + + /* Recurse for the remainder of the string. */ + status = string_set_contains_ign(state, string_set, buffer, index + + 1, len, buffer_charsize); + if (status != 0) + return status; + } + + return 0; + } else { + /* No Turkic 'I'. */ + PyObject* string; + int status; + + if (state->is_unicode) + string = build_unicode_value(buffer, len, buffer_charsize); + else + string = build_bytes_value(buffer, len, buffer_charsize); + if (!string) + return RE_ERROR_MEMORY; + + status = PySet_Contains(string_set, string); + Py_DECREF(string); + + return status; + } +} + +/* Creates a partial string set for truncation at the left or right side. */ +Py_LOCAL_INLINE(int) make_partial_string_set(RE_State* state, RE_Node* node) { + PatternObject* pattern; + int partial_side; + PyObject* string_set; + PyObject* partial_set; + PyObject* iter = NULL; + PyObject* item = NULL; + PyObject* slice = NULL; + + pattern = state->pattern; + partial_side = state->partial_side; + if (partial_side != RE_PARTIAL_LEFT && partial_side != RE_PARTIAL_RIGHT) + return RE_ERROR_INTERNAL; + + /* Fetch the full string set. PyList_GET_ITEM borrows a reference. */ + string_set = PyList_GET_ITEM(pattern->named_list_indexes, node->values[0]); + if (!string_set) + return RE_ERROR_INTERNAL; + + /* Gets the list of partial string sets. */ + if (!pattern->partial_named_lists[partial_side]) { + size_t size; + + size = pattern->named_lists_count * sizeof(PyObject*); + pattern->partial_named_lists[partial_side] = re_alloc(size); + if (!pattern->partial_named_lists[partial_side]) + return RE_ERROR_INTERNAL; + + memset(pattern->partial_named_lists[partial_side], 0, size); + } + + /* Get the partial string set. */ + partial_set = pattern->partial_named_lists[partial_side][node->values[0]]; + if (partial_set) + return 1; + + /* Build the partial string set. */ + partial_set = PySet_New(NULL); + if (!partial_set) + return RE_ERROR_INTERNAL; + + iter = PyObject_GetIter(string_set); + if (!iter) + goto error; + + item = PyIter_Next(iter); + + while (item) { + Py_ssize_t len; + Py_ssize_t first; + Py_ssize_t last; + + len = PySequence_Length(item); + if (len == -1) + goto error; + + first = 0; + last = len; + + while (last - first > 1) { + int status; + + /* Shorten the entry. */ + if (partial_side == RE_PARTIAL_LEFT) + ++first; + else + --last; + + slice = PySequence_GetSlice(item, first, last); + if (!slice) + goto error; + + status = PySet_Add(partial_set, slice); + Py_DECREF(slice); + if (status < 0) + goto error; + } + + Py_DECREF(item); + item = PyIter_Next(iter); + } + + if (PyErr_Occurred()) + goto error; + + Py_DECREF(iter); + + pattern->partial_named_lists[partial_side][node->values[0]] = partial_set; + + return 1; + +error: + Py_XDECREF(item); + Py_XDECREF(iter); + Py_DECREF(partial_set); + + return RE_ERROR_INTERNAL; +} + +/* Tries to match a string at the current position with a member of a string + * set, forwards or backwards. + */ +Py_LOCAL_INLINE(int) string_set_match_fwdrev(RE_SafeState* safe_state, RE_Node* + node, BOOL reverse) { + RE_State* state; + Py_ssize_t min_len; + Py_ssize_t max_len; + Py_ssize_t text_available; + Py_ssize_t slice_available; + int partial_side; + Py_ssize_t len; + Py_ssize_t first; + Py_ssize_t last; + int status; + PyObject* string_set; + + state = safe_state->re_state; + + min_len = (Py_ssize_t)node->values[1]; + max_len = (Py_ssize_t)node->values[2]; + + acquire_GIL(safe_state); + + if (reverse) { + text_available = state->text_pos; + slice_available = state->text_pos - state->slice_start; + partial_side = RE_PARTIAL_LEFT; + } else { + text_available = state->text_length - state->text_pos; + slice_available = state->slice_end - state->text_pos; + partial_side = RE_PARTIAL_RIGHT; + } + + /* Get as many characters as we need for the longest possible match. */ + len = min_ssize_t(max_len, slice_available); + + if (reverse) { + first = state->text_pos - len; + last = state->text_pos; + } else { + first = state->text_pos; + last = state->text_pos + len; + } + + /* If we didn't get all of the characters we need, is a partial match + * allowed? + */ + if (len < max_len && len == text_available && state->partial_side == + partial_side) { + if (len == 0) { + /* An empty string is always a possible partial match. */ + status = RE_ERROR_PARTIAL; + goto finished; + } + + /* Make a set of the possible partial matches. */ + status = make_partial_string_set(state, node); + if (status < 0) + goto finished; + + /* Fetch the partial string set. */ + string_set = + state->pattern->partial_named_lists[partial_side][node->values[0]]; + + /* Is the text we have a partial match? */ + status = string_set_contains(state, string_set, first, last); + if (status < 0) + goto finished; + + if (status == 1) { + /* Advance past the match. */ + if (reverse) + state->text_pos -= len; + else + state->text_pos += len; + + status = RE_ERROR_PARTIAL; + goto finished; + } + } + + /* Fetch the string set. PyList_GET_ITEM borrows a reference. */ + string_set = PyList_GET_ITEM(state->pattern->named_list_indexes, + node->values[0]); + if (!string_set) { + status = RE_ERROR_INTERNAL; + goto finished; + } + + /* We've already looked for a partial match (if allowed), but what about a + * complete match? + */ + while (len >= min_len) { + status = string_set_contains(state, string_set, first, last); + + if (status == 1) { + /* Advance past the match. */ + if (reverse) + state->text_pos -= len; + else + state->text_pos += len; + + status = 1; + goto finished; + } + + /* Look for a shorter match. */ + --len; + if (reverse) + ++first; + else + --last; + } + + /* No match. */ + status = 0; + +finished: + release_GIL(safe_state); + + return status; +} + +/* Tries to match a string at the current position with a member of a string + * set, ignoring case, forwards or backwards. + */ +Py_LOCAL_INLINE(int) string_set_match_fld_fwdrev(RE_SafeState* safe_state, + RE_Node* node, BOOL reverse) { + RE_State* state; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + folded); + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + Py_ssize_t folded_charsize; + void (*set_char_at)(void* text, Py_ssize_t pos, Py_UCS4 ch); + Py_ssize_t min_len; + Py_ssize_t max_len; + Py_ssize_t buf_len; + void* folded; + int status; + BOOL* end_of_fold = NULL; + Py_ssize_t text_available; + Py_ssize_t slice_available; + Py_ssize_t t_pos; + Py_ssize_t f_pos; + int step; + int partial_side; + Py_ssize_t len; + Py_ssize_t consumed; + Py_UCS4 codepoints[RE_MAX_FOLDED]; + Py_ssize_t first; + Py_ssize_t last; + PyObject* string_set; + + state = safe_state->re_state; + full_case_fold = state->encoding->full_case_fold; + char_at = state->char_at; + + /* The folded string will have the same width as the original string. */ + folded_charsize = state->charsize; + + switch (folded_charsize) { + case 1: + set_char_at = bytes1_set_char_at; + break; + case 2: + set_char_at = bytes2_set_char_at; + break; + case 4: + set_char_at = bytes4_set_char_at; + break; + default: + return RE_ERROR_INTERNAL; + } + + min_len = (Py_ssize_t)node->values[1]; + max_len = (Py_ssize_t)node->values[2]; + + acquire_GIL(safe_state); + + /* Allocate a buffer for the folded string. */ + buf_len = max_len + RE_MAX_FOLDED; + folded = re_alloc((size_t)(buf_len * folded_charsize)); + if (!folded) { + status = RE_ERROR_MEMORY; + goto finished; + } + + end_of_fold = re_alloc((size_t)buf_len * sizeof(BOOL)); + if (!end_of_fold) { + status = RE_ERROR_MEMORY; + goto finished; + } + + memset(end_of_fold, 0, (size_t)buf_len * sizeof(BOOL)); + + if (reverse) { + text_available = state->text_pos; + slice_available = state->text_pos - state->slice_start; + t_pos = state->text_pos - 1; + f_pos = buf_len; + step = -1; + partial_side = RE_PARTIAL_LEFT; + } else { + text_available = state->text_length - state->text_pos; + slice_available = state->slice_end - state->text_pos; + t_pos = state->text_pos; + f_pos = 0; + step = 1; + partial_side = RE_PARTIAL_RIGHT; + } + + /* We can stop getting characters as soon as the case-folded string is long + * enough (each codepoint from the text can expand to more than one folded + * codepoint). + */ + len = 0; + end_of_fold[len] = TRUE; + + consumed = 0; + while (len < max_len && consumed < slice_available) { + int count; + int j; + + count = full_case_fold(state->locale_info, char_at(state->text, t_pos), + codepoints); + + if (reverse) + f_pos -= count; + + for (j = 0; j < count; j++) + set_char_at(folded, f_pos + j, codepoints[j]); + + if (!reverse) + f_pos += count; + + len += count; + end_of_fold[len] = TRUE; + ++consumed; + t_pos += step; + } + + if (reverse) { + first = f_pos; + last = buf_len; + } else { + first = 0; + last = f_pos; + } + + /* If we didn't get all of the characters we need, is a partial match + * allowed? + */ + if (len < max_len && len == text_available && state->partial_side == + partial_side) { + if (len == 0) { + /* An empty string is always a possible partial match. */ + status = RE_ERROR_PARTIAL; + goto finished; + } + + /* Make a set of the possible partial matches. */ + status = make_partial_string_set(state, node); + if (status < 0) + goto finished; + + /* Fetch the partial string set. */ + string_set = + state->pattern->partial_named_lists[partial_side][node->values[0]]; + + /* Is the text we have a partial match? */ + status = string_set_contains_ign(state, string_set, folded, first, + last, folded_charsize); + if (status < 0) + goto finished; + + if (status == 1) { + /* Advance past the match. */ + if (reverse) + state->text_pos -= consumed; + else + state->text_pos += consumed; + + status = RE_ERROR_PARTIAL; + goto finished; + } + } + + /* Fetch the string set. PyList_GET_ITEM borrows a reference. */ + string_set = PyList_GET_ITEM(state->pattern->named_list_indexes, + node->values[0]); + if (!string_set) { + status = RE_ERROR_INTERNAL; + goto finished; + } + + /* We've already looked for a partial match (if allowed), but what about a + * complete match? + */ + while (len >= min_len) { + if (end_of_fold[len]) { + status = string_set_contains_ign(state, string_set, folded, first, + last, folded_charsize); + + if (status == 1) { + /* Advance past the match. */ + if (reverse) + state->text_pos -= consumed; + else + state->text_pos += consumed; + + status = 1; + goto finished; + } + + --consumed; + } + + /* Look for a shorter match. */ + --len; + if (reverse) + ++first; + else + --last; + } + + /* No match. */ + status = 0; + +finished: + re_dealloc(end_of_fold); + re_dealloc(folded); + + release_GIL(safe_state); + + return status; +} + +/* Tries to match a string at the current position with a member of a string + * set, ignoring case, forwards or backwards. + */ +Py_LOCAL_INLINE(int) string_set_match_ign_fwdrev(RE_SafeState* safe_state, + RE_Node* node, BOOL reverse) { + RE_State* state; + Py_UCS4 (*simple_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch); + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + Py_ssize_t folded_charsize; + void (*set_char_at)(void* text, Py_ssize_t pos, Py_UCS4 ch); + Py_ssize_t min_len; + Py_ssize_t max_len; + void* folded; + int status; + Py_ssize_t text_available; + Py_ssize_t slice_available; + Py_ssize_t t_pos; + Py_ssize_t f_pos; + int step; + int partial_side; + Py_ssize_t len; + Py_ssize_t i; + Py_ssize_t first; + Py_ssize_t last; + PyObject* string_set; + + state = safe_state->re_state; + simple_case_fold = state->encoding->simple_case_fold; + char_at = state->char_at; + + /* The folded string will have the same width as the original string. */ + folded_charsize = state->charsize; + + switch (folded_charsize) { + case 1: + set_char_at = bytes1_set_char_at; + break; + case 2: + set_char_at = bytes2_set_char_at; + break; + case 4: + set_char_at = bytes4_set_char_at; + break; + default: + return RE_ERROR_INTERNAL; + } + + min_len = (Py_ssize_t)node->values[1]; + max_len = (Py_ssize_t)node->values[2]; + + acquire_GIL(safe_state); + + /* Allocate a buffer for the folded string. */ + folded = re_alloc((size_t)(max_len * folded_charsize)); + if (!folded) { + status = RE_ERROR_MEMORY; + goto finished; + } + + if (reverse) { + text_available = state->text_pos; + slice_available = state->text_pos - state->slice_start; + t_pos = state->text_pos - 1; + f_pos = max_len - 1; + step = -1; + partial_side = RE_PARTIAL_LEFT; + } else { + text_available = state->text_length - state->text_pos; + slice_available = state->slice_end - state->text_pos; + t_pos = state->text_pos; + f_pos = 0; + step = 1; + partial_side = RE_PARTIAL_RIGHT; + } + + /* Get as many characters as we need for the longest possible match. */ + len = min_ssize_t(max_len, slice_available); + + for (i = 0; i < len; i ++) { + Py_UCS4 ch; + + ch = simple_case_fold(state->locale_info, char_at(state->text, t_pos)); + set_char_at(folded, f_pos, ch); + t_pos += step; + f_pos += step; + } + + if (reverse) { + first = f_pos; + last = max_len; + } else { + first = 0; + last = f_pos; + } + + /* If we didn't get all of the characters we need, is a partial match + * allowed? + */ + if (len < max_len && len == text_available && state->partial_side == + partial_side) { + if (len == 0) { + /* An empty string is always a possible partial match. */ + status = RE_ERROR_PARTIAL; + goto finished; + } + + /* Make a set of the possible partial matches. */ + status = make_partial_string_set(state, node); + if (status < 0) + goto finished; + + /* Fetch the partial string set. */ + string_set = + state->pattern->partial_named_lists[partial_side][node->values[0]]; + + /* Is the text we have a partial match? */ + status = string_set_contains_ign(state, string_set, folded, first, + last, folded_charsize); + if (status < 0) + goto finished; + + if (status == 1) { + /* Advance past the match. */ + if (reverse) + state->text_pos -= len; + else + state->text_pos += len; + + status = RE_ERROR_PARTIAL; + goto finished; + } + } + + /* Fetch the string set. PyList_GET_ITEM borrows a reference. */ + string_set = PyList_GET_ITEM(state->pattern->named_list_indexes, + node->values[0]); + if (!string_set) { + status = RE_ERROR_INTERNAL; + goto finished; + } + + /* We've already looked for a partial match (if allowed), but what about a + * complete match? + */ + while (len >= min_len) { + status = string_set_contains_ign(state, string_set, folded, first, + last, folded_charsize); + + if (status == 1) { + /* Advance past the match. */ + if (reverse) + state->text_pos -= len; + else + state->text_pos += len; + + status = 1; + goto finished; + } + + /* Look for a shorter match. */ + --len; + if (reverse) + ++first; + else + --last; + } + + /* No match. */ + status = 0; + +finished: + re_dealloc(folded); + + release_GIL(safe_state); + + return status; +} + +/* Checks whether any additional fuzzy error is permitted. */ +Py_LOCAL_INLINE(BOOL) any_error_permitted(RE_State* state) { + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + return fuzzy_info->total_cost <= values[RE_FUZZY_VAL_MAX_COST] && + fuzzy_info->counts[RE_FUZZY_ERR] < values[RE_FUZZY_VAL_MAX_ERR] && + state->total_errors <= state->max_errors; +} + +/* Checks whether this additional fuzzy error is permitted. */ +Py_LOCAL_INLINE(BOOL) this_error_permitted(RE_State* state, int fuzzy_type) { + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + return fuzzy_info->total_cost + values[RE_FUZZY_VAL_COST_BASE + fuzzy_type] + <= values[RE_FUZZY_VAL_MAX_COST] && fuzzy_info->counts[fuzzy_type] < + values[RE_FUZZY_VAL_MAX_BASE + fuzzy_type] && state->total_errors + 1 <= + state->max_errors; +} + +/* Checks whether we've reachsd the end of the text during a fuzzy partial + * match. + */ +Py_LOCAL_INLINE(int) check_fuzzy_partial(RE_State* state, Py_ssize_t text_pos) + { + switch (state->partial_side) { + case RE_PARTIAL_LEFT: + if (text_pos < 0) + return RE_ERROR_PARTIAL; + break; + case RE_PARTIAL_RIGHT: + if (text_pos > state->text_length) + return RE_ERROR_PARTIAL; + break; + } + + return RE_ERROR_FAILURE; +} + +/* Checks a fuzzy match of an item. */ +Py_LOCAL_INLINE(int) next_fuzzy_match_item(RE_State* state, RE_FuzzyData* data, + BOOL is_string, int step) { + Py_ssize_t new_pos; + + if (this_error_permitted(state, data->fuzzy_type)) { + switch (data->fuzzy_type) { + case RE_FUZZY_DEL: + /* Could a character at text_pos have been deleted? */ + if (is_string) + data->new_string_pos += step; + else + data->new_node = data->new_node->next_1.node; + return RE_ERROR_SUCCESS; + case RE_FUZZY_INS: + /* Could the character at text_pos have been inserted? */ + if (!data->permit_insertion) + return RE_ERROR_FAILURE; + + new_pos = data->new_text_pos + step; + if (state->slice_start <= new_pos && new_pos <= state->slice_end) { + data->new_text_pos = new_pos; + return RE_ERROR_SUCCESS; + } + + return check_fuzzy_partial(state, new_pos); + case RE_FUZZY_SUB: + /* Could the character at text_pos have been substituted? */ + new_pos = data->new_text_pos + step; + if (state->slice_start <= new_pos && new_pos <= state->slice_end) { + data->new_text_pos = new_pos; + if (is_string) + data->new_string_pos += step; + else + data->new_node = data->new_node->next_1.node; + return RE_ERROR_SUCCESS; + } + + return check_fuzzy_partial(state, new_pos); + } + } + + return RE_ERROR_FAILURE; +} + +/* Tries a fuzzy match of an item of width 0 or 1. */ +Py_LOCAL_INLINE(int) fuzzy_match_item(RE_SafeState* safe_state, BOOL search, + Py_ssize_t* text_pos, RE_Node** node, int step) { + RE_State* state; + RE_FuzzyData data; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + + state = safe_state->re_state; + + if (!any_error_permitted(state)) { + *node = NULL; + return RE_ERROR_SUCCESS; + } + + data.new_text_pos = *text_pos; + data.new_node = *node; + + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + if (step == 0) { + if (data.new_node->status & RE_STATUS_REVERSE) { + data.step = -1; + data.limit = state->slice_start; + } else { + data.step = 1; + data.limit = state->slice_end; + } + } else + data.step = step; + + /* Permit insertion except initially when searching (it's better just to + * start searching one character later). + */ + data.permit_insertion = !search || data.new_text_pos != + state->search_anchor; + + for (data.fuzzy_type = 0; data.fuzzy_type < RE_FUZZY_COUNT; + data.fuzzy_type++) { + int status; + + status = next_fuzzy_match_item(state, &data, FALSE, step); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + goto found; + } + + *node = NULL; + return RE_ERROR_SUCCESS; + +found: + if (!add_backtrack(safe_state, (*node)->op)) + return RE_ERROR_FAILURE; + bt_data = state->backtrack; + bt_data->fuzzy_item.position.text_pos = *text_pos; + bt_data->fuzzy_item.position.node = *node; + bt_data->fuzzy_item.fuzzy_type = (RE_INT8)data.fuzzy_type; + bt_data->fuzzy_item.step = (RE_INT8)step; + + ++fuzzy_info->counts[data.fuzzy_type]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + ++state->total_errors; + + *text_pos = data.new_text_pos; + *node = data.new_node; + + return RE_ERROR_SUCCESS; +} + +/* Retries a fuzzy match of a item of width 0 or 1. */ +Py_LOCAL_INLINE(int) retry_fuzzy_match_item(RE_SafeState* safe_state, BOOL + search, Py_ssize_t* text_pos, RE_Node** node, BOOL advance) { + RE_State* state; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + RE_FuzzyData data; + int step; + + state = safe_state->re_state; + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + bt_data = state->backtrack; + data.new_text_pos = bt_data->fuzzy_item.position.text_pos; + data.new_node = bt_data->fuzzy_item.position.node; + data.fuzzy_type = bt_data->fuzzy_item.fuzzy_type; + data.step = bt_data->fuzzy_item.step; + + if (data.fuzzy_type >= 0) { + --fuzzy_info->counts[data.fuzzy_type]; + --fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost -= values[RE_FUZZY_VAL_COST_BASE + + data.fuzzy_type]; + --state->total_errors; + } + + /* Permit insertion except initially when searching (it's better just to + * start searching one character later). + */ + data.permit_insertion = !search || data.new_text_pos != + state->search_anchor; + + step = advance ? data.step : 0; + + for (++data.fuzzy_type; data.fuzzy_type < RE_FUZZY_COUNT; + data.fuzzy_type++) { + int status; + + status = next_fuzzy_match_item(state, &data, FALSE, step); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + goto found; + } + + discard_backtrack(state); + *node = NULL; + return RE_ERROR_SUCCESS; + +found: + bt_data->fuzzy_item.fuzzy_type = (RE_INT8)data.fuzzy_type; + + ++fuzzy_info->counts[data.fuzzy_type]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + ++state->total_errors; + + *text_pos = data.new_text_pos; + *node = data.new_node; + + return RE_ERROR_SUCCESS; +} + +/* Tries a fuzzy insertion. */ +Py_LOCAL_INLINE(int) fuzzy_insert(RE_SafeState* safe_state, Py_ssize_t + text_pos, RE_Node* node) { + RE_State* state; + RE_BacktrackData* bt_data; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + + state = safe_state->re_state; + + /* No insertion or deletion. */ + if (!add_backtrack(safe_state, node->op)) + return RE_ERROR_FAILURE; + bt_data = state->backtrack; + bt_data->fuzzy_insert.position.text_pos = text_pos; + bt_data->fuzzy_insert.position.node = node; + bt_data->fuzzy_insert.count = 0; + bt_data->fuzzy_insert.too_few_errors = state->too_few_errors; + bt_data->fuzzy_insert.fuzzy_node = node; /* END_FUZZY node. */ + + /* Check whether there are too few errors. */ + fuzzy_info = &state->fuzzy_info; + + /* The node in this case is the END_FUZZY node. */ + values = node->values; + + if (fuzzy_info->counts[RE_FUZZY_DEL] < values[RE_FUZZY_VAL_MIN_DEL] || + fuzzy_info->counts[RE_FUZZY_INS] < values[RE_FUZZY_VAL_MIN_INS] || + fuzzy_info->counts[RE_FUZZY_SUB] < values[RE_FUZZY_VAL_MIN_SUB] || + fuzzy_info->counts[RE_FUZZY_ERR] < values[RE_FUZZY_VAL_MIN_ERR]) + state->too_few_errors = RE_ERROR_SUCCESS; + + return RE_ERROR_SUCCESS; +} + +/* Retries a fuzzy insertion. */ +Py_LOCAL_INLINE(int) retry_fuzzy_insert(RE_SafeState* safe_state, Py_ssize_t* + text_pos, RE_Node** node) { + RE_State* state; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + Py_ssize_t new_text_pos; + RE_Node* new_node; + int step; + Py_ssize_t limit; + RE_Node* fuzzy_node; + + state = safe_state->re_state; + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + bt_data = state->backtrack; + new_text_pos = bt_data->fuzzy_insert.position.text_pos; + new_node = bt_data->fuzzy_insert.position.node; + + if (new_node->status & RE_STATUS_REVERSE) { + step = -1; + limit = state->slice_start; + } else { + step = 1; + limit = state->slice_end; + } + + /* Could the character at text_pos have been inserted? */ + if (!this_error_permitted(state, RE_FUZZY_INS) || new_text_pos == limit) { + size_t count; + + count = bt_data->fuzzy_insert.count; + + fuzzy_info->counts[RE_FUZZY_INS] -= count; + fuzzy_info->counts[RE_FUZZY_ERR] -= count; + fuzzy_info->total_cost -= values[RE_FUZZY_VAL_INS_COST] * count; + state->total_errors -= count; + state->too_few_errors = bt_data->fuzzy_insert.too_few_errors; + + discard_backtrack(state); + *node = NULL; + return RE_ERROR_SUCCESS; + } + + ++bt_data->fuzzy_insert.count; + + ++fuzzy_info->counts[RE_FUZZY_INS]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_INS_COST]; + ++state->total_errors; + + /* Check whether there are too few errors. */ + state->too_few_errors = bt_data->fuzzy_insert.too_few_errors; + fuzzy_node = bt_data->fuzzy_insert.fuzzy_node; /* END_FUZZY node. */ + values = fuzzy_node->values; + if (fuzzy_info->counts[RE_FUZZY_DEL] < values[RE_FUZZY_VAL_MIN_DEL] || + fuzzy_info->counts[RE_FUZZY_INS] < values[RE_FUZZY_VAL_MIN_INS] || + fuzzy_info->counts[RE_FUZZY_SUB] < values[RE_FUZZY_VAL_MIN_SUB] || + fuzzy_info->counts[RE_FUZZY_ERR] < values[RE_FUZZY_VAL_MIN_ERR]) + state->too_few_errors = RE_ERROR_SUCCESS; + + *text_pos = new_text_pos + step * (Py_ssize_t)bt_data->fuzzy_insert.count; + *node = new_node; + + return RE_ERROR_SUCCESS; +} + +/* Tries a fuzzy match of a string. */ +Py_LOCAL_INLINE(int) fuzzy_match_string(RE_SafeState* safe_state, BOOL search, + Py_ssize_t* text_pos, RE_Node* node, Py_ssize_t* string_pos, BOOL* matched, + int step) { + RE_State* state; + RE_FuzzyData data; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + + state = safe_state->re_state; + + if (!any_error_permitted(state)) { + *matched = FALSE; + return RE_ERROR_SUCCESS; + } + + data.new_text_pos = *text_pos; + data.new_string_pos = *string_pos; + data.step = step; + + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + /* Permit insertion except initially when searching (it's better just to + * start searching one character later). + */ + data.permit_insertion = !search || data.new_text_pos != + state->search_anchor; + + for (data.fuzzy_type = 0; data.fuzzy_type < RE_FUZZY_COUNT; + data.fuzzy_type++) { + int status; + + status = next_fuzzy_match_item(state, &data, TRUE, data.step); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + goto found; + } + + *matched = FALSE; + return RE_ERROR_SUCCESS; + +found: + if (!add_backtrack(safe_state, node->op)) + return RE_ERROR_FAILURE; + bt_data = state->backtrack; + bt_data->fuzzy_string.position.text_pos = *text_pos; + bt_data->fuzzy_string.position.node = node; + bt_data->fuzzy_string.string_pos = *string_pos; + bt_data->fuzzy_string.fuzzy_type = (RE_INT8)data.fuzzy_type; + bt_data->fuzzy_string.step = (RE_INT8)step; + + ++fuzzy_info->counts[data.fuzzy_type]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + ++state->total_errors; + + *text_pos = data.new_text_pos; + *string_pos = data.new_string_pos; + *matched = TRUE; + + return RE_ERROR_SUCCESS; +} + +/* Retries a fuzzy match of a string. */ +Py_LOCAL_INLINE(int) retry_fuzzy_match_string(RE_SafeState* safe_state, BOOL + search, Py_ssize_t* text_pos, RE_Node** node, Py_ssize_t* string_pos, BOOL* + matched) { + RE_State* state; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + RE_FuzzyData data; + RE_Node* new_node; + + state = safe_state->re_state; + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + bt_data = state->backtrack; + data.new_text_pos = bt_data->fuzzy_string.position.text_pos; + new_node = bt_data->fuzzy_string.position.node; + data.new_string_pos = bt_data->fuzzy_string.string_pos; + data.fuzzy_type = bt_data->fuzzy_string.fuzzy_type; + data.step = bt_data->fuzzy_string.step; + + --fuzzy_info->counts[data.fuzzy_type]; + --fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost -= values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + --state->total_errors; + + /* Permit insertion except initially when searching (it's better just to + * start searching one character later). + */ + data.permit_insertion = !search || data.new_text_pos != + state->search_anchor; + + for (++data.fuzzy_type; data.fuzzy_type < RE_FUZZY_COUNT; + data.fuzzy_type++) { + int status; + + status = next_fuzzy_match_item(state, &data, TRUE, data.step); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + goto found; + } + + discard_backtrack(state); + *matched = FALSE; + return RE_ERROR_SUCCESS; + +found: + bt_data->fuzzy_string.fuzzy_type = (RE_INT8)data.fuzzy_type; + + ++fuzzy_info->counts[data.fuzzy_type]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + ++state->total_errors; + + *text_pos = data.new_text_pos; + *node = new_node; + *string_pos = data.new_string_pos; + *matched = TRUE; + + return RE_ERROR_SUCCESS; +} + +/* Checks a fuzzy match of a atring. */ +Py_LOCAL_INLINE(int) next_fuzzy_match_string_fld(RE_State* state, RE_FuzzyData* + data) { + int new_pos; + + if (this_error_permitted(state, data->fuzzy_type)) { + switch (data->fuzzy_type) { + case RE_FUZZY_DEL: + /* Could a character at text_pos have been deleted? */ + data->new_string_pos += data->step; + return RE_ERROR_SUCCESS; + case RE_FUZZY_INS: + /* Could the character at text_pos have been inserted? */ + if (!data->permit_insertion) + return RE_ERROR_FAILURE; + + new_pos = data->new_folded_pos + data->step; + if (0 <= new_pos && new_pos <= data->folded_len) { + data->new_folded_pos = new_pos; + return RE_ERROR_SUCCESS; + } + + return check_fuzzy_partial(state, new_pos); + case RE_FUZZY_SUB: + /* Could the character at text_pos have been substituted? */ + new_pos = data->new_folded_pos + data->step; + if (0 <= new_pos && new_pos <= data->folded_len) { + data->new_folded_pos = new_pos; + data->new_string_pos += data->step; + return RE_ERROR_SUCCESS; + } + + return check_fuzzy_partial(state, new_pos); + } + } + + return RE_ERROR_FAILURE; +} + +/* Tries a fuzzy match of a string, ignoring case. */ +Py_LOCAL_INLINE(int) fuzzy_match_string_fld(RE_SafeState* safe_state, BOOL + search, Py_ssize_t* text_pos, RE_Node* node, Py_ssize_t* string_pos, int* + folded_pos, int folded_len, BOOL* matched, int step) { + RE_State* state; + Py_ssize_t new_text_pos; + RE_FuzzyData data; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + + state = safe_state->re_state; + + if (!any_error_permitted(state)) { + *matched = FALSE; + return RE_ERROR_SUCCESS; + } + + new_text_pos = *text_pos; + data.new_string_pos = *string_pos; + data.new_folded_pos = *folded_pos; + data.folded_len = folded_len; + data.step = step; + + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + /* Permit insertion except initially when searching (it's better just to + * start searching one character later). + */ + data.permit_insertion = !search || new_text_pos != state->search_anchor; + if (step > 0) { + if (data.new_folded_pos != 0) + data.permit_insertion = RE_ERROR_SUCCESS; + } else { + if (data.new_folded_pos != folded_len) + data.permit_insertion = RE_ERROR_SUCCESS; + } + + for (data.fuzzy_type = 0; data.fuzzy_type < RE_FUZZY_COUNT; + data.fuzzy_type++) { + int status; + + status = next_fuzzy_match_string_fld(state, &data); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + goto found; + } + + *matched = FALSE; + return RE_ERROR_SUCCESS; + +found: + if (!add_backtrack(safe_state, node->op)) + return RE_ERROR_FAILURE; + bt_data = state->backtrack; + bt_data->fuzzy_string.position.text_pos = *text_pos; + bt_data->fuzzy_string.position.node = node; + bt_data->fuzzy_string.string_pos = *string_pos; + bt_data->fuzzy_string.folded_pos = (RE_INT8)(*folded_pos); + bt_data->fuzzy_string.folded_len = (RE_INT8)folded_len; + bt_data->fuzzy_string.fuzzy_type = (RE_INT8)data.fuzzy_type; + bt_data->fuzzy_string.step = (RE_INT8)step; + + ++fuzzy_info->counts[data.fuzzy_type]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + ++state->total_errors; + + *text_pos = new_text_pos; + *string_pos = data.new_string_pos; + *folded_pos = data.new_folded_pos; + *matched = TRUE; + + return RE_ERROR_SUCCESS; +} + +/* Retries a fuzzy match of a string, ignoring case. */ +Py_LOCAL_INLINE(int) retry_fuzzy_match_string_fld(RE_SafeState* safe_state, + BOOL search, Py_ssize_t* text_pos, RE_Node** node, Py_ssize_t* string_pos, + int* folded_pos, BOOL* matched) { + RE_State* state; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + Py_ssize_t new_text_pos; + RE_Node* new_node; + RE_FuzzyData data; + + state = safe_state->re_state; + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + bt_data = state->backtrack; + new_text_pos = bt_data->fuzzy_string.position.text_pos; + new_node = bt_data->fuzzy_string.position.node; + data.new_string_pos = bt_data->fuzzy_string.string_pos; + data.new_folded_pos = bt_data->fuzzy_string.folded_pos; + data.folded_len = bt_data->fuzzy_string.folded_len; + data.fuzzy_type = bt_data->fuzzy_string.fuzzy_type; + data.step = bt_data->fuzzy_string.step; + + --fuzzy_info->counts[data.fuzzy_type]; + --fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost -= values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + --state->total_errors; + + /* Permit insertion except initially when searching (it's better just to + * start searching one character later). + */ + data.permit_insertion = !search || new_text_pos != state->search_anchor; + if (data.step > 0) { + if (data.new_folded_pos != 0) + data.permit_insertion = RE_ERROR_SUCCESS; + } else { + if (data.new_folded_pos != bt_data->fuzzy_string.folded_len) + data.permit_insertion = RE_ERROR_SUCCESS; + } + + for (++data.fuzzy_type; data.fuzzy_type < RE_FUZZY_COUNT; + data.fuzzy_type++) { + int status; + + status = next_fuzzy_match_string_fld(state, &data); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + goto found; + } + + discard_backtrack(state); + *matched = FALSE; + return RE_ERROR_SUCCESS; + +found: + bt_data->fuzzy_string.fuzzy_type = (RE_INT8)data.fuzzy_type; + + ++fuzzy_info->counts[data.fuzzy_type]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + ++state->total_errors; + + *text_pos = new_text_pos; + *node = new_node; + *string_pos = data.new_string_pos; + *folded_pos = data.new_folded_pos; + *matched = TRUE; + + return RE_ERROR_SUCCESS; +} + +/* Checks a fuzzy match of a atring. */ +Py_LOCAL_INLINE(int) next_fuzzy_match_group_fld(RE_State* state, RE_FuzzyData* + data) { + int new_pos; + + if (this_error_permitted(state, data->fuzzy_type)) { + switch (data->fuzzy_type) { + case RE_FUZZY_DEL: + /* Could a character at text_pos have been deleted? */ + data->new_gfolded_pos += data->step; + return RE_ERROR_SUCCESS; + case RE_FUZZY_INS: + /* Could the character at text_pos have been inserted? */ + if (!data->permit_insertion) + return RE_ERROR_FAILURE; + + new_pos = data->new_folded_pos + data->step; + if (0 <= new_pos && new_pos <= data->folded_len) { + data->new_folded_pos = new_pos; + return RE_ERROR_SUCCESS; + } + + return check_fuzzy_partial(state, new_pos); + case RE_FUZZY_SUB: + /* Could the character at text_pos have been substituted? */ + new_pos = data->new_folded_pos + data->step; + if (0 <= new_pos && new_pos <= data->folded_len) { + data->new_folded_pos = new_pos; + data->new_gfolded_pos += data->step; + return RE_ERROR_SUCCESS; + } + + return check_fuzzy_partial(state, new_pos); + } + } + + return RE_ERROR_FAILURE; +} + +/* Tries a fuzzy match of a group reference, ignoring case. */ +Py_LOCAL_INLINE(int) fuzzy_match_group_fld(RE_SafeState* safe_state, BOOL + search, Py_ssize_t* text_pos, RE_Node* node, int* folded_pos, int folded_len, + Py_ssize_t* group_pos, int* gfolded_pos, int gfolded_len, BOOL* matched, int + step) { + RE_State* state; + Py_ssize_t new_text_pos; + RE_FuzzyData data; + Py_ssize_t new_group_pos; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + + state = safe_state->re_state; + + if (!any_error_permitted(state)) { + *matched = FALSE; + return RE_ERROR_SUCCESS; + } + + new_text_pos = *text_pos; + data.new_folded_pos = *folded_pos; + data.folded_len = folded_len; + new_group_pos = *group_pos; + data.new_gfolded_pos = *gfolded_pos; + data.step = step; + + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + /* Permit insertion except initially when searching (it's better just to + * start searching one character later). + */ + data.permit_insertion = !search || new_text_pos != state->search_anchor; + if (data.step > 0) { + if (data.new_folded_pos != 0) + data.permit_insertion = RE_ERROR_SUCCESS; + } else { + if (data.new_folded_pos != folded_len) + data.permit_insertion = RE_ERROR_SUCCESS; + } + + for (data.fuzzy_type = 0; data.fuzzy_type < RE_FUZZY_COUNT; + data.fuzzy_type++) { + int status; + + status = next_fuzzy_match_group_fld(state, &data); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + goto found; + } + + *matched = FALSE; + return RE_ERROR_SUCCESS; + +found: + if (!add_backtrack(safe_state, node->op)) + return RE_ERROR_FAILURE; + bt_data = state->backtrack; + bt_data->fuzzy_string.position.text_pos = *text_pos; + bt_data->fuzzy_string.position.node = node; + bt_data->fuzzy_string.string_pos = *group_pos; + bt_data->fuzzy_string.folded_pos = (RE_INT8)(*folded_pos); + bt_data->fuzzy_string.folded_len = (RE_INT8)folded_len; + bt_data->fuzzy_string.gfolded_pos = (RE_INT8)(*gfolded_pos); + bt_data->fuzzy_string.gfolded_len = (RE_INT8)gfolded_len; + bt_data->fuzzy_string.fuzzy_type = (RE_INT8)data.fuzzy_type; + bt_data->fuzzy_string.step = (RE_INT8)step; + + ++fuzzy_info->counts[data.fuzzy_type]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + ++state->total_errors; + + *text_pos = new_text_pos; + *group_pos = new_group_pos; + *folded_pos = data.new_folded_pos; + *gfolded_pos = data.new_gfolded_pos; + *matched = TRUE; + + return RE_ERROR_SUCCESS; +} + +/* Retries a fuzzy match of a group reference, ignoring case. */ +Py_LOCAL_INLINE(int) retry_fuzzy_match_group_fld(RE_SafeState* safe_state, BOOL + search, Py_ssize_t* text_pos, RE_Node** node, int* folded_pos, Py_ssize_t* + group_pos, int* gfolded_pos, BOOL* matched) { + RE_State* state; + RE_FuzzyInfo* fuzzy_info; + RE_CODE* values; + RE_BacktrackData* bt_data; + Py_ssize_t new_text_pos; + RE_Node* new_node; + Py_ssize_t new_group_pos; + RE_FuzzyData data; + + state = safe_state->re_state; + fuzzy_info = &state->fuzzy_info; + values = fuzzy_info->node->values; + + bt_data = state->backtrack; + new_text_pos = bt_data->fuzzy_string.position.text_pos; + new_node = bt_data->fuzzy_string.position.node; + new_group_pos = bt_data->fuzzy_string.string_pos; + data.new_folded_pos = bt_data->fuzzy_string.folded_pos; + data.folded_len = bt_data->fuzzy_string.folded_len; + data.new_gfolded_pos = bt_data->fuzzy_string.gfolded_pos; + data.fuzzy_type = bt_data->fuzzy_string.fuzzy_type; + data.step = bt_data->fuzzy_string.step; + + --fuzzy_info->counts[data.fuzzy_type]; + --fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost -= values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + --state->total_errors; + + /* Permit insertion except initially when searching (it's better just to + * start searching one character later). + */ + data.permit_insertion = !search || new_text_pos != state->search_anchor || + data.new_folded_pos != bt_data->fuzzy_string.folded_len; + + for (++data.fuzzy_type; data.fuzzy_type < RE_FUZZY_COUNT; + data.fuzzy_type++) { + int status; + + status = next_fuzzy_match_group_fld(state, &data); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + goto found; + } + + discard_backtrack(state); + *matched = FALSE; + return RE_ERROR_SUCCESS; + +found: + bt_data->fuzzy_string.fuzzy_type = (RE_INT8)data.fuzzy_type; + + ++fuzzy_info->counts[data.fuzzy_type]; + ++fuzzy_info->counts[RE_FUZZY_ERR]; + fuzzy_info->total_cost += values[RE_FUZZY_VAL_COST_BASE + data.fuzzy_type]; + ++state->total_errors; + + *text_pos = new_text_pos; + *node = new_node; + *group_pos = new_group_pos; + *folded_pos = data.new_folded_pos; + *gfolded_pos = data.new_gfolded_pos; + *matched = TRUE; + + return RE_ERROR_SUCCESS; +} + +/* Locates the required string, if there's one. */ +Py_LOCAL_INLINE(Py_ssize_t) locate_required_string(RE_SafeState* safe_state, + BOOL search) { + RE_State* state; + PatternObject* pattern; + Py_ssize_t found_pos; + Py_ssize_t end_pos; + + state = safe_state->re_state; + pattern = state->pattern; + + if (!pattern->req_string) + /* There isn't a required string, so start matching from the current + * position. + */ + return state->text_pos; + + /* Search for the required string and calculate where to start matching. */ + switch (pattern->req_string->op) { + case RE_OP_STRING: + { + BOOL is_partial; + Py_ssize_t limit; + + if (search || pattern->req_offset < 0) + limit = state->slice_end; + else { + limit = state->slice_start + pattern->req_offset + + (Py_ssize_t)pattern->req_string->value_count; + if (limit > state->slice_end || limit < 0) + limit = state->slice_end; + } + + if (state->req_pos < 0 || state->text_pos > state->req_pos) + /* First time or already passed it. */ + found_pos = string_search(safe_state, pattern->req_string, + state->text_pos, limit, &is_partial); + else { + found_pos = state->req_pos; + is_partial = FALSE; + } + + if (found_pos < 0) + /* The required string wasn't found. */ + return -1; + + if (!is_partial) { + /* Record where the required string matched. */ + state->req_pos = found_pos; + state->req_end = found_pos + + (Py_ssize_t)pattern->req_string->value_count; + } + + if (pattern->req_offset >= 0) { + /* Step back from the required string to where we should start + * matching. + */ + found_pos -= pattern->req_offset; + if (found_pos >= state->text_pos) + return found_pos; + } + break; + } + case RE_OP_STRING_FLD: + { + BOOL is_partial; + Py_ssize_t limit; + + if (search || pattern->req_offset < 0) + limit = state->slice_end; + else { + limit = state->slice_start + pattern->req_offset + + (Py_ssize_t)pattern->req_string->value_count; + if (limit > state->slice_end || limit < 0) + limit = state->slice_end; + } + + if (state->req_pos < 0 || state->text_pos > state->req_pos) + /* First time or already passed it. */ + found_pos = string_search_fld(safe_state, pattern->req_string, + state->text_pos, limit, &end_pos, &is_partial); + else { + found_pos = state->req_pos; + is_partial = FALSE; + } + + if (found_pos < 0) + /* The required string wasn't found. */ + return -1; + + if (!is_partial) { + /* Record where the required string matched. */ + state->req_pos = found_pos; + state->req_end = end_pos; + } + + if (pattern->req_offset >= 0) { + /* Step back from the required string to where we should start + * matching. + */ + found_pos -= pattern->req_offset; + if (found_pos >= state->text_pos) + return found_pos; + } + break; + } + case RE_OP_STRING_FLD_REV: + { + BOOL is_partial; + Py_ssize_t limit; + + if (search || pattern->req_offset < 0) + limit = state->slice_start; + else { + limit = state->slice_end - pattern->req_offset - + (Py_ssize_t)pattern->req_string->value_count; + if (limit < state->slice_start) + limit = state->slice_start; + } + + if (state->req_pos < 0 || state->text_pos < state->req_pos) + /* First time or already passed it. */ + found_pos = string_search_fld_rev(safe_state, pattern->req_string, + state->text_pos, limit, &end_pos, &is_partial); + else { + found_pos = state->req_pos; + is_partial = FALSE; + } + + if (found_pos < 0) + /* The required string wasn't found. */ + return -1; + + if (!is_partial) { + /* Record where the required string matched. */ + state->req_pos = found_pos; + state->req_end = end_pos; + } + + if (pattern->req_offset >= 0) { + /* Step back from the required string to where we should start + * matching. + */ + found_pos += pattern->req_offset; + if (found_pos <= state->text_pos) + return found_pos; + } + break; + } + case RE_OP_STRING_IGN: + { + BOOL is_partial; + Py_ssize_t limit; + + if (search || pattern->req_offset < 0) + limit = state->slice_end; + else { + limit = state->slice_start + pattern->req_offset + + (Py_ssize_t)pattern->req_string->value_count; + if (limit > state->slice_end || limit < 0) + limit = state->slice_end; + } + + if (state->req_pos < 0 || state->text_pos > state->req_pos) + /* First time or already passed it. */ + found_pos = string_search_ign(safe_state, pattern->req_string, + state->text_pos, limit, &is_partial); + else { + found_pos = state->req_pos; + is_partial = FALSE; + } + + if (found_pos < 0) + /* The required string wasn't found. */ + return -1; + + if (!is_partial) { + /* Record where the required string matched. */ + state->req_pos = found_pos; + state->req_end = found_pos + + (Py_ssize_t)pattern->req_string->value_count; + } + + if (pattern->req_offset >= 0) { + /* Step back from the required string to where we should start + * matching. + */ + found_pos -= pattern->req_offset; + if (found_pos >= state->text_pos) + return found_pos; + } + break; + } + case RE_OP_STRING_IGN_REV: + { + BOOL is_partial; + Py_ssize_t limit; + + if (search || pattern->req_offset < 0) + limit = state->slice_start; + else { + limit = state->slice_end - pattern->req_offset - + (Py_ssize_t)pattern->req_string->value_count; + if (limit < state->slice_start) + limit = state->slice_start; + } + + if (state->req_pos < 0 || state->text_pos < state->req_pos) + /* First time or already passed it. */ + found_pos = string_search_ign_rev(safe_state, pattern->req_string, + state->text_pos, limit, &is_partial); + else { + found_pos = state->req_pos; + is_partial = FALSE; + } + + if (found_pos < 0) + /* The required string wasn't found. */ + return -1; + + if (!is_partial) { + /* Record where the required string matched. */ + state->req_pos = found_pos; + state->req_end = found_pos - + (Py_ssize_t)pattern->req_string->value_count; + } + + if (pattern->req_offset >= 0) { + /* Step back from the required string to where we should start + * matching. + */ + found_pos += pattern->req_offset; + if (found_pos <= state->text_pos) + return found_pos; + } + break; + } + case RE_OP_STRING_REV: + { + BOOL is_partial; + Py_ssize_t limit; + + if (search || pattern->req_offset < 0) + limit = state->slice_start; + else { + limit = state->slice_end - pattern->req_offset - + (Py_ssize_t)pattern->req_string->value_count; + if (limit < state->slice_start) + limit = state->slice_start; + } + + if (state->req_pos < 0 || state->text_pos < state->req_pos) + /* First time or already passed it. */ + found_pos = string_search_rev(safe_state, pattern->req_string, + state->text_pos, limit, &is_partial); + else { + found_pos = state->req_pos; + is_partial = FALSE; + } + + if (found_pos < 0) + /* The required string wasn't found. */ + return -1; + + if (!is_partial) { + /* Record where the required string matched. */ + state->req_pos = found_pos; + state->req_end = found_pos - + (Py_ssize_t)pattern->req_string->value_count; + } + + if (pattern->req_offset >= 0) { + /* Step back from the required string to where we should start + * matching. + */ + found_pos += pattern->req_offset; + if (found_pos <= state->text_pos) + return found_pos; + } + break; + } + } + + /* Start matching from the current position. */ + return state->text_pos; +} + +/* Tries to match a character pattern. */ +Py_LOCAL_INLINE(int) match_one(RE_State* state, RE_Node* node, Py_ssize_t + text_pos) { + switch (node->op) { + case RE_OP_ANY: + return try_match_ANY(state, node, text_pos); + case RE_OP_ANY_ALL: + return try_match_ANY_ALL(state, node, text_pos); + case RE_OP_ANY_ALL_REV: + return try_match_ANY_ALL_REV(state, node, text_pos); + case RE_OP_ANY_REV: + return try_match_ANY_REV(state, node, text_pos); + case RE_OP_ANY_U: + return try_match_ANY_U(state, node, text_pos); + case RE_OP_ANY_U_REV: + return try_match_ANY_U_REV(state, node, text_pos); + case RE_OP_CHARACTER: + return try_match_CHARACTER(state, node, text_pos); + case RE_OP_CHARACTER_IGN: + return try_match_CHARACTER_IGN(state, node, text_pos); + case RE_OP_CHARACTER_IGN_REV: + return try_match_CHARACTER_IGN_REV(state, node, text_pos); + case RE_OP_CHARACTER_REV: + return try_match_CHARACTER_REV(state, node, text_pos); + case RE_OP_PROPERTY: + return try_match_PROPERTY(state, node, text_pos); + case RE_OP_PROPERTY_IGN: + return try_match_PROPERTY_IGN(state, node, text_pos); + case RE_OP_PROPERTY_IGN_REV: + return try_match_PROPERTY_IGN_REV(state, node, text_pos); + case RE_OP_PROPERTY_REV: + return try_match_PROPERTY_REV(state, node, text_pos); + case RE_OP_RANGE: + return try_match_RANGE(state, node, text_pos); + case RE_OP_RANGE_IGN: + return try_match_RANGE_IGN(state, node, text_pos); + case RE_OP_RANGE_IGN_REV: + return try_match_RANGE_IGN_REV(state, node, text_pos); + case RE_OP_RANGE_REV: + return try_match_RANGE_REV(state, node, text_pos); + case RE_OP_SET_DIFF: + case RE_OP_SET_INTER: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_UNION: + return try_match_SET(state, node, text_pos); + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_UNION_IGN: + return try_match_SET_IGN(state, node, text_pos); + case RE_OP_SET_DIFF_IGN_REV: + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_UNION_IGN_REV: + return try_match_SET_IGN_REV(state, node, text_pos); + case RE_OP_SET_DIFF_REV: + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION_REV: + return try_match_SET_REV(state, node, text_pos); + } + + return FALSE; +} + +/* Tests whether 2 nodes contains the same values. */ +Py_LOCAL_INLINE(BOOL) same_values(RE_Node* node_1, RE_Node* node_2) { + size_t i; + + if (node_1->value_count != node_2->value_count) + return FALSE; + + for (i = 0; i < node_1->value_count; i++) { + if (node_1->values[i] != node_2->values[i]) + return FALSE; + } + + return TRUE; +} + +/* Tests whether 2 nodes are equivalent (both string-like in the same way). */ +Py_LOCAL_INLINE(BOOL) equivalent_nodes(RE_Node* node_1, RE_Node* node_2) { + switch (node_1->op) { + case RE_OP_CHARACTER: + case RE_OP_STRING: + switch (node_2->op) { + case RE_OP_CHARACTER: + case RE_OP_STRING: + return same_values(node_1, node_2); + } + break; + case RE_OP_CHARACTER_IGN: + case RE_OP_STRING_IGN: + switch (node_2->op) { + case RE_OP_CHARACTER_IGN: + case RE_OP_STRING_IGN: + return same_values(node_1, node_2); + } + break; + case RE_OP_CHARACTER_IGN_REV: + case RE_OP_STRING_IGN_REV: + switch (node_2->op) { + case RE_OP_CHARACTER_IGN_REV: + case RE_OP_STRING_IGN_REV: + return same_values(node_1, node_2); + } + break; + case RE_OP_CHARACTER_REV: + case RE_OP_STRING_REV: + switch (node_2->op) { + case RE_OP_CHARACTER_REV: + case RE_OP_STRING_REV: + return same_values(node_1, node_2); + } + break; + } + + return FALSE; +} + +/* Prunes the backtracking. */ +Py_LOCAL_INLINE(void) prune_backtracking(RE_State* state) { + RE_AtomicBlock* current; + + current = state->current_atomic_block; + if (current && current->count > 0) { + /* In an atomic group or a lookaround. */ + RE_AtomicData* atomic; + + /* Discard any backtracking info from inside the atomic group or + * lookaround. + */ + atomic = ¤t->items[current->count - 1]; + state->current_backtrack_block = atomic->current_backtrack_block; + state->current_backtrack_block->count = atomic->backtrack_count; + } else { + /* In the outermost pattern. */ + while (state->current_backtrack_block->previous) + state->current_backtrack_block = + state->current_backtrack_block->previous; + + /* Keep the bottom FAILURE on the backtracking stack. */ + state->current_backtrack_block->count = 1; + } +} + +/* Saves the match as the best POSIX match (leftmost longest) found so far. */ +Py_LOCAL_INLINE(BOOL) save_best_match(RE_SafeState* safe_state) { + RE_State* state; + size_t group_count; + size_t g; + + state = safe_state->re_state; + + state->best_match_pos = state->match_pos; + state->best_text_pos = state->text_pos; + state->found_match = TRUE; + + memmove(state->best_fuzzy_counts, state->total_fuzzy_counts, + sizeof(state->total_fuzzy_counts)); + + group_count = state->pattern->true_group_count; + if (group_count == 0) + return TRUE; + + acquire_GIL(safe_state); + + if (!state->best_match_groups) { + /* Allocate storage for the groups of the best match. */ + state->best_match_groups = (RE_GroupData*)re_alloc(group_count * + sizeof(RE_GroupData)); + if (!state->best_match_groups) + goto error; + + memset(state->best_match_groups, 0, group_count * + sizeof(RE_GroupData)); + + for (g = 0; g < group_count; g++) { + RE_GroupData* best; + RE_GroupData* group; + + best = &state->best_match_groups[g]; + group = &state->groups[g]; + + best->capture_capacity = group->capture_capacity; + best->captures = (RE_GroupSpan*)re_alloc(best->capture_capacity * + sizeof(RE_GroupSpan)); + if (!best->captures) + goto error; + } + } + + /* Copy the group spans and captures. */ + for (g = 0; g < group_count; g++) { + RE_GroupData* best; + RE_GroupData* group; + + best = &state->best_match_groups[g]; + group = &state->groups[g]; + + best->span = group->span; + best->capture_count = group->capture_count; + + if (best->capture_count < best->capture_capacity) { + /* We need more space for the captures. */ + re_dealloc(best->captures); + best->captures = (RE_GroupSpan*)re_alloc(best->capture_capacity * + sizeof(RE_GroupSpan)); + if (!best->captures) + goto error; + } + + /* Copy the captures for this group. */ + memmove(best->captures, group->captures, group->capture_count * + sizeof(RE_GroupSpan)); + } + + release_GIL(safe_state); + + return TRUE; + +error: + release_GIL(safe_state); + + return FALSE; +} + +/* Restores the best match for a POSIX match (leftmost longest). */ +Py_LOCAL_INLINE(void) restore_best_match(RE_SafeState* safe_state) { + RE_State* state; + size_t group_count; + size_t g; + + state = safe_state->re_state; + + if (!state->found_match) + return; + + state->match_pos = state->best_match_pos; + state->text_pos = state->best_text_pos; + + memmove(state->total_fuzzy_counts, state->best_fuzzy_counts, + sizeof(state->total_fuzzy_counts)); + + group_count = state->pattern->true_group_count; + if (group_count == 0) + return; + + /* Copy the group spans and captures. */ + for (g = 0; g < group_count; g++) { + RE_GroupData* group; + RE_GroupData* best; + + group = &state->groups[g]; + best = &state->best_match_groups[g]; + + group->span = best->span; + group->capture_count = best->capture_count; + + /* Copy the captures for this group. */ + memmove(group->captures, best->captures, best->capture_count * + sizeof(RE_GroupSpan)); + } +} + +/* Checks whether the new match is better than the current match for a POSIX + * match (leftmost longest) and saves it if it is. + */ +Py_LOCAL_INLINE(BOOL) check_posix_match(RE_SafeState* safe_state) { + RE_State* state; + Py_ssize_t best_length; + Py_ssize_t new_length; + + state = safe_state->re_state; + + if (!state->found_match) + return save_best_match(safe_state); + + /* Check the overall match. */ + if (state->reverse) { + /* We're searching backwards. */ + best_length = state->match_pos - state->best_text_pos; + new_length = state->match_pos - state->text_pos; + } else { + /* We're searching forwards. */ + best_length = state->best_text_pos - state->match_pos; + new_length = state->text_pos - state->match_pos; + } + + if (new_length > best_length) + /* It's a longer match. */ + return save_best_match(safe_state); + + return TRUE; +} + +/* Performs a depth-first match or search from the context. */ +Py_LOCAL_INLINE(int) basic_match(RE_SafeState* safe_state, BOOL search) { + RE_State* state; + RE_EncodingTable* encoding; + RE_LocaleInfo* locale_info; + PatternObject* pattern; + RE_Node* start_node; + RE_NextNode start_pair; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + Py_ssize_t pattern_step; /* The overall step of the pattern (forwards or backwards). */ + Py_ssize_t string_pos; + BOOL do_search_start; + Py_ssize_t found_pos; + int status; + RE_Node* node; + int folded_pos; + int gfolded_pos; + TRACE(("<<basic_match>>\n")) + + state = safe_state->re_state; + encoding = state->encoding; + locale_info = state->locale_info; + pattern = state->pattern; + start_node = pattern->start_node; + + /* Look beyond any initial group node. */ + start_pair.node = start_node; + start_pair.test = pattern->start_test; + + /* Is the pattern anchored to the start or end of the string? */ + switch (start_pair.test->op) { + case RE_OP_END_OF_STRING: + if (state->reverse) { + /* Searching backwards. */ + if (state->text_pos != state->text_length) + return RE_ERROR_FAILURE; + + /* Don't bother to search further because it's anchored. */ + search = FALSE; + } + break; + case RE_OP_START_OF_STRING: + if (!state->reverse) { + /* Searching forwards. */ + if (state->text_pos != 0) + return RE_ERROR_FAILURE; + + /* Don't bother to search further because it's anchored. */ + search = FALSE; + } + break; + } + + char_at = state->char_at; + pattern_step = state->reverse ? -1 : 1; + string_pos = -1; + do_search_start = pattern->do_search_start; + state->fewest_errors = state->max_errors; + + if (do_search_start && pattern->req_string && + equivalent_nodes(start_pair.test, pattern->req_string)) + do_search_start = FALSE; + + /* Add a backtrack entry for failure. */ + if (!add_backtrack(safe_state, RE_OP_FAILURE)) + return RE_ERROR_BACKTRACKING; + +start_match: + /* If we're searching, advance along the string until there could be a + * match. + */ + if (pattern->pattern_call_ref >= 0) { + RE_GuardList* guard_list; + + guard_list = &state->group_call_guard_list[pattern->pattern_call_ref]; + guard_list->count = 0; + guard_list->last_text_pos = -1; + } + + /* Locate the required string, if there's one, unless this is a recursive + * call of 'basic_match'. + */ + if (!pattern->req_string) + found_pos = state->text_pos; + else { + found_pos = locate_required_string(safe_state, search); + if (found_pos < 0) + return RE_ERROR_FAILURE; + } + + if (search) { + state->text_pos = found_pos; + + if (do_search_start) { + RE_Position new_position; + +next_match_1: + /* 'search_start' will clear 'do_search_start' if it can't perform + * a fast search for the next possible match. This enables us to + * avoid the overhead of the call subsequently. + */ + status = search_start(safe_state, &start_pair, &new_position, 0); + if (status == RE_ERROR_PARTIAL) { + state->match_pos = state->text_pos; + return status; + } else if (status != RE_ERROR_SUCCESS) + return status; + + node = new_position.node; + state->text_pos = new_position.text_pos; + + if (node->op == RE_OP_SUCCESS) { + /* Must the match advance past its start? */ + if (state->text_pos != state->search_anchor || + !state->must_advance) + return RE_ERROR_SUCCESS; + + state->text_pos = state->match_pos + pattern_step; + goto next_match_1; + } + + /* 'do_search_start' may have been cleared. */ + do_search_start = pattern->do_search_start; + } else { + /* Avoiding 'search_start', which we've found can't perform a fast + * search for the next possible match. + */ + node = start_node; + +next_match_2: + if (state->reverse) { + if (state->text_pos < state->slice_start) { + if (state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + } else { + if (state->text_pos > state->slice_end) { + if (state-> partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + return RE_ERROR_FAILURE; + } + } + + state->match_pos = state->text_pos; + + if (node->op == RE_OP_SUCCESS) { + /* Must the match advance past its start? */ + if (state->text_pos != state->search_anchor || + !state->must_advance) { + BOOL success; + + if (state->match_all) { + /* We want to match all of the slice. */ + if (state->reverse) + success = state->text_pos == state->slice_start; + else + success = state->text_pos == state->slice_end; + } else + success = TRUE; + + if (success) + return RE_ERROR_SUCCESS; + } + + state->text_pos = state->match_pos + pattern_step; + goto next_match_2; + } + } + } else { + /* The start position is anchored to the current position. */ + if (found_pos != state->text_pos) + return RE_ERROR_FAILURE; + + node = start_node; + } + +advance: + /* The main matching loop. */ + for (;;) { + TRACE(("%d|", state->text_pos)) + + /* Should we abort the matching? */ + ++state->iterations; + + if (state->iterations == 0 && safe_check_signals(safe_state)) + return RE_ERROR_INTERRUPTED; + + switch (node->op) { + case RE_OP_ANY: /* Any character except a newline. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_ANY(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) { + ++state->text_pos; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_ANY_ALL: /* Any character at all. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_ANY_ALL(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) { + ++state->text_pos; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_ANY_ALL_REV: /* Any character at all, backwards. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_ANY_ALL_REV(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) { + --state->text_pos; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_ANY_REV: /* Any character except a newline, backwards. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_ANY_REV(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) { + --state->text_pos; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_ANY_U: /* Any character except a line separator. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_ANY_U(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) { + ++state->text_pos; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_ANY_U_REV: /* Any character except a line separator, backwards. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_ANY_U_REV(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) { + --state->text_pos; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_ATOMIC: /* Start of an atomic group. */ + { + RE_AtomicData* atomic; + TRACE(("%s\n", re_op_text[node->op])) + + if (!add_backtrack(safe_state, RE_OP_ATOMIC)) + return RE_ERROR_BACKTRACKING; + state->backtrack->atomic.too_few_errors = state->too_few_errors; + state->backtrack->atomic.capture_change = state->capture_change; + + atomic = push_atomic(safe_state); + if (!atomic) + return RE_ERROR_MEMORY; + atomic->backtrack_count = state->current_backtrack_block->count; + atomic->current_backtrack_block = state->current_backtrack_block; + atomic->is_lookaround = FALSE; + atomic->has_groups = (node->status & RE_STATUS_HAS_GROUPS) != 0; + atomic->has_repeats = (node->status & RE_STATUS_HAS_REPEATS) != 0; + + /* Save the groups and repeats. */ + if (atomic->has_groups && !push_groups(safe_state)) + return RE_ERROR_MEMORY; + + if (atomic->has_repeats && !push_repeats(safe_state)) + return RE_ERROR_MEMORY; + + node = node->next_1.node; + break; + } + case RE_OP_BOUNDARY: /* On a word boundary. */ + TRACE(("%s %d\n", re_op_text[node->op], node->match)) + + status = try_match_BOUNDARY(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_BRANCH: /* 2-way branch. */ + { + RE_Position next_position; + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match(state, &node->next_1, state->text_pos, + &next_position); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) { + if (!add_backtrack(safe_state, RE_OP_BRANCH)) + return RE_ERROR_BACKTRACKING; + state->backtrack->branch.position.node = + node->nonstring.next_2.node; + state->backtrack->branch.position.text_pos = state->text_pos; + + node = next_position.node; + state->text_pos = next_position.text_pos; + } else + node = node->nonstring.next_2.node; + break; + } + case RE_OP_CALL_REF: /* A group call reference. */ + { + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + if (!push_group_return(safe_state, NULL)) + return RE_ERROR_MEMORY; + + if (!add_backtrack(safe_state, RE_OP_CALL_REF)) + return RE_ERROR_BACKTRACKING; + + node = node->next_1.node; + break; + } + case RE_OP_CHARACTER: /* A character. */ + TRACE(("%s %d %d\n", re_op_text[node->op], node->match, + node->values[0])) + + if (state->text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + matches_CHARACTER(encoding, locale_info, node, + char_at(state->text, state->text_pos)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_CHARACTER_IGN: /* A character, ignoring case. */ + TRACE(("%s %d %d\n", re_op_text[node->op], node->match, + node->values[0])) + + if (state->text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + matches_CHARACTER_IGN(encoding, locale_info, node, + char_at(state->text, state->text_pos)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_CHARACTER_IGN_REV: /* A character, backwards, ignoring case. */ + TRACE(("%s %d %d\n", re_op_text[node->op], node->match, + node->values[0])) + + if (state->text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + matches_CHARACTER_IGN(encoding, locale_info, node, + char_at(state->text, state->text_pos - 1)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_CHARACTER_REV: /* A character, backwards. */ + TRACE(("%s %d %d\n", re_op_text[node->op], node->match, + node->values[0])) + + if (state->text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + matches_CHARACTER(encoding, locale_info, node, + char_at(state->text, state->text_pos - 1)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_CONDITIONAL: /* Start of a conditional subpattern. */ + { + RE_AtomicData* conditional; + TRACE(("%s %d\n", re_op_text[node->op], node->match)) + + if (!add_backtrack(safe_state, RE_OP_CONDITIONAL)) + return RE_ERROR_BACKTRACKING; + state->backtrack->lookaround.too_few_errors = + state->too_few_errors; + state->backtrack->lookaround.capture_change = + state->capture_change; + state->backtrack->lookaround.inside = TRUE; + state->backtrack->lookaround.node = node; + + conditional = push_atomic(safe_state); + if (!conditional) + return RE_ERROR_MEMORY; + conditional->backtrack_count = + state->current_backtrack_block->count; + conditional->current_backtrack_block = + state->current_backtrack_block; + conditional->slice_start = state->slice_start; + conditional->slice_end = state->slice_end; + conditional->text_pos = state->text_pos; + conditional->node = node; + conditional->backtrack = state->backtrack; + conditional->is_lookaround = TRUE; + conditional->has_groups = (node->status & RE_STATUS_HAS_GROUPS) != + 0; + conditional->has_repeats = (node->status & RE_STATUS_HAS_REPEATS) + != 0; + + /* Save the groups and repeats. */ + if (conditional->has_groups && !push_groups(safe_state)) + return RE_ERROR_MEMORY; + + if (conditional->has_repeats && !push_repeats(safe_state)) + return RE_ERROR_MEMORY; + + conditional->saved_groups = state->current_saved_groups; + conditional->saved_repeats = state->current_saved_repeats; + + state->slice_start = 0; + state->slice_end = state->text_length; + + node = node->next_1.node; + break; + } + case RE_OP_DEFAULT_BOUNDARY: /* On a default word boundary. */ + TRACE(("%s %d\n", re_op_text[node->op], node->match)) + + status = try_match_DEFAULT_BOUNDARY(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_DEFAULT_END_OF_WORD: /* At the default end of a word. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_DEFAULT_END_OF_WORD(state, node, + state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_DEFAULT_START_OF_WORD: /* At the default start of a word. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_DEFAULT_START_OF_WORD(state, node, + state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_END_ATOMIC: /* End of an atomic group. */ + { + RE_AtomicData* atomic; + + /* Discard any backtracking info from inside the atomic group. */ + atomic = top_atomic(safe_state); + state->current_backtrack_block = atomic->current_backtrack_block; + state->current_backtrack_block->count = atomic->backtrack_count; + + node = node->next_1.node; + break; + } + case RE_OP_END_CONDITIONAL: /* End of a conditional subpattern. */ + { + RE_AtomicData* conditional; + + conditional = pop_atomic(safe_state); + while (!conditional->is_lookaround) { + if (conditional->has_repeats) + drop_repeats(state); + + if (conditional->has_groups) + drop_groups(state); + + conditional = pop_atomic(safe_state); + } + state->text_pos = conditional->text_pos; + state->slice_end = conditional->slice_end; + state->slice_start = conditional->slice_start; + + /* Discard any backtracking info from inside the lookaround. */ + state->current_backtrack_block = + conditional->current_backtrack_block; + state->current_backtrack_block->count = + conditional->backtrack_count; + state->current_saved_groups = conditional->saved_groups; + state->current_saved_repeats = conditional->saved_repeats; + + /* It's a positive lookaround that's succeeded. We're now going to + * leave the lookaround. + */ + conditional->backtrack->lookaround.inside = FALSE; + + if (conditional->node->match) { + /* It's a positive lookaround that's succeeded. + * + * Go to the 'true' branch. + */ + node = node->next_1.node; + } else { + /* It's a negative lookaround that's succeeded. + * + * Go to the 'false' branch. + */ + node = node->nonstring.next_2.node; + } + break; + } + case RE_OP_END_FUZZY: /* End of fuzzy matching. */ + TRACE(("%s\n", re_op_text[node->op])) + + if (!fuzzy_insert(safe_state, state->text_pos, node)) + return RE_ERROR_BACKTRACKING; + + /* If there were too few errors, in the fuzzy section, try again. + */ + if (state->too_few_errors) { + state->too_few_errors = FALSE; + goto backtrack; + } + + state->total_fuzzy_counts[RE_FUZZY_SUB] += + state->fuzzy_info.counts[RE_FUZZY_SUB]; + state->total_fuzzy_counts[RE_FUZZY_INS] += + state->fuzzy_info.counts[RE_FUZZY_INS]; + state->total_fuzzy_counts[RE_FUZZY_DEL] += + state->fuzzy_info.counts[RE_FUZZY_DEL]; + + node = node->next_1.node; + break; + case RE_OP_END_GREEDY_REPEAT: /* End of a greedy repeat. */ + { + RE_CODE index; + RE_RepeatData* rp_data; + BOOL changed; + BOOL try_body; + int body_status; + RE_Position next_body_position; + BOOL try_tail; + int tail_status; + RE_Position next_tail_position; + RE_BacktrackData* bt_data; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Repeat indexes are 0-based. */ + index = node->values[0]; + rp_data = &state->repeats[index]; + + /* The body has matched successfully at this position. */ + if (!guard_repeat(safe_state, index, rp_data->start, + RE_STATUS_BODY, FALSE)) + return RE_ERROR_MEMORY; + + ++rp_data->count; + + /* Have we advanced through the text or has a capture group change? + */ + changed = rp_data->capture_change != state->capture_change || + state->text_pos != rp_data->start; + + /* The counts are of type size_t, so the format needs to specify + * that. + */ + TRACE(("min is %" PY_FORMAT_SIZE_T "u, max is %" PY_FORMAT_SIZE_T + "u, count is %" PY_FORMAT_SIZE_T "u\n", node->values[1], + node->values[2], rp_data->count)) + + /* Could the body or tail match? */ + try_body = changed && (rp_data->count < node->values[2] || + ~node->values[2] == 0) && !is_repeat_guarded(safe_state, index, + state->text_pos, RE_STATUS_BODY); + if (try_body) { + body_status = try_match(state, &node->next_1, state->text_pos, + &next_body_position); + if (body_status < 0) + return body_status; + + if (body_status == RE_ERROR_FAILURE) + try_body = FALSE; + } else + body_status = RE_ERROR_FAILURE; + + try_tail = (!changed || rp_data->count >= node->values[1]) && + !is_repeat_guarded(safe_state, index, state->text_pos, + RE_STATUS_TAIL); + if (try_tail) { + tail_status = try_match(state, &node->nonstring.next_2, + state->text_pos, &next_tail_position); + if (tail_status < 0) + return tail_status; + + if (tail_status == RE_ERROR_FAILURE) + try_tail = FALSE; + } else + tail_status = RE_ERROR_FAILURE; + + if (!try_body && !try_tail) { + /* Neither the body nor the tail could match. */ + --rp_data->count; + goto backtrack; + } + + if (body_status < 0 || (body_status == 0 && tail_status < 0)) + return RE_ERROR_PARTIAL; + + /* Record info in case we backtrack into the body. */ + if (!add_backtrack(safe_state, RE_OP_BODY_END)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.index = index; + bt_data->repeat.count = rp_data->count - 1; + bt_data->repeat.start = rp_data->start; + bt_data->repeat.capture_change = rp_data->capture_change; + + if (try_body) { + /* Both the body and the tail could match. */ + if (try_tail) { + /* The body takes precedence. If the body fails to match + * then we want to try the tail before backtracking + * further. + */ + + /* Record backtracking info for matching the tail. */ + if (!add_backtrack(safe_state, RE_OP_MATCH_TAIL)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.position = next_tail_position; + bt_data->repeat.index = index; + bt_data->repeat.count = rp_data->count; + bt_data->repeat.start = rp_data->start; + bt_data->repeat.capture_change = rp_data->capture_change; + bt_data->repeat.text_pos = state->text_pos; + } + + /* Record backtracking info in case the body fails to match. */ + if (!add_backtrack(safe_state, RE_OP_BODY_START)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.index = index; + bt_data->repeat.text_pos = state->text_pos; + + rp_data->capture_change = state->capture_change; + rp_data->start = state->text_pos; + + /* Advance into the body. */ + node = next_body_position.node; + state->text_pos = next_body_position.text_pos; + } else { + /* Only the tail could match. */ + + /* Advance into the tail. */ + node = next_tail_position.node; + state->text_pos = next_tail_position.text_pos; + } + break; + } + case RE_OP_END_GROUP: /* End of a capture group. */ + { + RE_CODE private_index; + RE_CODE public_index; + RE_GroupData* group; + RE_BacktrackData* bt_data; + TRACE(("%s %d\n", re_op_text[node->op], node->values[1])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + */ + private_index = node->values[0]; + public_index = node->values[1]; + group = &state->groups[private_index - 1]; + + if (!add_backtrack(safe_state, RE_OP_END_GROUP)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->group.private_index = private_index; + bt_data->group.public_index = public_index; + bt_data->group.text_pos = group->span.end; + bt_data->group.capture = (BOOL)node->values[2]; + bt_data->group.current_capture = group->current_capture; + + if (pattern->group_info[private_index - 1].referenced && + group->span.end != state->text_pos) + ++state->capture_change; + group->span.end = state->text_pos; + + /* Save the capture? */ + if (node->values[2]) { + group->current_capture = (Py_ssize_t)group->capture_count; + if (!save_capture(safe_state, private_index, public_index)) + return RE_ERROR_MEMORY; + } + + node = node->next_1.node; + break; + } + case RE_OP_END_LAZY_REPEAT: /* End of a lazy repeat. */ + { + RE_CODE index; + RE_RepeatData* rp_data; + BOOL changed; + BOOL try_body; + int body_status; + RE_Position next_body_position; + BOOL try_tail; + int tail_status; + RE_Position next_tail_position; + RE_BacktrackData* bt_data; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Repeat indexes are 0-based. */ + index = node->values[0]; + rp_data = &state->repeats[index]; + + /* The body has matched successfully at this position. */ + if (!guard_repeat(safe_state, index, rp_data->start, + RE_STATUS_BODY, FALSE)) + return RE_ERROR_MEMORY; + + ++rp_data->count; + + /* Have we advanced through the text or has a capture group change? + */ + changed = rp_data->capture_change != state->capture_change || + state->text_pos != rp_data->start; + + /* The counts are of type size_t, so the format needs to specify + * that. + */ + TRACE(("min is %" PY_FORMAT_SIZE_T "u, max is %" PY_FORMAT_SIZE_T + "u, count is %" PY_FORMAT_SIZE_T "u\n", node->values[1], + node->values[2], rp_data->count)) + + /* Could the body or tail match? */ + try_body = changed && (rp_data->count < node->values[2] || + ~node->values[2] == 0) && !is_repeat_guarded(safe_state, index, + state->text_pos, RE_STATUS_BODY); + if (try_body) { + body_status = try_match(state, &node->next_1, state->text_pos, + &next_body_position); + if (body_status < 0) + return body_status; + + if (body_status == RE_ERROR_FAILURE) + try_body = FALSE; + } else + body_status = RE_ERROR_FAILURE; + + try_tail = (!changed || rp_data->count >= node->values[1]); + if (try_tail) { + tail_status = try_match(state, &node->nonstring.next_2, + state->text_pos, &next_tail_position); + if (tail_status < 0) + return tail_status; + + if (tail_status == RE_ERROR_FAILURE) + try_tail = FALSE; + } else + tail_status = RE_ERROR_FAILURE; + + if (!try_body && !try_tail) { + /* Neither the body nor the tail could match. */ + --rp_data->count; + goto backtrack; + } + + if (body_status < 0 || (body_status == 0 && tail_status < 0)) + return RE_ERROR_PARTIAL; + + /* Record info in case we backtrack into the body. */ + if (!add_backtrack(safe_state, RE_OP_BODY_END)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.index = index; + bt_data->repeat.count = rp_data->count - 1; + bt_data->repeat.start = rp_data->start; + bt_data->repeat.capture_change = rp_data->capture_change; + + if (try_body) { + /* Both the body and the tail could match. */ + if (try_tail) { + /* The tail takes precedence. If the tail fails to match + * then we want to try the body before backtracking + * further. + */ + + /* Record backtracking info for matching the body. */ + if (!add_backtrack(safe_state, RE_OP_MATCH_BODY)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.position = next_body_position; + bt_data->repeat.index = index; + bt_data->repeat.count = rp_data->count; + bt_data->repeat.start = rp_data->start; + bt_data->repeat.capture_change = rp_data->capture_change; + bt_data->repeat.text_pos = state->text_pos; + + /* Advance into the tail. */ + node = next_tail_position.node; + state->text_pos = next_tail_position.text_pos; + } else { + /* Only the body could match. */ + + /* Record backtracking info in case the body fails to + * match. + */ + if (!add_backtrack(safe_state, RE_OP_BODY_START)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.index = index; + bt_data->repeat.text_pos = state->text_pos; + + rp_data->capture_change = state->capture_change; + rp_data->start = state->text_pos; + + /* Advance into the body. */ + node = next_body_position.node; + state->text_pos = next_body_position.text_pos; + } + } else { + /* Only the tail could match. */ + + /* Advance into the tail. */ + node = next_tail_position.node; + state->text_pos = next_tail_position.text_pos; + } + break; + } + case RE_OP_END_LOOKAROUND: /* End of a lookaround subpattern. */ + { + RE_AtomicData* lookaround; + + lookaround = pop_atomic(safe_state); + while (!lookaround->is_lookaround) { + if (lookaround->has_repeats) + drop_repeats(state); + + if (lookaround->has_groups) + drop_groups(state); + + lookaround = pop_atomic(safe_state); + } + state->text_pos = lookaround->text_pos; + state->slice_end = lookaround->slice_end; + state->slice_start = lookaround->slice_start; + + /* Discard any backtracking info from inside the lookaround. */ + state->current_backtrack_block = + lookaround->current_backtrack_block; + state->current_backtrack_block->count = + lookaround->backtrack_count; + state->current_saved_groups = lookaround->saved_groups; + state->current_saved_repeats = lookaround->saved_repeats; + + if (lookaround->node->match) { + /* It's a positive lookaround that's succeeded. We're now going + * to leave the lookaround. + */ + lookaround->backtrack->lookaround.inside = FALSE; + + node = node->next_1.node; + } else { + /* It's a negative lookaround that's succeeded. The groups and + * certain flags may have changed. We need to restore them and + * then backtrack. + */ + if (lookaround->has_repeats) + pop_repeats(state); + + if (lookaround->has_groups) + pop_groups(state); + + state->too_few_errors = + lookaround->backtrack->lookaround.too_few_errors; + state->capture_change = + lookaround->backtrack->lookaround.capture_change; + + discard_backtrack(state); + goto backtrack; + } + break; + } + case RE_OP_END_OF_LINE: /* At the end of a line. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_END_OF_LINE(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_END_OF_LINE_U: /* At the end of a line. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_END_OF_LINE_U(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_END_OF_STRING: /* At the end of the string. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_END_OF_STRING(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_END_OF_STRING_LINE: /* At end of string or final newline. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_END_OF_STRING_LINE(state, node, + state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_END_OF_STRING_LINE_U: /* At end of string or final newline. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_END_OF_STRING_LINE_U(state, node, + state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_END_OF_WORD: /* At the end of a word. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_END_OF_WORD(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_FAILURE: /* Failure. */ + goto backtrack; + case RE_OP_FUZZY: /* Fuzzy matching. */ + { + RE_FuzzyInfo* fuzzy_info; + RE_BacktrackData* bt_data; + TRACE(("%s\n", re_op_text[node->op])) + + fuzzy_info = &state->fuzzy_info; + + /* Save the current fuzzy info. */ + if (!add_backtrack(safe_state, RE_OP_FUZZY)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + memmove(&bt_data->fuzzy.fuzzy_info, fuzzy_info, + sizeof(RE_FuzzyInfo)); + bt_data->fuzzy.index = node->values[0]; + bt_data->fuzzy.text_pos = state->text_pos; + + /* Initialise the new fuzzy info. */ + memset(fuzzy_info->counts, 0, 4 * sizeof(fuzzy_info->counts[0])); + fuzzy_info->total_cost = 0; + fuzzy_info->node = node; + + node = node->next_1.node; + break; + } + case RE_OP_GRAPHEME_BOUNDARY: /* On a grapheme boundary. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_GRAPHEME_BOUNDARY(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_GREEDY_REPEAT: /* Greedy repeat. */ + { + RE_CODE index; + RE_RepeatData* rp_data; + RE_BacktrackData* bt_data; + BOOL try_body; + int body_status; + RE_Position next_body_position; + BOOL try_tail; + int tail_status; + RE_Position next_tail_position; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Repeat indexes are 0-based. */ + index = node->values[0]; + rp_data = &state->repeats[index]; + + /* We might need to backtrack into the head, so save the current + * repeat. + */ + if (!add_backtrack(safe_state, RE_OP_GREEDY_REPEAT)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.index = index; + bt_data->repeat.count = rp_data->count; + bt_data->repeat.start = rp_data->start; + bt_data->repeat.capture_change = rp_data->capture_change; + bt_data->repeat.text_pos = state->text_pos; + + /* Initialise the new repeat. */ + rp_data->count = 0; + rp_data->start = state->text_pos; + rp_data->capture_change = state->capture_change; + + /* Could the body or tail match? */ + try_body = node->values[2] > 0 && !is_repeat_guarded(safe_state, + index, state->text_pos, RE_STATUS_BODY); + if (try_body) { + body_status = try_match(state, &node->next_1, state->text_pos, + &next_body_position); + if (body_status < 0) + return body_status; + + if (body_status == RE_ERROR_FAILURE) + try_body = FALSE; + } else + body_status = RE_ERROR_FAILURE; + + try_tail = node->values[1] == 0; + if (try_tail) { + tail_status = try_match(state, &node->nonstring.next_2, + state->text_pos, &next_tail_position); + if (tail_status < 0) + return tail_status; + + if (tail_status == RE_ERROR_FAILURE) + try_tail = FALSE; + } else + tail_status = RE_ERROR_FAILURE; + if (!try_body && !try_tail) + /* Neither the body nor the tail could match. */ + goto backtrack; + + if (body_status < 0 || (body_status == 0 && tail_status < 0)) + return RE_ERROR_PARTIAL; + + if (try_body) { + if (try_tail) { + /* Both the body and the tail could match, but the body + * takes precedence. If the body fails to match then we + * want to try the tail before backtracking further. + */ + + /* Record backtracking info for matching the tail. */ + if (!add_backtrack(safe_state, RE_OP_MATCH_TAIL)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.position = next_tail_position; + bt_data->repeat.index = index; + bt_data->repeat.count = rp_data->count; + bt_data->repeat.start = rp_data->start; + bt_data->repeat.capture_change = rp_data->capture_change; + bt_data->repeat.text_pos = state->text_pos; + } + + /* Advance into the body. */ + node = next_body_position.node; + state->text_pos = next_body_position.text_pos; + } else { + /* Only the tail could match. */ + + /* Advance into the tail. */ + node = next_tail_position.node; + state->text_pos = next_tail_position.text_pos; + } + break; + } + case RE_OP_GREEDY_REPEAT_ONE: /* Greedy repeat for one character. */ + { + RE_CODE index; + RE_RepeatData* rp_data; + size_t count; + BOOL is_partial; + BOOL match; + RE_BacktrackData* bt_data; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Repeat indexes are 0-based. */ + index = node->values[0]; + rp_data = &state->repeats[index]; + + if (is_repeat_guarded(safe_state, index, state->text_pos, + RE_STATUS_BODY)) + goto backtrack; + + /* Count how many times the character repeats, up to the maximum. + */ + count = count_one(state, node->nonstring.next_2.node, + state->text_pos, node->values[2], &is_partial); + if (is_partial) { + state->text_pos += (Py_ssize_t)count * node->step; + return RE_ERROR_PARTIAL; + } + + /* Unmatch until it's not guarded. */ + match = FALSE; + for (;;) { + if (count < node->values[1]) + /* The number of repeats is below the minimum. */ + break; + + if (!is_repeat_guarded(safe_state, index, state->text_pos + + (Py_ssize_t)count * node->step, RE_STATUS_TAIL)) { + /* It's not guarded at this position. */ + match = TRUE; + break; + } + + if (count == 0) + break; + + --count; + } + + if (!match) { + /* The repeat has failed to match at this position. */ + if (!guard_repeat(safe_state, index, state->text_pos, + RE_STATUS_BODY, TRUE)) + return RE_ERROR_MEMORY; + goto backtrack; + } + + if (count > node->values[1]) { + /* Record the backtracking info. */ + if (!add_backtrack(safe_state, RE_OP_GREEDY_REPEAT_ONE)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.position.node = node; + bt_data->repeat.index = index; + bt_data->repeat.text_pos = rp_data->start; + bt_data->repeat.count = rp_data->count; + + rp_data->start = state->text_pos; + rp_data->count = count; + } + + /* Advance into the tail. */ + state->text_pos += (Py_ssize_t)count * node->step; + node = node->next_1.node; + break; + } + case RE_OP_GROUP_CALL: /* Group call. */ + { + size_t index; + size_t g; + size_t r; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + index = node->values[0]; + + /* Save the capture groups and repeat guards. */ + if (!push_group_return(safe_state, node->next_1.node)) + return RE_ERROR_MEMORY; + + /* Clear the capture groups for the group call. They'll be restored + * on return. + */ + for (g = 0; g < state->pattern->true_group_count; g++) { + RE_GroupData* group; + + group = &state->groups[g]; + group->span.start = -1; + group->span.end = -1; + group->current_capture = -1; + } + + /* Clear the repeat guards for the group call. They'll be restored + * on return. + */ + for (r = 0; r < state->pattern->repeat_count; r++) { + RE_RepeatData* repeat; + + repeat = &state->repeats[r]; + repeat->body_guard_list.count = 0; + repeat->body_guard_list.last_text_pos = -1; + repeat->tail_guard_list.count = 0; + repeat->tail_guard_list.last_text_pos = -1; + } + + /* Call a group, skipping its CALL_REF node. */ + node = pattern->call_ref_info[index].node->next_1.node; + + if (!add_backtrack(safe_state, RE_OP_GROUP_CALL)) + return RE_ERROR_BACKTRACKING; + break; + } + case RE_OP_GROUP_EXISTS: /* Capture group exists. */ + { + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + * + * Check whether the captured text, if any, exists at this position + * in the string. + * + * A group index of 0, however, means that it's a DEFINE, which we + * should skip. + */ + if (node->values[0] == 0) + /* Skip past the body. */ + node = node->nonstring.next_2.node; + else { + RE_GroupData* group; + + group = &state->groups[node->values[0] - 1]; + if (group->current_capture >= 0) + /* The 'true' branch. */ + node = node->next_1.node; + else + /* The 'false' branch. */ + node = node->nonstring.next_2.node; + } + break; + } + case RE_OP_GROUP_RETURN: /* Group return. */ + { + RE_Node* return_node; + RE_BacktrackData* bt_data; + TRACE(("%s\n", re_op_text[node->op])) + + return_node = top_group_return(state); + + if (!add_backtrack(safe_state, RE_OP_GROUP_RETURN)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->group_call.node = return_node; + bt_data->group_call.capture_change = state->capture_change; + + if (return_node) { + /* The group was called. */ + node = return_node; + + /* Save the groups. */ + if (!push_groups(safe_state)) + return RE_ERROR_MEMORY; + + /* Save the repeats. */ + if (!push_repeats(safe_state)) + return RE_ERROR_MEMORY; + } else + /* The group was not called. */ + node = node->next_1.node; + + pop_group_return(state); + break; + } + case RE_OP_KEEP: /* Keep. */ + { + RE_BacktrackData* bt_data; + TRACE(("%s\n", re_op_text[node->op])) + + if (!add_backtrack(safe_state, RE_OP_KEEP)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->keep.match_pos = state->match_pos; + state->match_pos = state->text_pos; + node = node->next_1.node; + break; + } + case RE_OP_LAZY_REPEAT: /* Lazy repeat. */ + { + RE_CODE index; + RE_RepeatData* rp_data; + RE_BacktrackData* bt_data; + BOOL try_body; + int body_status; + RE_Position next_body_position; + BOOL try_tail; + int tail_status; + RE_Position next_tail_position; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Repeat indexes are 0-based. */ + index = node->values[0]; + rp_data = &state->repeats[index]; + + /* We might need to backtrack into the head, so save the current + * repeat. + */ + if (!add_backtrack(safe_state, RE_OP_LAZY_REPEAT)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.index = index; + bt_data->repeat.count = rp_data->count; + bt_data->repeat.start = rp_data->start; + bt_data->repeat.capture_change = rp_data->capture_change; + bt_data->repeat.text_pos = state->text_pos; + + /* Initialise the new repeat. */ + rp_data->count = 0; + rp_data->start = state->text_pos; + rp_data->capture_change = state->capture_change; + + /* Could the body or tail match? */ + try_body = node->values[2] > 0 && !is_repeat_guarded(safe_state, + index, state->text_pos, RE_STATUS_BODY); + if (try_body) { + body_status = try_match(state, &node->next_1, state->text_pos, + &next_body_position); + if (body_status < 0) + return body_status; + + if (body_status == RE_ERROR_FAILURE) + try_body = FALSE; + } else + body_status = RE_ERROR_FAILURE; + + try_tail = node->values[1] == 0; + if (try_tail) { + tail_status = try_match(state, &node->nonstring.next_2, + state->text_pos, &next_tail_position); + if (tail_status < 0) + return tail_status; + + if (tail_status == RE_ERROR_FAILURE) + try_tail = FALSE; + } else + tail_status = RE_ERROR_FAILURE; + + if (!try_body && !try_tail) + /* Neither the body nor the tail could match. */ + goto backtrack; + + if (body_status < 0 || (body_status == 0 && tail_status < 0)) + return RE_ERROR_PARTIAL; + + if (try_body) { + if (try_tail) { + /* Both the body and the tail could match, but the tail + * takes precedence. If the tail fails to match then we + * want to try the body before backtracking further. + */ + + /* Record backtracking info for matching the tail. */ + if (!add_backtrack(safe_state, RE_OP_MATCH_BODY)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.position = next_body_position; + bt_data->repeat.index = index; + bt_data->repeat.count = rp_data->count; + bt_data->repeat.start = rp_data->start; + bt_data->repeat.capture_change = rp_data->capture_change; + bt_data->repeat.text_pos = state->text_pos; + + /* Advance into the tail. */ + node = next_tail_position.node; + state->text_pos = next_tail_position.text_pos; + } else { + /* Advance into the body. */ + node = next_body_position.node; + state->text_pos = next_body_position.text_pos; + } + } else { + /* Only the tail could match. */ + + /* Advance into the tail. */ + node = next_tail_position.node; + state->text_pos = next_tail_position.text_pos; + } + break; + } + case RE_OP_LAZY_REPEAT_ONE: /* Lazy repeat for one character. */ + { + RE_CODE index; + RE_RepeatData* rp_data; + size_t count; + BOOL is_partial; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Repeat indexes are 0-based. */ + index = node->values[0]; + rp_data = &state->repeats[index]; + + if (is_repeat_guarded(safe_state, index, state->text_pos, + RE_STATUS_BODY)) + goto backtrack; + + /* Count how many times the character repeats, up to the minimum. + */ + count = count_one(state, node->nonstring.next_2.node, + state->text_pos, node->values[1], &is_partial); + if (is_partial) { + state->text_pos += (Py_ssize_t)count * node->step; + return RE_ERROR_PARTIAL; + } + + /* Have we matched at least the minimum? */ + if (count < node->values[1]) { + /* The repeat has failed to match at this position. */ + if (!guard_repeat(safe_state, index, state->text_pos, + RE_STATUS_BODY, TRUE)) + return RE_ERROR_MEMORY; + goto backtrack; + } + + if (count < node->values[2]) { + /* The match is shorter than the maximum, so we might need to + * backtrack the repeat to consume more. + */ + RE_BacktrackData* bt_data; + + /* Get the offset to the repeat values in the context. */ + rp_data = &state->repeats[index]; + if (!add_backtrack(safe_state, RE_OP_LAZY_REPEAT_ONE)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->repeat.position.node = node; + bt_data->repeat.index = index; + bt_data->repeat.text_pos = rp_data->start; + bt_data->repeat.count = rp_data->count; + + rp_data->start = state->text_pos; + rp_data->count = count; + } + + /* Advance into the tail. */ + state->text_pos += (Py_ssize_t)count * node->step; + node = node->next_1.node; + break; + } + case RE_OP_LOOKAROUND: /* Start of a lookaround subpattern. */ + { + RE_AtomicData* lookaround; + TRACE(("%s %d\n", re_op_text[node->op], node->match)) + + if (!add_backtrack(safe_state, RE_OP_LOOKAROUND)) + return RE_ERROR_BACKTRACKING; + state->backtrack->lookaround.too_few_errors = + state->too_few_errors; + state->backtrack->lookaround.capture_change = + state->capture_change; + state->backtrack->lookaround.inside = TRUE; + state->backtrack->lookaround.node = node; + + lookaround = push_atomic(safe_state); + if (!lookaround) + return RE_ERROR_MEMORY; + lookaround->backtrack_count = + state->current_backtrack_block->count; + lookaround->current_backtrack_block = + state->current_backtrack_block; + lookaround->slice_start = state->slice_start; + lookaround->slice_end = state->slice_end; + lookaround->text_pos = state->text_pos; + lookaround->node = node; + lookaround->backtrack = state->backtrack; + lookaround->is_lookaround = TRUE; + lookaround->has_groups = (node->status & RE_STATUS_HAS_GROUPS) != + 0; + lookaround->has_repeats = (node->status & RE_STATUS_HAS_REPEATS) != + 0; + + /* Save the groups and repeats. */ + if (lookaround->has_groups && !push_groups(safe_state)) + return RE_ERROR_MEMORY; + + if (lookaround->has_repeats && !push_repeats(safe_state)) + return RE_ERROR_MEMORY; + + lookaround->saved_groups = state->current_saved_groups; + lookaround->saved_repeats = state->current_saved_repeats; + + state->slice_start = 0; + state->slice_end = state->text_length; + + node = node->next_1.node; + break; + } + case RE_OP_PROPERTY: /* A property. */ + TRACE(("%s %d %d\n", re_op_text[node->op], node->match, + node->values[0])) + + if (state->text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + matches_PROPERTY(encoding, locale_info, node, + char_at(state->text, state->text_pos)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_PROPERTY_IGN: /* A property, ignoring case. */ + TRACE(("%s %d %d\n", re_op_text[node->op], node->match, + node->values[0])) + + if (state->text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + matches_PROPERTY_IGN(encoding, locale_info, node, + char_at(state->text, state->text_pos)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_PROPERTY_IGN_REV: /* A property, backwards, ignoring case. */ + TRACE(("%s %d %d\n", re_op_text[node->op], node->match, + node->values[0])) + + if (state->text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + matches_PROPERTY_IGN(encoding, locale_info, node, + char_at(state->text, state->text_pos - 1)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_PROPERTY_REV: /* A property, backwards. */ + TRACE(("%s %d %d\n", re_op_text[node->op], node->match, + node->values[0])) + + if (state->text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + matches_PROPERTY(encoding, locale_info, node, + char_at(state->text, state->text_pos - 1)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_PRUNE: /* Prune the backtracking. */ + TRACE(("%s\n", re_op_text[node->op])) + + prune_backtracking(state); + + node = node->next_1.node; + break; + case RE_OP_RANGE: /* A range. */ + TRACE(("%s %d %d %d\n", re_op_text[node->op], node->match, + node->values[0], node->values[1])) + + if (state->text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && matches_RANGE(encoding, + locale_info, node, char_at(state->text, state->text_pos)) == + node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_RANGE_IGN: /* A range, ignoring case. */ + TRACE(("%s %d %d %d\n", re_op_text[node->op], node->match, + node->values[0], node->values[1])) + + if (state->text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + matches_RANGE_IGN(encoding, locale_info, node, + char_at(state->text, state->text_pos)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_RANGE_IGN_REV: /* A range, backwards, ignoring case. */ + TRACE(("%s %d %d %d\n", re_op_text[node->op], node->match, + node->values[0], node->values[1])) + + if (state->text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + matches_RANGE_IGN(encoding, locale_info, node, + char_at(state->text, state->text_pos - 1)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_RANGE_REV: /* A range, backwards. */ + TRACE(("%s %d %d %d\n", re_op_text[node->op], node->match, + node->values[0], node->values[1])) + + if (state->text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && matches_RANGE(encoding, + locale_info, node, char_at(state->text, state->text_pos - 1)) == + node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_REF_GROUP: /* Reference to a capture group. */ + { + RE_GroupData* group; + RE_GroupSpan* span; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + * + * Check whether the captured text, if any, exists at this position + * in the string. + */ + + /* Did the group capture anything? */ + group = &state->groups[node->values[0] - 1]; + if (group->current_capture < 0) + goto backtrack; + + span = &group->captures[group->current_capture]; + + if (string_pos < 0) + string_pos = span->start; + + /* Try comparing. */ + while (string_pos < span->end) { + if (state->text_pos >= state->text_length && + state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + same_char(char_at(state->text, state->text_pos), + char_at(state->text, string_pos))) { + ++string_pos; + ++state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string(safe_state, search, + &state->text_pos, node, &string_pos, &matched, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + } + + string_pos = -1; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_REF_GROUP_FLD: /* Reference to a capture group, ignoring case. */ + { + RE_GroupData* group; + RE_GroupSpan* span; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, + Py_UCS4* folded); + int folded_len; + int gfolded_len; + Py_UCS4 folded[RE_MAX_FOLDED]; + Py_UCS4 gfolded[RE_MAX_FOLDED]; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + * + * Check whether the captured text, if any, exists at this position + * in the string. + */ + + /* Did the group capture anything? */ + group = &state->groups[node->values[0] - 1]; + if (group->current_capture < 0) + goto backtrack; + + span = &group->captures[group->current_capture]; + + full_case_fold = encoding->full_case_fold; + + if (string_pos < 0) { + string_pos = span->start; + folded_pos = 0; + folded_len = 0; + gfolded_pos = 0; + gfolded_len = 0; + } else { + folded_len = full_case_fold(locale_info, char_at(state->text, + state->text_pos), folded); + gfolded_len = full_case_fold(locale_info, char_at(state->text, + string_pos), gfolded); + } + + /* Try comparing. */ + while (string_pos < span->end) { + /* Case-fold at current position in text. */ + if (folded_pos >= folded_len) { + if (state->text_pos >= state->text_length && + state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end) + folded_len = full_case_fold(locale_info, + char_at(state->text, state->text_pos), folded); + else + folded_len = 0; + + folded_pos = 0; + } + + /* Case-fold at current position in group. */ + if (gfolded_pos >= gfolded_len) { + gfolded_len = full_case_fold(locale_info, + char_at(state->text, string_pos), gfolded); + gfolded_pos = 0; + } + + if (folded_pos < folded_len && folded[folded_pos] == + gfolded[gfolded_pos]) { + ++folded_pos; + ++gfolded_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_group_fld(safe_state, search, + &state->text_pos, node, &folded_pos, folded_len, + &string_pos, &gfolded_pos, gfolded_len, &matched, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + + if (folded_pos >= folded_len && folded_len > 0) + ++state->text_pos; + + if (gfolded_pos >= gfolded_len) + ++string_pos; + } + + string_pos = -1; + + if (folded_pos < folded_len || gfolded_pos < gfolded_len) + goto backtrack; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_REF_GROUP_FLD_REV: /* Reference to a capture group, ignoring case. */ + { + RE_GroupData* group; + RE_GroupSpan* span; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, + Py_UCS4* folded); + int folded_len; + int gfolded_len; + Py_UCS4 folded[RE_MAX_FOLDED]; + Py_UCS4 gfolded[RE_MAX_FOLDED]; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + * + * Check whether the captured text, if any, exists at this position + * in the string. + */ + + /* Did the group capture anything? */ + group = &state->groups[node->values[0] - 1]; + if (group->current_capture < 0) + goto backtrack; + + span = &group->captures[group->current_capture]; + + full_case_fold = encoding->full_case_fold; + + if (string_pos < 0) { + string_pos = span->end; + folded_pos = 0; + folded_len = 0; + gfolded_pos = 0; + gfolded_len = 0; + } else { + folded_len = full_case_fold(locale_info, char_at(state->text, + state->text_pos - 1), folded); + gfolded_len = full_case_fold(locale_info, char_at(state->text, + string_pos - 1), gfolded); + } + + /* Try comparing. */ + while (string_pos > span->start) { + /* Case-fold at current position in text. */ + if (folded_pos <= 0) { + if (state->text_pos <= 0 && state->partial_side == + RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start) + folded_len = full_case_fold(locale_info, + char_at(state->text, state->text_pos - 1), folded); + else + folded_len = 0; + + folded_pos = folded_len; + } + + /* Case-fold at current position in group. */ + if (gfolded_pos <= 0) { + gfolded_len = full_case_fold(locale_info, + char_at(state->text, string_pos - 1), gfolded); + gfolded_pos = gfolded_len; + } + + if (folded_pos > 0 && folded[folded_pos - 1] == + gfolded[gfolded_pos - 1]) { + --folded_pos; + --gfolded_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_group_fld(safe_state, search, + &state->text_pos, node, &folded_pos, folded_len, + &string_pos, &gfolded_pos, gfolded_len, &matched, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + + if (folded_pos <= 0 && folded_len > 0) + --state->text_pos; + + if (gfolded_pos <= 0) + --string_pos; + } + + string_pos = -1; + + if (folded_pos > 0 || gfolded_pos > 0) + goto backtrack; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_REF_GROUP_IGN: /* Reference to a capture group, ignoring case. */ + { + RE_GroupData* group; + RE_GroupSpan* span; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + * + * Check whether the captured text, if any, exists at this position + * in the string. + */ + + /* Did the group capture anything? */ + group = &state->groups[node->values[0] - 1]; + if (group->current_capture < 0) + goto backtrack; + + span = &group->captures[group->current_capture]; + + if (string_pos < 0) + string_pos = span->start; + + /* Try comparing. */ + while (string_pos < span->end) { + if (state->text_pos >= state->text_length && + state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + same_char_ign(encoding, locale_info, char_at(state->text, + state->text_pos), char_at(state->text, string_pos))) { + ++string_pos; + ++state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string(safe_state, search, + &state->text_pos, node, &string_pos, &matched, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + } + + string_pos = -1; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_REF_GROUP_IGN_REV: /* Reference to a capture group, ignoring case. */ + { + RE_GroupData* group; + RE_GroupSpan* span; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + * + * Check whether the captured text, if any, exists at this position + * in the string. + */ + + /* Did the group capture anything? */ + group = &state->groups[node->values[0] - 1]; + if (group->current_capture < 0) + goto backtrack; + + span = &group->captures[group->current_capture]; + + if (string_pos < 0) + string_pos = span->end; + + /* Try comparing. */ + while (string_pos > span->start) { + if (state->text_pos <= 0 && state->partial_side == + RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + same_char_ign(encoding, locale_info, char_at(state->text, + state->text_pos - 1), char_at(state->text, string_pos - 1))) + { + --string_pos; + --state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string(safe_state, search, + &state->text_pos, node, &string_pos, &matched, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + } + + string_pos = -1; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_REF_GROUP_REV: /* Reference to a capture group. */ + { + RE_GroupData* group; + RE_GroupSpan* span; + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + * + * Check whether the captured text, if any, exists at this position + * in the string. + */ + + /* Did the group capture anything? */ + group = &state->groups[node->values[0] - 1]; + if (group->current_capture < 0) + goto backtrack; + + span = &group->captures[group->current_capture]; + + if (string_pos < 0) + string_pos = span->end; + + /* Try comparing. */ + while (string_pos > span->start) { + if (state->text_pos <= 0 && state->partial_side == + RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + same_char(char_at(state->text, state->text_pos - 1), + char_at(state->text, string_pos - 1))) { + --string_pos; + --state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string(safe_state, search, + &state->text_pos, node, &string_pos, &matched, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + } + + string_pos = -1; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_SEARCH_ANCHOR: /* At the start of the search. */ + TRACE(("%s %d\n", re_op_text[node->op], node->values[0])) + + if (state->text_pos == state->search_anchor) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_SET_DIFF: /* Character set. */ + case RE_OP_SET_INTER: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_UNION: + TRACE(("%s %d\n", re_op_text[node->op], node->match)) + + if (state->text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && matches_SET(encoding, + locale_info, node, char_at(state->text, state->text_pos)) == + node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_SET_DIFF_IGN: /* Character set, ignoring case. */ + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_UNION_IGN: + TRACE(("%s %d\n", re_op_text[node->op], node->match)) + + if (state->text_pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && matches_SET_IGN(encoding, + locale_info, node, char_at(state->text, state->text_pos)) == + node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_SET_DIFF_IGN_REV: /* Character set, ignoring case. */ + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_UNION_IGN_REV: + TRACE(("%s %d\n", re_op_text[node->op], node->match)) + + if (state->text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + matches_SET_IGN(encoding, locale_info, node, char_at(state->text, + state->text_pos - 1)) == node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_SET_DIFF_REV: /* Character set. */ + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION_REV: + TRACE(("%s %d\n", re_op_text[node->op], node->match)) + + if (state->text_pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && matches_SET(encoding, + locale_info, node, char_at(state->text, state->text_pos - 1)) == + node->match) { + state->text_pos += node->step; + node = node->next_1.node; + } else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_SKIP: /* Skip the part of the text already matched. */ + TRACE(("%s\n", re_op_text[node->op])) + + if (node->status & RE_STATUS_REVERSE) + state->slice_end = state->text_pos; + else + state->slice_start = state->text_pos; + + prune_backtracking(state); + node = node->next_1.node; + break; + case RE_OP_START_GROUP: /* Start of a capture group. */ + { + RE_CODE private_index; + RE_CODE public_index; + RE_GroupData* group; + RE_BacktrackData* bt_data; + TRACE(("%s %d\n", re_op_text[node->op], node->values[1])) + + /* Capture group indexes are 1-based (excluding group 0, which is + * the entire matched string). + */ + private_index = node->values[0]; + public_index = node->values[1]; + group = &state->groups[private_index - 1]; + + if (!add_backtrack(safe_state, RE_OP_START_GROUP)) + return RE_ERROR_BACKTRACKING; + bt_data = state->backtrack; + bt_data->group.private_index = private_index; + bt_data->group.public_index = public_index; + bt_data->group.text_pos = group->span.start; + bt_data->group.capture = (BOOL)node->values[2]; + bt_data->group.current_capture = group->current_capture; + + if (pattern->group_info[private_index - 1].referenced && + group->span.start != state->text_pos) + ++state->capture_change; + group->span.start = state->text_pos; + + /* Save the capture? */ + if (node->values[2]) { + group->current_capture = (Py_ssize_t)group->capture_count; + if (!save_capture(safe_state, private_index, public_index)) + return RE_ERROR_MEMORY; + } + + node = node->next_1.node; + break; + } + case RE_OP_START_OF_LINE: /* At the start of a line. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_START_OF_LINE(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_START_OF_LINE_U: /* At the start of a line. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_START_OF_LINE_U(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_START_OF_STRING: /* At the start of the string. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_START_OF_STRING(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_START_OF_WORD: /* At the start of a word. */ + TRACE(("%s\n", re_op_text[node->op])) + + status = try_match_START_OF_WORD(state, node, state->text_pos); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS) + node = node->next_1.node; + else if (node->status & RE_STATUS_FUZZY) { + status = fuzzy_match_item(safe_state, search, &state->text_pos, + &node, 0); + if (status < 0) + return status; + + if (!node) + goto backtrack; + } else + goto backtrack; + break; + case RE_OP_STRING: /* A string. */ + { + Py_ssize_t length; + RE_CODE* values; + TRACE(("%s %d\n", re_op_text[node->op], node->value_count)) + + if ((node->status & RE_STATUS_REQUIRED) && state->text_pos == + state->req_pos && string_pos < 0) + state->text_pos = state->req_end; + else { + length = (Py_ssize_t)node->value_count; + + if (string_pos < 0) + string_pos = 0; + + values = node->values; + + /* Try comparing. */ + while (string_pos < length) { + if (state->text_pos >= state->text_length && + state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + same_char(char_at(state->text, state->text_pos), + values[string_pos])) { + ++string_pos; + ++state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string(safe_state, search, + &state->text_pos, node, &string_pos, &matched, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + } + } + + string_pos = -1; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_STRING_FLD: /* A string, ignoring case. */ + { + Py_ssize_t length; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, + Py_UCS4* folded); + RE_CODE* values; + int folded_len; + Py_UCS4 folded[RE_MAX_FOLDED]; + TRACE(("%s %d\n", re_op_text[node->op], node->value_count)) + + if ((node->status & RE_STATUS_REQUIRED) && state->text_pos == + state->req_pos && string_pos < 0) + state->text_pos = state->req_end; + else { + length = (Py_ssize_t)node->value_count; + + full_case_fold = encoding->full_case_fold; + + if (string_pos < 0) { + string_pos = 0; + folded_pos = 0; + folded_len = 0; + } else { + folded_len = full_case_fold(locale_info, + char_at(state->text, state->text_pos), folded); + if (folded_pos >= folded_len) { + if (state->text_pos >= state->slice_end) + goto backtrack; + + ++state->text_pos; + folded_pos = 0; + folded_len = 0; + } + } + + values = node->values; + + /* Try comparing. */ + while (string_pos < length) { + if (folded_pos >= folded_len) { + if (state->text_pos >= state->text_length && + state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end) + folded_len = full_case_fold(locale_info, + char_at(state->text, state->text_pos), folded); + else + folded_len = 0; + + folded_pos = 0; + } + + if (folded_pos < folded_len && same_char_ign(encoding, + locale_info, folded[folded_pos], values[string_pos])) { + ++string_pos; + ++folded_pos; + + if (folded_pos >= folded_len) + ++state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string_fld(safe_state, search, + &state->text_pos, node, &string_pos, &folded_pos, + folded_len, &matched, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + + if (folded_pos >= folded_len && folded_len > 0) + ++state->text_pos; + } else { + string_pos = -1; + goto backtrack; + } + } + + if (node->status & RE_STATUS_FUZZY) { + while (folded_pos < folded_len) { + BOOL matched; + + if (!fuzzy_match_string_fld(safe_state, search, + &state->text_pos, node, &string_pos, &folded_pos, + folded_len, &matched, 1)) + return RE_ERROR_BACKTRACKING; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + + if (folded_pos >= folded_len && folded_len > 0) + ++state->text_pos; + } + } + + string_pos = -1; + + if (folded_pos < folded_len) + goto backtrack; + } + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_STRING_FLD_REV: /* A string, ignoring case. */ + { + Py_ssize_t length; + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, + Py_UCS4* folded); + RE_CODE* values; + int folded_len; + Py_UCS4 folded[RE_MAX_FOLDED]; + TRACE(("%s %d\n", re_op_text[node->op], node->value_count)) + + if ((node->status & RE_STATUS_REQUIRED) && state->text_pos == + state->req_pos && string_pos < 0) + state->text_pos = state->req_end; + else { + length = (Py_ssize_t)node->value_count; + + full_case_fold = encoding->full_case_fold; + + if (string_pos < 0) { + string_pos = length; + folded_pos = 0; + folded_len = 0; + } else { + folded_len = full_case_fold(locale_info, + char_at(state->text, state->text_pos - 1), folded); + if (folded_pos <= 0) { + if (state->text_pos <= state->slice_start) + goto backtrack; + + --state->text_pos; + folded_pos = 0; + folded_len = 0; + } + } + + values = node->values; + + /* Try comparing. */ + while (string_pos > 0) { + if (folded_pos <= 0) { + if (state->text_pos <= 0 && state->partial_side == + RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start) + folded_len = full_case_fold(locale_info, + char_at(state->text, state->text_pos - 1), + folded); + else + folded_len = 0; + + folded_pos = folded_len; + } + + if (folded_pos > 0 && same_char_ign(encoding, locale_info, + folded[folded_pos - 1], values[string_pos - 1])) { + --string_pos; + --folded_pos; + + if (folded_pos <= 0) + --state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string_fld(safe_state, search, + &state->text_pos, node, &string_pos, &folded_pos, + folded_len, &matched, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + + if (folded_pos <= 0 && folded_len > 0) + --state->text_pos; + } else { + string_pos = -1; + goto backtrack; + } + } + + if (node->status & RE_STATUS_FUZZY) { + while (folded_pos > 0) { + BOOL matched; + + if (!fuzzy_match_string_fld(safe_state, search, + &state->text_pos, node, &string_pos, &folded_pos, + folded_len, &matched, -1)) + return RE_ERROR_BACKTRACKING; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + + if (folded_pos <= 0 && folded_len > 0) + --state->text_pos; + } + } + + string_pos = -1; + + if (folded_pos > 0) + goto backtrack; + } + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_STRING_IGN: /* A string, ignoring case. */ + { + Py_ssize_t length; + RE_CODE* values; + TRACE(("%s %d\n", re_op_text[node->op], node->value_count)) + + if ((node->status & RE_STATUS_REQUIRED) && state->text_pos == + state->req_pos && string_pos < 0) + state->text_pos = state->req_end; + else { + length = (Py_ssize_t)node->value_count; + + if (string_pos < 0) + string_pos = 0; + + values = node->values; + + /* Try comparing. */ + while (string_pos < length) { + if (state->text_pos >= state->text_length && + state->partial_side == RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (state->text_pos < state->slice_end && + same_char_ign(encoding, locale_info, char_at(state->text, + state->text_pos), values[string_pos])) { + ++string_pos; + ++state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string(safe_state, search, + &state->text_pos, node, &string_pos, &matched, 1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + } + } + + string_pos = -1; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_STRING_IGN_REV: /* A string, ignoring case. */ + { + Py_ssize_t length; + RE_CODE* values; + TRACE(("%s %d\n", re_op_text[node->op], node->value_count)) + + if ((node->status & RE_STATUS_REQUIRED) && state->text_pos == + state->req_pos && string_pos < 0) + state->text_pos = state->req_end; + else { + length = (Py_ssize_t)node->value_count; + + if (string_pos < 0) + string_pos = length; + + values = node->values; + + /* Try comparing. */ + while (string_pos > 0) { + if (state->text_pos <= 0 && state->partial_side == + RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + same_char_ign(encoding, locale_info, char_at(state->text, + state->text_pos - 1), values[string_pos - 1])) { + --string_pos; + --state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string(safe_state, search, + &state->text_pos, node, &string_pos, &matched, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + } + } + + string_pos = -1; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_STRING_REV: /* A string. */ + { + Py_ssize_t length; + RE_CODE* values; + TRACE(("%s %d\n", re_op_text[node->op], node->value_count)) + + if ((node->status & RE_STATUS_REQUIRED) && state->text_pos == + state->req_pos && string_pos < 0) + state->text_pos = state->req_end; + else { + length = (Py_ssize_t)node->value_count; + + if (string_pos < 0) + string_pos = length; + + values = node->values; + + /* Try comparing. */ + while (string_pos > 0) { + if (state->text_pos <= 0 && state->partial_side == + RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (state->text_pos > state->slice_start && + same_char(char_at(state->text, state->text_pos - 1), + values[string_pos - 1])) { + --string_pos; + --state->text_pos; + } else if (node->status & RE_STATUS_FUZZY) { + BOOL matched; + + status = fuzzy_match_string(safe_state, search, + &state->text_pos, node, &string_pos, &matched, -1); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (!matched) { + string_pos = -1; + goto backtrack; + } + } else { + string_pos = -1; + goto backtrack; + } + } + } + + string_pos = -1; + + /* Successful match. */ + node = node->next_1.node; + break; + } + case RE_OP_STRING_SET: /* Member of a string set. */ + { + int status; + TRACE(("%s\n", re_op_text[node->op])) + + status = string_set_match_fwdrev(safe_state, node, FALSE); + if (status < 0) + return status; + if (status == 0) + goto backtrack; + node = node->next_1.node; + break; + } + case RE_OP_STRING_SET_FLD: /* Member of a string set, ignoring case. */ + { + int status; + TRACE(("%s\n", re_op_text[node->op])) + + status = string_set_match_fld_fwdrev(safe_state, node, FALSE); + if (status < 0) + return status; + if (status == 0) + goto backtrack; + node = node->next_1.node; + break; + } + case RE_OP_STRING_SET_FLD_REV: /* Member of a string set, ignoring case. */ + { + int status; + TRACE(("%s\n", re_op_text[node->op])) + + status = string_set_match_fld_fwdrev(safe_state, node, TRUE); + if (status < 0) + return status; + if (status == 0) + goto backtrack; + node = node->next_1.node; + break; + } + case RE_OP_STRING_SET_IGN: /* Member of a string set, ignoring case. */ + { + int status; + TRACE(("%s\n", re_op_text[node->op])) + + status = string_set_match_ign_fwdrev(safe_state, node, FALSE); + if (status < 0) + return status; + if (status == 0) + goto backtrack; + node = node->next_1.node; + break; + } + case RE_OP_STRING_SET_IGN_REV: /* Member of a string set, ignoring case. */ + { + int status; + TRACE(("%s\n", re_op_text[node->op])) + + status = string_set_match_ign_fwdrev(safe_state, node, TRUE); + if (status < 0) + return status; + if (status == 0) + goto backtrack; + node = node->next_1.node; + break; + } + case RE_OP_STRING_SET_REV: /* Member of a string set. */ + { + int status; + TRACE(("%s\n", re_op_text[node->op])) + + status = string_set_match_fwdrev(safe_state, node, TRUE); + if (status < 0) + return status; + if (status == 0) + goto backtrack; + node = node->next_1.node; + break; + } + case RE_OP_SUCCESS: /* Success. */ + /* Must the match advance past its start? */ + TRACE(("%s\n", re_op_text[node->op])) + + if (state->text_pos == state->search_anchor && state->must_advance) + goto backtrack; + + if (state->match_all) { + /* We want to match all of the slice. */ + if (state->reverse) { + if (state->text_pos != state->slice_start) + goto backtrack; + } else { + if (state->text_pos != state->slice_end) + goto backtrack; + } + } + + if (state->pattern->flags & RE_FLAG_POSIX) { + /* If we're looking for a POSIX match, check whether this one + * is better and then keep looking. + */ + if (!check_posix_match(safe_state)) + return RE_ERROR_MEMORY; + + goto backtrack; + } + + return RE_ERROR_SUCCESS; + default: /* Illegal opcode! */ + TRACE(("UNKNOWN OP %d\n", node->op)) + return RE_ERROR_ILLEGAL; + } + } + +backtrack: + for (;;) { + RE_BacktrackData* bt_data; + TRACE(("BACKTRACK ")) + + /* Should we abort the matching? */ + ++state->iterations; + + if (state->iterations == 0 && safe_check_signals(safe_state)) + return RE_ERROR_INTERRUPTED; + + bt_data = last_backtrack(state); + + switch (bt_data->op) { + case RE_OP_ANY: /* Any character except a newline. */ + case RE_OP_ANY_ALL: /* Any character at all. */ + case RE_OP_ANY_ALL_REV: /* Any character at all, backwards. */ + case RE_OP_ANY_REV: /* Any character except a newline, backwards. */ + case RE_OP_ANY_U: /* Any character except a line separator. */ + case RE_OP_ANY_U_REV: /* Any character except a line separator, backwards. */ + case RE_OP_CHARACTER: /* A character. */ + case RE_OP_CHARACTER_IGN: /* A character, ignoring case. */ + case RE_OP_CHARACTER_IGN_REV: /* A character, ignoring case, backwards. */ + case RE_OP_CHARACTER_REV: /* A character, backwards. */ + case RE_OP_PROPERTY: /* A property. */ + case RE_OP_PROPERTY_IGN: /* A property, ignoring case. */ + case RE_OP_PROPERTY_IGN_REV: /* A property, ignoring case, backwards. */ + case RE_OP_PROPERTY_REV: /* A property, backwards. */ + case RE_OP_RANGE: /* A range. */ + case RE_OP_RANGE_IGN: /* A range, ignoring case. */ + case RE_OP_RANGE_IGN_REV: /* A range, ignoring case, backwards. */ + case RE_OP_RANGE_REV: /* A range, backwards. */ + case RE_OP_SET_DIFF: /* Set difference. */ + case RE_OP_SET_DIFF_IGN: /* Set difference, ignoring case. */ + case RE_OP_SET_DIFF_IGN_REV: /* Set difference, ignoring case, backwards. */ + case RE_OP_SET_DIFF_REV: /* Set difference, backwards. */ + case RE_OP_SET_INTER: /* Set intersection. */ + case RE_OP_SET_INTER_IGN: /* Set intersection, ignoring case. */ + case RE_OP_SET_INTER_IGN_REV: /* Set intersection, ignoring case, backwards. */ + case RE_OP_SET_INTER_REV: /* Set intersection, backwards. */ + case RE_OP_SET_SYM_DIFF: /* Set symmetric difference. */ + case RE_OP_SET_SYM_DIFF_IGN: /* Set symmetric difference, ignoring case. */ + case RE_OP_SET_SYM_DIFF_IGN_REV: /* Set symmetric difference, ignoring case, backwards. */ + case RE_OP_SET_SYM_DIFF_REV: /* Set symmetric difference, backwards. */ + case RE_OP_SET_UNION: /* Set union. */ + case RE_OP_SET_UNION_IGN: /* Set union, ignoring case. */ + case RE_OP_SET_UNION_IGN_REV: /* Set union, ignoring case, backwards. */ + case RE_OP_SET_UNION_REV: /* Set union, backwards. */ + TRACE(("%s\n", re_op_text[bt_data->op])) + + status = retry_fuzzy_match_item(safe_state, search, + &state->text_pos, &node, TRUE); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (node) + goto advance; + break; + case RE_OP_ATOMIC: /* Start of an atomic group. */ + { + RE_AtomicData* atomic; + /* backtrack to the start of an atomic group. */ + atomic = pop_atomic(safe_state); + + if (atomic->has_repeats) + pop_repeats(state); + + if (atomic->has_groups) + pop_groups(state); + + state->too_few_errors = bt_data->atomic.too_few_errors; + state->capture_change = bt_data->atomic.capture_change; + + discard_backtrack(state); + break; + } + case RE_OP_BODY_END: + { + RE_RepeatData* rp_data; + TRACE(("%s %d\n", re_op_text[bt_data->op], bt_data->repeat.index)) + + /* We're backtracking into the body. */ + rp_data = &state->repeats[bt_data->repeat.index]; + + /* Restore the repeat info. */ + rp_data->count = bt_data->repeat.count; + rp_data->start = bt_data->repeat.start; + rp_data->capture_change = bt_data->repeat.capture_change; + + discard_backtrack(state); + break; + } + case RE_OP_BODY_START: + { + TRACE(("%s %d\n", re_op_text[bt_data->op], bt_data->repeat.index)) + + /* The body may have failed to match at this position. */ + if (!guard_repeat(safe_state, bt_data->repeat.index, + bt_data->repeat.text_pos, RE_STATUS_BODY, TRUE)) + return RE_ERROR_MEMORY; + + discard_backtrack(state); + break; + } + case RE_OP_BOUNDARY: /* On a word boundary. */ + case RE_OP_DEFAULT_BOUNDARY: /* On a default word boundary. */ + case RE_OP_DEFAULT_END_OF_WORD: /* At a default end of a word. */ + case RE_OP_DEFAULT_START_OF_WORD: /* At a default start of a word. */ + case RE_OP_END_OF_LINE: /* At the end of a line. */ + case RE_OP_END_OF_LINE_U: /* At the end of a line. */ + case RE_OP_END_OF_STRING: /* At the end of the string. */ + case RE_OP_END_OF_STRING_LINE: /* At end of string or final newline. */ + case RE_OP_END_OF_STRING_LINE_U: /* At end of string or final newline. */ + case RE_OP_END_OF_WORD: /* At end of a word. */ + case RE_OP_GRAPHEME_BOUNDARY: /* On a grapheme boundary. */ + case RE_OP_SEARCH_ANCHOR: /* At the start of the search. */ + case RE_OP_START_OF_LINE: /* At the start of a line. */ + case RE_OP_START_OF_LINE_U: /* At the start of a line. */ + case RE_OP_START_OF_STRING: /* At the start of the string. */ + case RE_OP_START_OF_WORD: /* At start of a word. */ + TRACE(("%s\n", re_op_text[bt_data->op])) + + status = retry_fuzzy_match_item(safe_state, search, + &state->text_pos, &node, FALSE); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (node) + goto advance; + break; + case RE_OP_BRANCH: /* 2-way branch. */ + TRACE(("%s\n", re_op_text[bt_data->op])) + + node = bt_data->branch.position.node; + state->text_pos = bt_data->branch.position.text_pos; + discard_backtrack(state); + goto advance; + case RE_OP_CALL_REF: /* A group call ref. */ + case RE_OP_GROUP_CALL: /* Group call. */ + TRACE(("%s\n", re_op_text[bt_data->op])) + + pop_group_return(state); + discard_backtrack(state); + break; + case RE_OP_CONDITIONAL: /* Conditional subpattern. */ + { + TRACE(("%s\n", re_op_text[bt_data->op])) + + if (bt_data->lookaround.inside) { + /* Backtracked to the start of a lookaround. */ + RE_AtomicData* conditional; + + conditional = pop_atomic(safe_state); + state->text_pos = conditional->text_pos; + state->slice_end = conditional->slice_end; + state->slice_start = conditional->slice_start; + state->current_backtrack_block = + conditional->current_backtrack_block; + state->current_backtrack_block->count = + conditional->backtrack_count; + + /* Restore the groups and repeats and certain flags. */ + if (conditional->has_repeats) + pop_repeats(state); + + if (conditional->has_groups) + pop_groups(state); + + state->too_few_errors = bt_data->lookaround.too_few_errors; + state->capture_change = bt_data->lookaround.capture_change; + + if (bt_data->lookaround.node->match) { + /* It's a positive lookaround that's failed. + * + * Go to the 'false' branch. + */ + node = bt_data->lookaround.node->nonstring.next_2.node; + } else { + /* It's a negative lookaround that's failed. + * + * Go to the 'true' branch. + */ + node = bt_data->lookaround.node->nonstring.next_2.node; + } + + discard_backtrack(state); + + goto advance; + } else { + /* Backtracked to a lookaround. If it's a positive lookaround + * that succeeded, we need to restore the groups; if it's a + * negative lookaround that failed, it would have completely + * backtracked inside and already restored the groups. We also + * need to restore certain flags. + */ + if (bt_data->lookaround.node->match) + pop_groups(state); + + state->too_few_errors = bt_data->lookaround.too_few_errors; + state->capture_change = bt_data->lookaround.capture_change; + + discard_backtrack(state); + } + break; + } + case RE_OP_END_FUZZY: /* End of fuzzy matching. */ + TRACE(("%s\n", re_op_text[bt_data->op])) + + state->total_fuzzy_counts[RE_FUZZY_SUB] -= + state->fuzzy_info.counts[RE_FUZZY_SUB]; + state->total_fuzzy_counts[RE_FUZZY_INS] -= + state->fuzzy_info.counts[RE_FUZZY_INS]; + state->total_fuzzy_counts[RE_FUZZY_DEL] -= + state->fuzzy_info.counts[RE_FUZZY_DEL]; + + /* We need to retry the fuzzy match. */ + status = retry_fuzzy_insert(safe_state, &state->text_pos, &node); + if (status < 0) + return RE_ERROR_PARTIAL; + + /* If there were too few errors, in the fuzzy section, try again. + */ + if (state->too_few_errors) { + state->too_few_errors = FALSE; + goto backtrack; + } + + if (node) { + state->total_fuzzy_counts[RE_FUZZY_SUB] += + state->fuzzy_info.counts[RE_FUZZY_SUB]; + state->total_fuzzy_counts[RE_FUZZY_INS] += + state->fuzzy_info.counts[RE_FUZZY_INS]; + state->total_fuzzy_counts[RE_FUZZY_DEL] += + state->fuzzy_info.counts[RE_FUZZY_DEL]; + + node = node->next_1.node; + goto advance; + } + break; + case RE_OP_END_GROUP: /* End of a capture group. */ + { + RE_CODE private_index; + RE_GroupData* group; + TRACE(("%s %d\n", re_op_text[bt_data->op], + bt_data->group.public_index)) + + private_index = bt_data->group.private_index; + group = &state->groups[private_index - 1]; + + /* Unsave the capture? */ + if (bt_data->group.capture) + unsave_capture(state, bt_data->group.private_index, + bt_data->group.public_index); + + if (pattern->group_info[private_index - 1].referenced && + group->span.end != bt_data->group.text_pos) + --state->capture_change; + group->span.end = bt_data->group.text_pos; + group->current_capture = bt_data->group.current_capture; + + discard_backtrack(state); + break; + } + case RE_OP_FAILURE: + { + TRACE(("%s\n", re_op_text[bt_data->op])) + + /* Have we been looking for a POSIX match? */ + if (state->found_match) { + restore_best_match(safe_state); + return RE_OP_SUCCESS; + } + + /* Do we have to advance? */ + if (!search) + return RE_ERROR_FAILURE; + + /* Can we advance? */ + state->text_pos = state->match_pos; + + if (state->reverse) { + if (state->text_pos <= state->slice_start) + return RE_ERROR_FAILURE; + } else { + if (state->text_pos >= state->slice_end) + return RE_ERROR_FAILURE; + } + + /* Skip over any repeated leading characters. */ + switch (start_node->op) { + case RE_OP_GREEDY_REPEAT_ONE: + case RE_OP_LAZY_REPEAT_ONE: + { + size_t count; + BOOL is_partial; + + /* How many characters did the repeat actually match? */ + count = count_one(state, start_node->nonstring.next_2.node, + state->text_pos, start_node->values[2], &is_partial); + + /* If it's fewer than the maximum then skip over those + * characters. + */ + if (count < start_node->values[2]) + state->text_pos += (Py_ssize_t)count * pattern_step; + break; + } + } + + /* Advance and try to match again. e also need to check whether we + * need to skip. + */ + if (state->reverse) { + if (state->text_pos > state->slice_end) + state->text_pos = state->slice_end; + else + --state->text_pos; + } else { + if (state->text_pos < state->slice_start) + state->text_pos = state->slice_start; + else + ++state->text_pos; + } + + /* Clear the groups. */ + clear_groups(state); + + goto start_match; + } + case RE_OP_FUZZY: /* Fuzzy matching. */ + { + RE_FuzzyInfo* fuzzy_info; + TRACE(("%s\n", re_op_text[bt_data->op])) + + /* Restore the previous fuzzy info. */ + fuzzy_info = &state->fuzzy_info; + memmove(fuzzy_info, &bt_data->fuzzy.fuzzy_info, + sizeof(RE_FuzzyInfo)); + + discard_backtrack(state); + break; + } + case RE_OP_GREEDY_REPEAT: /* Greedy repeat. */ + case RE_OP_LAZY_REPEAT: /* Lazy repeat. */ + { + RE_RepeatData* rp_data; + TRACE(("%s\n", re_op_text[bt_data->op])) + + /* The repeat failed to match. */ + rp_data = &state->repeats[bt_data->repeat.index]; + + /* The body may have failed to match at this position. */ + if (!guard_repeat(safe_state, bt_data->repeat.index, + bt_data->repeat.text_pos, RE_STATUS_BODY, TRUE)) + return RE_ERROR_MEMORY; + + /* Restore the previous repeat. */ + rp_data->count = bt_data->repeat.count; + rp_data->start = bt_data->repeat.start; + rp_data->capture_change = bt_data->repeat.capture_change; + + discard_backtrack(state); + break; + } + case RE_OP_GREEDY_REPEAT_ONE: /* Greedy repeat for one character. */ + { + RE_RepeatData* rp_data; + size_t count; + Py_ssize_t step; + Py_ssize_t pos; + Py_ssize_t limit; + RE_Node* test; + BOOL match; + BOOL m; + size_t index; + TRACE(("%s\n", re_op_text[bt_data->op])) + + node = bt_data->repeat.position.node; + + rp_data = &state->repeats[bt_data->repeat.index]; + + /* Unmatch one character at a time until the tail could match or we + * have reached the minimum. + */ + state->text_pos = rp_data->start; + + count = rp_data->count; + step = node->step; + pos = state->text_pos + (Py_ssize_t)count * step; + limit = state->text_pos + (Py_ssize_t)node->values[1] * step; + + /* The tail failed to match at this position. */ + if (!guard_repeat(safe_state, bt_data->repeat.index, pos, + RE_STATUS_TAIL, TRUE)) + return RE_ERROR_MEMORY; + + /* A (*SKIP) might have change the size of the slice. */ + if (step > 0) { + if (limit < state->slice_start) + limit = state->slice_start; + } else { + if (limit > state->slice_end) + limit = state->slice_end; + } + + if (pos == limit) { + /* We've backtracked the repeat as far as we can. */ + rp_data->start = bt_data->repeat.text_pos; + rp_data->count = bt_data->repeat.count; + discard_backtrack(state); + break; + } + + test = node->next_1.test; + + m = test->match; + index = node->values[0]; + + match = FALSE; + + if (test->status & RE_STATUS_FUZZY) { + for (;;) { + int status; + RE_Position next_position; + + pos -= step; + + status = try_match(state, &node->next_1, pos, + &next_position); + if (status < 0) + return status; + + if (status != RE_ERROR_FAILURE && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + if (pos == limit) + break; + } + } else { + /* A repeated single-character match is often followed by a + * literal, so checking specially for it can be a good + * optimisation when working with long strings. + */ + switch (test->op) { + case RE_OP_CHARACTER: + { + Py_UCS4 ch; + + ch = test->values[0]; + + for (;;) { + --pos; + + if (same_char(char_at(state->text, pos), ch) == m && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + if (pos == limit) + break; + + } + break; + } + case RE_OP_CHARACTER_IGN: + { + Py_UCS4 ch; + + ch = test->values[0]; + + for (;;) { + --pos; + + if (same_char_ign(encoding, locale_info, + char_at(state->text, pos), ch) == m && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + if (pos == limit) + break; + + } + break; + } + case RE_OP_CHARACTER_IGN_REV: + { + Py_UCS4 ch; + + ch = test->values[0]; + + for (;;) { + ++pos; + + if (same_char_ign(encoding, locale_info, + char_at(state->text, pos - 1), ch) == m && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + if (pos == limit) + break; + + } + break; + } + case RE_OP_CHARACTER_REV: + { + Py_UCS4 ch; + + ch = test->values[0]; + + for (;;) { + ++pos; + + if (same_char(char_at(state->text, pos - 1), ch) == m + && !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + if (pos == limit) + break; + + } + break; + } + case RE_OP_STRING: + { + Py_ssize_t length; + + length = (Py_ssize_t)test->value_count; + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + pos = min_ssize_t(pos - 1, state->slice_end - length); + + for (;;) { + Py_ssize_t found; + BOOL is_partial; + + if (pos < limit) + break; + + found = string_search_rev(safe_state, test, pos + + length, limit, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + pos = found - length; + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + --pos; + } + break; + } + case RE_OP_STRING_FLD: + { + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 + ch, Py_UCS4* folded); + Py_ssize_t folded_length; + size_t i; + Py_UCS4 folded[RE_MAX_FOLDED]; + + full_case_fold = encoding->full_case_fold; + + folded_length = 0; + for (i = 0; i < test->value_count; i++) + folded_length += full_case_fold(locale_info, + test->values[i], folded); + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + pos = min_ssize_t(pos - 1, state->slice_end - + folded_length); + + for (;;) { + Py_ssize_t found; + Py_ssize_t new_pos; + BOOL is_partial; + + if (pos < limit) + break; + + found = string_search_fld_rev(safe_state, test, pos + + folded_length, limit, &new_pos, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + pos = found - folded_length; + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + --pos; + } + break; + } + case RE_OP_STRING_FLD_REV: + { + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 + ch, Py_UCS4* folded); + Py_ssize_t folded_length; + size_t i; + Py_UCS4 folded[RE_MAX_FOLDED]; + + full_case_fold = encoding->full_case_fold; + + folded_length = 0; + for (i = 0; i < test->value_count; i++) + folded_length += full_case_fold(locale_info, + test->values[i], folded); + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + pos = max_ssize_t(pos + 1, state->slice_start + + folded_length); + + for (;;) { + Py_ssize_t found; + Py_ssize_t new_pos; + BOOL is_partial; + + if (pos > limit) + break; + + found = string_search_fld(safe_state, test, pos - + folded_length, limit, &new_pos, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + pos = found + folded_length; + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + ++pos; + } + break; + } + case RE_OP_STRING_IGN: + { + Py_ssize_t length; + + length = (Py_ssize_t)test->value_count; + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + pos = min_ssize_t(pos - 1, state->slice_end - length); + + for (;;) { + Py_ssize_t found; + BOOL is_partial; + + if (pos < limit) + break; + + found = string_search_ign_rev(safe_state, test, pos + + length, limit, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + pos = found - length; + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + --pos; + } + break; + } + case RE_OP_STRING_IGN_REV: + { + Py_ssize_t length; + + length = (Py_ssize_t)test->value_count; + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + pos = max_ssize_t(pos + 1, state->slice_start + length); + + for (;;) { + Py_ssize_t found; + BOOL is_partial; + + if (pos > limit) + break; + + found = string_search_ign(safe_state, test, pos - + length, limit, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + pos = found + length; + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + ++pos; + } + break; + } + case RE_OP_STRING_REV: + { + Py_ssize_t length; + + length = (Py_ssize_t)test->value_count; + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + pos = max_ssize_t(pos + 1, state->slice_start + length); + + for (;;) { + Py_ssize_t found; + BOOL is_partial; + + if (pos > limit) + break; + + found = string_search(safe_state, test, pos - length, + limit, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + pos = found + length; + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + ++pos; + } + break; + } + default: + for (;;) { + RE_Position next_position; + + pos -= step; + + status = try_match(state, &node->next_1, pos, + &next_position); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + if (pos == limit) + break; + } + break; + } + } + + if (match) { + count = (size_t)abs_ssize_t(pos - state->text_pos); + + /* The tail could match. */ + if (count > node->values[1]) + /* The match is longer than the minimum, so we might need + * to backtrack the repeat again to consume less. + */ + rp_data->count = count; + else { + /* We've reached or passed the minimum, so we won't need to + * backtrack the repeat again. + */ + rp_data->start = bt_data->repeat.text_pos; + rp_data->count = bt_data->repeat.count; + discard_backtrack(state); + + /* Have we passed the minimum? */ + if (count < node->values[1]) + goto backtrack; + } + + node = node->next_1.node; + state->text_pos = pos; + goto advance; + } else { + /* Don't try this repeated match again. */ + if (step > 0) { + if (!guard_repeat_range(safe_state, bt_data->repeat.index, + limit, pos, RE_STATUS_BODY, TRUE)) + return RE_ERROR_MEMORY; + } else if (step < 0) { + if (!guard_repeat_range(safe_state, bt_data->repeat.index, + pos, limit, RE_STATUS_BODY, TRUE)) + return RE_ERROR_MEMORY; + } + + /* We've backtracked the repeat as far as we can. */ + rp_data->start = bt_data->repeat.text_pos; + rp_data->count = bt_data->repeat.count; + discard_backtrack(state); + } + break; + } + case RE_OP_GROUP_RETURN: /* Group return. */ + { + RE_Node* return_node; + TRACE(("%s\n", re_op_text[bt_data->op])) + + return_node = bt_data->group_call.node; + + push_group_return(safe_state, return_node); + + if (return_node) { + /* Restore the groups. */ + pop_groups(state); + state->capture_change = bt_data->group_call.capture_change; + + /* Restore the repeats. */ + pop_repeats(state); + } + + discard_backtrack(state); + break; + } + case RE_OP_KEEP: /* Keep. */ + { + state->match_pos = bt_data->keep.match_pos; + discard_backtrack(state); + break; + } + case RE_OP_LAZY_REPEAT_ONE: /* Lazy repeat for one character. */ + { + RE_RepeatData* rp_data; + size_t count; + Py_ssize_t step; + Py_ssize_t pos; + Py_ssize_t available; + size_t max_count; + Py_ssize_t limit; + RE_Node* repeated; + RE_Node* test; + BOOL match; + BOOL m; + size_t index; + TRACE(("%s\n", re_op_text[bt_data->op])) + + node = bt_data->repeat.position.node; + + rp_data = &state->repeats[bt_data->repeat.index]; + + /* Match one character at a time until the tail could match or we + * have reached the maximum. + */ + state->text_pos = rp_data->start; + count = rp_data->count; + + step = node->step; + pos = state->text_pos + (Py_ssize_t)count * step; + available = step > 0 ? state->slice_end - state->text_pos : + state->text_pos - state->slice_start; + max_count = min_size_t((size_t)available, node->values[2]); + limit = state->text_pos + (Py_ssize_t)max_count * step; + + repeated = node->nonstring.next_2.node; + + test = node->next_1.test; + + m = test->match; + index = node->values[0]; + + match = FALSE; + + if (test->status & RE_STATUS_FUZZY) { + for (;;) { + RE_Position next_position; + + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + pos += step; + + status = try_match(state, &node->next_1, pos, + &next_position); + if (status < 0) + return status; + + if (status == RE_ERROR_SUCCESS && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + if (pos == limit) + break; + } + } else { + /* A repeated single-character match is often followed by a + * literal, so checking specially for it can be a good + * optimisation when working with long strings. + */ + switch (test->op) { + case RE_OP_CHARACTER: + { + Py_UCS4 ch; + + ch = test->values[0]; + + /* The tail is a character. We don't want to go off the end + * of the slice. + */ + limit = min_ssize_t(limit, state->slice_end - 1); + + for (;;) { + if (pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (pos >= limit) + break; + + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + ++pos; + + if (same_char(char_at(state->text, pos), ch) == m && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_CHARACTER_IGN: + { + Py_UCS4 ch; + + ch = test->values[0]; + + /* The tail is a character. We don't want to go off the end + * of the slice. + */ + limit = min_ssize_t(limit, state->slice_end - 1); + + for (;;) { + if (pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (pos >= limit) + break; + + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + ++pos; + + if (same_char_ign(encoding, locale_info, + char_at(state->text, pos), ch) == m && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_CHARACTER_IGN_REV: + { + Py_UCS4 ch; + + ch = test->values[0]; + + /* The tail is a character. We don't want to go off the end + * of the slice. + */ + limit = max_ssize_t(limit, state->slice_start + 1); + + for (;;) { + if (pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (pos <= limit) + break; + + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + --pos; + + if (same_char_ign(encoding, locale_info, + char_at(state->text, pos - 1), ch) == m && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_CHARACTER_REV: + { + Py_UCS4 ch; + + ch = test->values[0]; + + /* The tail is a character. We don't want to go off the end + * of the slice. + */ + limit = max_ssize_t(limit, state->slice_start + 1); + + for (;;) { + if (pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (pos <= limit) + break; + + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + --pos; + + if (same_char(char_at(state->text, pos - 1), ch) == m + && !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_STRING: + { + Py_ssize_t length; + + length = (Py_ssize_t)test->value_count; + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + limit = min_ssize_t(limit, state->slice_end - length); + + for (;;) { + Py_ssize_t found; + BOOL is_partial; + + if (pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (pos >= limit) + break; + + /* Look for the tail string. */ + found = string_search(safe_state, test, pos + 1, limit + + length, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + if (repeated->op == RE_OP_ANY_ALL) + /* Anything can precede the tail. */ + pos = found; + else { + /* Check that what precedes the tail will match. */ + while (pos != found) { + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + ++pos; + } + + if (pos != found) + /* Something preceding the tail didn't match. + */ + break; + } + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_STRING_FLD: + { + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + limit = min_ssize_t(limit, state->slice_end); + + for (;;) { + Py_ssize_t found; + Py_ssize_t new_pos; + BOOL is_partial; + + if (pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (pos >= limit) + break; + + /* Look for the tail string. */ + found = string_search_fld(safe_state, test, pos + 1, + limit, &new_pos, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + if (repeated->op == RE_OP_ANY_ALL) + /* Anything can precede the tail. */ + pos = found; + else { + /* Check that what precedes the tail will match. */ + while (pos != found) { + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + ++pos; + } + + if (pos != found) + /* Something preceding the tail didn't match. + */ + break; + } + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_STRING_FLD_REV: + { + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + limit = max_ssize_t(limit, state->slice_start); + + for (;;) { + Py_ssize_t found; + Py_ssize_t new_pos; + BOOL is_partial; + + if (pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (pos <= limit) + break; + + /* Look for the tail string. */ + found = string_search_fld_rev(safe_state, test, pos - + 1, limit, &new_pos, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + if (repeated->op == RE_OP_ANY_ALL) + /* Anything can precede the tail. */ + pos = found; + else { + /* Check that what precedes the tail will match. */ + while (pos != found) { + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + --pos; + } + + if (pos != found) + /* Something preceding the tail didn't match. + */ + break; + } + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_STRING_IGN: + { + Py_ssize_t length; + + length = (Py_ssize_t)test->value_count; + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + limit = min_ssize_t(limit, state->slice_end - length); + + for (;;) { + Py_ssize_t found; + BOOL is_partial; + + if (pos >= state->text_length && state->partial_side == + RE_PARTIAL_RIGHT) + return RE_ERROR_PARTIAL; + + if (pos >= limit) + break; + + /* Look for the tail string. */ + found = string_search_ign(safe_state, test, pos + 1, + limit + length, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + if (repeated->op == RE_OP_ANY_ALL) + /* Anything can precede the tail. */ + pos = found; + else { + /* Check that what precedes the tail will match. */ + while (pos != found) { + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + ++pos; + } + + if (pos != found) + /* Something preceding the tail didn't match. + */ + break; + } + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_STRING_IGN_REV: + { + Py_ssize_t length; + + length = (Py_ssize_t)test->value_count; + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + limit = max_ssize_t(limit, state->slice_start + length); + + for (;;) { + Py_ssize_t found; + BOOL is_partial; + + if (pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (pos <= limit) + break; + + /* Look for the tail string. */ + found = string_search_ign_rev(safe_state, test, pos - + 1, limit - length, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + if (repeated->op == RE_OP_ANY_ALL) + /* Anything can precede the tail. */ + pos = found; + else { + /* Check that what precedes the tail will match. */ + while (pos != found) { + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + --pos; + } + + if (pos != found) + /* Something preceding the tail didn't match. + */ + break; + } + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + case RE_OP_STRING_REV: + { + Py_ssize_t length; + + length = (Py_ssize_t)test->value_count; + + /* The tail is a string. We don't want to go off the end of + * the slice. + */ + limit = max_ssize_t(limit, state->slice_start + length); + + for (;;) { + Py_ssize_t found; + BOOL is_partial; + + if (pos <= 0 && state->partial_side == RE_PARTIAL_LEFT) + return RE_ERROR_PARTIAL; + + if (pos <= limit) + break; + + /* Look for the tail string. */ + found = string_search_rev(safe_state, test, pos - 1, + limit - length, &is_partial); + if (is_partial) + return RE_ERROR_PARTIAL; + + if (found < 0) + break; + + if (repeated->op == RE_OP_ANY_ALL) + /* Anything can precede the tail. */ + pos = found; + else { + /* Check that what precedes the tail will match. */ + while (pos != found) { + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + --pos; + } + + if (pos != found) + /* Something preceding the tail didn't match. + */ + break; + } + + if (!is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + } + break; + } + default: + for (;;) { + RE_Position next_position; + + status = match_one(state, repeated, pos); + if (status < 0) + return status; + + if (status == RE_ERROR_FAILURE) + break; + + pos += step; + + status = try_match(state, &node->next_1, pos, + &next_position); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (status == RE_ERROR_SUCCESS && + !is_repeat_guarded(safe_state, index, pos, + RE_STATUS_TAIL)) { + match = TRUE; + break; + } + + if (pos == limit) + break; + } + break; + } + } + + if (match) { + /* The tail could match. */ + count = (size_t)abs_ssize_t(pos - state->text_pos); + state->text_pos = pos; + + if (count < max_count) { + /* The match is shorter than the maximum, so we might need + * to backtrack the repeat again to consume more. + */ + rp_data->count = count; + } else { + /* We've reached or passed the maximum, so we won't need to + * backtrack the repeat again. + */ + rp_data->start = bt_data->repeat.text_pos; + rp_data->count = bt_data->repeat.count; + discard_backtrack(state); + + /* Have we passed the maximum? */ + if (count > max_count) + goto backtrack; + } + + node = node->next_1.node; + goto advance; + } else { + /* The tail couldn't match. */ + rp_data->start = bt_data->repeat.text_pos; + rp_data->count = bt_data->repeat.count; + discard_backtrack(state); + } + break; + } + case RE_OP_LOOKAROUND: /* Lookaround subpattern. */ + { + TRACE(("%s\n", re_op_text[bt_data->op])) + + if (bt_data->lookaround.inside) { + /* Backtracked to the start of a lookaround. */ + RE_AtomicData* lookaround; + + lookaround = pop_atomic(safe_state); + state->text_pos = lookaround->text_pos; + state->slice_end = lookaround->slice_end; + state->slice_start = lookaround->slice_start; + state->current_backtrack_block = + lookaround->current_backtrack_block; + state->current_backtrack_block->count = + lookaround->backtrack_count; + + /* Restore the groups and repeats and certain flags. */ + if (lookaround->has_repeats) + pop_repeats(state); + + if (lookaround->has_groups) + pop_groups(state); + + state->too_few_errors = bt_data->lookaround.too_few_errors; + state->capture_change = bt_data->lookaround.capture_change; + + if (bt_data->lookaround.node->match) { + /* It's a positive lookaround that's failed. */ + discard_backtrack(state); + } else { + /* It's a negative lookaround that's failed. Record that + * we've now left the lookaround and continue to the + * following node. + */ + bt_data->lookaround.inside = FALSE; + node = bt_data->lookaround.node->nonstring.next_2.node; + goto advance; + } + } else { + /* Backtracked to a lookaround. If it's a positive lookaround + * that succeeded, we need to restore the groups; if it's a + * negative lookaround that failed, it would have completely + * backtracked inside and already restored the groups. We also + * need to restore certain flags. + */ + if (bt_data->lookaround.node->match && + (bt_data->lookaround.node->status & RE_STATUS_HAS_GROUPS)) + pop_groups(state); + + state->too_few_errors = bt_data->lookaround.too_few_errors; + state->capture_change = bt_data->lookaround.capture_change; + + discard_backtrack(state); + } + break; + } + case RE_OP_MATCH_BODY: + { + RE_RepeatData* rp_data; + TRACE(("%s %d\n", re_op_text[bt_data->op], bt_data->repeat.index)) + + /* We want to match the body. */ + rp_data = &state->repeats[bt_data->repeat.index]; + + /* Restore the repeat info. */ + rp_data->count = bt_data->repeat.count; + rp_data->start = bt_data->repeat.start; + rp_data->capture_change = bt_data->repeat.capture_change; + + /* Record backtracking info in case the body fails to match. */ + bt_data->op = RE_OP_BODY_START; + + /* Advance into the body. */ + node = bt_data->repeat.position.node; + state->text_pos = bt_data->repeat.position.text_pos; + goto advance; + } + case RE_OP_MATCH_TAIL: + { + RE_RepeatData* rp_data; + TRACE(("%s %d\n", re_op_text[bt_data->op], bt_data->repeat.index)) + + /* We want to match the tail. */ + rp_data = &state->repeats[bt_data->repeat.index]; + + /* Restore the repeat info. */ + rp_data->count = bt_data->repeat.count; + rp_data->start = bt_data->repeat.start; + rp_data->capture_change = bt_data->repeat.capture_change; + + /* Advance into the tail. */ + node = bt_data->repeat.position.node; + state->text_pos = bt_data->repeat.position.text_pos; + + discard_backtrack(state); + goto advance; + } + case RE_OP_REF_GROUP: /* Reference to a capture group. */ + case RE_OP_REF_GROUP_IGN: /* Reference to a capture group, ignoring case. */ + case RE_OP_REF_GROUP_IGN_REV: /* Reference to a capture group, backwards, ignoring case. */ + case RE_OP_REF_GROUP_REV: /* Reference to a capture group, backwards. */ + case RE_OP_STRING: /* A string. */ + case RE_OP_STRING_IGN: /* A string, ignoring case. */ + case RE_OP_STRING_IGN_REV: /* A string, backwards, ignoring case. */ + case RE_OP_STRING_REV: /* A string, backwards. */ + { + BOOL matched; + TRACE(("%s\n", re_op_text[bt_data->op])) + + status = retry_fuzzy_match_string(safe_state, search, + &state->text_pos, &node, &string_pos, &matched); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (matched) + goto advance; + + string_pos = -1; + break; + } + case RE_OP_REF_GROUP_FLD: /* Reference to a capture group, ignoring case. */ + case RE_OP_REF_GROUP_FLD_REV: /* Reference to a capture group, backwards, ignoring case. */ + { + BOOL matched; + TRACE(("%s\n", re_op_text[bt_data->op])) + + status = retry_fuzzy_match_group_fld(safe_state, search, + &state->text_pos, &node, &folded_pos, &string_pos, &gfolded_pos, + &matched); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (matched) + goto advance; + + string_pos = -1; + break; + } + case RE_OP_START_GROUP: /* Start of a capture group. */ + { + RE_CODE private_index; + RE_GroupData* group; + TRACE(("%s %d\n", re_op_text[bt_data->op], + bt_data->group.public_index)) + + private_index = bt_data->group.private_index; + group = &state->groups[private_index - 1]; + + /* Unsave the capture? */ + if (bt_data->group.capture) + unsave_capture(state, bt_data->group.private_index, + bt_data->group.public_index); + + if (pattern->group_info[private_index - 1].referenced && + group->span.start != bt_data->group.text_pos) + --state->capture_change; + group->span.start = bt_data->group.text_pos; + group->current_capture = bt_data->group.current_capture; + + discard_backtrack(state); + break; + } + case RE_OP_STRING_FLD: /* A string, ignoring case. */ + case RE_OP_STRING_FLD_REV: /* A string, backwards, ignoring case. */ + { + BOOL matched; + TRACE(("%s\n", re_op_text[bt_data->op])) + + status = retry_fuzzy_match_string_fld(safe_state, search, + &state->text_pos, &node, &string_pos, &folded_pos, &matched); + if (status < 0) + return RE_ERROR_PARTIAL; + + if (matched) + goto advance; + + string_pos = -1; + break; + } + default: + TRACE(("UNKNOWN OP %d\n", bt_data->op)) + return RE_ERROR_ILLEGAL; + } + } +} + +/* Saves group data for fuzzy matching. */ +Py_LOCAL_INLINE(RE_GroupData*) save_groups(RE_SafeState* safe_state, + RE_GroupData* saved_groups) { + RE_State* state; + PatternObject* pattern; + size_t g; + + /* Re-acquire the GIL. */ + acquire_GIL(safe_state); + + state = safe_state->re_state; + pattern = state->pattern; + + if (!saved_groups) { + saved_groups = (RE_GroupData*)re_alloc(pattern->true_group_count * + sizeof(RE_GroupData)); + if (!saved_groups) + goto error; + + memset(saved_groups, 0, pattern->true_group_count * + sizeof(RE_GroupData)); + } + + for (g = 0; g < pattern->true_group_count; g++) { + RE_GroupData* orig; + RE_GroupData* copy; + + orig = &state->groups[g]; + copy = &saved_groups[g]; + + copy->span = orig->span; + + if (orig->capture_count > copy->capture_capacity) { + RE_GroupSpan* cap_copy; + + cap_copy = (RE_GroupSpan*)re_realloc(copy->captures, + orig->capture_count * sizeof(RE_GroupSpan)); + if (!cap_copy) + goto error; + + copy->capture_capacity = orig->capture_count; + copy->captures = cap_copy; + } + + copy->capture_count = orig->capture_count; + Py_MEMCPY(copy->captures, orig->captures, orig->capture_count * + sizeof(RE_GroupSpan)); + } + + /* Release the GIL. */ + release_GIL(safe_state); + + return saved_groups; + +error: + if (saved_groups) { + for (g = 0; g < pattern->true_group_count; g++) + re_dealloc(saved_groups[g].captures); + + re_dealloc(saved_groups); + } + + /* Release the GIL. */ + release_GIL(safe_state); + + return NULL; +} + +/* Restores group data for fuzzy matching. */ +Py_LOCAL_INLINE(void) restore_groups(RE_SafeState* safe_state, RE_GroupData* + saved_groups) { + RE_State* state; + PatternObject* pattern; + size_t g; + + /* Re-acquire the GIL. */ + acquire_GIL(safe_state); + + state = safe_state->re_state; + pattern = state->pattern; + + for (g = 0; g < pattern->true_group_count; g++) + re_dealloc(state->groups[g].captures); + + Py_MEMCPY(state->groups, saved_groups, pattern->true_group_count * + sizeof(RE_GroupData)); + + re_dealloc(saved_groups); + + /* Release the GIL. */ + release_GIL(safe_state); +} + +/* Discards group data for fuzzy matching. */ +Py_LOCAL_INLINE(void) discard_groups(RE_SafeState* safe_state, RE_GroupData* + saved_groups) { + RE_State* state; + PatternObject* pattern; + size_t g; + + /* Re-acquire the GIL. */ + acquire_GIL(safe_state); + + state = safe_state->re_state; + pattern = state->pattern; + + for (g = 0; g < pattern->true_group_count; g++) + re_dealloc(saved_groups[g].captures); + + re_dealloc(saved_groups); + + /* Release the GIL. */ + release_GIL(safe_state); +} + +/* Saves the fuzzy info. */ +Py_LOCAL_INLINE(void) save_fuzzy_counts(RE_State* state, size_t* fuzzy_counts) + { + Py_MEMCPY(fuzzy_counts, state->total_fuzzy_counts, + sizeof(state->total_fuzzy_counts)); +} + +/* Restores the fuzzy info. */ +Py_LOCAL_INLINE(void) restore_fuzzy_counts(RE_State* state, size_t* + fuzzy_counts) { + Py_MEMCPY(state->total_fuzzy_counts, fuzzy_counts, + sizeof(state->total_fuzzy_counts)); +} + +/* Makes the list of best matches found so far. */ +Py_LOCAL_INLINE(void) make_best_list(RE_BestList* best_list) { + best_list->capacity = 0; + best_list->count = 0; + best_list->entries = NULL; +} + +/* Clears the list of best matches found so far. */ +Py_LOCAL_INLINE(void) clear_best_list(RE_BestList* best_list) { + best_list->count = 0; +} + +/* Adds a new entry to the list of best matches found so far. */ +Py_LOCAL_INLINE(BOOL) add_to_best_list(RE_SafeState* safe_state, RE_BestList* + best_list, Py_ssize_t match_pos, Py_ssize_t text_pos) { + RE_BestEntry* entry; + + if (best_list->count >= best_list->capacity) { + RE_BestEntry* new_entries; + + best_list->capacity = best_list->capacity == 0 ? 16 : + best_list->capacity * 2; + new_entries = safe_realloc(safe_state, best_list->entries, + best_list->capacity * sizeof(RE_BestEntry)); + if (!new_entries) + return FALSE; + + best_list->entries = new_entries; + } + + entry = &best_list->entries[best_list->count++]; + entry->match_pos = match_pos; + entry->text_pos = text_pos; + + return TRUE; +} + +/* Destroy the list of best matches found so far. */ +Py_LOCAL_INLINE(void) destroy_best_list(RE_SafeState* safe_state, RE_BestList* + best_list) { + if (best_list->entries) + safe_dealloc(safe_state, best_list->entries); +} + +/* Performs a match or search from the current text position for a best fuzzy + * match. + */ +Py_LOCAL_INLINE(int) do_best_fuzzy_match(RE_SafeState* safe_state, BOOL search) + { + RE_State* state; + Py_ssize_t available; + int step; + size_t fewest_errors; + BOOL must_advance; + BOOL found_match; + RE_BestList best_list; + Py_ssize_t start_pos; + int status; + TRACE(("<<do_best_fuzzy_match>>\n")) + + state = safe_state->re_state; + + if (state->reverse) { + available = state->text_pos - state->slice_start; + step = -1; + } else { + available = state->slice_end - state->text_pos; + step = 1; + } + + /* The maximum permitted cost. */ + state->max_errors = PY_SSIZE_T_MAX; + fewest_errors = PY_SSIZE_T_MAX; + + state->best_text_pos = state->reverse ? state->slice_start : + state->slice_end; + + must_advance = state->must_advance; + found_match = FALSE; + + make_best_list(&best_list); + + /* Search the text for the best match. */ + start_pos = state->text_pos; + while (state->slice_start <= start_pos && start_pos <= state->slice_end) { + state->text_pos = start_pos; + state->must_advance = must_advance; + + /* Initialise the state. */ + init_match(state); + + status = RE_ERROR_SUCCESS; + if (state->max_errors == 0 && state->partial_side == RE_PARTIAL_NONE) { + /* An exact match, and partial matches not permitted. */ + if (available < state->min_width || (available == 0 && + state->must_advance)) + status = RE_ERROR_FAILURE; + } + + if (status == RE_ERROR_SUCCESS) + status = basic_match(safe_state, search); + + /* Has an error occurred, or is it a partial match? */ + if (status < 0) + break; + + if (status == RE_ERROR_SUCCESS) { + /* It was a successful match. */ + found_match = TRUE; + + if (state->total_errors < fewest_errors) { + /* This match was better than any of the previous ones. */ + fewest_errors = state->total_errors; + + if (state->total_errors == 0) + /* It was a perfect match. */ + break; + + /* Forget all the previous worse matches and remember this one. + */ + clear_best_list(&best_list); + if (!add_to_best_list(safe_state, &best_list, state->match_pos, + state->text_pos)) + return RE_ERROR_MEMORY; + } else if (state->total_errors == fewest_errors) + /* This match was as good as the previous matches. Remember + * this one. + */ + add_to_best_list(safe_state, &best_list, state->match_pos, + state->text_pos); + } + + /* Should we keep searching? */ + if (!search) + break; + + start_pos = state->match_pos + step; + } + + if (found_match) { + /* We found a match. */ + if (fewest_errors > 0) { + /* It doesn't look like a perfect match. */ + int i; + Py_ssize_t slice_start; + Py_ssize_t slice_end; + size_t error_limit; + size_t best_fuzzy_counts[RE_FUZZY_COUNT]; + RE_GroupData* best_groups; + Py_ssize_t best_match_pos; + Py_ssize_t best_text_pos; + + slice_start = state->slice_start; + slice_end = state->slice_end; + + error_limit = fewest_errors; + + if (error_limit > RE_MAX_ERRORS) + error_limit = RE_MAX_ERRORS; + + best_groups = NULL; + + /* Look again at the best of the matches that we've seen. */ + for (i = 0; i < best_list.count; i++) { + RE_BestEntry* entry; + Py_ssize_t max_offset; + Py_ssize_t offset; + + /* Look for the best fit at this position. */ + entry = &best_list.entries[i]; + + if (search) { + max_offset = state->reverse ? entry->match_pos - + state->slice_start : state->slice_end - entry->match_pos; + + if (max_offset > (Py_ssize_t)fewest_errors) + max_offset = (Py_ssize_t)fewest_errors; + + if (max_offset > (Py_ssize_t)error_limit) + max_offset = (Py_ssize_t)error_limit; + } else + max_offset = 0; + + start_pos = entry->match_pos; + offset = 0; + + while (offset <= max_offset) { + state->max_errors = 1; + + while (state->max_errors <= error_limit) { + state->text_pos = start_pos; + init_match(state); + status = basic_match(safe_state, FALSE); + + if (status == RE_ERROR_SUCCESS) { + BOOL better; + + if (state->total_errors < error_limit || i == 0 && + offset == 0) + better = TRUE; + else if (state->total_errors == error_limit) + /* The cost is as low as the current best, but + * is it earlier? + */ + better = state->reverse ? state->match_pos > + best_match_pos : state->match_pos < + best_match_pos; + + if (better) { + save_fuzzy_counts(state, best_fuzzy_counts); + + best_groups = save_groups(safe_state, + best_groups); + if (!best_groups) { + destroy_best_list(safe_state, &best_list); + return RE_ERROR_MEMORY; + } + + best_match_pos = state->match_pos; + best_text_pos = state->text_pos; + error_limit = state->total_errors; + } + + break; + } + + ++state->max_errors; + } + + start_pos += step; + ++offset; + } + + if (status == RE_ERROR_SUCCESS && state->total_errors == 0) + break; + } + + if (best_groups) { + status = RE_ERROR_SUCCESS; + state->match_pos = best_match_pos; + state->text_pos = best_text_pos; + + restore_groups(safe_state, best_groups); + restore_fuzzy_counts(state, best_fuzzy_counts); + } else { + /* None of the "best" matches could be improved on, so pick the + * first. + */ + RE_BestEntry* entry; + + /* Look at only the part of the string around the match. */ + entry = &best_list.entries[0]; + + if (state->reverse) { + state->slice_start = entry->text_pos; + state->slice_end = entry->match_pos; + } else { + state->slice_start = entry->match_pos; + state->slice_end = entry->text_pos; + } + + /* We'll expand the part that we're looking at to take to + * compensate for any matching errors that have occurred. + */ + if (state->slice_start - slice_start >= + (Py_ssize_t)fewest_errors) + state->slice_start -= (Py_ssize_t)fewest_errors; + else + state->slice_start = slice_start; + + if (slice_end - state->slice_end >= (Py_ssize_t)fewest_errors) + state->slice_end += (Py_ssize_t)fewest_errors; + else + state->slice_end = slice_end; + + state->max_errors = fewest_errors; + state->text_pos = entry->match_pos; + init_match(state); + status = basic_match(safe_state, search); + } + + state->slice_start = slice_start; + state->slice_end = slice_end; + } + } + + destroy_best_list(safe_state, &best_list); + + return status; +} + +/* Performs a match or search from the current text position for an enhanced + * fuzzy match. + */ +Py_LOCAL_INLINE(int) do_enhanced_fuzzy_match(RE_SafeState* safe_state, BOOL + search) { + RE_State* state; + PatternObject* pattern; + Py_ssize_t available; + size_t fewest_errors; + RE_GroupData* best_groups; + Py_ssize_t best_match_pos; + BOOL must_advance; + Py_ssize_t slice_start; + Py_ssize_t slice_end; + int status; + size_t best_fuzzy_counts[RE_FUZZY_COUNT]; + Py_ssize_t best_text_pos = 0; /* Initialise to stop compiler warning. */ + TRACE(("<<do_enhanced_fuzzy_match>>\n")) + + state = safe_state->re_state; + pattern = state->pattern; + + if (state->reverse) + available = state->text_pos - state->slice_start; + else + available = state->slice_end - state->text_pos; + + /* The maximum permitted cost. */ + state->max_errors = PY_SSIZE_T_MAX; + fewest_errors = PY_SSIZE_T_MAX; + + best_groups = NULL; + + state->best_match_pos = state->text_pos; + state->best_text_pos = state->reverse ? state->slice_start : + state->slice_end; + + best_match_pos = state->text_pos; + must_advance = state->must_advance; + + slice_start = state->slice_start; + slice_end = state->slice_end; + + for (;;) { + /* If there's a better match, it won't start earlier in the string than + * the current best match, so there's no need to start earlier than + * that match. + */ + state->must_advance = must_advance; + + /* Initialise the state. */ + init_match(state); + + status = RE_ERROR_SUCCESS; + if (state->max_errors == 0 && state->partial_side == RE_PARTIAL_NONE) { + /* An exact match, and partial matches not permitted. */ + if (available < state->min_width || (available == 0 && + state->must_advance)) + status = RE_ERROR_FAILURE; + } + + if (status == RE_ERROR_SUCCESS) + status = basic_match(safe_state, search); + + /* Has an error occurred, or is it a partial match? */ + if (status < 0) + break; + + if (status == RE_ERROR_SUCCESS) { + BOOL better; + + better = state->total_errors < fewest_errors; + + if (better) { + BOOL same_match; + + fewest_errors = state->total_errors; + state->max_errors = fewest_errors; + + save_fuzzy_counts(state, best_fuzzy_counts); + + same_match = state->match_pos == best_match_pos && + state->text_pos == best_text_pos; + same_match = FALSE; + + if (best_groups) { + size_t g; + + /* Did we get the same match as the best so far? */ + for (g = 0; same_match && g < pattern->public_group_count; + g++) { + same_match = state->groups[g].span.start == + best_groups[g].span.start && + state->groups[g].span.end == best_groups[g].span.end; + } + } + + /* Save the best result so far. */ + best_groups = save_groups(safe_state, best_groups); + if (!best_groups) { + status = RE_ERROR_MEMORY; + break; + } + + best_match_pos = state->match_pos; + best_text_pos = state->text_pos; + + if (same_match || state->total_errors == 0) + break; + + state->max_errors = state->total_errors; + if (state->max_errors < RE_MAX_ERRORS) + --state->max_errors; + } else + break; + + if (state->reverse) { + state->slice_start = state->text_pos; + state->slice_end = state->match_pos; + } else { + state->slice_start = state->match_pos; + state->slice_end = state->text_pos; + } + + state->text_pos = state->match_pos; + + if (state->max_errors == PY_SSIZE_T_MAX) + state->max_errors = 0; + } else + break; + } + + state->slice_start = slice_start; + state->slice_end = slice_end; + + if (best_groups) { + if (status == RE_ERROR_SUCCESS && state->total_errors == 0) + /* We have a perfect match, so the previous best match. */ + discard_groups(safe_state, best_groups); + else { + /* Restore the previous best match. */ + status = RE_ERROR_SUCCESS; + + state->match_pos = best_match_pos; + state->text_pos = best_text_pos; + + restore_groups(safe_state, best_groups); + restore_fuzzy_counts(state, best_fuzzy_counts); + } + } + + return status; +} + +/* Performs a match or search from the current text position for a simple fuzzy + * match. + */ +Py_LOCAL_INLINE(int) do_simple_fuzzy_match(RE_SafeState* safe_state, BOOL + search) { + RE_State* state; + Py_ssize_t available; + int status; + TRACE(("<<do_simple_fuzzy_match>>\n")) + + state = safe_state->re_state; + + if (state->reverse) + available = state->text_pos - state->slice_start; + else + available = state->slice_end - state->text_pos; + + /* The maximum permitted cost. */ + state->max_errors = PY_SSIZE_T_MAX; + + state->best_match_pos = state->text_pos; + state->best_text_pos = state->reverse ? state->slice_start : + state->slice_end; + + /* Initialise the state. */ + init_match(state); + + status = RE_ERROR_SUCCESS; + if (state->max_errors == 0 && state->partial_side == RE_PARTIAL_NONE) { + /* An exact match, and partial matches not permitted. */ + if (available < state->min_width || (available == 0 && + state->must_advance)) + status = RE_ERROR_FAILURE; + } + + if (status == RE_ERROR_SUCCESS) + status = basic_match(safe_state, search); + + return status; +} + +/* Performs a match or search from the current text position for an exact + * match. + */ +Py_LOCAL_INLINE(int) do_exact_match(RE_SafeState* safe_state, BOOL search) { + RE_State* state; + Py_ssize_t available; + int status; + TRACE(("<<do_exact_match>>\n")) + + state = safe_state->re_state; + + if (state->reverse) + available = state->text_pos - state->slice_start; + else + available = state->slice_end - state->text_pos; + + /* The maximum permitted cost. */ + state->max_errors = 0; + + state->best_match_pos = state->text_pos; + state->best_text_pos = state->reverse ? state->slice_start : + state->slice_end; + + /* Initialise the state. */ + init_match(state); + + status = RE_ERROR_SUCCESS; + if (state->max_errors == 0 && state->partial_side == RE_PARTIAL_NONE) { + /* An exact match, and partial matches not permitted. */ + if (available < state->min_width || (available == 0 && + state->must_advance)) + status = RE_ERROR_FAILURE; + } + + if (status == RE_ERROR_SUCCESS) + status = basic_match(safe_state, search); + + return status; +} + +/* Performs a match or search from the current text position. + * + * The state can sometimes be shared across threads. In such instances there's + * a lock (mutex) on it. The lock is held for the duration of matching. + */ +Py_LOCAL_INLINE(int) do_match(RE_SafeState* safe_state, BOOL search) { + RE_State* state; + PatternObject* pattern; + int status; + TRACE(("<<do_match>>\n")) + + state = safe_state->re_state; + pattern = state->pattern; + + /* Is there enough to search? */ + if (state->reverse) { + if (state->text_pos < state->slice_start) + return FALSE; + } else { + if (state->text_pos > state->slice_end) + return FALSE; + } + + /* Release the GIL. */ + release_GIL(safe_state); + + if (pattern->is_fuzzy) { + if (pattern->flags & RE_FLAG_BESTMATCH) + status = do_best_fuzzy_match(safe_state, search); + else if (pattern->flags & RE_FLAG_ENHANCEMATCH) + status = do_enhanced_fuzzy_match(safe_state, search); + else + status = do_simple_fuzzy_match(safe_state, search); + } else + status = do_exact_match(safe_state, search); + + if (status == RE_ERROR_SUCCESS || status == RE_ERROR_PARTIAL) { + Py_ssize_t max_end_index; + RE_GroupInfo* group_info; + size_t g; + + /* Store the results. */ + state->lastindex = -1; + state->lastgroup = -1; + max_end_index = -1; + + if (status == RE_ERROR_PARTIAL) { + /* We've matched up to the limit of the slice. */ + if (state->reverse) + state->text_pos = state->slice_start; + else + state->text_pos = state->slice_end; + } + + /* Store the capture groups. */ + group_info = pattern->group_info; + + for (g = 0; g < pattern->public_group_count; g++) { + RE_GroupSpan* span; + + span = &state->groups[g].span; + /* The string positions are of type Py_ssize_t, so the format needs + * to specify that. + */ + TRACE(("group %d from %" PY_FORMAT_SIZE_T "d to %" PY_FORMAT_SIZE_T + "d\n", g + 1, span->start, span->end)) + + if (span->start >= 0 && span->end >= 0 && group_info[g].end_index > + max_end_index) { + max_end_index = group_info[g].end_index; + state->lastindex = (Py_ssize_t)g + 1; + if (group_info[g].has_name) + state->lastgroup = (Py_ssize_t)g + 1; + } + } + } + + /* Re-acquire the GIL. */ + acquire_GIL(safe_state); + + if (status < 0 && status != RE_ERROR_PARTIAL && !PyErr_Occurred()) + set_error(status, NULL); + + return status; +} + +/* Gets a string from a Python object. + * + * If the function returns true and str_info->should_release is true then it's + * the responsibility of the caller to release the buffer when it's no longer + * needed. + */ +Py_LOCAL_INLINE(BOOL) get_string(PyObject* string, RE_StringInfo* str_info) { + /* Given a Python object, return a data pointer, a length (in characters), + * and a character size. Return FALSE if the object is not a string (or not + * compatible). + */ + PyBufferProcs* buffer; + Py_ssize_t bytes; + Py_ssize_t size; + + /* Unicode objects do not support the buffer API. So, get the data directly + * instead. + */ + if (PyUnicode_Check(string)) { + /* Unicode strings doesn't always support the buffer interface. */ + str_info->characters = (void*)PyUnicode_AS_DATA(string); + str_info->length = PyUnicode_GET_SIZE(string); + str_info->charsize = sizeof(Py_UNICODE); + str_info->is_unicode = TRUE; + str_info->should_release = FALSE; + return TRUE; + } + + /* Get pointer to string buffer. */ +#if PY_VERSION_HEX >= 0x02060000 + buffer = Py_TYPE(string)->tp_as_buffer; + str_info->view.len = -1; +#else + buffer = string->ob_type->tp_as_buffer; +#endif + + if (!buffer) { + PyErr_SetString(PyExc_TypeError, "expected string or buffer"); + return FALSE; + } + +#if PY_VERSION_HEX >= 0x02060000 + if (buffer->bf_getbuffer && (*buffer->bf_getbuffer)(string, + &str_info->view, PyBUF_SIMPLE) >= 0) + /* It's a new-style buffer. */ + str_info->should_release = TRUE; + else +#endif + if (buffer->bf_getreadbuffer && buffer->bf_getsegcount && + buffer->bf_getsegcount(string, NULL) == 1) + /* It's an old-style buffer. */ + str_info->should_release = FALSE; + else { + PyErr_SetString(PyExc_TypeError, "expected string or buffer"); + return FALSE; + } + + /* Determine buffer size. */ +#if PY_VERSION_HEX >= 0x02060000 + if (str_info->should_release) { + /* It's a new-style buffer. */ + bytes = str_info->view.len; + str_info->characters = str_info->view.buf; + + if (str_info->characters == NULL) { + PyBuffer_Release(&str_info->view); + PyErr_SetString(PyExc_ValueError, "buffer is NULL"); + return FALSE; + } + } else +#endif + /* It's an old-style buffer. */ + bytes = buffer->bf_getreadbuffer(string, 0, &str_info->characters); + + if (bytes < 0) { +#if PY_VERSION_HEX >= 0x02060000 + if (str_info->should_release) + PyBuffer_Release(&str_info->view); +#endif + PyErr_SetString(PyExc_TypeError, "buffer has negative size"); + return FALSE; + } + + /* Determine character size. */ + size = PyObject_Size(string); + + if (PyString_Check(string) || bytes == size) + str_info->charsize = 1; + else { +#if PY_VERSION_HEX >= 0x02060000 + if (str_info->should_release) + PyBuffer_Release(&str_info->view); +#endif + PyErr_SetString(PyExc_TypeError, "buffer size mismatch"); + return FALSE; + } + + str_info->length = size; + str_info->is_unicode = FALSE; + + return TRUE; +} + +/* Deallocates the groups storage. */ +Py_LOCAL_INLINE(void) dealloc_groups(RE_GroupData* groups, size_t group_count) + { + size_t g; + + if (!groups) + return; + + for (g = 0; g < group_count; g++) + re_dealloc(groups[g].captures); + + re_dealloc(groups); +} + +/* Initialises a state object. */ +Py_LOCAL_INLINE(BOOL) state_init_2(RE_State* state, PatternObject* pattern, + PyObject* string, RE_StringInfo* str_info, Py_ssize_t start, Py_ssize_t end, + BOOL overlapped, int concurrent, BOOL partial, BOOL use_lock, BOOL + visible_captures, BOOL match_all) { + int i; + Py_ssize_t final_pos; + + state->groups = NULL; + state->best_match_groups = NULL; + state->repeats = NULL; + state->visible_captures = visible_captures; + state->match_all = match_all; + state->backtrack_block.previous = NULL; + state->backtrack_block.next = NULL; + state->backtrack_block.capacity = RE_BACKTRACK_BLOCK_SIZE; + state->backtrack_allocated = RE_BACKTRACK_BLOCK_SIZE; + state->current_atomic_block = NULL; + state->first_saved_groups = NULL; + state->current_saved_groups = NULL; + state->first_saved_repeats = NULL; + state->current_saved_repeats = NULL; + state->lock = NULL; + state->fuzzy_guards = NULL; + state->first_group_call_frame = NULL; + state->current_group_call_frame = NULL; + state->group_call_guard_list = NULL; + state->req_pos = -1; + + /* The call guards used by recursive patterns. */ + if (pattern->call_ref_info_count > 0) { + state->group_call_guard_list = + (RE_GuardList*)re_alloc(pattern->call_ref_info_count * + sizeof(RE_GuardList)); + if (!state->group_call_guard_list) + goto error; + memset(state->group_call_guard_list, 0, pattern->call_ref_info_count * + sizeof(RE_GuardList)); + } + + /* The capture groups. */ + if (pattern->true_group_count) { + size_t g; + + if (pattern->groups_storage) { + state->groups = pattern->groups_storage; + pattern->groups_storage = NULL; + } else { + state->groups = (RE_GroupData*)re_alloc(pattern->true_group_count * + sizeof(RE_GroupData)); + if (!state->groups) + goto error; + memset(state->groups, 0, pattern->true_group_count * + sizeof(RE_GroupData)); + + for (g = 0; g < pattern->true_group_count; g++) { + RE_GroupSpan* captures; + + captures = (RE_GroupSpan*)re_alloc(sizeof(RE_GroupSpan)); + if (!captures) { + size_t i; + + for (i = 0; i < g; i++) + re_dealloc(state->groups[i].captures); + + goto error; + } + + state->groups[g].captures = captures; + state->groups[g].capture_capacity = 1; + } + } + } + + /* Adjust boundaries. */ + if (start < 0) + start += str_info->length; + if (start < 0) + start = 0; + else if (start > str_info->length) + start = str_info->length; + + if (end < 0) + end += str_info->length; + if (end < 0) + end = 0; + else if (end > str_info->length) + end = str_info->length; + + state->overlapped = overlapped; + state->min_width = pattern->min_width; + + /* Initialise the getters and setters for the character size. */ + state->charsize = str_info->charsize; + state->is_unicode = str_info->is_unicode; + +#if PY_VERSION_HEX >= 0x02060000 + /* Are we using a buffer object? If so, we need to copy the info. */ + state->should_release = str_info->should_release; + if (state->should_release) + state->view = str_info->view; + +#endif + switch (state->charsize) { + case 1: + state->char_at = bytes1_char_at; + state->set_char_at = bytes1_set_char_at; + state->point_to = bytes1_point_to; + break; + case 2: + state->char_at = bytes2_char_at; + state->set_char_at = bytes2_set_char_at; + state->point_to = bytes2_point_to; + break; + case 4: + state->char_at = bytes4_char_at; + state->set_char_at = bytes4_set_char_at; + state->point_to = bytes4_point_to; + break; + default: + goto error; + } + + state->encoding = pattern->encoding; + state->locale_info = pattern->locale_info; + + /* The state object contains a reference to the string and also a pointer + * to its contents. + * + * The documentation says that the end of the slice behaves like the end of + * the string. + */ + state->text = str_info->characters; + state->text_length = end; + + state->reverse = (pattern->flags & RE_FLAG_REVERSE) != 0; + if (partial) + state->partial_side = state->reverse ? RE_PARTIAL_LEFT : + RE_PARTIAL_RIGHT; + else + state->partial_side = RE_PARTIAL_NONE; + + state->slice_start = start; + state->slice_end = state->text_length; + state->text_pos = state->reverse ? state->slice_end : state->slice_start; + + /* Point to the final newline and line separator if it's at the end of the + * string, otherwise just -1. + */ + state->final_newline = -1; + state->final_line_sep = -1; + final_pos = state->text_length - 1; + if (final_pos >= 0) { + Py_UCS4 ch; + + ch = state->char_at(state->text, final_pos); + if (ch == 0x0A) { + /* The string ends with LF. */ + state->final_newline = final_pos; + state->final_line_sep = final_pos; + + /* Does the string end with CR/LF? */ + --final_pos; + if (final_pos >= 0 && state->char_at(state->text, final_pos) == + 0x0D) + state->final_line_sep = final_pos; + } else { + /* The string doesn't end with LF, but it could be another kind of + * line separator. + */ + if (state->encoding->is_line_sep(ch)) + state->final_line_sep = final_pos; + } + } + + /* If the 'new' behaviour is enabled then split correctly on zero-width + * matches. + */ + state->version_0 = (pattern->flags & RE_FLAG_VERSION1) == 0; + state->must_advance = FALSE; + + state->pattern = pattern; + state->string = string; + + if (pattern->repeat_count) { + if (pattern->repeats_storage) { + state->repeats = pattern->repeats_storage; + pattern->repeats_storage = NULL; + } else { + state->repeats = (RE_RepeatData*)re_alloc(pattern->repeat_count * + sizeof(RE_RepeatData)); + if (!state->repeats) + goto error; + memset(state->repeats, 0, pattern->repeat_count * + sizeof(RE_RepeatData)); + } + } + + if (pattern->fuzzy_count) { + state->fuzzy_guards = (RE_FuzzyGuards*)re_alloc(pattern->fuzzy_count * + sizeof(RE_FuzzyGuards)); + if (!state->fuzzy_guards) + goto error; + memset(state->fuzzy_guards, 0, pattern->fuzzy_count * + sizeof(RE_FuzzyGuards)); + } + + Py_INCREF(state->pattern); + Py_INCREF(state->string); + + /* Multithreading is allowed during matching when explicitly enabled or on + * immutable strings. + */ + switch (concurrent) { + case RE_CONC_NO: + state->is_multithreaded = FALSE; + break; + case RE_CONC_YES: + state->is_multithreaded = TRUE; + break; + default: + state->is_multithreaded = PyUnicode_Check(string) || + PyString_Check(string); + break; + } + + /* A state struct can sometimes be shared across threads. In such + * instances, if multithreading is enabled we need to protect the state + * with a lock (mutex) during matching. + */ + if (state->is_multithreaded && use_lock) + state->lock = PyThread_allocate_lock(); + + for (i = 0; i < MAX_SEARCH_POSITIONS; i++) + state->search_positions[i].start_pos = -1; + + return TRUE; + +error: + re_dealloc(state->group_call_guard_list); + re_dealloc(state->repeats); + dealloc_groups(state->groups, pattern->true_group_count); + re_dealloc(state->fuzzy_guards); + state->repeats = NULL; + state->groups = NULL; + state->fuzzy_guards = NULL; + return FALSE; +} + +#if PY_VERSION_HEX >= 0x02060000 +/* Releases the string's buffer, if necessary. */ +Py_LOCAL_INLINE(void) release_buffer(RE_StringInfo* str_info) { + if (str_info->should_release) + PyBuffer_Release(&str_info->view); +} + +#endif +/* Initialises a state object. */ +Py_LOCAL_INLINE(BOOL) state_init(RE_State* state, PatternObject* pattern, + PyObject* string, Py_ssize_t start, Py_ssize_t end, BOOL overlapped, int + concurrent, BOOL partial, BOOL use_lock, BOOL visible_captures, BOOL + match_all) { + RE_StringInfo str_info; + + /* Get the string to search or match. */ + if (!get_string(string, &str_info)) + return FALSE; + + /* If we fail to initialise the state then we need to release the buffer if + * the string is a buffer object. + */ + if (!state_init_2(state, pattern, string, &str_info, start, end, + overlapped, concurrent, partial, use_lock, visible_captures, match_all)) + { +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return FALSE; + } + + /* The state has been initialised successfully, so now the state has the + * responsibility of releasing the buffer if the string is a buffer object. + */ + return TRUE; +} + +/* Deallocates repeat data. */ +Py_LOCAL_INLINE(void) dealloc_repeats(RE_RepeatData* repeats, size_t + repeat_count) { + size_t i; + + if (!repeats) + return; + + for (i = 0; i < repeat_count; i++) { + re_dealloc(repeats[i].body_guard_list.spans); + re_dealloc(repeats[i].tail_guard_list.spans); + } + + re_dealloc(repeats); +} + +/* Deallocates fuzzy guards. */ +Py_LOCAL_INLINE(void) dealloc_fuzzy_guards(RE_FuzzyGuards* guards, size_t + fuzzy_count) { + size_t i; + + if (!guards) + return; + + for (i = 0; i < fuzzy_count; i++) { + re_dealloc(guards[i].body_guard_list.spans); + re_dealloc(guards[i].tail_guard_list.spans); + } + + re_dealloc(guards); +} + +/* Finalises a state object, discarding its contents. */ +Py_LOCAL_INLINE(void) state_fini(RE_State* state) { + RE_BacktrackBlock* current_backtrack; + RE_AtomicBlock* current_atomic; + PatternObject* pattern; + RE_SavedGroups* saved_groups; + RE_SavedRepeats* saved_repeats; + RE_GroupCallFrame* frame; + size_t i; + + /* Discard the lock (mutex) if there's one. */ + if (state->lock) + PyThread_free_lock(state->lock); + + /* Deallocate the backtrack blocks. */ + current_backtrack = state->backtrack_block.next; + while (current_backtrack) { + RE_BacktrackBlock* next; + + next = current_backtrack->next; + re_dealloc(current_backtrack); + state->backtrack_allocated -= RE_BACKTRACK_BLOCK_SIZE; + current_backtrack = next; + } + + /* Deallocate the atomic blocks. */ + current_atomic = state->current_atomic_block; + while (current_atomic) { + RE_AtomicBlock* next; + + next = current_atomic->next; + re_dealloc(current_atomic); + current_atomic = next; + } + + state->current_atomic_block = NULL; + + pattern = state->pattern; + + saved_groups = state->first_saved_groups; + while (saved_groups) { + RE_SavedGroups* next; + + next = saved_groups->next; + re_dealloc(saved_groups->spans); + re_dealloc(saved_groups->counts); + re_dealloc(saved_groups); + saved_groups = next; + } + + saved_repeats = state->first_saved_repeats; + while (saved_repeats) { + RE_SavedRepeats* next; + + next = saved_repeats->next; + + dealloc_repeats(saved_repeats->repeats, pattern->repeat_count); + + re_dealloc(saved_repeats); + saved_repeats = next; + } + + if (state->best_match_groups) + dealloc_groups(state->best_match_groups, pattern->true_group_count); + + if (pattern->groups_storage) + dealloc_groups(state->groups, pattern->true_group_count); + else + pattern->groups_storage = state->groups; + + if (pattern->repeats_storage) + dealloc_repeats(state->repeats, pattern->repeat_count); + else + pattern->repeats_storage = state->repeats; + + frame = state->first_group_call_frame; + while (frame) { + RE_GroupCallFrame* next; + + next = frame->next; + + dealloc_groups(frame->groups, pattern->true_group_count); + dealloc_repeats(frame->repeats, pattern->repeat_count); + + re_dealloc(frame); + frame = next; + } + + for (i = 0; i < pattern->call_ref_info_count; i++) + re_dealloc(state->group_call_guard_list[i].spans); + + if (state->group_call_guard_list) + re_dealloc(state->group_call_guard_list); + + if (state->fuzzy_guards) + dealloc_fuzzy_guards(state->fuzzy_guards, pattern->fuzzy_count); + + Py_DECREF(state->pattern); + Py_DECREF(state->string); +#if PY_VERSION_HEX >= 0x02060000 + + if (state->should_release) + PyBuffer_Release(&state->view); +#endif +} + +/* Converts a string index to an integer. + * + * If the index is None then the default will be returned. + */ +Py_LOCAL_INLINE(Py_ssize_t) as_string_index(PyObject* obj, Py_ssize_t def) { + Py_ssize_t value; + + if (obj == Py_None) + return def; + + value = PyInt_AsSsize_t(obj); + if (value != -1 || !PyErr_Occurred()) + return value; + + PyErr_Clear(); + + value = PyLong_AsLong(obj); + if (value != -1 || !PyErr_Occurred()) + return value; + + set_error(RE_ERROR_INDEX, NULL); + return 0; +} + +/* Deallocates a MatchObject. */ +static void match_dealloc(PyObject* self_) { + MatchObject* self; + + self = (MatchObject*)self_; + + Py_XDECREF(self->string); + Py_XDECREF(self->substring); + Py_DECREF(self->pattern); + if (self->groups) + re_dealloc(self->groups); + Py_XDECREF(self->regs); + PyObject_DEL(self); +} + +/* Restricts a value to a range. */ +Py_LOCAL_INLINE(Py_ssize_t) limited_range(Py_ssize_t value, Py_ssize_t lower, + Py_ssize_t upper) { + if (value < lower) + return lower; + + if (value > upper) + return upper; + + return value; +} + +/* Gets a slice from a Unicode string. */ +Py_LOCAL_INLINE(PyObject*) unicode_slice(PyObject* string, Py_ssize_t start, + Py_ssize_t end) { + Py_ssize_t length; + Py_UNICODE* buffer; + + length = PyUnicode_GET_SIZE(string); + start = limited_range(start, 0, length); + end = limited_range(end, 0, length); + + buffer = PyUnicode_AsUnicode(string); + + return PyUnicode_FromUnicode(buffer + start, end - start); +} + +/* Gets a slice from a bytestring. */ +Py_LOCAL_INLINE(PyObject*) bytes_slice(PyObject* string, Py_ssize_t start, + Py_ssize_t end) { + Py_ssize_t length; + char* buffer; + + length = PyString_GET_SIZE(string); + start = limited_range(start, 0, length); + end = limited_range(end, 0, length); + + buffer = PyString_AsString(string); + + return PyString_FromStringAndSize(buffer + start, end - start); +} + +/* Gets a slice from a string, returning either a Unicode string or a + * bytestring. + */ +Py_LOCAL_INLINE(PyObject*) get_slice(PyObject* string, Py_ssize_t start, + Py_ssize_t end) { + if (PyUnicode_Check(string)) + return unicode_slice(string, start, end); + + if (PyString_Check(string)) + return bytes_slice(string, start, end); + + return PySequence_GetSlice(string, start, end); +} + +/* Gets a MatchObject's group by integer index. */ +static PyObject* match_get_group_by_index(MatchObject* self, Py_ssize_t index, + PyObject* def) { + RE_GroupSpan* span; + + if (index < 0 || (size_t)index > self->group_count) { + /* Raise error if we were given a bad group number. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } + + if (index == 0) + return get_slice(self->substring, self->match_start - + self->substring_offset, self->match_end - self->substring_offset); + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + span = &self->groups[index - 1].span; + + if (span->start < 0 || span->end < 0) { + /* Return default value if the string or group is undefined. */ + Py_INCREF(def); + return def; + } + + return get_slice(self->substring, span->start - self->substring_offset, + span->end - self->substring_offset); +} + +/* Gets a MatchObject's start by integer index. */ +static PyObject* match_get_start_by_index(MatchObject* self, Py_ssize_t index) + { + RE_GroupSpan* span; + + if (index < 0 || (size_t)index > self->group_count) { + /* Raise error if we were given a bad group number. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } + + if (index == 0) + return Py_BuildValue("n", self->match_start); + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + span = &self->groups[index - 1].span; + return Py_BuildValue("n", span->start); +} + +/* Gets a MatchObject's starts by integer index. */ +static PyObject* match_get_starts_by_index(MatchObject* self, Py_ssize_t index) + { + RE_GroupData* group; + PyObject* result; + PyObject* item; + size_t i; + + if (index < 0 || (size_t)index > self->group_count) { + /* Raise error if we were given a bad group number. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } + + if (index == 0) { + result = PyList_New(1); + if (!result) + return NULL; + + item = Py_BuildValue("n", self->match_start); + if (!item) + goto error; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(result, 0, item); + + return result; + } + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + group = &self->groups[index - 1]; + + result = PyList_New((Py_ssize_t)group->capture_count); + if (!result) + return NULL; + + for (i = 0; i < group->capture_count; i++) { + item = Py_BuildValue("n", group->captures[i].start); + if (!item) + goto error; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(result, i, item); + } + + return result; + +error: + Py_DECREF(result); + return NULL; +} + +/* Gets a MatchObject's end by integer index. */ +static PyObject* match_get_end_by_index(MatchObject* self, Py_ssize_t index) { + RE_GroupSpan* span; + + if (index < 0 || (size_t)index > self->group_count) { + /* Raise error if we were given a bad group number. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } + + if (index == 0) + return Py_BuildValue("n", self->match_end); + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + span = &self->groups[index - 1].span; + return Py_BuildValue("n", span->end); +} + +/* Gets a MatchObject's ends by integer index. */ +static PyObject* match_get_ends_by_index(MatchObject* self, Py_ssize_t index) { + RE_GroupData* group; + PyObject* result; + PyObject* item; + size_t i; + + if (index < 0 || (size_t)index > self->group_count) { + /* Raise error if we were given a bad group number. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } + + if (index == 0) { + result = PyList_New(1); + if (!result) + return NULL; + + item = Py_BuildValue("n", self->match_end); + if (!item) + goto error; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(result, 0, item); + + return result; + } + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + group = &self->groups[index - 1]; + + result = PyList_New((Py_ssize_t)group->capture_count); + if (!result) + return NULL; + + for (i = 0; i < group->capture_count; i++) { + item = Py_BuildValue("n", group->captures[i].end); + if (!item) + goto error; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(result, i, item); + } + + return result; + +error: + Py_DECREF(result); + return NULL; +} + +/* Gets a MatchObject's span by integer index. */ +static PyObject* match_get_span_by_index(MatchObject* self, Py_ssize_t index) { + RE_GroupSpan* span; + + if (index < 0 || (size_t)index > self->group_count) { + /* Raise error if we were given a bad group number. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } + + if (index == 0) + return Py_BuildValue("nn", self->match_start, self->match_end); + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + span = &self->groups[index - 1].span; + return Py_BuildValue("nn", span->start, span->end); +} + +/* Gets a MatchObject's spans by integer index. */ +static PyObject* match_get_spans_by_index(MatchObject* self, Py_ssize_t index) + { + PyObject* result; + PyObject* item; + RE_GroupData* group; + size_t i; + + if (index < 0 || (size_t)index > self->group_count) { + /* Raise error if we were given a bad group number. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } + + if (index == 0) { + result = PyList_New(1); + if (!result) + return NULL; + + item = Py_BuildValue("nn", self->match_start, self->match_end); + if (!item) + goto error; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(result, 0, item); + + return result; + } + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + group = &self->groups[index - 1]; + + result = PyList_New((Py_ssize_t)group->capture_count); + if (!result) + return NULL; + + for (i = 0; i < group->capture_count; i++) { + item = Py_BuildValue("nn", group->captures[i].start, + group->captures[i].end); + if (!item) + goto error; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(result, i, item); + } + + return result; + +error: + Py_DECREF(result); + return NULL; +} + +/* Gets a MatchObject's captures by integer index. */ +static PyObject* match_get_captures_by_index(MatchObject* self, Py_ssize_t + index) { + PyObject* result; + PyObject* slice; + RE_GroupData* group; + size_t i; + + if (index < 0 || (size_t)index > self->group_count) { + /* Raise error if we were given a bad group number. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } + + if (index == 0) { + result = PyList_New(1); + if (!result) + return NULL; + + slice = get_slice(self->substring, self->match_start - + self->substring_offset, self->match_end - self->substring_offset); + if (!slice) + goto error; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(result, 0, slice); + + return result; + } + + /* Capture group indexes are 1-based (excluding group 0, which is the + * entire matched string). + */ + group = &self->groups[index - 1]; + + result = PyList_New((Py_ssize_t)group->capture_count); + if (!result) + return NULL; + + for (i = 0; i < group->capture_count; i++) { + slice = get_slice(self->substring, group->captures[i].start - + self->substring_offset, group->captures[i].end - + self->substring_offset); + if (!slice) + goto error; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(result, i, slice); + } + + return result; + +error: + Py_DECREF(result); + return NULL; +} + +/* Converts a group index to an integer. */ +Py_LOCAL_INLINE(Py_ssize_t) as_group_index(PyObject* obj) { + Py_ssize_t value; + + value = PyInt_AsSsize_t(obj); + if (value != -1 || !PyErr_Occurred()) + return value; + + PyErr_Clear(); + + value = PyLong_AsLong(obj); + if (value != -1 || !PyErr_Occurred()) + return value; + + set_error(RE_ERROR_INDEX, NULL); + return -1; +} + +/* Gets a MatchObject's group index. + * + * The supplied index can be an integer or a string (group name) object. + */ +Py_LOCAL_INLINE(Py_ssize_t) match_get_group_index(MatchObject* self, PyObject* + index, BOOL allow_neg) { + Py_ssize_t group; + + /* Is the index an integer? */ + group = as_group_index(index); + if (group != -1 || !PyErr_Occurred()) { + Py_ssize_t min_group = 0; + + /* Adjust negative indices where valid and allowed. */ + if (group < 0 && allow_neg) { + group += (Py_ssize_t)self->group_count + 1; + min_group = 1; + } + + if (min_group <= group && (size_t)group <= self->group_count) + return group; + + return -1; + } + + /* The index might be a group name. */ + if (self->pattern->groupindex) { + /* Look up the name. */ + PyErr_Clear(); + + index = PyObject_GetItem(self->pattern->groupindex, index); + if (index) { + /* Check that we have an integer. */ + group = as_group_index(index); + Py_DECREF(index); + if (group != -1 || !PyErr_Occurred()) + return group; + } + } + + PyErr_Clear(); + return -1; +} + +/* Gets a MatchObject's group by object index. */ +Py_LOCAL_INLINE(PyObject*) match_get_group(MatchObject* self, PyObject* index, + PyObject* def, BOOL allow_neg) { + /* Check that the index is an integer or a string. */ + if (PyInt_Check(index) || PyLong_Check(index) || PyUnicode_Check(index) || + PyString_Check(index)) + return match_get_group_by_index(self, match_get_group_index(self, + index, allow_neg), def); + + set_error(RE_ERROR_GROUP_INDEX_TYPE, index); + return NULL; +} + +/* Gets info from a MatchObject by object index. */ +Py_LOCAL_INLINE(PyObject*) get_by_arg(MatchObject* self, PyObject* index, + RE_GetByIndexFunc get_by_index) { + /* Check that the index is an integer or a string. */ + if (PyInt_Check(index) || PyLong_Check(index) || PyUnicode_Check(index) || + PyString_Check(index)) + return get_by_index(self, match_get_group_index(self, index, FALSE)); + + set_error(RE_ERROR_GROUP_INDEX_TYPE, index); + return NULL; +} + +/* MatchObject's 'group' method. */ +static PyObject* match_group(MatchObject* self, PyObject* args) { + Py_ssize_t size; + PyObject* result; + Py_ssize_t i; + + size = PyTuple_GET_SIZE(args); + + switch (size) { + case 0: + /* group() */ + result = match_get_group_by_index(self, 0, Py_None); + break; + case 1: + /* group(x). PyTuple_GET_ITEM borrows the reference. */ + result = match_get_group(self, PyTuple_GET_ITEM(args, 0), Py_None, + FALSE); + break; + default: + /* group(x, y, z, ...) */ + /* Fetch multiple items. */ + result = PyTuple_New(size); + if (!result) + return NULL; + + for (i = 0; i < size; i++) { + PyObject* item; + + /* PyTuple_GET_ITEM borrows the reference. */ + item = match_get_group(self, PyTuple_GET_ITEM(args, i), Py_None, + FALSE); + if (!item) { + Py_DECREF(result); + return NULL; + } + + /* PyTuple_SET_ITEM borrows the reference. */ + PyTuple_SET_ITEM(result, i, item); + } + break; + } + + return result; +} + +/* Generic method for getting info from a MatchObject. */ +Py_LOCAL_INLINE(PyObject*) get_from_match(MatchObject* self, PyObject* args, + RE_GetByIndexFunc get_by_index) { + Py_ssize_t size; + PyObject* result; + Py_ssize_t i; + + size = PyTuple_GET_SIZE(args); + + switch (size) { + case 0: + /* get() */ + result = get_by_index(self, 0); + break; + case 1: + /* get(x). PyTuple_GET_ITEM borrows the reference. */ + result = get_by_arg(self, PyTuple_GET_ITEM(args, 0), get_by_index); + break; + default: + /* get(x, y, z, ...) */ + /* Fetch multiple items. */ + result = PyTuple_New(size); + if (!result) + return NULL; + + for (i = 0; i < size; i++) { + PyObject* item; + + /* PyTuple_GET_ITEM borrows the reference. */ + item = get_by_arg(self, PyTuple_GET_ITEM(args, i), get_by_index); + if (!item) { + Py_DECREF(result); + return NULL; + } + + /* PyTuple_SET_ITEM borrows the reference. */ + PyTuple_SET_ITEM(result, i, item); + } + break; + } + + return result; +} + +/* MatchObject's 'start' method. */ +static PyObject* match_start(MatchObject* self, PyObject* args) { + return get_from_match(self, args, match_get_start_by_index); +} + +/* MatchObject's 'starts' method. */ +static PyObject* match_starts(MatchObject* self, PyObject* args) { + return get_from_match(self, args, match_get_starts_by_index); +} + +/* MatchObject's 'end' method. */ +static PyObject* match_end(MatchObject* self, PyObject* args) { + return get_from_match(self, args, match_get_end_by_index); +} + +/* MatchObject's 'ends' method. */ +static PyObject* match_ends(MatchObject* self, PyObject* args) { + return get_from_match(self, args, match_get_ends_by_index); +} + +/* MatchObject's 'span' method. */ +static PyObject* match_span(MatchObject* self, PyObject* args) { + return get_from_match(self, args, match_get_span_by_index); +} + +/* MatchObject's 'spans' method. */ +static PyObject* match_spans(MatchObject* self, PyObject* args) { + return get_from_match(self, args, match_get_spans_by_index); +} + +/* MatchObject's 'captures' method. */ +static PyObject* match_captures(MatchObject* self, PyObject* args) { + return get_from_match(self, args, match_get_captures_by_index); +} + +/* MatchObject's 'groups' method. */ +static PyObject* match_groups(MatchObject* self, PyObject* args, PyObject* + kwargs) { + PyObject* result; + size_t g; + + PyObject* def = Py_None; + static char* kwlist[] = { "default", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:groups", kwlist, &def)) + return NULL; + + result = PyTuple_New((Py_ssize_t)self->group_count); + if (!result) + return NULL; + + /* Group 0 is the entire matched portion of the string. */ + for (g = 0; g < self->group_count; g++) { + PyObject* item; + + item = match_get_group_by_index(self, (Py_ssize_t)g + 1, def); + if (!item) + goto error; + + /* PyTuple_SET_ITEM borrows the reference. */ + PyTuple_SET_ITEM(result, g, item); + } + + return result; + +error: + Py_DECREF(result); + return NULL; +} + +/* MatchObject's 'groupdict' method. */ +static PyObject* match_groupdict(MatchObject* self, PyObject* args, PyObject* + kwargs) { + PyObject* result; + PyObject* keys; + Py_ssize_t g; + + PyObject* def = Py_None; + static char* kwlist[] = { "default", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:groupdict", kwlist, + &def)) + return NULL; + + result = PyDict_New(); + if (!result || !self->pattern->groupindex) + return result; + + keys = PyMapping_Keys(self->pattern->groupindex); + if (!keys) + goto failed; + + for (g = 0; g < PyList_GET_SIZE(keys); g++) { + PyObject* key; + PyObject* value; + int status; + + /* PyList_GET_ITEM borrows a reference. */ + key = PyList_GET_ITEM(keys, g); + if (!key) + goto failed; + + value = match_get_group(self, key, def, FALSE); + if (!value) + goto failed; + + status = PyDict_SetItem(result, key, value); + Py_DECREF(value); + if (status < 0) + goto failed; + } + + Py_DECREF(keys); + + return result; + +failed: + Py_XDECREF(keys); + Py_DECREF(result); + return NULL; +} + +/* MatchObject's 'capturesdict' method. */ +static PyObject* match_capturesdict(MatchObject* self) { + PyObject* result; + PyObject* keys; + Py_ssize_t g; + + result = PyDict_New(); + if (!result || !self->pattern->groupindex) + return result; + + keys = PyMapping_Keys(self->pattern->groupindex); + if (!keys) + goto failed; + + for (g = 0; g < PyList_GET_SIZE(keys); g++) { + PyObject* key; + Py_ssize_t group; + PyObject* captures; + int status; + + /* PyList_GET_ITEM borrows a reference. */ + key = PyList_GET_ITEM(keys, g); + if (!key) + goto failed; + + group = match_get_group_index(self, key, FALSE); + if (group < 0) + goto failed; + + captures = match_get_captures_by_index(self, group); + if (!captures) + goto failed; + + status = PyDict_SetItem(result, key, captures); + Py_DECREF(captures); + if (status < 0) + goto failed; + } + + Py_DECREF(keys); + + return result; + +failed: + Py_XDECREF(keys); + Py_DECREF(result); + return NULL; +} + +/* Gets a Python object by name from a named module. */ +Py_LOCAL_INLINE(PyObject*) get_object(char* module_name, char* object_name) { + PyObject* module; + PyObject* object; + + module = PyImport_ImportModule(module_name); + if (!module) + return NULL; + + object = PyObject_GetAttrString(module, object_name); + Py_DECREF(module); + + return object; +} + +/* Calls a function in a module. */ +Py_LOCAL_INLINE(PyObject*) call(char* module_name, char* function_name, + PyObject* args) { + PyObject* function; + PyObject* result; + + if (!args) + return NULL; + + function = get_object(module_name, function_name); + if (!function) + return NULL; + + result = PyObject_CallObject(function, args); + Py_DECREF(function); + Py_DECREF(args); + + return result; +} + +/* Gets a replacement item from the replacement list. + * + * The replacement item could be a string literal or a group. + */ +Py_LOCAL_INLINE(PyObject*) get_match_replacement(MatchObject* self, PyObject* + item, size_t group_count) { + Py_ssize_t index; + + if (PyUnicode_Check(item) || PyString_Check(item)) { + /* It's a literal, which can be added directly to the list. */ + Py_INCREF(item); + return item; + } + + /* Is it a group reference? */ + index = as_group_index(item); + if (index == -1 && PyErr_Occurred()) { + /* Not a group either! */ + set_error(RE_ERROR_REPLACEMENT, NULL); + return NULL; + } + + if (index == 0) { + /* The entire matched portion of the string. */ + return get_slice(self->substring, self->match_start - + self->substring_offset, self->match_end - self->substring_offset); + } else if (index >= 1 && (size_t)index <= group_count) { + /* A group. If it didn't match then return None instead. */ + RE_GroupData* group; + + group = &self->groups[index - 1]; + + if (group->capture_count > 0) + return get_slice(self->substring, group->span.start - + self->substring_offset, group->span.end - + self->substring_offset); + else { + Py_INCREF(Py_None); + return Py_None; + } + } else { + /* No such group. */ + set_error(RE_ERROR_NO_SUCH_GROUP, NULL); + return NULL; + } +} + +/* Initialises the join list. */ +Py_LOCAL_INLINE(void) init_join_list(JoinInfo* join_info, BOOL reversed, BOOL + is_unicode) { + join_info->list = NULL; + join_info->item = NULL; + join_info->reversed = reversed; + join_info->is_unicode = is_unicode; +} + +/* Adds an item to the join list. */ +Py_LOCAL_INLINE(int) add_to_join_list(JoinInfo* join_info, PyObject* item) { + PyObject* new_item; + int status; + + if (join_info->is_unicode) { + if (PyUnicode_Check(item)) { + new_item = item; + Py_INCREF(new_item); + } else { + new_item = PyUnicode_FromObject(item); + if (!new_item) { + set_error(RE_ERROR_NOT_UNICODE, item); + return RE_ERROR_NOT_UNICODE; + } + } + } else { + if (PyString_Check(item)) { + new_item = item; + Py_INCREF(new_item); + } else { + new_item = PyUnicode_FromObject(item); + if (!new_item) { + set_error(RE_ERROR_NOT_STRING, item); + return RE_ERROR_NOT_STRING; + } + } + } + + /* If the list already exists then just add the item to it. */ + if (join_info->list) { + status = PyList_Append(join_info->list, new_item); + if (status < 0) + goto error; + + Py_DECREF(new_item); + return status; + } + + /* If we already have an item then we now have 2(!) and we need to put them + * into a list. + */ + if (join_info->item) { + join_info->list = PyList_New(2); + if (!join_info->list) { + status = RE_ERROR_MEMORY; + goto error; + } + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(join_info->list, 0, join_info->item); + join_info->item = NULL; + + /* PyList_SET_ITEM borrows the reference. */ + PyList_SET_ITEM(join_info->list, 1, new_item); + return 0; + } + + /* This is the first item. */ + join_info->item = new_item; + + return 0; + +error: + Py_DECREF(new_item); + set_error(status, NULL); + return status; +} + +/* Clears the join list. */ +Py_LOCAL_INLINE(void) clear_join_list(JoinInfo* join_info) { + Py_XDECREF(join_info->list); + Py_XDECREF(join_info->item); +} + +/* Joins together a list of strings for pattern_subx. */ +Py_LOCAL_INLINE(PyObject*) join_list_info(JoinInfo* join_info) { + /* If the list already exists then just do the join. */ + if (join_info->list) { + PyObject* joiner; + PyObject* result; + + if (join_info->reversed) + /* The list needs to be reversed before being joined. */ + PyList_Reverse(join_info->list); + + if (join_info->is_unicode) { + /* Concatenate the Unicode strings. */ + joiner = PyUnicode_FromUnicode(NULL, 0); + if (!joiner) { + clear_join_list(join_info); + return NULL; + } + + result = PyUnicode_Join(joiner, join_info->list); + } else { + joiner = PyString_FromString(""); + if (!joiner) { + clear_join_list(join_info); + return NULL; + } + + /* Concatenate the bytestrings. */ + result = _PyString_Join(joiner, join_info->list); + } + + Py_DECREF(joiner); + clear_join_list(join_info); + + return result; + } + + /* If we have only 1 item, so we'll just return it. */ + if (join_info->item) + return join_info->item; + + /* There are no items, so return an empty string. */ + if (join_info->is_unicode) + return PyUnicode_FromUnicode(NULL, 0); + else + return PyString_FromString(""); +} + +/* Checks whether a string replacement is a literal. + * + * To keep it simple we'll say that a literal is a string which can be used + * as-is. + * + * Returns its length if it is a literal, otherwise -1. + */ +Py_LOCAL_INLINE(Py_ssize_t) check_replacement_string(PyObject* str_replacement, + unsigned char special_char) { + RE_StringInfo str_info; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + Py_ssize_t pos; + + if (!get_string(str_replacement, &str_info)) + return -1; + + switch (str_info.charsize) { + case 1: + char_at = bytes1_char_at; + break; + case 2: + char_at = bytes2_char_at; + break; + case 4: + char_at = bytes4_char_at; + break; + default: +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); +#endif + return -1; + } + + for (pos = 0; pos < str_info.length; pos++) { + if (char_at(str_info.characters, pos) == special_char) { +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return -1; + } + } + +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return str_info.length; +} + +/* MatchObject's 'expand' method. */ +static PyObject* match_expand(MatchObject* self, PyObject* str_template) { + Py_ssize_t literal_length; + PyObject* replacement; + JoinInfo join_info; + Py_ssize_t size; + Py_ssize_t i; + + /* Is the template just a literal? */ + literal_length = check_replacement_string(str_template, '\\'); + if (literal_length >= 0) { + /* It's a literal. */ + Py_INCREF(str_template); + return str_template; + } + + /* Hand the template to the template compiler. */ + replacement = call(RE_MODULE, "_compile_replacement_helper", + PyTuple_Pack(2, self->pattern, str_template)); + if (!replacement) + return NULL; + + init_join_list(&join_info, FALSE, PyUnicode_Check(self->string)); + + /* Add each part of the template to the list. */ + size = PyList_GET_SIZE(replacement); + for (i = 0; i < size; i++) { + PyObject* item; + PyObject* str_item; + + /* PyList_GET_ITEM borrows a reference. */ + item = PyList_GET_ITEM(replacement, i); + str_item = get_match_replacement(self, item, self->group_count); + if (!str_item) + goto error; + + /* Add to the list. */ + if (str_item == Py_None) + Py_DECREF(str_item); + else { + int status; + + status = add_to_join_list(&join_info, str_item); + Py_DECREF(str_item); + if (status < 0) + goto error; + } + } + + Py_DECREF(replacement); + + /* Convert the list to a single string (also cleans up join_info). */ + return join_list_info(&join_info); + +error: + clear_join_list(&join_info); + Py_DECREF(replacement); + return NULL; +} + +#if PY_VERSION_HEX >= 0x02060000 +/* Gets a MatchObject's group dictionary. */ +Py_LOCAL_INLINE(PyObject*) match_get_group_dict(MatchObject* self) { + PyObject* result; + PyObject* keys; + Py_ssize_t g; + + result = PyDict_New(); + if (!result || !self->pattern->groupindex) + return result; + + keys = PyMapping_Keys(self->pattern->groupindex); + if (!keys) + goto failed; + + for (g = 0; g < PyList_GET_SIZE(keys); g++) { + PyObject* key; + PyObject* value; + int status; + + /* PyList_GET_ITEM borrows a reference. */ + key = PyList_GET_ITEM(keys, g); + if (!key) + goto failed; + + value = match_get_group(self, key, Py_None, FALSE); + if (!value) + goto failed; + + status = PyDict_SetItem(result, key, value); + Py_DECREF(value); + if (status < 0) + goto failed; + } + + Py_DECREF(keys); + + return result; + +failed: + Py_XDECREF(keys); + Py_DECREF(result); + return NULL; +} + +static PyTypeObject Capture_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "_" RE_MODULE "." "Capture", + sizeof(MatchObject) +}; + +/* Creates a new CaptureObject. */ +Py_LOCAL_INLINE(PyObject*) make_capture_object(MatchObject** match_indirect, + Py_ssize_t index) { + CaptureObject* capture; + + capture = PyObject_NEW(CaptureObject, &Capture_Type); + if (!capture) + return NULL; + + capture->group_index = index; + capture->match_indirect = match_indirect; + + return (PyObject*)capture; +} + +#if PY_VERSION_HEX >= 0x02060000 +/* Makes a MatchObject's capture dictionary. */ +Py_LOCAL_INLINE(PyObject*) make_capture_dict(MatchObject* match, MatchObject** + match_indirect) { + PyObject* result; + PyObject* keys; + PyObject* values = NULL; + Py_ssize_t g; + + result = PyDict_New(); + if (!result) + return result; + + keys = PyMapping_Keys(match->pattern->groupindex); + if (!keys) + goto failed; + + values = PyMapping_Values(match->pattern->groupindex); + if (!values) + goto failed; + + for (g = 0; g < PyList_GET_SIZE(keys); g++) { + PyObject* key; + PyObject* value; + Py_ssize_t v; + int status; + + /* PyList_GET_ITEM borrows a reference. */ + key = PyList_GET_ITEM(keys, g); + if (!key) + goto failed; + + /* PyList_GET_ITEM borrows a reference. */ + value = PyList_GET_ITEM(values, g); + if (!value) + goto failed; + + v = PyLong_AsLong(value); + if (v == -1 && PyErr_Occurred()) + goto failed; + + value = make_capture_object(match_indirect, v); + if (!value) + goto failed; + + status = PyDict_SetItem(result, key, value); + Py_DECREF(value); + if (status < 0) + goto failed; + } + + Py_DECREF(values); + Py_DECREF(keys); + + return result; + +failed: + Py_XDECREF(values); + Py_XDECREF(keys); + Py_DECREF(result); + return NULL; +} +#endif + +/* MatchObject's 'expandf' method. */ +static PyObject* match_expandf(MatchObject* self, PyObject* str_template) { + PyObject* format_func; + PyObject* args = NULL; + size_t g; + PyObject* kwargs = NULL; + PyObject* result; + + format_func = PyObject_GetAttrString(str_template, "format"); + if (!format_func) + return NULL; + + args = PyTuple_New((Py_ssize_t)self->group_count + 1); + if (!args) + goto error; + + for (g = 0; g < self->group_count + 1; g++) + /* PyTuple_SetItem borrows the reference. */ + PyTuple_SetItem(args, (Py_ssize_t)g, make_capture_object(&self, + (Py_ssize_t)g)); + + kwargs = make_capture_dict(self, &self); + if (!kwargs) + goto error; + + result = PyObject_Call(format_func, args, kwargs); + + Py_DECREF(kwargs); + Py_DECREF(args); + Py_DECREF(format_func); + + return result; + +error: + Py_XDECREF(args); + Py_DECREF(format_func); + return NULL; +} + +#endif +Py_LOCAL_INLINE(PyObject*) make_match_copy(MatchObject* self); + +/* MatchObject's '__copy__' method. */ +static PyObject* match_copy(MatchObject* self, PyObject *unused) { + return make_match_copy(self); +} + +/* MatchObject's '__deepcopy__' method. */ +static PyObject* match_deepcopy(MatchObject* self, PyObject* memo) { + return make_match_copy(self); +} + +/* MatchObject's 'regs' attribute. */ +static PyObject* match_regs(MatchObject* self) { + PyObject* regs; + PyObject* item; + size_t g; + + regs = PyTuple_New((Py_ssize_t)self->group_count + 1); + if (!regs) + return NULL; + + item = Py_BuildValue("nn", self->match_start, self->match_end); + if (!item) + goto error; + + /* PyTuple_SET_ITEM borrows the reference. */ + PyTuple_SET_ITEM(regs, 0, item); + + for (g = 0; g < self->group_count; g++) { + RE_GroupSpan* span; + + span = &self->groups[g].span; + item = Py_BuildValue("nn", span->start, span->end); + if (!item) + goto error; + + /* PyTuple_SET_ITEM borrows the reference. */ + PyTuple_SET_ITEM(regs, g + 1, item); + } + + Py_INCREF(regs); + self->regs = regs; + + return regs; + +error: + Py_DECREF(regs); + return NULL; +} + +/* MatchObject's slice method. */ +Py_LOCAL_INLINE(PyObject*) match_get_group_slice(MatchObject* self, PyObject* + slice) { + Py_ssize_t start; + Py_ssize_t end; + Py_ssize_t step; + Py_ssize_t slice_length; + + if (PySlice_GetIndicesEx((PySliceObject*)slice, + (Py_ssize_t)self->group_count + 1, &start, &end, &step, &slice_length) < + 0) + return NULL; + + if (slice_length <= 0) + return PyTuple_New(0); + else { + PyObject* result; + Py_ssize_t cur; + Py_ssize_t i; + + result = PyTuple_New(slice_length); + if (!result) + return NULL; + + cur = start; + for (i = 0; i < slice_length; i++) { + /* PyTuple_SetItem borrows the reference. */ + PyTuple_SetItem(result, i, match_get_group_by_index(self, cur, + Py_None)); + cur += step; + } + + return result; + } +} + +/* MatchObject's length method. */ +Py_LOCAL_INLINE(Py_ssize_t) match_length(MatchObject* self) { + return (Py_ssize_t)self->group_count + 1; +} + +/* MatchObject's '__getitem__' method. */ +static PyObject* match_getitem(MatchObject* self, PyObject* item) { + if (PySlice_Check(item)) + return match_get_group_slice(self, item); + + return match_get_group(self, item, Py_None, TRUE); +} + +/* Determines the portion of the target string which is covered by the group + * captures. + */ +Py_LOCAL_INLINE(void) determine_target_substring(MatchObject* match, + Py_ssize_t* slice_start, Py_ssize_t* slice_end) { + Py_ssize_t start; + Py_ssize_t end; + size_t g; + + start = match->pos; + end = match->endpos; + + for (g = 0; g < match->group_count; g++) { + RE_GroupSpan* span; + size_t c; + + span = &match->groups[g].span; + if (span->start >= 0 && span->start < start) + start = span->start; + if (span->end >= 0 && span->end > end) + end = span->end; + + for (c = 0; c < match->groups[g].capture_count; c++) { + RE_GroupSpan* span; + + span = match->groups[g].captures; + if (span->start >= 0 && span->start < start) + start = span->start; + if (span->end >= 0 && span->end > end) + end = span->end; + } + } + + *slice_start = start; + *slice_end = end; +} + +/* MatchObject's 'detach_string' method. */ +static PyObject* match_detach_string(MatchObject* self, PyObject* unused) { + if (self->string) { + Py_ssize_t start; + Py_ssize_t end; + PyObject* substring; + + determine_target_substring(self, &start, &end); + + substring = get_slice(self->string, start, end); + if (substring) { + Py_XDECREF(self->substring); + self->substring = substring; + self->substring_offset = start; + + Py_DECREF(self->string); + self->string = NULL; + } + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* The documentation of a MatchObject. */ +PyDoc_STRVAR(match_group_doc, + "group([group1, ...]) --> string or tuple of strings.\n\ + Return one or more subgroups of the match. If there is a single argument,\n\ + the result is a single string, or None if the group did not contribute to\n\ + the match; if there are multiple arguments, the result is a tuple with one\n\ + item per argument; if there are no arguments, the whole match is returned.\n\ + Group 0 is the whole match."); + +PyDoc_STRVAR(match_start_doc, + "start([group1, ...]) --> int or tuple of ints.\n\ + Return the index of the start of one or more subgroups of the match. If\n\ + there is a single argument, the result is an index, or -1 if the group did\n\ + not contribute to the match; if there are multiple arguments, the result is\n\ + a tuple with one item per argument; if there are no arguments, the index of\n\ + the start of the whole match is returned. Group 0 is the whole match."); + +PyDoc_STRVAR(match_end_doc, + "end([group1, ...]) --> int or tuple of ints.\n\ + Return the index of the end of one or more subgroups of the match. If there\n\ + is a single argument, the result is an index, or -1 if the group did not\n\ + contribute to the match; if there are multiple arguments, the result is a\n\ + tuple with one item per argument; if there are no arguments, the index of\n\ + the end of the whole match is returned. Group 0 is the whole match."); + +PyDoc_STRVAR(match_span_doc, + "span([group1, ...]) --> 2-tuple of int or tuple of 2-tuple of ints.\n\ + Return the span (a 2-tuple of the indices of the start and end) of one or\n\ + more subgroups of the match. If there is a single argument, the result is a\n\ + span, or (-1, -1) if the group did not contribute to the match; if there are\n\ + multiple arguments, the result is a tuple with one item per argument; if\n\ + there are no arguments, the span of the whole match is returned. Group 0 is\n\ + the whole match."); + +PyDoc_STRVAR(match_groups_doc, + "groups(default=None) --> tuple of strings.\n\ + Return a tuple containing all the subgroups of the match. The argument is\n\ + the default for groups that did not participate in the match."); + +PyDoc_STRVAR(match_groupdict_doc, + "groupdict(default=None) --> dict.\n\ + Return a dictionary containing all the named subgroups of the match, keyed\n\ + by the subgroup name. The argument is the value to be given for groups that\n\ + did not participate in the match."); + +PyDoc_STRVAR(match_capturesdict_doc, + "capturesdict() --> dict.\n\ + Return a dictionary containing the captures of all the named subgroups of the\n\ + match, keyed by the subgroup name."); + +PyDoc_STRVAR(match_expand_doc, + "expand(template) --> string.\n\ + Return the string obtained by doing backslash substitution on the template,\n\ + as done by the sub() method."); + +#if PY_VERSION_HEX >= 0x02060000 +PyDoc_STRVAR(match_expandf_doc, + "expandf(format) --> string.\n\ + Return the string obtained by using the format, as done by the subf()\n\ + method."); + +#endif +PyDoc_STRVAR(match_captures_doc, + "captures([group1, ...]) --> list of strings or tuple of list of strings.\n\ + Return the captures of one or more subgroups of the match. If there is a\n\ + single argument, the result is a list of strings; if there are multiple\n\ + arguments, the result is a tuple of lists with one item per argument; if\n\ + there are no arguments, the captures of the whole match is returned. Group\n\ + 0 is the whole match."); + +PyDoc_STRVAR(match_starts_doc, + "starts([group1, ...]) --> list of ints or tuple of list of ints.\n\ + Return the indices of the starts of the captures of one or more subgroups of\n\ + the match. If there is a single argument, the result is a list of indices;\n\ + if there are multiple arguments, the result is a tuple of lists with one\n\ + item per argument; if there are no arguments, the indices of the starts of\n\ + the captures of the whole match is returned. Group 0 is the whole match."); + +PyDoc_STRVAR(match_ends_doc, + "ends([group1, ...]) --> list of ints or tuple of list of ints.\n\ + Return the indices of the ends of the captures of one or more subgroups of\n\ + the match. If there is a single argument, the result is a list of indices;\n\ + if there are multiple arguments, the result is a tuple of lists with one\n\ + item per argument; if there are no arguments, the indices of the ends of the\n\ + captures of the whole match is returned. Group 0 is the whole match."); + +PyDoc_STRVAR(match_spans_doc, + "spans([group1, ...]) --> list of 2-tuple of ints or tuple of list of 2-tuple of ints.\n\ + Return the spans (a 2-tuple of the indices of the start and end) of the\n\ + captures of one or more subgroups of the match. If there is a single\n\ + argument, the result is a list of spans; if there are multiple arguments,\n\ + the result is a tuple of lists with one item per argument; if there are no\n\ + arguments, the spans of the captures of the whole match is returned. Group\n\ + 0 is the whole match."); + +PyDoc_STRVAR(match_detach_string_doc, + "detach_string()\n\ + Detaches the target string from the match object. The 'string' attribute\n\ + will become None."); + +/* MatchObject's methods. */ +static PyMethodDef match_methods[] = { + {"group", (PyCFunction)match_group, METH_VARARGS, match_group_doc}, + {"start", (PyCFunction)match_start, METH_VARARGS, match_start_doc}, + {"end", (PyCFunction)match_end, METH_VARARGS, match_end_doc}, + {"span", (PyCFunction)match_span, METH_VARARGS, match_span_doc}, + {"groups", (PyCFunction)match_groups, METH_VARARGS|METH_KEYWORDS, + match_groups_doc}, + {"groupdict", (PyCFunction)match_groupdict, METH_VARARGS|METH_KEYWORDS, + match_groupdict_doc}, + {"capturesdict", (PyCFunction)match_capturesdict, METH_NOARGS, + match_capturesdict_doc}, + {"expand", (PyCFunction)match_expand, METH_O, match_expand_doc}, +#if PY_VERSION_HEX >= 0x02060000 + {"expandf", (PyCFunction)match_expandf, METH_O, match_expandf_doc}, +#endif + {"captures", (PyCFunction)match_captures, METH_VARARGS, + match_captures_doc}, + {"starts", (PyCFunction)match_starts, METH_VARARGS, match_starts_doc}, + {"ends", (PyCFunction)match_ends, METH_VARARGS, match_ends_doc}, + {"spans", (PyCFunction)match_spans, METH_VARARGS, match_spans_doc}, + {"detach_string", (PyCFunction)match_detach_string, METH_NOARGS, + match_detach_string_doc}, + {"__copy__", (PyCFunction)match_copy, METH_NOARGS}, + {"__deepcopy__", (PyCFunction)match_deepcopy, METH_O}, + {"__getitem__", (PyCFunction)match_getitem, METH_O|METH_COEXIST}, + {NULL, NULL} +}; + +PyDoc_STRVAR(match_doc, "Match object"); + +/* MatchObject's 'lastindex' attribute. */ +static PyObject* match_lastindex(PyObject* self_) { + MatchObject* self; + + self = (MatchObject*)self_; + + if (self->lastindex >= 0) + return Py_BuildValue("n", self->lastindex); + + Py_INCREF(Py_None); + return Py_None; +} + +/* MatchObject's 'lastgroup' attribute. */ +static PyObject* match_lastgroup(PyObject* self_) { + MatchObject* self; + + self = (MatchObject*)self_; + + if (self->pattern->indexgroup && self->lastgroup >= 0) { + PyObject* index; + PyObject* result; + + index = Py_BuildValue("n", self->lastgroup); + + /* PyDict_GetItem returns borrows a reference. */ + result = PyDict_GetItem(self->pattern->indexgroup, index); + Py_DECREF(index); + if (result) { + Py_INCREF(result); + return result; + } + PyErr_Clear(); + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* MatchObject's 'string' attribute. */ +static PyObject* match_string(PyObject* self_) { + MatchObject* self; + + self = (MatchObject*)self_; + + if (self->string) { + Py_INCREF(self->string); + return self->string; + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#if PY_VERSION_HEX < 0x02060000 + +/* MatchObject's 'partial' attribute. */ +static PyObject* match_partial(PyObject* self_) { + MatchObject* self; + PyObject* result; + + self = (MatchObject*)self_; + + result = self->partial ? Py_True : Py_False; + Py_INCREF(result); + + return result; +} +#endif + +/* MatchObject's 'fuzzy_counts' attribute. */ +static PyObject* match_fuzzy_counts(PyObject* self_) { + MatchObject* self; + + self = (MatchObject*)self_; + + return Py_BuildValue("nnn", self->fuzzy_counts[RE_FUZZY_SUB], + self->fuzzy_counts[RE_FUZZY_INS], self->fuzzy_counts[RE_FUZZY_DEL]); +} + +static PyGetSetDef match_getset[] = { + {"lastindex", (getter)match_lastindex, (setter)NULL, + "The group number of the last matched capturing group, or None."}, + {"lastgroup", (getter)match_lastgroup, (setter)NULL, + "The name of the last matched capturing group, or None."}, + {"regs", (getter)match_regs, (setter)NULL, + "A tuple of the spans of the capturing groups."}, + {"string", (getter)match_string, (setter)NULL, + "The string that was searched, or None if it has been detached."}, +#if PY_VERSION_HEX < 0x02060000 + {"partial", (getter)match_partial, (setter)NULL, + "Whether it's a partial match."}, +#endif + {"fuzzy_counts", (getter)match_fuzzy_counts, (setter)NULL, + "A tuple of the number of substitutions, insertions and deletions."}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef match_members[] = { + {"re", T_OBJECT, offsetof(MatchObject, pattern), READONLY, + "The regex object that produced this match object."}, + {"pos", T_PYSSIZET, offsetof(MatchObject, pos), READONLY, + "The position at which the regex engine starting searching."}, + {"endpos", T_PYSSIZET, offsetof(MatchObject, endpos), READONLY, + "The final position beyond which the regex engine won't search."}, +#if PY_VERSION_HEX >= 0x02060000 + {"partial", T_BOOL, offsetof(MatchObject, partial), READONLY, + "Whether it's a partial match."}, +#endif + {NULL} /* Sentinel */ +}; + +static PyMappingMethods match_as_mapping = { + (lenfunc)match_length, /* mp_length */ + (binaryfunc)match_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +static PyTypeObject Match_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "_" RE_MODULE "." "Match", + sizeof(MatchObject) +}; + +/* Copies the groups. */ +Py_LOCAL_INLINE(RE_GroupData*) copy_groups(RE_GroupData* groups, size_t + group_count) { + size_t span_count; + size_t g; + RE_GroupData* groups_copy; + RE_GroupSpan* spans_copy; + size_t offset; + + /* Calculate the total size of the group info. */ + span_count = 0; + for (g = 0; g < group_count; g++) + span_count += groups[g].capture_count; + + /* Allocate the storage for the group info in a single block. */ + groups_copy = (RE_GroupData*)re_alloc(group_count * sizeof(RE_GroupData) + + span_count * sizeof(RE_GroupSpan)); + if (!groups_copy) + return NULL; + + /* The storage for the spans comes after the other group info. */ + spans_copy = (RE_GroupSpan*)&groups_copy[group_count]; + + /* There's no need to initialise the spans info. */ + memset(groups_copy, 0, group_count * sizeof(RE_GroupData)); + + offset = 0; + for (g = 0; g < group_count; g++) { + RE_GroupData* orig; + RE_GroupData* copy; + + orig = &groups[g]; + copy = &groups_copy[g]; + copy->span = orig->span; + + copy->captures = &spans_copy[offset]; + offset += orig->capture_count; + + if (orig->capture_count > 0) { + Py_MEMCPY(copy->captures, orig->captures, orig->capture_count * + sizeof(RE_GroupSpan)); + copy->capture_capacity = orig->capture_count; + copy->capture_count = orig->capture_count; + } + } + + return groups_copy; +} + +/* Makes a copy of a MatchObject. */ +Py_LOCAL_INLINE(PyObject*) make_match_copy(MatchObject* self) { + MatchObject* match; + + if (!self->string) { + /* The target string has been detached, so the MatchObject is now + * immutable. + */ + Py_INCREF(self); + return (PyObject*)self; + } + + /* Create a MatchObject. */ + match = PyObject_NEW(MatchObject, &Match_Type); + if (!match) + return NULL; + + Py_MEMCPY(match, self, sizeof(MatchObject)); + + Py_INCREF(match->string); + Py_INCREF(match->substring); + Py_INCREF(match->pattern); + + /* Copy the groups to the MatchObject. */ + if (self->group_count > 0) { + match->groups = copy_groups(self->groups, self->group_count); + if (!match->groups) { + Py_DECREF(match); + return NULL; + } + } + + return (PyObject*)match; +} + +/* Creates a new MatchObject. */ +Py_LOCAL_INLINE(PyObject*) pattern_new_match(PatternObject* pattern, RE_State* + state, int status) { + /* Create MatchObject (from state object). */ + if (status > 0 || status == RE_ERROR_PARTIAL) { + MatchObject* match; + + /* Create a MatchObject. */ + match = PyObject_NEW(MatchObject, &Match_Type); + if (!match) + return NULL; + + match->string = state->string; + match->substring = state->string; + match->substring_offset = 0; + match->pattern = pattern; + match->regs = NULL; + + if (pattern->is_fuzzy) { + match->fuzzy_counts[RE_FUZZY_SUB] = + state->total_fuzzy_counts[RE_FUZZY_SUB]; + match->fuzzy_counts[RE_FUZZY_INS] = + state->total_fuzzy_counts[RE_FUZZY_INS]; + match->fuzzy_counts[RE_FUZZY_DEL] = + state->total_fuzzy_counts[RE_FUZZY_DEL]; + } else + memset(match->fuzzy_counts, 0, sizeof(match->fuzzy_counts)); + + match->partial = status == RE_ERROR_PARTIAL; + Py_INCREF(match->string); + Py_INCREF(match->substring); + Py_INCREF(match->pattern); + + /* Copy the groups to the MatchObject. */ + if (pattern->public_group_count > 0) { + match->groups = copy_groups(state->groups, + pattern->public_group_count); + if (!match->groups) { + Py_DECREF(match); + return NULL; + } + } else + match->groups = NULL; + + match->group_count = pattern->public_group_count; + + match->pos = state->slice_start; + match->endpos = state->slice_end; + + if (state->reverse) { + match->match_start = state->text_pos; + match->match_end = state->match_pos; + } else { + match->match_start = state->match_pos; + match->match_end = state->text_pos; + } + + match->lastindex = state->lastindex; + match->lastgroup = state->lastgroup; + + return (PyObject*)match; + } else if (status == 0) { + /* No match. */ + Py_INCREF(Py_None); + return Py_None; + } else { + /* Internal error. */ + set_error(status, NULL); + return NULL; + } +} + +/* Gets the text of a capture group from a state. */ +Py_LOCAL_INLINE(PyObject*) state_get_group(RE_State* state, Py_ssize_t index, + PyObject* string, BOOL empty) { + RE_GroupData* group; + Py_ssize_t start; + Py_ssize_t end; + + group = &state->groups[index - 1]; + + if (string != Py_None && index >= 1 && (size_t)index <= + state->pattern->public_group_count && group->capture_count > 0) { + start = group->span.start; + end = group->span.end; + } else { + if (empty) + /* Want an empty string. */ + start = end = 0; + else { + Py_INCREF(Py_None); + return Py_None; + } + } + + return get_slice(string, start, end); +} + +/* Acquires the lock (mutex) on the state if there's one. + * + * It also increments the owner's refcount just to ensure that it won't be + * destroyed by another thread. + */ +Py_LOCAL_INLINE(void) acquire_state_lock(PyObject* owner, RE_SafeState* + safe_state) { + RE_State* state; + + state = safe_state->re_state; + + if (state->lock) { + /* In order to avoid deadlock we need to release the GIL while trying + * to acquire the lock. + */ + Py_INCREF(owner); + if (!PyThread_acquire_lock(state->lock, 0)) { + release_GIL(safe_state); + PyThread_acquire_lock(state->lock, 1); + acquire_GIL(safe_state); + } + } +} + +/* Releases the lock (mutex) on the state if there's one. + * + * It also decrements the owner's refcount, which was incremented when the lock + * was acquired. + */ +Py_LOCAL_INLINE(void) release_state_lock(PyObject* owner, RE_SafeState* + safe_state) { + RE_State* state; + + state = safe_state->re_state; + + if (state->lock) { + PyThread_release_lock(state->lock); + Py_DECREF(owner); + } +} + +/* Implements the functionality of ScanObject's search and match methods. */ +Py_LOCAL_INLINE(PyObject*) scanner_search_or_match(ScannerObject* self, BOOL + search) { + RE_State* state; + RE_SafeState safe_state; + PyObject* match; + + state = &self->state; + + /* Initialise the "safe state" structure. */ + safe_state.re_state = state; + safe_state.thread_state = NULL; + + /* Acquire the state lock in case we're sharing the scanner object across + * threads. + */ + acquire_state_lock((PyObject*)self, &safe_state); + + if (self->status == RE_ERROR_FAILURE || self->status == RE_ERROR_PARTIAL) { + /* No or partial match. */ + release_state_lock((PyObject*)self, &safe_state); + Py_INCREF(Py_None); + return Py_None; + } else if (self->status < 0) { + /* Internal error. */ + release_state_lock((PyObject*)self, &safe_state); + set_error(self->status, NULL); + return NULL; + } + + /* Look for another match. */ + self->status = do_match(&safe_state, search); + if (self->status >= 0 || self->status == RE_ERROR_PARTIAL) { + /* Create the match object. */ + match = pattern_new_match(self->pattern, state, self->status); + + if (search && state->overlapped) { + /* Advance one character. */ + Py_ssize_t step; + + step = state->reverse ? -1 : 1; + state->text_pos = state->match_pos + step; + state->must_advance = FALSE; + } else + /* Continue from where we left off, but don't allow 2 contiguous + * zero-width matches. + */ + state->must_advance = state->text_pos == state->match_pos; + } else + /* Internal error. */ + match = NULL; + + /* Release the state lock. */ + release_state_lock((PyObject*)self, &safe_state); + + return match; +} + +/* ScannerObject's 'match' method. */ +static PyObject* scanner_match(ScannerObject* self, PyObject* unused) { + return scanner_search_or_match(self, FALSE); +} + +/* ScannerObject's 'search' method. */ +static PyObject* scanner_search(ScannerObject* self, PyObject *unused) { + return scanner_search_or_match(self, TRUE); +} + +/* ScannerObject's 'next' method. */ +static PyObject* scanner_next(PyObject* self) { + PyObject* match; + + match = scanner_search((ScannerObject*)self, NULL); + + if (match == Py_None) { + /* No match. */ + Py_DECREF(Py_None); + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + return match; +} + +/* Returns an iterator for a ScannerObject. + * + * The iterator is actually the ScannerObject itself. + */ +static PyObject* scanner_iter(PyObject* self) { + Py_INCREF(self); + return self; +} + +/* Gets the next result from a scanner iterator. */ +static PyObject* scanner_iternext(PyObject* self) { + PyObject* match; + + match = scanner_search((ScannerObject*)self, NULL); + + if (match == Py_None) { + /* No match. */ + Py_DECREF(match); + return NULL; + } + + return match; +} + +/* Makes a copy of a ScannerObject. + * + * It actually doesn't make a copy, just returns the original object. + */ +Py_LOCAL_INLINE(PyObject*) make_scanner_copy(ScannerObject* self) { + Py_INCREF(self); + return (PyObject*)self; +} + +/* ScannerObject's '__copy__' method. */ +static PyObject* scanner_copy(ScannerObject* self, PyObject *unused) { + return make_scanner_copy(self); +} + +/* ScannerObject's '__deepcopy__' method. */ +static PyObject* scanner_deepcopy(ScannerObject* self, PyObject* memo) { + return make_scanner_copy(self); +} + +/* The documentation of a ScannerObject. */ +PyDoc_STRVAR(scanner_match_doc, + "match() --> MatchObject or None.\n\ + Match at the current position in the string."); + +PyDoc_STRVAR(scanner_search_doc, + "search() --> MatchObject or None.\n\ + Search from the current position in the string."); + +/* ScannerObject's methods. */ +static PyMethodDef scanner_methods[] = { + {"next", (PyCFunction)scanner_next, METH_NOARGS}, + {"match", (PyCFunction)scanner_match, METH_NOARGS, scanner_match_doc}, + {"search", (PyCFunction)scanner_search, METH_NOARGS, scanner_search_doc}, + {"__copy__", (PyCFunction)scanner_copy, METH_NOARGS}, + {"__deepcopy__", (PyCFunction)scanner_deepcopy, METH_O}, + {NULL, NULL} +}; + +PyDoc_STRVAR(scanner_doc, "Scanner object"); + +/* Deallocates a ScannerObject. */ +static void scanner_dealloc(PyObject* self_) { + ScannerObject* self; + + self = (ScannerObject*)self_; + + state_fini(&self->state); + Py_DECREF(self->pattern); + PyObject_DEL(self); +} + +static PyMemberDef scanner_members[] = { + {"pattern", T_OBJECT, offsetof(ScannerObject, pattern), READONLY, + "The regex object that produced this scanner object."}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject Scanner_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "_" RE_MODULE "." "Scanner", + sizeof(ScannerObject) +}; + +/* Decodes a 'concurrent' argument. */ +Py_LOCAL_INLINE(int) decode_concurrent(PyObject* concurrent) { + Py_ssize_t value; + + if (concurrent == Py_None) + return RE_CONC_DEFAULT; + + value = PyLong_AsLong(concurrent); + if (value == -1 && PyErr_Occurred()) { + set_error(RE_ERROR_CONCURRENT, NULL); + return -1; + } + + return value ? RE_CONC_YES : RE_CONC_NO; +} + +/* Decodes a 'partial' argument. */ +Py_LOCAL_INLINE(BOOL) decode_partial(PyObject* partial) { + Py_ssize_t value; + + if (partial == Py_False) + return FALSE; + + if (partial == Py_True) + return TRUE; + + value = PyLong_AsLong(partial); + if (value == -1 && PyErr_Occurred()) { + PyErr_Clear(); + return TRUE; + } + + return value != 0; +} + +/* Creates a new ScannerObject. */ +static PyObject* pattern_scanner(PatternObject* pattern, PyObject* args, + PyObject* kwargs) { + /* Create search state object. */ + ScannerObject* self; + Py_ssize_t start; + Py_ssize_t end; + int conc; + BOOL part; + + PyObject* string; + PyObject* pos = Py_None; + PyObject* endpos = Py_None; + Py_ssize_t overlapped = FALSE; + PyObject* concurrent = Py_None; + PyObject* partial = Py_False; + static char* kwlist[] = { "string", "pos", "endpos", "overlapped", + "concurrent", "partial", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOnOO:scanner", kwlist, + &string, &pos, &endpos, &overlapped, &concurrent, &partial)) + return NULL; + + start = as_string_index(pos, 0); + if (start == -1 && PyErr_Occurred()) + return NULL; + + end = as_string_index(endpos, PY_SSIZE_T_MAX); + if (end == -1 && PyErr_Occurred()) + return NULL; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + part = decode_partial(partial); + + /* Create a scanner object. */ + self = PyObject_NEW(ScannerObject, &Scanner_Type); + if (!self) + return NULL; + + self->pattern = pattern; + Py_INCREF(self->pattern); + + /* The MatchObject, and therefore repeated captures, will be visible. */ + if (!state_init(&self->state, pattern, string, start, end, overlapped != 0, + conc, part, TRUE, TRUE, FALSE)) { + PyObject_DEL(self); + return NULL; + } + + self->status = RE_ERROR_SUCCESS; + + return (PyObject*) self; +} + +/* Performs the split for the SplitterObject. */ +Py_LOCAL_INLINE(PyObject*) next_split_part(SplitterObject* self) { + RE_State* state; + RE_SafeState safe_state; + PyObject* result = NULL; /* Initialise to stop compiler warning. */ + + state = &self->state; + + /* Initialise the "safe state" structure. */ + safe_state.re_state = state; + safe_state.thread_state = NULL; + + /* Acquire the state lock in case we're sharing the splitter object across + * threads. + */ + acquire_state_lock((PyObject*)self, &safe_state); + + if (self->status == RE_ERROR_FAILURE || self->status == RE_ERROR_PARTIAL) { + /* Finished. */ + release_state_lock((PyObject*)self, &safe_state); + result = Py_False; + Py_INCREF(result); + return result; + } else if (self->status < 0) { + /* Internal error. */ + release_state_lock((PyObject*)self, &safe_state); + set_error(self->status, NULL); + return NULL; + } + + if (self->index == 0) { + if (self->split_count < self->maxsplit) { + Py_ssize_t step; + Py_ssize_t end_pos; + + if (state->reverse) { + step = -1; + end_pos = state->slice_start; + } else { + step = 1; + end_pos = state->slice_end; + } + +retry: + self->status = do_match(&safe_state, TRUE); + if (self->status < 0) + goto error; + + if (self->status == RE_ERROR_SUCCESS) { + if (state->version_0) { + /* Version 0 behaviour is to advance one character if the + * split was zero-width. Unfortunately, this can give an + * incorrect result. GvR wants this behaviour to be + * retained so as not to break any existing software which + * might rely on it. + */ + if (state->text_pos == state->match_pos) { + if (self->last_pos == end_pos) + goto no_match; + + /* Advance one character. */ + state->text_pos += step; + state->must_advance = FALSE; + goto retry; + } + } + + ++self->split_count; + + /* Get segment before this match. */ + if (state->reverse) + result = get_slice(state->string, state->match_pos, + self->last_pos); + else + result = get_slice(state->string, self->last_pos, + state->match_pos); + if (!result) + goto error; + + self->last_pos = state->text_pos; + + /* Version 0 behaviour is to advance one character if the match + * was zero-width. Unfortunately, this can give an incorrect + * result. GvR wants this behaviour to be retained so as not to + * break any existing software which might rely on it. + */ + if (state->version_0) { + if (state->text_pos == state->match_pos) + /* Advance one character. */ + state->text_pos += step; + + state->must_advance = FALSE; + } else + /* Continue from where we left off, but don't allow a + * contiguous zero-width match. + */ + state->must_advance = TRUE; + } + } else + goto no_match; + + if (self->status == RE_ERROR_FAILURE || self->status == + RE_ERROR_PARTIAL) { +no_match: + /* Get segment following last match (even if empty). */ + if (state->reverse) + result = get_slice(state->string, 0, self->last_pos); + else + result = get_slice(state->string, self->last_pos, + state->text_length); + if (!result) + goto error; + } + } else { + /* Add group. */ + result = state_get_group(state, self->index, state->string, FALSE); + if (!result) + goto error; + } + + ++self->index; + if ((size_t)self->index > state->pattern->public_group_count) + self->index = 0; + + /* Release the state lock. */ + release_state_lock((PyObject*)self, &safe_state); + + return result; + +error: + /* Release the state lock. */ + release_state_lock((PyObject*)self, &safe_state); + + return NULL; +} + +/* SplitterObject's 'split' method. */ +static PyObject* splitter_split(SplitterObject* self, PyObject *unused) { + PyObject* result; + + result = next_split_part(self); + + if (result == Py_False) { + /* The sentinel. */ + Py_DECREF(Py_False); + Py_INCREF(Py_None); + return Py_None; + } + + return result; +} + +/* SplitterObject's 'next' method. */ +static PyObject* splitter_next(PyObject* self) { + PyObject* result; + + result = next_split_part((SplitterObject*)self); + + if (result == Py_False) { + /* No match. */ + Py_DECREF(Py_False); + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + return result; +} + +/* Returns an iterator for a SplitterObject. + * + * The iterator is actually the SplitterObject itself. + */ +static PyObject* splitter_iter(PyObject* self) { + Py_INCREF(self); + return self; +} + +/* Gets the next result from a splitter iterator. */ +static PyObject* splitter_iternext(PyObject* self) { + PyObject* result; + + result = next_split_part((SplitterObject*)self); + + if (result == Py_False) { + /* No match. */ + Py_DECREF(result); + return NULL; + } + + return result; +} + +/* Makes a copy of a SplitterObject. + * + * It actually doesn't make a copy, just returns the original object. + */ +Py_LOCAL_INLINE(PyObject*) make_splitter_copy(SplitterObject* self) { + Py_INCREF(self); + return (PyObject*)self; +} + +/* SplitterObject's '__copy__' method. */ +static PyObject* splitter_copy(SplitterObject* self, PyObject *unused) { + return make_splitter_copy(self); +} + +/* SplitterObject's '__deepcopy__' method. */ +static PyObject* splitter_deepcopy(SplitterObject* self, PyObject* memo) { + return make_splitter_copy(self); +} + +/* The documentation of a SplitterObject. */ +PyDoc_STRVAR(splitter_split_doc, + "split() --> string or None.\n\ + Return the next part of the split string."); + +/* SplitterObject's methods. */ +static PyMethodDef splitter_methods[] = { + {"next", (PyCFunction)splitter_next, METH_NOARGS}, + {"split", (PyCFunction)splitter_split, METH_NOARGS, splitter_split_doc}, + {"__copy__", (PyCFunction)splitter_copy, METH_NOARGS}, + {"__deepcopy__", (PyCFunction)splitter_deepcopy, METH_O}, + {NULL, NULL} +}; + +PyDoc_STRVAR(splitter_doc, "Splitter object"); + +/* Deallocates a SplitterObject. */ +static void splitter_dealloc(PyObject* self_) { + SplitterObject* self; + + self = (SplitterObject*)self_; + + state_fini(&self->state); + Py_DECREF(self->pattern); + PyObject_DEL(self); +} +#if PY_VERSION_HEX >= 0x02060000 + +/* Converts a captures index to an integer. + * + * A negative capture index in 'expandf' and 'subf' is passed as a string + * because negative indexes are not supported by 'str.format'. + */ +Py_LOCAL_INLINE(Py_ssize_t) index_to_integer(PyObject* item) { + Py_ssize_t value; + + value = PyInt_AsSsize_t(item); + if (value != -1 || !PyErr_Occurred()) + return value; + + PyErr_Clear(); + + value = PyLong_AsLong(item); + if (value != -1 || !PyErr_Occurred()) + return value; + + PyErr_Clear(); + + /* Is the index a string representation of an integer? */ + if (PyUnicode_Check(item)) { + PyObject* int_obj; + Py_UNICODE* characters; + Py_ssize_t length; + + characters = (Py_UNICODE*)PyUnicode_AS_DATA(item); + length = PyUnicode_GET_SIZE(item); + int_obj = PyLong_FromUnicode(characters, length, 0); + if (!int_obj) + goto error; + + value = PyLong_AsLong(int_obj); + Py_DECREF(int_obj); + if (!PyErr_Occurred()) + return value; + } else if (PyString_Check(item)) { + char* characters; + PyObject* int_obj; + + characters = PyString_AsString(item); + int_obj = PyLong_FromString(characters, NULL, 0); + if (!int_obj) + goto error; + + value = PyLong_AsLong(int_obj); + Py_DECREF(int_obj); + if (!PyErr_Occurred()) + return value; + } + +error: + PyErr_Format(PyExc_TypeError, "list indices must be integers, not %.200s", + item->ob_type->tp_name); + + return -1; +} + +/* CaptureObject's length method. */ +Py_LOCAL_INLINE(Py_ssize_t) capture_length(CaptureObject* self) { + MatchObject* match; + RE_GroupData* group; + + if (self->group_index == 0) + return 1; + + match = *self->match_indirect; + group = &match->groups[self->group_index - 1]; + + return (Py_ssize_t)group->capture_count; +} + +/* CaptureObject's '__getitem__' method. */ +static PyObject* capture_getitem(CaptureObject* self, PyObject* item) { + Py_ssize_t index; + MatchObject* match; + Py_ssize_t start; + Py_ssize_t end; + + index = index_to_integer(item); + if (index == -1 && PyErr_Occurred()) + return NULL; + + match = *self->match_indirect; + + if (self->group_index == 0) { + if (index < 0) + index += 1; + + if (index != 0) { + PyErr_SetString(PyExc_IndexError, "list index out of range"); + return NULL; + } + + start = match->match_start; + end = match->match_end; + } else { + RE_GroupData* group; + RE_GroupSpan* span; + + group = &match->groups[self->group_index - 1]; + + if (index < 0) + index += group->capture_count; + + if (index < 0 || index >= (Py_ssize_t)group->capture_count) { + PyErr_SetString(PyExc_IndexError, "list index out of range"); + return NULL; + } + + span = &group->captures[index]; + + start = span->start; + end = span->end; + } + + return get_slice(match->substring, start - match->substring_offset, end - + match->substring_offset); +} + +static PyMappingMethods capture_as_mapping = { + (lenfunc)capture_length, /* mp_length */ + (binaryfunc)capture_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +/* CaptureObject's methods. */ +static PyMethodDef capture_methods[] = { + {"__getitem__", (PyCFunction)capture_getitem, METH_O|METH_COEXIST}, + {NULL, NULL} +}; + +/* Deallocates a CaptureObject. */ +static void capture_dealloc(PyObject* self_) { + CaptureObject* self; + + self = (CaptureObject*)self_; + PyObject_DEL(self); +} + +/* CaptureObject's 'str' method. */ +static PyObject* capture_str(PyObject* self_) { + CaptureObject* self; + MatchObject* match; + + self = (CaptureObject*)self_; + match = *self->match_indirect; + + return match_get_group_by_index(match, self->group_index, Py_None); +} +#endif + +static PyMemberDef splitter_members[] = { + {"pattern", T_OBJECT, offsetof(SplitterObject, pattern), READONLY, + "The regex object that produced this splitter object."}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject Splitter_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "_" RE_MODULE "." "Splitter", + sizeof(SplitterObject) +}; + +/* Creates a new SplitterObject. */ +Py_LOCAL_INLINE(PyObject*) pattern_splitter(PatternObject* pattern, PyObject* + args, PyObject* kwargs) { + /* Create split state object. */ + int conc; + SplitterObject* self; + RE_State* state; + + PyObject* string; + Py_ssize_t maxsplit = 0; + PyObject* concurrent = Py_None; + static char* kwlist[] = { "string", "maxsplit", "concurrent", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|nO:splitter", kwlist, + &string, &maxsplit, &concurrent)) + return NULL; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + /* Create a splitter object. */ + self = PyObject_NEW(SplitterObject, &Splitter_Type); + if (!self) + return NULL; + + self->pattern = pattern; + Py_INCREF(self->pattern); + + if (maxsplit == 0) + maxsplit = PY_SSIZE_T_MAX; + + state = &self->state; + + /* The MatchObject, and therefore repeated captures, will not be visible. + */ + if (!state_init(state, pattern, string, 0, PY_SSIZE_T_MAX, FALSE, conc, + FALSE, TRUE, FALSE, FALSE)) { + PyObject_DEL(self); + return NULL; + } + + self->maxsplit = maxsplit; + self->last_pos = state->reverse ? state->text_length : 0; + self->split_count = 0; + self->index = 0; + self->status = 1; + + return (PyObject*) self; +} + +/* Implements the functionality of PatternObject's search and match methods. */ +Py_LOCAL_INLINE(PyObject*) pattern_search_or_match(PatternObject* self, + PyObject* args, PyObject* kwargs, char* args_desc, BOOL search, BOOL + match_all) { + Py_ssize_t start; + Py_ssize_t end; + int conc; + BOOL part; + RE_State state; + RE_SafeState safe_state; + int status; + PyObject* match; + + PyObject* string; + PyObject* pos = Py_None; + PyObject* endpos = Py_None; + PyObject* concurrent = Py_None; + PyObject* partial = Py_False; + static char* kwlist[] = { "string", "pos", "endpos", "concurrent", + "partial", NULL }; + /* When working with a short string, such as a line from a file, the + * relative cost of PyArg_ParseTupleAndKeywords can be significant, and + * it's worth not using it when there are only positional arguments. + */ + Py_ssize_t arg_count; + if (args && !kwargs && PyTuple_CheckExact(args)) + arg_count = PyTuple_GET_SIZE(args); + else + arg_count = -1; + + if (1 <= arg_count && arg_count <= 5) { + /* PyTuple_GET_ITEM borrows the reference. */ + string = PyTuple_GET_ITEM(args, 0); + if (arg_count >= 2) + pos = PyTuple_GET_ITEM(args, 1); + if (arg_count >= 3) + endpos = PyTuple_GET_ITEM(args, 2); + if (arg_count >= 4) + concurrent = PyTuple_GET_ITEM(args, 3); + if (arg_count >= 5) + partial = PyTuple_GET_ITEM(args, 4); + } else if (!PyArg_ParseTupleAndKeywords(args, kwargs, args_desc, kwlist, + &string, &pos, &endpos, &concurrent, &partial)) + return NULL; + + start = as_string_index(pos, 0); + if (start == -1 && PyErr_Occurred()) + return NULL; + + end = as_string_index(endpos, PY_SSIZE_T_MAX); + if (end == -1 && PyErr_Occurred()) + return NULL; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + part = decode_partial(partial); + + /* The MatchObject, and therefore repeated captures, will be visible. */ + if (!state_init(&state, self, string, start, end, FALSE, conc, part, FALSE, + TRUE, match_all)) + return NULL; + + /* Initialise the "safe state" structure. */ + safe_state.re_state = &state; + safe_state.thread_state = NULL; + + status = do_match(&safe_state, search); + + if (status >= 0 || status == RE_ERROR_PARTIAL) + /* Create the match object. */ + match = pattern_new_match(self, &state, status); + else + match = NULL; + + state_fini(&state); + + return match; +} + +/* PatternObject's 'match' method. */ +static PyObject* pattern_match(PatternObject* self, PyObject* args, PyObject* + kwargs) { + return pattern_search_or_match(self, args, kwargs, "O|OOOO:match", FALSE, + FALSE); +} + +/* PatternObject's 'fullmatch' method. */ +static PyObject* pattern_fullmatch(PatternObject* self, PyObject* args, + PyObject* kwargs) { + return pattern_search_or_match(self, args, kwargs, "O|OOOO:fullmatch", + FALSE, TRUE); +} + +/* PatternObject's 'search' method. */ +static PyObject* pattern_search(PatternObject* self, PyObject* args, PyObject* + kwargs) { + return pattern_search_or_match(self, args, kwargs, "O|OOOO:search", TRUE, + FALSE); +} + +/* Gets the limits of the matching. */ +Py_LOCAL_INLINE(BOOL) get_limits(PyObject* pos, PyObject* endpos, Py_ssize_t + length, Py_ssize_t* start, Py_ssize_t* end) { + Py_ssize_t s; + Py_ssize_t e; + + s = as_string_index(pos, 0); + if (s == -1 && PyErr_Occurred()) + return FALSE; + + e = as_string_index(endpos, PY_SSIZE_T_MAX); + if (e == -1 && PyErr_Occurred()) + return FALSE; + + /* Adjust boundaries. */ + if (s < 0) + s += length; + if (s < 0) + s = 0; + else if (s > length) + s = length; + + if (e < 0) + e += length; + if (e < 0) + e = 0; + else if (e > length) + e = length; + + *start = s; + *end = e; + + return TRUE; +} + +/* Gets a replacement item from the replacement list. + * + * The replacement item could be a string literal or a group. + * + * It can return None to represent an empty string. + */ +Py_LOCAL_INLINE(PyObject*) get_sub_replacement(PyObject* item, PyObject* + string, RE_State* state, size_t group_count) { + Py_ssize_t index; + + if (PyUnicode_CheckExact(item) || PyString_CheckExact(item)) { + /* It's a literal, which can be added directly to the list. */ + Py_INCREF(item); + return item; + } + + /* Is it a group reference? */ + index = as_group_index(item); + if (index == -1 && PyErr_Occurred()) { + /* Not a group either! */ + set_error(RE_ERROR_REPLACEMENT, NULL); + return NULL; + } + + if (index == 0) { + /* The entire matched portion of the string. */ + if (state->match_pos == state->text_pos) { + /* Return None for "". */ + Py_INCREF(Py_None); + return Py_None; + } + + if (state->reverse) + return get_slice(string, state->text_pos, state->match_pos); + else + return get_slice(string, state->match_pos, state->text_pos); + } else if (1 <= index && (size_t)index <= group_count) { + /* A group. */ + RE_GroupData* group; + + group = &state->groups[index - 1]; + + if (group->capture_count == 0 && group->span.start != group->span.end) + { + /* The group didn't match or is "", so return None for "". */ + Py_INCREF(Py_None); + return Py_None; + } + + return get_slice(string, group->span.start, group->span.end); + } else { + /* No such group. */ + set_error(RE_ERROR_INVALID_GROUP_REF, NULL); + return NULL; + } +} + +/* PatternObject's 'subx' method. */ +Py_LOCAL_INLINE(PyObject*) pattern_subx(PatternObject* self, PyObject* + str_template, PyObject* string, Py_ssize_t maxsub, int sub_type, PyObject* + pos, PyObject* endpos, int concurrent) { + RE_StringInfo str_info; + Py_ssize_t start; + Py_ssize_t end; + BOOL is_callable = FALSE; + PyObject* replacement = NULL; + BOOL is_literal = FALSE; +#if PY_VERSION_HEX >= 0x02060000 + BOOL is_format = FALSE; +#endif + BOOL is_template = FALSE; + RE_State state; + RE_SafeState safe_state; + JoinInfo join_info; + Py_ssize_t sub_count; + Py_ssize_t last_pos; + Py_ssize_t step; + PyObject* item; + MatchObject* match; +#if PY_VERSION_HEX >= 0x02060000 + BOOL built_capture = FALSE; +#endif + PyObject* args; + PyObject* kwargs; + Py_ssize_t end_pos; + + /* Get the string. */ + if (!get_string(string, &str_info)) + return NULL; + + /* Get the limits of the search. */ + if (!get_limits(pos, endpos, str_info.length, &start, &end)) { +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return NULL; + } + + /* If the pattern is too long for the string, then take a shortcut, unless + * it's a fuzzy pattern. + */ + if (!self->is_fuzzy && self->min_width > end - start) { + PyObject* result; + + Py_INCREF(string); + + if (sub_type & RE_SUBN) + result = Py_BuildValue("Nn", string, 0); + else + result = string; + +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return result; + } + + if (maxsub == 0) + maxsub = PY_SSIZE_T_MAX; + + /* sub/subn takes either a function or a string template. */ + if (PyCallable_Check(str_template)) { + /* It's callable. */ + is_callable = TRUE; + + replacement = str_template; + Py_INCREF(replacement); +#if PY_VERSION_HEX >= 0x02060000 + } else if (sub_type & RE_SUBF) { + /* Is it a literal format? + * + * To keep it simple we'll say that a literal is a string which can be + * used as-is, so no placeholders. + */ + Py_ssize_t literal_length; + + literal_length = check_replacement_string(str_template, '{'); + if (literal_length > 0) { + /* It's a literal. */ + is_literal = TRUE; + + replacement = str_template; + Py_INCREF(replacement); + } else if (literal_length < 0) { + /* It isn't a literal, so get the 'format' method. */ + is_format = TRUE; + + replacement = PyObject_GetAttrString(str_template, "format"); + if (!replacement) { + release_buffer(&str_info); + return NULL; + } + } +#endif + } else { + /* Is it a literal template? + * + * To keep it simple we'll say that a literal is a string which can be + * used as-is, so no backslashes. + */ + Py_ssize_t literal_length; + + literal_length = check_replacement_string(str_template, '\\'); + if (literal_length > 0) { + /* It's a literal. */ + is_literal = TRUE; + + replacement = str_template; + Py_INCREF(replacement); + } else if (literal_length < 0 ) { + /* It isn't a literal, so hand it over to the template compiler. */ + is_template = TRUE; + + replacement = call(RE_MODULE, "_compile_replacement_helper", + PyTuple_Pack(2, self, str_template)); + if (!replacement) { +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return NULL; + } + } + } + + /* The MatchObject, and therefore repeated captures, will be visible only + * if the replacement is callable or subf is used. + */ +#if PY_VERSION_HEX >= 0x02060000 + if (!state_init_2(&state, self, string, &str_info, start, end, FALSE, + concurrent, FALSE, FALSE, is_callable || (sub_type & RE_SUBF) != 0, + FALSE)) { + release_buffer(&str_info); + +#else + if (!state_init_2(&state, self, string, &str_info, start, end, FALSE, + concurrent, FALSE, FALSE, is_callable, FALSE)) { +#endif + Py_XDECREF(replacement); + return NULL; + } + + /* Initialise the "safe state" structure. */ + safe_state.re_state = &state; + safe_state.thread_state = NULL; + + init_join_list(&join_info, state.reverse, PyUnicode_Check(string)); + + sub_count = 0; + last_pos = state.reverse ? state.text_length : 0; + step = state.reverse ? -1 : 1; + while (sub_count < maxsub) { + int status; + + status = do_match(&safe_state, TRUE); + if (status < 0) + goto error; + + if (status == 0) + break; + + /* Append the segment before this match. */ + if (state.match_pos != last_pos) { + if (state.reverse) + item = get_slice(string, state.match_pos, last_pos); + else + item = get_slice(string, last_pos, state.match_pos); + if (!item) + goto error; + + /* Add to the list. */ + status = add_to_join_list(&join_info, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + /* Add this match. */ + if (is_literal) { + /* The replacement is a literal string. */ + status = add_to_join_list(&join_info, replacement); + if (status < 0) + goto error; +#if PY_VERSION_HEX >= 0x02060000 + } else if (is_format) { + /* The replacement is a format string. */ + size_t g; + + /* We need to create the arguments for the 'format' method. We'll + * start by creating a MatchObject. + */ + match = (MatchObject*)pattern_new_match(self, &state, 1); + if (!match) + goto error; + + /* We'll build the args and kwargs the first time. They'll be using + * capture objects which refer to the match object indirectly; this + * means that args and kwargs can be reused with different match + * objects. + */ + if (!built_capture) { + /* The args are a tuple of the capture group matches. */ + args = PyTuple_New(match->group_count + 1); + if (!args) { + Py_DECREF(match); + goto error; + } + + for (g = 0; g < match->group_count + 1; g++) + /* PyTuple_SetItem borrows the reference. */ + PyTuple_SetItem(args, (Py_ssize_t)g, + make_capture_object(&match, (Py_ssize_t)g)); + + /* The kwargs are a dict of the named capture group matches. */ + kwargs = make_capture_dict(match, &match); + if (!kwargs) { + Py_DECREF(args); + Py_DECREF(match); + goto error; + } + + built_capture = TRUE; + } + + /* Call the 'format' method. */ + item = PyObject_Call(replacement, args, kwargs); + + Py_DECREF(match); + if (!item) + goto error; + + /* Add the result to the list. */ + status = add_to_join_list(&join_info, item); + Py_DECREF(item); + if (status < 0) + goto error; +#endif + } else if (is_template) { + /* The replacement is a list template. */ + Py_ssize_t count; + Py_ssize_t index; + Py_ssize_t step; + + /* Add each part of the template to the list. */ + count = PyList_GET_SIZE(replacement); + if (join_info.reversed) { + /* We're searching backwards, so we'll be reversing the list + * when it's complete. Therefore, we need to add the items of + * the template in reverse order for them to be in the correct + * order after the reversal. + */ + index = count - 1; + step = -1; + } else { + /* We're searching forwards. */ + index = 0; + step = 1; + } + + while (count > 0) { + PyObject* item; + PyObject* str_item; + + /* PyList_GET_ITEM borrows a reference. */ + item = PyList_GET_ITEM(replacement, index); + str_item = get_sub_replacement(item, string, &state, + self->public_group_count); + if (!str_item) + goto error; + + /* Add the result to the list. */ + if (str_item == Py_None) + /* None for "". */ + Py_DECREF(str_item); + else { + status = add_to_join_list(&join_info, str_item); + Py_DECREF(str_item); + if (status < 0) + goto error; + } + + --count; + index += step; + } + } else if (is_callable) { + /* Pass a MatchObject to the replacement function. */ + PyObject* match; + PyObject* args; + + /* We need to create a MatchObject to pass to the replacement + * function. + */ + match = pattern_new_match(self, &state, 1); + if (!match) + goto error; + + /* The args for the replacement function. */ + args = PyTuple_Pack(1, match); + if (!args) { + Py_DECREF(match); + goto error; + } + + /* Call the replacement function. */ + item = PyObject_CallObject(replacement, args); + Py_DECREF(args); + Py_DECREF(match); + if (!item) + goto error; + + /* Add the result to the list. */ + status = add_to_join_list(&join_info, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + ++sub_count; + + last_pos = state.text_pos; + + if (state.version_0) { + /* Always advance after a zero-width match. */ + if (state.match_pos == state.text_pos) { + state.text_pos += step; + state.must_advance = FALSE; + } else + state.must_advance = TRUE; + } else + /* Continue from where we left off, but don't allow a contiguous + * zero-width match. + */ + state.must_advance = state.match_pos == state.text_pos; + } + + /* Get the segment following the last match. We use 'length' instead of + * 'text_length' because the latter is truncated to 'slice_end', a + * documented idiosyncracy of the 're' module. + */ + end_pos = state.reverse ? 0 : str_info.length; + if (last_pos != end_pos) { + int status; + + /* The segment is part of the original string. */ + if (state.reverse) + item = get_slice(string, 0, last_pos); + else + item = get_slice(string, last_pos, str_info.length); + if (!item) + goto error; + + status = add_to_join_list(&join_info, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + Py_XDECREF(replacement); + + /* Convert the list to a single string (also cleans up join_info). */ + item = join_list_info(&join_info); + + state_fini(&state); + +#if PY_VERSION_HEX >= 0x02060000 + if (built_capture) { + Py_DECREF(kwargs); + Py_DECREF(args); + } + +#endif + if (!item) + return NULL; + + if (sub_type & RE_SUBN) + return Py_BuildValue("Nn", item, sub_count); + + return item; + +error: +#if PY_VERSION_HEX >= 0x02060000 + if (built_capture) { + Py_DECREF(kwargs); + Py_DECREF(args); + } + +#endif + clear_join_list(&join_info); + state_fini(&state); + Py_XDECREF(replacement); + return NULL; +} + +/* PatternObject's 'sub' method. */ +static PyObject* pattern_sub(PatternObject* self, PyObject* args, PyObject* + kwargs) { + int conc; + + PyObject* replacement; + PyObject* string; + Py_ssize_t count = 0; + PyObject* pos = Py_None; + PyObject* endpos = Py_None; + PyObject* concurrent = Py_None; + static char* kwlist[] = { "repl", "string", "count", "pos", "endpos", + "concurrent", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|nOOO:sub", kwlist, + &replacement, &string, &count, &pos, &endpos, &concurrent)) + return NULL; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + return pattern_subx(self, replacement, string, count, RE_SUB, pos, endpos, + conc); +} + +#if PY_VERSION_HEX >= 0x02060000 +/* PatternObject's 'subf' method. */ +static PyObject* pattern_subf(PatternObject* self, PyObject* args, PyObject* + kwargs) { + int conc; + + PyObject* format; + PyObject* string; + Py_ssize_t count = 0; + PyObject* pos = Py_None; + PyObject* endpos = Py_None; + PyObject* concurrent = Py_None; + static char* kwlist[] = { "format", "string", "count", "pos", "endpos", + "concurrent", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|nOOO:sub", kwlist, + &format, &string, &count, &pos, &endpos, &concurrent)) + return NULL; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + return pattern_subx(self, format, string, count, RE_SUBF, pos, endpos, + conc); +} + +#endif +/* PatternObject's 'subn' method. */ +static PyObject* pattern_subn(PatternObject* self, PyObject* args, PyObject* + kwargs) { + int conc; + + PyObject* replacement; + PyObject* string; + Py_ssize_t count = 0; + PyObject* pos = Py_None; + PyObject* endpos = Py_None; + PyObject* concurrent = Py_None; + static char* kwlist[] = { "repl", "string", "count", "pos", "endpos", + "concurrent", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|nOOO:subn", kwlist, + &replacement, &string, &count, &pos, &endpos, &concurrent)) + return NULL; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + return pattern_subx(self, replacement, string, count, RE_SUBN, pos, endpos, + conc); +} + +#if PY_VERSION_HEX >= 0x02060000 +/* PatternObject's 'subfn' method. */ +static PyObject* pattern_subfn(PatternObject* self, PyObject* args, PyObject* + kwargs) { + int conc; + + PyObject* format; + PyObject* string; + Py_ssize_t count = 0; + PyObject* pos = Py_None; + PyObject* endpos = Py_None; + PyObject* concurrent = Py_None; + static char* kwlist[] = { "format", "string", "count", "pos", "endpos", + "concurrent", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|nOOO:subn", kwlist, + &format, &string, &count, &pos, &endpos, &concurrent)) + return NULL; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + return pattern_subx(self, format, string, count, RE_SUBF | RE_SUBN, pos, + endpos, conc); +} + +#endif +/* PatternObject's 'split' method. */ +static PyObject* pattern_split(PatternObject* self, PyObject* args, PyObject* + kwargs) { + int conc; + + RE_State state; + RE_SafeState safe_state; + PyObject* list; + PyObject* item; + int status; + Py_ssize_t split_count; + size_t g; + Py_ssize_t start_pos; + Py_ssize_t end_pos; + Py_ssize_t step; + Py_ssize_t last_pos; + + PyObject* string; + Py_ssize_t maxsplit = 0; + PyObject* concurrent = Py_None; + static char* kwlist[] = { "string", "maxsplit", "concurrent", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|nO:split", kwlist, + &string, &maxsplit, &concurrent)) + return NULL; + + if (maxsplit == 0) + maxsplit = PY_SSIZE_T_MAX; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + /* The MatchObject, and therefore repeated captures, will not be visible. + */ + if (!state_init(&state, self, string, 0, PY_SSIZE_T_MAX, FALSE, conc, + FALSE, FALSE, FALSE, FALSE)) + return NULL; + + /* Initialise the "safe state" structure. */ + safe_state.re_state = &state; + safe_state.thread_state = NULL; + + list = PyList_New(0); + if (!list) { + state_fini(&state); + return NULL; + } + + split_count = 0; + if (state.reverse) { + start_pos = state.text_length; + end_pos = 0; + step = -1; + } else { + start_pos = 0; + end_pos = state.text_length; + step = 1; + } + + last_pos = start_pos; + while (split_count < maxsplit) { + status = do_match(&safe_state, TRUE); + if (status < 0) + goto error; + + if (status == 0) + /* No more matches. */ + break; + + if (state.version_0) { + /* Version 0 behaviour is to advance one character if the split was + * zero-width. Unfortunately, this can give an incorrect result. + * GvR wants this behaviour to be retained so as not to break any + * existing software which might rely on it. + */ + if (state.text_pos == state.match_pos) { + if (last_pos == end_pos) + break; + + /* Advance one character. */ + state.text_pos += step; + state.must_advance = FALSE; + continue; + } + } + + /* Get segment before this match. */ + if (state.reverse) + item = get_slice(string, state.match_pos, last_pos); + else + item = get_slice(string, last_pos, state.match_pos); + if (!item) + goto error; + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + /* Add groups (if any). */ + for (g = 1; g <= self->public_group_count; g++) { + item = state_get_group(&state, (Py_ssize_t)g, string, FALSE); + if (!item) + goto error; + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + ++split_count; + last_pos = state.text_pos; + + /* Version 0 behaviour is to advance one character if the match was + * zero-width. Unfortunately, this can give an incorrect result. GvR + * wants this behaviour to be retained so as not to break any existing + * software which might rely on it. + */ + if (state.version_0) { + if (state.text_pos == state.match_pos) + /* Advance one character. */ + state.text_pos += step; + + state.must_advance = FALSE; + } else + /* Continue from where we left off, but don't allow a contiguous + * zero-width match. + */ + state.must_advance = TRUE; + } + + /* Get segment following last match (even if empty). */ + if (state.reverse) + item = get_slice(string, 0, last_pos); + else + item = get_slice(string, last_pos, state.text_length); + if (!item) + goto error; + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + state_fini(&state); + + return list; + +error: + Py_DECREF(list); + state_fini(&state); + return NULL; +} + +/* PatternObject's 'splititer' method. */ +static PyObject* pattern_splititer(PatternObject* pattern, PyObject* args, + PyObject* kwargs) { + return pattern_splitter(pattern, args, kwargs); +} + +/* PatternObject's 'findall' method. */ +static PyObject* pattern_findall(PatternObject* self, PyObject* args, PyObject* + kwargs) { + Py_ssize_t start; + Py_ssize_t end; + int conc; + RE_State state; + RE_SafeState safe_state; + PyObject* list; + Py_ssize_t step; + int status; + Py_ssize_t b; + Py_ssize_t e; + size_t g; + + PyObject* string; + PyObject* pos = Py_None; + PyObject* endpos = Py_None; + Py_ssize_t overlapped = FALSE; + PyObject* concurrent = Py_None; + static char* kwlist[] = { "string", "pos", "endpos", "overlapped", + "concurrent", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOnO:findall", kwlist, + &string, &pos, &endpos, &overlapped, &concurrent)) + return NULL; + + start = as_string_index(pos, 0); + if (start == -1 && PyErr_Occurred()) + return NULL; + + end = as_string_index(endpos, PY_SSIZE_T_MAX); + if (end == -1 && PyErr_Occurred()) + return NULL; + + conc = decode_concurrent(concurrent); + if (conc < 0) + return NULL; + + /* The MatchObject, and therefore repeated captures, will not be visible. + */ + if (!state_init(&state, self, string, start, end, overlapped != 0, conc, + FALSE, FALSE, FALSE, FALSE)) + return NULL; + + /* Initialise the "safe state" structure. */ + safe_state.re_state = &state; + safe_state.thread_state = NULL; + + list = PyList_New(0); + if (!list) { + state_fini(&state); + return NULL; + } + + step = state.reverse ? -1 : 1; + while (state.slice_start <= state.text_pos && state.text_pos <= + state.slice_end) { + PyObject* item; + + status = do_match(&safe_state, TRUE); + if (status < 0) + goto error; + + if (status == 0) + break; + + /* Don't bother to build a MatchObject. */ + switch (self->public_group_count) { + case 0: + if (state.reverse) { + b = state.text_pos; + e = state.match_pos; + } else { + b = state.match_pos; + e = state.text_pos; + } + item = get_slice(string, b, e); + if (!item) + goto error; + break; + case 1: + item = state_get_group(&state, 1, string, TRUE); + if (!item) + goto error; + break; + default: + item = PyTuple_New((Py_ssize_t)self->public_group_count); + if (!item) + goto error; + + for (g = 0; g < self->public_group_count; g++) { + PyObject* o; + + o = state_get_group(&state, (Py_ssize_t)g + 1, string, TRUE); + if (!o) { + Py_DECREF(item); + goto error; + } + + /* PyTuple_SET_ITEM borrows the reference. */ + PyTuple_SET_ITEM(item, g, o); + } + break; + } + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + if (state.overlapped) { + /* Advance one character. */ + state.text_pos = state.match_pos + step; + state.must_advance = FALSE; + } else + /* Continue from where we left off, but don't allow 2 contiguous + * zero-width matches. + */ + state.must_advance = state.text_pos == state.match_pos; + } + + state_fini(&state); + + return list; + +error: + Py_DECREF(list); + state_fini(&state); + return NULL; +} + +/* PatternObject's 'finditer' method. */ +static PyObject* pattern_finditer(PatternObject* pattern, PyObject* args, + PyObject* kwargs) { + return pattern_scanner(pattern, args, kwargs); +} + +/* Makes a copy of a PatternObject. */ +Py_LOCAL_INLINE(PyObject*) make_pattern_copy(PatternObject* self) { + Py_INCREF(self); + return (PyObject*)self; +} + +/* PatternObject's '__copy__' method. */ +static PyObject* pattern_copy(PatternObject* self, PyObject *unused) { + return make_pattern_copy(self); +} + +/* PatternObject's '__deepcopy__' method. */ +static PyObject* pattern_deepcopy(PatternObject* self, PyObject* memo) { + return make_pattern_copy(self); +} + +/* The documentation of a PatternObject. */ +PyDoc_STRVAR(pattern_match_doc, + "match(string, pos=None, endpos=None, concurrent=None) --> MatchObject or None.\n\ + Match zero or more characters at the beginning of the string."); + +PyDoc_STRVAR(pattern_fullmatch_doc, + "fullmatch(string, pos=None, endpos=None, concurrent=None) --> MatchObject or None.\n\ + Match zero or more characters against all of the string."); + +PyDoc_STRVAR(pattern_search_doc, + "search(string, pos=None, endpos=None, concurrent=None) --> MatchObject or None.\n\ + Search through string looking for a match, and return a corresponding\n\ + match object instance. Return None if no match is found."); + +PyDoc_STRVAR(pattern_sub_doc, + "sub(repl, string, count=0, flags=0, pos=None, endpos=None, concurrent=None) --> newstring\n\ + Return the string obtained by replacing the leftmost (or rightmost with a\n\ + reverse pattern) non-overlapping occurrences of pattern in string by the\n\ + replacement repl."); + +#if PY_VERSION_HEX >= 0x02060000 +PyDoc_STRVAR(pattern_subf_doc, + "subf(format, string, count=0, flags=0, pos=None, endpos=None, concurrent=None) --> newstring\n\ + Return the string obtained by replacing the leftmost (or rightmost with a\n\ + reverse pattern) non-overlapping occurrences of pattern in string by the\n\ + replacement format."); + +#endif +PyDoc_STRVAR(pattern_subn_doc, + "subn(repl, string, count=0, flags=0, pos=None, endpos=None, concurrent=None) --> (newstring, number of subs)\n\ + Return the tuple (new_string, number_of_subs_made) found by replacing the\n\ + leftmost (or rightmost with a reverse pattern) non-overlapping occurrences\n\ + of pattern with the replacement repl."); + +#if PY_VERSION_HEX >= 0x02060000 +PyDoc_STRVAR(pattern_subfn_doc, + "subfn(format, string, count=0, flags=0, pos=None, endpos=None, concurrent=None) --> (newstring, number of subs)\n\ + Return the tuple (new_string, number_of_subs_made) found by replacing the\n\ + leftmost (or rightmost with a reverse pattern) non-overlapping occurrences\n\ + of pattern with the replacement format."); + +#endif +PyDoc_STRVAR(pattern_split_doc, + "split(string, string, maxsplit=0, concurrent=None) --> list.\n\ + Split string by the occurrences of pattern."); + +PyDoc_STRVAR(pattern_splititer_doc, + "splititer(string, maxsplit=0, concurrent=None) --> iterator.\n\ + Return an iterator yielding the parts of a split string."); + +PyDoc_STRVAR(pattern_findall_doc, + "findall(string, pos=None, endpos=None, overlapped=False, concurrent=None) --> list.\n\ + Return a list of all matches of pattern in string. The matches may be\n\ + overlapped if overlapped is True."); + +PyDoc_STRVAR(pattern_finditer_doc, + "finditer(string, pos=None, endpos=None, overlapped=False, concurrent=None) --> iterator.\n\ + Return an iterator over all matches for the RE pattern in string. The\n\ + matches may be overlapped if overlapped is True. For each match, the\n\ + iterator returns a MatchObject."); + +PyDoc_STRVAR(pattern_scanner_doc, + "scanner(string, pos=None, endpos=None, overlapped=False, concurrent=None) --> scanner.\n\ + Return an scanner for the RE pattern in string. The matches may be overlapped\n\ + if overlapped is True."); + +/* The methods of a PatternObject. */ +static PyMethodDef pattern_methods[] = { + {"match", (PyCFunction)pattern_match, METH_VARARGS|METH_KEYWORDS, + pattern_match_doc}, + {"fullmatch", (PyCFunction)pattern_fullmatch, METH_VARARGS|METH_KEYWORDS, + pattern_fullmatch_doc}, + {"search", (PyCFunction)pattern_search, METH_VARARGS|METH_KEYWORDS, + pattern_search_doc}, + {"sub", (PyCFunction)pattern_sub, METH_VARARGS|METH_KEYWORDS, + pattern_sub_doc}, +#if PY_VERSION_HEX >= 0x02060000 + {"subf", (PyCFunction)pattern_subf, METH_VARARGS|METH_KEYWORDS, + pattern_subf_doc}, +#endif + {"subn", (PyCFunction)pattern_subn, METH_VARARGS|METH_KEYWORDS, + pattern_subn_doc}, +#if PY_VERSION_HEX >= 0x02060000 + {"subfn", (PyCFunction)pattern_subfn, METH_VARARGS|METH_KEYWORDS, + pattern_subfn_doc}, +#endif + {"split", (PyCFunction)pattern_split, METH_VARARGS|METH_KEYWORDS, + pattern_split_doc}, + {"splititer", (PyCFunction)pattern_splititer, METH_VARARGS|METH_KEYWORDS, + pattern_splititer_doc}, + {"findall", (PyCFunction)pattern_findall, METH_VARARGS|METH_KEYWORDS, + pattern_findall_doc}, + {"finditer", (PyCFunction)pattern_finditer, METH_VARARGS|METH_KEYWORDS, + pattern_finditer_doc}, + {"scanner", (PyCFunction)pattern_scanner, METH_VARARGS|METH_KEYWORDS, + pattern_scanner_doc}, + {"__copy__", (PyCFunction)pattern_copy, METH_NOARGS}, + {"__deepcopy__", (PyCFunction)pattern_deepcopy, METH_O}, + {NULL, NULL} +}; + +PyDoc_STRVAR(pattern_doc, "Compiled regex object"); + +/* Deallocates a PatternObject. */ +static void pattern_dealloc(PyObject* self_) { + PatternObject* self; + size_t i; + int partial_side; + + self = (PatternObject*)self_; + + /* Discard the nodes. */ + for (i = 0; i < self->node_count; i++) { + RE_Node* node; + + node = self->node_list[i]; + re_dealloc(node->values); + if (node->status & RE_STATUS_STRING) { + re_dealloc(node->string.bad_character_offset); + re_dealloc(node->string.good_suffix_offset); + } + re_dealloc(node); + } + re_dealloc(self->node_list); + + /* Discard the group info. */ + re_dealloc(self->group_info); + + /* Discard the call_ref info. */ + re_dealloc(self->call_ref_info); + + /* Discard the repeat info. */ + re_dealloc(self->repeat_info); + + dealloc_groups(self->groups_storage, self->true_group_count); + + dealloc_repeats(self->repeats_storage, self->repeat_count); + + if (self->weakreflist) + PyObject_ClearWeakRefs((PyObject*)self); + Py_XDECREF(self->pattern); + Py_XDECREF(self->groupindex); + Py_XDECREF(self->indexgroup); + + for (partial_side = 0; partial_side < 2; partial_side++) { + if (self->partial_named_lists[partial_side]) { + for (i = 0; i < self->named_lists_count; i++) + Py_XDECREF(self->partial_named_lists[partial_side][i]); + + re_dealloc(self->partial_named_lists[partial_side]); + } + } + + Py_DECREF(self->named_lists); + Py_DECREF(self->named_list_indexes); + re_dealloc(self->locale_info); + PyObject_DEL(self); +} + +/* Info about the various flags that can be passed in. */ +typedef struct RE_FlagName { + char* name; + int value; +} RE_FlagName; + +/* We won't bother about the A flag in Python 2. */ +static RE_FlagName flag_names[] = { + {"B", RE_FLAG_BESTMATCH}, + {"D", RE_FLAG_DEBUG}, + {"S", RE_FLAG_DOTALL}, + {"F", RE_FLAG_FULLCASE}, + {"I", RE_FLAG_IGNORECASE}, + {"L", RE_FLAG_LOCALE}, + {"M", RE_FLAG_MULTILINE}, + {"P", RE_FLAG_POSIX}, + {"R", RE_FLAG_REVERSE}, + {"T", RE_FLAG_TEMPLATE}, + {"U", RE_FLAG_UNICODE}, + {"X", RE_FLAG_VERBOSE}, + {"V0", RE_FLAG_VERSION0}, + {"V1", RE_FLAG_VERSION1}, + {"W", RE_FLAG_WORD}, +}; + +/* Appends a string to a list. */ +Py_LOCAL_INLINE(BOOL) append_string(PyObject* list, char* string) { + PyObject* item; + int status; + + item = Py_BuildValue("s", string); + if (!item) + return FALSE; + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + return FALSE; + + return TRUE; +} + +/* Appends a (decimal) integer to a list. */ +Py_LOCAL_INLINE(BOOL) append_integer(PyObject* list, Py_ssize_t value) { + PyObject* int_obj; + PyObject* repr_obj; + int status; + + int_obj = Py_BuildValue("n", value); + if (!int_obj) + return FALSE; + + repr_obj = PyObject_Repr(int_obj); + Py_DECREF(int_obj); + if (!repr_obj) + return FALSE; + + status = PyList_Append(list, repr_obj); + Py_DECREF(repr_obj); + if (status < 0) + return FALSE; + + return TRUE; +} + +/* MatchObject's '__repr__' method. */ +static PyObject* match_repr(PyObject* self_) { + MatchObject* self; + PyObject* list; + PyObject* matched_substring; + PyObject* matched_repr; + int status; + PyObject* separator; + PyObject* result; + + self = (MatchObject*)self_; + + list = PyList_New(0); + if (!list) + return NULL; + + if (!append_string(list, "<regex.Match object; span=(")) + goto error; + + if (!append_integer(list, self->match_start)) + goto error; + + if (! append_string(list, ", ")) + goto error; + + if (!append_integer(list, self->match_end)) + goto error; + + if (!append_string(list, "), match=")) + goto error; + + matched_substring = get_slice(self->substring, self->match_start - + self->substring_offset, self->match_end - self->substring_offset); + if (!matched_substring) + goto error; + + matched_repr = PyObject_Repr(matched_substring); + Py_DECREF(matched_substring); + if (!matched_repr) + goto error; + + status = PyList_Append(list, matched_repr); + Py_DECREF(matched_repr); + if (status < 0) + goto error; + + if (self->fuzzy_counts[RE_FUZZY_SUB] != 0 || + self->fuzzy_counts[RE_FUZZY_INS] != 0 || self->fuzzy_counts[RE_FUZZY_DEL] + != 0) { + if (! append_string(list, ", fuzzy_counts=(")) + goto error; + + if (!append_integer(list, + (Py_ssize_t)self->fuzzy_counts[RE_FUZZY_SUB])) + goto error; + + if (! append_string(list, ", ")) + goto error; + + if (!append_integer(list, + (Py_ssize_t)self->fuzzy_counts[RE_FUZZY_INS])) + goto error; + + if (! append_string(list, ", ")) + goto error; + if (!append_integer(list, + (Py_ssize_t)self->fuzzy_counts[RE_FUZZY_DEL])) + goto error; + + if (! append_string(list, ")")) + goto error; + } + + if (self->partial) { + if (!append_string(list, ", partial=True")) + goto error; + } + + if (! append_string(list, ">")) + goto error; + + separator = Py_BuildValue("s", ""); + if (!separator) + goto error; + + result = PyUnicode_Join(separator, list); + Py_DECREF(separator); + Py_DECREF(list); + + return result; + +error: + Py_DECREF(list); + return NULL; +} + +/* PatternObject's '__repr__' method. */ +static PyObject* pattern_repr(PyObject* self_) { + PatternObject* self; + PyObject* list; + PyObject* item; + int status; + int flag_count; + unsigned int i; + Py_ssize_t pos; + PyObject *key; + PyObject *value; + PyObject* separator; + PyObject* result; + + self = (PatternObject*)self_; + + list = PyList_New(0); + if (!list) + return NULL; + + if (!append_string(list, "regex.Regex(")) + goto error; + + item = PyObject_Repr(self->pattern); + if (!item) + goto error; + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + flag_count = 0; + for (i = 0; i < sizeof(flag_names) / sizeof(flag_names[0]); i++) { + if (self->flags & flag_names[i].value) { + if (flag_count == 0) { + if (!append_string(list, ", flags=")) + goto error; + } else { + if (!append_string(list, " | ")) + goto error; + } + + if (!append_string(list, "regex.")) + goto error; + + if (!append_string(list, flag_names[i].name)) + goto error; + + ++flag_count; + } + } + + pos = 0; + /* PyDict_Next borrows references. */ + while (PyDict_Next(self->named_lists, &pos, &key, &value)) { + if (!append_string(list, ", ")) + goto error; + + status = PyList_Append(list, key); + if (status < 0) + goto error; + + if (!append_string(list, "=")) + goto error; + + item = PyObject_Repr(value); + if (!item) + goto error; + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + if (!append_string(list, ")")) + goto error; + + separator = Py_BuildValue("s", ""); + if (!separator) + goto error; + + result = PyUnicode_Join(separator, list); + Py_DECREF(separator); + Py_DECREF(list); + + return result; + +error: + Py_DECREF(list); + return NULL; +} + +/* PatternObject's 'groupindex' method. */ +static PyObject* pattern_groupindex(PyObject* self_) { + PatternObject* self; + + self = (PatternObject*)self_; + + return PyDict_Copy(self->groupindex); +} + +static PyGetSetDef pattern_getset[] = { + {"groupindex", (getter)pattern_groupindex, (setter)NULL, + "A dictionary mapping group names to group numbers."}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef pattern_members[] = { + {"pattern", T_OBJECT, offsetof(PatternObject, pattern), READONLY, + "The pattern string from which the regex object was compiled."}, + {"flags", T_PYSSIZET, offsetof(PatternObject, flags), READONLY, + "The regex matching flags."}, + {"groups", T_PYSSIZET, offsetof(PatternObject, public_group_count), + READONLY, "The number of capturing groups in the pattern."}, + {"named_lists", T_OBJECT, offsetof(PatternObject, named_lists), READONLY, + "The named lists used by the regex."}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject Pattern_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "_" RE_MODULE "." "Pattern", + sizeof(PatternObject) +}; + +/* Building the nodes is made simpler by allowing branches to have a single + * exit. These need to be removed. + */ +Py_LOCAL_INLINE(void) skip_one_way_branches(PatternObject* pattern) { + BOOL modified; + + /* If a node refers to a 1-way branch then make the former refer to the + * latter's destination. Repeat until they're all done. + */ + do { + size_t i; + + modified = FALSE; + + for (i = 0; i < pattern->node_count; i++) { + RE_Node* node; + RE_Node* next; + + node = pattern->node_list[i]; + + /* Check the first destination. */ + next = node->next_1.node; + if (next && next->op == RE_OP_BRANCH && + !next->nonstring.next_2.node) { + node->next_1.node = next->next_1.node; + modified = TRUE; + } + + /* Check the second destination. */ + next = node->nonstring.next_2.node; + if (next && next->op == RE_OP_BRANCH && + !next->nonstring.next_2.node) { + node->nonstring.next_2.node = next->next_1.node; + modified = TRUE; + } + } + } while (modified); + + /* The start node might be a 1-way branch. Skip over it because it'll be + * removed. It might even be the first in a chain. + */ + while (pattern->start_node->op == RE_OP_BRANCH && + !pattern->start_node->nonstring.next_2.node) + pattern->start_node = pattern->start_node->next_1.node; +} + +/* Adds guards to repeats which are followed by a reference to a group. + * + * Returns whether a guard was added for a node at or after the given node. + */ +Py_LOCAL_INLINE(RE_STATUS_T) add_repeat_guards(PatternObject* pattern, RE_Node* + node) { + RE_STATUS_T result; + + result = RE_STATUS_NEITHER; + + for (;;) { + if (node->status & RE_STATUS_VISITED_AG) + return node->status & (RE_STATUS_REPEAT | RE_STATUS_REF); + + switch (node->op) { + case RE_OP_BRANCH: + { + RE_STATUS_T branch_1_result; + RE_STATUS_T branch_2_result; + RE_STATUS_T status; + + branch_1_result = add_repeat_guards(pattern, node->next_1.node); + branch_2_result = add_repeat_guards(pattern, + node->nonstring.next_2.node); + status = max_status_3(result, branch_1_result, branch_2_result); + node->status = RE_STATUS_VISITED_AG | status; + return status; + } + case RE_OP_END_GREEDY_REPEAT: + case RE_OP_END_LAZY_REPEAT: + node->status |= RE_STATUS_VISITED_AG; + return result; + case RE_OP_GREEDY_REPEAT: + case RE_OP_LAZY_REPEAT: + { + BOOL limited; + RE_STATUS_T body_result; + RE_STATUS_T tail_result; + RE_RepeatInfo* repeat_info; + RE_STATUS_T status; + + limited = ~node->values[2] != 0; + if (limited) + body_result = RE_STATUS_LIMITED; + else + body_result = add_repeat_guards(pattern, node->next_1.node); + tail_result = add_repeat_guards(pattern, + node->nonstring.next_2.node); + + repeat_info = &pattern->repeat_info[node->values[0]]; + if (body_result != RE_STATUS_REF) + repeat_info->status |= RE_STATUS_BODY; + if (tail_result != RE_STATUS_REF) + repeat_info->status |= RE_STATUS_TAIL; + if (limited) + result = max_status_2(result, RE_STATUS_LIMITED); + else + result = max_status_2(result, RE_STATUS_REPEAT); + status = max_status_3(result, body_result, tail_result); + node->status |= RE_STATUS_VISITED_AG | status; + return status; + } + case RE_OP_GREEDY_REPEAT_ONE: + case RE_OP_LAZY_REPEAT_ONE: + { + BOOL limited; + RE_STATUS_T tail_result; + RE_RepeatInfo* repeat_info; + RE_STATUS_T status; + + limited = ~node->values[2] != 0; + tail_result = add_repeat_guards(pattern, node->next_1.node); + + repeat_info = &pattern->repeat_info[node->values[0]]; + repeat_info->status |= RE_STATUS_BODY; + if (tail_result != RE_STATUS_REF) + repeat_info->status |= RE_STATUS_TAIL; + if (limited) + result = max_status_2(result, RE_STATUS_LIMITED); + else + result = max_status_2(result, RE_STATUS_REPEAT); + status = max_status_3(result, RE_STATUS_REPEAT, tail_result); + node->status = RE_STATUS_VISITED_AG | status; + return status; + } + case RE_OP_GROUP_CALL: + case RE_OP_REF_GROUP: + case RE_OP_REF_GROUP_FLD: + case RE_OP_REF_GROUP_FLD_REV: + case RE_OP_REF_GROUP_IGN: + case RE_OP_REF_GROUP_IGN_REV: + case RE_OP_REF_GROUP_REV: + result = RE_STATUS_REF; + node = node->next_1.node; + break; + case RE_OP_GROUP_EXISTS: + { + RE_STATUS_T branch_1_result; + RE_STATUS_T branch_2_result; + RE_STATUS_T status; + + branch_1_result = add_repeat_guards(pattern, node->next_1.node); + branch_2_result = add_repeat_guards(pattern, + node->nonstring.next_2.node); + status = max_status_4(result, branch_1_result, branch_2_result, + RE_STATUS_REF); + node->status = RE_STATUS_VISITED_AG | status; + return status; + } + case RE_OP_SUCCESS: + node->status = RE_STATUS_VISITED_AG | result; + return result; + default: + node = node->next_1.node; + break; + } + } +} + +/* Adds an index to a node's values unless it's already present. + * + * 'offset' is the offset of the index count within the values. + */ +Py_LOCAL_INLINE(BOOL) add_index(RE_Node* node, size_t offset, size_t index) { + size_t index_count; + size_t first_index; + size_t i; + RE_CODE* new_values; + + if (!node) + return TRUE; + + index_count = node->values[offset]; + first_index = offset + 1; + + /* Is the index already present? */ + for (i = 0; i < index_count; i++) { + if (node->values[first_index + i] == index) + return TRUE; + } + + /* Allocate more space for the new index. */ + new_values = re_realloc(node->values, (node->value_count + 1) * + sizeof(RE_CODE)); + if (!new_values) + return FALSE; + + ++node->value_count; + node->values = new_values; + + node->values[first_index + node->values[offset]++] = (RE_CODE)index; + + return TRUE; +} + +/* Records the index of every repeat and fuzzy section within atomic + * subpatterns and lookarounds. + */ +Py_LOCAL_INLINE(BOOL) record_subpattern_repeats_and_fuzzy_sections(RE_Node* + parent_node, size_t offset, size_t repeat_count, RE_Node* node) { + while (node) { + if (node->status & RE_STATUS_VISITED_REP) + return TRUE; + + node->status |= RE_STATUS_VISITED_REP; + + switch (node->op) { + case RE_OP_BRANCH: + case RE_OP_GROUP_EXISTS: + if (!record_subpattern_repeats_and_fuzzy_sections(parent_node, + offset, repeat_count, node->next_1.node)) + return FALSE; + node = node->nonstring.next_2.node; + break; + case RE_OP_END_FUZZY: + node = node->next_1.node; + break; + case RE_OP_END_GREEDY_REPEAT: + case RE_OP_END_LAZY_REPEAT: + return TRUE; + case RE_OP_FUZZY: + /* Record the fuzzy index. */ + if (!add_index(parent_node, offset, repeat_count + + node->values[0])) + return FALSE; + node = node->next_1.node; + break; + case RE_OP_GREEDY_REPEAT: + case RE_OP_LAZY_REPEAT: + /* Record the repeat index. */ + if (!add_index(parent_node, offset, node->values[0])) + return FALSE; + if (!record_subpattern_repeats_and_fuzzy_sections(parent_node, + offset, repeat_count, node->next_1.node)) + return FALSE; + node = node->nonstring.next_2.node; + break; + case RE_OP_GREEDY_REPEAT_ONE: + case RE_OP_LAZY_REPEAT_ONE: + /* Record the repeat index. */ + if (!add_index(parent_node, offset, node->values[0])) + return FALSE; + node = node->next_1.node; + break; + default: + node = node->next_1.node; + break; + } + } + + return TRUE; +} + +/* Marks nodes which are being used as used. */ +Py_LOCAL_INLINE(void) use_nodes(RE_Node* node) { + while (node && !(node->status & RE_STATUS_USED)) { + node->status |= RE_STATUS_USED; + if (!(node->status & RE_STATUS_STRING)) { + if (node->nonstring.next_2.node) + use_nodes(node->nonstring.next_2.node); + } + node = node->next_1.node; + } +} + +/* Discards any unused nodes. + * + * Optimising the nodes might result in some nodes no longer being used. + */ +Py_LOCAL_INLINE(void) discard_unused_nodes(PatternObject* pattern) { + size_t i; + size_t new_count; + + /* Mark the nodes which are being used. */ + use_nodes(pattern->start_node); + + for (i = 0; i < pattern->call_ref_info_capacity; i++) + use_nodes(pattern->call_ref_info[i].node); + + new_count = 0; + for (i = 0; i < pattern->node_count; i++) { + RE_Node* node; + + node = pattern->node_list[i]; + if (node->status & RE_STATUS_USED) + pattern->node_list[new_count++] = node; + else { + re_dealloc(node->values); + if (node->status & RE_STATUS_STRING) { + re_dealloc(node->string.bad_character_offset); + re_dealloc(node->string.good_suffix_offset); + } + re_dealloc(node); + } + } + + pattern->node_count = new_count; +} + +/* Marks all the group which are named. Returns FALSE if there's an error. */ +Py_LOCAL_INLINE(BOOL) mark_named_groups(PatternObject* pattern) { + size_t i; + + for (i = 0; i < pattern->public_group_count; i++) { + RE_GroupInfo* group_info; + PyObject* index; + int status; + + group_info = &pattern->group_info[i]; + index = Py_BuildValue("n", i + 1); + if (!index) + return FALSE; + + status = PyDict_Contains(pattern->indexgroup, index); + Py_DECREF(index); + if (status < 0) + return FALSE; + + group_info->has_name = status == 1; + } + + return TRUE; +} + +/* Gets the test node. + * + * The test node lets the matcher look ahead in the pattern, allowing it to + * avoid the cost of housekeeping, only to find that what follows doesn't match + * anyway. + */ +Py_LOCAL_INLINE(void) set_test_node(RE_NextNode* next) { + RE_Node* node = next->node; + RE_Node* test; + + next->test = node; + next->match_next = node; + next->match_step = 0; + + if (!node) + return; + + test = node; + while (test->op == RE_OP_END_GROUP || test->op == RE_OP_START_GROUP) + test = test->next_1.node; + + next->test = test; + + if (test != node) + return; + + switch (test->op) { + case RE_OP_ANY: + case RE_OP_ANY_ALL: + case RE_OP_ANY_ALL_REV: + case RE_OP_ANY_REV: + case RE_OP_ANY_U: + case RE_OP_ANY_U_REV: + case RE_OP_BOUNDARY: + case RE_OP_CHARACTER: + case RE_OP_CHARACTER_IGN: + case RE_OP_CHARACTER_IGN_REV: + case RE_OP_CHARACTER_REV: + case RE_OP_DEFAULT_BOUNDARY: + case RE_OP_DEFAULT_END_OF_WORD: + case RE_OP_DEFAULT_START_OF_WORD: + case RE_OP_END_OF_LINE: + case RE_OP_END_OF_LINE_U: + case RE_OP_END_OF_STRING: + case RE_OP_END_OF_STRING_LINE: + case RE_OP_END_OF_STRING_LINE_U: + case RE_OP_END_OF_WORD: + case RE_OP_GRAPHEME_BOUNDARY: + case RE_OP_PROPERTY: + case RE_OP_PROPERTY_IGN: + case RE_OP_PROPERTY_IGN_REV: + case RE_OP_PROPERTY_REV: + case RE_OP_RANGE: + case RE_OP_RANGE_IGN: + case RE_OP_RANGE_IGN_REV: + case RE_OP_RANGE_REV: + case RE_OP_SEARCH_ANCHOR: + case RE_OP_SET_DIFF: + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_DIFF_IGN_REV: + case RE_OP_SET_DIFF_REV: + case RE_OP_SET_INTER: + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION: + case RE_OP_SET_UNION_IGN: + case RE_OP_SET_UNION_IGN_REV: + case RE_OP_SET_UNION_REV: + case RE_OP_START_OF_LINE: + case RE_OP_START_OF_LINE_U: + case RE_OP_START_OF_STRING: + case RE_OP_START_OF_WORD: + case RE_OP_STRING: + case RE_OP_STRING_FLD: + case RE_OP_STRING_FLD_REV: + case RE_OP_STRING_IGN: + case RE_OP_STRING_IGN_REV: + case RE_OP_STRING_REV: + next->match_next = test->next_1.node; + next->match_step = test->step; + break; + case RE_OP_GREEDY_REPEAT_ONE: + case RE_OP_LAZY_REPEAT_ONE: + if (test->values[1] > 0) + next->test = test; + break; + } +} + +/* Sets the test nodes. */ +Py_LOCAL_INLINE(void) set_test_nodes(PatternObject* pattern) { + RE_Node** node_list; + size_t i; + + node_list = pattern->node_list; + for (i = 0; i < pattern->node_count; i++) { + RE_Node* node; + + node = node_list[i]; + set_test_node(&node->next_1); + if (!(node->status & RE_STATUS_STRING)) + set_test_node(&node->nonstring.next_2); + } +} + +/* Optimises the pattern. */ +Py_LOCAL_INLINE(BOOL) optimise_pattern(PatternObject* pattern) { + size_t i; + + /* Building the nodes is made simpler by allowing branches to have a single + * exit. These need to be removed. + */ + skip_one_way_branches(pattern); + + /* Add position guards for repeat bodies containing a reference to a group + * or repeat tails followed at some point by a reference to a group. + */ + add_repeat_guards(pattern, pattern->start_node); + + /* Record the index of repeats and fuzzy sections within the body of atomic + * and lookaround nodes. + */ + if (!record_subpattern_repeats_and_fuzzy_sections(NULL, 0, + pattern->repeat_count, pattern->start_node)) + return FALSE; + + for (i = 0; i < pattern->call_ref_info_count; i++) { + RE_Node* node; + + node = pattern->call_ref_info[i].node; + if (!record_subpattern_repeats_and_fuzzy_sections(NULL, 0, + pattern->repeat_count, node)) + return FALSE; + } + + /* Discard any unused nodes. */ + discard_unused_nodes(pattern); + + /* Set the test nodes. */ + set_test_nodes(pattern); + + /* Mark all the group that are named. */ + if (!mark_named_groups(pattern)) + return FALSE; + + return TRUE; +} + +/* Creates a new pattern node. */ +Py_LOCAL_INLINE(RE_Node*) create_node(PatternObject* pattern, RE_UINT8 op, + RE_CODE flags, Py_ssize_t step, size_t value_count) { + RE_Node* node; + + node = (RE_Node*)re_alloc(sizeof(*node)); + if (!node) + return NULL; + memset(node, 0, sizeof(RE_Node)); + + node->value_count = value_count; + if (node->value_count > 0) { + node->values = (RE_CODE*)re_alloc(node->value_count * sizeof(RE_CODE)); + if (!node->values) + goto error; + } else + node->values = NULL; + + node->op = op; + node->match = (flags & RE_POSITIVE_OP) != 0; + node->status = (RE_STATUS_T)(flags << RE_STATUS_SHIFT); + node->step = step; + + /* Ensure that there's enough storage to record the new node. */ + if (pattern->node_count >= pattern->node_capacity) { + RE_Node** new_node_list; + + pattern->node_capacity *= 2; + if (pattern->node_capacity == 0) + pattern->node_capacity = RE_INIT_NODE_LIST_SIZE; + new_node_list = (RE_Node**)re_realloc(pattern->node_list, + pattern->node_capacity * sizeof(RE_Node*)); + if (!new_node_list) + goto error; + pattern->node_list = new_node_list; + } + + /* Record the new node. */ + pattern->node_list[pattern->node_count++] = node; + + return node; + +error: + re_dealloc(node->values); + re_dealloc(node); + return NULL; +} + +/* Adds a node as a next node for another node. */ +Py_LOCAL_INLINE(void) add_node(RE_Node* node_1, RE_Node* node_2) { + if (!node_1->next_1.node) + node_1->next_1.node = node_2; + else + node_1->nonstring.next_2.node = node_2; +} + +/* Ensures that the entry for a group's details actually exists. */ +Py_LOCAL_INLINE(BOOL) ensure_group(PatternObject* pattern, size_t group) { + size_t old_capacity; + size_t new_capacity; + RE_GroupInfo* new_group_info; + + if (group <= pattern->true_group_count) + /* We already have an entry for the group. */ + return TRUE; + + /* Increase the storage capacity to include the new entry if it's + * insufficient. + */ + old_capacity = pattern->group_info_capacity; + new_capacity = pattern->group_info_capacity; + while (group > new_capacity) + new_capacity += RE_LIST_SIZE_INC; + + if (new_capacity > old_capacity) { + new_group_info = (RE_GroupInfo*)re_realloc(pattern->group_info, + new_capacity * sizeof(RE_GroupInfo)); + if (!new_group_info) + return FALSE; + memset(new_group_info + old_capacity, 0, (new_capacity - old_capacity) + * sizeof(RE_GroupInfo)); + + pattern->group_info = new_group_info; + pattern->group_info_capacity = new_capacity; + } + + pattern->true_group_count = group; + + return TRUE; +} + +/* Records that there's a reference to a group. */ +Py_LOCAL_INLINE(BOOL) record_ref_group(PatternObject* pattern, size_t group) { + if (!ensure_group(pattern, group)) + return FALSE; + + pattern->group_info[group - 1].referenced = TRUE; + + return TRUE; +} + +/* Records that there's a new group. */ +Py_LOCAL_INLINE(BOOL) record_group(PatternObject* pattern, size_t group, + RE_Node* node) { + if (!ensure_group(pattern, group)) + return FALSE; + + if (group >= 1) { + RE_GroupInfo* info; + + info = &pattern->group_info[group - 1]; + info->end_index = (Py_ssize_t)pattern->true_group_count; + info->node = node; + } + + return TRUE; +} + +/* Records that a group has closed. */ +Py_LOCAL_INLINE(void) record_group_end(PatternObject* pattern, size_t group) { + if (group >= 1) + pattern->group_info[group - 1].end_index = ++pattern->group_end_index; +} + +/* Ensures that the entry for a call_ref's details actually exists. */ +Py_LOCAL_INLINE(BOOL) ensure_call_ref(PatternObject* pattern, size_t call_ref) + { + size_t old_capacity; + size_t new_capacity; + RE_CallRefInfo* new_call_ref_info; + + if (call_ref < pattern->call_ref_info_count) + /* We already have an entry for the call_ref. */ + return TRUE; + + /* Increase the storage capacity to include the new entry if it's + * insufficient. + */ + old_capacity = pattern->call_ref_info_capacity; + new_capacity = pattern->call_ref_info_capacity; + while (call_ref >= new_capacity) + new_capacity += RE_LIST_SIZE_INC; + + if (new_capacity > old_capacity) { + new_call_ref_info = (RE_CallRefInfo*)re_realloc(pattern->call_ref_info, + new_capacity * sizeof(RE_CallRefInfo)); + if (!new_call_ref_info) + return FALSE; + memset(new_call_ref_info + old_capacity, 0, (new_capacity - + old_capacity) * sizeof(RE_CallRefInfo)); + + pattern->call_ref_info = new_call_ref_info; + pattern->call_ref_info_capacity = new_capacity; + } + + pattern->call_ref_info_count = 1 + call_ref; + + return TRUE; +} + +/* Records that a call_ref is defined. */ +Py_LOCAL_INLINE(BOOL) record_call_ref_defined(PatternObject* pattern, size_t + call_ref, RE_Node* node) { + if (!ensure_call_ref(pattern, call_ref)) + return FALSE; + + pattern->call_ref_info[call_ref].defined = TRUE; + pattern->call_ref_info[call_ref].node = node; + + return TRUE; +} + +/* Records that a call_ref is used. */ +Py_LOCAL_INLINE(BOOL) record_call_ref_used(PatternObject* pattern, size_t + call_ref) { + if (!ensure_call_ref(pattern, call_ref)) + return FALSE; + + pattern->call_ref_info[call_ref].used = TRUE; + + return TRUE; +} + +/* Checks whether a node matches one and only one character. */ +Py_LOCAL_INLINE(BOOL) sequence_matches_one(RE_Node* node) { + while (node->op == RE_OP_BRANCH && !node->nonstring.next_2.node) + node = node->next_1.node; + + if (node->next_1.node || (node->status & RE_STATUS_FUZZY)) + return FALSE; + + return node_matches_one_character(node); +} + +/* Records a repeat. */ +Py_LOCAL_INLINE(BOOL) record_repeat(PatternObject* pattern, size_t index, + size_t repeat_depth) { + size_t old_capacity; + size_t new_capacity; + + /* Increase the storage capacity to include the new entry if it's + * insufficient. + */ + old_capacity = pattern->repeat_info_capacity; + new_capacity = pattern->repeat_info_capacity; + while (index >= new_capacity) + new_capacity += RE_LIST_SIZE_INC; + + if (new_capacity > old_capacity) { + RE_RepeatInfo* new_repeat_info; + + new_repeat_info = (RE_RepeatInfo*)re_realloc(pattern->repeat_info, + new_capacity * sizeof(RE_RepeatInfo)); + if (!new_repeat_info) + return FALSE; + memset(new_repeat_info + old_capacity, 0, (new_capacity - old_capacity) + * sizeof(RE_RepeatInfo)); + + pattern->repeat_info = new_repeat_info; + pattern->repeat_info_capacity = new_capacity; + } + + if (index >= pattern->repeat_count) + pattern->repeat_count = index + 1; + + if (repeat_depth > 0) + pattern->repeat_info[index].status |= RE_STATUS_INNER; + + return TRUE; +} + +Py_LOCAL_INLINE(Py_ssize_t) get_step(RE_CODE op) { + switch (op) { + case RE_OP_ANY: + case RE_OP_ANY_ALL: + case RE_OP_ANY_U: + case RE_OP_CHARACTER: + case RE_OP_CHARACTER_IGN: + case RE_OP_PROPERTY: + case RE_OP_PROPERTY_IGN: + case RE_OP_RANGE: + case RE_OP_RANGE_IGN: + case RE_OP_SET_DIFF: + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_INTER: + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_UNION: + case RE_OP_SET_UNION_IGN: + case RE_OP_STRING: + case RE_OP_STRING_FLD: + case RE_OP_STRING_IGN: + return 1; + case RE_OP_ANY_ALL_REV: + case RE_OP_ANY_REV: + case RE_OP_ANY_U_REV: + case RE_OP_CHARACTER_IGN_REV: + case RE_OP_CHARACTER_REV: + case RE_OP_PROPERTY_IGN_REV: + case RE_OP_PROPERTY_REV: + case RE_OP_RANGE_IGN_REV: + case RE_OP_RANGE_REV: + case RE_OP_SET_DIFF_IGN_REV: + case RE_OP_SET_DIFF_REV: + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION_IGN_REV: + case RE_OP_SET_UNION_REV: + case RE_OP_STRING_FLD_REV: + case RE_OP_STRING_IGN_REV: + case RE_OP_STRING_REV: + return -1; + } + + return 0; +} + +Py_LOCAL_INLINE(int) build_sequence(RE_CompileArgs* args); + +/* Builds an ANY node. */ +Py_LOCAL_INLINE(int) build_ANY(RE_CompileArgs* args) { + RE_UINT8 op; + RE_CODE flags; + Py_ssize_t step; + RE_Node* node; + + /* codes: opcode, flags. */ + if (args->code + 1 > args->end_code) + return RE_ERROR_ILLEGAL; + + op = (RE_UINT8)args->code[0]; + flags = args->code[1]; + + step = get_step(op); + + /* Create the node. */ + node = create_node(args->pattern, op, flags, step, 0); + if (!node) + return RE_ERROR_MEMORY; + + args->code += 2; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + ++args->min_width; + + return RE_ERROR_SUCCESS; +} + +/* Builds a FUZZY node. */ +Py_LOCAL_INLINE(int) build_FUZZY(RE_CompileArgs* args) { + RE_CODE flags; + RE_Node* start_node; + RE_Node* end_node; + RE_CODE index; + RE_CompileArgs subargs; + int status; + + /* codes: opcode, flags, constraints, sequence, end. */ + if (args->code + 13 > args->end_code) + return RE_ERROR_ILLEGAL; + + flags = args->code[1]; + + /* Create nodes for the start and end of the fuzzy sequence. */ + start_node = create_node(args->pattern, RE_OP_FUZZY, flags, 0, 9); + end_node = create_node(args->pattern, RE_OP_END_FUZZY, flags, 0, 5); + if (!start_node || !end_node) + return RE_ERROR_MEMORY; + + index = (RE_CODE)args->pattern->fuzzy_count++; + start_node->values[0] = index; + end_node->values[0] = index; + + /* The constraints consist of 4 pairs of limits and the cost equation. */ + end_node->values[RE_FUZZY_VAL_MIN_DEL] = args->code[2]; /* Deletion minimum. */ + end_node->values[RE_FUZZY_VAL_MIN_INS] = args->code[4]; /* Insertion minimum. */ + end_node->values[RE_FUZZY_VAL_MIN_SUB] = args->code[6]; /* Substitution minimum. */ + end_node->values[RE_FUZZY_VAL_MIN_ERR] = args->code[8]; /* Error minimum. */ + + start_node->values[RE_FUZZY_VAL_MAX_DEL] = args->code[3]; /* Deletion maximum. */ + start_node->values[RE_FUZZY_VAL_MAX_INS] = args->code[5]; /* Insertion maximum. */ + start_node->values[RE_FUZZY_VAL_MAX_SUB] = args->code[7]; /* Substitution maximum. */ + start_node->values[RE_FUZZY_VAL_MAX_ERR] = args->code[9]; /* Error maximum. */ + + start_node->values[RE_FUZZY_VAL_DEL_COST] = args->code[10]; /* Deletion cost. */ + start_node->values[RE_FUZZY_VAL_INS_COST] = args->code[11]; /* Insertion cost. */ + start_node->values[RE_FUZZY_VAL_SUB_COST] = args->code[12]; /* Substitution cost. */ + start_node->values[RE_FUZZY_VAL_MAX_COST] = args->code[13]; /* Total cost. */ + + args->code += 14; + + subargs = *args; + subargs.within_fuzzy = TRUE; + + /* Compile the sequence and check that we've reached the end of the + * subpattern. + */ + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + if (subargs.code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + args->min_width += subargs.min_width; + args->has_captures |= subargs.has_captures; + args->is_fuzzy = TRUE; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + ++args->code; + + /* Append the fuzzy sequence. */ + add_node(args->end, start_node); + add_node(start_node, subargs.start); + add_node(subargs.end, end_node); + args->end = end_node; + + return RE_ERROR_SUCCESS; +} + +/* Builds an ATOMIC node. */ +Py_LOCAL_INLINE(int) build_ATOMIC(RE_CompileArgs* args) { + RE_Node* atomic_node; + RE_CompileArgs subargs; + int status; + RE_Node* end_node; + + /* codes: opcode, sequence, end. */ + if (args->code + 1 > args->end_code) + return RE_ERROR_ILLEGAL; + + atomic_node = create_node(args->pattern, RE_OP_ATOMIC, 0, 0, 0); + if (!atomic_node) + return RE_ERROR_MEMORY; + + ++args->code; + + /* Compile the sequence and check that we've reached the end of it. */ + subargs = *args; + + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + if (subargs.code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + ++args->code; + + /* Check the subpattern. */ + args->min_width += subargs.min_width; + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + if (subargs.has_groups) + atomic_node->status |= RE_STATUS_HAS_GROUPS; + + if (subargs.has_repeats) + atomic_node->status |= RE_STATUS_HAS_REPEATS; + + /* Create the node to terminate the subpattern. */ + end_node = create_node(subargs.pattern, RE_OP_END_ATOMIC, 0, 0, 0); + if (!end_node) + return RE_ERROR_MEMORY; + + /* Append the new sequence. */ + add_node(args->end, atomic_node); + add_node(atomic_node, subargs.start); + add_node(subargs.end, end_node); + args->end = end_node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a BOUNDARY node. */ +Py_LOCAL_INLINE(int) build_BOUNDARY(RE_CompileArgs* args) { + RE_UINT8 op; + RE_CODE flags; + RE_Node* node; + + /* codes: opcode, flags. */ + if (args->code + 1 > args->end_code) + return RE_ERROR_ILLEGAL; + + op = (RE_UINT8)args->code[0]; + flags = args->code[1]; + + args->code += 2; + + /* Create the node. */ + node = create_node(args->pattern, op, flags, 0, 0); + if (!node) + return RE_ERROR_MEMORY; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a BRANCH node. */ +Py_LOCAL_INLINE(int) build_BRANCH(RE_CompileArgs* args) { + RE_Node* branch_node; + RE_Node* join_node; + Py_ssize_t min_width; + RE_CompileArgs subargs; + int status; + + /* codes: opcode, branch, next, branch, end. */ + if (args->code + 2 > args->end_code) + return RE_ERROR_ILLEGAL; + + /* Create nodes for the start and end of the branch sequence. */ + branch_node = create_node(args->pattern, RE_OP_BRANCH, 0, 0, 0); + join_node = create_node(args->pattern, RE_OP_BRANCH, 0, 0, 0); + if (!branch_node || !join_node) + return RE_ERROR_MEMORY; + + /* Append the node. */ + add_node(args->end, branch_node); + args->end = join_node; + + min_width = PY_SSIZE_T_MAX; + + subargs = *args; + + /* A branch in the regular expression is compiled into a series of 2-way + * branches. + */ + do { + RE_Node* next_branch_node; + + /* Skip over the 'BRANCH' or 'NEXT' opcode. */ + ++subargs.code; + + /* Compile the sequence until the next 'BRANCH' or 'NEXT' opcode. */ + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + min_width = min_ssize_t(min_width, subargs.min_width); + + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + /* Append the sequence. */ + add_node(branch_node, subargs.start); + add_node(subargs.end, join_node); + + /* Create a start node for the next sequence and append it. */ + next_branch_node = create_node(subargs.pattern, RE_OP_BRANCH, 0, 0, 0); + if (!next_branch_node) + return RE_ERROR_MEMORY; + + add_node(branch_node, next_branch_node); + branch_node = next_branch_node; + } while (subargs.code < subargs.end_code && subargs.code[0] == RE_OP_NEXT); + + /* We should have reached the end of the branch. */ + if (subargs.code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + + ++args->code; + args->min_width += min_width; + + return RE_ERROR_SUCCESS; +} + +/* Builds a CALL_REF node. */ +Py_LOCAL_INLINE(int) build_CALL_REF(RE_CompileArgs* args) { + RE_CODE call_ref; + RE_Node* start_node; + RE_Node* end_node; + RE_CompileArgs subargs; + int status; + + /* codes: opcode, call_ref. */ + if (args->code + 1 > args->end_code) + return RE_ERROR_ILLEGAL; + + call_ref = args->code[1]; + + args->code += 2; + + /* Create nodes for the start and end of the subpattern. */ + start_node = create_node(args->pattern, RE_OP_CALL_REF, 0, 0, 1); + end_node = create_node(args->pattern, RE_OP_GROUP_RETURN, 0, 0, 0); + if (!start_node || !end_node) + return RE_ERROR_MEMORY; + + start_node->values[0] = call_ref; + + /* Compile the sequence and check that we've reached the end of the + * subpattern. + */ + subargs = *args; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + if (subargs.code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + args->min_width += subargs.min_width; + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + ++args->code; + + /* Record that we defined a call_ref. */ + if (!record_call_ref_defined(args->pattern, call_ref, start_node)) + return RE_ERROR_MEMORY; + + /* Append the node. */ + add_node(args->end, start_node); + add_node(start_node, subargs.start); + add_node(subargs.end, end_node); + args->end = end_node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a CHARACTER or PROPERTY node. */ +Py_LOCAL_INLINE(int) build_CHARACTER_or_PROPERTY(RE_CompileArgs* args) { + RE_UINT8 op; + RE_CODE flags; + Py_ssize_t step; + RE_Node* node; + + /* codes: opcode, flags, value. */ + if (args->code + 2 > args->end_code) + return RE_ERROR_ILLEGAL; + + op = (RE_UINT8)args->code[0]; + flags = args->code[1]; + + step = get_step(op); + + if (flags & RE_ZEROWIDTH_OP) + step = 0; + + /* Create the node. */ + node = create_node(args->pattern, op, flags, step, 1); + if (!node) + return RE_ERROR_MEMORY; + + node->values[0] = args->code[2]; + + args->code += 3; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + if (step != 0) + ++args->min_width; + + return RE_ERROR_SUCCESS; +} + +/* Builds a CONDITIONAL node. */ +Py_LOCAL_INLINE(int) build_CONDITIONAL(RE_CompileArgs* args) { + RE_CODE flags; + BOOL forward; + RE_Node* test_node; + RE_CompileArgs subargs; + int status; + RE_Node* end_test_node; + RE_Node* end_node; + Py_ssize_t min_width; + + /* codes: opcode, flags, forward, sequence, next, sequence, next, sequence, + * end. + */ + if (args->code + 4 > args->end_code) + return RE_ERROR_ILLEGAL; + + flags = args->code[1]; + forward = (BOOL)args->code[2]; + + /* Create a node for the lookaround. */ + test_node = create_node(args->pattern, RE_OP_CONDITIONAL, flags, 0, 0); + if (!test_node) + return RE_ERROR_MEMORY; + + args->code += 3; + + add_node(args->end, test_node); + + /* Compile the lookaround test and check that we've reached the end of the + * subpattern. + */ + subargs = *args; + subargs.forward = forward; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + if (subargs.code[0] != RE_OP_NEXT) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + ++args->code; + + /* Check the lookaround subpattern. */ + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + if (subargs.has_groups) + test_node->status |= RE_STATUS_HAS_GROUPS; + + if (subargs.has_repeats) + test_node->status |= RE_STATUS_HAS_REPEATS; + + /* Create the node to terminate the test. */ + end_test_node = create_node(args->pattern, RE_OP_END_CONDITIONAL, 0, 0, 0); + if (!end_test_node) + return RE_ERROR_MEMORY; + + /* test node -> test -> end test node */ + add_node(test_node, subargs.start); + add_node(subargs.end, end_test_node); + + /* Compile the true branch. */ + subargs = *args; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + /* Check the true branch. */ + args->code = subargs.code; + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + min_width = subargs.min_width; + + /* Create the terminating node. */ + end_node = create_node(args->pattern, RE_OP_BRANCH, 0, 0, 0); + if (!end_node) + return RE_ERROR_MEMORY; + + /* end test node -> true branch -> end node */ + add_node(end_test_node, subargs.start); + add_node(subargs.end, end_node); + + if (args->code[0] == RE_OP_NEXT) { + /* There's a false branch. */ + ++args->code; + + /* Compile the false branch. */ + subargs.code = args->code; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + /* Check the false branch. */ + args->code = subargs.code; + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + min_width = min_ssize_t(min_width, subargs.min_width); + + /* test node -> false branch -> end node */ + add_node(test_node, subargs.start); + add_node(subargs.end, end_node); + } else + /* end test node -> end node */ + add_node(end_test_node, end_node); + + if (args->code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->min_width += min_width; + + ++args->code; + + args->end = end_node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a GROUP node. */ +Py_LOCAL_INLINE(int) build_GROUP(RE_CompileArgs* args) { + RE_CODE private_group; + RE_CODE public_group; + RE_Node* start_node; + RE_Node* end_node; + RE_CompileArgs subargs; + int status; + + /* codes: opcode, private_group, public_group. */ + if (args->code + 2 > args->end_code) + return RE_ERROR_ILLEGAL; + + private_group = args->code[1]; + public_group = args->code[2]; + + args->code += 3; + + /* Create nodes for the start and end of the capture group. */ + start_node = create_node(args->pattern, args->forward ? RE_OP_START_GROUP : + RE_OP_END_GROUP, 0, 0, 3); + end_node = create_node(args->pattern, args->forward ? RE_OP_END_GROUP : + RE_OP_START_GROUP, 0, 0, 3); + if (!start_node || !end_node) + return RE_ERROR_MEMORY; + + start_node->values[0] = private_group; + end_node->values[0] = private_group; + start_node->values[1] = public_group; + end_node->values[1] = public_group; + + /* Signal that the capture should be saved when it's complete. */ + start_node->values[2] = 0; + end_node->values[2] = 1; + + /* Record that we have a new capture group. */ + if (!record_group(args->pattern, private_group, start_node)) + return RE_ERROR_MEMORY; + + /* Compile the sequence and check that we've reached the end of the capture + * group. + */ + subargs = *args; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + if (subargs.code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + args->min_width += subargs.min_width; + args->has_captures |= subargs.has_captures | subargs.visible_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= TRUE; + args->has_repeats |= subargs.has_repeats; + + ++args->code; + + /* Record that the capture group has closed. */ + record_group_end(args->pattern, private_group); + + /* Append the capture group. */ + add_node(args->end, start_node); + add_node(start_node, subargs.start); + add_node(subargs.end, end_node); + args->end = end_node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a GROUP_CALL node. */ +Py_LOCAL_INLINE(int) build_GROUP_CALL(RE_CompileArgs* args) { + RE_CODE call_ref; + RE_Node* node; + + /* codes: opcode, call_ref. */ + if (args->code + 1 > args->end_code) + return RE_ERROR_ILLEGAL; + + call_ref = args->code[1]; + + /* Create the node. */ + node = create_node(args->pattern, RE_OP_GROUP_CALL, 0, 0, 1); + if (!node) + return RE_ERROR_MEMORY; + + node->values[0] = call_ref; + + node->status |= RE_STATUS_HAS_GROUPS; + node->status |= RE_STATUS_HAS_REPEATS; + + args->code += 2; + + /* Record that we used a call_ref. */ + if (!record_call_ref_used(args->pattern, call_ref)) + return RE_ERROR_MEMORY; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a GROUP_EXISTS node. */ +Py_LOCAL_INLINE(int) build_GROUP_EXISTS(RE_CompileArgs* args) { + RE_CODE group; + RE_Node* start_node; + RE_Node* end_node; + RE_CompileArgs subargs; + int status; + Py_ssize_t min_width; + + /* codes: opcode, sequence, next, sequence, end. */ + if (args->code + 2 > args->end_code) + return RE_ERROR_ILLEGAL; + + group = args->code[1]; + + args->code += 2; + + /* Record that we have a reference to a group. If group is 0, then we have + * a DEFINE and not a true group. + */ + if (group > 0 && !record_ref_group(args->pattern, group)) + return RE_ERROR_MEMORY; + + /* Create nodes for the start and end of the structure. */ + start_node = create_node(args->pattern, RE_OP_GROUP_EXISTS, 0, 0, 1); + end_node = create_node(args->pattern, RE_OP_BRANCH, 0, 0, 0); + if (!start_node || !end_node) + return RE_ERROR_MEMORY; + + start_node->values[0] = group; + + subargs = *args; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + args->code = subargs.code; + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + min_width = subargs.min_width; + + /* Append the start node. */ + add_node(args->end, start_node); + add_node(start_node, subargs.start); + + if (args->code[0] == RE_OP_NEXT) { + RE_Node* true_branch_end; + + ++args->code; + + true_branch_end = subargs.end; + + subargs.code = args->code; + + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + args->code = subargs.code; + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + + if (group == 0) { + /* Join the 2 branches end-to-end and bypass it. The sequence + * itself will never be matched as a whole, so it doesn't matter. + */ + min_width = 0; + + add_node(start_node, end_node); + add_node(true_branch_end, subargs.start); + } else { + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + min_width = min_ssize_t(min_width, subargs.min_width); + + add_node(start_node, subargs.start); + add_node(true_branch_end, end_node); + } + + add_node(subargs.end, end_node); + } else { + add_node(start_node, end_node); + add_node(subargs.end, end_node); + + min_width = 0; + } + + args->min_width += min_width; + + if (args->code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + ++args->code; + + args->end = end_node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a LOOKAROUND node. */ +Py_LOCAL_INLINE(int) build_LOOKAROUND(RE_CompileArgs* args) { + RE_CODE flags; + BOOL forward; + RE_Node* lookaround_node; + RE_CompileArgs subargs; + int status; + RE_Node* end_node; + RE_Node* next_node; + + /* codes: opcode, flags, forward, sequence, end. */ + if (args->code + 3 > args->end_code) + return RE_ERROR_ILLEGAL; + + flags = args->code[1]; + forward = (BOOL)args->code[2]; + + /* Create a node for the lookaround. */ + lookaround_node = create_node(args->pattern, RE_OP_LOOKAROUND, flags, 0, + 0); + if (!lookaround_node) + return RE_ERROR_MEMORY; + + args->code += 3; + + /* Compile the sequence and check that we've reached the end of the + * subpattern. + */ + subargs = *args; + subargs.forward = forward; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + if (subargs.code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + ++args->code; + + /* Check the subpattern. */ + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + if (subargs.has_groups) + lookaround_node->status |= RE_STATUS_HAS_GROUPS; + + if (subargs.has_repeats) + lookaround_node->status |= RE_STATUS_HAS_REPEATS; + + /* Create the node to terminate the subpattern. */ + end_node = create_node(args->pattern, RE_OP_END_LOOKAROUND, 0, 0, 0); + if (!end_node) + return RE_ERROR_MEMORY; + + /* Make a continuation node. */ + next_node = create_node(args->pattern, RE_OP_BRANCH, 0, 0, 0); + if (!next_node) + return RE_ERROR_MEMORY; + + /* Append the new sequence. */ + add_node(args->end, lookaround_node); + add_node(lookaround_node, subargs.start); + add_node(lookaround_node, next_node); + add_node(subargs.end, end_node); + add_node(end_node, next_node); + + args->end = next_node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a RANGE node. */ +Py_LOCAL_INLINE(int) build_RANGE(RE_CompileArgs* args) { + RE_UINT8 op; + RE_CODE flags; + Py_ssize_t step; + RE_Node* node; + + /* codes: opcode, flags, lower, upper. */ + if (args->code + 3 > args->end_code) + return RE_ERROR_ILLEGAL; + + op = (RE_UINT8)args->code[0]; + flags = args->code[1]; + + step = get_step(op); + + if (flags & RE_ZEROWIDTH_OP) + step = 0; + + /* Create the node. */ + node = create_node(args->pattern, op, flags, step, 2); + if (!node) + return RE_ERROR_MEMORY; + + node->values[0] = args->code[2]; + node->values[1] = args->code[3]; + + args->code += 4; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + if (step != 0) + ++args->min_width; + + return RE_ERROR_SUCCESS; +} + +/* Builds a REF_GROUP node. */ +Py_LOCAL_INLINE(int) build_REF_GROUP(RE_CompileArgs* args) { + RE_CODE flags; + RE_CODE group; + RE_Node* node; + + /* codes: opcode, flags, group. */ + if (args->code + 2 > args->end_code) + return RE_ERROR_ILLEGAL; + + flags = args->code[1]; + group = args->code[2]; + node = create_node(args->pattern, (RE_UINT8)args->code[0], flags, 0, 1); + if (!node) + return RE_ERROR_MEMORY; + + node->values[0] = group; + + args->code += 3; + + /* Record that we have a reference to a group. */ + if (!record_ref_group(args->pattern, group)) + return RE_ERROR_MEMORY; + + /* Append the reference. */ + add_node(args->end, node); + args->end = node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a REPEAT node. */ +Py_LOCAL_INLINE(int) build_REPEAT(RE_CompileArgs* args) { + BOOL greedy; + RE_CODE min_count; + RE_CODE max_count; + int status; + + /* codes: opcode, min_count, max_count, sequence, end. */ + if (args->code + 3 > args->end_code) + return RE_ERROR_ILLEGAL; + + greedy = args->code[0] == RE_OP_GREEDY_REPEAT; + min_count = args->code[1]; + max_count = args->code[2]; + if (args->code[1] > args->code[2]) + return RE_ERROR_ILLEGAL; + + args->code += 3; + + if (min_count == 1 && max_count == 1) { + /* Singly-repeated sequence. */ + RE_CompileArgs subargs; + + subargs = *args; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + if (subargs.code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + args->min_width += subargs.min_width; + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats |= subargs.has_repeats; + + ++args->code; + + /* Append the sequence. */ + add_node(args->end, subargs.start); + args->end = subargs.end; + } else { + size_t index; + RE_Node* repeat_node; + RE_CompileArgs subargs; + + index = args->pattern->repeat_count; + + /* Create the nodes for the repeat. */ + repeat_node = create_node(args->pattern, greedy ? RE_OP_GREEDY_REPEAT : + RE_OP_LAZY_REPEAT, 0, args->forward ? 1 : -1, 4); + if (!repeat_node || !record_repeat(args->pattern, index, + args->repeat_depth)) + return RE_ERROR_MEMORY; + + repeat_node->values[0] = (RE_CODE)index; + repeat_node->values[1] = min_count; + repeat_node->values[2] = max_count; + repeat_node->values[3] = args->forward; + + if (args->within_fuzzy) + args->pattern->repeat_info[index].status |= RE_STATUS_BODY; + + /* Compile the 'body' and check that we've reached the end of it. */ + subargs = *args; + subargs.visible_captures = TRUE; + ++subargs.repeat_depth; + status = build_sequence(&subargs); + if (status != RE_ERROR_SUCCESS) + return status; + + if (subargs.code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + args->code = subargs.code; + args->min_width += (Py_ssize_t)min_count * subargs.min_width; + args->has_captures |= subargs.has_captures; + args->is_fuzzy |= subargs.is_fuzzy; + args->has_groups |= subargs.has_groups; + args->has_repeats = TRUE; + + ++args->code; + + /* Is it a repeat of something which will match a single character? + * + * If it's in a fuzzy section then it won't be optimised as a + * single-character repeat. + */ + if (sequence_matches_one(subargs.start)) { + repeat_node->op = greedy ? RE_OP_GREEDY_REPEAT_ONE : + RE_OP_LAZY_REPEAT_ONE; + + /* Append the new sequence. */ + add_node(args->end, repeat_node); + repeat_node->nonstring.next_2.node = subargs.start; + args->end = repeat_node; + } else { + RE_Node* end_repeat_node; + RE_Node* end_node; + + end_repeat_node = create_node(args->pattern, greedy ? + RE_OP_END_GREEDY_REPEAT : RE_OP_END_LAZY_REPEAT, 0, args->forward + ? 1 : -1, 4); + if (!end_repeat_node) + return RE_ERROR_MEMORY; + + end_repeat_node->values[0] = repeat_node->values[0]; + end_repeat_node->values[1] = repeat_node->values[1]; + end_repeat_node->values[2] = repeat_node->values[2]; + end_repeat_node->values[3] = args->forward; + + end_node = create_node(args->pattern, RE_OP_BRANCH, 0, 0, 0); + if (!end_node) + return RE_ERROR_MEMORY; + + /* Append the new sequence. */ + add_node(args->end, repeat_node); + add_node(repeat_node, subargs.start); + add_node(repeat_node, end_node); + add_node(subargs.end, end_repeat_node); + add_node(end_repeat_node, subargs.start); + add_node(end_repeat_node, end_node); + args->end = end_node; + } + } + + return RE_ERROR_SUCCESS; +} + +/* Builds a STRING node. */ +Py_LOCAL_INLINE(int) build_STRING(RE_CompileArgs* args, BOOL is_charset) { + RE_CODE flags; + RE_CODE length; + RE_UINT8 op; + Py_ssize_t step; + RE_Node* node; + size_t i; + + /* codes: opcode, flags, length, characters. */ + flags = args->code[1]; + length = args->code[2]; + if (args->code + 3 + length > args->end_code) + return RE_ERROR_ILLEGAL; + + op = (RE_UINT8)args->code[0]; + + step = get_step(op); + + /* Create the node. */ + node = create_node(args->pattern, op, flags, step * (Py_ssize_t)length, + length); + if (!node) + return RE_ERROR_MEMORY; + if (!is_charset) + node->status |= RE_STATUS_STRING; + + for (i = 0; i < length; i++) + node->values[i] = args->code[3 + i]; + + args->code += 3 + length; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + /* Because of full case-folding, one character in the text could match + * multiple characters in the pattern. + */ + if (op == RE_OP_STRING_FLD || op == RE_OP_STRING_FLD_REV) + args->min_width += possible_unfolded_length((Py_ssize_t)length); + else + args->min_width += (Py_ssize_t)length; + + return RE_ERROR_SUCCESS; +} + +/* Builds a SET node. */ +Py_LOCAL_INLINE(int) build_SET(RE_CompileArgs* args) { + RE_UINT8 op; + RE_CODE flags; + Py_ssize_t step; + RE_Node* node; + Py_ssize_t min_width; + int status; + + /* codes: opcode, flags, members. */ + op = (RE_UINT8)args->code[0]; + flags = args->code[1]; + + step = get_step(op); + + if (flags & RE_ZEROWIDTH_OP) + step = 0; + + node = create_node(args->pattern, op, flags, step, 0); + if (!node) + return RE_ERROR_MEMORY; + + args->code += 2; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + min_width = args->min_width; + + /* Compile the character set. */ + do { + switch (args->code[0]) { + case RE_OP_CHARACTER: + case RE_OP_PROPERTY: + status = build_CHARACTER_or_PROPERTY(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_RANGE: + status = build_RANGE(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_SET_DIFF: + case RE_OP_SET_INTER: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_UNION: + status = build_SET(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_STRING: + /* A set of characters. */ + if (!build_STRING(args, TRUE)) + return FALSE; + break; + default: + /* Illegal opcode for a character set. */ + return RE_ERROR_ILLEGAL; + } + } while (args->code < args->end_code && args->code[0] != RE_OP_END); + + /* Check that we've reached the end correctly. (The last opcode should be + * 'END'.) + */ + if (args->code >= args->end_code || args->code[0] != RE_OP_END) + return RE_ERROR_ILLEGAL; + + ++args->code; + + /* At this point the set's members are in the main sequence. They need to + * be moved out-of-line. + */ + node->nonstring.next_2.node = node->next_1.node; + node->next_1.node = NULL; + args->end = node; + + args->min_width = min_width; + + if (step != 0) + ++args->min_width; + + return RE_ERROR_SUCCESS; +} + +/* Builds a STRING_SET node. */ +Py_LOCAL_INLINE(int) build_STRING_SET(RE_CompileArgs* args) { + RE_CODE index; + RE_CODE min_len; + RE_CODE max_len; + RE_Node* node; + + /* codes: opcode, index, min_len, max_len. */ + if (args->code + 3 > args->end_code) + return RE_ERROR_ILLEGAL; + + index = args->code[1]; + min_len = args->code[2]; + max_len = args->code[3]; + node = create_node(args->pattern, (RE_UINT8)args->code[0], 0, 0, 3); + if (!node) + return RE_ERROR_MEMORY; + + node->values[0] = index; + node->values[1] = min_len; + node->values[2] = max_len; + + args->code += 4; + + /* Append the reference. */ + add_node(args->end, node); + args->end = node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a SUCCESS node . */ +Py_LOCAL_INLINE(int) build_SUCCESS(RE_CompileArgs* args) { + RE_Node* node; + /* code: opcode. */ + + /* Create the node. */ + node = create_node(args->pattern, (RE_UINT8)args->code[0], 0, 0, 0); + if (!node) + return RE_ERROR_MEMORY; + + ++args->code; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a zero-width node. */ +Py_LOCAL_INLINE(int) build_zerowidth(RE_CompileArgs* args) { + RE_CODE flags; + RE_Node* node; + + /* codes: opcode, flags. */ + if (args->code + 1 > args->end_code) + return RE_ERROR_ILLEGAL; + + flags = args->code[1]; + + /* Create the node. */ + node = create_node(args->pattern, (RE_UINT8)args->code[0], flags, 0, 0); + if (!node) + return RE_ERROR_MEMORY; + + args->code += 2; + + /* Append the node. */ + add_node(args->end, node); + args->end = node; + + return RE_ERROR_SUCCESS; +} + +/* Builds a sequence of nodes from regular expression code. */ +Py_LOCAL_INLINE(int) build_sequence(RE_CompileArgs* args) { + int status; + + /* Guarantee that there's something to attach to. */ + args->start = create_node(args->pattern, RE_OP_BRANCH, 0, 0, 0); + args->end = args->start; + + args->min_width = 0; + args->has_captures = FALSE; + args->is_fuzzy = FALSE; + args->has_groups = FALSE; + args->has_repeats = FALSE; + + /* The sequence should end with an opcode we don't understand. If it + * doesn't then the code is illegal. + */ + while (args->code < args->end_code) { + /* The following code groups opcodes by format, not function. */ + switch (args->code[0]) { + case RE_OP_ANY: + case RE_OP_ANY_ALL: + case RE_OP_ANY_ALL_REV: + case RE_OP_ANY_REV: + case RE_OP_ANY_U: + case RE_OP_ANY_U_REV: + /* A simple opcode with no trailing codewords and width of 1. */ + status = build_ANY(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_ATOMIC: + /* An atomic sequence. */ + status = build_ATOMIC(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_BOUNDARY: + case RE_OP_DEFAULT_BOUNDARY: + case RE_OP_DEFAULT_END_OF_WORD: + case RE_OP_DEFAULT_START_OF_WORD: + case RE_OP_END_OF_WORD: + case RE_OP_GRAPHEME_BOUNDARY: + case RE_OP_KEEP: + case RE_OP_SKIP: + case RE_OP_START_OF_WORD: + /* A word or grapheme boundary. */ + status = build_BOUNDARY(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_BRANCH: + /* A 2-way branch. */ + status = build_BRANCH(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_CALL_REF: + /* A group call ref. */ + status = build_CALL_REF(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_CHARACTER: + case RE_OP_CHARACTER_IGN: + case RE_OP_CHARACTER_IGN_REV: + case RE_OP_CHARACTER_REV: + case RE_OP_PROPERTY: + case RE_OP_PROPERTY_IGN: + case RE_OP_PROPERTY_IGN_REV: + case RE_OP_PROPERTY_REV: + /* A character literal or a property. */ + status = build_CHARACTER_or_PROPERTY(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_CONDITIONAL: + /* A lookaround conditional. */ + status = build_CONDITIONAL(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_END_OF_LINE: + case RE_OP_END_OF_LINE_U: + case RE_OP_END_OF_STRING: + case RE_OP_END_OF_STRING_LINE: + case RE_OP_END_OF_STRING_LINE_U: + case RE_OP_SEARCH_ANCHOR: + case RE_OP_START_OF_LINE: + case RE_OP_START_OF_LINE_U: + case RE_OP_START_OF_STRING: + /* A simple opcode with no trailing codewords and width of 0. */ + status = build_zerowidth(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_FAILURE: + case RE_OP_PRUNE: + case RE_OP_SUCCESS: + status = build_SUCCESS(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_FUZZY: + /* A fuzzy sequence. */ + status = build_FUZZY(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_GREEDY_REPEAT: + case RE_OP_LAZY_REPEAT: + /* A repeated sequence. */ + status = build_REPEAT(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_GROUP: + /* A capture group. */ + status = build_GROUP(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_GROUP_CALL: + /* A group call. */ + status = build_GROUP_CALL(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_GROUP_EXISTS: + /* A conditional sequence. */ + status = build_GROUP_EXISTS(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_LOOKAROUND: + /* A lookaround. */ + status = build_LOOKAROUND(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_RANGE: + case RE_OP_RANGE_IGN: + case RE_OP_RANGE_IGN_REV: + case RE_OP_RANGE_REV: + /* A range. */ + status = build_RANGE(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_REF_GROUP: + case RE_OP_REF_GROUP_FLD: + case RE_OP_REF_GROUP_FLD_REV: + case RE_OP_REF_GROUP_IGN: + case RE_OP_REF_GROUP_IGN_REV: + case RE_OP_REF_GROUP_REV: + /* A reference to a group. */ + status = build_REF_GROUP(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_SET_DIFF: + case RE_OP_SET_DIFF_IGN: + case RE_OP_SET_DIFF_IGN_REV: + case RE_OP_SET_DIFF_REV: + case RE_OP_SET_INTER: + case RE_OP_SET_INTER_IGN: + case RE_OP_SET_INTER_IGN_REV: + case RE_OP_SET_INTER_REV: + case RE_OP_SET_SYM_DIFF: + case RE_OP_SET_SYM_DIFF_IGN: + case RE_OP_SET_SYM_DIFF_IGN_REV: + case RE_OP_SET_SYM_DIFF_REV: + case RE_OP_SET_UNION: + case RE_OP_SET_UNION_IGN: + case RE_OP_SET_UNION_IGN_REV: + case RE_OP_SET_UNION_REV: + /* A set. */ + status = build_SET(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + case RE_OP_STRING: + case RE_OP_STRING_FLD: + case RE_OP_STRING_FLD_REV: + case RE_OP_STRING_IGN: + case RE_OP_STRING_IGN_REV: + case RE_OP_STRING_REV: + /* A string literal. */ + if (!build_STRING(args, FALSE)) + return FALSE; + break; + case RE_OP_STRING_SET: + case RE_OP_STRING_SET_FLD: + case RE_OP_STRING_SET_FLD_REV: + case RE_OP_STRING_SET_IGN: + case RE_OP_STRING_SET_IGN_REV: + case RE_OP_STRING_SET_REV: + /* A reference to a list. */ + status = build_STRING_SET(args); + if (status != RE_ERROR_SUCCESS) + return status; + break; + default: + /* We've found an opcode which we don't recognise. We'll leave it + * for the caller. + */ + return RE_ERROR_SUCCESS; + } + } + + /* If we're here then we should be at the end of the code, otherwise we + * have an error. + */ + return args->code == args->end_code; +} + +/* Compiles the regular expression code to 'nodes'. + * + * Various details about the regular expression are discovered during + * compilation and stored in the PatternObject. + */ +Py_LOCAL_INLINE(BOOL) compile_to_nodes(RE_CODE* code, RE_CODE* end_code, + PatternObject* pattern) { + RE_CompileArgs args; + int status; + + /* Compile a regex sequence and then check that we've reached the end + * correctly. (The last opcode should be 'SUCCESS'.) + * + * If successful, 'start' and 'end' will point to the start and end nodes + * of the compiled sequence. + */ + args.code = code; + args.end_code = end_code; + args.pattern = pattern; + args.forward = (pattern->flags & RE_FLAG_REVERSE) == 0; + args.visible_captures = FALSE; + args.has_captures = FALSE; + args.repeat_depth = 0; + args.is_fuzzy = FALSE; + args.within_fuzzy = FALSE; + status = build_sequence(&args); + if (status == RE_ERROR_ILLEGAL) + set_error(RE_ERROR_ILLEGAL, NULL); + + if (status != RE_ERROR_SUCCESS) + return FALSE; + + pattern->min_width = args.min_width; + pattern->is_fuzzy = args.is_fuzzy; + pattern->do_search_start = TRUE; + pattern->start_node = args.start; + + /* Optimise the pattern. */ + if (!optimise_pattern(pattern)) + return FALSE; + + pattern->start_test = locate_test_start(pattern->start_node); + + /* Get the call_ref for the entire pattern, if any. */ + if (pattern->start_node->op == RE_OP_CALL_REF) + pattern->pattern_call_ref = (Py_ssize_t)pattern->start_node->values[0]; + else + pattern->pattern_call_ref = -1; + + return TRUE; +} + +/* Gets the required characters for a regex. + * + * In the event of an error, it just pretends that there are no required + * characters. + */ +Py_LOCAL_INLINE(void) get_required_chars(PyObject* required_chars, RE_CODE** + req_chars, size_t* req_length) { + Py_ssize_t len; + RE_CODE* chars; + Py_ssize_t i; + + *req_chars = NULL; + *req_length = 0; + + len = PyTuple_GET_SIZE(required_chars); + if (len < 1 || PyErr_Occurred()) { + PyErr_Clear(); + return; + } + + chars = (RE_CODE*)re_alloc((size_t)len * sizeof(RE_CODE)); + if (!chars) + goto error; + + for (i = 0; i < len; i++) { + PyObject* o; + size_t value; + + /* PyTuple_SET_ITEM borrows the reference. */ + o = PyTuple_GET_ITEM(required_chars, i); + + value = PyLong_AsUnsignedLong(o); + if ((Py_ssize_t)value == -1 && PyErr_Occurred()) + goto error; + + chars[i] = (RE_CODE)value; + if (chars[i] != value) + goto error; + } + + *req_chars = chars; + *req_length = (size_t)len; + + return; + +error: + PyErr_Clear(); + re_dealloc(chars); +} + +/* Makes a STRING node. */ +Py_LOCAL_INLINE(RE_Node*) make_STRING_node(PatternObject* pattern, RE_UINT8 op, + size_t length, RE_CODE* chars) { + Py_ssize_t step; + RE_Node* node; + size_t i; + + step = get_step(op); + + /* Create the node. */ + node = create_node(pattern, op, 0, step * (Py_ssize_t)length, length); + if (!node) + return NULL; + + node->status |= RE_STATUS_STRING; + + for (i = 0; i < length; i++) + node->values[i] = chars[i]; + + return node; +} + +/* Scans all of the characters in the current locale for their properties. */ +Py_LOCAL_INLINE(void) scan_locale_chars(RE_LocaleInfo* locale_info) { + int c; + + for (c = 0; c < 0x100; c++) { + unsigned short props = 0; + + if (isalnum(c)) + props |= RE_LOCALE_ALNUM; + if (isalpha(c)) + props |= RE_LOCALE_ALPHA; + if (iscntrl(c)) + props |= RE_LOCALE_CNTRL; + if (isdigit(c)) + props |= RE_LOCALE_DIGIT; + if (isgraph(c)) + props |= RE_LOCALE_GRAPH; + if (islower(c)) + props |= RE_LOCALE_LOWER; + if (isprint(c)) + props |= RE_LOCALE_PRINT; + if (ispunct(c)) + props |= RE_LOCALE_PUNCT; + if (isspace(c)) + props |= RE_LOCALE_SPACE; + if (isupper(c)) + props |= RE_LOCALE_UPPER; + + locale_info->properties[c] = props; + locale_info->uppercase[c] = (unsigned char)toupper(c); + locale_info->lowercase[c] = (unsigned char)tolower(c); + } +} + +/* Compiles regular expression code to a PatternObject. + * + * The regular expression code is provided as a list and is then compiled to + * 'nodes'. Various details about the regular expression are discovered during + * compilation and stored in the PatternObject. + */ +static PyObject* re_compile(PyObject* self_, PyObject* args) { + PyObject* pattern; + Py_ssize_t flags = 0; + PyObject* code_list; + PyObject* groupindex; + PyObject* indexgroup; + PyObject* named_lists; + PyObject* named_list_indexes; + Py_ssize_t req_offset; + PyObject* required_chars; + Py_ssize_t req_flags; + size_t public_group_count; + Py_ssize_t code_len; + RE_CODE* code; + Py_ssize_t i; + RE_CODE* req_chars; + size_t req_length; + PatternObject* self; + BOOL unicode; + BOOL locale; + BOOL ascii; + BOOL ok; + + if (!PyArg_ParseTuple(args, "OnOOOOOnOnn:re_compile", &pattern, &flags, + &code_list, &groupindex, &indexgroup, &named_lists, &named_list_indexes, + &req_offset, &required_chars, &req_flags, &public_group_count)) + return NULL; + + /* Read the regex code. */ + code_len = PyList_GET_SIZE(code_list); + code = (RE_CODE*)re_alloc((size_t)code_len * sizeof(RE_CODE)); + if (!code) + return NULL; + + for (i = 0; i < code_len; i++) { + PyObject* o; + size_t value; + + /* PyList_GET_ITEM borrows a reference. */ + o = PyList_GET_ITEM(code_list, i); + + value = PyLong_AsUnsignedLong(o); + if ((Py_ssize_t)value == -1 && PyErr_Occurred()) + goto error; + + code[i] = (RE_CODE)value; + if (code[i] != value) + goto error; + } + + /* Get the required characters. */ + get_required_chars(required_chars, &req_chars, &req_length); + + /* Create the PatternObject. */ + self = PyObject_NEW(PatternObject, &Pattern_Type); + if (!self) { + set_error(RE_ERROR_MEMORY, NULL); + re_dealloc(req_chars); + re_dealloc(code); + return NULL; + } + + /* Initialise the PatternObject. */ + self->pattern = pattern; + self->flags = flags; + self->weakreflist = NULL; + self->start_node = NULL; + self->repeat_count = 0; + self->true_group_count = 0; + self->public_group_count = public_group_count; + self->group_end_index = 0; + self->groupindex = groupindex; + self->indexgroup = indexgroup; + self->named_lists = named_lists; + self->named_lists_count = (size_t)PyDict_Size(named_lists); + self->partial_named_lists[0] = NULL; + self->partial_named_lists[1] = NULL; + self->named_list_indexes = named_list_indexes; + self->node_capacity = 0; + self->node_count = 0; + self->node_list = NULL; + self->group_info_capacity = 0; + self->group_info = NULL; + self->call_ref_info_capacity = 0; + self->call_ref_info_count = 0; + self->call_ref_info = NULL; + self->repeat_info_capacity = 0; + self->repeat_info = NULL; + self->groups_storage = NULL; + self->repeats_storage = NULL; + self->fuzzy_count = 0; + self->recursive = FALSE; + self->req_offset = req_offset; + self->req_string = NULL; + self->locale_info = NULL; + Py_INCREF(self->pattern); + Py_INCREF(self->groupindex); + Py_INCREF(self->indexgroup); + Py_INCREF(self->named_lists); + Py_INCREF(self->named_list_indexes); + + /* Initialise the character encoding. */ + unicode = (flags & RE_FLAG_UNICODE) != 0; + locale = (flags & RE_FLAG_LOCALE) != 0; + ascii = (flags & RE_FLAG_ASCII) != 0; + if (!unicode && !locale && !ascii) { + if (PyString_Check(self->pattern)) + ascii = RE_FLAG_ASCII; + else + unicode = RE_FLAG_UNICODE; + } + if (unicode) + self->encoding = &unicode_encoding; + else if (locale) + self->encoding = &locale_encoding; + else if (ascii) + self->encoding = &ascii_encoding; + + /* Compile the regular expression code to nodes. */ + ok = compile_to_nodes(code, code + code_len, self); + + /* We no longer need the regular expression code. */ + re_dealloc(code); + + if (!ok) { + Py_DECREF(self); + re_dealloc(req_chars); + return NULL; + } + + /* Make a node for the required string, if there's one. */ + if (req_chars) { + /* Remove the FULLCASE flag if it's not a Unicode pattern or not + * ignoring case. + */ + if (!(self->flags & RE_FLAG_UNICODE) || !(self->flags & + RE_FLAG_IGNORECASE)) + req_flags &= ~RE_FLAG_FULLCASE; + + if (self->flags & RE_FLAG_REVERSE) { + switch (req_flags) { + case 0: + self->req_string = make_STRING_node(self, RE_OP_STRING_REV, + req_length, req_chars); + break; + case RE_FLAG_IGNORECASE | RE_FLAG_FULLCASE: + self->req_string = make_STRING_node(self, RE_OP_STRING_FLD_REV, + req_length, req_chars); + break; + case RE_FLAG_IGNORECASE: + self->req_string = make_STRING_node(self, RE_OP_STRING_IGN_REV, + req_length, req_chars); + break; + } + } else { + switch (req_flags) { + case 0: + self->req_string = make_STRING_node(self, RE_OP_STRING, + req_length, req_chars); + break; + case RE_FLAG_IGNORECASE | RE_FLAG_FULLCASE: + self->req_string = make_STRING_node(self, RE_OP_STRING_FLD, + req_length, req_chars); + break; + case RE_FLAG_IGNORECASE: + self->req_string = make_STRING_node(self, RE_OP_STRING_IGN, + req_length, req_chars); + break; + } + } + + re_dealloc(req_chars); + } + + if (locale) { + /* Store info about the characters in the locale for locale-sensitive + * matching. + */ + self->locale_info = re_alloc(sizeof(RE_LocaleInfo)); + if (!self->locale_info) { + Py_DECREF(self); + return NULL; + } + + scan_locale_chars(self->locale_info); + } + + return (PyObject*)self; + +error: + re_dealloc(code); + set_error(RE_ERROR_ILLEGAL, NULL); + return NULL; +} + +/* Gets the size of the codewords. */ +static PyObject* get_code_size(PyObject* self, PyObject* unused) { + return Py_BuildValue("n", sizeof(RE_CODE)); +} + +/* Gets the property dict. */ +static PyObject* get_properties(PyObject* self_, PyObject* args) { + Py_INCREF(property_dict); + + return property_dict; +} + +/* Folds the case of a string. */ +static PyObject* fold_case(PyObject* self_, PyObject* args) { + RE_StringInfo str_info; + Py_UCS4 (*char_at)(void* text, Py_ssize_t pos); + RE_EncodingTable* encoding; + RE_LocaleInfo locale_info; + Py_ssize_t folded_charsize; + void (*set_char_at)(void* text, Py_ssize_t pos, Py_UCS4 ch); + Py_ssize_t buf_size; + void* folded; + Py_ssize_t folded_len; + PyObject* result; + + Py_ssize_t flags; + PyObject* string; + if (!PyArg_ParseTuple(args, "nO:fold_case", &flags, &string)) + return NULL; + + if (!(flags & RE_FLAG_IGNORECASE)) { + Py_INCREF(string); + return string; + } + + /* Get the string. */ + if (!get_string(string, &str_info)) + return NULL; + + /* Get the function for reading from the original string. */ + switch (str_info.charsize) { + case 1: + char_at = bytes1_char_at; + break; + case 2: + char_at = bytes2_char_at; + break; + case 4: + char_at = bytes4_char_at; + break; + default: +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return NULL; + } + + /* What's the encoding? */ + if (flags & RE_FLAG_UNICODE) + encoding = &unicode_encoding; + else if (flags & RE_FLAG_LOCALE) { + encoding = &locale_encoding; + scan_locale_chars(&locale_info); + } else if (flags & RE_FLAG_ASCII) + encoding = &ascii_encoding; + else + encoding = &unicode_encoding; + + /* The folded string will have the same width as the original string. */ + folded_charsize = str_info.charsize; + + /* Get the function for writing to the folded string. */ + switch (folded_charsize) { + case 1: + set_char_at = bytes1_set_char_at; + break; + case 2: + set_char_at = bytes2_set_char_at; + break; + case 4: + set_char_at = bytes4_set_char_at; + break; + default: +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return NULL; + } + + /* Allocate a buffer for the folded string. */ + if (flags & RE_FLAG_FULLCASE) + /* When using full case-folding with Unicode, some single codepoints + * are mapped to multiple codepoints. + */ + buf_size = str_info.length * RE_MAX_FOLDED; + else + buf_size = str_info.length; + + folded = re_alloc((size_t)(buf_size * folded_charsize)); + if (!folded) { +#if PY_VERSION_HEX >= 0x02060000 + release_buffer(&str_info); + +#endif + return NULL; + } + + /* Fold the case of the string. */ + folded_len = 0; + + if (flags & RE_FLAG_FULLCASE) { + /* Full case-folding. */ + int (*full_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch, Py_UCS4* + folded); + Py_ssize_t i; + Py_UCS4 codepoints[RE_MAX_FOLDED]; + + full_case_fold = encoding->full_case_fold; + + for (i = 0; i < str_info.length; i++) { + int count; + int j; + + count = full_case_fold(&locale_info, char_at(str_info.characters, + i), codepoints); + for (j = 0; j < count; j++) + set_char_at(folded, folded_len + j, codepoints[j]); + + folded_len += count; + } + } else { + /* Simple case-folding. */ + Py_UCS4 (*simple_case_fold)(RE_LocaleInfo* locale_info, Py_UCS4 ch); + Py_ssize_t i; + + simple_case_fold = encoding->simple_case_fold; + + for (i = 0; i < str_info.length; i++) { + Py_UCS4 ch; + + ch = simple_case_fold(&locale_info, char_at(str_info.characters, + i)); + set_char_at(folded, i, ch); + } + + folded_len = str_info.length; + } + + /* Build the result string. */ + if (str_info.is_unicode) + result = build_unicode_value(folded, folded_len, folded_charsize); + else + result = build_bytes_value(folded, folded_len, folded_charsize); + + re_dealloc(folded); + +#if PY_VERSION_HEX >= 0x02060000 + /* Release the original string's buffer. */ + release_buffer(&str_info); + +#endif + return result; +} + +/* Returns a tuple of the Unicode characters that expand on full case-folding. + */ +static PyObject* get_expand_on_folding(PyObject* self, PyObject* unused) { + int count; + PyObject* result; + int i; + + /* How many characters are there? */ + count = sizeof(re_expand_on_folding) / sizeof(re_expand_on_folding[0]); + + /* Put all the characters in a tuple. */ + result = PyTuple_New(count); + if (!result) + return NULL; + + for (i = 0; i < count; i++) { + Py_UNICODE codepoint; + PyObject* item; + + codepoint = re_expand_on_folding[i]; + + item = build_unicode_value(&codepoint, 1, sizeof(codepoint)); + if (!item) + goto error; + + /* PyTuple_SetItem borrows the reference. */ + PyTuple_SetItem(result, i, item); + } + + return result; + +error: + Py_DECREF(result); + return NULL; +} + +/* Returns whether a character has a given value for a Unicode property. */ +static PyObject* has_property_value(PyObject* self_, PyObject* args) { + BOOL v; + + Py_ssize_t property_value; + Py_ssize_t character; + if (!PyArg_ParseTuple(args, "nn:has_property_value", &property_value, + &character)) + return NULL; + + v = unicode_has_property((RE_CODE)property_value, (Py_UCS4)character) ? 1 : + 0; + + return Py_BuildValue("n", v); +} + +/* Returns a list of all the simple cases of a character. + * + * If full case-folding is turned on and the character also expands on full + * case-folding, a None is appended to the list. + */ +static PyObject* get_all_cases(PyObject* self_, PyObject* args) { + RE_EncodingTable* encoding; + RE_LocaleInfo locale_info; + int count; + Py_UCS4 cases[RE_MAX_CASES]; + PyObject* result; + int i; + Py_UCS4 folded[RE_MAX_FOLDED]; + + Py_ssize_t flags; + Py_ssize_t character; + if (!PyArg_ParseTuple(args, "nn:get_all_cases", &flags, &character)) + return NULL; + + /* What's the encoding? */ + if (flags & RE_FLAG_UNICODE) + encoding = &unicode_encoding; + else if (flags & RE_FLAG_LOCALE) { + encoding = &locale_encoding; + scan_locale_chars(&locale_info); + } else if (flags & RE_FLAG_ASCII) + encoding = &ascii_encoding; + else + encoding = &ascii_encoding; + + /* Get all the simple cases. */ + count = encoding->all_cases(&locale_info, (Py_UCS4)character, cases); + + result = PyList_New(count); + if (!result) + return NULL; + + for (i = 0; i < count; i++) { + PyObject* item; + + item = Py_BuildValue("n", cases[i]); + if (!item) + goto error; + + /* PyList_SetItem borrows the reference. */ + PyList_SetItem(result, i, item); + } + + /* If the character also expands on full case-folding, append a None. */ + if ((flags & RE_FULL_CASE_FOLDING) == RE_FULL_CASE_FOLDING) { + count = encoding->full_case_fold(&locale_info, (Py_UCS4)character, + folded); + if (count > 1) + PyList_Append(result, Py_None); + } + + return result; + +error: + Py_DECREF(result); + return NULL; +} + +/* The table of the module's functions. */ +static PyMethodDef _functions[] = { + {"compile", (PyCFunction)re_compile, METH_VARARGS}, + {"get_code_size", (PyCFunction)get_code_size, METH_NOARGS}, + {"get_properties", (PyCFunction)get_properties, METH_VARARGS}, + {"fold_case", (PyCFunction)fold_case, METH_VARARGS}, + {"get_expand_on_folding", (PyCFunction)get_expand_on_folding, METH_NOARGS}, + {"has_property_value", (PyCFunction)has_property_value, METH_VARARGS}, + {"get_all_cases", (PyCFunction)get_all_cases, METH_VARARGS}, + {NULL, NULL} +}; + +/* Initialises the property dictionary. */ +Py_LOCAL_INLINE(BOOL) init_property_dict(void) { + size_t value_set_count; + size_t i; + PyObject** value_dicts; + + property_dict = NULL; + + /* How many value sets are there? */ + value_set_count = 0; + + for (i = 0; i < sizeof(re_property_values) / sizeof(re_property_values[0]); + i++) { + RE_PropertyValue* value; + + value = &re_property_values[i]; + if (value->value_set >= value_set_count) + value_set_count = (size_t)value->value_set + 1; + } + + /* Quick references for the value sets. */ + value_dicts = (PyObject**)re_alloc(value_set_count * + sizeof(value_dicts[0])); + if (!value_dicts) + return FALSE; + + memset(value_dicts, 0, value_set_count * sizeof(value_dicts[0])); + + /* Build the property values dictionaries. */ + for (i = 0; i < sizeof(re_property_values) / sizeof(re_property_values[0]); + i++) { + RE_PropertyValue* value; + PyObject* v; + int status; + + value = &re_property_values[i]; + if (!value_dicts[value->value_set]) { + value_dicts[value->value_set] = PyDict_New(); + if (!value_dicts[value->value_set]) + goto error; + } + + v = Py_BuildValue("i", value->id); + if (!v) + goto error; + + status = PyDict_SetItemString(value_dicts[value->value_set], + re_strings[value->name], v); + Py_DECREF(v); + if (status < 0) + goto error; + } + + /* Build the property dictionary. */ + property_dict = PyDict_New(); + if (!property_dict) + goto error; + + for (i = 0; i < sizeof(re_properties) / sizeof(re_properties[0]); i++) { + RE_Property* property; + PyObject* v; + int status; + + property = &re_properties[i]; + v = Py_BuildValue("iO", property->id, + value_dicts[property->value_set]); + if (!v) + goto error; + + status = PyDict_SetItemString(property_dict, + re_strings[property->name], v); + Py_DECREF(v); + if (status < 0) + goto error; + } + + /* DECREF the value sets. Any unused ones will be deallocated. */ + for (i = 0; i < value_set_count; i++) + Py_XDECREF(value_dicts[i]); + + re_dealloc(value_dicts); + + return TRUE; + +error: + Py_XDECREF(property_dict); + + /* DECREF the value sets. */ + for (i = 0; i < value_set_count; i++) + Py_XDECREF(value_dicts[i]); + + re_dealloc(value_dicts); + + return FALSE; +} + +/* Initialises the module. */ +PyMODINIT_FUNC init_regex(void) { + PyObject* m; + PyObject* d; + PyObject* x; + +#if defined(VERBOSE) + /* Unbuffered in case it crashes! */ + setvbuf(stdout, NULL, _IONBF, 0); + +#endif + /* Initialise Pattern_Type. */ + Pattern_Type.tp_dealloc = pattern_dealloc; + Pattern_Type.tp_repr = pattern_repr; + Pattern_Type.tp_flags = Py_TPFLAGS_HAVE_WEAKREFS; + Pattern_Type.tp_doc = pattern_doc; + Pattern_Type.tp_weaklistoffset = offsetof(PatternObject, weakreflist); + Pattern_Type.tp_methods = pattern_methods; + Pattern_Type.tp_members = pattern_members; + Pattern_Type.tp_getset = pattern_getset; + + /* Initialise Match_Type. */ + Match_Type.tp_dealloc = match_dealloc; + Match_Type.tp_repr = match_repr; + Match_Type.tp_as_mapping = &match_as_mapping; + Match_Type.tp_flags = Py_TPFLAGS_DEFAULT; + Match_Type.tp_doc = match_doc; + Match_Type.tp_methods = match_methods; + Match_Type.tp_members = match_members; + Match_Type.tp_getset = match_getset; + + /* Initialise Scanner_Type. */ + Scanner_Type.tp_dealloc = scanner_dealloc; + Scanner_Type.tp_flags = Py_TPFLAGS_DEFAULT; + Scanner_Type.tp_doc = scanner_doc; + Scanner_Type.tp_iter = scanner_iter; + Scanner_Type.tp_iternext = scanner_iternext; + Scanner_Type.tp_methods = scanner_methods; + Scanner_Type.tp_members = scanner_members; + + /* Initialise Splitter_Type. */ + Splitter_Type.tp_dealloc = splitter_dealloc; + Splitter_Type.tp_flags = Py_TPFLAGS_DEFAULT; + Splitter_Type.tp_doc = splitter_doc; + Splitter_Type.tp_iter = splitter_iter; + Splitter_Type.tp_iternext = splitter_iternext; + Splitter_Type.tp_methods = splitter_methods; + Splitter_Type.tp_members = splitter_members; +#if PY_VERSION_HEX >= 0x02060000 + + /* Initialise Capture_Type. */ + Capture_Type.tp_dealloc = capture_dealloc; + Capture_Type.tp_str = capture_str; + Capture_Type.tp_as_mapping = &capture_as_mapping; + Capture_Type.tp_flags = Py_TPFLAGS_DEFAULT; + Capture_Type.tp_methods = capture_methods; +#endif + + /* Initialize object types */ + if (PyType_Ready(&Pattern_Type) < 0) + return; + if (PyType_Ready(&Match_Type) < 0) + return; + if (PyType_Ready(&Scanner_Type) < 0) + return; + if (PyType_Ready(&Splitter_Type) < 0) + return; +#if PY_VERSION_HEX >= 0x02060000 + if (PyType_Ready(&Capture_Type) < 0) + return; +#endif + + error_exception = NULL; + + m = Py_InitModule("_" RE_MODULE, _functions); + if (!m) + return; + + d = PyModule_GetDict(m); + + x = PyInt_FromLong(RE_MAGIC); + if (x) { + PyDict_SetItemString(d, "MAGIC", x); + Py_DECREF(x); + } + + x = PyInt_FromLong(sizeof(RE_CODE)); + if (x) { + PyDict_SetItemString(d, "CODE_SIZE", x); + Py_DECREF(x); + } + + x = PyString_FromString(copyright); + if (x) { + PyDict_SetItemString(d, "copyright", x); + Py_DECREF(x); + } + + /* Initialise the property dictionary. */ + if (!init_property_dict()) + return; +} + +/* vim:ts=4:sw=4:et */ diff --git a/lib/regex/_regex.h b/lib/regex/_regex.h new file mode 100644 index 000000000..37ab8a9c6 --- /dev/null +++ b/lib/regex/_regex.h @@ -0,0 +1,243 @@ +/* + * Secret Labs' Regular Expression Engine + * + * regular expression matching engine + * + * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. + * + * NOTE: This file is generated by regex.py. If you need + * to change anything in here, edit regex.py and run it. + * + * 2010-01-16 mrab Re-written + */ + +/* Supports Unicode version 8.0.0. */ + +#define RE_MAGIC 20100116 + +#include "_regex_unicode.h" + +/* Operators. */ +#define RE_OP_FAILURE 0 +#define RE_OP_SUCCESS 1 +#define RE_OP_ANY 2 +#define RE_OP_ANY_ALL 3 +#define RE_OP_ANY_ALL_REV 4 +#define RE_OP_ANY_REV 5 +#define RE_OP_ANY_U 6 +#define RE_OP_ANY_U_REV 7 +#define RE_OP_ATOMIC 8 +#define RE_OP_BOUNDARY 9 +#define RE_OP_BRANCH 10 +#define RE_OP_CALL_REF 11 +#define RE_OP_CHARACTER 12 +#define RE_OP_CHARACTER_IGN 13 +#define RE_OP_CHARACTER_IGN_REV 14 +#define RE_OP_CHARACTER_REV 15 +#define RE_OP_CONDITIONAL 16 +#define RE_OP_DEFAULT_BOUNDARY 17 +#define RE_OP_DEFAULT_END_OF_WORD 18 +#define RE_OP_DEFAULT_START_OF_WORD 19 +#define RE_OP_END 20 +#define RE_OP_END_OF_LINE 21 +#define RE_OP_END_OF_LINE_U 22 +#define RE_OP_END_OF_STRING 23 +#define RE_OP_END_OF_STRING_LINE 24 +#define RE_OP_END_OF_STRING_LINE_U 25 +#define RE_OP_END_OF_WORD 26 +#define RE_OP_FUZZY 27 +#define RE_OP_GRAPHEME_BOUNDARY 28 +#define RE_OP_GREEDY_REPEAT 29 +#define RE_OP_GROUP 30 +#define RE_OP_GROUP_CALL 31 +#define RE_OP_GROUP_EXISTS 32 +#define RE_OP_KEEP 33 +#define RE_OP_LAZY_REPEAT 34 +#define RE_OP_LOOKAROUND 35 +#define RE_OP_NEXT 36 +#define RE_OP_PROPERTY 37 +#define RE_OP_PROPERTY_IGN 38 +#define RE_OP_PROPERTY_IGN_REV 39 +#define RE_OP_PROPERTY_REV 40 +#define RE_OP_PRUNE 41 +#define RE_OP_RANGE 42 +#define RE_OP_RANGE_IGN 43 +#define RE_OP_RANGE_IGN_REV 44 +#define RE_OP_RANGE_REV 45 +#define RE_OP_REF_GROUP 46 +#define RE_OP_REF_GROUP_FLD 47 +#define RE_OP_REF_GROUP_FLD_REV 48 +#define RE_OP_REF_GROUP_IGN 49 +#define RE_OP_REF_GROUP_IGN_REV 50 +#define RE_OP_REF_GROUP_REV 51 +#define RE_OP_SEARCH_ANCHOR 52 +#define RE_OP_SET_DIFF 53 +#define RE_OP_SET_DIFF_IGN 54 +#define RE_OP_SET_DIFF_IGN_REV 55 +#define RE_OP_SET_DIFF_REV 56 +#define RE_OP_SET_INTER 57 +#define RE_OP_SET_INTER_IGN 58 +#define RE_OP_SET_INTER_IGN_REV 59 +#define RE_OP_SET_INTER_REV 60 +#define RE_OP_SET_SYM_DIFF 61 +#define RE_OP_SET_SYM_DIFF_IGN 62 +#define RE_OP_SET_SYM_DIFF_IGN_REV 63 +#define RE_OP_SET_SYM_DIFF_REV 64 +#define RE_OP_SET_UNION 65 +#define RE_OP_SET_UNION_IGN 66 +#define RE_OP_SET_UNION_IGN_REV 67 +#define RE_OP_SET_UNION_REV 68 +#define RE_OP_SKIP 69 +#define RE_OP_START_OF_LINE 70 +#define RE_OP_START_OF_LINE_U 71 +#define RE_OP_START_OF_STRING 72 +#define RE_OP_START_OF_WORD 73 +#define RE_OP_STRING 74 +#define RE_OP_STRING_FLD 75 +#define RE_OP_STRING_FLD_REV 76 +#define RE_OP_STRING_IGN 77 +#define RE_OP_STRING_IGN_REV 78 +#define RE_OP_STRING_REV 79 +#define RE_OP_STRING_SET 80 +#define RE_OP_STRING_SET_FLD 81 +#define RE_OP_STRING_SET_FLD_REV 82 +#define RE_OP_STRING_SET_IGN 83 +#define RE_OP_STRING_SET_IGN_REV 84 +#define RE_OP_STRING_SET_REV 85 +#define RE_OP_BODY_END 86 +#define RE_OP_BODY_START 87 +#define RE_OP_END_ATOMIC 88 +#define RE_OP_END_CONDITIONAL 89 +#define RE_OP_END_FUZZY 90 +#define RE_OP_END_GREEDY_REPEAT 91 +#define RE_OP_END_GROUP 92 +#define RE_OP_END_LAZY_REPEAT 93 +#define RE_OP_END_LOOKAROUND 94 +#define RE_OP_GREEDY_REPEAT_ONE 95 +#define RE_OP_GROUP_RETURN 96 +#define RE_OP_LAZY_REPEAT_ONE 97 +#define RE_OP_MATCH_BODY 98 +#define RE_OP_MATCH_TAIL 99 +#define RE_OP_START_GROUP 100 + +char* re_op_text[] = { + "RE_OP_FAILURE", + "RE_OP_SUCCESS", + "RE_OP_ANY", + "RE_OP_ANY_ALL", + "RE_OP_ANY_ALL_REV", + "RE_OP_ANY_REV", + "RE_OP_ANY_U", + "RE_OP_ANY_U_REV", + "RE_OP_ATOMIC", + "RE_OP_BOUNDARY", + "RE_OP_BRANCH", + "RE_OP_CALL_REF", + "RE_OP_CHARACTER", + "RE_OP_CHARACTER_IGN", + "RE_OP_CHARACTER_IGN_REV", + "RE_OP_CHARACTER_REV", + "RE_OP_CONDITIONAL", + "RE_OP_DEFAULT_BOUNDARY", + "RE_OP_DEFAULT_END_OF_WORD", + "RE_OP_DEFAULT_START_OF_WORD", + "RE_OP_END", + "RE_OP_END_OF_LINE", + "RE_OP_END_OF_LINE_U", + "RE_OP_END_OF_STRING", + "RE_OP_END_OF_STRING_LINE", + "RE_OP_END_OF_STRING_LINE_U", + "RE_OP_END_OF_WORD", + "RE_OP_FUZZY", + "RE_OP_GRAPHEME_BOUNDARY", + "RE_OP_GREEDY_REPEAT", + "RE_OP_GROUP", + "RE_OP_GROUP_CALL", + "RE_OP_GROUP_EXISTS", + "RE_OP_KEEP", + "RE_OP_LAZY_REPEAT", + "RE_OP_LOOKAROUND", + "RE_OP_NEXT", + "RE_OP_PROPERTY", + "RE_OP_PROPERTY_IGN", + "RE_OP_PROPERTY_IGN_REV", + "RE_OP_PROPERTY_REV", + "RE_OP_PRUNE", + "RE_OP_RANGE", + "RE_OP_RANGE_IGN", + "RE_OP_RANGE_IGN_REV", + "RE_OP_RANGE_REV", + "RE_OP_REF_GROUP", + "RE_OP_REF_GROUP_FLD", + "RE_OP_REF_GROUP_FLD_REV", + "RE_OP_REF_GROUP_IGN", + "RE_OP_REF_GROUP_IGN_REV", + "RE_OP_REF_GROUP_REV", + "RE_OP_SEARCH_ANCHOR", + "RE_OP_SET_DIFF", + "RE_OP_SET_DIFF_IGN", + "RE_OP_SET_DIFF_IGN_REV", + "RE_OP_SET_DIFF_REV", + "RE_OP_SET_INTER", + "RE_OP_SET_INTER_IGN", + "RE_OP_SET_INTER_IGN_REV", + "RE_OP_SET_INTER_REV", + "RE_OP_SET_SYM_DIFF", + "RE_OP_SET_SYM_DIFF_IGN", + "RE_OP_SET_SYM_DIFF_IGN_REV", + "RE_OP_SET_SYM_DIFF_REV", + "RE_OP_SET_UNION", + "RE_OP_SET_UNION_IGN", + "RE_OP_SET_UNION_IGN_REV", + "RE_OP_SET_UNION_REV", + "RE_OP_SKIP", + "RE_OP_START_OF_LINE", + "RE_OP_START_OF_LINE_U", + "RE_OP_START_OF_STRING", + "RE_OP_START_OF_WORD", + "RE_OP_STRING", + "RE_OP_STRING_FLD", + "RE_OP_STRING_FLD_REV", + "RE_OP_STRING_IGN", + "RE_OP_STRING_IGN_REV", + "RE_OP_STRING_REV", + "RE_OP_STRING_SET", + "RE_OP_STRING_SET_FLD", + "RE_OP_STRING_SET_FLD_REV", + "RE_OP_STRING_SET_IGN", + "RE_OP_STRING_SET_IGN_REV", + "RE_OP_STRING_SET_REV", + "RE_OP_BODY_END", + "RE_OP_BODY_START", + "RE_OP_END_ATOMIC", + "RE_OP_END_CONDITIONAL", + "RE_OP_END_FUZZY", + "RE_OP_END_GREEDY_REPEAT", + "RE_OP_END_GROUP", + "RE_OP_END_LAZY_REPEAT", + "RE_OP_END_LOOKAROUND", + "RE_OP_GREEDY_REPEAT_ONE", + "RE_OP_GROUP_RETURN", + "RE_OP_LAZY_REPEAT_ONE", + "RE_OP_MATCH_BODY", + "RE_OP_MATCH_TAIL", + "RE_OP_START_GROUP", +}; + +#define RE_FLAG_ASCII 0x80 +#define RE_FLAG_BESTMATCH 0x1000 +#define RE_FLAG_DEBUG 0x200 +#define RE_FLAG_DOTALL 0x10 +#define RE_FLAG_ENHANCEMATCH 0x8000 +#define RE_FLAG_FULLCASE 0x4000 +#define RE_FLAG_IGNORECASE 0x2 +#define RE_FLAG_LOCALE 0x4 +#define RE_FLAG_MULTILINE 0x8 +#define RE_FLAG_POSIX 0x10000 +#define RE_FLAG_REVERSE 0x400 +#define RE_FLAG_TEMPLATE 0x1 +#define RE_FLAG_UNICODE 0x20 +#define RE_FLAG_VERBOSE 0x40 +#define RE_FLAG_VERSION0 0x2000 +#define RE_FLAG_VERSION1 0x100 +#define RE_FLAG_WORD 0x800 diff --git a/lib/regex/_regex.so b/lib/regex/_regex.so new file mode 100644 index 0000000000000000000000000000000000000000..f0a87043e5c65d0996b2a8dbc501bdeeed7685dc GIT binary patch literal 1189938 zcmb<-^>JfjWMqH=W(GS35KrMAM8p9?F$81@LRkz94h$9y><kVJDh!HXwXAR%Ovan7 z5Lp<_z@PwD1hRvTfq@}m4@5p;k1ALkMlYBH<}&Pn(lC8Y3=9k)eFb|V`U>_!^ug#0 zAcGkg7+^F9#25w^1_m%b0hLFmRiNr%G)O&2DBx*H3W$9p8e$(h&BFsy#lQfgL25xF zflo_PK<)&wiNOL;ML|&eaE1#rMBxG`4fPJ#Zz2o~3?N~5zfcB;2RtjQ!l#QqS}35o zriMr0`mzUmKsxs^FfgPrFff4Z<6>Z7@VF4*(!juQgrP~nxj~3QfrsS;gGUIHLL(@^ z6%`mv7CJIWOkiN*5inv=a8hA05aeLeXb@m*Xb=(PVibJX#K6eVz#zcjz`@V}3StLF zCI%Jeg$uYD6`GFdDsVBf^awB{Hq2mXVqs8Va$sd}krd!$Xy9dGU}S0FlK{IV73459 zoQA|tXJBB+K;ok_GZ`2dvY>)FP&$`^fgz89fgzuPfuR5@Rs^MysS+rs6iUOWG8hAm zE=S{6K>3wW8bnor2nGg*YA9X9z`#(;z`#()z`#%s6$4R?AcBE`p^1Tkp&81?N3}9A zFtkAxv@<X;bTTk7bV2#u3=9lC3=9l?Q2qo428M}HHi(+Uz`!sW#9&}xm;$Adsj0{u z1_p*{Nc`yx3=A_E7#L<TFfhz!U|;}c$@x(E1yCB9TFAh_u!w<yVKG#2DFXw;GAJ9F zT8_-Y!(M?Tw-U-;#lXO@8p;MyYZw?9)`A#lcpU=+!+JEaO$-bSTNoG^wnD|XL1`GZ z9mYVTcR=|&q4X{W28P`X3=H6m#lXM-%3C0*{U8DfAB3_GF)%P3hO*^t(sz5;HU3`w zZT;Wfk0;giUKEsl&i7PX?8l|W;!hj)?(F@@XPt3ChI!7fr&I59+3W09Uze(G>>sl8 zP^@zAgxx3WejDkE@}+G2_hSFm)`JNzw{7$goL6-7_ySg)*UsO<Cp&A+`FXJH!OPXx zqL;<^yYv_DO-{`e;GQYivgwlT9q;PE6N^{gYr5Sly36`s?`mn@fa<^eoLvgdJX>T0 z6({!lc`FpO`YxH}_KGu_F@D+F_CvEzXWm{PtNH(v;_EOLi&DdV9^A!leqUt+tTRtO zIQ-(PO4svu>W4z-s5W+g3HZo%Ng?3h&p^Y2tG>Tqy+iy$_2bfIKf6?q9nPLPL2sk; zEk?Nq6Ta46{20GvvB3X}PlfFztORcsM#!x7*{-tGZHr9jq6ZR-n+vA1I-Xq|vBBwa z0B4==)^El=cNlVSK0m)j_VwlWe8TS!&b-MKqwKG;gIoCXLCvL#Z)BZaufFMieQMX2 zE$+6p2~T2qC+1u*^ImPUF=8FldckDZ#Swe$#4V1*L8AdH#mvCTaNq{2R5Bw2gAl3^ znAgn+DNDf|Bw`0McJX8!;xCx6tM6pTF0PD2{2LB)mat+sCmDyh3<IbFK=KfjxseUK zIp1)ELp=+2_0w>Kg9i@tU*k}J7KeZBaEOQF2)8mE>fLaJk3J4__TeyRKMr#=aQL?p zhxis8>97}vy?Qvrxp0{8k3;<p9O7~~#HDeVufU2uABExwpT#)LvBi-dPU8^w!VzD; zaoBqrhqxgQ^Or!|RM2b!r&i!d|DSO9%L+$60=4rXEoZpVV8&4#>g#a06Vy<|R*r${ zdr+GYY$y_;gu@&W9O4N$(nBB)_4{yy!!#WJ3c(Sc&v2+ejYGWv4tFlaVGgM6g)Mya zaikMn9QHoNp`H~-_}JnIpS3vLe+h?rUL5Ja7>7I6apco=INV=_!~B~#{FR8Keu=^1 z{@Xb0O~>K>ejMhw;ZUEAL;YhM;_NugS%4#6GI4~D1&(l7h9lgBafHJj9PSBXU|>)} z%Fi&S6b|(aINVu|quvR{A@0EgD(ATw7=D6!?1;Y5TP~>hCx|$xUj%ZKDF;Nn;UPpE z)^}3n1GOI{7$hRqK+53w2roo^0=UNu>05y$g<<yofS3a_{~k;|BdCXrP=5nzFT+z* z^`N{igryxg9h@(?85kZx)Wh8Q3TplXsQEB^??c_Q;WtD*tPi>qYH!06h&W9BMs@}U zK?ZS#1_OwH8#F+^VqnPRhWNMP3#xiusQC?O{{6`bQP1!ORsCD2ISk)X#VcX%{|6BV z^_@XBe}}mf8a^=p?t!^e8{$qMh&G0Pn7uC{0x*A7LEZTQnjW%2hBGiQu*1|pgQy4f zkwH>3pzaCy4-tpiyB2EBg<lYHm^-<k{%Sx=SM|{J*6<#p9%had)O>-z5OLVJfi_e< z!&`_r%soqC=6^*M-wt#CD~LGEzxQF{uTjPIp!PPP*((KA-vA8<m^*hs?PUNp?4jcb zpzw)-#tXw^i1{#gRzt-bpyDw1Y=_$W02-b!@c@|lXz7X{8V(I;>1P(yd<L|1%K<fi z!5@fwKw~l>cP@aL^8q3bvvzi8>?CIckhK4606Q&>2Jz|46EF$X3-8RmYpcmehC zg&4RQ9x$Vta~ftZT722V#GgaVhlS@CXnHe%`U{qB-Js#n04)b#>R-dmL5ufuF!z6i zm=6nwWT<)uPzMk?Mg$5sA(;6OAnIZM1&vdI%7+KKkbKtxk!H{Ym&4o)2`mu*g2sD5 z<~Tybr{Ob1A#D6<8Ve+xH{64WgT~81>T{s(WI)R=YoPIa0h)ebW1ElIA?7$h&4Go_ zEvWenpoR?t0|RV~uO3={CftSC3ri=bpyf%z56F0~2E-c-D$sCXfYuAJaEpMtrvO@B z!Sb&pw7y_Kt1o(>@t6QDH(+CeSE25|0Cf+{{a>N(SGW&xC#;;=4htV>Ji_9g4dza0 ze8I#gz|=$2KTJFh>YfH@c?cS(1cm=~SbU-7_bO;QX@I5^SU6mShQkB2dQc3Uk9Zg) z*q{LkGMs^d!5-@01=k_|Ish5gX1K@*t#>#f>FNSh{XAH_a6!Z+6d@8Hpyo_~h7T<K z*<k*H)-SMhD+G=gaR&5w5rC%K0%$scjT1UU{q+G_PQv{A2JA0xh6HH&025b*h8qJk zox{X!Vd0NfZXAcY(*as9!{YZp)cgi$`44lyHZ=Sh(8`BnQ1uCD<qSi7e0pwvUVL#$ zVo^zaJVShDUS<hHd|GB+CPTbuh;MvKYEf!>W^qYsQHZZ|PJUi$NMce>Dnt&uOnh=h zBFKQmoXo0JhIp^C_#mhW&N+$2#i_*%MXB-WsU`6RMfnA(MJ1IC0hNwL>G1)HMa8Kh zr3E>u3;~t#PNkVSDPf5@r68V9W^qZpV?jY`UJ6`uesN|+d__uTdS(f#SV3uCGMZ3g zPF`s)swxzX8L1WVC<1AvIXUskiN&e$Y56%RnR)3j&52O)VutvD%Fw*b<ouM>Q0HKi z_>g>`{PNTykRFEk_~eqxg4Fn=_?-M?IIkqXw4flh2u&;pqzfz-Pzm;?Uuro6lo^~_ z;#rcK3$iNSFF!9exTL5wxr8Ae6kEyJ@yQw4@o9;fISc`n;9!dfX)Q{PPpv3O%u9*S z&x1Q2rT}Ijs?*(y@^hhHVF;-7Ps&P7E{P8YMP5LqYejN=NM%8)Yf({t5ko+wYf({r zaB4|#Nf9Ve0xF$K)6!Cl;)7CiQWJ|oQ3cWfjy9MM&)kCiqLO$B<(r>U3XWf>E$*o$ zjwK~UaN|I_gG%#CGILREbjvTwO)LRfUy&T|nU|7U0W%%s9>09w)ZF|cP&)EW&5d^} zN(DItYJ+cL4k!}f{_+H;?|{nqsMMl-Sbzpp#)lM@!ooM8(kDMJJ>IbxM1d?VDvI|{ zPA)AfN=<=sopVwXLBSE6T3DKzmz)~!o>~%|lbH;%7~+|D$6^SBA)pe%hzEr;gz1=< z0u5hixPz0BV=<J+5K!rvR{}DjII}7>9^`S5Q;-Y*r4W!CK}I4pLA?wO0N1j_ocQ3x zveb}_qSQpN%RwSRsl_GvMKHnK)ZF9(P{@G-(m5kFIXgHrJufk*7?h~M`r^}yQd2<* zG@Ai4*+6VWq?(}A0#L9(odoukSAJ$5LqMfVW^zfqb3WKEFxxM+0^~7>3Gv_n4NpzX z4oXdf7ZBz7MJe$~MX8A(t05tW90u|5kcHX-&Obq^WvNBQsjv)NoS9pYlZrJjrxzs_ zWTfV%#wX{Lf(kgeRiIP^O*grzxy7j^47sVfx%p+F*o6dUJUCS)mcRohI2Gi(ypqJs zJW$YqObAK^E5az<9P?7VQ!7D!gL^bRH7~U&5me%oq^5&XGPHyQl_sEw1DOuW-0)QF z4lWr$LG4+RS`-frOL%I8CN7XR#}tqOP*)d0vMDS!!SMhwDi;xl2mx@424{azA_gUI zP&)U_OUX=5Eq1Md#VU%+oD*|$d{av@@<G;uEkG1Hu((dl0hd{j<dv9QSejV`4@gi6 z5L}X90P<I2NoGDM?SRr|aB2yt2m~cdP!$1l6_Sw<CPP3a$O)iIDjuAk5vdlY2bz*V z#TwkWfW%}_A_HeLP(CcqOwUVA0dZlzgoY{DOV0Vwm<H9B@kOcWsi43B85kdwnwSF0 zPw}9F22$La#G9Eg1XMyepb!L^0nOu}42)t6IAU`lP6VZ4SS1->2C56eDW(`)_=1^G z*-S{KnqLrK0?Jj;)RUBxpPUUc160`-Cl_TFz^ldL)VvaKxdq8xPz||>B^faFi8%!s ziAkv?naMB_a7_hDkuU*pb%rbeV#jBu=j9iHf*+<VIU_MIJ+(N#JR>zP9&QM{B1e&i z8;M=IB(o$Z6=Vx`X^;%4zD~t0ots!tfRN5i0ac|%@L<VIiBHbYE6L0&g_j={=xRU$ z=!#NO(-KQ_N)UdJ2ek|03-UAb;AX>1o79RDq;?EkI0;mLLL&k$n3I_YkHPYc%#zgj z;)29vxZ{#CQ!>FWF3Qh=1$S0{W*)j&N@6iQVlyfWGE(zkstZf=OTgJIJ~y!lZdFNY zQEp~lVop4`EmaCu0Mn6Ql95^zkCIf1lQT2pG1^cNg_!9mGbJ@2>_J4*O36%2F3Lno zYmhKXEkd;vo_J7&;fV$&oR^=MoRL_Rm<%c#F=GPaFt8Iqr9Z?jnBL5k;`pS@yu_l) zcu?y!u_Pbv2#^#g>tT~CN=(U2PJ~B7X<lYpW@<`2+^;a>A$Aj!UQ$vEic*skOAx8G zI6ti<J|({dNhl{j9b|2Meo;zlQG9Aea%us%%!H;Rh;8U87%qyQj6szkyqR2<Sd<Bl z`1s<~oYZ8ba41MDDM>BLi^q~6VN%7Fc_oPzFi(RDfYO}!;>w&HP{9xnY8=DlAW0b1 zF@T9AC+6knfqj*npPQ7KmzkFypNy;^B{exew;;bb6XaDC!%`EAOX3rYGZXXT%QI6D zg&8PMfQ(5m$}cT|*#i?qc1=!ZUMjR101d*t(%jUd%w!a6U}E4R8D<>VBe|KNb}}N$ zGC_4^Jj|_FYIukONXTR+$AjV<xd8yqJy7dl7A59Y!k9@piHPz6oS<Pc1w}|j52&pH zlYsV^ax#+&O28GcUU9yj0Ygq^QZk5T#t@&Hl30?+5FejZTnsH8<3Z(>yN{=nbG(tB zp$P+wX{2WgXCp+7Aw6$Mza7FRo<to5fUJeXryDvffKLgM9BkDak`NXqL=_VQ69eNA zhYSpi3@i*B;4uw|N2g4j#>T+F%Af@mE0xOR1ckCGlrNH*$qQPY=LqEsgZWGh-h2@K zuyv2H^@<mu>n}5)>mp&|6QJS^XyRTWkQkeQCf;ua5nq5NJ~0d;z5z{K#sMOJ08M;* z07U!(n)pko_yaU?6<>(@4`|{AP<t7m>o#Hjng<mZKoi&XftaI!Chp}A5jQ{+SB8o^ zpo#k_Lvla>ns^{oJONGII0$0?fkTk-NSHfe`~weB#lut}=AS?l?*~nwgVv*>iT6Oo zZ=i`!hKfHx6Q2haXLy8a?=q-32bws{JpyRrtD)*8(8OWxR6rAlxl;p89Oh03H1Si4 zkZ|xo6NkAc08Jd`o(ME?n0pe?#9{8qKof_#rvXhI7Cs$l;xPA2Kof_#X9k)$%smUx z#9{7PfhG=f&jB=XSooYk6NkCy0-89?JvY$AVeWZ=CJuAY3p8<<dl(+0rXQHS9BASj zq3J{bO&n&90-89?91S#am^lV$;xKa@(8OWpc%X^H{2PEK4)bpWn)oiL`xDT_VfHql ziNoyeKof`AI{{4`X73C%ahSae(8OW(u0Ruq<?91z;zyzGzkoyh0h)LmRQ(Gy@dsz1 z?S&_(@qQMno&!x>;UlCzQa}^G4OOp!CVt=pM7;x=cs;BfKoc*h2d!jcU`Rj{e+4x^ z15MoEC&c^)H1X&1kaDI2P5c7XJqysp85JPvSD=YM_y#fm2bwqov|ea<h8k}CQ1Jz5 z;taY_^U=h`q3TbdiOWL8FQAF9f`;1%G;s&0`9ILa&7tN<JV$kB0aSegnz%DmeFmEN z1W`!5RG^8^fSPjvO<WKfUnkJS4R%7(+YK~vg|iU%JwOv@fSU6HO<VwK&JQ&4hBFXz z7+yeVSiSrS8g3kD;vaTE+#`V|zTh-OzXF=LH`F~EXyObfA?8@1i8q{rnB#yZ9s&(# z4>WOu6A*JE(8Lo?Ld;1(6Ssr9Cj(79;W)&c3N&$r6A*J6(8MF5?&&}ipKuIf&I~m1 z2ge}hEI<=~3JvEKXyOT*Am;2q7l)d208Km|>Yfv5;s=gH(%}s>@drmC?s<SFego>B z7ii)O)<N9!15KO(Y7WCoh<UK|lL>VX2b%Z>sCy*P#2*}n=vP1!p9po22AcQ<&_YcH z1_lc>aR#V44rt;<Q1^JCi95W7lvfF8;?+>~8EE1GQ1v^|#5<ts9bQBH0SgCM`Idkt zegRqzEI<>V0X1g@n)m}YNIX736BmHG=LMQLOg+OJRQD`_n$LkI9<T-yzY1vLu=J^c zCVpTwM7;%?_ycIU<bWm)4}UcA51<KX(7rV^ad`Nni8Da`y8}&pCDdOI?@;{<4}Uaq z4n0WyvH(pS7CtM`!~>w|{{Wgeyxc|;cYvzDfhNA-9K?MO(8OWo?F%&V2cU(i3=9lE z(8L{}<}kd6m<LO5uyE!;6F&eAX9YBISU78-iC=)KuRs&u0S)I5A5iUG09F43O*{d# zkpZ+O^CPPIQfT|Z0ZrTi8ZRDb;;?pO2AX&URDA)OIIKN60Zn`dRQ(Jzaag-?2b%a% zsDBwgp}PMuRGb4%ydWBqPbJXAe?aZkKofrpHOBx=JmCw(90xRU3w=ns4L}qB4mBqN zO}yX-q<qUj7l)ctfhG=Xw>O}PJ3#$40Zn`X)SLxq;(ws-S%D_*0BsNNKofreHHYCd zYB=A7rV|b{afk1a@R2|he{dh7Uja>=6FQ!tfhOMY8)A+Hnz+I}h&c{u;`~r^JkZ1o zpyouNi6`8Fn3I4eE(|p%15G>uYEA{Z_$`Pz4QS%9_H756IKwH3zh<C`FSrRYX91e{ z7HB=Y0!`fE2gIBmXyOZQK+HLSCN2T>*9kQ72~dCCKoe(xn)3inTor203pDWy(01bw zH1UM%5ce>AfzYt>PzP!b2bws;e~7;%(8M2HgXmX46E}gHqk$$K0QHvznz+MNh&c{u z;<iw8JkZ1)pyouNi6>l!n3I4e?gBL@15MljYEA{3c*8}AISpvyo=|f-(8Lv>=FC77 zZ#WMzX91eHKh&HRXyO8#kZ|6CCe8pg=Kz{Gto}NICSH&RG3NoAIIRA9fhL{+RnPDh z;$B#KfVHnU(8OW&R0NuMEYx2QzM-09@EB6AzCaV7AOmsd2Q={uQ1KsV;sUY|^$g!p z%@>G;h;yKcD=0$51<=F=pyCo};uoOhwgQ@X1JwN*XyOJ?a}3bLH$cTL(8ZzVIG~9K ze1(LM2b%Z-sCxp?#1)|GBhbVzK+R7;6BmH0&p;D@@C9OT0h;&%sCWgMcmh<s0Zklc zP6wJe%$x~m;xKb&pouTI4{^@|H1Q2k@fB#|4N{Q!-GC<k0V=)&O+4WN#QXzj;uE0a zC(y(nK*cYhi8DNem~#V7TmdTn08Lx~8ZR%<#1)|F`~#Z!18IoAexQjjfQmCf*D1sD zDa;%WG;x?Y0%+neb0pBkq2a86CJqaK4K#6B_#2>!!@}PJP5c1Vd<Qh~3s7+nH1Q8m z@c=Y&257uQpouF$#bN8XVd0YlEtfLT)K@{p3(&+npyCy1;*+4_4QS#EpyC~9;+vr2 z6VSvDK*eXEiJyUrFF+H&0~KF^CjJsCz5z}A2UL6qnmDT}q&<89O<V*jegaKg1uA|4 zP22)1egjQB04n|fO*{oE{sK+B3@ZKsO}rN>{sT>XIaHkCCu;eB7%I+zCVmzwE`TO} zA1W?^CjJ>Ju7D=at_BG=4K#5DsJH=|xHVMV0!`c(D(-+L9tjoqKoc*7iU**Hw?f4u z(8MP~#S_rP7eU1{(8RYw#S75H4?x8$(8Mo6#T(GXA4A1E(8S+C#V4SN|AmUrKob{G zhlKM2G;wvP_zE;}E2#JeG;uem_zpDjAgK5OH1Q;;_z5)eGN||kH1Rg5_zg7iX;AS8 zXyU7&;xEv|4?)F0pow3DivK_pe+Lz3_=TGOnV=h1IMBq!pyC2(;;K+_2{dsFsJH@} zxIa`}15G>$DsF%#UIZ1lKof6+iaVf*&x4A4powpSiU**HABBoXpo!msiYK6ne}{@^ zpo#NoLeg6Snz$TPyaG+!2rAxyChi3l??4legNjc;6VHT-&p;Eeg^Djg6Q2wfUx6mR z5-Pp{O?)p@d<UBNDX91XH1XR|@e^p`AE4qF(8L+FAmMfcO<W8r{s2u}9V-3;P22)1 z{sB$g7b^Y(O*{=M&hQ&G{Z~W9InczXK*a^n#8*PaCD6ooK*bf%#7{!SHPFOwLB$Qw z#6LjAEzranv?1Z<fF>>h756|B*MN!#po!Z-#Us$fgQ4OHXyVyW@eDNaI;eO7n)no` zcm<mHQmA+Xn)o)TIBZ`atlb6MXZHfSUl1mK0lMGq1DZH&AKwo&ao9dV20h3=Fqk>8 z{c#*<;;?;T0%+n4(EV`|XyOe}aRoGS*uFmvG;!EIaRW4Q*uHEFG;!EIO$Rh_*#21$ zG;!Ge*#I<g*uK{YG;!EI?gTV(*uL@%H1P)Le(C}=aR%uA-wHHw*uLNfG;!E|v<@`! z2IxMu325T5eWo+e#9{ji7odqVK=<daKof6(if=#@XMpaj-GL_F02M!gCe8reCwl@- zya6hH0ZkmXpZx}!IBcKt12l2iKJFK2;;{XFAJD`Zp!)=Wpoup?#ToQb(?4vVEC-r6 zY`?hxnm7Y=AE*SHcmq^i0Zp6%y3bStO}qgrZh$7v0Nwv?fhG>y7w>>34%;v3fhOJn z-A5UKCe8reCmw+&4m+110Zp6%x-UBeO}qgrUVtVJ+vi?^Cf)$u58Hqy4m-!715LaE zy8m<nnm7aWoP-%@;tf#o1!&^1^D9=MiNp5oZa@=<oo}!MO&qrG_yC$X>>Ps=XyOjg zeW@4F#9`-1+&~kD?N5JzCJx(={{l@McJ9FkG;!Fu1V7NkVf%0ypyyJ+%757YYYsH= z2he@l0%+o}^Bg45#9{l&70|?C=M`w6iNntQFhCQB?Sr;J6HkDi%i(|~4m+>I15F&Z zzc>I*`~h^|aRi!p0(Adx0-E>(s5tCA4_LS<7(mvScA%;6hKf%>6Tf2yQ9lDsTotN* z0h)LpRD1=RxE54=1Dd!ZRD1`TxIR?;0GhZlRQv>*xH(k(0-CrrRQv{-xII+-0h+io zRQv^+xI0w*1Dd!xRQv~;xHnXs!4S3F_J@jdpoxb-#RbsBqoCpvXyS=baRoH-OsKd9 zns_l(+yG6y9x85uCVmzg?+$3<SD@k^XyUh_;sI#ly-@Qb(8MQ0#S_rPA41K^Kog$; zRbPN6{tl|X0!{oYRJ;LA{0~&T15JDe)cgr(;!Nhy^p7Ua2^C*}CN2OKUx6lm6l(qk zG;vv|`W<NEbD`=Fpo!~1#ZRD#YeLPrfF`~Os{RI=_#<dIJU|mSg_`pMO<Wmj&IdH{ z<xue-XyU7(;tWQp>Hh{)oC8gKJycu(O+3U968;is;+vuB70|?YLd7-E#1BBl4ba3L zq5ic%6F&k~?|>$L0xIr-CVmDg9)Ko(87dxuCjK58{t0N}*P-e&(8O;;#S75H??c5a z(8M1@#T(GXpF_nv(8PVA;V=PB{54el3^eigQ1Jz5;-8`7E6~KhL&Z0si9djf??4m( z4HZ9tCLRfO=Lt0NCs6el(8N=r>TjTlzksTLfF}M88ZR%<#0{b9KcI>Khl>9|6Q2nc zXD~)h|18k-%z-A(0TmZO6X$`7OQ4A}L;a<ICN2O~uYo2m0u?tv6PJXFTcC-{L&Y7? z#A~7UdZ3AOLB#{m#08<^5oqE+py88%CO#FaJ_AjBHdMR-O<WD?&I&YfO{jPSnz#;B zyaP?#6zZM{XyW=%^)t}K|3K9*Kod8Hs$YR7ZVnaSfF^DO72km-?gSM-fF|w<6+eL{ z?gJISfF|w_6~BQd9t0JCfF>RZ6@P&y9s?EsfF>Rf75{-Io&*(VFhNcKsZenaH1Q0m zxB!~CB{ZBR(8RN$>J`w$=RwtLpotei)f=FRmq5iW(8MdC;tpuy)lhK{H1T?<cmSGs z3sgJ;O}q;#o`5E93w3`6n)pPh`T{iZDNykWH1Ts#a~jaZXF%0=po!0eicdfjcZ8ZV z15JD>RQ&=p@x@T}E6~JOLDg?S6JG}v-+?B+5h{KFO?)d<`~;f#E~xkgH1Pva@f&F3 zu26SAKodU>RsRA_{1jCD1Dg0bsQ3>w@yk$g22<4Ze-kRsfhN8XTD}ROiF-obDS;;b z0BVi`n)nl_xCWZ|bEvohn)qv|xCNT{2dKCMn)o-UxCff}a;W<Q(8P72`$Hqp#NQc1 z+I0zN;(wv$XP}AyfvPV+6K6Mqm{Wl!E(#TIKoggOig%!i%R|K{powpT?lYZ%CSDT( zv3CKQ_zS4|6=>orQ1dsSiEBZ{cc6)zLd6fDiMvC^PoRnWK*cYhiHkwqa|2CW9J){T z0h+jsAH-iT(8NQa=6^sFPlSs9KohTpiZhs@rvEOeI0u@zB(&WnfF?c#s$K$3d?i#| z0Zn{2R9pj1{5n+J08RWgRNMkhybrpL!U0WuE_A=J2b%aVs5t>>;{T!I5oqFU(Da;u zCN2mS&p;Dbgo+oSiR(edE6~JOK+|Ufnz$uYeFvJj8&rG(ns@+Id<L3$6jXcxns_l( zd<B|#4^(^un)o!R_zpDj<xuehXyO~7;wR9=w?f4)powqsf~4mgXyUt|>K~wqAApL# zKoj2xjrR{|;yTcM;y=*D&q2*$Fh@=Qm!RStXyVtQ;sR*mx1r(^XyOl`;tFWu&!FNO zXyRX>;s$8q>?V-#u|N|Sg^D|%iK|1!J<!ArpyB~&;ucWx2sClnxd91i;<ixr8EE2$ zb`W<Kpou#`)mNa2yF$eq(8T?q;vHz>!BFuDXyTDj@fm31Nl@_xXyQds@fB#|wNUX5 zXyQ#!@f~R5-B9rZXyOy0;wR9=r$NOppo!0fir+vJp92+tfF{ll6@P&yE(;a^fF`~G zD*gjad^J>@!2&h?Z-9z(poyn?K;l;bO?(Gby#$*0KB%|?n)nH*xCWZ|6{xrYn)ofK zxCNT{bEvojn)q9&xCff}U1<IaKokD}RUd&S{tYUgfF>RXEl)Dg#D7867odrAm_ov> z0!^G7D&Bx5E&vtpKoggQicdfjSA~ktKoe(%mWK<_#08+@E6~Kvpyq5q6L*7(??4mJ zb%OZo0GfCpRQ(Aw@i3_P1vK$ksQ3*u@j|Hh12pk6sQ3#s@j9sZ2Q=|MsQ3>w@tII@ z220fRzZ5FYfhN8lDlUK~z6UBUfhK+sDz1PgehMnCfhK+)DsF%#eibTifhPVED(-+L z{sSuRfhN8Q+Rh9>6BmHC>mty^g`wgJXyOu3@eDL^8K`&xnz%kxyaG+!5h~t*ChiRt z??4kzhKf%>6VHH(&p;Eego-af6R(4cuRs%Tg^F)L6X&pir1KqU;!~jN51@%JfQp|$ z6JG}vzknvL0X<je2AcRmsQL$J;s>DOFVMt~K*c|xiJyXs|3DMJ2o-0rLQVfSpyC{8 z;`gB90%+n-q2dx~;;*6N3TWb=pyC>6;y<C{2592{q2d;3;%w0VtOJ@jFI3zEO<V*j z9)Kn;1r?7#6IXzWC!mR|LB%uB#C4$J1!&?%Q1J>haSNz;1Dd!URJ;RC+yyE=0ZrTs zDn0{EJOC=b08Km$D!u|uJO(Ph0Zlv!D!v0vJOe6z08Km(Dt-b@yaXzK0ZqILDt-e^ zya6iy08P9ND*ggZyay`&0Zn`oRQv~;_zb8xgEeaUp9dA^KoegA6&FAge`XCSA0*Jk zS3%V)powpUiff>WZ-a^(poyP>id&$GKZ1%opo#y0ihH1m^Fhn805owGsCWdLxF%FQ z0ZrTpDxQHRZU+@FKodXZ4hf$MH1R;F`UW)d2&i}mns_g?e=z|~JQ}Kg2AX&(RD1!N zcnMT|1)6v(RD1)P_-v^74m9x#(Ej@YH1Tav^(WB8PeH{mpou?(ir+vJe+d<TfF}MC zD*ggZ{3lfW1DZIaB_#ZRpow!r#Tjf+)4v2%oC8f<4=OHzCT<B8mp~JDfr=}jiN`|4 zHPFO=K*Q4jO<W=p;(iM>aSN!p1DbdQRNMniyaFm7fF@o9bx#DEcqvpo0ZqIbDxQHR z-UAgcKofrrEgve-#OFfQH=v0xfQomZi7$hSPe2o22^F7#CcYLbz5q>p1~eR2pot%V zif=#@e*qQWfhK+&YW@K<@mo;w6KLWupyC(M#2Kw1@p}VJoC7NU08LyTD*ggZJPwxr z(Zuzk>VKe#TS3JcY*Ev{CsdpRO*{oEE`TOp2^E(>6K{r!E1-!_go<mRi7$YP8=#4A zfr?w8i64QAJD`bQfr@*eiNA!32cU^FLepminz%4jJONEy4Jw|2CT<TEFF+Fyfr?k4 ziJyV?mmAQ;Q=sZQ(8Noi;uFxstsEigX9k*hB~<+aH1Qs&_zE=f8Bp;JXyR+2;ycj9 z*F(h*poz;t$L&v`iQ7Tju@}(9|Jp*_e*;Z?3)K7vXyO7<5cMz6#E(MNe?Swz0u}#( zCjJB}&R~a{{$E1HInc!4LB$2o#6LmBCD6pbLB$o&#D77>HPFPFY#{zJKob{+id&$G z%Rt2)(8M*N;vQ(?W>E0}G;t58cm$ew6jVF`O*{=Mo`EJ_2^B9u6YqzLSD=eS^J4>= z_!SpOICP+iPl1{<0Zn`cRD1@S_+qH|0yOazQ1KOL;^9#74QS%Apo?x97#Mb-iSK}_ zKY%8F04jb0P5c~G`~sTz1E}~7H1S_h@ds$)|DfV8(8M{R`RD_hxFA&g2b#DTRGh&c zHT^3>#W~Q#^`YVdXyVDPkob~76L*5DS3nbYgNkdQi3dQ%4ba46q2d;3;<-?92Q={( zsJI83_zBqfCz|*-Xn7TZCO!pfP6C?vY^Zn!n)q_4cmbODW~g`tn)rUGcmtaFX{dMy zn)o@W_yjcZ%TVzdXyVtP;tSBkA3?=epou?+if=#@H-(-XyaP?V4LY8708RV@)SMG& z;_T4=%LO!XF{t<rG;wLD_yaU?eW>^gG;tHC_y;s`8>sjXH1SV?kaWx7fSUf@pz1l$ z#J!;60%+pVP;m(~aRX>QrGO@02vx6vCf*7aH$W4g2o<+L6JHAzcR&+A0u}c_6MqC1 z4?q()gSJZ}(8R?+7f*xEb3+sV05vBAP5diVyZ}x7A5^>oO`OXf68;Tn;^I*84m5EM zsQ3glaVx0!3^Z{!sQ3aj@kprn3N-NysQ3mn@hYhJ4m5EYBZ&JCpo#ZF)t^8Ue*vx6 zE})5<K-V+eKog${HRl1E_#&wI3p8=q`Q#ta#MeXB|3DMp3KeH?L{0zupyC{8;s>GP z0%+n#q2dx~;<KRjy8@c{NvL`aH1S(daRW5*=TLDAG;uNLIKKm$_%En>4>a*F&~hvQ zP5eJpeFT~~Gjv=!0Zp6-DxQHRE&vrTKoggOidUeC%R$8((8N`s;vHz>>QM0sXyV#X z@fm31R#5Q;XyRT_@fB#|u~6|1XyUn0@f~R54N&m|XyP-V;wR9==R(CVpouSoir+vJ z-vkwZfF^zvD*ggZ`~+0|1Dg0*sQ3>w@f%Qa1}D_?e-|pwfhPV4DlUK~{t_xKfhPVA zDz1PgeiS<YsDUQ_4XWM%O`I9pzq3FS*MXKB4rt;MQ1u>Y;;K;b05owOsCWdLxDix5 z0ZrTyDxQHR?gbSuKogIEidUeC>q6UI4QS%AQ1u;X;wez^325TEQ1KaP;zdyL1!&^c zQ1KOL;vG=&4QS#GQ1KmT;>}R;18CxHQ1KIJ;$2Yj3uxjOp!M$!H1XL`^$*a*mqNu~ zpot%Vihn>8KMxiEfhK+#D$d}Hn*MJ<#W~Q#??c4}(8Rw(#U;?hnV|FI3TWbDP;m`3 zaV@C00h+iKRNMkh+!reDfF>RU756|B4~L2epozyr#Us$fQ=sArXyX2!ka|A@O*|W_ zz5q?U04iRACSDE|Z$J|-g7&96(8P~J!+8Rl_%`Uelo@E^($MzF0yOcX(0<SgH1QUw zdp4kn&xMNbKoegF6+eI`z85Nf0!{oIto?%~eh8}m2AcR$sQ3dk@l#Oo7ii+=q2eFV z#IHcbf1ruqfQmD?pr-%3P;m}4@dr?G0W|ST&~}Ifnz#wHd{aOZcYv1L8ffCHp!I?Q zn)q|5y%uQV0nq-11Dd!c)LsuX@y}3m0?@>NK*b}_#JQXy={x~VJOJwc3^Z|msQLml zaV4mD1)6v*biShjO`H$fZtOr4H-efo0ZrTsDn0{EJOL`c08Km>D!u|uyc{aN0ZqIH zD!v0vyag(L08M;4RQv>*_#CMC1vK%6Q1KgR;@hC&575NVK*e96iQj;Ve?Sv|0u}#( zCjJ#F&ftog{+Xfqlmkti3o0&vCf*6Hza-GaC7|jR(8Oh+;u>h;s!(wQH1Q(n_?rcq zxE55s1Dd!oRNMni+y*KhfF|w;6^}p@_kxNipo#lJ#WT>v1EAssXyRc|@d`BYB&c`; zn)vf@NWSYp6VHRLyPJS64mD>6ns^mdd;ywxEmV93ns@_Ld;^;JQt1AO9cbdsQ1u7U z#Jiy4C(y)iL&YzkiT6XrZ=i`!hl)Qy6JG`ue}N{x2`c^pO?)R*{0Ex&0jM~G8*2JL z4;AM?6Tc3vX9dv3q3R{j#2-M#70|@rK*crC#Q#IZ4ba4;pzQ$*G;t-UxC5HF22|Vw zO<Wf$9)Kop0Tqux6SsqkC!mRYL&Y=D!~>w>1!&@-Q1J>h@ffIh1DbdmRJ;RCJR2%L z0ZqINDn0{Eyc#OL08JcrFWU+<@j9sb4QS##(0Jd0Cf*2De*jIq11f$3O}rN>egRE< zB2@eans_;Me((XB_%x{c7ii*hq2eFV#Fs<Gf1ru)fQmD?qo)5uP;m}4@vBgA0W|UZ zP;m(~@t06>1vK%`P;m`3@xM@U12l0SSox18t^yTzKod8HihH1mJ3++*(8L3w;t^=# z2~hC_H1SlZcm|qy22{KNP23eaj#_~xo()ysfF`aAZ3lLsi5o!0C!mR!L(Q3iCf*Dc zUw|gw4;5d5CcXqJz5z{qJ5+oJn)qp`_yIKW3sCVBXyU%m{>TM1@#|3aH_*giLd74T ziGPENzd#fJ2^If<CjJj9{sT>%1-dSa!2>n@b3(;A(8T$m;sR*mVo-4jG;ukoxB{BE zK2%%-P22%0Zh$841r@hI6Ay-pJD`atLd8AM#OFiLH4Q)${{U^zN1%zfLFY#j(8SZA z=4YUZXF<ga(8O0l`y&--;+ar;8_>iXpyqU-iBEuvPe2o&2Nj=zCcX$Nz5q>p8B}}) zn)r5aNV{tTnz$D<|L#B&Uj;Si0Gjw(sQ3vq@f}d{3uxkdpyD^s#7{uQAE1e!g^IsG z6Tbo#|9~cb6Ds}#O&oR)DT60!`hNgb&w(cX7%DD+CjJI0E`cWg5h|{LCjJvDu7M`b z2CZie(8L9x;udJ)qEK-MG;vv|xCffJ4pck<P23PF9)Tuq0~Jp|6Ze6NXP}8kL&Xcw z#IvB{6=>qsQ1J#d@m{ES2b%aCsQ3gl@ikEK8EE3Wq2deB#Lq&-SD=aCfr@WH6MqX8 z-+?Cn8!CPPO`H=tK6V04TpTKX0Zm*5Dt-e^+!!kU08QKpD*ggZJP0cO0Zlv^D*gja zybvnR;Dwt0>!IQtXyScPaRD^(1yFGbH1Um4aRoH-{ZMfYH1Ts#aRW5*FVOPG0!`cm zI*#XnCVmfUjt81}1$4Y908RWgRDA@R_=aFed6<AE&IsMFnt>+%8(Pm6po#N9)mNa2 z^FzfO(8PtH;vHz>XQAUl6VSxPpz3F!iAzJp7odr&L&aC1iJL*iH=v38K*e{Ui3dW( z51@(1LB&s?i6=wFFQAEMK*evMiB~|yAE1f%K*e96iBE)ze?SwT4Hf@^CcX$N&ftxj z{?|aoIncy6K*a^n#AiU$l?0mj0jRhFn)q(0IT~o<7og$>XyPxR=2)PKzk{lGKoe($ z*5e*%;$l$o05oxRsCWdLxH(ij0ZrT)DxQHR9tzE;1!&@dQ1umP;@hG5w*gH&4642Z zO*|SZJ^@Yqdk`d_&Oj5-f~sGDCSC~@Ux6mx3l-mhCjJuIPTPSdJ{PL~0Gjx6sQ3vq z@zqfA3uxjSpyD^s#J57lAE1fvf{MRD6aNGqhyQ>k&Inz1`U6e;5Y!w7AJp`J3M$Tl zCVmYnE`TQf2r4dtCjJ>Ju7D=a0v*TGKoggOy3+tnoD-_v0!@4sG`%^XiSt0!d!UJn zLB#{m#HFC(5oqFaQ1Jvbab>7@2AViGv|U$#CawilUx6mB4;61f6BmNED>~4`o1p1% z0-Crf)SMY;;<ixn1!&^#Q1KOL;-8`8EgR6p1EA`6poxb-#Sfr~M?%F<poxb-=UFbG ziN`?I-#`;jfr>vs6VHK)zd#eOhKhec6BmNk??2GQCqmUT_@bu&*-&u~H1TCnaRD^( ztx$0ZH1PvaaRoH-qfl`TH1W$&aRW5*t<Zj`1)6v&G`~2Yi9djv<AEmL0Bt`6pou?$ zs*gYue-0H-KohrzmfIO<;=)k#3(&;hLd~f_6K{f!hcuvx|AMOTKoe(!_M<1DiL*lE zcLticFjV~lG;v;NK3ah$E(TS<0Zm*MD!v0vJRDlS9Y7OTgsMM*Cawk*zknvL0~No4 zCT<KBe}E=#0Tq9NCT<HA|9~d$3Kjo>Chi3lXYfN!|KU(^4m9yZsJH-{cp+3=0!_Rc zDz1Pg-Ub!dKog$^6*oW=UjP-iKoeg96?Z@rUk?@cKoggS-j@-8CjJXrk3^t}Z-ttZ zfF^zvDxQHR{tzl&fF}MNDqevm{st=EfF}MQD&B!6&H_!h6VSvtq2e>p#08<^3(&;H zq2eph#08=u?WhfC;_6WKJJ7_fq2dS7#J!>7C(y(rpyC(M#8aT+H_*h3q2dqF#A~7A zFVMuhpyD6U#HT^Uf1rskhl(@!qo)7OP;m}4@k3B?0W|TGP;m(~@iS0y1vK$1P;m`3 z@dD`hwgH;>eW-d1H1S7JaR)T<cTjN;H1W?+@c=aOzfkcAG;smwdeH<ladD`42Aa4k zRJ;I9+#D)ifhKMX6>mTjcY=y{poyP__OB<PiTgm+&p;FRhl(#i6Ay-puRs%5fY!4c z(8R-`>UW@t$3g3_18CwgQ1vI!#1o<77tq8rq2f2t#EYQf575Nxq2e#l#9N@^AJD`* zq2fQ##QUJ)3<0R=e<oC%15KPAIzJ_VCcYG^UII=05L8?NP5dHMTmwz~3RK(xP5dEL z+yYIU7uw%(KofrkRquf&{u?SDfF>>o-NzAuCN2XNPe2n_hl*#Qi5o%13(&;vpyCy1 z;=WMv1~l;isCWmOcra9a0-AUtRD1@ScqUYQ0h)L|RD1=RcokH91DbdzRD1`Tcpp^! z0GjwjsQ3vq@dZ%v3uxjxQ1KgR;(^e0H4o6lmq69OKoegN75{)Hz6C1&15JD<RGc9Y zHT~~}igTceAA*VtpoyP=ic6r0pN5Jnpow3Fiff>W&x4i!XyT8d>MhX3Uqi(m(8T{j z#XZo(d7<T70GhZUR6GJrTmmYdfF@oI?RR9LiAzG=Uw|gA3^k_$P22z~-hd`<0~PN; z6Ay%nPe2n-fr`&S6R&`ZFF+Hog^I606K{lyZ$J}og^KS$6YqqIA3zhI3Kc(rCO!)) zegRE<EmZsln)qg@_yaWYiO_I<fhN8Ks{R9-xG=Pz@&iqLH&i`C5Ni7Of%d03(8Tvc z)eE4BpM;7_poyP@iYuUrUxtcnpo!mriW{Jbe}#@4SfGj5K+AInH1Wq!b3D+*|3KIK z1)zz)g{qH06K8;~H%>qk{{U5=fhPVHDqeslejmEOrUFfz4LWYwfF>>i74JY3e+Ctw zfF>>h6`z47E)5l5fF`a06<>iSt^pO_fF^DL72km-ZVDAYfF^DQ6+eL{9t0J?fF>Re z6~BQd9s?DBfF?c#x*qQZns^FS{RcGhOsMz|H1SfXI72XM`mchDbD)VgLB$2o#AiUo zCD6nlLdP)_(8Om$)oY-MFNBI4poy=6id&$G?}LgvpovRD*U@>Pi64Th4?q(?3l)z* z6Tbu%Pe2pD2Nlmi6E}qB#{x9*hfwtuXyPlO;nRR7{sF4K15KPAIv+OyO<VvfJ_Ai$ z7%IL1O<Wu*z5-302U<RCKoggPs^5Vot^yT5fF|A#6+eL{ZU7a(fF^DV6~BQdZVwfI zfF|w(6@P&yz8Km*|9~c*09F43O*|Va&Jco{{>z}^9BAT=P;mh?@ph=V1e*9{sJH@} z_*$sA2AcRjsJH=|_$8>g1)BIXsJH`~xB+w=!vjtH7gT)!ns@|MeFU1g5VV|0Koi%8 zif5pS`#{AD(8NQa;uUD(F;MXaH1TYxcn6wz3sigpns^^nd<L5M0;u=`H1U;C@fB#| zYoOvA(8Tvb#dn~IAA*V>Koe($&P$#^6F&!4e*sPWGF1Eqn)ofK_yaU?U1+)S0!{o0 zRQ(4u@i$QMA86tqq2dgosOkR~RGb4%{6AD&08N}3I({gDCe8^JS3naNgo<mRiAzGo z4ba3@q2d;3;ucVG2Q+b8sJI83xHD8d08QKjDjtC*?h6%9Kobv!if5pSM?l33(8P10 z?a2x>@mQ$(1~l;usCWmOcsW#j0-AUYRD1@S_(SMC#R4?(W~llVXyTnvcWyuvpAJ>O z15JDmRQv#%_(G`o2{iF#Q1J_B;%lMeH_*g4LB$`SiSL4nzd#e;4;BA_CVm1c{sT?? zEL5B!3^n~<g^F{ai9dyk3!sUAfQn0?iT{L(E1-%0g^FvSiE}~wH3n$n@=$RLG;uAc zxC5HFEmYhCP23YI9)Knu1{IG$6HkJQC!mQJLB%uB#5<wl1!&@4&~{M;ns^^neFK{K zWT<!tn)qy}_yjcZ1yJ!BXyPlO;tSBk*F(iupoup?>%k3Z;wzxyJJ7^;LCraUCVm<! zegaLr0$OfgKodU<9e=)oCcXnI{s2w<GSvJRXyWgn^E)5V#2-V||3DLe4i#qzM@|24 zpyC{8;{TxH0%+ob(EUmhXySTMaRoGSbEvomnz%bu+yG772P$rXCLRhEcR&+QhKhTj ziDyB@1JJ~apyCl|;!RNT1T^susCWjN_;jdv0h;&<sCWgM_$H`$1Dg0&sCWmO_ztM} z1T=ADXn$b_n)p7b`UPm>hoRyt(8M1=>-P<4;^(01cc6*uK=bthH1Ug2^(WB8uR+Bx zpox1x*QMV;6Tb;n{{T(=F;x5on)nWAyXym*_$25)(;sN!$D#EjLj-F2e+M<615Nxh zR9pZ}{6AD&0!^F+YOeyCI4@LO15I2ADsF%#E)Es9Koh?NEgu}v#3w?>VLZ^p??dMc z0?@>zpyo%QiK{`y6VSx9pyC;5;<`}r0yJ?WsCWgMxFuA)0ZrT=D&B!69t9PjfF_;> z6`z47o(&aWfF`~WD!u|uya+130ZqIVD!v0vyb3CQ08P9BDt-b@ycH^b0ZqIEDt-e^ zyc;V108P9PD*ggZd?Hl*1Dg0usQ3>w@p({jhyAJyAi&1Jz`zM&K(PmsI1f||L<Jy; z^MVARI08vr4k`ws5|G3dK>|>mfh4XB6$4QPNa89W0Vu9O65j?D15ph~;sziADDFTK zH-d_Rs0m2og&+Yao`EFp2o(cS3y{R!Kmt&_0!e%>R18FIKoa)=2|)1<B=PM~F%WeC zNjv}~0L3Se#6zHBAnF2=csNJ^if<r^S3t!;)B_~(XpjIDzd#a?hl+tHi35=Iz|O$H zzyuP2Vg)2|W~dm5(m)bt0SQ2{0g^amWd%gS0!bW{<{+XB3=T-*AiH7W9!TP_JHkP7 z0Z8IpAOR?jKoaMMih-yEBym2F02F5+iSt9nKvV&exBy51iYt)B1)*XfssTw{2qXZ- z9Z2HBP%#iS0ZCj0Bml)Tki<oyVjyY(lDHU10E$;2iHk$UK-2~#aS4zB6z@P1mxPLe zr~^piQXl~+K7k}I4HW}X7m&ndKmt&F14&#KDh8q+Ac@O^1fci@lDGm?3`BiE5?2BV zK=BVGaY#yqN*sjnKxqe3qeH|wki^v?LSRw=Nn8Ug0wE-j#5Ey8U{V1|Tnj7$AvBQ0 zwIM=a(f~<Z2P^_1ERe)?Awpo%0ZCjBECL}ski_*NLSQlgN!$=D0wE%h#F5Lr1SD}| zh*B__fh2AM7J(22NaCgtAuw5iByI*4fe;Ny;^q(`Fxi15ZUGj75EGEZEg?c+at4yP z6<7p9EI<;sh6sVl6-eSXU=awh0ZH5zA_OLPAc@<7MIgihByoF)5SToHB<=tffe;sv z#GN2QVDbi%xHDJ;LOehccYz3j$rniCu3!-e@c~KP9U=rKe;|o_f<>UjAw>H3f(n5s z4kU4JkN^}5Ac^}x#Xyt<lDIEO0E!im#QmURAW8#C+#e(W#Rf>?flx6JWq~9f1QLK^ z2PE-es2GUyKoSoH2|#fGl6V+Y3`9jBiAR70pf~|ZJQ6AfqB4-gqd)>sT!17V0~G^N z6-eT-AOR?DKoXCGih-yOByr?=b^?-k0!Seg&p;ASgo=Tv1xVsaAOR>|fh3*`6$4Qo z&2Kn7x>@%=Q(^FEJy62>Vm1>4gGcibj>8~X28REpO3zgo{;R4yS7G3ncVPIh3gTyg zBws%G|NsAgRjKDH3>l0J3=A(XfcZioKB!6X@&K661>%F60xvg!`Ai@_s0r|L0hs^m znF>P&sOo<?0nGmd;)AOEmknV4D-a)4T)!*;^B;lupep`l0+@db#0ND=Uj~5rmq2__ zRsYff%s&O<gR1zK24Man5FgZ3eW?KE?*j2bP1KhHVE!f$A5`_fWB~J5f%u>*|K*2& zApb4`@j+Gn%LicoED#@5#lO4&=1&6gK~?+917Lm^h!3jLUv2>Nn?QU}RsM1Tm|q3r zgR1bC6Tti;5Fb=^zia^Wvp{@MmHn~+%ufRGK~?q31Ta4e#0OQ;F9X2*AP^r^HNSKK z^SwZPP?h}B0L*s+@j+GbO9e3B3d9Fh!7l~Cd?OGa)HHv|0Oo6f_@Juz<%ho@|0{v` zpep#~12A6-#0NFmUtR$7g+P2zRs8Y*n9l{`gPQIyH-Py}AU>$c{&E4B|Lds=Lk6f& zdN~2i{{-TLs^*ssVE!u*A5<m3ECBN#f%u@R_+<i^e+$G1RlzR<!2C-fKB($_=>X=R z0`Wmr?n?tO{}6}|s%l><fcd*Xd{7npQUJ`~1mc6L)|U)m{wfe3RHeTB@CW4oMIb(? z3-R&+m_G}|2UV>vFM#=zKzvY@`tksn-v#1>s?wJm!2Bi<A5?|DTma@*f%u@R^W_9E zzX-$!Rhcgv!2B!_A5>MoECBP9KzvXY`7!~_j{@;QRpZM5Fh2;y2UUqL9l(4q5Fb<( zzBB;yoj`n075GvC%(nvZK~>*N0WjYP#0OP*FB!mmEf616)xG@i8{~f_5Fb><y?g-X zOM&>HuF%U1V7?HD531^39su*XKzvXY_i_W6&jjLws<xL4!2DlNR2VWqRocr5VE!i% zAJm0<*#PFh0`Wmr+sgtl{}G4}>N34d0P}Bw_@FB8WdN9e3B(6gZ7&_b{8J!4s7iZj z0OlV8@j+GDO9e207l;q4!d?o1`I|s|P}TL40nA?o;)ANJmmhwC{J#jq2X(<-J^=G) zf%u@R>*WP7e-elfs<K`l`1Sw4M=$GxDpdxLUfWAmstlnXnm;@`pL#U^;45c}J?zoV z%JL9W@P3N@{{U3Jdi1icgs5MHr2gO!W{=K89tU69doW(`xcH+KtXVW3toc&Or&y29 zWB(t4^f%aMR;e;Dl&X6)+qyqgVPFXL=zLnj<I!y^^hAZ>h3x<T|6gSM|NlSs7^_2- zDg)!^!)YL($1mRkD$^M}nr&y?RbgN#QS<0#z5f8>IH^#NUfZU-DhwW-Pyb(teHs4$ z|9^h@29SoA-v9sqPlIcpz`u>f{BRmVphE=2Mesd(QRVr!vy>yNPwNyx;d}I=%6GGD z2e}w-VW$Wx-=i0-04~1+R5Kk$*x&q~tNA}O|Fi>#)A;3G7#Iu>fQ&=PcYf?N0_$mh zc)$6jWAjhRa*pP23LBU@i&#D%PMgphw!ia3^9P0IUy}Up#|}^Eb=%+kL$Ztms_Ss` zHwCcvPNTzM^PB&9b{dsvHvh{!?!*F0r8_`XV+r3*Pz|<$`8W$#CIbV*=fkk@-wCQ2 z!D?PV2F0i0fyRIT85kJKWjeos4Q|+D4(f1wmT-eu%nS^f{8J7f^g~obT~lu8*!jVU ze_IPXNQ>pM66H=OmYpC6IPq`W!_L6Wz+m~WguU01vD2pOaOVkrd4|u2cY;Ff^*w0( zc7Cu0Th9SDg&E{jl=K3zubiv%8~?T~<{%THc0<D#;{QYf{^#G;pUK3)V0o^DwfU!K zsS^LTeov6&&Xw>r|I94m^ho~De1Nf&XA{Vy;B@Dce4zONqf;kO)9Xv1a6$^V)oDoS zFEqIG_rcc!pk_|<?|?D^!vmnkO?2#GsQ)~=Ss&h2Vc_5XzVlY+_x~anEMJr;x85$j z-uzas`7cNLd5>Pv2o)v<k7S-z9*n0znG9?{I4)jqp3wY)vH1rR|Fi?ImxJTG^WZLq z0tSZ5zdA28KVW=43B+e$;D_;>!2BNuQ2xt&a87bzU;v5p!Sz9N7)1O4BSPFBNj<2H zhMA*|B+l5t4zvFyKTN#!0RPkj;1&%i`Vi^e@PLQqc94%eI)BwO9i9LRD39*?ivN=e zgc%r`f7F-pLV^q$pPk3{GctHHyjb$@|NoW)C66KDa4!v(ew%;QcYZJ9Ol$qdKjpyT zv<aQpURM75{~s>j`MvYn%k+Qn@&mv9V7oexy)XmY#y=Gtd7umn&)>c7-<p3gmq|4K zZ)IR$C=qCUuc5%e(EOhfl*Rew8NlvC%Kx38z#`3$?l=GPEa7Q<XTZq7(EJai9F(>g zAn7#_kzPww8~=gS9CzXX<<y;^mg5dkCM^v)&cbEL$iQ$Ioc=)K$DKGpO2N_yNl@rA z9Cu;?X@{u*wTC^DPc$E3^f>sC389vu@gK;@<4!yvgG%o}i)Tc9S)KxiE8PE%;U1kA z9K$?2zd43FhJ*%tG{2E>wQ!5|=ngON=q|7D=w6>7%)sE&y*xvhfx(e~%2AKzUrZjI zB`O^JE&CZjZT(*?C5ax*zZgq|J(`b5M8`VDIL120ImUzi<<Tp{=F@A#=VST1i08H3 z1dry!j6RmXOGG@o-2(Wx3Hx+D?>6`!%HU{mwB)fz=M4{S7Zna{=5*JnaCr2}wEOhh z^n(oJf2{xtH;?ASES{G4%Y;GZb)y*f1e<Xl2Y)a*#)A?gw7%*t0h{aQ(`(}gF;@|4 zE)&RHhV7>w1O9v6Q&#vh(Q$M7uYc`ZM|qxm&BJVinN=ie6mA2~oDVG99p#4vcE zg=abcYpB0pgTk{I?C<X2Kgh;m_ctU!VfohsEj-KlUn@bv^ED_uiy`jZ9{dm4V65(h z1gJ;rC6CUZbryyPJgld8FfcIGvx3uAut(=-P&wn#TlD|`L{M4d(H$IM&BQ<DfQ55x zH2;(X{M$g~NAvy-A`A?Sk_Va}d_2rA&k!2y(fJ!(NVQ(76GE6O4=vW=>cRZx2OmKt zy$b__N9R|#b(101l?HfpcY`uV=OK^o-JtBzZT4T#!ZntE3b>q!=HGT87F5_E*C$Z_ zI3R4s?H}&|YeyFTX@{&C`6nMxJY;ylA{gv5kU!w=1o;c8zVhg1_vsF>@NK<RlIYXj zt|838VEE1OB)t6e=se-m-9H1AaSuLV@nm)o@aPWU@aSgn><lsF@a$wU^z3$!@aPsf z;nCd=R@Quw(W5iOK)|E<n1PSw#nQc=-3%U`V3R)|p5W2lK0%m)!K2$Fpx2GnV@9_~ zqf57=g5d!V=IJ{?1&8IKa&4dFW6j4HJ(9n5i){3=oN|1IFatxes7H7E50F++A!gmq zAi}`FKkWdtz(SNyVUC^G97BDQUpj_(cK!+V==>Vo9cR*c??uypa1A2V{F|{vvH3R> zf6EC528Pz#B{qh)J8!^>Ay_@#P;0?Z^2~=hm&4b3jS46s)x>~U6F^BS$fq-x<Mk;} zI`!#%;lb?6;c49hQN#seffUuHI(AP{0VO8S?j99TncRAbzXdcU)BKCEw9mux#Ey@k z%F_SF>qdTg2FLC>P{l0}#kWghntwC$x5oef|KISor{z(Y@|XUg7BD>jbh>hY+~L~t ztwh*|F;`+Ys5$jg8)P)7z3I{I$^aJf0L6kU!%IQ141;g$TZn-kH(ve&SC7#0?cfjl z&cn@*KfZhcszxWEhoTL*W-I}RqBJ-ZXF)<S3>J!zb|^Uf-gLVvFuST$a9X=+@b`5x zFu=X(F~hTa3e2O`&A%B-4;bF|u)F|qlmCs^n_=yxWLJgGL(HKn6|B~w8g&Vd-7Vm7 z@<_e}57z@FiOoM4OB*~aPr>wq$8JF3;*spC0P#Pgs{|yP!R0}xtIA8C|NsAcv>t%y z@woBQ=KufyouMiojIJo52#PxfkAuJLJ1>L6MGO@Di1db*%4Gf`QW-tMryC`B60rwQ z6exHak%OlYoC;hR7`mas!|1954IM~71yA@ey6S*K^ra3c{NUjt2MU)79^LK<9?b_B zVfo0T^M^+>JBSNSU)1JzhYNsPSRSpHO2j?7-6cG_IXrq<f;<jBVDVsf%<$-Tc<>pi z{w83*W0*&;=x+%o22k?`RDc-11yvp0&JimV_*><e7#JF=V-oq>=P)ub@Ne^u@kr+B z@?gC9{~<KJcD`snq0s3Z(fI<YL;=+Y-Nrth&tJ?11zC5of@9}V5V!CD|NoAbM|XjS z_Z<11k2e1h;CDJ?`GVi+Xy>8kQwp8I5pZK5@#4|#9N+;q%cVPm<ApESyx@T59|GN6 zpe*%(fq}uJSEQ?(i@zlS)YLr5+{xwG{P#b9iz6cg!!A%au=y_szw_1RA1vTb48QY< z&I^_o`JE49SjjKX0I}=E(|`Z}dm!9>?ce|ZE|xDG`JIlw^aI5^JU@7JI|npZaQt`V zpL&$PrH=tr)E6uGbUt_72eSIb9I!#b0jRER=3;FAAyC5H%*Eu|^0vea$(b&e9~}9e zj#*ydcRJJ^9KnemEYNWG=$76K3dNT};KGc-qg(o=`@jGHcY&JD&3{?=ov*&$g7A+h z*clLqbsl@c2Id9_I5z)73rGHz<p2Nwzl?_E7bNpu?)vxtfAdMkPOg`;L1iVlKi2#} z<Ruf>3DET3`XAJSDAjsx0G97O)NqdR+YzSD;D}N_kM3#(kJkSstS_#D<5Ak9^>ztI zw{yg|Ba9_iqe1nyM>oI6ad*)0D}zVtNsmr{4v$WMff6nLZH_TEDWxhNpk6dMeKGKF z3y5j>l~VGh;a5z_dk@Ql9-ZzSC7hoR+r*UI@aR0zP!Z!lq5K2?HjfyOUeWpD;7(>2 zxRdG8E#T1^F5uDa;Na2eE^!#q{%`*8UvBNu%ep~=iJ|kqN3zHc55^1sA0XNX$HAlS z@bLt2UPRK5KHlNc&FYw;%HYw=04ft47(o38kIwHNy{2sXObi~qJ}Mj@osSF;yeNMC z|9|Hp!vn3CN|Zot#BLjv7xO>-|8Mxsqt{l;kcpw?fCK;3V}=J_%zX9#Kd1))F4sMJ zZFvou81{jp-SEK6+E@SogF2vk@cOd5A5@upbWaBr>xKuweU@I^`FczYF9Tlv{~zkn z`PZY@_Kpz~gW&;i@8*Ri$o$U3h6g~gBk$39&!hAGi!Cof8G_rRH}F5Wi6&ek`u)Pe z7ocXLM06}tdD9)t(dnYX<I#Ele~>}9iwe){>&Wf9#$TY}j$r=n_sUf|KOB4^(EKBy zT*{-Dw_XAiKP-(Nj0gWeM8rqyZT=|-q7mkU+6W*4nEwn9H2<pSpLW3VFj(D$=6`DZ z?JkU<LV>kFl8K@7n@2Lw1`oy)py5AIfB*W6PC+IHaCe=58%!I*zRv3}UJ5WVAjW5| zzla3!&q4ED=k*sS`I#7A??do!{$gZ!y$;H6{uK{S5+zGJFY#|@2?q^Q%;J}4X#S<Z zKjlF4FHZg`hd3`FZ0@|ozs)6_vH1Zb)CW*=AiC2IzTiZba$taqvtkv$_ySoN<X^BW z(hk00LzX}?hXo<tc?s<FZ!F<V{H-1T{{L_M{r^7$12|w2at$Rfq7NguEG0LQxf~^@ z5L}mVMy8To2tG@=LdoiAP?H~?K0&GMTb(wj%<yP_#|Y{}{D<}*z+HyUL;JwvAxHlH z{~z4~8e{>tfZ_Uskp?(^*K>l(5KuyAz-dr5xWU3d^#Dkz1A}7-xXJ9%`TGT^jo{Jw z&ZG0bW9N|<i^0uj{warCTb_VaD;|pNyx<rEay)W+M$&HxD%0TlLB%M@y<q)Q4nz~y z{}EIu!}Tlw|NlSQ(c(xf|I`DHG4S%{<?cVA91iz?=ZDVk%@6K3|L`mk>1_kekTw5c zEaUX(y!O&ifQey3rxiF(;rctjgQVE_nHZq;Yx5h97vG=$|8M&WGzuDE`+|Xiq1dwd zM}CP(xm5Fy)Dqq1ADJZ@%|8-Ll%XS4pmt{SkJ1w1qIbHW`nUN9Q~48Ux&_+?>KlTx ztP2B!?H7=-lD3~f#y--0!vGreVk)~~cmUMobzso_2^BeJc;NNIv<cubmd@WVL|**= z@6mbm#hXX}|9f^GdGY1>|NlO{&7g5s!*AevEtG+OJC9N4skDQyn42Flc3y8j&-Cpm z3%~1i5R>KGK_-6J>yDil9h*;bd^^hF$nSa)!V^eqJ?Y5r0p%%tJIaKRbNF`D0mf@M z+3@Wk3zV1U*m#nG@!L@a5WnFmV+nJ^Ii|Eu5o6D85f#to1B~4wMUEW-|2?_|x<!gS znvXF#c1!>jbsmlXK#t~b^#Vm`EHnkd;}0|*0;&y!G?^G2!$6I&&|r_oHy$hu3@IMn zEUKW1IFHU26$=*7SURk}c;Wi=|Nqtl{4L$!1_z6ZPp|2w|0)bVy)`NZD-`%!mNGCf z`1IzeD1fRGpU(e2ov$4Khb4F<{{W@!<qyFf0guk3CBBBYU0a{jE5BUKz`y_sS5R?( z$fNT;XdvoE-xE-K?GAq*Xz<UmyJRkCqGhfYsIGV+^aN~3=~<8FTn0w|mPHH<42Ew# zx_iKGP4O`1fja&Lf9q6`GaHY9d<ARYfrnWf7*??O^qPK$Ii0`7{r~^}Aouo~CjD1o zc=7h{|Nk$I!Hp4U`=XmgMODdxfx*KTM3(k@^oFPiKz-@>Kdhkn2P1!987M1un}#0; zO{u(p@%_>N|1XpN|Noyh!K2%i!=v;4{{RDUzw!moBT#lil>gxT{5KfftnaO0eDUWI z*q5H2M@kAjx<frYx=S5AdTkTb!C}Dn7%W@j=h^9^B4K#jqq#PLp;YR1Afmm{e3;R* z)8&qYhh=O+nWaZBPqas`ts+>v$H9jz9?ZTO9^HW)9^Hm74nXWGJ?+`7qrzCC5*=#@ z$@(q~44%y_DvUmzsRcfr4?sZ(>u-2=yQpyZbUyzdVDS1ps9znrAGCM?oQ;~_XuPn2 zm<ft%f5Y28-MY%hK*=OUMdr06C?7+rcW`7M1SxI)ol%nP$iF=#mSG>LXYJGZyfZ{a z=Eax$V4r$)9x6Qpj_?vukLDv9hL=E&0S%GD$ALiOlkh<H=ner7S85=X!OEQ@8Xldm z5W>*%0}=il9-W`RHLPY}U=ZMMy$VXt-3u0g($R4@@InIL)+hWe^BEWzKtZ4YT3k_V zc<KLz)&mvU9^JynoGTf9x|0RKjg{a8{uXCO1_qFpj$I%#I^Vze1j+>6p&Z|uKz4BO zw|)efWqE|Z1ylq!R97<axBdgE?Pk4yM1{el*Yuz&6N5*$#fumB|Nl2U@Y)U(exA)X zDhwskAU6nr+#qQ94V1htc!1JjOC3c2e29K~sQwjT{UEP{#)sS`JUZPHJi6TlJdV2+ zfZB1#+$tE4xm7ZFbh>5i07U{=DJ0xL?keH%=)C{p&^=I#jMW)pHV@QnpZov+Bd42V z&Xo)vy}=1!XF2{CQSt0N^70F~!49b>JDoE;jyo5CtO46~%(;RAyz&ELAIPSNd;kBx z{0i2?0OET<Y(97x94gJA*_&>Q7nkmVLgl3@D4QYWC(x*Q=T}gf25yITKJ)B+`a%pc zs-or5?V?iP(fosnzoi)zRLwtFN^~JX2jVx>sDNSttg-P3n1VFFK>^|jN^uN6y{ev% zK_f;U%{3|+3=aI;-qy)N%dzenl?2ECS1gZ~Yy%~556c*pjQVF_LrdL3R%duXOobcg z05b37qyPWm?Kz)b)lLw99yERWbiRM#argiKm)YP#i2=m7xcmSA>-CWEg0v=@-$=Yr zg5{XQC0^iW#qk&VcR^8x+=vzcHRRFT&mNtiJOpk)gIoz6?uU#s86J3j2D$wKwhuaD z+iZI`T9tu;zr~-CfuXmWxA{L0f4ecHEzH0VYF<NoGobM4HT6;kC1=)82US3+8dPUT z$G+sb3z|A|U;vksy{2FFKxzUcJi1wLA=DWjh>m^v=svtW?KNErRrmTDG@LwoO?N7R z^xAqwgStOQIHF@;uD=5^9Fp#OO#{JtSep*2Fube=hbE|B*K2wUtdDgQNF3CP0{0_5 zTHiu)JJexobU}s(NPvrVh=012m>6F7A?#nE!~`=K)b9eh05tywqF-LU4RQ&@K6QwF zpAM)ny!;GK<q&a2P#@_wSik|4{jV#6I&8Mr6qy)afcw*(ta(wY3@;}^I>WH~iFM-v zaK3N?yU+p@uHf+6bPp6>jv*ec-$H}=J3fQbyLEYjN9S+;_GchY>o@+ES`ec<Jj0`x zr3^fx)0@L|!K1rY;Kk>g-~t#_5e0cP*9tK3w}6IS4R5~|2bV+LJ}MF(oh&yYDLq!8 ztkk3Prbn;sHU&^k1+8i{U{y`%9k6MoCm>Z#B*=xJrpbi;pn~8<8^W@u9^Jtn9^J*D zS}zN1)(Z)U=90sn-60@LnGkgnLv*Z9=L4knDyU$D)=!29UeB6f_-+4p&>9IBP<tLy zzoGP3;o<Wb6cVuTxd!62ek-Z;z#2B!Z-9LR3L9@|*z|x}Be!1*dtwWhTOPf(px87# z;BoK~iwAS4hDUc1#|wwsU}IqQZwNF{!08VXE_n!x9(i;ZdwBHP_A7$I_XP_?Z^;qN zFaf7`Sh%2u$9#BrnEZj4+mwgL@#|oZfWpIz*zh<Gj$1@{7`-sO1vaJh3?z+&LW2WR z&OyQ>4q?$lXd0;poAcreBn4qhBY47N9y~l$e&Yy_P|wc4(AEtHXe`Oj@U}<mH_y&5 z9-V(n#3y)k*J^;XK=V<97yZ}&|M#(cT>1)>34J=>L(2J=71u$TADUmALFEBxN)}SS zI-;BJX80Cn{_hfn2_D_m0v^pr89ka0XS~n{dB3~*!V5L9MNdl~qFTfcwg~KBkOahf za1RpR{s))euqn50+uTno3@=uIjO{gb`~+&f9)F>E4IB@kT0cX;qccJRViv@`9=+|L zE|^F6bP3R0(ed^lprw(Z>~g$a0JJI+#O!Qm07-VYD}X4_j3Ib>%A>m-G$rKG*}em! z>Hq@+Ln+5=6;OE!nzU;IO*(nB9w`0j(FrxbNdmOA5~RGF-J`j}gP|nI@Dj@W*#!^g z_6;Bfpoy{L4xmv`29M(o5}?JN9-Ro;Lm3{&9YE{+K(gSaq#oVu9+n;+<-0wacL;#W zp^`w5+2H&K(fI-7ew^k)WDf;kHMav~V)+h_<_Zgj5@*AchL@n`p73CXPUIb8^k_b4 z;Bg$hF4g0>0|(g2#~nc9tRP2%*Rz5gX&qoubj+i92PnK4N~|D;LDN$w$UFrR(8NF3 zJPnXRpp{(kFaY^-0!VB5rG_0EV1N309B-cii>>4B3lPi|2<8R^^8kW*0>QihWf~sv zINlDLT!dH)o<#&PkG1auMc%RY10V`yC5UEVU`Xq1-y#BDXaSo41+7@NwmrO4g`vdI z!`fDAmkMM?twiL79VjU_{{Z#OOH@Hs^3=ni#0Z*6f%f-7eV`*8U;%LWzW99=l6o(` zNWA+0|Ag1)LB$(V`w+CIqT8Lrqq|`R0|UcKP;<XD2}E&}3WGAt%Uf5F7r=S6e)H(| z7vS%h3`$~_=R7(;d+__;^z6Lj(Rzu$qZcfC%7fqUlt<^;7wbTlcfRxLwNdcUyazV! z^d(T{XMmbhVebJq!=qORG+oKy(R!&O!ow15h-c@S7o1l?UD9VBy*3&!L!Mm%FS!A& zZ+mNa`}J9be_Rcp7~TfY#zD$S!?!O%ol$sy(ADtC>$OPp(cm6GXwnu`26TQ0&Ah)@ zaS1fQ`{O@<OB={L-N6zb-CQ2s!Jy7eNtj2kNRUT&VFtLUZav`98zOdrzas|J%<C=^ z@UT3_-xm+!faimpG(5TuAT!vG3Lf2+(6R$m-g#&q^3Xi?vKrjL0WD7F@AwX~uJr(a zs~pIH?m`ET?m&;%M?me17sgk>@yOq@j)8&U#nj87;hFa@bU~ucKmPN#hJtE!J`fi) zE&-bRgv5U~NM&~@hevl4Jc9XKL1h*=lKERe14N(^W~M9u{~KNc^;3?&SO$u>)&riM zNBCPVfVzI2&tDw63>pbK>e<br;?Wxb8in@k6j9-C0d)<b?l}aSoo>Cv-#Q1>l><!z zweI`(|No0}kQ-VLlqPs|JA>!Tx}8CtwNhb^<`bZ$Q=s-<^8rTi@-2_-0|p-5ff*jX zAxsyL<UwJ}_<_F@H1z>0AG=uvw}abxzMz3?Q2*y5DAtZ}K&nYc(CX7t0gvul3ByY- z+%A9v59FU#&?<QkaDcG3Zv!>GgCsn9O`m`p;}(VoUUXgg|No`u|NsAynhGzaVeMnk zcs+QS-lMx-z@zm)f6Hl5q;<O{cyz}acyxzafO?_TKHa_<KD{Cio(CVX_%fbCq)*Ro zMvvA573?0ChducHZ+Lb)azOcqJotSscy<>Gc(i^iG4$+q)bQyBty|zQJisr{;0w~^ zV|mz<-~Wb3>q(FSr#$$5E`sJhJuE+z-uG-iXy9vkv2>nC=e-wdf584NG4kjRl>p6D z@_Te!dGz}K2hZtnl~{UohYEOfx=MI-#wvI;YcZBcc^r3zl(XHe3%06&CrqGyBT)0# zquZSWG{kWe6rhm)QFl1U{~!Z!2kG?&c>V(CCr|)!c(i`w?@$FLWN<6;FMqoPhy!Y6 zPX`AXqLuB_o5FO#v%5w`;6=|lu**F=kCw!GHrJ?t+S#Dm(eU<bX;41!?DmB>vpp?i zR0R0jW`a!Xyy?^JssMJ~!G|oK%swixwziMvAs@|SFNA-C4KCg12^uadDTj=uf{G-^ zSfu$QNV|#+;fm`XU~{1(Pu;Z=kbL{%Flg~}>w%IDo}eJ^WCE4LKCoF>P+<(MkD8Bg zAPG<K>~`hw0grzf9(Y*=PF3Lc39Mbd<|A4v1{c?e^z$<MJZQiS-hKg%lnH>!uGRx3 z!l1IIHw=_DJvs&WTR;Ybb6o3z5`Gl9k_eCPU=47@+j(?XXMh@VtS=s%1$B>|3qUDY z3Oub1ooo^)<@V^VE`TNikIwtiFRz>f72}|OYPAMb{Pk9FzO0sj@n?hi^#V{nxKxHr zG`tb;=w@%&u@_YTmFjqOet(eywwoC$R>JvWGAPS-I)ggj0!Z^u9^LgE;MILEl)ySl zR5)JmM=sAHO9f$#)NcKTy%1aKe7aM?Lsj4ms%Ll`Qa^a~vP6U1tKj0g6}+OTyF%l| zl{1h6z{11wM)@s|?!91{UJ(bM&TpWS|3&jTP_6X+#cHrZR|bU3Z=MGgl&&0*wB;BF znJ4V7=XjA1R-4NJ4he|;i1l8G0MUN&?fn1$oku;ok;0?-JgCG6hX)5JnSqiRsN4jZ zD}Ltxf6($E7Y5iEwnryu40V48sJ#ZB@D%|CXZPM0LJSNZt^Z58Un@cE_vpOu(LJ9- zh=Jk7!_%Oox0XYgfx*F}^>*nKkM6zTMlD#q6pDHg28I_KLF!v?m%j1no(pc+9&ZIH z2eouSVGS~Z=d~_$Jiw>(HK^(C)1A%nq5`C`yISH!K8VqI|3&)g|Nnhk-<IC;=$>l; zcJl2~&ev+7{O;5F+OxYv#RIPS_GeHy+<##V)pO6Idv5?(&;JrGkWz~m5ugA6_iWyy z!T@U9@wa3$g2pIIR4kye_`(7z&jFDKEl34v^zi833u-vOr~p~fJy!tKh-|%G%KzF5 zk^i9XvGC}wmH>O^^(Rn5fw)lNg)K;H=lvH`PeFWi!SFW7C7{+OLy0ChKTHKTPJ3Bq zgOgn^%OQ{M3V|0Xr~d!<w49>?QVC9PU{&C#^E~zczfbp8Q0v;a^;?OHZ}%D%P-EKF z@QF|7XW!PhC7KBLfx^kN`3R_g6!3!o6evSizj(n0a*pMj(uY2s-(UPZ2@(xu0B8Sl za8(GZ0b(2WZU^T_9;AfJ5be=hD-a86uQ<lNJbx0D=|RN`gGYBOD6Bj?TU0>7`0@<6 z83QU0z<CHH4hsI4Q{dvY9ME#$^)zre)G|Q%FDt>tAf&tmmk+)55-*JZf?OFa0CFX` z_<<E8;NmCr6v!nm3=DAf%y`wS!qh|R@6OMlhF(LxAcIG@8%K!~Y@EC0DX1=KW@hy0 zt_9_j5<gIFYH<oQ+0%Ix+URZ8X5?>K0#<kgk`?*o86YNblpX>VX6~SL4Qhtn1gQcU z)p;0{>o$2b*K#oMw}1{l*bmyK@M7+9aINIg>B>-I0*(sM>c&n1&@wuY<F4S&8)#C; zLBgXm0#p`4EJVcjUeF#Uk7jMglF-ighTmS~yar`rgu6Vtok643{{sv_6%@GO0JjT4 z&RhUW9-!uCBxt&(^WOf?|NsBr3nE^qzW)FJr3^?IuK)FXP=562jO8$V`;rkP)p>9K z7myAR@#6EV|NmdU`Uk3<kjF>C>-ju-ZC~(%hG{}UEdzlU;h+@QdcdReaEZG|=kXWM zK@MrwW(3WWxOud`Eiv@yd<R;uwC@Bc5%@B|mpz0sl$d%n9})O|Av*Tp3wiKB7bHD4 z9}$4G{Gk1EguB;)-Q67uT^I2lwk~2jNF3Z=@#y?(_#fmni5EP_pb>JoMAD<zl!K3n z0p!Bad!S`@rJCT776!K+pexDY<uELqUq1wy3!bOyy#L}4WTgGLN3ZD(UXWS$UvPrW zD%s-EUFrcE&c6v!vc#j;bO%()v%4VcN+#lHd$@lBI}+5KZOH=1#PJu4Pl5_P9Ti5; z&ZGP->%p@vZ~0q5+n0R6liDu|kN^Mg*m=aW+m!*{fDdKhZ<)-<z~I{Ygug=`WK?r) z1p|NU8BoG)-U|*|{?>gE9%ur9fghaj8}@<*KN(8IJ$g-*ppG^KJDR`c2526=*OU_~ zECm%l2ob)*10KbF4Nea)d|~PTD1XZWkOI*3=kXWIj)SVuBj8*B>1%j&hBB0Lfd`+# zttXFeW{@X*Iv;>%n1uOTGeO$BgE@RUQ$TBaK+9jijZ^TpQIB5JG?2xe_g{3~0fjAp ziy>HjX$2?>!XS$hT4X_Dy{39lrSVXukmXuiL7@RstpQd29#rJ?nleCD8$wmz010<* z1%(z!^{+n=`woJHdrd!ZLs9`VRQ0OA|Np;S`wvu!z-JO)I)eGImg37ea3Kn^9<;6^ z;^p$=pwbV_cMONjT6=c>G5inCUKTGr4ug{iD0_j@@I<hU$6rjm4RT1SE;tQ~gVV4D ztf+@<IRLjO;OY1-C>=vu<!<n3Dmm!Uc?{$&<)au`lfR{mk%6K0K*d~-US02Hpm|PH zljSN5u7+=4he6u`9=)b~VE5dA!3cH_e@hN1+4Y(-K!so30y&ev1=OMS=q~*Mvg{gE z%n`JH5j^^LRKUmbYl)#pvMYmUtBZ<6$s5<!w<QZbdQJCpK>~OYSWoHFmpq^uG1z(~ zkK-;X;MHxQsd^2c-VzlB&^*1wYd%Q(ru6`S2WZX#<nIih&YwP=pL{ysy*PRlR0Z(2 ztOezSUQ=GE9d=+l_*+(j(oJ`XiUP>7oJYWFJ3v*RN3ZD{PA2fIoe)%QEm&=hipGl% zpdbVlgGc#WKwE~OA$t-OecdR99)D{JD3Bm!7<m31yta$uC1^b+q&&xzEB^QY{{)X- z(>kaFCf<Yu>>o%%^acs{+9vUU3g#C_K$b$?avP)ok`ZN(V9SUl`k2{o9Y_<l>^BuG z5ejwn>!aWf4ZQyXDt}!KpFpZ*(8yQweo#xBp;Y>{4=DY4blwLwXI{7*{Quv_aw$lz z%-Ey56*Lp=)h+YDqq{obh5Et&|Gzm$GL>8et#EuHaS)U=t2JKmAN>FS^<HTB>;-9g z@#g?Y4${v10AYj1U|&3eutDP@FK&R@rJ(jAs15Ui1HAUG6x2QhwRK)ZfcVX|3Jjnj z;rA~x!OC4hBO0JC7zezv65wyq1$BA5T_ZqUDq)l^6@LpOL@oiF+*#0|OK+-xXXjhb z?%E2E*8e3@9?i7{3?+Vsw_i&j>VL3o^I=90%h-Z4Bad#D`yRbKcNsv*wDmyAQ<Pp` z=_Su@*9wnr8+c3X;Di^i_k-$<qa41LPfMqGbcbeu8eAm~9^F&H1z58dBWT^|UN8qT z2;kAZ_XlVUYU6%LQ^Ny3u)qHRyj=3=uFZh9??4NU7(BXbL38Y&L4=o}rB(3!*j*a| zRrm5eIA_$t8hS4e|N9Rr(jbjBkJfK>THwW!W&gp8%cg>MxitUaEm3KH!PWc&wC5Gn z!~t)8gv>YY0JZO7eCxTO^0M~12Pil@@BN2r=i;Aw;Pr7tc%sY)Kz0t+3xf@U8xArD zR1Q}_7RojM;3|<scLIDo#H00X-2so@(*GXa)o=~GAWPSS(<NyA-K9EdkIs*v7Aa`A zkVm)ue?id3L>_Pv%>mlB=fD8lmk4T~v>YgT0(R2t`=Iq&pshc>+d-k>(fJD69<Bhd zp94>OgICjiJqFJE7^~@U>j%yJBJ^*C=%0$dnhv*qP$VPtmmWh|O@|0?i2qg5{SVt1 zDSH&`Zpg++(Eeob`joF8nEu@iDj>lP9ncD^R$TVs)L)FE-<4$lf)+_3{L2c_k0rdr zJv;xy_cJ<nUIEwH8sOpZ^`JPc<MglwZ&<By@r3x&v$wv&r}NW`i~m3)4&Qt_|92jK zvE=Cg|1K5}_@^9jv3Sfs^^oEP&~hN8>Kt0%diKUavddME&U>ES^%b2iDgytng2vh$ zJCD9Ndw`L_rSm;##qf)R2N)TeYgBkx`CH_e7#Nyguz(hZv>jq(U;wQnZvMf>-?Ehv zD#6I#wh}G@T2$NogPniM0ggi*Au4kGtw%v+TkGvgC6IyK$6ZuFJxd1uDK0AVuVcXF zdh=^WkLKV1OVmJ)^60$x|7z!Pkc+HAuK2;(>7pW1a;y0Vd&yOBK54E|5#a`{;H^>N z;qGuzkpr96a)7_pi;;oBvH7n$e@hFfqu+dt(H9zk5-$yy7@+GcJwf7-{9GpoveKvX z8_3ERr}r~5G=uGvfZEqkqaw$^-wGblZ+^|l-|-#f=^7OgPDlQ!$6oV;-2>X!2zI{| z$S|n;U(|sOV=s9K+8%YM`3Ls|{ua;z5RmKn`M3FioPCJnVhJ}WSorx{<}olZv|QqE z-3?xZ$<N<n&Il0*g<8o4^)eU#ltUoTg6f}^lcn4(2TIvNh6;cw9)Xf@2v4G+Mn#^X z6f|<m!(Zama;ek~(ynpod<V``FHRr)|G&FLMF7m}Jp5w!!T<kXZu$$GPXX)aZ&?j) zaf0So4q#7L%|BT9TW|gaEjfC97pln$v~}#j>vJBhxA{9jtq)Ktdm(idw6GprUbS4} zZ;Af*|3CO76L6Y(@VEH~4}UADsnlGfBEiky+W8M`W@mSZib(ULzs<jR_*?h>`~M$m z$0o4!%XHA{;7j~1pd)Jx54?={|Nnoti;76|3trH=ulAjwof0)FJpBBv>mVA;z+(&! zkdzEn1)53#*~-t~3Obs`@BsLP99RSK<rlE;z^BY0l~<QuT7p&p!|JbHpgQIyXuKT~ z&Y<x;@ZbXO`pTv^;9K)A<}w~z(5lW-QE+<N0Wt?(#W*nNg13490PWj`ibBlyfYxK@ zvDIVy(<YEte`!LUbPepJlJl6h!~6@X&o*GK&*p>u5enLX1=<83+<6+Z*W%HCf#x5K zbt;xIDl8=ipyi?*%`ccddRdxYbEi%4Xg<VZ8TF?`+{5xv=_k;P1E?1d>QqC<gFqtS zuz}X&mZwU1JerRuyq*RL53n54_&q56skDDKG{B>KGickaBiIs;?%klxwB16M7fSd* zPUt-Nf)CUm={#U~zy)brEoe_IqJ0|z4Ulu-f?l9Iip8Z{$)ocT=%|r^gw_LfI^7~F zmY0h?J$ge~nqM$_^zt;l7KXPUx|zTsbhuc|!}3(=N6^>@D1bm~g}~wMXnCakDM%FT zMo{^G*wOMz`KH&eA>jjB-%R%Y`QxBwWb-eUl1w7^&nv?B&x`YK<M!+h3-IZD2AW(1 ztzLAocv12I$Bu8ff6@1S7xBMVhSV33ee_}=L%X@5#?Axn3$u7p@(`P`h^_Q4pj}}Y z`{-4m=7RQpmx=RlM|Q6()L^Xch3x#Md-#^~yjFtmr<dU0=KUWO7SNEG@)y(|cu{g6 zTS$P{dPDOQ=DvDpI6(H*i+6kfK{D1BYAp6}fNibE629g9uT>%83)xpM!N1-6ACkdO z{@`&hB!I!`n-=-G^B^=Yzli(u|GyQs4e^Nl8|Kk^3DijiIplJQy=Uhi&(5!oo#zbS zg5BrQdCR5qqYq@b5_F1I=d0#Vj8@LE9^KOXF0Vk7Aa|lWUqDCD4;#LH{SnjwhMUj7 z%_+vS^JDW19vi!IAD>=U?x&0l{M!zACbKknG9GXQZ+!!YZ;=ORVp!9oJ1D@Ci@#+y zXo1~<*8<RXo+VEce@i<!zaC%&O$fg;Ja8C1&h8Xo$pJo71LEH9AcdEpd=1s-0pg2; zHkZmn`!zudFWJGBC7h2u|KZVH6yVX#-g(NW^L)42{|6xFSbMSXPdi}E#XtFw;ssFA z?ZN;%mt+ETJ(p+am(b3$!5*EL!QGtJOQn3TO~C1+8@vfoqw}Rl=PjSkXQ1`Gpq=1W z&atJBK{X$|T!XAP1_?n5KnUM4)Uop{Xps2}WC#Ib!HcE;{{OdfjxA9`SMLI|2vjQ7 zLsi}K0K1~}5lA(%g=u8DUju3^*!^(FLdpwt8(L5D_l1F4u;IOq|9yIGIi4^wfVv0Q zKmqp^<gq`Yp1nB@EWtjVhe2TnjW<XBZEi7;R8s><F6};yKmI@P><;q)P59e;c9#Wo zb6fM3#2Ov|4{ky7za>vwi8o}?z=hIJuO&RY!xTWpDR{WuA3PFWstrmv&4*b$EMJ#S zH#`73$b<oOd`GvibpT8GQ)_{uhw$<m99kexfvO&)b5$VfeLCNF8-P~5w;U*0`}#7! zJj5IZOXkvfkaaC6_V<JBFKK?g5^g?<d_Ht26?8_y>sD}jMv(^%$hI8dZvmYm1YVd7 z-3^K~KMcxm%`Z%C_{&3mdReDEM$2py!1=-bFVZ+hkiyIFzft+m(d5e!e5j4^{0*&t zVI>HtX(-_WJ-^1M8`t?Y{4J0H_+Km~dElxSv~CDG;Q&6m1|B}Xy*6x~y)1m5md8Cn z%hrnoA@io5&BqyitZP&_O2i%cxA8+V7bwmDhSqHNKrJhu&L5C?^U?eOHg|$=uT7h0 zFH0YidEj!~v-voSk7bPtN12#oH<EE{pvK+DG!CWy_3gEB^Xz5uLoydUzw6n2oC#vC zxFi2|B!h#Y24it2q<VnlCwhl(x!`McP`TyV{2m&<5{~@a{Qn~YcowwEyo(tkj`4@l z!?zqBU+*E|TP*I_?f(a1oHx`sY~g@Z|DuO4k~<;cTP)$ozuo^I!r)KP8W@W^Aps1| zU!ZD_O6_TITN+yPdUU?(Joo~XpRrcIi2fF0e&b8$IZzSr)A<})jYxQOKJx5*`ad+m zvGW-JHs_e;7iKoR<+>idqW|uL^R-7ZOOprVq5qE%=6Q5SD}X9xb!a&X9%Dn!Sx|9E zjZpHyxATQd=SM`S`TPP`ET!P|4lev483>$vK;>q4w1TVQf6&%#T~Ipl=)46n0@_aF z`St(5OXn|m8^ML&<rm03*FmYp1(qG5rh)7IXa&#C2jDgas3q;udF#cApI|qXXc!)N zX$P*V9T*^%Al$hGqK3Z(v~msNnbO-YLA(1P`34pa(%{{F$l(L3kNI0b2PvYP82~Y} z<hJ2~m&d>Z1P%=J&VK~Ux6`=u3+VhS^!!&O2x+I{D&K0L#)0D-wYa3Ld{c%Rj4fY- zkLrTN6TQP1socO(zGdMqH^?d9q@l)P3kQnIw?n@nJ&XY?-|T<m=w2Y@x90y$9-S9l zI}bwk6L~cMXDRXWX#USw0zQw^gYkn0<3|t66UCYh7F-VeQ;)k?94`?8ErtZQ&l@bb z_@^Fnu=rKV32D`xD1GCh`2oJ)+@qIenny1W==`T*^=7bQM~e$3!mpw0xnM#U!TMP| zEDx50^n=niWZhaXOPEJ5j~~bd8fh@qU<1JQ16&AV029c7*B+W5JTCr#=qJ{HWm*jt z|B*ciN&g^0ga=<RdRU$)1(^@qe~aY5GL7bnKcLWndJybBxDeQVFF+nF0_lf(kii4t zzcS6V=8AtP1~@PvgdhfhJXrJ!?m-5}Fptg;pw^vZ=b6x8k8b}0pYD9vse~TQKbiPj z7J_?aKUqqGJ(_<qmhi$V3~=~(_Qsep`1Y2FGWb}2E)oK@wZZWUS{&wM`ME@Zf1CCH zP=}TS;F10ZCC@!TYx^Bzd^BHxTHA*`dt<hO^iPHAmxt9~;ByD>mGO7OwL4lIDS3gc z9TW!O<%yvE&Al<DAag>Y<|rV{Ve+)RS0=!}9mOCNcYxXppk<EW^@jw*1GMhkqxmOG zNi?DGC>H`9`UedU&^dyiO9c71RsTm0kY~s~0{aH69wl7Lk;4@<&T+3;pu74H%otF> zp!x??@PItzz~G4-E~xGSg-fv@|Mu#C$OfUf2NE#I>!B=sR5-eY_@^B7X#USsQV7oO z*5K^U>eG4P@B~lB51x!4eJxKE@qspoGk}gZ@U=Wq!t2PtjluE|==i{cC9ga+KY z%QN`(vb1^i+Vpu^{wU&kEi%Eg`2dTL<?%8uM{5Cy;<uifA3P8KU;=4Ko8Z^W;^x(B z;|EbM4pPqqQqSwiza7PV&x=1mIvm42JI{kUr2ia4eLIgrcYaBDbhm>BLme$HdUWSk zIPy<9;?cbyG*;@-`Ka5>qx13qfCR_RgPz@P(Cx*)!I|I(3x7)^gvD4Q1ZwiYt5k54 z$E(-Hmcf(pt&io4VsFRZ!2d4b-3B33SYYGNuB|8ew^`K5xmX@AG4|*!W_isA&gUM@ z$5^cE{*=gjSY9jrWO&li;taS%WAw3nQF_Wl^Mz;U@z;H66Lx~e*<U~O>J8b;;MeQ2 zmchsJP_c(&uj7B0ZV?ra-kKH`uz9YnC%cX6<XtSUmDqUndb7Odhm=Pi%?DYm3;&d; zdszM~{bYE+(c%cmo<l62mKVxTd1$`!?7Z%I@ITW_&|)gs`K+&Rdi93Xg6zx1V;}!^ zBapwYl~{u<g!v1*eN4#qUHtzNboM@|y$NoTzP<_X9}ZajmEnoMivM-8pa?VqM<6fA z-8ka!1u^kgh9mxp{tz9WuR!rvL}dKou#bOx(La#Cu9aAUBM|B@tnv2>6n}90@Wda{ z<wZH)Ybnt9O7jawP<g@U$iL0uKdiub2`w<d`5#tZl=Hk6o#5I00#sfUb30lG{DBl0 zZ;%QM<np4N=d}b#J*d1W=5yrV?(h%Ud}x6I8r?)1Z*j5kiuLF`;Rr49_*+1SJ%CHS zP;jZo4qDy->gRyUy$?Q?2a1hBLyG*{I9$4eSh}rV3&HZ;0Y)Fo10|v$8Ow|O(+*kw z<ez*{@sy**#ga#$77tWiFH4_SuT7hW<&R=RkV)NOgIv0qKohs1bs+~>JS>lwiFR|i zfQ@mnJcwcrs8U3lPx9zx@$>4naRZrW1Tv3*JF<Zc9?b`sK<0^o#4!wf2yG}J+Iu92 zN0|xO)@>gDVF3azpFEmDS>r&77(Yk`Y%x-Rfb4~?gGGb~Xn9ajK)1&qkU`J@Ven`M zwTX@wi-E+!#vlg>$Q($3z{+2w@BppD3JT!g?(q+9AZR}C1t>g<#lhkj20nlV2+TZC z(<wB#TgvemBWOK1w6<siEgt|)F+<0}elV7(HA7Cy^?;l}QOfRkjFHi!^8}<d1#j>A zbf;H%_v*0tFh2CKe97PJ!o<Mf+4;h!H;~a&^Mot^wy6J}y-xpqIxo6d-Y${!vAoLP zrT|jYe2CGr`32+aJJ590{ExBD9xOc*G(5|{jmy%DrSt}*{*Z;2uRfLs`CG~v85msn zx5;q0Sju#jZt(!^khMHl`Zzk)NAm$_?L25u95Vh2a-U}}Pmg!6P7Bz9&5WRvf*e^q zI$wBbo^Z5|_~X+X@!O~Kpl7ecpAtDA%cJ~lp#6lPirusMH48`uNXn=4AZWb$A7h;d zSdB8smE9a5M_%#myajfr=fPJ@p3R4tUdtf4_ac7_XrG}=w@!zPrA}ArGMICpK%EPU zaL}G8a5{s!*T=tC$HfN}prCUBd_V!}$mF4U!lO6iuPgudfPbF74*z^Q54u=hE|KvC zxf!&i!4s4@n%^>k0~O+b4^X&T`&u3?i*@AR4tDNM&(7Bn=U#m6*?gGkwJ^UtgOBCS z5^ER!?IIp7mLgrHTValT4s+ziA0RhQfToJlbWn15*$rBM4t6(8+#e+V`V`#%FmZE` z_)9Lx(oh!$1|Q9b9tVFiy_^Of%5?zqFaCVl4(3ASUsi!uGD4Ey%T}-iX#4}zI64Pw z9C?6_S^>{NRQPo72Q`tPCzFHby%Rh-ZB%?ZeN-gC2WonBhNuWQ{tr(8&3Q_Imf(1F zv#1<nXYlBDfaZg9;LD0Yt)>!4j$ka&11W0W0XpcHk-v2!3utQ7MMdHm1EWvp2ao0> z6_5-8Zcc&6qgYftx*aOKdU*^y8IOX-h#h=7b5sn#kr@Zl-s{Nd0a_8_sd*M8qXAk# zt>Dpl9K1l|1*1=IP8*A7uX}=L=YNoP3m1@tZ*Tg4pU$6-{M*9+yS5&vQ}yU}Q4#QH zKEPrfqLNT5_*xH^j}EX{2mC3K2erdKfkt`__}iW{gAP<W&gjvcqoTnGI_rhOqxml* zf8QTw1_r2_R%QkUN9YkqzkE7tR6IbA3-IlH4sPQ3c7E~c&QY=O=`K;xc&!GHU(jMv zAIqowEeXsF48E3Md^#U^_lnr?w^%cS4p0^9=4rOPSaK8OVFl1!6X?92mII|He7Zq4 zbmyo9fF};%=7B;R5`HEdK)w&~>SbBsVR;P1w%~8pVrF0f6=|TAE*`ypEFR4-SfSzP zq5?jC5mZk1mb61d@IFYNhexmDACJy+-GP5VTnA8iwjQWcLJdN`*T$gu1BGb7pAvN+ z%MYcWJS=@wEcn~DfRaTosLXHyZF2<eb!^U2F<^PUYyu=4wLuCXYPp#}r{m|S7<lwL zvVczZ>MT)-@aQ}RK2q1S^9aZV8lX`0@I3gI2^74bBal3sk1~07{&}qm>W6w*-sNw( z0V=0De?i8{UVHSifcI+ix2y)2J3K7iLY5an>u*5|syn~G_yS(_UwQy!a)fW^^OvCI z+#vse8uZ}wXi@;O8Wf%xi12(3O6Hm$KsiFdqt}ngqxl6h;qdIPQQ`6Eb^Pbid5(X3 z;6ISL4z2&|6j1||_q7qIeuAV&l<+JC+1l&K1k$MC(G3evCU|%*2lZJX;R!ky36%0c z;mL$QJTJZl1t+M(0Siva3DEE?iSPl3C6kZkYmd$wB?kQ4g&eziS}ZS?+=Zsi?=Loh zw+NJ;frnutc(51TzXhdBcqsrmg$_~*fc)$Lx}_ihxiA2AL_rrGFd*qG4F{<Ol^h@% ziylxfIR3D2=W}ShLG3G*2dM|?^XdEnvdID2Cdc^0&?@?69%#$0wvUQ~XXm+>u3%1y zN`PnQH_wCrm|mJ73tas75_IB-Z|C#Zt3WMCr1>+?&g+h0zMaQFEd|g4$)MwrJ(}NC z_&~a#9^LCf{Z7y#{sa$j*;=yc52%pk@ac3>5%B4J<<a@x@qa)<r;7>;$l)A5;F1b- zF3mB}#z6Q9ST8}D2Hfaq2c?JRAB-i2u=A}!yQCyoA!RMcF$Pe>1I^RW@e<I!feKH? ze;$lKJvwVt9DFQ4`gDHx=`2x^;BVsrnb2GKzuQNJ#Y6K4C}Br{(wu=uXO4=6XXh7C z5vR}__}{aaXFkX?8lJr#3BJ80Q&~Vf0hjI&6&By#92G{N&WDct+eB1cTQ5P%4F!17 z%l8^Ge+x<H^5AmA$1+7lg1@<)1yt_7VSK#}QNDo=bnTnX0xEaGYT7_mLbr>G0jNaO z*b7R%Aa{Czyb$2S_`tXGpKs?IkIrMBoo~S<u}9}ukWvfKP>lnFXY)};AIsPLEvvz0 z{5uv;%eOwAFZf$_gG$Qp7aY6&et=xgVtKLTGB}(2bpCsB47@d=^pH<?ii!s)xd*%q z0jG8Fj@=56UY-t6I5~LrvTOh!Gz>0(CHUKxgG}vp`~z}g1UU4-K?$k7SUfxbfVxAy zKCLXCy)5%VMre5UIwbgZo&~W5JUTyke7^u<8g!R{LbgPO1r)N~A}X!lFvC?Dln<cg zu{g9o1BaOm*spI`UhkaX0S?bNP@xM6b0v@qLE(uUlsPH^e!VsdzMWq@JKuR8e9z?B z>&^rUMGcS6W8hHr?feT5SOsvu?iizw<!AmDYj9)rEsLk+JD<)E{4JM3X%!T%zCRqh zML-3}6?m}b-vF(iE<FqlR#4>zDTW>3;pzeMJt$lYz~Ks>M3&%hvjydaUdMkJ;d-pw zMTNz)^9zA+{Q(PCP-B6`x3@%v2^6sW+d)b8K%F`$BO;Ql@M{fNe*m0}1OAktgzqZw z!rnJbuQx)&7qlS_nx@Zy{ErmAIiQ9@ib{ZQ=U30pcc4)Ay!akV@cw}ZuPkW%0kl3C z9KMGcz<C53z9r@!ps@8(`2jkU(UrmSV#!r_@CsZ92k#M3b>IOC<A9f8;8Y80FLtM> zMEG{TbL`#&zCWP#KY#0WP%YB!q5>*FK#etR7ZnGOi$Bl`5=gtrxAPgu#0c1>4xm#t zAjc-4=|A`bbo2mftIvgj!56e5=>w#6zWC$yY*_e$%<Y7VAN;}e8a9sZ+xZ+vX%2JG zbFh0{yL-U?acw;b+KvZt%<ChtBLDd>&|c*fl?V?|x$klD=gVEFBA_<*aulJLGhkfs zZBAfYULO7T|G#hNdobs9w{PeBmvP{bWq{2G`}C@81?_SP4R-3zQThYwKKgWr7kD=R zV&ZR^4^A(?Som8&J7GLQcmJq&w}YC@pmO{w69WUN^$+hnLfgvVdex`9zQVUxgx{C( zhp*)!{<a<_1_s~WsQ-@q+oXMa3z$3^uXyzOZ2GYaw0+lC^O}d|C(mAIM&Hg`mUl}O zd@XPAH%EeV=xauh7ErQ+%a=X?_kQfast$v8U4kyeaj_IEz2Iti2~vLgT3+LCS<cA7 z;KaWzM&Pe|cg~bAp3Scq9WC#cuJW|JRC>=x6S`&t>}=2)63<?qKL1{wHc*qk4b+12 z>5cs3$#})5w`S81(8;sb+8*C8dG<Q9_;lVZk@U5^%inwi97Le*q&8Hd^f}md0buD= zaH}r*kE5l0=?$-587Wu81K>WI$HCW39-vN|JS1IsSl)!JT6gabVfg|YO>(miVfazH z7}jCC=cD-n)T2b{mN+ng-0j;d<LBS2<K}Dmn!imLv}3h5_#e#OplJmD?Lr>kZ}@gz z_UQFtU;!O211YV4c=kFmSzZP8G_UeEgEm=vHoszW<lim?lJM=kY<acx3D`GozLr;u zs~!2b2mN!j6feEu(H*1m!|;+1<7JQL$NxOO-+m4E$E}iJ7yj)o0>50lV?w@oG{0hU zwLDz947BX_5y%f0e;|1QUVnCP2Pdm<{H>s~MnTESNAt%^&?0w8_;mM!<xiF-ftJ`D zV`TKW_~Z39#CTBmbg=xTQYV=F!5>U7&%^t%KAksxG(Unek~L_J{sf=Si$0n^U+RIo zpP+I^^CP(5DvQ)NRRc>x`rFX)U*FDuj-jrGZ=u^?I{yWO4vKnH0UgH&HB};@BlzHE z6R2-0;R-sg-lM>y`9K1wK@|W}#o^Jt5424MRBd>G8YK!I-F_7y84V9mQ3opGI(<OB z4Ih;RP+zta)MEkl=ifT=&p+Z~dAK&u#qw~C5omFe0Dtch&_=h;f1one08}Yx7~cMW z0d#7uMdvY)AO}cL;r|84&I7J3PfBV%k`H(Lv?PE;C7ilNR#;vv^#L96+kBMK@PLcu zVg9)XJQ<JsSi)?pndI8?q_h{LKEMOCB_DJ}gioghsQg9kEJNZ8)cEEAS?uA`>7wHD zLgyl=j0e?XE~V2!x!Kk5t*hYy-_Gxbw_ieTr3056pk6}+v=sEQ+zAq^pVzz-6uFG0 zQ$0Yp$S^Q4fLe~tM=E?eKOm(gQ2CE4f;4`L5r3YYUqTJvqD5afbo*C`ih@V?bWnR3 zbW(K!C@LjDaTDR&`M{&IM8&|Pdy5L_+y+pbg5nr-yOIDja(z@Bps^YP>Q#nU9Ajeu zsj&dnkG`PWH|8I)e9hkrT4ieay2b_+gA)9Gx4<#zqQc?XTmByuau%S#Hh{&W10)^= zK!O_oFMt}~9Gyo&hq||b4h!pb1?y4Jyx}ne6tO;?2YfVNbceM6vAkFs1dmzEyZrMG zcxoO;IHL!2Y6QrbR?xM{Ak!i73+|uck6!_hD?LDg!|}rBJScv_t}31A+5C>t)$pz1 zTS)YR&Y$yb{SS@R`t_h>8PPon6sN71N*9nCso-%b@NxN|z4JKkBLwY71TCio-3|r{ zchJVL&R>xG#kPYsVb(+L;{q)Og&){I36vw7f1v7x<bUXR7yN!P$a(z`2jDude==y? zs`L2&Ajq^c|2FVh{Sfm!TEBs|7}nW?E(n7-h$k9(yd3Qk0qkZuFhJcE4RIKvJOqW$ zbcpkQg6?Ys_x8H$D?B={p<Sb9g>;P?LVqae77gfmyp~{t>Ulun3En;enrDFClh%0+ zvU}5`n}}^5>SsYGCtz&z0L2gK_F=!44Sg9Lti47=cwk(^W(B{7&4Gb*_rb#hu}t3l zEao~EXn&V<^D&nJW?aE$JS4qQ!~7lBahngmkFEi<?c4BxV=K>w5=+q0zs)~E`*7Mk zy1^%DmT39(x}4DP?DpXBXgyiN>D%og;At&btL%B)0d!F~gJ)-egdzBnS_Yq9mJ_e9 zBGRKrFN>$Ab;!XFb-bRP9ty9oK*P=O8??T${8(n~(aXZ;aq)#muZ@Feugy;nYw=oX z@R-=aVtLR4AQY*Zrykbf9CczIy#<V~4|sMKD7>rzw~aw{4g>!-cLC)4hN1NbXz&EK zlrzAmdp+oAQrOjaogylp-M$>2{QE)$e7Xw)K()F6_>>UPf@1!ChdNzUY#jOb<qEbQ z=*&^ksL=9gUUPkhFarZ)Zvd-D^Lo(1n2a9DmmIrArg><BuhsKtUJtrEkI|v!8+eCY z373a;H|QKUe$W=;105kM4xn@38TegZH6LK~=}l4LX#T+jvZzk#wKeFzmLH7WE-E%9 z_00!B7tn%a>Xi*Ib-o0zqrCs3Wdi7sg~}Tq%|`-|>e2}w-OZ4*R(D8%j=Jg%hn$8s z0mLg6_ULY4013AqC}H>L1grEs&JOBFfXo4Rypaq8<!{FhR{@YiJi2XEJd!UoA7J%p zZn+HhyvGcW=4P<p9lKfDJer%qKK5X21|9tgs*nUcI@v)qhi4}{F7rWqI(R&~+d#*< zf=+t1_vjY)=x&0#vXuL^G&G%bUI3dm0U~p$^d0EJ?Bh)!e}TqmVEs6cUYCa7;FGHv zKY(vp68GqK;PB`KD?AJu|9m3>Izkn4BplLN1tqo~-E5$v2>4NsDFAJCJ0gLYzw+p| z18vv$=ym`dC?o;8)d!sJJi6^c4Gh@H0^nxU>$ULvL_pG@!xBOFVl-3>Fz~m6#w;7E z1sO_XJ$h~TA7f;A@vs4UY{^l`{TUwJ#vYx=UsyF`JAwk#`Gnmh;n7<RI^*aC=zLhj zQP`03!|<Eo{}<cqU?!LNLrryQ1e^ML1vEdt2yO(e@n?7mx;z{-&I39>sP#bUCa@bd zL8-!{*H-)(Bg0F}2JmKCi1{$PLH7k49(dghQV6p1rE?wFJn+2&;J|KXU<7dvL*uiX z)iMQg%p&MOV)%HcM|XLEN9(srevfWe377(qdwOk`9%E!MJn&KqWIbGc=cmpO(93>B zdZ&T!pZUdD#@YG(rPE4AhO`Ob+j@}Olc4(mz?CZKu#MJl{C&GXO?JqRX^+mo;M`gx z2&ytWFMvD)-ev>pudN3)GC(IUID+?0GdD9bc=Q&7P80%dMTH2yYhq+@Z2rSk64C4N zA8b_fPo}a>j?I6VOV&8{`l$SG{>fOjsQCwbNnGb4@b+Gg(i+F!5S1UzzZlDs9Gm~J z@`KMUbZma|*RlBzONn3e4<7!hhhBotIpUXRFuVl1TW}%tf;7;4{t*taMIg6!UI5Jr zg4<ajGk=22<O8w3HUDBPUj)j}hL>J~k_o*3*8GdJw9~Qq4`*q!;iZ?X@cBTHJ*@n# zrT_o`2cJs;tB+yh51_&nyqN=Z%w?Y(Xpj#QhMt}O!R1H|=zb35h2@ZV_vuak@6inp zOK?znG%_;ybUt(JJPO)N4i++P1O?<@RtNqm$3R2UQw};PoO9?XQQ_ln1&vd~Hs>z| zjb$|dW#ey|4Py1W{sHZOZtDY$TC_f?6n~ixn)Z?hjmy4a^lbj~pTFf0Xn3N#zQU*T z8Q7{*4d4y%j-AJ#``?=%d<Pw;F~voN&$0O*Giakdzfb2wkLH(*l|nDuzy>macpl9^ z|Ci`_be{9*<WYe;y$WI;sP6FS=20oR3X1Iuu<-NjZU>bYptJEU57$V(c82CZm^gUq z{9vsPx~VD<Qyn{xf(^X@GW0ZxrW5rNFWcbz{XxSIp4R;!yK1%34ZB_s_3{C*VV6OM zorA<rLyZa_LkYi2=L={sv{ZoxCR(3VNV#;ffPD!LxaJ?~ptWiIEi8--3=mC{U`^l+ z?JbR<@ssAiOpvt!pfNv)aix+jogbPXKor%175!u4ZvoBjI)ao>ImF)*0Wp!Ep+vy3 z`7dKBmm?@TT0l2AHP@(cFqaBAHva<)|7GTHxedDIwD})D|C9rc&42m%ryS&Ov4EJs z$56u2a+1FlRPTZg4{(JXD!{?d-vS!n0SUE2oB%prz_Ix+7bv;$w{XF<m&UdnD2;5n zR2mApsF1&<60G_!Xgi@}^FI#$DTfp<G=E@xDfAa~d=;pX4nD@=h5A%brwS5255OA* z7(i?N!1)4{PaPrmZ#y>s6)2Gd8C|*(9Cn~|SHjzJvXs-I<y*;AaDD}yKY*N#py|b> z^TTl$74Z3ZkdOr>Q$%`w3EmIec>pTcS_M7@;PvK)8Wny9{+2Be?h?4I{H>s4nG6rS zo(S4Mb^t65E=OKBfDSEq2NiH&c$xbjw1MMY^Rw@uywM8U|73Uoe4qU>7ZrX6SHo{F z&;R-V-_`KI%ME}3|99*y``rAGv5d>H`5*LFx0aLqEe%iu8D4@mT7d2i>MaGU;{aKg z_3!_G$L1gW{H+OKbuWK_1^x=~w_bq=JpcFqKg|CxSNs80HPHH~`3;BRxBZ|~0beA{ zh8)m?-X8o8IWi1Xgn_p9NPvp4miwS;pjV{Kqw|O1Nze(W9*n<0^$xQKXjb3A!}3Du zXYXE}dXHWkO^<`mSzb)4hE}RBpsUuwGo+obJi2pKI1Irjq<VC|_vuX$Wq6TU0Xhrw ze@Tw3;WyAxV4|RS1<ezAG#@tbXg+G-Yx%dt-=~|~2Yh_gcaZ5mjL&^KeHnZ@LFY!* zYJd*k_GG*U+Squ^qq|rG<h~b-m7t@XoFV5JgHDw0bQO5L9GX7B?JiKFLrQOkCtre& zsCWT7pw93B<eWZG!x%KH2wu3+{D!0X*GJGL@+Io6m+F;4gYKYta*uA-o)}1F4XPL+ zMnczDb=yYvs4%=JDgXc9qt{fo2Xbe?*HUQU9OiEU9Wf6%xXxJse8l5%XK=5<r!!dt zboCFo`PmsP;n=W81*DdNzl9AvsWBCF%)CcuaD_+r)Dxg1PCF-q$}^Adsi2eQJvt|Y z#sgeBR)f3C;O2$11n8nmk8WoT5Dhxi)BzN^t>AOak2`~IrUK9WICykACwO$Xf(-EJ zbT;tlZUr0C>1@%kcPHqqmQp*9?pDxYq#m8l4j$dDphL1eI-NbBM{jv_ItO@kw}MU- z_vmzv0EG&49X7PRfY1Wc4K>K)^#KpaNxCme{z3!DIfK776Lj_t=nP8>!vim8fyO@J z?HiBd&K2MQI^GQOJCq4>H<;P&Eb#Id_}D1ev2~!+K)^)~BtIB_>%9IVe8&I(h6lhW zdB74qhevZg=xk{oa7f!u?NVWQu>llmy{4I6kT^6f0Xvbu1=KrS!RXO>+@tfJ;||af zWc;n5i}W169cSWi{l&n*;PCA@3x6x<P*#`Dg9l$SJAOOB^4Vi)X{O`115Bmfj?E7k zOHDnx0|Y!ek9ssSF?w_dNI+=~2wj}u(fX}45E76tz@rsl|MG7$;dAIP;eUM)Qa|x; z7vW3m6yf)1KF9<zwq)h&JD~j3%?GlolOKG=Zb{$k>+pO~o$-SG57;Ljovs<BbHTTV zmGVPU<oo{tkb7bc-@a}JnFG2!8RCe)(H@;IKzBSr-68DJTfykjTg>RVVS0za%m3g& zX5in>=h1we34GyR^9!c5PXB)|L8T?EzV8M}vw&9jIyS#x>Gc2e@)k<`zRU)35&dPz z&^dw+i!V^8r2%?iv@z)LQejYr10PsdVvTZ8a#R`UfMD=J$r2u&ke~#eOA9+U`c)CA z*Z`dyy@Ow#;l=4+|Np<<2X7C8PwC+B=+^h>o+|;$bFH^aqCL7NgZpRJlR;+!7g>9B zH*<gw2R=|@?{T~VBnL{3;0Y4LOCG%<lRP>*K%$^>%7YPfG!q9nN<sG+c`<r)-gtcr z8bA9$=ls3^-SG|Td)a!l-uCF60lxO*cmv1|khvbm8$k9tFfcHb@OyNE?S9=3Ne>>K z;6s;Rf*PuZ2VPHg3~}uI66(>dAMDXR7t#}M2K5m=teXXd7#NB)KyHV2fxSIC4|^O3 zOM|WHMA6V2!06G-a=@dP<*-NRWUv{Xt)OuOmyQGncvTMXcQ)<?9UT4tKR-hqx8Z@; zkKz6U9W8AJ%A&0YN}ho{2eSQm1L(+Uu&u`%Kn??W4&*hEWOFOXC<l-3*&szPL8sHg z;|J3Ih8^$S{3gSrTOKrE!Qs*gT2#^9JVBU&!KXVRz_Hs!MZv=o+!-(D1MvhrEWzFN zB4yCXEvQj+40=w1NAr;kNTCj%Qv>IN?q+Z-Sb}@?<!8Z5HoBQSJFj_kPf-Dtdk!t% zd^?YK`hd=+?ok09F4!#u8b23s1RdMY*%_kZvkTNS2Tdb@Tqx1q13ulk(?`VtbR4^n ziU49v1S$tQ4Zw*JbcQB>s|zCoLq~{8#Bmpu5O6?&u9JlC`|5U432BB6jfC*`Yl4bU z@bPCaZZ?93M)*6_{{R0E>MUIWSIRz}@A<ct#=HO>?g+Z)7}U;S0pD^U=F!~@idWFp zksxn)bZU5X25@+E3xZCpV)5t<5OC}k@9>ZUDFFGrBR~=)2k}lvfOPj1@SH+NfJ}FY zN=S!?ENC)Bq9Z^8<VbvjCN0<oO*}i<K=A`{2}ie!iVMtM$4>Tc7ZsmQMvy!t{un_~ z2TzR9b|onOtX))C_*?e;`~Ux#i%JCJ%WdG|3bbAmeEu5fbcM6f%`6e%Bm}xA5te!i z0vx;D;E9LVvD*!vcH}*eyMkha0c@#9^AY%k>EJ_F7(nAsprQeqZZ3k`A+D_lTtFQc z22lEt@aSd)(E|Lfpp(bHIYc_}x0r&fp=MCMcHBh;bVUVtt)+rP%QyZO&}oYv-OV5$ zdsw=tNE~-jaRIr3f2xa$2Wad};I$0Ayme7=fu{=>{(jH_p-97KzO|6F;S5b17eM2D zKAr#hw-v;^IPe>EQkUTYP|{EWrKfJt?bxuyAqYwop537g9-RW+Au28o9T|ch0ieM# z7kqJyZD35o0~CiYDgvIJt_+|s1P3Nk4n&my$6QoA7-1d;FA(U=Q89S=1RR~9@%-*) zP|F1rXrRmn3Rnf`=45cag}uB0MRq4B1HxjQaCrer_#~7U;CSzblndaJ;+Sved2o5) z(aiuVG(hVpKuvDY#igELA@J;*g=04(sKkJj5E3XQ1Zd(8R6>C6(Eu%Eki}X;NP_z< zpsWW^2O<3ZmZ12Aln*m&AZY+}BpSSY5P+2ryMH1Qfe1(w_`o1YBH-T!@epW?5>iNb zc8ej(2aq(#jiB-YQV@_?21tO1I$c0Z;@Ch1fFo!ewFO-2LS|4A*K$I$JiLCeZUGm? z{4I~+S^qAmg#pVSp!DDgnhf^^9UB9#?IHPJL<Lkxb9fMyA|xG;xgzQg<RSnq|6d2K zxAg25aA^6~y+;L<Qhhs*dvt^6RW&+mR181~R)W73bW<@(Qir*>y9cbxqc=xI;I&lR z1W<6m<JE<~A9QSGw~vYeBxyk+IHM90!Jy@$pa?z<tpL7*D}Y<y5fKbJD8-}OL<L#{ z@Na{-(E}8#;7Y)=TO1U>ko;aD3yDv>`5jxCF5v+x(_K_FJUiJzAqOh8;eiIu`=C{$ z*5DEjme;|B+snV8xI^AQ3F^ASYX5F|k8Wn4?gR_45x%WYN(y|sH){wpFo5n!DD~{l z<M3#GTT+8^KUjA&r~&WMc^%Zv^kA+P@ag8^@aYcW;PC0>_vs7=r2w#s?h+2rG5iwX zJIOjtE_gH_V)U_mRJzxrb2Z2gu%)mv3%>uvqkA>@E{xW<r5&JpXdkGn2U=)u{1wzJ zJqkMS^lixr$Vo>Y2cNOLkox-nzgMrxQ&5TS(di1xh%eYcin~i8UE9`gCGw!gp_Fgy zH=oW@2H3p-CHkJ7c^oh0z&AF(f3av5s9jqs0kUiwh~4=P#O$60?)wCTvT`TbZ7<(| z3r|S@$T55u=s0XputKXt37>9#(3$}bPe=#Qvv(_~AL`k=_l7WN;Z!T=_A*A#&Q=Fu z(DK<<P|wh_v(-bGf#JovEYLuZE2sfx0lMtB1$0>zxG41KhTV|D-wL|l*Ry*r<kpVc z{4JnEmR$}18@_$bF##$LT4l<-6%-FXjK4j)t2I2k**v?$I6OL;JvxIqJV7<GwX=jr zr?Wt@+za(|kk_jvKvDYQR~ERlQxfXY>8#<=dEB!*Ou)0dOapXlpl9=O15lg%1#1@Q zIO(UQ6VM!2nhxskgX$y+$UbZI^5w<scmMx;9CrrY3(f%6Y<L^tw~}KoZfAnr0;)JW z9XVca11s_9uHbl4l=lBWcz_vPKEK$EqOQ}sS0=^d;A0k#&igMcpgt(`=)C3G*$N6L z@U>iqw_nJn|Njp<^NRtzj=a-V!^iS*i4*7&8_({kpx^>e{d6AnWL^u>=FwflB;nCn z3kjJAAS+uBlpcO@?JdX?u3%5R1l0r3V1HQ(x~^--+yDPPkAv+6CCZng;ORwJdI2RB z&(5o$qp8jyoi?e_?V?iAJw*jn>$-q${&ndtQ7PzTQR!|`0X1tt*WH0k1J!xQ+d+;4 z@jQ;VgK7*Avt#-$5e9~i_Whvd)A9BPh<g-4m)3w(cpPv4AjH4`?kXH_2lo*mrhz(Y zPz9jO2W5gXDU@j-3}P@acpPuHKrlhK<AB6L<|0HvT`j06s0#;Wg1Tu?<^mB0$PI<v zZ3jU1gR0`Apb3C(R}0V*BXIimZ9M?GKP-d412mfIX?ehh-~WJb>q-6&e$dK0%Y#1r z{wG{IcJBcBwi~R)qq8{zoTWf3(mS0C9Qn6}s8n=J-X_Ap0A^RVT<Vw%x}$}^6;xV4 z?rjbLwH!fH1mMwG576*`%Y3kFc7xpff;$D29(RL6`NjWa5F2!``EF2Jd7<9};_n9C zEc4<qNbYzuNFBtV;BpegG(6yOycwhh#6dd^c7jLuR8aN>UH;di%mONT4|sG=2AS@0 zycHw^(gX>K&TpVw4HysbcfS1x9v}q;N9RYd;6eUQ(5Y{(h9_TwhBguHKTpuE?B*X# z9-YAgp5V*OJA)-a3;~bMU<D9E!lN@-1H@49=nOUhF*H0ngDpS|1CP#N2N1);qchk8 z#BlKF3=RM>JUlvsBS4G*kIvu(5F^5)GdP346||AsqnFhs15{i$gXg?@S=}JK3ebQ+ zFRKTH*C5Qm@Y)dC&-LhKb%Y2*wKzj~P%W+y9!QHvck2gGvgvFFEwl0HZUr~*JDWkP zwm`cF5_~$lL7B*-v%12g+f~Bj;0vzi2aK*Q-#VH>Lv#Es9*hhOorjt~G4gM_)HxZn z9I54iZ|51GPH_Lhm*4fEXY(6IpU!Sjd&8%*8B{`h9B&3)<OjJ?v-t)CBnkU;`lxVJ z7=vasCWGAF{DaE})ad7Lfh?;=UcFPo<I}kqq!C<xfn0X1c@Jo)(4$k^r!$+w<KPPp zkLC}I%?}tmns<YfwW|lH-cs`Do(ej*7@S6=KuNQ6GANmO9B%~)gA#~GH+b+2oO>35 z5_C5+XaLg#Gzr_xz}W4gk^x$~rw*CG$YA7e1>IVBtQmB*7h{8s3Il&@Fe3wl;eiPr z$6J^+U>UUefPhEyj{@-hvl=M%HT=Gd<ISM(0|m>=?f<~{CD=58LPfv_Jb`uaIrEE^ zpd8eC0K7&F6gmR@EfYY?<2#!{8O)=5DyS3y7tlx=L1RD%A2GkkNdzs32Hot@?V}>_ z@&@=MIZ(f)mvv(*auVAN;X%{ORtOK2US5JuG=jHpLHEabd<Wmv$-uxM!9Vpt%YhDX zIPkZC8cWc@HwH-9FuZ&TuIoYbe=nJQIv0b|05lqXJJ0ZUn1Tjox~nUEI?qAYLpA^4 z<!_Y-ozct(YI;q0k(~r~C8(@?IR|VQC~-1)^x8CltJ`i+xdDnC&^0n1o#2APqkAf7 zEEF6j;L)&yk62zLCH(*YQW<1luZ;szNP*@O+#rS^2XO)@8FYdRA<(UgV6%{PfiB?$ znaYy@y0y}yxz>UKp}W-jWz8?poCs*V%_PX98(iFh8gQT%mq(|IIcOLRQb@TNfVxu* z9+sgN{B3T({{P<(x&{$slLROU2!K{Va=cgy3We4K9-tBkWRCzSAxM-i^ym~(dC~C< zRE&zKfD?GjFHqU`1lE4{?qx{<DFW?F1lP^XP%l+zyeNo+=<~F^QhNQRH|RpaUJ(b6 zUYiLXpmb*lv9qMqqZ3>#dvu3-fP=xKmnX=hSES1Wl)$*-|Nnov=g<HDFBn12I1Wyf z44}h7|G)hI^Z)-BubzVHdT_jeggv^!^}@@?Ah~l8IZ#L-%N+;FZGy;waw@Xi>YxAr zBd;fboDTw;MhOk}XnX@|+j;cHs3d@zkO812ArT&(*Fhoa0UDum@c`W`5~5<!y#_qj z;nKZC#Q?N>x_gZZXx7rDdy9$#BLl;Wl3LIdA}G8qT)KT!TtI>DQ7UP8z@za9$PQS; z8$9ab!obkoqM`swG2JC95w85(PJ)(r2DtKX`{v5O?X4^SwkHn!+upi#@~F6UmZ)g> zb{=%;%u!Kr=}b|P=*&?O@a_Bo&hMQ!Ur2%bN8r+;<x=T{hCM2v1?CJTEuf~^*_i+T zUzQ`ahddz5C@R7|tjiP113}Aqbl{hy_uB4Q#K-`S_FfmJ3y^zY4Zugval8zQ1KmgO z(H-vL(OnL@-MwcKXnhB0@cpG3NDf@i!tRBfQV(jzgCs%AMG*BPR{MLQm)A49XL$75 z<}LwktuMF%wf9ym%6-1w<)B;CJ(s}k-i0C$vtNXK``<1G+kXLSe-w)OP`{sC47cA3 zMIL7VlsfGGcMJiYj|rN=11)OdZvovq51JnM0?J$9=>h1uTCKP1EM57xv#6vwHvag} zP#)%Lc+&9XYjJ4(4U+Ql>^upTwE(RjaO-g4W$>`PS^CJM*ER<flbyFbI^P){crh&o zR4Bgp=sf&lbrgts1C$=Y=k0dBe=#HK|NrP%!vl^nhbMr<Jvu*mbpG?`wbcN}@XLsp z|NmbEKLUB^^>NT{N0j{J(fQq@*OXxq=p0x?wqjvmNb%@qQ3W;GJvv)dZm@u=JJ2d` z3((ebgBP1)z@7dU(E9doE-D60r8c0xhXte_bphRe*aOy;;$h4KHio~|98?)M9(lpS zzyNY5w66nBcOJbiDgmHOWC6aF$VElt#luQamMk#<jlP1aJV^T!y#K8E2TO@6r0ogX z58SXv<s_)g;BW0^0y*<_Gra!t=;f7+2Zb1@htwSc830l6=#?!lQ(^daNTF2f+aVVI z)?82p+shhPuENmx2vp&C@Vnjs`9}d_>t9e_2e*2eK`KF`99=%Wx_NQ`|9^8)F?8T> z0qrLNxfDEV+H2$B!N0ynMd8IAP|QJZHon5hzyMVZS{mWeyhjD3n!%&n8N4@Hg1?Us zWO{Fl3b@|vEm3jsIL@L1@|xjo(8!Iz1jy*g9`FD-=-fxpQjh-^d^$fNEqj;vf59X9 zMeEx-ap!K4eV)B0^5D~1JD)rLzv^Q7xBjI^Gm8qN2jgqdV3CJq3)mI)OB?p6fLd-0 z&~u$T?}2W_dlL={yu%=-VZ{Icu#ooXyusfh1*%-TYg9BmI%QN|C`Ev37Z#P5%%F-K zRv&>jX@EQl9Ry(T=;hUn1sUn1!U3L(gXRa1US3DIIOr@^n0R*x=$0$6TN*&qik*jF z<|CDdKHbF`9^G@mUI5(#(Ff{XdO$<a@GUsdK;t5yt5RV5MId1YN^An1#~lA(^XPon zdB_JGJaD5xmws8ku0Qee8MqC?;M1$S5mXj~S}7p&e0p^+g82Xb{{R2t`#nU+@V9`D za73g(Q24xH3kPMsmoLE!CqT)I0p>)HUfIkN$o%g|@I+w?c+v*sZiScCpc^4>zf1;o zl7quR4GidhG*9qgqEF{h=+UGep3Og*K&L%`DnJfUV?uzxr4W>6ds$CKgDzb9$>P(i zx;`2d@`pTnSw%s~lHc_Z$Q})kUe>SC|Nr~&yF37;2v5tm9^D<F&Vo<(4p1)vG*Btw z(_I)*BldC+0|Nud|Dg30K9+C!`#|UGKrZ}q{C@>>R}3gbTMm@m1IvQ8T!6BX1Vo8X zcR>Y6S#OGp0a&f8;eVgbM~?rmwI1knQBm>jEoJ2IgLF`GR3t#-h#H{dIY8~n<BTqr zH7Y9neW2>mxm#op#K`|wK}{n8P<<`o)A`Y(yF`Tpym+?r)(g<Krq%-`hhMuQ#RrQ@ z!wygvm!Z@dRNV`JZlvve_d*%e90FaNd7HllJbnNkhwi-hqAC=$szsZz#KoiehzCZw z)p_3obYEKtC}a=6KH$;I+8Oo#zfZ4f5xAgsQPFt09Grw8;~5^^!5p0|DjuDn`vO5{ z6QG(o0b~d42ygJ@;11Bzn%6C`{NxC_-vKfV()>oCVL#X>C7vFd7d=2zg`hQ2r~gj? zwd27ppW`pIAujW1y<Jia7JC0;1xToy8-8z?aET@8MhB4B4*PVc7eEc`JPlfX2{+6K zbce+87r%c%e7X)g9}M!3iwY0aLm*c35dpAK;BtM(zyJULzg_?jf5&ieO9wQl10Da1 z@aT^60JVB8Jeq&A)boSpXF&R)wS#BppZyFR3=E*2qbF$VcU8cPc2J=Ns%$}LUig5P z15E>$n>L{Bmtd7IQV>cMAU(`raMj?^T%%&Z0E&16{uWmzP-2GU$4(h0kLCj$9=0IM zN<aAY`lvYg^alS2ZSsa}OZV;k>)CnVr}G48Q=frv=R?oVBd;Z(<&*>ewr`%6uY5X9 zKG#)%hW<c1;JQoJ{_)g&*K)Gt0q7ofP*LF1`KZ%JMaBno77Hk!a)7Si3P=ET_k2`9 z<H9Ne-99QZb;5?<9Qn72Z1HG5#%TG$r_<ze{d-T#SM^IgdRa4OgDz(J?b~?<yk#{9 zQ~<lEaJ-iC>HO`{%L*!AVG_QbXFyq;19q~>3y<FHe;&=RnSDE-9&=HVVeswz;?WtS zq5-<6Pyn>Y)U)##D1ZC5KB=?u=)B>>_@KhXvsXmNv)jzGyOsge(|7FVneNyv(hkbl z9RDx094G}(I|_jADDvq|`0v~Lr2dFU>+Omv$8MhWj@=^jp_<_uN)kY78){T!82DQ| z!J`u~DjFaMF+#%=q*TD8^)1Le{?0tmXmU5WQ30OAs89fv-U=R_6&j%A0!~s$X$G{8 zth)wOZ@lnmc2N;v^w7Kkve2WG<-JGqTSl<O9{jE#f4APQ5CU1u@VW=&ZxEC5b)!e? z+loF=DpByzd<_!!=)B?a{f0*;%PWup9?gIMm%e!kT1WKa*9}m;SEFL^LLd;rQh31@ z2rlmpL6yU;8~;HEFmSw#`TPI>1jlgS&f}mXe7=FEgs+14NB#v}1*_rFE$?`Yf#I85 zL<6Y6<tTOZfM3zg-}(fcz#)5CdchaN7@h=O(zWK#|NoASei5KV47$Fw0kUR=zcm@O zB)8j70W|2<QU>Ovd4NKd!?!yuz@t060yIeNqxrzMH%0|K`77Yj?Ofp1%L6`W#l4$l z7NqG@1*%3s4K5Q%gA26(4!T9Hc{gY<gOR@_4dlA!BN`}i0CExoD4qnoKnEXs^w#WU z@UZ+*e#oac^AE_@fBwBDTCYXH<plq>OP<{#7d?7eCU{sLFPhga!r^Fnq4b4s>+K3X zr*07qM@uoE&ReBEps<m6EeI<QyLl!!TJlUP)%R$A#pu!e>woE656u^#4ZxtmosK9_ z!a;QytlaVe)h{B~KsLVaftOD(_v$iuFuwP&{K4NY@c;jR&>0y2e0xi{{(!H_0i9pU z%yQAg@;HAJXgtx0f18K||F%<(mM2P|`L@2T&;^M=ocyXp?6nr6|HQwIM*!?#F}Q=@ zcxb+O=?8ApI52oHvs`<<451$8o?Hfx-kMra`0=;bfrc=9eYpPkGTw%|toR=!TtpDz z7yx!&(7*rxo%pwlNbqk5JMTWod3xXl6Chy&OF^H`S0%!))uH*0e>+bA7Wa8v{P7ZW zN7n?8Zg7wVyj}<i#~0C{MBnMk;R{V`t)P{#2={w-hXr_cmwEVh{sdLg96p^dK|{Kr zOSPeQlsLw}Oy2}*+d$JxJ;#gfzW@I_f-(YsE9maqm!P}0q3#2@TmWI%CD8Vq=7Wr{ zS3%`L`;V@}YZvgwyb6zQdqmCxwW&e#;h-Uc2+*K`MX4`hJr{qgBJ?~G@JUY&;3ZZb zy&J&IpxzzeMp5Ud7iK?^a~(50(_I5~aJoUqEOZwrfVWbXfNH1rpkt=GOF(@h4-b#t z5S0Xv?uY=7ZU+tUP)&DPg>UyXklCQJ+Q6gN@xN#HI<S?UJ}Tfr4F!*06VUBl44{fb z1EfsAqw~E7=$MyolV->NK?$J3$^*Py-A5$=bPlRdH)va}<|#-n)&%F!A1ozCkX#HJ z1cBya50J~6_d&CA8ORH$JC#ADhJs(O%??mr@q}FbpizDh)D>~;b^Q+-*%0vQEoozc zoQdS365!eElmI>u;|NHvgk!e|JLnJ`(79cp<GM;5LG4V?(Ho$-B}lwO&pA;CpK}7z z#lgSrl1Deo1>atqcF-AJ8pWGmdcpTgfK-4s;Cgl*s}z0h2+zNuEw!G_|NfWgId$`F zcJ3D0Zh5if74&-V^OHe6%F^2)%?4iGEFVB-zXtEqfF4Gn053T}?S9ZsX`~_zq)owt z@sKCuaZk(ZpeBn3e>-Rt7da#(aD@aUzktjT=&n%#-QvK*-uj<^+r>IXaG03F!-NM^ ze?h|oa*i2D7l#kC%?U_A>;hld3@$f8GO%#qeQh}b;eTiTZ6fBNK(M@6@)8;V=O%#z z;1)=ifgiKY2hdq);P8j}AH2>IBn^u23Xfiv09b_cx1ae7s^37zu65g}fMQ+3gYf`3 zErODP59rh}PzvyYoZALX1m8V7|A8(Jmxr}~T~q|ROF;hL&coLFjek4L|F&5DukK^{ zqx2KVK^)*wHkK2<y(VrxmL)2n^k4-#qW~=&j-sW9w~U_6fB%>0IrDE9$#d%FS&bAP z`z9iVhgUbt3y|Tj7lO_MgdAF;04n(2fU5CgA5gp3nd7Als2AE@&GACe6A~N_-y9+u z_*=jH{{R0a=n#0gdmKPLlL*kI&fPAcReLp{0w5s3qr1T4g~<j`uL9-tJdh_HK!v0S zSRbh5w*aa20d0&82=LJS2pS}M@oWA6|1YD!JxS2{*BI^T44>{K4^YWq0ZO4Bp3Q$C zjcVi-hn}5(A&u&@KHWtDFU&!HgS4j|_**72F)(=b+JL6~7+%OAq%`<j!k8Esd^#V2 z&S?Pc-v53J)SPx;;BRpPx1>Sy44op3$n9v*h*1D|j@!4lM&+N6=3~$iKLU=OFMK=S z_<-8;IpFiUz(+>9Fff4H(Y~$!Jv(E*GSsDjZao3bJiE3WaOy7E%Tn?H94emOEGj;o zpFk%jaKPHnpd%AF>O{cJW}9u0X0vB!%wLB3cfPIv>$iIJvKmYP9rE(jv-6!#cZ!Mt zIOV<;_vmH)9*8O7xF58>3*=Z1-_9?-o$q`*|AJ0TvH-P|LHB$z9CJ}oX8@nF1QL<} zHJ*2a2yp!Sw!W=%0=1ttUx3!oanzZ5biVTJHuvnd^U-|e+5DE#v72WK=maHDv<rY+ z(WgO736Ot%dNcm}w!W?3=V2M6!ckuZ^0fx&#Li|H6$wad_An!)HG34)npI~2w`L8% z%K7_1H3isp9+o+v-mr^`1jt1G{xnc?2$mQfJUSumXbosP8e^3cXrD+o&l<;WkvR~j zgB%EUD}M{9?F({hLyd|$Ln#la6JY?dlks&In0na&S|`$-qQc>6`KGQ5R6S{UXo3!( zgSVrPGJ0CRDSZpxYWHIESx|1OQE_;2(iOtec(LCV+>Evd<-f^iL2U$vmr0;3k7!>I zQQsSYnnn)r`d$OIzBh%|_uw-L4RF-=(@<L3i27aus@cE;QQr&jw}^v|Hv=6M>Cqhl ztM4<Q^?is6hi7*msIvF$F01g=yzkq+3{us*s7QdaE##Dw?otMy&gcIF!N<sfs&i1o z2RaxDI%C!Y)FL;=T9<2dPXpEEt(W*)4q~s&JwR2y1GGlp04?AZigts}+5$;&fDe(( z0axe=pllN0)A_@<^Sx*1QP8?%$L>&u){~I)$~Zu^IH>+A#W+6_T5b!#>+zgc1|Q2D z6@{{0NcYHt&o6TaHIO=MR4gF<RIpWVL8Ctb@b-2y^gJ?UW(Ef5ZlA3T(7OE@s0G;h z{>A5Ra8Z8U$MUmJ=UZ52<?nj~YWQ@!s7OHS`<zw=@O%m=C42bw>R5O%yQnyLT7pk1 zRp4&|?bJdIh8mP$cmW;-I1X=bfG)f0u2Eru)a3FYV-Z2Z{aOPYJn-{KLDB-SdR(FG z&`Z$0kKpnGa*kUxxO~k)gu`FY&iB~E!I+7G!I^(sh$(|}HxH<h^As8oue!hiaSa?0 zf1v{b7W{qxLH#&TK!ED~m(_@pphU&NuU7|D*B2ndVa;Fg4Yw?a<bWIwNcoR{yDtOi zFgr+9t_JcUA^?P5n?u{>uy8=s=K}EhJjWH9E;hY1{R_(FE({Ey@Te;Vox_SXO~Ci- zfR4xk-CN-fs@}`N2?JEWL&M@;Cpau_fx`lxDC%TEu7o8BSBBS(2><wY{`KvA<)a;< z;^BGmmuD}F6u2L`1XSsxw~DJdUhq1C$|@HX18^IlWi#lE$!>W0(DLB-|Nk#6U7#(8 zOJG^>QHMS%uy%n1q-^howhL<3Vr>@~fK)-+6QCx6XXg(Okd9s-JMeKf8ZTVd{{R0{ z@DHdSdk-3~@a_Bs@x;MjOdwypGzE2IJO6rU$EbLC_PWS2yae5tJ^@sIeg}<5T!S7d z><PYC1l(}~ZA*c5oJt*#M<XD^ZCk<RR_|U=VFntF`1K<1JE&je4;_twheN9vWDl@{ z2WU?|s4L>z9q-}M?ONd39UtJ)?OWm7?XKagc@NZA={DwX@dKq*Pzh%MDdDz(hKL|1 zA$L!O6>f&0oZWnc1F40cHo>b`WTRg%&q5!|Q^mrsPfy_AcEF>X<+w+$NRy}K#qwR< zOqRz=UwJnFW~@{1c4Dz~X5nxB%?Mpv`<i=#OSesvi=|C-shLOf0Y*>D!=UkmQyvF@ zF?qq#LKP_MpxFygj~)jfFne~hFn}z4-3vPZo=4G>@tm*axnjwepheNp{NTfEa@^PQ zBY(T<|No%F-q`rJU9h}X`U31SJ<x57j9|BAg0{ed7u!P4lXvFd#v<lq$uhZA*0cE# zqp#)V(l<VxH#{^?_%MEW83XShc^-Vk?9Xf>@Ol=c-{I9OQs~z!lIUZ3saXCcXweZw z91%WFo|c#R+oyrXs<$(_@NYliVtKCg0mME0+nt!e&H*(FcYqp3uLU9cUHG^2IJsEz zG?&W40_=^4<|&Vhzd+&j@-)aec~E{vgp1SbsfhgcLK>8VI-NN@yX(QpqxB)Ef`^Bv zXLopjXLq@WZ|6(j&Ih0Y{^lbb9-Z$Ik^VAr1M>L@-Sr$V%56d82kxL&dD-9p|6c}z zOBHZG$Gg`@`9H)k@a1LqUa#8$+SU8g9kk_j7nphE-~ay*L2!Q)G-C>P4`}`yw%@fN z0i0JkQ1`on$M+mN!Q*>pLEEFQfUc3-pTNMt02=4xcyZDJykOL$^DuwQRYnE|@K!&^ zT_6Sgt#cT`yZe~<TS4oRAp86ZKnoT+FB%>I@ANyw^4ViGe=BId(ec|MCjQo!;2qhI z82MX4n__)i|NC@Sd+@ie0_Pd$0MP8AOXmZ}gHO0!I*MbOA27OfCP(;mW;=9%ZjFVk z;_TiETD#?8`H8;?wC@{qP$}a9{+4qfGddxAH4i>x@#x+Q+9vZt+!pKu{uVV*K7}p> z?Q8|fdGvzzoArWLczATmTmTK5qGlCn`uA=9e~g_0?9vv{A(mdfHhV!XeX-geYW8!m z-Od3oL5s%W^(|;p|2R`xr~5z0=0}YDExe%Vg-wX`1Up^jI14y%z{~PqpF+rc90&Ub zwB}|XC_%i`01vz|cyxoE|I!A`bzp#)<JkO)#iRK+%S+I1ZE!t(jDZ2PFVS&R{L4_# z`YpptpzVl`&99i!I>Z0H^oFJP7mp8t+phn>0uBr>4ud<-)d4SFS%Ip9`!60@ffayu z3%uBN2()Gf5?6-*U*AT!7dj&kE05VddILB-EW<hYo4OdlmjEcZbZ4-*bUuG!6bMT6 zN0~rdKD(<0K$ETvkekF^I$wG4yS{J%U5)+nMHI*(tp`d%Ji56-l}NWIM<<s@cd&*> zGc%7zvlT;$wnwLvf=BCH(1PY2pv+#v>e1{h0a`^5Eo?!Dia`7GolKxJh8T8$mbAUT z3z@CDhS&W(5ckhQbAM3)ru$twUpejo^{J5DKMPb9gF35+N)o|da0mc-fdkBT3h4Ia z>GWXn0B=cV7Vv1c0Q)BZ;-6p*!u}D!@1Jn6Pf<r0B|N(MUAhxFT)GQcTsoh6^oFd2 zdU6iPliiw*o!=e#w|(vW-dUo;^P<E5|9=nu^+!Frt3g8{Jf5ICA3u3?9(=J6)Iw-I zz~6F~fq}ub<$p<m;U$mOw>x=2TQTK8^}0v5akmYh2k6+O=7TJqHq*eX%q*{#KLedR z!t8PI5t9d_FeEyy82DRiz`4jt!Lj+RHz+<G8~!quX?ZYaOY8u}EZUCsv<ZzK5e!TW z46oav`4nuwN4K{`iG)Y<U#3lrbpjr{!a^Yc)Fb`=%7gI~#H`L>jB8^jcyuy>dxDUJ zF<}Bc;JVp8I)C$T(*Pyk&i5}${2;OK*?9yMEa24J`Oc;Dl@Gt`16RWXKAjI=EC5Z> zf@1zCe+y_oG&GVqK#|PfatZ9y3<ZyFW{=Kn4WC}q1v!iiKHb?8-2%<8c$$CN@wd+d z9k!dyGR=qa!~X~1{0u(vAGFd6v{Dch@}NQibY_orF-LJxw^;Kpk<x=6&Bs`LTc3Dz z8iS@-JfJ)BJUU$!KsJ@IfG5K{p+O3(nV|diJMX^$o$CiW-FwArwFw}rH2#1$N|wxc zEe7V_fbu(DvrPb}F1QhC6FfRKUTXdS{~r>5&tUgMyYO#&=F<5*)T8riut(=<P_6XB z&=-=d3@?Eb6zFPeP@2m62`*4eY@t!>(fYPT&7)UjEvUFP{05C@mu?<okM4>9mrfpY zkas;Sua&+AE&qbJ<*?y_*B?RY4>I)(E{>45NLzqMqp!XAYX-K=qw^@}d?#1~u!Qr4 zi6yAiL*69~X+L;$gZ4>>sBpYEWD06Zhp2G8-VE9&J^9D~|F8E<fGzt6dGoVJcNqsL zdM%F?t9yV>VCW5F1h24U`~cde;nAt#)0w~lPK+RDgV#s39w^n`1zH%s6IAAVbQcSF zbaKA{wT3}!g%v<MIy}0IIbLsux~G>1w1AG)qw~<~OQ7?A4!quivL7e}bY5F1XpLR4 zM>oGucO$5(^X$!GxZu&t5(E-&*a_NL0J_PI;X;W$=qL$~Zf6M(%R|Lc;1mn0ArC%c z@#ypx@aQgO@aV4P@aQxV@aU`&Kyf=LB{F~$Tz4pkN4K|zM<=64XCOq4$8l%Sl2`CL zDv&>YdQ}*9Kz*p;+5AVqhu`S|Xv!Y6Xcg490r^%4R08;PCQE=E^%As|Yl7i{*Ar0u z7lv}f`PE<#%P<N47FBRX@5ce!eBvhH(Rs?l@^Y~&Xq>c{MauKy3;$jo2ftpP6Q0(Z zwbDM8hkZH^6{~qN-awVAdFpAM$-&?E9+VAxBN#m_FBk0t8P<BcM9!y|CED}g17@#Y zABGnm-Gv;WF+2~;cl<5jxeCk6#S$L9JkcJ#JSRN54Z&wof+Rr~y?J)?sCZajEI0P- zJm<^!jlX3X*aDG0(27-$&SNj!Aax#p3+VU-(BgXz-%b-1Ptbw=9-S9GG;ezJ%0%<G z)`N<WgO8a(WvXW<c+;ki1ZXpXfJbMJ1P7Sq(vc$};Cb9dMFLdg!P6ROwE`#}Ji6l) zJU|<Vy4?g`s(>34;CKY3N5@#Oa>qEN^DB2XfExJwKr#0+2-L9vS>G$->CtN<;n7<o zAp!Eji({sseNNz^BNr6`a5Ep4Z!Izf_*;L2R$_H(zD$9)|6Rc1pj&f6F~#r0c)+9E z!UJ?Xo#oXM0gui?j+f^D!3_}5jlM2c83LuRASbsug8j(>y6_2f<eTQLQZ7(HalB*) z@7@Lt|M!Xnft=;lTf!jW)oa7>LJjOb7ZtFs*B8<d^*{K$Lk{2WPSBwij;%Z!OH7*| zf(|<@^XP5>t@Q<67FsIk(OYst!;yd63GkXH(ELZQ$q6t&8{Bq>Y=1tSHUYGs64WLF z9ZL&pPQ!PPIX3JCt<GoQZ&idGpyb(|%K$kH1$6Xqtp$T`=kwB#*L9G5?%7?h0V+CP zcpHIBEdCZydkVaFtJ594jjJ<U!n3<fz_YVl03-|=>jcf1!?$#O0%^32wJ6g8Z64En z<_p<B2HiO3*~#wN8O{MV!Ma?cydPxY>rEK@zmh;TF!T^PP#S`*FMcuk^Z);Fhd(Wm zgT}XKcdZ84Da}U>Ud%W6|KGzh)}s6+Xno#95D&Dr&DHP;(iX9KFLoG%N><PUNYECs zK5)I~(fJ!x^uF*h0PPph26xmvKpTocOBX>)<dsdp;o{kOq(mAdVff9b^SK9Ty*Q{u zVKw~!|0U=Qx);B<|NsBG1KvId)$fq~lAsfAe7cu|YI0A@FXbXWosU8B+I(2R!}4|6 zcTdYZMM|KBGT=Pn*}3)r17cMuqJ8Pvy%)475wvm%w0UtZ$d91ql*d4=)LTBC&keu% zXg&h%3;^x3^I$ywBFg|Y2(lHV%d>MX$okhaUbub&rS;eI!R-$e_ks3Vds==eMs;5i z*nL8tow*iZ_d&9+W87hnUXy6g?!DlYd+-_a3m$}nbUiw6fL!wc6iT3V_@2Es$3Xey z#dUqKlR%n0JLiIIe7*F=rjKAJEd_Ni5%~qwR|Ksr2G!^u*5wxEY98HFK^j4O?mT)~ z4jps&0d9yxodr7&;{|9m2fPvo+}(oj4|NQ8H9P>?kh+fx)J+7H?jfNby}Au4j0~WI zbwM|qcy!kbfVTA;ys-NSP7NNN&Y+4BbXpK-vnInaXDfzd&ejaaoNXD7Iop92taA8R zzTj^IjmtQ8mohZ}3*~R;6k%ZK{N#}wGL^xD@zwuF;B@BM8_MHhd4a!aF=#JSceMb> zEsi^wLG9b_S{B#tQV!SdT%H$4K7f-Of6HpHx_|(ePAgFF#HF){1wv<WKw7n+Mm?xy z>%+~!0Pd)CI%|Ngv4!+UKpPhBzX<yRS)6Pv%)kK2?>^ny9^KAhSGR+D^`4eT`KKH} zp5W!*cEB<IFsOYN@fFfOJN@#r5KO;kZzu=YAGd`V7#zDpSwO8+!;_E>7RVPbm>@pz z>^#cf@(gsWNVh|POXoM2P6Keqs=Hd?1!$iO=)AAvU}?~yNfr$JtrNh5-x@D_m>C$r z?ZFeD!1nXExc~qE|7AYN4e;>?cz6aQxdR-<`;@_9TFUXF;ypMl!7hmbS=G$U1IoOh zhOrCiOm5KGMc~C$pu?=ds}(?7TfuXbpoj;r#0KqUby11%=ynG08Bg%&cDC^7bWzE0 z=_pajc(LR!#Eq>N1R22lQVp6dMEG0xf_TjbSU_6|b3Hnl`M0^KWN|oKf#_@wXKN6h z#}RDH;cVA(sf5F^(KmvDfsvu*0F2GV018;JhYT<6i(_D5c(D?+0~Va;`CEEH=66>M zz!ZQI^@|RO0{)hKuq>!23rZl#pp#iTvlKj=Ye7y7Q3>F0u>@^+1hw0{LsTL_on8JG zIk2)K1<z(X2L2Y%E>F)+H-(o=A@vSC-X9AvFt~P?vUqd~yS9ES5dk^VrSsbh??0d+ zX8xAX0w7CZ=^NCy0&m5B@fx)C70tz-okw3>28n}wb)3Ity8vh-<6HigbpoI*&aMGI zozFq(hB^`U;>T}jT&aNF2u=baDjqnZCzr$777|U)cHs7Evjqcx%Y8x6%x?xPFkL`_ zc}Ng+*P$z@$JK11!rvk+1iH$kmVtr4WseZ(N|0Iw&_H_xq<=PF2xLNqg5d#BUSxEZ zaBQw+5aDm_;s@zE%Hq>mt>M$D{c`?q<ozO^y`e1Ne3#6R5x(+}1kduK{S6|)XM@#& zvYr7XbulyebXN;_G&6JfT5ktcGW^ZqpkO}ujK!CEJE)p)Jm$v9i12E`AJFp33<V!h zFS1+vB_GH^u=Qr}^l+IEk{(>UYdJidkMcBBTQGvs1f*>9>C~3|1}f^BpZ#b)mh$51 z>;L~zRd{wDmHcLS$?#<JGmhqCF)t3k2K&LI^ALZ_az1E!n8yc658%Y$6yVZn;MwgO z;L=&a;?WJ_R<MBh#~nb!mJA@F;|}00jvycVT9;e!xA*XajQUn0?Fl(s+Pjyh%%j)k z#0$@Ee+!?^3=WS@P?ri+JamevfHJrNsGI~bEWll?PEyN<7o9)B$<3o%M1{X~BQGf0 zd{hjYEk*cS=Ye^kf`$=vblgD}P(sUU@U~^-a<=m5<mPfw$?QNcEu8IqTfc$aQX=Eg z?GfPJYg6XYy#ZV&f==)3^a$|i^!fJ!5rL5cpp=H1R&7904$k<XLJgdx6+yY7GXS)W zSHq`U`=#2?|Nkez%h%65p!fyVP@x>Ime={)AMr3SI5z)c^yn1kZ%O50U_itt=mtBN zP7xI!(B>w|lOD|{8Jqvf@=rNn!`R`X!U2ls=4Y(U$7EmJeF=_b&(4GVEeCl(LE|mp z)9q{kz6`p1B51p-r{%RGY0u89kb29r`KW-e<-6kVK9;Zeo8I#<F!)#(dK7VZ_PQ~8 zbUJ$Yg3tQxZUi;2of>&IFtC79j3XqefGAKB0nq^*{M$gglpO;)19%#$Z5T>G>pcZL zx*Nb+n|U@cf=;&pUCYBmaXM&rQAq$FI+StDMJ1E*McH?7lJM!~QQ>c$2dcP1Hz<K? zjBaiQ23OE{0;JN&^5I_}qvF94Yy~c!LR7LrMMwZgur;_!@nMXX;0U%o=4{8vzbzo9 z;TKnl;A=5ZyRw@BDp1Pq(OjXxQ1TX1>@@PD$COVekBSE*mKthQI2a+(!_<5%`i0#K zX!INfJGDiQ8x)#>0iaw4u5!SwD3Af&LY)Gh-4L1XAePPm76gx@6BgL6puh%|3Wk?n zJbDf`o4@5G7bxC?H9%HCs-RMSP=fVR@NBjOFHH~NZz%$mik*%Mpmh__iYgqe3bj5{ z1uJt^c=_!cXyO!fe|)G3xV$vx0;S(lcsqrYfq`M4G-x=o^V5qkkXyiwF_+FyjyqUE za#5f@bLY_)Rv<Z0#dM6nMUD$pdc5Ut5rw!}08~zNH-SbTJbFDCJvzHUOwaDnfKDdR zO3zLvk8WYlZnuE$Adb!e9ymt;e7+Y{Mxrx-1umn|86W`WxCV3w@xb(eI3h4TAdU=7 z4~V0JtVg3WKn8ArPG^7woMX@#AOh!@fHHbBvju4Qs1sZRQq%}K=Ax3r_+tJSaI)ra zJ<S1%QWq6SBM3Bi1e)&##hr%-W3U7$>1KI!GJ{fex3dO_o68Yw3!?Klg6)nuTQfou zYZ5moA)uCAaiBDaT5>sql@%!XbQXh4uADER$On%z!0St64n#W@oOE9#f;QNKhB!UI zt_3xdm^r`+uyxB9(C8e~%MD+^Q)!^}&Ap)l;B@qw9nBo@SOvJnD*!PN+|K;M4hjKK zlNL0I0BSMVb3*(ADOErf3Z*p*q~)pg85-zeU@O6auEO8y4B}C*<rxWTdFnp_XIape zh!$aXP%{PGk_0z?u5rK`Kky2L8|>o(P<sfJPpFX0Uf4pr0<D>BpzwvR9q@1kt-yn{ zHNhnkxU9r%X(Kn~K!w(exW`Zr9p`UxVgq@|4%~PT0F_1jEm~Y4Ke-{d+W5IZu~-4} z6S$36#R<{H-;%)zYU0%?_;hPSTXF|jL2^e~d>FkYV6C}}A3;gefdSO7GI$D!xt9D- z|Np<7&dR`$2C8o%?Ym~sU6r7Aa(5|cJe>p7c=GAghIFv@af6~`!6VQ(2BPr~nlS}6 zr8_|4pc3mie@hN4w8TmVIi{Q1P3g5CAYN#_30wIEuEdUkd7w5mBWMl;T9rX^JSdlE z!|E@jT<_BP$)~$mz|(pfq)|EzTywX98l<41jAJeW9E_gbV7^bM4ydx$fD{r<?4T$? zEhOqdsS~x3NCqpbPyok7xAsf-51{?up3O(Wo!};6u#*vi3GPYRJwy#*kf3Mhu@`C} zaZm^!<!|8vpStpmzl9NO0w~LavU>z8$W@@k@5})zb@zjYNB{r-Uk`3ZcjkaD0fzTE zq5E(hzysr*p!Q%lqT`9y-+Y}0ns)&8$v`auXlDX6SM1XXn(V1jaR3)@hL^xO;YIZW za1!TlNdjMz3F^~zfLNWNWDT;{8gv=}7k_gXGXq2OPZj>wMzHiTkLE)x;A|VBV({YV zJ7_$A1T8WFt(f$*JnqBq{{!4$MeEQaXERVQ66DSR{#H-Wcq!x{GKi~NK?4uaaXg<+ z4e-P^yu5B^LX@+hNCJDxr&IgIs{2q6f!5_hhCr+s_*)s@|NsBe;}^WVZa&Ha8j@)O z-PZ|fae(z3UfKuh8oWq@m;o-Sy+A%{W(NC}N*&DUZ=s<Dx?~XKdq_RF0W@iVR3w9B zJUkekC9nk|!BGLEh>if229xeV!v%CoJUm=l>cFEZsI8<#a14hcHKtk*z5W0HCFf62 z;e=Xmg60!CzjvCbym)B%|37S`$JOuv=-9f>gD<3E)-r*72bng9)S}?+gs>4Q@Ps+2 zS8?GD$PVODjTblWLS4QeG~WO*f(23$YlECs^aG>?GM?qw{2z4f`e#tSbmoAiPf%@2 zZujifYj9+hN`ayS98oPx7~%UM!Sj#E!(!kuv1>k^-$7edUdX-zjdnn~cK*Nr|DOP! zmqb$!%C8_H=n$o2418eQfdPElDpEFVssyiOXK4N@!rxjCS~A?5#p1(QEdd$|m;&k( zckcvsP`r9gz!R3BHSHRpwY&<T-IEe7ogpeBpz9!BG)sX4)wA=kM>mg(OLvHh2xu9- z1ZY7$IN~+<TN1&`D_?l_+Dtb5=An53v@jTAn8(2fOvnZv@&RpWmj{^%x}MvoTL#@o zpU#;eclmU#1a;b8rhNj%nFE7Q=Qp3u|1ZA20EH8O>$LaKsoR&JL*PO4Wng)5SQfqn zsey!L<Oi5K(9$f>r1|#0piu~omwUf~XoHtaeu0tz{wfC41qM|&$c;--BQyX~ae#Kf zdVt!&-Jm8*r?bY(XRrSM2c3!f(()b17LAv(@1O#z-$4wvm-AkMB#d6(0}ruW^6U&{ z;BVda1|;dq@N(-LkgV3rnP7oXhL^LzV<%cK>%g?Z%a7kcuJ!?qwH*MLIH2?Nz!Phr zHh3qG%8Tfi;N%F3G>}s{_*?5hlXal}A(mE+YwI`u)|H^13TRM|zoi2-*V9?6-~sMA zfg5IRpnTcsr~qmr!3X&=!MDj*DZFfW3v#i>%UNGQw86`rU!Y!@3l;z!2p{_vG+JNF z@X{VMMAs?s^7ebEn*HxVnlxT6hSF6(KzxgrGk-u8)c*MY|HaLlu(;d;O5599RFEqO z4@*!Sc3$w{_j6J4Kpva40v*)W84C6be~Sv(#gz)+<MMnv?Osj+1!Xfh6hUn{@X3TO zDh4m4UVsA-+=6R$0xgcg5q6+b7sVxzY7Klc9Mm1)00gx_JW<;cpseT9S)uTfADkL3 zUcUVb@-f@XOQ3@zI|W{@{0j}U;(wqZv3e;1N-v#^h6i4j{rms_n?s}me{1Q7|Nk3) zx$w8=fQHI@Swnn5SNib!f~aJc2_B3m{y%{9$PY9>U}`=P@7UoO14`4N<XfWd)9C?j znFe@vIx6^ddTYE?e*v=0@})3nLu6+t!^?wU+c{p}@aeR8nGANLE5l1;(7b9ZH0_@I z0CKr2!^<PjL7KT<wtoQ8S}&J^90gJ($luxoR>b(S4%B7>P0+x{-y!oe&2Knfd=dez z{`>LYqw{bH`2HgBg#_^Vn9pM1@~A}bMI?yT`ToU65Zw&cP<sBwWe~6RKnb7WxBcJ# z|Ns9&$>RV2*V{dg9|kp?;QO*XtZf^ADTAhJK6rHgF5!Mr4Yjs}H5xSE{GuCbj@gTk zqM%OP{TH1ek>(%&OYgnN12J0<lt{uX*=zp)zu|#s#QYj`4<N`aa6RZ@Z7cXonE|}$ zszf6Ag$>9Ah@(q{L5GRnj^>{VzIqN)KfT}vDQG=V^4Rd({=Xo9M4A8p-+3eY^=<I{ zk7Kw;FYj_$6$a46AY`{Q=)jrIYaYF#GowLsP~bDwS?ByzW&rKExA5r&l?=ySR6H0! zwPPo!%?KWY>;z9suuA__1|Mr30Xi`Te4D)lh!p^;5(Pjk4-kt3#B%WHWKl6Z@Ipcu zG8+V11p-<}ZDIKB1!(Tlqxr{w(AKmT&}q0J2?vjTpuh*UT3<ASrqsa-j`FvB139I; zMkV0Ioqr&+96OKkcWi_TM!eW=2P)-k8Prr5T==*BcyV1Ad^yJjRmi~}5klak0ABD3 zS&x+=;A(i%quG^#5wwBI13c(s;L%$FI%*qqfGY!NeF10=!MF3panJ^K@SUd*JwPj> zYE(EJ`KKOq0X0$HZ2?swB}&JeABceFQ9$eFk7R%oBD~gxj3a{jUlW8ukq<h-95$T^ zo^1xl`wM4Kx&n<3eE#?U|LYsD@$MJeF!4X&n_yla0A&ZneC11N&}lCSzGH|-FK@WC z3Iiykz+-xUBqXK-Ji1voeTT%f11P4!yQx42Y=Yy|0~Bi>&{#_n1cfwzixj9l@2*kt zc#-fIB;we4gujCoENI)OsKW3f6BN9?rb&vB(0(O=2<>~IDC*1*fCn{bui8wIV0Va$ zg-36-1o%{@<1Q-jPzR+tSdhbRyzvCLia^Q2rBg)(blyklW^lk)g4$3H9-ZLeH$2d= z`y%Lo!qOm*&gK`O6A6zue*g)9EbP4DalH8lly|(DK@=We;QIt&<%S2`Do`?ZcnLa3 z7u=qLoFn{tDYX9piq8Xrpc3dv0OY(7=z25g`C#SX^T7&1=Y#opfX4j0-7`E6K4kIe zb>X<+(QV+-`mI74yx^ntK!ub?x4(i%cRgtN9*2kK2@lP49-W6?2H1eiXJYi|1m7re z7~&3h1^5|aa2<y{G>^ShwE6#k0_^;(5MyXs1FeomSr6ha;n7_#;L$C*@~bj~N4LdG zP(KN=egKqywo9U=pWbjt`VsKxW-a;(N<W~rCZH443=h1p<Od~-l3<VS+JqN2pfCaT zRF9N6c{JB1FqHU$cOSl%faC+uPM13Z9+t5Q9=*YgWriM|m%*(L(E5VT_b>K=GJWfT z(wm;mIx37MN}yv=;Gql7Cm<2%at&~Jc!CyOfOcvH7`(m+9*1VV_(hq)!}4GWw@2%x zIyMjMaECHU!%K$WTw4y5Sa@`vfBC}-)K-L)pB|94G%OyyAz~LinyUmDN-leJ8+mjG zae$Pb1uF%upKoSh1eNz_>*0(%ERBnUJzD>lgn?Ek_KK8&SGa)=>I(v$OVN6u#1^u| zt&{<Lp-eG{N3TekC#W+6Qj`k{uH(+AYnedyfVz;cFQiR?o-2kDKVAZ$^Upx*J3ztL z8^rMfw6Cz`0O(*~Zwrs!U;~fdQVr0O6ws*#EeA@pJi46&JUU$iJi47DJUU%Lv&sn` zovsP+9s+#5f=72LXps4Z3uuvicc}%)6bpy{@bk?e>wh3CI6&)vIDER*!P`N*D?Gra z`L_Nq@%QYWqhbMCNYHw_q|~Rom;-b`ca7m~!?&>gWT2HN9?gd{d@TQ#7<+)uS-a=S z47pdrler9hD7N5#P#!P63R-fGvdaXN4}3efKx}9}0$GrUQlvUC_;znmF@T&5UfRwt z&#(`)kOFi*_UxJ9;>ok~SjjT)UY2^F&i9_ZI_(|@pE83AmKvC}n?Q*%XfeYp(4Cai zctM(Odv?ls9(M!v=ov~leL9OdUV^US067I@)k_&r_X*U$c>jXc9+drFg05XaEw3Tf zFev_lJ-XL}4jlGqt_5981X}*&(`{~e((s#SbDaf4i90`d-$u8qglG3270@b2@Pa(h z$>kp1EF2!4A^aR3oh+b>eIz`(LpT&XyIUa2Izu=VJem(0cv_ZOl<zaV?P~bev$@WL zp+pn3p7j<euQ7)zcrrt-1~>c<T0RSkix=}jtw_r}kMf(KWsjbnEh=CKcDsTP6aw`k z52LuB-KRUl!?*QqiIY$Fc2InSVi=OoVNn_;fUpM^=P%4beJjhirMEzfwK`kC5ebVw z@NtJo^;tNwjh&z?7&&~p^*y^=K~D4Su4Mr24{N!~02-dn0G<EN-va6of-lyI72t1~ z&A`B5c**d9XLG#;Ly3cDH~5rsk8WQH(0&=8ZXPk8?hr8!pH3b@pUx0L4$p4L=85hS zK?Sg?P7tH{kb$SAyG8i{P-uShY_9iUC>46`lr{mh{|yv&zLw=4WkxVxLskqimnwj= zk0*GA0*-X-*|`?v5ztCsaQ1-(1Gr@Xng{4!3p$|MqxCI+D`>04KG1=HAX6_-10~4= zpq0MMJiFaN8P%iL!~wjfZaYY>6O?e=JwQVipjBhNCKEhB3#Mj+<hpAmz^kWQ|CfY- z7N+`xOfdWgS~vw7`+TvC3$)(R-NU21Rsky9D>4DRmZ}>p1rB}K!So)WHQ}xdB}N{d z85}R0KxKI6Kad;$TZ7tVkWlyxVt2j+3BRxgXAaP{QJ$S(@4fT|Z_@ymvu`-MnLK($ zgVj_R96Jv<9(*C-VR^kI-J@63L0yFb#IEiQ`0v^IvH2HMxq{(=#-E_(W4U1GNpJu< z@^2UM{C129bUxt`j>C@4FE|{Ve=wF^>pazY%%}4_D5PILQ-F=zL8{iYgD+T{A21$H zo6sBZzxfwanFU;*Tj#mX<6upV0{{QR<R#$pYRy0E%WkK&9^jvH;C0W0&hsxMc^Da* zUx02g=AU-pWf^!4DnsY_7YhX#8D6G?R)`|i$ByA1ofkm+|Gt4%Rf6xQkZ`qdi}mOZ zFYxFtukh$z58B@6)4d$D+0T)G%2AKzUreBV${hSH$3dyS`4>w`Cg@Pi5@FC<MwA2u zIuBQd&8OFf&&Tq25zlMI381x$K9;{r#QC>zgJuk&8#P=1{r~S`@uK7bXv2^;sB&*U zf_w`jy#4_-(>XkPW!imuZTdlm^1oIFmB${<hgm!=@0W>z4DIHI8mk92_8~T7K}UVS z&t(ShbM@$zar5c5@q?JF0yUQjWUe^>c4YTn{fo!F9v6SW&81`bmh-$;ngBZH3KYH) z{M)?$gTewD5-Ly!-^Ugb;LHwfk960lAct=`|7&PCyat7Dv3R%lA0%VX{=pXx2Y)bu zjRxf_@VR`5@Ga+mtqKX>*P!q%mf+v+{SV3D5~#sg-3tj|aQzetUBt)%YHSG^9`LZ9 z4m!QBULKTXqGJy?@81CG8A*bc`f)@zKlli`VGiV;&aa@;?L2ym{zDF;_UOiP5&gU0 zm^Znj)~5}1F)sY=&sZ23978>NS*O}EGWd2Lg|*WDLeFQE;NKP&<B=>f!GrMvXyXZJ zKJ12vc00(akQpve$!&SUgWvri=&D-HgD)mS&U*x%PN;d@)AEl;=MVn2Z=n7BmN)oY z-!d~Wz<0t<Ipo+8%7Dmx;PzW50rQ1p{{Mfya02L3cF@95aM=ZNgyszos5^RjBwqAJ zftsqI@Y1~D(LEK^1U5YBp?M25HQaK6nSsHv!x!0RLgvMW{{Ii&-vLgBVE=<otL?5Y z@aQhB@a(+d2swcP>V(dlo|*?<Sfu~|{}R+K1+}jU`Rlgd|Nr0%nV{`}?sm}5MnoW^ z`p?(*|9|iW&9MG1G(3<T1$7ZZU*}DqUKWWLlT!cxfB6(%ZxC|VXTJacUxLabsJjTM zKMXof26Vb8R6TxwnEv|z|78TI=EJW}miPbvmj+-*L-RY_9Uh>Qx;k%qcES<>|2Dr^ zNEJ|D;Hi1=I16Y<lHtXz<p2L)rh->Tg3<+CKmWF{SdZ?q034C{I1%K3(B58fzJ-P} zNE0-GyUPMV$MnHt9vsMT<NyDE*#Nc->RyQdAdbC((17e%pI#mbm}~#U{r~?GbbtuF z-wewapv2yJ51QB^sZR6Ii#4(T|G#VpjoZqD5<3IbouD==JOw}$_liio$O9b#`|uCA zGKabk9v-luLnJ-8*E}>2z6g%~|No^NL@P=-LS5uxd62(d04{HM>81D|(0u_=|HB>g zG8Lo-K0XO^Z+CeG)M8NH>b!*rDGyM-{u=rJ|4Yz5G{XZgK`9Mhzj|68;crt1ZAjY- zE;#w89DFGO4i1R_3=e=C$Z-2Y4G+LeV9V*CtX9thEqWpG3@v9nui+_X+rMEJv(Wg0 zl&_#Meg18JF&@3F?v{)U9?3i%;L+l=2`^6vgGwldmy097w1@R{P`vT?WP|6l7z__U zYk*xJK@B07L!hBgP<TVs)!u)(sv3E{>C0?husJV3dxL4Hd9~YLDuL8Zfbo~Vyi^66 zPk*_a2dwU8F?cSK0Vd!6@)2lY45psHw-~gR+YY1$94;@7q55{agY`r8@%MUz)Gez7 zCn<zG`FlZw*f0GM;+@xCKB)NrAFfXrG|1g~?PYfb#JtXHFAXX{?ObS$$Upx8sETy| z3>_ze_@BS`Hh5Ui!x|iS{BsVVh7W)5Zcw4pdF|!qa<I9E2O#>Pp(=Tx`N2m}ee?2g z6<8kBKlcP}euq?n(E2aKqdSk|n`@*(seD6ijDNW#|F%$2WcFDwGI)0Wh1ZN}6Tm&P z7!?lBP8JnNorHQ<rboA{hevlm=<*P7jSXu0pXcwp2U<K~Jr`7;^7jRS);EDl>FWRB ze0$JC^M+3+=v?4lmL||aMFOB>og_RuPy2TM@$CHX*?A1q$N{Ar22aa7{4LQOptTIo zKxdtLT5p9I`j;Ja?u^bd(8e>*?%E2+|JN;#mRvAA@DhB+B+N-5*M$c7c7qSa@aR16 z+1(3jID@+=pp$C*Opsl=0<>(S*FV9h7vj{;7!?JO+cZ2n|AHnb1w1-$`gXqY&;)tG zxAU(@FG~~1Cm^?bb{>B%Kf%-TE`Q5@@KTR=pi`wht+%K^9PG%!z~J6(vL52#8kLIw zR~;>nm7IgLbh~R*1Y9~lb{=}M%LP2lvCZ(n%L$;khWR8EbU+6-pIidDjDx=~3pA^4 zJr|Vc`FpQ`I1ryOf&v}x6Oi-4S0DEB2!o2;@3%l_^MDR61f3@6(RtbvbX?rg*D8>D z4(y-zY@qOe2O1Xu`3GV{B|8Iy8~8-s?iv+P@PmAF0o^xmoWZ`?VR+!>A@DpRBtkqo z4}!*11w6Xj!AYm}B#5iv(cKQ}-FS5G2S-vTsO{TbQ2;s?QiH$G5)`bxHC=zehnTq< zUIGu=A4u>7-6;{H;sA;%56~IoK9FPOC0sgBgL+05zMWTIt3mwJe3TJ%6^G#g%h&w# z4tO@dXYtfL=3|+nBEa7w!^Xg1`MTcN*AkM(dS`;xEcKTC2c<92$g+ar+y56p<C6xE z)7?2hf*SuXbRO<?{_oh$uld4bMt4X{faS%K>F|J%c=5ss90o0h2VU+2&j*5{5}IXP z13X$Ud32uV?*kp2?+J@OQP83gNDTS(Rx^5h2aVEzQW^&&mSR+}<QWBic}RZI2Cw~j z2J)z<HTWb6{$9{h5a;fkWq&}Pfn=7;h6i5if*s@<;MsW}bkIL2H<J@JPOJ<JhHpKZ z4<tb2=76i=B~X+~biM#N+rp#sE-0r%;>fr27%0sexO86j?EK=`dHl5+ti9*he4G(9 zhGKZY(egh3+yfqrM?r@R3h=k=V_{%$vHV?c>}k0coC5p!LDAD)TLC^x-bF>h)$l)# z=<!TG?%d6@1sXTAk>bY35ga!ih6i3Mfj9f#u2c2sE&dPcj5z+E3QC;KKe+g(9N^z} z0MtJM-LKX9;}>W+#7zKHD0(u-NqBa~fVNs5!8owZ@Brux{CNjnpMa(8&U^m@JX#L$ zPX!tHdLy#109<$$C}VcMdl4V_|NrY=59|4$qL9Cbp9OT8f@3qJB><~H(dLh!T3vkp z|9|NZs)Hc9YmY%Cyi5N72hU-H`eyS%+wu6P9C!&j^#-co7wiB3FF{wnLHSiK(0l7( z?&z%m84DVsefMH+@&ErXK{t27C8H6N4j@U;i8D}>6hJ0J>Mzhe8ZRz<{Qn<(iY;6- z=q^L3V&m`s|G(@56@U;A@b@wzOl~Xs|NmtnND^9LfP&Qm7Je_o(PSjxGPWQYn1Po6 zAocNl&;TJQ6hQmI;BEm;q=CZ-boT>DmJy~L92;{Fy!-$z&>$ri$N>RxYp#K0tmlK1 zY40TvryFz<{5|v(^>Qs(4q=1_+=$6&G6ry&N;DY-E7w>^0(}_=l7hrcGV}lcFZ~ew z$9Dh!zqAAKVcy>e;y@H1EBOEar6PzAlLVa_42_9QkR%I85~6tJ_y7Mvciw#j6&<~x z(Y%M?qkG=J=m*8wEs!u$v~VDtw;v?cdGE#c{Qv)7ZUS*&)~SMxzxN^*Br^jf(|PX& z1Blc82O3G>S`^f1y3OBT20H%_nh(HDo|mcLL2WNcM(Mo{(*urxIR{?4flP$iwG+hY zy!WCo@BjaoI-rFy;IWWyP?_4Dq9XB95h^ADI#^x-bT*~NOJ0zTpvwOPNYKH>@~jW& zm|l>i2Y*i+Xmw6Ev_NRClVIR)dGQ;x0>ecGG8^&o=(qp>T{?d_f^Kwt?`U}ltQ%|; z#E5=pOe2<pji`Yc(ew>u;?tMR;FH6izWfL(uOLx!4HQL*zyJS-6%IQ<t?ON&xl?}E zLoYqPf!0{ufZU{xI$i>5A3{bN!DHzf&Hot7lRZEeonQ6n1`pkIGkA0dNqBVY9P;Sp zISMiobYg|QNAfYBUK7ymnib%CubM$Kz|A&Xke%Tr=Acuhk;WchBv*qjm2|5B82}nL zXg;C=zKZ}!4XAuI{Qu(Rr+@#!?K2k@j*{yho$p_ye}jui@V7hzjdFG#f3fg0RFp-9 zzoi&teYcIui|jA|{(}}0fCj>=Tn(SRb^y2UL3JVI8j4pwozGsdf>eQ~C6AYcLWVFq zkG*jC_V2%Ex4B2J-+zzRlb)UYC5o*FDoi~(U-)P~03UqT2fEOp^>)cd(4q6M*MsWY zZhsEYu?0Sz|NjRXfDToDeH!Esk8T+i=-vha@Ya1$!<f008|;Q|caHx72B6rN0IOk9 zK~loy(e2Cuy1oITf&)VZf9rlwDF#;s4#t-~;6Vfi(0vh}2cIy5#??Q31qTp+OFeiQ z&LhuGw+he{*6|nDK7oAd%HY`@R?*GjaquDYO9zO4(9y{r-E1BpKfxWvR+0e<B#9S# zu&7~C;cx8)U4t?Q5()gRdZ5y|I|g)!J2Pm-09-S3DUy#s*Nubx&CxC3+3i*VI+6<9 zt9W@9Jf`5l;L+{I;nDg1e}Dnx{C`CH^k{qos;zvw%|Xq<0#L)QqH~H0s2kidMWq?E z2Ccgd(t30NU*F^4+1&=Jzd?TS03X>2zDLqUC4;}k6*O=MzCQ?bekCYwLG{xA5($sS zBOqH)E`J5bXD_QDC>3_PfDQ|HQK@ij*atq}z@ytCpxc9`+kvCA2Rhy6qGG_``Ww`O z1F!sQKEMo~>V@8e3c80V<3$K00fOAd!Qb){<ecs~;GP0#C4m`2R)fFgG&2JODBf;? zrXHm~{sZ0m1M)Tle@iN;nGac%?$Nyltp9k63h0z025>Gp=&9WYDmXz`Mu7@Lk6s7x z-2xJz_E*48(CQk{iWkrg94;yv#~2v-=N|$I1Tb`7;P0Ep%)kJ;489NKyySyEy=%ai zYlDv01s#w9ULz7=cpDTxp51j7pk|c>s7dVc|AJ@tK2Q@LG{y1?B;@e_Lc<<#EHOBC z9`Tp~^0-0?vxnwUSHu6VtxxLoLCr%8pU&T|ozFo<VuE9L$Xo`GUK4H56a^^xaX9|J z;%Irl{uw9%SwPNT?L6VZ_yT6I2fxb;kX;%cjHf-o_e>mPkzfRgM0hZQPII-4Q8D13 zf1pkPq*=hj(nlqLzu)Qq|NpLrZyWZ2lM+J-w`b>DSXg>k&jDY*&EMhzE?<09Dm=T{ zJh}q|Jh~e|;o2?a(d{7N)2(s=oC`sBPzSm|r)NDa@7Ftl8ov^tj){T`|F$jQ008+; z<265|KLYMkS>CUgbZoGZVJHRP!{OTUt@Jslgz>bzU-uleZn8#21C$|O*uDD)K3|gK zg$bAix+%6>095KU>;k7o&>41?pyPQ%K7v&Gs1$f~I&!>J`wJVt0Ik8z0CizFJUd^2 zA`E<UhlEe(%NK^z!3!6mWodJciU8xwT+kp5c;2~}wGfomLFxbHZ}0#Vs2Riny59sY z{uoKT8|=0Uju%yL|NVdY2`uBnzyR8A4?fcsl1d#d+d#uw{H+T>MOy1^aJ|Lf!UdW+ z2X!k!_XC1bvuC%RXK#3bOXsr}UO&Ojj}Fj*@Q~98**ufacr+j61N+Sr)ZzFJiWip? z;M)zlO~73?ju(N`AR5>HgZQ&m=O6f*C~#H+r4j-DmMx%SL=(Kp1MHNS4d7zYfx)x8 z59D9y1`xxy&|C&u6Xwwgxue~)yAPbjIzdTIvjv=RAlVG8O4CIp;AKDPqQ;04(CKn6 zDxj%b2JnLImy$?M0W}RIz$vtZ$5XTK1Oo%Z%b$P$|Mz5su-}5(N}wfCpe$~98$6oo z$%y1;(7EvN{uFe+96Xa);nS@SDm)!Pg{MVlh)P69h)O(YodBrZ^y#k10I^|ZW&mg{ zO#$c#P*7&o;BT=26$9WxQw+jlgcO?6pvC1VB_?9N-lLcG=7)dKG6!^HjAO$d6>w4o z7n}j$f-^)V17w8(e=94<;BL^JGN58o<Av^fa1{oU<=}7OVP;@xs8NYv;BN^5pJ3Vo zz9H?ni%JeCEI=pifj6)8M*R2GhLnb&cr^eWwhdia2}=3~9+p=<`2Ambw7#vYaRrsE zE}-soj!J?L=os{`pa=kueRsO3C_u+OJv#4!F6XTP<yXi5*FZ}!Amy9m|LdT*Qs_Jc zE~7y9NqF>{NdCXz!5pI^;A#1`^e{+)fJd+Ef1mCgl>`s|^#?r}&w4T*HM|Y>O$moj z=WCFZ0qA^ik5X<BWx(GL>Ue;(T7VYRU@3G=V1>@Nl2n)O8kG!B%lGw>pfh>}JUid> zZ_81Me=P)afAe8R&}9Vz^%kIPq5%pA4&Tlf9-TMAOXQ!wFq#a>7A>H)*`R~o9a`R& zo&b$uf%Vm$fD}aF*3OHqFG0l%cq=-3@$(*3{9Jtlk`GaF@aS~nczFk0xr6c#L$`}c zj-zFZ3MAKq`+Kc#!C9?jAt>A}Kpx5Q?6y(y><tTW>HPO1>MJPQRy2bqBS8V-nS36W z*)@DYnf*C5vx6q;;F&#T5=a++2WT-HxX->Bl*1rt=L$HJuK}kWu+`wBccJBR_Z+Y| zC?kLz3|cGGy#}0@LCFl9lR;?=+-mXAj8XCM(7gL{8N7V;=w<Z;70sYR;3YT0-yXfJ zjBxSK;L;FeJR~`nX!>eGF8g~q6I$MY(vbuB-gv7Q|Ng&x0oLKdz~IXWRagLOeeo5* zmbNE?mRvI;`^g_X-VHiW_@iSd;tH1LHx?e<ZUNAeK%g^3MW(xe1)eKix<N+}fNn`? zeaQ&UZ88k}Euis!&>AjKS>)My!&e*HmH`dw-0-zL>dWtU)bONl=P@76qdt}&N;!Qk zzw-Bk&L0KG7-oH`bHTUuEq@E>w0=mqg9dpx>P<X4Ux8C3cwNYXN#GO-I)MRnKO^WW z4cC_c{4JpSUqNe!z<TP=7+wMemH^TbOJbn2!d?Wv0;Rzk6#>wty&j$J93IU_EIKcs zbT}9sEqkD85p>#@Z|gVkuu92VND2%L=sXA7hN98wqoVNQ!Y5Fg>O54@56MEwKA_Rz z=P)mV6oS@S@p*K+oZ#^2ya`H|CKo(9U%j|C5u%g7Bk(`iPap#n_*?Hm5+x{zL4oC~ zxdoi~z;1Y{_8&CA)cl(9<$Z9X0H+tvZb%&ac0(!{@Z1+Hv3hF0@X&k#jSt_>W1hV- z8ZUD|=>R@I1ioqul-?jVfmi5*+Hau4r@$U~aq8*6|6u36{100G#Rye6<KO@PzEBRt zeQp2#|KA0=PVQw5!hPV$r|x~A>!LxcAh}<7zX0{ev>8hv>*GBj-Ii#NZU=+^57H(e z)(dpIYj`wQBrt#uLM@K~t%vpY=(XLT3%VHgh3a!q9dZ=B`JnTiM|VZSi=t;BgL4@e zK@0vZK=<hhdo(kH&td{ExQ5J&yvTm`??0%&+0A<LlrjVOHdM%oOptWv(d`O4z8KWs z*aHd&(9)ZK9-Y_GPThMk<veKZEqJp)i8pAn{NM}wmII|4h6g+>Z+IL(3TjE$b9h?I zsCe+Zy^i+id;z^q2|9n{+VZyKGAN~Z^u{nVf=iFrM?vQ*MuWW3dZ1)AsA1V$5y4R6 z^<w)|Q1^@tJg!+0@q*(iv=TgAq6g{%aDd#>c@)$!><#!29zPZ?(enV^f!6kd8)6=R zO9g0U(S#S-Ak7f>y-WqS=D_I)8V?|UA45KGFw~>-19%;$2k30a?`@#Nu^1Q{BumUd zCzSP;CwTPMYj|{C1sxXOdFaJiP_qqu0`z^4&R5Z~9-YTta$W{EHo?s*29M5T`+tD8 zAzWqy1>g}5(DGfR^Y$RC`Jju_K^7}`bhCax0a~%^#!;f^(Jd-;Qkemq%0VLmv7laN z^AQDDCO+)Z?Ft@p<^XM0knrgR9f#NX$fLJ_@x{c)|Ngfgs3-!}%Mu=~|LdY(ta}0u zNzcxsCDEX6js$~Gw~9)gnNPRr>J!QghPNm9bUyO|UDx~mh0`U__KkZUpsPsVzfb`M zBd88#PEir?u#QoYsJY_P`2u?PsRM)I{}+Kz{{5fuG7fTzUpP3Z9T-66?{|nNA&Wdg z>%qJ81V9V;TKYkKwC2BzC7B?<2_hWl3X%r5CJk@<ber;@P-XyKy9rwV*XeRs0^B3A zd|guH*<Es90(8{-Cs2PM)P?tGJy3evvpJW6u_OmH?+XfU_|^k(J}8s$=oa039F!N1 zK#vawU2JK1@+AW(GP^|=Lgfs<!9v+F{-w!9&~%~$Xl)6HPv;AS3^aX%?xpwXd=E-b z9mwg)5wzc={k1X!cv|T@=u*sH(FjeX%Z;1gXn1t9>KuoxB9{O)^&|`ry!iY86np$F zpiO-*#9%>ljKAePsP+aG7TpyQKAj)HN7#e5AA9tg@@g`I*LJ@6_5c>o{4L<^1<e%# z4E%l9LE+u)AmP!i0UE=q1`Q9XLdt>G0~I--F(O~fR000JbznPOCA_+I9)MH^f|^Sb zh9|+-HvNAA8easDP#rEY2c5|XTE_VO#mC2>`UdG5&(c*MklQ%H!#P2q(IJl31C=vC z<qRZ>3_Y9m7)zwVE1ETs!xUa#dv?2V_<+Yl3=h1%37U^}<$#1UNNefQ*Y_aq2M?_D z2K)zy2zYB%>1xmJP!3T46RaV~08~qIyq=0W{)d#`{Wv_nH^TF~9%_CE`Tfv~d!S8^ zu<U*eT*`r#Pq{EKKr;C|CZtRb9(RMMf6vaVpeg7-;Eo7~2P_MK_uqiBKpZFw@I8da z+p!XJQ0u(X@HQgd--D<7ByhU->3juB^;V!Fq4hxNT~L6O#A76SNcebkmw|UvAOiU% zBcy&k!hxL7|38ANZ$84|)A<3R9G=c$@$q^pC>}j}MOT7G+C`xG-J@4@C(Hl;FIkZI zplOdU;3^Wd{!;WN-~azFpCI@<8~^`*c?H4OZ2JHICFsn1sQEKNd{Cbm#CHq_*HDll zTaV^98ZUk!rS(7HbnB+!0nQsQz<J{pDAD(tepH3#jazr2VSI$Y1vH%N0m&Pneb%6{ z43BOJ(41AvVvte2ZW^HZR2LP{`9|QIX9Qkrf#bFH0DlLlA_29-JS}rn1o-=Qf^>i` zH1X(`c>$`AlR@JP0-)lT1C)Gjy_gTWtG4w(g_j3tT<H6Y^WbIl2Ru8EL-Wx+(2D$( z4?wjaKlD0W&rb2uNuJ%|pniu8Xp|M4k7Qa8RJMS^1F|th6Ox@k>kUyamz;nomkbZQ zegw)tE-IjH+y4U$z<Ns0yuJfA2Xyx@IRCux0Bt%HQ7PR5KA0AC-z-=IECbC2WgvL| z1jPq5GC+qWy^-($?E_1@4-Mm^{4EyXkt1f0Uei1kP!xdT#_+(4;5$$yNBCR#;7S6J zl^DR39OG|!0t(#b8Wm76rU2TQ3JGXXImX|z9VFT9CILE97IdQr=-vUt+pkUe<rzTn z1lcQan9<WZMn!<X_aRs{Xpo{?=LV>BtM&xzwYdRmvE{wE2}&T~Er!QRJV4{sFRrtI z*Or6g$sbnUy??=YA6Gnaz~hMne6bCv-T`e6dXWb*y7d4kU2v2v0f&3D9%CsNqLjlD zpUCTuk>d09Bd~tNejn8M1Qo2BkOmj1RD{RsI%urwsDO3}fg+Vf<@HR2174p-jo(n{ zwa5~nffLZsv`4S$MkPju7msd1Bk(AHiv&0VT|jqxRU~*cA7^~w0*O11&ZGP-FF`pF z)CmC{>dE2JT$S*@#0<;^-69XZO1+dB*4O|QgWExxK^njneYaBr_;4r7o8=chdaE=* z^B1k4F(A;C!r_uyM2xJz`wz5%5~X1XtpK}S89ZQ(#ZZQl3PgSJ0G!@K8A?QuR;(h{ zpMmQT6%|mO0|_i7RiJnUH8nsZ{IF&;Bo??3`3|HW)(Sa1!J|7A+};i{cv%T9f4hUB zwd2bO5FZ@g9-Xj-x*Q(e>>k|$9^I2b?SOAAn;1)EK^vxEhZS4i^z1z2(JL~|r&s4f zt#lgbzHZ2j8>Hku3<^OX%g6Q4p-sqJ;9JVyzgV&aJn#Pixw-(Yn*#Mmz?(`uy7fGo z_k#K{3?=p+&FqYZ-#og({1+ZKK~0lVO~f^&r4~@ReV_q?7wR`bEdkhR>!sWvOQJyw zok8;()u4VTM|AAV#2f$qPk6EVHmGOg%<+0JxLidlPa5AuFfuUsblZbco`FNh7L_(| z&V|kvL%QIe-R+=pC=bv{pq}0AKHUl6<60bix-&rMH@m3B*ogDDurV<(*m&@_g3jA_ zY}mgOwC$0<rLhupni^yQ4rqffs1E}&C;`+`aPT<ZqVj=(fdM4SzpX)&k%6J}SVxOW zJH!zTA`A=;E#Evl+c`u)Crq9OCvxzJPy?ud0&l|spGV8TtwbfJH|$HpFK7PtB_L(J zys5&B44`|+l0_zZFkbxs5T4#(uJeR03_@`ie`^w`vImKQ!ue$gsFDRAJI7bT+VIP- z)Dx7sJv^)fJWB06S}&Dod3Kkm1b|K{>}IrNE7dYQ@R|=&zjxa-TH3Ugf;atmSY9Z7 ze+=R?2H1ERXb{ry+lw_Rpmf6D@~aGV(MXF5=zKMw&L!}f9t)4&78USD=1$Nd4!tcZ zkb6=<qu;%d00aeT03=Ae8^B|Ftp_UPJ$gkNJiFT!K-*G#SuS|M4vjqS0N(ruQQy1+ z6qF1l;C-zIpsKlB+oO9cNYJAdbgsHO0|Wmyx0vP^UN)@!?bFpkjue#u2krrnWR@lm z#zPHz!0CnobO5je11x;`Ta(IBV;>ZvAg3n0{0knog7n`!yW2s7J}^W0Te1Zh7#j9~ zQvri#Cs>ldWgh4((QZhnK*AT~evpItw>5)On@{&<kTER>O7wiJH-lzQYOP(mXM;uo zU0ZMSx43|pws<ri0VR1z)&+G+4o~p0-V7R4uj7T^Z7m5}+yBi)1$2VJI)2!Bz#iSg z4ZAczekg@1{m<Xx&kve#@aSEw0ZyVf_&}S&x<MR|){_-7o|fn8#XY*a4MZ3iJT1@h z_dei*pS$ANP@@v#&)@!smw~~Nf18g=j7P8SbwNhZTGxIL#-HFM>jFCcaW%+&{Jp}U zlmD8JGJ?yO`~R;xTDhpimOk+5{Oa5J4m7x1;oJI+ze609WaS+gd@Y~yx7IO%*42IT zWDa!zANauEq6<DT*GI(yV$2KMYoLK6{*LS^3=I6O{DPq91_vl;9wh>F8fo)8Mrerg z_kREP|NqOS|B%<u`gC`L+~?6<Tmd<xtiY%9lTYVK&{$8!i#lD<s)=S$L(a4F7--a| zq6Qj#jQlO@LAR7!e&p{5ts{CF3o>`YF-U4>a0JCb12mRFyC1DLED&K}D3SN*?f^xv z59mx2P){0^k3cn+@?_8&gkzvU=yYHLEo6Gh`|tmMV$1`10K>e+tqAizFoMn7`xk5; zC<CIp&!U9a$9g-+ef%xjrJzLMqEg_~4c_kH)A<opXas;t3g|_zoh&LZTD;)KcTDO4 zCwZ{B{H+r~N1T9S?*OPEgdBSkHUVU)59mJGDd5ro8hv$FAknvG5-9o%z^a--!2=r0 z^t3$2-)B1!9;GiWh;hGd3CJpl`@sQ&-Tf>c2=_CWAiMt}0|PkqwahIB9oYTer*jRo zRNJBgYR9}>!w1e(ko-@6{4~HEP#I!)0KBFK)LtXTJWv2(#&<W&JpK-2NQ8ijT?YP^ zjiBW^oe;-Ei!0C0c2Mj<2KSdCpBLP6z@xhvG~4CT?H~YZ+gE_^#eN5Fs5yX5dIws3 z0m;AoEjHkRcLxR^>(wA<@%L$ij(==^%jnU0->35(JfFW51}g;5Z!VAlo%^sGq@IDl zU-dU=e)-!=F3`S!2`vXoN_<<t)s=X3=BQM>7W06adl`JR=QsYA%0f_~@YT1s?7v5M zb%jsoH^@r;<{w<87d=6PpE)WOFZcY0jK_FFmBH2Uw=Deq|39?Y<nOHk9U<6!jM2CC zTm5BF=4)yC{r~^V3BN$vLF?JOyFu;*-{|uRyf^Lsixo=H=Kird2UywC?+wxx2|wS= z1B<=@b(p^T6R`Qm?q*OK4?4#JG(!P5?JsD53^!;r0a8Ap+OLXSD%FU<i+(w9CmB>) zF~B_7vl(R4!JptENAUbIB7QAU<F~{Ik}IKc#@~|g3lvb$cnt<0q=*@>*5Hr=#Xl&w z@%L(j)nSzLFNGoMkkgk00s9~PM6&-f==@47>1sD*AA^rIw4PZFR)=Z-WT?9C$)Naz zl=mk+x~mI(IzRe!KJx9n;L{nRQt_hN5Ee<G3+XHP`)a_Y{3rgN%%A`Nzbprzbp~pu zVx|iLNtjmtJ{8cCB1lcn->(nSY5N1T|H1=Hd{&4f$0vUeXq*xjr>(}II?EN5H^A** zpYF+^aK`Qa)%wVWf&G9~W&HsynX~-F-#_Qa|Nk#neFvp-aC*U(UO(J`_TY~3_l1DA zpu_wV57M0b9qJ!Yc%r1w9Y+!2AcL$=9i-0`qz^tmfGs>Ejv#x1zsCS{cK>crK4Ii< zRsQ?`|4UOykp;=0pzuJk|HZX`|2?c>wKjjx2hjE)n10Y2jF(@L^C^b@3|UnBHz4a@ z{OAAwms{cb!RZ;aB8tD&Py*CUI>z503|>xn?4`x`|Nng;=?&aOiuLXM0Io|xu?I?e z$5~VuK($ThMM&Dy2Q@c9tL~10<Bh++1$3(|yo9j-_5Z)G<tP50```Zm2OnvbHo*~e zep)Q3at?4cJn7R7u04G^KY~_CDQF|&VG5+s1H~18|9p`CwO>K;hoK)78ZiBusNt{z zbS^(E9A|^dyS4Cg1)462^QX85vcV9)_JFqN!Bc$%D3<+yg8CMS@`{5%d9mRuQhBi$ zv~LAVdC~n9l;#oTRW(>0W_bZRjSJL&hr}Of4&fUpOl{N<Vd?-bAHl74{(kST|Np;? z`~oXyJs|lMuJ4NqvX}XLVt)Pq5BIG<sEkeomk&OWdIOX~u(U_dDZ^|Bw?+B;rb~hv zD<AoL&V2d*|K+XEAish0bIXB}Vjs)RAU1#BBE)&i=v5|vODpIgIZKEydUHYR<v>kl zh}QZGFVnz!LFYlDRJHsqz7Tz&^upg~3f2d@@)~Sq{e_o$5PdBN_*=n83w1Yxx^|wH z$Ljbzx*>Vf@|Y*T?=cU{X0RDOGM~XMn3rEZg98!Lz5=!5J48VJ2(ZEYz1Kef|Nrvi zC$JK<`pkep`k3$;DSd#>F9j{$KyRmHgToM!K0sGogBG=*)QzPNUOGe6f%8AKy`(_E zegS0rnL!J5u%yq|;DZ?v_TOe?V8CYoVTd|p`!xvIKj9Oy|H0;B*5}y}bK&)AG*}&G z{&0q<JJtjm_<$^G_UYUVYE_~)n8EdyNB0zPE83&G2i%JG?Cb}1cpIu>7(F_tsQh36 zbpV!2F)(;q_d|+P{*V9vJ2vd!0~w29{0KT419So*Gq~vr?OMOw@&T0O9T+^AyCZ}d z7(A@O-8ugL^B+J*nn60Y;PrXkuzoFnhr`GJ|3UrP9&@mv@cwPTI*8xd4f4beP*)kW zat+kvhxrJ+F9gzm2kpUuwXyoEB*Fcw86QC9l~3nK{ua=>Ko4!0lfb2Jj}5{}FQ<Z? z?E==O20FA4)*kfeW<CZUOJabACfFe`2lzmn@h?GZ^uXcjX$cu(Xq^ojV(4504oT3M z3uqAtNE>L~1ZdczBF(Y6eGBMp0sfZb;Ne(^+J@Sg6#n+rzMyUvc#mTcGpG{}-TLp* z4LSs=M#aF@@W4(`kaSBpcHZ^wb;)J$>3rwe8`8=Ey{N;dSLFhza*KGea}p>R_**tL zfd)`Or{MK^FnV_Ss04U+x6c5NCAg?0@VB&-gL-N$5Epj`fY0E+=3)7<h_(6G|56=r z-`TT!Iw+Zdtp?@5Zi^RL;2lZ)EsFIB?S^l`tK}S6JS{;-LN@WGgRBN?1sk&ybSEoR zzZOJ)>#H<`eyBA*o&Uk>$vk=s6ka?!3hA4+zXM%(J@u$d=L-jp7yPZe(m)w9M<sv} z)L#PyH7N0VbVAmfz(cvj)w8>u0hA=WCrE%UHR=ty^c^$`>)W|rK$wBSv7y!>g}?m_ zh{?ar*TJJ#GzT2=JY61)r$B>Ipt73*G&^MQ;@h`>|62}}tljY&wA=RUTTq&Zl#dYk zYhZc)7In}~&|oze!D>L~HoW)fZU%)RG(GurmUDP?vV)V=%QQs05}I@z`L|664M>5e zAUs|NPw=z^j{@-bZ%zcAwGPgEjt#XjjQp+BKx-~7+d;XtB*C#^|9((TEy-)SRN@5F zHRWK-N&eQSO$-bkj31y`w&hakDo;!BDF?Od9HIMi`CC#G!I?o4>TakC{wW7vL)M)@ zOM=pEFq=xJfx;i+kH>Fd^?_rztAJ;BKWIv)`2{2FfF2LVQ;rR_5)37Bj@_jq9=#@I zpmTpbdVLHpcyt#EfW``286`pKp+v>OqnE|O<KP2k59T(|r7aB=0t}$pfD%CuOQsSY z&*t@@G|UJR0-byNlm#><#^BiPY5=yaS7ZV-M|kwIT=47;mGI~c2=MF<Rq*Hxi16$V z)$r&HNPy^uq<zp-05ojiNoE5i$%H{xuYeN}f2$Q}V@`Ji11L3tdW>3sKour9k%4#S zgTjLhcQ_t%bqEKgUr;(ma?Hz9pbazKp`bf_7+~@qy*xo4y*yo>-P1vV<I!t!%A-3h z!lQGG3aHrh?1W~z3<uB7cRu{<%K|(ZuYp%<NGE{uWIw2`_t3luz7e=PLIHH7Uw0ix zw;9L+u!)vGL56`+l?T7uPtVRj9-WXSef--hVtx46KXvHXqXL?S_hh^RKBu*%8(Q>% z1VJN-7O#ci<K@1V&-hyo#e;?nK6!NB1D$moc>t2J*S-4xpTA{UJSf_?fJ-4y#&<rQ zFTn$V|6e>h2vOGl3cMn%bSt<d@#x;70&*L)MET^=ZSmrU4XD5?o$^vS3X~SW`PQ>L z7Ls>8I`6$m-vVm3-7ZlASub@CG%W~PrSRXg6I>p?xV{;bp`rS~@#)cxH3iHAZD~SE z0e!y_DF8HXkv0Jo|DN5U(3`Zur30*f_2}hs@aW~~KqSu-U|)dtwRX0sfCInVAp)8d zeY%SrJi3Ddd^^AR@UKq`=#FA>>@GF%<X?Z!lko`XrdGzoFV6LVe9qrupbfgk;2VFR zYaA$&!9|9L<zWwgpBvx}>aSuEfehMO&%ezz7PP&-^Eha&Wj8o=fntxp#RVLfAACBW zg6cX@lJe2~>hb*ss44@6J9rH&Xa_>)(bwXj^QkRg^S4-_#Nk_TkNy=XtmVK_?c4c= zzvIA*|Nl!a!s8HR8`$?gov%P~C}IurN$Cck?ls`}_33=zsSU1LK`j<g&hVJ&VR!*_ z2zPg2Kz9)M5{P!ET99``1w6X<K*Fyx9F)Z+JUYVzJiDhuivL}Zyj=d@qxG9d=Oz%Z zRM?|?5+omADq;8RgsOxa+_V*R334~1Z|8DQ(GAKQASb-E0QC{T^*;IK91mzlr5jH< zC*)!2#DQMWIdOnW2GD8oFC!s~c_H-=s7eRjQK#8{K$w96JiX`H-M#=KbOJ<mx32(E zn(Y_Bl_BV~+-?bv?&b+#?rKn72x^0OgXb|oQ&B$Mxu9!=IY6y1P&9#j4@#Vl-L4U; z3=RwoKK!oW?n?6yMpwxvj?GV)UYMT#_usMk4I`*I+ygEh_*)qH7#MskA&2(yw|oX& z<<;ye!06H4400Z537kiF^9GPBIyXZ^Di}RFcY{Q_9VI*%n|FZZnwx(JGcYjnZ*x`X zXx;}7J68#CYr})b!?GLXH~!{FLJSPYz_N_K$xlFO6%<`Q{OeN%e3+MjlLj;@9UJz8 z3}N7JiQ!{la6Hxy=Dj=$9*J=QU8zuN;MpB206Pq&M#TW!_<51s2}+>+t+zq_?gQ%J zA!x9AlIoq8bD-m*;EdqYZQ$8G4-^BQ-EENg$N-&rDd5wc%mKRfuCql2G-t%WFIcj3 zii!#7h?0HIQlJwEK`luBeZ>+E{QHh|wy1!%|M2hgmhJ3O0o{J=*}Uf43{bH37O;9Y zuLA`sqet?$?vkwxo|@Z0;R;D9&FjE{3!6PF;r6v&2Fm3Q{8Nv$o&-(ldVuWgI~U2o z(0Zwa<5-IdNPw~V0O+_ln7#dmpoy!_HQ;#n=+05Gc&!a<PlMLBx4hwFVCaT85>4lK zP^y3|Pg`^fG;3XX4l*b{@d>C%295K9@37JE?4An=r&drn@$d7N^ymhK6*DBLK(}<a zfbH+>K@TVXea_OIEh;vUAPbh@-**V+@_og!om*fbb`=t00j!?QYoQ@_sXJsUgQw<H zP>AgUHNQQZ*P@0Pudnq|P>AujEQn@cXg<K$F$Ek%%|Do6mh^7`U53@U2Am8b!J`AO zAHm_{&WkI2O2Jy+ftDSihEL&ReBlGSp&T4O(Ef@?x3hpxw=yUvgVK`+|31`UKu$Ph z1p}9-^;U2&Og)4M18`k|2#7k<m&-U|`KI{?BQ!<ux47_vLk^*mzrU7~fuS3cmcii; z-UIUfh2C*+8aV_>Be{=2J=9u32L2XHP&k2iGj?-$bWfB3l^oqokdrE3FoM@Yf_eNc zg`l+BE7Ra{@G*-A<7JO-X9sAF3%aq?vlHAf2Hj%n*$JseyO}*Zp>2X-4v$W7Td<+p zi-Es&VI=6PI8O%t)~-nKEUc@5V+XiE0hQIz#s#RQ*-#M+QvUt_f5&64f(#zr4ho>C z0F_ZMpMozuVgR)nJUgd@D%_X#;KB$}zJuy~a4P~*Ac4yw(2epQy&`S!hJ*~Loh$I- zKnu8tYEe=K)rzhX9-Ypho9GlgI-MgtyInOrI-L_<N`nn&0GB`f+n{SUdZDdG$VM@U zRiIiq&IHm1Qws*o80`T!(ir$#ri1Qd()kD;ziill0MzdQ)!U%1E`RHpeDG`@Xe|f> ze@h@IIAp+m8qaQU_XQf<ppBbqpvekra5(e#fjEx*+k9ghe#Y~+D04C}H2h5DZ;|4J zZ1OAxsqF?IJLF+`h`;w952!-~cDQFZJLvi+kLC){%|eFv!JQFE_<3}m@MLZWbw@y# zw1ZBW^*9b*G6GuA09rcYaohp2SM<1p0*LR~&F*0xU{Q4J<;#2j|4#t5Vt%an|NrG< z$hajao?s0+(E3$)gBxpmkpnd3fYe_60lpamoSI&~c@GLdeDMGtfV6mV(+Cm?3j<Li zK?rm@#q#%{VgNi253bnpCVo(>XA@{$4Cs_^c8|`D;B3;%(%^CM0gFek3=<?-7X`zT zb#yQ|@?1F($r`k#0@VL{c^NXk=-Im;R3Ps%0G~Mj?uF0xfB#=@2g`xm(~b?G{MkAw z2b3g385*iUIl&!bZm57`$8=Er3k^uHxlLfb44|pN)~<X1|Gz8(MJIf|!lxTN4(VeF zZm#h6q=S?=-3NsN*gnMAX03n+e9V)-#~ySIk0p3?p1<D+q)G5KDEvW_+`Z?Y!}Y%u zy9Z)3@VAJAixluI6R61oZh1@ww+2CL7DYgpSy^7-@8@P=U^oU@cf;V(*$kS*_vr2g zuYl=1?x9`j;nBH!g$M(~OJOF^{bl?u9h{)HICz*0i@7nN4ThE%_<Mqx@tMmEH`k-P z7c@i$S}+1Wirw-;Ei>o<_SOUZozL!o`~nUa!vinv?*9KjfxqQC=&<JO$kTZn(dG zg0_WQp5X8Qzy!7*JckYU7jz7<b27+B9^F$RHecfJ0G&|c!Fa&~r1K1DLud_j9SMJn z0qArs$gCUOK3F(baDono>;)}O22H42fJ?d;9EqUr+5!F!(0$eod%)wC3?AKJP5iCg z|NsAg`3gMK>cYUl-!c<)>QXm&3>$7f!asMQ{^_|)f`2M_K>ZBM6a2kfL0b+wPawwo z_*;H}Z`Q+=E=<7oUEuH6hS&#MkA;ygj(KPodU$khhJ+$4{`fmwK(;ZS0EJ1v1!&&U z4CGDl0y5qb&W2yU{H-jvKu7z4u7t4;@ZfL#dJ_~>m-t)m-UNyJfEJS#1az}mGV-^A z?v*h-@LCYuU+K1Kv$SdCZ#@Fi(0qUqw5II!%jGx!|A$<I$`3N%pTBhp*c`a|Q$fmG zFY&k3g2hqHue=F5`2@}U6p#kE`R+HtMKLIzz`o!C+2Y0DY6mtAZi_xh6UY{EusDh> z!Vp`8q3hmY{`dnrtsZXA{TncQz}F$X48HaMKYV7tHTv2A|1X;$`5GyiF(!e+?f`%1 z%)kHtzq}0|+ySkT>kbP5oub-Z23~Gs0Xi(=g(zg{0Ds4pKj1ytkZjQnPG|hBpxxsy zL7OhpCLozVF%x2bM*~C;Bq_j>F$*YuPs8U?k@U^XgX;6ZrVq4v`sHd!7Z6(RM}SVZ z#BlN=(2N(TsA7Z|gX&~{ke|PR*Mxw}2k0Q>%OlYKiD!2!r1y-aUIW!5uu(8j?-`;J zX*uT0vRAP9@aT3G@X&<x_%y-2z3xB>4^42V&Vzsb1rN>1pvE<F_pX^pi#<9aDvv{k z@jx@VFJ(W0rYRtUDg4_UVmew>K+|5$FL-R6`P-Y_L8I&!JbGD0-!m}4PZ0#w>7a_K zMD&|e6k`cLsLubg8#GD;uBjdQw_&TPJi2#)h7vq_!%+qat6%>A|MCXtCa7+RyTA)g zQ(uCz=X6jH!;^73sH^VTJss5P^<<n5>7`Ex^<+I6r$c(_;1L;c`|v2ZZesAX1~*Lk zdv1LL<+_)J*Fi-)Xy3nYfM<89LboTVa|TIbtta_gPJu370_|r9?+^R`qGdVA0i^*R zod;n<%+R$+pnY_fANiYSfbNUpU+<&h;lX$U+D3cX44O)ZoU;RJboF<G6g3}UgvmF3 zLz+gteeM7Mmr}Pu4rAco<{ASH_0w*!P+$KJCDhv>5sft%AqP*vQW<~CT+m>5Cs@bJ ziGM+n?4sfTUG@uI^!u{)+5i90P6g<I4A7WGhqDAsB>6(~0anlE7D)fbV}@sQE4Y`} z?b6EN+1v{5)_F3vf>NVLH>58K9Wwxpgn?EX!`kfKE#NL@XNwAGlBXNewd`zBc>}8W zL{S|98bHC~h-;vfzzA|g8<Hd1ppIw*IRa`q$St5g8*ESm<Oq;sP#y7d-qZj8vDibT zXF>HiXw@(%#X+MMW-rXXmo}i=dy%UAAE3=LuzD9#9co_i(023i0G(Uwd*bCiu!2^Q z*`Peo5f0)rgPUMJP<_m99zK=_`1_qfQmqGk_&pE2GzYP}5!w!bPR@d8gDzI_U<O;l z-@gVV)q22#-}A)F1t2z(^CZE1aI*lqDg#ufYlAKH;rBi8@;@jkBiWMy;$ycb1SAEu z2Xs~ya?9r?=teWBcc5zrP=jGVSOKiv&EHZFp5z3#y<ZlC*v;=4Jvs{oUOoriUI^oS zdIs95_5z;KBtQTE-|fNSVR??fN$4$z&EjF%4N67)O-vxZgMf$SasKANZ$Q)Ypv%;{ zT?4?)seLI73=G{GF5MX{pc5W?LFZ6D2K9bDndgK0MK3bVAl=5f;7Pl7&@c-~qeEwg zfJ<kFh)1`uN2dU2{uq=xKts~qK^z^Rd$WT<Qz{%@y(~{bWv>TwJ1ECNrfa|>w<WwD zmdqs_p3UG<c1F+*bg(k9J0QTNJBh=!^$CCLvH(zn7*XO~23^k!cYO>2*RM8(xSm@G z)Wg8ydQhhw$@yLZ-JTpB9ugki-U=R_9{8Nk=V9s0f$V<JO|?z|-31(=TLu4xfSk<) z${(PBc`5S<wA!dd#lW+>9W?N;4|Lry=pM}O2!R)@P*<i3B3#*e+oMy$vwJ$I+v5Sc z0=}gz5HwHn-J^33xTpPMy)DFVTf)HNFZ5?Z_)Eb2|1aWa{r~@R3FxlfZr6Zr&?OGg zxic16pBFU4(|Vh~<0?2zw}3ZHfa2bRIYI+8>()^o&A{N>3m)Zm;or6fycGX+G}8Gf z-92Epd~=Lc;BQfN18w*KE&uLzQ3(J|LV%V}L(k53>3sLX)*8}R^^E}!0)Xoy{+8X* zprC@<ZduI1-`5%q4hK-T!_&GQ)B>sDL(D~PcLJ?LfK+(BRiJBeyXz}Jqe%&l|F3l( z_3Yjb*4SNN;rRcW<x&3DFwnM(Zpd~9&t`DqV+0Q{cS0<E-3gjV#~VVMLBR+b69R=y z^B!<WF@Op|(Bc7~?u-Kdj`uzwN5k!gIS6!K68OlX2+*#<=Z^ocdV)?<?1os~dDQX$ zHPH54(2XaZ#~lA(1!W~r3GUc=q#II&fYv!#26OQDsWLJ!SRSpnfcemRJ9^Muj|3fL z>S?_lG>^yMQ{>9Pa2#R?C=a?=F9Fv={C$2-;2}j&1!Z}(PO*CmSllD|gGchI?ma!A zh_T$W1=J?wZ|QRdMGta3*G7RvTvQ6sMu<SI)Cy4HVBpyK5_Ah1XiU_l6SU<Vbf5@m zG9FY3g4R2L&z=ncH=bWW2QZ{T<7Iom8&DYdTOLQkT?loZ<<WYc*0*(_0s|DkjE<I9 z`1`mWKu(zgjt>t|AoYVzptgL0vi+y$>;3=#T`ZxV?|FV7)IfkXzaX<);4E4R?gM~= zlmT}LPYVVmK4|!^4`g5fO_cGsOa_fHK*!6#A>h#q2{BK|;2C7l?1ha9Bm)GXjF`3l zzx)6H%Ygf!HW)Z{f-1y58L&-|(Xak*0Wh013EBi2r+RV87-G}2Amm}8)~O(yp524B zzoGv0>HPoVp$tTW*;(*}Du2rda0vzpCdf?gI#2-N^(5HpaH!R9&iw!H(F_|FY?T68 zoeeTVo&g+0kjtbw4IrgfCJ%Tt2|D5fFR2YW0ysRo!CZJ@DDXl=AKVE7-+}ll5R~u0 zZE=t825_Uh(;1`()P6kX?7@Ikv+!>Zh-!Ys*b(g6?GSPBg<$go#+FMR&R+bjRsNvO zHvFw!pkXw4J%p%sV(x$vjVoy05S04BBdwr`GVr*X1?VK1cQ5kfz+P!>1Kr3BHN(~L zn<r$Ij7N7xfJb+MLbpE)d_<`AB!BDb2nL4ka1I~KPyFq-VnFj}>kAxw8NmgK2U7C9 zb^HJSZ?2IIB_g0HJn%vtaAM(ai38_HNcrZ`9qR!~J{^pp+M>HO09t&*OYacSIt19P zDWu%a1<l%Z<0!W~-@Rxvg(MBFP~>8pI~0`lptf2b=I?6(C0J<Ti&-==+QKtVZG&sK zP8*~cbWut8f58z{N@rjzr9pl8PKX_^XTf|A4jNC`mKZyb@6n57P%!v(Clv5^PIY5o zfRzTILJ+AuG`wVZz!kJODL23a<bkuGt4l1e^7pe^gOU`?8c>OVT>tRK!hOQuGs_C4 zhy@!5$@!454Fu(M&~74(veHMT0#sl^nqi;=(LkjoxNLOk)KPiC2rU-1A%PAmi$T+p zVGIo2Q@}k77t2>QyspS4BB(wBRUIJD^n*_L2MK_RHc0l>3<VuQ{ffUgDUN}`weyW< z?|e{FhNTRT&V!&jD+07eBjB|ycwDGqj|$Y{6|qPbKdKP}9eM~aid`%p@%J7FmlqJf zgV(?Mg3jl<1<vX)?}6s>N>n&N_g=nGFa)Kl(m6Z6f`;t&pZNbDw7(s^Jrq$a{SUAJ zO&UW=A^z4ipkfeo`)`ZVEzsB$WP$>01zLgYYIxwqIX#dK{2j?BPzs`nH~#;B`3N-j z1X>FO3Ib>&gTH5?BLf4Xp;2H1D|EMjN5XL11})xWp%#8Rj$)w#$il*#|Nn#RO8`x` zLvjgdpLu`=vj1fK85kgu<wU?nkpJH4Lj2c^YTd@`|Np;ag8C0vA}+E-O2n@2Fpr+T zfj?NG9xZ{|tYe8%yO@J)?f@Af51C(qMx-ZZaq!6k$$otgnEl=$&3LOAP%e4#MF-;Z zH5MrLZwC!AaDa?}+7CKw(*Sft-u)Mi6TpRY%WiK5hJD<ieY~CbU-W1~1mCbi$}&)9 zK`wLCuA$b+qJAJ(L+j)#a92+S4Y%NRHMnB5gStA%8^zU(AXf)n2c>_od-s86oItDD z-)TTx=f{d!HFn<b2oON87~xeVs1XVB%P|K*MyMi>PJtJ9A>)+bYO~G@RL0?`Ha$9- zJs}$mJvyC1T^3M#(xcPa0y-+>(dp~}D_lG}odrC*r-M3%$DBPFJvxIW_;FUV7hECL z>}POexm^P^6Wi&m;Mv`70by!*c7q%Ip2xwV3@WkVv#yX*06Bh+T>;g~H7W+XK*<7h z<7L+hNXp1R0$M)y=PF1ZQ(qrUpW!8trm1TonykQ@j^of23DdMsk%58X1!%t*X#M*J z(BgV{3k5Vq>(L2{8F0Y@*~teFDx~V18*Cb^7p?^A$4WSM-tq2bN%8CzX@Sq)>Rfp7 zObwC_LL$IX1`6rU2m=q$6=R*Co_1)2M`wV7M|Ws~M`wTrthvz{AmDM_H2@rfkZsVg zRN>L-8Ub1v1e&z!bWQN+b_#ImF5qwl^;-|SGcdRsJ^_sf7@l+mopRUd&cM)l!iD1j zf6Lk{|NpzTJmGI~^I>2BtsQNKwgEtu1gxtI@+=2vSvq8U^-HD8pg4$8G1#XJ&2?f> z|CBLNDP6OIw%mcwli+Vz<_-=dP~#7?IY_f~?db+d*J2RcDNWb!E`rJ(TxIt*V^Aps z?SquL!pctm%S4skJJkP!4#4jyFh(hbsxSWk|MJWw(C{p1T@5H<mO|R;-4ZWcx*;hL z<W0~NIA|)p#0E604q0zc%se`L{@gOaqWm04uSau*2SbS+zdQp(KWKmmIuD-#nt%r- zT@Toa0>>RdlgJDn#~mC%g(=7tmH{5+M?q8M4&Xrx&|t$pc~BZ-o&m~ypo2sZ8Scdb zWw0MwCtdjeAJXRVJ^)IYAfMK#80?e9mj#m{np<v%fKwMOvS7Cd2dFzR-vyijyB$~z zPlA>ar@O$jpWa1i_G|ZmWWNc}&I-Qlw-|KX9(Vu<bnC%?P=(#?idbOMavonw^D#t9 zX&$z)l(zH&Q7O$&6_V1}3{g^=;Q9amUnX4u)pyY9#M2tQPMyD{*A}$&9=cXt8h+YC z%Pvs1g02K<RlfjoGW$0NDMtR5iN2tk8{7+HXxIT>Y*J#_umiM6p24HL15_OIw_F7E z0Xk24Xm^0qh4uswaAgCYO6|J=4xkzpgBR<xAt~h8dDyw6n)^UjfxB7oHsUK#n*($p zO(&#YdFgl#Q;{+xoDuy;nRAe|hIf?77wi=1Xa;Pwsr3?nhm1P|gA4z*CEx)#&))T5 zZ&(V~7DEFN)Z^<@21gdS+T`z-H2{SRq%{udiL@IbkIsRPD($XOF*wem0?JOHqp9aZ z3x?I6$lbgiPmn=SH(3hT^n%KFP}>65sRC(7u9J<<!h-|stZv9~9cZA8zu(^)mWC3~ z5lTZKgI{ndL(<S8J(M(b_6+F0yK_k67@*ZEp1u1)<^GFQ&^_eP4mh&Yrk_D|8ZWZb zCRoCpcI+%sPRmz<I87bZX+|KYHG_<h2kpKAM^)<q{=OZKpu7Z)o<_K3ksy_X+m1|7 z%XaCagu~I(|Np-fJBt(!Ap5Yix0`f9wnOWTRCvec_8CH61vRW*v?xNnYJ+OKJIMAK zAS0mdZBNL}DbQv=xPJq>Kn0d$_*<%-U>%%9kTyL226b?Fq1G<ih0>q8a0+za%Ngwb zDbRf~2>WUrVD^1Hjo+sr`{pV@ylI4LUjoR!ji>+rp8y+yXa_Cifo<+;nSq=|x?Md$ zH;Qz0g9lYo17O{2Xm1;IYhDp@uDz)ZiWq3J=+OpcHsoA;Q4clOLi^K@T-*8X#Vct3 zka0%Nw11sI=0G!T>!tcW(2OT$rgcS>Pc<jevW5eav#Q~#b@eIyq38%+VeukF9ukl| zApanS0L4%K|Nk-rWCT3DfZI5r@yCno5G~twK*t}m?LmHkl(CRLu^vb#o+Jac{JI>( zq?{cnsb<!R|NmdUJ_%~zppEbH_as>%_xme!U?ssGkaD~>g0n{|)W%=iQEb!z*;siJ zoH=3rVT|#O0ObC^jXf;6IuWoLlw9A*LOj}xYVF43p#DF|TF`g{s5}6d!ycgU>@D~Y z8h3O2KOJ;Ve)A75{uT|8IJB^Yg!8cz_#+u)cM;TXc2v7fL3X!+#&W>pM_9@O17!d1 z0W~SW<2gY@*!d4SL_K*MN-!Th_W%D&A&{LDK#f5I5B!UKLF-ds^Q|R_b&l{gzYWzM z4E(KALG4IT1qt>CXz62#G^ou9Zn6r(`m5cb1&^SfYeTgs15AMhLkUO$xDNnU0Acat zTX{^BqgqPAL!{&^N%rV=R`BeE^tZd6H9R{Z{q1gN0r2YMmiyr5g0sXi2haj!q;etU zD7aimKpVA^K#s+2py~u1i#^8(ryL7uNUA*x+IEE~8-5@8|No^ANWVO&{{d=|OM{Lm z>YWc-LHgp6IJj-r;`9aNo_<gRd>5!g-~qZl@%)R=J)m>w4#En45735Hq0T@HcxO1! z!1H)JXhIy+sqj1w-U9<=f|k}mnV{7cP$p=R1(?~{4srl;Ga}#!$PM6<3qIABXa*{+ zkOEE{tOzoe0o?`M+inFbSdxzt4k&&pNI)&wjFQ|h9sd9Sr7}oAXdVU9^7ZJR0BZku zKo_<5x-|R-ui9n2;L$Ar-@xR+;nCRyo*+SPzBV0(v`oRHsECckop(Jz`-nlOcw^m1 zTo?w4kjtQ^IcUDu1HACZ1+<TNJ806{)3OA#kN76IxeTe&K(|122XS;>^RWED-?Tu9 zfuZ@=e~-=+{4G<IKoghXXbWQTv<y)R;BR^W8lr^Foq$fK>Mc+J)fCS=A>q=q33cl) zuMTYM?@JbN;KO1FG=R_FBVvqP#!pd$Ma7N7_@e?k<k=_zu~lpnN^H0s`v3psRFDy% zeeBSR-4nVd3e@al1@#0#OMxF)z-&wdDaTu@fSbPjP#c$QM6vPGK~VgIi~z+yu9^J| z1<<z6j^Bs=|Mxifl*NNNLjyDy2bvy0o401y19xdb^#rJD0L=j;c=Q(hcl>`1bMCtH zFer<GOaZBdDnpsSM$B7-hMPbtI}gJ5vv^pBGVu3)WME)uJ;~qar_8|M(hXS|<q8U` zUN!K(4$!o8>&ZHGs8sVyMvvr=p#8$h7rOUML7AMcPyuB-Na})gbB@@6MDR~e-vmug zgTk2L<g~X4B-TN<-88@_rx#ko-3fIg$UAz<44?yR`>d5gE|>zHlMZFz@8SRd|9|UA z<PH;mf8xRa|GW2q=iOWX^Y;fG1m!|VYX`Zkd9ok*JUoKy6u800K-Lz32Sq^Z6~HI= zSz_x!f)-nVwqn52E#_hiFGX-W5H$YF-&3Uo@)>ey8>yf+PhWvc5%`jiXD_@YApw|R zjodyhv;r9fbtY)ZM?YvC2PkP_Hc}C7)EnU5ICxJ7<RAdhbkhG9*IOZ0)U1QH9FJLp zOoxRf(r%jR`;ewk!3o|P+>+;Sk+p+#t3c_M0kmHf)LjML4h_1Ij=yt;7U-}!$UG$K z1mbOv?%V*6&STJVu-0$<efvSl`x1X&zdQp&^DD+~pRIp9k}q{nfu!7v{4H|w5cfgs zC{fAq=&eY2(Qq1ciU5D>XE~58av)DI2Z^|-L=c`s1SM+l9Ac-8%8PxXkbrSB2Zudq zl!$@9Mco`elh=BQzfVySWH>Y`TEFr4JAxY-kkEs)2d`Lw_V@Akfl~@(@Buy;0iHF? zQHcN*8v(C5U=z#W2|#dK@a_Bnn*9Sg40HerhezkDP8k)@c!NB|c`aJ@$j;+*02K<5 zDNfhcOZ978Z-dr+fQLL738j#2dqB;5(7IRTvLAW!_8NGQ1lkX7*M=3zm-iA?3DgKd zLUaP^h?e~x(4lmD|Nn>PBXE0L9c&Y{W4BxvW>Y3Xn?OPJLI`TpDOpIu1mz0`{?@Cz z|Nnofwin!`0I%;xU6J71y&N=%+N}X?2wQZ%2JLXa`WKS4-tIvZGT0U-pezz@oeV1F z{8>Qr!3Lg|=Zei<T55oH(1Y&r0Uh6}tpQ3okc0v{5fWN^gBLRJYk(>ha8~u{{N~wR z%K+Za?ZLmkz`=tNwAcTI1K3feTi|gDYO@8gfV4N|S>s*@aS7Bp2er)Mb3WH5Lt>?R z6==?f1Id%fCF!hP;P?UULxlI-t+im$a%ngI1OV#u)CoZB5nhE-Av^B+|NrHr-H`E2 zT<MNU22_qh)7?%Dn6=Ratc5l{SfDoVkw!@k$9Mk!|55^E1hhX5&T)`At%K0};?h=v z0_8I}&>(Sz94M_j|Nr-F-VZ7Z8Tk9k3}Eg$yo+#f<nu#<gJmU3a2SCc)eO=P9{)n` zIS3&49AZEgf_n}@APsohM4<5h#|N=+@(L6S5AFc<|8_ybAESTmfo$IyeOUkc(N4l1 z?1S2;jA~yX$i788q4t4-4xHb;^g%HU&F=;3u#l`IU=eiCLJ4ZuEzmh9h+fgt?V$Uv zL3V-rq2F919iU|le~Ub5BPV1>s%Q6hQ2QS~{`m)Vf;VPtPX~3@!E*ti))eF*6^zB8 zCqTm{-O%O>@*s4iA(q9UVPJQ`ihW2EOjZWDQGm1#6jZW-)`6M|LL&025xC?Bwdff5 zTeca2$`h!?mcsnKM+`uP2zc}z9Kvzn1E|0uT-WQ-TLd1;098D&g`Z%hNDWiavZ6$2 zbq1>0J)5D`2<R;NPKZsfr+~}KeNmv!W;b|6j7R5jpKgU0`J9j*&iSXHf}=$R)aUf* zjNtI-)PQXZb>!bRMFq4+(W5g%q9Z^7+&&B7@wA+x0_s&3sdoo(fH<JN97R&y0W2U6 zs25tq+wCCW3A#y6n1P{~)A3jf*ce8{0R+MdNCyy@Y)5Lipo~4*f@blcYg<8!o<ITB zu~iqTvFFpB8vvg&ILzPI3ic?jaw<w3l!~F{RIE5CMv$8#PEyEGi!_M<8V3Va>QT@_ zVY?o3)LMat$YHLuJj~x;4UQGa8CS^dVh`k==?l;Z0ZK{#W+EivOk4sg)Z{?pK+usZ z<U(!37F?l~3l1$v#DiLQ;E~#1Es&pb1NgViL7rcE9h5c!HC?+(f)v8m$su_jl&(AP zy@-S6VL4r7zxRN~%whQn)Z+S~iJGhp#Xvz0N!ER?-6>Q5fcjP7WZVs19(RPlwGbR) zkR~YBbi5d;@Na2{gj42XP&j=9ha+Zj&;c4)fNzU$1eGA5lWjoD0(#+R8;LM5fCi^F zVD1bSndEWs5sL@og@y{yh}mSsh#5meg%@}@Ox&Zn$^$$cHXSrd=Fu(W*$HMfRM2F+ z47ynv>LloRS%8Bl?A)UO3(ro-9`Wt~1JBOskQO*%gv5ixqq7S#LK2|h**P6Dau=Wh zb+t!#fW$E;&<L3WblfZ+R3Rg$S(8ow|AWWCU%p)kX>5TSWZ)A?KucRd{V34g(w>$_ zeE9tzfZ9G_H#8qe0Ii<@sRgai^u=5-#iql+06H$IceWU4xj(201sV*|Du={${X$So z*MpK$^D#zv;sKq%e4+Eu4p8>AyjmgxIyfu?)V~4Q)cNuS_x^wXEg;*OO1r=XA+{rw zrhbOBA3-;;`E-8pgdDx&+X*g2;3w}`L+&%>Z<=iiO5mV#cOb`3d31(B&)o?Ft)B%= z(Rp-(PuhWARt^nxP&&grcL!!{yFF;+1xt26xPfp$L-&Vb3D<R6&~SAYMGn_XrI2t{ zUjPbMby3jJ5_-5=9_62M02X>naE4y^Cs62>HiK^~g!U7=A)63Ax*a?`IuCg=gHxiX z<rN=(pGQ8O&pkAcgU;0gZ4U!&b?*~~I<jvb=&()j#=UOHK{}qGoZ1aJMh9E~yr?h% z??DAus-SbhTc2t&Fo3ItL=n)|{TOEc7I#qJ#PV3Fz-wc0{S9iq`&fQ0Q3aiO4>^bH zVhJPw)aL*HUwYri@+&A^^0&%@N(V@pfqB$c?njVM`CC2y|NsB;GI$>zUVlFG&^!h@ zP7Ll(A5BPP^lcLc6}2v)Bk6JZGe8f`pK>sNRtm%X$p>naf&HnE;!j0{KVKF@{Ao82 z=Fe73(CPqOe(d}J@uLsOkMF@-WI^NAFn<Pkcy@!grg&&xfM#;fUKSBh<_7x`Gh+)F zL;T!(2z0m>THG>%vLh(3LgF?@70u6=L2V|`Hr`etn4gQGeiq@EXLxM_NpIluFj3?8 zV-dvPZgWBY_OyHliC<gL@pZWT-uoWncQcURKZ8y;gDesRd(`KFPv-;Q&M&^5?>sw? z`nEpd@0+U*^4c5zzSSUSLeo5GXu!g=*GJ_)Xn}Bd0BBLX2PpGIcyzL;fEWg#!$l6h z1m6@79<%@_<9N{ZARw>Cfm)5#pk%BLn$)&DQY!e`0Orp(j6Rl6OB6wkPDncDg!(gX z4#=NApsNpL!L9+1oTH@SM<9DwzJvJD8sx|Cpo354LE{PFI0gIDAJw1M%R$4-$U_WU zK<#A9H~hU@L8AodzGlShYwqW0zD@_30rGW*AuNI&LF3$(M@og$CLrSX2-x4M2!9I} zLgF`VHq77h4h;OQiXii_#4p(IYu`fruKxf3e_znmAJ1O$g3i*0pRWO`uOO+B^mM-7 z0F)S>@%P^1$IRPk>0Ct~Eu9yE=BGfxFqa<|3{IelbIW6;BCn0%<IAvgj+DPS3n1YT zHwzLDU-?@j`9aNE%=iZ%7r62bBn&_o^S$H;A4`v%za0X)LG?Ja(q;sw2%iVA^Q=Jk z6!1HCZwH-*4jLi}RDt-p?-}T{GtkYP;4=<O^C50JH4{|1oPu720<W||w<7$%fKt0G zgXRmA^a^(G&(|RLmbSi32Jei8l|$Iw35q|c(^1<St>5bT9lQ5~-Tj}xWu`LJ-FhhQ z-kS$;w-DIfLfGAnr?oM68=|$*gVRUqU?1_frh{@|FWPzZt^uI^)}T%|_^#76;Bze@ zeZbDYpm8tA#Zn&71{G+u=d~yzeOTV%Z^;AIp`Fh_!40}I6SQd%O9Kg{v-9PPS6l!6 zceFTK3SMVu04j|^#W;8{3zXNoEnZZ-0!P3SpKgd_UdFEnwMUR03Xve-N*;cB2Jmr= z=Rk9<&2PYsIG@gE_}%A4(0w;vLfrQ<8r*|`xC^u{&KGo9Ec84@(9Q1M8jhWBI{&^F zM(pqOw7kdPQpyWz{Jev@=bPjItB#gO_@^9jusB+J4<3t1!2(W~i4be|`gVSK>ADUy zU{mV>pLz1=b|~O)N!13mxRyXqbc7ru>e>0mr@IDJ@8_sw7@h>J-Jb)A<IXMM!x}(4 z-NEO{bbj{iJm(48?+M;)`pcv9y;HZz22c?I+D19UgYmGhWr>PM`8}V`ryiX*e7Yev z_;f=J>W0|t(G9hrdyNXj;A5W5;NBQ0b@*65@ZtA;;A8oizo(EJ<la3{_ij-Er9#in z?;sa}!V?s~1-_k6L9=ECukAtgDY)77S&)IjuUE$cmUuk7ZB(EM=+z6*{pg@o<cB~5 zrl1R$EMN2Y90cE^6dK^sUFrco^3+5HRBJ}~X#NCg_Uydz;?V<eMFGCmr(_1?#Ix$D zSc91r)W7Y7c-9AcS}S@m{|D_V273Y{n17QL%=d^5W@t0Xv-u69hvo-xF#Canxu1)R zgkW|8x9h;++${hq*0+Gq`2z)$kLC|>FoPP{okw3>cm~P`{4Ejcpyd3P(Z}*Ne}6bw z4<w|$dwpyfd^(|F?b&$)WXKE4he!cE9TLz&Q@}ag1MK+DV^FVnXnp`!2mTOmICG+U z0~ETT)Y1)cAt=>=@{DiiQ_s#fpo9MmUR!|u@g966QY{0L`$1#qokw5rLcI{Ggye+- z;1io%R4lxELu?tq_s>5AUBg(Qfv7`EmO&gIIT`Hm0C3p}>70Sw0V?Z2r2%p&AOK3g zko*t!$Tv{N0+pG>=KrUl;4a<!@;CTYTvrcJZu01a=PghN*asX^J(3_FbRzN<5g}y* zY43r<Yd<?Ie?ey%z{df;dU5{=$OZf@>Yyo1Sk{^ZJ`c(TbVb=KP*`dH^63Nz*G4g< z;93p|F0D!6;DS2+IILJea(W!dI1H!%2bD3P$OQ#1h(?ZNOVE0EP*a8-hr89G?#@>L z#qv>bLhZT525mHfPQ>lJ=>vCnq$H-hizdR{{f5z_^Ef<x{y<Kji$Nxm=5$E<^amet zg)M#NK8A$&R(X)ip;=4;WDlrYgO)xgC?Gi;nm%_<`2QakW}tZ|i`RVM{0$oTXqm+d z%22R@55qJ_Tcm9Qs0~#I-oS<BoakNY5M}2<i%oto;yEXJ!y}O6OQ(S9aZsGS6nYN| zMHkSK4d96^@Sb1LdYEpD7tLVB{4J~spfU$CwD!{aF39z`Pe=CbJO(-&8GHn)B-me| zGN?rY9G(!j`!as@0G)9SJ|FoX*o)xvkr(uX=eA4FVXJS!M<joI2zLJ3mp7Jy=0IWV z4eKjFIuib0058($JO=7VW;p)8(t5zL^C;3(Hh-TB=w2Y`7_H$YX#PsR)a}#C0H3=q zQG>T2yJ7RzC)R*0=5Jv^(%-!YyhRnXBnZ^=1&zJ;hBLZYzUqWb?DF??gI4H3&tiC4 z2f9)SdLjZiT%ac(c!1A4fy_<!2K;Zm1k>I>`N9AHuu;&LZ4VIhp%BF$y<y;sD?k>% z+`kVLAfR3v%6jZ$tngu1{{HhU49F)X7~Xza`QZQm39j8aQ!qzkkMg&Ij_U&j1;_%J zJHR*8LT43Tid+HB54m>tgPPQ?pp6~kSN{Kp6f2i|VeWP9E&*N90FNX`%VYelvY<oa zpb-H-t^#Qz^h?th|Npyow?j-mz~8$6<^TV$A@>nOH+q2<(<HJoFc{wUgzlQ?*JWj3 z=$r$NmY40I&FpCNCyt<&6#4`Tcw_c)6!&1xpu8*s?;!zs0vh^|VQbVBcl9`Ap$}+H zonz;bm$iHU|8Kp`-*@T`QcUgzISlG`l;Avk8#D)lIOYTFTJZ2Aq)NI5cIjdimx4Wm za?r<1uDk#LBf4j;=gxuFBZE%SgU+8iLgEV)U6Y^w|3BfSH&~JZ<k0K)kQ{pI-v9rd zJ>bxU9qsh<HmJDRq5|q)xpcnlJmk?^mjE7beX(>UB#S6s`v3psQKb7xUAw1)V$tw` zYwIQc*4^E(6tb}!RFeFE(Ubr0|H~O`ARPbD|Nmb$u0gitBeE^_P+Pn}2iolhodE|v zpVT8CepcDbrE5UJ=qdneih}#`oo=ABUnM*`-9XoWfJSCtI)RS6-{&o5d5XVPYuW$* zFLjrJZg;-usof9q+RF#?L1uu*L%@f~``>uEZ6WAz8^{UIo}eS$y7z$F>>i!&P~+X< z#y<v);v%007cmDk*zg9_ZUnVMKvgHG0RSt8J)8eA@b|3)Z58Y;H2@V~20on+Jvwi_ z=x>9}&jp?VIS9L_UV1Hn?e>c80)^ZjaQuLy1rkZk#}i(#u7JePY0%aUL8SPBXK~Px zExiT*v6iJ*9XpS}5&`7sLQqx!&)k5rKm@$dz1n&7WiEIk9;++rmqT1J>lWw^wZEWH z`GOo8pwM~w_TT^i%?B8p4>3Xy80&<b-SYA-*x($M3RlB#FJnM{g|79+>gFr02RaW! zP2lhAg(h84Y1Vn@rTcAI`2^dH3thn4dSx~Ej#AJ<AcmLqZJ-PRT|3aa6Im*$10+=o zI)GCWbe$yVa0$?IK+tF&=%Sa-`!5s|AQiK}90LP?>t|_Di{TjPGFZ?Q34hPBZp6`X zEjMMsw~x&30L5TC_*~3R$W5*<Z+3&q!ug<F1iFg1RqGcl59MM}{kjX1l}vK}{ePLc z48r*y`2YXQAW*HjFIdX*BLCDwFN2nV?k9q*l}8Fc<fuISa@{YG)rbPVA8z%hc2Jun z<1eVNfu>ndTeI`gi`&iMG~4R)_y7NwGeB$f!S$W1;oFy1GeN!st$PjwZ7==^9#-IQ zk(LEbrhpGXacFtl8DZerU852JK3A|aK*F;-L<PM27sLQ>|K)E{6kuR*ZFyS~2hJ?} zl0l{|hMIO392<}WX+68a%cw!|0zQSUld&_>z@sye!xOxmx6@Gq#E9_dbW{K_5<EH` zK|^1lUav=|qX5M8aFFS)P}B24i(DXEp}`IXH=8^<!5h`VOM*LHR1%=;7d*i?PIp71 z5!6Tkt#AV6H{`v;&!&OG1ZlhC)oK6#!wx&_P1pzO@4}B8M43BY1J(%-&Bfr+2>zDq zpmYXtAuKdOS8RhOQE-IjcRp}v^0%x8Plkg`Oal2i5Ncv8cm@#ajqWKbVCO?awv!PQ zoSl&dpzsUuARMBQh(QhwP|QHj+<SRrDyX0XuVVLP1mEZYItd0eTEy56IVQ3lwCcc< z5qg;eq*;nQGd^wV|NmH)el%AoFz|N>f`|T*mau1l?6YnM9g)f3!g(A#sjk4^vV0oh zr8B{=K}WIkcf4o;MG^cAAhD_c|G!KJ>6eGJ$E@2yLoWO+7mmS9_XNojSP1m>6~y!| zRMU4%`TzeV=QMEP)dFf~fOcnq>#RF>{{IK<;oxtb1D+cKoyy({p5<|t;NRvT06oFl zquUvDc?1XiaCYeN2w2acTmTwvK{~yA0w}Z~XTiU0nF^{9Aw61O(1;dfun1JAf$FxG z>OVm-32(n36)_g@+TsfIoEOK=qm~!=`@Vw@hK8<~fHsj{I)avOLEC(Ii+iY9orggS z5SD@?8qFvZ@Tmu(9EJywdId;rHdn(ZhPOKpf=)LFRTD1_FM+bAFh1pP;fifGgZv9A z4nPY!Aj{gI9jbdTR3QffwE9kgZM@yk1ZuN0pe=*|6;+U8qC_R5^B`y*8x*~uQwLuJ zE(EQ4g4|VS<rM4Dd6mCa_ZY~HP7a=o;I^PgHxn#tz3c&X?a_k_qT8dpD8Qrhs<j&n z|FnbFZ2XfCC?2%%isf(h#%-J}$hdB2NTKM``Kt4sN2jyG3mGMFao_Sx45^!_w;mK2 zNNofH)^>o_@ONYN#W7GvAD1s`R5Coey#hK9fl5_SICUHThaYul0SzM1IT;?<4bPnT zA92NB>&8j{|G!K)gv045@$n*bIc8k&T*ny~3=?sf>IIGmM$jy~Q!IaLAo$1(tO2oi z0+tFJ7Otp{%UFf!xIoZ((bz*F8Duh+qI4E$OdEOSht&iezCgA9<w{KJ8^BBMuzKuC zKd~MI=Yno;P$)w@4$B2jxRY&LKcoqyat>0UTGoSy8DP6b`CD&-&TxbJ9^My49IEir z734fnj((vCnYC$A1Kqf`&DravD0sasmSm0+9$;5bor@VJG2m@USi|H(A2A^fHo4jh zGdw1NS3zPmxvY=a(0)<41k=q@;4%@b$ufPAkhVGt3F-5-Xd&GV9X<h-Z`eZmb}z)! z$XhqJfD3X+H4j;;&)rYBZr=VF;sl>sl)Aa9_y7Nwr||^<T6Sc>9gcdvpkT)+4tZrE z#UT^8pAJc-FRy`CQ-hPxy%&%UkOULxNN8uTm)W3|J?O<7tlkO@=oYfPz~6d`odI&e zsfBASf9rKfwnP;9NbQ*!J&+)TSGVX!IiVP(gvTgfbfX}i0kw&VD0F?meKoA<<V!aw z<UprUTzC!%jjNzF2%ro#XD29e7Ab)DzCbrNf|h_?4}$chYO4SL=bw7;r9ZedK^wOO zjdFKeyf}RpVrVKms6v7{+9}{A2S`&drr}jk!{wod7w!hR54+)cP{Zrc4c7r{0(q+2 z37i62Z|s28^v9||y{Pq&4%}P*){~$ynJaHV)jw$79i%A?swrh1Ow&ZLCd3HXl{K(l zUwIYEektfc1E?`*c;Mxmb)X&@xU<-Lo4++}@BjZVGv6S!7hSu-lHi`?W00&j*dYwi zaT@R_jm5tI|6dwI#G$d^X&s^x!QUzhI)@EfSQ@^4=?>;1wl7J7I_2HaGzWDUXyqtM zzvW9OXs`vc%+sUu(97hjpfn3^zQFrEYM?aIe1H+Dp$03lp<M;TOQ1R*)J_BSETusc z($G^BL1w1z2gM;$#;6Bb3UxSqG!v<mkM9J<(Q-%}J>hSCy#N3Imj<8%sgV>}fedMe zpGl44D><+M@UC);*xUdAU%o%~|3AoiHsJaR$vGc7K-Pg5QGm~+T6W?8|Ca|}{r^7! zxi51H+`mTz?=Cd{Dm4BqG=3KvzY5HU6sFh$Aqp%HjXvax-?0Ofs$gp;TJLQ9|Nmt^ zco2yJ*}YO={h;*RW)H~(zc>B=|MDB;l2=H71-29#nqDm~fg&H=Y&LugO=KAzpfN<S zS&(CFx*Nbfa?Mj7nkQarfyxL%X>39}DE{&HHFJni<3WTPm3Bmjy%jVh^Afb-0WuDL zx{R<pp0xe{4+|AgW_hUz@;XxPc_{%B=mwqR4O!Ry@-nC>Lss#v9acI-f^0;Vx()V% zF=!DYD7U=4c?_mh4_)a7xGBQuQd2>uAR7x>e+}{D^HPW(LDf34Kaf)f1IUlaK9~kR zEC}JM$|W#Y%_xPj`bt5m4qOC+4tBbC6(&(y3MxAx>B$i?z6=^%UV06tAOx%c-2X-z z?+&>ElQ%)ggGv#6E_Z81i=A$;SCED`I}dt*Mpze50=1SPndjxaAK*(W&Xk~O_5*8% zjG%WOL=2y&A!`-`EptK+Yl$|P!>dbRtU`37KchSJ5opusCFB_4??2c2|NqOi$N&F# z?R@HD`3gm{XCuf2Xc>tddCs6Q2xRwkw8C8Rx)`2)L3MTn_yTLlJl_i?Z%Czmycm=| zAQiDA|I{O(!}wm-f!aODW}38uLu?jEF|xP}+^+g!n60IFY+d68u{986P&b;bouKUi z$foZDc@6onu<&Nkum*VY{QrvpXT<!iCur0HQgrgSdV<$4R0MQ~vv^uw<8PJ+UoHYJ z#X#*N1C-AHyB8W_kkT-q1y;LnE&^A{;QfuD@is0728I_8uR>Zbj9}9tPKO<B*&U<+ zIdc)TMF(0ZgU-wcC0_y1#g5RUx<Gv70k!rfP~bx=ThNsJa_}^AtjEiJ;C>Hyi6DnZ zH_2NgWF~olc1|%KrsM)aD^}1_N6=7}GUNbISJ*fe+698hHwMD*sKz>GHLVfkVZ^;D zZH<T<imMtyGY&}eFp&A2mlMF{D^ei+fd$gsr6AM5fh5te8+`T}f9pgRXmkAKn*xwD zq)-NJwCRK%zXck<hqhB*egR!X)A|3!;*fv;U*^4m^pVo6{{Md&{sxqJU=1J8@CARL zE-1d5Ul7&sF}(dU5j=2$<ba%QU^9^&@EWv-2zGije@8dy;<j$+8p7^a56~IXFC#%0 zW+2qmg4IBZDWC4Nf|re8r$Q6Bj|xgYw{nN%xcmA4|2yzcJqk*Kpc3_^0C>AFc1^5M zO{>r~ZD~NtfamRTWI$PP1XY0cT7a4cFU>)7?VU$na$E+*_OX`=pwlHgkG%W><$nN= ztAKJ^JSeA?UxMT`ACNzLS)!qbyTbNDdGxY0dvtPjW*K<)vK;Z~_K5K4^b!DF4eZhB z1-h{UWT#JOK>+ARb+~u+KvVqS14Texy?u$G%|b}$ig@(uTzK*CBE;f#h!a&|BYvRs zPC?g<fR8#AfE|4bol5ZO1l?TZ8UeR#=OR$b19=EMJ+TaG?k$A5)R~@euY&~WYtRXR zoyT4tz5ufqJn5nbwRah~y9;S=;hb~<Z5;&75O{REf~Q<Ox?Ry{TwV%*IzZs#vM1!i ziu<-)n4REhiCq^UVW<JM6Ex!n3Ph~a5=7Vu3R8SVs7o!(A%AmF%8!?Cz^SSkk{ULH zhP{0{OF29`T_L^ommSq0hvSyd0Ldd)NBVWJ3Sv?YMn2wt98z{f1^oN}^1?3&N7n5B z|Cc*|f$ebs4fpqMz5%9ogYMYcB>_r=oy{Ppd2}~>fMhzs?s>7q1=L(&_UTTr0BvMw z(f<Aazu{YtUY}eBkLII{9tR(?cyylkVD1h8X=2_Dx+%w_o6(~?ki(;s-J>&{!=u|# zz@yV$z@s}*z@xiR!=t-i1GHW4g{RBE|2~#aOE<l|_Ur%u?%AMD6{s`%8gz?LXNwA` zetP*iAJm2e?>j&$tUNkjylniA?sbspkA1LN?(%H3M%B+6aMMBU>i_?S-(I#|0|f?r zc0S-G=s-8fX{ff@Ff)untUsXD*P0;HV7rSt-2z^+HKSYqa_VQ8zTa7}2>O_X<_ge- zV-Rbufux{4l$WPKr7!fhNtoZi=fP6c>a73&eLyD$fSc0LD-m8!1=#}51l@2}Bb-&9 z1sYfT$ltOd55!>PZ&9cO4P1j1fHuLs48v-u14#ZO$jfF~pk(~@rOADmGgPubkpXEh zhdYKjhI)4XbqonLd>ic1_y#m7@6pZPS)wB0&{3k206Kg_z@yuMe_I3S3{sEI5)}o| z`rayVevDBO@aP8Lj_zT(8I&{ldpyAsB`N|9`}Tsa%JuC0#^0I_6;x>02fE#hfxo2& zED|R1?U(?6t3Ie@@T`Hq<si63n+K{LTMl@3uLBwB(HWy+;n_V8G>;BqJMg!tfiEis z?WXE{?b*2woE0<=gCq?+c7u9V|NsB5_w1Yp+T-cj*#^o9j{Mu&_K1LwqBXqj3tHry zqr%bQqN3pe-rWe&0P?B?|27wu1kkkZYZHEX2H(!*Ah&pSt_Rx=SL$im4lx_NdWgSe z2{ULZUyce`yM|Baf5(n?kZmur)Bl0aBLFF8D23hm^Pj(^nVEs18x*|VH7Xo0rlx}9 z*0J*#f2#ls1A}k(deGfyKGq-;n;m&TGv?btS=_T5Iu%-@!r|Mw9%L-2s|Ru%=!{BG z=Iv}z0o_at5(eku&U-JOaU*UQ*}(-etQ};$XZLzgQQ_MSz7Gks^t6Q=6tK-dILlc; zn>u0V)q-w>76Gkl04>(=>|75D65r0{AX{I)`1k+6r{%l|VFm{N{#uZBQ0D&&x_P2u zA1DYI_*>pHfWrMhXg@%=D?`H$aJt}cJ;unu&~n?Q^KXg7i^q2V{(H0@DCIN!=F<89 zg-OD{|Dg4{9*svp39|D=bgW~HW2|GGWBg&Tcy|dX<w4Sf<vdU_;qPnv`~UyzvoBuQ zgUY%R6$Q^uR}SCqQU++61El~^66SAh`3q{Hdv^DMJmT3s4dfBe?l!O~En%R+zRts* zoo%3G<$1gf6v&|Q3lGiHpktjO$;qel`-}cmP~7eV%Y))}AE@a2&))(HCH`$CDiWZ3 z%y%XK|L@p&+_CxpN0-hYj{H-OI5z)hbl{(I)P?gYNR)r-;e#*a6%RYKyyb7X1G*Bm z+eJmdwdIM&@di*tf|9ex@dnTeP7ss7^(uJ%sgH_+XLBW}MBNEm_{86`@E@ot6`~?w zcwiT(VDLPC0HoftdlM)byygI}%kbzt<<V^8#!#W>(Ol!k!BC;*(apo|V|lPn*0XaG z$hw9ac0mTu&J!g(o}H6GmO}J3ACU0040Q15^<pgF1-|4P8jCGfpajre%ke_N`rrST zI{z?(mcPZ74V37>0qn64<SI~h^X%LP3OWyXSbKDy@X$OB&99&U)=R-2z_J{m@pNba zM<#*t&PPxf^G`X_`~w`e2Vck|f>-hI%lY7CZw?He$HDm)6rC@9pjV(8cv`lDl5f2@ zC=0Zv{Xv=~Y>D^-3e_A?Zu{qI2wpE`;n8~Av$GA9c0dE~4xXKDpajI<BJmGY(k=rf zBahB|o}KGJxyz^Xz2R+F!zV93=75sr6cvzK$Nk_K>YN73)1cN2D2hBgr-2qdfViND z@a&uhx*YT6+rOZ?|A$X^jtYls=XcM}V=v48V+&+E(E3kED)#6+b=*ZI1r*XAorl2* zH%3JRvuLyi`@*wx8nlF*2g<M>$6ZtkKp_NTR6rOlD&Xn@G;-f_1QeOp5I^^C2d(vm zN2y10j!KG0ua8OrsGcwYWr75c&O^ssR5%zsI(<|kT==&=bM1WY+4-jP>&ulO;~?uM zTsr@OGD|Ob4V7c(LH-WVnQ4yAKN(9?eY>ZDN-H1h5ETRd<|bwa24B$SxjrCgboYTm z+p~KbG^jngOH?9UI(<O5{)MP;z<u!IG7~7s_*;@#K+DcUR5)C^eN-Gk4rBD`3{f#~ zXn9*w;nVrZlNoH3r}Z>Y{MMv;T7yf6`WR5`fC{w^a2SD7=Y&K=>f~<)ZLf1|h86*> zI^eBIE-D(HmTgFBOY}b|ZNZ8~_Wz*q;*+nN8){TI7+gEg^S7jcYQ^Rn6$1wTmIx+r zrUAKQFKEO7R6Rv_c7p>R#LfVvJ_C>DA6y>%ZZABVYg8&2_*?uzeWm0Wl@yTQH9)av zc;L8;iUWw{+Ihma^E;@T<N%+h$P9_rh8mRw2L6__%nS^$65pd+g1_ZG=yLAP8=l(W z%|oEwKn6aRUw!!fzIs|7^WpdZ0NTpk@f%#YKY^~VZhZ^QVxR<Gs{HaaIF1=SyZga; zy*H52!}2QV07p=L()5=JRBitE>;xZE^1?3R-+#kzpoGv82g=lprN<pR4|sNiYby`S zqx@~4`>j2@`$5?TS{3?qz6V9RhD#^7InV>nU!L9l;H(6%3wu;RIqbz<7HCE2qN3pf zTEfrYVgnB4c2N59gw(pnTvRL=K>_Rm?T&%i0TBOz_k1wNsBrjNe&Gilr}N*j`3EC^ z%PJ-YhGrKP2S$%%7ZnE&YZsLY{-znA!o0agMT5b&^B;f9R#0$uyQp}8-3u-k_JTsm z)$oZY>{<=bX_Js^CSDxOK$I7ro#0D!K}7?|AE4tOKvjMB6mV7V*$KWz7gDHz5=8`P zIV>o{A{A2^&|>Pf9;p5VRXi@8KVa(cR7;?16I&8~g36%xFV3d_`~Ncd=l}oUjKkl0 z><_5*j580ui2eWXKRBbkoC7LsJ1=9aRS$b~UU<3m$N&G(Q||d&K=)R8fJWdNK#AX@ z6I^k;<oNmjKXPO3vZpq<;`i(XSJBW49BKo|%uOH--7YE)uqGm?D0~e{{+O-Amsdge zmUdq7)COAyDm_7Q1B!a279y%uFZY0ZVq3svqettv5)M$(U77ap|89_8$Nk_g2PgPu zR&a9#nsvc8ytMrB|NjKX&I2!3fTva<<J-QiPfB^ZLsT3<C7kfz|NlKKUzA8S)Tmf6 z@VC^1)>!@nO|QWBFC+4+i;72yq^Bh~A@k37QL%V^(xY1fw01Tg6iL=d5*s|5_k&7Q z2L4tLkdYB!2Y}KGXg>j{e)7<~@sbBr^L74k?fed|dcY&1E-DsCNeWi;AvFg|cs)T) z0a0Vn5^Yxok8TZc>b~dETx-ElBJSB;rvb`Bp3TP$UN9K{`|o3&YEkqObT`l^BZ!s; zNV}qh^M!)hzyF{n9Ju;?`3%&Z1Z7#MYrtg^D6u<0uhV(i{_p>P!`m+>e}y+l>pysa z8lxA#BbT~|zyJULGVkmE|E`ArL2JO?8s2`{@C`IZejb(`3~$3S=F7j}*1$ecyAizE z6BJ4w-JsDS4*nJ%@U1Z5RwVdXT5weXYGQR>^w9QEiSV%YQ8D1}xdQ5zYlo;9cy@wo z6=;hSG=!evp?T565~QksE=UzKL=`v=x?@xfd_WaGxEtfq&EN^i2rtB=|NRG5t>E@$ zR3xaRL~H)Ia6WDR@e$UteEH%xDEoq<(gW0VIR*+g3urikTL2&dSZwmQ1b{-Yc^@>1 zXo12@5>%9R$Eaj@c7og7FXO-!T#QPDXD7I~_7b$k9_l}jZVwL6&OVSQUVi-d|9=`{ z|E*)FN3XktV@PPQN9S*k<~I@^pv`J5Dxk`W15`!5;EDj<WzQfAI-e5kiIxgR@G+z< z1rWMp3aF0kp8NxJa0h52$N)4T-rf8F#J6nzAi}^<Q{mCQ`32~3klhzST~Pj(7_gdZ z1&_{8hTmRHivb<3d=zw`Y|9-`uy;3uP3sPpfb8&raIKvj%D6p_gE=6#d32kofRgHR z&>Ec17aq+=Bp}_A!|?ss4G;@`x>tjm&OZFEt3gdUh$y44<P*o{CrqGCmahXL^$Do* zXuZzBz|gS!GQ_2?AnthprY3_HlX`#_g(NdFF!)%m26>9Vr4Gbld~mG!3P_JfXER9k zK2Vth+9#>;0W?C}*$fK9%6TwHfARzmo4Ef6_5B&Z*8z8L2K5|V4G(y9S4+G|Q~*uX zAAR`@6l1Xc|1jNKJVC=Otb0Y289bU97*V|rp4XTRs+c^ws}){YM1w>3<y=sHd<#m4 zExG?dPU3H!{0}sA0d9*!$~%wl&0sG!gX4`65>N~!5gy%>L0$!$_aX{x9yqr2{{R0E z4Jwaru(v%rn?Wi|LOraTK}MD2K!c?l8~`tC{-dm)===mS6Pi3;^g@h&y%_G_&TB98 z!9fIZAH*z1s96Rev%vi!pU!tL=YtyNj1Qo}|GEPfRvjMy|99^Og}&jp7ukXT{u>^6 zc>!F+Ixu)Nzu|cC9b6Y4_pr9j6;@^_;r3`g!U0M`pm1!~XDpHO=w@9l3=e6J!-)Lj z(e2LhKfvJiUFi8g!7o;U46|lc2C45nT*Bwkc_SKR7{vV^-5iFuJ$eKGd$eBi=oBb@ z=+Pa(;n8{ge~`iJtDt?GAorw0jCdfV%uvDya|~QRLU)O<M>nez$f9nG7fyO0kG;Nt zWZ=WJ367zTonIY8e0tL*kh8c?rwURQR}TSYaZr8*Ck{};0bL9PDlb5DRu@3cZ2oOu zJvyJih<OVx-8#X!yF}6OZR>$bX;?tZyMt0Jaz32k(OoUjdB~^p*^5nE|NVDudF#=6 zv?Qrv_XSW!DT#!nFGi1UaK?f}E`x{Uf#wHH9=$FC|6y@C7oHxVS$>NfIQ@$X!D9s} z>x1I08=Uw&EWv4#e+oEfz6^$^7nqZC+(2mq<aNVuFZ}&MLHN=JDZPO+@yl(HEC@2U z`3(nH?8PEb32e<eSx}jwgcBS|pmrSSY5;ibmIxXi0Lvh!7g%%~9(a8+ZGz#qmyr;| zq45hh-w9&AF+TIb@h_;%-~oz%9gyQ+Uqy@maF1Tz=_bkypkYPFkWi1#zaF3-$2E^$ zQNdyc2G9s&1gLE9W<4pO%;3}OqY~iL3u+S|cToY|AkF}4aRq>g2oRCs(d(j;0b&V& zSm4n}4iF1`wvOR}7tvbawSSO~6KEre;kOshMM3KTfBXku3-}6LThypTAO;>@9D^$G z>^#EX@(`*Z<AuB!xRmMuE!T53JPGPiB{2B(?t!%Lx<kN8SOHWM_;x-x?xF%3Zf5|U z1q>4M=mZ_Y4X(pLr~Y)lbL5|T5VV>t^9*DH;}9bQgW-Rl&Ki{hP;Ce9SijKG1P^<G z9Ws*<e9E4sC<6l{qz~QQqT&InxH^4Q416WuIyOIL@@#&~=waCc-j%`M4_ZnG;(;bK z_<N3n>kQB=5vVG7q3{ag9WzGIwxv~I$yx`G*8lu1xgb-U|1<Kpt_BPHfI1~xz(e(r z$!yR{bWl?VJd*UnO$yv?^6Wgu-va8FgZHC9f3aQ*)MVp>77(7DVvu?kl!p(2s<b^S z7NVf;U&}oPP&e;|2Xl*x10w^&u@)5@kOau(plKY?8g`IB`TIZxEVO+6|KdQ*zyCg+ z4_<bI%z*A^d{M0h4X|S+i5|_h4h;M)dH?@I8r-kNCwO+c+!OGyj&<<pb!Oym24z6d z07y4Z4}$jHrAvanS9-{^S&y-l%d^{!!>9B6{{REeW*rqq{uWUE42ntp7*LY;XgmT+ z!H}9CIv@#d8iKswVGZ#j{}hNHUj~3HJBEfmDxi2}0JShdK>+H>fz+39dUSs9=)C3u zTGU>n;_>2lFwB4aty4j@Wd*1Vfw%WPyTdp-T~us9?h7*Lc2Ti;*^8W?U(ALiDE<}> z@P5gcpi{m0<r!Wy!^A<m0bhd7y8?$ZXde%UPv`Uh0R}H6L4y<E{A&Qqzn-0c;Mq4Z zpMfD1DgScvLh^3{DE~%)k~BR3!g4K(is6A5JJldL^ba`AL53Pw;dyQ~^jPS7pe9Z4 z8hBQVa5X$>coKB1Jt%XbXSU;@0VU9}pYQ?sP8JpL84&MZ{5%24Z#~fbmV=z%_Nan` z7j*ts3#ds2ix>XZE|BwJySI+JfHNM0;cZaS;{Ymf3S2=)w}6(4Sc9&C=naN?!2(h| zZ+-^JY|P-yX2t~0%GRJ%&fgLaH5wG*@C0D_fxqP)BF8iEx440ofl8{*TQ5Ebf*L)C zLH(DO^`JZox)$X93mbLNpn$OlXnz-|Mi=I9NrX0wJ3-;mT%+Q^0PcO3%D?0Xxdk%5 z)_R~K$D{c$qo-wziUWVsSCEO_pdLy$B-eu*fzX1k^VW+{F>sFX>^xp#3hIb=zJGB@ z4V2l%J$k+Vd$gYP?Bp+9?b+=QN{l9;Yq0`BiP5C>K;=wuy7FkwWnkoQxeLzt?_Ye4 zz@Bp^Aou58R1#i3hqaTs=YTJ21|=)d;d}o<4DcA%%Y5YY4vRlnu=2O=0@Y*P;7|me ztngx005~9DZUSkW06O!dVUG$pVnFBQfTE-%%ENjNxM1RMdB=b#o?Z%p^Dd|uWbo(? z27A?`^ZtKuzB75*@ejOy)0M-c^Zow-15nqWFXG?-mm;7H3u`|>i#o=a$G{$A@UV_i zaVU}UV0KaQfXaave1hEZa?xM#`Uh}9Q5p;?A|R99V9(rp@xUMK6L6B41M(##NoazS zbT?9xC~*Xpz*V5>fX??Xs=`6KUc9Ua>41dGEyHgwCPNH+y%E&)^YCE20V{-G{sk*U z_wMV};COWmhm}{SKsCH;=TXNH*UmGbdLC3Xcz}+a>=u2;0cs=I_JgR_1EBF$2N2-_ zA_6=*Yg8imTdhE;y%)5&q!%<=+Y27OJ8pOYG)yc2s{R5%EDjLM!=sZ&#iN&Zzm_t? zi!3EjaROR*<-p%E8<fYtz4*opsqn!SR3*sZ?iv*jqzeBkT)}bv7EnJDq(9(=9`t<M z5U7@jP8Ss&kKP!S0$2WRPhR}@{rA5+L`8>x+W}X@lc1|L6hH+LsB-n~`~j=@J6%*b zd^$gNil~4#M_Yi-)9AeaqWB1;6#5CuX}vWn369_vZH!6+C?#ir3bhyYkHC#v{+4f$ zA=K6b{GG2s>O1ehI2;PD{5%?ufWj21vVrs)L8Ef5d%-a!Yr(F};9(iT!QZ+D6z8CG zn0r0`do=%K;cuS>RuUlL+5Cgir}Mf;XNZagf8R;4XoZ4j^A9FabU5(0fSQ~h-2ob& z%|Dnysyz5x7K2rRYQY~YAfW*MmI+`X2Mb7181bT(2NCeCpz)U%(#qfhmcOME)cZT| z5_IYysQzJwi8n#SmBEc(hR#DCkW|dP3N+Yr<Rt?r_aL=jdwIWPfF=)5LfR!St3lNo ziu|Jt1_np)&O2Ab123a7<S#??gCy<1l4(fe5r|F-s9Dqp3jAKvbr}o{9=*2TGZ+|N zSUZFL1L{T;gPnT(MTHWm4EAM!wtGA}Lm5iMJ-WH!eBlypNC)}w1dr}e4p0>WE+m2s zK&rZ3R5%byORs{87N`<X*#gRs98m3P6ATZ$-h%92$8gWi{~n#!L7C6D^DJZ_<+DfQ z8x7DQy1h%lCyIe0%K(&nEI@<eptF%)Y_W$VN6=^oc!V9azTSe7f#Jn2E?5*E<!=Fx zL3Qs@NdRr{gU{-`m<CsKjK9T_6Ep$ymcL^IX!OhStq;HdTku4{RxtmaC%@l2$A<lo zY5vw`P>^=M@X>tXY1s~HJJywed}sg~Mfm2T62Qpc%E1M$EImQ_3M^M*?bG@E#YZ_% zFuSN2yw*X)_lw;M;P~Kg2?dP=>`?*h0u8)Ae{nz#TFHRI0x8Ep`bnVsmf)=gkIwrZ zttUPBT@Qjn-r_~5BB)T|Z)If!-PKW|65!bUjETP`je~(<Up3eV_g^?V{QK|G{DXnN zuLQJnwV9dmn2Sm-!!Z|?91s!C2s-rQ+c6iFd<Ooh2RdC;6g+x+R02SO()qHxMI{3i zrrjYb2B7mYJI{mSC*rsZ=pG&hP$vSVGvU+u5LB!jXHfwa6h58*UI_01M_?<cG28(4 zHh*g_Xzgh0ZT=P(&~RL{HY0!QFK`xh<^a`H0S1N#UcLrZe6ae+lkqKor~5z9;3qiJ zIzi`wcrw1@?*#S#4Bx&4QQ&a_sP}e&Vg>F;kVo?wU&@0rDY!lW#a9IA%y>?a?~b>C z&ng4C8MM|IG`a+1gJ$u;Y}k|x=*Z5FDUee&x*a$?y1_Ht9tU5@G(TW$xzy33auC$+ zZ;=8`$9Jz$xd1Aok3$wRg6B1xL4)GQTvYNHKqaaN$Z-C?37`P!Z2?b<`1C?g5eBX1 z@aSy;PXU3dIZ%!6)A_--^PgwuaZpW=0BOU$zXNH*@w0+DOs~NWye%pMAlmX>iGRZ$ z6_76(_*=|C3kgBX{yaKgz3}t^Rj`o3#H%0)$R$2XpoV-mN-4qLvX=!sl-aSCg@K`= zMkSnqzqJd(%wgbf-49{rGVr&68m|qIRZR>Y-Pr>CEn1*i|K8O99*}8$&}dj68^~z~ zA253!e8LP$$PTaVpyNV55J$`n0yWy;j_~XZW#Dh&V1_tC12h%NoTB33(fpQ?zXeo; zg7%C1cIP>Gb=xR-bh~qSShGekDl_o6OaWyAYu3LEAWl021H%g;Hb}X_-{Jw9*8(ls zuLmjWhK5*oiwdY8?O}P0zXdcFiU_?EAXh^|?~_O8u@@ZfprZe%FX(ReDIl}DS=TZs zGkAh7J23eVI(GxqmTV0KF}qzsb1b0ViW4XePVi`EXY}m+>(R|x#h}da`ZP#W6X*;x z&{Te3C@AfKZiN8t76+xDgAZ6ddR>?<cr;fCFz~m^fn8$YVO_xC(fNtLMHI|u)n|Z= z@_2S01y8l}%Yz~-pMk#xa*1MzN(8uH44MS?UJNdez#WR~Anl#Uz;pYC2VOXU3b57# z;J*Dj&@_4HG0^C-r3`4=SVx7?v-2o_%OucrEu^tr!3rr@Tl&GXqVGUm&N~5EA~25? z5`myaRnYkMXa@CI8uqAgi-99y4LI=mTS`E_wq65H#QZJan8B@E9x>1e186SA!_q~? zgTM6$=+x(JQ^3jFxAR}iCH|J9pp@MDw!|5fKMXuNUm1RT5$}pf>EK2bD3N`CVF7Ak zqo?!_(3B2p*m|_yF5&TL)@Dqbz~6ELl<GRazbKjsD&E0O+ruxe`uzLfP@|H^P+|p| z6{!F%J?sRn!v>x0(0ah5laarL8x&IAprr-f;MrLN&<Z6FsN{XHb8qvv{Qmj>Ke%4; z=)A$-@*X?`7^33g(QEr90W_s>&+x#Dbg6&;Utan7|9{#9&+brAyFSCG^F5?ppYie` zXo3w^UV=xrx*@0Zyoh%JC)Afg;I=%d6oK3)@6mbag)>yn3@pds0SogfEh?bk2KUlI z<tjYgzk4A7T9es%^yQ)7pdoq4c-)KIlHl|NnlNsi&&0rB_-+5s|NsBLP_`x<LT;e+ zhbx4nK=a(aWuTFb7j4s_VgDk=```bUyTFG6L;TYTKF>GR4_qI1E(QArbU#w(zt;_5 z_k+XB0o3OK_k$c>>ih<8Zh*Mo1GN4Eo;maufIR~$b3uc=B|E`!%-;$wO}Z-*JerR) zzE~*%Rpg>lz~9mUE&%p`%WA{79?kU$|4Upz2apzk&YA$Z5mN7flHU6lXYD~L@*U`a z`OZTx_(5UU4bcl;)!OZz0G`jWd{KT5)I$Vm_h>$@@#3l_xPJ>OGb{L84lpr*Dz-dE zPy@aK<b?uIM*RRbZVK2b9-T)$nzb1DTa-Z=s`YJ&h6l(6|6f@9f`SrW!h#xu{4HXT zE_o+tDUomIzn5N+D%gdA;l*!puy6TW9YE&*Pk6cM8z}M__Qim*ZRh<Lx6PrgjQ&~Q z|NrN2Jp&pqgSRsbZy$3}31@gY5hMq?pV9CwsG<eUl7s5$m(PEIOml&5Sa3Cb0#4@O zc!SiO#~{g(!GrMyC{{ov^vh4+^DG@XJUY*V3fAkOS?6vzcQvTZ3?F|2HOwMJ|NVd2 z26h1`dHw<=&lZb+|6j&{Qwu1N!8gu<q8wC-fD8roI3VpXjP!FE5;)NG(`)M!3(BX* z3@;fTc(GRu6u&Mi1}|@a1)V_j3DmghH9ZgmQgZ*rI}vbTg2#D3d<CycfHcTJO|x)@ zms#LB<0&ejhOUR@RsNPDuvcqT3_LpDy_f)M&~+XKCt#0mS8x+Oz~E)&FVLC>aHkGD zEEWgmL!1pSCLCT?gXjKX?W~uYV3WbB4B(l=;N?%y!fj|f<mD2Omd;x~o&WYTF@O$$ zdLau+`<=&MlzxV$$HV+B>QIk>`~YcLGQ3;}TAYB06Cto=@F;o)G8dHZeLMeQmYk(r z9&nwYF$Hk6fYNg7Td*<E`~sO+dU^9VXbs!H*H^(KIN)Na6|4*%K4lOUFOz=%|34wv zqnB4hMwtOLn+qG~?~4ZA>GdYSqnq{V4<+yzzXoWGPyy6VGXS;IK>5xBL^yy5576+M z2Z$vBVu8iLqy1nd;L&~q&`J7+2VUd~fy$*4Pf(@g@gn~ph*x0^YAS;oq7ELN_d&-B zTY%~i1IHcApgh$bqM`xH(h49WUhMe<YPB2*08f#D=U0vd7#@f|3@h(J{*jPI^$+MK zC68vXe?+P5pSgk{AHe)G|1XG#+drTz4l1IM{PX4`VgGPQq59`{Bs_jxzf(7U&I*8h z0Q1lJKOi1%|9~<9ntx=7@XrqkRR3rr{8Rdky8ijg5Ap%bKmUJ&c)0un9zsP)Z2ljR zA_rQ&HKV3CNAUW4$V6GNN9Si)J>b#&#^S|!LvZm{A?4A`%;?d1!=w3#1xCNNRKuh5 z7OXruD-0?xkH1g_o%9NAdV*G2dvv=BfL5=<#;iQL9XO7;s3?HDPT-y=YX7UlMMcrK zJ4Xea4Fy0s$G7!uedx<x@Q5-v->@@!bhEO4RRU*y%ftLFkeMVK70~^Rs&1fK5ott( zzXde^1R9rA0Ck9)k2CsOzUObT1#!AvR3v=5OB1}hZGM323y<&Mt{?*ggGA}{7ps1P zE5!f&EueW}P_w>eIcTKY@)&;;XzeFR1bjoO2e_dhngL$f2yf(<`g(M;#(e=f^RNeK z<$~p5{#MY~0jNF7-&zFfH+REJ@nrC*6v%&&@m?NKFB;s=;dyEQA3Wacs{n5k)=GGQ zHmRR`;bsL2cF<sbiHk=!>-*124B&O~p!>F56+F5_Jv_Qg9Xz^08V-4A9(y4Q(eV20 zi`V?%I4&(S{I>r)s3x@nMS3@jibpS~Q*!*ptzKw%p+p-zyNjFx(k6gss7f?Fx@}Zm zDD?gN|MCX74rV}_*D*Zs`UHG^3X=HiQ~dJq`um8%zyGg0(k3{Dc(i^44T^m(ap`u8 zSm97=>(Q(GEr5Z+v-1zXJVWydhfe1R{%y`N%`doY!pr&jw;k|Ec4-AQDE>bH`Bxg$ z+UMwYj#%MPa&ZE9_yCk<K}!<A;c5(OLQDlOU@l?v=#Ex+ea`T}>pReX3}}4$yGO5S zawr3A3K=x64BB7_>cN9LlAsac1drYjl?c%6aRI1i$pF<X@YyE=kV??98&H*^0pftG zJjjSl3wV7fNJauA18O;eI07IJsO19UfUBGeP^vdP@ZuF8DAV(|G=VD<7nKa3?g9b7 zUY)I=iVQU9FDU?yL2x4^9&|)4XkevY@^uNQz0=9Ey7PdK<%3cqP=ge7BT+1<rpoZ> zc98Jswt4a5H6J8-dv+cvz4YQPFW9V77MD(y)u1-$9d6Ky*!`dZj~5F1|Na}^_Gml; z4vo$mpkRe=iGas1Xjl*ATc7Sy0pHedb$l<nAV%`H++|^4nDF8?&p*Um3MkktK>qOP zwp}Eq%<#e%B-?9RCI%{DkH6T*1$GX&b9)*zveNCM5&@eU_Bieeo(uOl?h0OJ>~Y)` zJP6})+!bPGC+GxI7tnyWYXI1i&e{x*PFD$!Zpbi^N2jX;C@=~<Izd|<T~sPOI$aZ9 zWP&X1bS(fW14qwsSMXf9$MIHB1c6OD-U?bQ0%d~y3ubn<g4K6J<M?<hC=q~Vj<<qR z4wQ))mTE<eO0|Nvsey_~kK?VNwZKrdpb`ek1T8LtGC|9&piIyV4U`F*%7!vQ^YI|& zu~sc`rG2~=RP2F7JdU@5q7Tdj6-S`Wkq|loTs(EVW<Y3&U%*nJGbTHafToBKzF_w} z_=d^j;49YV2aJx*PZ*mYF}id<<hWQW*K(j#3haSy2aazIpwgIwzvT!61H+C6P;R@# z-?|+%&Cv3MzXde1=F!W_uMUdlR?s;S9^I}I9=&^1Kw;y_co%ee0*L9;3As+gqZ4xD z$FUZbmtdzKe8F|BMFq50jRDjFfd@$Q2gYM9DxgUW&@BHOklN-4j2$iDgN0x8-TL>x z`Nx0$7H80M=^u<GVW2erg9DTWKw~^DN5Q?n8Wjt}+b<q+{QLis4_ur%FnIK`K2(Lf z_N6b#M+f*@TtUTmFY7Tigp?6Ts2g%~+p$(nMg|5(!*3qlQ$eZ3qq7w>1kJw<;>3<t z&>%G^oq?;CPFD@X1Fz*F{mbq^1&`KCC2S2<F<d2%F#VuIt1T}SdwO(F1*I*IZk|IP z-GU&6=RgWePk1y}DKL~C^yn4>4=H+d3clU~DhD9qI}qZ53a?l3%QHB%eB%eF4Ug_p zjfT1yt`ecw;^6+8nMWr?q?FyGxlVzh<PB(TUyL-UVlCD1=&tp6;r|P=r(ru}JlCUn zFF56vSR%&DeL7!&l!EI-=2lQ@_UJZvAqLf0_YX9>c9hY_dM!w2(M6B$S`Amjw=Y~} zK~b_7oIOg^_~jXVI$wB#_lZ7W@qn3Q@j^}(w1RgnNLkTc@N(L>9?g3}A<a-K_tJ%t zfx)lS<cDYTaYm44AM3RsDgNe%;Gz_qJN>#%UO0Ypi)1Xl>e*cv@Zt^IzyBaU6MyS` z(29W0?=Nn${rmrVzu^JHZ!a7E{r~@BWgn;*eEH=UX#Z#D@fVz0|Ng(+^7sFLuU;Ey zp<JTk0NQa=4=PDO14J$=7NyEB{Xk<xpiwWkPMZZToj*J+kCz&QRwIMf^0$C`YF^zn zPbFSNaDqJl<G*L;@zV1zyg;e5^*|{nsHn04X??Mr9TW*QDi$wn*|3$SFW3J1|KGRu zzfUJPEPT4xf)Y!KldIvk*TRtbJJ39|kM&kicob{-ch`Jn0FCzX$$)(C9?4XC-?O(~ z;l)4cfB#=!@i^WJQU}UQu7=-SL8BtI4h*GQFPDJ^$YJyQ{2d$q{QuwlgVB+Hio2j^ z^Dzk@>s$x^=0;Fs2^4j`wx4}Lolc+5ccAT$%zHr@{6#d#FQC<TMW981-L(ne>3$a# zj~5$RLB0fyoi%GQmd^9&_5zKRd3bbQ{|}z*_jujq*<Gjb!jcs<L|Lct!hrSPfACNT zIQ_!=i#lL~LCw?;{M!Oz8h$C47?qnf{NgT=Pn*Es#tT|1@k_RZz2TQq+JsUo=%w4O zmrCM2x*4E4K-UB~D7@a^@GG9b<rk<C+ExVCm&o7p8nlSwS4zo6kLC&o29M5zrJ4=D zT*?(adU@x0GB9`~i%j%jy!ij2M>hxmw!oN%U*aVk4Zl1}m_0y;fBG6U)WvY~w+Mp9 z&W^W&3Ib4N3o4fqSU~9jG|Ny@1S%h!A1HY=|4;&z7vT8hZg{dEG>8J?d~=m#;cuw} zCG6%OOwh)#Et3OiA_24(3beIR8PqMc0CjjA_W6TUcD{e%a~9Nc1eXBNatD;Go-u>g zE<W+-e9hm&^W*>j{R}J&3@<joqDllb2Gt3!M?5-@zqld)@BhoEkntD~u<g@8rh{!S z$p+cH63J#SkX+~c7njb!ZGPQ}B<}~6--M8VnFcOXK{IHetqJ>lpi26YlsKa*@rEkN zK~kcCs>Bzn#1lygXzfA6FHZhe1JFPZB*L0sc{TiE_273u@zNJ88W7;od9d4w1(a&7 z!6KdtFO5J&Lthst#&Q`Lk!o4~))U}SAaHs#ybT(R+W8Gsbbxnfyf_VBJpl^zLMBjv z+&8@aBAw~q|Cdw1y4(Ul2ZDi<#mg6<<r%+X_*+u`{{R274>T?|;e~Y9zyB{^L54tG z7#JMGJ$hNUg9es*Z4G!~%cw$wAxqs^eR-8ZRoOLLP_e<fnOB(sw5G-5IO_#oWd=}d z%A=Dt5TfSOD<uYx<E;D=$_%d=nLtbzQ8?352+Hhat%4|D%nzypS(iX~p?qN8JP1!6 zA~yrVi!=uLlhsKa?38YBPlPoF!s`Whty$9`yvA>!ZWHSaC=Wc0z<LP6n+#F82f|C0 z1lzX_!ut*y!|Y^L04)~pWi1Bx5m_fec(1`dRMs^Ro-F8$-cHtK5Z-=Kuqg{5yg&$V z7KB#}G4qT7*wtIX9XeJ!&|1`9*5eQ}BOtt&pff9vvu=}t2a!8O;s9vzZYQfFg!c(V zb+TH6s9shRBXFSbf`+nsS<60vdEpRV21K7BgeL{z&6ZJSc#+2l?i+N7feMdaTaf!- zL_x*CM?&`6z7|quc;N;W1FiS+=(WAhugvhm2r709l$m>NlckjzUdTbkz+LuU)AyVp z&)VjBFfbUNG`!@v<146nbexsBUKvywFub_;|1W6MG=IxfkRhGNU*t0X`@dggixHy- z=zb_rf(BK8hf8EUdQJ5_K!c`-K&^;x|Nj05_kUX$nHU&A(~b-;7NM$UL#RFhSA7^{ zO|R{DHf4qvhZvxJ1BmGl+~KCrfU9o-sqfbP{{)<rUFsmt1}#W;W%B6VDy7K40PXkj zZxaA*E%vay<-zZK)1&zSn}_9x(w9EHDxf7~44^*aMu-E!vu0f28L~&9WhlpA%=rKJ zzfbR02}K5m*D_G^KqC#H?WG>gN7+DEy|#dcfcdw%GWqtV9A)rqwozdyDdyi^%Hq*$ ztM3k~7GDHHJmlGVq@;*{yDNiFx2@b0B?i!*&~B6CAibX5HAfj<?EL%pKj<PY@QL-U zmpnT~R7xj+POAr1k368Yl+XbK(8^87LJs)W^$9O-Gl1rhSX98~HP;+vVBl{p0T+z6 z8nwy{9<BdN*gQa+0ICe%zUG<$>NR>;-Yrr0>~?tzy7ac>tw*;7)N0Uz{(GL?HE$U} z!zQ3@0T)2_`E<TVbPZq5`uq342V#T+v=alto$yi@T!6v`KqkC=4<7voZ!!S2Tfy_6 zFVrAG<I&05QUjU*hFn3f>;4!t+P2k)iGks@IHX+z3U@=0cbbo~fO@;-{M%fa`L~ra zdGy+zbOr5a4LQnSc;LkoP#S>_P%(lAUABWZi`lByD8mB$Mc^OIa9)5VoL_f?JdNbz zj+TG_4d1@zg@zq}%X3gO7Z!dmQN!;RO8A}W`~h0O9>B!V`20UR1H-`=+?~IgpV#wm zV_`h_iru4^H(HO0;ot)n#tR<3qVIi~82Gn|Fm?WDe$IdJ1xv5U1&?0VnGQ@0{M$tW zI)7Yz(JOL+@qpnE{%s<Hy(|G9y`pn;Kw4Os4nxx^*uHdauzma>`*@J;JNSSVY*Cv! zNEf<A6Qp1k9elym%QL~FSJYMr<T@t9AN<=zyn00@c=WQq;smK;2{;UHUx4k~&JMN@ z(|<6JnXzKCFX0Cx)MIe_);?ooIQRf&9}CC{v+gl6@NWbA(1n2kY+w3aMo@TS`p=`6 z_w+fC2@r4YzXj0>3Ig_nub5%ZS$+W|4)Q+8Vvzudjo>f^*|_2iNE&P-#J=yR!S>06 z!jrf2mjnN{|Bwje@aW}@I*hQ;?-WEQ$U^>uuUKFfeme>hhg&!S&BE;mLDDQthtnp2 z{nvf~Y#+>jsP?^D4YCiCVD{|;X#|<u{9OLv3sy*id9fWiTtzOR+2*+dqzUXj2dMv+ zgYBya`H#Qz7bK}de8}z5%e!M4!p5HEAk82fL9ROZf}@uOlyXhs!Vp&+e837yr96Tj zy`nRhGBO-|$jW%I^F;GQ_Jc2&L9u!HNAm+ln192Uf*kz+;0pmv%c~cFEQiGB`XyK` zuUHHd#<YCS9GsRT(y#j*6#Mx>he3eyCyz%jZ}lvY{gB{(KO1Bb3lk&>9DKzAi(fOa z1SnK6U2tm#9v48ufBFm*``Kakg93l|G?4ue|MP>?fy)P!z^|ANau#;mW2WM<9hRTO zr{cC>dNRWP{wXkvQ0)IP877SB`#Tefu)lmF-t;HY2XX==3f@eB*@fZ+QMfRs6D+%N zIsuyBu6N<JzoHXie@+)x`@eR=gfZ=}>>y;ndIw7QqolvtZ3xTHw}WiKmj1vJ*i%4M zD^BbA<ss>hy%o3p)y)X|*SFxXUkogP-F~Ykg7!ae#BG0c1H%4(kh*PH{Qs>1<S-oe z*AuqC4v+n{NcMx&VYR;&kNq_S?f+hlVn0g$k1hw<56S=jAa$7e-=Ye)?R$#x*bdA8 z;l=pVe;&vQko>o%2xb>b{wpnn31g=J$Sj;rfaX8(EZqLDP6b&G@qd0M%oY^OBQjvZ zn3ms3#A!LA{4Y<$>;D-sASXcle<lHD7m5?6#>0d$olqHp(+M#DN8q)8M;OBXo^Y5& zDE5oOg)!~71Zl@x|6UKpYyXcRg#A9DSnZ!00u#oxe`Ww7|C<M(+s_QE0=YbTd4ITr z?1v-)9e<cb&Clx(zF>zmKgxVzqL5be!3Qkx=Ep5pko7Fsnjf(8FWePuzdfk^!v}7H zpx7_r0<s?x|8HDiHlf%L7X^EN0&?@G${A!o*5(h${$C!wyyni3^#4kr^GD<J|MDQu ziwGWk!Ryh>JKKqo0pdwUkScKL)A;;9BLl<1SDdi+$2KRJs}8<k>jky=*x>$`ast`U zhR^@k9U=BZ+g~X4PqZV#^Zg)o;PMCRf)|_+r-i{KFzx^4K+yi}4!G_225E=(kMcq4 zu-R`0m%w7bJz@Lpaoc~}4&ncNkUDJki@_x@?Uw=>@DP9a+v2ug+7@AdKS&)m`_14I znD$rM5cI#f4Q~6Ztr7OWw}vG%wD_L}m%z0DmK8z!%dNoq52^owlK*~~gPZ`#|7$E^ zPC(9o*DPS7nE7v)8OVN|`47}T4>v=%AEgSGFa_BU&Htt_n^4N%sc>Pi11=yJ06&d! zIv{OACn*1KH->~iw*2pH49owGAXTu~X9ks3T#(%70ha(3h@jlh4$u8}j6jyNW6%Ad z`X6L}yAf{tt&L#z2ZB^#vtI))fyI790``N#f4U*WerWlF68P5opumSjK0inu*aawo zUts`?6U@M$qYJVhN9=?A53)a87q|V=ItcsE>%c5Ru|EPXjKzL!V(iz(ZU1f!B>T0n z+HV0D#<c&B8WHwSSHqkBEmT2HfTVvNHLOn9stOavbizy(oKAqYf5KHz{Et%qR4XGa z=Le|+$3I&6vrQRcJ$n5&O9^B>-tyO639tWWC?cG2MiFKcN)ng~7sm9zB}hBo_Sbg> z-2Ts2Kv=#Wqz;?^)8G=A<u4b=0Q~jOae3VK-<CtzpAS-p&3-Ys1g8B~AOrB*|6LZh z{oS$%``3fiVY9ysE`h~<kO2hjm%(Gd43hmIby)3(OJK1dWB>vCrE%L|EsbP9NF7%D z;SvX5upWHG!g%Q516KZRJc5QlJbFbrKt^CsfRORV<5IZo-z|x-KOdwHoBiJ;VYvd+ z|56|W@caL`1aA9pizC?&QisicF}MV#{Z=3Y@Y{b~3~&DbA&PK9ju_0RDEVI$E{vJ~ zD?!?^2S2p@6BosAzc9l7H6mE;4}}Y3+HVQcj@SP0Lb$`fS_onJdXPG7;a>)qzzlyW zkO8>EAJqOhE(oz7Yx_qK*8Z3XQiZ+$69$%ojQ^mQ|E~n_mj598mkZ#wUs?cWe<4T} zcKgl1QrPXE#gEs1aQlNFVn4R_k2EhR{2}e1^L$t%-vTa-8Tm3iAlnJFe~xpb*pE{G zT62T!hgAOgAa&T{-wZB+8UMSuKrX;r|CV#%w*NOL!hU^_I&AiT<Aenarv0-(2H;MA z;P3}&-;RC$A({hZKP3F$bHJ=Y$^0|m!kFQIhaKd50^vWM9mW4B?cd+52>bOx>cA-w z)cJYE4(t9;XM?%x;0tDG@y`sJBQ*R0=^=!%f)ubY9fp+m>MY>$1~$KhG9Q`!lYs#? zO?aOXqz#m;q4Sa8xgO2G;PDBj&L7~}GxW(Ut?wXd@bnU_eO>;I0W@C+n?Hjz-XQZ! zoF2Wr5g$NSLXy~;uMpj!88rEWuUKJTEcpx)2RQ|7Df*n2={t}#3)5l9{MlO+`#_Ne zu?;j|*YyNs8ze&I-axd0Y(t)}b9(_2huenk#pe$}(un!G?GM2A!NL>OzFoIL_Cb8t zcORk=-9EFsAaR`bZNCMQM%b5r3v3@!e;FmoWZXp9_~#}>E6B$BgNP)v1S}4*5o?k; zd>x|ka2g1}_v=Bz_ZrxKn14~ykN0_y<&fZ=e+6U)$P(oAv+^QL6daZ4fy;FcB#n}O zuAjkeU-Vjped|wxv?AFDnLppd0G(#WGJk$&HAoBCHt=F_u>Y2?La`4uKD(BHEQG}8 zy%iwc5DUTcuiy;Au?!}9@C9?P$b^Frm_hw<(3CxBuGDi8NE&P<bpBO*As+jxW+3eI zn-9{8ZePS4m?$_v(euX7DIjTN`?iBb5ak_8dB1ux!n$~nI&9@F8(adjy!D#Iz;F<M zdD}h_>|a=UfEvD9-5>`*641Q~5T}DuFLM55>j#O0!U0Q^W_E!jQT*G9$G%<72>af3 zK(wOU_pJ>i4zdp%!stoXya6PQ$e-*WJ|cWkGN*Pm!n*i+kUo&Hpdf%xrfbx}gfZ(A zopMm<;;v72fVKx9o$m=AU%Flf$^rimVrySnmoY%cmluLmfom5?0gW`i90ryGITSO# zca?%%gtvYVF9nA`uJUR}J}5{aIjW}w=694JF)fA(V+P62Y>)%61_`u%9iENf{!}FU zvtSmX+Mfv%#$taWLHiT&+8+^vuzyVg%pw%~OXFd}nD$#n5VHSyI3)g|?MsyWULA(8 zydR_voC3g>Bi1)u3qx4X1}@PM^9#E|LDu7q|MF1W_H%=@L&x{^LF%xTH{U`~?7wjE z0h0Y8gzX1u$B`dr`-A)sDQ?aO!>j@OAGWw<Mi5LGGw}C#gKWo|_>kroy;1y+QXW*h zBkX_g1+xgnehIiR7W-WY*bnO8q`N@u$Cm%4U10g&5Tptg`pDzU9B>KD@!dVnAp3ET zFZ=Ye{&WH@+v@yz@C9$<L(t?_r=27JHWr4?Q~cXb9(={&)5|IbntFq-P2+Nc2Eqw& zW0n;(mci`EzfDBI@C2kayTS^jf`!SWSH!`im$mXYXw@Lg1j9>?-8>U|ofunBmPj!E z?DUz&;?aDF(WChnbLrC^EDQ_`htno}#?qcK-EPUifaE^@Z7ggs$9eRcTACqZ=&S|E zY7wSRWXJJu6A^>Ct;ZOmz@wK{(TIV8*=9oLkHbjkgLVG!>19nfM6nM&G*0LuEUYnr zn1^B^N@!GRK~#A3vYrR6jP7N*z--ZJ$G<&*C9U;RiCkwG3;%WpwzSp*B}$!c9N<mt zpv{wz{ln<>#SSJ=IDz+bgANVjILv?_1l;-i-{Q{S{|a~h{^z*!_y336fB!$Y{rCTc z+kgKbxc&Ek#+|?akG%N%-{v`l2C;Ero)?7G;L?jOZUZ%I%ZtDNL!frU#L(5^;)C>J z!?@HB25~#6Ut#ndC?7<-z4-edgtK1!{f`a9<UlkCqpLv|L+2x_hsl9xd>B~|hz(K; zqLDF33>!vPLoOR+F0p!Bp8x$1!X?lD{s-Zd=YRi$FpLe7L&hL+5C*9MVPrXKu|ej8 zFvxrm2AK=ugD{8>!XPnR7^Dt_LFzyl#0Fsy8-zh@5C*Y97{mr)5F3QCnG2E!VURqG z2C<Jk|NDQ-^S}S6V8MG{{QbY=#ozxuFR;sF(}PW{ghKt;%%ruLAIR^||Nf78@%R6m z7k~d>dGYtZ%ZtDNO<w%{ukzyWe~}k||FgXK`~S)FzyAZC{r%7S;_rXa7k~eseU2S} z!y*10hd3^C@QL3*GHcKCzyA}S{r#`=@$dhTkAMH?eEj>r=i}f1OFsVnzvtuM|93wA z{r~3U-~WF;{{7GL>F<A$Pk;aGeER#J=i}f14$}YrThSO#z+(OaEMmCa5rC#P0gYdP z#&1C5V>6dS`rrR6GXMTBp)uZr#rz&DVz}I)B8%$ACun+E(BxCl<guA+BJ=P63YmZZ zcR(?SJp;-HiGkQ1NPG~x0*Mb|pHTSsAA~g&{{07G1;u~=K^Vja;R=O+|3MhU24M+> zfB!-F!{mSe1*XszW3z*S1Uny~xsg;pHvctD{`ddQItcDr_wPS8?6daYf0niX{+F!& z_n&3ezyEJmqT)HrA@U$KAbe;2zyBf2F)>IkWGO`LltuskZ&~o~zsiDt|Ch`|h?&g& z_aCGm-42i&MdtqdkBryM`S%|i2H8Dj&cFYZV6a<}`~c?9`S(8r3w|>D-+ydaaPhzY zAiQVOzyH`UNDdjJt3j5d78{!#*u-$zhpv`XzQpl=|3Uc0v48(h9Q*fw!Lfh;GmicH zZ*lD3e~x1iIgmUIpE&yO|AeFe{zn}B_g?~pq55I+)SzD+`S<_8k$?Xu9QpS@;RwVH z8b|*9|8e-={}YG*{hx3cB8JPZ43PZcfB!!m`uBgrp@08L<(r)N_aB61PW=1-=lH+> zPmcflf9LqW|3{Ah`@iQnL=Gem!%L3;``>f?-~X25|Nhrtk%y_HgibmB?|;bgfB$`s z|NHL(v*Y-`|0>7-{g*lZ@4pC44C;1tyZ#*e_y5YVfB(-M`}hCIv48(b<u@Gv_aB74 zF8uqibK&2A)(ijs|2Y5e|Go47{$D%~k=u6u-~S#cJOBK@|3T;f{dYS5@4pdLO#J-6 z|9{W@`~UbHME$OF|NbvH_wRrIxqtr)&;9!!eD2?WOQ@VYlm^*}3p1Vj_y6hHfB&zZ z{rCUqS%{e{&i?z~efHn~>a+j;XG6uX>5n@5@4w^OfB%`!{`>#!%)kGJWbi@e=AZfZ z|HPSp|M#8w_y7KxfB!F^`u9KO^uPb-PyYMQc=F$W^ArF6FF5}1{|n*&|CNOQ|9|-A z-~T4z|NkxC{u>b(<ZqZ?k^O%`;QxOE!T<jg1pfbj!2kb$2mk;73k3iFm-zD!jBmXA z_rCy&Gk*U2Z}9Wq{~tdfaurY-q*md_zyCMB|NB4V`@jDd-y!l6zyJN8@$29J8NVU? z4L>1t#jk(=Eq+1R9$z5ziO&!kWEL(w<J-Ug5#Rp(m-zPY|AVg(^&MaT{rC9#@4vv; zfB#QF&BChx`@jD;zWw`O@%i6>gU|o||M)~YA7pOECy2cspZ@)S@$uh(iI4yOcYOHw z|HS)$|0~}A`=9ag-+ztw|Nh_j`|p2+?*IQ+EdT%iq5S{9ht>c86~_Pne=z?4|A*ZF z{{`~@|9cqz|G&!i|Nk4R|NmdG|Nmb=?*IP|x&QxV)c*hPQ2YOXhvEPK5vKqDXQ=%D z@8|XZzmdiN{~0F#|A$-r|1YEW|G%T&|NnC4|Nl=g|Np;3`Tzf4+7NXD+W-IG*ZKcn zQ{(^ta*hB06Eq>>Q?&m7k5K*ppUwFH{{a2}{}1T@|Nl?t|Nj#P|Np<%|NlS2;Q#-7 zQvd%m$o&7m!|MP46*m9>r|JFw|JvpM{}+z`|9d$7|9?Z}|9=aY|NlK4NawF``Tzfh z$^ZW+od5s->-7JBg!BLZD-{0!U#9r~|7+X-|ChM^|F2>6|NkZT|Nl=o{r`W|<NyCX z>i_?LQUCvcgX91Iv)m#60ENX4i~s+BdHnzX*Y*GZ?SKCLUoZLpKZE7}|0hiU|KH*8 z|9_g)|Nm+V2=yN&{{KJ1`Tu{2!2ka>oY=7s_y7Ma-2eY?;rjo72^XfCCs28i8W0AV z1){OxBT%&}P&+~9fz<PG{{IgWbK$`57LXcr_qnkD|F6OZ!5}sagX~3DODZ2^AIKbJ z3^M-@%m4o%3}VADNG&#egysMLDJ&5GfaG16A?!U&5E>+Qh2j7I7>57<RsR3`f92o5 z|33fz{lD_}-~X8&|Nk4f|Np<h|Ns9Ve*gccM2rl~;P?Ok51;@41$_Shm+=1o-@xnt ze-F?9|2GEw|1TNv|9^r1|NmP;{{I(>7#a9?@c;jlg8u*44*dUrcG&;_k3;|e*9iUp zKVbd;{{~dT$Y#OVFq&F+3`TPtpy3Cjk=<Cc?f-ue&ffO_KQ;`KL&oT8kmabw#%2dL zF<kbct0k46muHocSR7walwXiqR8kpVmY7qT%8-|r$B>t2m7ZD>pO}*qpPX2nTFj7I zQIMKklA5AWTvC*om#&asq>xmamX=z?0HG8zixvDreSBctjKpGvywvo>lFYJHh2qSr zRG0*at&p2poSRsZoWYP<RFq%D5MPv<o>~!~oL`j6P?DdokXxFZp^%iAoLy3sn4F!N zm(Gx!pO;)(RFs-mqL7zgqL7(a0&;<0eqJg=W?orhPG*WikgGy+ehPx0UX)*2piq>W zmRgjWmz>Iwm#<I^G8Zfk*Hx5Skdv64nwy$e!jO|$3^F<;wL&4kv_v64O`#|;FFlpP z2JAdNUyzFx@{_VslS`}>iVG6+Y&95k6c{vh6u_RbWzbPbE3K-kj8D!l%_}Lk)nL$M zuw&3sC`c?S$xO_#4Jj&3Wq_C!1oALQcUn$jda*53ltH0JfuUHLA<y3`Ek7p(9P|wS zHU9p2{{B|Q$%%P+sYML_HF^G4#RWN;B_*jv3^o2%U=J0;St*&xB@EfAmBkF;=qzSP z%P-1JEP<M1m6n;8l9-c&WDHmaWI<|TQE~>1msXmS1NIh70K$n+&d)8#%t?($3b^=; z)SQCUA_l)a27i$2KxUODp>P;LUhoHbBrngZC>3T3H0Bw69o;>h8JzuHT;qd1qg)x1 z^9w4AGSf3k7%~|${TWj8QVQ~mkrDv|G)Nghu3!KMH$!e>1vpd~^2<_-auN#)Qd1aG zQqvMkb4nOMX&IC_;z6ko%!8)+%+z8qH?^W5F)t-PKQA5>@Sqe2Nt1d3iJ)N4V<>>q z;P_$)$w*ZIM<gWXK^X!pp~sMyn46js4@ybJ4B&{#%u7kFU<fl{2s30TE=@`U6L}0^ zr)Gj;5ELO`MtnTT+wt)X@$o6CsRb|&*dD!LScJ#Nr>B-=mZav!$1~)mR+K=4P%jvo zJfQ9dCsu~=jMS2h)FOq<67^z*L|8(EI9iV(C$YE$;ye%w>{?JlEM`DX#SFzIiA5!# zC}033d4}Y~f|Amr)M9Wlg9IC>a7fHcNrTHl^b~_M6@zsZGo+-JBqnFXLrM>ro1GzM zF$B5B`v=6kIePkp2Dw6c!J*F1uED`jo}*tRf)(%R<AchJ4{{Ad5<%sKBFTgzs|@k? z^>l_xI{AnCxi|*FY;g*5^mF!r$~imw_{0afy1}>}jzNyjA+AB_{CH1yKWt)1b~?lK zB8&O^xp;<n`ujP;eC6Wm<{0V|5|8F6n6Rs#OT52Ze7Jv*3)DK8OmK)}Pzaiws~=1i zRG*KhA1siOMB-t=1QiPo3G(!FhnkNf7LREFL@`(b-5exWx`jqX!2;Ml$T7gf)z=l> zkM2RPt}c=BL9PL=jv-Ln-Glr?1CZJApcp|BaE<T`4uK^WZ&z2Cav#SiG{b!S{k<K7 zK(P%qz|S=TW>G+pe}HRH2+WNzE>gTA3n4`zTo{q^0)j%}!5rl1=MD>UFdNB4I3LM4 zh#11GAXm3|<UoM)<K28<k&7aRY!*x}vYp6cSk)rz4t8}6a`uP^We0y)iVt=TiFfgI zgJlR97s&=>AtVdn!pP=_#C!T7auY}in2%-}Tnx=Ph&ZZw!I8e`?t=-TnTjloW-wd| z)$C9|Pk&f02e}o@M>7>JhGry09LYRyPgp{Q=Uk-Bg(3#eS8!2Crh}$pxF}M7LsJ0C z&qxUdA`22gb|pC1BYOuffT;?}BM?==uCSa0H5?Q`h$M<Ch-n^3HKGVbQtj#Phg~(o z1y24hk@1M44$K9I2{e2_C72^}fe#XJ#wd9~qR7P*h>xvEf`}uEB@iF8tO1Lnmp3TJ z#rwkx32+9F4{{9&4T7aFBomPo`Z|U<d&Gl$2=yC8AjHuVR)2s!4lOaj)w)7vUP@+i zYOz9YX>o}{QYxqcotmCnR19iGL+a#W9dOH9RnN%4pqPOH(s&_8A9NWQ#1xzwKy3t+ z4n$^NaY<rca;lC3#7c#<{L;J>286oOyi8D&O#z`0)bfWYW?*2*%qvMP%1g{q0Cyo2 zGV?$UveKNyB8Ak7f}+&o;>`R!h19(C%)C^v3qegXoUTLjJ4geli3e@;!TLODMftf3 z<r$gD844xv1|XzApirJztN?8qr|7}-=auFrr51rY5|CCCsGSIok7AH}L1sX0&|?5M zDZ!0L1&~XN6-tX!QxuXa5jKHMWnger0JXd_^YapmDiv}Q3ku+dDuA>XE0p9bfbCF# z*j^0P2l5ZNDGBYtD3oL*mM9by<)@S;r=}>BWMmdAfE%SHAQyu>IbfYc>4UUAp(ca1 zf&D|sK5&-;MHeI-3i69HOF;3LSb`DWkSJ9Ew@N`?F9!EoGV{_Q)`IO#%LFA1xG71g zmHBxoxJ)X~&r>f^fEWXf2juVr#U3c8Kuu&w!#OoY0WCUpK-mb?8HMTxMFgq(92H7R z3vyCn+KLMj^NL{{H0!`o3W^tKo&(1uC;>t|k(vT`k3wdeLS~5qs573FnwqBoX@#eP zQYe~zki-JAu(&j-xFoZr6cho)I-u;AS_Eb*B<7_kq@?Dgf_c#31|@a;m=OaY)H5+K zKxJVxGYbO*vO;Mn#lpY_5od=`91NTw1{VthH#Z9d0}n4B3j;SlgMc7tIj?{agD^;z zg+YXcL4<(;OpA(wgv3DdAPjPZI12*=fn-3KfrUXr9K?{6l7?FhQ!T^7AS(wlOkP0| zDy9VDg4iGoGLb<EOfo1lsHmtgs4}RjsY5-;$O!WyBWNcE1NbHjuwuAg5KmJ}n}LBr zN0&iQpMk-^kb#B4h=qYcT3Xr|3J|J6?qp#wflx4#fx#3j0GSQKX67h>!5kzAA}zoG z;vW_UODk&|TRR2@dj_zug@uI!gCm0zgEIqz1q8Z4fh!caL4i9IctC+C6fiJ&dHXQ< z`dL```v))tGB7X%1&1(%hJ}ZRhev=N5gEn65FKM-5gQBUGQ`CvFeD~fSR^MKfdx`h z(-_h-GBdNXvcY^1k;A~ikedf$F)-wVsRAfq0C@#00wN0GfQ6x`xCA5z3bE3%@(R$2 z=kP$StOV<0fhmCUs=x|Z7^*?yHMMp13=9p8P0b)9TA-k{t-YhO3v_8NLr-sC|AdK9 zpo#z>hhShvCMMABVNh`v1|}v}HV}s$3^+JBxxfr=9$r3v0YM>Q5m8Ptc6M>Fpai?5 zlr%d?HEt{emXei|S5Rb9QdVJ5Ra0kYXV=iw(qdu)og5941{tQU10vbkb@d=Lgw!`M zFfaruVSvN|m`OGPO2Q_lL}@pJ*pEz_TOjioEE&ugEJ37|HG?GsgN?17y@R8Zvx}>n zyN9P2=va6LA79WJ^9&6B{tOJ@i(v>_9T*f81W{xF0uZk=1c!u%A=@n(t_o4az`y_& zj{wt>a6u?ThaDQI><p3YQL^mp(V)v0p^_+6EF8du;~3&$Oq?`o+Cf#800O8&czKCn zkc3waBE>K;FfcMPv#_$k0XrKT2b{yk$;HOa#>N8`;DyqpQ+)hvY-|D~>JwyRV-o`D z5e5-#Y-}P>8bpbL2sSo021X_(CT1oUR*<I#5aSDQ&^i6=%nS^m3Zwo1|Nme9{r`W7 zfq~&10|UcZ1_lOy1_p*x3=9mX85kJOGcYh*W?*2r$iTpGm4Sib8Uq8vEd~aLn+yyL zw;321K-)Lw|NsC0#Q*>Q>;C`$udT(v5c~iCe+?}L2HXGt|GP3UFt{@?FqAMbFsLyw zFo04x=*j?l1_lNW1_p*l|NsAg{{R2~kN^MwfA#<W|5N|}|G)76|NkTZ|NlSm|Nno` z-LarN#?QdOV8Fn@kRZdr@Z<mg|HuCS|L^$d|Nm|O|NjSFJNEnW|NpE0|NlP+bjca$ zX7&I7|2O^r|9|nL|NkBD|Np=1{{R1=GE0DgfkBFafr0bd|Nl}S|Nob~_W%ErtN;Hq zU;F?6;nn~DA6@<b|KQdC|NoT!|9>Ou|NlKv|Nn1{`u~4x)c^ncqyGPY7xn-DwW$C9 zUq}7_zkr#6!64)Re?*;&6zcH$8O}gRBDGJL86c1WMnkyFED)NNfepl9XJ+64-FnT* z#m&sX!Nb7I2P)io`56R2vdj#E%nX7c69kzVgoHps!XSAN23ab?%m6_k84zY*W&nvW zGl+_bL+u3%gA_AJFf&L>fee$Dk%gKd2jYU*APiO{2PPTh859&07!(<ll+fEDARQ1j zU{YBH6!B{63>umY3|iU@%nUlr3=HDp;<``(Gg%Mh5Cmpm(8mfGK*cBmgOdh0ff#}T z$aat+V7rYN7>pUf!iI*13{YrjXkuc@V8#Gywt%V*LqiMjrC)}ImX<mU3=CFQ*47|# zun7z{3=9mQb_<9HO1TUS4p6`VvI-;&#*T2n%;4ng0+Itc15|f_!W`yF22W40J`f)y z?*(EoFf(|AH2C=Xf$EjOpkR<x2o!`ufg%EcZNx>Omf_&~pB1DQQcSUPaIk|}oLul0 z0EY-08>AEyV-uH<1h*DII<R9<9%5jSmXVcXWRh24U{F+2W@BShQB_j|6?zaoARE*n z_ONMaf_N~jrLC<E3T22oN=Z->0<~+2FvkGq0t9VngkZC=F&Hx#Fc^bL69!`jP|^YM zz)6L{0@Na4V6d_Vr3VHETU!POJ0dJ)V6b;^02O)+3=G;J0QEYkS%GY~xT_-EG_bfK z0|Ofyn>bt$%3x=M1}Yna8=Jc{8=D6xIYA{+C@(lb7Di1Q$TAEJ-XMS~gqP<72B52O za4SQkh(TSygMu7X$3rT4Ha0d`Jr9zA)%8Q6eh0?{tlD8>WQH<Xpfp&TffWkaSlOX0 z4k!(g2a%l6el!<UCxU`?I=R7mc)%nw0W+SLj~@aB1R*p7ghG%|0U?Npu!ty#A;!lq zF3!i#AR#Hm$1e^lW%&5z8Dtd{l|Zt5{K|a%$_xx(T15pUq{_z+;)9zQ4C)%15CoC| zVJ$v>kRTtwwvH~;K9DfTiwq2UeEj+bhK79nM#d(5{HA8+7M2WFRt(k*HnyO8z>dM5 z!NHNi$(g~$m4U&T!Ofk40enp)=vp!c1}`vT@b>cYh4l&`#)8NIP=6HEB?Y++>Ouwv zMMXu1V30HeLkL4?XedJ%LwI-uV<aPE6eDAFOe|v@BO@cIlgh}L#K@S;$e5CvnwrL% z&dQp>%9@##&6>l?%9@*(U%<**$jVy8%354fQd(Bdz`#&ZSyc_{Mm8|;@i+4EGwAB- zHbFr%6o6a-u^SWueEcmC3P!dvv|$Ad4DDC}Lk9|A0Nr2703th~pcD)k7^=X4fq|im zfq|i$fuW}tW+jLQyQq(Wp`QV)ytK3wth}nKilGz=s~Es=0=Ne}Y0~7$Q>ILv%D_-s z$}kP2yQ*sX^cf5@K};}z)~s2xK$p&ymNLu+3076jnFFFrOBv=e)PmeIZyxAox%u-K zEC8tk$uTf2WME)ev>3u(0-?a<Qif$vup9~)Kv4>o0}(6WU?m)Y#6gNytzNTs9mtb> z{OdPt+{D1g4?+;x&6~mIfFlJ=Fl<2q3|qkJKqSb7tzb2L{M$h0Zr`zU7X!oYJ$v_o zq!<_&_Jh<OfC3O3WY0k;I0OY@2mn@6i~wLhjDU2**g;KnP-~e1-cAq#0bvnQF%T^z zE+Hue%AWEHilRzFLdsx46(LnMbs-H+Eo~iLJ$-0LA7(I=hV=W5O~CmLJSt=_BqZeE z=;XxB&CSiv&(H7d3^L5c6+{XNxw%7V2<hSJ>FLGb?c?Kvzvh8jMu773^Yilubuw_M z3xtCpXugFj41u$t42b(6WGI*l3kOqBA|f&<I3f~6Mny+N#>9fy5s`8635iL`DX9r* zAY;-qG8q`M7#Om%b8>P)0t9hhetv#IK|vt{19(`>3sNZ;6_=Dktp!u6W#tSN6%`eg zRRj%W0BZ<i0MVe9JBkKZA!wusF;olH7z+v2g6<7OQNqB$P|v`?&;SQ8Sx}yXv2oFi z+}tdz{QS_ghpGx(oPo<SRB>E9$S@kLSZl|s43^@+l8}TCs`9}YW&DAYiyJif02_Az zk0O9okxB@F+8N-n1QK+Dnj_$52&9$H#wLcOSscn98ts$+%%Btl!r(ARCJ=25Fbmeu z0CO0Tdzlasba~L22C{M3*#G`B{9$BZ_`%4)Q1S2f<xU2M1_m33KmQ-B|NozXLBRoZ zuP6h9184*nr2P-WGZ4k#0BL1QFdX>La01*VWDsy*U}8x45AG5kU}j+WUvB^&RpH?Q z_2eKUEI&Xasi3pIm>3uYKvwbm`~UwB14Do<XdH%tf#JaZ`~TS){($Y^03Ec?!0?|L z><{pb;0zKB;GQXnQ@{_p)C)9x2)g65-kyQsKl=xH1_lPu705sSgGW>t7BD|x0Ko(X zhX4QngYf_Vpv+jqP{Y7b&3xhDdM5@R`8^B_3_KuH{<DK5|ARg8;s5{t=l}oz54tM; z|KtCEnZYJMU}w1Zzy3eSYLIEa|NjS##{B1(`~Uwx=&(QrhQnYHhCd+t7#TpK4B#7; zIUE=m{xE>72lsG6eM){%IDKGX0NvNiz##t59>i*320I-zn8fhsKWL~2G^$h251J`p zV5qML_3s-%;{W~&|L6bz|Gzu~C`1_=7#SG;GlGL0T;4FqHGt!S;Ra|h;DIs&=(c2g zP}l3<|Njhp;2;JCmjcK@5N|^LzyJT`Wf&MFKwdxm|3B!4KPCnS4oKnCz|QdC{{Q#< z3=Cjf>;L@+L2zjDFfcIu`Ogj-qhbez3L`Vadr)x7^D}^)_n+ZEGsq^8B`BDYiJ5_g z72Fp`h_ivx2nQzv0~bh)8$>XGMtS%J1Q{5F7#M^_M8RSVVhrLCUobE*NH8!kz{Z9_ z-EnCK1_sa&mn=jn189r})My5C6_pqmpo1_BDhv$j3=H6*7zQn{ESLcG$aNXO6L9*l z@L^zJ0Hrn+BNdPmP=A<#$Ap1_0n{H>VE|3=fW$2z^E&pn3=9sAP7KaQ7{JBI)qug& zje)_)$jHqdA`Kc&2i?R9G67VLgWS(x<mb;2U}O{sQQ`)g%wf>c0=t(Xl))&>Hax;8 zJkki%G-m*r2GVS8<f;<Iz!1&Azz`D~#{e1OPGCr6U`R@4NCE{%3dqT+3=Aq#5U(?& zF{CpvFl59rfC2+F?XPJKnf_s9VggV8_~$X?7id`*g3Om?06VP+#AaY%1ubJ>kOdhF z7ATQrU??pEO%s8d;0z2E3=EZV3=9lmRScksof^=h2e3j022jHj%mkHupj-!X7FYm8 zfKxOB0|RKVypbWV2_y!_&0wE`L&VLf#i+Hd-AKd82-4IxGJ;NPfmL>@H#Qa(rKfj+ z+Son4AbovCMuGhk+^i?6O`6QWFlDL>xOohUhXAALU^9)3j3Q?kfx~<zL!Z$sBO6i3 zc<pTD)B!dh-hXH3fQ=k6G71VYg4%=P5|WIJkj9{_tb(kptfG>#imIBrhAgC{Mj|yK zY%NAe0};YwG-Na~HeobnG-EVpWCSHCkhqPl9V2K02E=v-5sWUbV2Y8E(T$Oj5i}{o z$ms1OE9(mrW@KdKV)XM5kd+M#3JwW{2*aEYonK&Q=K#;JfC=ba7>ErT4P)mAu|QY= zM1Zj%JG+oDyNIY5n1_b|*T0|v4;Y49jLi7Y{0~%|vM8|p2NkUU|NlSypP4~EfB}4m zE(62;Jr39Z|NjjtPyX-!&kQP8|Nr~{|9?Fbhy)dc3=IGGZv&M(?4Y9KKl}gx|Nk+7 z3Zwt;<^Mfom;);N86JQPU<Q>_|NpZau>Al3{}1RqEKsoxDx-cdgXI6e|9}7Ce}0C4 z|KHF5`TzglqmSoDHf-Q$U}kvN4&vt?ZkJ?=1C_+@_W%C>|L@z!`$4CFGcYi{oB#9w ze8~eF_!*cO-W<2RCUN}#;p4K64Dav%{r~^(|Ht=b6WBpD#XB(nJ&5nX&%g$9_4E6& z4GsJZ41eDIumAJ^|8M>JhWP&s3_Kt?_Id>dhH0uF{;hlS-#T7_;RC2H1bbip-+$)+ z5Z3?y^*^5<{Qtk+fx%$E0@xql|J&Bv{s&DufEo4w|3f(Kpj!R^e|!7?poxm({~7<^ z2bIzM4FB)*GJxwl#s&dU%>@p{|Mnm^)c@!I&-mY79#ZZzgF^E^|37H8^Z);QNS*MR z;m`j6%nS?<z%`#e!@YclhX3*m_V%Eui2Lz(<j?c}m%k&=z`)eN!0=zb<&VByz~29# zA2@?+El|}Dj(+(H9)|xQ760oQ>g((4L3JCb`uxXiC;$I{{R40`+W!MpqYVG$|Nm#; z0kt$f^fNFt{0Aou&?Jfde}>)PK|cMX0ZLl`|9}5~|35<ws9OL3zy1g-1B1o?|27PD zb#)B&^`P4RKS&t^C?x7Z6Dr^;|37m*!;c65|JKLf1_cOsrUg_VgR1xY;Q5$8fBwk) ze-C!{_W$;vh5{o4|9?9MP&>o^FDM@NPyGMi92ABJ@;|))2dV=>Rr3G;?w|@u1d^)f zGt}?>zyCku|NrwP8y|o=!T<l;g9=1@K?ZPGKL&LkKmtGh|9>FQ@E;V)4FB2x%l~Km z@BIJ&eUL@>Q||qL4XShZKfezWsE+^h7v!M-e?guH5&R4c)Bb(uYI!wn)wJ*5<Np8t z&;RcCe|}IRLe7mSY(^#~24)r(<iRXfR#5#6D)CssBVD{8N!YYGtC%<|tAwN!t28UC z40t}AfkBBunH600!Mngl#s=W7E_4Niy@M@i@YmJNUBJT=vVwuZ&mS~g#2O?O9KsqJ z#>yJb8W9=A8qFHR5X;IMCjhb&g5#N3SrZbIl9E|dQW+RnSwTZX8JSs9*{nIa40)`X z`3waN46KDk46MbhC9D~xWef}q<q)k56>+SUWvo@zpjrvsYpZW)WJT08(2!_m&1G%L z1Ziny4P|X>@91D<=!D31bqlce2(X6r_OY_|doW0W6tQN2oB?8GfXDq57^KQ3Ok|zJ z%E}r$c?v7*)M*Tprwg(sv9j9CkWHT%$U3WYHf#HwxvZJ<<}X+nwuqILbumac>yo8; ztgNidSXq}dtXR2<l~ranD{Gk)>l#+UwG0dl>sVP?0~wf@*0W}AVA!~6GsrFv1_oB> z+873gK-Mh`tWxP7Wvp94a;zYavu<Mr57x7`X9$9Puzg2HdnN<J&Wz;@46LB>lO%?~ zKnA}(tQi?IL5(L?)+PqlnXEHeS@$wbQIu+8ohh}Cm32SEfrAVThYqtgsT&*tn+GzB zmGvlyVqiUXoPjm-1Oq4>PC`eIAQG&gX;cOV23AFo|CWPTtgQVYHY>zg46OZNl?<$` zpxIWC%1qD<<s8;_n=_!Aot5=$=4$BNEJ!6QE9?0SU~?|AUb=jR_3E|j5aU4PjhkT? zFSfMYx^)}ue^%B#3|H>ly>}l}m`FW*2pUTODPv`AXJA!=u*x75ggnQ<017jZJvcF> ze~eWB3knG{iinDVM}inZIY3@P9s(4Vl;u@a<>etYFcPT-XM>k9X)r?a30#zsQ6FN0 zyu3W40k~ZN87E={Eg!OXke8Q-W*-kvc`vY1Mo=Tb7h;zmSOmoOmuF-Q2$YwX4+@5g zfv6A=Aulf<8WtWA31&yh%SVG5P`AdwoDvHfi-mC*8RO*{856(?355?*`+}2;8`NOn z;pOFpF1Fzn;S~k(#6ScHi%anGO7ikbN%MkKqGK6$SvhujkUCxkFo}l%wQrb_0ak~C zi&KaU!gF92dVd<C2#Ex%!cCy-2i4t3x}i+${eO_FKp3is;Q+Xy0}+9+K|Ny#AH;%T zP-_~5Vfq+AI>BurP<@CH1+zfwGeB)KW+nzECMFgpRwh;^HYPS^kQfUah-3zn>`ZJ- zOdL#1Yz$zEiHV7gg^7t1Bnn#K#KjF(%EZLV#K6M@GJy#+$qHKg0g~Va+l7q)2OSeL zBMS(yvay5tjG)SehZn-eLb5S}sv9g4LLf0=5CO#^qJoTkjAG&(P(Ff^5J0dQC1Gp~ zC&I#q2@)c#(7*tB3KSe*8i^pTy^C%Oxc%FXs}21BKWh6I(&U4+eZehY)VA+oP;=n@ z{QrOd&j+_2KnqI#Gc)}EZ_fg0|A2b$^5E9Y`Ty{C?&tZH4I9q?hqrStw_7sB9r$n0 z0Bz@f-fzvwaQ;8Eoojhu!}<S6?OfYNP%1;Tb!`*a&;Ms&fVOo%-?vS0IR78o)~#)5 zIR781t=kg+|G<ApTlatce`a`F*Z%)!Mg|7v|I7;xL0Y;0KrJ1Rhvh+I7@&3wXmAF9 zdlwV}$ZcIv>j~D@?Kc4D9t*HZp!O8VI8a*+)ZXO>XBl{#7p!J}1H*ri95cfYeun># zLHP-^+NPfQ|9|@r80}op0v!g>pbTi04W!Kr8VkbN?gg3Iz|a6{*rK$1|AX7T&rsXF zpceH1|DcxW|Nr0jqqluQMTI;A*v%TCMj&Iuf9C)8Ab$NHc5vIb2DR;L{+}7%_Wh5$ z-TVK)1gL$_z`$_-KYH7jfq_8*RKCI5zo5Pjwl?tp|NQNswk)W@ec(Ss!+%DQyFg7= ze+GG(|KgxFF|0lOpZ!1ce9OiM=jG)Y_JjRn4;m7*7X-8aKmN}Q3a|f+{0#q@VQpiO z2xxo_+CENx@R<F_bC6?fAOk}G|Nkf4Rt63G{jdM=UtR$;O8Mj8e+CA5hI*{+XY|~R z!iSD;L5HKE6gy}QA9VDb4YY)hjSX5ALKdBfiA%6avaw0ANwdkwGBC(7$g{DrDZmC0 zz^wx<&`gpMe8R{Ax-gU9*@eN?&7A=}nB?onz{cj!7Qhx5#KsoP77`l97S3kF5W&V4 z30m+6bxag98(VaYTx=X0TYN$y8(R`vGFwV&T6zXsW)_1iTWWSz4rtv)ZXO#OTP#~r zegFeQK_OIcQ6yV&02^q!6*Sk%AOV_fWrGdez{8=DErG2m6{Mw^Es(9HwXKbf0Xp^2 z(aF!&#m^Sh-NVM#>&zelQpA=5vJb>c0j>97>tm1z=%2tgk&TT_YSLsjwkcB?Cb3Op zi)CY*K7(y$tS=ketW>tv*>l*~=FXeHAZQ^Q8`~m~ZnnisWZBr*X0ov@WmvX+1sj{x zN;b9t3AR;i(^f~US-Xynjm?*li8-Aubv*-UCX$Vf&6$CLO^$(q0n`O!@MWvn!X}aI z9Kf~}?2dp01_rimY}-NpVrxwS4;<~-nbMldz_2T2DFXxBZU%-udl`Iv8NBwfrKIc! z4MMW9H88O4XWP%lw(kI&v&2ERLlTGC*p4t9O=4g;cAV{mqSi@hn;7J0NSl}K%vlCD zwsQ<@NeK++L5)<9g&@qv25RauFfg!P1o>|%h{eX%3u3cDoW;Nf8sq|t?*r*zV7r)_ zz`!7p!Pa`|GHCrb8{3ss*t$iK0yZ|b>o-6Q28NrrKnoZd?tpo0Y=_wH-n-BC;Nc^P z@gVZ?lb}O~nwp+Iea6Q2obAQSS0FcoBwjPzfAjX;d(f!6gug#%WF92S#@5QfCI?{! zKqv@#4Y5ubBESZ68rWV8Vz{)gL&N05BO>7q>lkpWIaXdi4$OeIv*TgSkc31S3rr`; zGcqPa6sABZcsN1Bhm7{M6tA=l*7mh5yPP~bq-Cv$-n3Ri69BcZk=G|f;}c9lR%O9m zijx6qt$+seK;v;A;NyItYQFv-i2VNyG+O~0p8?g~|L_0b4_zb+Zqxi{|8EZ-H1r3L z;r;&)8@GeDXa4g;$L(;mXP6=5cHs8Rf5f;QxIOcq9X@skY0vz}-JY@k2Oqlww`XKP z?Fi6vVt(ZI46Gdk8lU`s|35pp?Sg%rPX7OYP`zjm8i`{3e;?%cdInI14;iNe*XGQi z_7Z4l3O-iH|DPYUb{H{M_y0I_5^Mhd|NNj?ogbif%m4o<ZI=J{|DS*V|NotO&;Z)~ z|DQnvTJoTl6R5ocuB+KWJW!<wat>lH3>@;Hp}+>l22h&?Jo@_o|8LL?5v;ueYFB{9 z=|HXrw^!^zt&@iTpphr}10X;0H~ioK|L}kN|DV|z7=ECP$=UyBhK<L8$Kw7&#^RX4 zt=B)G1{i3B%pNqvRnHG;lhoHU*ddR}fg<7m|Nj5~<^S`8+9&`2|KHCK8rKBfa@@ec zfM}!a{|}l~`~Tk@Gy>TF|NsBy|NsAg0FB#$+ARMW{xjD90Qmwm_y`(eW|m|42N}Br zwPpVQXaDox9y9{U&;V*}gW5HqF;aMY1~k5m)SiLNiGlpj-tfO(o&nTE0nMfTKg_`J z-`<|Vp5g!R-~azV0C^G=`)KW&&!D+F&{!g_aX;|XB|ihW?K1yAuC@y^!++>J9;m$w zo4flDnzsWP&hVcRG-e5&io}+K85x<-`nb$2tc;9|Z0sDIj9lD2;PQ)+k(ZH=k&&NK zfKd=Mea9dI5{3?vfW}A^6y>0^BjCo3p1zKOA-HkFVQv8$BV=G?w6V40v4>jaz{tqx z$mrzk!syCq;O5TA=m9d%jgisQ%iG775j5Mu$QZ~N#2CyJ5*o$`UQNan<u3?YzZVXg zCW&T@iDhJ9;DQZj#CtF%_%S9XB{QZlrlv8aXMoHL2N4h~z?hi@VKFjh=j7()GZrv` z_=SuPj78qX#f*%MB@CsEj6NViM#kVWMn(=sP+zvZf|0R6fUy#+nvoGg1w&LZG6sYF zQ^i=#$XEjw3j~w340Vk4jDd`d4StM_jc$yLO-zj5&5VqUE#9qdjO~n!j2&Qgm7U<3 za8R@`GBS2CGP*N#_w+I{GWIbtGNv>3GftT3&Nyi@BO_xJxc|#EW$Lu)GeC9<%$&u@ zID1Yyc;k*6V}=D|WstyJ#(5w)M$j!+j9iQhz~*`fPXN&i7cFLFTEeh&*>VO3#ufdH zD_5-sEk0PoxHf0q`VF9P-N?YWiE%S@3j_lr<Cd+AjN7&|CRTbgGIs6Q>BqpZYd7N_ zQMtX4h6!luj1jy<i-9r6kAac#00U#-e1_a`kZn-R$aoOKV)O+07qoAJk&*Ebh|S0d zxfz4;2vh|lXq=FNfzgxcC<8<KF-FGYCr*OxIK>2NRh@w-1Nralxx*m$f=vl#JP)Qf zGBQG%R*a0^plry%$e75;bMX@6<ttag3c$oQFv-Zscpc2RaT8|FErv_CK?92n4;aH4 z8GRl;3a%<(3>RQvc>F}*DcEL4M#fUcOJHq`jEou27{O^pm?4~TKO@(32JpoEONKch z_ko7)7#N|07=AD<Ao>gg131DM7#LoG)_#Ef2a;es2__jCVbWkb!2~GHfp!qVMkpEi z1sEB@8{EXiB^V{8q#0$P6Oy34YoJUBH42F$Vtf*|_kfYn(1;P<D*<I%dk04YCj<C~ zF#{j4QbtBbUq62X1CT8NU=a{I(14LKDA>TjAS4tj2BN|Y3=BYRr|1~a@MAnk$RGhs zCK@Cqr!X>tIxCrA0kC<Dj7WJEWC0xK8Za{EffeQ#fGMbYM)15gIK0paJnbuXc6QJ( zl#np?@k>zqN)~<W62zBdmseniw5-sZQ@qM(?JJOaBn)15fZ{}m`@rojbb_6Yg@XmM za05jPItSdwf;7TFlZ1cJ+h8NT{q+yja`+FLCk55Qp!%O5I#)`YHW;{RQv;gS8*PXE z2RGbC+hL$qCf0TsXnYAiMhzZ_W&w{;vvV+VGI4S9fQ6Wtc$xT^nE06ln4s-1kT5ar zFGgtli-`%+{sQS^Vsc<&Vsd11a&}>IWioJcXJYaIiF-0Jftp`_Oica(flN$6Ou<Yc z%%Nf7Oc9YxQB2Iy0fGz+OiU4OOiWBMOtEoH3=CZHAbm_sObH%LiT+HWCBJFR=^4zK zSs?KU5COpgOxZaQ784U_sed6;5qLbln8|^u#HX~BiHWI<p`3}y7osSnf{}?4JUm}n z#l%!3z*G%Z4V#S!fv93)3IWY|Fx4>CGBMSG#e%?OJwpRiBU2C)Q<FavQ?nZrQwuYb zPb(7>Q=3nF2U8~#6H^yhU3E9S{l&z@)WgK&&d}S}&&0$ufr*JJlW8K;q{;3~Q>HR8 zF-3!;h<V!d88c^r>=c+ihly$KyiCy6QU*7sEDNUUV1fBe3qW#A{tFoxn7Ei0fy!wn zpO8r)dhwE_Ow7v|makaJz`(R>BGc+MYnhlBnAS0^&jSrzF)%T0W?<UFv=zLM4%7%^ z+P0mEX~#~cq-q}~rk-89{Xt_>O#4LT_Cr=0g4IBpS4?^S3`|Ui7?^?<GUOwV#4s^I z8d(fXULgO1+FwjeOh-U$CQvgAWX3U&>p<}eIW2(6i}^SML*@x4rjw^Y?Jg#!Gt8hp zw4lATAeBr^Oy@5g1*r#{62f#5OmAjl0`0g3iTi-tUrbC%OgxvbFkQWN9jpLM+yIkI zOiVYy4A6KLn89?1;R<M4_aVb0rU)h`-^WivYKoX51Q-~eJ`;EjvKADs<xE!~s+pLw zUNC{viZDY2(?KS#mkbQA7#J8{Gt2|2V`5_RVPF!5sP%_X5b_*kFEGf!H=s!`kpCev zry&#*I6gu0a6H=ng15dH8Asb+@N@xVfc8Ga*iafYMghVgE_jhKWI!E4f&`H<v`dKy zWXSe5a37qJ5x&_CSqlmqp%L6Sh3IBvgl&#PNJCiY^PA8P3q%q`q8JY2faf>a8K}Iz z?>}UoQ~uw>|KNE}*!n&~Yx|JqJi%-GkmfwWYx|JqJi%-G;B%gmXmg&jXmg&jD080R z^?mR;&selM&j!StXT1W_oTmbK&XWN&F9{nfL|)?u8ZCmX^ZRcM9y<gLd|@9yBzv77 zXdDP>{17y1g|^o3KQm~E5VY10GM4zCZfpI(<A?Rw*ZP6x02vq%>-`uS{xjbPjbuXR zJ#no0Lz?#l4{hR}`@}Zq2^t?IG~Wqow}FPzK;wb`!NakjwSSO#&j0`Cqpke|#V~B` zABYW^;{=bv+Jnz}Ab<Ve`Tzg1t^b4NQmiyZ>;G6m3ji5F2O_XS);>c=_Y4e;^x=a9 z;F(K|^?#t{e?bfkte`_-Si>V&StD7aqGMQNS>qVuSy>a1*8e3kv9cy5r=+B^rltEa zf_8;wvSxt>=~(j%7z$aliwcSv7+6b68Cc6$%UQE3KxYqCK^Ay1R41_3RI=8BP8wop z0PV$UYHnd|MGuHJRzKGEY><`?)^OI&uI_GDh8~Eb-aY}=egW2q2@_daC&AYLWx>|} zWr5e7$ume*PM*R#m6eq>e%f?a))_Myrp*#$O<`r7J%=@OZV>Cd`3qRP7A|7VUc6-K zvWVrZtgI_Qma(o}RmjT9x|)@B4a3@X>seW4Hn6f*O0jNa72E`xqhw`e4Ps<s%4N;o z!mxGQc9315^?&e9^FgdT7+9q;Jt|pug5+2${XlE{Sa*Y5&f1j)UjMgeZ&p_}1H-<o zH4F@_`xzJxq%Z^pG58;3&B~e!S^w9{z&e+8E-ULHi|GndhgpwE9c5)b#&G-u1H;Kv ztcTU~PlL?^o5>0pWn(>ij)67%JOgW%AH#(eAQ>oTWd$9Z0$Q!70P^1&5Q`POE|QfM z;w%PMaBCM7evtMct3tLP1B29M)~?xCKqvLEvR=&w?MDKgy8<$mm6a7df62;vll9i^ zJFIu_-3J*B!4Dor+`QS|{^-$T2%q&J!<{EjpFIcdf{_Xd0S%TxB)Xt05?NU*Ap#Kc z8Uq6;v4HHsi6P@Ju+cSs&;a-7`ah)5f`$(n^ON*j|Hlp9gbvz=&H&kKBm+wN+>DHj zOdKG<0|T6#EX<q`z{<(VASK1Xz{$zU!NJK1G6B3z3N#-A(!&5cmxhOj2edey0i>US zffJ;f6}%LV6C}X_*;U2Cz{A790y2n`lYtYo6N-n2hf$K7n?V?CBFJ=3urUlEdl<zy z7&tgMK>Mv2KsrGL4+AGiju~VCCuk!Z0|Or)11BqJ-4SF!5p<9g0|P5a9RnvP8-s*| zgoG?38^~CY6M2M%!R~`>(UW120d4l<<YZ*x<YZ)IV`g9w1p)9TSRo+>5DkuE83rDZ zUqJ!D$tc0Z&L%7@EX>2ez|FwJ0}=%J8zjOhA;H7L$qBMlf(OLn<OE6a2!p~>LPCN^ zLQzpd0^~c8&p_Maco@J+I5?%HWdvnqWMtUbWk73sxIx;5goH#y7)2m}Q-+6!hYJG0 zN9c%y4hZ2C5fPD)0EZ1HkFYREDFZtv$O$r_$dzGaWE2ENE(<3oCo3qt!8{%w9w8wi zPEIy<At4?fMMY*F9v%@94h|Mh5fL^vkPIWE42zVM6oV8PfC7Pu3AD~bPM(1Qv~L!a za5y;FK)z#yI19w&0vX4{Bg6wvQ6eBa89*rptU`j56XaSEHjtBecz9Sig@lE9I2m|2 zIXOY$$si&EvWG`PTtb|Ifr*`ghXZ6L56DlP5^M|%EG*!2^Vk^~nfUlPK;aHbL`qPg z2&xYhKo)bdf>Rkg8-t=E7Y`2)D>FYo0|N^u=mZ9kB1TpgNlq3}=u1g)f+C)Ym4%U! zi4|lzCnFOp6DYPoh6oFTA`YYw<S-BhAE5$L2eO%gje&t3lrA_SvBCw~EDv7m%f%%q z#Ka`b!Nnya${{8OmKW!ekd)%#m6n!akY$hqg{6q7yn?u*l#;Tnv<d@*ssw`&6O)=c z1A~YthlU0x7niuI6c4YarV0at76Y%ev<ibZgB)nzo{p%zu&$n>xW0j*Bo~*Iwz9U7 z3WF>I1G^}Pt}fUvDPvwEBNYY{Ly%dzT&hw$3?>YwW(*7rTwI1AIdcYC(3T?$ODj<h z4i0%qE;%kPEiOZASza4nUTp>j233fc40(BN8F)4A>=hj(v>3Qlxl|n`6?u6zop_z? zT$q@cxxnKAu5JumTwomr0=&HL;5Au3zTnO1K0ZEvpo7H%I0AWjgMvetn3zMu1Yxrr zTwL)9c8N@&0WmHvaqd(Gh7>L?E>PJ7+7O=(3R`VnUS9W%OwhLY94;;{Ufx`WJWzY5 z2(-%`<R2~uP|h#QFK6JY;AP<A;tEKq%&)2j2}XmKX>o9HHSqHCHgYjEaWr$a@V2%w zF|~Jab@K9pj-=q~<}J(b0gcBntM>Nw^?{b<PnZZI8755zMf4N~2G%K{sApi&m^uw) z1;`H2_!bxFfQdOA930J{u$s%u%R7&Op|7v6kBMnM*Yu(V<y;lK3qgZhi#a$`mhkc} zod*j4Wn5fLO!EyPnqqmEuV7%v&(B|}%CIVimzOsIwC2AVWXl?`-@?{%t;=7}tGprB zgu$IlRh5g2VdEyyIg6XOaBzS^gLf+f!?x|)w{PD85@p!Ai-Uucmv=YAE>J*L@bd22 z%fwW)kBbX*s>Lo)p5fx+s^I12-Oq5q`5-9r4uQ&EE>Qe~e8J0mn1O-AP+X1+EM&j{ z_1!dH-Xoy&B_?>ZZ(`p>1_n_Mjuc*A-ebqJLHfbIso+iK1<yq?FmNRE^75W?2Kkq3 z+G*Z1AiqeR<>fsG8g~K*CMLlMoqA_xVE|_?R286Uc2M?$OhoaBi1G_SwjRg`$jUMB z$n)_F$SNrEfadko)HNg-G_?e@b#!?c^t1%@bqsh6^|UyQj7@k<&CEG0EUhH1Z8&W0 z?3uv(R-Hiyyf84hxqEPcr`18na4~Rj1b}xDhC%0PBO;?BqoN~XI5=YC;uDw{5^a-` zB{@=3IXKeNGe8@MGdVawjd5@+FfcIWMRDX8FflL`augMFaFmpCaDdJRh58A6Tnm^D zZjggnNCapAq>-bEfup&lwXL0_qm!wts+*&SqZf2i3J1po(0J<<j;Yh8GjYt|;NX}! zi(xkSj1!m<bLY)xTCk7<cAyDZcu5q;(q+q6tX##x0ZL*FYdF@fV_46zVdEx_&0CnZ zZiAdE0y$TNWB&oBgP`!#I0PLR1see-j&N`sJ;nrPf{EiCC%^-s$2mC8oMmEQ*vE11 zJV=`3!bM2K7OV;}feURWad3jhP(T|tK|3!QK}#kW85xmRszc;SCP6d4_%(@wrJ;!j zIwcI6;%5SnnzMlN!~kMgaADG{tgMWnV<s6v)1R!M;Z_D#22gTiVPRunWMyDwV_;+D zU}WWF;9_HBU}9onVPR!w;9vx`qBxmYxL6pOnVA`x8Ccob+1Z%b7@1f(xmcJOIXGAu znc2A6d6+nP*?4(*8Mql(nV8tvn3-8wnHhLlLCq{A$G~zaSO|P%Cp#k(2P-ERJ0ql? zgpdqeDD$zPiFsyFOoOz6$Gy2gi3HRyVrJkH;1XnDWDo+$F|e|+a|!dq3IJviRyJmC zQC=~SoG^$0Em8*45Q2kAoI!#|Qi_2I#Xbf`CJu2%Y4Fj=T%hI@gA9kbu&f+tw2>dw zY+~SGWd`d5pFaRG3v^5aKL;Z-GmE@}q6i11J*cFl3_1l%0L<rNW>8X60SyI$bwIo- zB*ewQz#^o|rl!Wo$fU~1%*en1IpPCCf}#tC!A=1aU<3F?1l1WC1;v?!Kw=zh3=C|{ z9OC?-_A(bEs7(vrt<S{4!3qvD1`ZJh(2<W|z2IIgm?gp}$fl(&A;|<@Yy>_@fQx}q zLq``BCL9_Z9IS$%GFnfGLz#mE<auaNajApbTihCqf^5wEOd^be>>3gh;62&`LX3<c zl_2w(7}@j<BqW7ld{ChdTJZytV_<+O<7X4(WE2O72oocRhzKLp5(Wki7DEn@dI_+L z83l~k*cjQ^n2e2$O~7#u^(QFwL2l<~1*Z(Sc2FMx)D8e0gNam(fMOEj0tQfUgP1~W zf^6&zTpWyS5=<P3GaNyNf}|N3xEKY^G&RgYVGTWT0c;fm13wc36KLnTo-!jsH3I_! zH#;W-c(ob>0|UPZhlV&SgEbqM2?GOYUnnFhM391o5tKC<ArZ&I%Fe_JI%^f~Jq9jD zHcd4_W~cxpxic{EbF#6qF+xHYl2jQO7&urN<(QDY$i>3S&M3&tz`(>I&dtNC2s%U+ zRA4edtz!VKO;xv1lwtx$0mQ!`S97wlF&HW^f}8>uWME)p;9zBAV`mZp_nhDoTq2xo zjEu0%2MSnFXlk+}y9BD3k%0k}`yowWPFBzX7VO*%3_M&64D7rNtRQhdegRg{@uWh+ zoD862(?!L^A>tB}U|L9ulNEfzf(+!K1yI)lWV!;VjSkvN%fP?@*-Z-)1)UxWK2{3M z0Ub|a$Y2CMM%oy12DBOE>~RJL3rmm^D+UGzYw%HSARg$vMtcVaN6>L@3=9meZVU|W zu+!Wa7(mrN1L!CU@VX<=dE!9~48aT`Zs2ZWXcz;i5fBj>#Q;5Pf*}qxL<2S#LO>3k zfUqHCG6Mr>moDgpb5K*3A)NtqxD!K`GXq05=%5M2IpYir4EY71@DeKIECQb~0Wkqi zmXyMoAVyg^cw~=(p{g3hWvHnIQ&?LPU>Q^bRslmqIl%{fLD)o*p!OGjg^+{|JsASj z&4DNf)w)cGc^VLRATY!+I7nvjvEHC#eL)zM#}LI3L<?vSFzEah2#<k*4YUUUbUZNw zlmW{sa`s7k!hi7p`Y&(O@D;S2cs@Jhng9O}GT0pkAF~50R+wRV^}jtsrFBICXvh5z z@X^N%jP{4j<*k@M7&H9&e?Pd&pa8VJ%3zNqcnQ;a(7N#d^#L^n9r`wA43NED|35?O z2nL2a@OCVOUsixeVnDtDyIKKEg3cNMGoVEvx<|os$OI!3^6pYFn*~g=f=M=Z4rXRf zW@d039W27k%*@X$0AYe!Wujsrwm67jW@eUv(#(>~%u>=|4tRYmvXv-oNMZra-GQg> zSXe<TZy7m3l`}*Ng#?XhftTdM%>bo)(AG-`W`$5tk_}2DQRwr3&|rcahmFA`Ei136 ztg5aF>OeEd%7Y3w9zjss1tG)C&c!1JJ_L_PP>h+KOD8gkL0VRxK_@Z^tW;B5P)1Hc zNkvUVixDKDsjXw?;1vSivJ6>y04eS?wWYylaC5M+@``fv3yX6~u=9yXO2eI?qpKGZ z8Jm=pmd(JR8j@ea#K53$09k9yz`z7PpAWq7SXx$IQ(K3L0c07(@jBW%(z2j}8#L{& zW9GmhugJgvIwT0@5(ab7DJ|gX4DfbrP}d1mg!+Q#GJ-=GK!+(YfJP*g!N=}MgGq*L z2G9hNHdul|nE_nNH$acXO#(?pf)0NG2W%5aktX=ia2<vuGY1CHd7BIj^70^MiVUED zWzaTbV9;UE1gi%fYXk~(kO?4jWg(~OFsOo!0G(E@uBpug330FnkU2VFX@~=)BO;?f z%P!*~=kcYbXJmqUb9wm%g+;|Yg3`(%v1$3rI-n3ugHYM|I?~e8va+(W$~qw606MTa zG8%O7eu$u;C@9t$7?^lqrb;X8gfM{DoFsyRTE{FBM9J%zMJ8n{D(jfZ$}55>kf@GX zq^!J@ASlcsQP04D$8ja4<rU!5KUp|=cm)K7M8zaPhB8QpFfd5#m?=VR0=YQ}H0lzc zkSNH*E6M;dIXogViAfrC@K9KI1cSP!wxTvTJ%Ce@th~IUvNV`1t5j5$mj@j|t*WlB zuBiz+%ov>3rInRs<)vkLMJ0Ge*?Bm5cm#Pxy+R=Ah(S}EK_@c1zM-*+fdO<Who&}@ zG!v7wEGUK;KnJaX(iJ#5PzlgdFOUce11lSh&A<*iCJS^y0tcwh0u8)z@_-69b`DPP z$yi`{Mt+bw(BWB(OhUro<}HIL6R58TI$S{-)P7`Q25sd58PCAT1RBL;V337gnE_iE z3>`!Ot5H%`(bPf^pp%?+boH3@!IB1sMoP-YhT!ZB766U1K^UT7GeIM63=9V53=B%j zuq9NGGqXUdK<68Q8VDe^3y1(?(8>8=h8u`<M?K@&i-Cc`n*nxu76SwH>{;*#8%Q|_ zgN9u|H278&2I&104Db^yLqb84Vd0R}7>RQJWfVw?ffYUg6Ak8r&esC9CBc_qKtw=C zRDce<0`tI)Aus{vGQjFW1_p*i@Tpc1Vc3~v$uPfy%mba!1(r^QI2X)j05_Q!z-M(C z7-m4k7#Ki<fD8=T3^||!(~*wXLfSf*$B@s#X=w$@wIz@PP(b@NLBoIzjZIa}Ees3{ zkXcV~s%UEm9a#%Dz7u>vEttvB4W093U;vG{fW$=m8S*Dg1hF7EAHoKmtOXI6qNEJk zaxin&>^Y#lnF|*wA)g1rzyKP41F2lfzyQ??;xT|!i7o>(!Nl?vAahpESOp%VF)%b* zvvwUc-Gk)WIXJ=YUJv4cW;(<e7@5SxLGy$hoQ4~fH!*GAvXyh&_H8?M?%KU)?}UB( z4;(yn7`k%+r2go!;}ES3AbU<g*q{w8OeZ-w89@Xn{6NB|3^Twdoq-b}NC33R4SGl! zhzn0-;3hEx!x;u}dWv9R0N;-WIWmy}N`aeIAYI@Z475xR!~x$s0cL;*P{Rqek_GkC zK%%gvexT7<P~QYZgD|M|1looLYPx{<ppgm|aEl#uFd)1m#|+|uYyb@_gJ=+j%qlT3 z@IoyBU(CY*Jt+`Wtbok{OG0>{^bc|n1A_o)WhuxEQ27Eno=*&^3n&4)LIETR!l3eo zK}MEAMh>*68hXYTwD1ER76ekG%mBVa2YfvOh_4O0tO>*bAIJwOW<X~ef`vf69tLQi z2Py!iOc@v;WetRH4mmm!!hsxK&%glc5rIXlK_sXp3!*_7bgCZgKzn-!M+OE4C(yM8 z44`wb!I_m2$^{LmfCB}j3xq)&S9tvo5`kbp2JpeU&~vcC0T&buI`%pYBp)6T$-oc= zy1gcrfgvsqa&R9=0$g$=f>{hn$)Mtz0bD>Zf)f=uPQbCr%m6DQAZkGuIc0$p7c8+b zFu;${h2G}`Qkh=>A{iJ88K8&yF%(0NyeowtfKv`W$q%B8fdS>nTv!l+`dy%dl%R?r z{VxUv2Gp}ipt3Lu+&l%H|G^3w%4THbVPu4EhG%4C6cJ@)6k}u*2Pu<aWQ1&3VPs^K zWn`2CiJ@Y71x7_iB}PVNMisDxsv0Asx&|X7BY5<Nk&#i0k&#iGkx>UM3nFwG85#9J z3`Ryqeb{agMnk9wjDl=wf$^}?=3r5N&~S-0BY3x(y#siUni-=Dqbq2S8dw2{0B=?U zF;K9l7nJJ_*&77qK`HoYekdD)7#aNopb7$mV7tmfz#`Cd{=jSq5eA{cAru-J0p-J@ z1x|zd2hgbD<^~N_@^f=@3xK9Nxw%2z7j988Zf<c1pPO3(#u%<NJY3Ki;GP<ofJuOc zpg}za@E$R!ATv0}LxLJA1R6C(5`Zvam)J2dFoSz_5K*uW_!vAy3`~M0Km>GC7^r{% zOE5ArGeas55DC!=QU{vXgYdvQKnoq9>OmAlhKUK(sbpXPkswi!BA5(pv>V1}0xh?P zaX@7s#1do{)Ch2BK*S*=xZDDlU?4v9>{?Xq2th`WLI@WeL{N`Hl!HPNA`j++%4DcK z$YhAcU=~OQ<XDiCkd;CV0Lg$cNE(Vk5ew1<iVu(~M$mG8hycU@CI$v(7Eq|LGBPlL zy5Ovg91N_CoD8gt3|y>?+zgDMMmZn90E3_q10$0#I|Bow2q@4P7(~Uy!JQ(|RKKvW z1UmzRxTKV{u(%{IgAC{*M9^F~_((Tp(B>~ycF-~sH3pCg48p3y3=FEmpl&6orw6|5 zP?>>26EqnP>X<Sxs0e{hbOjGR2{C|rg1R90>M=0rYr|_#bp{44kPgt;jv#{}Sg|fx zG1x#|1_oVq@X4pTAOjc}gapAe4%`e3>gqzEYF7p9bY2EwVPPTA9WkoH!n~l?ffmR_ z2GH@ipp`5Psw!Z845kbWf*@%#VPOto=-?!%6JQCFfp$ze#3hBfz&Fc)V~mwiRTwlr z%xKHV!^r@WVPIgi12uAlK>-2UZ_LQZC~nUHcCorPgEoT(xVNnZPF|qI5ArtzgYqEQ zRLGgspp`D*hCQgQ1j00HKS2hfz-buNY=XE3)Nlf`VEr-%P#+o8bdrE|48VOfNS6$( z65IfllV^}q04=SAwwOSP04xhiC7^r7q!`rHH6V*5K)o|6wVxov0$__^Z7KMa0$2{Y zxdj?<2Bj>Ja>yk?7SINhEok5wlvzQM0m_;nHb@y1gW|`Y!Q4U0(McF|ZxmFR0o0~q zfL}NQ)(pD%fB|yvfuFxH14BR{c#s-&rxfBg0#M%$+{A+PxEUB3Ktm!7jF65UIFW#? zWQL4n!)#+<h!GYR2KDJcB4FDX5M$iXkxh_PB50@p+yw<S&Ojno4B#A;0&iU^Lwf)q zNd|^A&~eKUCTJB4gavAPg2yN!Tu4NMOQ6yA5ol{aI4Bqy2c&%jX;dK<`H(mQbsj;J zxZw4lqwOPZ<h~?0gaiZxgv3NdM1|Ryc{sUvm>E@gI63$^*txkmK&#l;co;cVgqU~) z1Y}tlxi~pFc_bxyB$;`HBqarTBpC%Ic?1POqt61M<&-=O3_J`RLJSN7LIMIjYyzsP zstl^&C3FI80t{?yT-@9|Y&;xnLP88&;02Bh44e`?f`Tl9f<i28EIf=%LV`RjEDZAU z3JQvfLOe_itP(<;3_?OILNZd4phW<De0)M8BFvJktQ?F?ta3a|lAJs|e7w9&JiI(S zyu6Z<yiAM?Y-~b|LQ+zU3=F(Xyu7Tej7&_t0<63YYP@P{ylM;rtgLEkAg&r0k355t zva&L$n3k4iP!&++U}EB6V`SpvW#r}L;$@KIW#eVyQB&jLWDu9+1NERK7#SrQ)YX|m z{dsWsfe83mAE-ADZHt3f0D^9l;zI%gf)I{?kT7^=kpbMg09{GLCnh5+BO@gv$H&Jf z0a3&!Aul7(C&|ahCnF;Po&10dJ~J>VDJeng6OdXJK0Z|y6+S*bH8nmyb&v=jA0MbL z=R<HovM?zPO?6IKu@7=TsQMAsVbE1kQBl!D2Kp*0dIoABVUX#3hDN;LAxj2h6H~A) z!r=9rJUnLRd^%tO5CLi`=m;|~=m_gDFz|uH0VE8;>cS8jVvY&eMPMiBfNfDx0WFNR zR0I2l0o*EK5C&0T`$2vHX%}WtfjCM9qz=l2dkCZgiWzi3dKthx1}m^rgc(3~s;R4i zD6j+{pBl(uH5EQS6;KUnqGh7R05VidMTJkDk3j{jkI#sY&yZ6^MGK@~O+{UePhE{q z#e{*uL<OW?MN36RMV(I#qz|l~flnChBd~ftkYaVH`3zbNS_lttg7m3C&E*5Fq6FIy z(#rt0pO23Z#8J^v=i^gT0l8I0T}_QoMIFR}1`8<IOjJNEIZ(iWoC22MR0BzXJgNrL z2lXw;Ku~Mc1gu^~MazT_<Z5tO^6^333kng4dqMIlDk^H~NNT{&WB}_2iGu6{S)yVM zG8z;<5Su_D3Qd6OAor-Ln5bwmFqnWFoGRcTR#CCB#RBY9?4hC#;J~y}QL(YJ10fX^ z6<b@79vfR58x=6i79^lzYinm`YikRMEEQW@1xH!X9FM7~s|6n<0cZ;gySagxAjiU- z2u(KNMjs^8AnJPt1`{hjK2XwD<8yT9<MZ%TftYW_#|L(ot*tF6i>auntH3TU1YJi6 z!?1;<AR1(!8Xq4x96&S(gOeRZ3@QzxK%!a<(oiOtg2W9-uZk~N07?k>fyDUuq(ROE zB@a-30LiKG@iCZylMNpOqb7*K2fB$J6p>Kc*4E13Hvkr1Dk^-YR<fqT44xp}!jSw2 z(gtbcFsP`gshGmN42l|1c!T9Z99vsZtbjrv8a{R^pl}gpU{K+M$lHSA#@5!>4jSB` zqz5Z8z}X*k**hrpK*Lws3hYl%83v9qH5CS^a<CA1z!PLWAGjP*3ABP_ZWVaUS}}mK zGSp}tK4CsS1|2n!*FkGDK)C|sPjz*5m<AO{DF6}$1*tk8IH&O`s99NA1z80%Fa%kJ zNQQ=mhRN`O2LTm9H&L)hMf36T#l(V0K0ZEQD=TTMI6ly=!3+$1Dxi{EAYMWSO#6w8 zi?fMCfS5R&xEO>fE)Iz$6%{^12|h!R^%8suU<#xSTocHEY6M9cFaXsBGBPqC1>lN8 zMTJiiq>ztKQie}LQi4xHLR^B6Pf|igQbIyPMg}AaQV!7}393yX%K2pYWMn`D7=z7| zfL1(UOTi*wD<G^y5as5^#|JWkFA2os;}b~c<4XduA?XEF7Jvc|oDjjzMG%rQ;BWv} zW?;=S5>S7DTmf-}jHCp}QDAA1M?nmUq$H5H_+<F_l4Menz_#(FCh@_%%_k!vBatR4 z19NmLNE65w2%VY)0%<bosY&T+GD$LN=}8iNd@^Y=GU-W4=}BqnNl8g5d{AR0_}D{3 zSxx!)+#mqzdWcC7--6j-Sw4XbI7kKyg9uOnNq_<zl$JpPAPfl;Fbh<sg3>Jvg9O1? zn1N44CDY0`3&aCe3E)htqM`*V4L}%rovxLYm6dchUnpNF$Q4#r7FN=}e0(`pzE-&q zGeQ1^<2(=xE)HgZM3RzJRD|*il0Zhdfyu%musCR*B&j$lsh}jOw5+VG3?v99l9G}j z-14NPvI?!r=#na!d{S9?QbBcziVD~=kPZ+=<s~K6fCQ6jR8(rA0K^9ca8gnMhzZ3t zDoIHoRj_hQItyIr@TsUk^D`)TK$w9++R6&-AyBmo64BuUXLC@y2UKH%gzCWMrlToH z9k>|b1C<xx9A^dL%YxztR7!vfP-xKtQmvv6DbMRwK<0rI=s-$Yb+CI>RAA-59mpQ2 zMzF!4qJ|IFXi$;1vb6)72O(^1ZLRo(!RkP@IShj&!S*mRs5CSp18h8)7)Y0jinq6S zlS(tFh5=y}70CT@EiIs;P(`J+4b+@701<k6`g(f$`g*PUU>-;w<aQ{|00jh;&Bw=A zr_$bG+1aI{qLP%<4Nd3y-T6sLN%?-!rrjACAZATZFD#za`ZAN)lH8K=LE=eC(MkQ_ z2<@H#rjoiRbQihBP6S2$B!nd(Ry4?71XfcKOX`^nO79@wX)#OzNr9?ZkXld?t!||W zb2*F#Nx|G93~I!Iw1V`3+GZdcq#zKMCVW8*aJm311C{?Opw<q^5;YZYt1u9xflrMO zma;&386pN10of+Zpk)H7N7SKBZWR?!TM(2%K?NZnD4x~%_^hm~rh;q&8)9W;1!`-7 z%?D+25C=@de5|4Z76K~(rBzVMV_*cinGf3J0GS1DE`p?V_~4m<fx!fvG+_dK!U`Z~ zsOa!X`}#8IK<ZTmXhQ*9YceocN&AAr2i|LtRss2l0UT}&;C39i*{22eHu&Nx6BV!| zSREuFLG>xfDbuV>`J_Rf2l<hYL50y5Y(2RB0CF?99iU=k3wETf)pW2wRaDfitRN*F z$jdX_ium}F`M}i-h%ZnCBEi@VtWQP7ih+R<WE{v`Mo^~Z<MRdc!SbNk1O>E;IygIl z+?52D1;rRhLYR>O)Cd5_zc2#>NEN870x1kY0S;;>Ky-k+2(Xj^G8lxxZc*n0Cliny z2!m1%sClghY43uSK!O|OCUC<BT0k&>8!@mRkdCw!#OW%aRxuyMP(F1vHAtfb)Rj{Q zHP}?Z%@nXWh^3~c4zdk|LH!M|b?TsIHkjZ8m8mi^GgTn8j2f8cQ-Sbjg1rX1Y8H+m zr7b9*LkeC<h6PoLDXB?Gkoq$zg^w>a70gZniGtMd@$rELz!fOi5l}9e4JAMdlVJ4% zNDL&v2Wm8cS`DGIqxtx1z)k|$4`G2i6Cfc_+(9rS0}}%?188#%3j-SiI|CyF2LmSq z3j-GvvoK(T+zg;Sn=A~BP{_-`$H31Zz#z!L!XSjCLl}vTFh_)eg+Y`7M2azpGcYnp zFi0{;F-S9jx4?mQ$TKi9C@?63*${n74B$C&1~mqdt?EcJ8Vs5YDhw<P@(fxGEDYKV zIt;oDEDU-K`cTNiU;t(rGB7e2f%O?PFoKulurQc0fab(48Ne4jGk}I=L411#2L?w5 zCkAH*7X~2)3(&ADgFAx_g9n2rSiLKQ7lSv04TBGZF9T>BTL42KLlA=#Xl*b<D1!y) z24{u{1{;P*hA1#UnjxGahQWp*7R+a108MQtFxY^X!6Y-VFr+Y~GNds$fmQ=EWHMwj zWPw++fZU(UkjDVJzy~xl4dNFwlrX3;fTpEc7|Izc7%Ca67#JC<8EP16!F~kk2H`re zIUrU&*vAcEl7*oW2{ti6U^7Dt974jHg`t(94XmRTOoG<Kbb?td3>{Fqi=msLoPmX* z2g(QO>}BXf;(<i^82aHN6Bs5kOk$YKz``(vfrViz!z6}ja93tBOlO$E(8Dm3VFp}1 z3xf}sJqt{NOq<QX!l27AhhYxGT!u`Bc?|Qx`WG-{GAv|R1XZ;dESAZzgdvk*DZ>ng z84U9nmVv_t6n@LWs#Y+pWLU+pnqdvYS_T$|b>P&)!l1#h9xA(mVIx><6I`DU!)Asp z3|qnSAeTVW{Wb;`hF-8(FT-{Q7KR;QHVeZ}hMf$%7+4r~Gwfm5%dn4OKf?irgA9il z4l^8KC}%jzaE#$NINzLLILUB|;WSv+8HTeA=NQg2oCEV%7%qV6iwu_-E;C$VxXN&i z;W`5g!wrU;47V6=gXQipurS<ZxW{lG%mcX!9X|leJY>*kc*LO3@E8L=fs334vz{_M zW_ZT%oZ$t-ONLhruNhbv-Y~pnU}1R2@ScH%;RC}*1{Q`-3@i+v8NM)lW%$PMo#6-A z29Ui!8GbSRW_Zl-2Q2cJfra580}I1{1_nluEXbuS42)1VNQ8-zg@GBwXJlbuWwc;m zV`OL42kQmpUQj%PR3T#yMoy4=23T6=V&rCIVR*>E!w7+(I02or3qEF-5ft0{3_^^; zj6!fI0%sw_U@TEaeFiZ`aYhM7Nk%C~X+|MN8Ac&SSw=ZVc{p2vQIS!IQHfC*ZjK6= zr3xlR7}Xes7$G9+U=f(vFd8BU;=%kS#HfLyQWMJ40{e6mgEk`zgAO!YI2d&qSs3)d zG7$axVD=6MA80x?U^HZ8VK8De28**WfUZvjsa(Ne#yEq4k-?l1R8CqjvM^XOS}`^< zSTjP(LmNh0Mmxq#279nR@VX5~XGT!z0mGn^hUFRD7~L5?7<s^|SQtRL5o9_Hdos>o z@M834^kMX6^kMX4^kJku4`h=+V-rIFBd81m9eEtYhzudA06{)v2xbgn3}p<1`T^#4 zHpXxykqBsrgJdJY;*b!IVq|29W@KcDVT@&rV~l6q2~MvdRS=xOn8=vKD8!h|C<Ho4 znK6x#g(000l$J9XGa0iOvl(+3Sr|GQav66p>;T6ZL?4WN#E=KJEgwvRM9Ucp7~SD2 z3mHMBK1iH}!JR>op#ZF|h>?Zi5!fZgj4TW#V6jrhGH?z6l}R(e`XONe(hDh7Ss2RU zzI@04icOFoE5N!d8Ce*rz$}p44ZtRW@@6$73quVf3qvhb1SAGBv5t|20TNF2j17z| z42_IF3{6n|EMVI~s+t*Fpkl2cnvsQ}jgf_+ov{OM8q7QvhKXQZFn%XUJp&6v7m`Yl zoG!ThW?|@N>|u;#>}Bj@1eND34E>A~7(r&t0Lxo4PGp<}HeoVU-iC1s<5Wfw1_N+w zWg6pjxVbYJRT*Z2)p9V-Vw}M+n-SC=0M++%80Rw11IvKgAM+U@p$hUB2jc?9g^Y{9 zW`Ou0`Nc@`@(fF$Jbh4|#JGTA8CWeOMnL|6=u%-=4i|@rOa#}jFm)grA`T&S8CF2u z!@^Jm)d`USkt@M+tH30Pj}5N|%j!bI8rl3cU>SrSP#uDh2f0g^VJ*~7kT}Q}I~dkM zb**RIz_^i7h+z{WB=#XHKqSmW1BT6vj10>dKrsdqhv6+KW`azG=>Vnia)yZvTN$?@ zsX@qsSdbD)6r2}W7`8JqGVEYvVb}?E6(~)CRDtj=s2LzO1cUMb8&oAo1eUiaG3;jC z!?+hJvyX8<BMZX;#)FKZP+M3SSfFwsT`+uz@h}*I>Lf;nBcP4M3`ZG{F&<|;!6=R; zyiYPZf?Wi%=M>{<#xsm(8P74EXS~2zz<80d04%4^zy_`z#K0t|76D-qMioY7Fce}G zWxT}rnBg)bq^7?DrmsTv>N8wpyv}$7tXGxsCL<)Ig&1!!3NhXWiwZH`0n>LG?}2HM z>Gv5QFg|2_#P}F20;x-%fZ0zOg&3cKSs-;n;4pZO#DBr4$tVIAg@p+yl_J=XHaVmO z1GV%Z?MP7B1SuI|DdHyF{UDK-P~HnD4Y3<WDlxud)Cae$AmL7k)CbL9Gl4cNGC@{f z@<3N!!Y(ib?>$lkE&K;9i2^Ms(SqDjY-q$}%)|t~!xOS_AGB!S*~OKKiOG$L33Aj0 zY~M?0m_IonoES|JFh!9}Oi|HHF`!cj62a%lg14%GPX^D*rW%!#iw5#Y1|SFL7cemu zf)45cZCb3#tFEbKs;kdqVrs~1Yyux{(9zkIm)D(_m)FzV*FRz6q`bVzd7#7l^YUio z<>k#pSPF_3CMKp?vzg`~1d&)EdGNUhNMhKSOiWA*7h#h?69btCI;9LEynMw<2m^y$ zwHhMCv}WzP^?4ieHg4L?1UjD<q<`CXh#WSOX$L}d=dQfG-Fx=#+n?uuz#p_=|LC#f zpi>nPD)RC`p@|2dIh%LxJhFOpHfYle<Z@f248SD_I`cz_OISn{au67t6oa!M3^9<x z!HmU8bR1+%1dU_`oyW!sT9d#EBUwS4VL${3gO|C2)<`ljL#{~#?}-FOH4|u|Br_8e zD`>?jXh$RiNEsM2fs}&OGcz$UGcz!QjRu*<%E}5}waUcI3Oaj&86*!jm;uZJt*d1P z$*_XfjDeQ7GJ_?UK@y;o6CnF^K<)!=aARNu-Dt_czzEXA!obALz{~(zhRXsnmx%?m z9f_Hhm4O-LWT**fz6WtZ-e6*8Vga220rD3cI~&MHkZUUW`1$z-_ys|$426V6`1$$y zL5F3Dff)SaqM{O#QcTiJ(x8jngycZ$@Z}Z66#4m;#F>~ll==A;`1$#HRU}lz_!(50 zm_VB~7#M{3`T5nDm>AR<7*yGqm{?g^R3v%$`88M>`1vI@wX_*PJ_a3Q02=<|WM$%H z1f33`3R><8axDV`Co3ldBWQgjJF_$k3usL$8z*Rin>1*i12lEX2?{(0231Z~85RbR zLLJaLNF4@MT~L+40kVLDk%^6yfq{vI0VJiz2=Y4zlMILg1wYuStTIfX3)fjxLFZxU zurn~Kf;_;+#KNG%3JOg|27Ln-&?!?4`k>%2WZ+=tWY7UEw`7)PV9;UW&}C)fWM%^G ze`DeR1t5bAXkQN~q!}1gS=d+@bXfF2$%{#qfq{dGiAjculaYaw1>`Ewnp-0V22~Cw zPE{t5Y6b>&kQx>p9cIunRLFs5s!S}Pz+zTq;RG-9V_@X~xtN)Ofm24B6BJ}D42<Bg z(E**0#-Pf<z{(EVLdwC+Z(?f3XU@-KVX4n6A;x0`T6x02AYu)Qc~IapbFecqFoVv; zg03W01&OM1vdDmgkAXoI<TqVr8PK^=EDUVSOdNU)49tw6vj|k#z`kW?U}j<9U}a)p z;sB*&NaSHBLD2`c8I=B@%P2t$f|yuA3syn<`?#3E>+|^qK<n~F#h5@>5Hm1H%gFM} z$ulS@GC<b!sHie9s4=NCFlc}hk`4odt{#IngFXX;u7M7NA&52tk0CM`>zQbSm*j$$ zbjmP*cDI7Y78#7KAo2_#eIU15gBC6_FxW!(f!KrY<^&HPGBAKz1a9C38K83oJY~GR zA>IIKV_*OsDDK1H!vI>Z3)-{i54sl)luq~qgZN<^^C0{37(lBx!BgW5;5D1k3=A=* z46)ImkwgZDgg6EU*cwa*28Lwt5d)wzmD17~K-=UpA@(srH#Rbe!1m}twnBoBi^+nh zf$f$7^F(qOAPbb^av3Zc`1$$4duH+(nBo{13K$CGz#Djr7(lya7{L4LK+CJkbr>oj zUIv{WSp_zTfeDnPAWRSm+A0I0p`igvAs{aJ&;s~&nfiuChNfoF-aOEbUWV2-2C&1y zHz%qybbxLk?}}r9ZOapZ-u%bFz#!7c5XZovE5pzaQUlwdH%X=fY{z7XZBwR#dG6qA z48cqW2Jo%sZ6J;aXbBI9F&)0wk%56>hT2R9XgD!2fVW$M+8#_yZpHbrprz9w4Gatn zZ6No8k|_d1WiTml2!OIO=%^0xGD@^IC`b!<#~1?xXhFQZ0t2``ss<_?L5Ub7j|FQ% z7QpL*N)Ig3<Ot~-Ffc%OibGNbPD3Cig{3xl^{+Jp;<yd44m;5BI+(=(I(fqdbYCZE zXx+;jv4IsTjh*rZom>i*@&}XH2>AZhz#v#of+>UMC>R%&2IVs(k#L3xa6yG6gvtc% z4+5>wWC0%?z{bu2;&6fpE*5Sc5Iv+ZFNv;44IBhdmw}mCn^~8cMU#bvmzS4~O<rDE zSy544osp57+uYoofm2pkOhQUPTNhM>!1S@mg4#<A%-Y(zFn55??*khEJv#|<9i%+S zIFNaa;4>OIWo5b5xP^s<A=|LT#Kgov=T%yW@@fhS@_}}#gRr^2xwfvZo*o0_umI4l zq4Eq2%8HB(45Ca7tUNsI?EE@LLRwk|h9csU(kfgWG73s^?5Y~9JfIc>18AKl)LCF3 zDKj!Ma&mHWb4y4_NlA&x%PaHpD$DaSGn<?1YHORB@hU3|v#{{0bIOWI=<3RATQaaI za$CtW3WJWMv9)z{v||Wn2n}Uma0&=<adC0A4|H?)wXyN^^zriY@&=zl0}2=q2mc_% zHd~Mg8U`gvaHWV^oG>tmGKeuSKx#!$wFuhR4$`Uw-dO`;A+RdAuZ!S=Sm1TINW~mT z3@Zk;lwlI!mLsAZ#9%=$CV`2=w83eN^ZlW#;NcWpB~AvUs{(2+v$8U>vVzBASQ!~1 zyJjHUnZPYaa8V3y7lI523xRmxomQX)j3AAmraQPQ11*IIwWGl&OM{x-te|E!Ge`(> z{s}W^vlNI6KA;qwPGFt{wG2VEGpP9jIlC06+d&c_*MN4PK^NMygAP^V=Hce$<KpDw z;Nb)x0ww@D$(EOgL6Cu)hm)I~pBJQ&fq{#am6e-8NRXeKfn5M}kCq@iJG(GDAA^X9 zh=>p;A0H<>0|N&?4;SdjT0!vYby02}etz(QO=4U;3_RRCob2rE4D9TnMGc@GJbVmX zAay*T73o4e{QO*kLIUip4D6sy(%jsj?bUqjpiBzd;=>O+CX0uGL7V|(GbrPLRyp(X zGVt;73W1mmynGBi3}XDCDP(Z>N?4c^lqGmU8iW}@tyKno23~e{1_?&cG%*8%AjtjF z;A3G$WEenxVUPuRNJK<V9z@H@i!gv0A~GT(attCO?Cc^UA_^jMAhj|Ka$q$g42nt+ zpbVN2hhSAT6);p+(oj(awG-IcIT=9roPjznA|kM5;*h|Fkl@$>Rp2bJrRltUpa5cE z;0JBl0x1F=?90Fa+WH3CBL><n20F}D1hh#Ue8MfL&j3<~1%rw%G4TF4sH8Y}Ta*-N zt{fyH3pyPObThURc*8h@I;d5vqssug9UCOipo4S@1y}$?fSdrr#vnpiQCQeS*i_Wa z+`^K<%9??}hQXGB!H&VsUf2N?4~`6K3{D_jpl#m}nt{Ou#sJaqqbWcf2o`e#yWAa2 z!UzurPcJbsZyylf7et7O`HA_1x!z)80b*kAVq)H6Vu27T5a|sfaAA;5Fu5QxF)>6v zF9ti^f&nHkCI)J3Gr+<h>~Am;!XPFVim3F%BS0WBDk=(mLW?8v@f9HZ5`+_lg@u)b zg`vBjlElOUl93c4>;tp>!FC`Bbo&z#<1f%siVaFbDOeHC%Fimm$_nY&fKA3mh+`8J z6O#a$CCSQ)Br63H0%2)fKnyh0BO?n^gN)^nIUu&Y0*HiTBs)M%MOG!a6qtc#oif7z zY^<yx&mzWaxVgBvc)*$v1TTUKX7M7F4^9>znzO)179t4HnT`k^2M4Ht1tl{S461UO zK!rYdJO^}6Fz9|PE(SqCK}hWd9_9v@Lkys53ACRObkZlN&H!Dn1KO7hs?9+48@LJr zDFx|dVqyY24{RH#nG4dz1lrsSshuD~Ambn%Z;&D2(RC2P2-3^I#l^tI#RX|*aYMF0 zae>bu1=X6MZPDQCaly8NhW<e|u`q#6X9DR3pEnO031a|_KQV*G!9YbRxRzz$V&Y-| zozlU~0NTgR03NVnVg?OiF@XFo2s(=jbO{)!$;SjaoSOku0fFj1CI&_x1_nkR0ZvX% z0RaI4CJ<&~5&-Qd=V0exXJvztASW@v1UT3^IM`W1h=YRzR9~{Qvx5^FD=RCc!3l~j zkOV6zxiT=Yva_<Xvw{?XF)N74&JGe~We1NMf(&41Wd|Kb0$Q)e1Kz~NzyrFMj-Q_& z)Tid>VB=umVBlZ_Ly)teu3_U~2N}WuKFAtWrGl1tK`;Yo9}lQT2A#|b8PEf_Hz3Vr z@aPCAFhOH|5DpU)6DO!T5)cpoos<vuG>l*bAK3^R|K;G|;^yMv;^yK6d6O4J@NjVO zad7bqa0qfh&${R5VPF7lumV-4pi{!dICwcY#37Qr3?NMm3=9(BYD$C^M1gB_VJT?_ zQEo8?23`&Z23~H^XrLUcJOif?C<!PqFz`b5@^bL<a)2NQD4>`@Dj7hBB!O2)f_7#r zfNCTL(8U2@CvXaZyT)A7pnc%rga5(uyuzT(O~OK;ic^{!x^WfMDrFR6U{H`2Vh|Qm z69gS}4mmQ6fkBc%T15qH6oZfgv^rLokWdGm*slR1Kx0K9Wef~D5*!@544?}LICwxo zqNfKsv!6qZLtj*kgM)*YgNs*;!@$r;j=>mYu^fX5vnhia187SdGXt}^goK2amX@}b zmX-wrgEE7ql9jbJ^vE<PXBTT%TRR5Ox^Ykw1u{ScO3mPd&A1qZ6@(d77(75*(-c${ zKw$zp#vT-n92^Y1JfINt1Obp{FAxKCk`jY51E}!=3J}PU1!(w_S%j4Va<ZiqSQ66u z(NkdH6jS735N7scQ04Yl0+}NsBF7>jAtB<)zz_g3($mvhL_{PI0z^cD0s{j@L_|PU zuQ_DDHh5@2LWPTiTSbwV1JveX7Gcn{whrOo;O0=_6yx9z4TFNv(9rPEurSc^XP}eJ zLc_yCLqlW3<3J!D3K9}wASoFHQsUxLAyR3H$;s&%iJ4j1nK_vm3GtZ;xp|5CSp}K# zS%vWlS)dbS;);rkOBjklptMY+yrQC_qP&8kvZ}heCc3sRsupx(dtEe`0x|3B>uT$w z8ycG+pt%JOTH9K{u)V3HrImp}T9^TRMlC4S3aM~_)0_~8H3)Qq2<y(SZre_4YbRT4 zTWf1;ryg7DPHPaq$GW!<#IUxu1FP%n?zgq>v7W%tHF1J%cmIS5-Tgh*J=V6p*49oS z1q`;fPS%h%>?Ci{IP_!&1_scv^MQd=7(_&r86bmLpd>&DgL;L`&?YMA_6*2OCz~+1 z84A)Y#vl$k!cvlfK}woWhEG<GL0&<TL6Jd$0eS#~DrEBw19;pIqz7~mF^C5Dph4sE zAPy>41TDt|3+X|QEizy*1askpkuhjZGy{076(k56Zw1ldl*E7#hi<qB?WzY!gBEQv zfX*zku~oFQx3PtupadR$1<8Xj_+VGm=BXQaa+blz6MP62q)=yLWAg>G{Mh{2*!%<7 z0@>Kuf`UWXz~?JQMKgfrRYB%4K+gdL9Rvs-3Cw25$<1TPFDPIDU7-v*JqY_zt!Zg# zWhEt`6`NpVAlK?Z$3__#7+~X}U`YlBq*H{TA`Fnzge=pLK^+4_Jp)4nXwH<O6-<K0 zF55v$z@wuK3=C-<X=z>EB|Rl2B_+KjB|V^f2>SZdCV=U_G{|{nAUmM<4uO~q3=E)T z24aGe8Uu171BVh;0@RyfU<jNxosA6~d~9rNehdsVAV;6gn?G;CLWV_)mn;Q?Wy_bZ zSh;fLs?}?ju3fjDVZ+8vo3||Ax{ZNhJHrkJ@EK*`BgzgPK63OJ2pm6g;sj{s6LP;2 z$YBg;VK-`?2Xm1L97CRv3(gS=K?CCu-g)q~OqW3Q>17bOa{2P*tJkjIxP1BY&0Cjm z-?<ACM925g1uoyeeEIT?%MTttdJMi~34HP9%d}U0eGIP|7~VjvdfV6c?)?V{8$_}} zkHP}6v0_lRWo2UrHI2cu8=$ra1Pic3x&z`8V7?@~lr&_y0+>q@p`^^t&aR?LJWwNO zyegz8impyqk6quu5M30BZ^X_Hx)}{g1c_<R&Te6e#K+B4RkZ?3g3oIO4V-|RpzIuA ziiwF6O4E(P8n;kE&|XI)V_jWcU1Mz!sjI08qQE4msm=)EffSh;>FVkl85!y7GJr{4 zU63{-6H`+V0(JC_7(ntyAVN<Uq|ivu&<I-S^YHS4PiYon5EfxD(q-V4lmfSSK*wN$ zk`7ooNE5h40J4FB!3b<1Xh>F`fdOQ!uC5UXftVmkBakv9kj=W_fm@KQu9&!l5vc0` znl=S%0{K$`R6MgVC@HZpC@U)~gLcAzk{d)91LV9NHFfa$H4F@(jToRh#>j{TWD5hx z0SrtGps_MtP%X#+I(7$g>YW}aG{9Vt3=61>WMl+7033X>6N8bdmae9ek&%fm$jRW) zG-6<6G}1K!_mo%|7<3Gc_4M^YgLfd$=z@(BWncgWF(ZfvGL{K44a~p->P|5*Xlp|Z zHvo+yYcoK+4)P>!47M4x(1?wlg@uEYixHfe1q6j4CNY3^&cJkwh>9_avw-H!K^Yn* z3hIV|j;93;_Ar9>=Rl7NVqjoUV^rr;2OSj%Q48AytgWJ>D+-?g5@v*y&>;2tjEn{b zNVTvLgR!W#iK(bEsEY_P1XKlshJ!$CST$_U06r-YRO5n#K^Qdg2|6SY#0Fu|3@GTP zM-Ur?p>?sN6Nm$v40VH_t`4q!!NLp-puufO+Y~Cm067N67s9m$4}Bv~^*|?ggaaXM zSOZ4zcn_lqKe!zNKJpN%mw`c;3*sqI6P1C1Ap~@SJH%Yb;lT_Hp`1J{VI1K;43>rr zqKu*uk>JQ;WMnji&b2beSo+1ru|OLh!bkxZ4^zs(kdO#96|~C}#1v)#nE+xjFo4@l zV13Zw1L*_v8H}NU!~j3;5OkO;-1!Wk>0C=S_>33GJg6BA3=FA^d@QgtD?u_oDAkD? z=-40R6A{zn!PCJYm0+C709FBJGcd&ag8BrALxUK=)5g&GPBlhLMoZ8ss}RMY1Oj0( zKm`$lU>u-h@j+t+2*Yz2azVE<8M71`fR1(o=~M%IUkw_lAQ2W&6AHv20)whWm@-hu z4#Z|-=U{*w0t}u)1qp#_NDwW`06L9ROoD*{88d*|yOIzwFk1?=T!4WA-0)>k1noEi zb+SN<BtV9sVsMngh19|G#oz<K7{K#M&`hWenhFLLUwZnWcCQg+VFu)80tN=y-2{-U z3E+l7&g2BeGIS^wNrr&|w7$R*CIZP>`pyiX%Zl6}W1I{go}eTIQUdZIh=!hw3!StE zi(n;S$qtm<z!@$8lAyr(3!=lx8gyoGNGO9F187kKB(5VE#3aBcN<q|smnbkWSVuE} z8|X0%3=FVSq#z0)%N8JP@Zp|NCNh->KIs#3&?mAuDA^<Upo9Rf;2<{^fZA}NNe9qa z2SO1FOAtJAfFcCth=2~GV_*O!1xYDs(C|M*0({^Ygo#Q*YD$opJkrzvsHOvnfiY<Q zi<yZ9baxIDI|tp{9k2!t*ezHH@W>qpI|nB_H#a-DmC41z!U7%@WM*S#V}s~t0}FuW zcA!Rp_}tv=+}vzjAk_?9T-;n-EG$e+;Nd{fU?mqwHDq)UH0}lFLdGybmqLOr{$gPS zNr1QRfRE&7gbcKCaq+US@Uro8fyOpKqh27)!^+CS%F4^j%LC%^f-djl0-Z?2!vj*q z!^_LW1d(I}pPveH9;k!`S-=Rg20SzbE+|2^Lm8lnInWdw1L!hJ@KHP9O^pm(EL;pc z4B&&6Kr{oG;Nju|jSGRsq<MI_LHERfj%xx9J%WZk89CVn1cmtcAvXqaadAOIjFX!i zbmIj#H>f=XUgrl27SPU0E@l?cm3$y;K=(I-Yy&lK7(mCrGBYyr@^W$Ua`E!Aaj`K# z4P)WrVrB*fH!m+QHx~;F189I8H1-M_OJrnZ1cd}6BNrEF&JR@OfufcN9ExB&L1Vq3 zaAM*D-P8z;M^GsZavG?G%+3K`&IbxJUQpnIHgtlO2|(r@K<4v+4uaz05#s@^tK^Y@ zEEJWLl$7L=;*sVN<l*6w=HZc%lm#*5<UtaWk_w6nuz5CBHAzWz4G7SbR94o~)&X&J zCG{lr4GcLrj5LfPd`TrG6G=%^GbJT+1xZPHc?%^ac_mFrNl8nP^LVVRZ6vL2Z6tYk z?09&1Ae)|ftZl4$Z0&g*cpQ0ncpPP%WMyr6cx;@lZC${6c$|2wctD89nuo`hm&+AI zgJkTi?c8L+ns~U}d3bnaL8|OLczA3jEgg7dBrR<vovnFzK%SE2;qmm6vXPXulyu{< zk(9In`Oun&hsVZE($SHJN7l~9nuo{A5$q&eE-on^Z;%Z<yxevmRnC&KJXSW6k~|K! zTwFfBAeURpSo2u%*zj;mffb7L$T;!vSV>xX*hpH-NJ=_^yetWla<jCCg{+;dvn-F5 ztS1i-kEEm>509m!q@<gqwJndeEXb80<$h8;JfNFKL0HV+KOm5cD=0W5l#7ci43syx z!WkkW8KO9%-8nd--9a}L2)ax0@Q84WbNR)@^6-fBaB+)+jfev&m*U~!5&<#X!6C%O z1sWIz9bX1Y1zZwbAW8}(FD1dnCBg;q3J({Lgu9F^j|3>)$bbTt3zS?yiJgm!C!QO0 zE2f<+w>Ze13Ea?x<t_orV7wsJJiLjLl1V%~+&orRGCVwvAY+mt`5hFNc9OC@GLn)q zcCyys(B`qR1jT?150AB*leHa>tu-iFq$nw+DrsnFq-khqq-SJuWo5hP@Mvgg<jS$K zv9soJxq~j*iGjF@yAU*xYiG%$q@+|Nuc#!cq*QF81fC==;ZlZxQc#eC5}}NxBPcp- zBtfwPiXS_WsGYT}8%WrahsO>Se;zWRjrDf2JkBzbmYzI3GPaV=JUn*Jk{}(93?NcQ zmZyxNT+$Mhn0f4ED=K+-ct8%~i2-G`Do{vETFO9DTCtK+wT6ZU7Z;a12S*Jz7bvi} zK_v$Wb4h_hl!u20GExN2x!~;0%)-h78r<jN=Hcc66$(%c=?)5lfe>`1jEE>yQcPS> zLQ+Z!)Gw5k6O>l~U1%YzsH7~bA}c5Z=BO%2NvWwzX=qAGX@M5kfCRM#b-1}fcZhHs za&sFQa~qhLa&sG*8FHJ0XbWxwOG9pMD`RT|ZX0fHZd-0{J9}<!2S+CZXOMaqZUc}$ zZewl(uq#~Mz?8cOnBw;I0@2>wKHS{gzJC6K0oEWscVG~Auzg5q7?=Yg0=c=l!y!x- z2*n){31viaM{yf~Er^bZ<qnU73UhPELs<!lNl+GdvVZ`03O6@*YFausH+Kd|IFp+@ z%Z58UGlx5uo0~f?hnqXU03;5^g+;|6T2N51gqxeYw5+_mqEY~S2}gYccVlCtprAc? zdxHQscROfO3WPgA1Pbot=H_M<=mLv%3kdXZcXJB}^!9;F?iUacm;jc55)%a`2~3_c zbs9*Lo11(33@BqJgccM8shb5lv3fR$B`^m}c5=_1Coq2jSj|Fk2rUA!1q9}Bb60>A zb1&xR=3W9bm7AM;DM+3>nR^+C2ARPv0CE{OcR5Iad**U(2QVW?Kwv7Eu>wqTbFbuH zwVIoodku)YmRm4~n|mGi`W$X<9d7Oo+}s<vxw$uO-onjo#LdmU)sWi-5~bYRxVg9Q zkdgu=DFogLXL4}t5)2m<5ZJv3CbV}SD3!B<f(~TU{sRX=-VhKFI5ZdRKWOSW3=TzZ zZtf$X6br^j!KBnNDXHTS7Kl6nB2Iz`2tIY%kemC=S-#GhIo#amKsM-bpTEG(&3!S4 zoBPsbkO240D_{yl%)H8-!_94E&wUM~=K2kg7&rG#dlxX{mOYrdeFsbh-UU-o0;K*d zh&@vWL~?U;-@6ZGfT#x#A8|hhF+jmD@C52dfgZ3BH#aC2pK^mE=@~aS_wyGoUI+>b zz7!M;*tj<P6<872Kd<3I`$j-uE+{$$1m?V5vSf)s=R2^fR<QX30s^xI1l~jBK_tr| zNcs_grlSws+#uZ_!vzFB3VZ_V<o*nyK5%n$gF_y|{Q@e-Kp0%wAP5fF_#6`(sC5j! zB?`8F8?;bOT1FPs0+o|j08x;IuaJR7T_!yyCMMX`MdlWkrlzJ=rlw3xrlzK*);3JG zcJ>aArcTZ-Os=32e~@+(u@_0|y_uMN;2XY}!tm<_tug`4c*VvsF~uh^B__d@Cnu*Q zr>3QYX1bCyl9Q96*9|c-6@WKP!PVhql$ODy%V7)*x&kIrSyjzc1L4%x)q@xfjUW;` zPHqA_4NOEdCnvXnu1yAqd3%RxXID39zZR0t2}o>g%n1`GPMSOs4?tNT0-AE?1oaX? zO<Y!PRvunH5Dy9SBe6luLC+Texe|d1x))Zhqtl??4hX{n8MJ}|gh67U&H!YO7s4VC z3nm6K3#t#q1Ze|f5C<KD&4Ut9J)mw4SUre9R?o=D2)cffosEqRbgLv?D3DDY3`|U% zTp-slb0fP4!iKJffp9?Ek-#kI;yr3p+@J-dU<;55$T%>v2yQkstl<<EmH+-T{9$BZ zsQ4G<+`$08!0-S6{|x`@|NVbo{~ruMW3nIq^Zoz-AGB16fq~%wbom-+vl9~oGq~po zHklQ2g(-v$DzFh4EC#+R94ZD9;0E`l8Nll|Kz#7gkq`!iggYF<U}9zgt&|7%lNlM= z895-r5Rwx*R147wAwi3nm{}n5m?UHkFQzzL1hmWv&H*tXrXol?)EI&f;~an{4Yc*g zu<Iz`27t^&!m!&S5b`h)Bt=L}g6@ME1ldx-@c%zV035Aw0wRWw1eF0Gc~EB`M1wFh zGc#yu8HfqSFg2k4W}u}j=z<_|uow~nR|gYA@*8L!7?PPV6&N&=149FNO*J_23V^rc zf$RcL0`M>}w15l&kCG=afH%oOR>^@TgF)k)pqMgf06D~gK>+L_kO=q&uLEEc!7HE` z7#@Ikseu+^F&qI~*T4uC0WDl+NKjw^>5}I8_aAiUD#(omkeitrAQ$(7+yD|q$5_Nc zVGME(Xr>A}HVxXM23q<Iy2BS{0%$l1#0HPffL1|)II!#l+RF#xgE4ps5M*Q%e8?6^ z5PsVfh{FK7M;F3?kj&7z9?$|PanQnl(3mD<{Sx@<Rq(_gXhRX`LPd}kMh56AAqLp^ zygUPg04O(rMl+Q_OO-%VYapf2Bk7={;)r2zkd^8Th-oNjup^)M3)PNHfzk^TGYfPz zJ|i2bP+$ZPMDg;$?tcfV0b>D1K`@O<fX?^<ZP-VMN=Py?N`bl3GN7B1<P{hh6_vmO zjG)_*KrF}&Ns#sh8+fi5MsR>uyhAu#?A$yM7Kr2p5n#;6&d$ycW)UF3?OS+7$pl{c zkL-O^b_2NdhKv0Jry_8YhKpe^kP{#%<sm5mYX|djn*kc12Dy)!i3!^s_vqylIv?3~ zuuTYpkpY}%At?_bj9@|ZqPZC%jg<w_gOn6u0x;E}s71ywaRM}GW)-320W`c4z?lUs z4y`!BY;fs;2wc!83rHNpCm<!T@CEJm28A;kCMrBZrAGs(M1<J{TDk~wBB(HPU<9#1 z*JcWU!UB|KLBb$6g6wBt5P*~^2SDT4AZLOZpu!G3C=O+SByeDmsYn418Y+gX1nnMy zbD#`P*!~b`qX1rTLFFM+GlJk{2;g}+3DC|6VG#yU0RmNnpuqDxpw=o9W@BgN0QUnR zV&L8YXrl`cFSNKtXlGyq8GxK1aIqmx2_&5mCb$yAr3WMqQHGZUCk#-eG%$fGEQUXz zt!D5b1;s30?GQ;wG(b}UNC*<UAQmJ6K>Q9$E1>I8VfQ+NwSgS~QVG)o(E-}F1iJJc zq8=oVi9x{!Zc1Y(KxY~t%U~D)3L+dB(lWx(fn5L*N}y^Rss`Lb0A&U+ACj<;+jIXJ z|ALyr3=9mgrV>~oycGrE!AWpfA%!rg27`wbB(NZA!86UE`WrR}1Y$#^&`GG7pjGW4 zDVTAP$O4I=U^aH7wl!D=6#n4NV_*Wj$rQ|j65xh3lntU#%M&K>A{VeZ1PEC8Kxli0 z|NlXK22f8ys$mv0DE#WdCP3unLE&i+VgLUROH^P5I0)1vzyxz9yaCDx+mFY{2x<c` zvcoli@(m6{QH0?dKs^Rz4u}nLBIp)SggtBsI~eg<0=eNF5^)&OhDiIMGyy7~A*mW< zDHx-;5yJWZ|Nn1D*zqI686rg#$q3po0j@_NjzZD|O>IabkV7Ak1yI>wM<NlZvS>U= zO@btdZXY@y6d*{-u`rRW1~aiJK?uR`vWE$w+lS7lvi+bI4WuAqfvsAAHQ69V5yV}P zIu$~KMHoS~GNdoUz`&qz0NND*%OMkxu?fh%c%Xwa7+7El4qB{36+`qxRD+T(wD^Ja zB%o>#6nNh*lnYBSFms{8Yz*KXDv%>Xpkfe;kqOr21w{|MoetuFGb)&YAc$}8f^q=F z3Q#EnXQ47+@d>ZM(4=AFFd9`FlLs>oCIc^s;68xKAkmOM_ksVQQt&_Xe_RC~!fYl` z$C3pecVMF!q4hnaBmxV;2!uwM2re3nH>uG+fmsLJp~di@5#&rzn;4RC!7Di#7(RgF z?EnA&phKb=7=D0w5UorN3=IE4${}2E{fZJ!pxgn%P<gnILDnFxhK5R`P%yPjphK!r zWWZxppyP5N=^irH2QQ*{K|@iHkpfu507W;H18<*$3RGq$7ADZyKG0<^AR#6ub|wxy z-8M+=02)d`R0T*ZSVe~~eL)Ha&`vk7n^?fzCKgBo4pQ2HMK~aF0a{}TE_@*46Cl5V z6@kM^0knAO03)oa4>k|EK?D^5r6W+#L%E=V3ecHE46NW2z~Q2hq6Hkfpm9D>h(ZFC zfdOPKTpfcTs5lW~04*$rZj6C!H-f1Ewd_Gd5imXy4IW>E4e&wQ;GnPqodygV8{`CW zK#N<zG&dt74=<PnB_Klt0$?6^XaLD%2$K=i@j~s{vLd3Foe31Vka)(rDjYOEfE1lr zm=J3SkPhJ3X`H|aE<->Wr~dx~b_N3$P^S0+(agla@DJ25*bfe=AMGG54T&-g4FA~~ z7!LerWMKFKZj&+p`2Qc&7!zPn0Ife`f(%o!FfcTLmSBQ&;etbV7(hKjkiiXX4f`2* z82<5tk`EsP!-M$@44}rH03;57@Iw;|xVp0cuPDF_%I6=z<BSRq7#J89Fh5`b!2|}- zAR8#Hf%-3?@uCI>&=vCrkWsV-NIMYhLuTk@*&ML8xdo)g0;l62pw+#QG8N=Sh6A8v z1|5%P1EpU^@XEUcaL?p`ygcY+14w3IkY`|M`2WA*G5e3_po9ooehk_H1M((L%m_N( z9#s}R@d0hGF~I6PE(UJU9kt-mKz;!R7C}%-2HoWj(hKURN-&6l)`}uw2GEW-C?CuQ z4Htlh)?^q&WI+KW4~`qq2pY%`@J*$l&3h1z3U~pxnmXwCde9)5HUk5T4udYMAOizz zr<5URWj#o>G3Z7o5CgPH%8UVYjt7Ie1p|X6WWPRa_)p!I!Oot6!JY{e(2flJPR`Js z`mW%R1{vT6B0$X|CeTuB*eYyLfdG<#tQ!Sw8}wnY2dxHX0IdXNVDM)UlY}n11}TFq z3T5DD0xh)!xt)OlX@w{Qbcr=|C2?pNWQ`~T0|RKMA4oqE2K8^i>o=iuzu>IQD99)T zS~tfCzV{p=#0Wi^8^#0Cp!M;fu{{u*QIU~RNtqFP^Eo4<Dp(MFjET06F7%djeMaaV z=dc^jLHV176}1-yIl~9kn_}VQLO#O><uDvR78ZW68oUHC?IBRk`^U_{4k{Kv{jUFz zo)t4FoB#h0>1&~s)=0qzV?x@3kRwOng%k^n&jQ*P%*xEn2BMjnnb{ebIUuSyAryq< zg5?F6sR$aBeqhBUoJO48!omPL;t<qz21~*VSonq;urL!R189XP!c-Uw(W7Bv0-d)9 z69-pHa1J&G#9UMow3GmRIv;42Cb)h9Cj|(B#Sl=z$iVP_|2DAPe|Cl+|NlWM%>(lD zz?m16J^ud(m%$7Ne!Q1sVEDoO|39b*Vqjo6^Z)(>a2W+1=sGZ8vVoyp5~N3&VZSs3 z!+gmD;0Dxj*#?IDvIz|LWg9?+^?yjg`-vS=NPdzAmH*5S{{Q<Aiu51#l?)6A4!1JO zfp!Ces)+_rEdZ(pKtp@~`9Z7(eg=jI_5c3MgGxpQ28IWqs)FI4F{qdX731~{4UqH$ zZL@yh2h}x@wDjRWs2kD%(($jJfdga%!(2y(9|{aV;Dz-8_B=@I3{+u&Mo~bO(0_&x z^BEW#>_K&q0RzMTdQjSc)=dxxFw}#_2kJp>WpI#!39u?K3$%lug#mOz5olK%0|RL8 z16=enFn~5FfpWf(u!xwru!w|^u!y9Pl!!C~zle;GFc`}U2{Ay<^J7o~M~*V+L_hFl z&tTIb3-3Y4LNP#&Km+aNVPJr63e;zS?0YdX=4UVg?aPMjZUNVt3=Gg6rrMU^oh}Rv z44~Z!*5E_(Y(NM6@q<_2^MjgTpgk?1>p{SFGPuHuRR+kWZ3YjBID?6&7lV{H1A~bJ z1A__Zcr)+{do3M)5mC@~c8Dr2=pGtB&;fvf;IcFrT6iKhXeh%EDFNHX6&?<CBqMzL zKBx!<wR%BE&wzG5fSO$lkjesV5o~G)%mEP+3`pzTVXNFhjS2<^Xo>>uHd0Xq-J1#C zng!lp0}f%xUFe|sW6*#w%8pQW4mcN7e!@9yyl^HTgvAe`@Q}omp9~EDnL&pJf-uNe zC>WCK;Us!{oDd%=)uI@S%E4nly4&C`L1utDj8GGig%NB}VFFPA5rYphp}QZ4dmTW# z=@`J9Pe4rr8Sto(90NlFxF^BzUq0yv14GLneLI28zdt?z7Z49VZ?il6;lKTBeQ?nL zF6=<fJw|X@#(bdu!@(YYhAHwOL%=78GVmvWN+@v05abMy4g=_negLFyyAPu7r`-Gh z`u+d^|Mx$?4-%-3|MM4I<^BEt|NDOs2CZ5EanAq$KkeUlu9jERR!#f<J?{VS|NQTM z!-`!<a6rmw7#l`I7FcjFfRZ0Cq)-7#!S=d?c04jbHa~)d85ktNElycbxdLvnF@TOz z081$;DJ!X{swt^!XhIgTgGeoH9c5+E$#MEhY6hSa5y48a6KMT&@JuHgJLoDCNKArB zE^ZJ7+O-6t85zMdIS}(eqnXGfFc1YW66eAQ@GvJE=)^Km{|B{xMCC!O!ACL$F!X@B z6QHI)sO|x^*BJyr6YK&E{~=`xXoR>PoK8TM+Cz2_$?y-93P25e@Y)3sqkzQ*l(s-A z1F|C;R2_kuKk)f`P(lZfb%A;w3{Yv13b5s%IegF*DKoS`@Evjr12{E<6oC6jpx%jG zgFU2I0;-dtbNli(|G=G^0!Y~gs`VR~LH*bUNPUMr2ta;-$bgPY2H%qfb|2`hU5Gd* z0~g5E;93lHAOz@a7LW|cmmuQ>A&wM=HqyDl)A8U^SsZln6=csDwD6TcDttlLW`ny7 z3=E)t1*qcysp&v@nUR4(Lj}6g47?c>(ue>bPz%~_2CncVKnE^>V#N@=s}-Da!DWCf z1GGP33Qg%|pxy+ivI99B1%p!>cnlD-Hj0r8ECM398F?5Xy>U=CoRJZ{aFbDlkrA|i z6{LU>)Mke=kSIw;*z7zbBcn7UBXo*hQAruncvodqgQ-^6VAN!UAGvP~7B(?uG-EUe zGa-b9r4=KiCWH+pt&yfZ;Qb3uE^g3n9bA})7n=+pKc@hvpb#e~r!cy-2)Y1>FA5?! zImI9p5hS?(fxicV5*^q$kP~u|WI)FWF))A<I~#1gm*E3A@4yy$Fd*xJ51Anu0cWDM zpW#IjwB&)0*nk#pa3F;{%m8qNAP8v11sm{zNr1+WU^-A~^zmny4k%4j{RhhGFjJuc z0;&ZeLZAW(!~&HcAR3IpMI4ww(u>3dD*_XsbFQFnK~PMLuyBF~6Kr`4NC_K=08Mal zaDvNcFc(CC^gz|KLKJ~SQ7~pn1xnwD^n)=!NM`sz#;ZVuKG>5W0$jGj)-OR)7)TgW zs4y@vG=M@Dl#~Qubuyyp1echQNjCIAMDZSsgC+c+vvr^`L69m=P+)<_1;JG(cvuir z3W1%3Ou*AGGb1Q$p#jSdGY4cA5{6EsAQ?c7@C6wMDuzKc6eGe0B7n|^h=NGmG9X!^ z;uoxt0aO<;fLFOekC!<BOZuSUAcz`J*9^*nQz#Aq*K}|xkVa^ZMhZk^CPDXMS$_$# z5QIV2fV!s)%^()2-UrjD1U!CWwn7pH3$$Q@@UWCiFjHXKpzSo6dQe(HNFiA0{Skux z1K9&g^Pt?pz`z2D1ke&g5DRou6nGU5jA($AOQ4bm%whtKhk}}<VC^7bka-vw6k3oK z24Hop3=C}GEC((wIKadEoZ!v3pkaLIVqLIdpym-HsO7@Iz{AaeXt987U;+(*f`>!F z=Uah<@L_luf!ax^b6*UQMa7`@IJmP9@+~JXXn!dv5Euj*KnKc+@QI2sf{uw56a*a_ z2e%580m0P+*hg?-3<lV*;PQzRJYxY>z=$YI;4UPQ0W}E+_5c6>`v3R;|M_tJ_kZ?3 z|3J&GpD{RiGW=(kXE^Ym;e@?I!lD26fB!Q)uxDUm_`q(@BEb0H@yGxFuNm(#{{Q^{ z?fjqr|NlL@xG|Dp!{Pt``Cqi>9%f`<h?8M>vH$I3Mk!{dH}l^rH%k6zcyZkJn#A${ z&BtWl-T$vF2U;Tf|IPjP$+LOc82&Fm7|YOL|L@KJ`ai$*>lv8qc^m2*rm24Tx9$zE zLOjF&&+&i%|NAemAkT1M|9z&J|7{-rpM8J7!hhR(Tibfu|Mma>GuzvP=KYcx{15DB z{2>3IwSw<A<H!I1kJlgD&mi@Ho#DfO{TILa|NK9EWPc3L2ZJB~A9FA;-OFeAzv}+~ z^Y8D}|8F?|-~K<t2cCcR_5XkKGt~ch@So$rABKkiyT8xp=Rfer-u?hjgZZ2P`~M&Q z-zdrOUH||4Bs*46hw*$}T^+-HxgQJa<8S}}{=c68&!0as3<m$VxBu_|&;Osl!T#_5 ziTnSX|M=hk|HJ!#@;})BGs`jjYp`#>Suaq|VBer)U%&V5;(3;g57?qUF);hHNBqC; zz~Icl-k`wn|H1#s@ir14lo?X)y@&KVtK<JPu)pV@kJ7(<_ZwRjgA)rVbAcH!0;-aQ z6*>uyyq1KKlY<eo$(n}|bjBJZBQFDVnY*A6qp%1ggDBV#F>yvlUI|7?DJDkHlmP=H zC@(U|Ffz)@$ty6zw>l|-Hc9jA>ai*78)&N=8Yw9oo2aRqf)4Ao(&p#27E!jbwNtlu z5K(q?;#PNd5Mgw2byIiu@MQGz_Tks%U}W_5(_mom4+snbjVUp*FouRPhPy>DFhoZ2 zg3go)k7kVFh>c@rWP}}Nl90&An83)Gl$^rI$e7H?$jHkW&X@{zbQ)tgm`-N~U1*rW z3_1uWD?2AOl@}s!%E*`tQXt02l$T$?D9p#msKJ<8$jHc8#K_24TvD2v3Mov=7#Yhe z7<qFl8LJo>8LMj;88vF_pl9Kj);BbQ3;~5PV;Uo4S`(u%BO_yTa|>fDFJl`>l>$>Z zWW-woq!59585zA9coBs%=yJh2<%~|yGC*hv4^qt31@Ud0HlufUcXu}^92gl(LDg9g zNS?75Oo}mRFfyh>BCiiD!r0F!3~o9wfkFkO47K_L@x&l185tSH7#Kjo<qa0l0J&iT z*e8sP6B!wWL4B`DlNqOgLe3PVWNIqIG)BhhjNUUC!(|v5XU>}4%*4nzXYRZZ#`%nl zj0+YnS}e9?X*bAVka9*w#$~CD;9CzFp$CsZ{J{vdgfW$Y!FxI5ij`2+88Q$CICv@< z8BIZxw5be?jH^~RLu44k!JY$ggc+IEtnFSmpD|^<urLF|21dq>AmMNgZIC75;B=tP zp#id=myuD7aTX)vCNTyEM&VRYv@tL;g1odDWR*9gm?^^+M#im-+dzXEpo8WZw=?bl zohAb^ig71nB_pHuF2-KdbdU`ovE85`b7$-TUA(ssY91qFIw*b^8A}-%!B_i$cuXRU zjHdf585t@W84qMIrl-m<9^~(3WIR;K$avU>k%Nzsjp+!;fTOcg85x;MQ;n3Aj~zeJ z3v$=VQ>h@km`-zOfM~`uwqX9*b08lurk)2?svtW+jsl+@$H>T-4hmC{c_2YXMkY{` zlM!_B2qPppg3M(EO`n0)GR+1l1qI4QMn=X-Vm)k^poW6g@qsjfN1hnq$MQhT<qc22 z4Alw|5MyKn&4gcJxXL&iWHea+HJIzX88g7jK(WYtUG@eeBje3mjJNNIfJ8e$1Q;&{ z<s1fvy9^AX&G#V2gUz|mz`$^W5pr%_kH#}lTFL<BTd<~HCJqh`MzL^kJ4*vpn1K#< z2SoyJdImV$K+1X;89}y!n2d~!+ToD=14<kqAwC9BMq)Hg1*Hl|{$~J1CUm3(G?WZd z1`0SvMn-U$!jdo}BjlJq@FfXQH=|JC^&8AAOvrPH;IVJeARlP^IujG<o&)fh7w8^8 z*o2@SlRgs@Xz-57go%mC)C{ZuMwnYbF7;zV-0BCD0^jck=eRR@Ffn<0F?qxGenMqJ zLg9dkDJ(n!d^;cL5Xl77#H7&VlvJj)^iU?Ij8N$Q&Vs_C(9q)0(9n`n@RfL>q1B-^ zwRQEOp$(y-p^c%TP;;A_nOeXmw}MHCcv~ohZtv*qf(Uo_^g<Y+ef<yy(}anWCWlT5 zojPqg6VnW)nIQ9K%?=G^ngfvsk#pyThRz3-GGN=5EM;1@d<E!?MvxpxJ=0n+$;8C8 zj%huN38FVZ?B5s)y1W7<c3|@Zpot!C$k{EteBdKoPzios0YQX_5bVHo(5?;%&=iHR ztemhgue`9ZumVg@Q3=L^(aOTYDj>G1n!2#CF!&&RVT7RsSh~Etyn6Z|MFt=<AlMK> z;UU4}H_)S@u}@>5g$EWcXiyxq9^wbMKL(oqLz#;NU7-zHA~%0Nc$x^*0|T$Yg02Yx zHQEpSeh-?HVqo9{Ey{wnghl=-{sYfTf^;x2Fa*dcG=MrDAQuR<S%O5B8TNsv&=@Yr zDM02ZL0x`Eg$D45D#+4<wxDrah6yeP2@DJ^F%1k1pV%8fGk*-9q(LhLAiLxifYz?m z--pg!f_p9x{-!Z7FdSxNkYh+u01r++U|;|hc03FW3>)knK<iOJ<HAe~4DF08d<+Z? zoD2*O^-tx0fYztfgM7una6udtBsSIz3=WH#85ll*&iefR|F5C|^Z)<$A0X>U7#SEq z>rz0As6ZpXpj9Ic-@&85E*y}#2~Z~kGy!_R9x^Y=z;Hqs)For6OJm>wNixhm$Y9_A zx*`k|HVO<3@Oe+rT~px2HVh0EYz^;0(}bXY(}8-hR~jyZ=SmqF8W=$9R2Ue3E(LGZ zYOvYB$N+NPkL3&+`x*9tJp<AUI;|BPI|&O8Bi68hLgoMe`XB%06+oTzACO^ckW&yC zRCB_3pfCZkSy<UXqtKiT;G?hjKx;8TSD%5TU>QdObRwvXtb&B3qLKt?qEt;?Lqbwh zOF~jbT7rQAbUK+Xc)5vzA%l@I=vXpP!xCfy<gh;mYik=D(9LGx(H~I6LQfuZVsLh0 zaAja{b5|Dd@MQ3U95Chsp50_%U<A!%g8G@TH6x$}BcK%`jNr4wz~(cAsxvTzg@c!b zL`E@0$1sS-GQ=^&C&)4+N+v;+FeF39wn4|wLq$-|9)=$drUp9{EDchxL`Y_226<#L zFo<R|<ap%fF)-wV^amAqNELz(=>>5Z7>Y|EbZHp_L%AAw&5{-5WL}6k149+^$-K~E z6X;RA40ZM3A;U(nTE-^EX3)vdU=BkyxL$?E8Mp%rJ%9yt-w7x+fZ|C+6m%O8L<5)v zb$dZ0{a_9XAq~Fy3n~Pf7f@mVMJ8AP<aGwvy&rlE4B+WH@Y#AsAbDfxRcxSzLktYy zqY**E(0kqB<umd&Hst+p+&qX)Z|tC5ZxDt2?Cb)9>_QL@GAWGA0bLve@;zdF5`_gB ztOUCTKLN_D;IIZ|4~7Ov8_ED2U*IXC8qlgb*qX%ujGzE!VEDlfDp$b<Lo|Sy&{epg zogGj<(oh8(s7nnI=75ZsfH>fFrO+V-@bC%)E4WyNZf^pQkbuwe2i@EU(tv`&y$;x@ z5@<&}xTOOfh6HbR1Peeq84xBTBPV#01w66|IU0=-R55`tirF9zD14DemtjUgXdJ`H z;L&HW3Qkb763l`SAR|#Qc>N@(lE=i5Rh1~Jad7@K|NH;H{U2!MFv|iK(0~ajZVvxv zW{?j6#ohfK2d=|5MeP6o`wvJKR1elOfyn<1U~>O9(5e#fbwK~w{~rdac|ZUE-~XWc z;{X5WAU-o_KLrbDvC9Ad|M|hoX#W5IfByf!|L-BAET89BHf%WmpPAuZJ80A;_j0=> zQ`~|7_6+a#|Nj5~@7vG&tr;24|7T!&H~;7V`IZMZod3_n@CGvOa`?DyBWOg2;r;!; z|NsB}|M<Rb0{i*@3=HqU{P&;l+a@@i|IY?;<@5Ws4Gri2GyDOK$Nc~QTfe>~{{Mmh zJRmvt|MmZwAtN*Yt>f+ge+I1<|IfVO5M<@gzyJ6DLs+2V7PN-u|NsB?`xQX``~Mv@ zUi04;#D<RA{I~tj@DJo~P&nG#|DXT=|NrCv8UNo0tsCQK_<x_5;s5`B1MuQZ3$SSn z|G_x}w8!UvJ!o*{0LVCdc?M93{bvTt&i~K+pSgkIzx{vy|I7?O_!<5`2DMN?)ARM< zy4T+R|EmB0@5kSfKhOVP{tl>eYhVOtPsm#ENB<c?>j~@w{{H{`z?p#ov|0%?$RrOk zs{yo%v!0>8zP=tb&HL}ao&5j*_0JxF-SH2!><zq{{J%UX7D4{{upd-&gOkNiP-y%I zPj1OGfc>KZT0Fqm@Spj=J&0fbhn<1J;{X2|2GE#NeLZ+Z-T(jQ|CvF%ZR$Y{r2qe! z>luDL`2V*){x*1j*pL5^rR^XaB)~hx85p`&fySWzzXzw*?f>mTsfs}Xc7g)`e>;Z% z43ITo_MlB>|MyS)|KA)GzX$R^y#M$AKYu%@nFLDm2mUiO{AUEY4Yb_BpFv*czc?th z*YEwm|34$BE%%>!zGdTs^YZcx`@w#)2L+eCAejCC@qcDec!A2b|Dd(M4?r;s5&^XX z{<Hs=|Ihe8`2o_JSV()~KmK(V(DfDQ?GAZ{dIrL4E|9W0Xmbe*>~a)X3mCith?9$( zjRzzGUiJf9*~P{t#>OTtA;~7i#wN`s!zL>SnvPdsV`GC4d4cD9wULhYv9h+Y1fS>Q z<SgLg$^be<*wf1!)a7DeV*?%59~8{S20pBxErQK9GK!5YS^#7*1jjJ5vBk#8$0x9{ zB_<`av8Ax3vVqR;&tl8YVUS}>&&|sRjh7b`vazwnv!xUTf*J-8t!$;yY-NFL<rS3- zpkurkYU}D78rhnVywBF04pQ2}7R1)t*51z6(J8>zCBPQk-NVM#>%t%jl4MH*nGa&6 zF@VTE2Fbww32YPD*w~~eO=e@8GL>Nx+cdU#Hn!<A*k;E2v9ZlcXKR~1hmCFSy!i`) z7qYRjEduFdTf9V$jg4(48{1NbWy@Evu}QB4owdITEEg!rwwi4kW7L|p>)6=X{1};- zGuhI?N9%*^bYWm%1Le`pAgBAWZQ05und%bAwhbi57MR4qz_y)j2S|>sEe(7j>dswh zZRrdQyVI63FtF`mVA#8l!OxGudp}!R+5ymTB^z541KR<%18i*j53;#P9%4Hzd4!Ga zD8sQ728QD&*iI^GpE?aP4}{s+Ku0w&FtD9H$H2yRo`EeTiQz&4NCt}8*uV$yGcd4S z0{LYrXnQOhTQ7*s25}Yx8)%RPEWRJCiS1H)5(9%|7F*lpE1=V6+1Re8gT_OygJr?t zed8vCed{)8F+6D41FZir+r9e_*d9K53^5)=K6x5^_;7RcvuDrQ*j})`eDxZvpN);} z4a0-C@7{m-2;;Xgu*riou)%IPfN-ueFn}7kAnPCkY#=9rxbXHB^h5)G0Y*U~VMbW< zoDtMK2Q5_uV+BQdc_n3ec}Pu+L_&@#011E>HLEc~j=%y5K(Q{^01&|ly7>*%lm|8B z8Lhx~%gD>iJ2*NygDzV2koN?s0@>l^?E|sP7c2r|`^hsh`Ul9%%LfKQ#XwXrhzJRl z2aRLM%SVBD^3h;224oH+BV#Po8H{nTF=U89f;=N*B3Pw-5`=;T3K9uwzku&IfHxdD zxj-k;vV%tpAf*Pc2(Kup@goKzKv-OYmse7XSDF{8aSGSZF2gP>$1V?2$EyG)kqAX3 zHi(I@{|-912r`d`<X$XHNV`S>qg}(maDRsbQoDv3RK0+!c38UxWEiOYX9w@`U^oD3 z=jbye9AKEkzyPXLz)O1ngXWDGKzi8?SpNS9%^!j4L{QBk58A2$sx_4v7#ROEGyMDi z5Yo<3W^iEG0J`e^0i>M+sw(0@{qF~mb`D5As13~YVE#|gI*|>`3{0^3CFTd<_RN3K zf+Kd2#{YlcgV+wt4B)nnI)eka$NlHQ|N1}Rwhd@y@PAeYNSlULA)bNZ1E|Rj_A02< zkq7T#X<%Tmw@Eti@ISKy0|UPH3@AkY|3_}ifZ8|A4FB&lLTlUx0Z_I5pB3bFXgdbp zegTC|J$SMh)Sh_251L;3|DW*y!yix|7Th;@01i!ihI`2jnC%!y`=#ZNK4#mcf`{RM z0|TPn!VGS=Jos-9YqR|S59%5G2V39p9~5Q(q3xC3-$5+_P<;Q<0K51<sCB~*8ns}! z|DU1eJ3rXnNB;i@&xl$4XP1GuUy#}@{GhfDXlGqL!;b~X?U(=m|A6)<GK1PJp!OVk z+Xd8a0k!O4?H2Sl3nRn(f1s%P|KA<7Mo0wIJ^`(?gtbu^7#JUbR)2y9f5Ag0paz*e zIBXxtGcbbMl%RG9^M6q5^*`aZ2k~tTM$r0ZW|WR2XyAhZbmKb<=&))QZjdBs@g#$Q zAOnk#FbfN~d}Uz)jT}PS7Ywj<%-~T$&<Ulwpmm#i44{cLQ%fsrQ^*}$417+GpnD1! z+!)+FK$~M&K<x|`KYtdM0G7a@V3rV;PzG@pmM}h$oe&(($ifm485I@H5)%tLQaX+$ zo+TlXK_H1GIfWsWMV<k40)Ki;218~ROEwGWx*!IIe27*ChA@_bJeI;D(4|2op!uh= z@(LE@AqbYLM3B;I7C)An)Y@8xx_UmA20j-5^hOqzrced}kR(e2$XXC9fdNE%FbL!| zx3IM4v9O4@wX?8vbTYJcb+g2@$1wCr^*Z(`^iP<`QaNcd%ap0prqA&2Wnp3I2I*s& zIcqix3(Fi9mbnb`<}YAjiCW0Q!jh)|mdg`hS;WG>n1O*|2@4BL9|I%f(!9iF49i!n z1lbwNzyR_nD19?9^s%gBU=diIkjJtHB*y}}G@fNGOAbhuB{cz@1=p=lNKIs5*pM)n zfq`Ws1H+~mhQ2-q_suK`3H_kTlZB;{fu*0NKQoSjAyZtSGSg9DOJ@I8hHcv!7<TMT zWH8X(1sXjB8OgvRz8gX}?O|XM-^;)v#K15YECMB1SRj)~3@maWzkuXeSXi1sY!-;K z7+9c<Iu;hl02hlK=sE|1{Vd`+2S7_)Sy&D-EQD_m0EhRX!=S^%S&pzAJ$9Vs#K}_- z<3Qx;GyX@8R8^flI|=N67M9Hn$IqR=aPbmY9}5dhDg%o=NFfVL9)yCB77Prau!6c8 zLP6`tA>V$GmzNI#-KGU@21G*J4N;J00wZH|42%V)W91nc<G?DR^)ov=>^4Src4FHH zyu4D_+XvF@GP3M)pxU1o(n<g`u@fNsA)BM1BqUbQNP^=Vdq&4MM*R53KTvxF(N+NM zn5W<P2DE*^|NlR1eB=Iq^fm(MXbMm}0n~PbwG%*vHF{fNbbMoUe1mS|8-bE+tJtQk z9v#<!R5@&HY=dK51LB+k8Q)+8E!!{wwcHsY%QWQW<)I5T+}!0o!0iS`Mn+FBZ_t<q zBcl&k1jP20XJqvAmzS3h2!x7(DDYTEaELslc@hZ{f{${5n<I?y)=6v}L;y<0%QG@2 zfYr-GhdE%u0;i$vH!|8ULt%Vl&&V0y;DwHFfL7CvsPT;-h&IcBk8kiZFx>w?I=(SF zzCpF|4J_jtETiKZ3@o%B*ML=!(E5>{;~O+-Kgi3=2ZIJUz^#D@==cWwCef&9Xo~?t zf#ywO!74%H3!sg)WV8?HHogI#-(XN+0MCSjhTK8x6Yl^2{{Qp;&7gVnzyJCF{QrMn zDtoq?LZ>`S0%*Ao1H*ZwxsS)7-Tv>9=06}SIv7Cn0HASid(hCl(*OVe{UPJ=piyqb z+5!h27Vu$K;InfX4uF>OF@V+*fOp>f&pp5o8ke^RnFDeNXw;qI-+znq|3RBkAREO& zOA3BKPo)E|zk{Vd@Cs@MCT7s>L7+>rSimR#fMr2@Hw1-119QS40#xLJ#gPaWh>I9l zSU?9sftPN9luEO(ut>7V$g;@ED=;W3v4AE|z`9geR9V#2HCR|!G+|m97+ADG>z^5P zSacz$J{cH-Zeswi-2!QXZe3$xF$VF#n8kzzvZxEB5R!kO(~fLx>}+f(r*E+Fu)&Yr z;KO;`hJYX&8=DZ^5C}t<jg3tN#1sV)Y~YD|D2t6v9LfTvU&zQ93y5N6V+T>VG5Y>F z&?;20r|=NS=?}CPIW#nM)v7QsYxT8rw>MtBv`%ot+Eremq1Qq~Ls@gGLP0y`KnA)t zI9&e++KR-$(vTb)8oK!zB>6HhFouMNuGRvX0h)6IPuMW*ha_D4&`?nyhW`xqpamWb z_6(~)i%tIjpCdm<o`Ii1evUjSJ%Uz&f=;^tEe@UgO7#G%1`9)|C<6n?0U@EF<=+fh zp&G8Cp|+qy*`VnUJWqy^{un@;E<t4gs0?I*9)yE93WtG3j76M<MM4sM3INDga4Z6v zi3js!K<Q4Fg$0!46h)MjRTxwmL?J5!K?^2D8ALP~G+9_!v=|szAgAK!vVd-+1!*=E z5rG^J0OG(F{)!-y8-oZWwP9Q`08tM<YXh1Z(9$0pCl?zx8z>2a<-zMfz$|nEoHCJ? zY=GtAX%dtWK_cM92x73YvBA<LJ<}g(lQXCcTm=Ckp{v1+E1H5TZd!_gi!OwPZe6uX zd)10nToYPXp_hkU7T|r+paQf0@v2p;^f|#L*8l(inO3Y?6>=417U%>h(99+{WWi<P zs#UM}5lIoW?g><?-FLXdaGv2m!yRzB2%3)rt?vSrhe@Iy3|s*m;IzC-i-BR)s#RPo zR;^gIY8AtjRRUeBRxvFA?~$wrEt~{r29PVz(<In_7y+7pgC$QMR#34BmVs=tU=WvJ zm1LD-WtC=-fu3OjmW2|a<19eaiVUEW+7(z?SwV|@l)&c+X^4Xl69Ov~*J2RY)?sC3 z)s=vr)5O5Q3SP*{V906&S;A^6E+K)meifEbL8st>%>WS+&?79M<uXVJt$btyCsAl3 z;y_EE&{K(^vPcv^C~bn$Ba#@nyaXprSh|FzNsuBj5W&XAHki_9LNq9S>e4=a{=Xk^ zM*y5Y1wbbz54H3uF`7PcR^a5;-~a#D*Mn+XaNP~69z#R5an;=n42XK0p#fB-PQY1j zgX--6|Lqw<Lq#3X>uiSq4Dt-%I-7yyIvcbo2C`xUG#diCA`7y?j2XIv9<;duvN(x> zS%g`XnHhAyJgB@u#zN3?NJvN$6rfVf%*>z@D`n&r6d9Blgc0pRVFn>p1~q18W_1Pz zX2=aXTFlxyx}fGM#6Tf<xyZmE1Ul9SBn&!14y}AcY5(D@f5G)FsDuKQYizKB4b%Vx z7igez3uHbBgUyHZT3{s7qH=Z)PDb!rb(kDFjVS*>A%G2oEWwW(9KeT+K+Ysth;)YT ze*+fKniR;sDbUGOptjt9=sAu8paz^c;}6ifWl$U**#952f2x5Ayw(bQmg5h57H}I5 zRMTSJJH-St6y!JoIR)^MR^a_pXnUt7$SFV^2tDQfz(Lyt1_#i2A1t7Y??16004)S& z_$2KBv4@#~ft>wP{R#{fklqZ)QSE4ZrWzRj!}m;q)@y^-ZK3X)0`1m<?41HH8e<1n zpg+L-sF2V2{lUOIp$0U{23ZFMnh#_E?PUV(u>zfS-vDh}+t`7cz@Q`dkj|n04qBY~ z0etcnI|Bp0{a1*+SD@>)<{ku}Vr&or-Gc?{!*PK7YYYtF9Y75nkaG=j?afldwKog0 zFY8AucwZI+zI|Dsbi$C(!2IJs>}+AkI%@p;vOwt@oH{|N1co8|0ih@A!_HI%pD)bE z4_c$dAS5ipDhfIR40OjSNEfJBkO!|(QdCt_QB+q}(9l%YV$fz_*3nf~(9ly>U{F$K zV9;j(A75!?3_iQk+`^K90kpysd|Dl31E9UVg9C%369f2YN>>I32Jm5#phGFayR;a* zeYAZ2{22lm0zoJC28S?&vW11Sv4MACF@UcmWr&MsU<GaZ11*37?ZZk2Et`eyv`S55 z0BzmP$OO3>frGLb7_xI17;@SC7zFbe7)<iP>w61}KzpoYN>d6L%E~JkDyym)7{G@C z*MR$dwSMqTRE$i_f;tQg&{a6l6JNjy3360bYa7_I0)_UDM!!x52AM8~Zoi&h28KS6 zcw@hxBJ%_g3ydd%_Lws;Fif7pz%Z4Wfngd0gWYtn42+n;FcY*(8^WChp%`XEgyzhh z2NIYMA{f{fuq|8!TJ;3x%><JS3=H84&@vKKh_bP<fpa)a5`_krpYYvZ`~r-OpmWMW zXPk<OgHC@yDlkEJmB}ITK}^s<66`bx5TB8eQJs-dLlbl;8icP4reLdn85zw$N26JQ z1T8^?l{JWDWMs4f)3#s|OxS@)dj}8+9b`apIFti*KVo$)J3I0F!_aqwv9pUH8$_l3 zVc_ur7R35n&>51TZq|R$=8gZLGfhBS44~x}!+BhDRv2Sa*v6t57$D^$=&a0uz4D(x zds`s2JnFcVJakM7RFeI#fb3a;T(bmftw6^EKr1%?vojF8W)i$clo?!3K_dr5A+3*u zo?{K#Y|Q{V&72Q%LN$1iAgH?rQjUySpa;aVu!w=i4?x375E>*PE&*CU3A!T?)UT71 zS73o`6=q;yP-0PLQBegcRf8G9z`&vo4hIbu7EMsWrNgQVG7sKtfhI8)7Fgqjfq?~b z{5@z<90LoBA&Zf*35%(jImlcHwy<P1G*nTs0v&R$V`BqvFCqH@l0Ko?5Zt*#8UNzp zWn+VN>%hG@uu4JXrVgn42JX7S`fQ-FEeWtX2mx9~$|eh8BU=q-gYAdpAs7igstm;C z;N)TiZ{q`TP%&s@ALR5zRB==uG3{+o`p4J4p0EToHg*X#GzA_TLv3HngocKa*}ewt zIOYe9i81iESg6|t*zz9$4WWS=2H<v8Jp%)wu`yUX8?>ewe8C8)jRfIbNV$jLY<PYj zG>%Xm54!3011JxHD+lm8^FNS=`UlW{KhVZ0P<;&AZ3T`Wa5IOx?RN%9O)U()0h572 zO_)JQok4?{nOT#8ff+iU%M2OM1vNcE=Uai=gAfNX2!VI!f&>{DU^_KH)wmsqV=rl8 zX$sn{p{8!(;>y5a;pPS%)PU+nqrmA8k~Tpkwc7t6vp_@MY|xB=W-EdVwjVXk64yTf zWeZRqK-G)RBR2gruqc4`<~AUW&xOLr=L}W+v~&ZXIRt^mKQvdZTJ>vk>niZz*MIDF zM3)WPSe+zjlnmE6-Ktfu_TwF+gVq@gM>rT58U&0EI79X-BgX7**-N30*;%bv1sYuZ zKXp~WF6fvY_<n%zpuGcF#_s-rcOo9R5889U&%kit^?Ojpf{H>$6p=UwKy?Uc*DC{P zBgA*m$qN7f-{=2dZ_n`mF9XAO@Q!DA6l0G62?_}_Fo04AXi%D;Rhm_Xl@)q;GF%sg zp#a(w&CkyO9{*EhWn~2&!J-Vh^MZjv6LgNawvMh8ypCXy(r1t|Fl1$AHR6YlKeK{P zC17AMV+A#Ltr(=NZ9wLMx^fUJpcxEw?*&8vv_%25brwW3I667YgZDjwHVisC`O3Ta z*}H=d!}1H33ULpGtzQBqcNm6j`vLJl1M)PUKLJ?-I^hIFgD~6+Fb3-VLa6gA&^sKE zd)36`7nsp78dQIP@((EQfO5rQ=)P=FS<b+4A6I(`x;GnS3aHNw+O-23M+WUO`Tq|z zP51x*d-;D4L3^40|7QR%eq{zHxc}gNO#lCYZj1t5Ne(KvLH8+uGvob-|M?l<`?Vj> zk8Ie$&j8=AeYjncDUN{wzF+(CerZO~v4xQR+L8x0@G~Io*OqNW?A4Y{U<a*bh3?gU zd|x)ffgiG;>G}QG2GB(cf8cwy8{)xtGk|<lufPCZ_w(O6UIBUC&p%KV0~&Au?Op!) z{NVrp^$rXM1okzxgK{3~-X_rQ9)1SMKJD}W|1(1OHlgm(X8ez`zv(|e=*|bwkqDsj z>pf`i&}W7}`#}W?d~Z`e`0fWt8x}E_irRif?9GPM*Z=>6+pVD6y#7P>XM;9Uf%j-b z_d0>25o5p8hknp@AaGk3X}=Q#!ygS$)%^ee_y3?hPM{ss|Nqw?VFmBcwt??&0+nf? zb3Q>K0p6btz5fJ#k2Y)#GiY=I*BT($x@Bx@mO%{&NQ(k#kJJD846r>;^CcS}fHuhf z{|~;~!X9)k<^TWxAA{z7Kpngv|Nn#cUW3L~p!=?!Nol)d+w%lUM4%jm8zZ&vA+2H- z7V!QjP%9t0^$8>nnwJ5!@mWO0SXjg*Bw3_bSRkVcpwR&(7G=;a4)EPihDQ3vpcRx1 z(1Wh*Z9q*97gslacMk?nFK-4P246q_00st@KuOT9r%)D_FqZI$NR}v;XoeUTmRNp} zoe&(y$ifnzkeCSG@5I2ulFE|ClAe(%nZ=Tw!;s68kq0_^g{7d7fu)G0m?a&u%?YBF zp*)tQqLc+ZF2GPz%fJ8{4`6|usDczCO)NPq%^4sqEi54{pncXXpws6;DmuIPS-SaI zLVJ2ySo+)<BteQ;(m`Dc5Gx(L|3;ocvb28!%S09ymY7MCSy({(t)~gFB(ktrPnSuX z5x_FDV-`!>>^UqMp#9dNpiNJUK)P8LFUe(LVOh$;0^aAu!Xmwjg{4%IWi^Yy8U_Z2 zwJa<w0St_c>sT@%`>a`5Slk&H5Z$Bzmdy+-l4<UxEL*_t0PXK$*~(%KayLs`I%ryi zVcYifwhRV_9qG#$7+7{PFziZX2nb;C-OZAoJ_9tn%fiygz%qkn1`Ep`hRF(&jVv=H z_ky-F?LWZ4aPSaIqniF<uz6rJS-?l#F|Zsx#=w$soB<RLCl-NZpqPc_B!tBR-qW=V zynl(M5A0Wnvlv*w<0+u<I|WwCq5wL7O>#C%oAqhXXeJBGnT%Bo3=C(%vLJOV=Rl`2 zv9MfVxp?U^%ayCwAjW~n>o-C#Txf2-dGi+7|12!K87|+xbN3$Tz&XhW4?r8jK+0HH z+89_AA*@me1tHHeFo41gY87@0-2Ox!U%}j8tpHwWBQLM0q%5zZ3R(w@*2;t*HxC+H z0iArO2|j`VNtHg-2zhx%14BkoI~}yW+KLgpz1l%uUfvP9z1maW3#^opk<r`77h;zm zSOmoOmuF-IFFg(d@2Q50hd^o2`eXR&W5@<;(C`l<BN6M5u?G%l{s25bPv-t9X_EI> zqwk^ujgKO?Pe5fShFdupK%<4AHd_FL0BB?oRI{lwFfbG|8~``(KvFUcf($I6y{R>z z3z0y(XP|eM*n{pY`44Wh{{R2K?l5TRN8bQE$o>C6sBr+b6O`Y6{Qtin6eu4+EfwYi z|34gt++p$`G~me!YB4@wV3-f8D3}-+>KGXqSmZ!0f6%!dxc9XF$9Yc)Gq`=pz`_bL z8p+#8ch7<E>;catF@SD76Nds&%NkUCgEfKrn+(d}fp9RNL0p^x*87An@?Zc37y~0S z*tZ(u;+o=G+B)L8diqck#l^)9U`#`C9dH_EFb5q<$_PKLo{<qW(a#7PwP0fdb<jXr zfQONh7knlo=za%~0O)*Ch&w<*3A$h%B8r8?b&NLTx@RigPr|^)#Ky+Rz`zDsJHyKh zx~P(mn}G`q_&`qOVSwJN&B4LJ3%;ZSF%HQK8ZUqxF3rvhzHtn6t33w?NDD|60|#hv z9*h9dpuLD3U<L>1zHW910a69I@d2cZlamvCR1pJ62y(6zCj%$={xwbp4t53xkRk>S z(2Ne)CJqLusSF$(>>y<v><pX?kXaAVk$K=u#K6qJ%nUM-0kob1l&bjnz<0cXoXx|@ z$;r&c#lXeIz{$zX$;rtBy4;PGfti_;lNqED#NYzmSkAz}$-@IOlz|nb5M&@o2{R`H z10N5_jhxINt)Qd?asud#Kac<q4;L3F$ZW8cAQs4lT%e<|z#Pzl0-WIF2hz#R$pEqz zqy*$_24;}MK$9Vy5H6^sU<N4!d6tul3uHPoGbltrGi@N(ad3c?^Ki0(f{>FF6s-)* zJUrk~1i6%t2c(sifs>h&fd}j%9v)6k9v)^+PH?cYGqAIBfKJE*O<;gJS8Qyo%*>!r zV*mvk1IPuSVuOVR9GYwlyu1wHVBzHjS<lAGz{<+N%L^(hI6&LYIXFP(fntk+of9ky zieYw8@PXoygMopU7o>!pgPob3odI+S4kzf4Fg89OkQ6T`Cj$@2r5x;_&}HCc2d|X_ zofyEu4stO&2RkP_J3A;ELCSa-7&w_hJ^}?JGcz;DP3$1gf`WpNkAV;53to@~d^|kR zLp4F6%?eI5ybQd&JiNSo44j;x#KOSJ!vl%|P97cxkV&AJ<l$l9;p730qk#mOd6+?g z!^+Cc080A|AisgK4&-24&?R4>k$*035CCnq2lea)c)^FagNL$&MHobd89=h!V%*{& zS$+wAen}J{B_$;#jU)m({zO4hMqWuqQeIYGK}uOkMMfU3NKPKAlwVO!N?cMBY$h_H z3gf7$sB5UGfYuZ7gT|g27#MVrfFx)n&Oppift#D#h?^Ux$yiYdVm`l#97qc6bPxej zV5(yVmXwi_k_9s)B_$z#=jP@XGq>OaDFS0l1}lVJ)}T386)>;`&s^HsGdSqzI68ra zz=ShcH9NbDs~ecFqN3ui<Dui}<;~9S<Ld`Hsv9Z}R;c3`2<8Uq=mdi%5ZHadTon~( z9UaXO1~4ZmR7WQaa?2)|uL8d+4k8aa>>Fa3jt)ctlnjaj35V&Zs02l`vx6)O3smv- z1<S^$XlrOj>BPpyF+?)N2PLqxgTmJ;&^8e!ACsi%<ro<op3D%a&5*#(?w!KGU=^qm z9}m?Z6Q~)bV{HvS;t`~ufx#Rk4>n&XHa46g6=E*v3cj>JTa6gaq?p7ATTo1<I;MJs zM?^9(fXoG@LY+XBm>8(JDk_@kI$mDk3^uk55OcxyBIH5#g5?=9K=PnTOt4lJTaZ4G ze-c6BpevM8L7oO#siLA1ucBhEVw)MMlcl2rvKMp?G)NkvS4Aa2$B{wBS;vu`J)0rP zSKFLHB~T?WM<Xym$2wR=TPK{IJzE7bks8hbQp1p&m#?A%Hz5L?PP|mYbacQhP|z1J zKqpec@u{Oz$j)BGpc3RA6y>So9p<K@5~QOO%FbRK&TYg{0&|Coimi%@N{pt8ZHy+E zhDyLhz|s&EP#IgRQqD3^FoM$qhyhCH+~xf0Dk>@!AVrlRLZzx&MFkWX5g>IjK?MvT zS|z9kN;81z7*IbAtQ_P65Kd%B0s9x!e`Ba`0I|XHDj@ZZU^BpcO%NZ<venVi0p(E! zhG=$nu!xF^CMfV!R8*kqHNjyBmUGsDh=u7mf~?cg(TN4w0m2N4O(1g|b)cC^$4!NS zoxP};fgw^yhk=2i5Nv=7D9}Oq3ml*zaTTyZoMr6n*@5ivJf+eC3LA(eZUI59;UKeh zbix^Qz-53EzcfETH#fgJcR84bv)kGre13H>-Ok;i*UsI^ugA^6&(GZnVs~|Q_w@Gl z_cKfYO-@gq!Z39T$f{{})9vg;85m}OW~=%6`9VXX5Ic2rbU=+_gPF5ti<fYfGt4oZ z%dIv~kzu|%XaOg~LN!QAV_@h9nFG=er5P9)7J&*mbp)S*rN5e?zrP>U3Y`oUUfd7X z$kGo2)lfmO5QxnRD&@e%8-$RMUm~R-Da)@YsWo${8K|ASj6oC<v<#qi0+C?0Edzs! zEd)YsW9{!}UCzM3P__bODgy(<%E119*8cugfhs{dfto525Pdd*4Df8Cq6uY!hj_vA zDqtog4XA)p2|OLGR#8z|15(4z4!T^Efk9GIa;>9|&N^^>NlLB<$!&l(Vi*!tR5nUV zO2TR&ets!ANy$x{Ap-K{+@MwbAa^EiQQ5lH8KhOk8I)Kc>0U*pnSsF@EUKcSQ^3#+ zN`Iha<gJout`elP0W1Y7-eKxhApQdh6);Rm1gTdEP*GuL_pt@1NJj<+XB8Dtxv>qb z*LFMD_$W`eAjlX<q9&+Z*s+s=A<z~CA>j*&5)diD&#%q^K6}(zXBWsEaGe7&D-ldV z2~gN+LfI;ynv!3Ffnhg4!yXlt7*KJd9S<p7!Ad~oJUAG@JTS2rL~4Ktq+SRpuY&x) zkEI%%X7{h^@9#fQ&A@i>&|%PVZ6Kq;T2xeySc30+J<18GHwBIzKLM42P(d0hU<ggr zCqeFoBm+=Ur(z4LQb6v9IV~Ba4Wt;9M?h{>QLzQ-0ST+91UbUknxN7oP?LcHlp{bP z2Eq_U&@=?n42myJqyUJI09SFg@bZd5Bg}?DM<o#ABozinP|d^uTBV}06Jh|!Oi(I? zCQooX2y~A-$Ue}#I7nUvROsx2l*6F<g@J(qoNsh=98ZBt1BjXkNVx|}G^g3wL3a{E z#8p(BGZ+}$&ZM4YXFmr{^PoUc0ULV0OvN?^#En-`xd8J!NSm{cjw9GS9caA{s&^wI z83Kb+bqYWQj7~hLCz1`)<E#^|qjQmATT@e$N_b5KOdRA8XjrLef+{YMd48a@%)kK3 zQ6Os~7@%1cB-acI5l|x2ISbLJv#X3jr3_*oBvmp5f^u;fDDglr=+K==P+1L&U}$_s zfWlKnMe`D5brS;vgH^cIWxFdnj#ki&$&d<OP?QQy2;h2+dp0Q8K}9i$JDa~9#04?A zVKRmWSGf$iXD{R9=R+t1sp2;h<Ki~tz6NIV8;NnxSp?&t7{CoR3e6_4HjqMobr8u7 z)hofT2<D1`<@wwB!8~M1ehfkB1W*qVhQYz)7zv^Z7(gqs3P9;765Nsihj#=6gN`Eu zs7V5f5S1j~Kowh+9EOa<oWLaCoSY<IP{ACi4Q}PAs4xTuf|3g)GeaCx4o<V&+~pwS zAb4>#^v+5K20JJKIRGLFBN-W(7?`2=VzM)EFn|tD=4Rkw;AH?^@WjC&zyP`c6LkBf zD1#V-I2cMWfOfM;GsrN=GRQFqF>o-*GjK2{Fo3R=11*hJW&mwqQDabN&|uJH05$0~ z88jJm7<3u*81xwo7(hHj1|tSz1``IzCC=sy77XSLmJC)5)(kcbwhVR*_6!aTjtr2M zaIT>9ycj$fKnIn(Gk{088T=Sv%iw|;LKs3B!WhCCA{as$A{n9>q8VZsVnK(IGbAu1 zG9)pWGbA&lFr+e=Go&%3Gh{GiGGsAiGvqLsGiWm8GJpoS3m9Y>3K@#PE(5s^85c8X zGL$fsGL$iBGL$oDGE^{BGE_0hGRQJiGbCWuQNvIR7O!If?M!N9XkyT0Xl7_(Xk};v zi?=gqGITI#GITO@F?2KZF!VBLGW0R@Gng|>V9;ck$S{dvGQ$)Ga|UyUsSM2w(-@{R z%wU+wpvf?cL6c!N!yJaWV7Yk=^BIJYpg6+<a14NAVIh>Yh+#3q5{9J=iy4+NEN583 zu##aF!)k^#3~L$88P+kZXV}27kzo^q9K&X?i69$M@fL=yIApdl$T4gO%k5y;38r^3 z>;}^y6ZSBeGwfy9#~{nFpWy(*L8xmEF$ggnX3%6f!f=$~7{f6JafagzCm2q`b(u4q zVmJ*B(=%YQh~X^5IdF)bXSl#%&TtXThQv)LSQms1AweP#mL|g`u<FYUR~W7`a4=kB zxDM8NgW)E_Ed~yT+YEOY?lPD&++(=U@PI*+;UU8#hGvGx3{M!07@jgbV|dPB#qfgR zCBrKQO@`MDZy4S(XfnKGc+c>G;UmK*hR+ONz@e_m@D*$-#LjOF-x+>D`9Hz*F9vgl z-wb~k{(^b`82*E421bxC7#TGgKsTGQFoJHNVrPVy0lHS0i2=0ah=;L@ftQhwk)Kh3 zQIHXoPC+U_VE|%-@(L)Fg&0932tQ}gWDsT)Vbo-}$RNrn#wgCH$soaK&LGPm$tcCB z$so?y3Jou5Mok78xIRsWqYRx4OmMy|qa0%=!%>DVuxfe6P6m)KKz1sC)hIG5F)A}Y zXL!zF&XB>N!U#&koeZjs|G?@&>NFYD!1k&$3NdIf&S21F)MC_ToWY>OsLQCwID<i- z(SXsAaR$Q+1|vpu24hAOu(^;sVJ#Rf8QmGI7_Gr#Ab-KICWAhM4U(uPgDs;SI9&7@ z?7_4H*oTgcPDrX?x<RxogAju=ikv2c3pCs{p}vH;Uz6cH11Pj%7!g_^`SVbpg3JW* zH5puyOa+<Q#oz`OxyazosL9~L=*j5CSOwJuGSi#UhtU_T!kobmN<&P8n5W6$<cM zh$Iul7|a*~=ZC^s<_uws;fxWCk&ID{(Tp*Sv5awy@r((KiHu2%Aem&wNX8V#RK_&M zSjKcl(3M|VjM<DijQbgK8S@zP84DQ2844N28HyN-8B4&Xg4_qgrHtkbWsK#F6^x); zzN*3U<_tBAwT!ilbx<C}-Su!DOfQH&$^i0@1xi?g(j`a^6GK8m9BvZC<Oaq@Fof{T z!L<*h4g!%)jG*}m(3m%PsRwKhRZtXmc@XI8ASGo*MHNL=MMX7r4Mj~YMMXheKpU3? zTwDjl(iMcP)&;Sl*hG;K5HvLtgk4KyF9?~fRa8`TbN2wPZ1MF|^aq<DC@2^Z7zAd5 z36S~0U>1T96bwOdpe#Yb&@e?s#qbCyA4Elhh^S~q(7i>9iU}Yd94CTVNs5ZeAVvy^ z0ArASf`X|K3PGl&BiKl+46q&$At;y$q7<{T!4#Z;#1AVgD;qm2D=d;Y1wbRhf}Egb z^x&ae5Fsii&dDhOk{C)@l0?VCT3s+2JT1V?#LNtx_yLX8gT~=O6R42sT?om-!ob47 z!UCE;0L{gMlz^r^Ad<`=84w1m0?D&6FtD((fKCSo83Y<TXJKPwU}0lp0gE$$jztD> zKoh`hY#?oHpsg_=S<p-hXmLCUv#_u*uz_?zG_!&DU{k>+GO#ePfnCMM!UAgNf~IId z!NI};QV24Z8Db^K2(U}p7#P?<vLJb|&q2WeQV6mPG~mv_0=Axo0XA040h&Z$VBln9 z0Xd(A4df-TVz3?HJ?kux)oI{)0g!oU7&`t9wvHL30ld3|osEN;6XXjvHZE>1W@ZK+ zW@a`vUOr}Kb~b*HS^+jeHX&gS5oWMBGc#zX6<C~$jg6C&gPEC=8MM$$6ePwhCN9Ay zDaFag%q#%fBL)^@mXVN^lV@gUW>$dk#TD6wI6wwUa4E?%GcyZ=6e~mI#8gz(<iVTF zn3)A6)rC2jxxo?|np$cc65?v|s$i}%yS9$5o<18d$eFzQY+U@z%xoOY%pA<j1|q7Q zY;2-z!XR&fea6oWF`UzoO+k@al|z7&pP5-%ks0JA5hEibBV#Zy0RsV$r%l<+bTuW| z*qA|T*v&zdsGcOVrmh4tGrI*dvn3m^4kw$W9vgV?EH9fCGc&WHsHCP9GqW`_Gqa7E zDF?HdnXMVft-88wY}(AspgrS;Z2Tgs{2U;cvN1EW*fBFR*fTRTvqP4@39vCUbA!}z zC@ZrwJ2)~kJ3;j8>Vn)Pz{UXzXd@#dXLfBfNsyHw*D*75iaIJXgM!zEnVFeiScqSM znc2aN*_D~uK}tp5%^eI76o|#l%xvu83Gy$<2GDYJW@Z6iW=?KqHfCmCb}x`9GaDNy zc=bdTS$RQxb~Xu6f&fkRIB2tpS}}t|O3%!YnOTpGmswIy&Pi2(jo*UJ+t!eonOU1d z*OZx=nM03F)Rvi<U5d?=nOWPEjoC*R4t$xJnYlqTU(9|SHvS9&Y`ma^!z{qT#>>ve z4Dxee5Ey_HGWdQxCT2!P$Zaj?x43{xPF7Y{bYUhobP;w|4o)T}CN6F!CLUHMUa&MD zKNGV66RRMYBgD(Z#3RfkBFe-h1{PpqViISSU}6F-pJ$Q<U&ANEq{zgiq%6Rs0-{x! zWI*C-^6D~78ca-@S_(`|+DuG3x_V%7eFF%sX{aE>WW>b8WNZSaOrdx6nOlIYv1GDh zVq&tkv1PTh2l1I49GRSWon2hP940p~0L7(;CkTK=9l#`n@M7{}l3|izVq)_4@nvH2 zgNQOQG5JGT0fA5kgbHF}VhRoc-H;as5(sBviil+55sqStW@2KBiDhDniwB8=aYAAe zn1&LptgOi?OsPyvOpr)nVr7MdBUGM=DJ>n!OoPy%h{=F3Ga(d&Ov_@@0khJxnR394 zTqdTxd?wa(CZ+<W!dSN=2_~lE5~flnCZ@9T3MQsXCMKpT0jBC2uwpn-%LKa6qM@mQ zZd5}f%(iCt7EjMs^fd!aZE(kd8LUhwX_FZ&2qGZ<ZwE1vFf`U-BJkktfQfW6F?GS% zOiX%A-Aqh9y-ZALQA|vIAn|^t2@{!^m?lLrF-@KV5@4D-4NQTEwCPN-OiXe!6lQ|d zfJBh-tl7vsrnEV{bHM`hAg-9d0L*h(2&QllAai>$<Q6SnlGcwVx^x+m!15I<nO4F1 zUT_u@6Duq0YF1Xe(ltEnNTN_ChD~eNLCXUe#Rys-0-pPYwY@;yNk&H4{xwEPqzIIf zW@Ln3F^eQh3{#d7($EI4Q3Nd!S5w!(OKECB0Upz}85wnSLDykG)EI&(@Uj<k3-H!B zumGF@Z<K><l7oxkXCUpRLnySfhq9p?>!3VGD9z{uV?byvXQ)S@6hst3g4c6{B7ljB zor!~!3mS7YqquRH46aLX60DH6Ab7700~2`U4?NPz!wVf$1Pw8QbPIr5iQqfnIT=6{ z4>xE51_PMn0WrB5xWOx9xWO8Exk3BbxWOEdFo*@(dCveEOa|>91TFCZSB0RJB%pc` zv`7Nfh6F7E0WBB-DFR!?&CSlw&%h6I5IZ->9MDP&26lFCZqQN<cJL@L$VChcoZKJ< zpgTyo85nrM?%`$!aoIs@K0r2uv@r1VLl*IXT+IM-A$XAwH&_iwB|A4r0JIDQv|j-t z%)rji&(03<AtwVjgaxt=WHUEt=?7?G3LgUl52%sI$;k=6QHz%Wv<`-o6SANQ?04{@ z8}RZbE-uhY4Q_6bDIjY>EgDdWfEL*>Gcfal7Sjj_2!K|!aD$8iFAm}1WdIw-4qm*% z4xT{*y9yL^kkwDnhBJ5p8w&#~8yh<V2PY>d7dICJ0}n4BC?q+#1Ox?yIE5KRL`6Bp z1jQL7xWptmIVGh;r8&VPyP~4<3i67I;L8ftz*iKCYU}87a_Widiy9b8gSRnrYH)Cx znKKx1iV9dTSc-~TSy@{#*oca9n%P>3n%P-#a2gwPigAhv3fPL;+dDAWFh~k8$Z>Ev zia7}wYS`FYJ2P;IFxW}Eh;hhU3o|gdx^Zv`iU~N0ISLE7YPcF&3)_ft2<V9#GAM9z zFz`AtaOoL3FgOZwx^i&JxZ8+&xH344YA}ducrwU)iCSATFnBw=a$1RcF}Pd18rsWw zxr+K&Gstjpa>;QDn0c8o_%bkviZU=b+xYo2aB4BgiRyBSaxw&Pib{KOXo%{Xngs?0 zaf%3vaSAd7hcGaNhJ`b5M?^A2s79%(dNJ@iatMe?3UD%TfY#19YDqIhgSyx83Gwj> zMhv2fNl99qjxwCqHr8IQg2^0Q8rF`S3@NFKib{%U>8Tl!+z||!tXZ7d3^}<B40-YS zg2Dw1T#~}ZTmn`KoXHw|qJ>(#y27qrMZ#vTqLN<1aSQ?qstne0#i}Ku8gj1IyiOcq zk{nzdl3bj23|>ygK@35lm@X|V;{)YSOw0&6o(eQV!p^}4I(r|a0(>bX=rTwU2KC%9 zF+aNigP;%tgD?sJ?HOWcmw+pmWS5eLbC4Kp?Ck7(G9Z#2bn&mSFz6sPMMWiL1{GCd zHLyZ;VPO^xO)wKefR^mC=rQOUu(PuZ3kw@UL_wsHurULR2|GKxx~UmEyAfy~5vVY+ z0(k|5+1WuW{TUdng@uJ}KpNQDZH0yH*zH-^*c}|5*x4aBCwbblcrkeUfcT)b@!;D) z7`!DV*x8*!UFE~V8SGgYWW2%ZLtWhhB0TL`WFTAWquAr(6A~GMSr{0SM4Z{#o!OI9 z5>h!qr%!l;`Dv~x390G!EEx=7K0AA6N|r~qJqtrlE<0ZiJG(?4Uw(mbFiRoGxeN>p zMa3Q^EJ#bEi^>?vA^dO#h6<L_$|`nH;0ekJr&J5e2@4B@*VaQdgFIToQW{ZLU*5pb zD9_Ra^0H(zh-7a8lOQ6RFB(L#gGmqv7S~__kW?$kJW!DX;y`d)8^~G+8w=^k4ssSJ zTqcS=oL!`yft?+MI~dqSIzdJ<Ffeop3k!GmfPK`<QVLfOE~(ks+52E3ZJ>DM6Ydu7 z28AtDGn4}BW?*OU?}i9WC=nJGc4H|8i3>w7eq~^o2sUbx@Wjb1r3}{H3{%+I+3lu+ zQwzJ1a5u<&_DRCR3@p>YVvth^zy?ki7M?M)0<0a9;)I2T%e&aw+uA^4ZEftc*xA{^ zY<6~$*>eQvPG%RJBgin1Vg7=J42!_CD$8WD85p3651dn$GpxwTTPd`Pef65Ppo)f_ zogF;83APXqu?}P<JG&QD4<xD}Bt!;G3a;M(_760!K~uOM?ChXrgnVEC%Bo;XvcV*n z08i#NZj1$UAjGE42(V=h$YkVL=L5R|ROK=-Fl+{Gr-A5V0BhO`CLsh^9OiqlE>L(0 zva_>ngYx>PZ)ay`Hv(A!!4(V)4B6~C5DtWN0*5l#ERfueo$TzOl`jy<$qbO<Ws-0W z152r}u=FlaEE<)wv-d;dRuGck)rFU5vy}EQK+~FC3B%6apmf5p2V!vjUeNt+?CSd% z_V3@%zy}H07I5l=SOg~34?y%!W?*OMI|vq<%*Sx>kmBJZM~^9j`B1`(fq?;{04mT1 z^)Wj;%y(dm*(VDNA7>C2KEc3n60{!!{qzL((`T3&&a$yX%I0&Ba6JzyJack#E-)}K zTx3sU2VL8HiM@>-oOYr1!6;}v!EA?=gJ7#6L=QVVyC6hyI0I-Q4~Pv`4bH_z#o*9q zXJ?<xz7nhh#0Q5Qm<Lt~%eCyFrAHwBpftzM-Uc48YjXyPNL~i>+4(@}6J8OqvtMC= zh=Wo##6Xa0c6Ro45FP`=)vh!)cJ|5sAU@QsU{8Pw8qj@!5cQy#0%1-Da3%pMhGK|3 zBA-F{Aax)XC%8NSWp7Y{5zNBi0V*HZ*@drzeHPAA3gY(*-(Ugr*>6JCs|(+{%>ur4 z5M0|$5{BBwz`(bPU6MT<WHx)ha4}1161=`hTywXk2IPN`b;9=;AfX6RSuVVsfdPEU z5tz>|EPNkXUR@Z<16wmexErDu%mX(UA-Y&f8Q?h^A_8^^C^5p6LhXmjLh>@01ByG4 zA}|LYLl9qs#9%@oHYhYfX%NE|Z7~0pFid7(DLu%}z6Ml(fXszqsD5@q20Ku3R00Zd zm>h@(?L`F9Fbq=<ra9RK*~8f%K7vW2(Gwm&dHM`g8$3soU|?Xl4HW{L118{kP2|N( z_E%|OF?M#z*CHT^H?woV2^TC1D&(En!F6W~<b)FuaHa!gJa&*WkmOs?KspNx$Wy|? zC7`B;uy9Qe!#f5BYdJ*nfQP>z11QBYFo5iR&(`u`#mD!v-nX;_u}iQ|{v-j;N>CSr zD0X&sFBk^#A^7u`FJHbgFfc4b0ucLgkq~_-BxtCLnT3@JX^RzTn;~L`jEM=fJw=&` zi3!|$hiskFH!uW^)q(dIT3CXB6^KA!@Wv@iOH0s1mZgg;LV`pVyu0eg!~~lP18why zi<8C(CeC`$_#fyt%qVc<-4Zm;mz-kB#FT0YzF0OpCpRx2Vp2h2QE^FWnWbg9WkqFG zwWVc^rKM#pL=HqkomW@S)BxgQV5mG(BZdsB07zXEst{HlNL_PFYa2wey`vMtz#zN2 zVM0B<eU_G%{h(p7Nt365M5j&zk=QZQbcFbfnIQJ8*&q@Zo?|(89xgeMIC%aKoOM85 zP!`B26bm0ngr7wKay~PNGgPrKi4I4}n~?2SjG!zEidWFUGU&Q_Ch+i)va+V8rY2|# zf)O-00vZ!wW`yj{VFb+<f^G)|$$})z!2s+OO-&Ziei7&ZAOiy<E9~-H&}<iY_y%0) zF|xBWf*I_Lj0|9=rlux4JE%tnn#TieApvzPL7S*Rg9@PW7$*o|08dT?F@R`>`1tsE z28MWWTL4s5fyXp7HMtlWxxiC+;9(%p@=Xwf0W{VE8A<{T;ebYvKqEQe%|GCcJK*g* zpshfltt~tt9$1(MGzbD38RFpqZ~6gk`~mHc0dp9@+rdB^bifR7OPCKl3<GHygF+l6 z$iN3$X$u}I0SzR98K8yGpi>1HKtnuW5di@K0R{xYzyRSgfElovZw3&9fq_8~)Kmdu zK?WfxfG|Lo3Ne7$Af~V|xO)I8wi!S?25_ALA%qz~kb!|g1P(x&7(wF{qTtaA5D8+4 ziZY0TFat;c%n}71LnkIC#vleFKu6MviHU*IkC+&PxVX5u1Q;+#Fi0>kNJvO9g4R__ zfdB)T0FewJ0(AJfG#D^2$jHdZ$btX^1A`m{FfuaA$$<_71`l&GXla2Kx@&^~BO`e2 zFnD&0K^J<DF$05w0wbdWqXG;tC@6r;Wnj>Q00z*RA^KndJ{6C_0t^^HH0YK_3o|n_ zGX@4TGd~Dm@MB=`^Yde1(A3m~tYu_iU~qPJc8-mWjdgKxabaL!fRnDSuCAJznv9x^ zAd-=hQ4@?9G&LC*n3zC$hM5IIfq6{OnKf`O0}Z=@a+IbfBO_?=95j2(2+EY8ZTt)j z44U9P%M21?GBq_dWncgWHdv4mRNh%yS~4)0o10r%S%JnUzyx?fCukwF3IhY93J8Kk zK)G8|k^#O_LJ{OSMn*+NMMfn!U}R8+0R{%p;&@3(NsthT2~x(O3IYraAetdKI5-$I zT^14&62b)9hReXfWNmG2?da&}2%1522c;?o5DPSV$iUzM28`f|H3kL-Pe}#_&|$5i zp`i?np`jqfp`qg9;takJz~Jl4z~B!7AW8o)2ml>_&JY#`5@L{Kf>_K7T28<QnyqJG zV*|Aj*+In&13SB>CJPH_(3c$?a3Ftz4i=M?G%zqQU;q(d3d92KYhz$w)MQ}r0s#gF z25%5xU|>*FQ&Uri0EP+(U@!y&216S#U|`VD(9l54aWgPvWo2chr>CbgFfgR&K>$M@ z=%7Q;Y_JIfg9!tO!C(S9DjmcP2Lp!iva+(WQV0Nfu(SaJ7#cuZoEaDz92^`#o0}bc zzyPF#!6zvxDJd~AF_D2GG11o6)|SE6mVp7p0MQ^80|P@uL_|bMNl8fo1b{Ra1b_hp z14BSSKqMG2FhoHBLnRn6Fn}2h3=GjAz`(%J1OZ?cXskRA3_!-k#W652#KnLC14AkZ zFo0+Vh6E5`NJz+s0EQGWU|`4v0}#c)&<FtxjbIjtU|^_*0EX&n28QhH>}&|ZfM9^E zWdQY2va@TzfPtY33>X-SKmauIRm8xM1_lhE^F!+DKqrva)z#HP0LX;eau8r(V6X=R zFbz8VlpzoT!1p)T*Vi+IfOeP{g8@S^=muVf;^GW2U|`4r0}#!S1L7ou00RR<ax!S~ zHAAwJA_GGv1c0}2WoCjCB(%uT)C6x^2hE3oO92KD$<W-~3>r>p1~CdjfPn!*f&w1Y zONLDtFo0$aK&hO8U6GLyv^F2SuLEQp$b;B0Xcac-v|11bSMs3sG7Jn1AU*^$!`Yy1 zKuip*kh&ad7v{Qc&?;ci>TL)OTEi{GAOx;yK(!5MJvWF3t?3qHK!)NBFfj=RQSdr% zDF#%?!@vU;kp|N;3?RNNgB*h_gFJXuv;u=7m<Fwo28k$v-KY!}Qvs7884#9ZPz6IZ zFbQF+LRXDLRtRc=P3K|IMuIvFJPf)hP!Gxjtt~fTKtMyNZXO0%1|u-r7;3)>D93|W zpTk&YU=i3lbW7-ZP0+eSD+Y7us&o*~lEIR}j)8~4o`HwKfx(=?21z9kgCo+Kbyo%m zbYp<6Uw4D9X9ulh2dVX9;9>BFs`3E4&j)mJ3xgknAA>I(`ZM@4_%rx01b{_Aa-id1 zL7~pW;L8vM77bwFVF+g6VF-b)g%4v0g~M=$P=;`Z5b)}F&^q}rh6n~8hDZh;hA8lQ zd6-I&iIA1^(a^Q?aSWi<^RW!k49N`14Dk%n3?OzKLp(zgLn7F{JWx51d@KVGc$Iw$ zLmERGLkb+i*y#+Z3=p|=1|IM#e31GqusV?4ARXD@b@{mrxu9$d#SnHjLmrq9lFMV@ zVaR9TVJKk8VJHNPf_NY^iWrI*iWy26N*IbUU@2U#l!1q#42+8z%Ax8iz_Or~{nZR0 zSi?{Wra|m#hFUPYj-i^Nh5@vuznY;DEW!g-Rmso<4iC^O{}u*R2wL?I5#eEIXXpU0 z`sV?!`-iOk?_=m;=w|3<=x3O~(9O`v&<&;`<U|GpoCIf2W<an(@>3WlGE8Nd#=yfc z9SUbKz~D><9)?+9-fV^$40E7xE&~FB_7TiwSO6N@24ff-qGADD<pPF<Q1&7)J)ePx zVKE#of$lU|$^e4P7?2@o=fMgF7zFJ>0I^py@Gz_a!?g@+7}kNqZ#@GHZUFN)GQc1x zc6k^!f#GI`&0tv`hAk*?E10*Hfrnum!*&MHzJwhNI~aB`@GyXA7zUXHV((<&VOYwr z8*I`Z24uJw%Hv_!2W9PNK!yhx_A?v=>pR4N40*u3!we7_w#xyu(*c4(yB$t2AmB+T z`xKZy&A<buPcWPYmzeAfXBf_+KprUX9K(4iyubj1AYWZzxQGNVLD`U95LX#M5Tp;J z@*32J>kRA+pj{CR47V6=Gw?7lfp<ngY8)o;{)jsa51=*ALnzI}@Q8sOtQxdm=rIEa z0|;|4JYjeO-RA+~^F!HO3>*wk8MqigeAp=CGX@3*$ROSeaAo)sOfoRMg4zJua{+QC zsL%HnG{y#9>k3uIzyems3Oag$0i+A0?iDyb-ZMZT1H%WX;*U_80laqu!~xm-5v&?w z2T1p424o1?-vO#Gg&24kzA^|gd_#gf;GG^Iz26x?c?HA*(I5<Q1rNhdB=`%=2B`vx z{|4I(atEka%fRpl%>2vnlL53t1jP9XB0${=6pYv{!p4XS*%?_Gc^E)@M>rW#A!sKF zEXRXr9<V$wBgn@P_kcv_Fz`W5<OkCr>jfB*p&+9GqYxuVhcF`qiZCKW9tKfH1O%yj z2aX{~I(i9bi!p*kK=M2c;$XG}BQgZJ3lvX043dlp2vRS_2!qm$JPa~mo-88|xXw0V zkYkibfeMU@a0t?^#0c@HGN|)~gjK<8HAZztNV%y2>N7&I7TCYwUL>P7SPVq!FhZa% zSQcb94}%_~4x>IC8h~XMGJr};2nMMF`5V%LGX%>+)Ej|y88PxO%mJqgFwJNTbuCB? zl8%0ZQxGWSFfo{b^_wz^G4Oy}XVMH(3}%exj24WbR+=SP7S!GWwXZ-sX+SgtgWLgO zSu#Res5XqRK#>i`AQniUEh9t?h_qw;1eOC4_TaPuNqG*8j*N2{5LlTJlnX%^WXC6l zj|@(XAm|JxK`bXm7dYF6(G|*e1Jf>yJPfZGz|&g{3{V=<YISFHhx!NP0tN;TMo$#z z#pn%%K2UWxAPO0L8DS9QA|3`mMg;U{<Y5S4M8H5s9)=)BWC+@A6T%3AJPZ&z6l_Bn zBLs#sLSO_VGK>WCA{luYq8Jen<br5M7zFV^J9T0iAutYXZY*Owl#T_{kUcyJj0l(v z-N}>22!ZKfI)f1cGZ~R#79$TsHdrKw5dtCmeR3EJP+%cr5fTLL0V-vLz%oVz<N@yj zg786mfp{1y7%Ld77^@gTGz{}FR5R9qML<3TiPkdmFw`+ZU_B!QHZa0qBV!Yo-3az? zGb0SPFt#!_Gq%BDJDAnM2!Wl9JPaUK7b66AGx9L>F!qAY=wa+)>_dY6j64hz7$I;X zSOpIQh@Qka0h-1qGfqK)QyHg0`5-^=FidBh0fjT6`e!jB;A}=71`ux!BM-w|Mi`t2 zWkF1s&j^DHz$PqYM23qPtH7;DP#Y7{eq785flHt?gtwG&8RK%s6^ttxmw_RKUd716 zu$mD8*FgDe8F?7iF|KD^$GCxU1LHb4+{n0&aU<h;#!X-mklZH5&5T<Zc^E)yw=%9{ z+ziGbmv3W4Kp1~JBM-w4Mi|@)XYF9z$+!y+cZ2;2;_qSPVc5%vfFN@~ynT#34Eq@o z@BrgMD36EX5F-R02Agt(@hB2J#>m5PoDl&}!1*T`k29VE>o^W3c^FPJBEvI`XW{T1 zSOuuII?u?%aDfpSUIg<lF<xf8f&#BHUSYfn)_08&2Cu{UHyE!mf-p>mhv6pUEjYXl zRtMqV0ke4+?lRtkvhFh?-~&b;1_<vVSo9&3|A_HEBcxUY)sL?jAZ7AnMo=pXRNOsg zWM{a>@PrY(Mv;Mm;VD$+8K_mj@EBaPfYgE7T+bO_Fn)rzy<UP#cZg{W3@;fWCWBf> zAPf;<V0gs{YDqyvAf|xYR<9Y6At+{f7~U|xW_$}3f5!-e@1d*@U?0med<5scPmG@# zL9EG)vJ77sc^JMj8Z&%h{KyCr|HAkMOoQY=_#@*tu%7Q=5>zkoFnnbE$Oz&?Tm<qd z$dwEXKftzt!U^Pl1_qD}$aOFlNIirPVtoYFt6&{J8GeCjP#p`B{|)9r%mJwcjn9BE zhz*)~VPRke@0ka0%K~kJLcyR-OrXIa@G)l4b}*<$gYB&ct&-&iuTA9x9Rr4h`H|QN zImjM-5D6N$5e1LXh%$&X!1m!ocH&Dj$T3L6p*)xc8jDe60PV?#$S5;_AZT|!XtV{i zLm#n6Uxxt#r5SX=EIkHk27Nd*U@&CRN7=J)LDYVIe7p6r?bV0u)K5d%rw`ks589y* z>fM9(;+I4B;e*P~8U|1g9prk%j{9b?A0Yj1(9Zi7hBk(F@E-gYh7NEq9HhRB0TY7y z;*cJBFGC+1>}P<$2@Dg#tO;NevR@w}GZ`$>&oGstpJ5t9KX~sxqAxy+0Tn`a^Fw;t zpnf>0#R%K8581H~>M4UTsMieJyT6tJ0@s1*^>8yGyZJ%AW6-{RNFNx|3kL1Y2br=5 zyeoen!#;-n4Ew-4^A9o{VmJ&AD^PeGVSvD+VEP!taVUiJSx+*Y0>jhro+(HV2!nd0 zpx!4aKU@Om1xT+G#Aji+2F(N48Mqm4fEHVW`<&bix4>*}hTGsB`P>Y58Sa5H7!-r{ z=7V<TGcY`4&;gqOI?9=W;R$$;J_Fbm)N%=`5hn%fZR#+*V1S%I@`?c!qH2B3z`*c^ zfq?<k2L<i^*J1bw?nOdKkl&Ddji7S@AZh0-0|<gn1n6h@0V-p_7?hu3=Kq3*IOu$U z-wZJL7tVr-An1P#I-rYe8A0RJkiH-bBM3sy4`62mK~NtM)aT=2gr!?vMg|5hMn3Rx zHa{afV;=)Z1mcQ51_7`*ga^_u2sQ=8VrLWr%R%h_2T{VnfaE@qdvq8ey+H&i0_`7y z&L9v2pF<$dhz=zfbx=w<L{Co&Y^O9MC}bg6hLME<bS42Pjp;DRgXR<&A!iUMGlF_^ zs*Df_>ci<XK>BZ>^9HmTbr|~?Kqn69F@hlI)PXJrLq;Po1f4oy$_UE&W{eOB>OWaB zg8EA~j6cCC3v|8!EZ@#xuw=Akv}bexhpQuFAA=K^<;)0i7lMR@7ld?SgwPPu6|B~c z(VbBg+>e2j$1u{15meg3c<8h@qYk(<2Z{SIf=FMmX?~0x3`>7|jd7!U-O1Ia@$ zs2>Bm34@6NRCk3kvN5tVhJn)n=BWYkU=w2)ks+uT0*d-1Mo5jt&5+FapCJXT5+n{f zSpamd0H_a=$(Y6XpCKE3z5t}R0Xkm*)WawSpDs`e)&=QBfXH%25Cruns=zXkFsx<- z^&&tg41mrU&|!e|8qmoG#(G9j|3QbLnX!ct)MIF4YzOxcIvKkdyP@WR`UQQA{frYB zCqhMZ7$z}JVg$ANCo@iEoXj|xaT+72jX#rd7UOKjIgE3_ZFi7M<}uD=oX@xb+FD=4 zxR`MXBcc@!QUSrM7$I;q)P^;TAh;Gxu3=mU6@{pkW>^nyk%L;^8yPn-ZUDEww=ja> zR>p0N+ZjPE>7C%#^lrvIjC&aOGVX)I{fzq<LG9#2jE5NyF&=@NdzA4Q<8j6lV0e=8 z6ys?|P+RvL<9Wslj29U}E!oSAAP8!|f?NS<qh1HMSV8=oj1YK>@iy4ZJB%O*YM+8+ zbr|k5K41j3Mj<>1`G^q)AA|LPM4vE%;8VtD81Ok<4M-ib{0qjHaMiD%s*qciF!md$ z97z9LM(pq%R1KmmL1w*Y`~YrSf=qzn&x~IfzcPM<!tabf7=JSUV*Ca*2d3&bBLw~d z(|;NNG5$tT@gIe!gFIme-X+S+#KOdnqz0605SSG_OV0)-85r1^IG8w@7#O&qd~PNk zX#L2|z{3P_5kfC$%=A5i%K$o*9mM5j;$z}x0w2Q-TB5=v#KgcL%p}4j3N;l{qk&W+ zV=<_#I1?(AV3K5#g4zvofix3#2-@)rl9ywWW{_u6U{YjKVp3*OVNzvMV^U{QgPPz4 zE~!9Q+aT=bXM6}9VPRlkWzt{*)%Flsh&oVB4Ki7WL6eDrK@0587Yy7?@4+=FL?w*W z2Ft?y4ikmYI!qc&{9yYbd=QDu*9E%~VkZNG9+MJStsWBtgFaXW;szZCP^`e%It&n9 zIt)5cHIT9%L>hoaK&;;khD;C|M55LTAQ2<5N{E@i8C;N3iw=V^6QrF2axDmhdJ(uW zs#=hG`2IuCbram6bAJT{p_@NJ`?_W2<Q2fzR)Ut1sH&+WT`*y6!o*|>J|*AU+S<kz zw2;Kw+S<+z#IOe~`-ET1<Ll>d4O&$~00&@^hwgL_3JMAi2?3v<4_T6#09!ajfKCLh zwLr!0?#U^sY3b=1nOWd<oCSqR;3b^pNfi~9NmbP~wV=(?P0dM3ElEj9t!?ccon75Y zNj*uuef<-Xk|ri4B~3z@I2i(_Or1OpEI1tsW`KDJV&=@5GiN0wCC#2ad-j~U^X5Zj z7A#x@VPKGpmq3K9mo8hrB57sPs?}?(t=C$wTMq&oAaWpb<0cRZ!JD@vC2ifdeaFru zP#o;qZM|pjzWoOd9)ie1NR%)>jKXtwcR%8O6kQ0Je=I2p#61o%3QV3zI(Z7rfD+&| zg-k&2ZvY*h4p~mf&dJ3MTEfT6$Hxag7hhO}Pn1s##1jV*AS@xt$0sGtCnGBd)q_IG zvnwdFD}mJUDTB#Tf@UG0LNk*g1~W21AZY#rgh68`APi~UgGNzU89)%cUW|baIx7j9 zSA>*upfmSDn2P}dVeL@R7!3%6E&~8zP~QrKLDw0AumA%D3Nk<-WG)Y}{soqVMHnCu zG^PT=u$3^N)1^Qdv~UuHB^e+PG{*wMkoGlb+(m`~1VN)xAS}lKf%4$1%jFpqpiq$k z0+kpb5Htz{!YT|92pWw6VKoK_RA+!d4F(8=tcC%drKrUKg0S^4kXi$DvLa+fj2;67 z!p3c2Yher-Akc^b0*x6U5VrcolmP<G7$6X|(glPq7$6X|>IH<Y7$DG^0Rn9pAkdZp z0__+e(4GMT9T*@Gv@QmOofsg{nE?V_7$6X`QU<h6#*G04-5DUzg8>3P86eP$0Rp`l zAP}@(283a&Wk9QAK-ixF0s|NzFpvQPgBTz%m;nMo>t#R~wgx7Q0RqDrATWXf0wWn9 zFp2>JqZuGDh5-U&86Yr@0RrP0ATWUe0uvb^Fo^*ILF;8e7_{aEgi{$H5VUdzgwq)y zFoOXCGZ`Q-iva>b>tsMU2fX?vhXJ%I288n%ATXZ+0t*-*u#f=)LF;2c7`8eFv{D9y zOBo=ri~$1686dEN0Rk%-Ah3!70zsp0AY8)$fwc?}SjPZ?^$ZZ$zyN`bpp}FSpp!70 z7(lR@0Rj=@bFB;@*v0^X?F<kI8mR-}P6h}B_1QrfG|mFTJq!@o%K(9}aSzB?9B4(& z1O^ZUt&#!ZNemD;nE?W)FhJl`1_+$S0D+MCV9>dnkTHOn3=jw#y_?MdfslD+(21O| zF}!&UAUK}^0v9kq;6ernT*Lr@uyr#_7$6X|W(I^|t7eunKp<$$4un@SK;S9{2wcqo zfspkxYrx}xpfSO93=oJ|MYDkc1YxUaAnRu~F@VMhK^V4vW-9{(ZexJJ?F<kIS|<a- zI~gEw7Xt+DW`Mvw(DgBU86XfevIxSk)iMVdAn+gq1j1I!9A<z($Y>*IrOZ(V5QMCg zK_7Pnoe_GH0R$oAkDyaRPcwkv83qW1t&=&&0D+)&E+7nB9RphzbBO^0FEc>k6$S_d zt&IWUYYY&0odE)GFhJl<1_->x0D+(pOAx-p0D*TIAn+ao1m0(Wzy}Nv_>chtA2C4S zV+IHWt(gJgrwkDIi~#~cCv1Z73kC>$$pC?{(aP5h5cq}x0^c$~;5!Bggsg%AjahzR z071}*B?y0FfI!%|<rfAB1dUgMFl?R6cLoUj!2p4fbqGHhenBCq-vYva7$ER30|fqK zfWZF@5D2aQ;VW5~7$J}uw1<U}89ags8O3B}1VPC90?=(huo@adLs}7_Q7lj^0(4?A zs1*S^w;0lr0JR}NnBMIONb5m_0n~N?;nDWPX!`-wf`GIhKqpd<wjV$(2uSMzw7Pb* z{Qzo1Kw1x=**{1N0@8Yz3vN4%wjV&VlaLk!r1bzgL436R0BS)%S`Uw)tq0JF<)GFB zXhw9j{eZ9i0BJ>l=E7ht2?z~oO&~}}s{$JdX=gx4P)h@XVeJhF4Ovx*OhQI65F}_U z1GEYRGMWJ*VIvw48Z@SXj6q`?5DXgK0Nuy{8r|TBj&6WfgdoN@K>K+>7}h2M(V#X7 zGKRHJU^Hlq0~v$HI*>7B)B{0+#y*fSY#anegT_LTF=$K#8AC=#5F~7z1V+QgOJFo) z)C567Mo&;l&^QVVLq<~&Bxp<p8G}YwAR{Xf5;D4iAVK3Spz#$L292|T##lh3E1;1T z*tiOehK;YF(vYzh5D6J=0g<TVEue81(1;7DwFDYxLB)_U7Z3>=b3w+C(HGDy8K~ng zpgS`_BQT)&2Vpz3@fXM_4CqD;&?pS(UJcZ77|3XhGjuctGA08eVdF9o8Z<KFhB7h( z8I=K%kZ~C==(r4IbOyBc2Q)f^z_4)|7!4Y$0b$T+4P>MSm4uAiAV|>Y4P@j7LW0I_ zkTGl=2b~7B$sjj`K-yz45;CfTAR(hW2of^NgCHTJJqQvq>H{G`BR`N)9|Vay`UBcc z1sVSVk;vmepfMm2hKvFsNYGdiE-VaQBZWFX1R5oRj1WOc&=?WurW4pW5on|cG(rR$ z9fFJsAxO~J5Hg016QR?fF(VL$j2a<G$mkI=2^mKMt=2+r2g2HeFdEV-gpi<dB^U;c zE<r|?u#>RSCdepL19X%LGTMX~XM&6}A&)PCMwvh(OrWtP7zT|nfyS0#7%{#C8CybL z4Te6x1R7&PV9-bt1Vcuf5F}*O2|+?epAaNu6beB?MxziUY+MR4G6fl*f{`;BW`S>r zfs9CjT9T+3GAf0O1g$=Uj8nl#$Y>RUL?5>T-7Nzew^|C_?*%G*kTGZ!3p9QO!k`f> z#OM`d<O+Gr3ZxG<ZUwrT1~G00l7V2z_!Vg63WSBh`@LXeSRfiQinSFyiUk_M0*zhm zfbMz)jbMRBuRtSL5YvRgqgRlTD+mc1yMkd*3l!A;1gV9zK0#~cAR}0y(JRo{6==-r zDAatAI@tKtF{l`9{Tz%2sexh8nmW*!7BYs7Z^1^mAfsC#5;DRC8Qp@A$m3fVp!Pw= zxgcX)AQCdlg&-lLT@VuF4$znvGKP$PK}e81WE2cUf_TEv(Xbm(+aO|)kueAf5fO%t zl7aS=fkw#?7&2OhAR(h>*hq*;kkK@B5+Vm8A){;v5;EF`NrKwW4^dnVX-Pvy;gCs? zsR#^dU89l+Sz+j?9i;6IA|a!9sN{1bdm*EF2of@?hah3&d#|8+AftQ`5;W2W8RbKe zkkLML65&GFI3P9}qzW`Ph>SsFgrGZ)Aftp35;j%{q9LP&2of}Ah>T(5hcFs4iU=Z6 z#}U!T6Vb;NL1T*`3>jTSkdRSE1PK{!#7=_z2N{LLBw^!{*l74*B~Av&WC(_gW}=c1 z83+j*=Y-Lqu}<7rm;uy>LB_E0P#6vC$G~am{5uEeWF^e?9iR)E<rP3nJU~ZnD5IY6 z1-b#k1hmA%!qU<TbfA}=y@R8ple3G9t1J9o8qi@}LBYYGGet<mp^)Q2@RQ(UHb6u~ zWF&OyM`99a0SEYu2$G3(@bM2B8JVE-K0p|BP6cTgbZ|s2_=J#r=;2@Gpp(C9YRl{D z>dNaI8k<1pZ?$)nmv@$zmqV_Folssrv3%0xDO1bKr<IqNPX`@f0l_n%VCJmZ5CJrD z4iwBq6G!LH0|9hFGWhf7&!4|w;iAP5txJ|JTLxjFlgn2?L{_d`wQ6<wn)0<!unq*) zL*%fL8#W?DH*G2}-@Ik()@|FigTaoSyLRu{vv)5-MS1x?Qo#NL1i-;VhmVi|jv~7R z!ajEF*s<dfCYU@?e)1HUfrB_r0GuHJz~`fZ59<P*0t#9>1HQxxbOM!-fUt-t=vXHR z7MG9|5Rd{%3?(d$=2+0#Qpgx|%oH+)mupZ4KR=JSxVWUah^V-PxG*QDgoK2+IH+6} z5f_yblL8%^CL$p!!7nK(EiNU^DkC8vEhR0%%`L^vD$6a-D8b0YB*DZ8x<-MKnVE@| ziG@W<ibV=^{SgZjqc~_Wy#%8)=!7EBmU3}n1_o(qNd`7yHgPsKaaIOaad8F#0dYxb zX#oZnR#rAPR&iD~0nimjY|^aK(yR;uYzz#nEDUU*Bh%Q#Sr|At7`UaS85ktRrCB*R zSjD-y8H8DRSXo6xLFcuz3X8L{urn~RFf%hTFiSB>F-S2=Nr8?(6A%`bkrk2Q<rCl$ zmys107ZVeg5D^s@=iz1%WsziHV`pb$l$K_cW@3`&Vr1gt;+Bw>77>*el@JwSViRGI zW?|(L77-Pd<`QP%Vque#Vv*)%5oZ^dVw9BT5NF{K5)u?-l@J#Z784T@6O#~@V33ev z;AZ3IVi4zI<rWu^=HL(jc?BBiSSUs&W+oO^HYV^wEzm^<+(-bttPm-p1%-qeML=hT zOGrvd^E1iFO3NXsmgnZ?RzOm%2x23z^8-~?AS?hC<W^D=P*PCfR^k@m=2lYRRuJG; zQULJ<Kw?Tt3d#xsDk`c<YN{YzAS|Guq^7_P(JmkWQm&)~;R*<-gRBz(oeu@Znwnrr z19TjhCWxb@si~<2BETY`BhElrK|xDFM^}JLM@LhSTVJ1B3FaXK5Cd!^h)@#XRscI& zKtRDzNkNHQ!AM)%80=V(yOadL%0XJVx%Ew$n3zmKx}X@m*b-zJ0$bbIYTJPBYz3Vd z2FG?fIx6-KAYmOxM@J4$CnjgG7F!o4SCAq%ZEZ+AF{!wN_+adzt*z~8q^<4c?W3)& z0+v_tg<Y59550a}1#C`0AV>$8=?=C<M+c%FB&?#M;s%n^5M>I|(NR%R(bo0`@x4J5 z<W?6ZCI^s8H*Fn=BXvOP>>wstfz)biYX^f=Kyk2pNGM1sBs2tMG+0y{lGMV$92FI} z5RjYf0_{K)RLo9AMa9leM@2;kq(2}$B0LmiNw|)VN}!51)X5^!($bbXI#$7fDk^q5 zfp#i^b}BjnA)x^}RsmMp;QMz2L8d^xsIB4#b|+Z9ib|jg)IK{918k4BRd9Hy4#*;q zLMI&^oj@HO9V?joAnHOxwZZNIDG3I-$4^>X8pP3Y2fIVXPDMp0(9RA-=%|4F0P>TL zPDpTsPDn%u*!*yidg(}KXUixZonVOhAblW}5UdlZ9UKy&9iXiZazTI!)KSvX(veX2 zsDQknqT?PK5)u&{5du;d9-*V7V;2Zj3pU6uFeFqv0Aw#n6l8B8$hj7v%c;TkLR5hw z8{|C|l|T^FPA5PoJVZMn0Mstj34ug4G`LjUKq)CeCt5{C1(Z5$!Rd!d2ju5KJ3CN_ zfn5d);6PAxfz?~N>!_&M1!-$TBTq*~#n%~B#DlJ8igDI<b_Sis%gzoyr_jjAh=nBt z<T99@pa_O0uV8I$TWx4U)rN{`YikF9jtum1c8+ppVv2DVbpWMH6&01(Q0-uCZSBzD zP<MzsnV3{mR6?{>RJ7eg;$lNWLR8$ep*bly6tq$~7OXBL)R~)`+dxOh02Cf7D)!*q zrvi#waMDy!(LrW|+zJwh(kd!;R@xxn#e@9=A`)~!4g}rU&CSiNBOoB41G#S-ro-MN z3C0A`$th5KnNrh027~h%NWxA<MLPhLd~{T_ZNU+v0*ZQ2#6xKn6>U(K14Ughh@q{` z6vpJ_2FWx!IyxZNfD$Yd6H_{qjkb$Q2FP3p1{n-uxq-?d@cre`a8yyT(+&;^4GsY% z<dBe1Z5^E`9Y{U~sRtz|2n|)Q1I=_$Z-e{}DqVs@b(mCCVnI<75Da!5D9AzOMgZup zFwnhSIv}35wsxkrb{xb=m0)dcP&XwQ#MjY@0U2flQUzi}>A;l5YHNpRhiZfL1Z#(c zhH8g^d;^MaP*8x>hiL1VF+m)y14&sL8XBNzVgjkuwgsut2B#Ny6*m=?5O+IJkV1-Z zNL8Yt3*kX20f8(H4N#ia(b3jcPyknbU;<PPfZ`Y$*8-s94<R}QKyfbs>bD38<Opzc z=jLfbXiWhD9RY6cd`*x#Zten29f)`#=y<)NVn@f4k`kuUveGh;gUcNqD?oH*73kuy z+By)Mi4%N<SiPg83+Px##|BVLbJRA1<C~*41>%Ds9T8N}1o3sVaf@*am<dT;Iy%m+ z&TY=1idmt}*^ryt+1a_ZwUwK@ots-(Mcr9|n}fqpKtRg518lyuv^3}#LNM;^O6_*m z(c$3eadzf#cJ7gK?s0YoV^D-~IQM|KU?!+-1;-!}78bA;=iWXL4}_s=r9jF+d>H27 z;D9kfbd(M#e?@`f5#k<@ptG~Hw6wIdv$GV42PQxaXJ=<5)4;;u^L1HRAX-5>z-nsy zLFza-IKa{zwG-d~Apupv!NS48(IW-+vopvkV2j`kXB`W$Kop1sITDVYLFEi6x#)x9 zT|ht}M}b=b)S%#2P*4&O5YXUOQ0Q=WZd015t)Qc0#uU#aARu6>qhks#A9QqdOu&VJ zj*b<ym;i}`t52rVQl`o(&^>cZY)t8;Or;=SFfoDh7!y+o6I1#mh$BEwat1pO&IdEV z;?6ocj7*cKfV8qWgUG4Vrh_6`TRUWihK6(MOnYaLy;iekLkdWH4Obm#-f{*9J;W4e zXXhwz;6k||_8br)EiJ8rTmY%eh2&2a9UVv;Pr=Q29ynQsLHHV=3=VQZDyV=3IaE_q zb2cb#YeE!28fgj&3ZOiyqy%bCDkx~pS5j7h%YYhf+&bLc3JM_oplV)2Sw%&~T?L%& zRboSfot?qur1JuBxyuADAHWq7sB}>Ql_ww!&M8bF2QhhgfWjT*P*D8>YU*f1${!sw z6}J#=?U3LQ=<RYQ;QB=eR1Ug<!wk$<0aY#FS}iy<I23F!s0dO~35J&2OrZ9kuXAj$ zio3SEo4bmNHmHn-x?e{pFau<-ib||@2vf!)uzpZ$1cpKC!09VbLjzPUf%q!1p&`Kw zLqUx!5EtZrJ5bauhJqzf07{c!1*QT5Iy!;i%5o_<96)Zi12?<CTyU#eQ&SVH7!;~t zQ5Z1+WC4t$qOt(go?KR^qoc#Nd<8UL)h(-Is$R+Dz{E6l!h|}eRUE6Gm_YRjC^^G) z+WDJ7`zv7o2nyBKu|T{I4osLb6%`GTeF$u)BeKS_Ns6NfTt@giI)kJjZ77f)JC#5t z2c5N$Bnio^sj1zdCK@EgN=NB{HR^!N8E0o_Bb{{+#ZVHIULYQy0y1ws$lWU1psE_g zRZ&qf168LW_J-6{utHF#2l)XcuA`%42C74$J`J?f0k?+DK!&TRsQ7~NijEG%eJXY; zV3vxC3aBXu(g!jEOzY@KtAL7qkUlqUs{l~F1Zs^zYC9br?EoDskQ~SXpgIFoc7WWZ zqOwsAN=t)iKM<kf767u5iHQlM3>03V>Jt>$U@JgvQjl3HAjd<LfRuyvgBqLM`XF0$ zbOdr>jSi4%>4Z>FH4Zi()WHB{8Eso8CMJ-X;H&`ArDGKmsvR6^y9tu{baZrJ%`{Lu zLL1z~2>`2yDNu1^0$Ho05*q?CG&oMho{34@7LtGL?U|sFD=jUZ0JZ|+gpg3}kYI38 z0xHMsK!F9300)^pIRAqM+`t_fP>6!t&31voAravb;US>@LO8h03jsA<0(8KUuA`#^ zQXdRzGij@MfGh-?q5^iEPDqG$D9F>GH~?W46>YG3?cmT*TZlJ6;jN<L77FrAC|tje zm39c&^U(H{G*b-JJAvRrF*S8PG`e)OwQWJ#K#sCyVsduY(1-!^!GQ(}46qL&t^&C~ z7F48R;s|XoQ0c0n0g7jk0bm_EMz)TQARahBM1iXh=gpzo&YMF*wm3REZq?S-_5n50 zAlb)R8`Q#gb_V4kXKf#_Mo=3R!~o?3Zcs)L0JC5OsMvvsf=L*m&#j|xpl`sf4^k%} zU<&GGgD^;*+kjgi)C&j6L8?=b2sgLBz797xH$()a4unD4A-00bLl93MBn-o#kqeL@ z41*<Mf?y8uDnZ&5AjuFE_Mnh)1_cc$m1_Hd$`WWG`G~^OvW}Iuwu*`!D3+u_of}X% z!ICDZlLd}CP+|pjx9oI4rzfj`#X&4PyFe9CK?0Iju>-NdTEQLOKou1ZkYPGHkV4KG zRMSD5DWJ%Km2Z-on$D0$0n}|#I*`}{$2X`O1J&fL;}10t+~{<4bc9wL;0>#wRo$SK z+>jHELFeWmpJohN{RCR~1foIfoj`lIK<ll=7<d`P89<`E;B#;w>!-LGq!_pvK>M;l zClVv}UqRLrfzH8EW>8_^W>95NW8h^_XMn87g6IXAr2#cj6H0@`cp0=9I2p7VK)bPZ z8T1$w7(nL?8^X`R0j={fV^ClKogEBfL$D<SXq66VU5+7x0)riN{}o8io&mHD#}IrL zupxsB_;g@H26yOACl3Zg22U{Ci$Q_G8?4WV!I#00!Jh$i?r#u7Fhd9f=p0|r>f%TS z5I>3ma&9l^WZndZVDPEE$qeD((|AGhkkfR+88R8Npy%u6G2}DEG88a?PSgnopO{<9 z5YAA>P|i@q5DYz8C!C=g%r|7HVW?%OV+dyeoqXHKV93zK06OP3mH~9KEyzcZQ*A*z zwYtFP+JaU}^n?8XIwuEmI*uXugj&$L1qBAkDg}^wkZ#ag1?01DKyk7J?C+&u5~Kzl zgU-eQi7f-GSit}~9S6iWWLOCnF=SZH0NNE}$gmC?#~@Qds|p~hu#p=X3>iQ>V_<Ty zGjt3Yb}$$+>}1%+unTJTZiYSJb9DAI9AMZ77CQ(g4}r<UU=n1?5e7qsqYR+kEGHNY z8BQ{If@MxI7&5?4&@p5<2NpAAIL`n&KL@n-;1a`SaM*#u=L%T%D#JB~c?{RVr{)+k z++uJ9i{56q1ExWz=G+I1J%H;2otpz%9STa>PoZarJY#syz|HUi%!A~Imnf+mlGb0L zh=ERG1<gLcVR*~%n&BPvbkz^wQ*c1%-F#&LopJ*@<K`Cw=q#JRkW)+<JwO)^gZFnp z&ZmK#OanQahL`ay10N$l<5>pKWyFGv$WVy!EQ2s3WUa$l1`)=y45EyJjAD#u89;;i zl3*DrMh^x_Mh^zaS*OxqF^~=!Mou^z#7AJz2{Is_Jc_&m3hyif=v*02MrE+53Zo#S zDx)AH=v*0y`!pB@88yLjXBh+;wZJTG#<L7MjAy~;%z#dq@c^eaL-1)SMvS0SW*}h- zAx#<0z%u5HptNDgV8Lj~XvhFMbH<tx1VM3P!w5Qe#-7oE5fu9#3}+b}!8U<hf0n@s ziS5kj!2mi*6Lg}C2V)yJC4kKJWZVE}gU-hUr8|V|Q3fAIP{{={&xz5H;VgqMqaWj0 z27kr?u-PE?Sq2CjBnL9lkl_gQ+~KoulR;u27aB4ILVW{KITfl>3M~47A&AkCA(+tv zoTfq;Lm7D)!oV^h)u1zI<QXCvqZp$ZAt3}h7c-7Ao-qOH`$WbhMneYB$(JDYh76$d zW<V^^nKSzs6c{oYK{+9d(U2jVF^3UU!a&MP$oVq`jD=vkilFDu7&4TB`GySTjF59@ zAZN{3gJY$Ru^vo=att@vpAC$VyxquX$j}7Vd5QscN+pa2oj!w%L9T=0lVDR|ywl*k z*~$nxsiqTb9!w5&W=#*0NG~I(7J$h1fyEDjb@wwWFic=nV3-I!#bzp44x$c3f=;sm z?TrHMtDeCKI@QLIVKyVE3<8~O13A&gkYN?WLPpS<BS`##^ue$Z!y@nr!HXHWzz}ri zjSJ&4aBP55%yKXbqz{HcVGhxq%m6x{62xA`XvnY{?6NhCh74=LEYNwBAU&W`cmp_` zK`KG`B*Q+i-$1euUqeb!Lxzp;P_SYIr8iI*Yyz9MnbDA83!@>!R<Pf;L0x6Yu$|G6 zVF#lj!%nCO#5_ZWU5uQJyTLvKovCBUu#a&+;{irPhJ%cT42QsK4})xAJPJ15kl`4k zA;WP-LxvNKh76!nbs%m7ksud?@M*9vNPg#JI0N<(H^W(|C`cu!d;*=r15yn-i)T0Z zG@hH_Gyyt|$B^MP<35IKjD`%?8E-J&1lxRz5h8z^@eU*C44(Uph71V|pc8l?CVDVD zVtmZ_1gsWx*3LeL=Zv8GJ&^%)md-22*I*e#hBu6F86lwxvi%(+<op|u86dtP1L%}R zggodR90(5-Uf&o&XW@X<!Z0Y#K)GTP!%w*XAm*HA1l43Pbs!p&ZXo0@#@|r)7&0_M zb%OLAMT>8cm=%&dC}cosHXkYmG8g2slMH{L=7G-P`OnC}#K>fbWEMyr$Se@{1l{z* z#KLr$k(CJ~4#R9HJdmj%)evmNFqPpnBj^+!m}*e@3rhDe><lg=Ihi1QE+%d!9;UMl zyih(L6GA;d)E<aR5GlpT$tb|Y$sowY%^<`CI+5o!11EzhlNggY6X=v3h>R4IG!r)i z=rkQUCV3`K1_dT=28awu4>tou52}nJ6DI@gv>k2+H6~65btVlaO(rcSZU${8P6iz& zZU$W@Jun2-F$xU&Od#5T$&ks2$(TtGDGVVYVaQ;@WXc2z1v4f?26HA0CQBwOCTk`e zCR?Uc40cSXpgQcqK6e3!3doNj?8NBH=)eSmXBnIs*Me&pkoZ{!h&oWs4Kmq-!I9}K zgA>>UNk%><eQ<0;RKiGSuq=!Rs|#JwL?QCa7+sl=>qn3%GT#mCABY>yGPpCX1J_jU zOlKK9z%mdwc`$%t5ytjlfavmIfRusA=>?KvA>jpS*Fo5*r9MauIn9A;V}$7*44zCL z3|>qK2#Ocn*n<JNu4!TPW`e9A!FjnO=xRqL@Ukx@B_))@eL(BJAgjLY9bl*VxO#ZH zx;l6{IC%R&Zpd~Gh8)V{>IxccCV-=`$VbP(1Y?zyK$mEP*LJ06WWt2e=q%8j2Qmh& z#zMtPO1XLY1xiYgiy5J7vp`4sR4XYpRYTThb#`_4R9E*_S6BD-PnbAq@|5c8snye_ z&zM<VJ*&F9dN$-DBM=F-f6iQ`c_1zZhRQ3=N0R}GFQ~4r2GI)_EnWhVUb<{Kgn>b> zSP2nwUA216+Uj-H>o;uNw0X<cZJ@i#w(o$*fk>sDAQFOi?XIo{AMsP|deGJN&|%jj zM~@vpaq<*I7D9sThvU<57L;*D=`56ml{yD;|9OZ}VDdus#Y<oYlt2ypoXZ$XL!j#q z**QR?7GTWD#SNl)c;V;&h>D4di;9YhN`MFumXs0|m6j2em6HdlM8^v3ic0LtAa$ZD zU~-h8SqP{SFqscOP{S|Hr>1MDZ^U4zZ)gA_%oq&yjTsE}O&ARIO&JXJL40!tLwySd zLw!pILjwa4h7eE^M!{*Q8qicJ=*BohC<9C}7#ivu8i7b7218?zy`~_-T;GVnNZ**j zNZ*vf$iM)EVI+uS#9(A-!eC@*2ErgSjSMXqjEoEzjEsyJjEqbejEu|}jEpQ8jEoH! zjEs#KjEs#LjEqefj7$s|jP(u87>xBz7>xBz8I1KU7>o@JKo~+mNdpFBa|UAz24hPG z6Ma(#69WSfh7eHFfWgF&!NiEc#F)Xv*a+kb6B7m#Qw9?=1`~4z6AK0tO9oSr=S=m@ z7)<po7)%WqObx&oLO@9a1~U)>V*@B@z+ev2X|8X<U~a%*ZUDv*0!kV%Sbz{1LkK8o zz+kCw#9(Q_U}*rx5CTdXFgQB-hq?N=x`i+}y1KhM`uT<WI=Kb~GdQ|?I59YSxJEEI zdieV?IQlpScrZBnID5GIdOAD$ATfg@eVzP$f*BlrT-`uKNQi3?gQHJ?hoh5gh^KP^ zND$5mat(I%3vmqb^!Ib~5Ap@+3h;3B3-xsk@^of!^mX!d5A_cXW^nX#4Dt8zboBG^ zbPe)%_lXSf00}yKy84B<2f4a>`-TRCH29&3IQsbcLyTu|M3RI13L*w~a*(4FgJY1R z6UZnq<r)#<2o-US2yyjuadm<5eOw_v1FQ3O4Do=fgP0BF1h~PJ2e`qE4nX%bTnV}a zOh<5N0MxwT(0~9RS6^4Z5C+E}Uspd*M?Vn56GZqiI0id=dO}DK*9aF+ch3+8N00|X z9D^7fL!3a^hruz#HOQC2F(ky%*~8TZ9O0<EAWwG>kVc3CCs!Z;aJT|650XB@9NmK) zJscSv!(4+yK<)?&4q<RIVsLWw@dOiouEDMhPL95zV8Rzdg8Tv!^F{KClVh-_vyWql zryqlpV{kB-a13+wU~qB_ar6d}-V9Do3{K7rPOcsdU?)J_=;Z3>&Hy67R(iU4Ix{$d zNFT5%AeO&hNRYn|SkxCndIkmg2f4a1IC=WHgfcjJGdTG;`hiFg_ILJng@kV~gOeYF zlYf9enDF&?Loi$;Lg0e1RKeinAL{4g7!>K}8X6Mh=)>UTAK?<@814yjU=YYFL5?22 zo(xW4%FEx=&(qHxnpB*E9KjNvK0dA>1t2=W5tO?8f*G8GTphg~-9VWL%ye>f^A7?k zb9MA~@(=NVD1fD4r%-oK1`q)W`%n)T2B%ODPY@Xt8tma1?Clxp>fz)V6y)m4;1n6< z=ojMY=L*R@s9Z-Mn3>LM49<?;z6{Qeejx1V;_2w;=;R;d>F(+0=mQrDj`Z;X<(y!U zh`%4WRB(3mgRq?aeVxEwb_Pd{v!fqaS%{|(DBAt~d>w<l8Jr!1Ji!FmqM#s8M|amC z*O1U4kiEgKF3=q3>=^9o>F(zr<mlw%%HZr6>g*T{a*~geBRD@eJ3E8!ku@}ckj4<o z984KPNDDA!1R<f6C71_MZ4A+C43RR12%3Nun1V?&FbOdeVi;Jv5m=uQ*cKzOW+Sj( zBd}g0uwEmuUL&wxBd}g0uwICr#$de=iy`(JgY_DN^%{fqT7X3?4H=w09KC%VL6k3u z@N;(!4)<{N0|k(qzmJQn3%aPUV?Y3^sE>cRYY<2SirNs*5Fb|z(a?YZgj&}i24@e~ zAb)RHS2zQdw_$=v+1uGO$Qcw<UJTA&-p>BM0gfRE7NppROL#hY`gn#!BIKb0*p+#@ zxca*XIf5z!gb8@Xz)tk?2AAv3VA=^}mup0bGn96L(ykyn$k7Fqkb|Mxkhrj7*xAcF zI0O_7!65bUJmcjJWjgw~1cdrIhlGMFIgpxAKTkJLR~KZr`!G2B_y@Z}$N<M6SHBPs z*I>_Jr~tBlKL%&NkRTrhXMYA~e_s%WWGoj?N9Q2V5Rhv@nJE|{<KgJzh9(5!A{2ml z!EXLRP#1$FeEt2Pl&7DIr!%CWg2sWrudlyfuxo&0kYk8{5J*{oV~CfdFW5l=KCThY z{(d2#GSb=K&&3l|wLAKFIR-d_ilpEW#~@Hq=Irn1=jt5dAA}M%{(iy!evW=249+l? zi>spxg69S)aA1-iu8#0B0Vd`RDm+1sf*R%P>f#A1q+s#^K90_=9-z#Dq$nWB)!Eg> z)6YG~HNe9WVP8OytD9#8D8s<C2ZuU=%5GF4XJ=Pzy2Cv~Jc2_UoxMR>#UE7Ay1V*? zfD3Vu6`*=4z|%P-G{_YcSpgtl2ZVS!y9PKqhd=@xBpwRZ7~~HM1~?nsKyvg6W^fK* za1ICxVQ>!hbM*wp4_sqtP>`#ib0j2A8JtnL!3@sf&Tb&Wm%%yQhru~KguyvHl)*VN zAjs1{1V%$jbjP4b2It5iPahvoXE+0rf1skS5g|^<GSFrcOcSh%M5qDhCa7lQasopL zYPS!Aiz662hIj;d2755LI6?{J<m2M%?CA?q2caP$!QkQwDslXS!3i-WGQgF=#nsI* z)F%X9g*f}WxCZ!p`h_sKxCVf5kgKy}h^q^Oi)%2b{$X%&4RZt&eva;-DhkAhFhQvt zg$MRJgNvsl2t)ECf(42&q-+`t){V%v!AMyUSs5s_T>O1K{rnjqREVdKYcPY0f2fm> zE2Q}bVS^h+Q1QS}{}5LOmp}%W;Ghr&mk<V*P)8q7(gih0TtWkU7(j%7q@y2$t7DK) zq)ViqV{nM4Gdv+NxH<-hfGgH;PnQr623PmU05B1P)(Us^adKn;5y2p{eEmZ_LAjT~ z)z2A(LB*O2xb^QB>Wd)&YUKxGh=M%q>IW_nU7WqV;quO2-jF6hu%n-gufJc22SP8r zH1%{w){ZI+E3+Ynd-}P9N<IeH5D!l<;UD1X3}=8M2P6X$1gCZ+4kVV*1svge!7d2# z@C4~aO5d)bLH+^oJ{1FuizrHABA^DkC#c60#NZm??BnPQsXL$<2_y*Wiui^4`nZNL zxH<X+yE3>r1_gtfb_{N=3~sI-3~ru&AnfRaoI}9nXRxcUH>kJb<{1<m0;;<F+&zQ+ zeH=qv8QlE+LKxhF96{I_RB$u61$#2Mh5GnF(tx|88-u$eD37^2Gq^iDF}S<>g52o| zZNRy^`nd)<`haqeyMIt51C)>K1b0_|U)PWzPv>9{#{k!01~fh>)G>u1;qC6~9|Te# z<PPfffOsGVD6Em#uqbx-VQ}|xbO#guKA?J$!QCg+mBAg<WCjtQ&J6BBj(%Vw#KRFp z2Y9&px;i-qyTUonKB2)Ou0fD88{E)v^>y{}^z&wL4}vOmbp>%;UA-OsT%7$u1vmq^ zLkji@sAmd|H5ZUDNGU{ns28aE^ki@k^$KEe4-N7S_4WYqf<hTQj2JwO89W?47(g|* zqn`^X0zhSZFjxQ*6(A`v3n~w0!xZ^OIr{r~xO%wzJNhtqIQoIGJD3Os^W8&zK!u`X z2()npuDm@Q{oF%+(3mjCJ3@7Xq+w!CP%%g~0V<LpM!@*s!q&sl&l9B1A4G)u`Ge?? zAO;V|5K!QIxH<(ffC$%cC<V#j3?82D9sxm~VU8iLp~0@fp+Q0Z?v5d@!4Qd1GyxQD zkRyYK4}*tKXgGt1ub(@EN04_2gGX=(gGVTc7s=oe8Q|gS$ADBcFnGEIJAnyLKgXa* z{{UA=)5g;!I0Pgd5`-r18{it`>FDDa<ml_@>C6CbBLzU~7^L(M7Ka3fr!zcPc>1|{ zMu2N@uvQP(AWu+j<LT!a!T=&b?G-4EoO3+=0zfteLx>>H08j>X^a*hE@No?DWbpJ0 zc198m3h@Bt5>IGD4b)Qtvt1#*CQrXG&tOj{A5hO7+(7a44{;0*b%L0|;2GcuO1}`w z)i2o7-!GWKGuR)5K_!I?gJ+1N4+AtcIKmiC3|@|5AYK@_vB%)$&fw+40IGGIA*5qy zFsS|ki$HtkAhxHUJ2<g;g)n%9F?c&NcsqK63T1Clx8B>)&k>Z#p)@RmgScV-p3Wfi zydC|VLj4%L9sS%RJfRe<sSe`$Il6$%3~>Z$V(<nHDmeOqeF$Uw2YGk~Il4RgA=CwU z`1^qdtiZ_~5(3_ik&Yfdo($d|pwTaH562)7Gsr*K1MFH4UssTTuPeMI?d{<U67lz9 z@b>Wc@&@S)b#V-Lbn#^H_5-=e&p#x@)y2crgTWhYL>PmQ8Uv`ncky8G0lCJ<(VxM` z(H}&Fc={PKfV#_mhM@3(vtS_t764aOaFz?K{SDCvZm7W|o#A3GaF#2A1sUpsDF&Co zj+n-Q#hp-fAq)n~BAE^{6KsVagO3w~k28aht0Sn{4N6B0pg|>QVgR#4KqCwuA;@f3 zUnf@=kOpKySmg!M57pxz<{A{_>Eg=Z;~D_6B*59jkpWV&_;`A|LfXW^P!VW5(Z`L! z$J5sdOoTG{c=|bl2oT@T)iDUnadixW@tvThhi@=Q6-WY-%|Q)TA5TA5NR0tvxw*Q! zfYl(Xc^}W<PzE1=R|X${cTj)LKgh*3$Th;*6`Z9R;9YWvi$Q#(0?sGMmBA;-lffs* zpTQ@L!6yu2v5zZ*Pgn>DBlwZdo(w*b&Y*I@C(;GP1`XXX_^L7ZI(j&Ifl6jyM-ML$ z&&%J>9Y#Z%m%ffZj**T&j=l`Oj(#p+0#ww2IG}D{B*cfl5JUVtojn{~9sNMcpiD;} z*8mS>exxU|w3BNHvaE}v51N37qpzzAvJyAXFjpk=+&z6=eUSJbsQNuZ{aiq!d>{ux z+~y6+vk0yay7|7YV6Q@*?-z>fT7N%es{@cZf&M7o3~~)dc66|#iwCmmU`Jn9Z)61_ zj^Rk22ysQWH3Zd5As9gt67K335{aZg9L2Oqf0O`>a)d-dhzGnq4jQ(01+^}n9erT6 zUN8el6p}|6e4QD5T^W2`T^M{_LE+=-8sh5d>lo?k4HAH{L3M>MLJFzG^mPTL9Z)&o z?dk349qP&8>ly?yGsqvr@&`4{oI`_x!Ex&v1m+_T@%wr@`ZM@?!n*mso-Uwn1cU-L zetkWC8GJp1ogIUM{KJF6OjjQtM?Y8p&|rucunRhZ#%eLOgt$6;Kt?V>h9bft*b!_r zl7JIf0Mvhn4M2f(AX|xKmQSQFgRj4fCzx>abPa;$Y>*g;3(*Fb4e|GLg{TOIh(&t( zxkCgPeEt30!4n}2zMvos^7m)(4RQ5k@D23|0TbX18R-ZbQDyLj(BL`&!FECt2gQtw zBf0=cg>MjppBjUoBWMU7l(YRD{TTclgFx5`L<ISVGx#|<hA{X!gX%#)*Kkm2$KdB0 z9^&W;YH9nqMub3wy!{#cy#4(d{CpVv{GELKoxMRcXk44Y&mUUky9fD)1~B;fgH3aE z4si`abj$tx{XnW*gFx~i7O2km^Y;r5a0FHV;7%Nf3mseX^Y`~-08RS@1o(J5g9nNI z{6m6VA;qi<gI};OgI_3vU#NG8BLk%L2N?=79y0F)Zi_%0Xkm_Ezl1UPgEI|-zjF|S zzq_L^gTK26m<$3Xet%CFX9j;ymtY2eA7>9wZ%+{I&EW6j0%~+R27!uTe;*f6qsh}5 zB<$xO6ygD%X>#-f@dI3gd?7Lcu0f#DY>;5Ee<+HgkkBA+up-FJlRvnkVDJZ}N(TP` zS3l&|BDg04nxAtF0*7XhtFynmA9xUi!9U0|(viVG$lHU#KgiD!G@In;1FBY?97BQ` z{DVM}!M=_R{=vSEejrYWhiednKZpbe0(c$|!iV*Vz**h}BIfA=8UXb43w4F@!J~c< z4m25q4FkI$G|CK-g7^bu3v3PrtO^oC5G5!+1gV7P9I!(8@HB%zn91ND#sF#w_&7#_ z>EMu{P-kz305ygHM-Rtv4-ZgYVhC{b@dXo+L9TwT44_d>u%{5!b%0|?aHJmtY(^&1 zFT^nd#tZlG3~>deLy*pp@L+}j$53Z5;pyk$7|Iaf!Vuu%#t`7*$pDICh5!#ocQ6qg z;0U5UgG2m-;S3kgU}uH^Pzy7_!zX|tz{4+sA;81m)z1@Li6ga0LBeRQQ>1|gB!!@s zVt^+@fDfqfcXxISatUS#@CkBZ2=EVZ@eFbWck>;6Ks`st5Hvwp%L>%9iE#7*mAqgk zWMUD_1V;!1m<gS44Db&QLF(TG1TX{yF+c`Vp;-#V3joy%0YRWaH$*p&0l{<(a&=?~ z2nz9J2nc2f2zK-daSej?bOJ&h83ICsT%kj~fsT&O41tc0o(zGGZVZ8-T0Rh5Bm{;s zfJVU}V<&JHSPI%XU<h(`_w@I3^Z|F&K+PJk2&l^+?BNNXa|?3yU<h*ca`ba&2yzW` z4GMO332<cq&pv_*4Nyr0=7jiz+Mf^!2piE@MN$9@QY0Zz5(PI_gS<eN__#6z`FJt} z`S>#g`FnW!xkoxig4_`5>&Xxl>gx#(M8xD=P^h1$Gec0QUl2pE8bh$73q!D@uVauW zXhJsF(Km=87}RJDb_@lvLW3NGJwk#U8G_+CG}wh9*wxjKA=uR&R1P3gJ!oFu)z2B) zk_~nZU<h_+2zK`iX9xxrTrQ3vIt)~>1$#Jx1U>y2f<3^+nx6|KIe{z*0xbmy_6Txe z2=;V=5TJ51*wYWh0aa_Dm~mwY_Vn`r6OKNPFxoN5*~1Yw`4G$y?9CAD>*(VHAwgCI zBbZ^}LN){>;=>T^?-t?$?pZMe`-8meALJP9?;8M`Ee;M~0OwaEpF*n<e;3fa476?z zW(W>&b@l`e*#-wP1P2B&1P6vX2DvhT5{EBCa7d7&r@Kc8sKFH+3i1nR_7^n6!2sfd zyT72Uhs=TX(}F_-Ak7s=1~}6R!E^>OU4tN`qn}G~C|J@p2+}tPjeWv9>cJpYpmGz@ zctaEepz#S%69;S!)N+^@*hn-nu*GmOaL$HKS|OPk1ezj%4$FWtDT?9fk`P-$9U+QB z9ie>0Y*=t;0Hg#5`3ku)1@G`M1c!!%`1=KWy8DHLrnmgu8G^$dgFU<*-57!+8DO0q zSW5~d;14E3JpCNqgFG36BZHhlgr_5n0>xQ~qr0PzzdJ*Sqq`HBaP$jz1TlgcLL5C9 zKus7|5D98?gXl0%*ANgL5$fpY1<Ldxj-D_!)CUY9j^06F!jmDy(Fdg6CzK(?(bv-l zM1qL05QY#}4`0V1r_c~bBqn&AF2oh2+|?)4J(M8?)Ew|b9lQ;3^K)kiarbd&2=Q=q z1QUK>%9A0))5#T-;X^!~LKs3kLBqW69t<I#K@b9L8Ei%uS^*>4m<%8$xKs@B4`2xK z4{-Ezaq<rd@%KgHIQqGO3oWQ1xU_>P1~JiOp(=wMJ$*d=+`-*@(0E@kWNMQE!uAge z0gdB32B7od0U8wQ$^e?(2NR%nR;asU5Cez+^&1#ML7i}hP(R0DQ1WnfVL+c*WeD~2 z_VW+-V*s}YKrIcZ@53BD8NwVrK}3)zxPl0F^>GEwM>2p5641~ZOe7eK5F$e`gt@wS zI)mH>W59;X!d!zw7(fK5iw|c&imfnD(8wV}m}jtKkh>#@4t4Yaw@*QV$q?or?&<>} zU0gv`C1|}5SkTiCO#mFWVL=RG!3<&GS?X}mU<HWqb9eR(@ni^hVh9Hfg@k)Cg!>1% zK)UT97O47i4PuA@HC;gjc!o066(kT0CP2+;hKK-He};(Q&<KWz2!=>k574A0D0eVK zx&|{udNM?MdV&aWo{01WQ68=lpoKxcuvm=rgx0no^|1UJ%n%vs;sGW+Tp6O&7@{0K z{TQM=z$B<Di1J~G3Sfu|W{3)BhzbXn*bGsTk&z5hQBhHl6|3OpvoomlgLPpMty7rg zAeD%f0Z!nx1)x*|Y2qP8BLf42vzIq|0s>9ZLE_VqfV>j{d1nIhE(GLV89)>5*r)m- zjc->MkY_>ufUUJbnnVQU?_i|)MUYbXnjCa>sKJ6Uw~V%;0X{tk4t;R*1Uv@^tw|V= z2ijd+A(faXQa>*k<Q5Rdh)I}P&;^B{4mP}VkE|FP*^VGX7#P4EbA0NYu&INY;pc)} z4S`jHI~TYojX_l~)US>{$j(C4$zWqp)ZwuXR*E@0`k^hgf@B^~XK08)6DuBds0o8n zn%SC%hk>Dik%57u_V<4c9tH-B=HLGVco-Nwnt%V#;9+2hX#V}bfro)1qxtv$89WRO z70ti@Z{T5I=xF}^{{#;M!;I$N{~z!$Fsx|){r?9K1H+Ey-~R=885mA9|NgJR%fN7> z`S*VZUIvC2&A<Og@G>y`X#V}bfR}+mqUHDh4qgTZjh5g47w|GLc(nZfzk`>7p`+#Z z{|mef3_DtW|9`>Dz`)V^`#%F81A|8E@Bb2f3=AHvzyBNXF)*BH{r%sAkAdMu+wcDs zd<+a0?Z5xe;A3EjX#f3x10MrJM*HvoC-@i`R<!^A|A3Ey;YR!K|3CN`7=E<>{x87K zz%ZlZ_kRt328I<KzyCY%Gcf$<{Qdt1KLdkD_wWA&0t^f@x_|$lAi%&N(ewNN1px+z zik{#9KL{`|yy*G;pF@y=!K3&0e+5AXh8cao|0f7CFzo36{eOWV1H*|4zyCiFWMH^4 z;rIU^f(#5VCj9;{AjH7%W5VzM8bS;V920;4cMxJ=keK-Ue}oVNgT}<){|kf|7%V3K z{@)?Qz>qQV_x}Y#3=9<$fB)Yh#K6!o@%R4=LJSNyCjb8LA<V!aG3EFF3}FU_8B>1$ zZxCi+croSo{~5vz3_qs){=Y$(fx%<y@Bb%+85lCA{{H_!n1P{V>hJ$QK=M<6|JM*< zVBnbX`~M0N1_qBAzyF^QVPNQ(@%#S^5e9}6Gk*V<5M^NCnECs^hbRMs#mwLTD?}L> zJZAp>KS7j%p<?Fm|0_fp7&>PD{(nG}fk9%{@Ba@#>SzD{FCfOiFk{Z|{|RCY3=wmG z|6d@+z#uX2_kRX)28I{&fB*LoXJELo;P?L-;tUKP3xEH=AkM(>W6|&b77`2$5=(yn z?~q_%&{+EW{|yNS28(6C|7%DxFw9u?`+tQb0|UqM-~UfYGB9{7|NZ|1NPOk*{~S^b z3>>R||91e<tAGFRkYZr4So{0`4JihOh;_gJb4W8V>{$Q%e}yyy!-@63|4)!+V5r#e z`~L=M28J6Oe*eEA&A`yH@%MiT83u+M8-M@zkYQk$vE}#w88QqEC${|lze9$B;m4NW z|8K}JFl21|{a-_tfq`TD@Bam|3=BJV{{Fu~mVx2K&fot}$TBc=?D_p)LXLr9#lGMF zBjgwuB=-OQKS7Rxp=1B=|0m=a7*6c}{r`m=0|Uo_-~T1#85lec{QmDD&%n@e;P?Lu zc?O0Z2Y&ybAkV<?<G}C#2jm$TEDrwu|3RLCq2l20{|X8W3>HU!|L;&>VDLEl`~Lz3 z28M{EzyI%0U|_g$^!NV{ApWu6|1}gD7<L@{{XapGf#Jrn-~THV85lB-|Ng&2k%8gG z@!$U$lo%L1PX7KsLy3W5#i`%_4U`!eG*18iU!csu&~f_r{|(9v3>jyB|39J3z;NQs z@Bb1i3=A4)fB(-=VPNPu`}_Y66$XYM=YIc}P-S4qIRE><fhq&Ti3`8~A5djr(75#b ze}EbT!-`A4|4&e3VDPx|`@euX1H+1|zyB{#XJFvC{`<dy1_MLK_22&sG#D5%Zv6h= zp~1l5ar5{81sV(t5x0K-7tmy2sJQ+6e}g6igT|fT|7U12Fub_?`~M3~28I>)fB(18 zVqnO4@cVy&76U`Y!{7fiv=|sV9{v8`pvA!8@$~oq7g`JqD_;Hn-=NLFu;at;{}MV3 z3@<+a{=Yznfnmkh-~SbK85k<Q|Ng%~mw{o$_uv0_=rS<;`2PF<1ziRPiyy!Lf6!%M znDOKHe+fMXh8sVA|F_U%VDR|)`+tER1H+1+zyGh$V_<mk^Y{M~An{+n|1;<_Fs%6X z`@e-g0|UqJ-~S8r85myt{{6p0pMjy{@9+N{1`G^8{{H^2V8FnT@$dJ42N3=L_x}z9 z28J2`fB#=#z`$_g|L^}hK<fVg{(r%Mfx&|D&wm9&28J7~fBr8pWMKHg`se=+Lk0#8 z_CNnQj2IYxaQyinVZ^}D!S(0=1S1B98C-w<uP|a@Si$w@{{bTg1_|yz|34TpFx=q& z^IyT3fnf*FpZ@{I3=BW`{`}uz%)szM;Lm>r69$GKLVx~um@qKx5c%`}g9!rzhuEM0 z2Br)Q77~B{H<&Uocqsh&{{loS{`qfV#=vkw@z4JRGX@3^r9b~WKzyY?|2LR1Fw9W? z^Zy4(T>Z~~4|4_v56wUSPna_>aA^JcFJQsIz@hu+e}x4DLx#bh|354k7$Qvn{I9TN zV2H5z^Z$V*1H%m4KmQY~7#L1C{Q3XDih<#U!=L{@tQZ(9od5jyux4QR;r!=+hcyGk z370?rPgpZBaJc>XZ(zf~;Nkw~e}fGJ!wL64{}0$OFj#o}`TqgL_xST)!Iptxh3B9D z3APLjKfM3^-(btY;NkP<{|Q?Lh8Mnn{!7>~Fm(9+`5$4&z@Xv(=l={l28Ig%KmRY- zF)*CS`13!*o`Jz4^Uwbo_6!UwGXMNPV9&ttBlFMy7xoMcH?sfy4{%^$c#-|*e})4C zLq_hO|0^6A7&!9&{J-JAz@U-$=l=(geBPh`7LE)I9{GR%2RJe?oXG$4zXQZC`1AjO zBLhQ6;h+B+P7DklMSuQhI59BnDEjk%g%bnAijqJ71)LceW|aQ<@8Hb9@T2U{{~OK> z3=tK7{uj6~Fx;s4^M3<~uKn|0z?Ff)qVCUs4_5|;7xjPsC%7^&aCH6o|G<@jVMWiM z{~m4(3@;}B`9A|hPyO@%g&PAy#Ed`xE!;un{h$94?hFhPGynXb0HSC8`F{Z<KI_ka z4i5%~jM;zw2Y~1~fBwJlU|`^w_vgQbCj&#qyg&a7JQ)}~=KuN6;l;oZG5^nh1uq7M zjQM~5TX-=rRLuYLKfsHDp=17^{~2Bk3^V5c`QPBhz_4QepZ_zw7#McU|MP!?7pVOI z^Pj<+fk9*OpZ@{g3=A`t{Q1AZn}I=M>7V}$J`4;qmj3x~;lsdCVfXichYth83!lIL zZ-8k3zyCFS85k@A{{HvyWnicX`1?P@mx19#z~BEJz6=Z;L4W_>@MU0-2>Sc~gD(R^ zM$q5?0)7k(6+wUhYxprRoCx~+-@}iAp(E_?{~3M^3@f7k{(s@ez#tL(_rHZd1A|5E z-~R#r3=9#mfB$FrGcZ)d{{7$J&%mG&_xJw}e+GsfS%3d?1TZjkWdHqd5x~F@k@NR| zMgRkYM(*GLGXfYGe&qiBe*z?5@b|w#AOizO;otuWfeZ{Ms{j7q5XivL(fIejK@bDO zkH)|MJ%Sh*GMfJWPY7aQxY6|Ye?<_ez4iD1gdkA+>F@s)L7;Zg-~R`K7#LQx{QZ9e z#Bcrk{{x8M`u9IaFav`|+u#2R!3+#5+W!8x0P)-Z{tp20+yDO00P#Ei{%-*BJO2Kk z0pfT5{l5Xk@BI7!1c=}D_x}SBzw7V+A0U4B-~R$33=Au}|NhqqVPMec`TO4i#P9k0 zKLW(>{rkTF#P9w4zXQbY`}=<Zh~M}3{|*qp|L^|`Ab$Vf|1UuN34i}HgfcL!nDF<% zL@218^Y_02h(Gb~e-99U(%=6HApWGk|0_WJ$$$S(0P!dP{l5alpYr$r0T6%6-~Ts2 z{HcHce*p2P{{7Dp#=xL4?eBkuFb0Md)BgUq0P&~){T~40PyhQr1H_;4_kRP3KjZKJ z86f`5zyCLY_%r|hKLO&;`uqO@h(GJ^{~sX!?7#m7!WkG=%>MgdBb<RjW6t0I4j}%V zzyBjZ{JDSs7l8P4|Nid?2eohh{$CKzz|b-8@Bba)3=BW!{r!I-9F!ma{(lk9z;I*! z-~S8|3=9zq{{EMUU|=}0;O~Ef2nGg=g@6BhL@+R{SorsULIeYY#G=3dD<T*eW-R*q ze?kNU1IOaO|5rpXFjOr5`~N@$1H+HSfB)YAi7)y4|3d@=L&uW8|2ZNV7&w;x{jU(o zz|gVu?|%yrf7##v0U-XezyC8p{N;cDH-Pxd|Nfr=;;;Doe*=iW;_v?xkqitPEC2p~ z5XrzWW98rfKO!Oh5fKQ*z*rT;z*r%`D9yvpF@ceRL4|>VL56{W;YPvl{|>AS3=Dh% zZhR76{M_Xn4Gi{D)>_6YN+7Ku{h&VQgR<ZMLCQd4APf?Z0qHOM{htLS0L38j5(Wl_ z3s}_mFfcHjz#_hcfq~%w7V$j{3=BK4h+kn~VAz1g-Zu;k3@fmRvoJC+EWl!p3?l=> zjI!VV*`RI*Q6Tr5FfuSqDEs}N3nT!=AaNf?28NEZ-~XARVjv16p2En$&`|dKzZ^&a zib3KvQ1$#!F%Sh3p8{1c4HAH2koX#?dK;)1hysZpVTAZs10(>&An`klknr?`ih(GQ z_!mY728QzA|Aj#UPz)01VPat5DF6Lm3MvMoK;kM)3=9J0zyE{c4I~D_It&a9HcSi* z73IJG3xUjY<P&IPa^#a}X7=P$@aE!^aO4wk<m2$*2C0Umr5q*(hK!2e|J^}CA$$U% zTznkvP<fC&JxmM?Gb(=n7YE5hF-Uv~69dD7ir@b^p<*BkB)$i#-UK8N!Y2^V$H4#+ zzrw`8aG>J%e>IRe6obUyFoEjS-~VNyVjv16&ce*V@Sx)N|A`<0C<ckkFf%Y@RHDZJ z6b1$c6J`d6gv#Il{XzbA;uGj+a^;igV|L|J=w)%@)97J!<TGevbL6vVW_RHWsO93b zaO5*^<kN8CQ*h#waN-ki;^T1TuEZhE<bWyR#>e5s&E$w-kSiYt)V&~gondBRkf{3o zKY${4Rbg81#K+;n&E$wF;l{_|$<5@1$6cT@B!z{6;X>8#|Gpr%M)C<XGsPW-IUJOh zTR;Uw_3!_MAbCVOa_3VBfu|!!ZeIx7g^$CHyBM6jT=+P^Wz-QC1_p=f-~U5F`oZRB zz|9B6;}aGJhKB0j|MNlePz)0P!@|IDqx$#%AgCCK0=Y+om4QK_=J$U;XgKzP;<pzR zzdbB2d>U=6E_?>fY^hv)8jeUY3Q6f8{UNLj3<kBo|BFHO_b|EeNwhJ$@F_I2q(Xe* z1o8#b3^>OXnhh90`lqlmFbLHB{?A89|1`LMST=&Be^v&D2X&bF<qazX!;QM%|6yqc z<X=!Yu&^;OT)-kO!^XgH0*kl_8w0}uEaE<F3=BK4h^MeIFl@jgUc<(~u%hnwe|3;U zpcv$yDLBkugT;JMnREndz6TQn1A`NvKp&GQpF}US6Q2S!zMMhvRgM;4ZroL9BJSKw z=#lEe%`_8D6(pWP>Dq>!fuW<}_kUZE+g(8Ex&oH0!SN8o&cLvu;rIVCkUSKF{8z%x zz#!20`@b_(3`Bv%d)OHmJQ{!h=K%>oF-Uv~I|GA5<M01IATbv{1txbs4v_dBb_Rxu zM$B^j3OfTsL*wuN3Ly1R3@WeRfZW^o`+o{Z44lt=LFKgzpFt5MAvuDI8IU9cLk+0@ z=U`ws(fIp63n*OOLH^6<0;zE40%dzYE<O$??ouv34sUKIkc1<502iph59R_DZxAww z3zX0yY!K<l9nHnZ;m8frj7s`L#9;vl3g0;#4B)XyaQW^5vOg7`uR-#AI3Qz_pghCi z2$BaG;=!HF$H4&d?;Q@vSfwGzOHO<OJ)rstnh%rsB%JvKKqUxBk^!uqhZ8cUNvwJs zPEgzZ_kVD`i)MZ}pM(pa0HS;b#a9kgJ$gH-g_D6{18D3N;uJ6mQa^_iGNy{$&H(v) z3nv4^f#%=;`#>sP_yj<K@5-GBu1!JyzQW1CV9@gW|6`Cm!d;G_@`vd*xaf3(g)=Bk zv2Zakd}#UozXe*}^fUQ@>N6)S^_e?g11Hlp%!&Y9qk*gg#YG4g1A{@^@Be#20toxT z<sVZUB&mUmacGh51WGj^wG0dlpf=+aE(V4TZNLBT1c^EE3G_0#^GWnDJMt;Cu{iQ+ zG_$(!1u!LW@o6~oDL8^FR98?ci-2TQxQIKq3!jDyk`j<{ptkB4E(QjH_TT@HLj8-J zE|{J`+~)*J+-Y#OJ2$l82D_Vq0n}|}u;GS`DZc_440fjz+?~!Kce?X!U<6g?8XhoL zKx$G~WS2sM4wS}wxEUBMI+63I3p9UbLmUFmpAm4j8#e<3C@&x3W?(4j{QZ9&$UoqG z4c6zzr@-U{SM13h%LPvJj-W<Y7F^tio2dlOapHD?v%R>PQehe({?!4E^>_XLFATB= z9IxPd4U(=VLo_<^aX5pTHlXGo*t3ohaaT~05erUx;7kWfcQv3nh3?<~nX#GI0nrDp zC`;jN4{n5cj@)SGodL~Z^x!bB9d3p@sL+DC53hMLpt*?N-~XfG@#_kYUuZfs$mK#x zV@`Y=zMw?sh!ou*z0mZM1DXTr{rx`~BnD0|nC6wCndgJUJW&5^4=)3QMepzb)*$l` z@$Sy2kOK~AM`(Na2`>XfK=1GWzMyg(q!!eMWnf_V1Dbp3|NTD+BnH+GZoj+mDdZz1 z6*o8=l-@x3$Api8;XwcI|6f7+!0Esd)V=|ygD7|~`*YXA*&d*}78>N<+#qe>_~&C_ zsF?8kzc$ES;P61?9~V&m@!~UJN<_;me%#DnjL3Q42~?ke%kMLwxu8kE|AVgo0@Vj? zpmtX?vkRXA({qSZ9KmWq_UC~7&&R;vG5PoZKxq2wWkT2wi(jUtX!d)9yoD%MoIn+# z3tFT(b7vqGPT;Z@RG#PXGcZ_8LG34l%JUX}1_lGrTn@-#;ChUy3|z%BK>FcJ_!$@u zOo6s<!Se2W3b|Z-p#GH?I5Zi+?Zz{pIjt$b|E~g>18xVog4#i@pmvZWsNL!aY6mgZ zL41VN6=8tZe*%y>ByW&@M0x<FCwLlwH8er-XCnZaYuW*gzdlg-LEBwm`@rpFW-BH% z3td2&Si=>m?d-!13Wpv628JKgfB)Bp+TYBS3ah5T?ff+Ykhv^u@*ww|0nOdb`29Z# z6rW)CgUdm8J_ROFvE~d8Fc)s7Ot^2nxk05VOu`Q??#2yD=Q4tjIk7n)_kh#6FFc)t z5|uL-pMo<ur@4Y^Fc&x%TxfwbLE<Gvkbz;s%-{c4Lc<9;ow<S1xeK2|4kU~qm6sE! zh=%9~rJE&!3=9l&P}}n$|LzfFVE8fn_y0{$bNfNz-v?^<Li?d^Nd3@)9IUlAEIvW? zHV<ena^CO%3Q+T)?sNgwyWsrj!e_v|8I<|pwW<>zhYvUfg6c0HAqEDI1;78Nft(Hw ze{gx?&Zm$8&wj4p8paJA6`)QJNMDN(14F}t-~Y4W`e5-E1lQ-m9Rp{B(mW`=>=9yM z*s<XEe|wNVKR$sFJ`OiN0T2h0p6`I>P+{`m_yd<yexP<x8n|i$S4ChApmq}vXl|7R z^){e6)&;-+n}JL~Gd~(z`%jpGfn(wC|9T+xetZIP&~|EzFav|aLgLH;xpRv!1B1ar zsK3$d#pbUk!VC-!M5+h1CqzUT7y=gl{=W@0kcDl0!v!f_B`~>TO;{e>%x+9LWEdF0 z{i7NY28I<&e*b3&g$ERa()$z<28IPoP{-Fm>12%vXzt+m|2mLbl=M{rPaCe>{%|%Z zZG-eZ5n*6>u;lmuYPddZ=_?nm&y$;h0i;hx6f#E*YLi0h6Hick%nj0Cf|MFw+%9|) zkmL+99qg|VQ3i&9rN94+fkhw$D1YUMGB89e{r$fcB!uFAT;m)}Uf|-zk&gpZ9<32& zU|6yA_x~!8esH{d;wX>61&AjXpMo1$KctET>0$tl6MYe7V0f_f_x~tRIf*TPi5kDE z!5X<<+)U1x5^kUZjmZViI1$KQJz@+DE0+EKA5D?Fu#dNSa)ass^no}xP``o66_2|> z@uniqz)-OK_x~bL2oe=<CE#EOl`bItDdG$a8<zk64_>l^sF%Ux6X1FhTrY!0Bb<@y zL`QJB3d#>l#2FYaEdTxg9>{(u2ABWh3=A_?{Qe&c5(BrN!0kzJc@6D<7=Y>(H>BpM z3#dH_?>B*)CLrD5{tS-<1H+0HzyHU9M9|Cwm;dg327YKwFBk44J`H3GKzgC=-53c5 zh8-(@|4#skp_zy5K4j;BJ8f9Z1LeUr5)2F{R{Z{-2udGbd;-vZsw<yDFN-T^+!b6d zxq-%A-T53qqiYt9hxrT~kMU_Zo#9h(I?E^FbdFEJ={#tBf~k#<<M6RFXV0AnxelCf zc_bMaZmfj1&%vS$44^p)6-fpLgH;&$1$+NEpaL`bgByu;TznQTNaMh+e4ue)Cs1<| zT^yWU>M)dpMiW5p*&@lnz_1!4pOEODdaUko<i_Goa5kyO>K;(~c9CLWxUibM@mtV1 zQ;QS>gTR{K|L1_>6<i)Up^iU*+cEBtcnx6k!WzIHpdPw~8&Uv62HIUP0vI9=>dY`e z;+I95fx%$y;ECTUpoyP#gC~AfWEdDOtRpXeLFHG93<HC}`rrTWK=Txc0*Tjv)?%&y z{r@gV0E$8VvMDkQ3=1~={?83f576;<aCr$He+QSBX^<8(Qa7d)E&|D0j@%{4BH&b9 z3LCU{g^bRD>|>E-U~t&@`@aZ@_JR89kQo4oeIB5J1?ZfBD`>uy!<D-X%|4L7OJo@s zK5YE`e*@IdAPQ8T_kh-xZTkIxJJcP>^Ge|I4W(XY@<hwPkaP{HHo)ci9a#p3ADgLT z9?0D~atsU^TYvxG2y!qKgT!6r7#IY${r=AeayOEA3}_A9_TT^6k;Fmm%M#E;=#Jn2 zLGyQ@@p@=K12Mi=!-Z61d2%!LLHLkR_Tpxm3+IDNN~TG0zAI?>3FHJ&d*Y5914G2F z-~WR_1Fnd2#gz|MUx35Y1yZhTU}VBxa=UOd|7XT5a$G@kV#%1|ZlEECbWCw*8w}*G z5_tv&h5f((^MWRp!R|tvKLpof;Q2%F2x1FX(>%DD%~`O@xN{d^6?fwXmHC)+k)U>R z3j+fKj{*b3j3dAQYcNCRQ84<Gp73^mEf+>X>%q;m3{%vRn`srMs4F+qR!mV)zYbI% zOi^H9a5(<^|5i}CK?@&5J^<GTZhRXUnHd<ddKWUCgHpG`vWN?6T?P>bB^&U(hlnBr zL&J&R|4-qv9~?i<pmYPaUlFJMQ8=<FZu>#`X^J8P!-bQ-|AYE^3?bltEx3KM2DG;G zBy=7%hEE^@Hvg%}z@TvI_kRnJ55VI?ka-Z$d^l*F@QES=gTtxc|HDA?;CZS*@T^TT zc%sGwJjMd*PxB}-FjSoS{ofR%E)X=I5(}Si0riJ;lo%LhocjGARAxf@L(ZW7kUO6O zbRgdm+?@6VH-15bkI-?(6eR|R3#Wenj{unqZa-twR|M7uYBPhBf!s4kiGktAso(!W zZ43smd(u(dvqy=6LE`l9|KO!8IL$@O6@$zL`QwWc1B1h9(%b{mrvq9uOQJqde5NQf zFibf8`~Ovt1E3g`&TEtz7$VLfw|l_l0QPoV2P4x3tjXMo8`KlSZ1}ixGx_jgX%Imi z0CLwKWd??dv%ml6L4?30DBML<7#J?V#9jCliommepfu$P9v3rFVPH@=_xt}}kPdJ^ z6g;j19{*)3fOMmsK;!*P9&k=5cOW=^VLc9z{WU5K3^&gG{trr{AfrGSBtAujf#JhB z%=H^<R2UdI&j0=oUU&pj4DDwe0lDY=@BbFi{EAVZxbqprq4ir_xtZp0A@y8bKs{6j z$h@bBDg%SSh2Q^YfYv2|%OAA%D7fB$md^okSYySVoB0oD_yaRu9J#Zw>VV8EgZ;0{ zz+iCY_kU54A6@ta0{A$<_4yiA1_p~OzyI?<{RwR!U{5arOn0$bgVa9wqsqYW<I3;< zAE4$z!wuYj1o!t`_zY4YEhzZ7q#HLg7y1YwY#h%=je%jtwcr2SK<0t-BW(PSX%-iF zJj@X^P711LL6f5{AP&gCEoux5AFiX0i-W}Hs4+0SKobX*iCfed7#>{z{XZQPKFIbM zfF_Du;A4v*X^{FSY77htH-7&=01`l`2hF4hLlUSXs7DBr1<xahs53BZxbge{b&v=+ zeS_z@AoG{W5FM~NOHg~pMV)~m;pXrEp!HCo_6%~piKwr@>BWU_0V60^pbtcVCjpt) zfGQHq6axxhkUeYE85k^X|Nj3H6u#hei6}2H?3us_>d0YM_AcDaX4tI(Pt1Vq5z$~^ zIB@6pe|G3NFxmde$8Hg*o(I`e1KJyK@Av=BaQ}ewnJXVk`wSXx6Bw~AKmxa_n6*Im zpn26D)DDA;zdr%_=l<{i9#DIr^AzawBCU|54)0Kah5^C(PDPV};lhL8|25#@;f~rr zMU)3Fd;!etAlEo!w2wf(hRoNNXfiNtc=Y=}D`+7OI3B_N2IpgNJc8R7&~|A8^DB^f zXubnaPcyH=Uc!LdbD;jj9Zd#?h*!V=|A3}ryye9LCZ-gu1qP&}3@W2_v=|sTUjP1I z3^LD!PXM&`!<`$niqD%n2;7Q*%vXhIF)$<`smtZ!<M8JOwK@Dz)YWJ)Fzg_p4m9q$ zM2mqz;LY#<2SN6N^P>lR+!HjLCE*AjN^#-#gyc;Z(A+(Ap4f?xBLp;%1`3ZiS_}*` z-u(W*7T&(av(8}w7gH>r@Br1{HrfmfKi>cT{{xz?u;&wKJT@>gXJU^?H>^XG;23t{ z;<La^NuVMe6fS$T85k-){{Fum9xjM-3N%0E3+Wa?LdTJt$qktgiB|CX0Tvwwh7TWq z|Nl=+`kufTj5SysxtUe4Cr1}h_=CbLM~8u-<IC^=k?`;bmtU@YDES>6UM_qIOh>TV z<H60;j#a{)o4Fnoz{q24kcK^Ih6-f=8yyCQjPJkyzlPWAVEe)O4sG2gsNcnG3Nj8e z<2iCO{l)46M{ed2P!M3#0xIuvbQu_K{Qmv_D>31`fH4A>CE_4UFsli8IA76aU?}+a z`@cQB-;3X$Lb&|NY=@=e3ode*u(x^~xk2F^qQ}5s!2IXG60Ud#mv7*59W+k6468q# zxtX%DN_cZKr-B@e8P9&)ka?vmdJGH?SpQ(OpWcA>bg};V?*<QlaJabg!O|btU7nzR zoeQ4>(-*APLz-#ec>x>H9x(Pl|F=Q&AGH017{_quGhk|l)GF`>6r>se>#xygV94P3 z^Zyt={gbil2c@Sy`j9<h_VDrtXT9Xk7r?X^tACuaPS}8#@qnBKDmPUO7#J>a{`n8H z2U>rE$Kw$57fiPyE``^hZrq3gGDt>r<_7z_#DIa}0N0=YptJ&NFZF`f*Y+@bz~`Ak z;T8@t4IXZwLXQD5ue-&7fq{Yh4`e+rxIG7*&ocnc147z=pgIubesKHGfPuk*`_F&S z7zfBc<n_3ql`fD15}bX&?h`R&U|7KY=Ras40YtqksJ?Lp%^NdCLh2ZB`!5mBhG>TN zM?wr47#w*1{C9<>GiZ2$+Z*8VOUSwi6;L}GGJXgN9vALNh^HYz=nh(032Fx~0h!15 z=f5AwLr@Hgmpz6I3>AET{!fR;KcXE0F3-T}8Jy2RE9OAuDQ2dD)&rnEHjfbl!wLRB z|L2430MAo7p{^eRySotCjo_ILkaCcIA0q|^4S_%ZCxHZT>d!*c4~bY%|GEdX*HPfl ze-Ds;<akv8EnS05vU+g)g8Pxs;ROcJc-j#o28IO!fBrjx=0(8mDL*XjGY?R{29@hf z|1dLxE2y=|%qfg111|1Tv5JG&GGOUpgWBt$bQfdHz!0JN=YJ-ue_Z$!K<gSH=@eR= zfcM+<7&9=;fT;(scMahJjWU8319@>n#%Vz9ur0<63=cH_{0Fbo@du65gdFDM@C7j< zVFM(f^5Kp#WN+prc>3_f5-x$DZ~^s8nEkOAtL~uHx>!0aV5fu9r;7;#Lxc7ol>I-T z^bljhz)+$6=YKWS{vIZ9yDbouk6ic)m_-?p8zdgwC9vf*PJA4o@S6kLv#Rsw|71}3 zx$p_paq)5ZaeISDtw15;18+@2%KbAY3=A5&e^B<1fZ9n<Oc)ppbpIf&M@4pr0aG#Z z!W|FL0u==pa3TZu%0QMw%-1nxU|6C12QuFZ5eBala4}_I_@Mjee>q4D+&)FL!<fR6 z$_U8F7<h;al-_GVdvNvs{I7@W1G@{-&jc;IgA`1j+>v0{fc1gmb&DwjLxbL*|3=Vo z#oq3O=6eUG3>=BqhYQ>A5Ge72_V$3*f15EdMCkv4^qUd+8C>pz%Q5gg4AWZJ6bGaf z19=A0?}{;FV7Q_G2eQ5pw|;+Y`a%AjW5&S1VesewYb^dmE8jrv2<-KnD=5KW$)wP7 z50uVW%o!La82<VH1mtuVJ^?0B5_5!hY}~+uUs2$7><kRx^2WrRfg!-?&!AgBamJj1 zLBjaYpj$tYV!^=h!1xbY>nA|-{!1(v7!*wY{QnGUxBKu3^n%u>_ki}(w1M{HG_!(Y zKOI!99OhGSJjN&CbcRpB=`5(|1Pw=OIHCzVaWg+)hRpth^Dm1f1H%D}KO~JKgUUS{ zO9qA)7JvS)1*K0=+JTNAhFCH%G+6!lF9=$=53Y~E^|C7;Y=0P{UUuhOfMcA-k(-&1 z6>AQ4<i?SuK{Y8T-(0a|U{G-R^M4Ms{efIQfb$JvyuytyfytK(WBm)b(hI?AJG6WN zwLfgE7#Jel{`|Lq_cOr$#NMC!z{r$_wKoef5M*AD6$68Z_n-g&;Qa=i=DF}~U}TEM zY94qP3p{Rf$BKdBhu@$7necG%z*7G~%gF+!KUj@}w(LOT;yTt03=)BVAnW(Q^DLmv zEH2=h8C0J8STita1pfICTB8A(AAyg5`oPCO-MFDOf+Kju8B|ZUSTis*1pfK|1(b%s z<)jC^-32aZ!R@XB$RcA%+Yr3K3#1%m?-6ST28W<ONb4@a?O_)_g*+}k33u>#Xdr0( z9kOH%q>TYoFaEIx?UnxnnU@FK3vTa$%Xe_Q-W{}F*o7}36*5<c+*SppHIPA|_Lqwd z1A{;?!u=TIOo;Rj>Q{jlqGAqexPTV!VQFQ!gUfGB^`4-58q4G!wBZj*Pj_q>7%GDQ z{GSBMpWu8Li97xinAt$hAI!cxBt3z`S;v-v;Y7$E$bJAs`_u)z7#kGM;7J5O@XAu; z_GyYO1H*?9L^vad4`RFznhqVnU29DLx^sJA&8KeQaKKa#ZpYvbA5i)A$CiP?BJ>Z& zelrm}1_lEx;yR!+G_Z)f*fB6DU=fe8V_=ZLB3@$0z#xD{yvL4#fdfq(6pu^n7#J8r z|NL);$0N90aOH!Qr-<^*g)f0w7<)WAfrg>L<-!*`1_p<)KmRYInuopI3SbV#XCA12 za<OM%xDfv5e?HVajBx-^s(%KmpOBU{cyWiJiMVismb4%*hI8TuyLX8_1H*%eKmTh% z84kRT4zW)GJWu1!X8>C1g1l_bkvj!_3j#<txW4;h&%huM`3Gq|EWDfwfV5V@^C8g5 zJMg3~*d00!3=9#GfBxT`fn$9DA|BlNCNML928~l;W+-q(fa0|Vbau+eKmSwtFwFz! z3$S_Md3JZc4~)zcLFQqmbZ`X%GVcuNY!-&U|NlVC`(7qD)crc3cEWzNQ1{|ydW|Lo zDndZzy^JFR!w#mu|H1p`UHG8;)gj~EAbA@{28I_*e<Axn!Sw-14l=+4l8<p@VBlc> z3+cDJ@Ckr6LqcRB`>ARi85k^>|02y>g4b`lg2q9>`%ggbMW29k0*{M<RvE#EMnLWb z<>xbw3=A49e^JgQ0J-CdBLjm13&I`Y;2spnt=`-qeJoB43;`^EQPyFD^vQtEtijL+ zPDbGLVB^HVP{H!|{{eV?jkAC0&No4u`4A}nFw+CLh6Tl6j}rsK3--VNr9tgHaCwbp zULbt^3D`Uv?B>DK*B#JVJKTT&r$W<1Ka)4S{0FBmaJ>W`Z+7Qv&}I(7p3y<U3toSr z<IKQtgZD3FToDosU~v~`28IV%#ABQp7+&!H{VxPn3!*^vNQpB8!w25K|3T+kptKVV zm=N=kPTbY-yyy%m2i7<<Fl6xk{ofDTAPf#4M0*D8A8>vF*CSqh4|JG$u=@v`UqJny zKh6vcE2RGZKLK(WIQ>TRC4i>IK^u!e3vmUUp|d&+;CWFU7X}6oY2ww#xG*rhAVGbP z3j;%k47&L<VH=%X_>kNKYUl28VPMdZ{rkTU9>3sv0j(Sd$FDzMK`7Sh)0La~GxlI| z2G<v$a8hw)U<i=^`@a$tPKf?JWW57)y8*0021>^vt_%zd<p2KP1#kc3nP=L-#LNeB z0OrUXxTpY)kuGs%U=Yyy`~LvEogDyA$7t<&(EQ^M?1T1B+~5g5OnY3pnI7W^8_2ka ziW>t%fc{^|I2I@jp!+v$+!z>E=>LW6+d&c!absX$Fu)Yg0i8)@@b`Z%R4s@Cl|L<R z3=A6#{-Ufy0g2CXV_@Jg{0muc1X2vOcZ(YXLxSO7$UYDx@iT4=3?~f#Lh3an@h5H! z3<gGjA@wJc_#Zb0h8aef>P0{Ylo(@*>$o#8WEf-G>*CJ9Z~=>Wj5`B^g$bs;CGHFi z3$TdyxHB+tm}075;?BTOfJJ<dI|IWFQ%rYWac5v~FvE1`8+QhV6=s;~Sv(jR1k5qj z%Xly_RA3P|@nB$hfJNNLgMq=r0@M8|9t;c{u!z@qFfd41Vyd6w!NAafMSP711H%g} z;zv9f7y_&?&AH>jz_0_0_!kcb1_f(O^*o*o3>{d+RXiCOK3HRhkBuh-Lxc@xc!qc~ zFdV=lp5w{Dpka%tzQvP)VFDKMIi3s*Kd^{z@nm2~u)}oE8BYd=6Ly&Xdg95zU|^4_ z{*NaE!wh>&^&(yj3=9sK;yR$S->`_gcrh?sz#<;w#lT?U_!lz1iIne3ycif3IAWUL z<Hf+h;e;u^#EXHUzzNg+d%PGJZeS6=;>Ez=;EZX`8_?NwSj1Vp85jgyFxAU=GcZ)R zV8)k;Hv_{1Eb4u{85lfVG0jQwW?<OhifK*_=nOo!zmRz>r0|*I&A?FLhFN~D@n&E+ z;r91`0aPuB0+o|Tycrl2+%e0~J0SOC5&z=Nz;MC+FJwFpWC*le<?#WXt%zx_iVp+B z1P?6bKj@4;56tp8#D{^w!4p$F$A^Joh9_qDw16s2FHH4wd>9xayfFQ>#fO1mgBNCa zp7CK|knsMCabDLG9|i^iZ%ljtfX*EB#!Octz6=Zxu!!sUg3gx2RPW-;z_7sw)BG6F z*@V8B;w8Qe3=O`R?(gvhomq(~zQmV-A;1q)e2*^!!w$c{koh*Gc)a4vz~JEj_kSu> z3`Bv_$s1n=h8g~U|1SdxjN;J{7!85Z5Eu=C(GVDb5V!!{%sc@~qdUPXgcTwLJ<p-v z3d#rd^%)o#CWb-yP-zAk@FAt3b}K~N_5g@HR5`;-C?7Pm1X7{m3y}vc!3FUPpyn|! zFoL)Y^I&|a23;SBdWb9ogO@*q4?AN`8OnD6>t<l^Qw9x+F)%Pd6$HZgPy>vEAo>}g z4uA?X{NRNc0ONCj#zsMVvmn9@2W~(ZP-P4fY!G`uOJ6`T342sQ6a#1rDTo0zo&k2I zX9iRrG`@!{|MefF84BJDz`(!&bsa+l)L_t7HIVurPzGpd1H=!7x(_^9!oa{F0TN_j zV1T#^bZ{%gJ+OE<!46RXYL9?eP%h}&01%hq#|1FO02A-fgz#bhC|C~R!_ESQ+4t{1 z#Qhdf_5Y!KSU7%w^4CER8~h37KY{uOV(KUf3xR^Yka&afVe~g>`eVBZ5fOvZYEaq? zO1nYnFese{rOTjn8<d^~rI$hJZBY6cl)eU~pF!zwP@3%))P5+f2BpoQv>TKTgVJeG zx(rIULFs8wdKr}72BnWd>1$B>8I=A8rP*#n?T6B8P}&SiyFuwND4hnS%b;`{l%58q zmqF=mQ2H2@z6PbALFsQ$nhm;{S`137L1{B6?FOa8pmZ9PE`!o-P<k4aUIwMNLFr>q z`Wlpe2Bp72X*TGlZ80dV2BpoQv>TKTgVJeGx(rIULFs8wdKr}72BnWd>1$B>8I=A8 zrP-kMh8UEF#0G*qa0q+)g{g4P&n?KzNlj5GN=;9#P{>cpN=*ixB?^--PEO3rOD)nU zE-A{)OV?2-$S=0_%g;;IQAo{8L2&ZRQj2mD3kp(GY~2!bic@tIlJoPDON)w9^Gd+# zH5GJq?G&J<>2WD2C@2J_mXsFdDJ15>gcZ{Aixf&SQWb(+6$%ndN>YpR6f*M^AXe)s zD1>CBD&!`XBxj@+E953tDkP;UAnXMjnVE(pqL5jv5K>f{3O;5C<{xxAEi*4AvxI6M z1R0fBl3#@GLzp<&c?yX+IdG2<_A}ULIQ^}F-OmaNZuvzDsfo!M5Sw&B0Sh(>?vx^M z_!lcAD)@o|)gK)7=z#?apTwLTD#d?JW^oB>&?ta}6!Ozh!X!V9$oL0)2$2SGgavw> zBlC+3axzOmK?3(-Zej&k(AEG)A_l3(8^@KIsW~Z`dFh}aS13p<Dk%ngPyuW#EIC6R z0TzSX4N2-c3IyE%3N=VT27^2RHB=#~5^QIFG9>FIr-E&U<|Xu~1-qcQG$}1lBQ3uu zH?c$qY7EHVrFkWw07%P8OfLp85Lq21-($_?8hNSZFrVa=<|d^Uf&5=wnpCU_jxcD( z2j%pV(t?~+1(1e#R2}j8Y4IQ(@wtg9shSFD`K5U&Anz5W7UU!*LjxCVT25+ONp5~| ziGoIckwQ^sdIp47o>`Kikf>0UT9#T=oC-@%nhJUOdAhI^12P6FtiZ-0r9H3_AXh>x zP0dZsD*=^Jxrrt4uq-Z3%F_VJj|QHC0w|iHiA@1(;DG`P9(3^ZN9E)PPkrF{L`rOs z6sC}$RFasPmx`I36!0b`uxX?uC*;t8S7V?wT#}IrH4K!Nu_h~U*no)Q(j=-TuOS&Y zn8^kdG+<nunpl*a0WUyFDW;HWJ%#)t1yHL7TBL%_R4B<P$}dgNfEJ1hIr;h7paKI_ zVJE^W)x<nd%L&qsQAo}&DoQOb$j<{6{@@q}*V@oVl|p7-aY<rca;hG@=>xJ7RA=So zL(BuUSimJ6B9Ek%=H!4)p{^IfepjeUEy@RZJvYB7RUtVeu_!UQB(<nmAu&A><XCW> zoSz1+QXqK;;Vy9CGcYjF-)$ve_avpJXXfRB;sd_}!PR34s0IQzZ(z=b(~$ZX+ByZd zaX|qBiWo>*Kq@}+QY%Wp)j23*gQ^9vUTE^rV+e+{l%cT=vjCbtAr^o`9n#K1wIvvm z;?j!pbHToYRNp0_)^=t|W<IzX47L&MLgZM0gg*h}!S*U7LPCy^*$~CBjzCIkNn&ya zv~t#f)ZZ>(vD9L)Q%e$y(o;*Ip`ZZw0oYy8u+Rgy+|?mTNL?YZq@*Y_sk8)A2$pB& z<R~PiCg<m-Lb4z%<REl$L1JF9Ms#{nerbWBj)I<^UM$43pcW7)B8+q)ZHW9dh0Huq zORGo$E`UP~M$CdOQP4<KKr##z+@QVzv<(H~7MCOzl_-F+d}>~bCdhJFNuHcoP*Pe1 zX>H}_rGg?CoTrLQlfW(qH^V?ifP+O(LBSK8tBO)VIX+RLI5RIj2NE2KMd_uWb_Te; zu27U(T$)n?Q3Uf0*s<22VxuG@wJ22~u_#p`w=}0D6V#G|X)o3Rmp>pYk<CB}nPNyC z4RUX0NouY_L28i#Oh20WdHG=DV6w#!*MVIE4F(0|AOKlgo{^se?XH2`59w^Arsyds zxP#&nY@Pu~22C+?;$~oANX<*Z8~r%r93+7e(P$A0G8~)xK|zvVq<|5HU`K=8i!U1C zVF(E%<lu)zA=DYxVDmCzNdZShf^!+NDL5h#Vi>`QggXx!iBPw|f&nQ=zy^{StqMd$ zDI{Y<NpPN`T?|8vhj|X!Gb1>T;ei5<VNiNT4j2%hK;|VOmSHC2E5acaLouk-L`mDA zXoJdO<Ybt+AT_w65}b3eW>v7qA*C)nKj6-;P&c5JiLf#kY!+I{NJMs3z*^>l4MUXc zdHHbXL9;8ezln$`uzC3MWNJl0VqQubv<n8QS|R?$2y%!pO82Q06o%knhK(R37AvHH z;sZJo0UBO_wi<F%OEU6P(292ki2WKRsksF?i6yC|Iv^z<)C5dQOwKOONi5D#01X+G zWR{eI8%_CnV5h;%hE$1|4%E<8!0t9s5d{nHl+5H34T$SNOq9S$Q~(KO=I13ARf5Jn zLETG`vq13!(+^6gpkY6dSYBdoY6{4yC~X69RDn~gjzV^7C8U1}QVwfGD1h{#hacDo zko6iVscDI&Imn}}AYq~$ir-<7z6sP(U{@j{7i>MmVUY1zSVn-=>}82LrKt)f`Jlnt z^vtr<JkS7&0$3ZU{g_w+HZUbKMIkRA(z?z}&MZhQNd=Akf_x7S5p-XJVg`aCCSmbB zyd;4n+hVkUfJi6j=fNTi76!1;#%QvEMxT*<2X;5efzWVA_m={3egd_ju$7HSEz!)p z5|pA5RSMN(AP0erB&RV8%C`AMV1Hq>e83qA5*64RukdmTqZkDn2y&AS$QK&AhB^wm zhMLg!D<mbr!wM81kiG#Z$$&fqwiC7F1sez}ec|>(%SX(T7d#LJ@*8;tF39De%nNc1 zG1Vg2b!dulCtpze7F+bA$2qRnENIFG+<pX2$AFv(@+h{ZE7k}EyEPLu0{|+=b3lCw z9K8=v4>TpU0vv_9hFBvHY#31yh^@N<b{|sYflY&CQv4AJ(g^JZVb+$Jc`4u!f|!<( z3JWE)_yHAG=p7Uy+c@C16m?<{Y!nG`2=f$bFA|<1Fft;6I0PF9Duk&NhhXy(p`CGR z#vweMs1b|c`U6}1L8=W<kp)SG`DxHG4%7mdr2Ga-XRzKrBA&nj1@a40@`nxyK&=F4 zJJ6UE){F;ka==SXxM^5QPGT}1T7-d(!&-9U&3L#<O0aR@0RSTN7iccnNY7Z$K*2e` zpb|X2qo9$jsbFYnX|8KzU|<9qP!CQ`E=ny?@JUQ6R&aEJt~7$Q55iO+D{%V3hj1}4 z^g!v!P<kGehN#9QmqF!W=B$SDahZp1!YM_F{p9K&X!~LA-U#(KEZkuJBvu|hymsL* z4`wcmhM7-DKdyK@3bhAj9y&h`D*xaNWMvj?ofJ&{S*X0iM~Db?861Sez`$@DDu3Vu zL<n7fJv9Cc>LD@^5~3DFzJlsE_zB`MFrb_NTpp5sF5obqQ2`?V;2T6gy7>@M+$2u* z&~<tSI2GVFb5w?UA;1rf2iWpF*g8*mwCF<OUmU6qc9%J9Js?b-EL46KG(E!9!PaTQ z)Hy)SGl!~&*_#3N5=>nIR2@{9Y|0sG&jeAJ$q=JvP|18jSpM1x$={IBVqj1>3tbll zl@~yhZ#V;y{{#)M58%uF5bHS?oQBAIL-jM9L^Z$R6huA*>OX@MsPYLXA@X)m{RziW z<rPjq<RhW_CmchSe{c*U{}k%J1n}i|2=^sG<>R6H4;+VtFU<cBjzaX`fa+hc4%K`H zsC*_={|0pZhavhWLiJw&Um}aJp8+ag1l8~G780-M<y$pWJ^(5YTi*&%O_+rE3R7JN z)Lahm;o+F_qaySP0Yc^11q$=|3}`$*fL^)+J~NR7uL(fY9ZWyE)+JE$1J*$D2YUL2 z<-Y@~QRN>%%NJPs|L_K4GP-_P`e%T;gIei-CDh$7U<=G4){l~O4go^xpF<B;B9rGZ zc>08v-{|QJR{uGm$uBquDZgR$+k>^J>DK`&4@<uX2&CT&P<d*l-yP8O3p!$lypS2y zMNSATfW~9OH(WI<s;Q;W{s*j`r~#FR>2`poZ&?2g*6suyT?|qRQ&$012kW21)Pe8v zVSw#MVqn+-RR`<uLATv7OaK{%r0ys*{Xm7urXEAhDF9!_i7@5|)EzL9w@`HnUtm6h z$XMv3=I`%Nbp_zd2q0=eWC5BwSijc+&E5rQ>i$5@bAa|wL1uyQ12lCYF>>%tX#6^S zhxiLZGB7;24=F!5q2ntJzacUZl7T_t9z>oWDqnympKu2vFASAWK$pJ-k%#qH7{HfM zA=;M<ZbIa@K--@V;L8V)<ZnRaC7|X{fSM04AEEN9Q27hc_B*WnO1KWuuLG55_z&?J zO#Z<&h`b3@J^*SyL=^*r!&Qj9EmYnCO+Mi=MBW7|Z-6G>a1kQ!36)nsmp>1Y_lL?0 za6)W``wuD)E58fU(Bz?s0oMLVfXc)C2bE@kwXb010jcdPh^;Ww5Ogfm-J|6@B1{H? zW$+kc>jW7{OkIG|0<sXkKrDn-P=wF|Q2GM2Txx*oH-L(7fYDI#fUgkq7eLi3K=~J- z@&Zu)1Ms1r3=9jPbOMydCBEQ3#M})~x<Lx!z7J43;Q>T`0+fCLr5PSV#1){l0Ms1{ zP=7y=hM2PeO5+kIWd8xEyDmWK4^Wx`>J9}codeAWRZzMEN>75)3!wBSD187*pMlbM zp!7>9{R2v~szT}=5h$$!r7fUz0F+LF(q&M(7fLUO(ubk+StxxUN`HpZ>}n8u6ri*< zl=g+vkx;r2O1DDkiBNhGl->%Z4?yWlQ2H^Heg~!hLTLeYh`s7i+6qd$LFphUodl)J zpmZCQo(82?LFq$K`Wlpe2c?<7mq0Qwh(T#pC~X0y{h@RclrDnOZBTk1l->lTk3#7? zQ2INR=F^1uM-ECGL1`~29S5Z|p>!>jo(!c|Lg~Fw`V^GD4W&OoX+|xGJz`K=9ZFk3 zX<sOv2BoW^^b{z)5=!rY(kG$xEhzl~N;7Cf?2&-d8c^B}N(V#fY$#m^rKdpYrBHes zl!oM5+$3z$haF-q!<7I3!S^#kax(*i<^TWxL2|{63=E)q{@VZl{|_?l%RdNx3B+Mw zU^oY*&q8T`D18b_pN7(q+p-xLE<^bjp)~An!)s7Jbek*#bh|DCblWNeq_@SuQ1$Qs ze;#HAhWSwUoPg4GP+D6HVo)rUuL0%TLiw%`X$H_yZ6GFW@1`1v!@$4*X(cc)z{Yv) zq2i$J;T+72%%I^h5DlyMA3@E14y8Xr=~qzt6qLRIrH?@A15o-Wln#K}!w;ol?frZI z|Nn1dWnf5vZoK~iRe$N>|Nn-}3=DT4{{PRx%)oH};s5_!%nS?<p!D&F|Nrk}VqiE1 zRe$v%Bz{Yu{Qob^%)n3#p_vQ8G$V69n08`r`2YX^PG$y%3Mf4RN_RkMkh?+hJD~i1 zQ2O6vh`t|C`uF4i|DBi_7=D52oy^;y?)Zu%5Ay0JsQxcd@mEm#BUm2;!v`?ElX(?X z|7)oH^T%L+Gd}~<jLeV0>KGUvf%#6%bD-)$;vCEj%n)%#=DUv}{=W}Ze-El2<WG=$ zx}fg42DRrFl)emAe+f+Q1cwtyeG}B4vru)5A3^em<9!Id3ra(yj{!Cx0XtWL^BP2* z)JF&{3FSYz3gI(D`46G|M^O48l>So+QFkK>Lhpgn8=>@8D7_y_zk|}(p!91fy#QJc z7-T@qNn`yFJ{LTd^*{Lh#uV27;PVud!Qz<=NnkpMA&>R{e^4IEh0=*!|NobP(ks{h z|Ea7D4Dnq5|7Wu@Fl0c*W4ZqS&t+v`fc1MpVTcRE$|#ULi2VY@fF6x;A0z-}>p%;w z2hg|xDFd;OgBT193?TM?sCp0^d=DlA1H*GB1_n?W2T}%-P6H7P3=I2aK_U<gQtt*8 z2cP2!5r&@j0pa{dvKLftLPQxDB#^{m<tIq)%D?~rVddyykN_0J%2W^+R<45Bu(B1z zhLx`%Hmr;Vv0>#bhz%=iLF_~j0mZPg7sQ2?zaaKv5CO%o@)*Qz0ufLQE1yAJSQ!ms z!^&w88&+0>*syU35E~Rdu<{!eRv<n|{2zz|4Wo@v8YB)Y+d*tt`3_>k%6JePR?dUi zu(BS+hL!gqwmr1$2eD!0KZp&h13+w8Jpf|MBdM1`V#^`1^^n|Ygv2&RVw)kcO_A9C zOF_0kus0Ih5J`;&lDG>JTOUb{E)x4YsA7T`$>4~j{x6caHj;WRB=&P8HEKxWT1e*X zMiN&?V(&y^t0J*Qk@N~7u|pUb7(nGjr3{3II++1h?}GTSx);QT)xRJ%tPTdTonZwj zH1C6scn67tvS<Q`0Ts7oU|;~9vA*X2|NkI4RT;>UI^esk85kHO|3bt;T0rt<AO=(~ ztS**^@<CD{HTob1RIC<SM%+U3`)wq)F_Jq$WeDi%dXRPy8|F6<8+1$#=q`tz|NsAk z_^dLJ@(SjjpMN0YFuh_R2DD6rwGkfv{|_B)f#?NgE08jfd9d~bh#d=cGpG&+$%F7g zBsas_G$8Rq|NsAowQY|7|NkG<zENgiV7U1I|9@B;2h_HKwR2$Y5|Ca{T@8{0u{Zqx z{~y-&ft}3*5(oJgM1$~>|NsAk+C_K&|Np<>|Ns9m_rcmmAT_W!0kJ`5f@lzi`47Z~ zxeLUGg$;-evmeBUwWC1n3)4X)1haHOXb|}fHb@E`AGHPvGB7ZJ!qp1OKKlRve^}ef z5nAViq(DcYf@lzir8N*6bnY~W2H^$}0maB?a@Ik`KosnpA`lzab^@`#BgI$y<NyC} zKmPw8)YbxpIY=Ia_dbTy;UG4w9Ry;7+9)6zgkkL+5F6Cy1JNK1YtMk#&5!>7hqY%E z@BjY~YtziS|NlRFy9T8G36k4j?HrK!2PAP=y9Xrx3`rc;4g!h8+Cm^UtUUx`Uxl`L zK<pbxY*<?fBo1pYf!MG%6Nn9KH-Xr&wiAfG04c0sZ7GmAtUU!{!`f6JHmqF*V#C^2 zAU3Rh1!BY6SRgj6odsgU+FBqsti1(dC;UOg7p(mS5=U+$-9QQ}kQ^?2pTU69lko%N z1m+FQ?X1(}rpwKcn<+O-Zk}9#e2{#Ie3*QLe4~7me6xIue5-t$e7k&ye5ZVue7Ag$ ze6M_;e82nz`HAwA<R{Bdk)J9*O@6xk4EdSzv*c&X&yk-iKTm$X`~vxf@{8mb%P)~% zD!)vAx%>+GmGZ0PSIe)FUn{>(e!cuA`OWfM<hRLhm)|3QNdB<=5&2{C$K}t+Uy{Ep ze@*_n{3H3t@=xTS$v>C>B>!3di~KkF@A7}-8GbYVX8O(io8>p_Z?@mUzeRqF{ucW! z{u|w&N5BKNki}tVSU~d}po7d9!2{V$3_qC|7?>C~uz<L%%pfw68MH?17ZZp)$qYK5 z>^BpLoXE_;z{U^{CVw(AFt9V&GlR%@76t|m20LaDd4iFFfs>(u8ARR$vtKhYFmN$+ zFoVdm%nS_N44q&yj)j4Nhk=_FM1E&tVBlroWCfA8m>C%O7`RwL<a|a327U%@Fxdbm zC7Bo)1Q>!CK_m+c1A`z#BO{n(VPFtqSjh+?k1#SY2s1ol29dj&LFcNovw+B2Mg|5^ zhNsLRav~!GgBZg@W)OLfnSnu^;X9bLVFsP67R&-7L3d3_GMKY4Fi0{aGczzqF$A%I z$n(q$4AKk{EFiL-iGe|e;R`c}6k%dukY#wz3?dIRGcd?8NHT&*&~Tv~!%HRx26=`f zVDc!KG-C$Uul!)LgAv3Q0JG;XGcYJJl!D1JFv-orz@Wr%l^H~Gf!Wu<><$(N24w~v zFsTbB9a%v4LbI@fNLDbZ&IszE-(>=kpjNROLl-lsHCYQLpD-{oFfyD5ljoQi8JHN( zg2^*r@+uP}12e;UFnOJck%5KbBAA@W#K^$PFab;^urM;PF(fgA$Yw@H26l$4VA7h2 zk%5E33QTTeWMtrE*u%)kz{Ri|Ozs7f#~BzIxEbP@7#VmNb}=wA@G^9<Ff#Bl9AjW) z;Ac1rCRegBG6*oVF*7m<G8nQjG6*q<Gcz&>GwfktWDsH44JOYsGBSuVd|+T?5Myv= zVq_3!aAaa+kYL!$%*Y_gFq?&uL5g7!3nPOx!v-cs1{nrXW<~~Ch5!~u204aREQ}2D z3@4cw859^+voJC!GUPKeGAJ=<GBGkJGXya)GN>?21CxIl85vX=n3)(E)EJn+WHciq zgF3@e7DfgQhW%htjuFIO$;`;0$)Lr;$e_j0%fiT@%@E1N$e_a@%E-u|%h1ol$e_ot zi;0mzpCO%@k->oB7z-nVA;SSM`HqQ^!H8iJ3nPOuLo+iYg9*cKCPoHR24O}<1~Z1q zEQ}213@wa|3>FO6z~p@fMg~iUEEYxvD~9I`j11NcFPIn^Y#7!uGcwpR<ghR@*fC^- z$tx_34E79{!Q@9~Mg|9l4`A{WGb4i|!)Gwr#>mLv#Bc*lUSncpaAvpwCT*D*8C)1_ zz@!QjBZDggHw%a?W?^J-V<-ZX>lhgs+!<OK85uknu7k-=F!_p!k-?K;E;A#87sDwA zMh0&ND`rLp9|mhM8P3ee;L8vLCKoU=GWap<VP<6TXIKU%KQS^g1TcIAlWdHP41o-P z85kLY7*v@U8G;!?m>C&D7^1=CEk;I$P=+98Musqk2rya7!pIQLkOwAt85tQO7<j;B z84DvrBtt%!3}$9zh+>EYlNBtC4ABfFU^0}Mks*d55KQVZF*3w5++bp4h-0_}CKoX> zGQ=}zGchtGFld3vwJeMbi3|)(j0{N({}@5!PDVzCWQOfvvXX_7A%&qHOcpRQGNdx( zgUR(wj0|ZE>%e3=6C*=9LotM8WMs%-C<2q2OpFYf44ar38L}7{7#SI|83LIY8FCo< z7(wJ(CPs!_1|~*EhCBvFFj>gR$dJ#lm5Gs|fMGM3{Km-0P{{BVOr|h0G88c+fyq)v zMuuXBEHL?>g^{6z;UAbRVPs?|Wk>{*jI4|dWeg0gj11)rKbRO9Dj3eOFfvp!>|$hO zsAAXwCLb~|GE_69Ff%gLFx+HfWT<7h3?|!I7#Zpqx)~W6>KPiCK;%niMur9kQx-;s zMuv-wj0{Z-7r^8;CPs#41}PRsh8BikCPs!<hUs8(784^w8$&TOBSSkwA(#wdWMt@I zC}L)0=wv7WlT%n28M+u;nHd?n8C<|5BNHP-55s>(5P6G<k)fC23Yd&#WMt@Lhyjz% z%!~~E453Vn3=<dvz+?y$Bf~_78DKJ<iIHIvLn;#^!(@guFv-Zw$S{S0m4%UEDg!5& zWC4?DjEoG^7&5@*ItE6D=?rVZ<Y5Lzh8YY8!Q@32MuwRT55VMg7Dk3y4DF1J46_-$ zm_TGIBO}8chGZ}q#=^)jmmw5P9${c)n8$DkO!_b}GR$Z21e1v@j0_7HzA!N|EM)l1 z#K^FSVIBh`!(xW542%p*7`8DmGAw0y&CJNKjNuiS<Y8uHSk9or$jGpQVKE~k!%Bt? z%!~}H7z~*h8CElFVrFDm!*Ga&kzp;vW@bi)bqr@285!0yxUw)ZY+yLV%*e2j;VmO0 z!zKnzMn;Cs3?G>o8MZKNWn^U7%23C|$gqv!CNm?$c7_UOMur^>(M*gCI~m?HGcxRA z;A3WF*v;V0%*e2Z!Hb!ZVK2i@CPs#R3|p8P8TK=5WM*VIz#zrU$Z(M1CIchGA%<=y zMux)-T}+G&M;JI*7#WT-Y-MI-IL5G^nUUc*gC+|j!wCirFv-ct$Z(QjITItpDTZhk zQ0*1W$jESpL6Dh|;Vgp-3nRlhhCmiZhVu;jm>3x@Fyt^ZGF)WX&&0@ZiNTnOk>N7K zdKN~8D-7N&j0{&95||kot})DDVPv?@u#ka~;ReGK7Dk4f3=fzY8E!GGVq|2v&9H`% zk>L)*YB2eNk&)pp!)r!HhI<T~7#JDuGdyNsWO%^v2ux-$Gcr76c*elU@QC3$Gb6)e z1|Mcdh9?ZGnHU+KGOPlVPZ=2*o-uf^Ffu%6kYHqFc)=hJCi9pX8D28vg2^omj0~?B zHiOA&EQ}1V8K#2CrHqUWZy1(<NlRu%hPMnBV6usYk>MSKJ2NB0dxrfCj0_(b_JK(e zW=4jO48maY4kIJOCx+W#@+&hV!)J!ijEoFl7`}kXAB>C)Um3oG$uMR{hHnf}VDcR! zBg1!w{mhIEKN$9~Ff#mP*bOF^GcYpzVps+y)tML>elw_n$#ND(hCd7iV6uvZk>M{x z1DGshWMufqkPRlw85tS=Gvt8D3Pwf-2F6@4S;@%Az{r>fCes-i8JHL|!Q=!MMh0fa zODv2GEQ}AqWHk#T11n=Kn5+S{Kp5-5<Ovqgs2M*ii0ooyVqj$8V+E0?nV1-u7;b|} zD<&oeW(GSjxs8#DfrViom^{wF#K6jM5=_Q1F)^?)#Dd9P3``8{3_HPO7Yh>u2SX>A zT*<=3z{#)zOrB?CV&Gyp2PQu-Ffnj5ya$ucOiT<s3@%{Ok%@_cm%#~4?qz0T;A1!d zCTFuSG4M0Y0h5bZm>2{Y7J|tQOiT=d3<toZC^Hj-5JMc83}9hm5N7ZPldD*m7(^IO zGBGiTGMoaFt67*B#2D6q$$Vxe262WGFsaGJ#2~?-0VV^Om>47({K4d3MkWR+hCg63 znvsb?njs2I$}%!B$S^2^$(77Z46+O>z@!!n6N4OsHkjPS#Ka)aum?=0Gcz$LFr<OW zOeQ7<MTQJ8xrvF1L5X1{m}FpNVo+xI&j2F*n3xz;7{bBi9~LGCRffM{asx9HgBrtB zCJ<@J#KfS^U;rl1GBPn}Fgyg4Elf-dnhYGQAd-&-ME0^UF=#O?2a}OZObpr#5nxi3 zk%>WvK?F?pvoJB}GW3DTcT7wSdJJ#D<Rlg*27QK^VDb|a6N3T62QYbvg^9tC;UJiF zWnp44VsHbKhnSfdj2RAs$t{db3?>ZA!6XkO6N4!OH<%P=WMVL55CW5n7?~K%85V*` zUKS9$nVE^ff?*a5h}2_ZVz6Y;1(R!8m>8@WmVwE37A6L3hBh#{nVE^fhT#hfh}_A< z#9+&?15Dm$U}CUixCbUzF)}gOGkj$Mk?)zA7#tYhfytN5Obm_;FTi9QGZTXoLpzwv zVqs!%X2=ASFPNAZTo|5%$+gT(46Y38z~ng=CI&Z#^I+1Hg^9tP!30chV`5_PU|0br zrC694JQ<Y1Bo8wagBJrgm{eh8V(?~A29tcuObk8@ykIhfk%_^VVKNI7gCD~QCJ;G= zg^9tRVJ?`w$-u-Az;FvpzGh@%2xNE#CJkAb7=jp#z~m1mCWc^!hYU;%Aq)?|<SZs8 zhERr?VA7eHi6M+33ryBAF)@TQoL~TvTbP*`A{dNWKx8))6GJ2e8w-f+Vq#*5VtB(0 zBDXR#F+?-$1Cz^{m>6Oh4uQ$%3``8M3@^YWCnFO>90LcK6lZ2)h-VN3lfjHk3<(U< zVA6}3i6N1}15A1|GchDFc!EhEW+sMYhHqffg@uVBg~1a{Mzb(6q%uT-$r&t63~3D0 z!K5Z56GJ+~d<GEtjDd+EgW)NJWMpE<WOxE5-!d{WWHGz}lg3O;4A~4uVDdUM6GIL| z4ikvn$Hc^t%di(req>@|$YXd9CIy+981fl}z@!v26GH)mB$#w(W@0F0a08PG%uEbL z3>nN!48;tYU~(ff6GI6@HJGemW@0F1r~{KXnVA^M7;b>c2h2<i<qY@1<R)e&h6;vf zV3LD{iJ_9=Ei;JpW?^EeV(<c!`<a*+su_G)K;(KBCWab@bzt%d3ll>v!(%YHkb#Mz zj$so66GJ^iHWP@9W@2J!V2A>fIm}E9jSSgfay>H>LlZ+j6NvO-VPa@za0ZiGn3xz^ z7;?enVP??u-8U8xd7qIPRC|L-KNe;NCWa(18PCMbz|4>ZCRZ>rGq5ncVPa-rWoTk% zW?*CJ0Fzuy%na-d;w;Pz91JpGQj&?8fs;WTOm1RgX5eC23MN-FFf(v7++zZfzRb)F zJPfg5(vO*$ftMj3O!_l3Gw?Ckg30;J%nbYtv%n+|6ElMV13Q>J%nTaP`34&PU|7M( z!obMzhKYrNiD5n?3j;GlJ(%QTVqsumU<Z@pEG!JH3^HIco`r>hjUgCJ9${i(U}reX z!otA8(8I#Qz{$YQ%)-FMz{$+Qz|FwK!otAA@Shn(9%g1`U|{$L8dhLf!N>}>k(Ggo zfs2Wifti6FOggZzGO#dMgGosyRt8oEaWEOf!pgwL@Q;a=ft}$um^{V8%D};J5=?Gm zW@X@HxW@z{=QFc1a52mRlb0D-8MqnFfyp!$Rt6r1R4`f1$jZRWPz5F}SXdeO7|g+B zCo>xZ14Aj8<YfY}`M~6F7B&V(hVNjq2Tbk-lT(;MBkSd0@;(zg10%x`FnN!K9W(|4 zCU>&1GcYssfJqK!c2IbNNiJq~237`6F!`7XGy`x1Ov<xxFfcNRf=L!84hANM_lzKt zm5GCanc){Bh-72pU|?bR%?Kh7FmN!iGHe8sJ&YU-Yz!;G<Sk|n26l$q3?R~$g@b{E zK?h7eXW?MrWOxfE&oFZ^a53xzldOy!4BQO=7(nDiW)21(h8bXTF9QbyFT)Zr`HYE! zfscWO8AK|va4_&QsDa5ROdJdX3{1=*Qh|koL6AWOO#WczU=U(>3?`p4aWDuoFf)Tl zMHUVQ5e8K-DaORXAj%*ECVw(>Fo-cc0h3diIl;34AaWX*eVLh)0W`J*A}@j2vza*= zK)Dk{wu0Gfm^c}j8M+xj<Y6%T1`8(x3&SJ^5P2WW)@9^m0L3|oG+^XpU}KPE0Fg$F zoDA#?vY-^qV9LnJz`>xv03t0JIT<(^R2e{|6(c7D7lRrDh_q(pWZ-7dU;vRejGPQS z44Moe(w32vftNv>0Yus{ax(BS=rDjtdqz$Meg*>u5b40k$soXB#sDH6895mQ8LSyV zq!S}2gAjuw1Bi5H<YW+LaAN?GE{vQEA`Bi3AkvkQlR=chlL17!F>*49F?cb6NOwj~ z25|;&P^rP-!N|!V!QjIHB0U*786+8k89<~LBPW9tLj(he^k(E_kY<Qv0Fge7oD4Dy zQ4Ao`mywe}mLZw}MEWsuGRQH+Fn~yZMotEKhFAs=8NkTNpujMZ0YnBeaxy3~%wzzO zdW@V5N(|x*AX1-^lR=q5f&oMtGIBDgFvu`~NMlA&Xv%@Z)P#|fL5)G40YsWHax$nh zC^3LYb4E@E4F+Wf5P6M(lR=Zg6ii+OlUEox8MGM8!Q^=`d6j{aL7Tw>OkMz!Zy7il zbQr9_<WDg9hJlkom%#>1-UgHJ7&sa97;M2LJ0pnwfQgeqpTUj+L>>XNWmz~G3>fSg zK%^*`J%@pl!H~fLOfCVFvl%!Uj2N82<SZ~bmw}VPn86uL?f{eb7&#eC7+e@Y<WVsD zE+Z#{DT6Bmh&%vh_cL)am@&9BfXGQ;wgn?6gE@l=1BkrNz{z02U<f8Jfyq8*E(Qh$ zc}6Y<Mg|!$*~`cU$`N2vf{6>17nr#iSQu)-WE&F~11m!-m~>?3Vqjx%0F%N@Tny|C zLSS+V6Bh#q!(=cS&&b8V$?yS87P4?La53b9$qW`Q25yFHEL;pc3|GPA3l=U0UWOWG zE(Sh^Dli$s!o|SP5Dq3&ShyGj7?Q!HG!qwtAcGW`Y-9%ALQ)DQH!yHBFfz;nlip0+ z3``8JVDbeEHv==nBQPn;#Ld9MzzZg8n7J8P8Op(A4>Jz~1H)nl9`LvY4+9g!3I-kq zW`@@cJPa%h9~pQUSQ!p7@-VP59Ae~QU}t#7$iu+FAjrf6%0o;%3|tIyOgs$S4Dw7o z3_J`TOgs#{48BY}415d=SU_Y96AuGFLjn^Ig8)M!6AyzRLlP4YgAl_qCLRW1hK)=- z3?d8%nRpmP8ICdWFo-c6XX0THXK-TXVUS=5VCG?vWSGp%0~$SM=3$U#c*@MfAj81V z!owiTAi%-{8sTT*VUTB#X5nE_V9;mbVNhf+VBuj<Vz6Q1VNhnUW8q;?VQ^>RVNhjg zX5nE_W0=Uo!=TPEkA;UpgJC`ksN7k~!o#4&@RWszL7SnQg@-|h;Rg#3gD%5Q79IvY zhF>f^4EhXAtUL?`49u)N42BGBtUL@x4D75t48{!GnRpmX7zCJj7)%+&7<m}X82FiZ z7|a=#F!L~2FsQTeFjz9Av+yujF-SA>FjzClF!M0jFvv3VFxWE4G4n9kF~~FXFxWFF zF!L}tFeozfFgP+OG4n7uF(@<hFgP=)F!M0DFsL%~Ft{?PG4n9EF{m^1Ft{^lF!L~Y zFlaLKFnBU(G4n8ZF=#XMFnBZQF!M0@Fz7P#F!(a)G4n9^G3Yb%F!(bVF!L}3Fc>oP zFa$CfG4n74F&H!RFa$H0F!L~kFqks)FoZHpVCH3DVA#h3BIhyjGB7eoGJ?p*jJym? z4Ew>P91AZ4GlM9Ye8j}dz`}3@OzvXgWng9K0h1?KKvU7*z~pXbJ_bgHU0|}7k&l6i zp$be2v+yx6GYEl6eI`Bz76u(KIf<E%ft6u8nB-vMV_;)o2a|$~d<^UiEi53inTd~q zgJBYwWMtuE;ACI`lmD3c7`Pa|gUR{Kd<@(Sv%usU20jKJh9zLKjERqdm!Swu_Av1= z@G;bb$zCv7$i&CM&rkp+I~e&G1Q_bTBtIh`gCN681`x@|$j2bWaF+o@#<K7+2s6Zh zNeN~?1`&oI3?NdKk&i)?VFj4{#>B@U#_$GA&ST_b5NFT=ll_c*3=#}_EFf|N10RDV z!y+&#z{tlS#c+oKL^iVUF-S8wfk|FgJ_Z>E9x&O$%*P<h&;cgju<$X+F}wtmbC~!T z<QbMQ@i8bcEN0?kP-Jjp;$u)^aA)FUP-duM<YQ1_SOzBJnfMq~8M46S1V%mvHHHK* zDbB*jpw1uzCi|E{>lEfQ^MlG3FnN=apMi<t5|}*B$j`vca12aVG7EsGJO#kBSOVbj z8vzDTYh8c=)FKvO0JRwf7}yv<r2snvH-i8JsP!bkz{%jpAOLFdG6*nmGl1GapfW~) zftMkOL4bjeL7zc@fuEt6L4X0ITYy23p_xH|L5LxcL4ZM+p^QO*L4-k%L4ZM&A%H=E zL5v}ZL4ZM=p_D;@L4u)`L4ZM$L5e|uL5hKiL4ZM;!Jk2ZL54w>L4ZM)p@czzL5{(f zL4ZM?p@l(!L4hHWL4ZM#VKRdNgAzj#g8+jvgEWHxg9<|pg8+jngCc_fgBn8<g8+j% zgF1r%g9bx4g8*obi9vuti(wLj0E0FID8=Y7NHPd8=z_{820aD^1_1_r22}<D1_K5) z1_1^`1`P%Q1|tSd1_1_R25klb1``Gy1_1_B1_N-L#*9IL!JNUGL4d)6!I43L!IHs^ zL4d)E!Gl47!J5I7L4d)A!HYqF!Ir_BL4d)I!G}SB!JZ+QL4d)5A%a1G!I2@7L4d)D zA&NnO!I>eNL4d)9A%;PK!IdGFL4d)HA&x<S!JQ$VL4d)7A%Q`F!IL4GL4d)FA%j7H z!J8qIL4d)BA&WtP!IvS2L4d)JA(ugb!Ji?IL4YBEA)i5jA&{W}H2cd?$RNNF%uvoC zz!1Vv%OJoI%FxCjz!1jJ!yv#A&M<{RfFXi`pFw~jlEIKcfFX(@gh7BInjwWjfFXvV zf<b^GmZ6S8fFX{dok4&ho}rgPfFXflDyW74$8-{d5va~%2xSmpNMT525MW4UsALde zNMopH5MW4W=wJ|F$YAJW5Man;n8qN$ki{U#Ai$8#V9X%Eki!thAi$8zkj5aukjGHP zAi$8%(7+(TP{7d1Aiz+_(9a;iP{c5uL4cu{L5M+sp#)TZGn6t|G6*n~F@!S+FqAW- zGYBwLFk~|bFjO*BGYBwLF*Gs=FjO;iF$gf!Fic<&V5nu70V*9Bgc$@F>KQ~B1Q;3^ zL>UAa8X3eG1Q?na%9#Zj7#L((1VO!8CP4-!hFdIx49pA<nFJYF7&fyAGO#i*GYT@W zF>GNGWMF65!6L}O!LW@*kb#q7JBuI#7Xu5UAOknURu(}99)>H-f(*P2hna;K7#O}Y z2r)1+++-1AU}CtzAjH7TFq>J3fra4)ix2}V0}qQ30~<phlMn+t!(A331`dWhEJ6&N z47XW?7`PZ_FbXknGfZL>V&Gwz%qYab%P^Nwh=Gq`7NZaYKf_E$P<YH?6k-r$n9V4} zAjB}8QHVj9VG5%Vg9yV^Mj-}KhE!%D1~G<>EJ6(844YVl7$g{!n1mQ48I%}>7^D~^ zScDj)8CqF{7-SfxF$yurGW0VGF~~72U=m`GXPCz<#Gt@1hgpb0k%5a*h(QTlW`GD~ zCI$utCI*H-42+Dw85kKYS(up2S(un!voJG1V_{}CV`5=ZWMW~lXJKVEV_{`AXJ%tt z!@$nAnt`3|KNAPrKPC>g(=429r&u`I)EK$gG#I(r;uyKv!WntkHZt<CMKbcTZD!<U zi(uqq+r-Gn7Rt!aww{rnEsRlsZ3CkKTMd&STLqIKTM3g8TQQRm+h-PGwofd=Y#&)f z*gmj`u$^KQWxK>E%4W_a#%9kX#%9VS&SuFZ&St?R!REju!S<X*lI<OfBwH+tB-<AT zDYj1xQf%{?q}k>&NwXbhlwm7ml4TQNkz*5Nkz-rJBG0y%MV{?6vjW==W(BqsCPlVn zCPlV03`%SZ7?jwKFe|eyWmaY@V^U!&Wl~|g!l=r2nNgLEn@Nq0lSz&36tg<pc4l?9 z3(Oj97nwEKdYLuZ>X|j!jxlSotzy<<t76h-t7Ot<t7Xz*t7g(+6Jyb3JI<oVc9=zv z?I^Q8+j3@owlj<dY^NCw*dkdB*{oO$+5R#avHf8(VmrZX%(jNvnC&>T3EOIB6Sg!K zQ??5%W^4~w%-HTSo3q_xHfOuTY{7P$*@7*M$&x*f$&&pugB8as1}hE)Mr%$TMr+QK zj5b`47;U&-GTL%KXSC&B$YjSeo5_x65tBXdVkUduuS^bncbFXb7BD;V&t!JwU&!nv zu$b9N;2VQ8+gAo>wniow!44)D!6qhGp-v`Oq4f-IY^xaD*tRpcv;AW5U^8LzWMgLW zVtd8n&Gw$fhwV4BFWWC>Kej*2{%n7l13&{0pd^KY6F`eE7#J8XKxv12pxFrqh7C|! z;XZ^v0ZKDGfbbijG{ZwEA4)Skg7Tp>!(%8PN;5ox@}V@tQz#!wGdzRxp)`0=Hv<Dh z188wK$mK7f@=zMQbOj_2TEYU7e+89?(hQ&_J|KB0&F}^)52YF2Litdd;T@C@r5WBs z`B0kS1C$S?89qY!P@3Trln<pDK12CXn&AtS52YEtLitdd;Tx0>r5QlWk3jB+(hNVK z@=zMQL<%GiS}MiB!0-V|C;Wn_y8xvfK+B{U7#KD{X@fry`2|o~;4g&V0Hr@b>4JX{ z@dr>k;6H?a0ZKoB(h1O;9v?vI1V+%<B?H3)D4oCr<wNNN@CYXZ!viP{iaKmq!2mS6 z$H34HrSF(Q_^METAC%UD(uz=8A4(fTX>%xT4W;d&v@?`;htld$+8avyL+KDG9R;Nm zp>!scE{4+eQ2H#?omZgrZ7AIfm7ffyA40_^K>6>W{I5{@50st(m1i=C_@5I>3qa|k zP<dG>e=d}-1En>g;)|gCM^JN3q2kI=@#Ro@HI%*qrPo905JQOlo1y%jQ2GFrc7&RD z1j;`FrO!a=%TW3~)c)&G{%t6IA4)%l($Ar^FVx)EQ2u);{TWJshtdzA^lvB~3Dx%m z%1?#zUqJcapzbh)^8Z8WnNXSq>R%2h%>$*Gq2>rc`65tS5=zTM=~}2dE+{PsrGG%p zp9<yAhSF+KeVR~O2TGel)$2p~f1rG0DBm1P+dye2DD4TQeW0{Iln#Q@kx)7YO2<R# zBq*H<r8A(kCDh(*D1RQ5UjXHoK<Nr7T@9t{p>zwB?t;>`Q2i63{3%fS98`P;ls^wj zJ3_^mLivlK{8do?Iw-vnN^gbIyP)&|DD4W>cO1$;1*OkH>B~_1CX`+XjUP{_z6Vh8 zCs6u1lzt7RKS1ekP<lC3zb^DX<9Ei8eDoJ8{|CxvH-d<ZLTM=|Ef1x)LGLfFiGZkk z0p+Ve<+Y%+DU^1H(mqgH460rndhfA}AH<vxsC*)nu7%QFP+Ah2Po_ZmE1~pmD19AD zzlPF%(0$l*!yxAWf{Oo#(ri%w3PNc`D6I#jS3vz|3FW&%=>RAl1*MCjbPtrC2Bnun z=?zeNE0o^i1#!<VDE|PI-UxN44)lKKb5QY1Q2IKQz73@xK<Q^t`U{k1H-VTh3Z>Pd zv;ma1fYPvgt!<%vLpz8*2PoeaO8Y_SU??34rIVm^5tOcl(oImh8%j@v($k>yOej4E zO7lZ$Stz{#O0R~}8=!Qm2gH3lp!|JM`UI4|0;O+3>E}@TEtI|s4WAEC{x>Kc2MzCE zP(Ft##2#)aEdZq@p|mQLW`@R>0F*X^in~GSTqlS*flz)Jl#Yecg;2T-O4mW@J}5mC zN-u@d>!I`>D18u0pMuioq4ZTK{Sr$5fYO_w`Az_ukA$JL1eBJ6()v)^5lVYQ>0~IK z0i`RUbRCp#h0+`r5PwgB@)tnqbx>LZdO!L>DE|PIJ_4mrLFtQ7`UaG~2c@4v>DN&D z6O{f5rT;@|HfVXr3#CP%v=o$9fYNGES_euSL1_ypZ3m@YptKj14uH~OP&x)mCqd~9 zD4hqTOQ3WWlx~31ZBV)gN>75)GobW5D7^$qKeL8}?<y#NBb43-rO!a=M^O3)l;(rR ziwcz1gwjS(+73#ea)+272<1mW>0W605Dn$0Lg^AH-3p~=L+K09@^c%Me+o)Jgwije z^hYTD6G}5$LhR>+(h^Ww4@z4?X%{FR3#ETR?URUv=(m8<5m34UN*6)ZmqO`kDBT04 zUqi!pE|k9jN-u-bE1~pSC_Mve?g1$M0!kl;%HM+0FQ7D|6~uiUP+A^J$3gwC59M1y zX-_Df0;MaVbTgEm2&ET5=`B$D2$a48rC&m6MyUUUp|l#5wujOoQ2GqCJWPS|OQ5us zBgFlcP<{`To&lxTK<V{RS`J#z+d=c?Ut5U&El_!ZC<y;3lz#<EKY`LOq4YZ_{Rv8c zgVMjCG?NX)9APLe1En>gv>BB4fYMP=It@x!Lg{`O4Gpg=E)a94K*eW3>BUfb1(Xhl z(y?w3^*f;a15o-LlzsrEe?jSgP?{4OE`m^63`#3PX?-Z2><Tg83Ceea(g9F97E0$r z=@uw`0$TrlgT}`csQ7Fsy&Ot!hSK|?^l2!44oY8!($}E$BPjhGN}Kva+}8%JZ$3c9 z*`ei!7?hTV()v)^1WMaL=}&<WbKIbOFDM-ir469zrVz?+h0+tD^jau=1WG@G(q_<n zC>9Da{{vL~E0q2RrMc`O^5Rfh14>&#X*Vbx38gcjbQP4AfwnVyq5K!nbYuc;@6LpZ zFM`rC(0Xw_l)n{9?}O3@q4ZHGJqwyXPeS>(p!9PnEe5Uke?j?Qpz-n_%4de!#{;DW zptKZ}mV?qNP+A>IYeQ)(DD4HMW1(~|lx~31GobWbD7_3yZ-UZCq4WtTeHKdJfYNuN z^dl(!5=y^=(nq1~uWwL3GqiltfrhgLl&=b<b)d8nly-#DUQjv$O6x-NNi39~0;O}I zbP<%UhSD8Sx&cZzL+Lgs-36sDK-1rBD1RxGJ^-c9L+Q&<`UaG~52e3DX(nj<QVdFK zL1`-}?F*%YpmaEtj)&4IP}<)UlFqZC`~oOl4yB8r<;`)Zz1yJeaA|1zJ_;>YTA=FZ zLg{r-dM}jz22KBmp!}my`V^Et52dd_=^Ie`E|h)%r7uC#wFxwS9H8;M3Yrd{L)8U9 z%LhxSy3bJYA5fah8RG8%sD6GZUkOUrLfaF3(0pbD759SD2~av0N|!_F8YtZYrKdyb zIZ%2bl->rV&p_!LQ2Gg!{tBg;q2a~_r8}YNM*_;1fzql_x(HhTXhHeLP}&AcJ3?tM zDD4ZS1E6#mlum-u&%+_%ln3o6<w3=(pmZ&iZh+EDq2mk9P<|Jbz73`Oq4abpy$nik zg3>#o^Z_V+9!g(_rn~D<{sSoe21@^j(o)d$ssyDqptLTOwt&)hP}&<x2SDjiC>;Z( z)1Y)VlrDqP)lm8+w4SSj@_C@{Y=rVVpmZ;ko(QGOq3x?_Q2tyfy&OvKfYOJc^i?Q* zA4<Q3(x0L9UntE3jeiv=Z49NIpmZRVPJq&>P&xxjyF%;5Y$#t9ny(C?bU9SK8A|s< z=_OElJCr^Rr7u8fUub!89m;<RrN2SxpHTWAlxBhULpY%{ACwk@(sEE*A4)qwX)h=p z45bsH^nB>KbswPly$#yFNQ25}LFtvy@*)$et^q1O0ZPw<(u<(<GAO;>8<J1Fpy9U) zD!vv<?|{;Kp!5kSeHKbzfzmgjv<lQ651{<VQ2Gs&{s^UiLTNT=x)XrXqEK2EO6x#r zLnv(nrG21uG?dPQ($!G97fR28(rcjfZYX^gO5cIfZ=v*WD9s72U&Nub3Y0d6(oRr1 z2udeI=|U)752gE{^a3co5lZie(&wP`7ijpKK<lM@Q1J?AeefE}-w+ImFGlEi+;3>Q z<AL(|p|lW`J`1e}#Grg>D6I~q&7iaoln#W_aZoxLN@qan3MkzJr6)q^*-&~BlwJd+ zH$dqbP=6eN(z~JJ7ohYDsQ5c5pB0)e#h|o0ls1Rb&QLlO8g7A5{&r~ig+ckzQ2Kii zB;2x~{7NX@3#DH|^UYi+e>s$14W&0g>8((D7nJ@4t=Ac${lr61@l#Ox8kBwnr9VSy z7HGXB1=Yt1<*$PJhX=|RgVIt^S`JDpLuqbkKGK5n^`W#7G#xiV{bdRjw}sN~Q2H~p zJP&~KL!fjdln#NmJ7S>x6eyhorK_Q|5Hx*Ggz{%Y>19xQE0jI}rH?}C%TRhNv|LMt zhQ|Y_cmp*3K7sO|Luq?xJPSkR-$KQkpyl;1D4z{l9<f5*Ck*BDLc>K2%9n-G;n4U| zg!0v(v<{RuhSC;L+7?Q?LTN839S)@vp>!dXu7=WWP<k4aUI3+6K<V{RS{i!(<}YZv z*a{Ut3Z)-H>E}@T4V3;5rCFf<<b=|KP+A;H3q(Wmg*udP4W+%IbOe-6fzriLx)w@z zLFs8wdO4Kd45bf2>61|U43xeCr3;|-^L;4)5tM!hr9VUIzff8L+8-2$(yCC}97@|l zX(uRs7Fs^~K>7YqIv7eTK+|0~lphC8KQT~#B9zXA(nV0Z9!j@B=}su!2c>61X?AG) zW+{|^2ufds(pRALLnzG)El-|7`M;sGAhf+E1EtlWv=NlHgVMfGIsi%sL+L~)oe8D$ zp>!3L?u62PP<kSiUI3+apmZR#9lr$1Uk|0XK<S-OdM}hd1f@?v>C;g9DwLiFjsM3` z{%a`x7fSO&<3|umOF-#rXgMJX)vpW{H-OSMP&yDwr$FfnC|wJs8=-V7l<tJmQ=#-M zD7_X+Z-&wnq4w^8@`a)0#%?Iz2U_0jhw@KC>2px}GL*gnrN2VU*&1kkKZc6`f%d1~ zLir5P{_F=R|0|Tf4;>$2gVu8*Q2H5^mVnaIP+9>>Yd~oOC~XR*t)O%eln#f|F;IF6 zw7--B<!3_aQYc*orJJDi3@H5&S}xCq@)ttsHBfpVl$M0{iw;5gXQA{ZD18q~8$!eD zA(X!oYW@c(pB>s>5`faeP+A;H^FYH_4$4=7(*01{07{!eX?rN`1Em*3^LGN2pADtU zpmZaYZimv7q4ZiPy$?!Xg3`~Rv;nkS{srYnK>0$@aMy>@K2SOYO2<IyY$)9VrTd`t z0w}!_O0R*^d!h6pD9s9Ohn<7+FGJ~DP+Aun&QGBHH&FT`l>P;!|3hhJX#K?rr3In1 zB$QT#(iTwK7D_uqX%8sv3#EghbOe;nh32<dC_e*AmqY0qDE$!HZfJ(`JE8ifL-}){ z^g<}T3`(zs(wm_4E-1YpN}qtzXQA{}DE$;le}K|Iq4ZxU%>^w-<e{_{l(vP^o=`dr zN+&_-A}HMnrM;l}pbyHQ45ep7=><@FC6rzdr5m8>Y6X<u1r<LHr7NKEd>C5)?ts#l zq4Mvb?UTn){&Oh(21@^f(t^<O7(FO$4yE0pv=5XHh0@7TItxk{LFpza-2tViL+KSz zdJ~l13Z-{IX=7-4un)>V45c4H)8{!TUk4hF7oq%XP}&3955EcJKZepfp!sAHblmMY zG=05;%72E^|DiMsR2?sr7J|~^Q2G)yd?&)nb!dA)3M#J#rL~~6E|fNc(w0!#A4*3- z=`<*v4W$=C=^`jy3Z<){bOV%bh0+~Rx*JOOLFtK5dM1>f2c_Meovjo!LX%4KN=g+> z^epsDbd5^EJi|Ie6FmbHO^7Ii4g=_@I?xhE(571k1_9`W2Lb;;2bzJzQ87e)@jTFx z9UvLlLfmc0rv!o|pg4aX=r9lvhl9~lnSp^ZR*8Wj-2l5qObd(|7+9`=4((;;U|(&= zz`)GNkuJ!<z|6$HQJsN-nT34=Sdc9fEXZo)$-uz$!is@`RU?;yfq{jEH8Zb-ftiCf z)tP~TiI0tefwdWQ+$)p0CIbU&3y8_g!G1@Lfq|8SX_^891M3=)7#qiG29RBB1%eC= zOcf>!3~UB_85kJYOH3FTZm@r~Wny69C{SWxVE+cPhJho&fq{YjH|Shh4vrtj3=Hgl zKx_eyK1~J&_P-#u1jiI@1_t(jAhrU>JTV3a_WvNZ257G=13LpFh-1L12hz+4=2&p* zYcVjeGl4k{oK7+f4D2jmjt8eZ$UasuCxG*|3IhW>8<-QpSzy7yz|O%~%EZ8sz&S&J zfq|Wiv6_j2A%hcij59kABgnx8oYR~c7}y2CoDxoZCk6&~A;xM328IgGM|KPh>|%^= zpvAbHk)8|;?Ba}|Q*xU)r-Du*lwfRRVqj?IJO|Pv!#I(NfuV!b8N^X#Y-3<xn7}#7 zoPmK|j}deu%nZ(7vJ4FD`e4oi&Y2(?12AU==K>JN5X{-Yxg5kXV$@||VA#Rg3<@X{ zu-guB#)E?0jBz>>1H%ypuA71k4D7SmdvzHYB-j^#Jjvkz3Z#V~CIiPzP)IBSd6$D@ zHa`Oc`!bMk1sK>kT$mUb*f>CO$PJ2Gkh6Ivf>I#^8wV&h!J^BV7#P5!!VGL2po`Ra zL28+1C@?UvrOjdhnQ$0%iXtd%*f>B&Gjp&eI5RLXbFeS714)Bci}NuFI59AAYzN)s z$;`sSs0Wf~u{U5~V1Fgfz#zbx1-k2qqlAfpfioMtoSegk2gKgT3Oe19e?B_{LkA<L z3nPfn30CIH2v)}lR_6+q;NWBvV_@Kf$Ov%usWLEdx-o)$Ai>$_$-uzr4%VZ<`G5t^ z(cnyHXJFv;W|Ze;U@+jEYsbLA>BDHl%fMj4*#|mE(w8xpmw~~7^Q-^^1E(M3d|n0y z56)HG3=Ew9jMsP>7y>xAI5IGB1~5M2WnhTlj51(g;0$3r&Id|srD_ZeoS}?o`9NuH z4ajX_jF<Ts7z#Ldn=&wPMlrtQ1tl*@J_ZKPXvQEm28IUCBOp!;*t8DL%~}i$oUx3P zc|nP2zZC-mXB^`xUIvC4oC`okCo<mTV_;ao$>GYtz?s4b%BCwgI}I5aIMcx1-oUA8 z$-uyw#khzQl!&GZGca)GGS;v$FdX2V&B?&PS;)xG$G~uca|OuNCE(Dx!1>6Afq}Dv z(Tb0O;RYuQh*Qbv#>c?$fU{GZfq}DzQHGy^;RUBC$mm+eY<>oY51d=985lV07<chA zF#Hg>Y|FsFSr3j921WrNkiQ!k)!7&rI2Z-afy`@U4B}&85MUH|?!myo*~A#f$G{-L zC}1kYz`)rIc7+0?z;jUs2F@17EItMX4Mu@*4F(3zR>p;V3=9U00_92!44iF@Wqb?_ z7K{R|prq2y$OMWfMuBwDA)p;#UwSYKykccw;Oqq39>6GY2jqb+FfW2p-~=e*y1~2z zMu8Mi@b-Xt8H@r##taOcy<lDeqd){GDfNNVNCl(70W$^$&WVh^pyGg$fsF%{cA3t} zGca)GfR@|~N^s`=0i|1TI?V?sQ%G{o2PIz)4sa?3iwiI?>G3i!a2A|rWnf@x)&==d zhz*o$B-j`jgg{{i!onbefsNw`NY!O=kl!u9s_bB@Kxqf83QRCCwemACaJI*S6(z$I zfg&C(k02PBzUVP9aCWqVRrZ2Zg7WYjHU<W;A_gYV<)fUP%fa$%LGmE~>_Ny!YBDfz zb{z-Hp9RT-^gjg2!wCj94v+_!VkAIe`U$M`2S_PMl!qN^l`zO)2BvV3qCRnUP$0;% zLj%DKMG;h`v^yvo9KkBxK`KEuC!neX*}%Xg22wdG8?3Srrm`PZCCGNDmw$j#-sD+e z<qKfScfyr}%!H_9;{cWDY#g9^ifJnc0|V#W3t(N>K)OICzK81q@q|H!Ffhp|Ffee= z1F2*Z)L~%YoX^Su@+~(91A`DqnLGz9NI)zGHV%*=QzR%wE-(NqHG?VjhbslC5#A3v zU4-4*7*zNydBefLAd*y?#=yB0<V*&U{G==f&SfCybBN@m<}q+C2Wb-Ecnm5ISAf_O z91Wn-V-+YQ6*!_n>3lVat-^6int_3H4alP!9POaOYc0ra1{{+>d3_y-ZNV`Wlt<Tt z*bW>wK}FIAkP|#Ojyf<faBc#*A%cM$WX&HAXr%%=oC8!aZwC2;>8CjZ1LsyDPEcS- za569mfvh&<garne1rlOl;{ch!^bS;5Z*u~x_uyn;5RE`l%fPhGi-CdjdNNo+Cd|NE zxFS$UFfc`MF)(o6=m0C~gDC<PnBX`T205F7sSA`#ZmtC@+6+^425u7AOt4BZ3r5b{ zd$}1HI6#d<&L_H{tyvsjK%wyz#OB~I1{IahKx_eyR#2hw9K@F3PzAZ;1&FP{kp^=3 zOAuRw19TKJ=POVKH{f6arJUF7Yzzz*9Mz!o@CL+o;Lret@LLeugM&+ofr0ZKD6a=_ z*y=MdaJ~nLM{s~{<L3MTGBbhWHYnCUg4h`x8$kuoClI@U<1xrRpF!*j4tG##^aaFj z;NSv9(pONf@8DPtDzm?V%$&f{3aUH5gV-}T{(;p00I?TvtOBK$pCI-M4sK8?`UPTd z;MfQf{{u2}2ggZ}nSVj-0~|X*e*XtzpWwKy%)r3;AH=@EDQpiq(3ug$xxrZkDxev` z)#L-tw;-FDz%nm5lbjeBIGMqm51fYT44|VjL3(}&go5<2f@K&O1zbP{J{y?F!6>j7 zWCRzOC%`Ds28tGLFi(O}zzq~YJYb#zqrh5_lX$^A4Mu@Upqf*F5#%ESMh5XLP^~4% z2uchR0i42&p`hBR5>(BJFoMca22RjH*qowZ?HrtGpv)%5_?!*Y?AZxwVTgk{5}e*3 zUq~>53Pc6Y*`TJ7BqONc)8GW1HO(o-2rAnQIGsQZ5otzHTDRZ~1yy#kV51#4LHp7; z<-n$SFo@S1F)(n-GoIvTV33gDRAih4a#uX4(pG}HD+kmbPzGz};A|6QVBl0?1eJ;c z4C2R~85lTK854OJ7$gih)fvBmG@k$|(O^6ZDr7isgF;V}5mdl&a83ZVAG8>Mv4cD{ zM~;DkQ=3thgMmSU(?pVifm4T(os)q<fwKpcVRaeJIYF1oh^v4qaXrR)JPZutTA<K3 zWaQ%o<$V!e1_n`3dkm8K#2Hi=7&wg>wZW2xAW2aB44gd~#J7U7gfXKBSkjLdT56>8 z!b%M=3tDPyVqjnpKMl$^CXAI}4fP-mpmG4z!~&Zr3@YOo#3z8tK~qLhH7ouM)PgZ% zTn*L%$}Zr73Dgcl=m4`A#En3qV$KMvJH*34!DYb+Dt^RuWf>SaEg3;|yZ9lH1FRT- zfsJF}V_*;hHQYcAF{mLyAdS|H(tM!6Py{Iexe(MI11o?KU@wWU1qH7S#9VDq#<Yd7 zpMeSiJ4R3gK->q!wuiV$3Dg{NfUr-1yy(c-1h%FFW(}x?B@Ak(K&%1T%piUn)DCrG zTnkpZ8KxA}$b~2c73!eA1UJaVpbWsq0ZMLc9H4Z=zEp^TL4ivZ)NJH9pwGa-r3Pw~ zGjQk`GB9wdcMCEwaB$=)F)(m}JE#I2>p^O?Kx!ly*f{R<F)*-y;$~pb;8p=OHaS{2 z85p?Lz|q8E&%?mLtquwc4h|P51_o{okW&OW3Y8cbxHUoI5+W)2$qd}!i&7LA*f^9x zeW2~2YmIogLCsaLaXdU=<3!R^OBi@~nS>Y^I7Es;Y(8}%1_lAJ5j^}LH4+?~R2dj} z1VDv7$hbm&1_tp3pw`xGMo>GDS%AgLfPq1hg-KsufPq1(U!H-Xfk`UDhJm4hMe3_C z149Fc)PENSh6Vwtdy)(c4GK~g;tUK82N<L(L>U+w8W^Rvn}KL1sW?Ff21cnYc?Je1 zDH#?91{SIJqM+Uo(>x0X2Hplx^OLF9ih+T*6J(Qw0q=aqxuCKE)a>M40B%MzfV)S$ z3&3r04o+7o1_s`RU>O1F;*uf;-bG+75}f-?85npMgL@DPoZ4m#47^LgZC?!rHV!KR z1_tpBDhv#~%Nbn-7#IX4_{8rBFff3-YkU%*0A~QVZ}}wlfttM>nGy^Pe3GC<Ex^De zZ_dELC$%1=Pk~=T0TkIF2l7iI^zlnV^nu#N5OD#HLOuotekqW02?lOZA~X<$wiy^0 zm_)f582A;QgUr+r5Nm*$DIkt8Q$QSHCPW-!rhp`<St`L{t;4_|paxQ}z`)euz`!7& zZVejP;8tN^;1U95dnQn_<<bEqKQ@kOf(#5YR^XQFazO?LK@CB%?=Wixk*pPjTI<FQ z3MY`e1vuD1?vwzrB{=jsVd4r5OlR2{7z8CZfvlC~Wnkdp;1vSpA7LSAs~42^Ah{5f z+8CJHEf^ShIE}!HEI^7t={ryeR?>lNfRva_p!Cfn2uho590&Lr7-Zu!ix~oh1fGLZ zwxbpUgOCu2$-r@2gMmRvm`?<h1zTJg7=%PYrU-BxQD<Nf5(BX%I1Z>VFbIi**a{pC ziVO@wV83W^C`vIf2uXp&4LGt`85o45L2L_-=MoGILNXw>0|&bv1A~w}$ekVxY#d2K zAg7cRGprD@0NV+6iZzJIz#-1Vz#wGf2X=}zsCQtuMv#F)fMc&61A~w~$Os9Jdm;=B zLe3zz496`H+Xci{;E-2iU=VVX7hzz~;CN@oz#!xfVjFOLb7Ei+@&Gx-g5xR3+nyk{ z14pqb1A~w^i0#2q0J_4#2gDBG*e1)sAmj^TM{tyZ)cAwg2^_^BH31-Y2FFg2nm`b{ zfCF@Gnoux^UBR&!<o;-odm1>RK}BRNh~2_r0Ak03)O2vT+cPi-C4h3q1P)<M1_q%d zkeV4B?;IEygpxsO7BH}JycGhC*m*H92&pE4n4k-igmkq;K#9Wu<ZF;7P(VQv2Pl|? zK|)|I0~?2k2xwrBi-AEX9b^<6M+is&RD^PCgNi>UaV-W0VMfqhO-%Cz7#M_^K!aIK z79b`wXds6v0hFj%!6i%p=spV$kXlI=Va`1w3=C3^pj(lo963N&da!?HWnf?t7J37U zH&DqeEZio-z`!7qnZh6}QUJ1;V=X@egRm%w&CI|AQZFn9GKak!R9y&5sEaZ%fJ<Rv zNw8LkdMS{4Ca^KmAYm2;CXjkzneQMo)2ta7gyn2SK|$gy%D^BrQG|gZNR)v=7{p*; zx-7=PAi@gDHcWjM3=E9+AP+E212G-IJ{09;U|@6tDFz+d%fRRlW@dOYFfhKJ0ycJ* zC<6n(9}5G+2@ysOCQeXAYiY#5z^KV|L7jntf%7LD0|TQr6DZO+IPDY|7#MY#K;04n z&YR2(42=3rpu(kqbFCEv1ET>G$SD<^g02h<jD}#D2F|In3=E9MU`_|;5m0EFfH@O5 zIfNJ(7|obOR6!#>lRz@&V3`G+zUB-Jj22+d3QjXI1_nkeCLt9Dh7FuN+6)Yg)?k?( zoUtH#Y`~lYoC;1142-s5&I!&gkiYG~oC}<CHlUyZb8c|X;$~oAbY$w3XJB|BFbTwQ zVzLL_r6nw2sKda(=nPiJAuMpkkb!~Gg~?Tcfk8l6;GYHq1EVX`HFX9C31I<HgM-nX zX@VjHgMzTYUXVXLz&s6Mfkz;oC(|8u1_lFRfnrSt21YM1&q7!r0_0#Huni8v0#l_J z7#MxQJP%=kb`=H&Mn7<91PBYLfIJWYmWvP;;B{eOU<_n>q|U&QAS|E)ik~2+WGM!Q z3}FE_Py_`t{ZVINC=eF-?8v~t7{Vm3!N5=<EWj$lz`z*FWT?Tw&>$=jug1W@7{&xj zKOMpXrnU?WjNwclq!}0{2n%G`F)%PjFqvyGFdPsTm?pu%z!(YU-4GU735vleCU*@6 zh7ZC5Z%i2&7^9g$1GYbe1$KhMEC$SD5D~}*g>)>K#~~uX#m>OM7|-OZ0cu+aXn@?A z0Om=E2&8i|Ffb-E1#2)cD2NF7J2NmaCNV{7FfeF{2>1ywFfb;AO_?Dg@E7FH6mUo^ z5D|C^N*SqO-U<<c<sg-5OrTO>gNVRYcLoN=bS6+X+9D$02(mAOX^sd3!!{9t;~+m| zf_d9T1TKPNB@4{kAtJyDirZ{3Z>NaBbv_0L#vCwjmxzFoJm~mzFmJbrKnW=0<bip6 zL<B%*^fBgxd3!|!T0o&&0Osuz5s(Fid?A>(UqnC~6t_iS-T@H?xk}Kq1`FeV2T*Ry z2A}512x2n`Y$*lZLctgSx`d5M;7JJs10xfJ$IQSD%F!Sh@Idu{25wMhPmqMR7ydJd zgN$WlW(4J7`K7iD3=NEIj9#EtaX85H?2MV9tFAbGL4M$5Y?fkR;NWZr$#8)=0-PBp z3=E9iV2%W*KR*KlBM+FP!0Bnpz`)20=4fy}k!E0E<O6dII3I)b@Pj!PoG(FnSb%Yc z6lf&*BPh)Zf@M57r-Cx45SSCd83M|6!i<}wKqJY#S_}+~B8=CiK!Z6iLCz6l{0K_f zoX()EE)JF{;1mR<Ye_Jtf<Xursi5R93?_;}1tO?%$iT({3J*38kgvgtK)w|21j&Pn zDF!x<d{Au;(hhQ@FjP|~Oc3NGh?!8Cc_0~3^13L+z<`hu25}kKI6$h{ICg*(ftVn7 zBNQ>Paom++U=RmI1*0@$jwAzvyt6ZCKv<4Z8#F@T%E-XLD9<=k8dT1Li!TLmqm_d} z-i?ESA%SrfSdMclC`GPj)KvoA^jD$;>SBT#Sd81isvS5#NHQ=mZU;}HcyP*tLTv}* zG$qiOy{jSv1LID{I#C9O2<iL+2F6{Cp!z0;(+(7zyBR?N5zD~F0dkHw$Zd>@jLgyu z4DuXqpaEzV#s_eBs)F6Zz!?B4DAd3b9Gp6G3=E9wV4F1<xIsk%s5k+qPi6*jkRgm3 zjH{&?7$gK3br}_8KywSAEUO3B!oUg6Gx}g>aDdbpGMa<b+ykXOBgXllfMJkuU^HRO z011OjEK^316%3H#-V7W92AoqtrHMHsC?QxdNN6xxGA;vY0Gne4F^7`{R2o@>Ef(PH z1{F9qU@u8PTwx1#g#v?w1fxCUb+~Q^sP4<4Fm`0D76Enr!J+R2<_I!KC@{J(dV)H> zVC!6=8dia-N;k0k1USLE-N70pIH!Vq=K<zOa_$C|S)O2y6z6(jP-Ow;NOSH2$#{b~ zGMr!=eZU-9kd1zfQ$RL?6OBLE5(dsDP|+9w=5TN-fGW*EaEJ(SZUR-#L12yur#`6c z3kLgE0u<(<j5|O^ffHjGI7ApY!6k4wIIK7rBsdr&8P9{%fa}{RunnNm7RDGxUeF*2 zSZge@);Ne(NXW*6?GoTT4k}6$z#Ksa2@A#~##E4QaHu6iLk;Yq6tE2fkQhq^8zsRB z>S{2ifn^jR?n?*DXh0%A1ME8k21N%(9mdac3=B$opu)(2@sBhEgOU=6Z46F;N(G>@ z%mSRylzfaC7#QuqsY_|G45;=8Cmf{}AoafBWS}$+l*U8AF$~Ip42%)ruvg*}2i1e% zz*F)8c`XqfNJ@`D;hPB#83y_PjtmSQjM|LC@(c_LAn!7Ovtc$k8#*wEgX9@=zz*iG zRcByO5Ms<{ECvlvz6Yg+0x+9FKthy(fw7RW8Fc@rkN~JV!B_<5aR>>#0@XCd;II%7 z5&)<25-?9fh=ChqFGv?SOWHHAamdRvFo=U}Vk~6@vE@Y+85jZ>D;Pm-AWm?5s1nR( z;JgSbo~pn`ad3j;xEdVC0-WHcQ4J%gX(YjU6x80SW$aY|l}pti&(|@YP+(xtfVBMT z!BJ)aY56sPITn!2+X&`3aC(9QuL+zkJUDL{FfcGSgE^j@2bdTb7+b&`F9tRaP@9d7 z1LP%n&~T9g<3zBnoZ!%z1ZFdEf&*tVn8U#-2Fip}7%LP(xfWb<O=YZ81l>Nz#^J5N zz#uQI2bz+54t6laoEKm=)SQ=K4hN(O`ic?M1O-hgPgY=H5C;VuV=LoSc?JglP&H69 zhOvWDPZ2Z{4T`!>FdG_mU5s{$pxy!`>bk)^4iQMy^?*ZIKm-zXy<nb%2m|*y1qKF? zE_l@40@(_(iLs9n#FqDz163yb!QO-9j|1Q!WspwIOJQI<2<C7wuyF(^f;teO;#nLN zaE#L!L7r#hXi;QfkZ-mG%{+22Jq0-(9F&}3HZ%yhz#M1-;AWbo0t!lS%bf?@R+WGR z4llTU4jL+&1~ODamw`cnaS^!Af~1|r;IzZQc?8t_SOVs7a9V>Z|D|A#0H-FX5MKs% zv;>1V$OOjajITklrNFqBF+hodfpZS1CR_(L6<numU<6f%oGPF?X%i!8N``?`7t}o2 z4CZh!C@L_n0oR2}a-bS-J)@-(1A`K{PqPJVBm)}<sMZh%Rk)0M!1b~?C|ns2F@i!( zUeOtpK#nrLRAOL|&dD!lU_1tPAp>U~DBm3ib2vCZfeMKe;FK!BAP&;Rc#?685(9(0 z5f><U&oH`x2G_xP^(;7w86d^mIWPyBQqO}`32;h-T1yweMoVyR1NG)Ef|Y@S7vvCe zkfDs17{7un5M%;X)mIspf-GPJ4W?aVTmdSnIN3n8|8=lV0nYs(V{U*s5|FCmCfEW6 zPSDT~<1Mg$&^Q=KuQ<q1#@mcx$_xzhrMwIb8I1QB*+Apd%Rs)q&*%lZd6#n)D8?Ru zIT{e}JOo=107;FHz!pS6O6$j9nFI!Lka3Jp7`H2f(j>@CaZn~?e9Z{TRr05VLB-KK zMjyCk@4@CXKrH(J=4dd8gVZs8WK>aMV32230>#l6Mo`-1+ze`le+AnNS|h>0!1xU; z!NIv5RM>xq#1W@1sHgG+%n@W@<Cvqwz#tAXhw&$41jx)Mpg|eNKa3AS?f@5lf5FBu zaB`_LFfjfD>*U~61$C7EgE^cG;vh|o3`}c425fX?U|?ZnX5s+Nse{{LEKHzw7z2Yi zNQ#k_>8%PV1Xe3EFo=WlAR`|WIP08I2esS<nVLWv!5wa4aEF_LGaQu6MZtaS1n?jy zqZqg&lmY3Ai-S1@oZya+1h}(N!3pm7NP;<)4B{ZO8Ks!iR2e`uzY-H@ke9O=)U;6s zYi59Ssa3!n4hC^^P_kBK0xdD&4+3>)85x_w;{xEtDU2;(b&%0l#ulc1te`Fhc=VtZ zJYXQe2^yegYy&Hk;M4&%{o27Y3Y>>Q5zq-PPc%69feM8#FvoyX805`vaQDxG^SCtw z17i=E<G`s3>ZtXCIUbyAK@r^tHadWF1E^El4>mf2a~r6~I04K_;Jgot^@(6k2B(n{ z0|VnEumJ^}*FiFqnbw24%A9hbe(V&mvJTFCP+2hztZV{j7pRv$9n6`*dC7r+fpG@Y zehtui6gN;^H47}Wf-_%_fq`*0(@_o3D1{iP?=u%HvxC#vo`HdJ9+-20(+N~J%x5~S z0a}mJ0xHfHfW2^mbDakR1LGpFvKyQgAOjXNUDN=rM*)uoECDNf!3iD;SPC8r_`nGs z30MXm3HTvU56YU$!G$sdqktf&g}VY=DswOj2!cXxCD?EQMge0`XsrSpD8VS;45}no zg9BfIQ9v8i>|6s*FdB>kr$IvvYr#BCMuCGo3=E9xz&tI`ijaM(pcL!^u3aR+srfss zD+C&j5C)9~K~^!aalBAxU=RfjOCUsH-7hB4=qyMrXq*Be&A`SXtHA*2R)EGHz!f1A z0~-gZY+&P%0$UC$h`2$?I8y`K5oTi$2MuR27Bhi{w&Wc(L6u}J(_GN3JsYTatOK(d zIKx3ct!Ih{4e2qkaexdE2dQRkU@8UK3mQQOMKsu6kat1l3meA*ut!1V0viV?bBlvA z2IG1rP+-W<_F!O`!MKU(9B8=+Xaaz7GdP%`d1Ny*k3cFQaB36almaEntzeD>=L=AJ z-v-vBzzNPu+rfruNEhT6GcfJ|%NTI32MuoQ1amAnuY(#;yTBX=&R=E>42-+M91l+L zpxPcVCjc_2winEa;5-MiXCF90BtQn@_JdPS2B$hGmmUB+r-0K7lot<z2ZAa%y+H}= z5IFiLFtBl4*I-}}2YHzBFw<iV1_u7qt_%z>L>P}T8G`1~z`j2Ywhrq1<51s&y>$ZY zEdkCrP%NBe0)@VW0Jxky1s>0NAi^LHGLZ2!(*sQg2LAmD3=ABijOUn+!3{r;Z1{O( z!!IBkei3T;EYRTFC9s|cA_6j83=E8y!MqnD0`j09(iJf8gNOjQJ9!n%`ym2x)HSf9 z7(^MwK@MTO&Sa~_z#t!_4Ql4xW)jh6V1N{fcfh)!9=-#0Q$Hv--v!GEaDukUG2R1f z0nPhKX@m066-@>Pagb?@_n9t%OrC1Vz~I36nCSz^<XxZ=<Ovg~PtU-)4a9j0=5Rn# z$TO&K!R5qrur<*B(hEqghO`G?f;k$TpY$0R7+-;--hgv2DDk}p$EgJ;c<Ad5m}AK~ z4^&dU1#_%8!KK1GFvprR6V!ux59ZiF8tfmy99u|({UexT#~=>!1LG$q9c=~%c{^QD z9sQfh4zv<zE2!P@2b{zhI7>i<;9oF@gR>db@%jfgKmbya{ReXd8N@**FfuUz(PjV* z2{1E12ASlb%D}+L!VJop44j8q85kH@nL#5x9Gn-t7#JAYz#IY2WuPLC9n6t{^rbnN zL8CefoD)HvaZWHtg%jKv=K^yyI9G%G&kg42aDD-`o_N3<14wV47tAr?JPxV^_?ST> zDHaUka-e}eer7Kn1_pU;(2`F^L1uB#iV<+vSqSU`=-7oYn8U#!4pPS`!u%ej$HxW~ zOk&ItAU)vLqBt|C-41OnN`N^WoHd}nrzBVvs6PU-K^&x&QHohvmw`dt0n|p3WiHbN zrL7uW=tSEDT?PhWP<n%u$?}0pprY|o&<>Dgph*qJ%R!*YUk1)<e+CA|D?y+j;$RR5 zsbjnv1k$dY32H1@GJ{$&jIVSV7*r}47$IDhN*Ielxfi7BG&oQg<@FdCRKdCYG&86W zRt5L*PlJ80mYl)Bc!n7?cE+IW2O3Je5786?*K{9A(|v@d2T)B_pn|ZQ1!MwaEl3l% zO6+C<4GgM)OozxZDBlJpfmVpZWpIV92!#+i2IW>z!O#kE#$AxYXwaZ!D>JB@05-7| zY_+-uIFLG+K{F~049eR<327!o8@oQt%QKN&KNIYF2IWFfIkcS>lwueS;VQSYA_8YS zD<};!D8B@i8*|ygD%0R9=dvSI&SeLw1P9|hc2FQNDEERo51SyGX2UgYLejJep=mQz z6AP#@wVMNM(*=;GXi)QPH+X<kbrPt4fv~{=0AVsH2MaMUFdl$t0xfb@1-DHOaDb*M zRKf9ZkOLGCs$jzoA{ho@gAIc)HNfK*hrlbVH3UJu!owUOBh@k)7>|HO8Mr~-^EZIj z^2`j%7eUG{LaeVeU|>*5&M;(Pya={NB{{<g#%5412Sw=}h|0SL3=9y5-a&HcU9dwT zCfr3b0m6p3?jG135ZB#@yY2y4ltGyh)KvZku|voZX2&;(9pDbcHx5uS1@`oJuoVo- zH$ZL6KM+msa7}-ZH2pzn`U}<c7&OAm#0ie84!9;JPDHRXbAp2%Y%4P-+*W2zP!KXG zmw;^LfEaKRZU6_80h~w%a3T!ggc$%z>wFLcxQt+a<wG)nAISiIgaQ0e0~UZPeP4(H z-f#nakqq!dGQbaEfFIO=#UL*PLk#GF8xV|SKnRinAqWFPpax6?HEct<z=3!ZZa^p( zA`rv45P=xR1rNk9E>Iw9Fn~r2!@<G~%4(n(xdm~JnlUUqZXvnmHj-;@BV2PE>>7w+ zcfi67%Ev*0%K>p&G2Ac?B$sg_xr`I+1aP9_L~<DySeQY17HBX;8)DcQxMA8zhUp*~ zrUTXmHcSV}FkP^)Mh>Xgpa(XKLHQ)84e$Z#WfPc}KOlMeBa)Xtg8inN2`bM%a)PQA zRd6Q%$O)QAf%yLuSPg?RXx$Z~B-k3p2Dr(RNKTeQa<UZIZ4i^CxDX~waUo2W2CHFE z?gteZ4iJ-{!A*8RGT9NyWJj<rh{=vfCOaaT>;zT=2^nXw!3@g(SQr=>)4&`?2UA$c zq#@aoj$}(Z*aV0z=}5MuBiWJxR>Po~0xG0eaWO`LOkEFZL#^ThO`tMpo(Bc&1}?@P zkPNu4-M|H^BtXUIMlMhTLR|wqj=hBoG~3R=pe3%tz`*PS>U1%K7DZ`q(t+`{Z-BP7 z_<({^J6IDW3ewM@EC8womAOHKzKk#6_9%0M?9r$NRS_!Oph08?O*c>lXTZ(KX9lYV z4UkkBLR2a1f#yAvxC21_6%e7S0czMLafg{PFld1Nkc^}&g&Q>Wt^pp@PX%jYVB-J{ zk+N|t)?i>z<^#=l6oDleCxPq*n^uHmS}~GoC1BGSlxKrlzrsAA%+0t5qzi1XFb~*I zpw&5yB0Qk!YX)U?P=1%;0hbrIKq|q7s|;8xgR%lBW%clY>SD%e<}hFPfXz__tLy=* zgtUEn!3tHuEmSBQJRjE!HeLg~l%kIZ6gL{+8nvGXR4VDjnlmtHD1h?abRLi;8Udg( zYzCOkpi>Q(UB)v9#0JYQ2eTQ}A^NuRfHMVHW*e9dY9Gde*MVe+GB7BIgQD>S*m;a` z7BGLDK=Q{4B!8R)D}?w1$_D%6B$7W)A^GDp#2=~_Fn?SIyB*?>D_}N*jx${L5!mez z*~efu%pV`YZimQx0<#$ylvjY(Yq0Qw<3P$1=5`idP+UUdfQ1(n(U3S`1uKNO9m)o~ zos}09#gI5)<3+>)J1;m69>QJ6&x?oy0Wh0E=RaIlju#OJ@?bX1?Yg{(IM4&L85ooq zKr`YFU?(wNgS*`U$?c9vZg&JLgt#5b2D{x6$?Z-^Zg+;beJ*IAADn)?!S02GpAVSL zptBn;8wGYRL^c}ChPgcx>|TgW7MRVzpsWWPXsQ7_iE*M8EZl35++K_1_FAw)h})rT zu-j{q++GKEJGfC>&kL$;bt0@_Nu~qrT!<4o!E6SdGPvwCuyY}@)4^<*)0cpq3z1n0 zW-~A-+kl208^EcR@q!fts0GQu*uVp_5>g^J@PLXzNL|$cPH3PKwGk}Jpj-lKX>S92 zh;fEB%;(#Xe7+sY=i9*wAwGw)!9L%P<ntY1pF^6MJ9$A3U=47cxr-O%4xM;wSTGy` zdmZAPqhL0JPAy#aGT7@7*(+c+EEpbvy$+Fi3}!PhD1QPCetZQxk1@dp=8vyP{`iLE zk8faw5Pv|~V1Il=^2c{1fBZo5$4`hq)NNq?VCF;QA{H>4LB|y?E5wJ$MZ#b<%pZz; zh+L!uW-~A-&ji)HCVb!$K-w1O4--B_31G^HC;?2t3L*Z0vcdi^<wKMJW_+L$0Gtxd z`9R4+2efMtQkXh`oeK#{M=+Z~hrt%+F+Z?#A+r8pHq7ZUVCO<)V!><%2IW%Fz+@Ib z*y)eqPS4^;I6aFW;q+{<LWt9$Y_QX_`4LXf;RiV#Qi0_1g9<MVaF;BPAJnYXSp|1# z1wSGRD#2_9ofB}`c78+@bb#3~e@x>?M8R}0n}I?3DX58}E&%oiXwx>N<F77&@P~!~ z!XFx7g%E#0*<gQY2q65S30{K^@rRZG!XMfKV1E?Z!9v_r0O1caFq=VV0$kQb0O1c; zFdOENU;%_bLcnYW2IZrmhCv8;n;PRmdstEm5kmMQR0!dZP_ROXKcH*|P-7-k2&7j7 zv>u%?46H^2ygVmd2sD(Z5eJ%Jj}QW<s}Oqz21u=wEClukxS&pfu)!5}DwwUV;Q*T5 z$ON0lz@XCu*ViKi_90kbFN6)&*9T@p^-Y56Q-|2KRS4{Nu%2xYHdxPgFdM38H&hR3 znoNX&LHRmp%<Uyukde_57B(-D!sZoH*t`NOgoF*04Gx=ENMZ9DtOgP`Zy;d<Zuq<v zLd5qwAy9lnipKXsh@$ZWc$bRK6bD%N{RM{;B#-`su)%)%4`xIC#0(h%W6(JR*QYLw z2tN%78>~+g%!caIh3bQbUm$oP7{sn12pg;?7|e$135V)oP(BSRXxqSCMr$Wn$h8S0 zQdPS!B2~466+%J|$_9sAyD%bEb%51CLatL7oT|Vf*CmVyxo+^1G)Tzx2qQwS7rdHU z=bR%f<YoyYQr>I`8|<4oU^dh@3&1uqFzB#3!So#ghY%zbjzZXAeaFCTsJ>HBebA75 z2M!^Kp7#(oSkDJA8>;6ER1bslR#1OmSp;0J1Ukb)PFVyIaw;N-kW&FGgoGTF4GuXK z5k$zTg4IAmPE7<Ha^U=^E&|SvPvE{Y5<!HDF@z0vsR@`3b*Tl|GzJD88E06i#E2l$ zQ!IoH))xn6L-i#=^+7|WO$3o2+aYYQo(?b@s;394he0_7)NI@h<}&uVz(Qp=QmE`k z3Y9%zg^*Bzupx!OUJ-Cj0WPKXfz?1lWj`cTz>6ymh=3MXXn@yN9E7mJ6DWru>cJs* z7!q>wF0c~$A~=K~HQgmJTOC}L-T*69R|C&z-2t1xz`&pr0oV8z97GU}@4#$`#xKYk zf50?CJ2#x7;IIRS7Z;cf(Ig-WicqL+BBG!q1UmgeSrb%@35$XFjQd?-fhR16NKztV zh$JNfRtO0^2pbZ3qGE`^69cP(1fIAUI7xv6PeKe4c#;q{IKZUDKrYh>aD@e!x)?aA zfxVysW<$K72UZC6f)UsjP=Iy9H9Cud(;8T#3z!Yj=!LA&52g_sVDVyz080R~A)3;V zHD$pxF(^lZCLK0|cL6bObz@+Fbl5kGgF5Vx4%QZNM1XApD})3Xlsy?#CvFi31%@g( zKW-HV=f^nEXy7(+P{2d#`R(F}(rbq}xZDRX2Hq(SS|+RkUh%$L95lrYiG)4k;79=1 zt$W460S{i(un+78jTxY^f&Jp3CbR~4ist}?4Nma~A!@+2>LCbwVJ-s$<6&`-A0ea8 zN8O;K&P)u-_dxUf|G^3wL54v>;XhI+Fi0RmfdRa43=#@ZHaHX*BoLv%D1itCCJ96+ zFiRjpfkgrl3ak={P+*rpgaU^IA{00!5TU>Yb^|07xFrywzyo1}LxC5f1{?~Iy>1%d zP~evUhk|kfX#GG9c*=|MraJ?JDtPjv20Y!tplk;U;G1CM82LS5S?MO&FOZaR6Ddq? zf$d^Yo&;K+^a7&G6|U<AlCBp>x?VzceE<y`eTC?%hU@x@r0Xk^u5VCX*Fdv@ERrD4 zGj4?IVvz)eHpGQ2l89Jih3wi>UJM%65rOD>4%a1uq)P-zmnc+M0%$}_6{1Vl6BZh( zNV-&!bg4mgNrGHx0?`!=*JXmF%LGZ6DO8s%XsFE^qN@|G%Nj|SHIgnHs4id7Iu0+0 zuKjRbUP!vUkaT%Nbv1$(p;kb2eTD0)K+;u#q^lCDYa?iAvL2#K+Y1&N^+>uJkaRUb zb>)L*Q`#W965+bqkaV>p>1v1Sss$A=y%1f~;ktT}boC<X>VxW94VrhH0?~CAu4@XC zt|>^mrb2aDfR^jbg6LxQhWTq2lCD`ux@JRl1%Xx;Er95<gzH*>q-!CPu7yxtQ$dS0 zmP2&q!*wl3(zOCf*9xdEGtd;?I*6{Na9!(=bge_uwH~VL5olM}7KpCfa9vxFbZte_ zwH2yM3Dhas4bdg&0}GAaNV;|->DmL;l?qCSe<8Zu;ky1J>H3SL>mO9t5l}khlLF`E zTDUGgDMVi8lS1TWeko85&!8*}s=e$Xx;Ddg*(2$)N7Cg0)x``N+;WBJdI{I%iloaG zNtYW`*H_RghA@aOd0$v)gdyn)L(&xv)pZB7#-JUdD-^D)9Z6R^lCBP@u2-NT+dhb{ zZn&;KBwc+-y85BIet?FKr$BTagzK7uq-zS2uBlL6he0`I7DU%~xUN}9x@IBinhn)e z2kJ{NfaogqgN4Qdh%WGQ?FCX`aqvL-LL?Iwf=y7>1dX#Tl&bb)V1V?c7lFkg?SREn z;2IEI_b-tG*LvVuY$;d`0~-fu+YB2AXfKKKLr{l%AH*fW{xFy9LvqPJB$w<*a>;(M zX%Ls}M{>ylusFme2O%y2_rDHFA?ohKQi!_yh!nW)2D|VWL@(Io$005UH7FQQfE@^_ zuTMhQ;MUA3h<b3nd|C=pFRurU8!<_P^MYys%tuVp;Jg41TV`oQ*fL8a@&&UrC|^K) z#4L>nTNbc5#7C^s;IIYzh)o*dBX((ok2s_eKH`Gt1^b9w8XOm3AMt=42=Ngwgbnr) zA4EOaNBq(dAKe9w$-00QG46)@$OXwqE=WFdMe>m=*ffZbT#<a_1{R0-$Q{W?9!Ng& zg!l;TaxaL>!Exjb*2|!r1<KKx5LX2T!ooQd$yHfMuF67kRTkI;h^w-YT$K$Lhqx*S z$yK>XuF6AlRX&od3cz|9l;c5Nm>!6$&cj{RgXF3nBv<t!xvCdz0>o9lNUrJwi$h%1 z4{;SZOeP@NJ`t>jL0KKNjx13Il#Cc<gJ5BjC<9J=;Jln9gGg^lV1<x&f07Ki#S7kv zm@H!$#K6D+-mM2>fd@k%{eCtMkg>{kpq5c7SUY1Y+@4Y-d&-dPDFZ8n*i(jNPdUUM z<#<pVp%bi#@iN@BP9)R1kWA|WD}<QV1u+f0F|r$Knw%R01LIt<B1XkvSQyMjGHo7` zY4gAeA*RhkGHpK8G|<^6j2pp<81vz#ZA3C{6Ow70zzQLzZGxBv-kP}?YFYuPCO-;R z#JCr3+A$>4jv<+L46G1h+A$>4jzdi=1uYA`30B0&9Rl;|O(fH9A(?gytPo<_Er@C0 zJ*2mxrn!UC&Reh|#z?qn?~qJ;hh*A2utJDw?~qJ;4>j#3$TUV-a7vsFH;qvikrJ6? z5h;-gtPo-vlPoADGAO?SwUvax5{!4@CI}&!AdF;!FjyhP1YxKNe4xCj43=P&4~2z~ zGLi`@NG7O&6+%o<ftt_(TE3_UmS9YTo1lkef<BT7`e20+6ZD}bYzFN{umnpmE{2<6 ziDZHmk_lE|g%A_0peAesE%EgMOE5l#o8XINf-jN@zF>tA6MUg2q<|LECxRsyjl*E! zkcebL5|Rl?V1*D9lE5ZtfOjD$%Yw=R24!`SdrHBI7#rcHl_Hr|hGbe9SRur;G9=T= zp{Bh7=Y6mu#`AE~I+0B4LNcujtPo;a7sNF1Zs%^OX@Q{KX>-Af7-hp@KAnqX+B_uF z=7AMLOq+*f+I*;KMxY+{MzA8rc(`dBkxbi!WZEXMLWpUbz@{-M?*+}h90W@+E`*zK z5Xpo?NG2QtD}<PE2x>wRsK0v=EW!8~Zo)+*6D}c{a0#psV!|b;300t8(H*b^qiO^! zOzt3=a2LshyI_S76YfGy;0G-#d=8diEQXu#9La<iNG7}hD}<Qv0&Ic?cuV_BXs8x| z>W<%FMT`gGru{}T?GKV^f4~YMru{)O?JvYMO%2fQ#(#>8pW!C`Qv{bW;N7<W6hRAR zRKc5V|AW~KnoOWlAzX=(Hxd>W;YuJK44S&2e(^dbMsK*xI<Slecy;`GCD0B64e*AS z4N4$$AxmsFDuK?GV9@%;!oa|64jS)au8m}1fGpnu^R*W!fTZp)F)%=u@0f#hGiZJR zmEU)i7}tXA11}!BqXb$!qzYc_eg|xaS{?)AJur(w`5b66wL%#*5y!|L1sm<FPzI0o zf%Q~?mKrlamQz$Jg9=~O8=zAwDwRR2gCR?Js+2*#)d1U9tqih{LAe;TMtZ&)xXo$^ zG8sJnH(w2;5@OnX@QFnX%GIE9T^)Sx72{oyO7Nl{b#<^CE`z!i8tNbiLUd`UgU8gs zEe{QK(9R9jN>F1Gq6Xp~O?8kh8sMGeTIwJ(A;Y!W>fqL@A1G3E)In>UG~z)8nXWo$ znYjjd`?Vg}A-d|IqpPFT)iuDI6@0*gpk*|mZRyHxpgl!BU@^wY(XdeKfw&mF@VOVs zmAznBLR{8|<g$JwmrX!&*+huT!0X&6se?jX1H1r!vO1`CgSc`E*m4c<;-aY#HQ>$e z)4*!fAxl|Tse=;*gR&4PzlwklBxCfBfw@;i0};Za8i)`U)j))>s0KKM!Q0%S;*6ld z4KWQ+jSBITxCSD$B{UGBEvbPBZ7B^zXiIB=LmTWT84Zx1AffG|0S#?4uuB;jl$Aj< zAE&`QM(J3XyH6vz`wWu1&mg(`43fK{;@}PWXEjbRF)*lt{csk^59g5la30AI7m)mL z5#k4Myj%hw%?62=%V0l1QqmQ$-yr^B(S-Wv8`uX749eQ(pp9wZliV1Y<6!=oq=|@! z$(o3Gn5>D2hsm1YcmVrhvL@IMKR^QsQy^-<M+!`Vh=ct$MH3M}Q#BFsGffi_Khrf4 z@iRjc96w;c&D2DsxmlW^_|X7wjF=4(2j`eMnxHs@`13f-pF6=mV_;AQ?LJ_%(gt~j z(H!JYWl#s!N*iRBsuqF`T4rOd4Njz*pFs;^3$+=uLF&O}MWHs>-QY!sMcSZ+f$ETz zf#uqum4plonyWzLHg~ic4}$c7ON~3)ps<9b%exT$;5|_Hz_zHRGceu<v!DwlKWT#& zN^0bQHm7_Bvl$q)5;z$cm<_<C?&mmIsSD;qN?ij)scQgn`z26qHrJk!H=cn(RT$Km znQISXGq7>Q=`t{A8i3jm%iI}tLGt#Xcw6QUVl%LDfOg6#%YpKzgBR%ZCB|5gnscB< zxDWx2UJ%>S3lydd%ABC>Dv!a3eKA&pRDxw6dxO{vY#g91S;`zB4R*d@)8@m~*!hCk z3~U^romI*kK`mkBAh4Rla5c(7p!$eGxeT<i!w$0j<8wS@`v-&z+WrA$RWUHygOxBS z-vJe<E?^F$ZUO^?N;v}~gbUg@0%cV)FuH=3s8qpN44S7wV}r9p8M6}@7*xS^;_Oh6 z8#KUMFz1AVQavb@%nb#Fw7Lek*k2e5N=OV0nm<8x3|koEM38Q98e<CsNkP&Wgw3GY z3TiL=gfVUhsRG;K69%$F6}(o~Ck$jVgK`fC0|TQZBn}>eRD$CGA^?d4DQFxl0JY&f zS-`2AE0KW#vI)WyvB$&{y#ItjSqZd1#~-557_QJCq0k?y@E&MUV<<#n3|wI-LSZOW z;Vn>bMMD&}!WBj%6h=c8hJ)($bcn)raE0jzh3Qa*j-VmJdWgd7aE0{<h4oN{H$daD z#Sn!INibg*BNP@x6|M)>#)%Mxns9}Q2!)AIh3udS!CZ*KAh^O@gu+~?!Zn}<StUea zHC$mOLSZFT;X}~k<tyx<`BugyaD`XcLE~89_3jWk2IbqJAx=NY?wL;@h2R3m4{7&| zA9(i+WQZhy9Xvo54BB53$PQ}JXn?k?G6sR!49f1HqU#rAtIw2VSQGFUE2#K_G+KUv zxB5U#`^}0l?GG!$w7(FxJ?H?Ne-QO<AohO<8*~m7BLf>)eI=+AU}OUq)0050Cnkt^ zA4r@TB5n>EPGtd$Gw3z4F)%RQ1?gqpkj%iKA0M9#<um9#Qea?Ux(=2*1D6By8I<io zUhY)}7ZXe=3=FFOK(l$h%Amqg1$62*V=tsh$Oj67J}{d>^BYJ}rZb}qNQWXQs56~G zYz8(C&~|raQP6No6!^GtMjwzGPSDI^6bC3HYA}E#qrs94%Iu)BQqvV|e-lV0xVY1F z1r>&nCatC`I26DqB51jSPDfx+ZU#-4J#Yc*+5y+~zy+b}feS*{Ll=lH(79WTQz5$E z!F5eV(lr%H*K~+3&6gm%ia8iLQW+Rj3QCF?7>l897Xxh~p9~4Rl2lmOO-2g4$>8u( z1&7@fFk2%J<ol`0palY;Vs9Em6uc*6I@l=PEKr%ZPFY<onSpVkGH5+F1B0?0sDh~% z0~blfX|NPnkF?669<s`z2sDG!09FVYWN!q|{6m_@P2klGYDtMH42;c?6$~Yyo=ppw z4PMmI3SKp!0bUi-2G$B$-_Z_cYn%ghEjz>}rZF&R%mP`}1rc`uHRQU%)~XdVF!q2& zK_2V{vos!nTH$>Vwkv2rt6vPXf=OKibPO)za<BxnQM*PAlvF`_*MeEFQALoQ;N?)@ zbx`0<=U{??jRO=S%2B!u42;a;;2@fo4hte?@OlKu+%Pj_T?06XSilM)LBtAP1^@{n zHt@OwND#4u*Enc^gNOsn1_u$RIH>%A1Q8clD<p`x!E8tn@rZ+*3kf1Vh&VWi_`%kK zf(UZVE6AIW6ImfaBm_|l4kBT3q#)7;OF)Cj0KD)4q}LG4VqjoUUIwc0Gr&oc@k}}_ ztwID;!TB!(DgR}H6*4FrgBE0+1JABAz5)drco_jifI-<DloW%)z(Hz}0jt1+AeE~f zL=v2*pMjc0A>fKu;|!=s35BrjK;>0f80eS;2Ib|T3Wg72S{vLnK8R`Hs+td?3v3!c zgbg-L0K(1&t=SL^1D(ylpqvS6QW-)_+W<Gs5Xm${ur3DWVvyrh!MipYuVp|QI}k3Y zu>)Z-DEEMxIBUU$4I@J)1A_|a{sG3d;Gzk#M|2%nl0kVMXfQ$=TvRZc!Bt9w3l^1> z)HDW0h$KXp3|Jw9GUzlYMg{PG9L9u91_sqC(2Sn~_$XJ1N<|+~nZ=-d8?+bJ9;}UV z8b~F0kp@HnvIi5&25;ze04ror-UTXR%fXIdyaCb$E=kLgO1E;bN{9<9zzQ|MrD`Rl zq*I;)s)Y?9O;5WlSOzlm2G?@np#np1kPB78#~v7g6{@ZT#fgzOD2YI}CK-E!TioDt zzD&UC)xgIXnS$92n(IJ~f(j1C2#^inCVd45s7cSDSpzCh>o^#T;4*bkncJYH82;Xj zOF%LUL2Xxmi0xqg0p1|}kS;+W*tHDGA)xZ6mK)?O#up%6V3oDteMBlL#U%`kb=<)k z3=As8MaeL>y2e6KmD<1!I^&0dLHPx!^I-zss?Erk4M{f;E-2kVSPaS^Km}-%8rUY6 zY*?r@frmjL0~F0_h%Q-+8mLRgpd17$N2f#FR|V1qcHeX)_s!r29a9ByAB4@IX#xtn z-S&(N;5v5OgZvKZlI($WNt8D*fR18>?CF!tf$dxqLE6(NinOOs6ue&#+;|d&Y-0p( zlosOzZHU$YH;Kf-Y#pu~1_nq<RR#+%K=)1?L3j+xiJ;uB4+${)97upcxS#-ouo#qG zL2bAsK~TBDI1}VNaMLVF5S+}w&Hp4pa2p>y&XOz$PI57zc0w{(6J%U3SrFX+1@AXb z0gE#z?*%nW&Op{7oQIoy25B9_8KiXxXQAs5&Vd@pC32ujhw*tHtj}H|2M$>9+-r#( zc<wb1)DSC`1I@9jW`IV>O2L{S39n2J<bDnCd}z5G$jutyBeE*wKz@OAYbqgZZIFdk zav-}jz<#U-tJn1aHR%`0=@jKMFlc}m*0IWik`;qa3tS`+Dl!c&k_r`B0T-!+itK`m z^g>0>z(p29Mee~xwn9Zdz(r0%MVRtn?zsyU5rK<rP=L5l4K8v7Dq;Z_xd9dNf{VO? zij;vwzzgYqD}aLx?CU>ZS?JITw<0KF7#MUG!POclBGeixg6xN?b%3ee0#XY$D-|ry zpmPi^QUVpZ0T*e8h(HaQ4>g1he009DHfXK4lnTfZj63sT*;`5l6x)zig_H_7hQa+# zX%$evQv-aStqfR`h6Ts~Srt%QRYMe1+sZ-MPeJvyJcJECJ3|4&jscZHieR=b2WZ=G zsftcZKIj}iPyw&60?IcG>T2M&xt9t!BvL>*${WJI2`Z0$z-*|8!ocnTH4&zQ3Ro>j zMR-3Sl0P9_Q2vCl7?eMOx_1f?RllLCAY2Ab(AnpV)9e{V3t;0Q(;$V8<`mGNVTV1V zHAn`WqdV-uVFW&Uro$d|Tpxp`6X+b8etX75xT=06RTH49qCgoW-J5YTNENuaPe&^5 zGmwh=OlWcc2^2QWkP(rSAYI_fj~P57qRIyfMTjJ1i!TdUA%imLrc%buV51p7!*y*2 zkF`K_K_nTJ7lS(b!eDKTvV{x`s$laW0uVDrz>*BgHlSL42gFQwxJrnC26$J@POv0{ zvO8$3Pz^F3(G5}w9*=+sK*l4~!Q&C?8sNHD2dolenJ$>kz@RJv8do?3ZkI6L2AKhN z*CC|t(_wG}Ms*vg>^kfU$}Es-`UqH)y2e+~p7)br4g-TS=&nh|KJXFrjM+sD460xk z_j!OjR^StF`aM90<UpJ;0jy9%31s#}2phc0U=o-O8jG9k0ZO2{`#}?SM?BQkvQsM= z7#D%1AvMWjFk7vRfpG~~DP%N$sRt;(F)(OGgW8x8?u>ImZUK+0MS#cERKW+#M7V?e zqY6GKEfUOTP<{{ESjz`CmC>*m7E^o>AGm<#8X%Gy{h(GHKg3cVP>)0atVsiGi=YQ+ z*iHl7Y!(8m0X4RT!7NCwboNl!0B2hbh&BdL-KPm=L-U;})DsNKGN92%QFl-gz*rA* z6X*gaMp1XrAuXx~AdOJA0B9gf46IONE~wQi4q=14hZ0~mgK`mQ%pe*vqw^PJ8o22Y zjWnYZ1DVlb0tH13xPz&>5)_b7Hh6X?20Xi?`U$i^GZt(Zq#lbyn(2uL&-6e#Hwj>K z7?guR74hs4P{1=LmM}0t%D~wnpfXSu+?ksl0xF^)op%UZ<1DCaHU})OaRk&dn+s-x z!gL;(rNIp<n&yW<8b{fn+`JEB$6}BjVIWuTN3vr-SeGhzUJc3yw;Uj97!3Y{=HR`+ z2fPSCuRmo4?G^yFjOIKlVPG%>@6PsO290wth8lslFnB@AybuQCH=x4IpIHH_l?AR< zs}!cyA4RJ_RI3r_s2YE;y^Mhj2LC{D8v<5o05uEYj7+#$5EU?Igh0(=Fqj9LqKX8Y z=76Dn1zdY1*aDdLNSJnGeo!F9F$Z91{Q%b*hoUtOsuk>uIIz7SU#tWzw@U`AOu#Tp zzYG>45EU?IBty+&Fqp>1z`&RRHmv}v9TA{;aP1jj3t-waVA_q(frf2!nHw;)Zi8#h zMbVlI)e81SF4$g>FK){)FfbN@RZf7Kg@}u<aI+vPV9qFln#EuMI<J$l9K29|0aQC1 zJXx5OGcXu}E4^~?K|ze6hTw)>Ib8c^P+#I0So;R3b}qQ~v~mUp6Hv-I26io2++ZuH z68s9*aR91=87#xVz&K|K$U^Ww(67v(u}{WOuy4PD^)rSr7({~l+P}aH{Vzau%fWRs zRWL9Zf_49b>IU2X3#=P-roAC}{NXn<Xk!UupeeYK{ReDVKFAOQJJ1pZF7TM_2dL@% zAPI2xa)6u81s=eKx(%Wn<hCqOb48d1T*N<sXak=R4%XcV*DVay4R)I_*s@&=3=D?g zWriXw5VtWHtOqrC6v3t`KuxoO7|Osn=M~&EMW|`ua8d-D#u#D<KD|T<Y8J$w%3!0u zf{ZYD4w?wi1dsbBK+T3-ip)4CxDpnMnyipG0J~I^6*M{&!pOj220Hx;rjG|?AjpSW ztmcJ!3=ALz7O5b&hcYl2#DNm0K6re10@NZ;n1(rmRSXQ4;ONo^Thz<QzyS7!KG-V8 z5J+?xutI_a;tfNvVK*5W7&KZyOQwy$YOO#{g4qDFfYE~WX(a=L1;|N^A?6?l6*4g} zFqnZ3;o1&$3MWV<$n7?)=Ckw|81^zTFqrr0F)%y;DP=IY0-BF<0bB3@szn&4WllvE zESOxtu7@TEh;qgdutkrcy5Nb(4PucbGXsMK$Rb8iAgu<qjJ?4AVt5EKA9ND}h{ZVP zJ=_{Es5Ri&@B+KapBa{JyrF>vwiafJ5XcCS|9l|Uf|MhyRRZ-YL%`N5K&=H`i38HZ zIA?P;%y%Kk)`ozs?EqN|PGX@@YawwH1~%*pGc0bx!K$r6apMNH0d#2{*oG9=_G$(O zaFS_eVPF8q&<arb3HCu4y7Ekj4^D%Wf&%UXNGXGXKPbz!gA?)ss1_mUZQzV^OllYy zEWw@rc2>~oXu_<};;0?$u2NP822=1jKnE*mOyD|*4<3^01oKtd7#M8I7#O?2yl6HC z2HT5JJM0)knLmT>p0?KmH5=wbO0!2Gk1;T~LnsEuIa@&v02iP0!NsQ?I|G9yxB#6G zE<n5385kTuhrQi{YBc~w)&gep!+H!1huIkz9Jhlq5`#B`(;uh`P`ubNhO+W7GI)dR zG0+8dt|x;XAn+I@!oVO7p%@tFB-O%l*ko{l_!Hy+aEMF+pBHDu0S%EU;3Be)1L_`_ zNeLkLOog}yBxw$E4@i@<IMgm!UEy8Jz~FLTkAY!3=yG;fb*MDNxgpG-83P#@7z`3X z-Hnal=rVw+fak$E7vPTF2u^YjIAGDY2^@WroKVMZ0!LpeCj*14CDas%J>W>OV}x2~ z{~Odi-3zwO0jkaf9yD5Y3=EdwxY-My?wZF5i<`aRX{%S93=B?LP;F4-VWDpD5L9<h z2hYMaK$WP$O^&I9x^p^sK2I4`9~$_8<`IsAb!>p@kcI1*QwPgz$H6mxjG^E>ejKd7 z0IZ)A)N*?Rp0>LH)epHO98_!mfa`w))ekO4-+=YQYJ<1z$i?V8uu=QLX6t}@4A;P? zycIlwI0_c6jB~2$85j(~E9|d<k9}hdh19Uu!6(u&h8TfQwYv^J`;9Ts2z;&`low<M zIt~wJB)m~_gTo>RbSPzzMFNNlWMD9G038;U2R`tk0qO#Ocs6cogvC@I_^6C2+zbqs z;8t-S__z#t9$3ufgH3n_;)B;0LHN^o85k_V{k?qf(HTm73=F2=W^w`e_>8L{KG=ta ze4sr9Mf?m5rr>I+h!0f%J?3X%aDD)FmMJ8B7-lyxFqneRC8z<Ptq~%?z+ejA-&zai zmkTg3IDihX`w7*!0F;%Q_{?t^GB9ivU|=x+WXQm91H|}e$iVO$#CT@Nz+fTBz+lVq z6k>}VV<=M%i0O6#G+ERLvvL;rSdM5xn3c1^{5nAf1~ZVAo>1+4Agv&47eK7tCkVCn zA&3F8mPLqx!5n0*h7bdTZ8%gP+*)@AMwpe|;4?zJg<w|pfcbesP+u;DYBc~gsV73L zTL&^6<jad72FSWEAO^@fBVh&x+YL}XaO)ThGC)<5Hu&_A3sBhrcsl56hNS~-@VO(Q z!mxCp13r7?0f-MSU3I`FEEi#5umq<A2tPuU0h$hUz~_)W7KNn)UGP~X)5I7UOu^|u z4}2bpu{bOp=!4HBSt8EB;B4>=5~yYnieYgREQBn<XOoCZz(U9h%(sw$hL9&zV*)6I z?D@gzpiBZ9Au~Y?Pzdb-F+d^oQ-XoPHWI1_9zt#t7#SF(nqgLkgO4?lk%U<p0p{CF zLes%asCIbkBNk$9y(HAyMIZ*q+M^%_$l8~Z3=Fm_p!(p}x*ve20}t>SD?Cy#D?Pz{ zeb9|cprrQ^s`UfNmwpiIKvx=pQ~M+-1_pDGb)eG{z{&2j6a#}T<8w$@!mVSl2lrkx z!2{k3P+8C|cp!C*bJSa4osvxO2smSqA-FWi1do9;21B|iS>RD{#y~UBxs_f}L*SK7 zv7|?YAp?U)3Wz8G5jBPk4B+z052_YkUa@d`fXXWmP-(@$z+fK&>czhUJFWn#wh-hp zP)?h3ss)xv-+>(`CJjrZ@4(LM0P(?z^gY;tSELyjtUw{R6Ka$KXhQsl<hB+D25=hT zm4QZ<5r_c_y=)Kz6j^;T3=9mmFpYMMA*`Pn0}W*u80^o1%2NaI-mDK$b3@@y%xQ&% zp8<Gh87%w^!28Ky;b#cm&BYjK20B1f?gd0QFN9)Xw370WHez7#PzMplAi~~=fx!%P zo-2l0e@>4h`V0&nJ3z!5eXtS6hd>P*XQcqB!Qk!}H~^2rUFQs5Z_F6#30?>10$%sa z7-b4xRp+V%y4!BE3<HBrIs>B{Sne7qI1J8$M`FR#RtKPlgZA%$Y-OCI+6MD_KX@LC zG1L-#^-(`~wJ)zM1A`}c>FosY%oa=O1oioz;E}(H;OQ;KAO-`_xzvms!0HuVf=p## z@PijoQthzV-vBlr)YP&BFFV|z29nz)%fMhc71V9n2zH^Q90P-;J}9y_f=vvPV_+}^ z&l_w4FEO4c$G~6(THPE1wJHFVI!>saZew6Dmxy9u2$Dx;s6;U^bb#*Q0qfZf)w2Mk z=c?L&n4WzwJzxe%&liv$Cp)NmPz*rwq8(!hQ!xW*@i1u3q(Km9iAN}SNoc_<h}Xs7 zUjGXBdZ-3yd^|>hfdS(6Fb$B`pD4h*77jKcM-l3^Sx}=cfSj47v8)~HBhZB^U_XKx zAZNCLZVdr@P53p$dIiw<W{Jivm>vdYsAez&q{mE|fq}uP1!@PT*9>Ywt9-=3>pv$z zP341o>Qo0TKE=RGQTvo(o)Xstc}h$L<|zrV3G-B-p4tU93UqBJqqb&s2h?4lRYYJ< zff*nN?oefb>M?o)@zes4r%W~1!}J)bK{bOJAUzqN#-`H}s2x~5#Ry7bKAxa4>IG0! z;YHt^-yIALhT!pZA5SUJD0wJ@!DrCY*3;kt{0&eIpmsdSL5y>BI$;_hDi}i<4ER7J zfG@!s4q#~LhHH2Uo^xOfH3ZL9K-4pan1Pmx9)@ZIcjQ1>|CP5zUOebNNCpOjouDS^ zJFxK=pz09rc?CEA9n5$$(0l<zJ=FNiP;GGI-y<2H0$Smt2A<h?09A)DUb+kBU5E;h zcX`1h0bmUuFf>%bHMoE$W?<fR0jp;WF>8!xVE7Ex26vCEuf-&ocQ1lk_nu(o3~xb! z4!U3(LNm^}2{+yoW<1!t5cN>wnW5U?#(N<dzZ(=;OMF4eK>(@_bayJq0>(L7-7xPi z@ddFNLruYr<)yx$#Bo54fx#5KhGm&AX#E4|05t|S4h3xn27_&&nLbVMQd|e9v4~LY zha0O2UYiRG6o`7p5VNK63=GatZE#m>1zBu^1<E$i*pVLC_yDLngz@j-#_Pe12L}p7 zJ=FMMs5ZFq`bfq%fYN|U2xu=z0#uy`c-RNjqY|3Pz+efU`BVu3&3sCz!>V!B5YUW3 zq6PzlDR}lm4QvAFehKFtP-84X0n5O^P|(Z3V44g{IgMaN$21ujOp8FXV@+WGA5GAP zaZr=D8O*l^tysPUH3Z&??+9`3WneH@N?>4+*8-IU2@DJ%o#uQA3=Ga75tal7hBPe( z2CE~w3=A;+7eJ+m8}pkU1_tx{x=_7Wbs>5oO0Pn#VgL;xxG{@^tN|$n8wyeiGL*p# zWa<;BQh1}tjTvleC&+$~(j!_RPkSAPsslw&bT0z~q#j{#g1HJL4i#f?Ghkt0*wx3t zU|J6fqeSq$2!l2QgXtI05~w6_n0SHs;M-=BLqNC9G-$)ZECnoY4RW3RJCNlJ4BQZk zfiW-S3CuGmwHX-9K`wl&&A`B54O%kp1yv3o9xDtf>tkRre-Y2XAfUs*V16&2fx#HW zxDwC65U#_(0Ja6D4Q`7AvlmDkD9FItK(>IiL4qt4Y72bC%K;K(RUm^wN<jugjc#}k zak&75Vqh!?Sqd|HD@^@O9Z*#!3^f)xB%KSOf}rsukP+Seu#{&A4y8Xju#{&6<{Rn4 zLeUt^2M0?R)DSU{37`VmA|$RK8Z00U;9vo*W;X`~ONA~2#Ge@2p#B7F0r?Z8g@M7V z8frYq;br{{@T39L4-$tZ4fg~VSh8>iCyUv-3=F1cL2X7Cu!Apx_~2yX3g$cNF))~d zlZhKRnRMyFJm?OV2S?yDs0-i`7#_ks0h&E7>p_#rchI@d;PCheRSqBFjtZFzQVvQc zQu@$jVhdt`!XrT+8kaC_Nbz0>(gsQ<U~Ql<0cnE--w&uQ@SFt=zGjfYAf+IKp+*~^ zCzHsKGccp~!_+?pwXnU8K#c{3>XQlZ(Dw3%8UYgjgAjL$LK26?0)ufdXi3_)kPT2t zTUg4N;|D4q!M(0;;PQ~ofPumLHdGm?@MJJ%2dyM>3jF{TEQ9NvJ&}RI5WN1#DHOE+ zh%wX@Y?3p0bA*in1A{4eg^>%GKMRzm4dg&|!JaTs4ORd;VTpl(!5MDg*@+AcCZOA6 z_k@9ldB1^m%m6J<IUNg{n?3;50guQzJd+q048hA1ASxI`4Z%~?P+kaw!CKJF?1VUw z_6tz$K5z@KPGVrN1Z#(=&^KgYuml?b;b#~^Mvf=Ofu@tU7&0(;ZUmV(IS%9jrilrj z79jsjfpRmv-a@Tln#RPiW)e8IK;z4Hj3J!NPvjxfV|I*zOyvxAj6qBjGZ?%XKSEps z8kuwwhjL`6F@drbC$otXL=OXl!B5b}i(}w$P=G2!%wbqeW?--cHw7UoK-)Pi!A&&? zU)+d+!POY54a0Oh#t=pm1_lFmPzNRsymZ3>supx%Imm&GbEbi;0EZPs1!#=S6l`TY zcohhU@0tMB2DjoD)YA+M2J)aG{?|dEkc25q0T~S{q2~BbVPLQXU)S>*+)(H=VqmZY zFXMa-zAEO05d(uMcsauxu)Lx%1A{4eW8_=#?J-%#3=Cf7NVdyO0f#;(v%3l;2<^bb z$sr70Jy2D~)0h}+AgY+k7@%Dw+kH@3P~HJ`nGBYI7AZ^wZ~eFcm1BW7FQ!gsfX;kP z1W(^>F@_CwPhtTj;Uy-}A>>Km3BAjv3=F2=nTg3PpqYwd3rPB!0_Ig%GcZ_#Hva7T z1PK@TxZiV@*Hb`4E(r__du&0&2nh@f_d%K18nkQW0aQ7Bc<3RE)Kmrr^ZX<R1|~ZO z2J>Xl{t`P-tqIzQ^8>0JUTZ#K@dPP9oyfr80aCs{k%1u_tQ@q#N8mHWF>vMQSt>xv z7bP+<^nsL5OJrc!2v!c-ilhNmE(p=Zz_^fQIY_yBA_Kz}kaFon28Qoo<)E!O4p8NA z)9<ld11Wb)VqlQ6XJ9b5N@8HJ1zkXH4chV(fv)@v3+prn26NdY28INXa=|19hGwvG zkYjS7%Hgwcn^;Uh%0Z4<22u`k%t^5F>xm2u9Z==)(UBu8$spxl5*ZjkTl&pkBr-7Y zJAeWbw3%u;y7H|olR(M?5*Zkb92gkPT@o1>Lcz*GTdJ0!E8oF#5Tv{^k%6HCq&zo~ zfnhdSIVhU8LY2c4!9kV}AmyNF+6Ph&il&EP<)COf3RMnw`#u)w=?n}OplD(Yv;ajC zV<>}HI5hHor!g^@Ob2HX@SKiUIaDHP8WV#rvP3&nqI?<?LmINgbf`qnG$w|6BnhYG zP>F?*QVeW9gMB1us^vF0A3cD&6)}l(Wjbs;=Qj&z&;d4h^_vA$&@zTF*n5I%Ax7{V zkir*8+Ct2t>&<|5?-{{!KrD{1nFL1g98iEG1A|j2R2!(M0gbUiXWk4pfmZg&flW?; zDlvpuz`!`?B-~^<u*scpljXoBUv*?)Fs%daz>x==py<TFV9Ej7ilhMMhdVJac;!Ql z1(g=-W`F~osTf@1lrb<k)k0-KVE`$0KxGlAnl<1DEwK;=8?*qb3gNVrnJ`ZagPm68 z1oN~o*lEW=P60bj1Z;wkGt6nCV7`Yl1B2IksIefYdCo+4+FqzE7N^Ys4YzQB4SE1o zg;;s;AMP{`u+y@gVNT-!J8e72DPX5@f=ys{fjNx}%(rx5VDNemH5TNwCm2rq3zfy< zv`)}o*stJ}CIDV0!N34-l+7ub1uL<?f=lce7g&k?4P0VRb%B=H-@qmIITr>7S5c@b zkdy<OoD7B5CU%U0kmPM(4H|%X0G^CffGS6X$0N9<55V(!?5<Et9)M@|99$U~EWxWr z9)jog=DRX5xY|Qa*#^%=ArMPJ^K7i3)%j<@wgo`dfo1?f$(C_Wz-*Wo&wy=v4YTbG z*fte6P!|}~t2_&~t-y_e!8HnM$~A1Z<$~6yz5vHe0#sce$SQEnJ$*LRFE7Bexhvfm z7@RVpsy@I?3uRz1o(o#HdpqU>R1!M-$-p>g<s6uUZ^wWp>K?f<FxVtBFy4s)Ey2-q zXJBxa_y)0XC4^#VoWsCista1>b3X>OZ7vU_XdzS)Y;?f^q-Z{92<<!Aq<!uT3}&G1 zk?0B;zsHy_NoHVp0iN>r*#I>M(r{p4Fa?|O18jzf2Lpo{Xa^&@BE}yv9tV=a&h!2Q z^&;DJCI)X7sA=LL4iA)LJRNkK@+#0yq$S|S=>w=l3w+FS%RE@)6rv)+1J*c&@F#k} zrn#2Jfu=j}dN43}f~Px|#U)5GFfdQtP!3tnu{;hGj{2Sq44&Y{9xLKN`?i=TE+~f# zeXIn#V4f!fgH0v_<0`P~=OESKG`TtsbYL3u#0lk|;9GXrf?K=H6C27Qt>1M}en+`4 zcscmZxW(VW!NA})frWwL&Rkf_?I_q97cT||uO_GpyXi~}2j)VG9cUY@p22AzR2JTf zL^OlFq@i-47B>Gpuugaj`yaHi32$LDPwXgX@biOe(S#^xs%HQxsAXXAiiFC<PG@3p zhsbi&FmNcSa44uTcrpKgcmtHe;~}z4^$ZMthEVY;xEQDym^h)F!OIgW1!`MPf=ZV$ zF!)75#g`$(yi=fJMc|~M2s$=}(G%29DS%4sg;>wPIA_{?So59{Jks0i#lT>y4${E{ z)*<Q*3u9(5zr`CepTgn^TIu@0n}GomHA<dep=P@sU}0d0oX@~u$_N@U7V-q`K(+T_ zU~u{XRRItD-_THHVDN5%%9T%NV(?l7<$x5n&IiW?Co^2Jt@cld^FdV_Xztsf4>aqL z3r+(DP`RBDB@B#nPAq`=5TYW<hk*gofr0R6`!F#0gO~Z`fs<zjB<1FVjl1FlNx21J zDQ91pce~=IKrM0uEi;<GfPumD1!#7nI?hUffq`kF4`hXO4cHVb(B9)}s0Ibl($t2y zmIVw9=3kQ;7<zme7|dTLGceo$G43WaFsS%3FgW!<mG?{sci5O}K(#ufAp?U`K2&BC zR0f{98N6JeQlL=Wz5pC>OdJ^uw)>&tpb!IB|4E?Y;lCrO$MgXzcNAVcWG{mCkp4S@ zQq3wq1_n#;R_FhYpsmiX{tOJJ;LXx3PB-}&7(RpeaiFnMRwq!S$|it;!4$lHoDIyM z5dccszaTLZxe($ksH;I4hS7k*D-J4saXJ%2AzTTA*9@rm^XW_s6TspjoXmlsNga@J zemkJjE5TBsklq%^yc<vnkiHXO$q-IvewaQlx!)k|3=Ds#Gch~>ONWA{vAj&6;vjAR z!QvsD%oZ?het}SF`9(;cU^D<_(&7gU3|<{jd62Q@P&LI23|=#k#QhQCPV1oJAnU+A zc}<vA3<gU;iTaT{C_oQDl|iSd7#QbNE@oh`tOF&uN8qLL-vU5w4Nyb&u{)^Kp&!V= z;0cbhC+?tmB<6`1>P*3hjz4t=?a<E%WMHrXul#-nwqaKw1A`T46Z~JORSBR}`rTc4 zF$04+e+mP`$3O-Krx||`p$1Ay<)C>fkgg!8!~~Eob`MXOF7+S=2B#HJNl>7`ba~x^ z+5^g+8H>T8$^4lS)c9d4XJD{(hUx*eSHXSd7oer{Y+%1`fXX4d_{vKd7%agX3)sLL z3)+Gh7%T%oBg7E?{vZYh)5V}>6g$`i_h1GF)7PNdn*+?>7R<olwGV16{|qLEF9;Vg zcwK}_sLWtu5LyCuDx!^T`yVO`@)^jb#w?&FjWmbCUr1Q3gJ-L|AXkD17Nj8;ZiX;0 zSb|+4%>l}0lA#O?0VPmvpcD}>56ZEh!Nd@75y}C}ME`@B9yNoB!D}{@14<MJAr9nZ z&Vi;RaI#8+au{6CLDhnc1WPgKz}g}P%R%YM7QAWZ0#unW-1UE#!irH_SJ13aL>OqE z8Z@k93*JHVEetl@VGlN;F&tKLIDq+@5wKYzM_14+(aZ=)$?D_^sz}a9z)IFU*M$EN zzj^aQ8K64UD;mlH)d@3~f@1-xPVlONN`usHfJieMGI-5^3WIc=hX^C-@;eQcehiTU zt)z0g0~G@$Do~_yeg_R=889#y`~eLve*y0v{s2|P3=bdK<*@Mi0^WDb76}WVFW|k$ zOCn+Z|LOwrzkU=fyuN`=Oa<}5!%*MBeBNkC`227Ig->lXEPRw*OF>Jn7#JA5HK7cU zpS=p89FR|UE`x>-ymjL>2`UXzdjTR13ZFGlVUVu(5MiXDLBHEjY3Ai%1Hs|*1}cUb zJ{Lf_e-+sE42+O~L4=R(3Rw880*B9*Xju5H0*6mf3@m)sfWzl5h!37VTMIT(I2M+k z)`9uEV<F+Q9vnW}aj@{Y<<bJR+uIV#0QuRg9?Ajv^d`hRNIvyi1eFG<{Q!{$h0h+S zFi02w3UDAG>GFFGl~#sGfy3tyObn6}nc(Tim;uy#Omq@pf`m;pB$+cX&Uph08xDvp zC~YLgf!gPw6Vnr&K(T)%4$|mK2J?*L85pcUhj<u34LSe{_e`f9D;OBef2S}ofaZ+7 z9ift`Gng0{96-vwpdueY%CnuoN>9c!Ft|oQB|u6c%a%bafsFM)i<tVH3!t))4hU%G z^VCWP21_%DENG*)XaWO+C3rtlzca|W?Fo>mnE>WJOMp6e0@NS{HU<X9Y0l{@q0V(p zWMJ@K2$cl6fx#N&T$l)aRl;=VX)75RJU~A5UWKe2?7PiSkpPhCvz)=ct4U;Fa6Jf> z0GW*Noxwd&@m~ktwDth1!WteS&Z`(0EWvyJ>cD&cP9(w-Ydv@$oJbM_gQq^|%zy?D z@Pv4P_YbHcWiyx<oY<l1zIz4}sM*HJ?C=$|h67~2u?T2(cR!Z`REaNq#Mf*MY@}*G z7kH#96ta-%02k<Zu!JN?pdRD`1#xo{1A{f_YSw*FW8l+**SYqrVqh@$HDzFkNJe5n zl;1&DeuL{ZO!)+uau5TeT#N;hRN<!I<&s(rHFPUXIfwyKZi24-9+xLf`CFKB5QD)Q zbOCGtRJj)@TtGekM_jX3Gcb6hn?ea6bQLberVI>1DGUtm4QvbyE_J3144#l?05~uX zK@EgE_ZinonEg2^P<MkE4A!7)W}icqgC<QuPGEe+#khun!F-b`14BPZ`EpYRhAkk* z98(5{8z76!K>CDPA)%rO(gbq%Yc8;!e;}nGJxZwz4CWv`ZmA$81}>oej0QfS)^-6@ zZxToXq<7AmH4F@v-Vha_W<X9VY$V?hd??1bR0amGNl<M|XD~5LTmvqLK+Pov@1szO zT{D;%yw5{9M?raBIRvz4#2Z{#GX7e_zyLYB0wSOaKBK}LT=J@bSLvmuAV_(Is= zCcdA`Tu^W-n}Q}Qq9CSutc95d5rCK$1=a;IEgH;*m=*(JgH4Nd0d4$bFh~RKiO2?< z_5kW==)eX8<D97Vu>J=`g;p8^gC*F(*<hniq%km<fE}F!=BK4IFj#?nD8L4Zm<?<U z42*>?8S5Ar%$3(NFtB7WFqn(4Wni!ZF}T+<Fcf4kFt~%1x#+KDVAzwvz~ExNmVtpS zlYzksbV3Qtqz@pI5?mxeCUs^)RGQajGB9Xnf_A24GB6}&f*QdfJz7xP4M3Yz6J2~@ zde(xJg7ka`F$yyo81%D1)wdH={TWc}E)>dn2#PueD^Q|{fC?pm%*%5zTMwFkUc<mp zoyEXlesT>1!x0c;?-~Y%Us((c?jU6@57#g-L}oKExV&A%z_2YF>Km9z3qU3Xx;%rq z45Sk5o158C-w5Wwd{Ydy{Q*c%kc$+^c2Ecu<S;OR_3Q#MK)(3|(&OC@RsS30n^{l} zsPoFe;K~N&3eIE#O+JEBE@Z$l1k`f^Z~RbB290dzffpb#URck-pbBma<$)JGLH0}M zgC!wbkmva7Y+cX5pz#f~h`1i2C>*q`v%wdXTp<as5v+khT>~uB=L<@D5FPzs9SjTx zdqJJ7O<>*ws3W!E1+v8^Sd(QFc-fO(E(3!l*jW%A$8s4MOu-w}H-l~R%VS`GER5O$ zUKn)<#0RI_tzdcXd<F&^aJt<F<~!yyFj#>S`x~g$0-%YE9lkd<GBB8DuV-KYS>O&5 zbSYoYz)+sgz!2EHo`K;O2PB!hC2%k>2ybLyaM`w=fkD22fx+d_dIkok0tN=JckB=~ zpu}}>1GvCoDq~>q`UMp?oXNy+7c3qE?!SWi%zRLDL50j$uv{ppMdPgv6$h0uwrrdb zgF#JW(BO;nZm2A1fhed@3kNx9!g>aVRFH$_tY={805MjqXJA-Uz`)>i3aUN;oEQQb z4Hy`_en0~te<l+{{zkZKy?ddq1=;L98_EHdZeEL_9FSWVBI(!!6$k4$3gv)wIDLcK z3-9?{fcJbD+$L}^FfeU`wLnjS*RfqFfQ@0D_63b$v4NIw2c$yH1eJl_o=`JEwmGeX zazGt4aM$WHtk=b$$q#DGMSC)C+5|gTAR0W}raBcgbsgggJ`D8;DEc964RErF1uxHp zoFNnkVW%@N#)F66)FB5LrGkgoK<A~UAq(e&g+WWvn-mxr80)}ecLLC;6Nkhg1LK^> zn_(%u4(uGwLfF_CL`O#<1B3ek4h9BOuzMQ7Za7#7OU8|0{`W!#22&}}@d8a?Yov-8 z7_32gM-6IU4=7oH>be%svzr+h%wsn(Fr*YQFqj8!VqjPTVz_Q%V0co*z~F<f#U*nS z1A|mC1A|NHCI*H85TkJu14D5!1A|Wh)c9Da@h&}^7#OC5l+4(~z;GU<qywr%0;Xi~ zCI$vJ=oUt&T~Jw2RKf-t7!2w_{r3`ZpVfd15>kN>ih*&?{VlKnD*^X{wMt+O!%}cB zSiBV0t1APWun5ElA8S$$=6jXF(pUw!7rd(s(yFTj_kur^!Fn;vT`xiH_AY@kKs^)h zvrrDm$zH#p9FWYCE#O?kXvp9#%MEcZNR3|<lyej!&s596;8X$?!|1^oKLJfQzVn>` zRe+e=GTsX6k$~3Vy#p7P>g5a!kR=-L!9^p8?>ZH#tp%L3!IN1G3_&WOxfuhO1yD)Q z2~D8bV4SlHZl-|?sNs_UH`5T@Lj>``J%QIyt#Id9x-8iW?L1s6XJGLD0962L;xo9) z@Id^J;tXR|&_=SQ905>CP!9*<jIM1A43^+?B$k4Y-1-XIkq$oWa4GnpxR4441}o5s zT@g^NA3)i38Hf5d(3aFJ1_qD@?|D$ksWX`vApMsVs0eJzh;ao+6i5rG|FWfmfx$Zm zssQY2GtjAHFcDFZ%Rm`s6^FTZ76Zd|kn&on@?{_!7>wmXEvQKQ4N%clFfDVqcEJ1- z2|gf8ppt>X@+hct8D$UZTsBoQFqndOHAaKapZNmfgV(Xf*n`%widR88o3Zwwj)qkg z)c5<K7AbHtFfb<At8Rz-9%Q_CATK1y5x)NnRSxgHrP)V-w19j+vx<Si`wvtB*!NbT zLyp;?h6I3Y$gp1kQvN)Sf#GQt1B3aUI0gn@&~D(1aSRM@)eH<yvQXt4!Kp2TsSdPw z0@QJYNx%a>+x`~FERfaNAf2jEogk}0)fh-8IQ(G}@a2tp_TcbeQq92NZ4T8134eop z(Ej*!;2n_(P#N%UTTryk*#nP*b>K90w;EO~tOx6`sey)SCRFPLke@elRPTU>DoBI( zN~k0xR6z-(1}XwiAX_=sfwX`UNl^_0gLf}f0oak=RZ!kJP?FJH2&!u4IWxZ5!N8!} z4H~YW=j^V*z@RD!^2&U0XB~7b$pSEoA!rroIG=US8=!{lfVYd%_rjJ3taAo+R9Dw9 zFql39`Cz@Xy#fP+M=b+`sWxboVS_WM>$(KQ2dA)&&Y%>=P{+Vv3Qm!moI!hZ%s~8V zP;Yv(Gw5!BNg%#88v_I57H3}#28MTakV0}R*kNAvpuLt*N5Pgr-`&Z;U<zI^xDV`v zmGuk^W}w9I3aSuZb{upD4-MR}XJGJt50wNZ1O^6Y0qCv{*lH)ET`)sWg8jkN05wz! zst|7I8Hk}44Gaw4@=!^rp&n2!th4w5Zs;|z=c5~-!z12Mg$F<ZdDFRV7qo<%2r@JR zDhW0eTqwds;2G?e^G=wSEg&t4$XdW*0TY2|jN1@r-)I0$tRpE0WeU((HiL5mR1~%m zId(TJNFIWtimwq`+D(Njgq!``*?%|G2QG~a3{G>Q3P3)9nePqL1sVWja2DW)lp>&2 z*r4L<I^42f;B-(0v&<N(5N;Wx%O03z8$gy>LlvM{rV14VjYN9Gi~!pV4$x$%G(14r zT)6f?eekRiR9-_BfDDBc@l8+>csk&8v4v?dYGPpUZigy>XaSW9v!NpJ6-GQR(tAN2 zyB!P+p-rHU-3|tZiY5jIA9R&24|gyy%x;3FmJLvIK7dloGUqKYYkz^PJqVSASPRMq zFcG-tmODS*0~<^`g{&N$QqDs~-~qe}l2Qzt85o?dLnT3`gJKd?d@?Y2pMhqkn=_dh zymvrj8ywa*kU2k~98kc!3JO4C96B|``k66QS%85d2(;;wG0wLDDh*2zjB_r7iXO;P zQE)pX1hml=vQ!kj@dd>9s)cF;6?A*|g4;z*#S#pzv!UWB4TGS?pd$b{d=@|@5q8<{ zgSB}$z$cY%hTFyI13Ho(#P?bY)dsRlb05O4(@=2~y9^eB7KA0bgVs!3fGS9Wga8BM zoWA`G43^*{xDvq!eF=f~iGsVEiQrkPz*YtZ)5oA~KuKW!dk`NypOg$Xaa9|v^*G;s zsUXByFAgXJv~1uc*nQyrdR~@L@mDjM7#@PfArqj<P;pS3@&`nm(TKro3se}?WE9;G z@g{f%m(hU1?-W#06Dk3k@q7Rk1DWrLB>o#J4l*bnDh@UXH2vzw1wEOg7%EfDz~H0{ z6$9x2B{#Tg27?=*DZW48b-xZ!RdYZE2e=MyJOEqw`zHw0ywm~hJ_N6j{SyS5RNd0f zz+eg<Kllrle+%Lt0j+%a2j+Wrz}5l(2d@KO+5uYD0kuYU8WV%s0dP1V<^!N(8Vp{U zP*n`Gm>BFJszBcJYJ!S`A|nPW4xZNsb<O<_L1hb|GQ|uGw%4Fy5RWr37+eJPPK1L& z9(n+kTM4h2{~m^=Scr=EATNM(he$AJ&aS+Zfx#5K6j~H)!haB72-I^E1M@w)7#K`% zf(|Sb2lFR__}~;K5e!PRZ$W%ejm#(smiO$2HEX26{Q7QK3d{}WhfW=OeSta?l%l*2 zf`be+*uV{y2QB{2hX_MvRXLft6`@m-enwD5&2R+_UM^5EP<EM(BpwbG2N`$(E)MR9 ztHTWRN{1=}X}$qh#Nbs06$fel3l|4#)`DsFo(WY1>R5TLg>pb~taJ$MEzn?z*LkQo zD2^SV;*b@Ypk+b&B9MFuvL_W;mXp~NW{;N}lA<nDMecA#$xub0l(-F|2xUb&C$lD8 zZ533l*eoW7D-gAil@g#-={Ex^`vNY*;I|hl_6I5k%F@@MVj#CG9)|cB>@!Yg6S!5c zpo%~NYzbGy;KeA4@OT_t92~%i*pYxL0%<OTD`N1{hl+!2o(&gA)9eOS1k$`2u86@a z7Ag+Xd;=~H*6a#%wqGe!(Hp3O8U_a27N{5~@702irda{%|6T;A^a)Toczrs@_XsTS zK~&rZ<qdG&yA%v+^n&;SGojiHXMxt}nSg48Vz7A&pc0$ld3nWgn0XKtB0aDkcL~_M zP7oiQmrKDW`~&g9wM7}2?+)5o2+q6Z;JmvH#ODLmbroQ~O&<dTsI^}i3_3O;7`zsw zDi~BSGEZDl9}GUNvKnj>^TZAHp5TJB2Fl-2Zwf92YQg+?(EdE|Ol=*QKeZ253Y-qU z4E2}S4k!au3IrSlCmv8Ka2_fQikwo2Fsu}afJe?RsG?4|0tRm`*tYUnOblMKP!1^1 zZGkHS4e5Y}JmaCVAUkd%%W^WO!tC%WLsIk)RZ$*XQ5TXTv14FQfO3%6J|uB#xHve8 zmBKZ1Lx<hmW-&3ugB67^)q<8kG1Y;$?9?+bcqu{EfSlR_Rs*V)!3H66i8)je$hULg ziWt27pyD9S2jSvi%|<X+cqc#=fr>4!awrF6(i>zQ;QDA5RF>g5#HXOuhTEWGA_y_B z^H4F6y_QgMXt@B-U|w&aia^CxDnt>^;)+=UQdWS9t7eE=q~gj3Dmxu21F9wipkkn` z3R>S5<O~|`m=%-&6$hP|0csd9&PhH6%T}|%wfsj=;ss~Z+2BpWAU<fR>YO0Z=tl); zlO*^=<GJ8Az`uS51~&yR28Nd>K#c>?<ip~iSt<++N)w>%@&>4l30w>ejH`o!Pe6MR z5feab`k<1qmNTf21@3z+fJ(rNlVw3|FrC#C7#O@)LKT2?Li!%-p(1cCD}r{yv@8H= z*#T7m)&g$T!$d&ydEn4k83gVN9|0*ph^!o9#R;eod{f-oAeoa;|GWU{xdK%HwgT)C zm<U|Uh9G~K7T$>r4Bihgw7^8*S~dqY!L%5Gw7kL40uzC2*%q`FrX?Jt<p+kA2a=F9 z1rM&{K`&rhYCu|^LKPtVA}j^b0=MOCkn}02_veGO$U_x?wRm$t4FGp}B%mBnFULzA z%E_9=#Nc}ho|#+?q54t!2qCX@85kO%GHIY_0=H(rf^F7iU^p_7fx(<zkAXpX5(9&a zupR@$>PZX?&KIFNU?W3MPBAc8fRr%?Tl@yK=)!!^m4t#c+unm}1eKPc^vGa4A6cZH zfgwy@kAdL>R4^OvqT<sG4CV%U3=H2UF)*0h=rJ(VO=e(l@z7&nketH6;NvO{2|n0h zngvJ+W0(a<3u7>bbSOxpZ7@_5$Tem4AiF~D>M<}RK!xo=BH-{~J;T6Y{!5R6A!P~! zgT*^N1_riYAaXu3+X{4WUO80n1W=Bg#rzTEY|!C(b3yywK!?>`p2EQ3GzF>(v{@4A z@H|_XZqT+K=+QX_4xmZH-Qe@|7C^Nj9Jb*MY;bHh_&`75sjv+yyTJ$g#ZQH8>)8V~ zVKs;k-qy1h%zrqQfx&Ap)L2j$Pdx)pG0eq3KubxOi$AD=)|i(ugoC^bs(zeKLsf$g zGX#}@;KCPFgfke_fNo9L05<pmR29Oh(Pv>!-2iqf%QTo%H-McQFb(F^jbIbzfcRji zZUXZ!O=Dp2`U^D{<Wz^Vgq+GP14$HEoazkPjk^?VumV&S>;x^wIc(=(PF)Ii>Q}f^ zmx7(@I34EHWndF}L42@NmxK9-r!z2k*+Pv4IrTL`r+PzGV>&g2U7vv=0V)mJ3l2(^ zjB^UlF))~`=rb@p1MQBJ&<7<+5INrhM6&$?k@Nk^q52}vfx-%Q9+OiuR2VJ&8MuOG zPmX{$$V`ALL4=CTd040%0dIzq2W=?>pB!}rycsHc1}s#Lf=$>C;)6rw7?}Td1_OiF zTBxz0AZ9!da#Sd0S_8QqoYr<jRfA4qL`!S+pz-DNV1q9}RUy*aakx{@gPke>+Q$WU z>UpqJV`jpfdI4<03J@Rc)Qe#Ly_pOQUT>ksf}FaBpi_TCRbz2#0%(ibEwI4?vXB&s zaB9&7Sg76tJCzZ%#R}}yTVSX9&Vo7hHrRxjAU@cscfkDfvltk>bfLzAoEm<CP^el% zRbx8U-UM`-@g_FV+Mxibs%_BjG2<Mj%P^;IVgrrDe}X%8Gx(ykUeJCmBhbR<&0rIj zg0?Mzmw|6#11$q*2koFa02)%*3YHH5@qIx%`nQ4kYe0NWVFm`q?QHr23=HQ%d`HmA zxgB8s7Z4wOKI%^JWo*gw7#K{!*T?Mw^E*I%@TG6N!R{#qZ3_Wk<hBRQ{|(}UFLT=q zR`0Tafx$E$wBlqR*nIhgps8mNe?M5g6NnGKEbahU{bmqfm4$(U@gP{e#Ucg<uYPDS zfr9?hMM%)sLkiD&Na0z>5DrezpeFYmsA^Eqe}Je4Z{q<^DEVE7%CcMnnG*ua0$z`y zVl}gv805j?p{x@()K6hy@OqCVWe$-7&A9qy%Rv&EH&_f}Oes_hWK1$xJQS?<9#p&> zECzOl_fx1CNUfi?JjAdWPzA*d4BkdiF^~eUt56Q8EI9^N1U(EERI7qUA)X>Bc!j0_ z>J=w91&A%6Eq|a25#mDzgFB$q;tD>cR6r4;1b&RgoHTfPbY%yn$7s+_5^&aW1z$Nh zWf22|t2k5}D4#(WWI{*I>=?nzK@1!%85kJt!FDJ>6(jZ_o`Tz954Phh+zxxN9c+sk z7+kfW+OXSU$iNU{sL#M)096GU!3HgDjk&_WVD6;Pz+eyB!r-URz;Jvq1A|MHJ_CdQ z5(Wn6nNS_DW|sFA1_ld|GR9yFkT%9JA9N+5AkDVRpc+9@0BL6VBa48VS!wzV3>%<= z)u1W`+|aoNa#6WH1H+Le3=HO=BR_nWGBCLG>oYJsU&_GXBdP?61b8zGq=Yfd0;Gj8 z7(+S~q|sIxstM#8Xfvx4w89-K90RxA?<xa>`7{Ft2G3;-4Cadr7#OC480!oe7#=QT zVDRWNU|?|BWx&7?ww!?>;+O$w>r;TsB?AVA%H<3UwysbEA<+!mWeRG)fVPep*ntLA zK7eOLCqPxgyumof<r)KnCHSn(58zW}=Ykg4gEt|51fMJWe>r$}s38O6NAStA-YdX- zBN%`B3I+x<kgs4y!@E77ILtxr1{r7$@(D;K*l!>sz`g~U?Srl_6y(oPkRL-q9(0`r zbuuW1Ksf|7d69rf$)Mf4c8o#HpBaO~LDqp*y@A5j;0dT98U;Qaas$+eR*(cZy6vvR zO7tj6P!4(wS{e>M`y?8CIAkbj?KL<@MS~BAT)2{f!K%ZMf#E0Ar~@F=QzW&nGccHM zG-P171zKjk*N}lh7_`oJDd-4G5M!nx14HC01_lRE2(c(b(h%r$Bt|YyaIAtuW+F%_ zDAu=t7@%};eHG~RYHkJww9xQYf|>ye4bYjBF!vxeoI|{g7#JL&>Z;&gX1u|`U>;+{ z!0-!XNrn*vgBxhkZi5j6!=%*=3>GCupq3PfobPY}bU-B31Osl+NpPIz5A_)sPJ?s^ z8Za=hfEMtw8Za=}f*3#b85r6?jJNs>4CmJ{Ft~#>xyTqWFmSA8V2IFwCRh^#1_rye z3=FpMP_t1IY!E2HmVw=C@EEjjekJ(u*aJ|tQP99+oHOAjEb>-@kDo1D3)&A28YNf- zK7N*Q9jt-93T(m&5FdQ}EQDVPTKEb+S#1^g_*s?>u;XV}gO8ux4&sBe#2WDNvmqN{ z$Iq?>A3u9^BLjo8f(pbvpvw$EZvK4(b{6b*@Nu(dpk;2LBUKo8fccS|7#N(qp_)L+ z57exKOtRE4goA<|ytKk?0XGAK`%RcxSHb7VPKTRy4b0yKGHVe*v)nIm!^}AbJ{<NJ z+??ZJzU*cO2B+Ty&0#QD0y-M)1voGjR3ZM3hENQQbL4NsqT>ZPI$}Yq-N1S2B{(`> zfcW6N^b%~sdeGu7aCAWU37`c^;OKY>j*iz`VbSpl93Atv!L~`i21keWc35=00Y}H0 z?F<ag4NzynPJHXS1@rnhaPZ6SfSr^39n5#w!NB0O2C4~9bTIHRFkFM1B_aSixw{T- zmMECN5M&m!8YDn)o8@l619KCrz#MZ12GC41<fLjgFrQ^71A|ixL30=k_JQ^`$b%1+ zZGb8-0;NH4(Ka6*9rEBqWnFf{a)&(lSlMkLJ~%oQzz54R?P6f?I|((W<2JYf20gyk z=@C>I<S9rR1?A6>Vq*pd26c#kgW(oS-C<xbZ!u<Ium&xVm|)DnFbl+3Va&kra~A`H z#T;W$0RtlEyKFXQV2Iewz~Hjqn1Nx-ZUzP?L#TNmQ$Se*GGPZ9xdrvKgTUFt;0LH( zlnlOkFafIL9^3^1cVTr|GWaUO|GQzw5~P5yBYeMyfx(gibl_78_)0?Uec-x`fiVSq zEn(Dt1_rkT9tMWVcOWZaS~x+YlU@fvOP*%yGcc%VK>Q3pqNRlMG|1DSLv=y(pyr^1 zb(b7qU@!+AuKNh2FY+eTm51&!Fg&}%z!1)4%)kJuv=|~^LZ#yFF)%RRWnc*RhfDp0 zN`WF%1|$UvHi#}Zs3Sn0H3vz7u5kfNMJYg~;KeHFY>OZUhL9dmjR2K|t>a{z19Fo2 zTq6bsxq}Q0<|~aD7$$=l2aOmQI1Vu|Sbz*>`voHByPPp%V5mLBz~EE?H3k+>pco1P zZ<`JTPbma}M?^wy8Zj^|fGUlHM-a%T=C6zx7>*rcU@-q?1Ud$gfx(>5n1Nx#VFm^Z zP}s2j0+I7wB#jvuRF5z)c<qK71G3Tl9w>c6Hz1?f3R-db5J@E{4Z(_6P{jgCL!b$! z5H(Qp)r3SP=w1bo^BCt`xW~X?Zf?xLkavWE!Q9!Hf#ETT5e-VIM;RC_K#{}t3q;O$ zNi$|(ID3?V!N~$@49IOD8?lFm{aetXs8b-R7OEo=Vm|}poW%REbN#1)Q!Qf*<Xr!$ z;55q^YY0xUQ^6^gG0rd(bc^g%PS8X#V-VzenW>zhVu>*VG|@C2EaS5QY7OWFHjw{< zvltkUgOf9300V=8G^j~f!3FB>UeJQL1hMf<`vEK)Rd9jMr2x&rKyHVr<N}@UarYRk zM5*Kgjmj@M4$VfDT%g`-_6Y`t$e&PCL8<iaeFlbbP%;9i(nwBih=riE{R1S0NZXQ7 zDNs5Qet?`KTs5ImphSzvLktF>I*u_-05qfK09Ar$DXe%1tD4dTK+Qa(lQ5U334mO_ z7sLmbh3Q}uqCwLZ;HnA2=Q#~Ks6Jf)6jswu!>XnX0no|l>Sth8Q>FkYq-KEl;Kp;7 z04St5&oVH$P2gc*5Pt|O`bz{r7jsvgg;htTVE){*3=B?Jq3#DI2vA}|O}-2aZWnkM z7!u(oO#)XwSK%g22J?S_Oj6N-1Tij?+!c6X?r8>BJqG7s?r8z@gU^A^H6maNg8|6T zjLP8qQx`xLBiaS3k70qY48D1_8#K5GZWkznZ(kJyja7jIUj=N!77!mC_z-?7Xn+XZ zE>IB!1^$N%u)tRp1O<M>MOfgg34$E*5yS@vzPcbN@RKevFt`=)GB8Ye1oNz+ASm!J zUxIno2+aR>iGjh%Oc#HUxozNOU;y0?1G#I$Ul5e#^e)3p3IOv1FEcPWwGuGNodI;s z1^C7a2SHFfpc8J2BbdJiWXe4PrZ5<M2Msz`fLj3!dJxBhwoZU@JL8<i@W8JCw*sDk zy7S<`uK>3K!mhyD$d%w$z@jS*41S(aWBMM0N?G`zX(Ch@l9~d+1-!u)&=Q_ra0$`? zm7fD~1n9VvXRs2a7hHne2H6IVnm%v|(sLD-5BtC+i0w6K3DO5HL0GOcKo7d<=Ykw` zv!UJ;T<%Ta0+oBV*BKbBo|!N(T!Gpg0Ge=L#I^JZ=okf428K1DZkf6%1H%gt160__ zfqFm8rVI=rAjWqS28OO13=HldjV{Kf3=9WuFfcfY=tFz}stPbOAn3qH&>V6E6R20s zVBG*Z8W5@jb{PrdJTCjEpvu&Qf#Dm-rh_I73^F$v7|getFfe3*7;{Y+7`j1>i6#sT zM{Y7O_?Vy>;&Rr6f#L5>1_qa#CJYS5w-^|lLZC{Ba+%c`V+Mv6sE!Gspqb9~0pzN$ z#taNuw-^}Ac}*A?CV&{AQzJKn7%z<>?OgM_pyt{w1_pPKMi)tt^KXNW)_@uZa=@Xd zpyYwA5{3tl(;g)Cgo9Gegn{7>R0pgD&p3;#;u)ykHDO?2`OFwN-y$6}oE&Zu4Wi;K zoIzBKg*k`{^Fh}gQ4A6fb`k*HgTlbT0CG1$ci1k4rYDd}a03)HtN>~~fMVQ$4`k$0 za7Cd2H5O6T2)}@pc1yukjXkIt4$g2(!Bx#E5FcFHEd!g7afbnVCkliw32M87OS@&@ zs%FVuSZTK$T-BJ|gOzqGz*Wss5FebSR)VV<vHJ`RZU^`n7@D5LO1sVAs-^uttc=<M z=C8WXz~HnM>V8l$0rD(HRl~r~z;Fg`(nWC9@(6CyB`}}$0Rw{*iy<V>;4;ZQ0Cb5D zxU@S2u3Bs#z}#~f%m-ax=M+o86b6F_pwjLMIOrOniV;=KgqN_ue*zBt8K4#nIPjl> z178``4gd%KQ?LmKL40sk1K}4vg4Od+!GZr5RAYcICVB=A{NBf~z<&-7{C^-mIPhP9 z13&)>1B2TFeg=mBFJOWH2^`p<%Zeafyw6}h^HT-}Cov=ZLFV>=pMk;iCCnrNaMfb{ z6c%iPV169Pq*MYXxf_5kLjngrBe-gr1~-KX%-;zzWgh`k7z}oSZlF(>0+qcFpo$Sy z4c{wR;HQJDnvYLmg?l=<s!0Y_2H?QY09Q5ZpD{4_X&OVq;O9$FSpu(W9HGLH)C8$& zLT;HcFeE@F?trQ{a6h;1H3Ne=vpEC9i)Wxx-<*MA&2t6@3u#c_IKtw)8K|=fBIjGY z2D3pV3Ohm_q%<VL*qniZ;}=L;`GJ~q1JokWxtt)oF27=6aB(pQoxaV$;1XcYz!3F< zfx+Rf0RuxfRA~dKDxAw>{?34bq4ou6!rXv?VL6DwX2`&B9(?zS^8u)0*y#t_uNfFz zYRnlJ#9uNnxOA8^Fj&2WntK+ibOXrT-4JtQUqa1o2QfhAt_NQd;=^D92}syVcb6n{ z28OpU85kn+K;8=iyObBI=mE&YH4qa8UO`Q?0x>`)CcOfAm?315IRk?ORL5Pg5NOQ) z-5Ul5^V8-G4C_GZubVS46uo9(uy_ozCBkCAIVikA<a~?GU^a+EVMn|NDGiDE4GwRR zG$_0ipccWx+xZOxgA0!Z1H)fX07+OdFeto%hD$b7sQ~D}vzriKfRvm6GGJiH1vwAo zi>cu29h?_H6~n@1HppBT3kHV!AVz=%0|V1rsJUyQN*zGveutQ=`W6~G{vZa(+;Z@N z`aTb!ib03>g2Ki{%YuPn`&$Nv2s6-DF-Um7g(@llnfL-?;w_LtAeXYdV_+}`nWzIf z89Xcw<VjOVG(xx7F)+@t04dA2U|^^MG1@H{7+BvkFqluaU|?7bV$8Q-V9@)(z+h2r z0SbB$Ip1Zq1p~v^4-5=0+btLv(msMpB|!#;*>4#bTuxXpFkJu0z~FMlf`Nhe69a>p zkRby@3)Ef?kY7OgN|e`J*^q(3;1dIbIp{R)U=YLFkb$8Zd?>8j1wjUe*C6v4EEyPf zd;&$5B?H4dn0W`G=D{bDj3MR;eukQ70%Cy7ivgb@>n<R~z~J)Cf`OsoGXsOmCkqCK zZJ(iT<1vGT3EXWe5VLNAi~zaq4~PLWO9gV4Xqc2G1A_rn7j%Uz1LK?~P;hBmGB6~4 z0rl)H85rJz82*+F4BcND7|f$B85qRAF)&z|TY^FoM9z0fw`5?r`;CFYrPz{z0dxSN zTYwM)!_#*R3@$B}3=I3fGcdSJuw-EP`W@=psZe|2t__5^R^kWL=hh$w$hFDfbNAdD zKv%fEXJByIW68j<;0Gv*EEyQC{eYTx6KWp(jQo6vc|SqMfP63WlYzk;WFF)=I`;#h zkX&TRz>x8ifx%^+B?H5}pHQ<@&Cx<K0b<rckP#rWo`4u2vv?o}x`kY`WMBw@>O$-# z&HTW?VE*5dfx+t+1A{rY6$8Ty5JTLGfdO=+o5g2v0D{Q*DD3B8aS%D*0z|U?0+I70 zltGJ8LL&4*RG@PK)ILpUT9gANWP2+HhV0)A3@+YQ3=Dms<IJ2FK-GaSx?o^na096; zw_;$}2vXN<#lUa{q|WC8R2}T#5tmpi28Qpy85klmK^BHGFoayNVqjpffCSEV2n8C^ z2BrCjRtyYMe;63de_AmxRDu|+)(i~3e;F9e1+5tvuKs0Uuy|_)N-!XDJ~G=y)|!Dq z?jHj~gr+t47|(haQ)>o>kbeve&UsMVK<8P2{OJVBl5W-v3|;>i7+iv^85j=yV_<NY zXvM%#4^;}j`27^G`A#bahR6RH7|cN}6UqOenL;ZD2LJz{sUl$p2A88&3=FN16SJJJ zL-iJb>|kJESP8PG&YFQ?<$neSmo94thWr1a*1Uo$bpS0ucmc77iGdMfjRlCY$clj> zkAacF9i#+o%@WAzRL<^}kU)c3!v`u5Qmh#mPB1VsL==Fw<41t~5(QP*0J7vJ#FBp? zvq63_VPs@52U(KE$jIOhQUbPQ0py4$TWhETK@D-xq;ALzP$)qSM8w)vP-(Eznt|a6 z$hd>n3=GOlj11;ytr-};gBUlh85ruB85u0LT7zN@M9xQMyF3NCo0*Xz;v+29{#r9I z{9|ThaMrVe_!Aaupd;8__-q&$Kxgr|NZBwjWU(+pV$B|^^Z>}GoP6L|>t%rk=V1^7 z<kO!lj12A|C19WGLk?$go(|Ou3(j~@ndoN2z!1#J$lwxW!@$tV3bkexR4D`KNE>yC zHEUR*);t0+K-NgGF*3M=lz^@AgPi~1%x(?w4a}M&AZv7N7#ND!7#SigK$i(YVoeIF z&;ewL1jLdRY*0(?ffyi5MA$(|Lj;-vy&xwy*#3q_5IoirY#10CpayP)CQHUSdq72T zrws!`9y=q0`BWPQ1|AMZ2J?kB3=9z*j0_gFHlWA?k@Hd5g<x?IIo|?Avi$;)^CQ-R zTptp#6GR0%FM!$yD=G@VFfh2Bv|(WA=U`-TxoX3}unBaWg!2WcI@q?fnILtHwhRnc zLF#yI85n+m)cG*jKtc{QF97m_%X1qB25C-4hKSD~3qeJSv@HXJ15_C#yg{v}Gv62( z%q?sg80<J18O&X585mB27y-5n482^84CXPm3=9(7j0_e!wjlq4$oa_Zhz!t9;*f|E zTTo*aB(3~FEdq3N62~tv?}eIMg9rn|{;v!SE)#7T82q>y8C>StGB6Z!Gcq`Ut`4k% zI%)$bGVA!wLDvRO;bvqo2VEVw55xdnANT^q09_%-$HNG@MsNyLAAHuspVJ&P<KfT4 z$N--40BxBE&v-0=Y&Lg$Ai}`l0jfJs+A=U4<^hGYEd#?d9;ibOLk$!Ft+|>5aR?_b z)FH+o2FM|?AO^@G&Ad>D+=l9dAE*y?$N`W_(9{cP7c$r(ypY|G?hc|13@+<!85j)s z7#SjVgMu&=92O$Bka#QrIiVNggkV0X6KX*WkQ0`J7$7H{<%2pw2dWR<34;94uy6t~ zK(jxo{16*L9@{c71VFVxvIZzU+yT|%40a3*9Uu)nb_@){0*nmi5_Sv>*Fg*wI|ha% zK}H6PFW~e5BIhHsBMj^q7#Kq$Z0tbk0VEAd51`v0IDUb7FVx%?h%zvo2enfo?HCwl z2r@Fbq}nks>=uMZLpRh>7eHZPEdWjr_XMF~z#+uQU=9icBM<`=1`$HgFjxxJhn^m0 zgH(c|;WUT=3WIl$J!x(XVhjuspa#xFI|c?pVNk-fV_+~5hC1X5)IbB!S$B~Thk&-3 zffHt<Fw`NdK@5;XE(${(@&>98Ju(DDppjt%Vt^bH4`~d#Cx|gHxRl#5Ff@rUGDI|k zf)J7(RP7+KH38%VFNhPCf^>uO#(59}6c#^043HD#MWIfxh3Z3hLaHd#2{S+pkP~)8 zch`ihwqsyOfNI+V--mSnCj*1U9<YPVLAPw&1L=EU$H2fP&d6Z#1|$(_al#H%Hh{?a z$n1z8AnA|@7JCK;#y}SVdj^Jm;*1P#2gDc{sz7Z)MSBJYT?s}87hQV>hB6St(w>2V zSCWyzT|u0I!Ntv<fgvBfdfu%;oPl8-NKdal1H&>&Mh2Ie_6!VnK#ZmK3=9cUj12A< zKzcUWGcarfuU2<gkYHeNiL_^6cp(K!WA+RThSH1-E`|0C3@4;P6*@@1&Ypom5VBM> z<hVTpgMuR@#Dk%`x*6xhfSOh}>=_t5WEdIDKiD%cD9bW3nE$b7U`Ul?WH9G&U|`UZ zXJoK=Vh;)i5IG;2?IPm9z>p%($l#*jz`*bf#L#hIV0f#*$lzk(z`y`ntQO(o0Bw*3 zI505WQe<RsyCA{95D98HCOR-M2q`f#xa2x8Ff0HuDjgUYN|YHHtU#?XVJC=#1wiXI znS?Kc8or<w*=uEJeQ2cuZN*$wVPtRzDFau!so+Hh?hcX+3@&XB3=EyBjF1IWm!Ss2 zFItfhMl1<>p$an0fPsNqjS;dmOizuG!3SLr+VUW$Cr~p%%Y#7Udf@p;mWdzgAlHby zHApfrg#BS)a5?M1z>uNF$l!9*fq~%^i1E~cfgx3$k--||*%W7pXJPdwW0>%T-wX`k z#>{bbsD~LfKyI{RV6fI;Wbi@P0``16ct+O!0?6~992gi@Yd}4(?E<j??)e-f&p!ei zY{0<4stNVHh9)C}54s+7&)Y!Fgn1q`8Xbb<c>yT~2Is#F3@$Sr7#NZ?85vxbIxsM- z2QfA}Fff>EL1TI;)SLh*1_nkQ;k}@?KPaY~w4fee4q|{>KJ40z4DKLhV9!Q@=kMGb zq!<`nKo`8!X+u4m4;qSOU|@iI)(Oe8TeU%<Xu!a51H=F={`#%W$l!yn2i>y`P&09O z);0y2zd)$~G?X34GVuW@6@)N4GB7l_K|-Gwk}Mb)=NtyL#f3n3q3AF&m}@yQFf7$& zWH2{#WME*@XJjyUa%5mQsn5t@A?FCnd?0c@GTX%uv}4|Yk-;U(k%6JvfRQ0I&5?n@ zrO1(i;gbO)Lqwe;G~;zSGB7+eWMpvLAjQCN3DoJC?a06&X~f9jvfPn@VK0cW*^z<a zj4?Didfg$8WdJSxm@n)D>H&eG!`TED9UulMI-E>FtzBtoLv$W^D8k(U#6RH3z;MVE z8Xb`y5Ch@Su@xygjLo3Y5e8y_qNCD`k--OD4|;TDLCu6l2RN`n%Ra%IYe4gm(hLmy zL0vC)CkBRBW{eCj!cGhfW#)_wF7i$c46zna&%S_~170ouS=bWP%L94#j|J4TrIsM~ zSurrET7h~HAkTsNeU0Gmfcpm!U&o1oVW}0=vl~3oJj*G9C}qT~L53MHFj#^ZAkW5F zGcx#~>p}PIL8zIyJZm7szyLb(*yXY#1H%q$Mh2JrjtmU?HjE4|uN@f}#B8CSb?}0? ztpKzn=cKS2C=G%<yTBIeSr$8x`>Yrk=G!qcxPz2|Gum%(_36GqhJnH5ha&@nygk&j z{N4});W2w3DP||wgA6lZVAu>|fINH6o{_-^T@Siv6`*F~@T~1AXkG$kG|)6kAZU3x zLx{B#1H%L#h;uaHI|)uPGBQ~BfKChzHBWJ3V32oUWH2uP-Sgwf$Y4<el8Cg3Z~|p9 z5IG;2?b6}I!0^(Mk-=q(69a>*6C;Dm0w)HBg-)PKOoo9Wm64IbWup@VgMc$5gUeng z28Kuw<D?S<!wF|lRWHlH;Bw80fk6;lzPT01GBC^p>5+A2V5o9oWN^`RW?(oBVwgHJ zFc`TqGPrL5>2Y#qV3-CjZrneB_%EFp7|yyfGPrzoVqg$+V`Ok)bY@_f<;KY1ZXn0N z;KJw3z@P!iv?2Rl85j=uLxN!|gkoTv<HgL#VE)3Dfx*X}k-_|nD+9wr5QD*ufkD!P zk->t;4Yb0U!Qzf9C?r7Sd=&NtusDdE?;_#Gz!2!c$Pfy;7RSZFje%i~2P1=vjT-~Q zOAx~YbX$%mBZEtb8v{cdh>_sNz;GGF$Z=y};Pzr<aH()(V5kByT0n}u85vwAxG^wX z0x{;eF)&E`Ffv4}aDx<JR-mQ43j!d1DF9t2_*|TmnGv!`8@!x%lMghZy#O&l2~Exy zlzindmh(EX1VYS#C$1UwY<7$xEE8{l%4&w_Qm8#F6MxjPX)!Z`&h=!O*Z@|<RL{T= z(*alTp^nXtF%+!RjxmsBVn;n214GDBHwK0Vs3oT$b}=x{IR+Yrc<08z5aG+nVE)UE zf#EHP!RF4upz6oSU?JoV34hQ%K5V~0<a`Sd31eS#1H}u7obMv%&cG1s$H)+><<7w1 zV&=}kFx8Kd!Ntj)f#DH|;pfi4py$uX5E11L3c!x~h%_+cL!C>JI|D<8KO=)nojU`= zbr7S=oq?e#fRQ0|8pyCk?hFi{0vH)0)`4|ws0W9ARS?7<3qWCSB+<%(9QLJw(6C<( zVt~T_VIU)eJ4h{h#(n}d2P5pk^&~^IEmR&H-pg2^Vc!7_`+A0$0Jwr5(6E01q61kb zP5^~{*iCl^hJav*V_{>EjB`LEKjyF885pF47#Yl&JQx_#gBTe+zJYEo3aR#BU}%79 z0Ua0s8e?Fb;|3be>G5D-SOC(p#Djt1e-I;s#S9NnEP%-QwyU6;AnU+G3}qM?!Zvy^ zFkFC&!<H#C&RGdE?2rcoLqaekgZV8F28K1kj0_&<JfLI6KcPCH?Q#Z2IZ5!0z*CSa z&{#2e%$X|$<gc*j9t;cuU?u}Y7|5}pDTFzCppl^;9t;e+A&d;>0-g*E^FtUJELc22 z?gf$atwD1Su279|oAf26KsrJ54-BD<kogBy5Cb$a?HkI-z+h_!)r0KMU{3~y0;o8A zA;z5bY>W&RDWH9QK^6rdD%he1L<O3;8!|9VgsO*cRCbm0h%jVe@JIm>1t6jZw20py zv=;-a7Vb_KPLCV|1_qA=$m+U~PEQ7g4Nyg(Q+h!G!8oTKG@vyL<Tj8OmU%KT>;W-0 zc``6?gfT+46Fh+Gx**5Ez!)M~%Ff8(0oqPr4%$lK7{<t84%$kP0%Cx+610agGJv-d zyoTx%039BOyp>=-NGWJ5!6Og@l>S-6LA?=qjIzg@6BM8f3=E+3586rqa|>i|MIZx1 z*llkH1_h|92#}}1vC9D(pL^rYz@QP%$YB1%n}MMO#9;AZU^o!Y$Y6ESkbxlps>=c7 zzN3=gKtXW}w0$C+k-_7WA?PYhLk0%92u23;XNC+6juD`^g=s=KK+uPQp#f?XY$A_w zP9JDoP|1gZficKJ4@3o9Sb?ZOGte;CQmA_P@C3V*2WY6v1JsrG0CnR%KwWq<(1KnJ zwf>wQprI`f&@hGvsN2rKz!2gN@)^|BNO&+j2L(ff4+BFQ$QLO-3=Eq<i~=79hJO)^ z41OP=I&N}+N{$d_(D}>WoME7B!@vMqP|Xms*@uBa0V;wBUw6>J;1M4N2J=Wp2J;I( z3=GX6#vLC9hVzk(41NJn9Y&l8gPk&<g2-#MLSBM`4=RB$e>uqfUp@>B98ru6=4`$U z3?U$fkS_znf+$7?zYS0wlaS3n0To1^m{+c4Wnf@@p#)l-$H>gZ$e_9&bWGX{rC`u; zhu|6i7fPUonQA%t<qV9klt81V8hIiN42-YAY<0EV%oGO3Z(t7Opxy6EF3O-F4p|$? z!0-TS=~a*jIPI+ljg_8?WMH@%#mHcBEfO@~Vg4wRfx$YOk-=hLB&dJ^k@H>NMKUmy zL^CqD{EB2?SQX94-~jSBcQ_>c3qa|zQ`!6oCnLjyXwX;<CnJMc45*ftXJCl9z{$wK z7~*n=laav(B)CAHfx+b^CnLku7*Ml^laWC@7Gh9{VH70UgPe$10Jk1Ais~1|z)&B{ z$Y357#lWxw#7K)`U<iw2WH2v^Vqo|Z$H-ve6a{iJh@9_I7sbG!8_&q#(iO$P&<SEp zi(+8-2x2UX0-Z4qstKb&m1sk~%dRK}hUNrDhQMP{3=9mB5ZAbUkcSNTx}1+<U^tV& z$lx>wl(iTb7*yLp3oW4MoIqAigHEJ|EY~)BVamX;0jd&yL1mb_`Bzg02Bt(%mjF%D zh0%<GK^3xyImA4Ufgu6vggj6{faA)Bhmpa;B@X025IG;29T5-*D)5{Op}Jt(f)#ie z89YF$!GsUGbSOw01B1^7s5UvMWiGLC3=AG%*Tf3PGca6$$|D@p0CJ2%JjgL1a=rzK zWcvjo=erw#PSXT2JwOBlgTYSFdhIpf<KG;hOT3`h0x>YodBMxb069Qz4fp`L>_kQe zw*&<S2G9|nrj?*U=JjB$oJoufkXz0+fcf1?j0|4+Q1d3vVq*Bk!w727aUz`|)drPb zHj9Zth!-pkT5svM1S+nE5cApq6$1sd4^$kq%sqsum?IqI6YxITlTcNlL*g?asz4DR z0y-Dy4pdHT7864QL=I$|_gkp=fmuupPCC$aRv>FYog54+7$U`?N<f}E4>1SHQ?9m9 zX^<1_7(-#!GlYQdSaE<#szBqCagGxoDB;9|ww*-;fGM8{s3O>8xP=RL@lcR@P?^0w zfq|g_s!0uQ@<Gsm{)q$zhAT;o3@%ro4ChLyLYV1GVaj-u85w-gWkW&w7#LiqL3M%7 z+OcB{MI2Dazz|ZM#K5osDz5~$UIR4N-;>0^V35qnU_K*>fuROu!5XMS5vW3QkdhNH zC5$PI46gg2O0I&Ax?=>L`5XfB<&q=@h6_+B=p-ov<D6$8YqlmaFj%E9GMFDsVqmzG z!pIPMHi>~D^d^XSn#921zCeKiA{m~_$Y2gq-kS<)DJy_R2a^~WuBC!T2a^~Wc+wad zTtHfurZF-^g9=HIJDF-3Ht~bfCwQB+w`>fg1bQ`#i6L4W%3-QyxCT>G&){tY6$YsR zx&BiU1A_px9G$@eG88m3vI{f?!JN#%a665W!JI#tfnk0+BSWZkG6O@XI*2d^5%wU$ z8$^VIh-46v4<f2TM0+v=gZl-L2SNJFG8h@mK_>pr0F7RQJP0x^H<J-!q6^5xZJCS= zE+A9aXE8FkfK2`YVt`DaoXyDK0<xhghmj!^WQz;Pmd`ni3@#v>QgRs?q7$JWVJc%d zC4dN*45%<@$9QxJl*3fU@D8d5beeEAR2Zbj?>>~nF9<ONq~IA;46Hyu4ieIa5CzQT z3~UVEW>7(}idHBm7+FORR1mD<5|mSltl}nA5UfHX9%9Qhhzih|dwxa<5V2KIG0={9 zOQ;xV|2im6l~NcOHh>Fa1_s#fZN@naLW~Td_9+Yu7RD){A{a!@cRB!72Rm|-^N$@P zs90h!_y^i^I@Jqwj`jtpBFNMtD1j^#W@NAgU*iN(A(+R=V0jL-)L@zysD)9G$H-vn z2Rc1`I+*_`kCDM9oq=%%n5Uc1$e?i+bhOM&u=%FPK(i*Zyg*kE^yD)#n7V)lVP=EX zuFq#=umbr=5_){70B9fNVz29<2`-Qyp5=qu)`|=aE+Eed6)-Y*%R!a?pT)%BeFMq? zrD+BRE6}YrCQu;<kWp*A3_-J6n@kxPS_>E%%$J)oFdP9fK)2ewC}3o8a)&Axnau>M zDj)}RfeO$-&Uywzm3jsx6BPyqpG>GSe@HH3F!ckS^SlA<L*qh522*CxhM$dKeqA9W zg9GR!@JUcj4IroN_A&>Z0{*&?5poK+Y7rxYIp`E{&mu+!@A*)rpi{s>XY{QG9TxG@ z3w%B%RO$mHae!_E7GY$t1Rq5G(hGDZUrrGtgC+PV^jBV>T{ybMj0~3GGxJ_~ftHlM zD+Y}QD>5+ji7-NM_IT?BI!Lp!gb{LuP(upDaCNBn7@53XKyz`RfV)}(4Y;2m1}NZU zOBop$ymgWxdO$Ztd09g_pqru!M8G+VlR4o%^tLGP7^wN6bZWa1%7N5%3=B>up#so! z3Tny266z1olJH~TaJT?f)dsPafpJczC?kU<_|UUsVDC7W!n}7J%s)}e$N)Z)og)>h z2Xsd0H80TYA~-Ts%Ak=^3}S#HV^0~#YagIifxY%0$^m)JTNJ}<Ct+p_F*4XbhH^k& z1MTEAxCUC1`3HPGS3(-Z5%6B_oP%)p|M37theSCegQXd$X#~*`SkB1cnhMpn5*(_a znkkfl!Qd|_T)u<NEr2ROm@6p;3zzR;bGtz1@_|P8Avz9&%q@j#Lp67W1Oo$OJNUwY z2B?B`Selq4E6xbLVxS#-9Q+@Ux!?;@+c`i5Lv#frgC}T=k+B180%I8X!huexK@OnM z@8Xyt#>ilvn#I6yv4WAo`x;add^kL~ESL%vDF7+&;dl#D4k`-_DnY#mC1_cYQ_0BS zJr}AJR2Dcrg>pb;K{!YQ=+<Kf1_tnv_@Js4<PUg}*$26j;5NvZZBU)y)8-irG(kn1 z6?cFc1H%WX3}~k*NIT=4<?t}D;tn%oU=XQdgkD2n%?-+XWgvbnsP|*T4LUS_O%)@9 zPeM8*>H|P#Ffc%mc=rG+_*%ut;FJYb0E#(K@0?2k)K)RL07?r#z<Yo;Kvi6a8o@Zn zP68GiKe#|Hk*H>5Fv%}qVEn;VAi}_~xEkiLpWtiQ^lBIxAQ!Ry;sTv+pHjof;G>ZN zaacUmR8R2P^Nide1&jesUQlU-vxGo}Qxhoh*=mA{mI+W5Ht>mdDJht<Y&Aj7npMNd zV0jr7mv)+<j;v`dBZDQl?`o$Bx-MoFhz}b4X0+1;Z5cGGV`Q+L3c5wfPV)p41H&W` ze>Z4zf*sgKsd`2R!yllf!1iGMOcNOl*MpXV*@KlcP2@1#1yXGfR?Rd~zz{Sc$7rv4 z&>l4ADq*M(>O(s~WfdS-NH}VO;;y%zk-_vZD+2?glP0KJeXX96!80CoW2>{~V;xAp z(DVz)Y8SA)Spy@3si`dk1EZ_v-6#fz86dtq=$Ilm&1p&u3|ByWKhSnkcTHCX1_rK1 zMh0JXWd;VuQq3cokdU|H%VuC$2MxsqpkVUWJSxe^U@o7{zyNZPJ4nz)JDY()w~>*- z#XOsVp}rBM*Le-p2-vMKtWt~&rr=^f1MIsKjf@OV+o6iSwj*~>L1mNERj3pwNrMV3 z(B54GcF-Yvchx{~dm#&C4FkhV2*toSr%f6bw|CV*ar>_k7PoiRKyf>>2^P2a)If2| z*9@wNKnF+MQ`@h>z|aWdgTwzG*an#vSQ@>r28w_sEwBK8pau_cZ~#A40|oF~ka}<c zKT-n)ux2Z$B?1cI$7-Mej&Fqpu#ozLY>2bL0sIE)l?$K%ey*k_4GmzBh3+6hZ~!;8 zLIe0pD@d>N5vUO|(A-fd4GUmabx;7wwlOj|U4|;c+y8g^096GGYtXc%K{%+DRHFeh z>Oc<2Mg|6Ucs??bg@tvE1}Ln9+hAc`s{sn@cOX7EAJuAr!aBJf7S^>IpnP-~#9sxv zH3=fG+yTp1bsC_2wYr0m!4RCU>cB1l<s)!TsndYu6b?vEfyoL$a!S1hD58uzVL7Ei z1C�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 HV{Z%qGi|~Q literal 0 HcmV?d00001 diff --git a/lib/regex/_regex_core.py b/lib/regex/_regex_core.py new file mode 100644 index 000000000..25771b6fe --- /dev/null +++ b/lib/regex/_regex_core.py @@ -0,0 +1,4317 @@ +# +# Secret Labs' Regular Expression Engine core module +# +# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. +# +# This version of the SRE library can be redistributed under CNRI's +# Python 1.6 license. For any other use, please contact Secret Labs +# AB (info@pythonware.com). +# +# Portions of this engine have been developed in cooperation with +# CNRI. Hewlett-Packard provided funding for 1.6 integration and +# other compatibility work. +# +# 2010-01-16 mrab Python front-end re-written and extended + +import string +import sys +import unicodedata +from collections import defaultdict + +import _regex + +__all__ = ["A", "ASCII", "B", "BESTMATCH", "D", "DEBUG", "E", "ENHANCEMATCH", + "F", "FULLCASE", "I", "IGNORECASE", "L", "LOCALE", "M", "MULTILINE", "P", + "POSIX", "R", "REVERSE", "S", "DOTALL", "T", "TEMPLATE", "U", "UNICODE", + "V0", "VERSION0", "V1", "VERSION1", "W", "WORD", "X", "VERBOSE", "error", + "Scanner"] + +# The regex exception. +class error(Exception): + def __init__(self, message, pattern=None, pos=None): + newline = u'\n' if isinstance(pattern, unicode) else '\n' + self.msg = message + self.pattern = pattern + self.pos = pos + if pattern is not None and pos is not None: + self.lineno = pattern.count(newline, 0, pos) + 1 + self.colno = pos - pattern.rfind(newline, 0, pos) + + message = "%s at position %d" % (message, pos) + + if newline in pattern: + message += " (line %d, column %d)" % (self.lineno, self.colno) + + Exception.__init__(self, message) + +# The exception for when a positional flag has been turned on in the old +# behaviour. +class _UnscopedFlagSet(Exception): + pass + +# The exception for when parsing fails and we want to try something else. +class ParseError(Exception): + pass + +# The exception for when there isn't a valid first set. +class _FirstSetError(Exception): + pass + +# Flags. +A = ASCII = 0x80 # Assume ASCII locale. +B = BESTMATCH = 0x1000 # Best fuzzy match. +D = DEBUG = 0x200 # Print parsed pattern. +E = ENHANCEMATCH = 0x8000 # Attempt to improve the fit after finding the first + # fuzzy match. +F = FULLCASE = 0x4000 # Unicode full case-folding. +I = IGNORECASE = 0x2 # Ignore case. +L = LOCALE = 0x4 # Assume current 8-bit locale. +M = MULTILINE = 0x8 # Make anchors look for newline. +P = POSIX = 0x10000 # POSIX-style matching (leftmost longest). +R = REVERSE = 0x400 # Search backwards. +S = DOTALL = 0x10 # Make dot match newline. +U = UNICODE = 0x20 # Assume Unicode locale. +V0 = VERSION0 = 0x2000 # Old legacy behaviour. +V1 = VERSION1 = 0x100 # New enhanced behaviour. +W = WORD = 0x800 # Default Unicode word breaks. +X = VERBOSE = 0x40 # Ignore whitespace and comments. +T = TEMPLATE = 0x1 # Template (present because re module has it). + +DEFAULT_VERSION = VERSION1 + +_ALL_VERSIONS = VERSION0 | VERSION1 +_ALL_ENCODINGS = ASCII | LOCALE | UNICODE + +# The default flags for the various versions. +DEFAULT_FLAGS = {VERSION0: 0, VERSION1: FULLCASE} + +# The mask for the flags. +GLOBAL_FLAGS = (_ALL_ENCODINGS | _ALL_VERSIONS | BESTMATCH | DEBUG | + ENHANCEMATCH | POSIX | REVERSE) +SCOPED_FLAGS = FULLCASE | IGNORECASE | MULTILINE | DOTALL | WORD | VERBOSE + +ALPHA = frozenset(string.ascii_letters) +DIGITS = frozenset(string.digits) +ALNUM = ALPHA | DIGITS +OCT_DIGITS = frozenset(string.octdigits) +HEX_DIGITS = frozenset(string.hexdigits) +SPECIAL_CHARS = frozenset("()|?*+{^$.[\\#") | frozenset([""]) +NAMED_CHAR_PART = ALNUM | frozenset(" -") +PROPERTY_NAME_PART = ALNUM | frozenset(" &_-.") +SET_OPS = ("||", "~~", "&&", "--") + +# The width of the code words inside the regex engine. +BYTES_PER_CODE = _regex.get_code_size() +BITS_PER_CODE = BYTES_PER_CODE * 8 + +# The repeat count which represents infinity. +UNLIMITED = (1 << BITS_PER_CODE) - 1 + +# The regular expression flags. +REGEX_FLAGS = {"a": ASCII, "b": BESTMATCH, "e": ENHANCEMATCH, "f": FULLCASE, + "i": IGNORECASE, "L": LOCALE, "m": MULTILINE, "p": POSIX, "r": REVERSE, + "s": DOTALL, "u": UNICODE, "V0": VERSION0, "V1": VERSION1, "w": WORD, "x": + VERBOSE} + +# The case flags. +CASE_FLAGS = FULLCASE | IGNORECASE +NOCASE = 0 +FULLIGNORECASE = FULLCASE | IGNORECASE + +FULL_CASE_FOLDING = UNICODE | FULLIGNORECASE + +CASE_FLAGS_COMBINATIONS = {0: 0, FULLCASE: 0, IGNORECASE: IGNORECASE, + FULLIGNORECASE: FULLIGNORECASE} + +# The number of digits in hexadecimal escapes. +HEX_ESCAPES = {"x": 2, "u": 4, "U": 8} + +# A singleton which indicates a comment within a pattern. +COMMENT = object() +FLAGS = object() + +# The names of the opcodes. +OPCODES = """ +FAILURE +SUCCESS +ANY +ANY_ALL +ANY_ALL_REV +ANY_REV +ANY_U +ANY_U_REV +ATOMIC +BOUNDARY +BRANCH +CALL_REF +CHARACTER +CHARACTER_IGN +CHARACTER_IGN_REV +CHARACTER_REV +CONDITIONAL +DEFAULT_BOUNDARY +DEFAULT_END_OF_WORD +DEFAULT_START_OF_WORD +END +END_OF_LINE +END_OF_LINE_U +END_OF_STRING +END_OF_STRING_LINE +END_OF_STRING_LINE_U +END_OF_WORD +FUZZY +GRAPHEME_BOUNDARY +GREEDY_REPEAT +GROUP +GROUP_CALL +GROUP_EXISTS +KEEP +LAZY_REPEAT +LOOKAROUND +NEXT +PROPERTY +PROPERTY_IGN +PROPERTY_IGN_REV +PROPERTY_REV +PRUNE +RANGE +RANGE_IGN +RANGE_IGN_REV +RANGE_REV +REF_GROUP +REF_GROUP_FLD +REF_GROUP_FLD_REV +REF_GROUP_IGN +REF_GROUP_IGN_REV +REF_GROUP_REV +SEARCH_ANCHOR +SET_DIFF +SET_DIFF_IGN +SET_DIFF_IGN_REV +SET_DIFF_REV +SET_INTER +SET_INTER_IGN +SET_INTER_IGN_REV +SET_INTER_REV +SET_SYM_DIFF +SET_SYM_DIFF_IGN +SET_SYM_DIFF_IGN_REV +SET_SYM_DIFF_REV +SET_UNION +SET_UNION_IGN +SET_UNION_IGN_REV +SET_UNION_REV +SKIP +START_OF_LINE +START_OF_LINE_U +START_OF_STRING +START_OF_WORD +STRING +STRING_FLD +STRING_FLD_REV +STRING_IGN +STRING_IGN_REV +STRING_REV +STRING_SET +STRING_SET_FLD +STRING_SET_FLD_REV +STRING_SET_IGN +STRING_SET_IGN_REV +STRING_SET_REV +""" + +# Define the opcodes in a namespace. +class Namespace(object): + pass + +OP = Namespace() +for i, op in enumerate(OPCODES.split()): + setattr(OP, op, i) + +def _shrink_cache(cache_dict, args_dict, locale_sensitive, max_length, divisor=5): + """Make room in the given cache. + + Args: + cache_dict: The cache dictionary to modify. + args_dict: The dictionary of named list args used by patterns. + max_length: Maximum # of entries in cache_dict before it is shrunk. + divisor: Cache will shrink to max_length - 1/divisor*max_length items. + """ + # Toss out a fraction of the entries at random to make room for new ones. + # A random algorithm was chosen as opposed to simply cache_dict.popitem() + # as popitem could penalize the same regular expression repeatedly based + # on its internal hash value. Being random should spread the cache miss + # love around. + cache_keys = tuple(cache_dict.keys()) + overage = len(cache_keys) - max_length + if overage < 0: + # Cache is already within limits. Normally this should not happen + # but it could due to multithreading. + return + + number_to_toss = max_length // divisor + overage + + # The import is done here to avoid a circular dependency. + import random + if not hasattr(random, 'sample'): + # Do nothing while resolving the circular dependency: + # re->random->warnings->tokenize->string->re + return + + for doomed_key in random.sample(cache_keys, number_to_toss): + try: + del cache_dict[doomed_key] + except KeyError: + # Ignore problems if the cache changed from another thread. + pass + + # Rebuild the arguments and locale-sensitivity dictionaries. + args_dict.clear() + sensitivity_dict = {} + for pattern, pattern_type, flags, args, default_version, locale in tuple(cache_dict): + args_dict[pattern, pattern_type, flags, default_version, locale] = args + try: + sensitivity_dict[pattern_type, pattern] = locale_sensitive[pattern_type, pattern] + except KeyError: + pass + + locale_sensitive.clear() + locale_sensitive.update(sensitivity_dict) + +def _fold_case(info, string): + "Folds the case of a string." + flags = info.flags + if (flags & _ALL_ENCODINGS) == 0: + flags |= info.guess_encoding + + return _regex.fold_case(flags, string) + +def is_cased(info, char): + "Checks whether a character is cased." + return len(_regex.get_all_cases(info.flags, char)) > 1 + +def _compile_firstset(info, fs): + "Compiles the firstset for the pattern." + if not fs or None in fs: + return [] + + # If we ignore the case, for simplicity we won't build a firstset. + members = set() + for i in fs: + if isinstance(i, Character) and not i.positive: + return [] + + if i.case_flags: + if isinstance(i, Character): + if is_cased(info, i.value): + return [] + elif isinstance(i, SetBase): + return [] + + members.add(i.with_flags(case_flags=NOCASE)) + + # Build the firstset. + fs = SetUnion(info, list(members), zerowidth=True) + fs = fs.optimise(info, in_set=True) + + # Compile the firstset. + return fs.compile(bool(info.flags & REVERSE)) + +def _flatten_code(code): + "Flattens the code from a list of tuples." + flat_code = [] + for c in code: + flat_code.extend(c) + + return flat_code + +def make_character(info, value, in_set=False): + "Makes a character literal." + if in_set: + # A character set is built case-sensitively. + return Character(value) + + return Character(value, case_flags=info.flags & CASE_FLAGS) + +def make_ref_group(info, name, position): + "Makes a group reference." + return RefGroup(info, name, position, case_flags=info.flags & CASE_FLAGS) + +def make_string_set(info, name): + "Makes a string set." + return StringSet(info, name, case_flags=info.flags & CASE_FLAGS) + +def make_property(info, prop, in_set): + "Makes a property." + if in_set: + return prop + + return prop.with_flags(case_flags=info.flags & CASE_FLAGS) + +def _parse_pattern(source, info): + "Parses a pattern, eg. 'a|b|c'." + branches = [parse_sequence(source, info)] + while source.match("|"): + branches.append(parse_sequence(source, info)) + + if len(branches) == 1: + return branches[0] + return Branch(branches) + +def parse_sequence(source, info): + "Parses a sequence, eg. 'abc'." + sequence = [] + applied = False + while True: + # Get literal characters followed by an element. + characters, case_flags, element = parse_literal_and_element(source, + info) + if not element: + # No element, just a literal. We've also reached the end of the + # sequence. + append_literal(characters, case_flags, sequence) + break + + if element is COMMENT or element is FLAGS: + append_literal(characters, case_flags, sequence) + elif type(element) is tuple: + # It looks like we've found a quantifier. + ch, saved_pos = element + + counts = parse_quantifier(source, info, ch) + if counts: + # It _is_ a quantifier. + apply_quantifier(source, info, counts, characters, case_flags, + ch, saved_pos, applied, sequence) + applied = True + else: + # It's not a quantifier. Maybe it's a fuzzy constraint. + constraints = parse_fuzzy(source, ch) + if constraints: + # It _is_ a fuzzy constraint. + apply_constraint(source, info, constraints, characters, + case_flags, saved_pos, applied, sequence) + applied = True + else: + # The element was just a literal. + characters.append(ord(ch)) + append_literal(characters, case_flags, sequence) + applied = False + else: + # We have a literal followed by something else. + append_literal(characters, case_flags, sequence) + sequence.append(element) + applied = False + + return make_sequence(sequence) + +def apply_quantifier(source, info, counts, characters, case_flags, ch, + saved_pos, applied, sequence): + if characters: + # The quantifier applies to the last character. + append_literal(characters[ : -1], case_flags, sequence) + element = Character(characters[-1], case_flags=case_flags) + else: + # The quantifier applies to the last item in the sequence. + if applied: + raise error("multiple repeat", source.string, saved_pos) + + if not sequence: + raise error("nothing to repeat", source.string, saved_pos) + + element = sequence.pop() + + min_count, max_count = counts + saved_pos = source.pos + ch = source.get() + if ch == "?": + # The "?" suffix that means it's a lazy repeat. + repeated = LazyRepeat + elif ch == "+": + # The "+" suffix that means it's a possessive repeat. + repeated = PossessiveRepeat + else: + # No suffix means that it's a greedy repeat. + source.pos = saved_pos + repeated = GreedyRepeat + + # Ignore the quantifier if it applies to a zero-width item or the number of + # repeats is fixed at 1. + if not element.is_empty() and (min_count != 1 or max_count != 1): + element = repeated(element, min_count, max_count) + + sequence.append(element) + +def apply_constraint(source, info, constraints, characters, case_flags, + saved_pos, applied, sequence): + if characters: + # The constraint applies to the last character. + append_literal(characters[ : -1], case_flags, sequence) + element = Character(characters[-1], case_flags=case_flags) + sequence.append(Fuzzy(element, constraints)) + else: + # The constraint applies to the last item in the sequence. + if applied or not sequence: + raise error("nothing for fuzzy constraint", source.string, + saved_pos) + + element = sequence.pop() + + # If a group is marked as fuzzy then put all of the fuzzy part in the + # group. + if isinstance(element, Group): + element.subpattern = Fuzzy(element.subpattern, constraints) + sequence.append(element) + else: + sequence.append(Fuzzy(element, constraints)) + +def append_literal(characters, case_flags, sequence): + if characters: + sequence.append(Literal(characters, case_flags=case_flags)) + +def PossessiveRepeat(element, min_count, max_count): + "Builds a possessive repeat." + return Atomic(GreedyRepeat(element, min_count, max_count)) + +_QUANTIFIERS = {"?": (0, 1), "*": (0, None), "+": (1, None)} + +def parse_quantifier(source, info, ch): + "Parses a quantifier." + q = _QUANTIFIERS.get(ch) + if q: + # It's a quantifier. + return q + + if ch == "{": + # Looks like a limited repeated element, eg. 'a{2,3}'. + counts = parse_limited_quantifier(source) + if counts: + return counts + + return None + +def is_above_limit(count): + "Checks whether a count is above the maximum." + return count is not None and count >= UNLIMITED + +def parse_limited_quantifier(source): + "Parses a limited quantifier." + saved_pos = source.pos + min_count = parse_count(source) + if source.match(","): + max_count = parse_count(source) + + # No minimum means 0 and no maximum means unlimited. + min_count = int(min_count or 0) + max_count = int(max_count) if max_count else None + + if max_count is not None and min_count > max_count: + raise error("min repeat greater than max repeat", source.string, + saved_pos) + else: + if not min_count: + source.pos = saved_pos + return None + + min_count = max_count = int(min_count) + + if is_above_limit(min_count) or is_above_limit(max_count): + raise error("repeat count too big", source.string, saved_pos) + + if not source.match ("}"): + source.pos = saved_pos + return None + + return min_count, max_count + +def parse_fuzzy(source, ch): + "Parses a fuzzy setting, if present." + if ch != "{": + return None + + saved_pos = source.pos + + constraints = {} + try: + parse_fuzzy_item(source, constraints) + while source.match(","): + parse_fuzzy_item(source, constraints) + except ParseError: + source.pos = saved_pos + return None + + if not source.match("}"): + raise error("expected }", source.string, source.pos) + + return constraints + +def parse_fuzzy_item(source, constraints): + "Parses a fuzzy setting item." + saved_pos = source.pos + try: + parse_cost_constraint(source, constraints) + except ParseError: + source.pos = saved_pos + + parse_cost_equation(source, constraints) + +def parse_cost_constraint(source, constraints): + "Parses a cost constraint." + saved_pos = source.pos + ch = source.get() + if ch in ALPHA: + # Syntax: constraint [("<=" | "<") cost] + constraint = parse_constraint(source, constraints, ch) + + max_inc = parse_fuzzy_compare(source) + + if max_inc is None: + # No maximum cost. + constraints[constraint] = 0, None + else: + # There's a maximum cost. + cost_pos = source.pos + max_cost = int(parse_count(source)) + + # Inclusive or exclusive limit? + if not max_inc: + max_cost -= 1 + + if max_cost < 0: + raise error("bad fuzzy cost limit", source.string, cost_pos) + + constraints[constraint] = 0, max_cost + elif ch in DIGITS: + # Syntax: cost ("<=" | "<") constraint ("<=" | "<") cost + source.pos = saved_pos + try: + # Minimum cost. + min_cost = int(parse_count(source)) + + min_inc = parse_fuzzy_compare(source) + if min_inc is None: + raise ParseError() + + constraint = parse_constraint(source, constraints, source.get()) + + max_inc = parse_fuzzy_compare(source) + if max_inc is None: + raise ParseError() + + # Maximum cost. + cost_pos = source.pos + max_cost = int(parse_count(source)) + + # Inclusive or exclusive limits? + if not min_inc: + min_cost += 1 + if not max_inc: + max_cost -= 1 + + if not 0 <= min_cost <= max_cost: + raise error("bad fuzzy cost limit", source.string, cost_pos) + + constraints[constraint] = min_cost, max_cost + except ValueError: + raise ParseError() + else: + raise ParseError() + +def parse_constraint(source, constraints, ch): + "Parses a constraint." + if ch not in "deis": + raise error("bad fuzzy constraint", source.string, source.pos) + + if ch in constraints: + raise error("repeated fuzzy constraint", source.string, source.pos) + + return ch + +def parse_fuzzy_compare(source): + "Parses a cost comparator." + if source.match("<="): + return True + elif source.match("<"): + return False + else: + return None + +def parse_cost_equation(source, constraints): + "Parses a cost equation." + if "cost" in constraints: + raise error("more than one cost equation", source.string, source.pos) + + cost = {} + + parse_cost_term(source, cost) + while source.match("+"): + parse_cost_term(source, cost) + + max_inc = parse_fuzzy_compare(source) + if max_inc is None: + raise error("missing fuzzy cost limit", source.string, source.pos) + + max_cost = int(parse_count(source)) + + if not max_inc: + max_cost -= 1 + + if max_cost < 0: + raise error("bad fuzzy cost limit", source.string, source.pos) + + cost["max"] = max_cost + + constraints["cost"] = cost + +def parse_cost_term(source, cost): + "Parses a cost equation term." + coeff = parse_count(source) + ch = source.get() + if ch not in "dis": + raise ParseError() + + if ch in cost: + raise error("repeated fuzzy cost", source.string, source.pos) + + cost[ch] = int(coeff or 1) + +def parse_count(source): + "Parses a quantifier's count, which can be empty." + return source.get_while(DIGITS) + +def parse_literal_and_element(source, info): + """Parses a literal followed by an element. The element is FLAGS if it's an + inline flag or None if it has reached the end of a sequence. + """ + characters = [] + case_flags = info.flags & CASE_FLAGS + while True: + saved_pos = source.pos + ch = source.get() + if ch in SPECIAL_CHARS: + if ch in ")|": + # The end of a sequence. At the end of the pattern ch is "". + source.pos = saved_pos + return characters, case_flags, None + elif ch == "\\": + # An escape sequence outside a set. + element = parse_escape(source, info, False) + return characters, case_flags, element + elif ch == "(": + # A parenthesised subpattern or a flag. + element = parse_paren(source, info) + if element and element is not COMMENT: + return characters, case_flags, element + elif ch == ".": + # Any character. + if info.flags & DOTALL: + element = AnyAll() + elif info.flags & WORD: + element = AnyU() + else: + element = Any() + + return characters, case_flags, element + elif ch == "[": + # A character set. + element = parse_set(source, info) + return characters, case_flags, element + elif ch == "^": + # The start of a line or the string. + if info.flags & MULTILINE: + if info.flags & WORD: + element = StartOfLineU() + else: + element = StartOfLine() + else: + element = StartOfString() + + return characters, case_flags, element + elif ch == "$": + # The end of a line or the string. + if info.flags & MULTILINE: + if info.flags & WORD: + element = EndOfLineU() + else: + element = EndOfLine() + else: + if info.flags & WORD: + element = EndOfStringLineU() + else: + element = EndOfStringLine() + + return characters, case_flags, element + elif ch in "?*+{": + # Looks like a quantifier. + return characters, case_flags, (ch, saved_pos) + else: + # A literal. + characters.append(ord(ch)) + else: + # A literal. + characters.append(ord(ch)) + +def parse_paren(source, info): + """Parses a parenthesised subpattern or a flag. Returns FLAGS if it's an + inline flag. + """ + saved_pos = source.pos + ch = source.get() + if ch == "?": + # (?... + saved_pos_2 = source.pos + ch = source.get() + if ch == "<": + # (?<... + saved_pos_3 = source.pos + ch = source.get() + if ch in ("=", "!"): + # (?<=... or (?<!...: lookbehind. + return parse_lookaround(source, info, True, ch == "=") + + # (?<...: a named capture group. + source.pos = saved_pos_3 + name = parse_name(source) + group = info.open_group(name) + source.expect(">") + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + info.close_group() + return Group(info, group, subpattern) + if ch in ("=", "!"): + # (?=... or (?!...: lookahead. + return parse_lookaround(source, info, False, ch == "=") + if ch == "P": + # (?P...: a Python extension. + return parse_extension(source, info) + if ch == "#": + # (?#...: a comment. + return parse_comment(source) + if ch == "(": + # (?(...: a conditional subpattern. + return parse_conditional(source, info) + if ch == ">": + # (?>...: an atomic subpattern. + return parse_atomic(source, info) + if ch == "|": + # (?|...: a common/reset groups branch. + return parse_common(source, info) + if ch == "R" or "0" <= ch <= "9": + # (?R...: probably a call to a group. + return parse_call_group(source, info, ch, saved_pos_2) + if ch == "&": + # (?&...: a call to a named group. + return parse_call_named_group(source, info, saved_pos_2) + + # (?...: probably a flags subpattern. + source.pos = saved_pos_2 + return parse_flags_subpattern(source, info) + + if ch == "*": + # (*... + saved_pos_2 = source.pos + word = source.get_while(set(")>"), include=False) + if word[ : 1].isalpha(): + verb = VERBS.get(word) + if not verb: + raise error("unknown verb", source.string, saved_pos_2) + + source.expect(")") + + return verb + + # (...: an unnamed capture group. + source.pos = saved_pos + group = info.open_group() + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + info.close_group() + + return Group(info, group, subpattern) + +def parse_extension(source, info): + "Parses a Python extension." + saved_pos = source.pos + ch = source.get() + if ch == "<": + # (?P<...: a named capture group. + name = parse_name(source) + group = info.open_group(name) + source.expect(">") + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + info.close_group() + + return Group(info, group, subpattern) + if ch == "=": + # (?P=...: a named group reference. + name = parse_name(source, allow_numeric=True) + source.expect(")") + if info.is_open_group(name): + raise error("cannot refer to an open group", source.string, + saved_pos) + + return make_ref_group(info, name, saved_pos) + if ch == ">" or ch == "&": + # (?P>...: a call to a group. + return parse_call_named_group(source, info, saved_pos) + + source.pos = saved_pos + raise error("unknown extension", source.string, saved_pos) + +def parse_comment(source): + "Parses a comment." + source.skip_while(set(")"), include=False) + source.expect(")") + + return COMMENT + +def parse_lookaround(source, info, behind, positive): + "Parses a lookaround." + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + return LookAround(behind, positive, subpattern) + +def parse_conditional(source, info): + "Parses a conditional subpattern." + saved_flags = info.flags + saved_pos = source.pos + ch = source.get() + if ch == "?": + # (?(?... + ch = source.get() + if ch in ("=", "!"): + # (?(?=... or (?(?!...: lookahead conditional. + return parse_lookaround_conditional(source, info, False, ch == "=") + if ch == "<": + # (?(?<... + ch = source.get() + if ch in ("=", "!"): + # (?(?<=... or (?(?<!...: lookbehind conditional. + return parse_lookaround_conditional(source, info, True, ch == + "=") + + source.pos = saved_pos + raise error("expected lookaround conditional", source.string, + source.pos) + + source.pos = saved_pos + try: + group = parse_name(source, True) + source.expect(")") + yes_branch = parse_sequence(source, info) + if source.match("|"): + no_branch = parse_sequence(source, info) + else: + no_branch = Sequence() + + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + if yes_branch.is_empty() and no_branch.is_empty(): + return Sequence() + + return Conditional(info, group, yes_branch, no_branch, saved_pos) + +def parse_lookaround_conditional(source, info, behind, positive): + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + yes_branch = parse_sequence(source, info) + if source.match("|"): + no_branch = parse_sequence(source, info) + else: + no_branch = Sequence() + + source.expect(")") + + return LookAroundConditional(behind, positive, subpattern, yes_branch, + no_branch) + +def parse_atomic(source, info): + "Parses an atomic subpattern." + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + return Atomic(subpattern) + +def parse_common(source, info): + "Parses a common groups branch." + # Capture group numbers in different branches can reuse the group numbers. + initial_group_count = info.group_count + branches = [parse_sequence(source, info)] + final_group_count = info.group_count + while source.match("|"): + info.group_count = initial_group_count + branches.append(parse_sequence(source, info)) + final_group_count = max(final_group_count, info.group_count) + + info.group_count = final_group_count + source.expect(")") + + if len(branches) == 1: + return branches[0] + return Branch(branches) + +def parse_call_group(source, info, ch, pos): + "Parses a call to a group." + if ch == "R": + group = "0" + else: + group = ch + source.get_while(DIGITS) + + source.expect(")") + + return CallGroup(info, group, pos) + +def parse_call_named_group(source, info, pos): + "Parses a call to a named group." + group = parse_name(source) + source.expect(")") + + return CallGroup(info, group, pos) + +def parse_flag_set(source): + "Parses a set of inline flags." + flags = 0 + + try: + while True: + saved_pos = source.pos + ch = source.get() + if ch == "V": + ch += source.get() + flags |= REGEX_FLAGS[ch] + except KeyError: + source.pos = saved_pos + + return flags + +def parse_flags(source, info): + "Parses flags being turned on/off." + flags_on = parse_flag_set(source) + if source.match("-"): + flags_off = parse_flag_set(source) + if not flags_off: + raise error("bad inline flags: no flags after '-'", source.string, + source.pos) + else: + flags_off = 0 + + if flags_on & LOCALE: + # Remember that this pattern as an inline locale flag. + info.inline_locale = True + + return flags_on, flags_off + +def parse_subpattern(source, info, flags_on, flags_off): + "Parses a subpattern with scoped flags." + saved_flags = info.flags + info.flags = (info.flags | flags_on) & ~flags_off + source.ignore_space = bool(info.flags & VERBOSE) + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + return subpattern + +def parse_flags_subpattern(source, info): + """Parses a flags subpattern. It could be inline flags or a subpattern + possibly with local flags. If it's a subpattern, then that's returned; + if it's a inline flags, then FLAGS is returned. + """ + flags_on, flags_off = parse_flags(source, info) + + if flags_off & GLOBAL_FLAGS: + raise error("bad inline flags: cannot turn off global flag", + source.string, source.pos) + + if flags_on & flags_off: + raise error("bad inline flags: flag turned on and off", source.string, + source.pos) + + # Handle flags which are global in all regex behaviours. + new_global_flags = (flags_on & ~info.global_flags) & GLOBAL_FLAGS + if new_global_flags: + info.global_flags |= new_global_flags + + # A global has been turned on, so reparse the pattern. + raise _UnscopedFlagSet(info.global_flags) + + # Ensure that from now on we have only scoped flags. + flags_on &= ~GLOBAL_FLAGS + + if source.match(":"): + return parse_subpattern(source, info, flags_on, flags_off) + + if source.match(")"): + parse_positional_flags(source, info, flags_on, flags_off) + return FLAGS + + raise error("unknown extension", source.string, source.pos) + +def parse_positional_flags(source, info, flags_on, flags_off): + "Parses positional flags." + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + if version == VERSION0: + # Positional flags are global and can only be turned on. + if flags_off: + raise error("bad inline flags: cannot turn flags off", + source.string, source.pos) + + new_global_flags = flags_on & ~info.global_flags + if new_global_flags: + info.global_flags |= new_global_flags + + # A global has been turned on, so reparse the pattern. + raise _UnscopedFlagSet(info.global_flags) + else: + info.flags = (info.flags | flags_on) & ~flags_off + + source.ignore_space = bool(info.flags & VERBOSE) + +def parse_name(source, allow_numeric=False, allow_group_0=False): + "Parses a name." + name = source.get_while(set(")>"), include=False) + + if not name: + raise error("missing group name", source.string, source.pos) + + if name.isdigit(): + min_group = 0 if allow_group_0 else 1 + if not allow_numeric or int(name) < min_group: + raise error("bad character in group name", source.string, + source.pos) + else: + if not is_identifier(name): + raise error("bad character in group name", source.string, + source.pos) + + return name + +def is_identifier(name): + if not name: + return False + + if name[0] not in ALPHA and name[0] != "_": + return False + + name = name.replace("_", "") + + return not name or all(c in ALNUM for c in name) + +def is_octal(string): + "Checks whether a string is octal." + return all(ch in OCT_DIGITS for ch in string) + +def is_decimal(string): + "Checks whether a string is decimal." + return all(ch in DIGITS for ch in string) + +def is_hexadecimal(string): + "Checks whether a string is hexadecimal." + return all(ch in HEX_DIGITS for ch in string) + +def parse_escape(source, info, in_set): + "Parses an escape sequence." + saved_ignore = source.ignore_space + source.ignore_space = False + ch = source.get() + source.ignore_space = saved_ignore + if not ch: + # A backslash at the end of the pattern. + raise error("bad escape (end of pattern)", source.string, source.pos) + if ch in HEX_ESCAPES: + # A hexadecimal escape sequence. + return parse_hex_escape(source, info, HEX_ESCAPES[ch], in_set, ch) + elif ch == "g" and not in_set: + # A group reference. + saved_pos = source.pos + try: + return parse_group_ref(source, info) + except error: + # Invalid as a group reference, so assume it's a literal. + source.pos = saved_pos + + return make_character(info, ord(ch), in_set) + elif ch == "G" and not in_set: + # A search anchor. + return SearchAnchor() + elif ch == "L" and not in_set: + # A string set. + return parse_string_set(source, info) + elif ch == "N": + # A named codepoint. + return parse_named_char(source, info, in_set) + elif ch in "pP": + # A Unicode property, positive or negative. + return parse_property(source, info, ch == "p", in_set) + elif ch == "X" and not in_set: + # A grapheme cluster. + return Grapheme() + elif ch in ALPHA: + # An alphabetic escape sequence. + # Positional escapes aren't allowed inside a character set. + if not in_set: + if info.flags & WORD: + value = WORD_POSITION_ESCAPES.get(ch) + else: + value = POSITION_ESCAPES.get(ch) + + if value: + return value + + value = CHARSET_ESCAPES.get(ch) + if value: + return value + + value = CHARACTER_ESCAPES.get(ch) + if value: + return Character(ord(value)) + + return make_character(info, ord(ch), in_set) + elif ch in DIGITS: + # A numeric escape sequence. + return parse_numeric_escape(source, info, ch, in_set) + else: + # A literal. + return make_character(info, ord(ch), in_set) + +def parse_numeric_escape(source, info, ch, in_set): + "Parses a numeric escape sequence." + if in_set or ch == "0": + # Octal escape sequence, max 3 digits. + return parse_octal_escape(source, info, [ch], in_set) + + # At least 1 digit, so either octal escape or group. + digits = ch + saved_pos = source.pos + ch = source.get() + if ch in DIGITS: + # At least 2 digits, so either octal escape or group. + digits += ch + saved_pos = source.pos + ch = source.get() + if is_octal(digits) and ch in OCT_DIGITS: + # 3 octal digits, so octal escape sequence. + encoding = info.flags & _ALL_ENCODINGS + if encoding == ASCII or encoding == LOCALE: + octal_mask = 0xFF + else: + octal_mask = 0x1FF + + value = int(digits + ch, 8) & octal_mask + return make_character(info, value) + + # Group reference. + source.pos = saved_pos + if info.is_open_group(digits): + raise error("cannot refer to an open group", source.string, source.pos) + + return make_ref_group(info, digits, source.pos) + +def parse_octal_escape(source, info, digits, in_set): + "Parses an octal escape sequence." + saved_pos = source.pos + ch = source.get() + while len(digits) < 3 and ch in OCT_DIGITS: + digits.append(ch) + saved_pos = source.pos + ch = source.get() + + source.pos = saved_pos + try: + value = int("".join(digits), 8) + return make_character(info, value, in_set) + except ValueError: + if digits[0] in OCT_DIGITS: + raise error("incomplete escape \\%s" % ''.join(digits), + source.string, source.pos) + else: + raise error("bad escape \\%s" % digits[0], source.string, + source.pos) + +def parse_hex_escape(source, info, expected_len, in_set, type): + "Parses a hex escape sequence." + digits = [] + for i in range(expected_len): + ch = source.get() + if ch not in HEX_DIGITS: + raise error("incomplete escape \\%s%s" % (type, ''.join(digits)), + source.string, source.pos) + digits.append(ch) + + value = int("".join(digits), 16) + return make_character(info, value, in_set) + +def parse_group_ref(source, info): + "Parses a group reference." + source.expect("<") + saved_pos = source.pos + name = parse_name(source, True) + source.expect(">") + if info.is_open_group(name): + raise error("cannot refer to an open group", source.string, source.pos) + + return make_ref_group(info, name, saved_pos) + +def parse_string_set(source, info): + "Parses a string set reference." + source.expect("<") + name = parse_name(source, True) + source.expect(">") + if name is None or name not in info.kwargs: + raise error("undefined named list", source.string, source.pos) + + return make_string_set(info, name) + +def parse_named_char(source, info, in_set): + "Parses a named character." + saved_pos = source.pos + if source.match("{"): + name = source.get_while(NAMED_CHAR_PART) + if source.match("}"): + try: + value = unicodedata.lookup(name) + return make_character(info, ord(value), in_set) + except KeyError: + raise error("undefined character name", source.string, + source.pos) + + source.pos = saved_pos + return make_character(info, ord("N"), in_set) + +def parse_property(source, info, positive, in_set): + "Parses a Unicode property." + saved_pos = source.pos + ch = source.get() + if ch == "{": + negate = source.match("^") + prop_name, name = parse_property_name(source) + if source.match("}"): + # It's correctly delimited. + prop = lookup_property(prop_name, name, positive != negate, source) + return make_property(info, prop, in_set) + elif ch and ch in "CLMNPSZ": + # An abbreviated property, eg \pL. + prop = lookup_property(None, ch, positive, source) + return make_property(info, prop, in_set) + + # Not a property, so treat as a literal "p" or "P". + source.pos = saved_pos + ch = "p" if positive else "P" + return make_character(info, ord(ch), in_set) + +def parse_property_name(source): + "Parses a property name, which may be qualified." + name = source.get_while(PROPERTY_NAME_PART) + saved_pos = source.pos + + ch = source.get() + if ch and ch in ":=": + prop_name = name + name = source.get_while(ALNUM | set(" &_-./")).strip() + + if name: + # Name after the ":" or "=", so it's a qualified name. + saved_pos = source.pos + else: + # No name after the ":" or "=", so assume it's an unqualified name. + prop_name, name = None, prop_name + else: + prop_name = None + + source.pos = saved_pos + return prop_name, name + +def parse_set(source, info): + "Parses a character set." + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + + saved_ignore = source.ignore_space + source.ignore_space = False + # Negative set? + negate = source.match("^") + try: + if version == VERSION0: + item = parse_set_imp_union(source, info) + else: + item = parse_set_union(source, info) + + if not source.match("]"): + raise error("missing ]", source.string, source.pos) + finally: + source.ignore_space = saved_ignore + + if negate: + item = item.with_flags(positive=not item.positive) + + item = item.with_flags(case_flags=info.flags & CASE_FLAGS) + + return item + +def parse_set_union(source, info): + "Parses a set union ([x||y])." + items = [parse_set_symm_diff(source, info)] + while source.match("||"): + items.append(parse_set_symm_diff(source, info)) + + if len(items) == 1: + return items[0] + return SetUnion(info, items) + +def parse_set_symm_diff(source, info): + "Parses a set symmetric difference ([x~~y])." + items = [parse_set_inter(source, info)] + while source.match("~~"): + items.append(parse_set_inter(source, info)) + + if len(items) == 1: + return items[0] + return SetSymDiff(info, items) + +def parse_set_inter(source, info): + "Parses a set intersection ([x&&y])." + items = [parse_set_diff(source, info)] + while source.match("&&"): + items.append(parse_set_diff(source, info)) + + if len(items) == 1: + return items[0] + return SetInter(info, items) + +def parse_set_diff(source, info): + "Parses a set difference ([x--y])." + items = [parse_set_imp_union(source, info)] + while source.match("--"): + items.append(parse_set_imp_union(source, info)) + + if len(items) == 1: + return items[0] + return SetDiff(info, items) + +def parse_set_imp_union(source, info): + "Parses a set implicit union ([xy])." + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + + items = [parse_set_member(source, info)] + while True: + saved_pos = source.pos + if source.match("]"): + # End of the set. + source.pos = saved_pos + break + + if version == VERSION1 and any(source.match(op) for op in SET_OPS): + # The new behaviour has set operators. + source.pos = saved_pos + break + + items.append(parse_set_member(source, info)) + + if len(items) == 1: + return items[0] + return SetUnion(info, items) + +def parse_set_member(source, info): + "Parses a member in a character set." + # Parse a set item. + start = parse_set_item(source, info) + saved_pos1 = source.pos + if (not isinstance(start, Character) or not start.positive or not + source.match("-")): + # It's not the start of a range. + return start + + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + + # It looks like the start of a range of characters. + saved_pos2 = source.pos + if version == VERSION1 and source.match("-"): + # It's actually the set difference operator '--', so return the + # character. + source.pos = saved_pos1 + return start + + if source.match("]"): + # We've reached the end of the set, so return both the character and + # hyphen. + source.pos = saved_pos2 + return SetUnion(info, [start, Character(ord("-"))]) + + # Parse a set item. + end = parse_set_item(source, info) + if not isinstance(end, Character) or not end.positive: + # It's not a range, so return the character, hyphen and property. + return SetUnion(info, [start, Character(ord("-")), end]) + + # It _is_ a range. + if start.value > end.value: + raise error("bad character range", source.string, source.pos) + + if start.value == end.value: + return start + + return Range(start.value, end.value) + +def parse_set_item(source, info): + "Parses an item in a character set." + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + + if source.match("\\"): + # An escape sequence in a set. + return parse_escape(source, info, True) + + saved_pos = source.pos + if source.match("[:"): + # Looks like a POSIX character class. + try: + return parse_posix_class(source, info) + except ParseError: + # Not a POSIX character class. + source.pos = saved_pos + + if version == VERSION1 and source.match("["): + # It's the start of a nested set. + + # Negative set? + negate = source.match("^") + item = parse_set_union(source, info) + + if not source.match("]"): + raise error("missing ]", source.string, source.pos) + + if negate: + item = item.with_flags(positive=not item.positive) + + return item + + ch = source.get() + if not ch: + raise error("unterminated character set", source.string, source.pos) + + return Character(ord(ch)) + +def parse_posix_class(source, info): + "Parses a POSIX character class." + negate = source.match("^") + prop_name, name = parse_property_name(source) + if not source.match(":]"): + raise ParseError() + + return lookup_property(prop_name, name, not negate, source, posix=True) + +def float_to_rational(flt): + "Converts a float to a rational pair." + int_part = int(flt) + error = flt - int_part + if abs(error) < 0.0001: + return int_part, 1 + + den, num = float_to_rational(1.0 / error) + + return int_part * den + num, den + +def numeric_to_rational(numeric): + "Converts a numeric string to a rational string, if possible." + if numeric[ : 1] == "-": + sign, numeric = numeric[0], numeric[1 : ] + else: + sign = "" + + parts = numeric.split("/") + if len(parts) == 2: + num, den = float_to_rational(float(parts[0]) / float(parts[1])) + elif len(parts) == 1: + num, den = float_to_rational(float(parts[0])) + else: + raise ValueError() + + result = "%s%s/%s" % (sign, num, den) + if result.endswith("/1"): + return result[ : -2] + + return result + +def standardise_name(name): + "Standardises a property or value name." + try: + return numeric_to_rational("".join(name)) + except (ValueError, ZeroDivisionError): + return "".join(ch for ch in name if ch not in "_- ").upper() + +_posix_classes = set('ALNUM DIGIT PUNCT XDIGIT'.split()) + +def lookup_property(property, value, positive, source=None, posix=False): + "Looks up a property." + # Normalise the names (which may still be lists). + property = standardise_name(property) if property else None + value = standardise_name(value) + + if (property, value) == ("GENERALCATEGORY", "ASSIGNED"): + property, value, positive = "GENERALCATEGORY", "UNASSIGNED", not positive + + if posix and not property and value.upper() in _posix_classes: + value = 'POSIX' + value + + if property: + # Both the property and the value are provided. + prop = PROPERTIES.get(property) + if not prop: + if not source: + raise error("unknown property") + + raise error("unknown property", source.string, source.pos) + + prop_id, value_dict = prop + val_id = value_dict.get(value) + if val_id is None: + if not source: + raise error("unknown property value") + + raise error("unknown property value", source.string, source.pos) + + if "YES" in value_dict and val_id == 0: + positive, val_id = not positive, 1 + + return Property((prop_id << 16) | val_id, positive) + + # Only the value is provided. + # It might be the name of a GC, script or block value. + for property in ("GC", "SCRIPT", "BLOCK"): + prop_id, value_dict = PROPERTIES.get(property) + val_id = value_dict.get(value) + if val_id is not None: + return Property((prop_id << 16) | val_id, positive) + + # It might be the name of a binary property. + prop = PROPERTIES.get(value) + if prop: + prop_id, value_dict = prop + + if "YES" in value_dict: + return Property((prop_id << 16) | 1, positive) + + # It might be the name of a binary property starting with a prefix. + if value.startswith("IS"): + prop = PROPERTIES.get(value[2 : ]) + if prop: + prop_id, value_dict = prop + if "YES" in value_dict: + return Property((prop_id << 16) | 1, positive) + + # It might be the name of a script or block starting with a prefix. + for prefix, property in (("IS", "SCRIPT"), ("IN", "BLOCK")): + if value.startswith(prefix): + prop_id, value_dict = PROPERTIES.get(property) + val_id = value_dict.get(value[2 : ]) + if val_id is not None: + return Property((prop_id << 16) | val_id, positive) + + # Unknown property. + if not source: + raise error("unknown property") + + raise error("unknown property", source.string, source.pos) + +def _compile_replacement(source, pattern, is_unicode): + "Compiles a replacement template escape sequence." + ch = source.get() + if ch in ALPHA: + # An alphabetic escape sequence. + value = CHARACTER_ESCAPES.get(ch) + if value: + return False, [ord(value)] + + if ch in HEX_ESCAPES and (ch == "x" or is_unicode): + # A hexadecimal escape sequence. + return False, [parse_repl_hex_escape(source, HEX_ESCAPES[ch], ch)] + + if ch == "g": + # A group preference. + return True, [compile_repl_group(source, pattern)] + + if ch == "N" and is_unicode: + # A named character. + value = parse_repl_named_char(source) + if value is not None: + return False, [value] + + return False, [ord("\\"), ord(ch)] + + if isinstance(source.sep, str): + octal_mask = 0xFF + else: + octal_mask = 0x1FF + + if ch == "0": + # An octal escape sequence. + digits = ch + while len(digits) < 3: + saved_pos = source.pos + ch = source.get() + if ch not in OCT_DIGITS: + source.pos = saved_pos + break + digits += ch + + return False, [int(digits, 8) & octal_mask] + + if ch in DIGITS: + # Either an octal escape sequence (3 digits) or a group reference (max + # 2 digits). + digits = ch + saved_pos = source.pos + ch = source.get() + if ch in DIGITS: + digits += ch + saved_pos = source.pos + ch = source.get() + if ch and is_octal(digits + ch): + # An octal escape sequence. + return False, [int(digits + ch, 8) & octal_mask] + + # A group reference. + source.pos = saved_pos + return True, [int(digits)] + + if ch == "\\": + # An escaped backslash is a backslash. + return False, [ord("\\")] + + if not ch: + # A trailing backslash. + raise error("bad escape (end of pattern)", source.string, source.pos) + + # An escaped non-backslash is a backslash followed by the literal. + return False, [ord("\\"), ord(ch)] + +def parse_repl_hex_escape(source, expected_len, type): + "Parses a hex escape sequence in a replacement string." + digits = [] + for i in range(expected_len): + ch = source.get() + if ch not in HEX_DIGITS: + raise error("incomplete escape \\%s%s" % (type, ''.join(digits)), + source.string, source.pos) + digits.append(ch) + + return int("".join(digits), 16) + +def parse_repl_named_char(source): + "Parses a named character in a replacement string." + saved_pos = source.pos + if source.match("{"): + name = source.get_while(ALPHA | set(" ")) + + if source.match("}"): + try: + value = unicodedata.lookup(name) + return ord(value) + except KeyError: + raise error("undefined character name", source.string, + source.pos) + + source.pos = saved_pos + return None + +def compile_repl_group(source, pattern): + "Compiles a replacement template group reference." + source.expect("<") + name = parse_name(source, True, True) + + source.expect(">") + if name.isdigit(): + index = int(name) + if not 0 <= index <= pattern.groups: + raise error("invalid group reference", source.string, source.pos) + + return index + + try: + return pattern.groupindex[name] + except KeyError: + raise IndexError("unknown group") + +# The regular expression is parsed into a syntax tree. The different types of +# node are defined below. + +INDENT = " " +POSITIVE_OP = 0x1 +ZEROWIDTH_OP = 0x2 +FUZZY_OP = 0x4 +REVERSE_OP = 0x8 +REQUIRED_OP = 0x10 + +POS_TEXT = {False: "NON-MATCH", True: "MATCH"} +CASE_TEXT = {NOCASE: "", IGNORECASE: " SIMPLE_IGNORE_CASE", FULLCASE: "", + FULLIGNORECASE: " FULL_IGNORE_CASE"} + +def make_sequence(items): + if len(items) == 1: + return items[0] + return Sequence(items) + +# Common base class for all nodes. +class RegexBase(object): + def __init__(self): + self._key = self.__class__ + + def with_flags(self, positive=None, case_flags=None, zerowidth=None): + if positive is None: + positive = self.positive + else: + positive = bool(positive) + if case_flags is None: + case_flags = self.case_flags + else: + case_flags = CASE_FLAGS_COMBINATIONS[case_flags & CASE_FLAGS] + if zerowidth is None: + zerowidth = self.zerowidth + else: + zerowidth = bool(zerowidth) + + if (positive == self.positive and case_flags == self.case_flags and + zerowidth == self.zerowidth): + return self + + return self.rebuild(positive, case_flags, zerowidth) + + def fix_groups(self, pattern, reverse, fuzzy): + pass + + def optimise(self, info): + return self + + def pack_characters(self, info): + return self + + def remove_captures(self): + return self + + def is_atomic(self): + return True + + def can_be_affix(self): + return True + + def contains_group(self): + return False + + def get_firstset(self, reverse): + raise _FirstSetError() + + def has_simple_start(self): + return False + + def compile(self, reverse=False, fuzzy=False): + return self._compile(reverse, fuzzy) + + def dump(self, indent, reverse): + self._dump(indent, reverse) + + def is_empty(self): + return False + + def __hash__(self): + return hash(self._key) + + def __eq__(self, other): + return type(self) is type(other) and self._key == other._key + + def __ne__(self, other): + return not self.__eq__(other) + + def get_required_string(self, reverse): + return self.max_width(), None + +# Base class for zero-width nodes. +class ZeroWidthBase(RegexBase): + def __init__(self, positive=True): + RegexBase.__init__(self) + self.positive = bool(positive) + + self._key = self.__class__, self.positive + + def get_firstset(self, reverse): + return set([None]) + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if fuzzy: + flags |= FUZZY_OP + if reverse: + flags |= REVERSE_OP + return [(self._opcode, flags)] + + def _dump(self, indent, reverse): + print "%s%s %s" % (INDENT * indent, self._op_name, + POS_TEXT[self.positive]) + + def max_width(self): + return 0 + +class Any(RegexBase): + _opcode = {False: OP.ANY, True: OP.ANY_REV} + _op_name = "ANY" + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if fuzzy: + flags |= FUZZY_OP + return [(self._opcode[reverse], flags)] + + def _dump(self, indent, reverse): + print "%s%s" % (INDENT * indent, self._op_name) + + def max_width(self): + return 1 + +class AnyAll(Any): + _opcode = {False: OP.ANY_ALL, True: OP.ANY_ALL_REV} + _op_name = "ANY_ALL" + +class AnyU(Any): + _opcode = {False: OP.ANY_U, True: OP.ANY_U_REV} + _op_name = "ANY_U" + +class Atomic(RegexBase): + def __init__(self, subpattern): + RegexBase.__init__(self) + self.subpattern = subpattern + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info): + self.subpattern = self.subpattern.optimise(info) + + if self.subpattern.is_empty(): + return self.subpattern + return self + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + self.subpattern = self.subpattern.remove_captures() + return self + + def can_be_affix(self): + return self.subpattern.can_be_affix() + + def contains_group(self): + return self.subpattern.contains_group() + + def get_firstset(self, reverse): + return self.subpattern.get_firstset(reverse) + + def has_simple_start(self): + return self.subpattern.has_simple_start() + + def _compile(self, reverse, fuzzy): + return ([(OP.ATOMIC, )] + self.subpattern.compile(reverse, fuzzy) + + [(OP.END, )]) + + def _dump(self, indent, reverse): + print "%sATOMIC" % (INDENT * indent) + self.subpattern.dump(indent + 1, reverse) + + def is_empty(self): + return self.subpattern.is_empty() + + def __eq__(self, other): + return (type(self) is type(other) and self.subpattern == + other.subpattern) + + def max_width(self): + return self.subpattern.max_width() + + def get_required_string(self, reverse): + return self.subpattern.get_required_string(reverse) + +class Boundary(ZeroWidthBase): + _opcode = OP.BOUNDARY + _op_name = "BOUNDARY" + +class Branch(RegexBase): + def __init__(self, branches): + RegexBase.__init__(self) + self.branches = branches + + def fix_groups(self, pattern, reverse, fuzzy): + for b in self.branches: + b.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info): + # Flatten branches within branches. + branches = Branch._flatten_branches(info, self.branches) + + # Move any common prefix or suffix out of the branches. + prefix, branches = Branch._split_common_prefix(info, branches) + + # Try to reduce adjacent single-character branches to sets. + branches = Branch._reduce_to_set(info, branches) + + if len(branches) > 1: + sequence = [Branch(branches)] + else: + sequence = branches + + return make_sequence(prefix + sequence) + + def pack_characters(self, info): + self.branches = [b.pack_characters(info) for b in self.branches] + return self + + def remove_captures(self): + self.branches = [b.remove_captures() for b in self.branches] + return self + + def is_atomic(self): + return all(b.is_atomic() for b in self.branches) + + def can_be_affix(self): + return all(b.can_be_affix() for b in self.branches) + + def contains_group(self): + return any(b.contains_group() for b in self.branches) + + def get_firstset(self, reverse): + fs = set() + for b in self.branches: + fs |= b.get_firstset(reverse) + + return fs or set([None]) + + def _compile(self, reverse, fuzzy): + code = [(OP.BRANCH, )] + for b in self.branches: + code.extend(b.compile(reverse, fuzzy)) + code.append((OP.NEXT, )) + + code[-1] = (OP.END, ) + + return code + + def _dump(self, indent, reverse): + print "%sBRANCH" % (INDENT * indent) + self.branches[0].dump(indent + 1, reverse) + for b in self.branches[1 : ]: + print "%sOR" % (INDENT * indent) + b.dump(indent + 1, reverse) + + @staticmethod + def _flatten_branches(info, branches): + # Flatten the branches so that there aren't branches of branches. + new_branches = [] + for b in branches: + b = b.optimise(info) + if isinstance(b, Branch): + new_branches.extend(b.branches) + else: + new_branches.append(b) + + return new_branches + + @staticmethod + def _split_common_prefix(info, branches): + # Common leading items can be moved out of the branches. + # Get the items in the branches. + alternatives = [] + for b in branches: + if isinstance(b, Sequence): + alternatives.append(b.items) + else: + alternatives.append([b]) + + # What is the maximum possible length of the prefix? + max_count = min(len(a) for a in alternatives) + + # What is the longest common prefix? + prefix = alternatives[0] + pos = 0 + end_pos = max_count + while pos < end_pos and prefix[pos].can_be_affix() and all(a[pos] == + prefix[pos] for a in alternatives): + pos += 1 + count = pos + + if info.flags & UNICODE: + # We need to check that we're not splitting a sequence of + # characters which could form part of full case-folding. + count = pos + while count > 0 and not all(Branch._can_split(a, count) for a in + alternatives): + count -= 1 + + # No common prefix is possible. + if count == 0: + return [], branches + + # Rebuild the branches. + new_branches = [] + for a in alternatives: + new_branches.append(make_sequence(a[count : ])) + + return prefix[ : count], new_branches + + @staticmethod + def _split_common_suffix(info, branches): + # Common trailing items can be moved out of the branches. + # Get the items in the branches. + alternatives = [] + for b in branches: + if isinstance(b, Sequence): + alternatives.append(b.items) + else: + alternatives.append([b]) + + # What is the maximum possible length of the suffix? + max_count = min(len(a) for a in alternatives) + + # What is the longest common suffix? + suffix = alternatives[0] + pos = -1 + end_pos = -1 - max_count + while pos > end_pos and suffix[pos].can_be_affix() and all(a[pos] == + suffix[pos] for a in alternatives): + pos -= 1 + count = -1 - pos + + if info.flags & UNICODE: + # We need to check that we're not splitting a sequence of + # characters which could form part of full case-folding. + while count > 0 and not all(Branch._can_split_rev(a, count) for a + in alternatives): + count -= 1 + + # No common suffix is possible. + if count == 0: + return [], branches + + # Rebuild the branches. + new_branches = [] + for a in alternatives: + new_branches.append(make_sequence(a[ : -count])) + + return suffix[-count : ], new_branches + + @staticmethod + def _can_split(items, count): + # Check the characters either side of the proposed split. + if not Branch._is_full_case(items, count - 1): + return True + + if not Branch._is_full_case(items, count): + return True + + # Check whether a 1-1 split would be OK. + if Branch._is_folded(items[count - 1 : count + 1]): + return False + + # Check whether a 1-2 split would be OK. + if (Branch._is_full_case(items, count + 2) and + Branch._is_folded(items[count - 1 : count + 2])): + return False + + # Check whether a 2-1 split would be OK. + if (Branch._is_full_case(items, count - 2) and + Branch._is_folded(items[count - 2 : count + 1])): + return False + + return True + + @staticmethod + def _can_split_rev(items, count): + end = len(items) + + # Check the characters either side of the proposed split. + if not Branch._is_full_case(items, end - count): + return True + + if not Branch._is_full_case(items, end - count - 1): + return True + + # Check whether a 1-1 split would be OK. + if Branch._is_folded(items[end - count - 1 : end - count + 1]): + return False + + # Check whether a 1-2 split would be OK. + if (Branch._is_full_case(items, end - count + 2) and + Branch._is_folded(items[end - count - 1 : end - count + 2])): + return False + + # Check whether a 2-1 split would be OK. + if (Branch._is_full_case(items, end - count - 2) and + Branch._is_folded(items[end - count - 2 : end - count + 1])): + return False + + return True + + @staticmethod + def _merge_common_prefixes(info, branches): + # Branches with the same case-sensitive character prefix can be grouped + # together if they are separated only by other branches with a + # character prefix. + prefixed = defaultdict(list) + order = {} + new_branches = [] + for b in branches: + if Branch._is_simple_character(b): + # Branch starts with a simple character. + prefixed[b.value].append([b]) + order.setdefault(b.value, len(order)) + elif (isinstance(b, Sequence) and b.items and + Branch._is_simple_character(b.items[0])): + # Branch starts with a simple character. + prefixed[b.items[0].value].append(b.items) + order.setdefault(b.items[0].value, len(order)) + else: + Branch._flush_char_prefix(info, prefixed, order, new_branches) + + new_branches.append(b) + + Branch._flush_char_prefix(info, prefixed, order, new_branches) + + return new_branches + + @staticmethod + def _is_simple_character(c): + return isinstance(c, Character) and c.positive and not c.case_flags + + @staticmethod + def _reduce_to_set(info, branches): + # Can the branches be reduced to a set? + new_branches = [] + items = set() + case_flags = NOCASE + for b in branches: + if isinstance(b, (Character, Property, SetBase)): + # Branch starts with a single character. + if b.case_flags != case_flags: + # Different case sensitivity, so flush. + Branch._flush_set_members(info, items, case_flags, + new_branches) + + case_flags = b.case_flags + + items.add(b.with_flags(case_flags=NOCASE)) + else: + Branch._flush_set_members(info, items, case_flags, + new_branches) + + new_branches.append(b) + + Branch._flush_set_members(info, items, case_flags, new_branches) + + return new_branches + + @staticmethod + def _flush_char_prefix(info, prefixed, order, new_branches): + # Flush the prefixed branches. + if not prefixed: + return + + for value, branches in sorted(prefixed.items(), key=lambda pair: + order[pair[0]]): + if len(branches) == 1: + new_branches.append(make_sequence(branches[0])) + else: + subbranches = [] + optional = False + for b in branches: + if len(b) > 1: + subbranches.append(make_sequence(b[1 : ])) + elif not optional: + subbranches.append(Sequence()) + optional = True + + sequence = Sequence([Character(value), Branch(subbranches)]) + new_branches.append(sequence.optimise(info)) + + prefixed.clear() + order.clear() + + @staticmethod + def _flush_set_members(info, items, case_flags, new_branches): + # Flush the set members. + if not items: + return + + if len(items) == 1: + item = list(items)[0] + else: + item = SetUnion(info, list(items)).optimise(info) + + new_branches.append(item.with_flags(case_flags=case_flags)) + + items.clear() + + @staticmethod + def _is_full_case(items, i): + if not 0 <= i < len(items): + return False + + item = items[i] + return (isinstance(item, Character) and item.positive and + (item.case_flags & FULLIGNORECASE) == FULLIGNORECASE) + + @staticmethod + def _is_folded(items): + if len(items) < 2: + return False + + for i in items: + if (not isinstance(i, Character) or not i.positive or not + i.case_flags): + return False + + folded = u"".join(unichr(i.value) for i in items) + folded = _regex.fold_case(FULL_CASE_FOLDING, folded) + + # Get the characters which expand to multiple codepoints on folding. + expanding_chars = _regex.get_expand_on_folding() + + for c in expanding_chars: + if folded == _regex.fold_case(FULL_CASE_FOLDING, c): + return True + + return False + + def is_empty(self): + return all(b.is_empty() for b in self.branches) + + def __eq__(self, other): + return type(self) is type(other) and self.branches == other.branches + + def max_width(self): + return max(b.max_width() for b in self.branches) + +class CallGroup(RegexBase): + def __init__(self, info, group, position): + RegexBase.__init__(self) + self.info = info + self.group = group + self.position = position + + self._key = self.__class__, self.group + + def fix_groups(self, pattern, reverse, fuzzy): + try: + self.group = int(self.group) + except ValueError: + try: + self.group = self.info.group_index[self.group] + except KeyError: + raise error("invalid group reference", pattern, self.position) + + if not 0 <= self.group <= self.info.group_count: + raise error("unknown group", pattern, self.position) + + if self.group > 0 and self.info.open_group_count[self.group] > 1: + raise error("ambiguous group reference", pattern, self.position) + + self.info.group_calls.append((self, reverse, fuzzy)) + + self._key = self.__class__, self.group + + def remove_captures(self): + raise error("group reference not allowed", pattern, self.position) + + def _compile(self, reverse, fuzzy): + return [(OP.GROUP_CALL, self.call_ref)] + + def _dump(self, indent, reverse): + print "%sGROUP_CALL %s" % (INDENT * indent, self.group) + + def __eq__(self, other): + return type(self) is type(other) and self.group == other.group + + def max_width(self): + return UNLIMITED + +class Character(RegexBase): + _opcode = {(NOCASE, False): OP.CHARACTER, (IGNORECASE, False): + OP.CHARACTER_IGN, (FULLCASE, False): OP.CHARACTER, (FULLIGNORECASE, + False): OP.CHARACTER_IGN, (NOCASE, True): OP.CHARACTER_REV, (IGNORECASE, + True): OP.CHARACTER_IGN_REV, (FULLCASE, True): OP.CHARACTER_REV, + (FULLIGNORECASE, True): OP.CHARACTER_IGN_REV} + + def __init__(self, value, positive=True, case_flags=NOCASE, + zerowidth=False): + RegexBase.__init__(self) + self.value = value + self.positive = bool(positive) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + self.zerowidth = bool(zerowidth) + + if (self.positive and (self.case_flags & FULLIGNORECASE) == + FULLIGNORECASE): + self.folded = _regex.fold_case(FULL_CASE_FOLDING, unichr(self.value)) + else: + self.folded = unichr(self.value) + + self._key = (self.__class__, self.value, self.positive, + self.case_flags, self.zerowidth) + + def rebuild(self, positive, case_flags, zerowidth): + return Character(self.value, positive, case_flags, zerowidth) + + def optimise(self, info, in_set=False): + return self + + def get_firstset(self, reverse): + return set([self]) + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + + code = PrecompiledCode([self._opcode[self.case_flags, reverse], flags, + self.value]) + + if len(self.folded) > 1: + # The character expands on full case-folding. + code = Branch([code, String([ord(c) for c in self.folded], + case_flags=self.case_flags)]) + + return code.compile(reverse, fuzzy) + + def _dump(self, indent, reverse): + display = repr(unichr(self.value)).lstrip("bu") + print "%sCHARACTER %s %s%s" % (INDENT * indent, + POS_TEXT[self.positive], display, CASE_TEXT[self.case_flags]) + + def matches(self, ch): + return (ch == self.value) == self.positive + + def max_width(self): + return len(self.folded) + + def get_required_string(self, reverse): + if not self.positive: + return 1, None + + self.folded_characters = tuple(ord(c) for c in self.folded) + + return 0, self + +class Conditional(RegexBase): + def __init__(self, info, group, yes_item, no_item, position): + RegexBase.__init__(self) + self.info = info + self.group = group + self.yes_item = yes_item + self.no_item = no_item + self.position = position + + def fix_groups(self, pattern, reverse, fuzzy): + try: + self.group = int(self.group) + except ValueError: + try: + self.group = self.info.group_index[self.group] + except KeyError: + if self.group == 'DEFINE': + # 'DEFINE' is a special name unless there's a group with + # that name. + self.group = 0 + else: + raise error("unknown group", pattern, self.position) + + if not 0 <= self.group <= self.info.group_count: + raise error("invalid group reference", pattern, self.position) + + self.yes_item.fix_groups(pattern, reverse, fuzzy) + self.no_item.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info): + yes_item = self.yes_item.optimise(info) + no_item = self.no_item.optimise(info) + + return Conditional(info, self.group, yes_item, no_item, self.position) + + def pack_characters(self, info): + self.yes_item = self.yes_item.pack_characters(info) + self.no_item = self.no_item.pack_characters(info) + return self + + def remove_captures(self): + self.yes_item = self.yes_item.remove_captures() + self.no_item = self.no_item.remove_captures() + + def is_atomic(self): + return self.yes_item.is_atomic() and self.no_item.is_atomic() + + def can_be_affix(self): + return self.yes_item.can_be_affix() and self.no_item.can_be_affix() + + def contains_group(self): + return self.yes_item.contains_group() or self.no_item.contains_group() + + def get_firstset(self, reverse): + return (self.yes_item.get_firstset(reverse) | + self.no_item.get_firstset(reverse)) + + def _compile(self, reverse, fuzzy): + code = [(OP.GROUP_EXISTS, self.group)] + code.extend(self.yes_item.compile(reverse, fuzzy)) + add_code = self.no_item.compile(reverse, fuzzy) + if add_code: + code.append((OP.NEXT, )) + code.extend(add_code) + + code.append((OP.END, )) + + return code + + def _dump(self, indent, reverse): + print "%sGROUP_EXISTS %s" % (INDENT * indent, self.group) + self.yes_item.dump(indent + 1, reverse) + if not self.no_item.is_empty(): + print "%sOR" % (INDENT * indent) + self.no_item.dump(indent + 1, reverse) + + def is_empty(self): + return self.yes_item.is_empty() and self.no_item.is_empty() + + def __eq__(self, other): + return type(self) is type(other) and (self.group, self.yes_item, + self.no_item) == (other.group, other.yes_item, other.no_item) + + def max_width(self): + return max(self.yes_item.max_width(), self.no_item.max_width()) + +class DefaultBoundary(ZeroWidthBase): + _opcode = OP.DEFAULT_BOUNDARY + _op_name = "DEFAULT_BOUNDARY" + +class DefaultEndOfWord(ZeroWidthBase): + _opcode = OP.DEFAULT_END_OF_WORD + _op_name = "DEFAULT_END_OF_WORD" + +class DefaultStartOfWord(ZeroWidthBase): + _opcode = OP.DEFAULT_START_OF_WORD + _op_name = "DEFAULT_START_OF_WORD" + +class EndOfLine(ZeroWidthBase): + _opcode = OP.END_OF_LINE + _op_name = "END_OF_LINE" + +class EndOfLineU(EndOfLine): + _opcode = OP.END_OF_LINE_U + _op_name = "END_OF_LINE_U" + +class EndOfString(ZeroWidthBase): + _opcode = OP.END_OF_STRING + _op_name = "END_OF_STRING" + +class EndOfStringLine(ZeroWidthBase): + _opcode = OP.END_OF_STRING_LINE + _op_name = "END_OF_STRING_LINE" + +class EndOfStringLineU(EndOfStringLine): + _opcode = OP.END_OF_STRING_LINE_U + _op_name = "END_OF_STRING_LINE_U" + +class EndOfWord(ZeroWidthBase): + _opcode = OP.END_OF_WORD + _op_name = "END_OF_WORD" + +class Failure(ZeroWidthBase): + _op_name = "FAILURE" + + def _compile(self, reverse, fuzzy): + return [(OP.FAILURE, )] + +class Fuzzy(RegexBase): + def __init__(self, subpattern, constraints=None): + RegexBase.__init__(self) + if constraints is None: + constraints = {} + self.subpattern = subpattern + self.constraints = constraints + + # If an error type is mentioned in the cost equation, then its maximum + # defaults to unlimited. + if "cost" in constraints: + for e in "dis": + if e in constraints["cost"]: + constraints.setdefault(e, (0, None)) + + # If any error type is mentioned, then all the error maxima default to + # 0, otherwise they default to unlimited. + if set(constraints) & set("dis"): + for e in "dis": + constraints.setdefault(e, (0, 0)) + else: + for e in "dis": + constraints.setdefault(e, (0, None)) + + # The maximum of the generic error type defaults to unlimited. + constraints.setdefault("e", (0, None)) + + # The cost equation defaults to equal costs. Also, the cost of any + # error type not mentioned in the cost equation defaults to 0. + if "cost" in constraints: + for e in "dis": + constraints["cost"].setdefault(e, 0) + else: + constraints["cost"] = {"d": 1, "i": 1, "s": 1, "max": + constraints["e"][1]} + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, reverse, True) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + self.subpattern = self.subpattern.remove_captures() + return self + + def is_atomic(self): + return self.subpattern.is_atomic() + + def contains_group(self): + return self.subpattern.contains_group() + + def _compile(self, reverse, fuzzy): + # The individual limits. + arguments = [] + for e in "dise": + v = self.constraints[e] + arguments.append(v[0]) + arguments.append(UNLIMITED if v[1] is None else v[1]) + + # The coeffs of the cost equation. + for e in "dis": + arguments.append(self.constraints["cost"][e]) + + # The maximum of the cost equation. + v = self.constraints["cost"]["max"] + arguments.append(UNLIMITED if v is None else v) + + flags = 0 + if reverse: + flags |= REVERSE_OP + + return ([(OP.FUZZY, flags) + tuple(arguments)] + + self.subpattern.compile(reverse, True) + [(OP.END,)]) + + def _dump(self, indent, reverse): + constraints = self._constraints_to_string() + if constraints: + constraints = " " + constraints + print "%sFUZZY%s" % (INDENT * indent, constraints) + self.subpattern.dump(indent + 1, reverse) + + def is_empty(self): + return self.subpattern.is_empty() + + def __eq__(self, other): + return (type(self) is type(other) and self.subpattern == + other.subpattern) + + def max_width(self): + return UNLIMITED + + def _constraints_to_string(self): + constraints = [] + + for name in "ids": + min, max = self.constraints[name] + if max == 0: + continue + + con = "" + + if min > 0: + con = "%s<=" % min + + con += name + + if max is not None: + con += "<=%s" % max + + constraints.append(con) + + cost = [] + for name in "ids": + coeff = self.constraints["cost"][name] + if coeff > 0: + cost.append("%s%s" % (coeff, name)) + + limit = self.constraints["cost"]["max"] + if limit is not None and limit > 0: + cost = "%s<=%s" % ("+".join(cost), limit) + constraints.append(cost) + + return ",".join(constraints) + +class Grapheme(RegexBase): + def _compile(self, reverse, fuzzy): + # Match at least 1 character until a grapheme boundary is reached. Note + # that this is the same whether matching forwards or backwards. + grapheme_matcher = Atomic(Sequence([LazyRepeat(AnyAll(), 1, None), + GraphemeBoundary()])) + + return grapheme_matcher.compile(reverse, fuzzy) + + def _dump(self, indent, reverse): + print "%sGRAPHEME" % (INDENT * indent) + + def max_width(self): + return UNLIMITED + +class GraphemeBoundary: + def compile(self, reverse, fuzzy): + return [(OP.GRAPHEME_BOUNDARY, 1)] + +class GreedyRepeat(RegexBase): + _opcode = OP.GREEDY_REPEAT + _op_name = "GREEDY_REPEAT" + + def __init__(self, subpattern, min_count, max_count): + RegexBase.__init__(self) + self.subpattern = subpattern + self.min_count = min_count + self.max_count = max_count + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info): + subpattern = self.subpattern.optimise(info) + + return type(self)(subpattern, self.min_count, self.max_count) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + self.subpattern = self.subpattern.remove_captures() + return self + + def is_atomic(self): + return self.min_count == self.max_count and self.subpattern.is_atomic() + + def contains_group(self): + return self.subpattern.contains_group() + + def get_firstset(self, reverse): + fs = self.subpattern.get_firstset(reverse) + if self.min_count == 0: + fs.add(None) + + return fs + + def _compile(self, reverse, fuzzy): + repeat = [self._opcode, self.min_count] + if self.max_count is None: + repeat.append(UNLIMITED) + else: + repeat.append(self.max_count) + + subpattern = self.subpattern.compile(reverse, fuzzy) + if not subpattern: + return [] + + return ([tuple(repeat)] + subpattern + [(OP.END, )]) + + def _dump(self, indent, reverse): + if self.max_count is None: + limit = "INF" + else: + limit = self.max_count + print "%s%s %s %s" % (INDENT * indent, self._op_name, self.min_count, + limit) + + self.subpattern.dump(indent + 1, reverse) + + def is_empty(self): + return self.subpattern.is_empty() + + def __eq__(self, other): + return type(self) is type(other) and (self.subpattern, self.min_count, + self.max_count) == (other.subpattern, other.min_count, + other.max_count) + + def max_width(self): + if self.max_count is None: + return UNLIMITED + + return self.subpattern.max_width() * self.max_count + + def get_required_string(self, reverse): + max_count = UNLIMITED if self.max_count is None else self.max_count + if self.min_count == 0: + w = self.subpattern.max_width() * max_count + return min(w, UNLIMITED), None + + ofs, req = self.subpattern.get_required_string(reverse) + if req: + return ofs, req + + w = self.subpattern.max_width() * max_count + return min(w, UNLIMITED), None + +class Group(RegexBase): + def __init__(self, info, group, subpattern): + RegexBase.__init__(self) + self.info = info + self.group = group + self.subpattern = subpattern + + self.call_ref = None + + def fix_groups(self, pattern, reverse, fuzzy): + self.info.defined_groups[self.group] = (self, reverse, fuzzy) + self.subpattern.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info): + subpattern = self.subpattern.optimise(info) + + return Group(self.info, self.group, subpattern) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + return self.subpattern.remove_captures() + + def is_atomic(self): + return self.subpattern.is_atomic() + + def can_be_affix(self): + return False + + def contains_group(self): + return True + + def get_firstset(self, reverse): + return self.subpattern.get_firstset(reverse) + + def has_simple_start(self): + return self.subpattern.has_simple_start() + + def _compile(self, reverse, fuzzy): + code = [] + + key = self.group, reverse, fuzzy + ref = self.info.call_refs.get(key) + if ref is not None: + code += [(OP.CALL_REF, ref)] + + public_group = private_group = self.group + if private_group < 0: + public_group = self.info.private_groups[private_group] + private_group = self.info.group_count - private_group + + code += ([(OP.GROUP, private_group, public_group)] + + self.subpattern.compile(reverse, fuzzy) + [(OP.END, )]) + + if ref is not None: + code += [(OP.END, )] + + return code + + def _dump(self, indent, reverse): + group = self.group + if group < 0: + group = private_groups[group] + print "%sGROUP %s" % (INDENT * indent, group) + self.subpattern.dump(indent + 1, reverse) + + def __eq__(self, other): + return (type(self) is type(other) and (self.group, self.subpattern) == + (other.group, other.subpattern)) + + def max_width(self): + return self.subpattern.max_width() + + def get_required_string(self, reverse): + return self.subpattern.get_required_string(reverse) + +class Keep(ZeroWidthBase): + _opcode = OP.KEEP + _op_name = "KEEP" + +class LazyRepeat(GreedyRepeat): + _opcode = OP.LAZY_REPEAT + _op_name = "LAZY_REPEAT" + +class LookAround(RegexBase): + _dir_text = {False: "AHEAD", True: "BEHIND"} + + def __init__(self, behind, positive, subpattern): + RegexBase.__init__(self) + self.behind = bool(behind) + self.positive = bool(positive) + self.subpattern = subpattern + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, self.behind, fuzzy) + + def optimise(self, info): + subpattern = self.subpattern.optimise(info) + if self.positive and subpattern.is_empty(): + return subpattern + + return LookAround(self.behind, self.positive, subpattern) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + return self.subpattern.remove_captures() + + def is_atomic(self): + return self.subpattern.is_atomic() + + def can_be_affix(self): + return self.subpattern.can_be_affix() + + def contains_group(self): + return self.subpattern.contains_group() + + def _compile(self, reverse, fuzzy): + return ([(OP.LOOKAROUND, int(self.positive), int(not self.behind))] + + self.subpattern.compile(self.behind) + [(OP.END, )]) + + def _dump(self, indent, reverse): + print "%sLOOK%s %s" % (INDENT * indent, self._dir_text[self.behind], + POS_TEXT[self.positive]) + self.subpattern.dump(indent + 1, self.behind) + + def is_empty(self): + return self.positive and self.subpattern.is_empty() + + def __eq__(self, other): + return type(self) is type(other) and (self.behind, self.positive, + self.subpattern) == (other.behind, other.positive, other.subpattern) + + def max_width(self): + return 0 + +class LookAroundConditional(RegexBase): + _dir_text = {False: "AHEAD", True: "BEHIND"} + + def __init__(self, behind, positive, subpattern, yes_item, no_item): + RegexBase.__init__(self) + self.behind = bool(behind) + self.positive = bool(positive) + self.subpattern = subpattern + self.yes_item = yes_item + self.no_item = no_item + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, reverse, fuzzy) + self.yes_item.fix_groups(pattern, reverse, fuzzy) + self.no_item.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info): + subpattern = self.subpattern.optimise(info) + yes_item = self.yes_item.optimise(info) + no_item = self.no_item.optimise(info) + + return LookAroundConditional(self.behind, self.positive, subpattern, + yes_item, no_item) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + self.yes_item = self.yes_item.pack_characters(info) + self.no_item = self.no_item.pack_characters(info) + return self + + def remove_captures(self): + self.subpattern = self.subpattern.remove_captures() + self.yes_item = self.yes_item.remove_captures() + self.no_item = self.no_item.remove_captures() + + def is_atomic(self): + return (self.subpattern.is_atomic() and self.yes_item.is_atomic() and + self.no_item.is_atomic()) + + def can_be_affix(self): + return (self.subpattern.can_be_affix() and self.yes_item.can_be_affix() + and self.no_item.can_be_affix()) + + def contains_group(self): + return (self.subpattern.contains_group() or + self.yes_item.contains_group() or self.no_item.contains_group()) + + def get_firstset(self, reverse): + return (self.subpattern.get_firstset(reverse) | + self.no_item.get_firstset(reverse)) + + def _compile(self, reverse, fuzzy): + code = [(OP.CONDITIONAL, int(self.positive), int(not self.behind))] + code.extend(self.subpattern.compile(self.behind, fuzzy)) + code.append((OP.NEXT, )) + code.extend(self.yes_item.compile(reverse, fuzzy)) + add_code = self.no_item.compile(reverse, fuzzy) + if add_code: + code.append((OP.NEXT, )) + code.extend(add_code) + + code.append((OP.END, )) + + return code + + def _dump(self, indent, reverse): + print("%sCONDITIONAL %s %s" % (INDENT * indent, + self._dir_text[self.behind], POS_TEXT[self.positive])) + self.subpattern.dump(indent + 1, self.behind) + print("%sEITHER" % (INDENT * indent)) + self.yes_item.dump(indent + 1, reverse) + if not self.no_item.is_empty(): + print("%sOR".format(INDENT * indent)) + self.no_item.dump(indent + 1, reverse) + + def is_empty(self): + return (self.subpattern.is_empty() and self.yes_item.is_empty() or + self.no_item.is_empty()) + + def __eq__(self, other): + return type(self) is type(other) and (self.subpattern, self.yes_item, + self.no_item) == (other.subpattern, other.yes_item, other.no_item) + + def max_width(self): + return max(self.yes_item.max_width(), self.no_item.max_width()) + + def get_required_string(self, reverse): + return self.max_width(), None + +class PrecompiledCode(RegexBase): + def __init__(self, code): + self.code = code + + def _compile(self, reverse, fuzzy): + return [tuple(self.code)] + +class Property(RegexBase): + _opcode = {(NOCASE, False): OP.PROPERTY, (IGNORECASE, False): + OP.PROPERTY_IGN, (FULLCASE, False): OP.PROPERTY, (FULLIGNORECASE, False): + OP.PROPERTY_IGN, (NOCASE, True): OP.PROPERTY_REV, (IGNORECASE, True): + OP.PROPERTY_IGN_REV, (FULLCASE, True): OP.PROPERTY_REV, (FULLIGNORECASE, + True): OP.PROPERTY_IGN_REV} + + def __init__(self, value, positive=True, case_flags=NOCASE, + zerowidth=False): + RegexBase.__init__(self) + self.value = value + self.positive = bool(positive) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + self.zerowidth = bool(zerowidth) + + self._key = (self.__class__, self.value, self.positive, + self.case_flags, self.zerowidth) + + def rebuild(self, positive, case_flags, zerowidth): + return Property(self.value, positive, case_flags, zerowidth) + + def optimise(self, info, in_set=False): + return self + + def get_firstset(self, reverse): + return set([self]) + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + return [(self._opcode[self.case_flags, reverse], flags, self.value)] + + def _dump(self, indent, reverse): + prop = PROPERTY_NAMES[self.value >> 16] + name, value = prop[0], prop[1][self.value & 0xFFFF] + print "%sPROPERTY %s %s:%s%s" % (INDENT * indent, + POS_TEXT[self.positive], name, value, CASE_TEXT[self.case_flags]) + + def matches(self, ch): + return _regex.has_property_value(self.value, ch) == self.positive + + def max_width(self): + return 1 + +class Prune(ZeroWidthBase): + _op_name = "PRUNE" + + def _compile(self, reverse, fuzzy): + return [(OP.PRUNE, )] + +class Range(RegexBase): + _opcode = {(NOCASE, False): OP.RANGE, (IGNORECASE, False): OP.RANGE_IGN, + (FULLCASE, False): OP.RANGE, (FULLIGNORECASE, False): OP.RANGE_IGN, + (NOCASE, True): OP.RANGE_REV, (IGNORECASE, True): OP.RANGE_IGN_REV, + (FULLCASE, True): OP.RANGE_REV, (FULLIGNORECASE, True): OP.RANGE_IGN_REV} + _op_name = "RANGE" + + def __init__(self, lower, upper, positive=True, case_flags=NOCASE, + zerowidth=False): + RegexBase.__init__(self) + self.lower = lower + self.upper = upper + self.positive = bool(positive) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + self.zerowidth = bool(zerowidth) + + self._key = (self.__class__, self.lower, self.upper, self.positive, + self.case_flags, self.zerowidth) + + def rebuild(self, positive, case_flags, zerowidth): + return Range(self.lower, self.upper, positive, case_flags, zerowidth) + + def optimise(self, info, in_set=False): + # Is the range case-sensitive? + if not self.positive or not (self.case_flags & IGNORECASE) or in_set: + return self + + # Is full case-folding possible? + if (not (info.flags & UNICODE) or (self.case_flags & FULLIGNORECASE) != + FULLIGNORECASE): + return self + + # Get the characters which expand to multiple codepoints on folding. + expanding_chars = _regex.get_expand_on_folding() + + # Get the folded characters in the range. + items = [] + for ch in expanding_chars: + if self.lower <= ord(ch) <= self.upper: + folded = _regex.fold_case(FULL_CASE_FOLDING, ch) + items.append(String([ord(c) for c in folded], + case_flags=self.case_flags)) + + if not items: + # We can fall back to simple case-folding. + return self + + if len(items) < self.upper - self.lower + 1: + # Not all the characters are covered by the full case-folding. + items.insert(0, self) + + return Branch(items) + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + return [(self._opcode[self.case_flags, reverse], flags, self.lower, + self.upper)] + + def _dump(self, indent, reverse): + display_lower = repr(unichr(self.lower)).lstrip("bu") + display_upper = repr(unichr(self.upper)).lstrip("bu") + print "%sRANGE %s %s %s%s" % (INDENT * indent, POS_TEXT[self.positive], + display_lower, display_upper, CASE_TEXT[self.case_flags]) + + def matches(self, ch): + return (self.lower <= ch <= self.upper) == self.positive + + def max_width(self): + return 1 + +class RefGroup(RegexBase): + _opcode = {(NOCASE, False): OP.REF_GROUP, (IGNORECASE, False): + OP.REF_GROUP_IGN, (FULLCASE, False): OP.REF_GROUP, (FULLIGNORECASE, + False): OP.REF_GROUP_FLD, (NOCASE, True): OP.REF_GROUP_REV, (IGNORECASE, + True): OP.REF_GROUP_IGN_REV, (FULLCASE, True): OP.REF_GROUP_REV, + (FULLIGNORECASE, True): OP.REF_GROUP_FLD_REV} + + def __init__(self, info, group, position, case_flags=NOCASE): + RegexBase.__init__(self) + self.info = info + self.group = group + self.position = position + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + + self._key = self.__class__, self.group, self.case_flags + + def fix_groups(self, pattern, reverse, fuzzy): + try: + self.group = int(self.group) + except ValueError: + try: + self.group = self.info.group_index[self.group] + except KeyError: + raise error("unknown group", pattern, self.position) + + if not 1 <= self.group <= self.info.group_count: + raise error("invalid group reference", pattern, self.position) + + self._key = self.__class__, self.group, self.case_flags + + def remove_captures(self): + raise error("group reference not allowed", pattern, self.position) + + def _compile(self, reverse, fuzzy): + flags = 0 + if fuzzy: + flags |= FUZZY_OP + return [(self._opcode[self.case_flags, reverse], flags, self.group)] + + def _dump(self, indent, reverse): + print "%sREF_GROUP %s%s" % (INDENT * indent, self.group, + CASE_TEXT[self.case_flags]) + + def max_width(self): + return UNLIMITED + +class SearchAnchor(ZeroWidthBase): + _opcode = OP.SEARCH_ANCHOR + _op_name = "SEARCH_ANCHOR" + +class Sequence(RegexBase): + def __init__(self, items=None): + RegexBase.__init__(self) + if items is None: + items = [] + + self.items = items + + def fix_groups(self, pattern, reverse, fuzzy): + for s in self.items: + s.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info): + # Flatten the sequences. + items = [] + for s in self.items: + s = s.optimise(info) + if isinstance(s, Sequence): + items.extend(s.items) + else: + items.append(s) + + return make_sequence(items) + + def pack_characters(self, info): + "Packs sequences of characters into strings." + items = [] + characters = [] + case_flags = NOCASE + for s in self.items: + if type(s) is Character and s.positive: + if s.case_flags != case_flags: + # Different case sensitivity, so flush, unless neither the + # previous nor the new character are cased. + if s.case_flags or is_cased(info, s.value): + Sequence._flush_characters(info, characters, + case_flags, items) + + case_flags = s.case_flags + + characters.append(s.value) + elif type(s) is String or type(s) is Literal: + if s.case_flags != case_flags: + # Different case sensitivity, so flush, unless the neither + # the previous nor the new string are cased. + if s.case_flags or any(is_cased(info, c) for c in + characters): + Sequence._flush_characters(info, characters, + case_flags, items) + + case_flags = s.case_flags + + characters.extend(s.characters) + else: + Sequence._flush_characters(info, characters, case_flags, items) + + items.append(s.pack_characters(info)) + + Sequence._flush_characters(info, characters, case_flags, items) + + return make_sequence(items) + + def remove_captures(self): + self.items = [s.remove_captures() for s in self.items] + return self + + def is_atomic(self): + return all(s.is_atomic() for s in self.items) + + def can_be_affix(self): + return False + + def contains_group(self): + return any(s.contains_group() for s in self.items) + + def get_firstset(self, reverse): + fs = set() + items = self.items + if reverse: + items.reverse() + for s in items: + fs |= s.get_firstset(reverse) + if None not in fs: + return fs + fs.discard(None) + + return fs | set([None]) + + def has_simple_start(self): + return bool(self.items) and self.items[0].has_simple_start() + + def _compile(self, reverse, fuzzy): + seq = self.items + if reverse: + seq = seq[::-1] + + code = [] + for s in seq: + code.extend(s.compile(reverse, fuzzy)) + + return code + + def _dump(self, indent, reverse): + for s in self.items: + s.dump(indent, reverse) + + @staticmethod + def _flush_characters(info, characters, case_flags, items): + if not characters: + return + + # Disregard case_flags if all of the characters are case-less. + if case_flags & IGNORECASE: + if not any(is_cased(info, c) for c in characters): + case_flags = NOCASE + + if len(characters) == 1: + items.append(Character(characters[0], case_flags=case_flags)) + else: + items.append(String(characters, case_flags=case_flags)) + + characters[:] = [] + + def is_empty(self): + return all(i.is_empty() for i in self.items) + + def __eq__(self, other): + return type(self) is type(other) and self.items == other.items + + def max_width(self): + return sum(s.max_width() for s in self.items) + + def get_required_string(self, reverse): + seq = self.items + if reverse: + seq = seq[::-1] + + offset = 0 + + for s in seq: + ofs, req = s.get_required_string(reverse) + offset += ofs + if req: + return offset, req + + return offset, None + +class SetBase(RegexBase): + def __init__(self, info, items, positive=True, case_flags=NOCASE, + zerowidth=False): + RegexBase.__init__(self) + self.info = info + self.items = tuple(items) + self.positive = bool(positive) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + self.zerowidth = bool(zerowidth) + + self.char_width = 1 + + self._key = (self.__class__, self.items, self.positive, + self.case_flags, self.zerowidth) + + def rebuild(self, positive, case_flags, zerowidth): + return type(self)(self.info, self.items, positive, case_flags, + zerowidth).optimise(self.info) + + def get_firstset(self, reverse): + return set([self]) + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + code = [(self._opcode[self.case_flags, reverse], flags)] + for m in self.items: + code.extend(m.compile()) + + code.append((OP.END, )) + + return code + + def _dump(self, indent, reverse): + print "%s%s %s%s" % (INDENT * indent, self._op_name, + POS_TEXT[self.positive], CASE_TEXT[self.case_flags]) + for i in self.items: + i.dump(indent + 1, reverse) + + def _handle_case_folding(self, info, in_set): + # Is the set case-sensitive? + if not self.positive or not (self.case_flags & IGNORECASE) or in_set: + return self + + # Is full case-folding possible? + if (not (self.info.flags & UNICODE) or (self.case_flags & + FULLIGNORECASE) != FULLIGNORECASE): + return self + + # Get the characters which expand to multiple codepoints on folding. + expanding_chars = _regex.get_expand_on_folding() + + # Get the folded characters in the set. + items = [] + seen = set() + for ch in expanding_chars: + if self.matches(ord(ch)): + folded = _regex.fold_case(FULL_CASE_FOLDING, ch) + if folded not in seen: + items.append(String([ord(c) for c in folded], + case_flags=self.case_flags)) + seen.add(folded) + + if not items: + # We can fall back to simple case-folding. + return self + + return Branch([self] + items) + + def max_width(self): + # Is the set case-sensitive? + if not self.positive or not (self.case_flags & IGNORECASE): + return 1 + + # Is full case-folding possible? + if (not (self.info.flags & UNICODE) or (self.case_flags & + FULLIGNORECASE) != FULLIGNORECASE): + return 1 + + # Get the characters which expand to multiple codepoints on folding. + expanding_chars = _regex.get_expand_on_folding() + + # Get the folded characters in the set. + seen = set() + for ch in expanding_chars: + if self.matches(ord(ch)): + folded = _regex.fold_case(FULL_CASE_FOLDING, ch) + seen.add(folded) + + if not seen: + return 1 + + return max(len(folded) for folded in seen) + +class SetDiff(SetBase): + _opcode = {(NOCASE, False): OP.SET_DIFF, (IGNORECASE, False): + OP.SET_DIFF_IGN, (FULLCASE, False): OP.SET_DIFF, (FULLIGNORECASE, False): + OP.SET_DIFF_IGN, (NOCASE, True): OP.SET_DIFF_REV, (IGNORECASE, True): + OP.SET_DIFF_IGN_REV, (FULLCASE, True): OP.SET_DIFF_REV, (FULLIGNORECASE, + True): OP.SET_DIFF_IGN_REV} + _op_name = "SET_DIFF" + + def optimise(self, info, in_set=False): + items = self.items + if len(items) > 2: + items = [items[0], SetUnion(info, items[1 : ])] + + if len(items) == 1: + return items[0].with_flags(case_flags=self.case_flags, + zerowidth=self.zerowidth).optimise(info, in_set) + + self.items = tuple(m.optimise(info, in_set=True) for m in items) + + return self._handle_case_folding(info, in_set) + + def matches(self, ch): + m = self.items[0].matches(ch) and not self.items[1].matches(ch) + return m == self.positive + +class SetInter(SetBase): + _opcode = {(NOCASE, False): OP.SET_INTER, (IGNORECASE, False): + OP.SET_INTER_IGN, (FULLCASE, False): OP.SET_INTER, (FULLIGNORECASE, + False): OP.SET_INTER_IGN, (NOCASE, True): OP.SET_INTER_REV, (IGNORECASE, + True): OP.SET_INTER_IGN_REV, (FULLCASE, True): OP.SET_INTER_REV, + (FULLIGNORECASE, True): OP.SET_INTER_IGN_REV} + _op_name = "SET_INTER" + + def optimise(self, info, in_set=False): + items = [] + for m in self.items: + m = m.optimise(info, in_set=True) + if isinstance(m, SetInter) and m.positive: + # Intersection in intersection. + items.extend(m.items) + else: + items.append(m) + + if len(items) == 1: + return items[0].with_flags(case_flags=self.case_flags, + zerowidth=self.zerowidth).optimise(info, in_set) + + self.items = tuple(items) + + return self._handle_case_folding(info, in_set) + + def matches(self, ch): + m = all(i.matches(ch) for i in self.items) + return m == self.positive + +class SetSymDiff(SetBase): + _opcode = {(NOCASE, False): OP.SET_SYM_DIFF, (IGNORECASE, False): + OP.SET_SYM_DIFF_IGN, (FULLCASE, False): OP.SET_SYM_DIFF, (FULLIGNORECASE, + False): OP.SET_SYM_DIFF_IGN, (NOCASE, True): OP.SET_SYM_DIFF_REV, + (IGNORECASE, True): OP.SET_SYM_DIFF_IGN_REV, (FULLCASE, True): + OP.SET_SYM_DIFF_REV, (FULLIGNORECASE, True): OP.SET_SYM_DIFF_IGN_REV} + _op_name = "SET_SYM_DIFF" + + def optimise(self, info, in_set=False): + items = [] + for m in self.items: + m = m.optimise(info, in_set=True) + if isinstance(m, SetSymDiff) and m.positive: + # Symmetric difference in symmetric difference. + items.extend(m.items) + else: + items.append(m) + + if len(items) == 1: + return items[0].with_flags(case_flags=self.case_flags, + zerowidth=self.zerowidth).optimise(info, in_set) + + self.items = tuple(items) + + return self._handle_case_folding(info, in_set) + + def matches(self, ch): + m = False + for i in self.items: + m = m != i.matches(ch) + + return m == self.positive + +class SetUnion(SetBase): + _opcode = {(NOCASE, False): OP.SET_UNION, (IGNORECASE, False): + OP.SET_UNION_IGN, (FULLCASE, False): OP.SET_UNION, (FULLIGNORECASE, + False): OP.SET_UNION_IGN, (NOCASE, True): OP.SET_UNION_REV, (IGNORECASE, + True): OP.SET_UNION_IGN_REV, (FULLCASE, True): OP.SET_UNION_REV, + (FULLIGNORECASE, True): OP.SET_UNION_IGN_REV} + _op_name = "SET_UNION" + + def optimise(self, info, in_set=False): + items = [] + for m in self.items: + m = m.optimise(info, in_set=True) + if isinstance(m, SetUnion) and m.positive: + # Union in union. + items.extend(m.items) + else: + items.append(m) + + if len(items) == 1: + i = items[0] + return i.with_flags(positive=i.positive == self.positive, + case_flags=self.case_flags, + zerowidth=self.zerowidth).optimise(info, in_set) + + self.items = tuple(items) + + return self._handle_case_folding(info, in_set) + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + + characters, others = defaultdict(list), [] + for m in self.items: + if isinstance(m, Character): + characters[m.positive].append(m.value) + else: + others.append(m) + + code = [(self._opcode[self.case_flags, reverse], flags)] + + for positive, values in characters.items(): + flags = 0 + if positive: + flags |= POSITIVE_OP + if len(values) == 1: + code.append((OP.CHARACTER, flags, values[0])) + else: + code.append((OP.STRING, flags, len(values)) + tuple(values)) + + for m in others: + code.extend(m.compile()) + + code.append((OP.END, )) + + return code + + def matches(self, ch): + m = any(i.matches(ch) for i in self.items) + return m == self.positive + +class Skip(ZeroWidthBase): + _op_name = "SKIP" + _opcode = OP.SKIP + +class StartOfLine(ZeroWidthBase): + _opcode = OP.START_OF_LINE + _op_name = "START_OF_LINE" + +class StartOfLineU(StartOfLine): + _opcode = OP.START_OF_LINE_U + _op_name = "START_OF_LINE_U" + +class StartOfString(ZeroWidthBase): + _opcode = OP.START_OF_STRING + _op_name = "START_OF_STRING" + +class StartOfWord(ZeroWidthBase): + _opcode = OP.START_OF_WORD + _op_name = "START_OF_WORD" + +class String(RegexBase): + _opcode = {(NOCASE, False): OP.STRING, (IGNORECASE, False): OP.STRING_IGN, + (FULLCASE, False): OP.STRING, (FULLIGNORECASE, False): OP.STRING_FLD, + (NOCASE, True): OP.STRING_REV, (IGNORECASE, True): OP.STRING_IGN_REV, + (FULLCASE, True): OP.STRING_REV, (FULLIGNORECASE, True): + OP.STRING_FLD_REV} + + def __init__(self, characters, case_flags=NOCASE): + self.characters = tuple(characters) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + + if (self.case_flags & FULLIGNORECASE) == FULLIGNORECASE: + folded_characters = [] + for char in self.characters: + folded = _regex.fold_case(FULL_CASE_FOLDING, unichr(char)) + folded_characters.extend(ord(c) for c in folded) + else: + folded_characters = self.characters + + self.folded_characters = tuple(folded_characters) + self.required = False + + self._key = self.__class__, self.characters, self.case_flags + + def get_firstset(self, reverse): + if reverse: + pos = -1 + else: + pos = 0 + return set([Character(self.characters[pos], + case_flags=self.case_flags)]) + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if fuzzy: + flags |= FUZZY_OP + if self.required: + flags |= REQUIRED_OP + return [(self._opcode[self.case_flags, reverse], flags, + len(self.folded_characters)) + self.folded_characters] + + def _dump(self, indent, reverse): + display = repr("".join(unichr(c) for c in self.characters)).lstrip("bu") + print "%sSTRING %s%s" % (INDENT * indent, display, + CASE_TEXT[self.case_flags]) + + def max_width(self): + return len(self.folded_characters) + + def get_required_string(self, reverse): + return 0, self + +class Literal(String): + def _dump(self, indent, reverse): + for c in self.characters: + display = repr(unichr(c)).lstrip("bu") + print "%sCHARACTER MATCH %s%s" % (INDENT * indent, display, + CASE_TEXT[self.case_flags]) + +class StringSet(RegexBase): + _opcode = {(NOCASE, False): OP.STRING_SET, (IGNORECASE, False): + OP.STRING_SET_IGN, (FULLCASE, False): OP.STRING_SET, (FULLIGNORECASE, + False): OP.STRING_SET_FLD, (NOCASE, True): OP.STRING_SET_REV, + (IGNORECASE, True): OP.STRING_SET_IGN_REV, (FULLCASE, True): + OP.STRING_SET_REV, (FULLIGNORECASE, True): OP.STRING_SET_FLD_REV} + + def __init__(self, info, name, case_flags=NOCASE): + self.info = info + self.name = name + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + + self._key = self.__class__, self.name, self.case_flags + + self.set_key = (name, self.case_flags) + if self.set_key not in info.named_lists_used: + info.named_lists_used[self.set_key] = len(info.named_lists_used) + + def _compile(self, reverse, fuzzy): + index = self.info.named_lists_used[self.set_key] + items = self.info.kwargs[self.name] + + case_flags = self.case_flags + + if not items: + return [] + + encoding = self.info.flags & _ALL_ENCODINGS + fold_flags = encoding | case_flags + + if fuzzy: + choices = [self._folded(fold_flags, i) for i in items] + + # Sort from longest to shortest. + choices.sort(key=lambda s: (-len(s), s)) + + branches = [] + for string in choices: + branches.append(Sequence([Character(c, case_flags=case_flags) + for c in string])) + + if len(branches) > 1: + branch = Branch(branches) + else: + branch = branches[0] + branch = branch.optimise(self.info).pack_characters(self.info) + + return branch.compile(reverse, fuzzy) + else: + min_len = min(len(i) for i in items) + max_len = max(len(self._folded(fold_flags, i)) for i in items) + return [(self._opcode[case_flags, reverse], index, min_len, + max_len)] + + def _dump(self, indent, reverse): + print "%sSTRING_SET %s%s" % (INDENT * indent, self.name, + CASE_TEXT[self.case_flags]) + + def _folded(self, fold_flags, item): + if isinstance(item, unicode): + return [ord(c) for c in _regex.fold_case(fold_flags, item)] + else: + return [ord(c) for c in item] + + def _flatten(self, s): + # Flattens the branches. + if isinstance(s, Branch): + for b in s.branches: + self._flatten(b) + elif isinstance(s, Sequence) and s.items: + seq = s.items + + while isinstance(seq[-1], Sequence): + seq[-1 : ] = seq[-1].items + + n = 0 + while n < len(seq) and isinstance(seq[n], Character): + n += 1 + + if n > 1: + seq[ : n] = [String([c.value for c in seq[ : n]], + case_flags=self.case_flags)] + + self._flatten(seq[-1]) + + def max_width(self): + if not self.info.kwargs[self.name]: + return 0 + + if self.case_flags & IGNORECASE: + fold_flags = (self.info.flags & _ALL_ENCODINGS) | self.case_flags + return max(len(_regex.fold_case(fold_flags, i)) for i in + self.info.kwargs[self.name]) + else: + return max(len(i) for i in self.info.kwargs[self.name]) + +class Source(object): + "Scanner for the regular expression source string." + def __init__(self, string): + if isinstance(string, unicode): + self.string = string + self.char_type = unichr + else: + self.string = string + self.char_type = chr + + self.pos = 0 + self.ignore_space = False + self.sep = string[ : 0] + + def get(self): + string = self.string + pos = self.pos + + try: + if self.ignore_space: + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + else: + break + + ch = string[pos] + self.pos = pos + 1 + return ch + except IndexError: + # We've reached the end of the string. + self.pos = pos + return string[ : 0] + except ValueError: + # The comment extended to the end of the string. + self.pos = len(string) + return string[ : 0] + + def get_many(self, count=1): + string = self.string + pos = self.pos + + try: + if self.ignore_space: + substring = [] + + while len(substring) < count: + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + else: + break + + substring.append(string[pos]) + pos += 1 + + substring = "".join(substring) + else: + substring = string[pos : pos + count] + pos += len(substring) + + self.pos = pos + return substring + except IndexError: + # We've reached the end of the string. + self.pos = len(string) + return "".join(substring) + except ValueError: + # The comment extended to the end of the string. + self.pos = len(string) + return "".join(substring) + + def get_while(self, test_set, include=True): + string = self.string + pos = self.pos + + if self.ignore_space: + try: + substring = [] + + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + elif (string[pos] in test_set) == include: + substring.append(string[pos]) + pos += 1 + else: + break + + self.pos = pos + except IndexError: + # We've reached the end of the string. + self.pos = len(string) + except ValueError: + # The comment extended to the end of the string. + self.pos = len(string) + + return "".join(substring) + else: + try: + while (string[pos] in test_set) == include: + pos += 1 + + substring = string[self.pos : pos] + + self.pos = pos + + return substring + except IndexError: + # We've reached the end of the string. + substring = string[self.pos : pos] + + self.pos = pos + + return substring + + def skip_while(self, test_set, include=True): + string = self.string + pos = self.pos + + try: + if self.ignore_space: + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + elif (string[pos] in test_set) == include: + pos += 1 + else: + break + else: + while (string[pos] in test_set) == include: + pos += 1 + + self.pos = pos + except IndexError: + # We've reached the end of the string. + self.pos = len(string) + except ValueError: + # The comment extended to the end of the string. + self.pos = len(string) + + def match(self, substring): + string = self.string + pos = self.pos + + if self.ignore_space: + try: + for c in substring: + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + else: + break + + if string[pos] != c: + return False + + pos += 1 + + self.pos = pos + + return True + except IndexError: + # We've reached the end of the string. + return False + except ValueError: + # The comment extended to the end of the string. + return False + else: + if not string.startswith(substring, pos): + return False + + self.pos = pos + len(substring) + + return True + + def expect(self, substring): + if not self.match(substring): + raise error("missing %s" % substring, self.string, self.pos) + + def at_end(self): + string = self.string + pos = self.pos + + try: + if self.ignore_space: + while True: + if string[pos].isspace(): + pos += 1 + elif string[pos] == "#": + pos = string.index("\n", pos) + else: + break + + return pos >= len(string) + except IndexError: + # We've reached the end of the string. + return True + except ValueError: + # The comment extended to the end of the string. + return True + +class Info(object): + "Info about the regular expression." + + def __init__(self, flags=0, char_type=None, kwargs={}): + flags |= DEFAULT_FLAGS[(flags & _ALL_VERSIONS) or DEFAULT_VERSION] + self.flags = flags + self.global_flags = flags + self.inline_locale = False + + self.kwargs = kwargs + + self.group_count = 0 + self.group_index = {} + self.group_name = {} + self.char_type = char_type + self.named_lists_used = {} + self.open_groups = [] + self.open_group_count = {} + self.defined_groups = {} + self.group_calls = [] + self.private_groups = {} + + def open_group(self, name=None): + group = self.group_index.get(name) + if group is None: + while True: + self.group_count += 1 + if name is None or self.group_count not in self.group_name: + break + + group = self.group_count + if name: + self.group_index[name] = group + self.group_name[group] = name + + if group in self.open_groups: + # We have a nested named group. We'll assign it a private group + # number, initially negative until we can assign a proper + # (positive) number. + group_alias = -(len(self.private_groups) + 1) + self.private_groups[group_alias] = group + group = group_alias + + self.open_groups.append(group) + self.open_group_count[group] = self.open_group_count.get(group, 0) + 1 + + return group + + def close_group(self): + self.open_groups.pop() + + def is_open_group(self, name): + # In version 1, a group reference can refer to an open group. We'll + # just pretend the group isn't open. + version = (self.flags & _ALL_VERSIONS) or DEFAULT_VERSION + if version == VERSION1: + return False + + if name.isdigit(): + group = int(name) + else: + group = self.group_index.get(name) + + return group in self.open_groups + +def _check_group_features(info, parsed): + """Checks whether the reverse and fuzzy features of the group calls match + the groups which they call. + """ + call_refs = {} + additional_groups = [] + for call, reverse, fuzzy in info.group_calls: + # Look up the reference of this group call. + key = (call.group, reverse, fuzzy) + ref = call_refs.get(key) + if ref is None: + # This group doesn't have a reference yet, so look up its features. + if call.group == 0: + # Calling the pattern as a whole. + rev = bool(info.flags & REVERSE) + fuz = isinstance(parsed, Fuzzy) + if (rev, fuz) != (reverse, fuzzy): + # The pattern as a whole doesn't have the features we want, + # so we'll need to make a copy of it with the desired + # features. + additional_groups.append((parsed, reverse, fuzzy)) + else: + # Calling a capture group. + def_info = info.defined_groups[call.group] + group = def_info[0] + if def_info[1 : ] != (reverse, fuzzy): + # The group doesn't have the features we want, so we'll + # need to make a copy of it with the desired features. + additional_groups.append((group, reverse, fuzzy)) + + ref = len(call_refs) + call_refs[key] = ref + + call.call_ref = ref + + info.call_refs = call_refs + info.additional_groups = additional_groups + +def _get_required_string(parsed, flags): + "Gets the required string and related info of a parsed pattern." + + req_offset, required = parsed.get_required_string(bool(flags & REVERSE)) + if required: + required.required = True + if req_offset >= UNLIMITED: + req_offset = -1 + + req_flags = required.case_flags + if not (flags & UNICODE): + req_flags &= ~UNICODE + + req_chars = required.folded_characters + else: + req_offset = 0 + req_chars = () + req_flags = 0 + + return req_offset, req_chars, req_flags + +class Scanner: + def __init__(self, lexicon, flags=0): + self.lexicon = lexicon + + # Combine phrases into a compound pattern. + patterns = [] + for phrase, action in lexicon: + # Parse the regular expression. + source = Source(phrase) + info = Info(flags, source.char_type) + source.ignore_space = bool(info.flags & VERBOSE) + parsed = _parse_pattern(source, info) + if not source.at_end(): + raise error("unbalanced parenthesis", source.string, source.pos) + + # We want to forbid capture groups within each phrase. + patterns.append(parsed.remove_captures()) + + # Combine all the subpatterns into one pattern. + info = Info(flags) + patterns = [Group(info, g + 1, p) for g, p in enumerate(patterns)] + parsed = Branch(patterns) + + # Optimise the compound pattern. + parsed = parsed.optimise(info) + parsed = parsed.pack_characters(info) + + # Get the required string. + req_offset, req_chars, req_flags = _get_required_string(parsed, + info.flags) + + # Check the features of the groups. + _check_group_features(info, parsed) + + # Complain if there are any group calls. They are not supported by the + # Scanner class. + if info.call_refs: + raise error("recursive regex not supported by Scanner", + source.string, source.pos) + + reverse = bool(info.flags & REVERSE) + + # Compile the compound pattern. The result is a list of tuples. + code = parsed.compile(reverse) + [(OP.SUCCESS, )] + + # Flatten the code into a list of ints. + code = _flatten_code(code) + + if not parsed.has_simple_start(): + # Get the first set, if possible. + try: + fs_code = _compile_firstset(info, parsed.get_firstset(reverse)) + fs_code = _flatten_code(fs_code) + code = fs_code + code + except _FirstSetError: + pass + + # Check the global flags for conflicts. + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + if version not in (0, VERSION0, VERSION1): + raise ValueError("VERSION0 and VERSION1 flags are mutually incompatible") + + # Create the PatternObject. + # + # Local flags like IGNORECASE affect the code generation, but aren't + # needed by the PatternObject itself. Conversely, global flags like + # LOCALE _don't_ affect the code generation but _are_ needed by the + # PatternObject. + self.scanner = _regex.compile(None, (flags & GLOBAL_FLAGS) | version, + code, {}, {}, {}, [], req_offset, req_chars, req_flags, + len(patterns)) + + def scan(self, string): + result = [] + append = result.append + match = self.scanner.scanner(string).match + i = 0 + while True: + m = match() + if not m: + break + j = m.end() + if i == j: + break + action = self.lexicon[m.lastindex - 1][1] + if hasattr(action, '__call__'): + self.match = m + action = action(self, m.group()) + if action is not None: + append(action) + i = j + + return result, string[i : ] + +# Get the known properties dict. +PROPERTIES = _regex.get_properties() + +# Build the inverse of the properties dict. +PROPERTY_NAMES = {} +for prop_name, (prop_id, values) in PROPERTIES.items(): + name, prop_values = PROPERTY_NAMES.get(prop_id, ("", {})) + name = max(name, prop_name, key=len) + PROPERTY_NAMES[prop_id] = name, prop_values + + for val_name, val_id in values.items(): + prop_values[val_id] = max(prop_values.get(val_id, ""), val_name, + key=len) + +# Character escape sequences. +CHARACTER_ESCAPES = { + "a": "\a", + "b": "\b", + "f": "\f", + "n": "\n", + "r": "\r", + "t": "\t", + "v": "\v", +} + +# Predefined character set escape sequences. +CHARSET_ESCAPES = { + "d": lookup_property(None, "Digit", True), + "D": lookup_property(None, "Digit", False), + "s": lookup_property(None, "Space", True), + "S": lookup_property(None, "Space", False), + "w": lookup_property(None, "Word", True), + "W": lookup_property(None, "Word", False), +} + +# Positional escape sequences. +POSITION_ESCAPES = { + "A": StartOfString(), + "b": Boundary(), + "B": Boundary(False), + "K": Keep(), + "m": StartOfWord(), + "M": EndOfWord(), + "Z": EndOfString(), +} + +# Positional escape sequences when WORD flag set. +WORD_POSITION_ESCAPES = dict(POSITION_ESCAPES) +WORD_POSITION_ESCAPES.update({ + "b": DefaultBoundary(), + "B": DefaultBoundary(False), + "m": DefaultStartOfWord(), + "M": DefaultEndOfWord(), +}) + +# Regex control verbs. +VERBS = { + "FAIL": Failure(), + "F": Failure(), + "PRUNE": Prune(), + "SKIP": Skip(), +} diff --git a/lib/regex/_regex_unicode.c b/lib/regex/_regex_unicode.c new file mode 100644 index 000000000..47c896e68 --- /dev/null +++ b/lib/regex/_regex_unicode.c @@ -0,0 +1,14258 @@ +/* For Unicode version 8.0.0 */ + +#include "_regex_unicode.h" + +#define RE_BLANK_MASK ((1 << RE_PROP_ZL) | (1 << RE_PROP_ZP)) +#define RE_GRAPH_MASK ((1 << RE_PROP_CC) | (1 << RE_PROP_CS) | (1 << RE_PROP_CN)) +#define RE_WORD_MASK (RE_PROP_M_MASK | (1 << RE_PROP_ND) | (1 << RE_PROP_PC)) + +typedef struct RE_AllCases { + RE_INT32 diffs[RE_MAX_CASES - 1]; +} RE_AllCases; + +typedef struct RE_FullCaseFolding { + RE_INT32 diff; + RE_UINT16 codepoints[RE_MAX_FOLDED - 1]; +} RE_FullCaseFolding; + +/* strings. */ + +char* re_strings[] = { + "-1/2", + "0", + "1", + "1/10", + "1/12", + "1/16", + "1/2", + "1/3", + "1/4", + "1/5", + "1/6", + "1/7", + "1/8", + "1/9", + "10", + "100", + "1000", + "10000", + "100000", + "1000000", + "100000000", + "10000000000", + "1000000000000", + "103", + "107", + "11", + "11/12", + "11/2", + "118", + "12", + "122", + "129", + "13", + "13/2", + "130", + "132", + "133", + "14", + "15", + "15/2", + "16", + "17", + "17/2", + "18", + "19", + "2", + "2/3", + "2/5", + "20", + "200", + "2000", + "20000", + "200000", + "202", + "21", + "214", + "216", + "216000", + "218", + "22", + "220", + "222", + "224", + "226", + "228", + "23", + "230", + "232", + "233", + "234", + "24", + "240", + "25", + "26", + "27", + "28", + "29", + "3", + "3/16", + "3/2", + "3/4", + "3/5", + "3/8", + "30", + "300", + "3000", + "30000", + "300000", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "4", + "4/5", + "40", + "400", + "4000", + "40000", + "400000", + "41", + "42", + "43", + "432000", + "44", + "45", + "46", + "47", + "48", + "49", + "5", + "5/12", + "5/2", + "5/6", + "5/8", + "50", + "500", + "5000", + "50000", + "500000", + "6", + "60", + "600", + "6000", + "60000", + "600000", + "7", + "7/12", + "7/2", + "7/8", + "70", + "700", + "7000", + "70000", + "700000", + "8", + "80", + "800", + "8000", + "80000", + "800000", + "84", + "9", + "9/2", + "90", + "900", + "9000", + "90000", + "900000", + "91", + "A", + "ABOVE", + "ABOVELEFT", + "ABOVERIGHT", + "AEGEANNUMBERS", + "AGHB", + "AHEX", + "AHOM", + "AI", + "AIN", + "AL", + "ALAPH", + "ALCHEMICAL", + "ALCHEMICALSYMBOLS", + "ALEF", + "ALETTER", + "ALNUM", + "ALPHA", + "ALPHABETIC", + "ALPHABETICPF", + "ALPHABETICPRESENTATIONFORMS", + "ALPHANUMERIC", + "AMBIGUOUS", + "AN", + "ANATOLIANHIEROGLYPHS", + "ANCIENTGREEKMUSIC", + "ANCIENTGREEKMUSICALNOTATION", + "ANCIENTGREEKNUMBERS", + "ANCIENTSYMBOLS", + "ANY", + "AR", + "ARAB", + "ARABIC", + "ARABICEXTA", + "ARABICEXTENDEDA", + "ARABICLETTER", + "ARABICMATH", + "ARABICMATHEMATICALALPHABETICSYMBOLS", + "ARABICNUMBER", + "ARABICPFA", + "ARABICPFB", + "ARABICPRESENTATIONFORMSA", + "ARABICPRESENTATIONFORMSB", + "ARABICSUP", + "ARABICSUPPLEMENT", + "ARMENIAN", + "ARMI", + "ARMN", + "ARROWS", + "ASCII", + "ASCIIHEXDIGIT", + "ASSIGNED", + "AT", + "ATA", + "ATAR", + "ATB", + "ATBL", + "ATERM", + "ATTACHEDABOVE", + "ATTACHEDABOVERIGHT", + "ATTACHEDBELOW", + "ATTACHEDBELOWLEFT", + "AVAGRAHA", + "AVESTAN", + "AVST", + "B", + "B2", + "BA", + "BALI", + "BALINESE", + "BAMU", + "BAMUM", + "BAMUMSUP", + "BAMUMSUPPLEMENT", + "BASICLATIN", + "BASS", + "BASSAVAH", + "BATAK", + "BATK", + "BB", + "BC", + "BEH", + "BELOW", + "BELOWLEFT", + "BELOWRIGHT", + "BENG", + "BENGALI", + "BETH", + "BIDIC", + "BIDICLASS", + "BIDICONTROL", + "BIDIM", + "BIDIMIRRORED", + "BINDU", + "BK", + "BL", + "BLANK", + "BLK", + "BLOCK", + "BLOCKELEMENTS", + "BN", + "BOPO", + "BOPOMOFO", + "BOPOMOFOEXT", + "BOPOMOFOEXTENDED", + "BOTTOM", + "BOTTOMANDRIGHT", + "BOUNDARYNEUTRAL", + "BOXDRAWING", + "BR", + "BRAH", + "BRAHMI", + "BRAHMIJOININGNUMBER", + "BRAI", + "BRAILLE", + "BRAILLEPATTERNS", + "BREAKAFTER", + "BREAKBEFORE", + "BREAKBOTH", + "BREAKSYMBOLS", + "BUGI", + "BUGINESE", + "BUHD", + "BUHID", + "BURUSHASKIYEHBARREE", + "BYZANTINEMUSIC", + "BYZANTINEMUSICALSYMBOLS", + "C", + "C&", + "CAKM", + "CAN", + "CANADIANABORIGINAL", + "CANADIANSYLLABICS", + "CANONICAL", + "CANONICALCOMBININGCLASS", + "CANS", + "CANTILLATIONMARK", + "CARI", + "CARIAN", + "CARRIAGERETURN", + "CASED", + "CASEDLETTER", + "CASEIGNORABLE", + "CAUCASIANALBANIAN", + "CB", + "CC", + "CCC", + "CCC10", + "CCC103", + "CCC107", + "CCC11", + "CCC118", + "CCC12", + "CCC122", + "CCC129", + "CCC13", + "CCC130", + "CCC132", + "CCC133", + "CCC14", + "CCC15", + "CCC16", + "CCC17", + "CCC18", + "CCC19", + "CCC20", + "CCC21", + "CCC22", + "CCC23", + "CCC24", + "CCC25", + "CCC26", + "CCC27", + "CCC28", + "CCC29", + "CCC30", + "CCC31", + "CCC32", + "CCC33", + "CCC34", + "CCC35", + "CCC36", + "CCC84", + "CCC91", + "CF", + "CHAKMA", + "CHAM", + "CHANGESWHENCASEFOLDED", + "CHANGESWHENCASEMAPPED", + "CHANGESWHENLOWERCASED", + "CHANGESWHENTITLECASED", + "CHANGESWHENUPPERCASED", + "CHER", + "CHEROKEE", + "CHEROKEESUP", + "CHEROKEESUPPLEMENT", + "CI", + "CIRCLE", + "CJ", + "CJK", + "CJKCOMPAT", + "CJKCOMPATFORMS", + "CJKCOMPATIBILITY", + "CJKCOMPATIBILITYFORMS", + "CJKCOMPATIBILITYIDEOGRAPHS", + "CJKCOMPATIBILITYIDEOGRAPHSSUPPLEMENT", + "CJKCOMPATIDEOGRAPHS", + "CJKCOMPATIDEOGRAPHSSUP", + "CJKEXTA", + "CJKEXTB", + "CJKEXTC", + "CJKEXTD", + "CJKEXTE", + "CJKRADICALSSUP", + "CJKRADICALSSUPPLEMENT", + "CJKSTROKES", + "CJKSYMBOLS", + "CJKSYMBOLSANDPUNCTUATION", + "CJKUNIFIEDIDEOGRAPHS", + "CJKUNIFIEDIDEOGRAPHSEXTENSIONA", + "CJKUNIFIEDIDEOGRAPHSEXTENSIONB", + "CJKUNIFIEDIDEOGRAPHSEXTENSIONC", + "CJKUNIFIEDIDEOGRAPHSEXTENSIOND", + "CJKUNIFIEDIDEOGRAPHSEXTENSIONE", + "CL", + "CLOSE", + "CLOSEPARENTHESIS", + "CLOSEPUNCTUATION", + "CM", + "CN", + "CNTRL", + "CO", + "COM", + "COMBININGDIACRITICALMARKS", + "COMBININGDIACRITICALMARKSEXTENDED", + "COMBININGDIACRITICALMARKSFORSYMBOLS", + "COMBININGDIACRITICALMARKSSUPPLEMENT", + "COMBININGHALFMARKS", + "COMBININGMARK", + "COMBININGMARKSFORSYMBOLS", + "COMMON", + "COMMONINDICNUMBERFORMS", + "COMMONSEPARATOR", + "COMPAT", + "COMPATJAMO", + "COMPLEXCONTEXT", + "CONDITIONALJAPANESESTARTER", + "CONNECTORPUNCTUATION", + "CONSONANT", + "CONSONANTDEAD", + "CONSONANTFINAL", + "CONSONANTHEADLETTER", + "CONSONANTKILLER", + "CONSONANTMEDIAL", + "CONSONANTPLACEHOLDER", + "CONSONANTPRECEDINGREPHA", + "CONSONANTPREFIXED", + "CONSONANTSUBJOINED", + "CONSONANTSUCCEEDINGREPHA", + "CONSONANTWITHSTACKER", + "CONTINGENTBREAK", + "CONTROL", + "CONTROLPICTURES", + "COPT", + "COPTIC", + "COPTICEPACTNUMBERS", + "COUNTINGROD", + "COUNTINGRODNUMERALS", + "CP", + "CPRT", + "CR", + "CS", + "CUNEIFORM", + "CUNEIFORMNUMBERS", + "CUNEIFORMNUMBERSANDPUNCTUATION", + "CURRENCYSYMBOL", + "CURRENCYSYMBOLS", + "CWCF", + "CWCM", + "CWL", + "CWT", + "CWU", + "CYPRIOT", + "CYPRIOTSYLLABARY", + "CYRILLIC", + "CYRILLICEXTA", + "CYRILLICEXTB", + "CYRILLICEXTENDEDA", + "CYRILLICEXTENDEDB", + "CYRILLICSUP", + "CYRILLICSUPPLEMENT", + "CYRILLICSUPPLEMENTARY", + "CYRL", + "D", + "DA", + "DAL", + "DALATHRISH", + "DASH", + "DASHPUNCTUATION", + "DB", + "DE", + "DECIMAL", + "DECIMALNUMBER", + "DECOMPOSITIONTYPE", + "DEFAULTIGNORABLECODEPOINT", + "DEP", + "DEPRECATED", + "DESERET", + "DEVA", + "DEVANAGARI", + "DEVANAGARIEXT", + "DEVANAGARIEXTENDED", + "DI", + "DIA", + "DIACRITIC", + "DIACRITICALS", + "DIACRITICALSEXT", + "DIACRITICALSFORSYMBOLS", + "DIACRITICALSSUP", + "DIGIT", + "DINGBATS", + "DOMINO", + "DOMINOTILES", + "DOUBLEABOVE", + "DOUBLEBELOW", + "DOUBLEQUOTE", + "DQ", + "DSRT", + "DT", + "DUALJOINING", + "DUPL", + "DUPLOYAN", + "E", + "EA", + "EARLYDYNASTICCUNEIFORM", + "EASTASIANWIDTH", + "EGYP", + "EGYPTIANHIEROGLYPHS", + "ELBA", + "ELBASAN", + "EMOTICONS", + "EN", + "ENC", + "ENCLOSEDALPHANUM", + "ENCLOSEDALPHANUMERICS", + "ENCLOSEDALPHANUMERICSUPPLEMENT", + "ENCLOSEDALPHANUMSUP", + "ENCLOSEDCJK", + "ENCLOSEDCJKLETTERSANDMONTHS", + "ENCLOSEDIDEOGRAPHICSUP", + "ENCLOSEDIDEOGRAPHICSUPPLEMENT", + "ENCLOSINGMARK", + "ES", + "ET", + "ETHI", + "ETHIOPIC", + "ETHIOPICEXT", + "ETHIOPICEXTA", + "ETHIOPICEXTENDED", + "ETHIOPICEXTENDEDA", + "ETHIOPICSUP", + "ETHIOPICSUPPLEMENT", + "EUROPEANNUMBER", + "EUROPEANSEPARATOR", + "EUROPEANTERMINATOR", + "EX", + "EXCLAMATION", + "EXT", + "EXTEND", + "EXTENDER", + "EXTENDNUMLET", + "F", + "FALSE", + "FARSIYEH", + "FE", + "FEH", + "FIN", + "FINAL", + "FINALPUNCTUATION", + "FINALSEMKATH", + "FIRSTSTRONGISOLATE", + "FO", + "FONT", + "FORMAT", + "FRA", + "FRACTION", + "FSI", + "FULLWIDTH", + "GAF", + "GAMAL", + "GC", + "GCB", + "GEMINATIONMARK", + "GENERALCATEGORY", + "GENERALPUNCTUATION", + "GEOMETRICSHAPES", + "GEOMETRICSHAPESEXT", + "GEOMETRICSHAPESEXTENDED", + "GEOR", + "GEORGIAN", + "GEORGIANSUP", + "GEORGIANSUPPLEMENT", + "GL", + "GLAG", + "GLAGOLITIC", + "GLUE", + "GOTH", + "GOTHIC", + "GRAN", + "GRANTHA", + "GRAPH", + "GRAPHEMEBASE", + "GRAPHEMECLUSTERBREAK", + "GRAPHEMEEXTEND", + "GRAPHEMELINK", + "GRBASE", + "GREEK", + "GREEKANDCOPTIC", + "GREEKEXT", + "GREEKEXTENDED", + "GREK", + "GREXT", + "GRLINK", + "GUJARATI", + "GUJR", + "GURMUKHI", + "GURU", + "H", + "H2", + "H3", + "HAH", + "HALFANDFULLFORMS", + "HALFMARKS", + "HALFWIDTH", + "HALFWIDTHANDFULLWIDTHFORMS", + "HAMZAONHEHGOAL", + "HAN", + "HANG", + "HANGUL", + "HANGULCOMPATIBILITYJAMO", + "HANGULJAMO", + "HANGULJAMOEXTENDEDA", + "HANGULJAMOEXTENDEDB", + "HANGULSYLLABLES", + "HANGULSYLLABLETYPE", + "HANI", + "HANO", + "HANUNOO", + "HATR", + "HATRAN", + "HE", + "HEBR", + "HEBREW", + "HEBREWLETTER", + "HEH", + "HEHGOAL", + "HETH", + "HEX", + "HEXDIGIT", + "HIGHPRIVATEUSESURROGATES", + "HIGHPUSURROGATES", + "HIGHSURROGATES", + "HIRA", + "HIRAGANA", + "HL", + "HLUW", + "HMNG", + "HRKT", + "HST", + "HUNG", + "HY", + "HYPHEN", + "ID", + "IDC", + "IDCONTINUE", + "IDEO", + "IDEOGRAPHIC", + "IDEOGRAPHICDESCRIPTIONCHARACTERS", + "IDS", + "IDSB", + "IDSBINARYOPERATOR", + "IDST", + "IDSTART", + "IDSTRINARYOPERATOR", + "IMPERIALARAMAIC", + "IN", + "INDICNUMBERFORMS", + "INDICPOSITIONALCATEGORY", + "INDICSYLLABICCATEGORY", + "INFIXNUMERIC", + "INHERITED", + "INIT", + "INITIAL", + "INITIALPUNCTUATION", + "INPC", + "INSC", + "INSCRIPTIONALPAHLAVI", + "INSCRIPTIONALPARTHIAN", + "INSEPARABLE", + "INSEPERABLE", + "INVISIBLESTACKER", + "IOTASUBSCRIPT", + "IPAEXT", + "IPAEXTENSIONS", + "IS", + "ISO", + "ISOLATED", + "ITAL", + "JAMO", + "JAMOEXTA", + "JAMOEXTB", + "JAVA", + "JAVANESE", + "JG", + "JL", + "JOINC", + "JOINCAUSING", + "JOINCONTROL", + "JOINER", + "JOININGGROUP", + "JOININGTYPE", + "JT", + "JV", + "KA", + "KAF", + "KAITHI", + "KALI", + "KANA", + "KANASUP", + "KANASUPPLEMENT", + "KANAVOICING", + "KANBUN", + "KANGXI", + "KANGXIRADICALS", + "KANNADA", + "KAPH", + "KATAKANA", + "KATAKANAEXT", + "KATAKANAORHIRAGANA", + "KATAKANAPHONETICEXTENSIONS", + "KAYAHLI", + "KHAPH", + "KHAR", + "KHAROSHTHI", + "KHMER", + "KHMERSYMBOLS", + "KHMR", + "KHOJ", + "KHOJKI", + "KHUDAWADI", + "KNDA", + "KNOTTEDHEH", + "KTHI", + "KV", + "L", + "L&", + "LAM", + "LAMADH", + "LANA", + "LAO", + "LAOO", + "LATIN", + "LATIN1", + "LATIN1SUP", + "LATIN1SUPPLEMENT", + "LATINEXTA", + "LATINEXTADDITIONAL", + "LATINEXTB", + "LATINEXTC", + "LATINEXTD", + "LATINEXTE", + "LATINEXTENDEDA", + "LATINEXTENDEDADDITIONAL", + "LATINEXTENDEDB", + "LATINEXTENDEDC", + "LATINEXTENDEDD", + "LATINEXTENDEDE", + "LATN", + "LB", + "LC", + "LE", + "LEADINGJAMO", + "LEFT", + "LEFTANDRIGHT", + "LEFTJOINING", + "LEFTTORIGHT", + "LEFTTORIGHTEMBEDDING", + "LEFTTORIGHTISOLATE", + "LEFTTORIGHTOVERRIDE", + "LEPC", + "LEPCHA", + "LETTER", + "LETTERLIKESYMBOLS", + "LETTERNUMBER", + "LF", + "LIMB", + "LIMBU", + "LINA", + "LINB", + "LINEARA", + "LINEARB", + "LINEARBIDEOGRAMS", + "LINEARBSYLLABARY", + "LINEBREAK", + "LINEFEED", + "LINESEPARATOR", + "LISU", + "LL", + "LM", + "LO", + "LOE", + "LOGICALORDEREXCEPTION", + "LOWER", + "LOWERCASE", + "LOWERCASELETTER", + "LOWSURROGATES", + "LRE", + "LRI", + "LRO", + "LT", + "LU", + "LV", + "LVSYLLABLE", + "LVT", + "LVTSYLLABLE", + "LYCI", + "LYCIAN", + "LYDI", + "LYDIAN", + "M", + "M&", + "MAHAJANI", + "MAHJ", + "MAHJONG", + "MAHJONGTILES", + "MALAYALAM", + "MAND", + "MANDAIC", + "MANDATORYBREAK", + "MANI", + "MANICHAEAN", + "MANICHAEANALEPH", + "MANICHAEANAYIN", + "MANICHAEANBETH", + "MANICHAEANDALETH", + "MANICHAEANDHAMEDH", + "MANICHAEANFIVE", + "MANICHAEANGIMEL", + "MANICHAEANHETH", + "MANICHAEANHUNDRED", + "MANICHAEANKAPH", + "MANICHAEANLAMEDH", + "MANICHAEANMEM", + "MANICHAEANNUN", + "MANICHAEANONE", + "MANICHAEANPE", + "MANICHAEANQOPH", + "MANICHAEANRESH", + "MANICHAEANSADHE", + "MANICHAEANSAMEKH", + "MANICHAEANTAW", + "MANICHAEANTEN", + "MANICHAEANTETH", + "MANICHAEANTHAMEDH", + "MANICHAEANTWENTY", + "MANICHAEANWAW", + "MANICHAEANYODH", + "MANICHAEANZAYIN", + "MARK", + "MATH", + "MATHALPHANUM", + "MATHEMATICALALPHANUMERICSYMBOLS", + "MATHEMATICALOPERATORS", + "MATHOPERATORS", + "MATHSYMBOL", + "MB", + "MC", + "ME", + "MED", + "MEDIAL", + "MEEM", + "MEETEIMAYEK", + "MEETEIMAYEKEXT", + "MEETEIMAYEKEXTENSIONS", + "MEND", + "MENDEKIKAKUI", + "MERC", + "MERO", + "MEROITICCURSIVE", + "MEROITICHIEROGLYPHS", + "MIAO", + "MIDLETTER", + "MIDNUM", + "MIDNUMLET", + "MIM", + "MISCARROWS", + "MISCELLANEOUSMATHEMATICALSYMBOLSA", + "MISCELLANEOUSMATHEMATICALSYMBOLSB", + "MISCELLANEOUSSYMBOLS", + "MISCELLANEOUSSYMBOLSANDARROWS", + "MISCELLANEOUSSYMBOLSANDPICTOGRAPHS", + "MISCELLANEOUSTECHNICAL", + "MISCMATHSYMBOLSA", + "MISCMATHSYMBOLSB", + "MISCPICTOGRAPHS", + "MISCSYMBOLS", + "MISCTECHNICAL", + "ML", + "MLYM", + "MN", + "MODI", + "MODIFIERLETTER", + "MODIFIERLETTERS", + "MODIFIERSYMBOL", + "MODIFIERTONELETTERS", + "MODIFYINGLETTER", + "MONG", + "MONGOLIAN", + "MRO", + "MROO", + "MTEI", + "MULT", + "MULTANI", + "MUSIC", + "MUSICALSYMBOLS", + "MYANMAR", + "MYANMAREXTA", + "MYANMAREXTB", + "MYANMAREXTENDEDA", + "MYANMAREXTENDEDB", + "MYMR", + "N", + "N&", + "NA", + "NABATAEAN", + "NAN", + "NAR", + "NARB", + "NARROW", + "NB", + "NBAT", + "NCHAR", + "ND", + "NEUTRAL", + "NEWLINE", + "NEWTAILUE", + "NEXTLINE", + "NK", + "NKO", + "NKOO", + "NL", + "NO", + "NOBLOCK", + "NOBREAK", + "NOJOININGGROUP", + "NONCHARACTERCODEPOINT", + "NONE", + "NONJOINER", + "NONJOINING", + "NONSPACINGMARK", + "NONSTARTER", + "NOON", + "NOTAPPLICABLE", + "NOTREORDERED", + "NR", + "NS", + "NSM", + "NT", + "NU", + "NUKTA", + "NUMBER", + "NUMBERFORMS", + "NUMBERJOINER", + "NUMERIC", + "NUMERICTYPE", + "NUMERICVALUE", + "NUN", + "NV", + "NYA", + "OALPHA", + "OCR", + "ODI", + "OGAM", + "OGHAM", + "OGREXT", + "OIDC", + "OIDS", + "OLCHIKI", + "OLCK", + "OLDHUNGARIAN", + "OLDITALIC", + "OLDNORTHARABIAN", + "OLDPERMIC", + "OLDPERSIAN", + "OLDSOUTHARABIAN", + "OLDTURKIC", + "OLETTER", + "OLOWER", + "OMATH", + "ON", + "OP", + "OPENPUNCTUATION", + "OPTICALCHARACTERRECOGNITION", + "ORIYA", + "ORKH", + "ORNAMENTALDINGBATS", + "ORYA", + "OSMA", + "OSMANYA", + "OTHER", + "OTHERALPHABETIC", + "OTHERDEFAULTIGNORABLECODEPOINT", + "OTHERGRAPHEMEEXTEND", + "OTHERIDCONTINUE", + "OTHERIDSTART", + "OTHERLETTER", + "OTHERLOWERCASE", + "OTHERMATH", + "OTHERNEUTRAL", + "OTHERNUMBER", + "OTHERPUNCTUATION", + "OTHERSYMBOL", + "OTHERUPPERCASE", + "OUPPER", + "OV", + "OVERLAY", + "OVERSTRUCK", + "P", + "P&", + "PAHAWHHMONG", + "PALM", + "PALMYRENE", + "PARAGRAPHSEPARATOR", + "PATSYN", + "PATTERNSYNTAX", + "PATTERNWHITESPACE", + "PATWS", + "PAUC", + "PAUCINHAU", + "PC", + "PD", + "PDF", + "PDI", + "PE", + "PERM", + "PF", + "PHAG", + "PHAGSPA", + "PHAISTOS", + "PHAISTOSDISC", + "PHLI", + "PHLP", + "PHNX", + "PHOENICIAN", + "PHONETICEXT", + "PHONETICEXTENSIONS", + "PHONETICEXTENSIONSSUPPLEMENT", + "PHONETICEXTSUP", + "PI", + "PLAYINGCARDS", + "PLRD", + "PO", + "POPDIRECTIONALFORMAT", + "POPDIRECTIONALISOLATE", + "POSIXALNUM", + "POSIXDIGIT", + "POSIXPUNCT", + "POSIXXDIGIT", + "POSTFIXNUMERIC", + "PP", + "PR", + "PREFIXNUMERIC", + "PREPEND", + "PRINT", + "PRIVATEUSE", + "PRIVATEUSEAREA", + "PRTI", + "PS", + "PSALTERPAHLAVI", + "PUA", + "PUNCT", + "PUNCTUATION", + "PUREKILLER", + "QAAC", + "QAAI", + "QAF", + "QAPH", + "QMARK", + "QU", + "QUOTATION", + "QUOTATIONMARK", + "R", + "RADICAL", + "REGIONALINDICATOR", + "REGISTERSHIFTER", + "REH", + "REJANG", + "REVERSEDPE", + "RI", + "RIGHT", + "RIGHTJOINING", + "RIGHTTOLEFT", + "RIGHTTOLEFTEMBEDDING", + "RIGHTTOLEFTISOLATE", + "RIGHTTOLEFTOVERRIDE", + "RJNG", + "RLE", + "RLI", + "RLO", + "ROHINGYAYEH", + "RUMI", + "RUMINUMERALSYMBOLS", + "RUNIC", + "RUNR", + "S", + "S&", + "SA", + "SAD", + "SADHE", + "SAMARITAN", + "SAMR", + "SARB", + "SAUR", + "SAURASHTRA", + "SB", + "SC", + "SCONTINUE", + "SCRIPT", + "SD", + "SE", + "SEEN", + "SEGMENTSEPARATOR", + "SEMKATH", + "SENTENCEBREAK", + "SEP", + "SEPARATOR", + "SG", + "SGNW", + "SHARADA", + "SHAVIAN", + "SHAW", + "SHIN", + "SHORTHANDFORMATCONTROLS", + "SHRD", + "SIDD", + "SIDDHAM", + "SIGNWRITING", + "SIND", + "SINGLEQUOTE", + "SINH", + "SINHALA", + "SINHALAARCHAICNUMBERS", + "SK", + "SM", + "SMALL", + "SMALLFORMS", + "SMALLFORMVARIANTS", + "SML", + "SO", + "SOFTDOTTED", + "SORA", + "SORASOMPENG", + "SP", + "SPACE", + "SPACESEPARATOR", + "SPACINGMARK", + "SPACINGMODIFIERLETTERS", + "SPECIALS", + "SQ", + "SQR", + "SQUARE", + "ST", + "STERM", + "STRAIGHTWAW", + "SUB", + "SUND", + "SUNDANESE", + "SUNDANESESUP", + "SUNDANESESUPPLEMENT", + "SUP", + "SUPARROWSA", + "SUPARROWSB", + "SUPARROWSC", + "SUPER", + "SUPERANDSUB", + "SUPERSCRIPTSANDSUBSCRIPTS", + "SUPMATHOPERATORS", + "SUPPLEMENTALARROWSA", + "SUPPLEMENTALARROWSB", + "SUPPLEMENTALARROWSC", + "SUPPLEMENTALMATHEMATICALOPERATORS", + "SUPPLEMENTALPUNCTUATION", + "SUPPLEMENTALSYMBOLSANDPICTOGRAPHS", + "SUPPLEMENTARYPRIVATEUSEAREAA", + "SUPPLEMENTARYPRIVATEUSEAREAB", + "SUPPUAA", + "SUPPUAB", + "SUPPUNCTUATION", + "SUPSYMBOLSANDPICTOGRAPHS", + "SURROGATE", + "SUTTONSIGNWRITING", + "SWASHKAF", + "SY", + "SYLLABLEMODIFIER", + "SYLO", + "SYLOTINAGRI", + "SYMBOL", + "SYRC", + "SYRIAC", + "SYRIACWAW", + "T", + "TAGALOG", + "TAGB", + "TAGBANWA", + "TAGS", + "TAH", + "TAILE", + "TAITHAM", + "TAIVIET", + "TAIXUANJING", + "TAIXUANJINGSYMBOLS", + "TAKR", + "TAKRI", + "TALE", + "TALU", + "TAMIL", + "TAML", + "TAVT", + "TAW", + "TEHMARBUTA", + "TEHMARBUTAGOAL", + "TELU", + "TELUGU", + "TERM", + "TERMINALPUNCTUATION", + "TETH", + "TFNG", + "TGLG", + "THAA", + "THAANA", + "THAI", + "TIBETAN", + "TIBT", + "TIFINAGH", + "TIRH", + "TIRHUTA", + "TITLECASELETTER", + "TONELETTER", + "TONEMARK", + "TOP", + "TOPANDBOTTOM", + "TOPANDBOTTOMANDRIGHT", + "TOPANDLEFT", + "TOPANDLEFTANDRIGHT", + "TOPANDRIGHT", + "TRAILINGJAMO", + "TRANSPARENT", + "TRANSPORTANDMAP", + "TRANSPORTANDMAPSYMBOLS", + "TRUE", + "U", + "UCAS", + "UCASEXT", + "UGAR", + "UGARITIC", + "UIDEO", + "UNASSIGNED", + "UNIFIEDCANADIANABORIGINALSYLLABICS", + "UNIFIEDCANADIANABORIGINALSYLLABICSEXTENDED", + "UNIFIEDIDEOGRAPH", + "UNKNOWN", + "UP", + "UPPER", + "UPPERCASE", + "UPPERCASELETTER", + "V", + "VAI", + "VAII", + "VARIATIONSELECTOR", + "VARIATIONSELECTORS", + "VARIATIONSELECTORSSUPPLEMENT", + "VEDICEXT", + "VEDICEXTENSIONS", + "VERT", + "VERTICAL", + "VERTICALFORMS", + "VIRAMA", + "VISARGA", + "VISUALORDERLEFT", + "VOWEL", + "VOWELDEPENDENT", + "VOWELINDEPENDENT", + "VOWELJAMO", + "VR", + "VS", + "VSSUP", + "W", + "WARA", + "WARANGCITI", + "WAW", + "WB", + "WHITESPACE", + "WIDE", + "WJ", + "WORD", + "WORDBREAK", + "WORDJOINER", + "WS", + "WSPACE", + "XDIGIT", + "XIDC", + "XIDCONTINUE", + "XIDS", + "XIDSTART", + "XPEO", + "XSUX", + "XX", + "Y", + "YEH", + "YEHBARREE", + "YEHWITHTAIL", + "YES", + "YI", + "YIII", + "YIJING", + "YIJINGHEXAGRAMSYMBOLS", + "YIRADICALS", + "YISYLLABLES", + "YUDH", + "YUDHHE", + "Z", + "Z&", + "ZAIN", + "ZHAIN", + "ZINH", + "ZL", + "ZP", + "ZS", + "ZW", + "ZWSPACE", + "ZYYY", + "ZZZZ", +}; + +/* strings: 12240 bytes. */ + +/* properties. */ + +RE_Property re_properties[] = { + { 547, 0, 0}, + { 544, 0, 0}, + { 252, 1, 1}, + { 251, 1, 1}, + {1081, 2, 2}, + {1079, 2, 2}, + {1259, 3, 3}, + {1254, 3, 3}, + { 566, 4, 4}, + { 545, 4, 4}, + {1087, 5, 5}, + {1078, 5, 5}, + { 823, 6, 6}, + { 172, 7, 6}, + { 171, 7, 6}, + { 767, 8, 6}, + { 766, 8, 6}, + {1227, 9, 6}, + {1226, 9, 6}, + { 294, 10, 6}, + { 296, 11, 6}, + { 350, 11, 6}, + { 343, 12, 6}, + { 433, 12, 6}, + { 345, 13, 6}, + { 435, 13, 6}, + { 344, 14, 6}, + { 434, 14, 6}, + { 341, 15, 6}, + { 431, 15, 6}, + { 342, 16, 6}, + { 432, 16, 6}, + { 636, 17, 6}, + { 632, 17, 6}, + { 628, 18, 6}, + { 627, 18, 6}, + {1267, 19, 6}, + {1266, 19, 6}, + {1265, 20, 6}, + {1264, 20, 6}, + { 458, 21, 6}, + { 466, 21, 6}, + { 567, 22, 6}, + { 575, 22, 6}, + { 565, 23, 6}, + { 569, 23, 6}, + { 568, 24, 6}, + { 576, 24, 6}, + {1255, 25, 6}, + {1262, 25, 6}, + {1117, 25, 6}, + { 244, 26, 6}, + { 242, 26, 6}, + { 671, 27, 6}, + { 669, 27, 6}, + { 451, 28, 6}, + { 625, 29, 6}, + {1044, 30, 6}, + {1041, 30, 6}, + {1188, 31, 6}, + {1187, 31, 6}, + { 971, 32, 6}, + { 952, 32, 6}, + { 612, 33, 6}, + { 611, 33, 6}, + { 204, 34, 6}, + { 160, 34, 6}, + { 964, 35, 6}, + { 933, 35, 6}, + { 630, 36, 6}, + { 629, 36, 6}, + { 468, 37, 6}, + { 467, 37, 6}, + { 523, 38, 6}, + { 521, 38, 6}, + { 970, 39, 6}, + { 951, 39, 6}, + { 976, 40, 6}, + { 977, 40, 6}, + { 909, 41, 6}, + { 895, 41, 6}, + { 966, 42, 6}, + { 938, 42, 6}, + { 634, 43, 6}, + { 633, 43, 6}, + { 637, 44, 6}, + { 635, 44, 6}, + {1046, 45, 6}, + {1223, 46, 6}, + {1219, 46, 6}, + { 965, 47, 6}, + { 935, 47, 6}, + { 460, 48, 6}, + { 459, 48, 6}, + {1113, 49, 6}, + {1082, 49, 6}, + { 765, 50, 6}, + { 764, 50, 6}, + { 968, 51, 6}, + { 940, 51, 6}, + { 967, 52, 6}, + { 939, 52, 6}, + {1126, 53, 6}, + {1232, 54, 6}, + {1248, 54, 6}, + { 989, 55, 6}, + { 990, 55, 6}, + { 988, 56, 6}, + { 987, 56, 6}, + { 598, 57, 7}, + { 622, 57, 7}, + { 243, 58, 8}, + { 234, 58, 8}, + { 288, 59, 9}, + { 300, 59, 9}, + { 457, 60, 10}, + { 482, 60, 10}, + { 489, 61, 11}, + { 487, 61, 11}, + { 673, 62, 12}, + { 667, 62, 12}, + { 674, 63, 13}, + { 675, 63, 13}, + { 757, 64, 14}, + { 732, 64, 14}, + { 928, 65, 15}, + { 921, 65, 15}, + { 929, 66, 16}, + { 931, 66, 16}, + { 246, 67, 6}, + { 245, 67, 6}, + { 641, 68, 17}, + { 648, 68, 17}, + { 642, 69, 18}, + { 649, 69, 18}, + { 175, 70, 6}, + { 170, 70, 6}, + { 183, 71, 6}, + { 250, 72, 6}, + { 564, 73, 6}, + {1027, 74, 6}, + {1258, 75, 6}, + {1263, 76, 6}, + {1019, 77, 6}, + {1018, 78, 6}, + {1020, 79, 6}, + {1021, 80, 6}, +}; + +/* properties: 588 bytes. */ + +/* property values. */ + +RE_PropertyValue re_property_values[] = { + {1220, 0, 0}, + { 383, 0, 0}, + {1228, 0, 1}, + { 774, 0, 1}, + { 768, 0, 2}, + { 761, 0, 2}, + {1200, 0, 3}, + { 773, 0, 3}, + { 865, 0, 4}, + { 762, 0, 4}, + { 969, 0, 5}, + { 763, 0, 5}, + { 913, 0, 6}, + { 863, 0, 6}, + { 505, 0, 7}, + { 831, 0, 7}, + {1119, 0, 8}, + { 830, 0, 8}, + { 456, 0, 9}, + { 896, 0, 9}, + { 473, 0, 9}, + { 747, 0, 10}, + { 904, 0, 10}, + { 973, 0, 11}, + { 905, 0, 11}, + {1118, 0, 12}, + {1291, 0, 12}, + { 759, 0, 13}, + {1289, 0, 13}, + { 986, 0, 14}, + {1290, 0, 14}, + { 415, 0, 15}, + { 299, 0, 15}, + { 384, 0, 15}, + { 537, 0, 16}, + { 338, 0, 16}, + {1028, 0, 17}, + { 385, 0, 17}, + {1153, 0, 18}, + { 425, 0, 18}, + { 452, 0, 19}, + { 994, 0, 19}, + { 955, 0, 20}, + {1031, 0, 20}, + { 381, 0, 21}, + { 997, 0, 21}, + { 401, 0, 22}, + { 993, 0, 22}, + { 974, 0, 23}, + {1015, 0, 23}, + { 828, 0, 24}, + {1107, 0, 24}, + { 429, 0, 25}, + {1079, 0, 25}, + { 867, 0, 26}, + {1106, 0, 26}, + { 975, 0, 27}, + {1112, 0, 27}, + { 647, 0, 28}, + {1012, 0, 28}, + { 532, 0, 29}, + { 999, 0, 29}, + { 963, 0, 30}, + { 281, 0, 30}, + { 282, 0, 30}, + { 745, 0, 31}, + { 708, 0, 31}, + { 709, 0, 31}, + { 822, 0, 32}, + { 783, 0, 32}, + { 392, 0, 32}, + { 784, 0, 32}, + { 924, 0, 33}, + { 885, 0, 33}, + { 886, 0, 33}, + {1035, 0, 34}, + { 981, 0, 34}, + {1034, 0, 34}, + { 982, 0, 34}, + {1160, 0, 35}, + {1068, 0, 35}, + {1069, 0, 35}, + {1089, 0, 36}, + {1284, 0, 36}, + {1285, 0, 36}, + { 295, 0, 37}, + { 733, 0, 37}, + { 205, 0, 38}, + { 906, 1, 0}, + { 893, 1, 0}, + { 228, 1, 1}, + { 203, 1, 1}, + { 718, 1, 2}, + { 717, 1, 2}, + { 716, 1, 2}, + { 725, 1, 3}, + { 719, 1, 3}, + { 727, 1, 4}, + { 721, 1, 4}, + { 657, 1, 5}, + { 656, 1, 5}, + {1120, 1, 6}, + { 866, 1, 6}, + { 387, 1, 7}, + { 469, 1, 7}, + { 571, 1, 8}, + { 570, 1, 8}, + { 438, 1, 9}, + { 444, 1, 10}, + { 443, 1, 10}, + { 445, 1, 10}, + { 199, 1, 11}, + { 606, 1, 12}, + { 186, 1, 13}, + {1162, 1, 14}, + { 198, 1, 15}, + { 197, 1, 15}, + {1193, 1, 16}, + { 902, 1, 17}, + {1073, 1, 18}, + { 791, 1, 19}, + { 188, 1, 20}, + { 187, 1, 20}, + { 463, 1, 21}, + { 240, 1, 22}, + { 579, 1, 23}, + { 577, 1, 24}, + { 957, 1, 25}, + {1179, 1, 26}, + {1186, 1, 27}, + { 688, 1, 28}, + { 789, 1, 29}, + {1104, 1, 30}, + {1194, 1, 31}, + { 713, 1, 32}, + {1195, 1, 33}, + { 879, 1, 34}, + { 553, 1, 35}, + { 594, 1, 36}, + { 662, 1, 36}, + { 509, 1, 37}, + { 515, 1, 38}, + { 514, 1, 38}, + { 347, 1, 39}, + {1221, 1, 40}, + {1215, 1, 40}, + { 286, 1, 40}, + { 937, 1, 41}, + {1066, 1, 42}, + {1165, 1, 43}, + { 601, 1, 44}, + { 277, 1, 45}, + {1167, 1, 46}, + { 698, 1, 47}, + { 871, 1, 48}, + {1222, 1, 49}, + {1216, 1, 49}, + { 750, 1, 50}, + {1170, 1, 51}, + { 899, 1, 52}, + { 699, 1, 53}, + { 275, 1, 54}, + {1171, 1, 55}, + { 388, 1, 56}, + { 470, 1, 56}, + { 223, 1, 57}, + {1130, 1, 58}, + { 231, 1, 59}, + { 744, 1, 60}, + { 941, 1, 61}, + {1132, 1, 62}, + {1131, 1, 62}, + {1236, 1, 63}, + {1235, 1, 63}, + {1009, 1, 64}, + {1008, 1, 64}, + {1010, 1, 65}, + {1011, 1, 65}, + { 390, 1, 66}, + { 472, 1, 66}, + { 726, 1, 67}, + { 720, 1, 67}, + { 573, 1, 68}, + { 572, 1, 68}, + { 548, 1, 69}, + {1035, 1, 69}, + {1139, 1, 70}, + {1138, 1, 70}, + { 430, 1, 71}, + { 389, 1, 72}, + { 471, 1, 72}, + { 393, 1, 72}, + { 746, 1, 73}, + { 925, 1, 74}, + { 202, 1, 75}, + { 826, 1, 76}, + { 827, 1, 76}, + { 855, 1, 77}, + { 860, 1, 77}, + { 416, 1, 78}, + { 956, 1, 79}, + { 934, 1, 79}, + { 498, 1, 80}, + { 497, 1, 80}, + { 262, 1, 81}, + { 253, 1, 82}, + { 549, 1, 83}, + { 852, 1, 84}, + { 859, 1, 84}, + { 474, 1, 85}, + { 850, 1, 86}, + { 856, 1, 86}, + {1141, 1, 87}, + {1134, 1, 87}, + { 269, 1, 88}, + { 268, 1, 88}, + {1142, 1, 89}, + {1135, 1, 89}, + { 851, 1, 90}, + { 857, 1, 90}, + {1144, 1, 91}, + {1140, 1, 91}, + { 853, 1, 92}, + { 849, 1, 92}, + { 558, 1, 93}, + { 728, 1, 94}, + { 722, 1, 94}, + { 418, 1, 95}, + { 555, 1, 96}, + { 554, 1, 96}, + {1197, 1, 97}, + { 512, 1, 98}, + { 510, 1, 98}, + { 441, 1, 99}, + { 439, 1, 99}, + {1145, 1, 100}, + {1151, 1, 100}, + { 368, 1, 101}, + { 367, 1, 101}, + { 687, 1, 102}, + { 686, 1, 102}, + { 631, 1, 103}, + { 627, 1, 103}, + { 371, 1, 104}, + { 370, 1, 104}, + { 617, 1, 105}, + { 690, 1, 106}, + { 256, 1, 107}, + { 593, 1, 108}, + { 398, 1, 108}, + { 685, 1, 109}, + { 258, 1, 110}, + { 257, 1, 110}, + { 369, 1, 111}, + { 693, 1, 112}, + { 691, 1, 112}, + { 502, 1, 113}, + { 501, 1, 113}, + { 356, 1, 114}, + { 354, 1, 114}, + { 373, 1, 115}, + { 362, 1, 115}, + {1279, 1, 116}, + {1278, 1, 116}, + { 372, 1, 117}, + { 353, 1, 117}, + {1281, 1, 118}, + {1280, 1, 119}, + { 760, 1, 120}, + {1230, 1, 121}, + { 442, 1, 122}, + { 440, 1, 122}, + { 225, 1, 123}, + { 868, 1, 124}, + { 729, 1, 125}, + { 723, 1, 125}, + {1159, 1, 126}, + { 395, 1, 127}, + { 640, 1, 127}, + {1001, 1, 128}, + {1077, 1, 129}, + { 465, 1, 130}, + { 464, 1, 130}, + { 694, 1, 131}, + {1050, 1, 132}, + { 595, 1, 133}, + { 663, 1, 133}, + { 666, 1, 134}, + { 883, 1, 135}, + { 881, 1, 135}, + { 340, 1, 136}, + { 882, 1, 137}, + { 880, 1, 137}, + {1172, 1, 138}, + { 837, 1, 139}, + { 836, 1, 139}, + { 513, 1, 140}, + { 511, 1, 140}, + { 730, 1, 141}, + { 724, 1, 141}, + { 349, 1, 142}, + { 348, 1, 142}, + { 835, 1, 143}, + { 597, 1, 144}, + { 592, 1, 144}, + { 596, 1, 145}, + { 664, 1, 145}, + { 615, 1, 146}, + { 613, 1, 147}, + { 614, 1, 147}, + { 769, 1, 148}, + {1029, 1, 149}, + {1033, 1, 149}, + {1028, 1, 149}, + { 358, 1, 150}, + { 360, 1, 150}, + { 174, 1, 151}, + { 173, 1, 151}, + { 195, 1, 152}, + { 193, 1, 152}, + {1233, 1, 153}, + {1248, 1, 153}, + {1239, 1, 154}, + { 391, 1, 155}, + { 586, 1, 155}, + { 357, 1, 156}, + { 355, 1, 156}, + {1110, 1, 157}, + {1109, 1, 157}, + { 196, 1, 158}, + { 194, 1, 158}, + { 588, 1, 159}, + { 585, 1, 159}, + {1121, 1, 160}, + { 756, 1, 161}, + { 755, 1, 162}, + { 158, 1, 163}, + { 181, 1, 164}, + { 182, 1, 165}, + {1003, 1, 166}, + {1002, 1, 166}, + { 780, 1, 167}, + { 292, 1, 168}, + { 419, 1, 169}, + { 944, 1, 170}, + { 561, 1, 171}, + { 946, 1, 172}, + {1218, 1, 173}, + { 947, 1, 174}, + { 461, 1, 175}, + {1093, 1, 176}, + { 962, 1, 177}, + { 493, 1, 178}, + { 297, 1, 179}, + { 753, 1, 180}, + { 437, 1, 181}, + { 638, 1, 182}, + { 985, 1, 183}, + { 888, 1, 184}, + { 603, 1, 185}, + {1007, 1, 186}, + { 782, 1, 187}, + { 843, 1, 188}, + { 842, 1, 189}, + { 697, 1, 190}, + { 948, 1, 191}, + { 945, 1, 192}, + { 794, 1, 193}, + { 217, 1, 194}, + { 651, 1, 195}, + { 650, 1, 196}, + {1032, 1, 197}, + { 949, 1, 198}, + { 943, 1, 199}, + {1065, 1, 200}, + {1064, 1, 200}, + { 265, 1, 201}, + { 679, 1, 202}, + {1115, 1, 203}, + { 339, 1, 204}, + { 785, 1, 205}, + {1092, 1, 206}, + {1105, 1, 207}, + { 702, 1, 208}, + { 876, 1, 209}, + { 703, 1, 210}, + { 563, 1, 211}, + {1199, 1, 212}, + {1099, 1, 213}, + { 864, 1, 214}, + {1176, 1, 215}, + { 161, 1, 216}, + {1252, 1, 217}, + { 992, 1, 218}, + { 426, 1, 219}, + { 428, 1, 220}, + { 427, 1, 220}, + { 488, 1, 221}, + { 491, 1, 222}, + { 178, 1, 223}, + { 227, 1, 224}, + { 226, 1, 224}, + { 872, 1, 225}, + { 230, 1, 226}, + { 983, 1, 227}, + { 844, 1, 228}, + { 683, 1, 229}, + { 682, 1, 229}, + { 485, 1, 230}, + {1096, 1, 231}, + { 280, 1, 232}, + { 279, 1, 232}, + { 878, 1, 233}, + { 877, 1, 233}, + { 180, 1, 234}, + { 179, 1, 234}, + {1174, 1, 235}, + {1173, 1, 235}, + { 421, 1, 236}, + { 420, 1, 236}, + { 825, 1, 237}, + { 824, 1, 237}, + {1154, 1, 238}, + { 839, 1, 239}, + { 191, 1, 240}, + { 190, 1, 240}, + { 788, 1, 241}, + { 787, 1, 241}, + { 476, 1, 242}, + { 475, 1, 242}, + {1013, 1, 243}, + { 499, 1, 244}, + { 500, 1, 244}, + { 504, 1, 245}, + { 503, 1, 245}, + { 854, 1, 246}, + { 858, 1, 246}, + { 494, 1, 247}, + { 959, 1, 248}, + {1212, 1, 249}, + {1211, 1, 249}, + { 167, 1, 250}, + { 166, 1, 250}, + { 551, 1, 251}, + { 550, 1, 251}, + {1143, 1, 252}, + {1136, 1, 252}, + {1146, 1, 253}, + {1152, 1, 253}, + { 374, 1, 254}, + { 363, 1, 254}, + { 375, 1, 255}, + { 364, 1, 255}, + { 376, 1, 256}, + { 365, 1, 256}, + { 377, 1, 257}, + { 366, 1, 257}, + { 359, 1, 258}, + { 361, 1, 258}, + {1168, 1, 259}, + {1234, 1, 260}, + {1249, 1, 260}, + {1147, 1, 261}, + {1149, 1, 261}, + {1148, 1, 262}, + {1150, 1, 262}, + {1224, 2, 0}, + {1295, 2, 0}, + { 394, 2, 1}, + {1294, 2, 1}, + { 715, 2, 2}, + { 731, 2, 2}, + { 570, 2, 3}, + { 574, 2, 3}, + { 438, 2, 4}, + { 446, 2, 4}, + { 199, 2, 5}, + { 201, 2, 5}, + { 606, 2, 6}, + { 605, 2, 6}, + { 186, 2, 7}, + { 185, 2, 7}, + {1162, 2, 8}, + {1161, 2, 8}, + {1193, 2, 9}, + {1192, 2, 9}, + { 463, 2, 10}, + { 462, 2, 10}, + { 240, 2, 11}, + { 239, 2, 11}, + { 579, 2, 12}, + { 580, 2, 12}, + { 577, 2, 13}, + { 578, 2, 13}, + { 957, 2, 14}, + { 960, 2, 14}, + {1179, 2, 15}, + {1180, 2, 15}, + {1186, 2, 16}, + {1185, 2, 16}, + { 688, 2, 17}, + { 704, 2, 17}, + { 789, 2, 18}, + { 862, 2, 18}, + {1104, 2, 19}, + {1103, 2, 19}, + {1194, 2, 20}, + { 713, 2, 21}, + { 714, 2, 21}, + {1195, 2, 22}, + {1196, 2, 22}, + { 879, 2, 23}, + { 884, 2, 23}, + { 553, 2, 24}, + { 552, 2, 24}, + { 592, 2, 25}, + { 591, 2, 25}, + { 509, 2, 26}, + { 508, 2, 26}, + { 347, 2, 27}, + { 346, 2, 27}, + { 285, 2, 28}, + { 289, 2, 28}, + { 937, 2, 29}, + { 936, 2, 29}, + {1066, 2, 30}, + {1067, 2, 30}, + { 698, 2, 31}, + { 700, 2, 31}, + { 871, 2, 32}, + { 870, 2, 32}, + { 617, 2, 33}, + { 616, 2, 33}, + { 690, 2, 34}, + { 681, 2, 34}, + { 256, 2, 35}, + { 255, 2, 35}, + { 590, 2, 36}, + { 599, 2, 36}, + {1276, 2, 37}, + {1277, 2, 37}, + { 944, 2, 38}, + { 661, 2, 38}, + { 561, 2, 39}, + { 560, 2, 39}, + { 461, 2, 40}, + { 481, 2, 40}, + { 644, 2, 41}, + {1288, 2, 41}, + {1038, 2, 41}, + {1165, 2, 42}, + {1191, 2, 42}, + { 601, 2, 43}, + { 600, 2, 43}, + { 277, 2, 44}, + { 276, 2, 44}, + {1167, 2, 45}, + {1166, 2, 45}, + { 750, 2, 46}, + { 749, 2, 46}, + {1170, 2, 47}, + {1177, 2, 47}, + { 754, 2, 48}, + { 752, 2, 48}, + {1218, 2, 49}, + {1217, 2, 49}, + {1093, 2, 50}, + {1094, 2, 50}, + { 962, 2, 51}, + { 961, 2, 51}, + { 436, 2, 52}, + { 423, 2, 52}, + { 268, 2, 53}, + { 267, 2, 53}, + { 275, 2, 54}, + { 274, 2, 54}, + { 418, 2, 55}, + { 417, 2, 55}, + {1037, 2, 55}, + { 899, 2, 56}, + {1178, 2, 56}, + { 558, 2, 57}, + { 557, 2, 57}, + {1197, 2, 58}, + {1190, 2, 58}, + {1159, 2, 59}, + {1158, 2, 59}, + { 947, 2, 60}, + {1268, 2, 60}, + { 697, 2, 61}, + { 696, 2, 61}, + { 223, 2, 62}, + { 222, 2, 62}, + { 426, 2, 63}, + {1269, 2, 63}, + {1007, 2, 64}, + {1006, 2, 64}, + {1001, 2, 65}, + {1000, 2, 65}, + { 902, 2, 66}, + { 903, 2, 66}, + {1130, 2, 67}, + {1129, 2, 67}, + { 744, 2, 68}, + { 743, 2, 68}, + { 941, 2, 69}, + { 942, 2, 69}, + {1230, 2, 70}, + {1231, 2, 70}, + {1077, 2, 71}, + {1076, 2, 71}, + { 694, 2, 72}, + { 680, 2, 72}, + {1050, 2, 73}, + {1059, 2, 73}, + { 780, 2, 74}, + { 779, 2, 74}, + { 292, 2, 75}, + { 291, 2, 75}, + { 782, 2, 76}, + { 781, 2, 76}, + { 340, 2, 77}, + {1171, 2, 78}, + { 712, 2, 78}, + {1172, 2, 79}, + {1181, 2, 79}, + { 217, 2, 80}, + { 218, 2, 80}, + { 491, 2, 81}, + { 490, 2, 81}, + {1073, 2, 82}, + {1074, 2, 82}, + { 760, 2, 83}, + { 225, 2, 84}, + { 224, 2, 84}, + { 666, 2, 85}, + { 665, 2, 85}, + { 835, 2, 86}, + { 874, 2, 86}, + { 638, 2, 87}, + { 200, 2, 87}, + { 948, 2, 88}, + {1075, 2, 88}, + { 651, 2, 89}, + {1030, 2, 89}, + { 650, 2, 90}, + {1004, 2, 90}, + { 949, 2, 91}, + { 958, 2, 91}, + { 679, 2, 92}, + { 706, 2, 92}, + { 231, 2, 93}, + { 232, 2, 93}, + { 265, 2, 94}, + { 264, 2, 94}, + { 791, 2, 95}, + { 790, 2, 95}, + { 339, 2, 96}, + { 283, 2, 96}, + { 842, 2, 97}, + { 840, 2, 97}, + { 843, 2, 98}, + { 841, 2, 98}, + { 844, 2, 99}, + {1014, 2, 99}, + {1092, 2, 100}, + {1097, 2, 100}, + {1115, 2, 101}, + {1114, 2, 101}, + {1176, 2, 102}, + {1175, 2, 102}, + { 297, 2, 103}, + { 159, 2, 103}, + { 230, 2, 104}, + { 229, 2, 104}, + { 485, 2, 105}, + { 484, 2, 105}, + { 493, 2, 106}, + { 492, 2, 106}, + { 563, 2, 107}, + { 562, 2, 107}, + { 983, 2, 108}, + { 620, 2, 108}, + { 702, 2, 109}, + { 701, 2, 109}, + { 753, 2, 110}, + { 751, 2, 110}, + { 785, 2, 111}, + { 786, 2, 111}, + { 794, 2, 112}, + { 793, 2, 112}, + { 839, 2, 113}, + { 838, 2, 113}, + { 864, 2, 114}, + { 872, 2, 115}, + { 873, 2, 115}, + { 945, 2, 116}, + { 891, 2, 116}, + { 888, 2, 117}, + { 894, 2, 117}, + { 985, 2, 118}, + { 984, 2, 118}, + { 992, 2, 119}, + { 991, 2, 119}, + { 946, 2, 120}, + { 998, 2, 120}, + {1032, 2, 121}, + {1005, 2, 121}, + {1099, 2, 122}, + {1098, 2, 122}, + { 703, 2, 123}, + {1101, 2, 123}, + {1199, 2, 124}, + {1198, 2, 124}, + {1252, 2, 125}, + {1251, 2, 125}, + { 161, 2, 126}, + { 178, 2, 127}, + { 619, 2, 127}, + { 603, 2, 128}, + { 602, 2, 128}, + { 876, 2, 129}, + { 875, 2, 129}, + { 943, 2, 130}, + { 623, 2, 130}, + {1100, 2, 131}, + {1091, 2, 131}, + { 692, 2, 132}, + { 621, 2, 132}, + { 963, 3, 0}, + {1270, 3, 0}, + { 479, 3, 1}, + { 480, 3, 1}, + {1102, 3, 2}, + {1122, 3, 2}, + { 607, 3, 3}, + { 618, 3, 3}, + { 424, 3, 4}, + { 748, 3, 5}, + { 898, 3, 6}, + { 904, 3, 6}, + { 522, 3, 7}, + {1047, 3, 8}, + {1052, 3, 8}, + { 537, 3, 9}, + { 535, 3, 9}, + { 690, 3, 10}, + { 677, 3, 10}, + { 169, 3, 11}, + { 734, 3, 11}, + { 845, 3, 12}, + { 861, 3, 12}, + { 846, 3, 13}, + { 863, 3, 13}, + { 847, 3, 14}, + { 829, 3, 14}, + { 927, 3, 15}, + { 922, 3, 15}, + { 524, 3, 16}, + { 519, 3, 16}, + { 963, 4, 0}, + {1270, 4, 0}, + { 424, 4, 1}, + { 748, 4, 2}, + { 415, 4, 3}, + { 383, 4, 3}, + { 522, 4, 4}, + { 519, 4, 4}, + {1047, 4, 5}, + {1052, 4, 5}, + {1119, 4, 6}, + {1107, 4, 6}, + { 708, 4, 7}, + {1229, 4, 8}, + {1164, 4, 9}, + { 775, 4, 10}, + { 777, 4, 11}, + {1026, 4, 12}, + {1023, 4, 12}, + { 963, 5, 0}, + {1270, 5, 0}, + { 424, 5, 1}, + { 748, 5, 2}, + { 522, 5, 3}, + { 519, 5, 3}, + {1088, 5, 4}, + {1083, 5, 4}, + { 537, 5, 5}, + { 535, 5, 5}, + {1116, 5, 6}, + { 766, 5, 7}, + { 763, 5, 7}, + {1226, 5, 8}, + {1225, 5, 8}, + { 950, 5, 9}, + { 734, 5, 9}, + { 927, 5, 10}, + { 922, 5, 10}, + { 211, 5, 11}, + { 206, 5, 11}, + {1126, 5, 12}, + {1125, 5, 12}, + { 379, 5, 13}, + { 378, 5, 13}, + {1080, 5, 14}, + {1079, 5, 14}, + { 905, 6, 0}, + { 885, 6, 0}, + { 525, 6, 0}, + { 526, 6, 0}, + {1275, 6, 1}, + {1271, 6, 1}, + {1164, 6, 1}, + {1213, 6, 1}, + { 916, 7, 0}, + { 887, 7, 0}, + { 735, 7, 1}, + { 708, 7, 1}, + {1246, 7, 2}, + {1229, 7, 2}, + {1209, 7, 3}, + {1164, 7, 3}, + { 776, 7, 4}, + { 775, 7, 4}, + { 778, 7, 5}, + { 777, 7, 5}, + { 739, 8, 0}, + { 708, 8, 0}, + {1055, 8, 1}, + {1045, 8, 1}, + { 516, 8, 2}, + { 495, 8, 2}, + { 517, 8, 3}, + { 506, 8, 3}, + { 518, 8, 4}, + { 507, 8, 4}, + { 192, 8, 5}, + { 177, 8, 5}, + { 396, 8, 6}, + { 425, 8, 6}, + { 986, 8, 7}, + { 219, 8, 7}, + {1085, 8, 8}, + {1068, 8, 8}, + {1255, 8, 9}, + {1261, 8, 9}, + { 972, 8, 10}, + { 953, 8, 10}, + { 261, 8, 11}, + { 254, 8, 11}, + { 913, 8, 12}, + { 920, 8, 12}, + { 189, 8, 13}, + { 164, 8, 13}, + { 742, 8, 14}, + { 772, 8, 14}, + {1058, 8, 15}, + {1062, 8, 15}, + { 740, 8, 16}, + { 770, 8, 16}, + {1056, 8, 17}, + {1060, 8, 17}, + {1016, 8, 18}, + { 995, 8, 18}, + { 741, 8, 19}, + { 771, 8, 19}, + {1057, 8, 20}, + {1061, 8, 20}, + { 534, 8, 21}, + { 540, 8, 21}, + {1017, 8, 22}, + { 996, 8, 22}, + { 917, 9, 0}, + { 1, 9, 0}, + { 918, 9, 0}, + { 979, 9, 1}, + { 2, 9, 1}, + { 978, 9, 1}, + { 923, 9, 2}, + { 130, 9, 2}, + { 901, 9, 2}, + { 684, 9, 3}, + { 139, 9, 3}, + { 707, 9, 3}, + {1240, 9, 4}, + { 146, 9, 4}, + {1247, 9, 4}, + { 301, 9, 5}, + { 14, 9, 5}, + { 304, 9, 6}, + { 25, 9, 6}, + { 306, 9, 7}, + { 29, 9, 7}, + { 309, 9, 8}, + { 32, 9, 8}, + { 313, 9, 9}, + { 37, 9, 9}, + { 314, 9, 10}, + { 38, 9, 10}, + { 315, 9, 11}, + { 40, 9, 11}, + { 316, 9, 12}, + { 41, 9, 12}, + { 317, 9, 13}, + { 43, 9, 13}, + { 318, 9, 14}, + { 44, 9, 14}, + { 319, 9, 15}, + { 48, 9, 15}, + { 320, 9, 16}, + { 54, 9, 16}, + { 321, 9, 17}, + { 59, 9, 17}, + { 322, 9, 18}, + { 65, 9, 18}, + { 323, 9, 19}, + { 70, 9, 19}, + { 324, 9, 20}, + { 72, 9, 20}, + { 325, 9, 21}, + { 73, 9, 21}, + { 326, 9, 22}, + { 74, 9, 22}, + { 327, 9, 23}, + { 75, 9, 23}, + { 328, 9, 24}, + { 76, 9, 24}, + { 329, 9, 25}, + { 83, 9, 25}, + { 330, 9, 26}, + { 88, 9, 26}, + { 331, 9, 27}, + { 89, 9, 27}, + { 332, 9, 28}, + { 90, 9, 28}, + { 333, 9, 29}, + { 91, 9, 29}, + { 334, 9, 30}, + { 92, 9, 30}, + { 335, 9, 31}, + { 93, 9, 31}, + { 336, 9, 32}, + { 145, 9, 32}, + { 337, 9, 33}, + { 153, 9, 33}, + { 302, 9, 34}, + { 23, 9, 34}, + { 303, 9, 35}, + { 24, 9, 35}, + { 305, 9, 36}, + { 28, 9, 36}, + { 307, 9, 37}, + { 30, 9, 37}, + { 308, 9, 38}, + { 31, 9, 38}, + { 310, 9, 39}, + { 34, 9, 39}, + { 311, 9, 40}, + { 35, 9, 40}, + { 214, 9, 41}, + { 53, 9, 41}, + { 209, 9, 41}, + { 212, 9, 42}, + { 55, 9, 42}, + { 207, 9, 42}, + { 213, 9, 43}, + { 56, 9, 43}, + { 208, 9, 43}, + { 237, 9, 44}, + { 58, 9, 44}, + { 249, 9, 44}, + { 236, 9, 45}, + { 60, 9, 45}, + { 219, 9, 45}, + { 238, 9, 46}, + { 61, 9, 46}, + { 263, 9, 46}, + { 736, 9, 47}, + { 62, 9, 47}, + { 708, 9, 47}, + {1053, 9, 48}, + { 63, 9, 48}, + {1045, 9, 48}, + { 156, 9, 49}, + { 64, 9, 49}, + { 164, 9, 49}, + { 155, 9, 50}, + { 66, 9, 50}, + { 154, 9, 50}, + { 157, 9, 51}, + { 67, 9, 51}, + { 184, 9, 51}, + { 478, 9, 52}, + { 68, 9, 52}, + { 453, 9, 52}, + { 477, 9, 53}, + { 69, 9, 53}, + { 448, 9, 53}, + { 655, 9, 54}, + { 71, 9, 54}, + { 658, 9, 54}, + { 312, 9, 55}, + { 36, 9, 55}, + { 215, 9, 56}, + { 49, 9, 56}, + { 210, 9, 56}, + { 910, 10, 0}, + { 287, 10, 1}, + { 284, 10, 1}, + { 397, 10, 2}, + { 386, 10, 2}, + { 536, 10, 3}, + { 907, 10, 4}, + { 893, 10, 4}, + { 646, 10, 5}, + { 645, 10, 5}, + { 833, 10, 6}, + { 832, 10, 6}, + { 531, 10, 7}, + { 530, 10, 7}, + { 660, 10, 8}, + { 659, 10, 8}, + { 351, 10, 9}, + { 496, 10, 9}, + {1137, 10, 10}, + {1133, 10, 10}, + {1128, 10, 11}, + {1238, 10, 12}, + {1237, 10, 12}, + {1256, 10, 13}, + { 892, 10, 14}, + { 890, 10, 14}, + {1108, 10, 15}, + {1111, 10, 15}, + {1124, 10, 16}, + {1123, 10, 16}, + { 539, 10, 17}, + { 538, 10, 17}, + { 897, 11, 0}, + { 885, 11, 0}, + { 176, 11, 1}, + { 154, 11, 1}, + { 587, 11, 2}, + { 581, 11, 2}, + {1256, 11, 3}, + {1250, 11, 3}, + { 541, 11, 4}, + { 525, 11, 4}, + { 892, 11, 5}, + { 887, 11, 5}, + { 908, 12, 0}, + { 163, 12, 1}, + { 165, 12, 2}, + { 168, 12, 3}, + { 235, 12, 4}, + { 241, 12, 5}, + { 449, 12, 6}, + { 450, 12, 7}, + { 486, 12, 8}, + { 529, 12, 9}, + { 533, 12, 10}, + { 542, 12, 11}, + { 543, 12, 12}, + { 584, 12, 13}, + { 589, 12, 14}, + {1184, 12, 14}, + { 604, 12, 15}, + { 608, 12, 16}, + { 609, 12, 17}, + { 610, 12, 18}, + { 678, 12, 19}, + { 689, 12, 20}, + { 705, 12, 21}, + { 710, 12, 22}, + { 711, 12, 23}, + { 834, 12, 24}, + { 848, 12, 25}, + { 915, 12, 26}, + { 930, 12, 27}, + { 997, 12, 28}, + {1039, 12, 29}, + {1040, 12, 30}, + {1049, 12, 31}, + {1051, 12, 32}, + {1071, 12, 33}, + {1072, 12, 34}, + {1084, 12, 35}, + {1086, 12, 36}, + {1095, 12, 37}, + {1155, 12, 38}, + {1169, 12, 39}, + {1182, 12, 40}, + {1183, 12, 41}, + {1189, 12, 42}, + {1253, 12, 43}, + {1163, 12, 44}, + {1272, 12, 45}, + {1273, 12, 46}, + {1274, 12, 47}, + {1282, 12, 48}, + {1283, 12, 49}, + {1286, 12, 50}, + {1287, 12, 51}, + { 695, 12, 52}, + { 528, 12, 53}, + { 278, 12, 54}, + { 527, 12, 55}, + { 932, 12, 56}, + {1063, 12, 57}, + {1127, 12, 58}, + { 795, 12, 59}, + { 796, 12, 60}, + { 797, 12, 61}, + { 798, 12, 62}, + { 799, 12, 63}, + { 800, 12, 64}, + { 801, 12, 65}, + { 802, 12, 66}, + { 803, 12, 67}, + { 804, 12, 68}, + { 805, 12, 69}, + { 806, 12, 70}, + { 807, 12, 71}, + { 808, 12, 72}, + { 809, 12, 73}, + { 810, 12, 74}, + { 811, 12, 75}, + { 812, 12, 76}, + { 813, 12, 77}, + { 814, 12, 78}, + { 815, 12, 79}, + { 816, 12, 80}, + { 817, 12, 81}, + { 818, 12, 82}, + { 819, 12, 83}, + { 820, 12, 84}, + { 821, 12, 85}, + { 912, 13, 0}, + {1214, 13, 0}, + { 670, 13, 1}, + { 281, 13, 1}, + { 483, 13, 2}, + { 447, 13, 2}, + {1054, 13, 3}, + {1045, 13, 3}, + { 738, 13, 4}, + { 708, 13, 4}, + {1210, 13, 5}, + {1164, 13, 5}, + {1224, 14, 0}, + {1270, 14, 0}, + { 955, 14, 1}, + { 954, 14, 1}, + { 381, 14, 2}, + { 378, 14, 2}, + {1043, 14, 3}, + {1042, 14, 3}, + { 559, 14, 4}, + { 556, 14, 4}, + { 914, 14, 5}, + { 919, 14, 5}, + { 520, 14, 6}, + { 519, 14, 6}, + { 273, 14, 7}, + {1156, 14, 7}, + { 643, 14, 8}, + { 658, 14, 8}, + {1025, 14, 9}, + {1024, 14, 9}, + {1022, 14, 10}, + {1015, 14, 10}, + { 927, 14, 11}, + { 922, 14, 11}, + { 172, 14, 12}, + { 164, 14, 12}, + { 630, 14, 13}, + { 626, 14, 13}, + { 652, 14, 14}, + { 639, 14, 14}, + { 653, 14, 14}, + { 625, 14, 15}, + { 624, 14, 15}, + { 392, 14, 16}, + { 382, 14, 16}, + { 271, 14, 17}, + { 233, 14, 17}, + { 270, 14, 18}, + { 221, 14, 18}, + {1117, 14, 19}, + {1116, 14, 19}, + { 792, 14, 20}, + { 248, 14, 20}, + { 293, 14, 21}, + { 424, 14, 21}, + { 758, 14, 22}, + { 748, 14, 22}, + { 414, 14, 23}, + { 298, 14, 23}, + { 399, 14, 24}, + {1070, 14, 24}, + { 176, 14, 25}, + { 162, 14, 25}, + { 272, 14, 26}, + { 220, 14, 26}, + {1153, 14, 27}, + {1090, 14, 27}, + {1293, 14, 28}, + {1292, 14, 28}, + { 900, 14, 29}, + { 904, 14, 29}, + {1260, 14, 30}, + {1257, 14, 30}, + { 668, 14, 31}, + { 676, 14, 32}, + { 675, 14, 33}, + { 582, 14, 34}, + { 583, 14, 35}, + { 380, 14, 36}, + { 422, 14, 36}, + { 607, 14, 37}, + { 618, 14, 37}, + { 400, 14, 38}, + { 352, 14, 38}, + {1047, 14, 39}, + {1052, 14, 39}, + { 910, 15, 0}, + { 927, 15, 1}, + { 922, 15, 1}, + { 473, 15, 2}, + { 466, 15, 2}, + { 455, 15, 3}, + { 454, 15, 3}, + { 889, 16, 0}, + { 0, 16, 1}, + { 1, 16, 2}, + { 5, 16, 3}, + { 4, 16, 4}, + { 3, 16, 5}, + { 13, 16, 6}, + { 12, 16, 7}, + { 11, 16, 8}, + { 10, 16, 9}, + { 78, 16, 10}, + { 9, 16, 11}, + { 8, 16, 12}, + { 7, 16, 13}, + { 82, 16, 14}, + { 47, 16, 15}, + { 115, 16, 16}, + { 6, 16, 17}, + { 131, 16, 18}, + { 81, 16, 19}, + { 118, 16, 20}, + { 46, 16, 21}, + { 80, 16, 22}, + { 98, 16, 23}, + { 117, 16, 24}, + { 133, 16, 25}, + { 26, 16, 26}, + { 2, 16, 27}, + { 79, 16, 28}, + { 45, 16, 29}, + { 116, 16, 30}, + { 77, 16, 31}, + { 132, 16, 32}, + { 97, 16, 33}, + { 147, 16, 34}, + { 114, 16, 35}, + { 27, 16, 36}, + { 124, 16, 37}, + { 33, 16, 38}, + { 130, 16, 39}, + { 39, 16, 40}, + { 139, 16, 41}, + { 42, 16, 42}, + { 146, 16, 43}, + { 14, 16, 44}, + { 25, 16, 45}, + { 29, 16, 46}, + { 32, 16, 47}, + { 37, 16, 48}, + { 38, 16, 49}, + { 40, 16, 50}, + { 41, 16, 51}, + { 43, 16, 52}, + { 44, 16, 53}, + { 48, 16, 54}, + { 54, 16, 55}, + { 59, 16, 56}, + { 65, 16, 57}, + { 70, 16, 58}, + { 72, 16, 59}, + { 73, 16, 60}, + { 74, 16, 61}, + { 75, 16, 62}, + { 76, 16, 63}, + { 83, 16, 64}, + { 88, 16, 65}, + { 89, 16, 66}, + { 90, 16, 67}, + { 91, 16, 68}, + { 92, 16, 69}, + { 93, 16, 70}, + { 94, 16, 71}, + { 95, 16, 72}, + { 96, 16, 73}, + { 99, 16, 74}, + { 104, 16, 75}, + { 105, 16, 76}, + { 106, 16, 77}, + { 108, 16, 78}, + { 109, 16, 79}, + { 110, 16, 80}, + { 111, 16, 81}, + { 112, 16, 82}, + { 113, 16, 83}, + { 119, 16, 84}, + { 125, 16, 85}, + { 134, 16, 86}, + { 140, 16, 87}, + { 148, 16, 88}, + { 15, 16, 89}, + { 49, 16, 90}, + { 84, 16, 91}, + { 100, 16, 92}, + { 120, 16, 93}, + { 126, 16, 94}, + { 135, 16, 95}, + { 141, 16, 96}, + { 149, 16, 97}, + { 16, 16, 98}, + { 50, 16, 99}, + { 85, 16, 100}, + { 101, 16, 101}, + { 121, 16, 102}, + { 127, 16, 103}, + { 136, 16, 104}, + { 142, 16, 105}, + { 150, 16, 106}, + { 17, 16, 107}, + { 51, 16, 108}, + { 86, 16, 109}, + { 102, 16, 110}, + { 122, 16, 111}, + { 128, 16, 112}, + { 137, 16, 113}, + { 143, 16, 114}, + { 151, 16, 115}, + { 18, 16, 116}, + { 52, 16, 117}, + { 57, 16, 118}, + { 87, 16, 119}, + { 103, 16, 120}, + { 107, 16, 121}, + { 123, 16, 122}, + { 129, 16, 123}, + { 138, 16, 124}, + { 144, 16, 125}, + { 152, 16, 126}, + { 19, 16, 127}, + { 20, 16, 128}, + { 21, 16, 129}, + { 22, 16, 130}, + { 887, 17, 0}, + {1053, 17, 1}, + { 736, 17, 2}, + {1242, 17, 3}, + { 737, 17, 4}, + {1203, 17, 5}, + { 259, 17, 6}, + {1204, 17, 7}, + {1208, 17, 8}, + {1206, 17, 9}, + {1207, 17, 10}, + { 260, 17, 11}, + {1205, 17, 12}, + { 980, 17, 13}, + { 963, 18, 0}, + { 247, 18, 1}, + {1241, 18, 2}, + { 216, 18, 3}, + { 923, 18, 4}, + {1240, 18, 5}, + {1036, 18, 6}, + { 654, 18, 7}, + {1245, 18, 8}, + {1244, 18, 9}, + {1243, 18, 10}, + { 408, 18, 11}, + { 402, 18, 12}, + { 403, 18, 13}, + { 413, 18, 14}, + { 410, 18, 15}, + { 409, 18, 16}, + { 412, 18, 17}, + { 411, 18, 18}, + { 407, 18, 19}, + { 404, 18, 20}, + { 405, 18, 21}, + { 869, 18, 22}, + {1201, 18, 23}, + {1202, 18, 24}, + { 546, 18, 25}, + { 290, 18, 26}, + {1048, 18, 27}, + {1157, 18, 28}, + { 406, 18, 29}, + { 911, 18, 30}, + { 672, 18, 31}, + { 926, 18, 32}, + { 924, 18, 33}, + { 266, 18, 34}, +}; + +/* property values: 5648 bytes. */ + +/* Codepoints which expand on full case-folding. */ + +RE_UINT16 re_expand_on_folding[] = { + 223, 304, 329, 496, 912, 944, 1415, 7830, + 7831, 7832, 7833, 7834, 7838, 8016, 8018, 8020, + 8022, 8064, 8065, 8066, 8067, 8068, 8069, 8070, + 8071, 8072, 8073, 8074, 8075, 8076, 8077, 8078, + 8079, 8080, 8081, 8082, 8083, 8084, 8085, 8086, + 8087, 8088, 8089, 8090, 8091, 8092, 8093, 8094, + 8095, 8096, 8097, 8098, 8099, 8100, 8101, 8102, + 8103, 8104, 8105, 8106, 8107, 8108, 8109, 8110, + 8111, 8114, 8115, 8116, 8118, 8119, 8124, 8130, + 8131, 8132, 8134, 8135, 8140, 8146, 8147, 8150, + 8151, 8162, 8163, 8164, 8166, 8167, 8178, 8179, + 8180, 8182, 8183, 8188, 64256, 64257, 64258, 64259, + 64260, 64261, 64262, 64275, 64276, 64277, 64278, 64279, +}; + +/* expand_on_folding: 208 bytes. */ + +/* General_Category. */ + +static RE_UINT8 re_general_category_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 14, 14, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 21, 23, 21, 21, 21, 21, 24, 21, 21, + 21, 21, 21, 21, 21, 21, 25, 26, 21, 21, 27, 28, 21, 29, 30, 31, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 32, 7, 33, 34, 7, 35, 21, 21, 21, 21, 21, 36, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 37, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 38, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 38, +}; + +static RE_UINT8 re_general_category_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 35, 36, 37, 38, 39, 34, 34, 34, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, + 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, + 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 91, + 92, 34, 34, 34, 34, 34, 34, 34, 34, 93, 34, 34, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 106, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 34, 34, 109, 110, 111, 112, 34, 34, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 123, 34, 34, 130, 123, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 123, 123, 141, 123, 123, 123, + 142, 143, 144, 145, 146, 147, 148, 123, 123, 149, 123, 150, 151, 152, 153, 123, + 123, 154, 123, 123, 123, 155, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 34, 34, 34, 34, 34, 34, 34, 156, 157, 34, 158, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 34, 34, 34, 34, 34, 34, 34, 34, 159, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 34, 34, 34, 34, 160, 123, 123, 123, + 34, 34, 34, 34, 161, 162, 163, 164, 123, 123, 123, 123, 123, 123, 165, 166, + 167, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 168, 169, 123, 123, 123, 123, 123, 123, + 69, 170, 171, 172, 173, 123, 174, 123, 175, 176, 177, 178, 179, 180, 181, 182, + 69, 69, 69, 69, 183, 184, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 34, 185, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 186, 187, 123, 123, + 188, 189, 190, 191, 192, 123, 69, 193, 69, 69, 194, 195, 69, 196, 197, 198, + 199, 200, 201, 202, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 203, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 204, 34, + 205, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 206, 123, 123, + 34, 34, 34, 34, 207, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 208, 123, 209, 210, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 211, +}; + +static RE_UINT16 re_general_category_stage_3[] = { + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 44, 11, 45, 46, 32, 47, 48, 49, 50, 51, + 52, 53, 49, 49, 54, 32, 55, 56, 49, 49, 49, 49, 49, 57, 58, 59, + 60, 61, 49, 32, 62, 49, 49, 49, 49, 49, 63, 64, 65, 49, 66, 67, + 49, 68, 69, 70, 49, 71, 72, 72, 72, 72, 49, 73, 72, 72, 74, 32, + 75, 49, 49, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 82, 83, 90, 91, 92, 93, 94, 95, 96, 83, 97, 98, 99, 87, 100, + 101, 82, 83, 102, 103, 104, 87, 105, 106, 107, 108, 109, 110, 111, 93, 112, + 113, 114, 83, 115, 116, 117, 87, 118, 119, 114, 83, 120, 121, 122, 87, 123, + 119, 114, 49, 124, 125, 126, 87, 127, 128, 129, 49, 130, 131, 132, 93, 133, + 134, 49, 49, 135, 136, 137, 72, 72, 138, 139, 140, 141, 142, 143, 72, 72, + 144, 145, 146, 147, 148, 49, 149, 150, 151, 152, 32, 153, 154, 155, 72, 72, + 49, 49, 156, 157, 158, 159, 160, 161, 162, 163, 9, 9, 164, 49, 49, 165, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 166, 167, 49, 49, + 166, 49, 49, 168, 169, 170, 49, 49, 49, 169, 49, 49, 49, 171, 172, 173, + 49, 174, 9, 9, 9, 9, 9, 175, 176, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 177, 49, 178, 179, 49, 49, 49, 49, 180, 181, + 182, 183, 49, 184, 49, 185, 182, 186, 49, 49, 49, 187, 188, 189, 190, 191, + 192, 190, 49, 49, 193, 49, 49, 194, 49, 49, 195, 49, 49, 49, 49, 196, + 49, 197, 198, 199, 200, 49, 201, 73, 49, 49, 202, 49, 203, 204, 205, 205, + 49, 206, 49, 49, 49, 207, 208, 209, 190, 190, 210, 211, 72, 72, 72, 72, + 212, 49, 49, 213, 214, 158, 215, 216, 217, 49, 218, 65, 49, 49, 219, 220, + 49, 49, 221, 222, 223, 65, 49, 224, 72, 72, 72, 72, 225, 226, 227, 228, + 11, 11, 229, 27, 27, 27, 230, 231, 11, 232, 27, 27, 32, 32, 32, 233, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 234, 13, 13, 13, 13, 13, 13, + 235, 236, 235, 235, 236, 237, 235, 238, 239, 239, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 72, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 266, 267, 268, 269, 205, 270, 271, 205, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 205, 275, 205, 205, 205, 205, 276, + 205, 277, 273, 278, 205, 279, 280, 281, 205, 205, 282, 72, 281, 72, 265, 265, + 265, 283, 205, 205, 205, 205, 284, 265, 205, 205, 205, 205, 205, 205, 205, 205, + 205, 205, 205, 285, 286, 205, 205, 287, 205, 205, 205, 205, 205, 205, 288, 205, + 205, 205, 205, 205, 205, 205, 289, 290, 265, 291, 205, 205, 292, 273, 293, 273, + 294, 295, 273, 273, 273, 296, 273, 297, 205, 205, 205, 273, 298, 205, 205, 299, + 205, 300, 205, 301, 302, 303, 304, 72, 9, 9, 305, 11, 11, 306, 307, 308, + 13, 13, 13, 13, 13, 13, 309, 310, 11, 11, 311, 49, 49, 49, 312, 313, + 49, 314, 315, 315, 315, 315, 32, 32, 316, 317, 318, 319, 320, 72, 72, 72, + 205, 321, 205, 205, 205, 205, 205, 322, 205, 205, 205, 205, 205, 323, 72, 324, + 325, 326, 327, 328, 134, 49, 49, 49, 49, 329, 176, 49, 49, 49, 49, 330, + 331, 49, 201, 134, 49, 49, 49, 49, 197, 332, 49, 50, 205, 205, 322, 49, + 205, 333, 334, 205, 335, 336, 205, 205, 334, 205, 205, 336, 205, 205, 205, 333, + 49, 49, 49, 196, 205, 205, 205, 205, 49, 49, 49, 49, 49, 196, 72, 72, + 49, 337, 49, 49, 49, 49, 49, 49, 149, 205, 205, 205, 282, 49, 49, 224, + 338, 49, 339, 72, 13, 13, 340, 341, 13, 342, 49, 49, 49, 49, 343, 344, + 31, 345, 346, 347, 13, 13, 13, 348, 349, 350, 351, 352, 72, 72, 72, 353, + 354, 49, 355, 356, 49, 49, 49, 357, 358, 49, 49, 359, 360, 190, 32, 361, + 65, 49, 362, 49, 363, 364, 49, 149, 75, 49, 49, 365, 366, 367, 368, 369, + 49, 49, 370, 371, 372, 373, 49, 374, 49, 49, 49, 375, 376, 377, 378, 379, + 380, 381, 315, 11, 11, 382, 383, 11, 11, 11, 11, 11, 49, 49, 384, 190, + 49, 49, 385, 49, 386, 49, 49, 202, 387, 387, 387, 387, 387, 387, 387, 387, + 388, 388, 388, 388, 388, 388, 388, 388, 49, 49, 49, 49, 49, 49, 201, 49, + 49, 49, 49, 49, 49, 203, 72, 72, 389, 390, 391, 392, 393, 49, 49, 49, + 49, 49, 49, 394, 395, 396, 49, 49, 49, 49, 49, 397, 72, 49, 49, 49, + 49, 398, 49, 49, 194, 72, 72, 399, 32, 400, 32, 401, 402, 403, 404, 405, + 49, 49, 49, 49, 49, 49, 49, 406, 407, 2, 3, 4, 5, 408, 409, 410, + 49, 411, 49, 197, 412, 413, 414, 415, 416, 49, 170, 417, 201, 201, 72, 72, + 49, 49, 49, 49, 49, 49, 49, 50, 418, 265, 265, 419, 266, 266, 266, 420, + 421, 324, 422, 72, 72, 205, 205, 423, 72, 72, 72, 72, 72, 72, 72, 72, + 49, 149, 49, 49, 49, 99, 424, 425, 49, 49, 426, 49, 427, 49, 49, 428, + 49, 429, 49, 49, 430, 431, 72, 72, 9, 9, 432, 11, 11, 49, 49, 49, + 49, 201, 190, 72, 72, 72, 72, 72, 49, 49, 194, 49, 49, 49, 433, 72, + 49, 49, 49, 314, 49, 196, 194, 72, 434, 49, 49, 435, 49, 436, 49, 437, + 49, 197, 438, 72, 72, 72, 49, 439, 49, 440, 49, 441, 72, 72, 72, 72, + 49, 49, 49, 442, 265, 443, 265, 265, 444, 445, 49, 446, 447, 448, 49, 449, + 49, 450, 72, 72, 451, 49, 452, 453, 49, 49, 49, 454, 49, 455, 49, 456, + 49, 457, 458, 72, 72, 72, 72, 72, 49, 49, 49, 49, 459, 72, 72, 72, + 9, 9, 9, 460, 11, 11, 11, 461, 72, 72, 72, 72, 72, 72, 265, 462, + 463, 49, 49, 464, 465, 443, 466, 467, 217, 49, 49, 468, 469, 49, 459, 190, + 470, 49, 471, 472, 473, 49, 49, 474, 217, 49, 49, 475, 476, 477, 478, 479, + 49, 96, 480, 481, 72, 72, 72, 72, 482, 483, 484, 49, 49, 485, 486, 190, + 487, 82, 83, 97, 488, 489, 490, 491, 49, 49, 49, 492, 493, 190, 72, 72, + 49, 49, 494, 495, 496, 497, 72, 72, 49, 49, 49, 498, 499, 190, 72, 72, + 49, 49, 500, 501, 190, 72, 72, 72, 49, 502, 503, 504, 72, 72, 72, 72, + 72, 72, 9, 9, 11, 11, 146, 505, 72, 72, 72, 72, 49, 49, 49, 459, + 49, 203, 72, 72, 72, 72, 72, 72, 266, 266, 266, 266, 266, 266, 506, 507, + 49, 49, 49, 49, 385, 72, 72, 72, 49, 49, 197, 72, 72, 72, 72, 72, + 49, 49, 49, 49, 314, 72, 72, 72, 49, 49, 49, 459, 49, 197, 367, 72, + 72, 72, 72, 72, 72, 49, 201, 508, 49, 49, 49, 509, 510, 511, 512, 513, + 49, 72, 72, 72, 72, 72, 72, 72, 49, 49, 49, 49, 73, 514, 515, 516, + 467, 517, 72, 72, 72, 72, 72, 72, 518, 72, 72, 72, 72, 72, 72, 72, + 49, 49, 49, 49, 49, 49, 50, 149, 459, 519, 520, 72, 72, 72, 72, 72, + 205, 205, 205, 205, 205, 205, 205, 323, 205, 205, 521, 205, 205, 205, 522, 523, + 524, 205, 525, 205, 205, 205, 526, 72, 205, 205, 205, 205, 527, 72, 72, 72, + 205, 205, 205, 205, 205, 282, 265, 528, 9, 529, 11, 530, 531, 532, 235, 9, + 533, 534, 535, 536, 537, 9, 529, 11, 538, 539, 11, 540, 541, 542, 543, 9, + 544, 11, 9, 529, 11, 530, 531, 11, 235, 9, 533, 543, 9, 544, 11, 9, + 529, 11, 545, 9, 546, 547, 548, 549, 11, 550, 9, 551, 552, 553, 554, 11, + 555, 9, 556, 11, 557, 558, 558, 558, 32, 32, 32, 559, 32, 32, 560, 561, + 562, 563, 46, 72, 72, 72, 72, 72, 49, 49, 49, 49, 564, 565, 72, 72, + 566, 49, 567, 568, 569, 570, 571, 572, 573, 202, 574, 202, 72, 72, 72, 575, + 205, 205, 324, 205, 205, 205, 205, 205, 205, 322, 333, 576, 576, 576, 205, 323, + 173, 205, 333, 205, 205, 205, 324, 205, 205, 281, 72, 72, 72, 72, 577, 205, + 578, 205, 205, 281, 526, 303, 72, 72, 205, 205, 205, 205, 205, 205, 205, 579, + 205, 205, 205, 205, 205, 205, 205, 321, 205, 205, 580, 205, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 422, 581, 322, 205, 205, 205, 205, 205, 205, 205, 322, + 205, 205, 205, 205, 205, 582, 72, 72, 324, 205, 205, 205, 583, 174, 205, 205, + 583, 205, 584, 72, 72, 72, 72, 72, 72, 526, 72, 72, 72, 72, 72, 72, + 582, 72, 72, 72, 422, 72, 72, 72, 49, 49, 49, 49, 49, 314, 72, 72, + 49, 49, 49, 73, 49, 49, 49, 49, 49, 201, 49, 49, 49, 49, 49, 49, + 49, 49, 518, 72, 72, 72, 72, 72, 49, 201, 72, 72, 72, 72, 72, 72, + 585, 72, 586, 586, 586, 586, 586, 586, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 72, 388, 388, 388, 388, 388, 388, 388, 587, +}; + +static RE_UINT8 re_general_category_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, + 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, + 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, + 53, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 54, 55, 56, 57, + 58, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 59, + 60, 61, 43, 60, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 62, 44, 44, 36, 63, 64, 44, 44, 44, 44, 44, + 65, 65, 65, 8, 9, 66, 2, 67, 43, 43, 43, 43, 43, 61, 68, 2, + 69, 36, 36, 36, 36, 70, 43, 43, 7, 7, 7, 7, 7, 2, 2, 36, + 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 72, 43, 43, 43, 73, 50, + 43, 43, 74, 75, 76, 43, 43, 36, 7, 7, 7, 7, 7, 36, 77, 78, + 2, 2, 2, 2, 2, 2, 2, 79, 70, 36, 36, 36, 36, 36, 36, 36, + 43, 43, 43, 43, 43, 80, 81, 36, 36, 36, 36, 43, 43, 43, 43, 43, + 71, 44, 44, 44, 44, 44, 44, 44, 7, 7, 7, 7, 7, 36, 36, 36, + 36, 36, 36, 36, 36, 70, 43, 43, 43, 43, 40, 21, 2, 82, 44, 44, + 36, 36, 36, 43, 43, 75, 43, 43, 43, 43, 75, 43, 75, 43, 43, 44, + 2, 2, 2, 2, 2, 2, 2, 64, 36, 36, 36, 36, 70, 43, 44, 64, + 44, 44, 44, 44, 44, 44, 44, 44, 36, 36, 62, 44, 44, 44, 44, 44, + 44, 58, 43, 43, 43, 43, 43, 43, 43, 83, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 83, 71, 84, 85, 43, 43, 43, 83, 84, 85, 84, + 70, 43, 43, 43, 36, 36, 36, 36, 36, 43, 2, 7, 7, 7, 7, 7, + 86, 36, 36, 36, 36, 36, 36, 36, 70, 84, 81, 36, 36, 36, 62, 81, + 62, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, + 62, 62, 44, 36, 36, 44, 71, 84, 85, 43, 80, 87, 88, 87, 85, 62, + 44, 44, 44, 87, 44, 44, 36, 81, 36, 43, 44, 7, 7, 7, 7, 7, + 36, 20, 27, 27, 27, 57, 44, 44, 58, 83, 81, 36, 36, 62, 44, 81, + 62, 36, 81, 62, 36, 44, 80, 84, 85, 80, 44, 58, 80, 58, 43, 44, + 58, 44, 44, 44, 81, 36, 62, 62, 44, 44, 44, 7, 7, 7, 7, 7, + 43, 36, 70, 44, 44, 44, 44, 44, 58, 83, 81, 36, 36, 36, 36, 81, + 36, 81, 36, 36, 36, 36, 36, 36, 62, 36, 81, 36, 36, 44, 71, 84, + 85, 43, 43, 58, 83, 87, 85, 44, 62, 44, 44, 44, 44, 44, 44, 44, + 66, 44, 44, 44, 81, 44, 44, 44, 58, 84, 81, 36, 36, 36, 62, 81, + 62, 36, 81, 36, 36, 44, 71, 85, 85, 43, 80, 87, 88, 87, 85, 44, + 44, 44, 44, 83, 44, 44, 36, 81, 78, 27, 27, 27, 44, 44, 44, 44, + 44, 71, 81, 36, 36, 62, 44, 36, 62, 36, 36, 44, 81, 62, 62, 36, + 44, 81, 62, 44, 36, 62, 44, 36, 36, 36, 36, 36, 36, 44, 44, 84, + 83, 88, 44, 84, 88, 84, 85, 44, 62, 44, 44, 87, 44, 44, 44, 44, + 27, 89, 67, 67, 57, 90, 44, 44, 83, 84, 81, 36, 36, 36, 62, 36, + 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 81, 43, + 83, 84, 88, 43, 80, 43, 43, 44, 44, 44, 58, 80, 36, 62, 44, 44, + 44, 44, 44, 44, 27, 27, 27, 89, 58, 84, 81, 36, 36, 36, 62, 36, + 36, 36, 81, 36, 36, 44, 71, 85, 84, 84, 88, 83, 88, 84, 43, 44, + 44, 44, 87, 88, 44, 44, 44, 62, 81, 62, 44, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 62, 81, 84, 85, 43, 80, 84, 88, 84, 85, 62, + 44, 44, 44, 87, 44, 44, 44, 81, 27, 27, 27, 44, 56, 36, 36, 36, + 44, 84, 81, 36, 36, 36, 36, 36, 36, 36, 36, 62, 44, 36, 36, 36, + 36, 81, 36, 36, 36, 36, 81, 44, 36, 36, 36, 62, 44, 80, 44, 87, + 84, 43, 80, 80, 84, 84, 84, 84, 44, 84, 64, 44, 44, 44, 44, 44, + 81, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 91, + 36, 36, 36, 75, 43, 43, 43, 61, 7, 7, 7, 7, 7, 2, 44, 44, + 81, 62, 62, 81, 62, 62, 81, 44, 44, 44, 36, 36, 81, 36, 36, 36, + 81, 36, 81, 81, 44, 36, 81, 36, 70, 36, 43, 43, 43, 58, 71, 44, + 36, 36, 62, 82, 43, 43, 43, 44, 7, 7, 7, 7, 7, 44, 36, 36, + 77, 67, 2, 2, 2, 2, 2, 2, 2, 92, 92, 67, 43, 67, 67, 67, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 84, + 36, 36, 36, 36, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 62, 44, + 58, 43, 43, 43, 43, 43, 43, 83, 43, 43, 61, 43, 36, 36, 70, 43, + 43, 43, 43, 43, 58, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, + 67, 67, 67, 76, 67, 67, 90, 67, 2, 2, 92, 67, 21, 64, 44, 44, + 36, 36, 36, 36, 36, 93, 85, 43, 83, 43, 43, 43, 85, 83, 85, 71, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 84, 43, 36, 36, 43, + 71, 84, 94, 93, 84, 84, 84, 36, 70, 43, 71, 36, 36, 36, 36, 36, + 36, 83, 85, 83, 84, 84, 85, 93, 7, 7, 7, 7, 7, 84, 85, 67, + 11, 11, 11, 48, 44, 44, 48, 44, 36, 36, 36, 36, 36, 63, 69, 36, + 36, 36, 36, 36, 62, 36, 36, 44, 36, 36, 36, 62, 62, 36, 36, 44, + 62, 36, 36, 44, 36, 36, 36, 62, 62, 36, 36, 44, 36, 36, 36, 36, + 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 62, 58, 43, + 2, 2, 2, 2, 95, 27, 27, 27, 27, 27, 27, 27, 27, 27, 96, 44, + 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, + 97, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 72, + 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 99, 100, 44, + 36, 36, 36, 36, 36, 63, 2, 101, 102, 36, 36, 36, 62, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 62, 36, 36, 43, 80, 44, 44, 44, 44, 44, + 36, 43, 61, 64, 44, 44, 44, 44, 36, 43, 44, 44, 44, 44, 44, 44, + 62, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 85, 43, 43, 43, 84, + 84, 84, 84, 83, 85, 43, 43, 43, 43, 43, 2, 86, 2, 66, 70, 44, + 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, + 2, 2, 2, 103, 2, 60, 43, 68, 36, 104, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 36, 36, 70, 62, 44, 44, + 36, 36, 36, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 62, + 43, 83, 84, 85, 83, 84, 44, 44, 84, 83, 84, 84, 85, 43, 44, 44, + 90, 44, 2, 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 44, + 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, + 7, 7, 7, 7, 7, 96, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 36, 36, 36, 70, 83, 85, 44, 2, 36, 36, 93, 83, 43, 43, 43, 80, + 83, 83, 85, 43, 43, 43, 83, 84, 84, 85, 43, 43, 43, 43, 80, 58, + 2, 2, 2, 86, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43, 105, + 43, 43, 94, 36, 36, 36, 36, 36, 36, 36, 83, 43, 43, 83, 83, 84, + 84, 83, 94, 36, 36, 36, 44, 44, 92, 67, 67, 67, 67, 50, 43, 43, + 43, 43, 67, 67, 67, 67, 90, 44, 43, 94, 36, 36, 36, 36, 36, 36, + 93, 43, 43, 84, 43, 85, 43, 36, 36, 36, 36, 83, 43, 84, 85, 85, + 43, 84, 44, 44, 44, 44, 2, 2, 36, 36, 84, 84, 84, 84, 43, 43, + 43, 43, 84, 43, 44, 54, 2, 2, 7, 7, 7, 7, 7, 44, 81, 36, + 36, 36, 36, 36, 40, 40, 40, 2, 2, 2, 2, 2, 44, 44, 44, 44, + 43, 61, 43, 43, 43, 43, 43, 43, 83, 43, 43, 43, 71, 36, 70, 36, + 36, 84, 71, 62, 43, 44, 44, 44, 16, 16, 16, 16, 16, 16, 40, 40, + 40, 40, 40, 40, 40, 45, 16, 16, 16, 16, 16, 16, 45, 16, 16, 16, + 16, 16, 16, 16, 16, 106, 40, 40, 43, 43, 43, 44, 44, 44, 43, 43, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, + 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16, 107, 107, 107, 107, + 16, 16, 108, 16, 11, 11, 109, 110, 41, 16, 108, 16, 11, 11, 109, 41, + 16, 16, 44, 16, 11, 11, 111, 41, 16, 16, 16, 16, 11, 11, 112, 41, + 44, 16, 108, 16, 11, 11, 109, 113, 114, 114, 114, 114, 114, 115, 65, 65, + 116, 116, 116, 2, 117, 118, 117, 118, 2, 2, 2, 2, 119, 65, 65, 120, + 2, 2, 2, 2, 121, 122, 2, 123, 124, 2, 125, 126, 2, 2, 2, 2, + 2, 9, 124, 2, 2, 2, 2, 127, 65, 65, 68, 65, 65, 65, 65, 65, + 128, 44, 27, 27, 27, 8, 125, 129, 27, 27, 27, 27, 27, 8, 125, 100, + 40, 40, 40, 40, 40, 40, 82, 44, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 130, 43, 43, 43, 43, 43, 43, 131, 51, + 132, 51, 132, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, + 67, 133, 67, 134, 67, 34, 11, 16, 11, 32, 134, 67, 49, 11, 11, 67, + 67, 67, 133, 133, 133, 11, 11, 135, 11, 11, 35, 36, 39, 67, 16, 11, + 8, 8, 49, 16, 16, 26, 67, 136, 27, 27, 27, 27, 27, 27, 27, 27, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 137, 138, 101, 139, 67, 44, 44, + 8, 8, 140, 67, 67, 8, 67, 67, 140, 26, 67, 140, 67, 67, 67, 140, + 67, 67, 67, 67, 67, 67, 67, 8, 67, 140, 140, 67, 67, 67, 67, 67, + 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67, 141, 142, 67, 67, + 67, 67, 67, 67, 67, 67, 140, 67, 67, 67, 67, 67, 67, 26, 8, 8, + 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, + 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 90, 44, 44, + 67, 67, 67, 90, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 67, 67, + 67, 67, 67, 67, 67, 27, 27, 27, 67, 67, 67, 26, 67, 67, 67, 67, + 26, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, + 67, 67, 67, 67, 67, 67, 67, 26, 67, 67, 67, 67, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, + 8, 8, 125, 143, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8, 125, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 143, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8, 140, 26, 8, 8, 140, 67, 67, 67, 44, 67, 67, 67, 67, 67, + 67, 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, 44, 56, 67, + 67, 67, 67, 67, 90, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 67, 67, 11, 11, 11, 11, 11, 11, 11, 47, + 16, 16, 16, 16, 16, 16, 16, 108, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 40, 11, 32, 32, 136, 67, 67, 134, 34, 145, + 43, 32, 44, 44, 54, 2, 95, 2, 16, 16, 16, 53, 44, 44, 53, 44, + 36, 36, 36, 36, 44, 44, 44, 52, 64, 44, 44, 44, 44, 44, 44, 58, + 36, 36, 36, 62, 44, 44, 44, 44, 36, 36, 36, 62, 36, 36, 36, 62, + 2, 117, 117, 2, 121, 122, 117, 2, 2, 2, 2, 6, 2, 103, 117, 2, + 117, 4, 4, 4, 4, 2, 2, 86, 2, 2, 2, 2, 2, 116, 2, 2, + 103, 146, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 56, 67, 67, + 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 44, 44, 1, 2, 147, 148, 4, 4, 4, 4, + 4, 67, 4, 4, 4, 4, 149, 150, 151, 101, 101, 101, 101, 43, 43, 84, + 152, 40, 40, 67, 101, 153, 63, 67, 36, 36, 36, 62, 58, 154, 155, 69, + 36, 36, 36, 36, 36, 63, 40, 69, 44, 44, 81, 36, 36, 36, 36, 36, + 67, 27, 27, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 90, + 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, + 156, 27, 27, 27, 27, 27, 27, 27, 36, 36, 104, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 157, 2, 7, 7, 7, 7, 7, 36, 44, 44, + 32, 32, 32, 32, 32, 32, 32, 70, 51, 158, 43, 43, 43, 43, 43, 86, + 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36, 101, 101, 101, 101, 101, + 43, 2, 2, 2, 44, 44, 44, 44, 41, 41, 41, 155, 40, 40, 40, 40, + 41, 32, 32, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, + 45, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42, 159, 34, 35, + 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 44, + 11, 11, 32, 32, 44, 44, 44, 44, 44, 44, 44, 81, 40, 35, 36, 36, + 36, 71, 36, 71, 36, 70, 36, 36, 36, 93, 85, 83, 67, 67, 44, 44, + 27, 27, 27, 67, 160, 44, 44, 44, 36, 36, 2, 2, 44, 44, 44, 44, + 84, 36, 36, 36, 36, 36, 36, 36, 36, 36, 84, 84, 84, 84, 84, 84, + 84, 84, 80, 44, 44, 44, 44, 2, 43, 36, 36, 36, 2, 72, 72, 44, + 36, 36, 36, 43, 43, 43, 43, 2, 36, 36, 36, 70, 43, 43, 43, 43, + 43, 84, 44, 44, 44, 44, 44, 54, 36, 70, 84, 43, 43, 84, 83, 84, + 161, 2, 2, 2, 2, 2, 2, 52, 7, 7, 7, 7, 7, 44, 44, 2, + 36, 36, 70, 69, 36, 36, 36, 36, 7, 7, 7, 7, 7, 36, 36, 62, + 36, 36, 36, 36, 70, 43, 43, 83, 85, 83, 85, 80, 44, 44, 44, 44, + 36, 70, 36, 36, 36, 36, 83, 44, 7, 7, 7, 7, 7, 44, 2, 2, + 69, 36, 36, 77, 67, 93, 83, 36, 71, 43, 71, 70, 71, 36, 36, 43, + 70, 62, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 81, 104, 2, + 36, 36, 36, 36, 36, 93, 43, 84, 2, 104, 162, 80, 44, 44, 44, 44, + 81, 36, 36, 62, 81, 36, 36, 62, 81, 36, 36, 62, 44, 44, 44, 44, + 16, 16, 16, 16, 16, 110, 40, 40, 16, 16, 16, 44, 44, 44, 44, 44, + 36, 93, 85, 84, 83, 161, 85, 44, 36, 36, 44, 44, 44, 44, 44, 44, + 36, 36, 36, 62, 44, 81, 36, 36, 163, 163, 163, 163, 163, 163, 163, 163, + 164, 164, 164, 164, 164, 164, 164, 164, 16, 16, 16, 108, 44, 44, 44, 44, + 44, 53, 16, 16, 44, 44, 81, 71, 36, 36, 36, 36, 165, 36, 36, 36, + 36, 36, 36, 62, 36, 36, 62, 62, 36, 81, 62, 36, 36, 36, 36, 36, + 36, 41, 41, 41, 41, 41, 41, 41, 41, 44, 44, 44, 44, 44, 44, 44, + 44, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 144, + 44, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 160, 44, + 2, 2, 2, 166, 126, 44, 44, 44, 6, 167, 168, 144, 144, 144, 144, 144, + 144, 144, 126, 166, 126, 2, 123, 169, 2, 64, 2, 2, 149, 144, 144, 126, + 2, 170, 8, 171, 66, 2, 44, 44, 36, 36, 62, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 62, 79, 54, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18, 125, 126, 4, 2, 36, 36, 36, 36, 36, + 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 62, 44, + 20, 172, 57, 130, 26, 8, 140, 90, 44, 44, 44, 44, 79, 65, 67, 44, + 36, 36, 36, 36, 36, 36, 81, 36, 36, 36, 36, 36, 36, 62, 36, 81, + 2, 64, 44, 173, 27, 27, 27, 27, 27, 27, 44, 56, 67, 67, 67, 67, + 101, 101, 139, 27, 89, 67, 67, 67, 67, 67, 67, 67, 67, 27, 90, 44, + 90, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 50, 44, + 174, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 44, 44, + 27, 27, 44, 44, 44, 44, 44, 44, 148, 36, 36, 36, 36, 175, 44, 44, + 36, 36, 36, 43, 43, 80, 44, 44, 36, 36, 36, 36, 36, 36, 36, 54, + 36, 36, 44, 44, 36, 36, 36, 36, 176, 101, 101, 44, 44, 44, 44, 44, + 11, 11, 11, 11, 16, 16, 16, 16, 36, 36, 44, 44, 44, 44, 44, 54, + 36, 36, 36, 44, 62, 36, 36, 36, 36, 36, 36, 81, 62, 44, 62, 81, + 36, 36, 36, 54, 27, 27, 27, 27, 36, 36, 36, 77, 156, 27, 27, 27, + 44, 44, 44, 173, 27, 27, 27, 27, 36, 62, 36, 44, 44, 173, 27, 27, + 36, 36, 36, 27, 27, 27, 44, 54, 36, 36, 36, 36, 36, 44, 44, 54, + 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, + 70, 43, 58, 80, 44, 44, 43, 43, 36, 36, 81, 36, 81, 36, 36, 36, + 36, 36, 44, 44, 43, 80, 44, 58, 27, 27, 27, 27, 44, 44, 44, 44, + 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36, 177, 30, + 36, 36, 36, 36, 36, 36, 177, 27, 36, 36, 36, 36, 78, 36, 36, 36, + 36, 36, 70, 80, 44, 173, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, + 36, 36, 36, 44, 54, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, + 36, 62, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 54, 2, 64, 44, + 44, 44, 44, 44, 173, 27, 27, 27, 36, 36, 36, 36, 62, 44, 44, 44, + 11, 47, 44, 44, 44, 44, 44, 44, 16, 108, 44, 44, 44, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 96, 85, 94, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 61, 2, 2, 2, 44, + 27, 27, 27, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, 44, 58, + 84, 85, 43, 83, 85, 61, 178, 2, 2, 44, 44, 44, 44, 44, 44, 44, + 43, 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, 85, 43, + 43, 43, 80, 7, 7, 7, 7, 7, 2, 2, 44, 44, 44, 44, 44, 44, + 36, 70, 2, 62, 44, 44, 44, 44, 36, 93, 84, 43, 43, 43, 43, 83, + 94, 36, 63, 2, 2, 43, 61, 44, 7, 7, 7, 7, 7, 63, 63, 2, + 173, 27, 27, 27, 27, 27, 27, 27, 27, 27, 96, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 84, 85, 43, 84, 83, 43, 2, 2, 2, 44, + 36, 36, 36, 62, 62, 36, 36, 81, 36, 36, 36, 36, 36, 36, 36, 81, + 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, + 84, 85, 43, 43, 43, 80, 44, 44, 43, 84, 81, 36, 36, 36, 62, 81, + 83, 84, 88, 87, 88, 87, 84, 44, 62, 44, 44, 87, 44, 44, 81, 36, + 36, 84, 44, 43, 43, 43, 80, 44, 43, 43, 80, 44, 44, 44, 44, 44, + 84, 85, 43, 43, 83, 83, 84, 85, 83, 43, 36, 72, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 93, 84, 43, 43, 44, 84, 84, 43, 85, + 61, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, + 84, 85, 43, 43, 43, 83, 85, 85, 61, 2, 62, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 70, 85, 84, 43, 43, 43, 85, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 44, 58, 43, 84, 43, 43, 85, 43, 43, 44, 44, + 7, 7, 7, 7, 7, 27, 2, 92, 27, 96, 44, 44, 44, 44, 44, 81, + 101, 101, 101, 101, 101, 101, 101, 175, 2, 2, 64, 44, 44, 44, 44, 44, + 43, 43, 61, 44, 44, 44, 44, 44, 43, 43, 43, 61, 2, 2, 67, 67, + 40, 40, 92, 44, 44, 44, 44, 44, 7, 7, 7, 7, 7, 173, 27, 27, + 27, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 44, 81, 36, + 93, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 88, 43, 74, 40, 40, 40, 40, 40, 40, + 36, 44, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 44, 50, 61, + 65, 65, 44, 44, 44, 44, 44, 44, 67, 67, 67, 90, 56, 67, 67, 67, + 67, 67, 179, 85, 43, 67, 179, 84, 84, 180, 65, 65, 65, 181, 43, 43, + 43, 76, 50, 43, 43, 43, 67, 67, 67, 67, 67, 67, 67, 43, 43, 67, + 67, 67, 67, 67, 90, 44, 44, 44, 67, 43, 76, 44, 44, 44, 44, 44, + 27, 44, 44, 44, 44, 44, 44, 44, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16, 108, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 47, 11, 44, 47, 48, 47, 48, 11, 47, 11, + 11, 11, 11, 16, 16, 53, 53, 16, 16, 16, 53, 16, 16, 16, 16, 16, + 16, 16, 11, 48, 11, 47, 48, 11, 11, 11, 47, 11, 11, 11, 47, 16, + 16, 16, 16, 16, 11, 48, 11, 47, 11, 11, 47, 47, 44, 11, 11, 11, + 47, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 44, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 44, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 43, 43, 43, 76, 67, 50, 43, 43, + 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, + 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 58, 43, 43, + 36, 36, 62, 173, 27, 27, 27, 27, 43, 43, 43, 80, 44, 44, 44, 44, + 36, 36, 81, 36, 36, 36, 36, 36, 81, 62, 62, 81, 81, 36, 36, 36, + 36, 62, 36, 36, 81, 81, 44, 44, 44, 62, 44, 81, 81, 81, 81, 36, + 81, 62, 62, 81, 81, 81, 81, 81, 81, 62, 62, 81, 36, 62, 36, 36, + 36, 62, 36, 36, 81, 36, 62, 62, 36, 36, 36, 36, 36, 81, 36, 36, + 81, 36, 81, 36, 36, 81, 36, 36, 8, 44, 44, 44, 44, 44, 44, 44, + 56, 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 67, 67, 67, 67, 67, + 67, 90, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 25, 41, 41, + 67, 67, 56, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 90, 44, + 67, 67, 90, 44, 44, 44, 44, 44, 67, 67, 67, 67, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 67, 44, 79, 44, 44, 44, 44, 44, 44, 44, + 65, 65, 65, 65, 65, 65, 65, 65, 164, 164, 164, 164, 164, 164, 164, 44, +}; + +static RE_UINT8 re_general_category_stage_5[] = { + 15, 15, 12, 23, 23, 23, 25, 23, 20, 21, 23, 24, 23, 19, 9, 9, + 24, 24, 24, 23, 23, 1, 1, 1, 1, 20, 23, 21, 26, 22, 26, 2, + 2, 2, 2, 20, 24, 21, 24, 15, 25, 25, 27, 23, 26, 27, 5, 28, + 24, 16, 27, 26, 27, 24, 11, 11, 26, 11, 5, 29, 11, 23, 1, 24, + 1, 2, 2, 24, 2, 1, 2, 5, 5, 5, 1, 3, 3, 2, 5, 2, + 4, 4, 26, 26, 4, 26, 6, 6, 0, 0, 4, 2, 1, 23, 1, 0, + 0, 1, 24, 1, 27, 6, 7, 7, 0, 4, 0, 2, 0, 23, 19, 0, + 0, 27, 27, 25, 0, 6, 19, 6, 23, 6, 6, 23, 5, 0, 5, 23, + 23, 0, 16, 16, 23, 25, 27, 27, 16, 0, 4, 5, 5, 6, 6, 5, + 23, 5, 6, 16, 6, 4, 4, 6, 6, 27, 5, 27, 27, 5, 0, 16, + 6, 0, 0, 5, 4, 0, 6, 8, 8, 8, 8, 6, 23, 4, 0, 8, + 8, 0, 11, 27, 27, 0, 0, 25, 23, 27, 5, 8, 8, 5, 23, 11, + 11, 0, 19, 5, 12, 5, 5, 20, 21, 0, 10, 10, 10, 5, 19, 23, + 5, 4, 7, 0, 2, 4, 3, 3, 2, 0, 3, 26, 2, 26, 0, 26, + 1, 26, 26, 0, 12, 12, 12, 16, 19, 19, 28, 29, 20, 28, 13, 14, + 16, 12, 23, 28, 29, 23, 23, 22, 22, 23, 24, 20, 21, 23, 23, 12, + 11, 4, 21, 4, 25, 0, 6, 7, 7, 6, 1, 27, 27, 1, 27, 2, + 2, 27, 10, 1, 2, 10, 10, 11, 24, 27, 27, 20, 21, 27, 21, 24, + 21, 20, 2, 6, 20, 0, 27, 4, 5, 10, 19, 20, 21, 21, 27, 10, + 19, 4, 10, 4, 6, 26, 26, 4, 27, 11, 4, 23, 7, 23, 26, 1, + 25, 27, 8, 23, 4, 8, 18, 18, 17, 17, 5, 24, 23, 20, 19, 22, + 22, 20, 22, 22, 24, 19, 24, 0, 24, 26, 0, 11, 6, 11, 10, 0, + 23, 10, 5, 11, 23, 16, 27, 8, 8, 16, 16, 6, +}; + +/* General_Category: 9628 bytes. */ + +RE_UINT32 re_get_general_category(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 11; + code = ch ^ (f << 11); + pos = (RE_UINT32)re_general_category_stage_1[f] << 4; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_general_category_stage_2[pos + f] << 3; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_general_category_stage_3[pos + f] << 3; + f = code >> 1; + code ^= f << 1; + pos = (RE_UINT32)re_general_category_stage_4[pos + f] << 1; + value = re_general_category_stage_5[pos + code]; + + return value; +} + +/* Block. */ + +static RE_UINT8 re_block_stage_1[] = { + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 16, 16, 16, 16, 18, 16, 19, 20, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 24, 25, 16, 16, 26, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 27, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +}; + +static RE_UINT8 re_block_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 28, + 29, 30, 31, 31, 32, 32, 32, 33, 34, 34, 34, 34, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 51, + 52, 53, 54, 55, 56, 56, 57, 57, 58, 59, 60, 61, 62, 62, 63, 64, + 65, 65, 66, 67, 68, 68, 69, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 82, 83, 83, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, 89, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 103, + 104, 104, 104, 104, 104, 104, 104, 105, 106, 106, 106, 106, 106, 106, 106, 106, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 108, 108, 108, 108, 109, 110, 110, 110, 110, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 119, 126, 126, 126, 119, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 119, 119, 137, 119, 119, 119, + 138, 139, 140, 141, 142, 143, 144, 119, 119, 145, 119, 146, 147, 148, 149, 119, + 119, 150, 119, 119, 119, 151, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 152, 152, 152, 152, 152, 152, 152, 152, 153, 154, 155, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 156, 156, 156, 156, 156, 156, 156, 156, 157, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 158, 158, 158, 158, 158, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 159, 159, 159, 159, 160, 161, 162, 163, 119, 119, 119, 119, 119, 119, 164, 165, + 166, 166, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 167, 168, 119, 119, 119, 119, 119, 119, + 169, 169, 170, 170, 171, 119, 172, 119, 173, 173, 173, 173, 173, 173, 173, 173, + 174, 174, 174, 174, 174, 175, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 176, 177, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 178, 178, 119, 119, + 179, 180, 181, 181, 182, 182, 183, 183, 183, 183, 183, 183, 184, 185, 186, 187, + 188, 188, 189, 189, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, + 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, + 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 191, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 193, 194, + 195, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, + 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, + 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 197, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 198, 198, 198, 198, 199, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 200, 119, 201, 202, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, + 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, +}; + +static RE_UINT16 re_block_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, + 17, 17, 17, 17, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, + 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, + 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 39, 39, 39, 39, 39, 39, + 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 42, 42, 42, 42, 42, 42, + 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, + 50, 50, 50, 50, 50, 51, 51, 51, 52, 52, 52, 52, 52, 52, 53, 53, + 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, + 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, + 60, 60, 60, 60, 60, 61, 61, 61, 19, 19, 19, 19, 62, 63, 63, 63, + 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, + 67, 67, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, + 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, 71, 71, 71, 72, 72, 72, + 73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, + 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 77, + 78, 78, 78, 78, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 81, 81, 81, 81, 81, 81, 81, 81, 82, 82, 83, 83, 83, 83, 83, 83, + 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 86, 86, 86, 87, 88, 88, 88, 88, 88, 88, 88, 88, + 89, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 90, 90, + 91, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, + 93, 93, 93, 93, 93, 93, 94, 94, 95, 95, 95, 95, 95, 95, 95, 95, + 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 99, 99, + 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 19, 103, + 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, + 107, 107, 107, 108, 108, 108, 108, 108, 108, 109, 110, 110, 111, 111, 111, 112, + 113, 113, 113, 113, 113, 113, 113, 113, 114, 114, 114, 114, 114, 114, 114, 114, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 116, 116, + 117, 117, 117, 117, 117, 117, 117, 117, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 119, 119, 119, 119, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 122, 122, 122, 122, 122, 122, 123, 123, 123, 123, 123, 123, + 124, 124, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 126, 126, 126, 127, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 130, 130, + 131, 131, 131, 132, 132, 132, 133, 133, 134, 134, 134, 134, 134, 134, 135, 135, + 136, 136, 136, 136, 136, 136, 137, 137, 138, 138, 138, 138, 138, 138, 139, 139, + 140, 140, 140, 141, 141, 141, 141, 142, 142, 142, 142, 142, 143, 143, 143, 143, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, + 146, 146, 146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 147, 147, + 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, + 150, 150, 150, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 153, 154, 155, 156, 156, 157, 157, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 160, 161, 161, 161, 161, 161, 161, 161, 161, + 162, 162, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 164, + 164, 165, 165, 165, 165, 166, 166, 166, 19, 19, 19, 19, 19, 19, 19, 19, + 167, 167, 168, 168, 168, 168, 169, 169, 170, 170, 170, 171, 171, 172, 172, 172, + 173, 173, 174, 174, 174, 174, 19, 19, 175, 175, 175, 175, 175, 176, 176, 176, + 177, 177, 177, 19, 19, 19, 19, 19, 178, 178, 178, 179, 179, 179, 179, 19, + 180, 180, 180, 180, 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 183, 183, + 184, 184, 184, 19, 19, 19, 185, 185, 186, 186, 187, 187, 19, 19, 19, 19, + 188, 188, 189, 189, 189, 189, 189, 189, 190, 190, 190, 190, 190, 190, 191, 191, + 192, 192, 19, 19, 193, 193, 193, 193, 194, 194, 194, 194, 195, 195, 196, 196, + 197, 197, 197, 19, 19, 19, 19, 19, 198, 198, 198, 198, 198, 19, 19, 19, + 199, 199, 199, 199, 199, 199, 199, 199, 19, 19, 19, 19, 19, 19, 200, 200, + 201, 201, 201, 201, 201, 201, 201, 201, 202, 202, 202, 202, 202, 203, 203, 203, + 204, 204, 204, 204, 204, 205, 205, 205, 206, 206, 206, 206, 206, 206, 207, 207, + 208, 208, 208, 208, 208, 19, 19, 19, 209, 209, 209, 210, 210, 210, 210, 210, + 211, 211, 211, 211, 211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 19, 19, + 213, 213, 213, 213, 213, 213, 213, 213, 214, 214, 214, 214, 214, 214, 19, 19, + 215, 215, 215, 215, 215, 19, 19, 19, 216, 216, 216, 216, 19, 19, 19, 19, + 19, 19, 217, 217, 217, 217, 217, 217, 19, 19, 19, 19, 218, 218, 218, 218, + 219, 219, 219, 219, 219, 219, 219, 219, 220, 220, 220, 220, 220, 220, 220, 220, + 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 19, 19, 19, + 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 19, 19, 19, 19, 19, + 223, 223, 223, 223, 223, 223, 223, 223, 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 225, 225, 225, 19, 19, 19, 19, 19, 19, 226, 226, 226, + 227, 227, 227, 227, 227, 227, 227, 227, 227, 19, 19, 19, 19, 19, 19, 19, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 19, 19, 19, 19, 19, 19, + 229, 229, 229, 229, 229, 229, 229, 229, 230, 230, 230, 230, 230, 230, 230, 230, + 230, 230, 231, 19, 19, 19, 19, 19, 232, 232, 232, 232, 232, 232, 232, 232, + 233, 233, 233, 233, 233, 233, 233, 233, 234, 234, 234, 234, 234, 19, 19, 19, + 235, 235, 235, 235, 235, 235, 236, 236, 237, 237, 237, 237, 237, 237, 237, 237, + 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 19, 19, 19, 19, 19, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 19, 19, + 240, 240, 240, 240, 240, 240, 240, 240, 241, 241, 241, 242, 242, 242, 242, 242, + 242, 242, 243, 243, 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, + 245, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 246, + 247, 247, 247, 247, 247, 248, 248, 248, 249, 249, 249, 249, 249, 249, 249, 249, + 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, 251, 251, 251, + 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 19, 19, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 19, 19, 19, 19, 19, + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 19, 19, 19, 19, 19, 19, + 259, 259, 259, 259, 259, 259, 259, 259, 260, 260, 260, 260, 260, 260, 260, 260, + 260, 260, 260, 260, 260, 260, 260, 19, 261, 261, 261, 261, 261, 261, 261, 261, + 262, 262, 262, 262, 262, 262, 262, 262, +}; + +static RE_UINT16 re_block_stage_4[] = { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, + 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, + 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, + 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, + 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, + 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, + 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, + 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, + 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, + 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, + 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, + 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, + 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, + 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, + 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, + 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, + 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, + 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, + 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, + 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, + 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, + 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 99, 99, 99, 99, + 100, 100, 100, 100, 101, 101, 101, 101, 102, 102, 102, 102, 103, 103, 103, 103, + 104, 104, 104, 104, 105, 105, 105, 105, 106, 106, 106, 106, 107, 107, 107, 107, + 108, 108, 108, 108, 109, 109, 109, 109, 110, 110, 110, 110, 111, 111, 111, 111, + 112, 112, 112, 112, 113, 113, 113, 113, 114, 114, 114, 114, 115, 115, 115, 115, + 116, 116, 116, 116, 117, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 119, + 120, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 123, 123, 123, 123, + 124, 124, 124, 124, 125, 125, 125, 125, 126, 126, 126, 126, 127, 127, 127, 127, + 128, 128, 128, 128, 129, 129, 129, 129, 130, 130, 130, 130, 131, 131, 131, 131, + 132, 132, 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 135, 135, 135, 135, + 136, 136, 136, 136, 137, 137, 137, 137, 138, 138, 138, 138, 139, 139, 139, 139, + 140, 140, 140, 140, 141, 141, 141, 141, 142, 142, 142, 142, 143, 143, 143, 143, + 144, 144, 144, 144, 145, 145, 145, 145, 146, 146, 146, 146, 147, 147, 147, 147, + 148, 148, 148, 148, 149, 149, 149, 149, 150, 150, 150, 150, 151, 151, 151, 151, + 152, 152, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155, 155, 155, + 156, 156, 156, 156, 157, 157, 157, 157, 158, 158, 158, 158, 159, 159, 159, 159, + 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, + 164, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, + 168, 168, 168, 168, 169, 169, 169, 169, 170, 170, 170, 170, 171, 171, 171, 171, + 172, 172, 172, 172, 173, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 175, + 176, 176, 176, 176, 177, 177, 177, 177, 178, 178, 178, 178, 179, 179, 179, 179, + 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, 182, 183, 183, 183, 183, + 184, 184, 184, 184, 185, 185, 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, + 188, 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191, 191, + 192, 192, 192, 192, 193, 193, 193, 193, 194, 194, 194, 194, 195, 195, 195, 195, + 196, 196, 196, 196, 197, 197, 197, 197, 198, 198, 198, 198, 199, 199, 199, 199, + 200, 200, 200, 200, 201, 201, 201, 201, 202, 202, 202, 202, 203, 203, 203, 203, + 204, 204, 204, 204, 205, 205, 205, 205, 206, 206, 206, 206, 207, 207, 207, 207, + 208, 208, 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, 211, 211, 211, 211, + 212, 212, 212, 212, 213, 213, 213, 213, 214, 214, 214, 214, 215, 215, 215, 215, + 216, 216, 216, 216, 217, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 219, + 220, 220, 220, 220, 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, + 224, 224, 224, 224, 225, 225, 225, 225, 226, 226, 226, 226, 227, 227, 227, 227, + 228, 228, 228, 228, 229, 229, 229, 229, 230, 230, 230, 230, 231, 231, 231, 231, + 232, 232, 232, 232, 233, 233, 233, 233, 234, 234, 234, 234, 235, 235, 235, 235, + 236, 236, 236, 236, 237, 237, 237, 237, 238, 238, 238, 238, 239, 239, 239, 239, + 240, 240, 240, 240, 241, 241, 241, 241, 242, 242, 242, 242, 243, 243, 243, 243, + 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247, 247, 247, + 248, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 251, 251, 251, 251, + 252, 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 255, 255, 255, 255, + 256, 256, 256, 256, 257, 257, 257, 257, 258, 258, 258, 258, 259, 259, 259, 259, + 260, 260, 260, 260, 261, 261, 261, 261, 262, 262, 262, 262, +}; + +static RE_UINT16 re_block_stage_5[] = { + 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, + 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, + 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 0, 0, 0, 0, + 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, + 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, + 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, + 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, + 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, + 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, + 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, + 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, + 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, + 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, + 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, + 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, + 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, + 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, + 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, + 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, + 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, + 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, + 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, + 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 99, 99, 99, 99, + 100, 100, 100, 100, 101, 101, 101, 101, 102, 102, 102, 102, 103, 103, 103, 103, + 104, 104, 104, 104, 105, 105, 105, 105, 106, 106, 106, 106, 107, 107, 107, 107, + 108, 108, 108, 108, 109, 109, 109, 109, 110, 110, 110, 110, 111, 111, 111, 111, + 112, 112, 112, 112, 113, 113, 113, 113, 114, 114, 114, 114, 115, 115, 115, 115, + 116, 116, 116, 116, 117, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 119, + 120, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 123, 123, 123, 123, + 124, 124, 124, 124, 125, 125, 125, 125, 126, 126, 126, 126, 127, 127, 127, 127, + 128, 128, 128, 128, 129, 129, 129, 129, 130, 130, 130, 130, 131, 131, 131, 131, + 132, 132, 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 135, 135, 135, 135, + 136, 136, 136, 136, 137, 137, 137, 137, 138, 138, 138, 138, 139, 139, 139, 139, + 140, 140, 140, 140, 141, 141, 141, 141, 142, 142, 142, 142, 143, 143, 143, 143, + 144, 144, 144, 144, 145, 145, 145, 145, 146, 146, 146, 146, 147, 147, 147, 147, + 148, 148, 148, 148, 149, 149, 149, 149, 150, 150, 150, 150, 151, 151, 151, 151, + 152, 152, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155, 155, 155, + 156, 156, 156, 156, 157, 157, 157, 157, 158, 158, 158, 158, 159, 159, 159, 159, + 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, + 164, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, + 168, 168, 168, 168, 169, 169, 169, 169, 170, 170, 170, 170, 171, 171, 171, 171, + 172, 172, 172, 172, 173, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 175, + 176, 176, 176, 176, 177, 177, 177, 177, 178, 178, 178, 178, 179, 179, 179, 179, + 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, 182, 183, 183, 183, 183, + 184, 184, 184, 184, 185, 185, 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, + 188, 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191, 191, + 192, 192, 192, 192, 193, 193, 193, 193, 194, 194, 194, 194, 195, 195, 195, 195, + 196, 196, 196, 196, 197, 197, 197, 197, 198, 198, 198, 198, 199, 199, 199, 199, + 200, 200, 200, 200, 201, 201, 201, 201, 202, 202, 202, 202, 203, 203, 203, 203, + 204, 204, 204, 204, 205, 205, 205, 205, 206, 206, 206, 206, 207, 207, 207, 207, + 208, 208, 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, 211, 211, 211, 211, + 212, 212, 212, 212, 213, 213, 213, 213, 214, 214, 214, 214, 215, 215, 215, 215, + 216, 216, 216, 216, 217, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 219, + 220, 220, 220, 220, 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, + 224, 224, 224, 224, 225, 225, 225, 225, 226, 226, 226, 226, 227, 227, 227, 227, + 228, 228, 228, 228, 229, 229, 229, 229, 230, 230, 230, 230, 231, 231, 231, 231, + 232, 232, 232, 232, 233, 233, 233, 233, 234, 234, 234, 234, 235, 235, 235, 235, + 236, 236, 236, 236, 237, 237, 237, 237, 238, 238, 238, 238, 239, 239, 239, 239, + 240, 240, 240, 240, 241, 241, 241, 241, 242, 242, 242, 242, 243, 243, 243, 243, + 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247, 247, 247, + 248, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 251, 251, 251, 251, + 252, 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 255, 255, 255, 255, + 256, 256, 256, 256, 257, 257, 257, 257, 258, 258, 258, 258, 259, 259, 259, 259, + 260, 260, 260, 260, 261, 261, 261, 261, 262, 262, 262, 262, +}; + +/* Block: 8720 bytes. */ + +RE_UINT32 re_get_block(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_block_stage_1[f] << 5; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_block_stage_2[pos + f] << 3; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_block_stage_3[pos + f] << 2; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_block_stage_4[pos + f] << 2; + value = re_block_stage_5[pos + code]; + + return value; +} + +/* Script. */ + +static RE_UINT8 re_script_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 12, 12, 12, 12, 13, 14, 14, 14, 14, 15, + 16, 17, 18, 19, 20, 14, 21, 14, 22, 14, 14, 14, 14, 23, 14, 14, + 14, 14, 14, 14, 14, 14, 24, 25, 14, 14, 26, 27, 14, 28, 29, 30, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 31, 7, 32, 33, 7, 34, 14, 14, 14, 14, 14, 35, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 36, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +}; + +static RE_UINT8 re_script_stage_2[] = { + 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, 37, 37, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 2, 2, 53, 54, + 55, 56, 57, 58, 59, 59, 59, 60, 61, 59, 59, 59, 59, 59, 59, 59, + 62, 62, 59, 59, 59, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 59, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 80, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 81, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 83, 84, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 97, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 71, 71, 99, 100, 101, 102, 103, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 98, 114, 115, 116, 117, 118, 119, 98, 120, 120, 121, 98, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 98, 98, 132, 98, 98, 98, + 133, 134, 135, 136, 137, 138, 139, 98, 98, 140, 98, 141, 142, 143, 144, 98, + 98, 145, 98, 98, 98, 146, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 147, 147, 147, 147, 147, 147, 147, 148, 149, 147, 150, 98, 98, 98, 98, 98, + 151, 151, 151, 151, 151, 151, 151, 151, 152, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 153, 153, 153, 153, 154, 98, 98, 98, + 155, 155, 155, 155, 156, 157, 158, 159, 98, 98, 98, 98, 98, 98, 160, 161, + 162, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 163, 164, 98, 98, 98, 98, 98, 98, + 59, 165, 166, 167, 168, 98, 169, 98, 170, 171, 172, 59, 59, 173, 59, 174, + 175, 175, 175, 175, 175, 176, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 177, 178, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 179, 180, 98, 98, + 181, 182, 183, 184, 185, 98, 59, 59, 59, 59, 186, 187, 59, 188, 189, 190, + 191, 192, 193, 194, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 195, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 196, 71, + 197, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 198, 98, 98, + 71, 71, 71, 71, 199, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 200, 98, 201, 202, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, +}; + +static RE_UINT16 re_script_stage_3[] = { + 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 12, 11, 11, 11, 13, 11, + 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 16, 17, 18, 16, 17, 19, 20, 21, 21, 22, 21, 23, 24, + 25, 26, 27, 27, 28, 29, 27, 30, 27, 27, 27, 27, 27, 31, 27, 27, + 32, 33, 33, 33, 34, 27, 27, 27, 35, 35, 35, 36, 37, 37, 37, 38, + 39, 39, 40, 41, 42, 43, 44, 44, 44, 44, 27, 45, 44, 44, 46, 27, + 47, 47, 47, 47, 47, 48, 49, 47, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 123, 124, 123, 125, 44, 44, 126, 127, 128, 129, 130, 131, 44, 44, + 132, 132, 132, 132, 133, 132, 134, 135, 132, 133, 132, 136, 136, 137, 44, 44, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 140, 139, 139, 141, + 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 144, 145, 143, 143, + 144, 143, 143, 146, 147, 148, 143, 143, 143, 147, 143, 143, 143, 149, 143, 150, + 143, 151, 152, 152, 152, 152, 152, 153, 154, 154, 154, 154, 154, 154, 154, 154, + 155, 156, 157, 157, 157, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 168, 168, 168, 168, 169, 170, 170, 171, 172, 173, 173, 173, 173, 173, 174, + 173, 173, 175, 154, 154, 154, 154, 176, 177, 178, 179, 179, 180, 181, 182, 183, + 184, 184, 185, 184, 186, 187, 168, 168, 188, 189, 190, 190, 190, 191, 190, 192, + 193, 193, 194, 195, 44, 44, 44, 44, 196, 196, 196, 196, 197, 196, 196, 198, + 199, 199, 199, 199, 200, 200, 200, 201, 202, 202, 202, 203, 204, 205, 205, 205, + 44, 44, 44, 44, 206, 207, 208, 209, 4, 4, 210, 4, 4, 211, 212, 213, + 4, 4, 4, 214, 8, 8, 8, 215, 11, 216, 11, 11, 216, 217, 11, 218, + 11, 11, 11, 219, 219, 220, 11, 221, 222, 0, 0, 0, 0, 0, 223, 224, + 225, 226, 0, 225, 44, 8, 8, 227, 0, 0, 228, 229, 230, 0, 4, 4, + 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 232, 0, 0, 233, 44, 232, 44, 0, 0, + 234, 234, 234, 234, 234, 234, 234, 234, 0, 0, 0, 0, 0, 0, 0, 235, + 0, 236, 0, 237, 238, 239, 240, 44, 241, 241, 242, 241, 241, 242, 4, 4, + 243, 243, 243, 243, 243, 243, 243, 244, 139, 139, 140, 245, 245, 245, 246, 247, + 143, 248, 249, 249, 249, 249, 14, 14, 0, 0, 0, 0, 250, 44, 44, 44, + 251, 252, 251, 251, 251, 251, 251, 253, 251, 251, 251, 251, 251, 251, 251, 251, + 251, 251, 251, 251, 251, 254, 44, 255, 256, 0, 257, 258, 259, 260, 260, 260, + 260, 261, 262, 263, 263, 263, 263, 264, 265, 266, 267, 268, 142, 142, 142, 142, + 269, 0, 266, 270, 0, 0, 271, 263, 142, 269, 0, 0, 0, 0, 142, 272, + 0, 0, 0, 0, 0, 263, 263, 273, 263, 263, 263, 263, 263, 274, 0, 0, + 251, 251, 251, 254, 0, 0, 0, 0, 251, 251, 251, 251, 251, 254, 44, 44, + 275, 275, 275, 275, 275, 275, 275, 275, 276, 275, 275, 275, 277, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 44, 14, 14, 14, 14, + 14, 14, 281, 281, 281, 281, 281, 282, 0, 0, 283, 4, 4, 4, 4, 4, + 284, 4, 285, 286, 44, 44, 44, 287, 288, 288, 289, 290, 291, 291, 291, 292, + 293, 293, 293, 293, 294, 295, 47, 296, 297, 297, 298, 299, 299, 300, 142, 301, + 302, 302, 302, 302, 303, 304, 138, 305, 306, 306, 306, 307, 308, 309, 138, 138, + 310, 310, 310, 310, 311, 312, 313, 314, 315, 316, 249, 4, 4, 317, 318, 152, + 152, 152, 152, 152, 313, 313, 319, 320, 142, 142, 321, 142, 322, 142, 142, 323, + 44, 44, 44, 44, 44, 44, 44, 44, 251, 251, 251, 251, 251, 251, 324, 251, + 251, 251, 251, 251, 251, 325, 44, 44, 326, 327, 21, 328, 329, 27, 27, 27, + 27, 27, 27, 27, 330, 46, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 331, 44, 27, 27, 27, 27, 332, 27, 27, 333, 44, 44, 334, + 8, 290, 335, 0, 0, 336, 337, 338, 27, 27, 27, 27, 27, 27, 27, 339, + 340, 0, 1, 2, 1, 2, 341, 262, 263, 342, 142, 269, 343, 344, 345, 346, + 347, 348, 349, 350, 351, 351, 44, 44, 348, 348, 348, 348, 348, 348, 348, 352, + 353, 0, 0, 354, 11, 11, 11, 11, 355, 255, 356, 44, 44, 0, 0, 357, + 358, 359, 360, 360, 360, 361, 362, 255, 363, 363, 364, 365, 366, 367, 367, 368, + 369, 370, 371, 371, 372, 373, 44, 44, 374, 374, 374, 374, 374, 375, 375, 375, + 376, 377, 378, 44, 44, 44, 44, 44, 379, 379, 380, 381, 381, 381, 382, 44, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 384, 383, 385, 386, 44, + 387, 388, 388, 389, 390, 391, 392, 392, 393, 394, 395, 44, 44, 44, 396, 397, + 398, 399, 400, 401, 44, 44, 44, 44, 402, 402, 403, 404, 403, 405, 403, 403, + 406, 407, 408, 409, 410, 411, 412, 412, 413, 413, 44, 44, 414, 414, 415, 416, + 417, 417, 417, 418, 419, 420, 421, 422, 423, 424, 425, 44, 44, 44, 44, 44, + 426, 426, 426, 426, 427, 44, 44, 44, 428, 428, 428, 429, 428, 428, 428, 430, + 44, 44, 44, 44, 44, 44, 27, 431, 432, 432, 432, 432, 433, 434, 432, 435, + 436, 436, 436, 436, 437, 438, 439, 440, 441, 441, 441, 442, 443, 444, 444, 445, + 446, 446, 446, 446, 447, 446, 448, 449, 450, 451, 450, 452, 44, 44, 44, 44, + 453, 454, 455, 456, 456, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, + 467, 467, 467, 467, 468, 469, 44, 44, 470, 470, 470, 471, 470, 472, 44, 44, + 473, 473, 473, 473, 474, 475, 44, 44, 476, 476, 476, 477, 478, 44, 44, 44, + 479, 480, 481, 479, 44, 44, 44, 44, 44, 44, 482, 482, 482, 482, 482, 483, + 44, 44, 44, 44, 484, 484, 484, 485, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 487, 44, 44, 44, 44, 44, 44, 486, 486, 486, 486, 486, 486, 488, 489, + 486, 486, 486, 486, 490, 44, 44, 44, 491, 491, 491, 491, 491, 491, 491, 491, + 491, 491, 492, 44, 44, 44, 44, 44, 493, 493, 493, 493, 493, 493, 493, 493, + 493, 493, 493, 493, 494, 44, 44, 44, 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 495, 496, 497, 498, 44, 44, 44, 44, 44, 44, 499, 500, 501, + 502, 502, 502, 502, 503, 504, 505, 506, 502, 44, 44, 44, 44, 44, 44, 44, + 507, 507, 507, 507, 508, 507, 507, 509, 510, 507, 44, 44, 44, 44, 44, 44, + 511, 44, 44, 44, 44, 44, 44, 44, 512, 512, 512, 512, 512, 512, 513, 514, + 515, 516, 271, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 517, + 0, 0, 518, 0, 0, 0, 519, 520, 521, 0, 522, 0, 0, 0, 523, 44, + 11, 11, 11, 11, 524, 44, 44, 44, 0, 0, 0, 0, 0, 233, 0, 239, + 0, 0, 0, 0, 0, 223, 0, 0, 0, 525, 526, 527, 528, 0, 0, 0, + 529, 530, 0, 531, 532, 533, 0, 0, 0, 0, 236, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 534, 0, 0, 0, 535, 535, 535, 535, 535, 535, 535, 535, + 536, 537, 538, 44, 44, 44, 44, 44, 539, 539, 539, 539, 539, 539, 539, 539, + 539, 539, 539, 539, 540, 541, 44, 44, 542, 27, 543, 544, 545, 546, 547, 548, + 549, 550, 551, 550, 44, 44, 44, 330, 0, 0, 255, 0, 0, 0, 0, 0, + 0, 271, 225, 340, 340, 340, 0, 517, 552, 0, 225, 0, 0, 0, 255, 0, + 0, 232, 44, 44, 44, 44, 553, 0, 554, 0, 0, 232, 523, 239, 44, 44, + 0, 0, 0, 0, 0, 0, 0, 555, 0, 0, 528, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 556, 552, 271, 0, 0, 0, 0, 0, 0, 0, 271, + 0, 0, 0, 0, 0, 557, 44, 44, 255, 0, 0, 0, 558, 290, 0, 0, + 558, 0, 559, 44, 44, 44, 44, 44, 44, 523, 44, 44, 44, 44, 44, 44, + 557, 44, 44, 44, 556, 44, 44, 44, 251, 251, 251, 251, 251, 560, 44, 44, + 251, 251, 251, 561, 251, 251, 251, 251, 251, 324, 251, 251, 251, 251, 251, 251, + 251, 251, 562, 44, 44, 44, 44, 44, 251, 324, 44, 44, 44, 44, 44, 44, + 563, 44, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 44, +}; + +static RE_UINT16 re_script_stage_4[] = { + 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 3, 0, 0, 0, 4, 0, + 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 5, 0, 2, 5, 6, 0, + 7, 7, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 8, 8, 8, 8, + 16, 8, 8, 8, 17, 18, 18, 18, 19, 19, 19, 19, 19, 20, 19, 19, + 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 21, 22, 22, 22, 24, 21, + 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 12, 26, 26, 27, 12, + 26, 28, 12, 12, 29, 30, 29, 31, 29, 29, 32, 33, 29, 29, 29, 29, + 31, 29, 34, 7, 7, 35, 29, 29, 36, 29, 29, 29, 29, 29, 29, 30, + 37, 37, 37, 38, 37, 37, 37, 37, 37, 37, 39, 40, 41, 41, 41, 41, + 42, 12, 12, 12, 43, 43, 43, 43, 43, 43, 44, 12, 45, 45, 45, 45, + 45, 45, 45, 46, 45, 45, 45, 47, 48, 48, 48, 48, 48, 48, 48, 49, + 12, 12, 12, 12, 29, 50, 12, 12, 51, 29, 29, 29, 52, 52, 52, 52, + 53, 52, 52, 52, 52, 54, 52, 52, 55, 56, 55, 57, 57, 55, 55, 55, + 55, 55, 58, 55, 59, 60, 61, 55, 55, 57, 57, 62, 12, 63, 12, 64, + 55, 60, 55, 55, 55, 55, 55, 12, 65, 65, 66, 67, 68, 69, 69, 69, + 69, 69, 70, 69, 70, 71, 72, 70, 66, 67, 68, 72, 73, 12, 65, 74, + 12, 75, 69, 69, 69, 72, 12, 12, 76, 76, 77, 78, 78, 77, 77, 77, + 77, 77, 79, 77, 79, 76, 80, 77, 77, 78, 78, 80, 81, 12, 12, 12, + 77, 82, 77, 77, 80, 12, 83, 12, 84, 84, 85, 86, 86, 85, 85, 85, + 85, 85, 87, 85, 87, 84, 88, 85, 85, 86, 86, 88, 12, 89, 12, 90, + 85, 89, 85, 85, 85, 85, 12, 12, 91, 92, 93, 91, 94, 95, 96, 94, + 97, 98, 93, 91, 99, 99, 95, 91, 93, 91, 94, 95, 98, 97, 12, 12, + 12, 91, 99, 99, 99, 99, 93, 12, 100, 101, 100, 102, 102, 100, 100, 100, + 100, 100, 102, 100, 100, 100, 103, 101, 100, 102, 102, 103, 12, 104, 105, 12, + 100, 106, 100, 100, 12, 12, 100, 100, 107, 107, 108, 109, 109, 108, 108, 108, + 108, 108, 109, 108, 108, 107, 110, 108, 108, 109, 109, 110, 12, 111, 12, 112, + 108, 113, 108, 108, 111, 12, 12, 12, 114, 114, 115, 116, 116, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 117, 114, 115, 116, 116, 117, 12, 118, 12, 118, + 115, 119, 115, 115, 115, 120, 114, 115, 121, 122, 123, 123, 123, 124, 121, 123, + 123, 123, 123, 123, 125, 123, 123, 126, 123, 124, 127, 128, 123, 129, 123, 123, + 12, 121, 123, 123, 121, 130, 12, 12, 131, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 133, 134, 132, 132, 132, 12, 135, 136, 137, 138, 12, 139, 140, 139, + 140, 141, 142, 140, 139, 139, 143, 144, 139, 137, 139, 144, 139, 139, 144, 139, + 145, 145, 145, 145, 145, 145, 146, 145, 145, 145, 145, 147, 146, 145, 145, 145, + 145, 145, 145, 148, 145, 149, 150, 12, 151, 151, 151, 151, 152, 152, 152, 152, + 152, 153, 12, 154, 152, 152, 155, 152, 156, 156, 156, 156, 157, 157, 157, 157, + 157, 157, 158, 159, 157, 160, 158, 159, 158, 159, 157, 160, 158, 159, 157, 157, + 157, 160, 157, 157, 157, 157, 160, 161, 157, 157, 157, 162, 157, 157, 159, 12, + 163, 163, 163, 163, 163, 164, 163, 164, 165, 165, 165, 165, 166, 166, 166, 166, + 166, 166, 166, 167, 168, 168, 168, 168, 168, 168, 169, 170, 168, 168, 171, 12, + 172, 172, 172, 173, 172, 174, 12, 12, 175, 175, 175, 175, 175, 176, 12, 12, + 177, 177, 177, 177, 177, 12, 12, 12, 178, 178, 178, 179, 179, 12, 12, 12, + 180, 180, 180, 180, 180, 180, 180, 181, 180, 180, 181, 12, 182, 183, 184, 185, + 184, 184, 186, 12, 184, 184, 184, 184, 184, 184, 12, 12, 184, 184, 185, 12, + 165, 187, 12, 12, 188, 188, 188, 188, 188, 188, 188, 189, 188, 188, 188, 12, + 190, 188, 188, 188, 191, 191, 191, 191, 191, 191, 191, 192, 191, 193, 12, 12, + 194, 194, 194, 194, 194, 194, 194, 12, 194, 194, 195, 12, 194, 194, 196, 197, + 198, 198, 198, 198, 198, 198, 198, 199, 200, 200, 200, 200, 200, 200, 200, 201, + 200, 200, 200, 202, 200, 200, 203, 12, 200, 200, 200, 203, 7, 7, 7, 204, + 205, 205, 205, 205, 205, 205, 205, 12, 205, 205, 205, 206, 207, 207, 207, 207, + 208, 208, 208, 208, 208, 12, 12, 208, 209, 209, 209, 209, 209, 209, 210, 209, + 209, 209, 211, 212, 213, 213, 213, 213, 207, 207, 12, 12, 214, 7, 7, 7, + 215, 7, 216, 217, 0, 218, 219, 12, 2, 220, 221, 2, 2, 2, 2, 222, + 223, 220, 224, 2, 2, 2, 225, 2, 2, 2, 2, 226, 7, 219, 12, 7, + 8, 227, 8, 227, 8, 8, 228, 228, 8, 8, 8, 227, 8, 15, 8, 8, + 8, 10, 8, 229, 10, 15, 8, 14, 0, 0, 0, 230, 0, 231, 0, 0, + 232, 0, 0, 233, 0, 0, 0, 234, 2, 2, 2, 235, 236, 12, 12, 12, + 0, 237, 238, 0, 4, 0, 0, 0, 0, 0, 0, 4, 2, 2, 5, 12, + 0, 0, 234, 12, 0, 234, 12, 12, 239, 239, 239, 239, 0, 240, 0, 0, + 0, 241, 0, 0, 0, 0, 241, 242, 0, 0, 231, 0, 241, 12, 12, 12, + 12, 12, 12, 0, 243, 243, 243, 243, 243, 243, 243, 244, 18, 18, 18, 18, + 18, 12, 245, 18, 246, 246, 246, 246, 246, 246, 12, 247, 248, 12, 12, 247, + 157, 160, 12, 12, 157, 160, 157, 160, 234, 12, 12, 12, 249, 249, 249, 249, + 249, 249, 250, 249, 249, 12, 12, 12, 249, 251, 12, 12, 0, 0, 0, 12, + 0, 252, 0, 0, 253, 249, 254, 255, 0, 0, 249, 0, 256, 257, 257, 257, + 257, 257, 257, 257, 257, 258, 259, 260, 261, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 263, 261, 12, 264, 265, 265, 265, 265, 265, 265, 265, 265, 265, 266, + 267, 156, 156, 156, 156, 156, 156, 268, 265, 265, 269, 12, 0, 12, 12, 12, + 156, 156, 156, 270, 262, 262, 262, 271, 262, 262, 0, 0, 272, 272, 272, 272, + 272, 272, 272, 273, 272, 274, 12, 12, 275, 275, 275, 275, 276, 276, 276, 276, + 276, 276, 276, 12, 277, 277, 277, 277, 277, 277, 12, 12, 238, 2, 2, 2, + 2, 2, 233, 2, 2, 2, 2, 278, 2, 2, 12, 12, 12, 279, 2, 2, + 280, 280, 280, 280, 280, 280, 280, 12, 0, 0, 241, 12, 281, 281, 281, 281, + 281, 281, 12, 12, 282, 282, 282, 282, 282, 283, 12, 284, 282, 282, 285, 12, + 52, 52, 52, 286, 287, 287, 287, 287, 287, 287, 287, 288, 289, 289, 289, 289, + 289, 12, 12, 290, 156, 156, 156, 291, 292, 292, 292, 292, 292, 292, 292, 293, + 292, 292, 294, 295, 151, 151, 151, 296, 297, 297, 297, 297, 297, 298, 12, 12, + 297, 297, 297, 299, 297, 297, 299, 297, 300, 300, 300, 300, 301, 12, 12, 12, + 12, 12, 302, 300, 303, 303, 303, 303, 303, 304, 12, 12, 161, 160, 161, 160, + 161, 160, 12, 12, 2, 2, 3, 2, 2, 305, 12, 12, 303, 303, 303, 306, + 303, 303, 306, 12, 156, 12, 12, 12, 156, 268, 307, 156, 156, 156, 156, 12, + 249, 249, 249, 251, 249, 249, 251, 12, 2, 308, 12, 12, 309, 22, 12, 25, + 26, 27, 26, 310, 311, 312, 26, 26, 313, 12, 12, 12, 29, 29, 29, 314, + 315, 29, 29, 29, 29, 29, 12, 12, 29, 29, 29, 313, 7, 7, 7, 316, + 234, 0, 0, 0, 0, 234, 0, 12, 29, 317, 29, 29, 29, 29, 29, 318, + 242, 0, 0, 0, 0, 319, 262, 262, 262, 262, 262, 320, 321, 156, 321, 156, + 321, 156, 321, 291, 0, 234, 0, 234, 12, 12, 242, 241, 322, 322, 322, 323, + 322, 322, 322, 322, 322, 324, 322, 322, 322, 322, 324, 325, 322, 322, 322, 326, + 322, 322, 324, 12, 234, 134, 0, 0, 0, 134, 0, 0, 8, 8, 8, 327, + 327, 12, 12, 12, 0, 0, 0, 328, 329, 329, 329, 329, 329, 329, 329, 330, + 331, 331, 331, 331, 332, 12, 12, 12, 216, 0, 0, 0, 333, 333, 333, 333, + 333, 12, 12, 12, 334, 334, 334, 334, 334, 334, 335, 12, 336, 336, 336, 336, + 336, 336, 337, 12, 338, 338, 338, 338, 338, 338, 338, 339, 340, 340, 340, 340, + 340, 12, 340, 340, 340, 341, 12, 12, 342, 342, 342, 342, 343, 343, 343, 343, + 344, 344, 344, 344, 344, 344, 344, 345, 344, 344, 345, 12, 346, 346, 346, 346, + 346, 346, 12, 12, 347, 347, 347, 347, 347, 12, 12, 348, 349, 349, 349, 349, + 349, 350, 12, 12, 349, 351, 12, 12, 349, 349, 12, 12, 352, 353, 354, 352, + 352, 352, 352, 352, 352, 355, 356, 357, 358, 358, 358, 358, 358, 359, 358, 358, + 360, 360, 360, 360, 361, 361, 361, 361, 361, 361, 361, 362, 12, 363, 361, 361, + 364, 364, 364, 364, 365, 366, 367, 364, 368, 368, 368, 368, 368, 368, 368, 369, + 370, 370, 370, 370, 370, 370, 371, 372, 373, 373, 373, 373, 374, 374, 374, 374, + 374, 374, 12, 374, 375, 374, 374, 374, 376, 377, 12, 376, 376, 378, 378, 376, + 376, 376, 376, 376, 376, 12, 379, 380, 376, 376, 12, 12, 376, 376, 381, 12, + 382, 382, 382, 382, 383, 383, 383, 383, 384, 384, 384, 384, 384, 385, 386, 384, + 384, 385, 12, 12, 387, 387, 387, 387, 387, 388, 389, 387, 390, 390, 390, 390, + 390, 391, 390, 390, 392, 392, 392, 392, 393, 12, 392, 392, 394, 394, 394, 394, + 395, 12, 396, 397, 12, 12, 396, 394, 398, 398, 398, 398, 398, 398, 399, 12, + 400, 400, 400, 400, 401, 12, 12, 12, 401, 12, 402, 400, 29, 29, 29, 403, + 404, 404, 404, 404, 404, 404, 404, 405, 406, 404, 404, 404, 12, 12, 12, 407, + 408, 408, 408, 408, 409, 12, 12, 12, 410, 410, 410, 410, 410, 410, 411, 12, + 410, 410, 412, 12, 413, 413, 413, 413, 413, 414, 413, 413, 413, 12, 12, 12, + 415, 415, 415, 415, 415, 416, 12, 12, 417, 417, 417, 417, 417, 417, 417, 418, + 122, 123, 123, 123, 123, 130, 12, 12, 419, 419, 419, 419, 420, 419, 419, 419, + 419, 419, 419, 421, 422, 423, 424, 425, 422, 422, 422, 425, 422, 422, 426, 12, + 427, 427, 427, 427, 427, 427, 428, 12, 427, 427, 429, 12, 430, 431, 430, 432, + 432, 430, 430, 430, 430, 430, 433, 430, 433, 431, 434, 430, 430, 432, 432, 434, + 435, 436, 12, 431, 430, 437, 430, 435, 430, 435, 12, 12, 438, 438, 438, 438, + 438, 438, 12, 12, 438, 438, 439, 12, 440, 440, 440, 440, 440, 441, 440, 440, + 440, 440, 440, 441, 442, 442, 442, 442, 442, 443, 12, 12, 442, 442, 444, 12, + 445, 445, 445, 445, 445, 445, 12, 12, 445, 445, 446, 12, 447, 447, 447, 447, + 447, 447, 448, 449, 447, 447, 447, 12, 450, 450, 450, 450, 451, 12, 12, 452, + 453, 453, 453, 453, 453, 453, 454, 12, 455, 455, 455, 455, 455, 455, 456, 12, + 455, 455, 455, 457, 455, 458, 12, 12, 455, 12, 12, 12, 459, 459, 459, 459, + 459, 459, 459, 460, 461, 461, 461, 461, 461, 462, 12, 12, 277, 277, 463, 12, + 464, 464, 464, 464, 464, 464, 464, 465, 464, 464, 466, 467, 468, 468, 468, 468, + 468, 468, 468, 469, 468, 469, 12, 12, 470, 470, 470, 470, 470, 471, 12, 12, + 470, 470, 472, 470, 472, 470, 470, 470, 470, 470, 12, 473, 474, 474, 474, 474, + 474, 475, 12, 12, 474, 474, 474, 476, 12, 12, 12, 477, 478, 12, 12, 12, + 479, 479, 479, 479, 479, 479, 480, 12, 479, 479, 479, 481, 479, 479, 481, 12, + 479, 479, 482, 479, 0, 241, 12, 12, 0, 234, 242, 0, 0, 483, 230, 0, + 0, 0, 483, 7, 214, 484, 7, 0, 0, 0, 485, 230, 0, 0, 486, 12, + 8, 227, 12, 12, 0, 0, 0, 231, 487, 488, 242, 231, 0, 0, 489, 242, + 0, 242, 0, 0, 0, 489, 234, 242, 0, 231, 0, 231, 0, 0, 489, 234, + 0, 490, 240, 0, 231, 0, 0, 0, 0, 0, 0, 240, 491, 491, 491, 491, + 491, 491, 491, 12, 12, 12, 492, 491, 493, 491, 491, 491, 494, 494, 494, 494, + 494, 495, 494, 494, 494, 496, 12, 12, 29, 497, 29, 29, 498, 499, 497, 29, + 403, 29, 500, 12, 501, 51, 500, 497, 498, 499, 500, 500, 498, 499, 403, 29, + 403, 29, 497, 502, 29, 29, 503, 29, 29, 29, 29, 12, 497, 497, 503, 29, + 0, 0, 0, 486, 12, 240, 0, 0, 504, 12, 12, 12, 0, 0, 489, 0, + 486, 12, 12, 12, 0, 486, 12, 12, 0, 0, 12, 12, 0, 0, 0, 241, + 249, 505, 12, 12, 249, 506, 12, 12, 251, 12, 12, 12, 507, 12, 12, 12, +}; + +static RE_UINT8 re_script_stage_5[] = { + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 35, 35, 41, 41, 41, 41, + 3, 3, 3, 3, 1, 3, 3, 3, 0, 0, 3, 3, 3, 3, 1, 3, + 0, 0, 0, 0, 3, 1, 3, 1, 3, 3, 3, 0, 3, 0, 3, 3, + 3, 3, 0, 3, 3, 3, 55, 55, 55, 55, 55, 55, 4, 4, 4, 4, + 4, 41, 41, 4, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 1, 5, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, + 6, 0, 0, 0, 7, 7, 7, 7, 7, 1, 7, 7, 1, 7, 7, 7, + 7, 7, 7, 1, 1, 0, 7, 1, 7, 7, 7, 41, 41, 41, 7, 7, + 41, 7, 7, 7, 8, 8, 8, 8, 8, 8, 0, 8, 8, 8, 8, 0, + 0, 8, 8, 8, 9, 9, 9, 9, 9, 9, 0, 0, 66, 66, 66, 66, + 66, 66, 66, 0, 82, 82, 82, 82, 82, 82, 0, 0, 82, 82, 82, 0, + 95, 95, 95, 95, 0, 0, 95, 0, 7, 0, 0, 0, 0, 0, 0, 7, + 10, 10, 10, 10, 10, 41, 41, 10, 1, 1, 10, 10, 11, 11, 11, 11, + 0, 11, 11, 11, 11, 0, 0, 11, 11, 0, 11, 11, 11, 0, 11, 0, + 0, 0, 11, 11, 11, 11, 0, 0, 11, 11, 11, 0, 0, 0, 0, 11, + 11, 11, 0, 11, 0, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 12, + 12, 0, 0, 12, 12, 12, 12, 12, 12, 0, 12, 12, 0, 12, 12, 0, + 12, 12, 0, 0, 0, 12, 0, 0, 12, 0, 12, 0, 0, 0, 12, 12, + 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 0, 13, 13, 0, 13, 13, + 13, 13, 0, 0, 13, 0, 0, 0, 0, 0, 13, 13, 0, 13, 0, 0, + 0, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 14, 14, 0, 14, 14, + 14, 14, 0, 0, 0, 0, 14, 14, 14, 14, 0, 14, 0, 0, 15, 15, + 0, 15, 15, 15, 15, 15, 15, 0, 15, 0, 15, 15, 15, 15, 0, 0, + 0, 15, 15, 0, 0, 0, 0, 15, 15, 0, 0, 0, 15, 15, 15, 15, + 16, 16, 16, 16, 0, 16, 16, 16, 16, 0, 16, 16, 16, 16, 0, 0, + 0, 16, 16, 0, 16, 16, 16, 0, 0, 0, 16, 16, 0, 17, 17, 17, + 17, 17, 17, 17, 17, 0, 17, 17, 17, 17, 0, 0, 0, 17, 17, 0, + 0, 0, 17, 0, 0, 0, 17, 17, 0, 18, 18, 18, 18, 18, 18, 18, + 18, 0, 18, 18, 18, 18, 18, 0, 0, 0, 0, 18, 0, 0, 18, 18, + 18, 18, 0, 0, 0, 0, 19, 19, 0, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 0, 19, 19, 0, 19, 0, 19, 0, 0, 0, 0, 19, 0, + 0, 0, 0, 19, 19, 0, 19, 0, 19, 0, 0, 0, 0, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 1, 0, 21, 21, 0, + 21, 0, 0, 21, 21, 0, 21, 0, 0, 21, 0, 0, 21, 21, 21, 21, + 0, 21, 21, 21, 0, 21, 0, 21, 0, 0, 21, 21, 21, 21, 0, 21, + 21, 21, 0, 0, 22, 22, 22, 22, 0, 22, 22, 22, 22, 0, 0, 0, + 22, 0, 22, 22, 22, 1, 1, 1, 1, 22, 22, 0, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 0, 24, 0, 24, 0, 0, 24, 24, 24, 1, + 25, 25, 25, 25, 26, 26, 26, 26, 26, 0, 26, 26, 26, 26, 0, 0, + 26, 26, 26, 0, 0, 26, 26, 26, 26, 0, 0, 0, 27, 27, 27, 27, + 27, 27, 0, 0, 28, 28, 28, 28, 29, 29, 29, 29, 29, 0, 0, 0, + 30, 30, 30, 30, 30, 30, 30, 1, 1, 1, 30, 30, 30, 0, 0, 0, + 42, 42, 42, 42, 42, 0, 42, 42, 42, 0, 0, 0, 43, 43, 43, 43, + 43, 1, 1, 0, 44, 44, 44, 44, 45, 45, 45, 45, 45, 0, 45, 45, + 31, 31, 31, 31, 31, 31, 0, 0, 32, 32, 1, 1, 32, 1, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 0, 0, 28, 28, 0, 0, + 46, 46, 46, 46, 46, 46, 46, 0, 46, 0, 0, 0, 47, 47, 47, 47, + 47, 47, 0, 0, 47, 0, 0, 0, 56, 56, 56, 56, 56, 56, 0, 0, + 56, 56, 56, 0, 0, 0, 56, 56, 54, 54, 54, 54, 0, 0, 54, 54, + 78, 78, 78, 78, 78, 78, 78, 0, 78, 0, 0, 78, 78, 78, 0, 0, + 41, 41, 41, 0, 62, 62, 62, 62, 62, 0, 0, 0, 67, 67, 67, 67, + 93, 93, 93, 93, 68, 68, 68, 68, 0, 0, 0, 68, 68, 68, 0, 0, + 0, 68, 68, 68, 69, 69, 69, 69, 41, 41, 41, 1, 41, 1, 41, 41, + 41, 1, 1, 1, 1, 41, 1, 1, 41, 1, 1, 0, 41, 41, 0, 0, + 2, 2, 3, 3, 3, 3, 3, 4, 2, 3, 3, 3, 3, 3, 2, 2, + 3, 3, 3, 2, 4, 2, 2, 2, 2, 2, 2, 3, 3, 3, 0, 0, + 0, 3, 0, 3, 0, 3, 3, 3, 41, 41, 1, 1, 1, 0, 1, 1, + 1, 2, 0, 0, 1, 1, 1, 2, 1, 1, 1, 0, 2, 0, 0, 0, + 41, 0, 0, 0, 1, 1, 3, 1, 1, 1, 2, 2, 53, 53, 53, 53, + 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 57, 57, 57, 57, + 57, 57, 57, 0, 0, 55, 55, 55, 58, 58, 58, 58, 0, 0, 0, 58, + 58, 0, 0, 0, 36, 36, 36, 36, 36, 36, 0, 36, 36, 36, 0, 0, + 1, 36, 1, 36, 1, 36, 36, 36, 36, 36, 41, 41, 41, 41, 25, 25, + 0, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 0, 0, 41, 41, 1, + 1, 33, 33, 33, 1, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 1, + 0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 0, 0, 0, 25, 25, 25, + 25, 25, 25, 0, 35, 35, 35, 0, 25, 25, 25, 1, 34, 34, 34, 0, + 37, 37, 37, 37, 37, 0, 0, 0, 37, 37, 37, 0, 83, 83, 83, 83, + 70, 70, 70, 70, 84, 84, 84, 84, 2, 2, 0, 0, 0, 0, 0, 2, + 59, 59, 59, 59, 65, 65, 65, 65, 71, 71, 71, 71, 71, 0, 0, 0, + 0, 0, 71, 71, 71, 71, 0, 0, 10, 10, 0, 0, 72, 72, 72, 72, + 72, 72, 1, 72, 73, 73, 73, 73, 0, 0, 0, 73, 25, 0, 0, 0, + 85, 85, 85, 85, 85, 85, 0, 1, 85, 85, 0, 0, 0, 0, 85, 85, + 23, 23, 23, 0, 77, 77, 77, 77, 77, 77, 77, 0, 77, 77, 0, 0, + 79, 79, 79, 79, 79, 79, 79, 0, 0, 0, 0, 79, 86, 86, 86, 86, + 86, 86, 86, 0, 2, 3, 0, 0, 86, 86, 0, 0, 0, 0, 0, 25, + 2, 2, 2, 0, 0, 0, 0, 5, 6, 0, 6, 0, 6, 6, 0, 6, + 6, 0, 6, 6, 7, 7, 0, 0, 7, 7, 1, 1, 0, 0, 7, 7, + 41, 41, 4, 4, 7, 0, 7, 7, 7, 0, 0, 1, 1, 1, 34, 34, + 34, 34, 1, 1, 0, 0, 25, 25, 48, 48, 48, 48, 0, 48, 48, 48, + 48, 48, 48, 0, 48, 48, 0, 48, 48, 48, 0, 0, 3, 0, 0, 0, + 1, 41, 0, 0, 74, 74, 74, 74, 74, 0, 0, 0, 75, 75, 75, 75, + 75, 0, 0, 0, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 0, + 120, 120, 120, 120, 120, 120, 120, 0, 49, 49, 49, 49, 49, 49, 0, 49, + 60, 60, 60, 60, 60, 60, 0, 0, 40, 40, 40, 40, 50, 50, 50, 50, + 51, 51, 51, 51, 51, 51, 0, 0, 106, 106, 106, 106, 103, 103, 103, 103, + 0, 0, 0, 103, 110, 110, 110, 110, 110, 110, 110, 0, 110, 110, 0, 0, + 52, 52, 52, 52, 52, 52, 0, 0, 52, 0, 52, 52, 52, 52, 0, 52, + 52, 0, 0, 0, 52, 0, 0, 52, 87, 87, 87, 87, 87, 87, 0, 87, + 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 0, 0, 0, 0, 117, + 128, 128, 128, 128, 128, 128, 128, 0, 128, 128, 0, 0, 0, 0, 0, 128, + 64, 64, 64, 64, 0, 0, 0, 64, 76, 76, 76, 76, 76, 76, 0, 0, + 0, 0, 0, 76, 98, 98, 98, 98, 97, 97, 97, 97, 0, 0, 97, 97, + 61, 61, 61, 61, 0, 61, 61, 0, 0, 61, 61, 61, 61, 61, 61, 0, + 0, 0, 0, 61, 61, 0, 0, 0, 88, 88, 88, 88, 116, 116, 116, 116, + 112, 112, 112, 112, 112, 112, 112, 0, 0, 0, 0, 112, 80, 80, 80, 80, + 80, 80, 0, 0, 0, 80, 80, 80, 89, 89, 89, 89, 89, 89, 0, 0, + 90, 90, 90, 90, 90, 90, 90, 0, 121, 121, 121, 121, 121, 121, 0, 0, + 0, 121, 121, 121, 121, 0, 0, 0, 91, 91, 91, 91, 91, 0, 0, 0, + 130, 130, 130, 130, 130, 130, 130, 0, 0, 0, 130, 130, 7, 7, 7, 0, + 94, 94, 94, 94, 94, 94, 0, 0, 0, 0, 94, 94, 0, 0, 0, 94, + 92, 92, 92, 92, 92, 92, 0, 0, 101, 101, 101, 101, 101, 0, 0, 0, + 101, 101, 0, 0, 96, 96, 96, 96, 96, 0, 96, 96, 111, 111, 111, 111, + 111, 111, 111, 0, 100, 100, 100, 100, 100, 100, 0, 0, 109, 109, 109, 109, + 109, 109, 0, 109, 109, 109, 0, 0, 129, 129, 129, 129, 129, 129, 129, 0, + 129, 0, 129, 129, 129, 129, 0, 129, 129, 129, 0, 0, 123, 123, 123, 123, + 123, 123, 123, 0, 123, 123, 0, 0, 107, 107, 107, 107, 0, 107, 107, 107, + 107, 0, 0, 107, 107, 0, 107, 107, 107, 107, 0, 0, 107, 0, 0, 0, + 0, 0, 0, 107, 0, 0, 107, 107, 124, 124, 124, 124, 124, 124, 0, 0, + 122, 122, 122, 122, 122, 122, 0, 0, 114, 114, 114, 114, 114, 0, 0, 0, + 114, 114, 0, 0, 102, 102, 102, 102, 102, 102, 0, 0, 126, 126, 126, 126, + 126, 126, 0, 0, 0, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 0, + 0, 0, 0, 125, 119, 119, 119, 119, 119, 0, 0, 0, 63, 63, 63, 63, + 63, 63, 0, 0, 63, 63, 63, 0, 63, 0, 0, 0, 81, 81, 81, 81, + 81, 81, 81, 0, 127, 127, 127, 127, 127, 127, 127, 0, 84, 0, 0, 0, + 115, 115, 115, 115, 115, 115, 115, 0, 115, 115, 0, 0, 0, 0, 115, 115, + 104, 104, 104, 104, 104, 104, 0, 0, 108, 108, 108, 108, 108, 108, 0, 0, + 108, 108, 0, 108, 0, 108, 108, 108, 99, 99, 99, 99, 99, 0, 0, 0, + 99, 99, 99, 0, 0, 0, 0, 99, 34, 33, 0, 0, 105, 105, 105, 105, + 105, 105, 105, 0, 105, 0, 0, 0, 105, 105, 0, 0, 1, 1, 1, 41, + 1, 41, 41, 41, 1, 1, 41, 41, 1, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 131, 131, 131, 131, + 0, 0, 0, 131, 0, 131, 131, 131, 113, 113, 113, 113, 113, 0, 0, 113, + 113, 113, 113, 0, 0, 7, 7, 7, 0, 7, 7, 0, 7, 0, 0, 7, + 0, 7, 0, 7, 0, 0, 7, 0, 7, 0, 7, 0, 7, 7, 0, 7, + 33, 1, 1, 0, 36, 36, 36, 0, 36, 0, 0, 0, 0, 1, 0, 0, +}; + +/* Script: 10928 bytes. */ + +RE_UINT32 re_get_script(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 11; + code = ch ^ (f << 11); + pos = (RE_UINT32)re_script_stage_1[f] << 4; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_script_stage_2[pos + f] << 3; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_script_stage_3[pos + f] << 2; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_script_stage_4[pos + f] << 2; + value = re_script_stage_5[pos + code]; + + return value; +} + +/* Word_Break. */ + +static RE_UINT8 re_word_break_stage_1[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, 6, 7, 4, 8, + 9, 10, 11, 12, 13, 4, 14, 4, 4, 4, 4, 15, 4, 16, 17, 18, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 19, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +}; + +static RE_UINT8 re_word_break_stage_2[] = { + 0, 1, 2, 2, 2, 3, 4, 5, 2, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 2, 2, 31, 32, 33, 34, 35, 2, 2, 2, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 2, 2, 51, 52, + 53, 54, 55, 56, 57, 57, 57, 57, 57, 58, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 59, 60, 61, 62, 63, 57, 57, 57, + 64, 65, 66, 67, 57, 68, 69, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 70, 2, 2, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 83, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 84, 85, 2, 2, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 57, 96, 97, 98, 2, 99, 100, 57, 2, 2, 101, 57, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 57, 57, 57, 57, 57, 57, + 112, 113, 114, 115, 116, 117, 118, 57, 57, 119, 57, 120, 121, 122, 123, 57, + 57, 124, 57, 57, 57, 125, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 2, 2, 2, 2, 2, 2, 2, 126, 127, 2, 128, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 2, 2, 2, 2, 2, 2, 2, 2, 129, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 2, 2, 130, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 2, 2, 2, 2, 131, 132, 133, 134, 57, 57, 57, 57, 57, 57, 135, 136, + 137, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 138, 139, 57, 57, 57, 57, 57, 57, + 57, 57, 140, 141, 142, 57, 57, 57, 143, 144, 145, 2, 2, 146, 147, 148, + 57, 57, 57, 57, 149, 150, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 2, 151, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 152, 153, 57, 57, + 57, 57, 154, 155, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 156, 57, 157, 158, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, +}; + +static RE_UINT8 re_word_break_stage_3[] = { + 0, 1, 0, 0, 2, 3, 4, 5, 6, 7, 7, 8, 6, 7, 7, 9, + 10, 0, 0, 0, 0, 11, 12, 13, 7, 7, 14, 7, 7, 7, 14, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 7, 16, 0, 17, 18, 0, 0, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 21, + 22, 23, 7, 7, 24, 7, 7, 7, 7, 7, 7, 7, 7, 7, 25, 7, + 26, 27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 14, 28, 6, 7, 7, 7, + 7, 29, 30, 19, 19, 19, 19, 31, 32, 0, 33, 33, 33, 34, 35, 0, + 36, 37, 19, 38, 7, 7, 7, 7, 7, 39, 19, 19, 4, 40, 41, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 42, 43, 44, 45, 4, 46, + 0, 47, 48, 7, 7, 7, 19, 19, 19, 49, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 50, 19, 51, 0, 4, 52, 7, 7, 7, 39, 53, 54, + 7, 7, 50, 55, 56, 57, 0, 0, 7, 7, 7, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 17, 0, 0, 0, 0, 0, 59, 19, 19, 19, + 60, 7, 7, 7, 7, 7, 7, 61, 19, 19, 62, 7, 63, 4, 6, 7, + 64, 65, 66, 7, 7, 67, 68, 69, 70, 71, 72, 73, 63, 4, 74, 0, + 75, 76, 66, 7, 7, 67, 77, 78, 79, 80, 81, 82, 83, 4, 84, 0, + 75, 25, 24, 7, 7, 67, 85, 69, 31, 86, 87, 0, 63, 4, 0, 28, + 75, 65, 66, 7, 7, 67, 85, 69, 70, 80, 88, 73, 63, 4, 28, 0, + 89, 90, 91, 92, 93, 90, 7, 94, 95, 96, 97, 0, 83, 4, 0, 0, + 98, 20, 67, 7, 7, 67, 7, 99, 100, 96, 101, 9, 63, 4, 0, 0, + 75, 20, 67, 7, 7, 67, 102, 69, 100, 96, 101, 103, 63, 4, 104, 0, + 75, 20, 67, 7, 7, 7, 7, 105, 100, 106, 72, 107, 63, 4, 0, 108, + 109, 7, 14, 108, 7, 7, 24, 110, 14, 111, 112, 19, 83, 4, 113, 0, + 0, 0, 0, 0, 0, 0, 114, 115, 72, 116, 4, 117, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 114, 118, 0, 119, 4, 117, 0, 0, 0, 0, + 87, 0, 0, 120, 4, 117, 121, 122, 7, 6, 7, 7, 7, 17, 30, 19, + 100, 123, 19, 30, 19, 19, 19, 124, 125, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 59, 19, 116, 4, 117, 88, 126, 127, 119, 128, 0, + 129, 31, 4, 130, 7, 7, 7, 7, 25, 131, 7, 7, 7, 7, 7, 132, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 91, 14, 91, 7, 7, 7, 7, + 7, 91, 7, 7, 7, 7, 91, 14, 91, 7, 14, 7, 7, 7, 7, 7, + 7, 7, 91, 7, 7, 7, 7, 7, 7, 7, 7, 133, 0, 0, 0, 0, + 7, 7, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 134, 134, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 65, 7, 7, + 6, 7, 7, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 90, 7, 87, + 7, 20, 135, 0, 7, 7, 135, 0, 7, 7, 136, 0, 7, 20, 137, 0, + 0, 0, 0, 0, 0, 0, 138, 19, 19, 19, 139, 140, 4, 117, 0, 0, + 0, 141, 4, 117, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, + 7, 7, 7, 7, 7, 142, 7, 7, 7, 7, 7, 7, 7, 7, 134, 0, + 7, 7, 7, 14, 19, 139, 19, 139, 83, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 117, 0, 0, 0, 0, + 7, 7, 143, 139, 0, 0, 0, 0, 0, 0, 144, 116, 19, 19, 19, 70, + 4, 117, 4, 117, 0, 0, 19, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 145, 7, 7, 7, 7, 7, 146, 19, 145, 147, 4, 117, 0, 59, 139, 0, + 148, 7, 7, 7, 62, 149, 4, 52, 7, 7, 7, 7, 50, 19, 139, 0, + 7, 7, 7, 7, 146, 19, 19, 0, 4, 150, 4, 52, 7, 7, 7, 134, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 19, 19, 152, 153, 120, + 7, 7, 7, 7, 7, 7, 7, 7, 19, 19, 19, 19, 19, 19, 119, 138, + 7, 7, 134, 134, 7, 7, 7, 7, 134, 134, 7, 154, 7, 7, 7, 134, + 7, 7, 7, 7, 7, 7, 20, 155, 156, 17, 157, 147, 7, 17, 156, 17, + 0, 158, 0, 159, 160, 161, 0, 162, 163, 0, 164, 0, 165, 166, 28, 107, + 0, 0, 7, 17, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 167, 0, + 168, 108, 110, 169, 18, 170, 7, 171, 172, 173, 0, 0, 7, 7, 7, 7, + 7, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 174, 7, 7, 7, 7, 7, 7, 74, 0, 0, + 7, 7, 7, 7, 7, 14, 7, 7, 7, 7, 7, 14, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 17, 175, 176, 0, + 7, 7, 7, 7, 25, 131, 7, 7, 7, 7, 7, 7, 7, 107, 0, 72, + 7, 7, 14, 0, 14, 14, 14, 14, 14, 14, 14, 14, 19, 19, 19, 19, + 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 131, 0, 0, 0, 0, 129, 177, 93, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 178, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 180, + 172, 7, 7, 7, 7, 134, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 14, 0, 0, 7, 7, 7, 9, 0, 0, 0, 0, 0, 0, 179, 179, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 179, 179, 179, 179, 181, + 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 0, 0, 0, 0, 0, + 7, 17, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 134, + 7, 17, 7, 7, 4, 182, 0, 0, 7, 7, 7, 7, 7, 143, 151, 183, + 7, 7, 7, 50, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 120, 0, + 0, 0, 107, 7, 108, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 66, 7, 7, 7, 134, 7, 0, 0, 0, 0, 0, 0, 0, 107, 7, + 184, 185, 7, 7, 39, 0, 0, 0, 7, 7, 7, 7, 7, 7, 147, 0, + 27, 7, 7, 7, 7, 7, 146, 19, 124, 0, 4, 117, 19, 19, 27, 186, + 4, 52, 7, 7, 50, 119, 7, 7, 143, 19, 139, 0, 7, 7, 7, 17, + 60, 7, 7, 7, 7, 7, 39, 19, 167, 107, 4, 117, 140, 0, 4, 117, + 7, 7, 7, 7, 7, 62, 116, 0, 185, 187, 4, 117, 0, 0, 0, 188, + 0, 0, 0, 0, 0, 0, 127, 189, 81, 0, 0, 0, 7, 39, 190, 0, + 191, 191, 191, 0, 14, 14, 7, 7, 7, 7, 7, 132, 134, 0, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 39, 192, 4, 117, + 7, 7, 7, 7, 147, 0, 7, 7, 14, 193, 7, 7, 7, 7, 7, 147, + 14, 0, 193, 194, 33, 195, 196, 197, 198, 33, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 74, 0, 0, 0, 193, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 134, 0, 0, 7, 7, 7, 7, 7, 7, + 7, 7, 108, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 7, 147, + 19, 19, 199, 0, 19, 19, 200, 0, 0, 201, 202, 0, 0, 0, 20, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 203, + 204, 3, 0, 205, 6, 7, 7, 8, 6, 7, 7, 9, 206, 179, 179, 179, + 179, 179, 179, 207, 7, 7, 7, 14, 108, 108, 108, 208, 0, 0, 0, 209, + 7, 102, 7, 7, 14, 7, 7, 210, 7, 134, 7, 134, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 17, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, + 7, 7, 7, 17, 7, 7, 7, 7, 7, 7, 87, 0, 167, 0, 0, 0, + 7, 7, 7, 7, 0, 0, 7, 7, 7, 9, 7, 7, 7, 7, 50, 115, + 7, 7, 7, 134, 7, 7, 7, 7, 147, 7, 169, 0, 0, 0, 0, 0, + 7, 7, 7, 134, 4, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 147, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 14, 0, 7, 7, 134, 0, 7, 0, 0, 0, + 134, 67, 7, 7, 7, 7, 25, 211, 7, 7, 134, 0, 7, 7, 14, 0, + 7, 7, 7, 14, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 212, 0, + 7, 7, 134, 0, 7, 7, 7, 74, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 174, 0, 0, 0, 0, 0, 0, 0, 0, + 213, 138, 102, 6, 7, 7, 147, 79, 0, 0, 0, 0, 7, 7, 7, 17, + 7, 7, 7, 17, 0, 0, 0, 0, 7, 6, 7, 7, 214, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 134, 0, 7, 7, 134, 0, 7, 7, 9, 0, + 7, 7, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 87, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 9, 0, 7, 7, 7, 7, 7, 7, 9, 0, + 148, 7, 7, 7, 7, 7, 7, 19, 116, 0, 0, 0, 83, 4, 0, 72, + 148, 7, 7, 7, 7, 7, 19, 215, 0, 0, 7, 7, 7, 87, 4, 117, + 148, 7, 7, 7, 143, 19, 216, 4, 0, 0, 7, 7, 7, 7, 217, 0, + 148, 7, 7, 7, 7, 7, 39, 19, 218, 219, 4, 220, 0, 0, 0, 0, + 7, 7, 24, 7, 7, 146, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 14, 170, 7, 25, 7, 87, 7, 7, 7, 7, 7, 143, 19, 115, 4, 117, + 98, 65, 66, 7, 7, 67, 85, 69, 70, 80, 97, 172, 221, 124, 124, 0, + 7, 7, 7, 7, 7, 7, 19, 19, 222, 0, 4, 117, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 143, 119, 19, 167, 0, 0, 187, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 19, 19, 223, 0, 4, 117, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 39, 19, 0, 4, 117, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 144, 19, 139, 4, 117, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 4, 117, 0, 107, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 87, + 7, 7, 7, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 14, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 147, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 14, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 87, 7, 7, 7, 14, 4, 117, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 134, 124, 0, + 7, 7, 7, 7, 7, 7, 116, 0, 147, 0, 4, 117, 193, 7, 7, 172, + 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 17, 0, 62, 19, 19, 19, 19, 116, + 0, 72, 148, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 7, 17, + 7, 87, 7, 225, 226, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 227, 228, 229, + 230, 139, 0, 0, 0, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 219, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 20, 7, 7, 7, 7, 7, + 7, 7, 7, 20, 232, 233, 7, 234, 102, 7, 7, 7, 7, 7, 7, 7, + 25, 235, 20, 20, 7, 7, 7, 236, 155, 108, 67, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 134, 7, 7, 7, 67, 7, 7, 132, 7, 7, 7, 132, + 7, 7, 20, 7, 7, 7, 20, 7, 7, 14, 7, 7, 7, 14, 7, 7, + 7, 67, 7, 7, 7, 67, 7, 7, 132, 237, 4, 4, 4, 4, 4, 4, + 19, 19, 19, 19, 19, 19, 116, 59, 19, 19, 19, 19, 19, 124, 140, 0, + 238, 0, 0, 59, 30, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 17, 0, 116, 0, 0, 0, 0, 0, + 102, 7, 7, 7, 239, 6, 132, 240, 168, 241, 239, 154, 239, 132, 132, 82, + 7, 24, 7, 147, 242, 24, 7, 147, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 7, 7, 74, 7, 7, 7, 74, 7, 7, + 7, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 244, 244, 244, + 245, 0, 0, 0, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, +}; + +static RE_UINT8 re_word_break_stage_4[] = { + 0, 0, 1, 2, 3, 4, 0, 5, 6, 6, 7, 0, 8, 9, 9, 9, + 10, 11, 10, 0, 0, 12, 13, 14, 0, 15, 13, 0, 9, 10, 16, 17, + 16, 18, 9, 19, 0, 20, 21, 21, 9, 22, 17, 23, 0, 24, 10, 22, + 25, 9, 9, 25, 26, 21, 27, 9, 28, 0, 29, 0, 30, 21, 21, 31, + 32, 31, 33, 33, 34, 0, 35, 36, 37, 38, 0, 39, 40, 41, 42, 21, + 43, 44, 45, 9, 9, 46, 21, 47, 21, 48, 49, 27, 50, 51, 0, 52, + 53, 9, 40, 8, 9, 54, 55, 0, 50, 9, 21, 16, 56, 0, 57, 21, + 21, 58, 58, 59, 58, 0, 60, 21, 21, 9, 54, 61, 58, 21, 54, 62, + 58, 8, 9, 51, 51, 9, 22, 9, 20, 17, 16, 61, 21, 63, 63, 64, + 0, 60, 0, 25, 16, 0, 30, 8, 10, 65, 22, 66, 16, 49, 40, 60, + 63, 59, 67, 0, 8, 20, 0, 62, 27, 68, 22, 8, 31, 59, 19, 0, + 0, 69, 70, 8, 10, 17, 22, 16, 66, 22, 65, 19, 16, 69, 40, 69, + 49, 59, 19, 60, 21, 8, 16, 46, 21, 49, 0, 32, 9, 8, 0, 13, + 66, 0, 10, 46, 49, 64, 0, 65, 17, 9, 69, 8, 9, 28, 71, 60, + 21, 72, 69, 0, 67, 21, 40, 0, 21, 40, 73, 0, 31, 74, 21, 59, + 59, 0, 0, 75, 67, 69, 9, 58, 21, 74, 0, 71, 59, 69, 49, 63, + 30, 74, 69, 21, 76, 59, 0, 28, 10, 9, 10, 30, 9, 16, 54, 74, + 54, 0, 77, 0, 0, 21, 21, 0, 0, 67, 60, 78, 79, 0, 9, 42, + 0, 30, 21, 45, 9, 21, 9, 0, 80, 9, 21, 27, 73, 8, 40, 21, + 45, 53, 54, 81, 82, 82, 9, 20, 17, 22, 9, 17, 0, 83, 84, 0, + 0, 85, 86, 87, 0, 11, 88, 89, 0, 88, 37, 90, 37, 37, 74, 0, + 13, 65, 8, 16, 22, 25, 16, 9, 0, 8, 16, 13, 0, 17, 65, 42, + 27, 0, 91, 92, 93, 94, 95, 95, 96, 95, 95, 96, 50, 0, 21, 97, + 98, 98, 42, 9, 65, 28, 9, 59, 60, 59, 74, 69, 17, 99, 8, 10, + 40, 59, 65, 9, 0, 100, 101, 33, 33, 34, 33, 102, 103, 101, 104, 89, + 11, 88, 0, 105, 5, 106, 9, 107, 0, 108, 109, 0, 0, 110, 95, 111, + 17, 19, 112, 0, 10, 25, 19, 51, 10, 16, 58, 32, 9, 99, 40, 14, + 21, 113, 42, 13, 45, 19, 69, 74, 114, 19, 54, 69, 21, 25, 74, 19, + 94, 0, 16, 32, 37, 0, 59, 30, 115, 37, 116, 21, 40, 30, 69, 59, + 13, 66, 8, 22, 25, 8, 10, 8, 25, 10, 9, 62, 0, 74, 66, 51, + 82, 0, 82, 8, 8, 8, 0, 117, 118, 118, 14, 0, +}; + +static RE_UINT8 re_word_break_stage_5[] = { + 0, 0, 0, 0, 0, 0, 5, 6, 6, 4, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 13, 0, 14, 0, 15, 15, 15, 15, 15, 15, 12, 13, + 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 0, 0, 0, 16, + 0, 6, 0, 0, 0, 0, 11, 0, 0, 9, 0, 0, 0, 11, 0, 12, + 11, 11, 0, 0, 0, 0, 11, 11, 0, 0, 0, 12, 11, 0, 0, 0, + 11, 0, 11, 0, 7, 7, 7, 7, 11, 0, 11, 11, 11, 11, 13, 11, + 0, 0, 11, 12, 11, 11, 0, 11, 11, 11, 0, 7, 7, 7, 11, 11, + 0, 11, 0, 0, 0, 13, 0, 0, 0, 7, 7, 7, 7, 7, 0, 7, + 0, 7, 7, 0, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 11, + 12, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 13, 13, 0, 0, + 7, 7, 7, 0, 9, 0, 0, 0, 11, 11, 11, 7, 15, 15, 0, 15, + 13, 0, 11, 11, 7, 11, 11, 11, 0, 11, 7, 7, 7, 9, 0, 7, + 7, 11, 11, 7, 7, 0, 7, 7, 15, 15, 11, 11, 11, 0, 0, 11, + 0, 0, 0, 9, 11, 7, 11, 11, 11, 11, 7, 7, 7, 11, 0, 0, + 13, 0, 11, 0, 7, 7, 11, 7, 11, 7, 7, 7, 7, 7, 0, 0, + 0, 0, 0, 7, 7, 11, 7, 7, 0, 0, 15, 15, 7, 0, 0, 7, + 7, 7, 11, 0, 0, 0, 0, 11, 0, 11, 11, 0, 0, 7, 0, 0, + 11, 7, 0, 0, 0, 0, 7, 7, 0, 0, 7, 11, 0, 0, 7, 0, + 7, 0, 7, 0, 15, 15, 0, 0, 7, 0, 0, 0, 0, 7, 0, 7, + 15, 15, 7, 7, 11, 0, 7, 7, 7, 7, 9, 0, 11, 7, 11, 0, + 7, 7, 7, 11, 7, 11, 11, 0, 0, 11, 0, 11, 7, 7, 9, 9, + 14, 14, 0, 0, 14, 0, 0, 12, 6, 6, 9, 9, 9, 9, 9, 0, + 16, 0, 0, 0, 13, 0, 0, 0, 9, 0, 9, 9, 0, 10, 10, 10, + 10, 10, 0, 0, 0, 7, 7, 10, 10, 0, 0, 0, 10, 10, 10, 10, + 10, 10, 10, 0, 7, 7, 0, 11, 11, 11, 7, 11, 11, 7, 7, 0, + 0, 3, 7, 3, 3, 0, 3, 3, 3, 0, 3, 0, 3, 3, 0, 3, + 13, 0, 0, 12, 0, 16, 16, 16, 13, 12, 0, 0, 11, 0, 0, 9, + 0, 0, 0, 14, 0, 0, 12, 13, 0, 0, 10, 10, 10, 10, 7, 7, + 0, 9, 9, 9, 7, 0, 15, 15, 15, 15, 11, 0, 7, 7, 7, 9, + 9, 9, 9, 7, 0, 0, 8, 8, 8, 8, 8, 8, +}; + +/* Word_Break: 4424 bytes. */ + +RE_UINT32 re_get_word_break(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_word_break_stage_1[f] << 5; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_word_break_stage_2[pos + f] << 4; + f = code >> 3; + code ^= f << 3; + pos = (RE_UINT32)re_word_break_stage_3[pos + f] << 1; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_word_break_stage_4[pos + f] << 2; + value = re_word_break_stage_5[pos + code]; + + return value; +} + +/* Grapheme_Cluster_Break. */ + +static RE_UINT8 re_grapheme_cluster_break_stage_1[] = { + 0, 1, 2, 2, 2, 3, 4, 5, 6, 2, 2, 7, 2, 8, 9, 10, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_grapheme_cluster_break_stage_2[] = { + 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 1, 17, 1, 1, 1, 18, 19, 20, 21, 22, 23, 24, 1, 1, + 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 26, 27, 1, 1, + 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 29, 1, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 34, 35, 36, 37, 38, 39, 40, 34, 35, 36, 37, 38, 39, + 40, 34, 35, 36, 37, 38, 39, 40, 34, 35, 36, 37, 38, 39, 40, 34, + 35, 36, 37, 38, 39, 40, 34, 41, 42, 42, 42, 42, 42, 42, 42, 42, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 43, 1, 1, 44, 45, + 1, 46, 47, 48, 1, 1, 1, 1, 1, 1, 49, 1, 1, 1, 1, 1, + 50, 51, 52, 53, 54, 55, 56, 57, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 58, 59, 1, 1, 1, 60, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 61, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 62, 63, 1, 1, 1, 1, 1, 1, 1, 64, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 65, 1, 1, 1, 1, 1, 1, 1, + 1, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 42, 67, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_grapheme_cluster_break_stage_3[] = { + 0, 1, 2, 2, 2, 2, 2, 3, 1, 1, 4, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 5, 8, 9, 2, 2, 2, + 10, 11, 2, 2, 12, 5, 2, 13, 2, 2, 2, 2, 2, 14, 15, 2, + 3, 16, 2, 5, 17, 2, 2, 2, 2, 2, 18, 13, 2, 2, 12, 19, + 2, 20, 21, 2, 2, 22, 2, 2, 2, 2, 2, 2, 2, 2, 23, 5, + 24, 2, 2, 25, 26, 27, 28, 2, 29, 2, 2, 30, 31, 32, 28, 2, + 33, 2, 2, 34, 35, 16, 2, 36, 33, 2, 2, 34, 37, 2, 28, 2, + 29, 2, 2, 38, 31, 39, 28, 2, 40, 2, 2, 41, 42, 32, 2, 2, + 43, 2, 2, 44, 45, 46, 28, 2, 29, 2, 2, 47, 48, 46, 28, 2, + 29, 2, 2, 41, 49, 32, 28, 2, 50, 2, 2, 2, 51, 52, 2, 50, + 2, 2, 2, 53, 54, 2, 2, 2, 2, 2, 2, 55, 56, 2, 2, 2, + 2, 57, 2, 58, 2, 2, 2, 59, 60, 61, 5, 62, 63, 2, 2, 2, + 2, 2, 64, 65, 2, 66, 13, 67, 68, 69, 2, 2, 2, 2, 2, 2, + 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 72, 73, 73, 73, 73, 73, + 2, 2, 2, 2, 2, 64, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 74, 2, 74, 2, 28, 2, 28, 2, 2, 2, 75, 76, 77, 2, 2, + 78, 2, 2, 2, 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, + 2, 2, 80, 81, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 82, 2, 2, 2, 83, 84, 85, 2, 2, 2, 86, 2, 2, 2, 2, + 87, 2, 2, 88, 89, 2, 12, 19, 90, 2, 91, 2, 2, 2, 92, 93, + 2, 2, 94, 95, 2, 2, 2, 2, 2, 2, 2, 2, 2, 96, 97, 98, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 99, + 100, 2, 101, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 5, 5, 13, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 102, 103, + 2, 2, 2, 2, 2, 2, 2, 102, 2, 2, 2, 2, 2, 2, 5, 5, + 2, 2, 104, 2, 2, 2, 2, 2, 2, 105, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 102, 106, 2, 44, 2, 2, 2, 2, 2, 103, + 107, 2, 108, 2, 2, 2, 2, 2, 109, 2, 2, 110, 111, 2, 5, 103, + 2, 2, 112, 2, 113, 93, 70, 114, 24, 2, 2, 115, 116, 2, 117, 2, + 2, 2, 118, 119, 120, 2, 2, 121, 2, 2, 2, 122, 16, 2, 123, 124, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 125, 2, + 126, 127, 128, 129, 128, 130, 128, 126, 127, 128, 129, 128, 130, 128, 126, 127, + 128, 129, 128, 130, 128, 126, 127, 128, 129, 128, 130, 128, 126, 127, 128, 129, + 128, 130, 128, 126, 127, 128, 129, 128, 130, 128, 126, 127, 128, 129, 128, 130, + 128, 126, 127, 128, 129, 128, 130, 128, 126, 127, 128, 129, 128, 130, 128, 126, + 127, 128, 129, 128, 130, 128, 126, 127, 128, 129, 128, 130, 128, 126, 127, 128, + 129, 128, 130, 128, 126, 127, 128, 129, 128, 130, 128, 126, 127, 128, 129, 128, + 130, 128, 126, 127, 128, 129, 128, 130, 128, 126, 127, 128, 129, 128, 130, 128, + 128, 129, 128, 130, 128, 126, 127, 128, 129, 128, 131, 71, 132, 73, 73, 133, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 134, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 44, 2, 2, 2, 2, 2, 135, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 69, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 13, 2, + 2, 2, 2, 2, 2, 2, 2, 136, 2, 2, 2, 2, 2, 2, 2, 2, + 137, 2, 2, 138, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, + 139, 2, 2, 140, 141, 2, 2, 102, 90, 2, 2, 142, 2, 2, 2, 2, + 143, 2, 144, 145, 2, 2, 2, 146, 90, 2, 2, 147, 148, 2, 2, 2, + 2, 2, 149, 150, 2, 2, 2, 2, 2, 2, 2, 2, 2, 102, 151, 2, + 93, 2, 2, 30, 152, 32, 153, 145, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 154, 155, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 102, 156, 13, 157, 2, 2, + 2, 2, 2, 158, 13, 2, 2, 2, 2, 2, 159, 160, 2, 2, 2, 2, + 2, 64, 161, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 145, + 2, 2, 2, 141, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 162, 163, 164, 102, 143, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 165, 166, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 167, 168, 169, 2, 170, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 171, 5, 5, 62, 117, 172, 12, 7, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 141, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 173, 174, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, +}; + +static RE_UINT8 re_grapheme_cluster_break_stage_4[] = { + 0, 0, 1, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, + 3, 3, 3, 5, 6, 6, 6, 6, 7, 6, 8, 3, 9, 6, 6, 6, + 6, 6, 6, 10, 11, 10, 3, 3, 0, 12, 3, 3, 6, 6, 13, 14, + 3, 3, 7, 6, 15, 3, 3, 3, 3, 16, 6, 17, 6, 18, 19, 8, + 20, 3, 3, 3, 6, 6, 13, 3, 3, 16, 6, 6, 6, 3, 3, 3, + 3, 16, 10, 6, 6, 9, 9, 8, 3, 3, 9, 3, 7, 6, 6, 6, + 21, 3, 3, 3, 3, 3, 22, 23, 24, 6, 25, 26, 9, 6, 3, 3, + 16, 3, 3, 3, 27, 3, 3, 3, 3, 3, 3, 28, 24, 29, 30, 31, + 3, 7, 3, 3, 32, 3, 3, 3, 3, 3, 3, 23, 33, 7, 18, 8, + 8, 20, 3, 3, 24, 10, 34, 31, 3, 3, 3, 19, 3, 16, 3, 3, + 35, 3, 3, 3, 3, 3, 3, 22, 36, 37, 38, 31, 25, 3, 3, 3, + 3, 3, 3, 16, 25, 39, 19, 8, 3, 11, 3, 3, 3, 3, 3, 40, + 41, 42, 38, 8, 24, 23, 38, 31, 37, 3, 3, 3, 3, 3, 35, 7, + 43, 44, 45, 46, 47, 6, 13, 3, 3, 7, 6, 13, 47, 6, 10, 15, + 3, 3, 6, 8, 3, 3, 8, 3, 3, 48, 20, 37, 9, 6, 6, 21, + 6, 19, 3, 9, 6, 6, 9, 6, 6, 6, 6, 15, 3, 35, 3, 3, + 3, 3, 3, 9, 49, 6, 32, 33, 3, 37, 8, 16, 9, 15, 3, 3, + 35, 33, 3, 20, 3, 3, 3, 20, 50, 50, 50, 50, 51, 51, 51, 51, + 51, 51, 52, 52, 52, 52, 52, 52, 16, 15, 3, 3, 3, 53, 6, 54, + 45, 41, 24, 6, 6, 3, 3, 20, 3, 3, 7, 55, 3, 3, 20, 3, + 21, 46, 25, 3, 41, 45, 24, 3, 3, 7, 56, 3, 3, 57, 6, 13, + 44, 9, 6, 25, 46, 6, 6, 18, 6, 6, 6, 13, 6, 58, 3, 3, + 3, 49, 21, 25, 41, 58, 3, 3, 59, 3, 3, 3, 60, 54, 53, 8, + 3, 22, 54, 61, 54, 3, 3, 3, 3, 45, 45, 6, 6, 43, 3, 3, + 13, 6, 6, 6, 49, 6, 15, 20, 37, 15, 8, 3, 6, 8, 3, 6, + 3, 3, 4, 62, 3, 3, 0, 63, 3, 3, 3, 7, 8, 3, 3, 3, + 3, 3, 16, 6, 3, 3, 11, 3, 13, 6, 6, 8, 35, 35, 7, 3, + 64, 65, 3, 3, 66, 3, 3, 3, 3, 45, 45, 45, 45, 15, 3, 3, + 3, 16, 6, 8, 3, 7, 6, 6, 50, 50, 50, 67, 7, 43, 54, 25, + 58, 3, 3, 3, 3, 20, 3, 3, 3, 3, 9, 21, 65, 33, 3, 3, + 7, 3, 3, 68, 3, 3, 3, 15, 19, 18, 15, 16, 3, 3, 64, 54, + 3, 69, 3, 3, 64, 26, 36, 31, 70, 71, 71, 71, 71, 71, 71, 70, + 71, 71, 71, 71, 71, 71, 70, 71, 71, 70, 71, 71, 71, 3, 3, 3, + 51, 72, 73, 52, 52, 52, 52, 3, 3, 3, 3, 35, 0, 0, 0, 3, + 3, 16, 13, 3, 9, 11, 3, 6, 3, 3, 13, 7, 74, 3, 3, 3, + 3, 3, 6, 6, 6, 13, 3, 3, 46, 21, 33, 5, 13, 3, 3, 3, + 3, 7, 6, 24, 6, 15, 3, 3, 7, 3, 3, 3, 64, 43, 6, 21, + 58, 3, 16, 15, 3, 3, 3, 46, 54, 49, 3, 3, 46, 6, 13, 3, + 25, 30, 30, 66, 37, 16, 6, 15, 56, 6, 75, 61, 49, 3, 3, 3, + 43, 8, 45, 53, 3, 3, 3, 8, 46, 6, 21, 61, 3, 3, 7, 26, + 6, 53, 3, 3, 43, 53, 6, 3, 76, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 77, 3, 3, 3, 11, 0, 3, 3, 3, 3, 78, 8, 60, + 79, 0, 80, 6, 13, 9, 6, 3, 3, 3, 16, 8, 6, 13, 7, 6, + 3, 15, 3, 3, 3, 81, 82, 82, 82, 82, 82, 82, +}; + +static RE_UINT8 re_grapheme_cluster_break_stage_5[] = { + 3, 3, 3, 3, 3, 3, 2, 3, 3, 1, 3, 3, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 3, 0, 0, 4, 4, 4, 4, 0, 0, 0, 4, + 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 0, 4, 0, 4, 4, 0, + 3, 3, 0, 0, 4, 4, 4, 0, 3, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 4, 4, 4, 3, 0, 4, 4, 0, 0, 4, 4, 0, 4, 4, + 0, 4, 0, 0, 4, 4, 4, 6, 0, 0, 4, 6, 4, 0, 6, 6, + 6, 4, 4, 4, 4, 6, 6, 6, 6, 4, 6, 6, 0, 4, 6, 6, + 4, 0, 4, 6, 4, 0, 0, 6, 6, 0, 0, 6, 6, 4, 0, 0, + 0, 4, 4, 6, 6, 4, 4, 0, 4, 6, 0, 6, 0, 0, 4, 0, + 4, 6, 6, 0, 0, 0, 6, 6, 6, 0, 6, 6, 6, 0, 4, 4, + 4, 0, 6, 4, 6, 6, 4, 6, 6, 0, 4, 6, 6, 6, 4, 4, + 4, 0, 4, 0, 6, 6, 6, 6, 6, 6, 6, 4, 0, 4, 0, 6, + 0, 4, 0, 4, 4, 6, 4, 4, 7, 7, 7, 7, 8, 8, 8, 8, + 9, 9, 9, 9, 4, 4, 6, 4, 4, 4, 6, 6, 4, 4, 3, 0, + 4, 6, 6, 4, 0, 6, 4, 6, 6, 0, 0, 0, 4, 4, 6, 0, + 0, 6, 4, 4, 6, 4, 6, 4, 4, 4, 3, 3, 3, 3, 3, 0, + 0, 0, 0, 6, 6, 4, 4, 6, 6, 6, 0, 0, 7, 0, 0, 0, + 4, 6, 0, 0, 0, 6, 4, 0, 10, 11, 11, 11, 11, 11, 11, 11, + 8, 8, 8, 0, 0, 0, 0, 9, 6, 4, 6, 0, 4, 6, 4, 6, + 0, 6, 6, 6, 6, 6, 6, 0, 0, 4, 6, 4, 4, 4, 4, 3, + 3, 3, 3, 4, 0, 0, 5, 5, 5, 5, 5, 5, +}; + +/* Grapheme_Cluster_Break: 2640 bytes. */ + +RE_UINT32 re_get_grapheme_cluster_break(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 13; + code = ch ^ (f << 13); + pos = (RE_UINT32)re_grapheme_cluster_break_stage_1[f] << 5; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_grapheme_cluster_break_stage_2[pos + f] << 4; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_grapheme_cluster_break_stage_3[pos + f] << 2; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_grapheme_cluster_break_stage_4[pos + f] << 2; + value = re_grapheme_cluster_break_stage_5[pos + code]; + + return value; +} + +/* Sentence_Break. */ + +static RE_UINT8 re_sentence_break_stage_1[] = { + 0, 1, 2, 3, 4, 5, 5, 5, 5, 6, 7, 5, 5, 8, 9, 10, + 11, 12, 13, 14, 15, 9, 16, 9, 9, 9, 9, 17, 9, 18, 19, 20, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21, 22, 23, 9, 9, 24, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 25, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static RE_UINT8 re_sentence_break_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 17, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 33, 33, 36, 33, 37, 33, 33, 38, 39, 40, 33, + 41, 42, 33, 33, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 43, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 44, + 17, 17, 17, 17, 45, 17, 46, 47, 48, 49, 50, 51, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 52, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 17, 53, 54, 17, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 17, 64, 65, 66, 67, 68, 69, 33, 33, 33, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 33, 79, 33, 33, 33, 33, 33, + 17, 17, 17, 80, 81, 82, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 17, 17, 17, 17, 83, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 17, 17, 84, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 17, 17, 85, 86, 33, 33, 33, 87, + 88, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 89, 33, 33, 33, + 33, 90, 91, 33, 92, 93, 94, 95, 33, 33, 96, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 97, 33, 33, 33, 33, 33, 98, 33, + 33, 99, 33, 33, 33, 33, 100, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 17, 17, 17, 17, 17, 17, 101, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 102, 103, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 104, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 17, 17, 105, 33, 33, 33, 33, 33, + 106, 107, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, +}; + +static RE_UINT16 re_sentence_break_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 8, 16, 17, 18, 19, 20, 21, 22, 23, 23, 23, 24, 25, 26, 27, 28, + 29, 30, 18, 8, 31, 8, 32, 8, 8, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 41, 41, 44, 45, 46, 47, 48, 41, 41, 49, 50, 51, + 52, 53, 54, 55, 55, 56, 55, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 71, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 85, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 55, 99, 100, 101, 55, 102, 103, 104, 105, 106, 107, 108, 55, + 41, 109, 110, 111, 112, 29, 113, 114, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 115, 41, 116, 117, 118, 41, 119, 41, 120, 121, 122, 29, 29, 123, + 96, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 124, 125, 41, 41, 126, + 127, 128, 129, 130, 41, 131, 132, 133, 134, 41, 41, 135, 41, 136, 41, 137, + 138, 139, 140, 141, 41, 142, 143, 55, 144, 41, 145, 146, 147, 148, 55, 55, + 149, 131, 150, 151, 152, 153, 41, 154, 41, 155, 156, 157, 55, 55, 158, 159, + 18, 18, 18, 18, 18, 18, 23, 160, 8, 8, 8, 8, 161, 8, 8, 8, + 162, 163, 164, 165, 163, 166, 167, 168, 169, 170, 171, 172, 173, 55, 174, 175, + 176, 177, 178, 30, 179, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 180, 181, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 182, 30, 183, + 55, 55, 184, 185, 55, 55, 186, 187, 55, 55, 55, 55, 188, 55, 189, 190, + 29, 191, 192, 193, 8, 8, 8, 194, 18, 195, 41, 196, 197, 198, 198, 23, + 199, 200, 201, 55, 55, 55, 55, 55, 202, 203, 96, 41, 204, 96, 41, 114, + 205, 206, 41, 41, 207, 208, 55, 209, 41, 41, 41, 41, 41, 137, 55, 55, + 41, 41, 41, 41, 41, 41, 137, 55, 41, 41, 41, 41, 210, 55, 209, 211, + 212, 213, 8, 214, 215, 41, 41, 216, 217, 218, 8, 219, 220, 221, 55, 222, + 223, 224, 41, 225, 226, 131, 227, 228, 50, 229, 230, 231, 58, 232, 233, 234, + 41, 235, 236, 237, 41, 238, 239, 240, 241, 242, 243, 244, 18, 18, 41, 245, + 41, 41, 41, 41, 41, 246, 247, 248, 41, 41, 41, 249, 41, 41, 250, 55, + 251, 252, 253, 41, 41, 254, 255, 41, 41, 256, 209, 41, 257, 41, 258, 259, + 260, 261, 262, 263, 41, 41, 41, 264, 265, 2, 266, 267, 268, 138, 269, 270, + 271, 272, 273, 55, 41, 41, 41, 208, 55, 55, 41, 56, 55, 55, 55, 274, + 55, 55, 55, 55, 231, 41, 275, 276, 41, 209, 277, 278, 279, 41, 280, 55, + 29, 281, 282, 41, 279, 133, 55, 55, 41, 283, 41, 284, 55, 55, 55, 55, + 41, 197, 137, 258, 55, 55, 55, 55, 285, 286, 137, 197, 138, 55, 55, 287, + 137, 250, 55, 55, 41, 288, 55, 55, 289, 290, 291, 231, 231, 55, 104, 292, + 41, 137, 137, 293, 254, 55, 55, 55, 41, 41, 294, 55, 29, 295, 18, 296, + 152, 297, 298, 299, 152, 300, 301, 302, 152, 303, 304, 305, 152, 232, 306, 55, + 307, 308, 55, 55, 309, 310, 311, 312, 313, 71, 314, 315, 55, 55, 55, 55, + 55, 55, 55, 55, 41, 47, 316, 55, 55, 55, 55, 55, 41, 317, 318, 55, + 41, 47, 319, 55, 41, 320, 133, 55, 321, 322, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 29, 18, 323, 55, 55, 55, 55, 55, 55, 41, 324, + 41, 41, 41, 41, 250, 55, 55, 55, 41, 41, 41, 207, 41, 41, 41, 41, + 41, 41, 284, 55, 55, 55, 55, 55, 41, 207, 55, 55, 55, 55, 55, 55, + 41, 41, 325, 55, 55, 55, 55, 55, 41, 324, 138, 326, 55, 55, 209, 327, + 41, 328, 329, 330, 122, 55, 55, 55, 41, 41, 331, 332, 333, 55, 55, 55, + 334, 55, 55, 55, 55, 55, 55, 55, 41, 41, 41, 335, 336, 337, 55, 55, + 55, 55, 55, 338, 339, 340, 55, 55, 55, 55, 341, 55, 55, 55, 55, 55, + 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 342, 343, 355, + 345, 356, 357, 358, 349, 359, 360, 361, 362, 363, 364, 191, 365, 366, 367, 368, + 23, 369, 23, 370, 371, 372, 55, 55, 41, 41, 41, 41, 41, 41, 373, 55, + 374, 375, 376, 377, 378, 379, 55, 55, 55, 380, 381, 381, 382, 55, 55, 55, + 55, 55, 55, 383, 55, 55, 55, 55, 41, 41, 41, 41, 41, 41, 197, 55, + 41, 56, 41, 41, 41, 41, 41, 41, 279, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 334, 55, 55, 279, 55, 55, 55, 55, 55, 55, 55, + 384, 385, 385, 385, 55, 55, 55, 55, 23, 23, 23, 23, 23, 23, 23, 386, +}; + +static RE_UINT8 re_sentence_break_stage_4[] = { + 0, 0, 1, 2, 0, 0, 0, 0, 3, 4, 5, 6, 7, 7, 8, 9, + 10, 11, 11, 11, 11, 11, 12, 13, 14, 15, 15, 15, 15, 15, 16, 13, + 0, 17, 0, 0, 0, 0, 0, 0, 18, 0, 19, 20, 0, 21, 19, 0, + 11, 11, 11, 11, 11, 22, 11, 23, 15, 15, 15, 15, 15, 24, 15, 15, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, + 26, 26, 27, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 28, 29, + 30, 31, 32, 33, 28, 31, 34, 28, 25, 31, 29, 31, 32, 26, 35, 34, + 36, 28, 31, 26, 26, 26, 26, 27, 25, 25, 25, 25, 30, 31, 25, 25, + 25, 25, 25, 25, 25, 15, 33, 30, 26, 23, 25, 25, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 37, 15, 15, + 15, 15, 15, 15, 15, 15, 38, 36, 39, 40, 36, 36, 41, 0, 0, 0, + 15, 42, 0, 43, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 25, 45, 46, 47, 0, 48, 22, 49, 32, 11, 11, 11, + 50, 11, 11, 15, 15, 15, 15, 15, 15, 15, 15, 51, 33, 34, 25, 25, + 25, 25, 25, 25, 15, 52, 30, 32, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 15, 15, 15, 15, 53, 44, 54, 25, 25, 25, 25, 25, + 28, 26, 26, 29, 25, 25, 25, 25, 25, 25, 25, 25, 10, 11, 11, 11, + 11, 11, 11, 11, 11, 22, 55, 56, 14, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 57, 0, 58, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 59, + 60, 59, 0, 0, 36, 36, 36, 36, 36, 36, 61, 0, 36, 0, 0, 0, + 62, 63, 0, 64, 44, 44, 65, 66, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 67, 44, 44, 44, 44, 44, 7, 7, 68, 69, 70, 36, 36, 36, + 36, 36, 36, 36, 36, 71, 44, 72, 44, 73, 74, 75, 7, 7, 76, 77, + 78, 0, 0, 79, 80, 36, 36, 36, 36, 36, 36, 36, 44, 44, 44, 44, + 44, 44, 65, 81, 36, 36, 36, 36, 36, 82, 44, 44, 83, 0, 0, 0, + 7, 7, 76, 36, 36, 36, 36, 36, 36, 36, 67, 44, 44, 41, 84, 0, + 36, 36, 36, 36, 36, 82, 85, 44, 44, 86, 86, 87, 0, 0, 0, 0, + 36, 36, 36, 36, 36, 36, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 36, 36, 36, 36, 88, 0, 0, 89, 44, 44, 44, 44, 44, 44, 44, + 44, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82, 90, + 44, 44, 44, 44, 86, 44, 36, 36, 82, 91, 7, 7, 81, 36, 36, 36, + 86, 81, 36, 77, 77, 36, 36, 36, 36, 36, 92, 36, 43, 40, 41, 90, + 44, 93, 93, 94, 0, 89, 0, 95, 82, 96, 7, 7, 41, 0, 0, 0, + 58, 81, 61, 97, 77, 36, 36, 36, 36, 36, 92, 36, 92, 98, 41, 74, + 65, 89, 93, 87, 99, 0, 81, 43, 0, 96, 7, 7, 75, 100, 0, 0, + 58, 81, 36, 95, 95, 36, 36, 36, 36, 36, 92, 36, 92, 81, 41, 90, + 44, 59, 59, 87, 88, 0, 0, 0, 82, 96, 7, 7, 0, 0, 55, 0, + 58, 81, 36, 77, 77, 36, 36, 36, 44, 93, 93, 87, 0, 101, 0, 95, + 82, 96, 7, 7, 55, 0, 0, 0, 102, 81, 61, 40, 92, 41, 98, 92, + 97, 88, 61, 40, 36, 36, 41, 101, 65, 101, 74, 87, 88, 89, 0, 0, + 0, 96, 7, 7, 0, 0, 0, 0, 44, 81, 36, 92, 92, 36, 36, 36, + 36, 36, 92, 36, 36, 36, 41, 103, 44, 74, 74, 87, 0, 60, 61, 0, + 82, 96, 7, 7, 0, 0, 0, 0, 58, 81, 36, 92, 92, 36, 36, 36, + 36, 36, 92, 36, 36, 81, 41, 90, 44, 74, 74, 87, 0, 60, 0, 104, + 82, 96, 7, 7, 98, 0, 0, 0, 36, 36, 36, 36, 36, 36, 61, 103, + 44, 74, 74, 94, 0, 89, 0, 97, 82, 96, 7, 7, 0, 0, 40, 36, + 101, 81, 36, 36, 36, 61, 40, 36, 36, 36, 36, 36, 95, 36, 36, 55, + 36, 61, 105, 89, 44, 106, 44, 44, 0, 96, 7, 7, 101, 0, 0, 0, + 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 80, 44, 65, 0, + 36, 67, 44, 65, 7, 7, 107, 0, 98, 77, 43, 55, 0, 36, 81, 36, + 81, 108, 40, 81, 80, 44, 59, 83, 36, 43, 44, 87, 7, 7, 107, 36, + 88, 0, 0, 0, 0, 0, 87, 0, 7, 7, 107, 0, 0, 109, 110, 111, + 36, 36, 81, 36, 36, 36, 36, 36, 36, 36, 36, 88, 58, 44, 44, 44, + 44, 74, 36, 86, 44, 44, 58, 44, 44, 44, 44, 44, 44, 44, 44, 112, + 0, 105, 0, 0, 0, 0, 0, 0, 36, 36, 67, 44, 44, 44, 44, 113, + 7, 7, 114, 0, 36, 82, 75, 82, 90, 73, 44, 75, 86, 70, 36, 36, + 82, 44, 44, 85, 7, 7, 115, 87, 11, 50, 0, 116, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 92, 41, 36, 61, 92, 41, + 36, 36, 92, 41, 36, 36, 36, 36, 36, 36, 36, 36, 92, 41, 36, 61, + 92, 41, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 92, 41, 36, 36, + 36, 36, 36, 36, 36, 36, 61, 58, 117, 9, 118, 0, 0, 0, 0, 0, + 36, 36, 36, 36, 0, 0, 0, 0, 11, 11, 11, 11, 11, 119, 15, 39, + 36, 36, 36, 120, 36, 36, 36, 36, 121, 36, 36, 36, 36, 36, 122, 123, + 36, 36, 61, 40, 36, 36, 88, 0, 36, 36, 36, 92, 82, 112, 0, 0, + 36, 36, 36, 36, 82, 124, 0, 0, 36, 36, 36, 36, 82, 0, 0, 0, + 36, 36, 36, 92, 125, 0, 0, 0, 36, 36, 36, 36, 36, 44, 44, 44, + 44, 44, 44, 44, 44, 97, 0, 100, 7, 7, 107, 0, 0, 0, 0, 0, + 126, 0, 127, 128, 7, 7, 107, 0, 36, 36, 36, 36, 36, 36, 0, 0, + 36, 36, 129, 0, 36, 36, 36, 36, 36, 36, 36, 36, 36, 41, 0, 0, + 36, 36, 36, 36, 36, 36, 36, 61, 44, 44, 44, 0, 44, 44, 44, 0, + 0, 91, 7, 7, 36, 36, 36, 36, 36, 36, 36, 41, 36, 88, 0, 0, + 36, 36, 36, 0, 36, 36, 36, 36, 36, 36, 41, 0, 7, 7, 107, 0, + 36, 36, 36, 36, 36, 67, 44, 0, 36, 36, 36, 36, 36, 86, 44, 65, + 44, 44, 44, 44, 44, 44, 44, 93, 7, 7, 107, 0, 7, 7, 107, 0, + 0, 97, 130, 0, 44, 44, 44, 65, 44, 70, 36, 36, 36, 36, 36, 36, + 44, 70, 36, 0, 7, 7, 114, 131, 0, 0, 89, 44, 44, 0, 0, 0, + 113, 36, 36, 36, 36, 36, 36, 36, 86, 44, 44, 75, 7, 7, 76, 36, + 36, 82, 44, 44, 44, 0, 0, 0, 36, 44, 44, 44, 44, 44, 9, 118, + 7, 7, 107, 81, 7, 7, 76, 36, 36, 36, 36, 36, 36, 36, 36, 132, + 0, 0, 0, 0, 65, 44, 44, 44, 44, 44, 70, 80, 82, 133, 87, 0, + 44, 44, 44, 44, 44, 87, 0, 44, 25, 25, 25, 25, 25, 34, 15, 27, + 15, 15, 11, 11, 15, 39, 11, 119, 15, 15, 11, 11, 15, 15, 11, 11, + 15, 39, 11, 119, 15, 15, 134, 134, 15, 15, 11, 11, 15, 15, 15, 39, + 15, 15, 11, 11, 15, 135, 11, 136, 46, 135, 11, 137, 15, 46, 11, 0, + 15, 15, 11, 137, 46, 135, 11, 137, 138, 138, 139, 140, 141, 142, 143, 143, + 0, 144, 145, 146, 0, 0, 147, 148, 0, 149, 148, 0, 0, 0, 0, 150, + 62, 151, 62, 62, 21, 0, 0, 152, 0, 0, 0, 147, 15, 15, 15, 42, + 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 112, 0, 0, 0, + 48, 153, 154, 155, 23, 116, 10, 119, 0, 156, 49, 157, 11, 38, 158, 33, + 0, 159, 39, 160, 0, 0, 0, 0, 161, 38, 88, 0, 0, 0, 0, 0, + 0, 0, 143, 0, 0, 0, 0, 0, 0, 0, 147, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 162, 11, 11, 15, 15, 39, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 143, 123, 0, 143, 143, 143, 5, 0, 0, + 0, 147, 0, 0, 0, 0, 0, 0, 0, 163, 143, 143, 0, 0, 0, 0, + 4, 143, 143, 143, 143, 143, 123, 0, 0, 0, 0, 0, 0, 0, 143, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 11, 11, 11, 22, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 24, 31, 164, 26, 32, 25, 29, 15, 33, + 25, 42, 153, 165, 54, 0, 0, 0, 15, 166, 0, 21, 36, 36, 36, 36, + 36, 36, 0, 97, 0, 0, 0, 89, 36, 36, 36, 36, 36, 61, 0, 0, + 36, 61, 36, 61, 36, 61, 36, 61, 143, 143, 143, 5, 0, 0, 0, 5, + 143, 143, 5, 167, 0, 0, 0, 118, 168, 0, 0, 0, 0, 0, 0, 0, + 169, 81, 143, 143, 5, 143, 143, 170, 81, 36, 82, 44, 81, 41, 36, 88, + 36, 36, 36, 36, 36, 61, 60, 81, 0, 81, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 41, 81, 36, 36, 36, 36, 36, 36, 61, 0, 0, 0, 0, + 36, 36, 36, 36, 36, 36, 61, 0, 0, 0, 0, 0, 36, 36, 36, 36, + 36, 36, 36, 88, 0, 0, 0, 0, 36, 36, 36, 36, 36, 36, 36, 171, + 36, 36, 36, 172, 36, 36, 36, 36, 7, 7, 76, 0, 0, 0, 0, 0, + 25, 25, 25, 173, 65, 44, 44, 174, 25, 25, 25, 25, 25, 25, 25, 175, + 36, 36, 36, 36, 176, 9, 0, 0, 0, 0, 0, 0, 0, 97, 36, 36, + 177, 25, 25, 25, 27, 25, 25, 25, 25, 25, 25, 25, 15, 15, 26, 30, + 25, 25, 178, 179, 25, 27, 25, 25, 25, 25, 31, 119, 11, 25, 0, 0, + 0, 0, 0, 0, 0, 97, 180, 36, 181, 181, 67, 36, 36, 36, 36, 36, + 67, 44, 0, 0, 0, 0, 0, 0, 36, 36, 36, 36, 36, 131, 0, 0, + 75, 36, 36, 36, 36, 36, 36, 36, 44, 112, 0, 131, 7, 7, 107, 0, + 44, 44, 44, 44, 75, 36, 97, 55, 36, 82, 44, 176, 36, 36, 36, 36, + 36, 67, 44, 44, 44, 0, 0, 0, 36, 36, 36, 36, 36, 36, 36, 88, + 36, 36, 36, 36, 67, 44, 44, 44, 112, 0, 148, 97, 7, 7, 107, 0, + 36, 80, 36, 36, 7, 7, 76, 61, 36, 36, 86, 44, 44, 65, 0, 0, + 67, 36, 36, 87, 7, 7, 107, 182, 36, 36, 36, 36, 36, 61, 183, 75, + 36, 36, 36, 36, 90, 73, 70, 82, 129, 0, 0, 0, 0, 0, 97, 41, + 36, 36, 67, 44, 184, 185, 0, 0, 81, 61, 81, 61, 81, 61, 0, 0, + 36, 61, 36, 61, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 24, 15, + 15, 39, 0, 0, 15, 15, 15, 15, 67, 44, 186, 87, 7, 7, 107, 0, + 36, 0, 0, 0, 36, 36, 36, 36, 36, 61, 97, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 0, 36, 36, 36, 41, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 41, 0, 15, 24, 0, 0, 187, 15, 0, 188, + 36, 36, 92, 36, 36, 61, 36, 43, 95, 92, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 41, 0, 0, 0, 0, 0, 0, 0, 97, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 189, 36, 36, 36, 36, 40, 36, 36, 36, + 36, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, 36, 0, + 44, 44, 44, 44, 190, 4, 123, 0, 44, 44, 44, 44, 191, 170, 143, 143, + 143, 192, 123, 0, 6, 193, 194, 195, 141, 0, 0, 0, 36, 92, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 196, 57, 0, 5, 6, 0, 0, 197, 9, + 14, 15, 15, 15, 15, 15, 16, 198, 199, 200, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 82, 40, 36, 40, 36, 40, 36, 40, 88, + 0, 0, 0, 0, 0, 0, 201, 0, 36, 36, 36, 81, 36, 36, 36, 36, + 36, 61, 36, 36, 36, 36, 61, 95, 36, 36, 36, 41, 36, 36, 36, 41, + 0, 0, 0, 0, 0, 0, 0, 99, 36, 36, 36, 36, 88, 0, 0, 0, + 112, 0, 0, 0, 0, 0, 0, 0, 36, 36, 61, 0, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 82, 65, 0, 36, 36, 36, 36, 36, 36, 36, 41, + 36, 0, 36, 36, 81, 41, 0, 0, 11, 11, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 36, 36, 36, 36, 36, 36, 0, 0, 36, 36, 36, 36, + 36, 0, 0, 0, 0, 0, 0, 0, 36, 41, 92, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 95, 88, 77, 36, 36, 36, 36, 61, 41, 0, 0, + 36, 36, 36, 36, 36, 36, 0, 40, 86, 60, 0, 44, 36, 81, 81, 36, + 36, 36, 36, 36, 36, 0, 65, 89, 0, 0, 0, 0, 0, 131, 0, 0, + 36, 185, 0, 0, 0, 0, 0, 0, 36, 36, 36, 36, 61, 0, 0, 0, + 36, 36, 88, 0, 0, 0, 0, 0, 11, 11, 11, 11, 22, 0, 0, 0, + 15, 15, 15, 15, 24, 0, 0, 0, 36, 36, 36, 36, 36, 36, 44, 44, + 44, 186, 118, 0, 0, 0, 0, 0, 0, 96, 7, 7, 0, 0, 0, 89, + 36, 36, 36, 36, 44, 44, 65, 202, 148, 0, 0, 0, 36, 36, 36, 36, + 36, 36, 88, 0, 7, 7, 107, 0, 36, 67, 44, 44, 44, 203, 7, 7, + 182, 0, 0, 0, 36, 36, 36, 36, 36, 36, 36, 36, 67, 104, 0, 0, + 70, 204, 101, 205, 7, 7, 206, 172, 36, 36, 36, 36, 95, 36, 36, 36, + 36, 36, 36, 44, 44, 44, 207, 118, 36, 61, 92, 95, 36, 36, 36, 95, + 36, 36, 208, 0, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 67, + 44, 44, 65, 0, 7, 7, 107, 0, 44, 81, 36, 77, 77, 36, 36, 36, + 44, 93, 93, 87, 88, 89, 0, 81, 82, 101, 44, 112, 44, 112, 0, 0, + 44, 95, 0, 0, 7, 7, 107, 0, 36, 36, 36, 67, 44, 87, 44, 44, + 209, 0, 182, 130, 130, 130, 36, 87, 124, 88, 0, 0, 7, 7, 107, 0, + 36, 36, 67, 44, 44, 44, 0, 0, 36, 36, 36, 36, 36, 36, 41, 58, + 44, 44, 44, 0, 7, 7, 107, 78, 7, 7, 107, 0, 0, 0, 0, 97, + 36, 36, 36, 36, 36, 36, 88, 0, 36, 61, 0, 0, 0, 0, 0, 0, + 7, 7, 107, 131, 0, 0, 0, 0, 36, 36, 36, 41, 44, 205, 0, 0, + 36, 36, 36, 36, 44, 186, 118, 0, 36, 118, 0, 0, 7, 7, 107, 0, + 97, 36, 36, 36, 36, 36, 0, 81, 36, 88, 0, 0, 86, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 65, 0, 0, 0, 89, 113, 36, 36, 36, + 41, 0, 0, 0, 0, 0, 0, 0, 36, 36, 61, 0, 36, 36, 36, 88, + 36, 36, 88, 0, 36, 36, 41, 210, 62, 0, 0, 0, 0, 0, 0, 0, + 0, 58, 87, 58, 211, 62, 212, 44, 65, 58, 44, 0, 0, 0, 0, 0, + 0, 0, 101, 87, 0, 0, 0, 0, 101, 112, 0, 0, 0, 0, 0, 0, + 11, 11, 11, 11, 11, 11, 155, 15, 15, 15, 15, 15, 15, 11, 11, 11, + 11, 11, 11, 155, 15, 135, 15, 15, 15, 15, 11, 11, 11, 11, 11, 11, + 155, 15, 15, 15, 15, 15, 15, 49, 48, 213, 10, 49, 11, 155, 166, 14, + 15, 14, 15, 15, 11, 11, 11, 11, 11, 11, 155, 15, 15, 15, 15, 15, + 15, 50, 22, 10, 11, 49, 11, 214, 15, 15, 15, 15, 15, 15, 50, 22, + 11, 156, 162, 11, 214, 15, 15, 15, 15, 15, 15, 11, 11, 11, 11, 11, + 11, 155, 15, 15, 15, 15, 15, 15, 11, 11, 11, 155, 15, 15, 15, 15, + 155, 15, 15, 15, 15, 15, 15, 11, 11, 11, 11, 11, 11, 155, 15, 15, + 15, 15, 15, 15, 11, 11, 11, 11, 15, 39, 11, 11, 11, 11, 11, 11, + 214, 15, 15, 15, 15, 15, 24, 15, 33, 11, 11, 11, 11, 11, 22, 15, + 15, 15, 15, 15, 15, 135, 15, 11, 11, 11, 11, 11, 11, 214, 15, 15, + 15, 15, 15, 24, 15, 33, 11, 11, 15, 15, 135, 15, 11, 11, 11, 11, + 11, 11, 214, 15, 15, 15, 15, 15, 24, 15, 27, 96, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 65, 89, 44, + 44, 44, 44, 112, 0, 99, 0, 0, 0, 112, 118, 0, 0, 0, 89, 44, + 58, 44, 44, 44, 0, 0, 0, 0, 36, 88, 0, 0, 44, 65, 0, 0, + 36, 81, 36, 36, 36, 36, 36, 36, 98, 77, 81, 36, 61, 36, 108, 0, + 104, 97, 108, 81, 98, 77, 108, 108, 98, 77, 61, 36, 61, 36, 81, 43, + 36, 36, 95, 36, 36, 36, 36, 0, 81, 81, 95, 36, 36, 36, 36, 0, + 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 119, 0, 11, 11, 11, 11, + 11, 11, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 123, 0, + 20, 0, 0, 0, 0, 0, 0, 0, 62, 62, 62, 62, 62, 62, 62, 62, + 44, 44, 44, 44, 0, 0, 0, 0, +}; + +static RE_UINT8 re_sentence_break_stage_5[] = { + 0, 0, 0, 0, 0, 6, 2, 6, 6, 1, 0, 0, 6, 12, 13, 0, + 0, 0, 0, 13, 13, 13, 0, 0, 14, 14, 11, 0, 10, 10, 10, 10, + 10, 10, 14, 0, 0, 0, 0, 12, 0, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 13, 0, 13, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 13, 0, 4, 0, 0, 6, 0, 0, 0, 0, 0, 7, 13, + 0, 5, 0, 0, 0, 7, 0, 0, 8, 8, 8, 0, 8, 8, 8, 7, + 7, 7, 7, 0, 8, 7, 8, 7, 7, 8, 7, 8, 7, 7, 8, 7, + 8, 8, 7, 8, 7, 8, 7, 7, 7, 8, 8, 7, 8, 7, 8, 8, + 7, 8, 8, 8, 7, 7, 8, 8, 8, 7, 7, 7, 8, 7, 7, 9, + 9, 9, 9, 9, 9, 7, 7, 7, 7, 9, 9, 9, 7, 7, 0, 0, + 0, 0, 9, 9, 9, 9, 0, 0, 7, 0, 0, 0, 9, 0, 9, 0, + 3, 3, 3, 3, 9, 0, 8, 7, 0, 0, 7, 7, 7, 7, 0, 8, + 0, 0, 8, 0, 8, 0, 8, 8, 8, 8, 0, 8, 7, 7, 7, 8, + 8, 7, 0, 8, 8, 7, 0, 3, 3, 3, 8, 7, 0, 9, 0, 0, + 0, 14, 0, 0, 0, 12, 0, 0, 0, 3, 3, 3, 3, 3, 0, 3, + 0, 3, 3, 0, 9, 9, 9, 0, 5, 5, 5, 5, 5, 5, 0, 0, + 14, 14, 0, 0, 3, 3, 3, 0, 5, 0, 0, 12, 9, 9, 9, 3, + 10, 10, 0, 10, 10, 0, 9, 9, 3, 9, 9, 9, 12, 9, 3, 3, + 3, 5, 0, 3, 3, 9, 9, 3, 3, 0, 3, 3, 3, 3, 9, 9, + 10, 10, 9, 9, 9, 0, 0, 9, 12, 12, 12, 0, 0, 0, 0, 5, + 9, 3, 9, 9, 0, 9, 9, 9, 9, 9, 3, 3, 3, 9, 0, 0, + 14, 12, 9, 0, 3, 3, 9, 3, 9, 3, 3, 3, 3, 3, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 3, 3, 9, 3, 3, 12, 12, 10, 10, + 9, 0, 9, 9, 3, 0, 0, 3, 3, 3, 9, 0, 9, 9, 0, 9, + 0, 0, 10, 10, 0, 0, 0, 9, 0, 9, 9, 0, 0, 3, 0, 0, + 9, 3, 0, 0, 0, 0, 3, 3, 0, 0, 3, 9, 0, 9, 3, 3, + 0, 0, 9, 0, 0, 0, 3, 0, 3, 0, 3, 0, 10, 10, 0, 0, + 0, 9, 0, 9, 0, 3, 0, 3, 0, 3, 13, 13, 13, 13, 3, 3, + 3, 0, 0, 0, 3, 3, 3, 9, 10, 10, 12, 12, 10, 10, 3, 3, + 0, 8, 0, 0, 0, 0, 12, 0, 12, 0, 0, 0, 8, 8, 0, 0, + 9, 0, 12, 9, 6, 9, 9, 9, 9, 9, 9, 13, 13, 0, 0, 0, + 3, 12, 12, 0, 9, 0, 3, 3, 0, 0, 14, 12, 14, 12, 0, 3, + 3, 3, 5, 0, 9, 3, 9, 0, 12, 12, 12, 12, 0, 0, 12, 12, + 9, 9, 12, 12, 3, 9, 9, 0, 0, 8, 0, 8, 7, 0, 7, 7, + 8, 0, 7, 0, 8, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 5, + 3, 3, 5, 5, 0, 0, 0, 14, 14, 0, 0, 0, 13, 13, 13, 13, + 11, 0, 0, 0, 4, 4, 5, 5, 5, 5, 5, 6, 0, 13, 13, 0, + 12, 12, 0, 0, 0, 13, 13, 12, 0, 0, 0, 6, 5, 0, 5, 5, + 0, 13, 13, 7, 0, 0, 0, 8, 0, 0, 7, 8, 8, 8, 7, 7, + 8, 0, 8, 0, 8, 8, 0, 7, 9, 7, 0, 0, 0, 8, 7, 7, + 0, 0, 7, 0, 9, 9, 9, 8, 0, 0, 8, 8, 0, 0, 13, 13, + 8, 7, 7, 8, 7, 8, 7, 3, 7, 7, 0, 7, 0, 0, 12, 9, + 0, 0, 13, 0, 6, 14, 12, 0, 0, 13, 13, 13, 9, 9, 0, 12, + 9, 0, 12, 12, 8, 7, 9, 3, 3, 3, 0, 9, 7, 7, 3, 3, + 3, 3, 0, 12, 0, 0, 8, 7, 9, 0, 0, 8, 7, 8, 7, 9, + 7, 7, 7, 9, 9, 9, 3, 9, 0, 12, 12, 12, 0, 0, 9, 3, + 12, 12, 9, 9, 9, 3, 3, 0, 3, 3, 3, 12, 0, 0, 0, 7, + 0, 9, 3, 9, 9, 9, 13, 13, 14, 14, 0, 14, 0, 14, 14, 0, + 13, 0, 0, 13, 0, 14, 12, 12, 14, 13, 13, 13, 13, 13, 13, 0, + 9, 0, 0, 5, 0, 0, 14, 0, 0, 13, 0, 13, 13, 12, 13, 13, + 14, 0, 9, 9, 0, 5, 5, 5, 0, 5, 12, 12, 3, 0, 10, 10, + 9, 12, 12, 0, 3, 12, 0, 0, 10, 10, 9, 0, 12, 12, 0, 12, + 9, 12, 0, 0, 3, 0, 12, 12, 0, 3, 3, 12, 3, 3, 3, 5, + 5, 5, 5, 3, 0, 8, 8, 0, 8, 0, 7, 7, +}; + +/* Sentence_Break: 6372 bytes. */ + +RE_UINT32 re_get_sentence_break(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_sentence_break_stage_1[f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_sentence_break_stage_2[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_sentence_break_stage_3[pos + f] << 3; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_sentence_break_stage_4[pos + f] << 2; + value = re_sentence_break_stage_5[pos + code]; + + return value; +} + +/* Math. */ + +static RE_UINT8 re_math_stage_1[] = { + 0, 1, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, +}; + +static RE_UINT8 re_math_stage_2[] = { + 0, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 6, 1, 1, +}; + +static RE_UINT8 re_math_stage_3[] = { + 0, 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 4, 5, 6, 7, 1, 8, 9, 10, 1, 6, 6, 11, 1, 1, 1, 1, + 1, 1, 1, 12, 1, 1, 13, 14, 1, 1, 1, 1, 15, 16, 17, 18, + 1, 1, 1, 1, 1, 1, 19, 1, +}; + +static RE_UINT8 re_math_stage_4[] = { + 0, 1, 2, 3, 0, 4, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, + 9, 10, 11, 12, 13, 0, 14, 15, 16, 17, 18, 0, 19, 20, 21, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 25, 0, 26, 27, 28, 29, 30, + 0, 0, 0, 0, 0, 31, 32, 33, 34, 0, 35, 36, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 23, 0, 19, 37, 0, 0, 0, 0, 0, + 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, + 1, 3, 3, 0, 0, 0, 0, 40, 23, 23, 41, 23, 42, 43, 44, 23, + 45, 46, 47, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 48, 23, 23, + 23, 23, 23, 23, 23, 23, 49, 23, 44, 50, 51, 52, 53, 54, 0, 55, +}; + +static RE_UINT8 re_math_stage_5[] = { + 0, 0, 0, 0, 0, 8, 0, 112, 0, 0, 0, 64, 0, 0, 0, 80, + 0, 16, 2, 0, 0, 0, 128, 0, 0, 0, 39, 0, 0, 0, 115, 0, + 192, 1, 0, 0, 0, 0, 64, 0, 0, 0, 28, 0, 17, 0, 4, 0, + 30, 0, 0, 124, 0, 124, 0, 0, 0, 0, 255, 31, 98, 248, 0, 0, + 132, 252, 47, 63, 16, 179, 251, 241, 255, 11, 0, 0, 0, 0, 255, 255, + 255, 126, 195, 240, 255, 255, 255, 47, 48, 0, 240, 255, 255, 255, 255, 255, + 0, 15, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 248, + 255, 255, 191, 0, 0, 0, 1, 240, 7, 0, 0, 0, 3, 192, 255, 240, + 195, 140, 15, 0, 148, 31, 0, 255, 96, 0, 0, 0, 5, 0, 0, 0, + 15, 224, 0, 0, 159, 31, 0, 0, 0, 2, 0, 0, 126, 1, 0, 0, + 4, 30, 0, 0, 255, 255, 223, 255, 255, 255, 255, 223, 100, 222, 255, 235, + 239, 255, 255, 255, 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, + 63, 255, 255, 255, 255, 207, 255, 255, 150, 254, 247, 10, 132, 234, 150, 170, + 150, 247, 247, 94, 255, 251, 255, 15, 238, 251, 255, 15, 0, 0, 3, 0, +}; + +/* Math: 538 bytes. */ + +RE_UINT32 re_get_math(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_math_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_math_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_math_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_math_stage_4[pos + f] << 5; + pos += code; + value = (re_math_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Alphabetic. */ + +static RE_UINT8 re_alphabetic_stage_1[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, +}; + +static RE_UINT8 re_alphabetic_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 13, 13, 26, 27, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 28, 7, 29, 30, 7, 31, 13, 13, 13, 13, 13, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_alphabetic_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 17, 18, 19, 1, 20, 21, 22, 23, 24, 25, 26, 27, 1, 28, + 29, 30, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 31, + 36, 37, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 40, 1, 41, 42, 43, 44, 45, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 47, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 48, 49, 1, 50, 51, 52, 53, 54, 55, 56, 57, 58, 1, 59, + 60, 61, 62, 63, 64, 31, 31, 31, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 31, 74, 31, 31, 31, 31, 31, 1, 1, 1, 75, 76, 77, 31, 31, + 1, 1, 1, 1, 78, 31, 31, 31, 31, 31, 31, 31, 1, 1, 79, 31, + 1, 1, 80, 81, 31, 31, 31, 82, 83, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 84, 31, 31, 31, 31, 31, 31, 31, 85, 86, 87, 88, + 89, 31, 31, 31, 31, 31, 90, 31, 31, 91, 31, 31, 31, 31, 31, 31, + 1, 1, 1, 1, 1, 1, 92, 1, 1, 1, 1, 1, 1, 1, 1, 93, + 94, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 95, 31, + 1, 1, 96, 31, 31, 31, 31, 31, +}; + +static RE_UINT8 re_alphabetic_stage_4[] = { + 0, 0, 1, 1, 0, 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 5, 6, 0, 0, 7, 8, 9, 10, 4, 11, + 4, 4, 4, 4, 12, 4, 4, 4, 4, 13, 14, 15, 16, 17, 18, 19, + 20, 4, 21, 22, 4, 4, 23, 24, 25, 4, 26, 4, 4, 27, 28, 29, + 30, 31, 32, 0, 0, 33, 0, 34, 4, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 47, 51, 52, 53, 54, 55, 0, + 56, 57, 58, 59, 60, 61, 62, 63, 60, 64, 65, 66, 67, 68, 69, 70, + 15, 71, 72, 0, 73, 74, 75, 0, 76, 0, 77, 78, 79, 80, 0, 0, + 4, 81, 25, 82, 83, 4, 84, 85, 4, 4, 86, 4, 87, 88, 89, 4, + 90, 4, 91, 0, 92, 4, 4, 93, 15, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 94, 1, 4, 4, 95, 96, 97, 97, 98, 4, 99, 100, 0, + 0, 4, 4, 101, 4, 102, 4, 103, 104, 105, 25, 106, 4, 107, 108, 0, + 109, 4, 104, 110, 0, 111, 0, 0, 4, 112, 113, 0, 4, 114, 4, 115, + 4, 103, 116, 117, 0, 0, 0, 118, 4, 4, 4, 4, 4, 4, 0, 119, + 93, 4, 120, 117, 4, 121, 122, 123, 0, 0, 0, 124, 125, 0, 0, 0, + 126, 127, 128, 4, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 130, 4, 108, 4, 131, 104, 4, 4, 4, 4, 132, + 4, 84, 4, 133, 134, 135, 135, 4, 0, 136, 0, 0, 0, 0, 0, 0, + 137, 138, 15, 4, 139, 15, 4, 85, 140, 141, 4, 4, 142, 71, 0, 25, + 4, 4, 4, 4, 4, 103, 0, 0, 4, 4, 4, 4, 4, 4, 103, 0, + 4, 4, 4, 4, 31, 0, 25, 117, 143, 144, 4, 145, 4, 4, 4, 92, + 146, 147, 4, 4, 148, 149, 0, 146, 150, 16, 4, 97, 4, 4, 59, 151, + 28, 102, 152, 80, 4, 153, 136, 154, 4, 134, 155, 156, 4, 104, 157, 158, + 159, 160, 85, 161, 4, 4, 4, 162, 4, 4, 4, 4, 4, 163, 164, 109, + 4, 4, 4, 165, 4, 4, 166, 0, 167, 168, 169, 4, 4, 27, 170, 4, + 4, 117, 25, 4, 171, 4, 16, 172, 0, 0, 0, 173, 4, 4, 4, 80, + 0, 1, 1, 174, 4, 104, 175, 0, 176, 177, 178, 0, 4, 4, 4, 71, + 0, 0, 4, 33, 0, 0, 0, 0, 0, 0, 0, 0, 80, 4, 179, 0, + 4, 25, 102, 71, 117, 4, 180, 0, 4, 4, 4, 4, 117, 0, 0, 0, + 4, 181, 4, 59, 0, 0, 0, 0, 4, 134, 103, 16, 0, 0, 0, 0, + 182, 183, 103, 134, 104, 0, 0, 184, 103, 166, 0, 0, 4, 185, 0, 0, + 186, 97, 0, 80, 80, 0, 77, 187, 4, 103, 103, 152, 27, 0, 0, 0, + 4, 4, 129, 0, 4, 152, 4, 152, 4, 4, 188, 0, 147, 32, 25, 129, + 4, 152, 25, 189, 4, 4, 190, 0, 191, 192, 0, 0, 193, 194, 4, 129, + 38, 47, 195, 59, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 196, 0, + 0, 0, 0, 0, 4, 197, 198, 0, 4, 104, 199, 0, 4, 103, 0, 0, + 200, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 201, + 0, 0, 0, 0, 0, 0, 4, 32, 4, 4, 4, 4, 166, 0, 0, 0, + 4, 4, 4, 142, 4, 4, 4, 4, 4, 4, 59, 0, 0, 0, 0, 0, + 4, 142, 0, 0, 0, 0, 0, 0, 4, 4, 202, 0, 0, 0, 0, 0, + 4, 32, 104, 0, 0, 0, 25, 155, 4, 134, 59, 203, 92, 0, 0, 0, + 4, 4, 204, 104, 170, 0, 0, 0, 205, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 4, 206, 207, 0, 0, 0, 4, 4, 208, 4, 209, 210, 211, 4, + 212, 213, 214, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 215, 216, 85, + 208, 208, 131, 131, 217, 217, 218, 0, 4, 4, 4, 4, 4, 4, 187, 0, + 211, 219, 220, 221, 222, 223, 0, 0, 0, 25, 224, 224, 108, 0, 0, 0, + 4, 4, 4, 4, 4, 4, 134, 0, 4, 33, 4, 4, 4, 4, 4, 4, + 117, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 205, 0, 0, + 117, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_alphabetic_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 4, 32, 4, 255, 255, 127, 255, + 255, 255, 255, 255, 195, 255, 3, 0, 31, 80, 0, 0, 32, 0, 0, 0, + 0, 0, 223, 188, 64, 215, 255, 255, 251, 255, 255, 255, 255, 255, 191, 255, + 3, 252, 255, 255, 255, 255, 254, 255, 255, 255, 127, 2, 254, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 255, 191, 182, 0, 255, 255, 255, 7, 7, 0, + 0, 0, 255, 7, 255, 255, 255, 254, 0, 192, 255, 255, 255, 255, 239, 31, + 254, 225, 0, 156, 0, 0, 255, 255, 0, 224, 255, 255, 255, 255, 3, 0, + 0, 252, 255, 255, 255, 7, 48, 4, 255, 255, 255, 252, 255, 31, 0, 0, + 255, 255, 255, 1, 255, 255, 31, 0, 248, 3, 255, 255, 255, 255, 255, 239, + 255, 223, 225, 255, 15, 0, 254, 255, 239, 159, 249, 255, 255, 253, 197, 227, + 159, 89, 128, 176, 15, 0, 3, 0, 238, 135, 249, 255, 255, 253, 109, 195, + 135, 25, 2, 94, 0, 0, 63, 0, 238, 191, 251, 255, 255, 253, 237, 227, + 191, 27, 1, 0, 15, 0, 0, 2, 238, 159, 249, 255, 159, 25, 192, 176, + 15, 0, 2, 0, 236, 199, 61, 214, 24, 199, 255, 195, 199, 29, 129, 0, + 239, 223, 253, 255, 255, 253, 255, 227, 223, 29, 96, 7, 15, 0, 0, 0, + 238, 223, 253, 255, 255, 253, 239, 227, 223, 29, 96, 64, 15, 0, 6, 0, + 255, 255, 255, 231, 223, 93, 128, 128, 15, 0, 0, 252, 236, 255, 127, 252, + 255, 255, 251, 47, 127, 128, 95, 255, 0, 0, 12, 0, 255, 255, 255, 7, + 127, 32, 0, 0, 150, 37, 240, 254, 174, 236, 255, 59, 95, 32, 0, 240, + 1, 0, 0, 0, 255, 254, 255, 255, 255, 31, 254, 255, 3, 255, 255, 254, + 255, 255, 255, 31, 255, 255, 127, 249, 231, 193, 255, 255, 127, 64, 0, 48, + 191, 32, 255, 255, 255, 255, 255, 247, 255, 61, 127, 61, 255, 61, 255, 255, + 255, 255, 61, 127, 61, 255, 127, 255, 255, 255, 61, 255, 255, 255, 255, 135, + 255, 255, 0, 0, 255, 255, 63, 63, 255, 159, 255, 255, 255, 199, 255, 1, + 255, 223, 15, 0, 255, 255, 15, 0, 255, 223, 13, 0, 255, 255, 207, 255, + 255, 1, 128, 16, 255, 255, 255, 0, 255, 7, 255, 255, 255, 255, 63, 0, + 255, 255, 255, 127, 255, 15, 255, 1, 255, 63, 31, 0, 255, 15, 255, 255, + 255, 3, 0, 0, 255, 255, 255, 15, 254, 255, 31, 0, 128, 0, 0, 0, + 255, 255, 239, 255, 239, 15, 0, 0, 255, 243, 0, 252, 191, 255, 3, 0, + 0, 224, 0, 252, 255, 255, 255, 63, 0, 222, 111, 0, 128, 255, 31, 0, + 63, 63, 255, 170, 255, 255, 223, 95, 220, 31, 207, 15, 255, 31, 220, 31, + 0, 0, 2, 128, 0, 0, 255, 31, 132, 252, 47, 62, 80, 189, 255, 243, + 224, 67, 0, 0, 255, 1, 0, 0, 0, 0, 192, 255, 255, 127, 255, 255, + 31, 120, 12, 0, 255, 128, 0, 0, 255, 255, 127, 0, 127, 127, 127, 127, + 0, 128, 0, 0, 224, 0, 0, 0, 254, 3, 62, 31, 255, 255, 127, 224, + 224, 255, 255, 255, 255, 63, 254, 255, 255, 127, 0, 0, 255, 31, 255, 255, + 0, 12, 0, 0, 255, 127, 240, 143, 0, 0, 128, 255, 252, 255, 255, 255, + 255, 249, 255, 255, 255, 63, 255, 0, 187, 247, 255, 255, 0, 0, 252, 40, + 255, 255, 7, 0, 255, 255, 247, 255, 223, 255, 0, 124, 255, 63, 0, 0, + 255, 255, 127, 196, 5, 0, 0, 56, 255, 255, 60, 0, 126, 126, 126, 0, + 127, 127, 255, 255, 63, 0, 255, 255, 255, 7, 0, 0, 15, 0, 255, 255, + 127, 248, 255, 255, 255, 63, 255, 255, 255, 255, 255, 3, 127, 0, 248, 224, + 255, 253, 127, 95, 219, 255, 255, 255, 0, 0, 248, 255, 255, 255, 252, 255, + 0, 0, 255, 15, 0, 0, 223, 255, 192, 255, 255, 255, 252, 252, 252, 28, + 255, 239, 255, 255, 127, 255, 255, 183, 255, 63, 255, 63, 255, 255, 1, 0, + 15, 255, 62, 0, 255, 0, 255, 255, 63, 253, 255, 255, 255, 255, 191, 145, + 255, 255, 55, 0, 255, 255, 255, 192, 111, 240, 239, 254, 31, 0, 0, 0, + 63, 0, 0, 0, 255, 255, 71, 0, 30, 0, 0, 20, 255, 255, 251, 255, + 255, 255, 159, 0, 127, 189, 255, 191, 255, 1, 255, 255, 159, 25, 129, 224, + 179, 0, 0, 0, 255, 255, 63, 127, 0, 0, 0, 63, 17, 0, 0, 0, + 255, 255, 255, 227, 0, 0, 0, 128, 127, 0, 0, 0, 248, 255, 255, 224, + 31, 0, 255, 255, 3, 0, 0, 0, 255, 7, 255, 31, 255, 1, 255, 67, + 255, 255, 223, 255, 255, 255, 255, 223, 100, 222, 255, 235, 239, 255, 255, 255, + 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, 63, 255, 255, 255, + 253, 255, 255, 247, 255, 253, 255, 255, 247, 15, 0, 0, 150, 254, 247, 10, + 132, 234, 150, 170, 150, 247, 247, 94, 255, 251, 255, 15, 238, 251, 255, 15, + 255, 3, 255, 255, +}; + +/* Alphabetic: 2085 bytes. */ + +RE_UINT32 re_get_alphabetic(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_alphabetic_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_alphabetic_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_alphabetic_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_alphabetic_stage_4[pos + f] << 5; + pos += code; + value = (re_alphabetic_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Lowercase. */ + +static RE_UINT8 re_lowercase_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_lowercase_stage_2[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 5, + 6, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static RE_UINT8 re_lowercase_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 9, 10, + 11, 12, 6, 6, 13, 6, 6, 6, 6, 6, 6, 6, 14, 15, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 16, 17, 6, 6, 6, 18, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 19, 6, 6, 6, 20, + 6, 6, 6, 6, 21, 6, 6, 6, 6, 6, 6, 6, 22, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 23, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 24, 25, 26, 27, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_lowercase_stage_4[] = { + 0, 0, 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 5, 13, 14, 15, 16, 17, 18, 19, 0, 0, 20, 21, 22, 23, 24, 25, + 0, 26, 15, 5, 27, 5, 28, 5, 5, 29, 0, 30, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, + 15, 15, 15, 15, 15, 15, 0, 0, 5, 5, 5, 5, 33, 5, 5, 5, + 34, 35, 36, 37, 35, 38, 39, 40, 0, 0, 0, 41, 42, 0, 0, 0, + 43, 44, 45, 26, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 47, + 0, 26, 48, 49, 5, 5, 5, 50, 15, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 52, 53, 0, 0, 0, 0, 54, 5, 55, 56, 57, 0, 58, + 0, 26, 59, 60, 15, 15, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 62, 63, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 15, 64, 0, 0, 0, 0, 0, 0, 15, 0, + 65, 66, 67, 31, 68, 69, 70, 71, 72, 73, 74, 75, 76, 65, 66, 77, + 31, 68, 78, 63, 71, 79, 80, 81, 82, 78, 83, 26, 84, 71, 85, 0, +}; + +static RE_UINT8 re_lowercase_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 4, 32, 4, 0, 0, 0, 128, + 255, 255, 127, 255, 170, 170, 170, 170, 170, 170, 170, 85, 85, 171, 170, 170, + 170, 170, 170, 212, 41, 49, 36, 78, 42, 45, 81, 230, 64, 82, 85, 181, + 170, 170, 41, 170, 170, 170, 250, 147, 133, 170, 255, 255, 255, 255, 255, 255, + 255, 255, 239, 255, 255, 255, 255, 1, 3, 0, 0, 0, 31, 0, 0, 0, + 32, 0, 0, 0, 0, 0, 138, 60, 0, 0, 1, 0, 0, 240, 255, 255, + 255, 127, 227, 170, 170, 170, 47, 25, 0, 0, 255, 255, 2, 168, 170, 170, + 84, 213, 170, 170, 170, 170, 0, 0, 254, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 63, 170, 170, 234, 191, 255, 0, 63, 0, 255, 0, 255, 0, + 63, 0, 255, 0, 255, 0, 255, 63, 255, 0, 223, 64, 220, 0, 207, 0, + 255, 0, 220, 0, 0, 0, 2, 128, 0, 0, 255, 31, 0, 196, 8, 0, + 0, 128, 16, 50, 192, 67, 0, 0, 16, 0, 0, 0, 255, 3, 0, 0, + 255, 255, 255, 127, 98, 21, 218, 63, 26, 80, 8, 0, 191, 32, 0, 0, + 170, 42, 0, 0, 170, 170, 170, 58, 168, 170, 171, 170, 170, 170, 255, 149, + 170, 80, 186, 170, 170, 2, 160, 0, 0, 0, 0, 7, 255, 255, 255, 247, + 63, 0, 255, 255, 127, 0, 248, 0, 0, 255, 255, 255, 255, 255, 0, 0, + 255, 255, 7, 0, 0, 0, 0, 252, 255, 255, 15, 0, 0, 192, 223, 255, + 252, 255, 255, 15, 0, 0, 192, 235, 239, 255, 0, 0, 0, 252, 255, 255, + 15, 0, 0, 192, 255, 255, 255, 0, 0, 0, 252, 255, 255, 15, 0, 0, + 192, 255, 255, 255, 0, 192, 255, 255, 0, 0, 192, 255, 63, 0, 0, 0, + 252, 255, 255, 247, 3, 0, 0, 240, 255, 255, 223, 15, 255, 127, 63, 0, + 255, 253, 0, 0, 247, 11, 0, 0, +}; + +/* Lowercase: 777 bytes. */ + +RE_UINT32 re_get_lowercase(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_lowercase_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_lowercase_stage_2[pos + f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_lowercase_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_lowercase_stage_4[pos + f] << 5; + pos += code; + value = (re_lowercase_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Uppercase. */ + +static RE_UINT8 re_uppercase_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_uppercase_stage_2[] = { + 0, 1, 2, 3, 4, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, + 8, 9, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 1, 1, 1, 12, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_uppercase_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 10, + 6, 11, 6, 6, 12, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 14, 15, 6, 6, 6, 6, 6, 6, 6, 16, + 6, 6, 6, 6, 17, 6, 6, 6, 6, 6, 6, 6, 18, 6, 6, 6, + 19, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 20, 21, 22, 23, + 6, 24, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_uppercase_stage_4[] = { + 0, 0, 1, 0, 0, 0, 2, 0, 3, 4, 5, 6, 7, 8, 9, 10, + 3, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 15, 16, 17, + 18, 19, 0, 3, 20, 3, 21, 3, 3, 22, 23, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 24, 0, + 0, 0, 0, 0, 0, 18, 18, 25, 3, 3, 3, 3, 26, 3, 3, 3, + 27, 28, 29, 30, 0, 31, 32, 33, 34, 35, 36, 19, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 19, 0, 18, 39, 0, 40, 3, 3, 3, 41, + 0, 0, 3, 42, 43, 0, 0, 0, 0, 44, 3, 45, 46, 47, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 18, 48, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 18, 49, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 50, 51, 52, + 53, 63, 25, 56, 57, 53, 64, 65, 66, 67, 38, 39, 56, 68, 69, 0, + 0, 56, 70, 70, 57, 0, 0, 0, +}; + +static RE_UINT8 re_uppercase_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 255, 255, 127, 127, 85, 85, 85, 85, + 85, 85, 85, 170, 170, 84, 85, 85, 85, 85, 85, 43, 214, 206, 219, 177, + 213, 210, 174, 17, 144, 164, 170, 74, 85, 85, 210, 85, 85, 85, 5, 108, + 122, 85, 0, 0, 0, 0, 69, 128, 64, 215, 254, 255, 251, 15, 0, 0, + 0, 128, 28, 85, 85, 85, 144, 230, 255, 255, 255, 255, 255, 255, 0, 0, + 1, 84, 85, 85, 171, 42, 85, 85, 85, 85, 254, 255, 255, 255, 127, 0, + 191, 32, 0, 0, 255, 255, 63, 0, 85, 85, 21, 64, 0, 255, 0, 63, + 0, 255, 0, 255, 0, 63, 0, 170, 0, 255, 0, 0, 0, 0, 0, 15, + 0, 15, 0, 15, 0, 31, 0, 15, 132, 56, 39, 62, 80, 61, 15, 192, + 32, 0, 0, 0, 8, 0, 0, 0, 0, 0, 192, 255, 255, 127, 0, 0, + 157, 234, 37, 192, 5, 40, 4, 0, 85, 21, 0, 0, 85, 85, 85, 5, + 84, 85, 84, 85, 85, 85, 0, 106, 85, 40, 69, 85, 85, 61, 95, 0, + 255, 0, 0, 0, 255, 255, 7, 0, 255, 255, 255, 3, 0, 0, 240, 255, + 255, 63, 0, 0, 0, 255, 255, 255, 3, 0, 0, 208, 100, 222, 63, 0, + 0, 0, 255, 255, 255, 3, 0, 0, 176, 231, 223, 31, 0, 0, 0, 123, + 95, 252, 1, 0, 0, 240, 255, 255, 63, 0, 0, 0, 3, 0, 0, 240, + 1, 0, 0, 0, 252, 255, 255, 7, 0, 0, 0, 240, 255, 255, 31, 0, + 255, 1, 0, 0, 0, 4, 0, 0, 255, 3, 255, 255, +}; + +/* Uppercase: 701 bytes. */ + +RE_UINT32 re_get_uppercase(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_uppercase_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_uppercase_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_uppercase_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_uppercase_stage_4[pos + f] << 5; + pos += code; + value = (re_uppercase_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Cased. */ + +static RE_UINT8 re_cased_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_cased_stage_2[] = { + 0, 1, 2, 3, 4, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, + 9, 10, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 1, 1, 1, 13, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_cased_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 10, 11, + 12, 13, 6, 6, 14, 6, 6, 6, 6, 6, 6, 6, 15, 16, 6, 6, + 6, 6, 6, 6, 6, 6, 17, 18, 6, 6, 6, 19, 6, 6, 6, 6, + 6, 6, 6, 20, 6, 6, 6, 21, 6, 6, 6, 6, 22, 6, 6, 6, + 6, 6, 6, 6, 23, 6, 6, 6, 24, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 25, 26, 27, 28, 6, 29, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_cased_stage_4[] = { + 0, 0, 1, 1, 0, 2, 3, 3, 4, 4, 4, 4, 4, 5, 6, 4, + 4, 4, 4, 4, 7, 8, 9, 10, 0, 0, 11, 12, 13, 14, 4, 15, + 4, 4, 4, 4, 16, 4, 4, 4, 4, 17, 18, 19, 20, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 21, 0, + 0, 0, 0, 0, 0, 4, 4, 22, 4, 4, 4, 4, 4, 4, 0, 0, + 4, 4, 4, 4, 4, 4, 4, 4, 22, 4, 23, 24, 4, 25, 26, 27, + 0, 0, 0, 28, 29, 0, 0, 0, 30, 31, 32, 4, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 34, 4, 35, 4, 36, 37, 4, 4, 4, 4, 38, + 4, 21, 0, 0, 0, 0, 0, 0, 0, 0, 4, 39, 24, 0, 0, 0, + 0, 40, 4, 4, 41, 42, 0, 43, 0, 44, 5, 45, 4, 4, 0, 0, + 46, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, + 4, 4, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 48, 4, 48, + 0, 0, 0, 0, 0, 4, 4, 0, 4, 4, 49, 4, 50, 51, 52, 4, + 53, 54, 55, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 56, 57, 5, + 49, 49, 36, 36, 58, 58, 59, 0, 0, 44, 60, 60, 35, 0, 0, 0, +}; + +static RE_UINT8 re_cased_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 4, 32, 4, 255, 255, 127, 255, + 255, 255, 255, 255, 255, 255, 255, 247, 240, 255, 255, 255, 255, 255, 239, 255, + 255, 255, 255, 1, 3, 0, 0, 0, 31, 0, 0, 0, 32, 0, 0, 0, + 0, 0, 207, 188, 64, 215, 255, 255, 251, 255, 255, 255, 255, 255, 191, 255, + 3, 252, 255, 255, 255, 255, 254, 255, 255, 255, 127, 0, 254, 255, 255, 255, + 255, 0, 0, 0, 191, 32, 0, 0, 255, 255, 63, 63, 63, 63, 255, 170, + 255, 255, 255, 63, 255, 255, 223, 95, 220, 31, 207, 15, 255, 31, 220, 31, + 0, 0, 2, 128, 0, 0, 255, 31, 132, 252, 47, 62, 80, 189, 31, 242, + 224, 67, 0, 0, 24, 0, 0, 0, 0, 0, 192, 255, 255, 3, 0, 0, + 255, 127, 255, 255, 255, 255, 255, 127, 31, 120, 12, 0, 255, 63, 0, 0, + 252, 255, 255, 255, 255, 120, 255, 255, 255, 63, 255, 0, 0, 0, 0, 7, + 0, 0, 255, 255, 63, 0, 255, 255, 127, 0, 248, 0, 255, 255, 0, 0, + 255, 255, 7, 0, 255, 255, 223, 255, 255, 255, 255, 223, 100, 222, 255, 235, + 239, 255, 255, 255, 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, + 63, 255, 255, 255, 253, 255, 255, 247, 255, 253, 255, 255, 247, 15, 0, 0, + 255, 3, 255, 255, +}; + +/* Cased: 709 bytes. */ + +RE_UINT32 re_get_cased(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_cased_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_cased_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_cased_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_cased_stage_4[pos + f] << 5; + pos += code; + value = (re_cased_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Case_Ignorable. */ + +static RE_UINT8 re_case_ignorable_stage_1[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, + 4, 4, +}; + +static RE_UINT8 re_case_ignorable_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, + 11, 12, 13, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 14, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 15, 7, 7, 16, 17, 7, 18, 19, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 20, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static RE_UINT8 re_case_ignorable_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 1, 17, 1, 1, 1, 18, 19, 20, 21, 22, 23, 24, 1, 25, + 26, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 28, 29, 1, + 30, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 31, 1, 1, 1, 32, 1, 33, 34, 35, 36, 37, 38, 1, 1, 1, 1, + 1, 1, 1, 39, 1, 1, 40, 41, 1, 42, 43, 44, 1, 1, 1, 1, + 1, 1, 45, 1, 1, 1, 1, 1, 46, 47, 48, 49, 50, 51, 52, 53, + 1, 1, 54, 55, 1, 1, 1, 56, 1, 1, 1, 1, 57, 1, 1, 1, + 1, 58, 59, 1, 1, 1, 1, 1, 1, 1, 60, 1, 1, 1, 1, 1, + 61, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 1, 1, 1, + 63, 64, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_case_ignorable_stage_4[] = { + 0, 1, 2, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 6, 6, 6, 6, 6, 7, 8, 0, 0, 0, + 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 10, 0, 11, 12, 13, 14, + 15, 0, 16, 17, 0, 0, 18, 19, 20, 5, 21, 0, 0, 22, 0, 23, + 24, 25, 26, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 33, 37, 38, 36, 33, 39, 35, 32, 40, 41, 35, 42, 0, 43, 0, + 3, 44, 45, 35, 32, 40, 46, 35, 32, 0, 34, 35, 0, 0, 47, 0, + 0, 48, 49, 0, 0, 50, 51, 0, 52, 53, 0, 54, 55, 56, 57, 0, + 0, 58, 59, 60, 61, 0, 0, 33, 0, 0, 62, 0, 0, 0, 0, 0, + 63, 63, 64, 64, 0, 65, 66, 0, 67, 0, 68, 0, 0, 69, 0, 0, + 0, 70, 0, 0, 0, 0, 0, 0, 71, 0, 72, 73, 0, 74, 0, 0, + 75, 76, 42, 77, 78, 79, 0, 80, 0, 81, 0, 82, 0, 0, 83, 84, + 0, 85, 6, 86, 87, 6, 6, 88, 0, 0, 0, 0, 0, 89, 90, 91, + 92, 93, 0, 94, 95, 0, 5, 96, 0, 0, 0, 97, 0, 0, 0, 98, + 0, 0, 0, 99, 0, 0, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, + 101, 102, 0, 0, 103, 0, 0, 104, 105, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 106, 0, 0, 107, 108, 0, 0, 109, + 6, 78, 0, 17, 110, 0, 0, 52, 111, 112, 0, 0, 0, 0, 113, 114, + 0, 115, 116, 0, 28, 117, 100, 112, 0, 118, 119, 120, 0, 121, 122, 123, + 0, 0, 87, 0, 0, 0, 0, 124, 2, 0, 0, 0, 0, 125, 78, 0, + 126, 127, 128, 0, 0, 0, 0, 129, 1, 2, 3, 17, 44, 0, 0, 130, + 0, 0, 0, 0, 0, 0, 0, 131, 0, 0, 0, 0, 0, 0, 0, 3, + 0, 0, 0, 132, 0, 0, 0, 0, 133, 134, 0, 0, 0, 0, 0, 112, + 32, 135, 136, 129, 78, 137, 0, 0, 28, 138, 0, 139, 78, 140, 141, 0, + 0, 142, 0, 0, 0, 0, 129, 143, 78, 33, 3, 144, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 145, 146, 0, 0, 0, 0, 0, 0, 147, 148, 0, + 0, 149, 3, 0, 0, 150, 0, 0, 62, 151, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 152, 0, 153, 75, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 154, 0, 0, 0, 0, 0, 0, 0, 155, 75, 0, 0, + 0, 0, 0, 156, 157, 158, 0, 0, 0, 0, 159, 0, 0, 0, 0, 0, + 6, 160, 6, 161, 162, 163, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, + 0, 0, 0, 0, 0, 0, 0, 87, 32, 6, 6, 6, 0, 0, 0, 0, + 6, 6, 6, 6, 6, 6, 6, 127, +}; + +static RE_UINT8 re_case_ignorable_stage_5[] = { + 0, 0, 0, 0, 128, 64, 0, 4, 0, 0, 0, 64, 1, 0, 0, 0, + 0, 161, 144, 1, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 48, 4, + 176, 0, 0, 0, 248, 3, 0, 0, 0, 0, 0, 2, 0, 0, 254, 255, + 255, 255, 255, 191, 182, 0, 0, 0, 0, 0, 16, 0, 63, 0, 255, 23, + 1, 248, 255, 255, 0, 0, 1, 0, 0, 0, 192, 191, 255, 61, 0, 0, + 0, 128, 2, 0, 255, 7, 0, 0, 192, 255, 1, 0, 0, 248, 63, 4, + 0, 0, 192, 255, 255, 63, 0, 0, 0, 0, 0, 14, 248, 255, 255, 255, + 7, 0, 0, 0, 0, 0, 0, 20, 254, 33, 254, 0, 12, 0, 2, 0, + 2, 0, 0, 0, 0, 0, 0, 16, 30, 32, 0, 0, 12, 0, 0, 0, + 6, 0, 0, 0, 134, 57, 2, 0, 0, 0, 35, 0, 190, 33, 0, 0, + 0, 0, 0, 144, 30, 32, 64, 0, 4, 0, 0, 0, 1, 32, 0, 0, + 0, 0, 0, 192, 193, 61, 96, 0, 64, 48, 0, 0, 0, 4, 92, 0, + 0, 0, 242, 7, 192, 127, 0, 0, 0, 0, 242, 27, 64, 63, 0, 0, + 0, 0, 0, 3, 0, 0, 160, 2, 0, 0, 254, 127, 223, 224, 255, 254, + 255, 255, 255, 31, 64, 0, 0, 0, 0, 224, 253, 102, 0, 0, 0, 195, + 1, 0, 30, 0, 100, 32, 0, 32, 0, 0, 0, 224, 0, 0, 28, 0, + 0, 0, 12, 0, 0, 0, 176, 63, 64, 254, 143, 32, 0, 120, 0, 0, + 8, 0, 0, 0, 0, 2, 0, 0, 135, 1, 4, 14, 0, 0, 128, 9, + 0, 0, 64, 127, 229, 31, 248, 159, 128, 0, 255, 127, 15, 0, 0, 0, + 0, 0, 208, 23, 0, 248, 15, 0, 3, 0, 0, 0, 60, 59, 0, 0, + 64, 163, 3, 0, 0, 240, 207, 0, 0, 0, 0, 63, 0, 0, 247, 255, + 253, 33, 16, 3, 0, 240, 255, 255, 255, 7, 0, 1, 0, 0, 0, 248, + 255, 255, 63, 240, 0, 0, 0, 160, 3, 224, 0, 224, 0, 224, 0, 96, + 0, 248, 0, 3, 144, 124, 0, 0, 223, 255, 2, 128, 0, 0, 255, 31, + 255, 255, 1, 0, 0, 0, 0, 48, 0, 128, 3, 0, 0, 128, 0, 128, + 0, 128, 0, 0, 32, 0, 0, 0, 0, 60, 62, 8, 0, 0, 0, 126, + 0, 0, 0, 112, 0, 0, 32, 0, 0, 16, 0, 0, 0, 128, 247, 191, + 0, 0, 0, 240, 0, 0, 3, 0, 0, 7, 0, 0, 68, 8, 0, 0, + 96, 0, 0, 0, 16, 0, 0, 0, 255, 255, 3, 0, 192, 63, 0, 0, + 128, 255, 3, 0, 0, 0, 200, 19, 0, 126, 102, 0, 8, 16, 0, 0, + 0, 0, 1, 16, 0, 0, 157, 193, 2, 0, 0, 32, 0, 48, 88, 0, + 32, 33, 0, 0, 0, 0, 252, 255, 255, 255, 8, 0, 255, 255, 0, 0, + 0, 0, 36, 0, 0, 0, 0, 128, 8, 0, 0, 14, 0, 0, 0, 32, + 0, 0, 192, 7, 110, 240, 0, 0, 0, 0, 0, 135, 0, 0, 0, 255, + 127, 0, 0, 0, 0, 0, 120, 38, 128, 239, 31, 0, 0, 0, 8, 0, + 0, 0, 192, 127, 0, 28, 0, 0, 0, 128, 211, 0, 248, 7, 0, 0, + 192, 31, 31, 0, 0, 0, 248, 133, 13, 0, 0, 0, 0, 0, 60, 176, + 1, 0, 0, 48, 0, 0, 248, 167, 0, 40, 191, 0, 188, 15, 0, 0, + 0, 0, 31, 0, 0, 0, 127, 0, 0, 128, 255, 255, 0, 0, 0, 96, + 128, 3, 248, 255, 231, 15, 0, 0, 0, 60, 0, 0, 28, 0, 0, 0, + 255, 255, 127, 248, 255, 31, 32, 0, 16, 0, 0, 248, 254, 255, 0, 0, +}; + +/* Case_Ignorable: 1474 bytes. */ + +RE_UINT32 re_get_case_ignorable(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_case_ignorable_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_case_ignorable_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_case_ignorable_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_case_ignorable_stage_4[pos + f] << 5; + pos += code; + value = (re_case_ignorable_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Changes_When_Lowercased. */ + +static RE_UINT8 re_changes_when_lowercased_stage_1[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, +}; + +static RE_UINT8 re_changes_when_lowercased_stage_2[] = { + 0, 1, 2, 3, 4, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, + 8, 9, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_changes_when_lowercased_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 10, + 6, 11, 6, 6, 12, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 14, 15, 6, 6, 6, 6, 6, 6, 6, 16, + 6, 6, 6, 6, 17, 6, 6, 6, 6, 6, 6, 6, 18, 6, 6, 6, + 19, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_changes_when_lowercased_stage_4[] = { + 0, 0, 1, 0, 0, 0, 2, 0, 3, 4, 5, 6, 7, 8, 9, 10, + 3, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 15, 16, 17, + 18, 19, 0, 3, 20, 3, 21, 3, 3, 22, 23, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 24, 0, + 0, 0, 0, 0, 0, 18, 18, 25, 3, 3, 3, 3, 26, 3, 3, 3, + 27, 28, 29, 30, 28, 31, 32, 33, 0, 34, 0, 19, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 19, 0, 18, 37, 0, 38, 3, 3, 3, 39, + 0, 0, 3, 40, 41, 0, 0, 0, 0, 42, 3, 43, 44, 45, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 18, 46, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 18, 47, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, +}; + +static RE_UINT8 re_changes_when_lowercased_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 255, 255, 127, 127, 85, 85, 85, 85, + 85, 85, 85, 170, 170, 84, 85, 85, 85, 85, 85, 43, 214, 206, 219, 177, + 213, 210, 174, 17, 176, 173, 170, 74, 85, 85, 214, 85, 85, 85, 5, 108, + 122, 85, 0, 0, 0, 0, 69, 128, 64, 215, 254, 255, 251, 15, 0, 0, + 0, 128, 0, 85, 85, 85, 144, 230, 255, 255, 255, 255, 255, 255, 0, 0, + 1, 84, 85, 85, 171, 42, 85, 85, 85, 85, 254, 255, 255, 255, 127, 0, + 191, 32, 0, 0, 255, 255, 63, 0, 85, 85, 21, 64, 0, 255, 0, 63, + 0, 255, 0, 255, 0, 63, 0, 170, 0, 255, 0, 0, 0, 255, 0, 31, + 0, 31, 0, 15, 0, 31, 0, 31, 64, 12, 4, 0, 8, 0, 0, 0, + 0, 0, 192, 255, 255, 127, 0, 0, 157, 234, 37, 192, 5, 40, 4, 0, + 85, 21, 0, 0, 85, 85, 85, 5, 84, 85, 84, 85, 85, 85, 0, 106, + 85, 40, 69, 85, 85, 61, 95, 0, 255, 0, 0, 0, 255, 255, 7, 0, +}; + +/* Changes_When_Lowercased: 538 bytes. */ + +RE_UINT32 re_get_changes_when_lowercased(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_changes_when_lowercased_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_changes_when_lowercased_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_changes_when_lowercased_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_changes_when_lowercased_stage_4[pos + f] << 5; + pos += code; + value = (re_changes_when_lowercased_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Changes_When_Uppercased. */ + +static RE_UINT8 re_changes_when_uppercased_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_changes_when_uppercased_stage_2[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 5, + 6, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static RE_UINT8 re_changes_when_uppercased_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 9, 10, + 6, 11, 6, 6, 12, 6, 6, 6, 6, 6, 6, 6, 13, 14, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 15, 16, 6, 6, 6, 17, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 18, 6, 6, 6, 19, + 6, 6, 6, 6, 20, 6, 6, 6, 6, 6, 6, 6, 21, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 22, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_changes_when_uppercased_stage_4[] = { + 0, 0, 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 5, 13, 14, 15, 16, 0, 0, 0, 0, 0, 17, 18, 19, 20, 21, 22, + 0, 23, 24, 5, 25, 5, 26, 5, 5, 27, 0, 28, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 0, 0, 0, 31, 0, 0, 0, 0, 5, 5, 5, 5, 32, 5, 5, 5, + 33, 34, 35, 36, 24, 37, 38, 39, 0, 0, 40, 23, 41, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 42, 0, 23, 43, 44, 5, 5, 5, 45, + 24, 46, 0, 0, 0, 0, 0, 0, 0, 0, 5, 47, 48, 0, 0, 0, + 0, 49, 5, 50, 51, 52, 0, 0, 0, 0, 53, 23, 24, 24, 0, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 55, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 57, + 0, 0, 0, 0, 0, 0, 24, 0, +}; + +static RE_UINT8 re_changes_when_uppercased_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 0, 32, 0, 0, 0, 0, 128, + 255, 255, 127, 255, 170, 170, 170, 170, 170, 170, 170, 84, 85, 171, 170, 170, + 170, 170, 170, 212, 41, 17, 36, 70, 42, 33, 81, 162, 96, 91, 85, 181, + 170, 170, 45, 170, 168, 170, 10, 144, 133, 170, 223, 26, 107, 155, 38, 32, + 137, 31, 4, 96, 32, 0, 0, 0, 0, 0, 138, 56, 0, 0, 1, 0, + 0, 240, 255, 255, 255, 127, 227, 170, 170, 170, 47, 9, 0, 0, 255, 255, + 255, 255, 255, 255, 2, 168, 170, 170, 84, 213, 170, 170, 170, 170, 0, 0, + 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 34, + 170, 170, 234, 15, 255, 0, 63, 0, 255, 0, 255, 0, 63, 0, 255, 0, + 255, 0, 255, 63, 255, 255, 223, 80, 220, 16, 207, 0, 255, 0, 220, 16, + 0, 64, 0, 0, 16, 0, 0, 0, 255, 3, 0, 0, 255, 255, 255, 127, + 98, 21, 72, 0, 10, 80, 8, 0, 191, 32, 0, 0, 170, 42, 0, 0, + 170, 170, 170, 10, 168, 170, 168, 170, 170, 170, 0, 148, 170, 16, 138, 170, + 170, 2, 160, 0, 0, 0, 8, 0, 127, 0, 248, 0, 0, 255, 255, 255, + 255, 255, 0, 0, 255, 255, 7, 0, +}; + +/* Changes_When_Uppercased: 609 bytes. */ + +RE_UINT32 re_get_changes_when_uppercased(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_changes_when_uppercased_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_changes_when_uppercased_stage_2[pos + f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_changes_when_uppercased_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_changes_when_uppercased_stage_4[pos + f] << 5; + pos += code; + value = (re_changes_when_uppercased_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Changes_When_Titlecased. */ + +static RE_UINT8 re_changes_when_titlecased_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_changes_when_titlecased_stage_2[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 5, + 6, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static RE_UINT8 re_changes_when_titlecased_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 9, 10, + 6, 11, 6, 6, 12, 6, 6, 6, 6, 6, 6, 6, 13, 14, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 15, 16, 6, 6, 6, 17, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 18, 6, 6, 6, 19, + 6, 6, 6, 6, 20, 6, 6, 6, 6, 6, 6, 6, 21, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 22, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_changes_when_titlecased_stage_4[] = { + 0, 0, 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 5, 13, 14, 15, 16, 0, 0, 0, 0, 0, 17, 18, 19, 20, 21, 22, + 0, 23, 24, 5, 25, 5, 26, 5, 5, 27, 0, 28, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 0, 0, 0, 31, 0, 0, 0, 0, 5, 5, 5, 5, 32, 5, 5, 5, + 33, 34, 35, 36, 34, 37, 38, 39, 0, 0, 40, 23, 41, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 42, 0, 23, 43, 44, 5, 5, 5, 45, + 24, 46, 0, 0, 0, 0, 0, 0, 0, 0, 5, 47, 48, 0, 0, 0, + 0, 49, 5, 50, 51, 52, 0, 0, 0, 0, 53, 23, 24, 24, 0, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 55, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 57, + 0, 0, 0, 0, 0, 0, 24, 0, +}; + +static RE_UINT8 re_changes_when_titlecased_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 0, 32, 0, 0, 0, 0, 128, + 255, 255, 127, 255, 170, 170, 170, 170, 170, 170, 170, 84, 85, 171, 170, 170, + 170, 170, 170, 212, 41, 17, 36, 70, 42, 33, 81, 162, 208, 86, 85, 181, + 170, 170, 43, 170, 168, 170, 10, 144, 133, 170, 223, 26, 107, 155, 38, 32, + 137, 31, 4, 96, 32, 0, 0, 0, 0, 0, 138, 56, 0, 0, 1, 0, + 0, 240, 255, 255, 255, 127, 227, 170, 170, 170, 47, 9, 0, 0, 255, 255, + 255, 255, 255, 255, 2, 168, 170, 170, 84, 213, 170, 170, 170, 170, 0, 0, + 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 34, + 170, 170, 234, 15, 255, 0, 63, 0, 255, 0, 255, 0, 63, 0, 255, 0, + 255, 0, 255, 63, 255, 0, 223, 64, 220, 0, 207, 0, 255, 0, 220, 0, + 0, 64, 0, 0, 16, 0, 0, 0, 255, 3, 0, 0, 255, 255, 255, 127, + 98, 21, 72, 0, 10, 80, 8, 0, 191, 32, 0, 0, 170, 42, 0, 0, + 170, 170, 170, 10, 168, 170, 168, 170, 170, 170, 0, 148, 170, 16, 138, 170, + 170, 2, 160, 0, 0, 0, 8, 0, 127, 0, 248, 0, 0, 255, 255, 255, + 255, 255, 0, 0, 255, 255, 7, 0, +}; + +/* Changes_When_Titlecased: 609 bytes. */ + +RE_UINT32 re_get_changes_when_titlecased(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_changes_when_titlecased_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_changes_when_titlecased_stage_2[pos + f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_changes_when_titlecased_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_changes_when_titlecased_stage_4[pos + f] << 5; + pos += code; + value = (re_changes_when_titlecased_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Changes_When_Casefolded. */ + +static RE_UINT8 re_changes_when_casefolded_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_changes_when_casefolded_stage_2[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 5, + 6, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static RE_UINT8 re_changes_when_casefolded_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 10, + 6, 11, 6, 6, 12, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 14, 15, 6, 6, 6, 16, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 17, 6, 6, 6, 18, + 6, 6, 6, 6, 19, 6, 6, 6, 6, 6, 6, 6, 20, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 21, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_changes_when_casefolded_stage_4[] = { + 0, 0, 1, 0, 0, 2, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11, + 4, 12, 13, 0, 0, 0, 0, 0, 0, 0, 14, 15, 16, 17, 18, 19, + 20, 21, 0, 4, 22, 4, 23, 4, 4, 24, 25, 0, 26, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 27, 0, + 0, 0, 0, 0, 0, 0, 0, 28, 4, 4, 4, 4, 29, 4, 4, 4, + 30, 31, 32, 33, 20, 34, 35, 36, 0, 37, 0, 21, 38, 0, 0, 0, + 0, 0, 0, 0, 0, 39, 21, 0, 20, 40, 0, 41, 4, 4, 4, 42, + 0, 0, 4, 43, 44, 0, 0, 0, 0, 45, 4, 46, 47, 48, 0, 0, + 0, 0, 0, 49, 20, 20, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 20, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 52, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, +}; + +static RE_UINT8 re_changes_when_casefolded_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 0, 32, 0, 255, 255, 127, 255, + 85, 85, 85, 85, 85, 85, 85, 170, 170, 86, 85, 85, 85, 85, 85, 171, + 214, 206, 219, 177, 213, 210, 174, 17, 176, 173, 170, 74, 85, 85, 214, 85, + 85, 85, 5, 108, 122, 85, 0, 0, 32, 0, 0, 0, 0, 0, 69, 128, + 64, 215, 254, 255, 251, 15, 0, 0, 4, 128, 99, 85, 85, 85, 179, 230, + 255, 255, 255, 255, 255, 255, 0, 0, 1, 84, 85, 85, 171, 42, 85, 85, + 85, 85, 254, 255, 255, 255, 127, 0, 128, 0, 0, 0, 191, 32, 0, 0, + 0, 0, 0, 63, 85, 85, 21, 76, 0, 255, 0, 63, 0, 255, 0, 255, + 0, 63, 0, 170, 0, 255, 0, 0, 255, 255, 156, 31, 156, 31, 0, 15, + 0, 31, 156, 31, 64, 12, 4, 0, 8, 0, 0, 0, 0, 0, 192, 255, + 255, 127, 0, 0, 157, 234, 37, 192, 5, 40, 4, 0, 85, 21, 0, 0, + 85, 85, 85, 5, 84, 85, 84, 85, 85, 85, 0, 106, 85, 40, 69, 85, + 85, 61, 95, 0, 0, 0, 255, 255, 127, 0, 248, 0, 255, 0, 0, 0, + 255, 255, 7, 0, +}; + +/* Changes_When_Casefolded: 581 bytes. */ + +RE_UINT32 re_get_changes_when_casefolded(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_changes_when_casefolded_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_changes_when_casefolded_stage_2[pos + f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_changes_when_casefolded_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_changes_when_casefolded_stage_4[pos + f] << 5; + pos += code; + value = (re_changes_when_casefolded_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Changes_When_Casemapped. */ + +static RE_UINT8 re_changes_when_casemapped_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_changes_when_casemapped_stage_2[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 5, + 6, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static RE_UINT8 re_changes_when_casemapped_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 10, 11, + 6, 12, 6, 6, 13, 6, 6, 6, 6, 6, 6, 6, 14, 15, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 16, 17, 6, 6, 6, 18, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 19, 6, 6, 6, 20, + 6, 6, 6, 6, 21, 6, 6, 6, 6, 6, 6, 6, 22, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 23, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_changes_when_casemapped_stage_4[] = { + 0, 0, 1, 1, 0, 2, 3, 3, 4, 5, 4, 4, 6, 7, 8, 4, + 4, 9, 10, 11, 12, 0, 0, 0, 0, 0, 13, 14, 15, 16, 17, 18, + 4, 4, 4, 4, 19, 4, 4, 4, 4, 20, 21, 22, 23, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 24, 0, + 0, 0, 0, 0, 0, 4, 4, 25, 0, 0, 0, 26, 0, 0, 0, 0, + 4, 4, 4, 4, 27, 4, 4, 4, 25, 4, 28, 29, 4, 30, 31, 32, + 0, 33, 34, 4, 35, 0, 0, 0, 0, 0, 0, 0, 0, 36, 4, 37, + 4, 38, 39, 40, 4, 4, 4, 41, 4, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 42, 43, 0, 0, 0, 0, 44, 4, 45, 46, 47, 0, 0, + 0, 0, 48, 49, 4, 4, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 4, 4, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 52, 4, 52, 0, 0, 0, 0, 0, 4, 4, 0, +}; + +static RE_UINT8 re_changes_when_casemapped_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 0, 32, 0, 255, 255, 127, 255, + 255, 255, 255, 255, 255, 255, 255, 254, 255, 223, 255, 247, 255, 243, 255, 179, + 240, 255, 255, 255, 253, 255, 15, 252, 255, 255, 223, 26, 107, 155, 38, 32, + 137, 31, 4, 96, 32, 0, 0, 0, 0, 0, 207, 184, 64, 215, 255, 255, + 251, 255, 255, 255, 255, 255, 227, 255, 255, 255, 191, 239, 3, 252, 255, 255, + 255, 255, 254, 255, 255, 255, 127, 0, 254, 255, 255, 255, 255, 0, 0, 0, + 191, 32, 0, 0, 255, 255, 63, 63, 0, 0, 0, 34, 255, 255, 255, 79, + 63, 63, 255, 170, 255, 255, 255, 63, 255, 255, 223, 95, 220, 31, 207, 15, + 255, 31, 220, 31, 64, 12, 4, 0, 0, 64, 0, 0, 24, 0, 0, 0, + 0, 0, 192, 255, 255, 3, 0, 0, 255, 127, 255, 255, 255, 255, 255, 127, + 255, 255, 109, 192, 15, 120, 12, 0, 255, 63, 0, 0, 255, 255, 255, 15, + 252, 255, 252, 255, 255, 255, 0, 254, 255, 56, 207, 255, 255, 63, 255, 0, + 0, 0, 8, 0, 0, 0, 255, 255, 127, 0, 248, 0, 255, 255, 0, 0, + 255, 255, 7, 0, +}; + +/* Changes_When_Casemapped: 597 bytes. */ + +RE_UINT32 re_get_changes_when_casemapped(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_changes_when_casemapped_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_changes_when_casemapped_stage_2[pos + f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_changes_when_casemapped_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_changes_when_casemapped_stage_4[pos + f] << 5; + pos += code; + value = (re_changes_when_casemapped_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* ID_Start. */ + +static RE_UINT8 re_id_start_stage_1[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, +}; + +static RE_UINT8 re_id_start_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 13, 13, 26, 13, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 27, 7, 28, 29, 7, 30, 13, 13, 13, 13, 13, 31, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_id_start_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 17, 18, 19, 1, 20, 21, 22, 23, 24, 25, 26, 27, 1, 28, + 29, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 31, 31, + 34, 35, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 37, + 1, 1, 1, 1, 38, 1, 39, 40, 41, 42, 43, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 45, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 46, 47, 1, 48, 49, 50, 51, 52, 53, 54, 55, 56, 1, 57, + 58, 59, 60, 61, 62, 31, 31, 31, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 31, 72, 31, 31, 31, 31, 31, 1, 1, 1, 73, 74, 75, 31, 31, + 1, 1, 1, 1, 76, 31, 31, 31, 31, 31, 31, 31, 1, 1, 77, 31, + 1, 1, 78, 79, 31, 31, 31, 80, 81, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 82, 31, 31, 31, 31, 31, 31, 31, 83, 84, 85, 86, + 87, 31, 31, 31, 31, 31, 88, 31, 1, 1, 1, 1, 1, 1, 89, 1, + 1, 1, 1, 1, 1, 1, 1, 90, 91, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 92, 31, 1, 1, 93, 31, 31, 31, 31, 31, +}; + +static RE_UINT8 re_id_start_stage_4[] = { + 0, 0, 1, 1, 0, 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 5, 6, 0, 0, 0, 7, 8, 9, 4, 10, + 4, 4, 4, 4, 11, 4, 4, 4, 4, 12, 13, 14, 15, 0, 16, 17, + 0, 4, 18, 19, 4, 4, 20, 21, 22, 23, 24, 4, 4, 25, 26, 27, + 28, 29, 30, 0, 0, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 45, 49, 50, 51, 52, 46, 0, + 53, 54, 55, 56, 53, 57, 58, 59, 53, 60, 61, 62, 63, 64, 65, 0, + 14, 66, 65, 0, 67, 68, 69, 0, 70, 0, 71, 72, 73, 0, 0, 0, + 4, 74, 75, 76, 77, 4, 78, 79, 4, 4, 80, 4, 81, 82, 83, 4, + 84, 4, 85, 0, 23, 4, 4, 86, 14, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 87, 1, 4, 4, 88, 89, 90, 90, 91, 4, 92, 93, 0, + 0, 4, 4, 94, 4, 95, 4, 96, 97, 0, 16, 98, 4, 99, 100, 0, + 101, 4, 31, 0, 0, 102, 0, 0, 103, 92, 104, 0, 105, 106, 4, 107, + 4, 108, 109, 110, 0, 0, 0, 111, 4, 4, 4, 4, 4, 4, 0, 0, + 86, 4, 112, 110, 4, 113, 114, 115, 0, 0, 0, 116, 117, 0, 0, 0, + 118, 119, 120, 4, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 122, 97, 4, 4, 4, 4, 123, 4, 78, 4, 124, 101, 125, 125, 0, + 126, 127, 14, 4, 128, 14, 4, 79, 103, 129, 4, 4, 130, 85, 0, 16, + 4, 4, 4, 4, 4, 96, 0, 0, 4, 4, 4, 4, 4, 4, 96, 0, + 4, 4, 4, 4, 72, 0, 16, 110, 131, 132, 4, 133, 110, 4, 4, 23, + 134, 135, 4, 4, 136, 137, 0, 134, 138, 139, 4, 92, 135, 92, 0, 140, + 26, 141, 65, 142, 32, 143, 144, 145, 4, 121, 146, 147, 4, 148, 149, 150, + 151, 152, 79, 141, 4, 4, 4, 139, 4, 4, 4, 4, 4, 153, 154, 155, + 4, 4, 4, 156, 4, 4, 157, 0, 158, 159, 160, 4, 4, 90, 161, 4, + 4, 110, 16, 4, 162, 4, 15, 163, 0, 0, 0, 164, 4, 4, 4, 142, + 0, 1, 1, 165, 4, 97, 166, 0, 167, 168, 169, 0, 4, 4, 4, 85, + 0, 0, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 142, 4, 170, 0, + 4, 16, 171, 96, 110, 4, 172, 0, 4, 4, 4, 4, 110, 0, 0, 0, + 4, 173, 4, 108, 0, 0, 0, 0, 4, 101, 96, 15, 0, 0, 0, 0, + 174, 175, 96, 101, 97, 0, 0, 176, 96, 157, 0, 0, 4, 177, 0, 0, + 178, 92, 0, 142, 142, 0, 71, 179, 4, 96, 96, 143, 90, 0, 0, 0, + 4, 4, 121, 0, 4, 143, 4, 143, 105, 94, 0, 0, 105, 23, 16, 121, + 105, 65, 16, 180, 105, 143, 181, 0, 182, 183, 0, 0, 184, 185, 97, 0, + 48, 45, 186, 56, 0, 0, 0, 0, 0, 0, 0, 0, 4, 23, 187, 0, + 0, 0, 0, 0, 4, 130, 188, 0, 4, 23, 189, 0, 4, 18, 0, 0, + 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 190, + 0, 0, 0, 0, 0, 0, 4, 30, 4, 4, 4, 4, 157, 0, 0, 0, + 4, 4, 4, 130, 4, 4, 4, 4, 4, 4, 108, 0, 0, 0, 0, 0, + 4, 130, 0, 0, 0, 0, 0, 0, 4, 4, 65, 0, 0, 0, 0, 0, + 4, 30, 97, 0, 0, 0, 16, 191, 4, 23, 108, 192, 23, 0, 0, 0, + 4, 4, 193, 0, 161, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 4, 194, 195, 0, 0, 0, 4, 4, 196, 4, 197, 198, 199, 4, + 200, 201, 202, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 203, 204, 79, + 196, 196, 122, 122, 205, 205, 146, 0, 4, 4, 4, 4, 4, 4, 179, 0, + 199, 206, 207, 208, 209, 210, 0, 0, 4, 4, 4, 4, 4, 4, 101, 0, + 4, 31, 4, 4, 4, 4, 4, 4, 110, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 56, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_id_start_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 4, 32, 4, 255, 255, 127, 255, + 255, 255, 255, 255, 195, 255, 3, 0, 31, 80, 0, 0, 0, 0, 223, 188, + 64, 215, 255, 255, 251, 255, 255, 255, 255, 255, 191, 255, 3, 252, 255, 255, + 255, 255, 254, 255, 255, 255, 127, 2, 254, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 255, 255, 255, 7, 7, 0, 255, 7, 0, 0, 0, 192, 254, 255, + 255, 255, 47, 0, 96, 192, 0, 156, 0, 0, 253, 255, 255, 255, 0, 0, + 0, 224, 255, 255, 63, 0, 2, 0, 0, 252, 255, 255, 255, 7, 48, 4, + 255, 255, 63, 4, 16, 1, 0, 0, 255, 255, 255, 1, 255, 255, 31, 0, + 240, 255, 255, 255, 255, 255, 255, 35, 0, 0, 1, 255, 3, 0, 254, 255, + 225, 159, 249, 255, 255, 253, 197, 35, 0, 64, 0, 176, 3, 0, 3, 0, + 224, 135, 249, 255, 255, 253, 109, 3, 0, 0, 0, 94, 0, 0, 28, 0, + 224, 191, 251, 255, 255, 253, 237, 35, 0, 0, 1, 0, 3, 0, 0, 2, + 224, 159, 249, 255, 0, 0, 0, 176, 3, 0, 2, 0, 232, 199, 61, 214, + 24, 199, 255, 3, 224, 223, 253, 255, 255, 253, 255, 35, 0, 0, 0, 7, + 3, 0, 0, 0, 255, 253, 239, 35, 0, 0, 0, 64, 3, 0, 6, 0, + 255, 255, 255, 39, 0, 64, 0, 128, 3, 0, 0, 252, 224, 255, 127, 252, + 255, 255, 251, 47, 127, 0, 0, 0, 255, 255, 13, 0, 150, 37, 240, 254, + 174, 236, 13, 32, 95, 0, 0, 240, 1, 0, 0, 0, 255, 254, 255, 255, + 255, 31, 0, 0, 0, 31, 0, 0, 255, 7, 0, 128, 0, 0, 63, 60, + 98, 192, 225, 255, 3, 64, 0, 0, 191, 32, 255, 255, 255, 255, 255, 247, + 255, 61, 127, 61, 255, 61, 255, 255, 255, 255, 61, 127, 61, 255, 127, 255, + 255, 255, 61, 255, 255, 255, 255, 7, 255, 255, 63, 63, 255, 159, 255, 255, + 255, 199, 255, 1, 255, 223, 3, 0, 255, 255, 3, 0, 255, 223, 1, 0, + 255, 255, 15, 0, 0, 0, 128, 16, 255, 255, 255, 0, 255, 5, 255, 255, + 255, 255, 63, 0, 255, 255, 255, 127, 255, 63, 31, 0, 255, 15, 255, 255, + 255, 3, 0, 0, 255, 255, 127, 0, 128, 0, 0, 0, 224, 255, 255, 255, + 224, 15, 0, 0, 248, 255, 255, 255, 1, 192, 0, 252, 63, 0, 0, 0, + 15, 0, 0, 0, 0, 224, 0, 252, 255, 255, 255, 63, 0, 222, 99, 0, + 63, 63, 255, 170, 255, 255, 223, 95, 220, 31, 207, 15, 255, 31, 220, 31, + 0, 0, 2, 128, 0, 0, 255, 31, 132, 252, 47, 63, 80, 253, 255, 243, + 224, 67, 0, 0, 255, 1, 0, 0, 255, 127, 255, 255, 31, 120, 12, 0, + 255, 128, 0, 0, 127, 127, 127, 127, 224, 0, 0, 0, 254, 3, 62, 31, + 255, 255, 127, 248, 255, 63, 254, 255, 255, 127, 0, 0, 255, 31, 255, 255, + 0, 12, 0, 0, 255, 127, 0, 128, 0, 0, 128, 255, 252, 255, 255, 255, + 255, 249, 255, 255, 255, 63, 255, 0, 187, 247, 255, 255, 7, 0, 0, 0, + 0, 0, 252, 40, 63, 0, 255, 255, 255, 255, 255, 31, 255, 255, 7, 0, + 0, 128, 0, 0, 223, 255, 0, 124, 247, 15, 0, 0, 255, 255, 127, 196, + 255, 255, 98, 62, 5, 0, 0, 56, 255, 7, 28, 0, 126, 126, 126, 0, + 127, 127, 255, 255, 15, 0, 255, 255, 127, 248, 255, 255, 255, 255, 255, 15, + 255, 63, 255, 255, 255, 255, 255, 3, 127, 0, 248, 160, 255, 253, 127, 95, + 219, 255, 255, 255, 0, 0, 248, 255, 255, 255, 252, 255, 0, 0, 255, 15, + 0, 0, 223, 255, 192, 255, 255, 255, 252, 252, 252, 28, 255, 239, 255, 255, + 127, 255, 255, 183, 255, 63, 255, 63, 255, 255, 1, 0, 255, 7, 255, 255, + 15, 255, 62, 0, 255, 0, 255, 255, 63, 253, 255, 255, 255, 255, 191, 145, + 255, 255, 55, 0, 255, 255, 255, 192, 1, 0, 239, 254, 31, 0, 0, 0, + 255, 255, 71, 0, 30, 0, 0, 20, 255, 255, 251, 255, 255, 15, 0, 0, + 127, 189, 255, 191, 255, 1, 255, 255, 0, 0, 1, 224, 176, 0, 0, 0, + 0, 0, 0, 15, 16, 0, 0, 0, 0, 0, 0, 128, 255, 63, 0, 0, + 248, 255, 255, 224, 31, 0, 1, 0, 255, 7, 255, 31, 255, 1, 255, 3, + 255, 255, 223, 255, 255, 255, 255, 223, 100, 222, 255, 235, 239, 255, 255, 255, + 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, 63, 255, 255, 255, + 253, 255, 255, 247, 255, 253, 255, 255, 150, 254, 247, 10, 132, 234, 150, 170, + 150, 247, 247, 94, 255, 251, 255, 15, 238, 251, 255, 15, +}; + +/* ID_Start: 1997 bytes. */ + +RE_UINT32 re_get_id_start(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_id_start_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_id_start_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_id_start_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_id_start_stage_4[pos + f] << 5; + pos += code; + value = (re_id_start_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* ID_Continue. */ + +static RE_UINT8 re_id_continue_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, + 6, 6, +}; + +static RE_UINT8 re_id_continue_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 26, 13, 27, 13, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 28, 7, 29, 30, 7, 31, 13, 13, 13, 13, 13, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 33, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_id_continue_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 17, 18, 19, 1, 20, 21, 22, 23, 24, 25, 26, 27, 1, 28, + 29, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 31, 31, + 34, 35, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 37, + 1, 1, 1, 1, 38, 1, 39, 40, 41, 42, 43, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 45, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 46, 47, 1, 48, 49, 50, 51, 52, 53, 54, 55, 56, 1, 57, + 58, 59, 60, 61, 62, 31, 31, 31, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 31, 72, 31, 31, 31, 31, 31, 1, 1, 1, 73, 74, 75, 31, 31, + 1, 1, 1, 1, 76, 31, 31, 31, 31, 31, 31, 31, 1, 1, 77, 31, + 1, 1, 78, 79, 31, 31, 31, 80, 81, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 82, 31, 31, 31, 31, 83, 84, 31, 85, 86, 87, 88, + 31, 31, 89, 31, 31, 31, 31, 31, 90, 31, 31, 31, 31, 31, 91, 31, + 1, 1, 1, 1, 1, 1, 92, 1, 1, 1, 1, 1, 1, 1, 1, 93, + 94, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 95, 31, + 1, 1, 96, 31, 31, 31, 31, 31, 31, 97, 31, 31, 31, 31, 31, 31, +}; + +static RE_UINT8 re_id_continue_stage_4[] = { + 0, 1, 2, 3, 0, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 7, 8, 6, 6, 6, 9, 10, 11, 6, 12, + 6, 6, 6, 6, 13, 6, 6, 6, 6, 14, 15, 16, 17, 18, 19, 20, + 21, 6, 6, 22, 6, 6, 23, 24, 25, 6, 26, 6, 6, 27, 6, 28, + 6, 29, 30, 0, 0, 31, 0, 32, 6, 6, 6, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 43, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 57, 61, 62, 63, 64, 65, 66, 67, + 16, 68, 69, 0, 70, 71, 72, 0, 73, 74, 75, 76, 77, 78, 79, 0, + 6, 6, 80, 6, 81, 6, 82, 83, 6, 6, 84, 6, 85, 86, 87, 6, + 88, 6, 61, 89, 90, 6, 6, 91, 16, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 92, 3, 6, 6, 93, 94, 31, 95, 96, 6, 6, 97, 98, + 99, 6, 6, 100, 6, 101, 6, 102, 103, 104, 105, 106, 6, 107, 108, 0, + 30, 6, 103, 109, 110, 111, 0, 0, 6, 6, 112, 113, 6, 6, 6, 95, + 6, 100, 114, 81, 0, 0, 115, 116, 6, 6, 6, 6, 6, 6, 6, 117, + 91, 6, 118, 81, 6, 119, 120, 121, 0, 122, 123, 124, 125, 0, 125, 126, + 127, 128, 129, 6, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 131, 103, 6, 6, 6, 6, 132, 6, 82, 6, 133, 134, 135, 135, 6, + 136, 137, 16, 6, 138, 16, 6, 83, 139, 140, 6, 6, 141, 68, 0, 25, + 6, 6, 6, 6, 6, 102, 0, 0, 6, 6, 6, 6, 6, 6, 102, 0, + 6, 6, 6, 6, 142, 0, 25, 81, 143, 144, 6, 145, 6, 6, 6, 27, + 146, 147, 6, 6, 148, 149, 0, 146, 6, 150, 6, 95, 6, 6, 151, 152, + 6, 153, 95, 78, 6, 6, 154, 103, 6, 134, 155, 156, 6, 6, 157, 158, + 159, 160, 83, 161, 6, 6, 6, 162, 6, 6, 6, 6, 6, 163, 164, 30, + 6, 6, 6, 153, 6, 6, 165, 0, 166, 167, 168, 6, 6, 27, 169, 6, + 6, 81, 25, 6, 170, 6, 150, 171, 90, 172, 173, 174, 6, 6, 6, 78, + 1, 2, 3, 105, 6, 103, 175, 0, 176, 177, 178, 0, 6, 6, 6, 68, + 0, 0, 6, 31, 0, 0, 0, 179, 0, 0, 0, 0, 78, 6, 180, 181, + 6, 25, 101, 68, 81, 6, 182, 0, 6, 6, 6, 6, 81, 98, 0, 0, + 6, 183, 6, 184, 0, 0, 0, 0, 6, 134, 102, 150, 0, 0, 0, 0, + 185, 186, 102, 134, 103, 0, 0, 187, 102, 165, 0, 0, 6, 188, 0, 0, + 189, 190, 0, 78, 78, 0, 75, 191, 6, 102, 102, 192, 27, 0, 0, 0, + 6, 6, 130, 0, 6, 192, 6, 192, 6, 6, 191, 193, 6, 68, 25, 194, + 6, 195, 25, 196, 6, 6, 197, 0, 198, 100, 0, 0, 199, 200, 6, 201, + 34, 43, 202, 203, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 204, 0, + 0, 0, 0, 0, 6, 205, 206, 0, 6, 6, 207, 0, 6, 100, 98, 0, + 208, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 209, + 0, 0, 0, 0, 0, 0, 6, 210, 6, 6, 6, 6, 165, 0, 0, 0, + 6, 6, 6, 141, 6, 6, 6, 6, 6, 6, 184, 0, 0, 0, 0, 0, + 6, 141, 0, 0, 0, 0, 0, 0, 6, 6, 191, 0, 0, 0, 0, 0, + 6, 210, 103, 98, 0, 0, 25, 106, 6, 134, 211, 212, 90, 0, 0, 0, + 6, 6, 213, 103, 214, 0, 0, 0, 215, 0, 0, 0, 0, 0, 0, 0, + 6, 6, 6, 216, 217, 0, 0, 0, 0, 0, 0, 218, 219, 220, 0, 0, + 0, 0, 221, 0, 0, 0, 0, 0, 6, 6, 195, 6, 222, 223, 224, 6, + 225, 226, 227, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 228, 229, 83, + 195, 195, 131, 131, 230, 230, 231, 6, 6, 232, 6, 233, 234, 235, 0, 0, + 6, 6, 6, 6, 6, 6, 236, 0, 224, 237, 238, 239, 240, 241, 0, 0, + 6, 6, 6, 6, 6, 6, 134, 0, 6, 31, 6, 6, 6, 6, 6, 6, + 81, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 215, 0, 0, + 81, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 90, +}; + +static RE_UINT8 re_id_continue_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 254, 255, 255, 135, 254, 255, 255, 7, + 0, 4, 160, 4, 255, 255, 127, 255, 255, 255, 255, 255, 195, 255, 3, 0, + 31, 80, 0, 0, 255, 255, 223, 188, 192, 215, 255, 255, 251, 255, 255, 255, + 255, 255, 191, 255, 251, 252, 255, 255, 255, 255, 254, 255, 255, 255, 127, 2, + 254, 255, 255, 255, 255, 0, 254, 255, 255, 255, 255, 191, 182, 0, 255, 255, + 255, 7, 7, 0, 0, 0, 255, 7, 255, 195, 255, 255, 255, 255, 239, 159, + 255, 253, 255, 159, 0, 0, 255, 255, 255, 231, 255, 255, 255, 255, 3, 0, + 255, 255, 63, 4, 255, 63, 0, 0, 255, 255, 255, 15, 255, 255, 31, 0, + 248, 255, 255, 255, 207, 255, 254, 255, 239, 159, 249, 255, 255, 253, 197, 243, + 159, 121, 128, 176, 207, 255, 3, 0, 238, 135, 249, 255, 255, 253, 109, 211, + 135, 57, 2, 94, 192, 255, 63, 0, 238, 191, 251, 255, 255, 253, 237, 243, + 191, 59, 1, 0, 207, 255, 0, 2, 238, 159, 249, 255, 159, 57, 192, 176, + 207, 255, 2, 0, 236, 199, 61, 214, 24, 199, 255, 195, 199, 61, 129, 0, + 192, 255, 0, 0, 239, 223, 253, 255, 255, 253, 255, 227, 223, 61, 96, 7, + 207, 255, 0, 0, 238, 223, 253, 255, 255, 253, 239, 243, 223, 61, 96, 64, + 207, 255, 6, 0, 255, 255, 255, 231, 223, 125, 128, 128, 207, 255, 0, 252, + 236, 255, 127, 252, 255, 255, 251, 47, 127, 132, 95, 255, 192, 255, 12, 0, + 255, 255, 255, 7, 255, 127, 255, 3, 150, 37, 240, 254, 174, 236, 255, 59, + 95, 63, 255, 243, 1, 0, 0, 3, 255, 3, 160, 194, 255, 254, 255, 255, + 255, 31, 254, 255, 223, 255, 255, 254, 255, 255, 255, 31, 64, 0, 0, 0, + 255, 3, 255, 255, 255, 255, 255, 63, 191, 32, 255, 255, 255, 255, 255, 247, + 255, 61, 127, 61, 255, 61, 255, 255, 255, 255, 61, 127, 61, 255, 127, 255, + 255, 255, 61, 255, 0, 254, 3, 0, 255, 255, 0, 0, 255, 255, 63, 63, + 255, 159, 255, 255, 255, 199, 255, 1, 255, 223, 31, 0, 255, 255, 15, 0, + 255, 223, 13, 0, 255, 255, 143, 48, 255, 3, 0, 0, 0, 56, 255, 3, + 255, 255, 255, 0, 255, 7, 255, 255, 255, 255, 63, 0, 255, 255, 255, 127, + 255, 15, 255, 15, 192, 255, 255, 255, 255, 63, 31, 0, 255, 15, 255, 255, + 255, 3, 255, 7, 255, 255, 255, 159, 255, 3, 255, 3, 128, 0, 255, 63, + 255, 15, 255, 3, 0, 248, 15, 0, 255, 227, 255, 255, 0, 0, 247, 255, + 255, 255, 127, 3, 255, 255, 63, 240, 63, 63, 255, 170, 255, 255, 223, 95, + 220, 31, 207, 15, 255, 31, 220, 31, 0, 0, 0, 128, 1, 0, 16, 0, + 0, 0, 2, 128, 0, 0, 255, 31, 226, 255, 1, 0, 132, 252, 47, 63, + 80, 253, 255, 243, 224, 67, 0, 0, 255, 1, 0, 0, 255, 127, 255, 255, + 31, 248, 15, 0, 255, 128, 0, 128, 255, 255, 127, 0, 127, 127, 127, 127, + 224, 0, 0, 0, 254, 255, 62, 31, 255, 255, 127, 254, 224, 255, 255, 255, + 255, 63, 254, 255, 255, 127, 0, 0, 255, 31, 0, 0, 255, 31, 255, 255, + 255, 15, 0, 0, 255, 255, 240, 191, 0, 0, 128, 255, 252, 255, 255, 255, + 255, 249, 255, 255, 255, 63, 255, 0, 255, 0, 0, 0, 31, 0, 255, 3, + 255, 255, 255, 40, 255, 63, 255, 255, 1, 128, 255, 3, 255, 63, 255, 3, + 255, 255, 127, 252, 7, 0, 0, 56, 255, 255, 124, 0, 126, 126, 126, 0, + 127, 127, 255, 255, 63, 0, 255, 255, 255, 55, 255, 3, 15, 0, 255, 255, + 127, 248, 255, 255, 255, 255, 255, 3, 127, 0, 248, 224, 255, 253, 127, 95, + 219, 255, 255, 255, 0, 0, 248, 255, 255, 255, 252, 255, 0, 0, 255, 15, + 255, 255, 24, 0, 0, 224, 0, 0, 0, 0, 223, 255, 252, 252, 252, 28, + 255, 239, 255, 255, 127, 255, 255, 183, 255, 63, 255, 63, 0, 0, 0, 32, + 255, 255, 1, 0, 1, 0, 0, 0, 15, 255, 62, 0, 255, 0, 255, 255, + 15, 0, 0, 0, 63, 253, 255, 255, 255, 255, 191, 145, 255, 255, 55, 0, + 255, 255, 255, 192, 111, 240, 239, 254, 255, 255, 15, 135, 127, 0, 0, 0, + 255, 255, 7, 0, 192, 255, 0, 128, 255, 1, 255, 3, 255, 255, 223, 255, + 255, 255, 79, 0, 31, 28, 255, 23, 255, 255, 251, 255, 127, 189, 255, 191, + 255, 1, 255, 255, 255, 7, 255, 3, 159, 57, 129, 224, 207, 31, 31, 0, + 191, 0, 255, 3, 255, 255, 63, 255, 1, 0, 0, 63, 17, 0, 255, 3, + 255, 255, 255, 227, 255, 3, 0, 128, 255, 255, 255, 1, 15, 0, 255, 3, + 248, 255, 255, 224, 31, 0, 255, 255, 0, 128, 255, 255, 3, 0, 0, 0, + 255, 7, 255, 31, 255, 1, 255, 99, 224, 227, 7, 248, 231, 15, 0, 0, + 0, 60, 0, 0, 28, 0, 0, 0, 255, 255, 255, 223, 100, 222, 255, 235, + 239, 255, 255, 255, 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, + 63, 255, 255, 255, 253, 255, 255, 247, 255, 253, 255, 255, 247, 207, 255, 255, + 255, 255, 127, 248, 255, 31, 32, 0, 16, 0, 0, 248, 254, 255, 0, 0, + 31, 0, 127, 0, 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, + 255, 251, 255, 15, 238, 251, 255, 15, +}; + +/* ID_Continue: 2186 bytes. */ + +RE_UINT32 re_get_id_continue(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_id_continue_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_id_continue_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_id_continue_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_id_continue_stage_4[pos + f] << 5; + pos += code; + value = (re_id_continue_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* XID_Start. */ + +static RE_UINT8 re_xid_start_stage_1[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, +}; + +static RE_UINT8 re_xid_start_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 13, 13, 26, 13, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 27, 7, 28, 29, 7, 30, 13, 13, 13, 13, 13, 31, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_xid_start_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 17, 18, 19, 1, 20, 21, 22, 23, 24, 25, 26, 27, 1, 28, + 29, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 31, 31, + 34, 35, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 37, + 1, 1, 1, 1, 38, 1, 39, 40, 41, 42, 43, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 45, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 1, 58, + 59, 60, 61, 62, 63, 31, 31, 31, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 31, 73, 31, 31, 31, 31, 31, 1, 1, 1, 74, 75, 76, 31, 31, + 1, 1, 1, 1, 77, 31, 31, 31, 31, 31, 31, 31, 1, 1, 78, 31, + 1, 1, 79, 80, 31, 31, 31, 81, 82, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 83, 31, 31, 31, 31, 31, 31, 31, 84, 85, 86, 87, + 88, 31, 31, 31, 31, 31, 89, 31, 1, 1, 1, 1, 1, 1, 90, 1, + 1, 1, 1, 1, 1, 1, 1, 91, 92, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 93, 31, 1, 1, 94, 31, 31, 31, 31, 31, +}; + +static RE_UINT8 re_xid_start_stage_4[] = { + 0, 0, 1, 1, 0, 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 5, 6, 0, 0, 0, 7, 8, 9, 4, 10, + 4, 4, 4, 4, 11, 4, 4, 4, 4, 12, 13, 14, 15, 0, 16, 17, + 0, 4, 18, 19, 4, 4, 20, 21, 22, 23, 24, 4, 4, 25, 26, 27, + 28, 29, 30, 0, 0, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 45, 49, 50, 51, 52, 46, 0, + 53, 54, 55, 56, 53, 57, 58, 59, 53, 60, 61, 62, 63, 64, 65, 0, + 14, 66, 65, 0, 67, 68, 69, 0, 70, 0, 71, 72, 73, 0, 0, 0, + 4, 74, 75, 76, 77, 4, 78, 79, 4, 4, 80, 4, 81, 82, 83, 4, + 84, 4, 85, 0, 23, 4, 4, 86, 14, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 87, 1, 4, 4, 88, 89, 90, 90, 91, 4, 92, 93, 0, + 0, 4, 4, 94, 4, 95, 4, 96, 97, 0, 16, 98, 4, 99, 100, 0, + 101, 4, 31, 0, 0, 102, 0, 0, 103, 92, 104, 0, 105, 106, 4, 107, + 4, 108, 109, 110, 0, 0, 0, 111, 4, 4, 4, 4, 4, 4, 0, 0, + 86, 4, 112, 110, 4, 113, 114, 115, 0, 0, 0, 116, 117, 0, 0, 0, + 118, 119, 120, 4, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 122, 97, 4, 4, 4, 4, 123, 4, 78, 4, 124, 101, 125, 125, 0, + 126, 127, 14, 4, 128, 14, 4, 79, 103, 129, 4, 4, 130, 85, 0, 16, + 4, 4, 4, 4, 4, 96, 0, 0, 4, 4, 4, 4, 4, 4, 96, 0, + 4, 4, 4, 4, 72, 0, 16, 110, 131, 132, 4, 133, 110, 4, 4, 23, + 134, 135, 4, 4, 136, 137, 0, 134, 138, 139, 4, 92, 135, 92, 0, 140, + 26, 141, 65, 142, 32, 143, 144, 145, 4, 121, 146, 147, 4, 148, 149, 150, + 151, 152, 79, 141, 4, 4, 4, 139, 4, 4, 4, 4, 4, 153, 154, 155, + 4, 4, 4, 156, 4, 4, 157, 0, 158, 159, 160, 4, 4, 90, 161, 4, + 4, 4, 110, 32, 4, 4, 4, 4, 4, 110, 16, 4, 162, 4, 15, 163, + 0, 0, 0, 164, 4, 4, 4, 142, 0, 1, 1, 165, 110, 97, 166, 0, + 167, 168, 169, 0, 4, 4, 4, 85, 0, 0, 4, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 142, 4, 170, 0, 4, 16, 171, 96, 110, 4, 172, 0, + 4, 4, 4, 4, 110, 0, 0, 0, 4, 173, 4, 108, 0, 0, 0, 0, + 4, 101, 96, 15, 0, 0, 0, 0, 174, 175, 96, 101, 97, 0, 0, 176, + 96, 157, 0, 0, 4, 177, 0, 0, 178, 92, 0, 142, 142, 0, 71, 179, + 4, 96, 96, 143, 90, 0, 0, 0, 4, 4, 121, 0, 4, 143, 4, 143, + 105, 94, 0, 0, 105, 23, 16, 121, 105, 65, 16, 180, 105, 143, 181, 0, + 182, 183, 0, 0, 184, 185, 97, 0, 48, 45, 186, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 23, 187, 0, 0, 0, 0, 0, 4, 130, 188, 0, + 4, 23, 189, 0, 4, 18, 0, 0, 157, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 4, 190, 0, 0, 0, 0, 0, 0, 4, 30, + 4, 4, 4, 4, 157, 0, 0, 0, 4, 4, 4, 130, 4, 4, 4, 4, + 4, 4, 108, 0, 0, 0, 0, 0, 4, 130, 0, 0, 0, 0, 0, 0, + 4, 4, 65, 0, 0, 0, 0, 0, 4, 30, 97, 0, 0, 0, 16, 191, + 4, 23, 108, 192, 23, 0, 0, 0, 4, 4, 193, 0, 161, 0, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 194, 195, 0, 0, 0, + 4, 4, 196, 4, 197, 198, 199, 4, 200, 201, 202, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 203, 204, 79, 196, 196, 122, 122, 205, 205, 146, 0, + 4, 4, 4, 4, 4, 4, 179, 0, 199, 206, 207, 208, 209, 210, 0, 0, + 4, 4, 4, 4, 4, 4, 101, 0, 4, 31, 4, 4, 4, 4, 4, 4, + 110, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 56, 0, 0, + 110, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_xid_start_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 7, 0, 4, 32, 4, 255, 255, 127, 255, + 255, 255, 255, 255, 195, 255, 3, 0, 31, 80, 0, 0, 0, 0, 223, 184, + 64, 215, 255, 255, 251, 255, 255, 255, 255, 255, 191, 255, 3, 252, 255, 255, + 255, 255, 254, 255, 255, 255, 127, 2, 254, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 255, 255, 255, 7, 7, 0, 255, 7, 0, 0, 0, 192, 254, 255, + 255, 255, 47, 0, 96, 192, 0, 156, 0, 0, 253, 255, 255, 255, 0, 0, + 0, 224, 255, 255, 63, 0, 2, 0, 0, 252, 255, 255, 255, 7, 48, 4, + 255, 255, 63, 4, 16, 1, 0, 0, 255, 255, 255, 1, 255, 255, 31, 0, + 240, 255, 255, 255, 255, 255, 255, 35, 0, 0, 1, 255, 3, 0, 254, 255, + 225, 159, 249, 255, 255, 253, 197, 35, 0, 64, 0, 176, 3, 0, 3, 0, + 224, 135, 249, 255, 255, 253, 109, 3, 0, 0, 0, 94, 0, 0, 28, 0, + 224, 191, 251, 255, 255, 253, 237, 35, 0, 0, 1, 0, 3, 0, 0, 2, + 224, 159, 249, 255, 0, 0, 0, 176, 3, 0, 2, 0, 232, 199, 61, 214, + 24, 199, 255, 3, 224, 223, 253, 255, 255, 253, 255, 35, 0, 0, 0, 7, + 3, 0, 0, 0, 255, 253, 239, 35, 0, 0, 0, 64, 3, 0, 6, 0, + 255, 255, 255, 39, 0, 64, 0, 128, 3, 0, 0, 252, 224, 255, 127, 252, + 255, 255, 251, 47, 127, 0, 0, 0, 255, 255, 5, 0, 150, 37, 240, 254, + 174, 236, 5, 32, 95, 0, 0, 240, 1, 0, 0, 0, 255, 254, 255, 255, + 255, 31, 0, 0, 0, 31, 0, 0, 255, 7, 0, 128, 0, 0, 63, 60, + 98, 192, 225, 255, 3, 64, 0, 0, 191, 32, 255, 255, 255, 255, 255, 247, + 255, 61, 127, 61, 255, 61, 255, 255, 255, 255, 61, 127, 61, 255, 127, 255, + 255, 255, 61, 255, 255, 255, 255, 7, 255, 255, 63, 63, 255, 159, 255, 255, + 255, 199, 255, 1, 255, 223, 3, 0, 255, 255, 3, 0, 255, 223, 1, 0, + 255, 255, 15, 0, 0, 0, 128, 16, 255, 255, 255, 0, 255, 5, 255, 255, + 255, 255, 63, 0, 255, 255, 255, 127, 255, 63, 31, 0, 255, 15, 255, 255, + 255, 3, 0, 0, 255, 255, 127, 0, 128, 0, 0, 0, 224, 255, 255, 255, + 224, 15, 0, 0, 248, 255, 255, 255, 1, 192, 0, 252, 63, 0, 0, 0, + 15, 0, 0, 0, 0, 224, 0, 252, 255, 255, 255, 63, 0, 222, 99, 0, + 63, 63, 255, 170, 255, 255, 223, 95, 220, 31, 207, 15, 255, 31, 220, 31, + 0, 0, 2, 128, 0, 0, 255, 31, 132, 252, 47, 63, 80, 253, 255, 243, + 224, 67, 0, 0, 255, 1, 0, 0, 255, 127, 255, 255, 31, 120, 12, 0, + 255, 128, 0, 0, 127, 127, 127, 127, 224, 0, 0, 0, 254, 3, 62, 31, + 255, 255, 127, 224, 255, 63, 254, 255, 255, 127, 0, 0, 255, 31, 255, 255, + 0, 12, 0, 0, 255, 127, 0, 128, 0, 0, 128, 255, 252, 255, 255, 255, + 255, 249, 255, 255, 255, 63, 255, 0, 187, 247, 255, 255, 7, 0, 0, 0, + 0, 0, 252, 40, 63, 0, 255, 255, 255, 255, 255, 31, 255, 255, 7, 0, + 0, 128, 0, 0, 223, 255, 0, 124, 247, 15, 0, 0, 255, 255, 127, 196, + 255, 255, 98, 62, 5, 0, 0, 56, 255, 7, 28, 0, 126, 126, 126, 0, + 127, 127, 255, 255, 15, 0, 255, 255, 127, 248, 255, 255, 255, 255, 255, 15, + 255, 63, 255, 255, 255, 255, 255, 3, 127, 0, 248, 160, 255, 253, 127, 95, + 219, 255, 255, 255, 0, 0, 248, 255, 255, 255, 252, 255, 0, 0, 255, 3, + 0, 0, 138, 170, 192, 255, 255, 255, 252, 252, 252, 28, 255, 239, 255, 255, + 127, 255, 255, 183, 255, 63, 255, 63, 255, 255, 1, 0, 255, 7, 255, 255, + 15, 255, 62, 0, 255, 0, 255, 255, 63, 253, 255, 255, 255, 255, 191, 145, + 255, 255, 55, 0, 255, 255, 255, 192, 1, 0, 239, 254, 31, 0, 0, 0, + 255, 255, 71, 0, 30, 0, 0, 20, 255, 255, 251, 255, 255, 15, 0, 0, + 127, 189, 255, 191, 255, 1, 255, 255, 0, 0, 1, 224, 176, 0, 0, 0, + 0, 0, 0, 15, 16, 0, 0, 0, 0, 0, 0, 128, 255, 63, 0, 0, + 248, 255, 255, 224, 31, 0, 1, 0, 255, 7, 255, 31, 255, 1, 255, 3, + 255, 255, 223, 255, 255, 255, 255, 223, 100, 222, 255, 235, 239, 255, 255, 255, + 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, 63, 255, 255, 255, + 253, 255, 255, 247, 255, 253, 255, 255, 150, 254, 247, 10, 132, 234, 150, 170, + 150, 247, 247, 94, 255, 251, 255, 15, 238, 251, 255, 15, +}; + +/* XID_Start: 2005 bytes. */ + +RE_UINT32 re_get_xid_start(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_xid_start_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_xid_start_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_xid_start_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_xid_start_stage_4[pos + f] << 5; + pos += code; + value = (re_xid_start_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* XID_Continue. */ + +static RE_UINT8 re_xid_continue_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, + 6, 6, +}; + +static RE_UINT8 re_xid_continue_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 26, 13, 27, 13, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 28, 7, 29, 30, 7, 31, 13, 13, 13, 13, 13, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 33, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_xid_continue_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 17, 18, 19, 1, 20, 21, 22, 23, 24, 25, 26, 27, 1, 28, + 29, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 31, 31, + 34, 35, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 37, + 1, 1, 1, 1, 38, 1, 39, 40, 41, 42, 43, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 45, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 1, 58, + 59, 60, 61, 62, 63, 31, 31, 31, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 31, 73, 31, 31, 31, 31, 31, 1, 1, 1, 74, 75, 76, 31, 31, + 1, 1, 1, 1, 77, 31, 31, 31, 31, 31, 31, 31, 1, 1, 78, 31, + 1, 1, 79, 80, 31, 31, 31, 81, 82, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 83, 31, 31, 31, 31, 84, 85, 31, 86, 87, 88, 89, + 31, 31, 90, 31, 31, 31, 31, 31, 91, 31, 31, 31, 31, 31, 92, 31, + 1, 1, 1, 1, 1, 1, 93, 1, 1, 1, 1, 1, 1, 1, 1, 94, + 95, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 96, 31, + 1, 1, 97, 31, 31, 31, 31, 31, 31, 98, 31, 31, 31, 31, 31, 31, +}; + +static RE_UINT8 re_xid_continue_stage_4[] = { + 0, 1, 2, 3, 0, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 7, 8, 6, 6, 6, 9, 10, 11, 6, 12, + 6, 6, 6, 6, 13, 6, 6, 6, 6, 14, 15, 16, 17, 18, 19, 20, + 21, 6, 6, 22, 6, 6, 23, 24, 25, 6, 26, 6, 6, 27, 6, 28, + 6, 29, 30, 0, 0, 31, 0, 32, 6, 6, 6, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 43, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 57, 61, 62, 63, 64, 65, 66, 67, + 16, 68, 69, 0, 70, 71, 72, 0, 73, 74, 75, 76, 77, 78, 79, 0, + 6, 6, 80, 6, 81, 6, 82, 83, 6, 6, 84, 6, 85, 86, 87, 6, + 88, 6, 61, 89, 90, 6, 6, 91, 16, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 92, 3, 6, 6, 93, 94, 31, 95, 96, 6, 6, 97, 98, + 99, 6, 6, 100, 6, 101, 6, 102, 103, 104, 105, 106, 6, 107, 108, 0, + 30, 6, 103, 109, 110, 111, 0, 0, 6, 6, 112, 113, 6, 6, 6, 95, + 6, 100, 114, 81, 0, 0, 115, 116, 6, 6, 6, 6, 6, 6, 6, 117, + 91, 6, 118, 81, 6, 119, 120, 121, 0, 122, 123, 124, 125, 0, 125, 126, + 127, 128, 129, 6, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 131, 103, 6, 6, 6, 6, 132, 6, 82, 6, 133, 134, 135, 135, 6, + 136, 137, 16, 6, 138, 16, 6, 83, 139, 140, 6, 6, 141, 68, 0, 25, + 6, 6, 6, 6, 6, 102, 0, 0, 6, 6, 6, 6, 6, 6, 102, 0, + 6, 6, 6, 6, 142, 0, 25, 81, 143, 144, 6, 145, 6, 6, 6, 27, + 146, 147, 6, 6, 148, 149, 0, 146, 6, 150, 6, 95, 6, 6, 151, 152, + 6, 153, 95, 78, 6, 6, 154, 103, 6, 134, 155, 156, 6, 6, 157, 158, + 159, 160, 83, 161, 6, 6, 6, 162, 6, 6, 6, 6, 6, 163, 164, 30, + 6, 6, 6, 153, 6, 6, 165, 0, 166, 167, 168, 6, 6, 27, 169, 6, + 6, 6, 81, 170, 6, 6, 6, 6, 6, 81, 25, 6, 171, 6, 150, 1, + 90, 172, 173, 174, 6, 6, 6, 78, 1, 2, 3, 105, 6, 103, 175, 0, + 176, 177, 178, 0, 6, 6, 6, 68, 0, 0, 6, 31, 0, 0, 0, 179, + 0, 0, 0, 0, 78, 6, 180, 181, 6, 25, 101, 68, 81, 6, 182, 0, + 6, 6, 6, 6, 81, 98, 0, 0, 6, 183, 6, 184, 0, 0, 0, 0, + 6, 134, 102, 150, 0, 0, 0, 0, 185, 186, 102, 134, 103, 0, 0, 187, + 102, 165, 0, 0, 6, 188, 0, 0, 189, 190, 0, 78, 78, 0, 75, 191, + 6, 102, 102, 192, 27, 0, 0, 0, 6, 6, 130, 0, 6, 192, 6, 192, + 6, 6, 191, 193, 6, 68, 25, 194, 6, 195, 25, 196, 6, 6, 197, 0, + 198, 100, 0, 0, 199, 200, 6, 201, 34, 43, 202, 203, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 204, 0, 0, 0, 0, 0, 6, 205, 206, 0, + 6, 6, 207, 0, 6, 100, 98, 0, 208, 112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 209, 0, 0, 0, 0, 0, 0, 6, 210, + 6, 6, 6, 6, 165, 0, 0, 0, 6, 6, 6, 141, 6, 6, 6, 6, + 6, 6, 184, 0, 0, 0, 0, 0, 6, 141, 0, 0, 0, 0, 0, 0, + 6, 6, 191, 0, 0, 0, 0, 0, 6, 210, 103, 98, 0, 0, 25, 106, + 6, 134, 211, 212, 90, 0, 0, 0, 6, 6, 213, 103, 214, 0, 0, 0, + 215, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 216, 217, 0, 0, 0, + 0, 0, 0, 218, 219, 220, 0, 0, 0, 0, 221, 0, 0, 0, 0, 0, + 6, 6, 195, 6, 222, 223, 224, 6, 225, 226, 227, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 228, 229, 83, 195, 195, 131, 131, 230, 230, 231, 6, + 6, 232, 6, 233, 234, 235, 0, 0, 6, 6, 6, 6, 6, 6, 236, 0, + 224, 237, 238, 239, 240, 241, 0, 0, 6, 6, 6, 6, 6, 6, 134, 0, + 6, 31, 6, 6, 6, 6, 6, 6, 81, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 215, 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, + 6, 6, 6, 6, 6, 6, 6, 90, +}; + +static RE_UINT8 re_xid_continue_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 254, 255, 255, 135, 254, 255, 255, 7, + 0, 4, 160, 4, 255, 255, 127, 255, 255, 255, 255, 255, 195, 255, 3, 0, + 31, 80, 0, 0, 255, 255, 223, 184, 192, 215, 255, 255, 251, 255, 255, 255, + 255, 255, 191, 255, 251, 252, 255, 255, 255, 255, 254, 255, 255, 255, 127, 2, + 254, 255, 255, 255, 255, 0, 254, 255, 255, 255, 255, 191, 182, 0, 255, 255, + 255, 7, 7, 0, 0, 0, 255, 7, 255, 195, 255, 255, 255, 255, 239, 159, + 255, 253, 255, 159, 0, 0, 255, 255, 255, 231, 255, 255, 255, 255, 3, 0, + 255, 255, 63, 4, 255, 63, 0, 0, 255, 255, 255, 15, 255, 255, 31, 0, + 248, 255, 255, 255, 207, 255, 254, 255, 239, 159, 249, 255, 255, 253, 197, 243, + 159, 121, 128, 176, 207, 255, 3, 0, 238, 135, 249, 255, 255, 253, 109, 211, + 135, 57, 2, 94, 192, 255, 63, 0, 238, 191, 251, 255, 255, 253, 237, 243, + 191, 59, 1, 0, 207, 255, 0, 2, 238, 159, 249, 255, 159, 57, 192, 176, + 207, 255, 2, 0, 236, 199, 61, 214, 24, 199, 255, 195, 199, 61, 129, 0, + 192, 255, 0, 0, 239, 223, 253, 255, 255, 253, 255, 227, 223, 61, 96, 7, + 207, 255, 0, 0, 238, 223, 253, 255, 255, 253, 239, 243, 223, 61, 96, 64, + 207, 255, 6, 0, 255, 255, 255, 231, 223, 125, 128, 128, 207, 255, 0, 252, + 236, 255, 127, 252, 255, 255, 251, 47, 127, 132, 95, 255, 192, 255, 12, 0, + 255, 255, 255, 7, 255, 127, 255, 3, 150, 37, 240, 254, 174, 236, 255, 59, + 95, 63, 255, 243, 1, 0, 0, 3, 255, 3, 160, 194, 255, 254, 255, 255, + 255, 31, 254, 255, 223, 255, 255, 254, 255, 255, 255, 31, 64, 0, 0, 0, + 255, 3, 255, 255, 255, 255, 255, 63, 191, 32, 255, 255, 255, 255, 255, 247, + 255, 61, 127, 61, 255, 61, 255, 255, 255, 255, 61, 127, 61, 255, 127, 255, + 255, 255, 61, 255, 0, 254, 3, 0, 255, 255, 0, 0, 255, 255, 63, 63, + 255, 159, 255, 255, 255, 199, 255, 1, 255, 223, 31, 0, 255, 255, 15, 0, + 255, 223, 13, 0, 255, 255, 143, 48, 255, 3, 0, 0, 0, 56, 255, 3, + 255, 255, 255, 0, 255, 7, 255, 255, 255, 255, 63, 0, 255, 255, 255, 127, + 255, 15, 255, 15, 192, 255, 255, 255, 255, 63, 31, 0, 255, 15, 255, 255, + 255, 3, 255, 7, 255, 255, 255, 159, 255, 3, 255, 3, 128, 0, 255, 63, + 255, 15, 255, 3, 0, 248, 15, 0, 255, 227, 255, 255, 0, 0, 247, 255, + 255, 255, 127, 3, 255, 255, 63, 240, 63, 63, 255, 170, 255, 255, 223, 95, + 220, 31, 207, 15, 255, 31, 220, 31, 0, 0, 0, 128, 1, 0, 16, 0, + 0, 0, 2, 128, 0, 0, 255, 31, 226, 255, 1, 0, 132, 252, 47, 63, + 80, 253, 255, 243, 224, 67, 0, 0, 255, 1, 0, 0, 255, 127, 255, 255, + 31, 248, 15, 0, 255, 128, 0, 128, 255, 255, 127, 0, 127, 127, 127, 127, + 224, 0, 0, 0, 254, 255, 62, 31, 255, 255, 127, 230, 224, 255, 255, 255, + 255, 63, 254, 255, 255, 127, 0, 0, 255, 31, 0, 0, 255, 31, 255, 255, + 255, 15, 0, 0, 255, 255, 240, 191, 0, 0, 128, 255, 252, 255, 255, 255, + 255, 249, 255, 255, 255, 63, 255, 0, 255, 0, 0, 0, 31, 0, 255, 3, + 255, 255, 255, 40, 255, 63, 255, 255, 1, 128, 255, 3, 255, 63, 255, 3, + 255, 255, 127, 252, 7, 0, 0, 56, 255, 255, 124, 0, 126, 126, 126, 0, + 127, 127, 255, 255, 63, 0, 255, 255, 255, 55, 255, 3, 15, 0, 255, 255, + 127, 248, 255, 255, 255, 255, 255, 3, 127, 0, 248, 224, 255, 253, 127, 95, + 219, 255, 255, 255, 0, 0, 248, 255, 240, 255, 255, 255, 255, 255, 252, 255, + 255, 255, 24, 0, 0, 224, 0, 0, 0, 0, 138, 170, 252, 252, 252, 28, + 255, 239, 255, 255, 127, 255, 255, 183, 255, 63, 255, 63, 0, 0, 0, 32, + 255, 255, 1, 0, 1, 0, 0, 0, 15, 255, 62, 0, 255, 0, 255, 255, + 15, 0, 0, 0, 63, 253, 255, 255, 255, 255, 191, 145, 255, 255, 55, 0, + 255, 255, 255, 192, 111, 240, 239, 254, 255, 255, 15, 135, 127, 0, 0, 0, + 255, 255, 7, 0, 192, 255, 0, 128, 255, 1, 255, 3, 255, 255, 223, 255, + 255, 255, 79, 0, 31, 28, 255, 23, 255, 255, 251, 255, 127, 189, 255, 191, + 255, 1, 255, 255, 255, 7, 255, 3, 159, 57, 129, 224, 207, 31, 31, 0, + 191, 0, 255, 3, 255, 255, 63, 255, 1, 0, 0, 63, 17, 0, 255, 3, + 255, 255, 255, 227, 255, 3, 0, 128, 255, 255, 255, 1, 15, 0, 255, 3, + 248, 255, 255, 224, 31, 0, 255, 255, 0, 128, 255, 255, 3, 0, 0, 0, + 255, 7, 255, 31, 255, 1, 255, 99, 224, 227, 7, 248, 231, 15, 0, 0, + 0, 60, 0, 0, 28, 0, 0, 0, 255, 255, 255, 223, 100, 222, 255, 235, + 239, 255, 255, 255, 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, + 63, 255, 255, 255, 253, 255, 255, 247, 255, 253, 255, 255, 247, 207, 255, 255, + 255, 255, 127, 248, 255, 31, 32, 0, 16, 0, 0, 248, 254, 255, 0, 0, + 31, 0, 127, 0, 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, + 255, 251, 255, 15, 238, 251, 255, 15, +}; + +/* XID_Continue: 2194 bytes. */ + +RE_UINT32 re_get_xid_continue(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_xid_continue_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_xid_continue_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_xid_continue_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_xid_continue_stage_4[pos + f] << 5; + pos += code; + value = (re_xid_continue_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Default_Ignorable_Code_Point. */ + +static RE_UINT8 re_default_ignorable_code_point_stage_1[] = { + 0, 1, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, + 2, 2, +}; + +static RE_UINT8 re_default_ignorable_code_point_stage_2[] = { + 0, 1, 2, 3, 4, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 7, 1, 1, 8, 1, 1, 1, 1, 1, + 9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_default_ignorable_code_point_stage_3[] = { + 0, 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 1, 1, 1, 1, 1, 5, 6, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 9, 10, 1, 1, 1, 1, 11, 1, 1, 1, + 1, 12, 1, 1, 1, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_default_ignorable_code_point_stage_4[] = { + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, + 7, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10, 0, 0, 0, 0, + 0, 0, 0, 11, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 5, 0, 12, 0, 0, 0, 0, 0, 13, 0, 0, + 0, 0, 0, 14, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, +}; + +static RE_UINT8 re_default_ignorable_code_point_stage_5[] = { + 0, 0, 0, 0, 0, 32, 0, 0, 0, 128, 0, 0, 0, 0, 0, 16, + 0, 0, 0, 128, 1, 0, 0, 0, 0, 0, 48, 0, 0, 120, 0, 0, + 0, 248, 0, 0, 0, 124, 0, 0, 255, 255, 0, 0, 16, 0, 0, 0, + 0, 0, 255, 1, 15, 0, 0, 0, 0, 0, 248, 7, 255, 255, 255, 255, +}; + +/* Default_Ignorable_Code_Point: 370 bytes. */ + +RE_UINT32 re_get_default_ignorable_code_point(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_default_ignorable_code_point_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_default_ignorable_code_point_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_default_ignorable_code_point_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_default_ignorable_code_point_stage_4[pos + f] << 5; + pos += code; + value = (re_default_ignorable_code_point_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Grapheme_Extend. */ + +static RE_UINT8 re_grapheme_extend_stage_1[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, + 4, 4, +}; + +static RE_UINT8 re_grapheme_extend_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, + 11, 12, 13, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 14, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 15, 7, 7, 16, 17, 7, 18, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 19, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static RE_UINT8 re_grapheme_extend_stage_3[] = { + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 0, 0, 15, 0, 0, 0, 16, 17, 18, 19, 20, 21, 22, 0, 0, + 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 25, 0, 0, + 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 33, 34, 0, 35, 36, 37, 0, 0, 0, 0, + 0, 0, 38, 0, 0, 0, 0, 0, 39, 40, 41, 42, 43, 44, 45, 46, + 0, 0, 47, 48, 0, 0, 0, 49, 0, 0, 0, 0, 50, 0, 0, 0, + 0, 51, 52, 0, 0, 0, 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_grapheme_extend_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, + 7, 0, 8, 9, 0, 0, 10, 11, 12, 13, 14, 0, 0, 15, 0, 16, + 17, 18, 19, 0, 0, 0, 0, 20, 21, 22, 23, 24, 25, 26, 27, 24, + 28, 29, 30, 31, 28, 29, 32, 24, 25, 33, 34, 24, 35, 36, 37, 0, + 38, 39, 40, 24, 25, 41, 42, 24, 25, 36, 27, 24, 0, 0, 43, 0, + 0, 44, 45, 0, 0, 46, 47, 0, 48, 49, 0, 50, 51, 52, 53, 0, + 0, 54, 55, 56, 57, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, 0, + 59, 59, 60, 60, 0, 61, 62, 0, 63, 0, 0, 0, 0, 64, 0, 0, + 0, 65, 0, 0, 0, 0, 0, 0, 66, 0, 67, 68, 0, 69, 0, 0, + 70, 71, 35, 16, 72, 73, 0, 74, 0, 75, 0, 0, 0, 0, 76, 77, + 0, 0, 0, 0, 0, 0, 1, 78, 79, 0, 0, 0, 0, 0, 13, 80, + 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 82, 0, 0, 0, 1, + 0, 83, 0, 0, 84, 0, 0, 0, 0, 0, 0, 85, 39, 0, 0, 86, + 87, 88, 0, 0, 0, 0, 89, 90, 0, 91, 92, 0, 21, 93, 0, 94, + 0, 95, 96, 29, 0, 97, 25, 98, 0, 0, 0, 0, 0, 0, 0, 99, + 36, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, + 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 101, 0, 0, 0, 0, + 102, 103, 0, 0, 0, 0, 0, 88, 25, 104, 105, 82, 72, 106, 0, 0, + 21, 107, 0, 108, 72, 109, 110, 0, 0, 111, 0, 0, 0, 0, 82, 112, + 72, 26, 113, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 116, 0, + 0, 0, 0, 0, 0, 117, 118, 0, 0, 119, 38, 0, 0, 120, 0, 0, + 58, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, + 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 0, 0, + 0, 0, 0, 0, 125, 0, 0, 0, 0, 0, 0, 126, 127, 128, 0, 0, + 0, 0, 129, 0, 0, 0, 0, 0, 1, 130, 1, 131, 132, 133, 0, 0, + 0, 0, 0, 0, 0, 0, 123, 0, 1, 1, 1, 1, 1, 1, 1, 2, +}; + +static RE_UINT8 re_grapheme_extend_stage_5[] = { + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 248, 3, 0, 0, + 0, 0, 254, 255, 255, 255, 255, 191, 182, 0, 0, 0, 0, 0, 255, 7, + 0, 248, 255, 255, 0, 0, 1, 0, 0, 0, 192, 159, 159, 61, 0, 0, + 0, 0, 2, 0, 0, 0, 255, 255, 255, 7, 0, 0, 192, 255, 1, 0, + 0, 248, 15, 0, 0, 0, 192, 251, 239, 62, 0, 0, 0, 0, 0, 14, + 248, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, 20, 254, 33, 254, 0, + 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 80, 30, 32, 128, 0, + 6, 0, 0, 0, 0, 0, 0, 16, 134, 57, 2, 0, 0, 0, 35, 0, + 190, 33, 0, 0, 0, 0, 0, 208, 30, 32, 192, 0, 4, 0, 0, 0, + 0, 0, 0, 64, 1, 32, 128, 0, 1, 0, 0, 0, 0, 0, 0, 192, + 193, 61, 96, 0, 0, 0, 0, 144, 68, 48, 96, 0, 0, 132, 92, 128, + 0, 0, 242, 7, 128, 127, 0, 0, 0, 0, 242, 27, 0, 63, 0, 0, + 0, 0, 0, 3, 0, 0, 160, 2, 0, 0, 254, 127, 223, 224, 255, 254, + 255, 255, 255, 31, 64, 0, 0, 0, 0, 224, 253, 102, 0, 0, 0, 195, + 1, 0, 30, 0, 100, 32, 0, 32, 0, 0, 0, 224, 0, 0, 28, 0, + 0, 0, 12, 0, 0, 0, 176, 63, 64, 254, 15, 32, 0, 56, 0, 0, + 0, 2, 0, 0, 135, 1, 4, 14, 0, 0, 128, 9, 0, 0, 64, 127, + 229, 31, 248, 159, 0, 0, 255, 127, 15, 0, 0, 0, 0, 0, 208, 23, + 3, 0, 0, 0, 60, 59, 0, 0, 64, 163, 3, 0, 0, 240, 207, 0, + 0, 0, 247, 255, 253, 33, 16, 3, 255, 255, 63, 240, 0, 48, 0, 0, + 255, 255, 1, 0, 0, 128, 3, 0, 0, 0, 0, 128, 0, 252, 0, 0, + 0, 0, 0, 6, 0, 128, 247, 63, 0, 0, 3, 0, 68, 8, 0, 0, + 96, 0, 0, 0, 16, 0, 0, 0, 255, 255, 3, 0, 192, 63, 0, 0, + 128, 255, 3, 0, 0, 0, 200, 19, 32, 0, 0, 0, 0, 126, 102, 0, + 8, 16, 0, 0, 0, 0, 157, 193, 0, 48, 64, 0, 32, 33, 0, 0, + 0, 0, 0, 32, 0, 0, 192, 7, 110, 240, 0, 0, 0, 0, 0, 135, + 0, 0, 0, 255, 127, 0, 0, 0, 0, 0, 120, 6, 128, 239, 31, 0, + 0, 0, 8, 0, 0, 0, 192, 127, 0, 28, 0, 0, 0, 128, 211, 0, + 248, 7, 0, 0, 1, 0, 128, 0, 192, 31, 31, 0, 0, 0, 249, 165, + 13, 0, 0, 0, 0, 128, 60, 176, 1, 0, 0, 48, 0, 0, 248, 167, + 0, 40, 191, 0, 188, 15, 0, 0, 0, 0, 31, 0, 0, 0, 127, 0, + 0, 128, 7, 0, 0, 0, 0, 96, 160, 195, 7, 248, 231, 15, 0, 0, + 0, 60, 0, 0, 28, 0, 0, 0, 255, 255, 127, 248, 255, 31, 32, 0, + 16, 0, 0, 248, 254, 255, 0, 0, +}; + +/* Grapheme_Extend: 1274 bytes. */ + +RE_UINT32 re_get_grapheme_extend(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_grapheme_extend_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_grapheme_extend_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_grapheme_extend_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_grapheme_extend_stage_4[pos + f] << 5; + pos += code; + value = (re_grapheme_extend_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Grapheme_Base. */ + +static RE_UINT8 re_grapheme_base_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, +}; + +static RE_UINT8 re_grapheme_base_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 13, + 13, 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 15, 13, 16, 17, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 19, 29, 30, 19, 19, 13, 31, 19, 19, + 19, 32, 19, 19, 19, 19, 19, 19, 19, 19, 33, 34, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 35, 19, 19, 36, + 19, 19, 19, 19, 37, 38, 39, 19, 19, 19, 40, 41, 42, 43, 44, 19, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 45, 13, 13, 13, 46, 47, 13, + 13, 13, 13, 48, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 49, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, +}; + +static RE_UINT8 re_grapheme_base_stage_3[] = { + 0, 1, 2, 2, 2, 2, 3, 4, 2, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 2, 2, 30, 31, 32, 33, 2, 2, 2, 2, 2, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 2, 47, 2, 2, 48, 49, + 50, 51, 2, 52, 2, 2, 2, 53, 54, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 55, 56, 57, 58, 59, 60, 61, 62, 2, 63, + 64, 65, 66, 67, 68, 69, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 70, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 71, + 2, 72, 2, 2, 73, 74, 2, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 2, 2, 2, 2, 2, 2, 2, 84, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 2, 2, 86, 87, 88, 89, 2, 2, 90, 91, 92, 93, 94, 95, + 96, 53, 97, 98, 85, 99, 100, 101, 2, 102, 103, 85, 2, 2, 104, 85, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 85, 85, 115, 85, 85, 85, + 116, 117, 118, 119, 120, 121, 122, 85, 85, 123, 85, 124, 125, 126, 127, 85, + 85, 128, 85, 85, 85, 129, 85, 85, 2, 2, 2, 2, 2, 2, 2, 130, + 131, 2, 132, 85, 85, 85, 85, 85, 133, 85, 85, 85, 85, 85, 85, 85, + 2, 2, 2, 2, 134, 85, 85, 85, 2, 2, 2, 2, 135, 136, 137, 138, + 85, 85, 85, 85, 85, 85, 139, 140, 141, 85, 85, 85, 85, 85, 85, 85, + 142, 143, 85, 85, 85, 85, 85, 85, 2, 144, 145, 146, 147, 85, 148, 85, + 149, 150, 151, 2, 2, 152, 2, 153, 2, 2, 2, 2, 154, 155, 85, 85, + 2, 156, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 157, 158, 85, 85, + 159, 160, 161, 162, 163, 85, 2, 2, 2, 2, 164, 165, 2, 166, 167, 168, + 169, 170, 171, 172, 85, 85, 85, 85, 2, 2, 2, 2, 2, 173, 2, 2, + 2, 2, 2, 2, 2, 2, 174, 2, 175, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 176, 85, 85, 2, 2, 2, 2, 177, 85, 85, 85, +}; + +static RE_UINT8 re_grapheme_base_stage_4[] = { + 0, 0, 1, 1, 1, 1, 1, 2, 0, 0, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, + 5, 1, 6, 1, 1, 1, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 8, 1, 9, 8, 1, 10, 0, 0, 11, 12, 1, 13, 14, + 15, 16, 1, 1, 13, 0, 1, 8, 1, 1, 1, 1, 1, 17, 18, 1, + 19, 20, 1, 0, 21, 1, 1, 1, 1, 1, 22, 23, 1, 1, 13, 24, + 1, 25, 26, 2, 1, 27, 0, 0, 0, 0, 1, 14, 0, 0, 0, 0, + 28, 1, 1, 29, 30, 31, 32, 1, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 34, 35, 42, 43, 44, 15, 45, 46, 6, 35, 47, 48, 43, 39, 49, + 50, 34, 35, 51, 52, 38, 39, 53, 54, 55, 56, 57, 58, 43, 15, 13, + 59, 20, 35, 60, 61, 62, 39, 63, 64, 20, 35, 65, 66, 11, 39, 67, + 64, 20, 1, 68, 69, 70, 39, 71, 72, 73, 1, 74, 75, 76, 15, 45, + 8, 1, 1, 77, 78, 40, 0, 0, 79, 80, 81, 82, 83, 84, 0, 0, + 1, 4, 1, 85, 86, 1, 87, 70, 88, 0, 0, 89, 90, 13, 0, 0, + 1, 1, 87, 91, 1, 92, 8, 93, 94, 3, 1, 1, 95, 1, 1, 1, + 1, 1, 1, 1, 96, 97, 1, 1, 96, 1, 1, 98, 99, 100, 1, 1, + 1, 99, 1, 1, 1, 13, 1, 87, 1, 101, 1, 1, 1, 1, 1, 102, + 1, 87, 1, 1, 1, 1, 1, 103, 3, 104, 1, 105, 1, 104, 3, 43, + 1, 1, 1, 106, 107, 108, 101, 101, 13, 101, 1, 1, 1, 1, 1, 53, + 1, 1, 109, 1, 1, 1, 1, 22, 1, 2, 110, 111, 112, 1, 19, 14, + 1, 1, 40, 1, 101, 113, 1, 1, 1, 114, 1, 1, 1, 115, 116, 117, + 101, 101, 19, 0, 0, 0, 0, 0, 118, 1, 1, 119, 120, 1, 13, 108, + 121, 1, 122, 1, 1, 1, 123, 124, 1, 1, 40, 125, 126, 1, 1, 1, + 0, 0, 0, 0, 53, 127, 128, 129, 1, 1, 1, 1, 0, 0, 0, 0, + 1, 102, 1, 1, 102, 130, 1, 19, 1, 1, 1, 131, 131, 132, 1, 133, + 13, 1, 134, 1, 1, 1, 0, 32, 2, 87, 1, 2, 0, 0, 0, 0, + 40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 13, + 1, 1, 75, 0, 13, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 135, + 1, 136, 1, 126, 35, 104, 137, 0, 1, 1, 2, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 138, 1, 1, 95, 1, 1, 1, 134, 43, + 1, 75, 139, 139, 139, 139, 0, 0, 1, 1, 1, 1, 117, 0, 0, 0, + 1, 140, 1, 1, 1, 1, 1, 141, 1, 1, 1, 1, 1, 22, 0, 40, + 1, 1, 101, 1, 8, 1, 1, 1, 1, 142, 1, 1, 1, 1, 1, 1, + 143, 1, 19, 8, 1, 1, 1, 1, 2, 1, 1, 13, 1, 1, 141, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 22, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, 0, 0, + 87, 1, 1, 1, 75, 1, 1, 1, 1, 1, 40, 0, 1, 1, 2, 144, + 1, 19, 1, 1, 1, 1, 1, 145, 1, 1, 19, 53, 0, 0, 0, 146, + 147, 1, 148, 101, 1, 1, 1, 53, 1, 1, 1, 1, 149, 101, 0, 150, + 1, 1, 151, 1, 75, 152, 1, 87, 28, 1, 1, 153, 154, 155, 131, 2, + 1, 1, 156, 157, 158, 84, 1, 159, 1, 1, 1, 160, 161, 162, 163, 22, + 164, 165, 139, 1, 1, 1, 22, 1, 1, 1, 1, 1, 1, 1, 166, 101, + 1, 1, 141, 1, 142, 1, 1, 40, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 19, 1, 1, 1, 1, 1, 1, 101, 0, 0, + 75, 167, 1, 168, 169, 1, 1, 1, 1, 1, 1, 1, 104, 28, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 121, 1, 1, 53, 0, 0, 19, + 0, 101, 0, 1, 1, 170, 171, 131, 1, 1, 1, 1, 1, 1, 1, 87, + 8, 1, 1, 1, 1, 1, 1, 1, 1, 19, 1, 2, 172, 173, 139, 174, + 159, 1, 100, 175, 19, 19, 0, 0, 176, 1, 1, 177, 1, 1, 1, 1, + 87, 40, 43, 0, 0, 1, 1, 87, 1, 87, 1, 1, 1, 43, 8, 40, + 1, 1, 141, 1, 13, 1, 1, 22, 1, 154, 1, 1, 178, 22, 0, 0, + 1, 19, 101, 0, 0, 0, 0, 0, 1, 1, 53, 1, 1, 1, 179, 0, + 1, 1, 1, 75, 1, 22, 53, 0, 180, 1, 1, 181, 1, 182, 1, 1, + 1, 2, 146, 0, 0, 0, 1, 183, 1, 184, 1, 57, 0, 0, 0, 0, + 1, 1, 1, 185, 1, 121, 1, 1, 43, 186, 1, 141, 53, 103, 1, 1, + 1, 1, 0, 0, 1, 1, 187, 75, 1, 1, 1, 71, 1, 136, 1, 188, + 1, 189, 190, 0, 0, 0, 0, 0, 1, 1, 1, 1, 103, 0, 0, 0, + 1, 1, 1, 117, 1, 1, 1, 7, 0, 0, 0, 0, 0, 0, 1, 2, + 20, 1, 1, 53, 191, 121, 1, 0, 121, 1, 1, 192, 104, 1, 103, 101, + 28, 1, 193, 15, 141, 1, 1, 194, 121, 1, 1, 195, 60, 1, 8, 14, + 1, 6, 2, 196, 0, 0, 0, 0, 197, 154, 101, 1, 1, 2, 117, 101, + 50, 34, 35, 198, 199, 200, 141, 0, 1, 1, 1, 201, 202, 101, 0, 0, + 1, 1, 2, 203, 8, 40, 0, 0, 1, 1, 1, 204, 61, 101, 0, 0, + 1, 1, 205, 206, 101, 0, 0, 0, 1, 101, 207, 1, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 208, 0, 0, 0, 0, 1, 1, 1, 103, + 1, 101, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 14, + 1, 1, 1, 1, 141, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 75, 0, 0, 0, 1, 1, 1, 103, 1, 2, 155, 0, + 0, 0, 0, 0, 0, 1, 19, 209, 1, 1, 1, 146, 22, 140, 6, 210, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 14, 1, 1, 2, + 0, 28, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 13, 87, 103, 211, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 22, 1, 1, 9, 1, 1, 1, 212, 0, + 213, 1, 155, 1, 1, 1, 103, 0, 1, 1, 1, 1, 214, 0, 0, 0, + 1, 1, 1, 1, 1, 75, 1, 104, 1, 1, 1, 1, 1, 131, 1, 1, + 1, 3, 215, 29, 216, 1, 1, 1, 217, 218, 1, 219, 220, 20, 1, 1, + 1, 1, 136, 1, 1, 1, 1, 1, 1, 1, 1, 1, 163, 1, 1, 1, + 0, 0, 0, 221, 0, 0, 21, 131, 222, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 223, 0, 0, 0, 216, 1, 224, 225, 226, 227, 228, 229, + 140, 40, 230, 40, 0, 0, 0, 104, 1, 1, 40, 1, 1, 1, 1, 1, + 1, 141, 2, 8, 8, 8, 1, 22, 87, 1, 2, 1, 1, 1, 40, 1, + 1, 13, 0, 0, 0, 0, 15, 1, 117, 1, 1, 13, 103, 104, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 140, 1, 1, 216, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 43, 87, 141, 1, 1, 1, 1, 1, 1, 1, 141, + 1, 1, 1, 1, 1, 14, 0, 0, 40, 1, 1, 1, 53, 101, 1, 1, + 53, 1, 19, 0, 0, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, + 14, 0, 0, 0, 43, 0, 0, 0, 1, 1, 1, 1, 1, 75, 0, 0, + 1, 1, 1, 14, 1, 1, 1, 1, 1, 19, 1, 1, 1, 1, 1, 1, + 1, 1, 104, 0, 0, 0, 0, 0, 1, 19, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_grapheme_base_stage_5[] = { + 0, 0, 255, 255, 255, 127, 255, 223, 255, 252, 240, 215, 251, 255, 7, 252, + 254, 255, 127, 254, 255, 230, 0, 64, 73, 0, 255, 7, 31, 0, 192, 255, + 0, 200, 63, 64, 96, 194, 255, 63, 253, 255, 0, 224, 63, 0, 2, 0, + 240, 7, 63, 4, 16, 1, 255, 65, 248, 255, 255, 235, 1, 222, 1, 255, + 243, 255, 237, 159, 249, 255, 255, 253, 197, 163, 129, 89, 0, 176, 195, 255, + 255, 15, 232, 135, 109, 195, 1, 0, 0, 94, 28, 0, 232, 191, 237, 227, + 1, 26, 3, 2, 236, 159, 237, 35, 129, 25, 255, 0, 232, 199, 61, 214, + 24, 199, 255, 131, 198, 29, 238, 223, 255, 35, 30, 0, 0, 7, 0, 255, + 236, 223, 239, 99, 155, 13, 6, 0, 255, 167, 193, 93, 0, 128, 63, 254, + 236, 255, 127, 252, 251, 47, 127, 0, 3, 127, 13, 128, 127, 128, 150, 37, + 240, 254, 174, 236, 13, 32, 95, 0, 255, 243, 95, 253, 255, 254, 255, 31, + 32, 31, 0, 192, 191, 223, 2, 153, 255, 60, 225, 255, 155, 223, 191, 32, + 255, 61, 127, 61, 61, 127, 61, 255, 127, 255, 255, 3, 63, 63, 255, 1, + 3, 0, 99, 0, 79, 192, 191, 1, 240, 31, 255, 5, 120, 14, 251, 1, + 241, 255, 255, 199, 127, 198, 191, 0, 26, 224, 7, 0, 240, 255, 47, 232, + 251, 15, 252, 255, 195, 196, 191, 92, 12, 240, 48, 248, 255, 227, 8, 0, + 2, 222, 111, 0, 255, 170, 223, 255, 207, 239, 220, 127, 255, 128, 207, 255, + 63, 255, 0, 240, 12, 254, 127, 127, 255, 251, 15, 0, 127, 248, 224, 255, + 8, 192, 252, 0, 128, 255, 187, 247, 159, 15, 15, 192, 252, 63, 63, 192, + 12, 128, 55, 236, 255, 191, 255, 195, 255, 129, 25, 0, 247, 47, 255, 239, + 98, 62, 5, 0, 0, 248, 255, 207, 126, 126, 126, 0, 223, 30, 248, 160, + 127, 95, 219, 255, 247, 255, 127, 15, 252, 252, 252, 28, 0, 48, 255, 183, + 135, 255, 143, 255, 15, 255, 15, 128, 63, 253, 191, 145, 191, 255, 55, 248, + 255, 143, 255, 240, 239, 254, 31, 248, 7, 255, 3, 30, 0, 254, 128, 63, + 135, 217, 127, 16, 119, 0, 63, 128, 44, 63, 127, 189, 237, 163, 158, 57, + 1, 224, 6, 90, 242, 0, 3, 79, 7, 88, 255, 215, 64, 0, 67, 0, + 7, 128, 32, 0, 255, 224, 255, 147, 95, 60, 24, 240, 35, 0, 100, 222, + 239, 255, 191, 231, 223, 223, 255, 123, 95, 252, 128, 7, 239, 15, 159, 255, + 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, 238, 251, +}; + +/* Grapheme_Base: 2544 bytes. */ + +RE_UINT32 re_get_grapheme_base(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_grapheme_base_stage_1[f] << 5; + f = code >> 10; + code ^= f << 10; + pos = (RE_UINT32)re_grapheme_base_stage_2[pos + f] << 3; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_grapheme_base_stage_3[pos + f] << 3; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_grapheme_base_stage_4[pos + f] << 4; + pos += code; + value = (re_grapheme_base_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Grapheme_Link. */ + +static RE_UINT8 re_grapheme_link_stage_1[] = { + 0, 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, +}; + +static RE_UINT8 re_grapheme_link_stage_2[] = { + 0, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_grapheme_link_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 3, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 8, 0, 9, 10, + 0, 0, 11, 0, 0, 0, 0, 0, 12, 9, 13, 14, 0, 15, 0, 16, + 0, 0, 0, 0, 17, 0, 0, 0, 18, 19, 20, 14, 21, 22, 1, 0, + 0, 23, 0, 17, 17, 24, 25, 0, +}; + +static RE_UINT8 re_grapheme_link_stage_4[] = { + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 3, 0, 0, + 4, 0, 0, 0, 0, 5, 0, 0, 6, 6, 0, 0, 0, 0, 7, 0, + 0, 0, 0, 8, 0, 0, 4, 0, 0, 9, 0, 10, 0, 0, 0, 11, + 12, 0, 0, 0, 0, 0, 13, 0, 0, 0, 8, 0, 0, 0, 0, 14, + 0, 0, 0, 1, 0, 11, 0, 0, 0, 0, 12, 11, 0, 15, 0, 0, + 0, 16, 0, 0, 0, 17, 0, 0, 0, 0, 0, 2, 0, 0, 18, 0, + 0, 14, 0, 0, 0, 19, 0, 0, +}; + +static RE_UINT8 re_grapheme_link_stage_5[] = { + 0, 0, 0, 0, 0, 32, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, + 16, 0, 0, 0, 0, 0, 0, 6, 0, 0, 16, 0, 0, 0, 4, 0, + 1, 0, 0, 0, 0, 12, 0, 0, 0, 0, 12, 0, 0, 0, 0, 128, + 64, 0, 0, 0, 0, 0, 8, 0, 0, 0, 64, 0, 0, 0, 0, 2, + 0, 0, 24, 0, 0, 0, 32, 0, 4, 0, 0, 0, 0, 8, 0, 0, +}; + +/* Grapheme_Link: 404 bytes. */ + +RE_UINT32 re_get_grapheme_link(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 14; + code = ch ^ (f << 14); + pos = (RE_UINT32)re_grapheme_link_stage_1[f] << 4; + f = code >> 10; + code ^= f << 10; + pos = (RE_UINT32)re_grapheme_link_stage_2[pos + f] << 3; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_grapheme_link_stage_3[pos + f] << 2; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_grapheme_link_stage_4[pos + f] << 5; + pos += code; + value = (re_grapheme_link_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* White_Space. */ + +static RE_UINT8 re_white_space_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_white_space_stage_2[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_white_space_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_white_space_stage_4[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 1, 1, 1, 1, 4, 5, 1, 1, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_white_space_stage_5[] = { + 0, 62, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 255, 7, 0, 0, 0, 131, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, +}; + +/* White_Space: 169 bytes. */ + +RE_UINT32 re_get_white_space(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_white_space_stage_1[f] << 3; + f = code >> 13; + code ^= f << 13; + pos = (RE_UINT32)re_white_space_stage_2[pos + f] << 4; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_white_space_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_white_space_stage_4[pos + f] << 6; + pos += code; + value = (re_white_space_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Bidi_Control. */ + +static RE_UINT8 re_bidi_control_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_bidi_control_stage_2[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_bidi_control_stage_3[] = { + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_bidi_control_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 2, 3, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_bidi_control_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, + 0, 192, 0, 0, 0, 124, 0, 0, 0, 0, 0, 0, 192, 3, 0, 0, +}; + +/* Bidi_Control: 129 bytes. */ + +RE_UINT32 re_get_bidi_control(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_bidi_control_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_bidi_control_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_bidi_control_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_bidi_control_stage_4[pos + f] << 6; + pos += code; + value = (re_bidi_control_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Join_Control. */ + +static RE_UINT8 re_join_control_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_join_control_stage_2[] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_join_control_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_join_control_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_join_control_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, +}; + +/* Join_Control: 97 bytes. */ + +RE_UINT32 re_get_join_control(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_join_control_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_join_control_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_join_control_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_join_control_stage_4[pos + f] << 6; + pos += code; + value = (re_join_control_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Dash. */ + +static RE_UINT8 re_dash_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_dash_stage_2[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +}; + +static RE_UINT8 re_dash_stage_3[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 3, 1, 4, 1, 1, 1, + 5, 6, 1, 1, 1, 1, 1, 7, 8, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, +}; + +static RE_UINT8 re_dash_stage_4[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, + 4, 1, 1, 1, 1, 1, 1, 1, 5, 6, 7, 1, 1, 1, 1, 1, + 8, 1, 1, 1, 1, 1, 1, 1, 9, 3, 1, 1, 1, 1, 1, 1, + 10, 1, 11, 1, 1, 1, 1, 1, 12, 13, 1, 1, 14, 1, 1, 1, +}; + +static RE_UINT8 re_dash_stage_5[] = { + 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 128, 4, 0, 0, 0, 12, + 0, 0, 0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 1, 8, 0, 0, 0, + 0, 32, 0, 0, 0, 0, 0, 0, +}; + +/* Dash: 297 bytes. */ + +RE_UINT32 re_get_dash(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_dash_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_dash_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_dash_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_dash_stage_4[pos + f] << 6; + pos += code; + value = (re_dash_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Hyphen. */ + +static RE_UINT8 re_hyphen_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_hyphen_stage_2[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +}; + +static RE_UINT8 re_hyphen_stage_3[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 4, 1, 1, 1, 1, 1, 1, 5, 6, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, +}; + +static RE_UINT8 re_hyphen_stage_4[] = { + 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, + 4, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 6, 1, 1, 1, 1, 1, 7, 1, 1, 8, 9, 1, 1, +}; + +static RE_UINT8 re_hyphen_stage_5[] = { + 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, +}; + +/* Hyphen: 241 bytes. */ + +RE_UINT32 re_get_hyphen(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_hyphen_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_hyphen_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_hyphen_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_hyphen_stage_4[pos + f] << 6; + pos += code; + value = (re_hyphen_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Quotation_Mark. */ + +static RE_UINT8 re_quotation_mark_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_quotation_mark_stage_2[] = { + 0, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_quotation_mark_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 3, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 5, +}; + +static RE_UINT8 re_quotation_mark_stage_4[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, + 5, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 7, 8, 1, 1, +}; + +static RE_UINT8 re_quotation_mark_stage_5[] = { + 0, 0, 0, 0, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 0, 8, 0, 0, 0, 255, 0, 0, 0, 6, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 224, 0, 0, 0, 0, + 30, 0, 0, 0, 0, 0, 0, 0, 132, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 0, 0, 0, +}; + +/* Quotation_Mark: 209 bytes. */ + +RE_UINT32 re_get_quotation_mark(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_quotation_mark_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_quotation_mark_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_quotation_mark_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_quotation_mark_stage_4[pos + f] << 6; + pos += code; + value = (re_quotation_mark_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Terminal_Punctuation. */ + +static RE_UINT8 re_terminal_punctuation_stage_1[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, +}; + +static RE_UINT8 re_terminal_punctuation_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 11, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 12, 13, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 14, + 15, 9, 16, 9, 17, 18, 9, 9, 9, 19, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 20, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 21, + 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static RE_UINT8 re_terminal_punctuation_stage_3[] = { + 0, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 4, 5, 6, 7, 8, + 9, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 1, 12, 1, + 13, 1, 1, 1, 1, 1, 14, 1, 1, 1, 1, 1, 15, 16, 17, 18, + 19, 1, 20, 1, 1, 21, 22, 1, 23, 1, 1, 1, 1, 1, 1, 1, + 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 25, 1, 1, 1, 26, 1, 1, 1, 1, 1, 1, 1, + 1, 27, 1, 1, 28, 29, 1, 1, 30, 31, 32, 33, 34, 35, 1, 36, + 1, 1, 1, 1, 37, 1, 38, 1, 1, 1, 1, 1, 1, 1, 1, 39, + 40, 1, 41, 1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 1, 1, + 1, 1, 1, 52, 53, 1, 54, 1, 55, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 56, 57, 58, 1, 1, 41, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 59, 1, 1, +}; + +static RE_UINT8 re_terminal_punctuation_stage_4[] = { + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, + 4, 0, 5, 0, 6, 0, 0, 0, 0, 0, 7, 0, 8, 0, 0, 0, + 0, 0, 0, 9, 0, 10, 2, 0, 0, 0, 0, 11, 0, 0, 12, 0, + 13, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 15, 0, 0, 0, 16, + 0, 0, 0, 17, 0, 18, 0, 0, 0, 0, 19, 0, 20, 0, 0, 0, + 0, 0, 11, 0, 0, 21, 0, 0, 0, 0, 22, 0, 0, 23, 0, 24, + 0, 25, 26, 0, 0, 27, 28, 0, 29, 0, 0, 0, 0, 0, 0, 24, + 30, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 32, 0, 0, 33, 0, + 0, 34, 0, 0, 0, 0, 26, 0, 0, 0, 35, 0, 0, 0, 36, 37, + 0, 0, 0, 38, 0, 0, 39, 0, 1, 0, 0, 40, 36, 0, 41, 0, + 0, 0, 42, 0, 36, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 43, + 0, 44, 0, 0, 45, 0, 0, 0, 0, 0, 46, 0, 0, 24, 47, 0, + 0, 0, 48, 0, 0, 0, 49, 0, 0, 50, 0, 0, 0, 4, 0, 0, + 0, 0, 51, 0, 0, 0, 29, 0, 0, 52, 0, 0, 0, 0, 0, 53, + 0, 0, 0, 33, 0, 0, 0, 54, 0, 55, 56, 0, 57, 0, 0, 0, +}; + +static RE_UINT8 re_terminal_punctuation_stage_5[] = { + 0, 0, 0, 0, 2, 80, 0, 140, 0, 0, 0, 64, 128, 0, 0, 0, + 0, 2, 0, 0, 8, 0, 0, 0, 0, 16, 0, 136, 0, 0, 16, 0, + 255, 23, 0, 0, 0, 0, 0, 3, 0, 0, 255, 127, 48, 0, 0, 0, + 0, 0, 0, 12, 0, 225, 7, 0, 0, 12, 0, 0, 254, 1, 0, 0, + 0, 96, 0, 0, 0, 56, 0, 0, 0, 0, 96, 0, 0, 0, 112, 4, + 60, 3, 0, 0, 0, 15, 0, 0, 0, 0, 0, 236, 0, 0, 0, 248, + 0, 0, 0, 192, 0, 0, 0, 48, 128, 3, 0, 0, 0, 64, 0, 16, + 2, 0, 0, 0, 6, 0, 0, 0, 0, 224, 0, 0, 0, 0, 248, 0, + 0, 0, 192, 0, 0, 192, 0, 0, 0, 128, 0, 0, 0, 0, 0, 224, + 0, 0, 0, 128, 0, 0, 3, 0, 0, 8, 0, 0, 0, 0, 247, 0, + 18, 0, 0, 0, 0, 0, 1, 0, 0, 0, 128, 0, 0, 0, 63, 0, + 0, 0, 0, 252, 0, 0, 0, 30, 128, 63, 0, 0, 3, 0, 0, 0, + 14, 0, 0, 0, 96, 32, 0, 192, 0, 0, 0, 31, 60, 254, 255, 0, + 0, 0, 0, 112, 0, 0, 31, 0, 0, 0, 32, 0, 0, 0, 128, 3, + 16, 0, 0, 0, 128, 7, 0, 0, +}; + +/* Terminal_Punctuation: 850 bytes. */ + +RE_UINT32 re_get_terminal_punctuation(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_terminal_punctuation_stage_1[f] << 5; + f = code >> 10; + code ^= f << 10; + pos = (RE_UINT32)re_terminal_punctuation_stage_2[pos + f] << 3; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_terminal_punctuation_stage_3[pos + f] << 2; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_terminal_punctuation_stage_4[pos + f] << 5; + pos += code; + value = (re_terminal_punctuation_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Other_Math. */ + +static RE_UINT8 re_other_math_stage_1[] = { + 0, 1, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, +}; + +static RE_UINT8 re_other_math_stage_2[] = { + 0, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 6, 1, 1, +}; + +static RE_UINT8 re_other_math_stage_3[] = { + 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 4, 1, 5, 1, 6, 7, 8, 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 10, 11, 1, 1, 1, 1, 12, 13, 14, 15, + 1, 1, 1, 1, 1, 1, 16, 1, +}; + +static RE_UINT8 re_other_math_stage_4[] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 8, 0, 9, 10, + 11, 12, 13, 0, 14, 15, 16, 17, 18, 0, 0, 0, 0, 19, 20, 21, + 0, 0, 0, 0, 0, 22, 23, 24, 25, 0, 26, 27, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 25, 28, 0, 0, 0, 0, 29, 0, 30, 31, + 0, 0, 0, 32, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 34, 34, 35, 34, 36, 37, 38, 34, 39, 40, 41, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 42, 43, 44, 35, 35, 45, 45, 46, 46, 47, 34, + 38, 48, 49, 50, 51, 52, 0, 0, +}; + +static RE_UINT8 re_other_math_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 39, 0, 0, 0, 51, 0, + 0, 0, 64, 0, 0, 0, 28, 0, 1, 0, 0, 0, 30, 0, 0, 96, + 0, 96, 0, 0, 0, 0, 255, 31, 98, 248, 0, 0, 132, 252, 47, 62, + 16, 179, 251, 241, 224, 3, 0, 0, 0, 0, 224, 243, 182, 62, 195, 240, + 255, 63, 235, 47, 48, 0, 0, 0, 0, 15, 0, 0, 0, 0, 176, 0, + 0, 0, 1, 0, 4, 0, 0, 0, 3, 192, 127, 240, 193, 140, 15, 0, + 148, 31, 0, 0, 96, 0, 0, 0, 5, 0, 0, 0, 15, 96, 0, 0, + 192, 255, 0, 0, 248, 255, 255, 1, 0, 0, 0, 15, 0, 0, 0, 48, + 10, 1, 0, 0, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 223, 255, + 255, 255, 255, 223, 100, 222, 255, 235, 239, 255, 255, 255, 191, 231, 223, 223, + 255, 255, 255, 123, 95, 252, 253, 255, 63, 255, 255, 255, 253, 255, 255, 247, + 255, 255, 255, 247, 255, 127, 255, 255, 255, 253, 255, 255, 247, 207, 255, 255, + 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, 255, 251, 255, 15, + 238, 251, 255, 15, +}; + +/* Other_Math: 502 bytes. */ + +RE_UINT32 re_get_other_math(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_other_math_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_other_math_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_other_math_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_other_math_stage_4[pos + f] << 5; + pos += code; + value = (re_other_math_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Hex_Digit. */ + +static RE_UINT8 re_hex_digit_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_hex_digit_stage_2[] = { + 0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_hex_digit_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, +}; + +static RE_UINT8 re_hex_digit_stage_4[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 1, +}; + +static RE_UINT8 re_hex_digit_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 126, 0, 0, 0, 126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 3, 126, 0, 0, 0, 126, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Hex_Digit: 129 bytes. */ + +RE_UINT32 re_get_hex_digit(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_hex_digit_stage_1[f] << 3; + f = code >> 13; + code ^= f << 13; + pos = (RE_UINT32)re_hex_digit_stage_2[pos + f] << 3; + f = code >> 10; + code ^= f << 10; + pos = (RE_UINT32)re_hex_digit_stage_3[pos + f] << 3; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_hex_digit_stage_4[pos + f] << 7; + pos += code; + value = (re_hex_digit_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* ASCII_Hex_Digit. */ + +static RE_UINT8 re_ascii_hex_digit_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_ascii_hex_digit_stage_2[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_ascii_hex_digit_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_ascii_hex_digit_stage_4[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_ascii_hex_digit_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 126, 0, 0, 0, 126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* ASCII_Hex_Digit: 97 bytes. */ + +RE_UINT32 re_get_ascii_hex_digit(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_ascii_hex_digit_stage_1[f] << 3; + f = code >> 13; + code ^= f << 13; + pos = (RE_UINT32)re_ascii_hex_digit_stage_2[pos + f] << 3; + f = code >> 10; + code ^= f << 10; + pos = (RE_UINT32)re_ascii_hex_digit_stage_3[pos + f] << 3; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_ascii_hex_digit_stage_4[pos + f] << 7; + pos += code; + value = (re_ascii_hex_digit_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Other_Alphabetic. */ + +static RE_UINT8 re_other_alphabetic_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_other_alphabetic_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, + 10, 11, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 14, 6, 6, 6, 6, 6, 6, 15, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_other_alphabetic_stage_3[] = { + 0, 0, 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 0, 0, 14, 0, 0, 0, 15, 16, 17, 18, 19, 20, 21, 0, 0, + 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, + 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, + 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 40, 0, 0, 0, 41, + 0, 0, 0, 0, 42, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_other_alphabetic_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 3, 0, 4, 0, 5, 6, 0, 0, 7, 8, + 9, 10, 0, 0, 0, 11, 0, 0, 12, 13, 0, 0, 0, 0, 0, 14, + 15, 16, 17, 18, 19, 20, 21, 18, 19, 20, 22, 23, 19, 20, 24, 18, + 19, 20, 25, 18, 26, 20, 27, 0, 15, 20, 28, 18, 19, 20, 28, 18, + 19, 20, 29, 18, 18, 0, 30, 31, 0, 32, 33, 0, 0, 34, 33, 0, + 0, 0, 0, 35, 36, 37, 0, 0, 0, 38, 39, 40, 41, 0, 0, 0, + 0, 0, 42, 0, 0, 0, 0, 0, 31, 31, 31, 31, 0, 43, 44, 0, + 0, 0, 0, 0, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, + 47, 0, 48, 49, 0, 0, 0, 0, 50, 51, 15, 0, 52, 53, 0, 54, + 0, 55, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 43, 57, 58, 0, 0, 0, 0, 0, 0, 0, 57, + 0, 0, 0, 59, 20, 0, 0, 0, 0, 60, 0, 0, 61, 62, 15, 0, + 0, 63, 64, 0, 15, 62, 0, 0, 0, 65, 66, 0, 0, 67, 0, 68, + 0, 0, 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, + 52, 73, 74, 0, 26, 75, 0, 0, 52, 64, 0, 0, 52, 76, 0, 0, + 0, 77, 0, 0, 0, 0, 42, 44, 15, 20, 21, 18, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 10, 61, 0, 0, 0, 0, 0, 0, 78, 79, 0, + 0, 80, 81, 0, 0, 82, 0, 0, 83, 84, 0, 0, 0, 0, 0, 0, + 0, 85, 0, 0, 0, 0, 0, 0, 0, 0, 35, 86, 0, 0, 0, 0, + 0, 0, 0, 0, 70, 0, 0, 0, 0, 10, 87, 87, 58, 0, 0, 0, +}; + +static RE_UINT8 re_other_alphabetic_stage_5[] = { + 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 255, 191, 182, 0, 0, 0, + 0, 0, 255, 7, 0, 248, 255, 254, 0, 0, 1, 0, 0, 0, 192, 31, + 158, 33, 0, 0, 0, 0, 2, 0, 0, 0, 255, 255, 192, 255, 1, 0, + 0, 0, 192, 248, 239, 30, 0, 0, 248, 3, 255, 255, 15, 0, 0, 0, + 0, 0, 0, 204, 255, 223, 224, 0, 12, 0, 0, 0, 14, 0, 0, 0, + 0, 0, 0, 192, 159, 25, 128, 0, 135, 25, 2, 0, 0, 0, 35, 0, + 191, 27, 0, 0, 159, 25, 192, 0, 4, 0, 0, 0, 199, 29, 128, 0, + 223, 29, 96, 0, 223, 29, 128, 0, 0, 128, 95, 255, 0, 0, 12, 0, + 0, 0, 242, 7, 0, 32, 0, 0, 0, 0, 242, 27, 0, 0, 254, 255, + 3, 224, 255, 254, 255, 255, 255, 31, 0, 248, 127, 121, 0, 0, 192, 195, + 133, 1, 30, 0, 124, 0, 0, 48, 0, 0, 0, 128, 0, 0, 192, 255, + 255, 1, 0, 0, 0, 2, 0, 0, 255, 15, 255, 1, 0, 0, 128, 15, + 0, 0, 224, 127, 254, 255, 31, 0, 31, 0, 0, 0, 0, 0, 224, 255, + 7, 0, 0, 0, 254, 51, 0, 0, 128, 255, 3, 0, 240, 255, 63, 0, + 128, 255, 31, 0, 255, 255, 255, 255, 255, 3, 0, 0, 0, 0, 240, 15, + 248, 0, 0, 0, 3, 0, 0, 0, 0, 0, 240, 255, 192, 7, 0, 0, + 128, 255, 7, 0, 0, 254, 127, 0, 8, 48, 0, 0, 0, 0, 157, 65, + 0, 248, 32, 0, 248, 7, 0, 0, 0, 0, 0, 64, 0, 0, 192, 7, + 110, 240, 0, 0, 0, 0, 0, 255, 63, 0, 0, 0, 0, 0, 255, 1, + 0, 0, 248, 255, 0, 240, 159, 0, 0, 128, 63, 127, 0, 0, 0, 48, + 0, 0, 255, 127, 1, 0, 0, 0, 0, 248, 63, 0, 0, 0, 0, 224, + 255, 7, 0, 0, 0, 0, 127, 0, 255, 255, 255, 127, 255, 3, 255, 255, +}; + +/* Other_Alphabetic: 945 bytes. */ + +RE_UINT32 re_get_other_alphabetic(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_other_alphabetic_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_other_alphabetic_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_other_alphabetic_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_other_alphabetic_stage_4[pos + f] << 5; + pos += code; + value = (re_other_alphabetic_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Ideographic. */ + +static RE_UINT8 re_ideographic_stage_1[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_ideographic_stage_2[] = { + 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 6, 2, 7, 8, 2, 9, 0, 0, 0, 0, 0, 10, +}; + +static RE_UINT8 re_ideographic_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 4, 0, 2, 5, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 7, + 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 0, + 2, 2, 10, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_ideographic_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 0, 0, + 3, 3, 3, 3, 3, 3, 4, 0, 3, 3, 3, 5, 3, 3, 6, 0, + 3, 3, 3, 3, 3, 3, 7, 0, 3, 8, 3, 3, 3, 3, 3, 3, + 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 10, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_ideographic_stage_5[] = { + 0, 0, 0, 0, 192, 0, 0, 0, 254, 3, 0, 7, 255, 255, 255, 255, + 255, 255, 63, 0, 255, 63, 255, 255, 255, 255, 255, 3, 255, 255, 127, 0, + 255, 255, 31, 0, 255, 255, 255, 63, 3, 0, 0, 0, +}; + +/* Ideographic: 333 bytes. */ + +RE_UINT32 re_get_ideographic(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_ideographic_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_ideographic_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_ideographic_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_ideographic_stage_4[pos + f] << 5; + pos += code; + value = (re_ideographic_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Diacritic. */ + +static RE_UINT8 re_diacritic_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_diacritic_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 7, 8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, + 10, 11, 12, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 13, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 14, 4, 4, 15, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +}; + +static RE_UINT8 re_diacritic_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 1, 1, 1, 1, 1, 17, 1, 18, 19, 20, 21, 22, 1, 23, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 24, 1, 25, 1, + 26, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 28, + 29, 30, 31, 32, 1, 1, 1, 1, 1, 1, 1, 33, 1, 1, 34, 35, + 1, 1, 36, 1, 1, 1, 1, 1, 1, 1, 37, 1, 1, 1, 1, 1, + 38, 39, 40, 41, 42, 43, 44, 45, 1, 1, 46, 1, 1, 1, 1, 47, + 1, 48, 1, 1, 1, 1, 1, 1, 49, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_diacritic_stage_4[] = { + 0, 0, 1, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 5, 5, 5, 5, 6, 7, 8, 0, 0, 0, + 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 10, 0, 11, 12, 13, 0, + 0, 0, 14, 0, 0, 0, 15, 16, 0, 4, 17, 0, 0, 18, 0, 19, + 20, 0, 0, 0, 0, 0, 0, 21, 0, 22, 23, 24, 0, 22, 25, 0, + 0, 22, 25, 0, 0, 22, 25, 0, 0, 22, 25, 0, 0, 0, 25, 0, + 0, 0, 25, 0, 0, 22, 25, 0, 0, 0, 25, 0, 0, 0, 26, 0, + 0, 0, 27, 0, 0, 0, 28, 0, 20, 29, 0, 0, 30, 0, 31, 0, + 0, 32, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, + 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, 0, 0, + 0, 38, 39, 40, 0, 41, 0, 0, 0, 42, 0, 43, 0, 0, 4, 44, + 0, 45, 5, 17, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, 49, 50, + 0, 0, 0, 0, 0, 0, 0, 51, 0, 52, 0, 0, 0, 0, 0, 0, + 0, 53, 0, 0, 54, 0, 0, 22, 0, 0, 0, 55, 56, 0, 0, 57, + 58, 59, 0, 0, 60, 0, 0, 20, 0, 0, 0, 0, 0, 0, 39, 61, + 0, 62, 63, 0, 0, 63, 2, 64, 0, 0, 0, 65, 0, 15, 66, 67, + 0, 0, 68, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 70, 0, 0, 0, 0, 0, 0, 0, 1, 2, 71, 72, 0, 0, 73, + 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 74, + 0, 0, 0, 0, 0, 75, 0, 0, 0, 76, 0, 63, 0, 0, 77, 0, + 0, 78, 0, 0, 0, 0, 0, 79, 0, 22, 25, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 15, 2, 0, + 0, 15, 0, 0, 0, 42, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, 0, 84, 0, 0, 0, + 0, 0, 0, 85, 86, 87, 0, 0, 0, 0, 0, 0, 0, 0, 88, 0, +}; + +static RE_UINT8 re_diacritic_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 129, 144, 1, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 127, 255, 224, 7, 0, 48, 4, + 48, 0, 0, 0, 248, 0, 0, 0, 0, 0, 0, 2, 0, 0, 254, 255, + 251, 255, 255, 191, 22, 0, 0, 0, 0, 248, 135, 1, 0, 0, 0, 128, + 97, 28, 0, 0, 255, 7, 0, 0, 192, 255, 1, 0, 0, 248, 63, 0, + 0, 0, 0, 3, 248, 255, 255, 127, 0, 0, 0, 16, 0, 32, 30, 0, + 0, 0, 2, 0, 0, 32, 0, 0, 0, 4, 0, 0, 128, 95, 0, 0, + 0, 31, 0, 0, 0, 0, 160, 194, 220, 0, 0, 0, 64, 0, 0, 0, + 0, 0, 128, 6, 128, 191, 0, 12, 0, 254, 15, 32, 0, 0, 0, 14, + 0, 0, 224, 159, 0, 0, 255, 63, 0, 0, 16, 0, 16, 0, 0, 0, + 0, 248, 15, 0, 0, 12, 0, 0, 0, 0, 192, 0, 0, 0, 0, 63, + 255, 33, 16, 3, 0, 240, 255, 255, 240, 255, 0, 0, 0, 0, 32, 224, + 0, 0, 0, 160, 3, 224, 0, 224, 0, 224, 0, 96, 0, 128, 3, 0, + 0, 128, 0, 0, 0, 252, 0, 0, 0, 0, 0, 30, 0, 128, 0, 176, + 0, 0, 0, 48, 0, 0, 3, 0, 0, 0, 128, 255, 3, 0, 0, 0, + 0, 1, 0, 0, 255, 255, 3, 0, 0, 120, 0, 0, 0, 0, 8, 0, + 32, 0, 0, 0, 0, 0, 0, 56, 7, 0, 0, 0, 0, 0, 64, 0, + 0, 0, 0, 248, 0, 48, 0, 0, 255, 255, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 192, 8, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 6, + 0, 0, 24, 0, 1, 28, 0, 0, 0, 0, 96, 0, 0, 6, 0, 0, + 192, 31, 31, 0, 12, 0, 0, 0, 0, 8, 0, 0, 0, 0, 31, 0, + 0, 128, 255, 255, 128, 227, 7, 248, 231, 15, 0, 0, 0, 60, 0, 0, + 0, 0, 127, 0, +}; + +/* Diacritic: 997 bytes. */ + +RE_UINT32 re_get_diacritic(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_diacritic_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_diacritic_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_diacritic_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_diacritic_stage_4[pos + f] << 5; + pos += code; + value = (re_diacritic_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Extender. */ + +static RE_UINT8 re_extender_stage_1[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, +}; + +static RE_UINT8 re_extender_stage_2[] = { + 0, 1, 2, 3, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 5, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, + 2, 2, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_extender_stage_3[] = { + 0, 1, 2, 1, 1, 1, 3, 4, 1, 1, 1, 1, 1, 1, 5, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 7, 1, 8, 1, 1, 1, + 9, 1, 1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, 11, 1, + 1, 12, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 14, + 1, 1, 1, 15, 1, 16, 1, 1, 1, 1, 1, 17, 1, 1, 1, 1, +}; + +static RE_UINT8 re_extender_stage_4[] = { + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 5, 0, 0, 0, 5, 0, + 6, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, + 0, 9, 0, 10, 0, 0, 0, 0, 11, 12, 0, 0, 13, 0, 0, 14, + 15, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 17, 5, 0, 0, 0, 18, 0, 0, 19, 20, + 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 22, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_extender_stage_5[] = { + 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 3, 0, 1, 0, 0, 0, + 0, 0, 0, 4, 64, 0, 0, 0, 0, 4, 0, 0, 8, 0, 0, 0, + 128, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 8, 32, 0, 0, 0, + 0, 0, 62, 0, 0, 0, 0, 96, 0, 0, 0, 112, 0, 0, 32, 0, + 0, 16, 0, 0, 0, 128, 0, 0, 0, 0, 1, 0, 0, 0, 0, 32, + 0, 0, 24, 0, 192, 1, 0, 0, 12, 0, 0, 0, +}; + +/* Extender: 414 bytes. */ + +RE_UINT32 re_get_extender(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_extender_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_extender_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_extender_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_extender_stage_4[pos + f] << 5; + pos += code; + value = (re_extender_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Other_Lowercase. */ + +static RE_UINT8 re_other_lowercase_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_other_lowercase_stage_2[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static RE_UINT8 re_other_lowercase_stage_3[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 4, 2, 5, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 7, 2, 8, 2, 2, +}; + +static RE_UINT8 re_other_lowercase_stage_4[] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 7, 0, + 0, 8, 9, 0, 0, 10, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 14, 0, 15, + 0, 0, 0, 0, 0, 16, 0, 0, +}; + +static RE_UINT8 re_other_lowercase_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, + 0, 0, 0, 0, 0, 0, 255, 1, 3, 0, 0, 0, 31, 0, 0, 0, + 32, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 240, 255, 255, + 255, 255, 255, 255, 255, 7, 0, 1, 0, 0, 0, 248, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 2, 128, 0, 0, 255, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 48, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 0, 0, 0, 240, 0, 0, 0, 0, +}; + +/* Other_Lowercase: 297 bytes. */ + +RE_UINT32 re_get_other_lowercase(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_other_lowercase_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_other_lowercase_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_other_lowercase_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_other_lowercase_stage_4[pos + f] << 6; + pos += code; + value = (re_other_lowercase_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Other_Uppercase. */ + +static RE_UINT8 re_other_uppercase_stage_1[] = { + 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, +}; + +static RE_UINT8 re_other_uppercase_stage_2[] = { + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, +}; + +static RE_UINT8 re_other_uppercase_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, + 0, 3, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_other_uppercase_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 1, 0, 0, 3, 4, 4, 5, 0, 0, 0, +}; + +static RE_UINT8 re_other_uppercase_stage_5[] = { + 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 192, 255, 0, 0, 255, 255, + 255, 3, 255, 255, 255, 3, 0, 0, +}; + +/* Other_Uppercase: 162 bytes. */ + +RE_UINT32 re_get_other_uppercase(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_other_uppercase_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_other_uppercase_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_other_uppercase_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_other_uppercase_stage_4[pos + f] << 5; + pos += code; + value = (re_other_uppercase_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Noncharacter_Code_Point. */ + +static RE_UINT8 re_noncharacter_code_point_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_noncharacter_code_point_stage_2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, +}; + +static RE_UINT8 re_noncharacter_code_point_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 0, 0, 0, 0, 0, 0, 0, 2, +}; + +static RE_UINT8 re_noncharacter_code_point_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 2, +}; + +static RE_UINT8 re_noncharacter_code_point_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 192, +}; + +/* Noncharacter_Code_Point: 121 bytes. */ + +RE_UINT32 re_get_noncharacter_code_point(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_noncharacter_code_point_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_noncharacter_code_point_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_noncharacter_code_point_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_noncharacter_code_point_stage_4[pos + f] << 6; + pos += code; + value = (re_noncharacter_code_point_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Other_Grapheme_Extend. */ + +static RE_UINT8 re_other_grapheme_extend_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_other_grapheme_extend_stage_2[] = { + 0, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_other_grapheme_extend_stage_3[] = { + 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 0, 7, 8, 0, 0, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_other_grapheme_extend_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 0, 3, 1, 2, 0, 4, + 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 1, 2, 0, 0, + 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, +}; + +static RE_UINT8 re_other_grapheme_extend_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, + 0, 0, 128, 0, 0, 0, 0, 0, 4, 0, 96, 0, 0, 0, 0, 0, + 0, 128, 0, 128, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 128, 0, 0, + 0, 0, 0, 0, 32, 192, 7, 0, +}; + +/* Other_Grapheme_Extend: 289 bytes. */ + +RE_UINT32 re_get_other_grapheme_extend(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_other_grapheme_extend_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_other_grapheme_extend_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_other_grapheme_extend_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_other_grapheme_extend_stage_4[pos + f] << 6; + pos += code; + value = (re_other_grapheme_extend_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* IDS_Binary_Operator. */ + +static RE_UINT8 re_ids_binary_operator_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_ids_binary_operator_stage_2[] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_ids_binary_operator_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + +static RE_UINT8 re_ids_binary_operator_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + +static RE_UINT8 re_ids_binary_operator_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 15, +}; + +/* IDS_Binary_Operator: 97 bytes. */ + +RE_UINT32 re_get_ids_binary_operator(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_ids_binary_operator_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_ids_binary_operator_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_ids_binary_operator_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_ids_binary_operator_stage_4[pos + f] << 6; + pos += code; + value = (re_ids_binary_operator_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* IDS_Trinary_Operator. */ + +static RE_UINT8 re_ids_trinary_operator_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_ids_trinary_operator_stage_2[] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_ids_trinary_operator_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + +static RE_UINT8 re_ids_trinary_operator_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + +static RE_UINT8 re_ids_trinary_operator_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, +}; + +/* IDS_Trinary_Operator: 97 bytes. */ + +RE_UINT32 re_get_ids_trinary_operator(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_ids_trinary_operator_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_ids_trinary_operator_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_ids_trinary_operator_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_ids_trinary_operator_stage_4[pos + f] << 6; + pos += code; + value = (re_ids_trinary_operator_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Radical. */ + +static RE_UINT8 re_radical_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_radical_stage_2[] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_radical_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + +static RE_UINT8 re_radical_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 2, 3, 2, 2, 2, 2, 2, 2, 4, 0, +}; + +static RE_UINT8 re_radical_stage_5[] = { + 0, 0, 0, 0, 255, 255, 255, 251, 255, 255, 255, 255, 255, 255, 15, 0, + 255, 255, 63, 0, +}; + +/* Radical: 117 bytes. */ + +RE_UINT32 re_get_radical(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_radical_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_radical_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_radical_stage_3[pos + f] << 4; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_radical_stage_4[pos + f] << 5; + pos += code; + value = (re_radical_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Unified_Ideograph. */ + +static RE_UINT8 re_unified_ideograph_stage_1[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_unified_ideograph_stage_2[] = { + 0, 0, 0, 1, 2, 3, 3, 3, 3, 4, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 7, 8, 0, 0, 0, +}; + +static RE_UINT8 re_unified_ideograph_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 0, 0, 0, 0, 0, 4, 0, 0, + 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 6, 7, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8, +}; + +static RE_UINT8 re_unified_ideograph_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 3, + 4, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 5, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 1, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 8, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_unified_ideograph_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 63, 0, 255, 255, 63, 0, 0, 0, 0, 0, + 0, 192, 26, 128, 154, 3, 0, 0, 255, 255, 127, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 31, 0, 255, 255, 255, 63, 255, 255, 255, 255, + 255, 255, 255, 255, 3, 0, 0, 0, +}; + +/* Unified_Ideograph: 281 bytes. */ + +RE_UINT32 re_get_unified_ideograph(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_unified_ideograph_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_unified_ideograph_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_unified_ideograph_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_unified_ideograph_stage_4[pos + f] << 6; + pos += code; + value = (re_unified_ideograph_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Other_Default_Ignorable_Code_Point. */ + +static RE_UINT8 re_other_default_ignorable_code_point_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, +}; + +static RE_UINT8 re_other_default_ignorable_code_point_stage_2[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +}; + +static RE_UINT8 re_other_default_ignorable_code_point_stage_3[] = { + 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + 7, 8, 8, 8, 8, 8, 8, 8, +}; + +static RE_UINT8 re_other_default_ignorable_code_point_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 7, 8, 0, 9, 9, 0, 0, 0, 10, + 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static RE_UINT8 re_other_default_ignorable_code_point_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 1, + 253, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 255, 255, +}; + +/* Other_Default_Ignorable_Code_Point: 281 bytes. */ + +RE_UINT32 re_get_other_default_ignorable_code_point(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_other_default_ignorable_code_point_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_other_default_ignorable_code_point_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_other_default_ignorable_code_point_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_other_default_ignorable_code_point_stage_4[pos + f] << 6; + pos += code; + value = (re_other_default_ignorable_code_point_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Deprecated. */ + +static RE_UINT8 re_deprecated_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, +}; + +static RE_UINT8 re_deprecated_stage_2[] = { + 0, 1, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static RE_UINT8 re_deprecated_stage_3[] = { + 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 0, 6, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_deprecated_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 0, 6, 0, 0, 0, 0, 0, 0, 7, 0, 0, 8, 0, 0, 0, 0, +}; + +static RE_UINT8 re_deprecated_stage_5[] = { + 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 8, 0, 0, 0, 128, 2, + 24, 0, 0, 0, 0, 252, 0, 0, 0, 6, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 128, +}; + +/* Deprecated: 230 bytes. */ + +RE_UINT32 re_get_deprecated(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_deprecated_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_deprecated_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_deprecated_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_deprecated_stage_4[pos + f] << 5; + pos += code; + value = (re_deprecated_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Soft_Dotted. */ + +static RE_UINT8 re_soft_dotted_stage_1[] = { + 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, +}; + +static RE_UINT8 re_soft_dotted_stage_2[] = { + 0, 1, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_soft_dotted_stage_3[] = { + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 6, 7, 5, 8, 9, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 11, 12, 13, 5, +}; + +static RE_UINT8 re_soft_dotted_stage_4[] = { + 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 10, 11, 0, 0, 0, 12, 0, 0, 0, 0, 13, 0, + 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, 0, 0, 0, 17, 18, 0, 19, 20, 0, 21, + 0, 22, 23, 0, 24, 0, 17, 18, 0, 19, 20, 0, 21, 0, 0, 0, +}; + +static RE_UINT8 re_soft_dotted_stage_5[] = { + 0, 0, 0, 0, 0, 6, 0, 0, 0, 128, 0, 0, 0, 2, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 32, 0, 0, 4, 0, 0, 0, 8, 0, + 0, 0, 64, 1, 4, 0, 0, 0, 0, 0, 64, 0, 16, 1, 0, 0, + 0, 32, 0, 0, 0, 8, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, + 0, 0, 0, 16, 12, 0, 0, 0, 0, 0, 192, 0, 0, 12, 0, 0, + 0, 0, 0, 192, 0, 0, 12, 0, 192, 0, 0, 0, 0, 0, 0, 12, + 0, 192, 0, 0, +}; + +/* Soft_Dotted: 342 bytes. */ + +RE_UINT32 re_get_soft_dotted(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_soft_dotted_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_soft_dotted_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_soft_dotted_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_soft_dotted_stage_4[pos + f] << 5; + pos += code; + value = (re_soft_dotted_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Logical_Order_Exception. */ + +static RE_UINT8 re_logical_order_exception_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_logical_order_exception_stage_2[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_logical_order_exception_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, +}; + +static RE_UINT8 re_logical_order_exception_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_logical_order_exception_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 224, 4, 0, 0, 0, 0, 0, 0, 96, 26, +}; + +/* Logical_Order_Exception: 145 bytes. */ + +RE_UINT32 re_get_logical_order_exception(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_logical_order_exception_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_logical_order_exception_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_logical_order_exception_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_logical_order_exception_stage_4[pos + f] << 6; + pos += code; + value = (re_logical_order_exception_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Other_ID_Start. */ + +static RE_UINT8 re_other_id_start_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_other_id_start_stage_2[] = { + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_other_id_start_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_other_id_start_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_other_id_start_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 64, 0, 0, + 0, 0, 0, 24, 0, 0, 0, 0, +}; + +/* Other_ID_Start: 113 bytes. */ + +RE_UINT32 re_get_other_id_start(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_other_id_start_stage_1[f] << 3; + f = code >> 13; + code ^= f << 13; + pos = (RE_UINT32)re_other_id_start_stage_2[pos + f] << 4; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_other_id_start_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_other_id_start_stage_4[pos + f] << 6; + pos += code; + value = (re_other_id_start_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Other_ID_Continue. */ + +static RE_UINT8 re_other_id_continue_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_other_id_continue_stage_2[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_other_id_continue_stage_3[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 4, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_other_id_continue_stage_4[] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4, +}; + +static RE_UINT8 re_other_id_continue_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, + 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 3, 0, + 0, 0, 0, 4, 0, 0, 0, 0, +}; + +/* Other_ID_Continue: 145 bytes. */ + +RE_UINT32 re_get_other_id_continue(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_other_id_continue_stage_1[f] << 3; + f = code >> 13; + code ^= f << 13; + pos = (RE_UINT32)re_other_id_continue_stage_2[pos + f] << 4; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_other_id_continue_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_other_id_continue_stage_4[pos + f] << 6; + pos += code; + value = (re_other_id_continue_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* STerm. */ + +static RE_UINT8 re_sterm_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_sterm_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, + 7, 11, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 13, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 14, 7, 7, 7, 15, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static RE_UINT8 re_sterm_stage_3[] = { + 0, 1, 1, 1, 1, 2, 3, 4, 1, 5, 1, 1, 1, 1, 1, 1, + 6, 1, 1, 7, 1, 1, 8, 9, 10, 11, 12, 13, 14, 1, 1, 1, + 15, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 16, 1, + 17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 19, 1, 20, 21, 22, 23, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 24, 25, 1, 1, 26, 1, 1, 1, 1, 1, + 27, 28, 29, 1, 1, 30, 31, 32, 1, 1, 33, 34, 1, 1, 1, 1, + 1, 1, 1, 1, 35, 1, 1, 1, 1, 1, 36, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_sterm_stage_4[] = { + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, + 5, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, + 0, 15, 0, 16, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, + 0, 19, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 21, 0, 0, 0, 0, 0, 0, 22, + 0, 0, 0, 23, 0, 0, 21, 0, 0, 24, 0, 0, 0, 0, 25, 0, + 0, 0, 26, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 28, + 0, 0, 29, 0, 0, 0, 0, 0, 1, 0, 0, 30, 0, 0, 0, 0, + 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 16, 32, 0, + 0, 0, 33, 0, 0, 0, 34, 0, 0, 35, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 37, 0, 0, 0, 0, 0, + 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 39, + 0, 40, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 42, 0, 0, 0, +}; + +static RE_UINT8 re_sterm_stage_5[] = { + 0, 0, 0, 0, 2, 64, 0, 128, 0, 2, 0, 0, 0, 0, 0, 128, + 0, 0, 16, 0, 7, 0, 0, 0, 0, 0, 0, 2, 48, 0, 0, 0, + 0, 12, 0, 0, 132, 1, 0, 0, 0, 64, 0, 0, 0, 0, 96, 0, + 8, 2, 0, 0, 0, 15, 0, 0, 0, 0, 0, 204, 0, 0, 0, 24, + 0, 0, 0, 192, 0, 0, 0, 48, 128, 3, 0, 0, 0, 64, 0, 16, + 4, 0, 0, 0, 0, 192, 0, 0, 0, 0, 136, 0, 0, 0, 192, 0, + 0, 128, 0, 0, 0, 3, 0, 0, 0, 0, 0, 224, 0, 0, 3, 0, + 0, 8, 0, 0, 0, 0, 196, 0, 2, 0, 0, 0, 128, 1, 0, 0, + 3, 0, 0, 0, 14, 0, 0, 0, 96, 32, 0, 192, 0, 0, 0, 27, + 12, 254, 255, 0, 6, 0, 0, 0, 0, 0, 0, 112, 0, 0, 32, 0, + 0, 0, 128, 1, 16, 0, 0, 0, 0, 1, 0, 0, +}; + +/* STerm: 709 bytes. */ + +RE_UINT32 re_get_sterm(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_sterm_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_sterm_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_sterm_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_sterm_stage_4[pos + f] << 5; + pos += code; + value = (re_sterm_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Variation_Selector. */ + +static RE_UINT8 re_variation_selector_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, +}; + +static RE_UINT8 re_variation_selector_stage_2[] = { + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_variation_selector_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_variation_selector_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, +}; + +static RE_UINT8 re_variation_selector_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, + 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, +}; + +/* Variation_Selector: 169 bytes. */ + +RE_UINT32 re_get_variation_selector(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_variation_selector_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_variation_selector_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_variation_selector_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_variation_selector_stage_4[pos + f] << 6; + pos += code; + value = (re_variation_selector_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Pattern_White_Space. */ + +static RE_UINT8 re_pattern_white_space_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_pattern_white_space_stage_2[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_pattern_white_space_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_pattern_white_space_stage_4[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_pattern_white_space_stage_5[] = { + 0, 62, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 3, 0, 0, +}; + +/* Pattern_White_Space: 129 bytes. */ + +RE_UINT32 re_get_pattern_white_space(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_pattern_white_space_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_pattern_white_space_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_pattern_white_space_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_pattern_white_space_stage_4[pos + f] << 6; + pos += code; + value = (re_pattern_white_space_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Pattern_Syntax. */ + +static RE_UINT8 re_pattern_syntax_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_pattern_syntax_stage_2[] = { + 0, 1, 1, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_pattern_syntax_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 3, 4, 4, 5, 4, 4, 6, 4, 4, 4, 4, 1, 1, 7, 1, + 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 10, 1, +}; + +static RE_UINT8 re_pattern_syntax_stage_4[] = { + 0, 1, 2, 2, 0, 3, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, + 8, 8, 8, 9, 10, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, + 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 14, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_pattern_syntax_stage_5[] = { + 0, 0, 0, 0, 254, 255, 0, 252, 1, 0, 0, 120, 254, 90, 67, 136, + 0, 0, 128, 0, 0, 0, 255, 255, 255, 0, 255, 127, 254, 255, 239, 127, + 255, 255, 255, 255, 255, 255, 63, 0, 0, 0, 240, 255, 14, 255, 255, 255, + 1, 0, 1, 0, 0, 0, 0, 192, 96, 0, 0, 0, +}; + +/* Pattern_Syntax: 277 bytes. */ + +RE_UINT32 re_get_pattern_syntax(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_pattern_syntax_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_pattern_syntax_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_pattern_syntax_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_pattern_syntax_stage_4[pos + f] << 5; + pos += code; + value = (re_pattern_syntax_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Hangul_Syllable_Type. */ + +static RE_UINT8 re_hangul_syllable_type_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_hangul_syllable_type_stage_2[] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_hangul_syllable_type_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 4, + 5, 6, 7, 8, 9, 10, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, + 7, 8, 9, 10, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 7, 8, + 9, 10, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 7, 8, 9, 10, + 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 7, 8, 9, 10, 4, 5, + 6, 7, 8, 9, 10, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 11, +}; + +static RE_UINT8 re_hangul_syllable_type_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 4, + 5, 6, 6, 7, 6, 6, 6, 5, 6, 6, 7, 6, 6, 6, 5, 6, + 6, 7, 6, 6, 6, 5, 6, 6, 7, 6, 6, 6, 5, 6, 6, 7, + 6, 6, 6, 5, 6, 6, 7, 6, 6, 6, 5, 6, 6, 7, 6, 6, + 6, 5, 6, 6, 7, 6, 6, 6, 5, 6, 6, 7, 6, 6, 6, 5, + 6, 6, 7, 6, 6, 6, 5, 6, 6, 7, 6, 6, 6, 5, 6, 6, + 7, 6, 6, 6, 5, 6, 6, 7, 6, 6, 6, 5, 6, 6, 7, 6, + 6, 6, 5, 6, 6, 7, 6, 6, 6, 5, 6, 6, 7, 6, 6, 6, + 6, 5, 6, 6, 8, 0, 2, 2, 9, 10, 3, 3, 3, 3, 3, 11, +}; + +static RE_UINT8 re_hangul_syllable_type_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 0, 0, 0, 4, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, + 5, 5, 5, 5, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, +}; + +/* Hangul_Syllable_Type: 497 bytes. */ + +RE_UINT32 re_get_hangul_syllable_type(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_hangul_syllable_type_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_hangul_syllable_type_stage_2[pos + f] << 4; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_hangul_syllable_type_stage_3[pos + f] << 4; + f = code >> 3; + code ^= f << 3; + pos = (RE_UINT32)re_hangul_syllable_type_stage_4[pos + f] << 3; + value = re_hangul_syllable_type_stage_5[pos + code]; + + return value; +} + +/* Bidi_Class. */ + +static RE_UINT8 re_bidi_class_stage_1[] = { + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 7, + 8, 9, 5, 5, 5, 5, 10, 5, 5, 5, 5, 11, 5, 12, 13, 14, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 16, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, +}; + +static RE_UINT8 re_bidi_class_stage_2[] = { + 0, 1, 2, 2, 2, 3, 4, 5, 2, 6, 2, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 2, 2, 2, 2, 30, 31, 32, 2, 2, 2, 2, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 2, 46, 2, 2, 2, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 53, 53, 53, 58, 53, 53, + 2, 2, 53, 53, 53, 53, 59, 60, 2, 61, 62, 63, 64, 65, 53, 66, + 67, 68, 2, 69, 70, 71, 72, 73, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 74, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 75, 2, 2, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 2, 86, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 87, 88, 88, 88, 89, 90, 91, 92, 93, 94, + 2, 2, 95, 96, 2, 97, 98, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 99, 99, 100, 99, 101, 102, 103, 99, 99, 99, 99, 99, 104, 99, 99, 99, + 105, 106, 107, 108, 109, 110, 111, 2, 2, 112, 2, 113, 114, 115, 116, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 117, 118, 2, 2, 2, 2, 2, 2, 2, 2, 119, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 120, 2, 2, 2, 2, 2, 2, + 2, 2, 121, 122, 123, 2, 124, 2, 2, 2, 2, 2, 2, 125, 126, 127, + 2, 2, 2, 2, 128, 129, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 99, 130, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 88, 131, 99, 99, + 132, 133, 134, 2, 2, 2, 53, 53, 53, 53, 135, 136, 53, 137, 138, 139, + 140, 141, 142, 143, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 144, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 144, + 145, 145, 146, 147, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, +}; + +static RE_UINT8 re_bidi_class_stage_3[] = { + 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 9, 10, 11, 12, 11, 12, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 13, 14, 14, 15, 16, + 17, 17, 17, 17, 17, 17, 17, 18, 19, 11, 11, 11, 11, 11, 11, 20, + 21, 11, 11, 11, 11, 11, 11, 11, 22, 23, 17, 24, 25, 26, 26, 26, + 27, 28, 29, 29, 30, 17, 31, 32, 29, 29, 29, 29, 29, 33, 34, 35, + 29, 36, 29, 17, 28, 29, 29, 29, 29, 29, 37, 32, 26, 26, 38, 39, + 26, 40, 41, 26, 26, 42, 26, 26, 26, 26, 29, 29, 29, 29, 43, 17, + 44, 11, 11, 45, 46, 47, 48, 11, 49, 11, 11, 50, 51, 11, 48, 52, + 53, 11, 11, 50, 54, 49, 11, 55, 53, 11, 11, 50, 56, 11, 48, 57, + 49, 11, 11, 58, 51, 59, 48, 11, 60, 11, 11, 11, 61, 11, 11, 62, + 63, 11, 11, 64, 65, 66, 48, 67, 49, 11, 11, 50, 68, 11, 48, 11, + 49, 11, 11, 11, 51, 11, 48, 11, 11, 11, 11, 11, 69, 70, 11, 11, + 11, 11, 11, 71, 72, 11, 11, 11, 11, 11, 11, 73, 74, 11, 11, 11, + 11, 75, 11, 76, 11, 11, 11, 77, 78, 79, 17, 80, 59, 11, 11, 11, + 11, 11, 81, 82, 11, 83, 63, 84, 85, 86, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 81, 11, 11, 11, 87, 11, 11, 11, 11, 11, 11, + 4, 11, 11, 11, 11, 11, 11, 11, 88, 89, 11, 11, 11, 11, 11, 11, + 11, 90, 11, 90, 11, 48, 11, 48, 11, 11, 11, 91, 92, 93, 11, 87, + 94, 11, 11, 11, 11, 11, 11, 11, 11, 11, 95, 11, 11, 11, 11, 11, + 11, 11, 96, 97, 98, 11, 11, 11, 11, 11, 11, 11, 11, 99, 16, 16, + 11, 100, 11, 11, 11, 101, 102, 103, 11, 11, 11, 104, 11, 11, 11, 11, + 105, 11, 11, 106, 60, 11, 107, 105, 108, 11, 109, 11, 11, 11, 110, 108, + 11, 11, 111, 112, 11, 11, 11, 11, 11, 11, 11, 11, 11, 113, 114, 115, + 11, 11, 11, 11, 17, 17, 17, 116, 11, 11, 11, 117, 118, 119, 119, 120, + 121, 16, 122, 123, 124, 125, 126, 127, 128, 11, 129, 129, 129, 17, 17, 63, + 130, 131, 132, 133, 134, 16, 11, 11, 135, 16, 16, 16, 16, 16, 16, 16, + 16, 136, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 137, 11, 11, 11, 5, 16, 138, 16, 16, 16, 16, 16, 139, + 16, 16, 140, 11, 139, 11, 16, 16, 141, 142, 11, 11, 11, 11, 143, 16, + 16, 16, 144, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 145, + 16, 146, 16, 147, 148, 149, 150, 11, 11, 11, 11, 11, 11, 11, 151, 152, + 11, 11, 11, 11, 11, 11, 11, 153, 11, 11, 11, 11, 11, 11, 17, 17, + 16, 16, 16, 16, 154, 11, 11, 11, 16, 155, 16, 16, 16, 16, 16, 156, + 16, 16, 16, 16, 16, 137, 11, 157, 158, 16, 159, 160, 11, 11, 11, 11, + 11, 161, 4, 11, 11, 11, 11, 162, 11, 11, 11, 11, 16, 16, 156, 11, + 11, 120, 11, 11, 11, 16, 11, 163, 11, 11, 11, 164, 150, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 165, 11, 11, 11, 11, 11, 99, 11, 166, + 11, 11, 11, 11, 16, 16, 16, 16, 11, 16, 16, 16, 140, 11, 11, 11, + 119, 11, 11, 11, 11, 11, 153, 167, 11, 64, 11, 11, 11, 11, 11, 108, + 16, 16, 149, 11, 11, 11, 11, 11, 168, 11, 11, 11, 11, 11, 11, 11, + 169, 11, 170, 171, 11, 11, 11, 172, 11, 11, 11, 11, 173, 11, 17, 108, + 11, 11, 174, 11, 175, 108, 11, 11, 44, 11, 11, 176, 11, 11, 177, 11, + 11, 11, 178, 179, 180, 11, 11, 50, 11, 11, 11, 181, 49, 11, 68, 59, + 11, 11, 11, 11, 11, 11, 182, 11, 11, 183, 184, 26, 26, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 185, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 8, 8, 186, 17, 87, 17, 16, 16, 187, 188, 29, + 29, 29, 29, 29, 29, 29, 29, 189, 190, 3, 4, 5, 4, 5, 137, 11, + 11, 11, 11, 11, 11, 11, 191, 192, 193, 11, 11, 11, 16, 16, 16, 16, + 194, 157, 4, 11, 11, 11, 11, 86, 11, 11, 11, 11, 11, 11, 195, 142, + 11, 11, 11, 11, 11, 11, 11, 196, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 197, 26, 26, 26, 26, 26, 26, 198, 26, 26, 199, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 200, 26, 26, 26, 26, 201, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 202, 203, 49, 11, 11, 204, 205, 14, 137, 153, + 108, 11, 11, 206, 11, 11, 11, 11, 44, 11, 207, 208, 11, 11, 11, 209, + 108, 11, 11, 210, 211, 11, 11, 11, 11, 11, 153, 212, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 153, 213, 11, 108, 11, 11, 50, 63, 11, 214, 208, + 11, 11, 11, 215, 216, 11, 11, 11, 11, 11, 11, 217, 63, 68, 11, 11, + 11, 11, 11, 218, 63, 11, 11, 11, 11, 11, 219, 220, 11, 11, 11, 11, + 11, 81, 221, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 208, + 11, 11, 11, 205, 11, 11, 11, 11, 153, 44, 11, 11, 11, 11, 11, 11, + 11, 222, 223, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 224, 225, + 226, 11, 227, 11, 11, 11, 11, 11, 16, 16, 16, 16, 228, 11, 11, 11, + 16, 16, 16, 16, 16, 140, 11, 11, 11, 11, 11, 11, 11, 162, 11, 11, + 11, 229, 11, 11, 166, 11, 11, 11, 230, 11, 11, 11, 231, 232, 232, 232, + 17, 17, 17, 233, 17, 17, 80, 177, 173, 107, 234, 11, 11, 11, 11, 11, + 26, 26, 26, 26, 26, 235, 26, 26, 29, 29, 29, 29, 29, 29, 29, 236, + 16, 16, 157, 16, 16, 16, 16, 16, 16, 156, 237, 164, 164, 164, 16, 137, + 238, 11, 11, 11, 11, 11, 133, 11, 16, 16, 16, 16, 16, 16, 16, 155, + 16, 16, 239, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 194, 156, + 16, 16, 16, 16, 16, 16, 16, 156, 16, 16, 16, 16, 16, 240, 11, 11, + 157, 16, 16, 16, 241, 87, 16, 16, 241, 16, 242, 11, 11, 11, 11, 11, + 11, 243, 11, 11, 11, 11, 11, 11, 240, 11, 11, 11, 4, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 244, 8, 8, 8, 8, 8, 8, 8, 8, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 8, +}; + +static RE_UINT8 re_bidi_class_stage_4[] = { + 0, 0, 1, 2, 0, 0, 0, 3, 4, 5, 6, 7, 8, 8, 9, 10, + 11, 12, 12, 12, 12, 12, 13, 10, 12, 12, 13, 14, 0, 15, 0, 0, + 0, 0, 0, 0, 16, 5, 17, 18, 19, 20, 21, 10, 12, 12, 12, 12, + 12, 13, 12, 12, 12, 12, 22, 12, 23, 10, 10, 10, 12, 24, 10, 17, + 10, 10, 10, 10, 25, 25, 25, 25, 12, 26, 12, 27, 12, 17, 12, 12, + 12, 27, 12, 12, 28, 25, 29, 12, 12, 12, 27, 30, 31, 25, 25, 25, + 25, 25, 25, 32, 33, 32, 34, 34, 34, 34, 34, 34, 35, 36, 37, 38, + 25, 25, 39, 40, 40, 40, 40, 40, 40, 40, 41, 25, 35, 35, 42, 43, + 44, 40, 40, 40, 40, 45, 25, 46, 25, 47, 48, 49, 8, 8, 50, 40, + 51, 40, 40, 40, 40, 45, 25, 25, 34, 34, 52, 25, 25, 53, 54, 34, + 34, 55, 32, 25, 25, 31, 31, 56, 34, 34, 31, 34, 41, 25, 25, 25, + 57, 12, 12, 12, 12, 12, 58, 59, 60, 25, 59, 61, 60, 25, 12, 12, + 62, 12, 12, 12, 61, 12, 12, 12, 12, 12, 12, 59, 60, 59, 12, 61, + 63, 12, 64, 12, 65, 12, 12, 12, 65, 28, 66, 29, 29, 61, 12, 12, + 60, 67, 59, 61, 68, 12, 12, 12, 12, 12, 12, 66, 12, 58, 12, 12, + 58, 12, 12, 12, 59, 12, 12, 61, 13, 10, 69, 12, 59, 12, 12, 12, + 12, 12, 12, 62, 59, 62, 70, 29, 12, 65, 12, 12, 12, 12, 10, 71, + 12, 12, 12, 29, 12, 12, 58, 12, 62, 72, 12, 12, 61, 25, 57, 64, + 12, 28, 25, 57, 61, 25, 67, 59, 12, 12, 25, 29, 12, 12, 29, 12, + 12, 73, 74, 26, 60, 25, 25, 57, 25, 70, 12, 60, 25, 25, 60, 25, + 25, 25, 25, 59, 12, 12, 12, 60, 70, 25, 65, 65, 12, 12, 29, 62, + 60, 59, 12, 12, 58, 65, 12, 61, 12, 12, 12, 61, 10, 10, 26, 12, + 75, 12, 12, 12, 12, 12, 13, 11, 62, 59, 12, 12, 12, 67, 25, 29, + 12, 58, 60, 25, 25, 12, 64, 61, 10, 10, 76, 77, 12, 12, 61, 12, + 57, 28, 59, 12, 58, 12, 60, 12, 11, 26, 12, 12, 12, 12, 12, 23, + 12, 28, 66, 12, 12, 58, 25, 57, 72, 60, 25, 59, 28, 25, 25, 66, + 25, 25, 25, 57, 25, 12, 12, 12, 12, 70, 57, 59, 12, 12, 28, 25, + 29, 12, 12, 12, 62, 29, 67, 29, 12, 58, 29, 73, 12, 12, 12, 25, + 25, 62, 12, 12, 57, 25, 25, 25, 70, 25, 59, 61, 12, 59, 29, 12, + 25, 29, 12, 25, 12, 12, 12, 78, 26, 12, 12, 24, 12, 12, 12, 24, + 12, 12, 12, 22, 79, 79, 80, 81, 10, 10, 82, 83, 84, 85, 10, 10, + 10, 86, 10, 10, 10, 10, 10, 87, 0, 88, 89, 0, 90, 8, 91, 71, + 8, 8, 91, 71, 84, 84, 84, 84, 17, 71, 26, 12, 12, 20, 11, 23, + 10, 78, 92, 93, 12, 12, 23, 12, 10, 11, 23, 26, 12, 12, 24, 12, + 94, 10, 10, 10, 10, 26, 12, 12, 10, 20, 10, 10, 10, 10, 71, 12, + 10, 71, 12, 12, 10, 10, 8, 8, 8, 8, 8, 12, 12, 12, 23, 10, + 10, 10, 10, 24, 10, 23, 10, 10, 10, 26, 10, 10, 10, 10, 26, 24, + 10, 10, 20, 10, 26, 12, 12, 12, 12, 12, 12, 10, 12, 24, 71, 28, + 29, 12, 24, 10, 12, 12, 12, 28, 71, 12, 12, 12, 10, 10, 17, 10, + 10, 12, 12, 12, 10, 10, 10, 12, 95, 11, 10, 10, 11, 12, 62, 29, + 11, 23, 12, 24, 12, 12, 96, 11, 12, 12, 13, 12, 12, 12, 12, 71, + 24, 10, 10, 10, 12, 13, 71, 12, 12, 12, 12, 13, 97, 25, 25, 98, + 12, 12, 11, 12, 58, 58, 28, 12, 12, 65, 10, 12, 12, 12, 99, 12, + 12, 10, 12, 12, 12, 59, 12, 12, 12, 62, 25, 29, 12, 28, 25, 25, + 28, 62, 29, 59, 12, 61, 12, 12, 12, 12, 60, 57, 65, 65, 12, 12, + 28, 12, 12, 59, 70, 66, 59, 62, 12, 61, 59, 61, 12, 12, 12, 100, + 34, 34, 101, 34, 40, 40, 40, 102, 40, 40, 40, 103, 104, 105, 10, 106, + 107, 71, 108, 12, 40, 40, 40, 109, 30, 5, 6, 7, 5, 110, 10, 71, + 0, 0, 111, 112, 92, 12, 12, 12, 10, 10, 10, 11, 113, 8, 8, 8, + 12, 62, 57, 12, 34, 34, 34, 114, 31, 33, 34, 25, 34, 34, 115, 52, + 34, 33, 34, 34, 34, 34, 116, 10, 35, 35, 35, 35, 35, 35, 35, 117, + 12, 12, 25, 25, 25, 57, 12, 12, 28, 57, 65, 12, 12, 28, 25, 60, + 25, 59, 12, 12, 28, 12, 12, 12, 12, 62, 25, 57, 12, 12, 62, 59, + 29, 70, 12, 12, 28, 25, 57, 12, 12, 62, 25, 59, 28, 25, 72, 28, + 70, 12, 12, 12, 62, 29, 12, 67, 28, 25, 57, 73, 12, 12, 28, 61, + 25, 67, 12, 12, 62, 67, 25, 12, 12, 12, 12, 65, 0, 12, 12, 12, + 12, 28, 29, 12, 118, 0, 119, 25, 57, 60, 25, 12, 12, 12, 62, 29, + 120, 121, 12, 12, 12, 92, 12, 12, 12, 12, 92, 12, 13, 12, 12, 122, + 8, 8, 8, 8, 25, 57, 28, 25, 60, 25, 25, 25, 25, 115, 34, 34, + 123, 40, 40, 40, 10, 10, 10, 71, 8, 8, 124, 11, 10, 24, 10, 10, + 10, 11, 12, 12, 10, 10, 12, 12, 10, 10, 10, 26, 10, 10, 11, 12, + 12, 12, 12, 125, +}; + +static RE_UINT8 re_bidi_class_stage_5[] = { + 11, 11, 11, 11, 11, 8, 7, 8, 9, 7, 11, 11, 7, 7, 7, 8, + 9, 10, 10, 4, 4, 4, 10, 10, 10, 10, 10, 3, 6, 3, 6, 6, + 2, 2, 2, 2, 2, 2, 6, 10, 10, 10, 10, 10, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 10, 11, 11, 7, 11, 11, + 6, 10, 4, 4, 10, 10, 0, 10, 10, 11, 10, 10, 4, 4, 2, 2, + 10, 0, 10, 10, 10, 2, 0, 10, 0, 10, 10, 0, 0, 0, 10, 10, + 0, 10, 10, 10, 12, 12, 12, 12, 10, 10, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 12, 12, 12, 0, 0, 0, 10, 10, 4, 1, 12, 12, 12, + 12, 12, 1, 12, 1, 12, 12, 1, 1, 1, 1, 1, 5, 5, 5, 5, + 5, 5, 10, 10, 13, 4, 4, 13, 6, 13, 10, 10, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 12, 5, 5, 4, 5, 5, 13, 13, 13, + 12, 13, 13, 13, 13, 13, 12, 12, 12, 5, 10, 12, 12, 13, 13, 12, + 12, 10, 12, 12, 12, 12, 13, 13, 2, 2, 13, 13, 13, 12, 13, 13, + 1, 1, 1, 12, 1, 1, 10, 10, 10, 10, 1, 1, 1, 1, 12, 12, + 12, 12, 1, 1, 12, 12, 12, 0, 0, 0, 12, 0, 12, 0, 0, 0, + 0, 12, 12, 12, 0, 12, 0, 0, 0, 0, 12, 12, 0, 0, 4, 4, + 0, 0, 0, 4, 0, 12, 12, 0, 12, 0, 0, 12, 12, 12, 0, 12, + 0, 4, 0, 0, 10, 4, 10, 0, 12, 0, 12, 12, 10, 10, 10, 0, + 12, 0, 12, 0, 0, 12, 0, 12, 0, 12, 10, 10, 9, 0, 0, 0, + 10, 10, 10, 12, 12, 12, 11, 0, 0, 10, 0, 10, 9, 9, 9, 9, + 9, 9, 9, 11, 11, 11, 0, 1, 9, 7, 16, 17, 18, 14, 15, 6, + 4, 4, 4, 4, 4, 10, 10, 10, 6, 10, 10, 10, 10, 10, 10, 9, + 11, 11, 19, 20, 21, 22, 11, 11, 2, 0, 0, 0, 2, 2, 3, 3, + 0, 10, 0, 0, 0, 0, 4, 0, 10, 10, 3, 4, 9, 10, 10, 10, + 0, 12, 12, 10, 12, 12, 12, 10, 12, 12, 10, 10, 4, 4, 0, 0, + 0, 1, 12, 1, 1, 3, 1, 1, 13, 13, 10, 10, 13, 10, 13, 13, + 6, 10, 6, 0, 10, 6, 10, 10, 10, 10, 10, 4, 10, 10, 3, 3, + 10, 4, 4, 10, 13, 13, 13, 11, 10, 4, 4, 0, 11, 10, 10, 10, + 10, 10, 11, 11, 12, 2, 2, 2, 1, 1, 1, 10, 12, 12, 12, 1, + 1, 10, 10, 10, 5, 5, 5, 1, 0, 0, 0, 11, 11, 11, 11, 12, + 10, 10, 12, 12, 12, 10, 0, 0, 0, 0, 2, 2, 10, 10, 13, 13, + 2, 2, 2, 10, 0, 0, 11, 11, +}; + +/* Bidi_Class: 3484 bytes. */ + +RE_UINT32 re_get_bidi_class(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_bidi_class_stage_1[f] << 5; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_bidi_class_stage_2[pos + f] << 3; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_bidi_class_stage_3[pos + f] << 2; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_bidi_class_stage_4[pos + f] << 2; + value = re_bidi_class_stage_5[pos + code]; + + return value; +} + +/* Canonical_Combining_Class. */ + +static RE_UINT8 re_canonical_combining_class_stage_1[] = { + 0, 1, 2, 2, 2, 3, 2, 4, 5, 2, 2, 6, 2, 7, 8, 9, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_canonical_combining_class_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, + 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, + 22, 23, 0, 0, 0, 24, 0, 0, 25, 26, 27, 28, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 31, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_canonical_combining_class_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 0, + 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, 0, 0, 0, 17, + 18, 19, 20, 0, 0, 0, 0, 21, 0, 22, 23, 0, 0, 22, 24, 0, + 0, 22, 24, 0, 0, 22, 24, 0, 0, 22, 24, 0, 0, 0, 24, 0, + 0, 0, 25, 0, 0, 22, 24, 0, 0, 0, 24, 0, 0, 0, 26, 0, + 0, 27, 28, 0, 0, 29, 30, 0, 31, 32, 0, 33, 34, 0, 35, 0, + 0, 36, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 39, 39, 0, 0, 0, 0, 40, 0, + 0, 0, 0, 0, 0, 41, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, + 43, 0, 0, 44, 0, 45, 0, 0, 0, 46, 47, 48, 0, 49, 0, 50, + 0, 51, 0, 0, 0, 0, 52, 53, 0, 0, 0, 0, 0, 0, 54, 55, + 0, 0, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 59, 0, 0, 0, 60, + 0, 61, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 64, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, + 66, 0, 0, 0, 0, 0, 47, 67, 0, 68, 69, 0, 0, 70, 71, 0, + 0, 0, 0, 0, 0, 72, 73, 74, 0, 0, 0, 0, 0, 0, 0, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 75, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, + 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 79, 0, 0, 0, 0, + 80, 81, 0, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 66, 59, 0, 83, 0, 0, 84, 85, 0, 70, 0, 0, 86, 0, + 0, 87, 0, 0, 0, 0, 0, 88, 0, 22, 24, 89, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 59, 91, 0, + 0, 59, 0, 0, 0, 92, 0, 0, 0, 93, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 94, 0, 95, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99, 0, 0, + 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_canonical_combining_class_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 31, 0, + 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 43, 36, 44, 45, + 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 47, 1, 1, 48, 48, 49, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 50, 0, 51, 21, 43, 52, 53, 21, 35, 1, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 55, 56, 57, 0, 0, + 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 55, 0, 58, 0, 0, + 0, 0, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, + 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, + 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 65, 66, 0, 0, 0, 0, 0, 67, 68, 69, 70, + 71, 72, 0, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 77, 0, 0, + 0, 0, 0, 0, 59, 0, 0, 78, 0, 0, 79, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 19, 81, 0, + 77, 0, 0, 0, 0, 48, 1, 82, 0, 0, 0, 0, 1, 52, 15, 41, + 0, 0, 0, 0, 0, 54, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, + 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 83, 0, 0, 0, 0, 0, + 0, 84, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, + 0, 0, 0, 0, 85, 9, 12, 4, 86, 8, 87, 76, 0, 57, 49, 0, + 21, 1, 21, 88, 89, 1, 1, 1, 1, 1, 1, 1, 1, 49, 0, 90, + 0, 0, 0, 0, 91, 1, 92, 57, 78, 93, 94, 4, 57, 0, 0, 0, + 0, 0, 0, 19, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 96, 97, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 19, 0, 1, 1, 49, + 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 49, 0, 0, 0, + 0, 59, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 49, 0, 0, 0, + 0, 0, 51, 64, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, 0, + 0, 0, 0, 0, 74, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 99, 100, 57, 38, 78, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 1, 14, 4, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, + 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 85, 0, + 0, 0, 0, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 95, + 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, + 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 77, 0, 0, + 77, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 0, + 0, 0, 106, 0, 0, 0, 0, 0, 0, 38, 1, 57, 1, 57, 0, 0, + 107, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 87, 0, 0, 0, 0, 0, 0, 1, 85, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 108, 0, 109, 110, 111, 112, 0, 51, 4, + 113, 48, 23, 0, 0, 0, 0, 0, 0, 0, 38, 49, 0, 0, 0, 0, + 38, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 113, 0, 0, +}; + +static RE_UINT8 re_canonical_combining_class_stage_5[] = { + 0, 0, 0, 0, 50, 50, 50, 50, 50, 51, 45, 45, 45, 45, 51, 43, + 45, 45, 45, 45, 45, 41, 41, 45, 45, 45, 45, 41, 41, 45, 45, 45, + 1, 1, 1, 1, 1, 45, 45, 45, 45, 50, 50, 50, 50, 54, 50, 45, + 45, 45, 50, 50, 50, 45, 45, 0, 50, 50, 50, 45, 45, 45, 45, 50, + 51, 45, 45, 50, 52, 53, 53, 52, 53, 53, 52, 50, 0, 0, 0, 50, + 0, 45, 50, 50, 50, 50, 45, 50, 50, 50, 46, 45, 50, 50, 45, 45, + 50, 46, 49, 50, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 15, + 16, 17, 0, 18, 0, 19, 20, 0, 50, 45, 0, 13, 25, 26, 27, 0, + 0, 0, 0, 22, 23, 24, 25, 26, 27, 28, 29, 50, 50, 45, 45, 50, + 45, 50, 50, 45, 30, 0, 0, 0, 0, 0, 50, 50, 50, 0, 0, 50, + 50, 0, 45, 50, 50, 45, 0, 0, 0, 31, 0, 0, 50, 45, 50, 50, + 45, 45, 50, 45, 45, 50, 45, 50, 45, 50, 50, 0, 50, 50, 0, 50, + 0, 50, 50, 50, 50, 50, 0, 0, 0, 45, 45, 45, 0, 0, 0, 45, + 50, 45, 45, 45, 22, 23, 24, 50, 2, 0, 0, 0, 0, 4, 0, 0, + 0, 50, 45, 50, 50, 0, 0, 0, 0, 32, 33, 0, 0, 0, 4, 0, + 34, 34, 4, 0, 35, 35, 35, 35, 36, 36, 0, 0, 37, 37, 37, 37, + 45, 45, 0, 0, 0, 45, 0, 45, 0, 43, 0, 0, 0, 38, 39, 0, + 40, 0, 0, 0, 0, 0, 39, 39, 39, 39, 0, 0, 39, 0, 50, 50, + 4, 0, 50, 50, 0, 0, 45, 0, 0, 0, 0, 2, 0, 4, 4, 0, + 0, 45, 0, 0, 4, 0, 0, 0, 0, 50, 0, 0, 0, 49, 0, 0, + 0, 46, 50, 45, 45, 0, 0, 0, 50, 0, 0, 45, 0, 0, 4, 4, + 0, 0, 2, 0, 50, 50, 50, 0, 50, 0, 1, 1, 1, 0, 0, 0, + 50, 53, 42, 45, 41, 50, 50, 50, 52, 45, 50, 45, 50, 50, 1, 1, + 1, 1, 1, 50, 0, 1, 1, 50, 45, 50, 1, 1, 0, 0, 0, 4, + 0, 0, 44, 49, 51, 46, 47, 47, 0, 3, 3, 0, 50, 0, 50, 50, + 45, 0, 0, 50, 0, 0, 21, 0, 0, 45, 0, 50, 50, 1, 45, 0, + 0, 50, 45, 0, 0, 4, 2, 0, 0, 2, 4, 0, 0, 0, 4, 2, + 0, 0, 1, 0, 0, 43, 43, 1, 1, 1, 0, 0, 0, 48, 43, 43, + 43, 43, 43, 0, 45, 45, 45, 0, +}; + +/* Canonical_Combining_Class: 2112 bytes. */ + +RE_UINT32 re_get_canonical_combining_class(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 13; + code = ch ^ (f << 13); + pos = (RE_UINT32)re_canonical_combining_class_stage_1[f] << 4; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_canonical_combining_class_stage_2[pos + f] << 4; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_canonical_combining_class_stage_3[pos + f] << 3; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_canonical_combining_class_stage_4[pos + f] << 2; + value = re_canonical_combining_class_stage_5[pos + code]; + + return value; +} + +/* Decomposition_Type. */ + +static RE_UINT8 re_decomposition_type_stage_1[] = { + 0, 1, 2, 2, 2, 3, 4, 5, 6, 2, 2, 2, 2, 2, 7, 8, + 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_decomposition_type_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 16, 7, 17, 18, 19, + 20, 21, 22, 23, 24, 7, 7, 7, 7, 7, 25, 7, 26, 27, 28, 29, + 30, 31, 32, 33, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 34, 35, 7, 7, 7, 36, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 38, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 37, 39, 40, 41, 42, 43, 44, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 45, 46, 7, 47, 48, 49, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 50, 7, 7, 51, 52, 53, 54, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 55, 7, + 7, 56, 57, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 37, 37, 58, 7, 7, 7, 7, 7, +}; + +static RE_UINT8 re_decomposition_type_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 3, 5, + 6, 7, 8, 9, 10, 11, 8, 12, 0, 0, 13, 14, 15, 16, 17, 18, + 6, 19, 20, 21, 0, 0, 0, 0, 0, 0, 0, 22, 0, 23, 24, 0, + 0, 0, 0, 0, 25, 0, 0, 26, 27, 14, 28, 14, 29, 30, 0, 31, + 32, 33, 0, 33, 0, 32, 0, 34, 0, 0, 0, 0, 35, 36, 37, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 0, 0, 0, 0, 41, 0, 0, 0, 0, 42, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, + 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, + 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 57, 0, 58, 0, 0, + 59, 0, 0, 0, 60, 61, 33, 62, 63, 60, 61, 33, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, + 66, 67, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 70, 71, 72, 73, 74, 75, 0, 76, 73, 73, 0, 0, 0, 0, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 77, 6, 6, 6, 6, 6, 78, + 6, 79, 6, 6, 79, 80, 6, 81, 6, 6, 6, 82, 83, 84, 6, 85, + 86, 87, 88, 89, 90, 91, 0, 92, 93, 94, 95, 0, 0, 0, 0, 0, + 96, 97, 98, 99, 100, 101, 102, 102, 103, 104, 105, 0, 106, 0, 0, 0, + 107, 0, 108, 109, 110, 0, 111, 112, 112, 0, 113, 0, 0, 0, 114, 0, + 0, 0, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 116, 117, 102, 102, 102, 118, 116, 116, 119, 0, + 120, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 122, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, 0, 0, 57, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 126, 0, 0, + 127, 0, 0, 128, 129, 130, 131, 132, 0, 133, 129, 130, 131, 132, 0, 134, + 0, 0, 0, 135, 102, 102, 102, 102, 136, 137, 0, 0, 0, 0, 0, 0, + 102, 136, 102, 102, 138, 139, 116, 140, 116, 116, 116, 116, 141, 116, 116, 140, + 142, 142, 142, 142, 142, 143, 102, 144, 142, 142, 142, 142, 142, 142, 102, 145, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 147, 0, 0, 0, 0, 0, 0, 0, 148, + 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 21, 0, 0, 0, 0, 0, + 81, 150, 151, 6, 6, 6, 81, 6, 6, 6, 6, 6, 6, 78, 0, 0, + 152, 153, 154, 155, 156, 157, 158, 158, 159, 158, 160, 161, 0, 162, 163, 164, + 165, 165, 165, 165, 165, 165, 166, 167, 167, 168, 169, 169, 169, 170, 171, 172, + 165, 173, 174, 175, 0, 176, 177, 178, 179, 180, 167, 181, 182, 0, 0, 183, + 0, 184, 0, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 194, 195, 196, + 197, 198, 198, 198, 198, 198, 199, 200, 200, 200, 200, 201, 202, 203, 204, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 205, 206, 0, 0, 0, 0, 0, + 0, 0, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 207, 209, 0, 0, 0, 0, 210, 14, 0, 0, 0, + 211, 211, 211, 211, 211, 212, 211, 211, 211, 213, 214, 215, 216, 211, 211, 211, + 217, 218, 211, 219, 220, 221, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 222, 211, 211, 211, 211, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 223, 211, 211, 211, + 216, 211, 224, 225, 226, 227, 228, 229, 230, 231, 232, 231, 0, 0, 0, 0, + 233, 102, 234, 142, 142, 0, 235, 0, 0, 236, 0, 0, 0, 0, 0, 0, + 237, 142, 142, 238, 239, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_decomposition_type_stage_4[] = { + 0, 0, 0, 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 8, 8, + 10, 11, 10, 12, 10, 11, 10, 9, 8, 8, 8, 8, 13, 8, 8, 8, + 8, 12, 8, 8, 14, 8, 10, 15, 16, 8, 17, 8, 12, 8, 8, 8, + 8, 8, 8, 15, 12, 0, 0, 18, 19, 0, 0, 0, 0, 20, 20, 21, + 8, 8, 8, 22, 8, 13, 8, 8, 23, 12, 8, 8, 8, 8, 8, 13, + 0, 13, 8, 8, 8, 0, 0, 0, 24, 24, 25, 0, 0, 0, 20, 5, + 24, 25, 0, 0, 9, 19, 0, 0, 0, 19, 26, 27, 0, 21, 11, 22, + 0, 0, 13, 8, 0, 0, 13, 11, 28, 29, 0, 0, 30, 5, 31, 0, + 9, 18, 0, 11, 0, 0, 32, 0, 0, 13, 0, 0, 33, 0, 0, 0, + 8, 13, 13, 8, 13, 8, 13, 8, 8, 12, 12, 0, 0, 3, 0, 0, + 13, 11, 0, 0, 0, 34, 35, 0, 36, 0, 0, 0, 18, 0, 0, 0, + 32, 19, 0, 0, 0, 0, 8, 8, 0, 0, 18, 19, 0, 0, 0, 9, + 18, 27, 0, 0, 0, 0, 10, 27, 0, 0, 37, 19, 0, 0, 0, 12, + 0, 19, 0, 0, 0, 0, 13, 19, 0, 0, 19, 0, 19, 18, 22, 0, + 0, 0, 27, 11, 3, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 1, + 18, 0, 0, 32, 27, 18, 0, 19, 18, 38, 17, 0, 32, 0, 0, 0, + 0, 27, 0, 0, 0, 0, 0, 25, 0, 27, 36, 36, 27, 0, 0, 0, + 0, 0, 18, 32, 9, 0, 0, 0, 0, 0, 0, 39, 24, 24, 39, 24, + 24, 24, 24, 40, 24, 24, 24, 24, 41, 42, 43, 0, 0, 0, 25, 0, + 0, 0, 44, 24, 8, 8, 45, 0, 8, 8, 12, 0, 8, 12, 8, 12, + 8, 8, 46, 46, 8, 8, 8, 12, 8, 22, 8, 47, 21, 22, 8, 8, + 8, 13, 8, 10, 13, 22, 8, 48, 49, 50, 30, 0, 51, 3, 0, 0, + 0, 30, 0, 52, 3, 53, 0, 54, 0, 3, 5, 0, 0, 3, 0, 3, + 55, 24, 24, 24, 42, 42, 42, 43, 42, 42, 42, 56, 0, 0, 35, 0, + 57, 34, 58, 59, 59, 60, 61, 62, 63, 64, 65, 66, 66, 67, 68, 59, + 69, 61, 62, 0, 70, 70, 70, 70, 20, 20, 20, 20, 0, 0, 71, 0, + 0, 0, 13, 0, 0, 0, 0, 27, 0, 0, 0, 10, 0, 19, 32, 19, + 0, 36, 0, 72, 35, 0, 0, 0, 32, 37, 32, 0, 36, 0, 0, 10, + 12, 12, 12, 0, 0, 0, 0, 8, 8, 0, 13, 12, 0, 0, 33, 0, + 73, 73, 73, 73, 73, 20, 20, 20, 20, 74, 73, 73, 73, 73, 75, 0, + 0, 0, 0, 35, 0, 30, 0, 0, 0, 0, 0, 19, 0, 0, 0, 76, + 0, 0, 0, 44, 0, 0, 0, 3, 20, 5, 0, 0, 77, 0, 0, 0, + 0, 26, 30, 0, 0, 0, 0, 36, 36, 36, 36, 36, 36, 46, 32, 0, + 9, 22, 33, 12, 0, 19, 3, 78, 0, 37, 11, 79, 34, 20, 20, 20, + 20, 20, 20, 30, 4, 24, 24, 24, 20, 73, 0, 0, 80, 73, 73, 73, + 73, 73, 73, 75, 20, 20, 20, 81, 81, 81, 81, 81, 81, 81, 20, 20, + 82, 81, 81, 81, 20, 20, 20, 83, 0, 0, 0, 55, 25, 0, 0, 0, + 0, 0, 55, 0, 0, 0, 0, 24, 36, 10, 8, 11, 36, 33, 13, 8, + 20, 30, 0, 0, 3, 20, 0, 46, 59, 59, 84, 8, 8, 11, 8, 36, + 9, 22, 8, 15, 85, 86, 86, 86, 86, 86, 86, 86, 86, 85, 85, 85, + 87, 85, 86, 86, 88, 0, 0, 0, 89, 90, 91, 92, 85, 87, 86, 85, + 85, 85, 93, 87, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 95, + 95, 96, 97, 97, 97, 97, 97, 97, 97, 97, 97, 98, 99, 99, 99, 99, + 99, 100, 94, 94, 101, 95, 95, 95, 95, 95, 95, 102, 97, 99, 99, 103, + 104, 97, 105, 106, 107, 105, 108, 105, 104, 96, 95, 105, 96, 109, 110, 97, + 111, 106, 112, 105, 95, 106, 113, 95, 96, 106, 0, 0, 94, 94, 94, 114, + 115, 115, 116, 0, 115, 115, 115, 115, 115, 117, 118, 20, 119, 120, 120, 120, + 120, 119, 120, 0, 121, 122, 123, 123, 124, 91, 125, 126, 90, 125, 127, 127, + 127, 127, 126, 91, 125, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, + 125, 126, 91, 128, 129, 130, 130, 130, 130, 130, 130, 130, 131, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 133, 134, 132, 134, 132, 134, 132, 134, 135, + 130, 136, 132, 133, 0, 0, 27, 19, 0, 0, 18, 0, 0, 0, 0, 13, + 0, 0, 18, 36, 8, 19, 0, 0, 0, 0, 18, 8, 59, 59, 59, 59, + 59, 137, 59, 59, 59, 59, 59, 137, 138, 139, 61, 137, 59, 59, 66, 61, + 59, 61, 59, 59, 59, 66, 140, 61, 59, 137, 59, 137, 59, 59, 66, 140, + 59, 141, 142, 59, 137, 59, 59, 59, 59, 62, 59, 59, 59, 59, 59, 142, + 139, 143, 61, 59, 140, 59, 144, 0, 138, 145, 144, 61, 139, 143, 144, 144, + 139, 143, 140, 59, 140, 59, 61, 141, 59, 59, 66, 59, 59, 59, 59, 0, + 61, 61, 66, 59, 20, 20, 30, 0, 20, 20, 146, 75, 0, 0, 4, 0, + 147, 0, 0, 0, 148, 0, 0, 0, 81, 81, 148, 0, 20, 20, 35, 0, + 149, 0, 0, 0, +}; + +static RE_UINT8 re_decomposition_type_stage_5[] = { + 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 10, 0, 0, 0, 0, 2, + 0, 0, 10, 10, 2, 2, 0, 0, 2, 10, 10, 0, 17, 17, 17, 0, + 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 1, 1, 1, 2, + 2, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, + 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, + 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 0, 0, 2, 0, 0, + 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 2, 10, 10, 10, 0, + 10, 10, 0, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, + 0, 0, 0, 10, 1, 1, 2, 1, 0, 1, 0, 1, 1, 2, 1, 2, + 1, 1, 2, 0, 1, 1, 2, 2, 2, 2, 2, 4, 0, 4, 0, 0, + 0, 0, 0, 4, 2, 0, 2, 2, 2, 0, 2, 0, 10, 10, 0, 0, + 11, 0, 0, 0, 2, 2, 3, 2, 0, 2, 3, 3, 3, 3, 3, 3, + 0, 3, 2, 0, 0, 3, 3, 3, 3, 3, 0, 0, 10, 2, 10, 0, + 3, 0, 1, 0, 3, 0, 1, 1, 3, 3, 0, 3, 3, 2, 2, 2, + 2, 3, 0, 2, 3, 0, 0, 0, 17, 17, 17, 17, 0, 17, 0, 0, + 2, 2, 0, 2, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, 9, 0, + 11, 10, 0, 0, 13, 0, 0, 0, 2, 0, 1, 12, 0, 0, 1, 12, + 16, 9, 9, 9, 16, 16, 16, 16, 2, 16, 16, 16, 2, 2, 2, 16, + 3, 3, 1, 1, 8, 7, 8, 7, 5, 6, 8, 7, 8, 7, 5, 6, + 8, 7, 0, 0, 0, 0, 0, 8, 7, 5, 6, 8, 7, 8, 7, 8, + 7, 8, 8, 7, 5, 8, 7, 5, 8, 8, 8, 8, 7, 7, 7, 7, + 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 8, 8, 8, 8, 7, 7, 7, 7, 5, 5, 5, 7, 8, 0, 0, + 5, 7, 5, 5, 7, 5, 7, 7, 5, 5, 7, 7, 5, 5, 7, 5, + 5, 7, 7, 5, 7, 7, 5, 7, 5, 5, 5, 7, 0, 0, 5, 5, + 5, 7, 7, 7, 5, 7, 5, 7, 8, 0, 0, 0, 12, 12, 12, 12, + 12, 12, 0, 0, 12, 0, 0, 12, 12, 2, 2, 2, 15, 15, 15, 0, + 15, 15, 15, 15, 8, 6, 8, 0, 8, 0, 8, 6, 8, 6, 8, 6, + 8, 8, 7, 8, 7, 8, 7, 5, 6, 8, 7, 8, 6, 8, 7, 5, + 7, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 0, 0, 0, + 13, 13, 13, 0, 3, 0, 3, 3, 0, 0, 3, 0, 0, 3, 3, 0, + 3, 3, 3, 0, 3, 0, 3, 0, 0, 0, 3, 3, 3, 0, 0, 3, + 0, 3, 0, 3, 0, 0, 0, 3, 2, 2, 2, 9, 16, 0, 0, 0, + 16, 16, 16, 0, 9, 9, 0, 0, +}; + +/* Decomposition_Type: 2964 bytes. */ + +RE_UINT32 re_get_decomposition_type(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 13; + code = ch ^ (f << 13); + pos = (RE_UINT32)re_decomposition_type_stage_1[f] << 5; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_decomposition_type_stage_2[pos + f] << 4; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_decomposition_type_stage_3[pos + f] << 2; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_decomposition_type_stage_4[pos + f] << 2; + value = re_decomposition_type_stage_5[pos + code]; + + return value; +} + +/* East_Asian_Width. */ + +static RE_UINT8 re_east_asian_width_stage_1[] = { + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 10, 10, 10, 12, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 13, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 13, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 14, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 15, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 15, +}; + +static RE_UINT8 re_east_asian_width_stage_2[] = { + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 7, 8, 9, 10, 11, 12, 13, 14, 5, 15, 5, 16, 5, 5, 17, 18, + 19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 24, 5, 5, 5, 5, 25, 5, 5, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 26, 5, 5, 5, 5, 5, 5, 5, 5, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 22, 22, 5, 5, 5, 28, 29, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 30, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 31, 32, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 33, + 5, 34, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 35, +}; + +static RE_UINT8 re_east_asian_width_stage_3[] = { + 0, 0, 1, 1, 1, 1, 1, 2, 0, 0, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 11, 0, 0, 0, 0, 0, 15, 16, 0, 0, + 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 17, 18, 0, 0, + 19, 19, 19, 19, 19, 19, 19, 0, 0, 20, 21, 20, 21, 0, 0, 0, + 9, 19, 19, 19, 19, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 22, 22, 22, 22, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 23, 24, 25, 0, 0, 0, 26, 27, 0, 28, 0, 0, 0, 0, 0, + 29, 30, 31, 0, 0, 32, 33, 34, 35, 34, 0, 36, 0, 37, 38, 0, + 39, 40, 41, 42, 43, 44, 45, 0, 46, 47, 48, 49, 0, 0, 0, 0, + 0, 44, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 51, 19, + 19, 19, 19, 19, 33, 19, 19, 52, 19, 53, 21, 54, 55, 56, 57, 0, + 58, 59, 0, 0, 60, 0, 61, 0, 0, 62, 0, 62, 63, 19, 64, 19, + 0, 0, 0, 65, 0, 38, 0, 66, 0, 0, 0, 0, 0, 0, 67, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 22, 70, 22, 22, 22, 22, 22, 71, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 72, 0, 73, + 74, 22, 22, 75, 76, 22, 22, 22, 22, 77, 22, 22, 22, 22, 22, 22, + 78, 22, 79, 76, 22, 22, 22, 22, 75, 22, 22, 80, 22, 22, 71, 22, + 22, 75, 22, 22, 81, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 75, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 0, 0, 0, 0, + 22, 22, 22, 22, 22, 22, 22, 22, 82, 22, 22, 22, 83, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 22, 82, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 71, 0, 0, 0, 0, 0, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 84, 0, 22, 22, 85, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 87, 88, 88, 88, 88, 88, 89, 90, 90, 90, 90, 91, 92, 93, 94, 65, + 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 19, 97, 19, 19, 19, 34, 19, 19, 96, 0, 0, 0, 0, 0, 0, + 98, 22, 22, 80, 99, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 79, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 97, +}; + +static RE_UINT8 re_east_asian_width_stage_4[] = { + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 7, 0, 10, 0, 0, 11, 12, 11, 13, 14, 10, 9, 14, + 8, 12, 9, 5, 15, 0, 0, 0, 16, 0, 12, 0, 0, 13, 12, 0, + 17, 0, 11, 12, 9, 11, 7, 15, 13, 0, 0, 0, 0, 0, 0, 10, + 5, 5, 5, 11, 0, 18, 17, 15, 11, 0, 7, 16, 7, 7, 7, 7, + 17, 7, 7, 7, 19, 7, 14, 0, 20, 20, 20, 20, 18, 9, 14, 14, + 9, 7, 0, 0, 8, 15, 12, 10, 0, 11, 0, 12, 17, 11, 0, 0, + 0, 0, 21, 11, 12, 15, 15, 0, 12, 10, 0, 0, 22, 10, 12, 0, + 12, 11, 12, 9, 7, 7, 7, 0, 7, 7, 14, 0, 0, 0, 15, 0, + 0, 0, 14, 0, 10, 11, 0, 0, 0, 12, 0, 0, 8, 12, 18, 12, + 15, 15, 10, 17, 18, 16, 7, 5, 0, 7, 0, 14, 0, 0, 11, 11, + 10, 0, 0, 0, 14, 7, 13, 13, 13, 13, 0, 0, 0, 15, 15, 0, + 0, 15, 0, 0, 0, 0, 0, 12, 0, 0, 23, 0, 7, 7, 19, 7, + 7, 0, 0, 0, 13, 14, 0, 0, 13, 13, 0, 14, 14, 13, 18, 13, + 14, 0, 0, 0, 13, 14, 0, 12, 0, 22, 15, 13, 0, 14, 0, 5, + 5, 0, 0, 0, 19, 19, 9, 19, 0, 0, 0, 13, 0, 7, 7, 19, + 19, 0, 7, 7, 0, 0, 0, 15, 0, 13, 7, 7, 0, 24, 1, 25, + 0, 26, 0, 0, 0, 17, 14, 0, 20, 20, 27, 20, 20, 0, 0, 0, + 20, 28, 0, 0, 20, 20, 20, 0, 29, 20, 20, 20, 20, 20, 20, 30, + 31, 20, 20, 20, 20, 30, 31, 20, 0, 31, 20, 20, 20, 20, 20, 28, + 20, 20, 30, 0, 20, 20, 7, 7, 20, 20, 20, 32, 20, 30, 0, 0, + 20, 20, 28, 0, 30, 20, 20, 20, 20, 30, 20, 0, 33, 34, 34, 34, + 34, 34, 34, 34, 35, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, + 38, 36, 38, 36, 38, 36, 38, 39, 34, 40, 36, 37, 28, 0, 0, 0, + 7, 7, 9, 0, 7, 7, 7, 14, 30, 0, 0, 0, 20, 20, 32, 0, +}; + +static RE_UINT8 re_east_asian_width_stage_5[] = { + 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, 0, 1, 5, 5, + 1, 5, 5, 1, 1, 0, 1, 0, 5, 1, 1, 5, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, + 3, 3, 3, 3, 0, 2, 0, 0, 0, 1, 1, 0, 0, 3, 3, 0, + 0, 0, 5, 5, 5, 5, 0, 0, 0, 5, 5, 0, 3, 3, 0, 3, + 3, 3, 0, 0, 4, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, + 3, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 4, 4, 4, 0, +}; + +/* East_Asian_Width: 1668 bytes. */ + +RE_UINT32 re_get_east_asian_width(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_east_asian_width_stage_1[f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_east_asian_width_stage_2[pos + f] << 4; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_east_asian_width_stage_3[pos + f] << 2; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_east_asian_width_stage_4[pos + f] << 2; + value = re_east_asian_width_stage_5[pos + code]; + + return value; +} + +/* Joining_Group. */ + +static RE_UINT8 re_joining_group_stage_1[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, +}; + +static RE_UINT8 re_joining_group_stage_2[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_joining_group_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_joining_group_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 0, 21, 0, 22, + 0, 0, 23, 24, 25, 26, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, + 0, 0, 0, 0, 34, 35, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 37, 38, 39, 40, 41, 42, 0, 0, +}; + +static RE_UINT8 re_joining_group_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 3, 3, 43, 3, 45, 3, + 4, 41, 4, 4, 13, 13, 13, 6, 6, 31, 31, 35, 35, 33, 33, 39, + 39, 1, 1, 11, 11, 55, 55, 55, 0, 9, 29, 19, 22, 24, 26, 16, + 43, 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 29, + 0, 3, 3, 3, 0, 3, 43, 43, 45, 4, 4, 4, 4, 4, 4, 4, + 4, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 31, 31, 31, 31, 31, 31, 31, 31, 31, 35, 35, 35, 33, 33, 39, + 1, 9, 9, 9, 9, 9, 9, 29, 29, 11, 38, 11, 19, 19, 19, 11, + 11, 11, 11, 11, 11, 22, 22, 22, 22, 26, 26, 26, 26, 56, 21, 13, + 41, 17, 17, 14, 43, 43, 43, 43, 43, 43, 43, 43, 55, 47, 55, 43, + 45, 45, 46, 46, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 6, 31, + 0, 0, 35, 33, 1, 0, 0, 21, 2, 0, 5, 12, 12, 7, 7, 15, + 44, 50, 18, 42, 42, 48, 49, 20, 23, 25, 27, 36, 10, 8, 28, 32, + 34, 30, 7, 37, 40, 5, 12, 7, 0, 0, 0, 0, 0, 51, 52, 53, + 4, 4, 4, 4, 4, 4, 4, 13, 13, 6, 6, 31, 35, 1, 1, 1, + 9, 9, 11, 11, 11, 24, 24, 26, 26, 26, 22, 31, 31, 35, 13, 13, + 35, 31, 13, 3, 3, 55, 55, 45, 43, 43, 54, 54, 13, 35, 35, 19, + 4, 4, 13, 39, 9, 29, 22, 24, 45, 45, 31, 43, 57, 0, 6, 33, + 11, 58, 31, 1, 19, 0, 0, 0, 59, 61, 61, 65, 65, 62, 0, 83, + 0, 85, 85, 0, 0, 66, 80, 84, 68, 68, 68, 69, 63, 81, 70, 71, + 77, 60, 60, 73, 73, 76, 74, 74, 74, 75, 0, 0, 78, 0, 0, 0, + 0, 0, 0, 72, 64, 79, 82, 67, +}; + +/* Joining_Group: 586 bytes. */ + +RE_UINT32 re_get_joining_group(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_joining_group_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_joining_group_stage_2[pos + f] << 4; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_joining_group_stage_3[pos + f] << 4; + f = code >> 3; + code ^= f << 3; + pos = (RE_UINT32)re_joining_group_stage_4[pos + f] << 3; + value = re_joining_group_stage_5[pos + code]; + + return value; +} + +/* Joining_Type. */ + +static RE_UINT8 re_joining_type_stage_1[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 6, + 7, 8, 4, 4, 4, 4, 9, 4, 4, 4, 4, 10, 4, 11, 12, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 13, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +}; + +static RE_UINT8 re_joining_type_stage_2[] = { + 0, 1, 0, 0, 0, 0, 2, 0, 0, 3, 0, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 28, 29, + 30, 31, 32, 0, 33, 34, 35, 36, 37, 38, 0, 39, 0, 0, 0, 0, + 40, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 43, 44, 0, 0, 0, 0, + 45, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 0, 0, + 49, 50, 51, 52, 53, 54, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 57, 43, 0, 58, + 0, 0, 0, 59, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 62, 63, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, + 65, 66, 67, 68, 69, 70, 71, 0, 0, 72, 0, 73, 74, 75, 76, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 77, 78, 0, 0, 0, 0, 0, 0, 0, 0, 79, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 81, 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 87, 0, 88, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_joining_type_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 2, 5, 6, 0, 0, 0, 0, 7, 8, 9, 10, 2, 11, 12, + 13, 14, 15, 15, 16, 17, 18, 19, 20, 21, 22, 2, 23, 24, 25, 26, + 0, 0, 27, 28, 29, 15, 30, 31, 0, 32, 33, 0, 34, 35, 0, 0, + 0, 0, 36, 37, 0, 0, 38, 2, 39, 0, 0, 40, 41, 42, 43, 0, + 44, 0, 0, 45, 46, 0, 43, 0, 47, 0, 0, 45, 48, 44, 0, 49, + 47, 0, 0, 45, 50, 0, 43, 0, 44, 0, 0, 51, 46, 52, 43, 0, + 53, 0, 0, 0, 54, 0, 0, 0, 28, 0, 0, 55, 56, 57, 43, 0, + 44, 0, 0, 51, 58, 0, 43, 0, 44, 0, 0, 0, 46, 0, 43, 0, + 0, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 61, 62, 0, 0, 0, + 0, 0, 0, 63, 64, 0, 0, 0, 0, 65, 0, 66, 0, 0, 0, 67, + 68, 69, 2, 70, 52, 0, 0, 0, 0, 0, 71, 72, 0, 73, 28, 74, + 75, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 0, 0, + 0, 76, 0, 76, 0, 43, 0, 43, 0, 0, 0, 77, 78, 79, 0, 0, + 80, 0, 15, 15, 15, 15, 15, 81, 82, 15, 83, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 0, 0, 0, 0, 0, 86, 0, 0, 0, 87, 88, 89, + 0, 0, 0, 90, 0, 0, 0, 0, 91, 0, 0, 92, 53, 0, 93, 91, + 94, 0, 95, 0, 0, 0, 96, 94, 0, 0, 97, 98, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 99, 100, 101, 0, 0, 0, 0, 2, 2, 2, 102, + 103, 0, 104, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 2, 2, 28, + 0, 0, 0, 0, 0, 0, 20, 94, 0, 0, 0, 0, 0, 0, 0, 20, + 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 106, 0, 0, 0, 0, 0, + 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 108, + 0, 55, 0, 0, 0, 0, 0, 94, 109, 0, 57, 0, 15, 15, 15, 110, + 0, 0, 0, 0, 111, 0, 2, 94, 0, 0, 112, 0, 113, 94, 0, 0, + 39, 0, 0, 114, 0, 0, 115, 0, 0, 0, 116, 117, 118, 0, 0, 45, + 0, 0, 0, 119, 44, 0, 120, 52, 0, 0, 0, 0, 0, 0, 121, 0, + 0, 122, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 124, + 125, 0, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 127, 128, 129, 0, + 130, 131, 132, 0, 0, 0, 0, 0, 44, 0, 0, 133, 134, 0, 0, 20, + 94, 0, 0, 135, 0, 0, 0, 0, 39, 0, 136, 137, 0, 0, 0, 138, + 94, 0, 0, 139, 140, 0, 0, 0, 0, 0, 20, 141, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 142, 0, 94, 0, 0, 45, 28, 0, 143, 137, + 0, 0, 0, 144, 145, 0, 0, 0, 0, 0, 0, 146, 28, 120, 0, 0, + 0, 0, 0, 147, 28, 0, 0, 0, 0, 0, 148, 149, 0, 0, 0, 0, + 0, 71, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, + 0, 0, 0, 134, 0, 0, 0, 0, 20, 39, 0, 0, 0, 0, 0, 0, + 0, 151, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 38, + 153, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 0, 0, 0, + 2, 2, 2, 154, 2, 2, 70, 115, 111, 93, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 134, 0, 0, 44, 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_joining_type_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 3, 2, 4, 0, + 5, 2, 2, 2, 2, 2, 2, 6, 7, 6, 0, 0, 2, 2, 8, 9, + 10, 11, 12, 13, 14, 15, 15, 15, 16, 15, 17, 2, 0, 0, 0, 18, + 19, 20, 15, 15, 15, 15, 21, 21, 21, 21, 22, 15, 15, 15, 15, 15, + 23, 21, 21, 24, 25, 26, 2, 27, 2, 27, 28, 29, 0, 0, 18, 30, + 0, 0, 0, 3, 31, 32, 22, 33, 15, 15, 34, 23, 2, 2, 8, 35, + 15, 15, 32, 15, 15, 15, 13, 36, 24, 36, 22, 15, 0, 37, 2, 2, + 9, 0, 0, 0, 0, 0, 18, 15, 15, 15, 38, 2, 2, 0, 39, 0, + 0, 37, 6, 2, 2, 5, 5, 4, 36, 25, 12, 15, 15, 40, 5, 0, + 15, 15, 25, 41, 42, 43, 0, 0, 3, 2, 2, 2, 8, 0, 0, 0, + 0, 0, 44, 9, 5, 2, 9, 1, 5, 2, 0, 0, 37, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 9, 5, 9, 0, 1, 7, 0, 0, 0, + 7, 3, 27, 4, 4, 1, 0, 0, 5, 6, 9, 1, 0, 0, 0, 27, + 0, 44, 0, 0, 44, 0, 0, 0, 9, 0, 0, 1, 0, 0, 0, 37, + 9, 37, 28, 4, 0, 7, 0, 0, 0, 44, 0, 4, 0, 0, 44, 0, + 37, 45, 0, 0, 1, 2, 8, 0, 0, 3, 2, 8, 1, 2, 6, 9, + 0, 0, 2, 4, 0, 0, 4, 0, 0, 46, 1, 0, 5, 2, 2, 8, + 2, 28, 0, 5, 2, 2, 5, 2, 2, 2, 2, 9, 0, 0, 0, 5, + 28, 2, 7, 7, 0, 0, 4, 37, 5, 9, 0, 0, 44, 7, 0, 1, + 37, 9, 0, 0, 0, 6, 2, 4, 0, 44, 5, 2, 2, 0, 0, 1, + 0, 47, 48, 4, 15, 15, 0, 0, 0, 47, 15, 15, 15, 15, 49, 0, + 8, 3, 9, 0, 44, 0, 5, 0, 0, 3, 27, 0, 0, 44, 2, 8, + 45, 5, 2, 9, 3, 2, 2, 27, 2, 2, 2, 8, 2, 0, 0, 0, + 0, 28, 8, 9, 0, 0, 3, 2, 4, 0, 0, 0, 37, 4, 6, 4, + 0, 44, 4, 46, 0, 0, 0, 2, 2, 37, 0, 0, 8, 2, 2, 2, + 28, 2, 9, 1, 0, 9, 4, 0, 2, 4, 0, 2, 0, 0, 3, 50, + 0, 0, 37, 8, 2, 9, 37, 2, 0, 0, 37, 4, 0, 0, 7, 0, + 8, 2, 2, 4, 44, 44, 3, 0, 51, 0, 0, 0, 0, 9, 0, 0, + 0, 37, 2, 4, 0, 3, 2, 2, 3, 37, 4, 9, 0, 1, 0, 0, + 0, 0, 5, 8, 7, 7, 0, 0, 3, 0, 0, 9, 28, 27, 9, 37, + 0, 0, 0, 4, 0, 1, 9, 1, 0, 0, 0, 44, 0, 0, 5, 0, + 0, 37, 8, 0, 5, 7, 0, 2, 0, 0, 8, 3, 15, 52, 53, 54, + 14, 55, 15, 12, 56, 57, 47, 13, 24, 22, 12, 58, 56, 0, 0, 0, + 0, 0, 20, 59, 0, 0, 2, 2, 2, 8, 0, 0, 3, 8, 7, 1, + 0, 3, 2, 5, 2, 9, 0, 0, 3, 0, 0, 0, 0, 37, 2, 8, + 0, 0, 37, 9, 4, 28, 0, 0, 3, 2, 8, 0, 0, 37, 2, 9, + 3, 2, 45, 3, 28, 0, 0, 0, 37, 4, 0, 6, 3, 2, 8, 46, + 0, 0, 3, 1, 2, 6, 0, 0, 37, 6, 2, 0, 0, 0, 0, 7, + 0, 3, 4, 0, 8, 5, 2, 0, 2, 8, 3, 2, +}; + +static RE_UINT8 re_joining_type_stage_5[] = { + 0, 0, 0, 0, 0, 5, 0, 0, 5, 5, 5, 5, 0, 0, 0, 5, + 5, 5, 0, 0, 0, 5, 5, 5, 5, 5, 0, 5, 0, 5, 5, 0, + 5, 5, 5, 0, 5, 0, 0, 0, 2, 0, 3, 3, 3, 3, 2, 3, + 2, 3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 1, 2, 2, 2, 3, 2, 2, 5, 0, 0, 2, 2, 5, 3, 3, 3, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 2, 2, 3, + 2, 3, 2, 3, 2, 2, 3, 3, 0, 3, 5, 5, 5, 0, 0, 5, + 5, 0, 5, 5, 5, 5, 3, 3, 2, 0, 0, 2, 3, 5, 2, 2, + 2, 3, 3, 3, 2, 2, 3, 2, 3, 2, 3, 2, 0, 3, 2, 2, + 3, 2, 2, 2, 0, 0, 5, 5, 2, 2, 2, 5, 0, 0, 1, 0, + 3, 2, 0, 0, 3, 0, 3, 2, 2, 3, 3, 2, 2, 0, 0, 0, + 0, 0, 5, 0, 5, 0, 5, 0, 0, 5, 0, 5, 0, 0, 0, 2, + 0, 0, 1, 5, 2, 5, 2, 0, 0, 1, 5, 5, 2, 2, 4, 0, + 2, 3, 0, 3, 0, 3, 3, 0, 0, 4, 3, 3, 2, 2, 2, 4, + 2, 3, 0, 0, 3, 5, 5, 0, 3, 2, 3, 3, 3, 2, 2, 0, +}; + +/* Joining_Type: 2292 bytes. */ + +RE_UINT32 re_get_joining_type(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_joining_type_stage_1[f] << 5; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_joining_type_stage_2[pos + f] << 3; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_joining_type_stage_3[pos + f] << 2; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_joining_type_stage_4[pos + f] << 2; + value = re_joining_type_stage_5[pos + code]; + + return value; +} + +/* Line_Break. */ + +static RE_UINT8 re_line_break_stage_1[] = { + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 10, 17, 10, 10, 10, 10, 18, 10, 19, 20, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 23, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, +}; + +static RE_UINT8 re_line_break_stage_2[] = { + 0, 1, 2, 2, 2, 3, 4, 5, 2, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 2, 2, 2, 2, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 2, 51, 2, 2, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 2, 2, 2, 70, 2, 2, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 87, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 88, 79, 79, 79, 79, 79, 79, 79, 79, 89, 2, 2, 90, 91, 2, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 101, + 102, 103, 104, 105, 106, 107, 101, 102, 103, 104, 105, 106, 107, 101, 102, 103, + 104, 105, 106, 107, 101, 102, 103, 104, 105, 106, 107, 101, 102, 103, 104, 105, + 106, 107, 101, 102, 103, 104, 105, 106, 107, 101, 102, 103, 104, 105, 106, 107, + 101, 102, 103, 104, 105, 106, 107, 101, 102, 103, 104, 105, 106, 107, 101, 102, + 103, 104, 105, 106, 107, 101, 102, 103, 104, 105, 106, 107, 101, 102, 103, 108, + 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 79, 79, 79, 79, 111, 112, 2, 2, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 110, 123, 124, 125, 2, 126, 127, 110, 2, 2, 128, 110, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 110, 110, 139, 110, 110, 110, + 140, 141, 142, 143, 144, 145, 146, 110, 110, 147, 110, 148, 149, 150, 151, 110, + 110, 152, 110, 110, 110, 153, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 2, 2, 2, 2, 2, 2, 2, 154, 155, 2, 156, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 2, 2, 2, 2, 157, 158, 159, 2, 160, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 2, 2, 2, 161, 162, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 2, 2, 2, 2, 163, 164, 165, 166, 110, 110, 110, 110, 110, 110, 167, 168, + 169, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 170, 171, 110, 110, 110, 110, 110, 110, + 2, 172, 173, 174, 175, 110, 176, 110, 177, 178, 179, 2, 2, 180, 2, 181, + 2, 2, 2, 2, 182, 183, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 2, 184, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 185, 186, 110, 110, + 187, 188, 189, 190, 191, 110, 79, 192, 79, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 204, + 205, 110, 206, 207, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, +}; + +static RE_UINT16 re_line_break_stage_3[] = { + 0, 1, 2, 3, 4, 5, 4, 6, 7, 1, 8, 9, 4, 10, 4, 10, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 11, 12, 4, 4, + 1, 1, 1, 1, 13, 14, 15, 16, 17, 4, 18, 4, 4, 4, 4, 4, + 19, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20, 4, 21, 20, 4, + 22, 23, 1, 24, 25, 26, 27, 28, 29, 30, 4, 4, 31, 1, 32, 33, + 4, 4, 4, 4, 4, 34, 35, 36, 37, 38, 4, 1, 39, 4, 4, 4, + 4, 4, 40, 41, 36, 4, 31, 42, 4, 43, 44, 45, 4, 46, 47, 47, + 47, 47, 4, 48, 47, 47, 49, 1, 50, 4, 4, 51, 1, 52, 53, 4, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 55, 56, 63, 64, 65, 66, 67, + 68, 18, 56, 69, 70, 71, 60, 72, 73, 55, 56, 69, 74, 75, 60, 76, + 77, 78, 79, 80, 81, 82, 66, 83, 84, 85, 56, 86, 87, 88, 60, 89, + 90, 85, 56, 91, 87, 92, 60, 93, 90, 85, 4, 94, 95, 96, 60, 97, + 98, 99, 4, 100, 101, 102, 66, 103, 104, 105, 105, 106, 107, 108, 47, 47, + 109, 110, 111, 112, 113, 114, 47, 47, 115, 116, 36, 117, 118, 4, 119, 120, + 121, 122, 1, 123, 124, 125, 47, 47, 105, 105, 105, 105, 126, 105, 105, 105, + 105, 127, 4, 4, 128, 4, 4, 4, 129, 129, 129, 129, 129, 129, 130, 130, + 130, 130, 131, 132, 132, 132, 132, 132, 4, 4, 4, 4, 133, 134, 4, 4, + 133, 4, 4, 135, 136, 137, 4, 4, 4, 136, 4, 4, 4, 138, 139, 119, + 4, 140, 4, 4, 4, 4, 4, 141, 142, 4, 4, 4, 4, 4, 4, 4, + 142, 143, 4, 4, 4, 4, 144, 145, 146, 147, 4, 148, 4, 149, 146, 150, + 105, 105, 105, 105, 105, 151, 152, 140, 153, 152, 4, 4, 4, 4, 4, 76, + 4, 4, 154, 4, 4, 4, 4, 155, 4, 45, 156, 156, 157, 105, 158, 159, + 105, 105, 160, 105, 161, 162, 4, 4, 4, 163, 105, 105, 105, 164, 105, 165, + 152, 152, 158, 166, 47, 47, 47, 47, 167, 4, 4, 168, 169, 170, 171, 172, + 173, 4, 174, 36, 4, 4, 40, 175, 4, 4, 168, 176, 177, 36, 4, 178, + 47, 47, 47, 47, 76, 179, 180, 181, 4, 4, 4, 4, 1, 1, 1, 182, + 4, 141, 4, 4, 141, 183, 4, 184, 4, 4, 4, 185, 185, 186, 4, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 119, 197, 198, 199, 1, 1, 200, + 201, 202, 203, 4, 4, 204, 205, 206, 207, 206, 4, 4, 4, 208, 4, 4, + 209, 210, 211, 212, 213, 214, 215, 4, 216, 217, 218, 219, 4, 4, 220, 4, + 221, 222, 223, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 224, + 4, 4, 225, 47, 226, 47, 227, 227, 227, 227, 227, 227, 227, 227, 227, 228, + 227, 227, 227, 227, 205, 227, 227, 229, 227, 230, 231, 232, 233, 234, 235, 4, + 236, 237, 4, 238, 239, 4, 240, 241, 4, 242, 4, 243, 244, 245, 246, 247, + 248, 4, 4, 4, 4, 249, 250, 251, 227, 252, 4, 4, 253, 4, 254, 4, + 255, 256, 4, 4, 4, 221, 4, 257, 4, 4, 4, 4, 4, 258, 4, 259, + 4, 260, 4, 261, 56, 262, 263, 47, 4, 4, 45, 4, 4, 45, 4, 4, + 4, 4, 4, 4, 4, 4, 264, 265, 4, 4, 128, 4, 4, 4, 266, 267, + 4, 225, 268, 268, 268, 268, 1, 1, 269, 270, 271, 272, 273, 47, 47, 47, + 274, 275, 274, 274, 274, 274, 274, 276, 274, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 274, 277, 47, 278, 279, 280, 281, 282, 283, 274, 284, 274, + 285, 286, 287, 274, 284, 274, 285, 288, 289, 274, 290, 291, 274, 274, 274, 274, + 292, 274, 274, 293, 274, 274, 276, 294, 274, 292, 274, 274, 295, 274, 274, 274, + 274, 274, 274, 274, 274, 274, 274, 292, 274, 274, 274, 274, 4, 4, 4, 4, + 274, 296, 274, 274, 274, 274, 274, 274, 297, 274, 274, 274, 298, 4, 4, 178, + 299, 4, 300, 47, 4, 4, 264, 301, 4, 302, 4, 4, 4, 4, 4, 303, + 4, 4, 184, 76, 47, 47, 47, 304, 305, 4, 306, 307, 4, 4, 4, 308, + 309, 4, 4, 168, 310, 152, 1, 311, 36, 4, 312, 4, 313, 314, 129, 315, + 50, 4, 4, 316, 317, 318, 105, 319, 4, 4, 320, 321, 322, 323, 105, 105, + 105, 105, 105, 105, 324, 325, 31, 326, 327, 328, 268, 4, 4, 4, 155, 4, + 4, 4, 4, 4, 4, 4, 329, 152, 330, 331, 332, 333, 332, 334, 332, 330, + 331, 332, 333, 332, 334, 332, 330, 331, 332, 333, 332, 334, 332, 330, 331, 332, + 333, 332, 334, 332, 330, 331, 332, 333, 332, 334, 332, 330, 331, 332, 333, 332, + 334, 332, 330, 331, 332, 333, 332, 334, 332, 330, 331, 332, 333, 332, 334, 332, + 333, 332, 335, 130, 336, 132, 132, 337, 338, 338, 338, 338, 338, 338, 338, 338, + 47, 47, 47, 47, 47, 47, 47, 47, 225, 339, 340, 341, 342, 4, 4, 4, + 4, 4, 4, 4, 262, 343, 4, 4, 4, 4, 4, 344, 47, 4, 4, 4, + 4, 345, 4, 4, 76, 47, 47, 346, 1, 347, 1, 348, 349, 350, 351, 185, + 4, 4, 4, 4, 4, 4, 4, 352, 353, 354, 274, 355, 274, 356, 357, 358, + 4, 359, 4, 45, 360, 361, 362, 363, 364, 4, 137, 365, 184, 184, 47, 47, + 4, 4, 4, 4, 4, 4, 4, 226, 366, 4, 4, 367, 4, 4, 4, 4, + 119, 368, 71, 47, 47, 4, 4, 369, 4, 119, 4, 4, 4, 71, 33, 368, + 4, 4, 370, 4, 226, 4, 4, 371, 4, 372, 4, 4, 373, 374, 47, 47, + 4, 184, 152, 47, 47, 47, 47, 47, 4, 4, 76, 4, 4, 4, 375, 47, + 4, 4, 4, 225, 4, 155, 76, 47, 376, 4, 4, 377, 4, 378, 4, 4, + 4, 45, 304, 47, 47, 47, 4, 379, 4, 380, 4, 381, 47, 47, 47, 47, + 4, 4, 4, 382, 4, 345, 4, 4, 383, 384, 4, 385, 76, 386, 4, 4, + 4, 4, 47, 47, 4, 4, 387, 388, 4, 4, 4, 389, 4, 260, 4, 390, + 4, 391, 392, 47, 47, 47, 47, 47, 4, 4, 4, 4, 145, 47, 47, 47, + 4, 4, 4, 393, 4, 4, 4, 394, 47, 47, 47, 47, 47, 47, 4, 45, + 173, 4, 4, 395, 396, 345, 397, 398, 173, 4, 4, 399, 400, 4, 145, 152, + 173, 4, 313, 401, 402, 4, 4, 403, 173, 4, 4, 316, 404, 405, 20, 48, + 4, 18, 406, 407, 47, 47, 47, 47, 408, 37, 409, 4, 4, 264, 410, 152, + 411, 55, 56, 69, 74, 412, 413, 414, 4, 4, 4, 1, 415, 152, 47, 47, + 4, 4, 264, 416, 417, 418, 47, 47, 4, 4, 4, 1, 419, 152, 47, 47, + 4, 4, 31, 420, 152, 47, 47, 47, 105, 421, 160, 422, 47, 47, 47, 47, + 47, 47, 4, 4, 4, 4, 36, 423, 47, 47, 47, 47, 4, 4, 4, 145, + 4, 140, 47, 47, 47, 47, 47, 47, 4, 4, 4, 4, 4, 4, 45, 424, + 4, 4, 4, 4, 370, 47, 47, 47, 4, 4, 4, 4, 4, 425, 4, 4, + 426, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 427, + 4, 4, 45, 47, 47, 47, 47, 47, 4, 4, 4, 4, 428, 4, 4, 4, + 4, 4, 4, 4, 225, 47, 47, 47, 4, 4, 4, 145, 4, 45, 429, 47, + 47, 47, 47, 47, 47, 4, 184, 430, 4, 4, 4, 431, 432, 433, 18, 434, + 4, 47, 47, 47, 47, 47, 47, 47, 4, 4, 4, 4, 48, 435, 1, 166, + 398, 173, 47, 47, 47, 47, 47, 47, 436, 47, 47, 47, 47, 47, 47, 47, + 4, 4, 4, 4, 4, 4, 226, 119, 145, 437, 438, 47, 47, 47, 47, 47, + 4, 4, 4, 4, 4, 4, 4, 155, 4, 4, 21, 4, 4, 4, 439, 1, + 440, 4, 441, 4, 4, 4, 145, 47, 4, 4, 4, 4, 442, 47, 47, 47, + 4, 4, 4, 4, 4, 225, 4, 262, 4, 4, 4, 4, 4, 185, 4, 4, + 4, 146, 443, 444, 445, 4, 4, 4, 446, 447, 4, 448, 449, 85, 4, 4, + 4, 4, 260, 4, 4, 4, 4, 4, 4, 4, 4, 4, 450, 451, 451, 451, + 1, 1, 1, 452, 1, 1, 453, 454, 455, 456, 23, 47, 47, 47, 47, 47, + 4, 4, 4, 4, 457, 321, 47, 47, 445, 4, 458, 459, 460, 461, 462, 463, + 464, 368, 465, 368, 47, 47, 47, 262, 274, 274, 278, 274, 274, 274, 274, 274, + 274, 276, 292, 291, 291, 291, 274, 277, 466, 227, 467, 227, 227, 227, 468, 227, + 227, 469, 47, 47, 47, 47, 470, 471, 472, 274, 274, 293, 473, 436, 47, 47, + 274, 474, 274, 475, 274, 274, 274, 476, 274, 274, 477, 478, 274, 274, 274, 274, + 479, 480, 481, 482, 483, 274, 274, 275, 274, 274, 484, 274, 274, 485, 274, 486, + 274, 274, 274, 274, 274, 4, 4, 487, 274, 274, 274, 274, 274, 488, 297, 276, + 4, 4, 4, 4, 4, 4, 4, 370, 4, 4, 4, 4, 4, 48, 47, 47, + 368, 4, 4, 4, 76, 140, 4, 4, 76, 4, 184, 47, 47, 47, 47, 47, + 47, 473, 47, 47, 47, 47, 47, 47, 489, 47, 47, 47, 488, 47, 47, 47, + 274, 274, 274, 274, 274, 274, 274, 290, 490, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 47, +}; + +static RE_UINT8 re_line_break_stage_4[] = { + 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 12, 12, 12, 12, 13, 14, 15, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 16, 17, 14, + 14, 14, 14, 14, 14, 16, 18, 19, 0, 0, 20, 0, 0, 0, 0, 0, + 21, 22, 23, 24, 25, 26, 27, 14, 22, 28, 29, 28, 28, 26, 28, 30, + 14, 14, 14, 24, 14, 14, 14, 14, 14, 14, 14, 24, 31, 28, 31, 14, + 25, 14, 14, 14, 28, 28, 24, 32, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 34, 34, 34, 35, 0, 0, 0, 0, 0, 0, + 14, 14, 14, 14, 36, 14, 14, 37, 36, 36, 14, 14, 14, 38, 38, 14, + 14, 39, 14, 14, 14, 14, 14, 14, 14, 19, 0, 0, 0, 14, 14, 14, + 39, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 38, 39, 14, 14, 14, + 14, 14, 14, 14, 40, 41, 39, 9, 42, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 43, 19, 44, 0, 45, 36, 36, 36, 36, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 36, 36, + 46, 48, 38, 36, 36, 36, 36, 36, 14, 14, 14, 14, 49, 50, 13, 14, + 0, 0, 0, 0, 0, 51, 52, 53, 14, 14, 14, 14, 14, 19, 0, 0, + 12, 12, 12, 12, 12, 54, 55, 14, 44, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 56, 0, 0, 0, 44, 19, 0, 0, 44, 19, 44, 0, 0, 14, + 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 39, + 19, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 52, 39, 14, + 14, 14, 14, 0, 0, 0, 0, 0, 44, 36, 36, 36, 36, 36, 36, 36, + 0, 0, 14, 14, 57, 38, 36, 36, 14, 14, 14, 0, 0, 19, 0, 0, + 0, 0, 19, 0, 19, 0, 0, 36, 14, 14, 14, 14, 14, 14, 14, 38, + 14, 14, 14, 14, 19, 0, 36, 38, 36, 36, 36, 36, 36, 36, 36, 36, + 14, 14, 38, 36, 36, 36, 36, 36, 36, 42, 0, 0, 0, 0, 0, 0, + 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 44, 0, + 19, 0, 0, 0, 14, 14, 14, 14, 14, 0, 58, 12, 12, 12, 12, 12, + 19, 0, 39, 14, 14, 14, 38, 39, 38, 39, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 38, 14, 14, 14, 38, 38, 36, 14, 14, 36, 44, 0, + 0, 0, 52, 42, 52, 42, 0, 38, 36, 36, 36, 42, 36, 36, 14, 39, + 14, 0, 36, 12, 12, 12, 12, 12, 14, 50, 14, 14, 49, 9, 36, 36, + 42, 0, 39, 14, 14, 38, 36, 39, 38, 14, 39, 38, 14, 36, 52, 0, + 0, 52, 36, 42, 52, 42, 0, 36, 42, 36, 36, 36, 39, 14, 38, 38, + 36, 36, 36, 12, 12, 12, 12, 12, 0, 14, 19, 36, 36, 36, 36, 36, + 42, 0, 39, 14, 14, 14, 14, 39, 38, 14, 39, 14, 14, 36, 44, 0, + 0, 0, 0, 42, 0, 42, 0, 36, 38, 36, 36, 36, 36, 36, 36, 36, + 9, 36, 36, 36, 39, 36, 36, 36, 42, 0, 39, 14, 14, 14, 38, 39, + 0, 0, 52, 42, 52, 42, 0, 36, 36, 36, 36, 0, 36, 36, 14, 39, + 14, 14, 14, 14, 36, 36, 36, 36, 36, 44, 39, 14, 14, 38, 36, 14, + 38, 14, 14, 36, 39, 38, 38, 14, 36, 39, 38, 36, 14, 38, 36, 14, + 14, 14, 14, 14, 14, 36, 36, 0, 0, 52, 36, 0, 52, 0, 0, 36, + 38, 36, 36, 42, 36, 36, 36, 36, 14, 14, 14, 14, 9, 38, 36, 36, + 0, 0, 39, 14, 14, 14, 38, 14, 38, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 36, 39, 0, 0, 0, 52, 0, 52, 0, 0, 36, + 36, 36, 42, 52, 14, 38, 36, 36, 36, 36, 36, 36, 14, 14, 14, 14, + 42, 0, 39, 14, 14, 14, 38, 14, 14, 14, 39, 14, 14, 36, 44, 0, + 36, 36, 42, 52, 36, 36, 36, 38, 39, 38, 36, 36, 36, 36, 36, 36, + 14, 14, 14, 14, 14, 38, 39, 0, 0, 0, 52, 0, 52, 0, 0, 38, + 36, 36, 36, 42, 36, 36, 36, 39, 14, 14, 14, 36, 59, 14, 14, 14, + 36, 0, 39, 14, 14, 14, 14, 14, 14, 14, 14, 38, 36, 14, 14, 14, + 14, 39, 14, 14, 14, 14, 39, 36, 14, 14, 14, 38, 36, 52, 36, 42, + 0, 0, 52, 52, 0, 0, 0, 0, 36, 0, 38, 36, 36, 36, 36, 36, + 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 62, 36, 63, 61, 61, 61, 61, 61, 61, 61, 64, + 12, 12, 12, 12, 12, 58, 36, 36, 60, 62, 62, 60, 62, 62, 60, 36, + 36, 36, 61, 61, 60, 61, 61, 61, 60, 61, 60, 60, 36, 61, 60, 61, + 61, 61, 61, 61, 61, 60, 61, 36, 61, 61, 62, 62, 61, 61, 61, 36, + 12, 12, 12, 12, 12, 36, 61, 61, 32, 65, 29, 65, 66, 67, 68, 53, + 53, 69, 56, 14, 0, 14, 14, 14, 14, 14, 43, 19, 19, 70, 70, 0, + 14, 14, 14, 14, 39, 14, 14, 14, 14, 14, 14, 14, 14, 14, 38, 36, + 42, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 14, 14, 19, 0, + 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 58, + 14, 14, 14, 44, 14, 14, 38, 14, 65, 71, 14, 14, 72, 73, 36, 36, + 12, 12, 12, 12, 12, 58, 14, 14, 12, 12, 12, 12, 12, 61, 61, 61, + 14, 14, 14, 39, 36, 36, 39, 36, 74, 74, 74, 74, 74, 74, 74, 74, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 14, 14, 14, 14, 38, 14, 14, 36, + 14, 14, 14, 38, 38, 14, 14, 36, 38, 14, 14, 36, 14, 14, 14, 38, + 38, 14, 14, 36, 14, 14, 14, 14, 14, 14, 14, 38, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 38, 42, 0, 27, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 36, 36, 36, 14, 14, 14, 36, 14, 14, 14, 36, + 77, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 16, 78, 36, + 14, 14, 14, 14, 14, 27, 58, 14, 14, 14, 14, 14, 38, 36, 36, 36, + 14, 14, 14, 14, 14, 14, 38, 14, 14, 0, 52, 36, 36, 36, 36, 36, + 14, 0, 1, 41, 36, 36, 36, 36, 14, 0, 36, 36, 36, 36, 36, 36, + 38, 0, 36, 36, 36, 36, 36, 36, 61, 61, 58, 79, 77, 80, 61, 36, + 12, 12, 12, 12, 12, 36, 36, 36, 14, 53, 58, 29, 53, 19, 0, 73, + 14, 14, 14, 14, 19, 38, 36, 36, 14, 14, 14, 36, 36, 36, 36, 36, + 0, 0, 0, 0, 0, 0, 36, 36, 38, 36, 53, 12, 12, 12, 12, 12, + 61, 61, 61, 61, 61, 61, 61, 36, 61, 61, 62, 36, 36, 36, 36, 36, + 61, 61, 61, 61, 61, 61, 36, 36, 61, 61, 61, 61, 61, 36, 36, 36, + 12, 12, 12, 12, 12, 62, 36, 61, 14, 14, 14, 19, 0, 0, 36, 14, + 61, 61, 61, 61, 61, 61, 61, 62, 61, 61, 61, 61, 61, 61, 62, 42, + 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 44, 14, 14, 14, 14, 14, + 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 44, 14, 14, 14, 36, 36, + 12, 12, 12, 12, 12, 58, 27, 58, 77, 14, 14, 14, 14, 19, 0, 0, + 0, 0, 14, 14, 14, 14, 38, 36, 0, 44, 14, 14, 14, 14, 14, 14, + 19, 0, 0, 0, 0, 0, 0, 14, 0, 0, 36, 36, 36, 36, 14, 14, + 0, 0, 0, 0, 36, 81, 58, 58, 12, 12, 12, 12, 12, 36, 39, 14, + 14, 14, 14, 14, 14, 14, 14, 58, 0, 44, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 44, 14, 19, 14, 14, 0, 44, 38, 0, 36, 36, 36, + 0, 0, 0, 36, 36, 36, 0, 0, 14, 14, 14, 14, 39, 39, 39, 39, + 14, 14, 14, 14, 14, 14, 14, 36, 14, 14, 38, 14, 14, 14, 14, 14, + 14, 14, 36, 14, 14, 14, 39, 14, 36, 14, 38, 14, 14, 14, 32, 38, + 58, 58, 58, 82, 58, 83, 0, 0, 82, 58, 84, 25, 85, 86, 85, 86, + 28, 14, 87, 88, 89, 0, 0, 33, 50, 50, 50, 50, 7, 90, 91, 14, + 14, 14, 92, 93, 91, 14, 14, 14, 14, 14, 14, 77, 58, 58, 27, 58, + 94, 14, 38, 0, 0, 0, 0, 0, 14, 36, 25, 14, 14, 14, 16, 95, + 24, 28, 25, 14, 14, 14, 16, 78, 23, 23, 23, 6, 23, 23, 23, 23, + 23, 23, 23, 22, 23, 6, 23, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 52, 36, 36, 36, 36, 36, 36, 36, 14, 49, 24, 14, 49, 14, 14, 14, + 14, 24, 14, 96, 14, 14, 14, 14, 24, 25, 14, 14, 14, 24, 14, 14, + 14, 14, 28, 14, 14, 24, 14, 25, 28, 28, 28, 28, 28, 28, 14, 14, + 28, 28, 28, 28, 28, 14, 14, 14, 14, 14, 14, 14, 24, 14, 36, 36, + 14, 25, 25, 14, 14, 14, 14, 14, 25, 28, 14, 24, 25, 24, 14, 24, + 24, 23, 24, 14, 14, 25, 24, 28, 25, 24, 24, 24, 28, 28, 25, 25, + 14, 14, 28, 28, 14, 14, 28, 14, 14, 14, 14, 14, 25, 14, 25, 14, + 14, 25, 14, 14, 14, 14, 14, 14, 28, 14, 28, 28, 14, 28, 14, 28, + 14, 28, 14, 28, 14, 14, 14, 14, 14, 14, 24, 14, 24, 14, 14, 14, + 14, 14, 24, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 24, + 14, 14, 14, 14, 14, 14, 14, 97, 14, 14, 14, 14, 70, 70, 14, 14, + 14, 25, 14, 14, 14, 98, 14, 14, 14, 14, 14, 14, 16, 99, 14, 14, + 98, 98, 14, 14, 14, 38, 36, 36, 14, 14, 14, 38, 36, 36, 36, 36, + 14, 14, 14, 14, 14, 38, 36, 36, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 25, 28, 28, 25, 14, 14, 14, 14, 14, + 14, 28, 28, 14, 14, 14, 14, 14, 28, 24, 28, 28, 28, 14, 14, 14, + 14, 28, 14, 28, 14, 14, 28, 14, 28, 14, 14, 28, 25, 24, 14, 28, + 28, 14, 14, 14, 14, 14, 14, 14, 14, 28, 28, 14, 14, 14, 14, 24, + 98, 98, 24, 25, 24, 14, 14, 28, 14, 14, 98, 28, 100, 98, 98, 98, + 14, 14, 14, 14, 101, 98, 14, 14, 25, 25, 14, 14, 14, 14, 14, 14, + 28, 24, 28, 24, 102, 25, 28, 24, 14, 14, 14, 14, 14, 14, 14, 101, + 14, 14, 14, 14, 14, 14, 14, 28, 14, 14, 14, 14, 14, 14, 101, 98, + 98, 98, 98, 98, 102, 28, 103, 101, 98, 103, 102, 28, 98, 28, 102, 103, + 98, 24, 14, 14, 28, 102, 28, 28, 103, 98, 98, 103, 98, 102, 103, 98, + 98, 98, 100, 14, 98, 98, 98, 14, 14, 14, 14, 24, 14, 7, 85, 85, + 5, 53, 14, 14, 70, 70, 70, 70, 70, 70, 70, 28, 28, 28, 28, 28, + 28, 28, 14, 14, 14, 14, 14, 14, 14, 14, 16, 99, 14, 14, 14, 14, + 14, 14, 14, 70, 70, 70, 70, 70, 14, 16, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 99, 14, 14, 14, 14, 14, 14, 14, 14, 14, 70, 14, + 14, 14, 24, 28, 28, 14, 14, 14, 14, 14, 36, 14, 14, 14, 14, 14, + 14, 14, 14, 36, 14, 14, 14, 14, 14, 14, 14, 14, 14, 36, 39, 14, + 14, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 19, 0, 14, 36, 36, 105, 58, 77, 106, + 14, 14, 14, 14, 36, 36, 36, 39, 41, 36, 36, 36, 36, 36, 36, 42, + 14, 14, 14, 38, 14, 14, 14, 38, 85, 85, 85, 85, 85, 85, 85, 58, + 58, 58, 58, 27, 107, 14, 85, 14, 85, 70, 70, 70, 70, 58, 58, 56, + 58, 27, 77, 14, 14, 108, 58, 77, 58, 109, 36, 36, 36, 36, 36, 36, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 110, 98, 98, + 98, 98, 36, 36, 36, 36, 36, 36, 98, 98, 98, 36, 36, 36, 36, 36, + 98, 98, 98, 98, 98, 98, 36, 36, 18, 111, 112, 98, 70, 70, 70, 70, + 70, 98, 70, 70, 70, 70, 113, 114, 98, 98, 98, 98, 98, 0, 0, 0, + 98, 98, 115, 98, 98, 112, 116, 98, 117, 118, 118, 118, 118, 98, 98, 98, + 98, 118, 98, 98, 98, 98, 98, 98, 98, 118, 118, 118, 98, 98, 98, 119, + 98, 98, 118, 120, 42, 121, 91, 116, 122, 118, 118, 118, 118, 98, 98, 98, + 98, 98, 118, 119, 98, 112, 123, 116, 36, 36, 110, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 36, 110, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 124, 98, 98, 98, 98, 98, 124, 36, 36, + 125, 125, 125, 125, 125, 125, 125, 125, 98, 98, 98, 98, 28, 28, 28, 28, + 98, 98, 112, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 124, 36, + 98, 98, 98, 124, 36, 36, 36, 36, 14, 14, 14, 14, 14, 14, 27, 106, + 12, 12, 12, 12, 12, 14, 36, 36, 0, 44, 0, 0, 0, 0, 0, 14, + 14, 14, 14, 14, 14, 14, 14, 0, 0, 27, 58, 58, 36, 36, 36, 36, + 36, 36, 36, 39, 14, 14, 14, 14, 14, 44, 14, 44, 14, 19, 14, 14, + 14, 19, 0, 0, 14, 14, 36, 36, 14, 14, 14, 14, 126, 36, 36, 36, + 14, 14, 65, 53, 36, 36, 36, 36, 0, 14, 14, 14, 14, 14, 14, 14, + 0, 0, 52, 36, 36, 36, 36, 58, 0, 14, 14, 14, 14, 14, 29, 36, + 14, 14, 14, 0, 0, 0, 0, 58, 14, 14, 14, 19, 0, 0, 0, 0, + 0, 0, 36, 36, 36, 36, 36, 39, 74, 74, 74, 74, 74, 74, 127, 36, + 14, 19, 0, 0, 0, 0, 0, 0, 44, 14, 14, 27, 58, 14, 14, 39, + 12, 12, 12, 12, 12, 36, 36, 14, 12, 12, 12, 12, 12, 61, 61, 62, + 14, 14, 14, 14, 19, 0, 0, 0, 0, 0, 0, 52, 36, 36, 36, 36, + 14, 19, 14, 14, 14, 14, 0, 36, 12, 12, 12, 12, 12, 36, 27, 58, + 61, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 61, 61, + 58, 14, 19, 52, 36, 36, 36, 36, 39, 14, 14, 38, 39, 14, 14, 38, + 39, 14, 14, 38, 36, 36, 36, 36, 14, 19, 0, 0, 0, 1, 0, 36, + 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 129, 129, 129, + 129, 129, 128, 129, 129, 129, 129, 129, 129, 129, 36, 36, 36, 36, 36, 36, + 75, 75, 75, 130, 36, 131, 76, 76, 76, 76, 76, 76, 76, 76, 36, 36, + 132, 132, 132, 132, 132, 132, 132, 132, 36, 39, 14, 14, 36, 36, 133, 134, + 46, 46, 46, 46, 48, 46, 46, 46, 46, 46, 46, 47, 46, 46, 47, 47, + 46, 133, 47, 46, 46, 46, 46, 46, 36, 39, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 104, 36, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 126, 36, 135, 136, 57, 137, 138, 36, 36, 36, + 98, 98, 139, 104, 104, 104, 104, 104, 104, 104, 111, 139, 111, 98, 98, 98, + 111, 78, 91, 53, 139, 104, 104, 111, 98, 98, 98, 124, 140, 141, 36, 36, + 14, 14, 14, 14, 14, 14, 38, 142, 105, 98, 6, 98, 70, 98, 111, 111, + 98, 98, 98, 98, 98, 91, 98, 143, 98, 98, 98, 98, 98, 139, 144, 98, + 98, 98, 98, 98, 98, 139, 144, 139, 114, 70, 93, 145, 125, 125, 125, 125, + 146, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 91, + 36, 14, 14, 14, 36, 14, 14, 14, 36, 14, 14, 14, 36, 14, 38, 36, + 22, 98, 140, 147, 14, 14, 14, 38, 36, 36, 36, 36, 42, 0, 148, 36, + 14, 14, 14, 14, 14, 14, 39, 14, 14, 14, 14, 14, 14, 38, 14, 39, + 58, 41, 36, 39, 14, 14, 14, 14, 14, 14, 36, 39, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 36, 36, 14, 14, 14, 14, 14, 14, 19, 36, + 14, 14, 36, 36, 36, 36, 36, 36, 14, 14, 14, 0, 0, 52, 36, 36, + 14, 14, 14, 14, 14, 14, 14, 81, 14, 14, 36, 36, 14, 14, 14, 14, + 77, 14, 14, 36, 36, 36, 36, 36, 14, 14, 36, 36, 36, 36, 36, 39, + 14, 14, 14, 36, 38, 14, 14, 14, 14, 14, 14, 39, 38, 36, 38, 39, + 14, 14, 14, 81, 14, 14, 14, 14, 14, 38, 14, 36, 36, 39, 14, 14, + 14, 14, 14, 14, 14, 14, 36, 81, 14, 14, 14, 14, 14, 36, 36, 39, + 14, 14, 14, 14, 36, 36, 14, 14, 19, 0, 42, 52, 36, 36, 0, 0, + 14, 14, 39, 14, 39, 14, 14, 14, 14, 14, 36, 36, 0, 52, 36, 42, + 58, 58, 58, 58, 38, 36, 36, 36, 14, 14, 19, 52, 36, 39, 14, 14, + 58, 58, 58, 149, 36, 36, 36, 36, 14, 14, 14, 36, 81, 58, 58, 58, + 14, 38, 36, 36, 14, 14, 14, 14, 14, 36, 36, 36, 39, 14, 38, 36, + 36, 36, 36, 36, 39, 14, 14, 14, 14, 38, 36, 36, 36, 36, 36, 36, + 14, 38, 36, 36, 36, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, + 0, 0, 0, 1, 77, 14, 14, 36, 14, 14, 14, 12, 12, 12, 12, 12, + 36, 36, 36, 36, 36, 36, 36, 42, 0, 0, 0, 0, 0, 44, 14, 58, + 58, 36, 36, 36, 36, 36, 36, 36, 0, 0, 52, 12, 12, 12, 12, 12, + 58, 58, 36, 36, 36, 36, 36, 36, 14, 19, 32, 38, 36, 36, 36, 36, + 44, 14, 27, 77, 77, 0, 44, 36, 12, 12, 12, 12, 12, 32, 27, 58, + 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 58, 27, 77, 36, + 14, 14, 14, 38, 38, 14, 14, 39, 14, 14, 14, 14, 27, 36, 36, 36, + 0, 0, 0, 0, 0, 52, 36, 36, 0, 0, 39, 14, 14, 14, 38, 39, + 38, 36, 36, 42, 36, 36, 39, 14, 14, 0, 36, 0, 0, 0, 52, 36, + 0, 0, 52, 36, 36, 36, 36, 36, 0, 0, 14, 14, 36, 36, 36, 36, + 0, 0, 0, 36, 0, 0, 0, 0, 150, 58, 53, 14, 27, 58, 58, 58, + 58, 58, 58, 58, 14, 14, 0, 36, 1, 77, 38, 36, 36, 36, 36, 36, + 0, 0, 0, 0, 36, 36, 36, 36, 61, 61, 61, 61, 61, 36, 60, 61, + 12, 12, 12, 12, 12, 61, 58, 151, 14, 38, 36, 36, 36, 36, 36, 39, + 58, 58, 41, 36, 36, 36, 36, 36, 14, 14, 14, 14, 152, 70, 114, 14, + 14, 99, 14, 70, 70, 14, 14, 14, 14, 14, 14, 14, 16, 114, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 70, 12, 12, 12, 12, 12, 36, 36, 58, + 0, 0, 1, 36, 36, 36, 36, 36, 0, 0, 0, 1, 58, 14, 14, 14, + 14, 14, 77, 36, 36, 36, 36, 36, 12, 12, 12, 12, 12, 39, 14, 14, + 14, 14, 14, 14, 36, 36, 39, 14, 19, 0, 0, 0, 0, 0, 0, 0, + 98, 36, 36, 36, 36, 36, 36, 36, 14, 14, 14, 14, 14, 36, 19, 1, + 0, 0, 36, 36, 36, 36, 36, 36, 14, 14, 19, 0, 0, 14, 19, 0, + 0, 44, 19, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 0, 0, 14, + 14, 0, 44, 36, 36, 36, 36, 36, 36, 38, 39, 38, 39, 14, 38, 14, + 14, 14, 14, 14, 14, 39, 39, 14, 14, 14, 39, 14, 14, 14, 14, 14, + 14, 14, 14, 39, 14, 38, 39, 14, 14, 14, 38, 14, 14, 14, 38, 14, + 14, 14, 14, 14, 14, 39, 14, 38, 14, 14, 38, 38, 36, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 0, 0, 0, 44, 14, 19, 0, 0, 0, 0, 0, 0, 0, 0, 44, 14, + 14, 14, 19, 14, 14, 14, 14, 14, 14, 14, 44, 27, 58, 77, 36, 36, + 36, 36, 36, 36, 36, 42, 0, 0, 14, 14, 38, 39, 14, 14, 14, 14, + 39, 38, 38, 39, 39, 14, 14, 14, 14, 38, 14, 14, 39, 39, 36, 36, + 36, 38, 36, 39, 39, 39, 39, 14, 39, 38, 38, 39, 39, 39, 39, 39, + 39, 38, 38, 39, 14, 38, 14, 14, 14, 38, 14, 14, 39, 14, 38, 38, + 14, 14, 14, 14, 14, 39, 14, 14, 39, 14, 39, 14, 14, 39, 14, 14, + 28, 28, 28, 28, 28, 28, 153, 36, 28, 28, 28, 28, 28, 28, 28, 38, + 28, 28, 28, 28, 28, 14, 36, 36, 28, 28, 28, 28, 28, 153, 36, 36, + 36, 36, 36, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 98, 124, 36, 36, 36, 36, 36, 36, 98, 98, 98, 98, 124, 36, 36, 36, + 98, 98, 98, 98, 98, 98, 14, 98, 98, 98, 100, 101, 98, 98, 101, 98, + 98, 98, 98, 98, 98, 100, 14, 14, 101, 101, 101, 98, 98, 98, 98, 100, + 100, 101, 98, 98, 98, 98, 98, 98, 14, 14, 14, 101, 98, 98, 98, 98, + 98, 98, 98, 100, 14, 14, 14, 14, 14, 14, 101, 98, 98, 98, 98, 98, + 98, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 98, 98, 98, + 98, 98, 110, 98, 98, 98, 98, 98, 98, 98, 14, 14, 14, 14, 98, 98, + 98, 98, 14, 14, 14, 98, 98, 98, 14, 14, 14, 85, 155, 91, 14, 14, + 124, 36, 36, 36, 36, 36, 36, 36, 98, 98, 124, 36, 36, 36, 36, 36, + 42, 36, 36, 36, 36, 36, 36, 36, +}; + +static RE_UINT8 re_line_break_stage_5[] = { + 16, 16, 16, 18, 22, 20, 20, 21, 19, 6, 3, 12, 9, 10, 12, 3, + 1, 36, 12, 9, 8, 15, 8, 7, 11, 11, 8, 8, 12, 12, 12, 6, + 12, 1, 9, 36, 18, 2, 12, 16, 16, 29, 4, 1, 10, 9, 9, 9, + 12, 25, 25, 12, 25, 3, 12, 18, 25, 25, 17, 12, 25, 1, 17, 25, + 12, 17, 16, 4, 4, 4, 4, 16, 0, 0, 8, 12, 12, 0, 0, 12, + 0, 8, 18, 0, 0, 16, 18, 16, 16, 12, 6, 16, 37, 37, 37, 0, + 37, 12, 12, 10, 10, 10, 16, 6, 16, 0, 6, 6, 10, 11, 11, 12, + 6, 12, 8, 6, 18, 18, 0, 10, 0, 24, 24, 24, 24, 0, 0, 9, + 24, 12, 17, 17, 4, 17, 17, 18, 4, 6, 4, 12, 1, 2, 18, 17, + 12, 4, 4, 0, 31, 31, 32, 32, 33, 33, 18, 12, 2, 0, 5, 24, + 18, 9, 0, 18, 18, 4, 18, 28, 26, 25, 3, 3, 1, 3, 14, 14, + 14, 18, 20, 20, 3, 25, 5, 5, 8, 1, 2, 5, 30, 12, 2, 25, + 9, 12, 12, 14, 13, 13, 2, 12, 13, 12, 12, 13, 13, 25, 25, 13, + 2, 1, 0, 6, 6, 18, 1, 18, 26, 26, 1, 0, 0, 13, 2, 13, + 13, 5, 5, 1, 2, 2, 13, 16, 5, 13, 0, 38, 13, 38, 38, 13, + 38, 0, 16, 5, 5, 38, 38, 5, 13, 0, 38, 38, 10, 12, 31, 0, + 34, 35, 35, 35, 32, 0, 0, 33, 27, 27, 0, 37, 16, 37, 8, 2, + 2, 8, 6, 1, 2, 14, 13, 1, 13, 9, 10, 13, 0, 30, 13, 6, + 13, 2, 12, 38, 38, 12, 9, 0, 23, 25, 14, 0, 16, 17, 18, 24, + 1, 1, 25, 0, 39, 39, 3, 5, +}; + +/* Line_Break: 8608 bytes. */ + +RE_UINT32 re_get_line_break(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_line_break_stage_1[f] << 5; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_line_break_stage_2[pos + f] << 3; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_line_break_stage_3[pos + f] << 3; + f = code >> 1; + code ^= f << 1; + pos = (RE_UINT32)re_line_break_stage_4[pos + f] << 1; + value = re_line_break_stage_5[pos + code]; + + return value; +} + +/* Numeric_Type. */ + +static RE_UINT8 re_numeric_type_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11, 12, + 13, 14, 15, 11, 11, 11, 16, 11, 11, 11, 11, 11, 11, 17, 18, 19, + 20, 11, 21, 22, 11, 11, 23, 11, 11, 11, 11, 11, 11, 11, 11, 24, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +}; + +static RE_UINT8 re_numeric_type_stage_2[] = { + 0, 1, 1, 1, 1, 1, 2, 3, 1, 4, 5, 6, 7, 8, 9, 10, + 11, 1, 1, 12, 1, 1, 13, 14, 15, 16, 17, 18, 19, 1, 1, 1, + 20, 21, 1, 1, 22, 1, 1, 23, 1, 1, 1, 1, 24, 1, 1, 1, + 25, 26, 27, 1, 28, 1, 1, 1, 29, 1, 1, 30, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 31, 32, + 1, 33, 1, 34, 1, 1, 35, 1, 36, 1, 1, 1, 1, 1, 37, 38, + 1, 1, 39, 40, 1, 1, 1, 41, 1, 1, 1, 1, 1, 1, 1, 42, + 1, 1, 1, 43, 1, 1, 44, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 45, 1, 1, 1, 46, 1, 1, 1, 1, 1, 1, 1, 47, 48, 1, 1, + 1, 1, 1, 1, 1, 1, 49, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 50, 1, 51, 52, 53, 54, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 55, 1, 1, 1, 1, 1, 15, + 1, 56, 57, 58, 59, 1, 1, 1, 60, 61, 62, 63, 64, 1, 65, 1, + 66, 67, 54, 1, 68, 1, 69, 70, 71, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 73, 74, 1, 1, 1, 1, + 1, 1, 1, 75, 1, 1, 1, 76, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 77, 1, 1, 1, 1, 1, 1, 1, + 1, 78, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 79, 80, 1, 1, 1, 1, 1, 1, 1, 81, 82, 83, 1, 1, 1, 1, + 1, 1, 1, 84, 1, 1, 1, 1, 1, 85, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 86, 1, 1, 1, 1, + 1, 1, 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 84, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_numeric_type_stage_3[] = { + 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, + 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 4, 0, 0, 0, 4, + 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 4, + 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0, 1, 0, + 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, + 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, + 0, 0, 0, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 14, 0, 0, 0, 0, 0, 15, 0, 0, 0, + 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 17, 0, 0, 0, 0, 0, 18, 19, 20, 0, 0, 0, + 0, 0, 0, 21, 22, 0, 0, 23, 0, 0, 0, 24, 25, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, 27, 28, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 0, 0, 0, 0, 30, 31, 0, 30, 32, 0, 0, + 33, 0, 0, 0, 34, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 0, 0, 0, 0, 37, 0, 26, 0, 38, 39, 40, 41, + 36, 0, 0, 42, 0, 0, 0, 0, 43, 0, 44, 45, 0, 0, 0, 0, + 0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 50, 0, 0, 0, 51, + 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, + 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, + 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, + 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, + 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, + 0, 42, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 0, 0, 0, 56, + 0, 3, 0, 0, 0, 0, 0, 61, 0, 62, 0, 0, 0, 0, 1, 0, + 3, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 63, 0, 55, 64, 26, + 65, 66, 19, 67, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, + 0, 70, 71, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 3, 0, 0, + 0, 0, 73, 74, 0, 75, 0, 76, 77, 0, 0, 0, 0, 78, 79, 19, + 0, 0, 80, 81, 82, 0, 0, 83, 0, 0, 73, 73, 0, 84, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 85, 0, 0, 0, 86, 0, 0, 0, 0, + 0, 0, 87, 88, 0, 0, 0, 1, 0, 89, 0, 0, 0, 0, 1, 90, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 3, 0, + 0, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, + 19, 19, 19, 93, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, + 0, 0, 94, 95, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 97, 98, 0, 0, 0, 0, 0, 0, 75, 0, + 99, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 43, 0, 0, 0, 100, + 0, 58, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 102, 103, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, + 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, +}; + +static RE_UINT8 re_numeric_type_stage_4[] = { + 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 3, 4, 1, 2, 0, 0, + 5, 1, 0, 0, 5, 1, 6, 7, 5, 1, 8, 0, 5, 1, 9, 0, + 5, 1, 0, 10, 5, 1, 11, 0, 1, 12, 13, 0, 0, 14, 15, 16, + 0, 17, 18, 0, 1, 2, 19, 7, 0, 0, 1, 20, 1, 2, 1, 2, + 0, 0, 21, 22, 23, 22, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, + 24, 7, 0, 0, 23, 25, 26, 27, 19, 23, 25, 13, 0, 28, 29, 30, + 0, 0, 31, 32, 23, 33, 34, 0, 0, 0, 0, 35, 36, 0, 0, 0, + 37, 7, 0, 9, 0, 0, 38, 0, 19, 7, 0, 0, 0, 19, 37, 19, + 0, 0, 37, 19, 35, 0, 0, 0, 39, 0, 0, 0, 0, 40, 0, 0, + 0, 35, 0, 0, 41, 42, 0, 0, 0, 43, 44, 0, 0, 0, 0, 36, + 18, 0, 0, 36, 0, 18, 0, 0, 0, 0, 18, 0, 43, 0, 0, 0, + 45, 0, 0, 0, 0, 46, 0, 0, 47, 43, 0, 0, 48, 0, 0, 0, + 0, 0, 0, 39, 0, 0, 42, 42, 0, 0, 0, 40, 0, 0, 0, 17, + 0, 49, 18, 0, 0, 0, 0, 45, 0, 43, 0, 0, 0, 0, 40, 0, + 0, 0, 45, 0, 0, 45, 39, 0, 42, 0, 0, 0, 45, 43, 0, 0, + 0, 0, 0, 18, 17, 19, 0, 0, 0, 0, 11, 0, 0, 39, 39, 18, + 0, 0, 50, 0, 36, 19, 19, 19, 19, 19, 13, 0, 19, 19, 19, 18, + 0, 51, 0, 0, 37, 19, 19, 13, 13, 0, 0, 0, 42, 40, 0, 0, + 0, 0, 52, 0, 0, 0, 0, 19, 0, 0, 0, 37, 36, 19, 0, 0, + 0, 0, 0, 53, 0, 0, 17, 13, 0, 0, 0, 54, 19, 19, 8, 19, + 55, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 57, 0, 53, 0, 0, + 0, 37, 0, 0, 0, 0, 0, 8, 23, 25, 19, 10, 0, 0, 58, 59, + 60, 1, 0, 0, 0, 0, 5, 1, 37, 19, 16, 0, 0, 0, 1, 61, + 1, 12, 9, 0, 19, 10, 0, 0, 0, 0, 1, 62, 7, 0, 0, 0, + 19, 19, 7, 0, 0, 5, 1, 1, 1, 1, 1, 1, 23, 63, 0, 0, + 40, 0, 0, 0, 39, 43, 0, 43, 0, 40, 0, 35, 0, 0, 0, 42, +}; + +static RE_UINT8 re_numeric_type_stage_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, + 0, 2, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 3, 3, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, + 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 3, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 0, 0, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 0, 0, 0, 0, + 3, 3, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0, +}; + +/* Numeric_Type: 2304 bytes. */ + +RE_UINT32 re_get_numeric_type(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_numeric_type_stage_1[f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_numeric_type_stage_2[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_numeric_type_stage_3[pos + f] << 2; + f = code >> 3; + code ^= f << 3; + pos = (RE_UINT32)re_numeric_type_stage_4[pos + f] << 3; + value = re_numeric_type_stage_5[pos + code]; + + return value; +} + +/* Numeric_Value. */ + +static RE_UINT8 re_numeric_value_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11, 12, + 13, 14, 15, 11, 11, 11, 16, 11, 11, 11, 11, 11, 11, 17, 18, 19, + 20, 11, 21, 22, 11, 11, 23, 11, 11, 11, 11, 11, 11, 11, 11, 24, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +}; + +static RE_UINT8 re_numeric_value_stage_2[] = { + 0, 1, 1, 1, 1, 1, 2, 3, 1, 4, 5, 6, 7, 8, 9, 10, + 11, 1, 1, 12, 1, 1, 13, 14, 15, 16, 17, 18, 19, 1, 1, 1, + 20, 21, 1, 1, 22, 1, 1, 23, 1, 1, 1, 1, 24, 1, 1, 1, + 25, 26, 27, 1, 28, 1, 1, 1, 29, 1, 1, 30, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 31, 32, + 1, 33, 1, 34, 1, 1, 35, 1, 36, 1, 1, 1, 1, 1, 37, 38, + 1, 1, 39, 40, 1, 1, 1, 41, 1, 1, 1, 1, 1, 1, 1, 42, + 1, 1, 1, 43, 1, 1, 44, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 45, 1, 1, 1, 46, 1, 1, 1, 1, 1, 1, 1, 47, 48, 1, 1, + 1, 1, 1, 1, 1, 1, 49, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 50, 1, 51, 52, 53, 54, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 55, 1, 1, 1, 1, 1, 15, + 1, 56, 57, 58, 59, 1, 1, 1, 60, 61, 62, 63, 64, 1, 65, 1, + 66, 67, 54, 1, 68, 1, 69, 70, 71, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 73, 74, 1, 1, 1, 1, + 1, 1, 1, 75, 1, 1, 1, 76, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 77, 1, 1, 1, 1, 1, 1, 1, + 1, 78, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 79, 80, 1, 1, 1, 1, 1, 1, 1, 81, 82, 83, 1, 1, 1, 1, + 1, 1, 1, 84, 1, 1, 1, 1, 1, 85, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 86, 1, 1, 1, 1, + 1, 1, 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 88, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_numeric_value_stage_3[] = { + 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, + 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 4, 0, 0, 0, 4, + 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 4, + 0, 0, 0, 9, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0, 1, 0, + 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, + 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, + 0, 0, 0, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 14, 0, 0, 0, 0, 0, 13, 0, 0, 0, + 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, + 0, 0, 0, 15, 3, 0, 0, 0, 0, 0, 16, 17, 18, 0, 0, 0, + 0, 0, 0, 19, 20, 0, 0, 21, 0, 0, 0, 22, 23, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 24, 25, 26, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 27, 0, 0, 0, 0, 28, 29, 0, 28, 30, 0, 0, + 31, 0, 0, 0, 32, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 0, 0, 34, 0, 0, 0, 0, 0, 35, 0, 36, 0, 37, 38, 39, 40, + 41, 0, 0, 42, 0, 0, 0, 0, 43, 0, 44, 45, 0, 0, 0, 0, + 0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 50, 0, 0, 0, 51, + 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, + 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, + 0, 0, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0, 0, 0, + 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, + 0, 62, 0, 0, 0, 0, 0, 0, 0, 63, 64, 65, 0, 0, 0, 66, + 0, 3, 0, 0, 0, 0, 0, 67, 0, 68, 0, 0, 0, 0, 1, 0, + 3, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 69, 0, 70, 71, 72, + 73, 74, 75, 76, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, + 0, 79, 80, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 3, 0, 0, + 0, 0, 82, 83, 0, 84, 0, 85, 86, 0, 0, 0, 0, 87, 88, 89, + 0, 0, 90, 91, 92, 0, 0, 93, 0, 0, 94, 94, 0, 95, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 97, 0, 0, 0, 0, + 0, 0, 98, 99, 0, 0, 0, 1, 0, 100, 0, 0, 0, 0, 1, 101, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 3, 0, + 0, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, + 104, 105, 106, 107, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, + 0, 0, 108, 109, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 111, 112, 0, 0, 0, 0, 0, 0, 113, 0, + 114, 0, 0, 0, 0, 0, 0, 0, 115, 0, 0, 116, 0, 0, 0, 117, + 0, 118, 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, 120, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 121, 122, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, + 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, 0, + 0, 0, 0, 0, 126, 0, 0, 0, +}; + +static RE_UINT8 re_numeric_value_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, + 0, 0, 0, 0, 4, 0, 5, 6, 1, 2, 3, 0, 0, 0, 0, 0, + 0, 7, 8, 9, 0, 0, 0, 0, 0, 7, 8, 9, 0, 10, 11, 0, + 0, 7, 8, 9, 12, 13, 0, 0, 0, 7, 8, 9, 14, 0, 0, 0, + 0, 7, 8, 9, 0, 0, 1, 15, 0, 7, 8, 9, 16, 17, 0, 0, + 1, 2, 18, 19, 20, 0, 0, 0, 0, 0, 21, 2, 22, 23, 24, 25, + 0, 0, 0, 26, 27, 0, 0, 0, 1, 2, 3, 0, 1, 2, 3, 0, + 0, 0, 0, 0, 1, 2, 28, 0, 0, 0, 0, 0, 29, 2, 3, 0, + 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 34, 35, 36, 37, + 38, 39, 40, 0, 0, 0, 0, 0, 34, 35, 36, 41, 42, 34, 35, 36, + 41, 42, 34, 35, 36, 41, 42, 0, 0, 0, 43, 44, 45, 46, 2, 47, + 0, 0, 0, 0, 0, 48, 49, 50, 34, 35, 51, 49, 50, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 52, 0, 53, 0, 0, 0, 0, 0, 0, + 21, 2, 3, 0, 0, 0, 54, 0, 0, 0, 0, 0, 48, 55, 0, 0, + 34, 35, 56, 0, 0, 0, 0, 0, 0, 0, 57, 58, 59, 60, 61, 62, + 0, 0, 0, 0, 63, 64, 65, 66, 0, 67, 0, 0, 0, 0, 0, 0, + 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, + 0, 0, 0, 70, 0, 0, 0, 0, 71, 72, 73, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 75, 0, 76, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 77, 78, 0, 0, 0, 0, 0, 0, 79, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, + 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, + 86, 87, 0, 88, 0, 0, 0, 0, 89, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 5, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 0, 0, 0, 0, 92, + 0, 0, 0, 15, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, + 0, 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, 0, + 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 97, 0, 98, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 99, 68, 0, 0, 0, + 0, 0, 0, 0, 75, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 0, + 0, 0, 0, 0, 0, 103, 0, 0, 0, 48, 49, 104, 0, 0, 0, 0, + 0, 0, 0, 0, 105, 106, 0, 0, 0, 0, 107, 0, 108, 0, 75, 0, + 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 109, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 110, 0, 111, 8, 9, 57, 58, 112, 113, + 114, 115, 116, 117, 118, 0, 0, 0, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 122, 131, 132, 0, 0, 0, 133, 0, 0, 0, 0, 0, + 21, 2, 22, 23, 24, 134, 135, 0, 136, 0, 0, 0, 0, 0, 0, 0, + 137, 0, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 140, 0, 0, + 0, 0, 0, 0, 0, 0, 141, 142, 0, 0, 0, 0, 0, 0, 21, 143, + 0, 111, 144, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 145, + 0, 0, 0, 0, 0, 146, 147, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 34, 35, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 34, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, + 0, 0, 0, 0, 0, 0, 0, 165, 0, 0, 111, 145, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 34, 163, 0, 0, 21, 166, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 167, 168, 34, 35, 149, 150, 169, 152, 170, 171, + 0, 0, 0, 0, 48, 49, 50, 172, 173, 174, 8, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 8, 9, 21, 2, 22, 23, 24, 175, 0, 0, + 0, 0, 0, 0, 1, 2, 22, 0, 1, 2, 22, 23, 176, 0, 0, 0, + 8, 9, 49, 177, 35, 178, 2, 179, 180, 181, 9, 182, 183, 182, 184, 185, + 186, 187, 188, 189, 144, 190, 191, 192, 193, 194, 195, 196, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 197, 198, 199, 0, 0, 0, 0, 0, 0, 0, + 34, 35, 149, 150, 200, 0, 0, 0, 0, 0, 0, 7, 8, 9, 1, 2, + 201, 8, 9, 1, 2, 201, 8, 9, 0, 111, 8, 9, 0, 0, 0, 0, + 202, 49, 104, 29, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, + 0, 203, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0, + 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 0, + 204, 0, 0, 88, 0, 0, 0, 88, 0, 0, 101, 0, 0, 0, 0, 73, + 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 80, 0, + 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 205, 0, 0, 0, 0, + 0, 0, 0, 0, 206, 0, 0, 0, +}; + +static RE_UINT8 re_numeric_value_stage_5[] = { + 0, 0, 0, 0, 2, 27, 29, 31, 33, 35, 37, 39, 41, 43, 0, 0, + 0, 0, 29, 31, 0, 27, 0, 0, 12, 17, 22, 0, 0, 0, 2, 27, + 29, 31, 33, 35, 37, 39, 41, 43, 3, 7, 10, 12, 22, 50, 0, 0, + 0, 0, 12, 17, 22, 3, 7, 10, 44, 89, 98, 0, 27, 29, 31, 0, + 44, 89, 98, 12, 17, 22, 0, 0, 41, 43, 17, 28, 30, 32, 34, 36, + 38, 40, 42, 1, 0, 27, 29, 31, 41, 43, 44, 54, 64, 74, 84, 85, + 86, 87, 88, 89, 107, 0, 0, 0, 0, 0, 51, 52, 53, 0, 0, 0, + 41, 43, 27, 0, 2, 0, 0, 0, 8, 6, 5, 13, 21, 11, 15, 19, + 23, 9, 24, 7, 14, 20, 25, 27, 27, 29, 31, 33, 35, 37, 39, 41, + 43, 44, 45, 46, 84, 89, 93, 98, 98, 102, 107, 0, 0, 37, 84, 111, + 116, 2, 0, 0, 47, 48, 49, 50, 51, 52, 53, 54, 0, 0, 2, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 27, 29, 31, 41, 43, 44, 2, + 0, 0, 27, 29, 31, 33, 35, 37, 39, 41, 43, 44, 43, 44, 27, 29, + 0, 17, 0, 0, 0, 0, 0, 2, 44, 54, 64, 0, 31, 33, 0, 0, + 43, 44, 0, 0, 44, 54, 64, 74, 84, 85, 86, 87, 0, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 0, 35, 0, 0, + 0, 0, 0, 29, 0, 0, 35, 0, 0, 39, 0, 0, 27, 0, 0, 39, + 0, 0, 0, 107, 0, 31, 0, 0, 0, 43, 0, 0, 29, 0, 0, 0, + 35, 0, 33, 0, 0, 0, 0, 128, 44, 0, 0, 0, 0, 0, 0, 98, + 31, 0, 0, 0, 89, 0, 0, 0, 128, 0, 0, 0, 0, 0, 130, 0, + 0, 29, 0, 41, 0, 37, 0, 0, 0, 44, 0, 98, 54, 64, 0, 0, + 74, 0, 0, 0, 0, 31, 31, 31, 0, 0, 0, 33, 0, 0, 27, 0, + 0, 0, 43, 54, 0, 0, 44, 0, 41, 0, 0, 0, 0, 0, 39, 0, + 0, 0, 43, 0, 0, 0, 89, 0, 0, 0, 33, 0, 0, 0, 29, 0, + 0, 98, 0, 0, 0, 0, 37, 0, 37, 0, 0, 0, 0, 0, 2, 0, + 39, 41, 43, 2, 12, 17, 22, 3, 7, 10, 0, 0, 0, 0, 0, 31, + 0, 0, 0, 44, 0, 37, 0, 37, 0, 44, 0, 0, 0, 0, 0, 27, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 12, 17, 27, 35, + 84, 93, 102, 111, 35, 44, 84, 89, 93, 98, 102, 35, 44, 84, 89, 93, + 98, 107, 111, 44, 27, 27, 27, 29, 29, 29, 29, 35, 44, 44, 44, 44, + 44, 64, 84, 84, 84, 84, 89, 91, 93, 93, 93, 93, 84, 17, 17, 21, + 22, 0, 0, 0, 0, 0, 2, 12, 90, 91, 92, 93, 94, 95, 96, 97, + 27, 35, 44, 84, 0, 88, 0, 0, 0, 0, 97, 0, 0, 27, 29, 44, + 54, 89, 0, 0, 27, 29, 31, 44, 54, 89, 98, 107, 33, 35, 44, 54, + 29, 31, 33, 33, 35, 44, 54, 89, 0, 0, 27, 44, 54, 89, 29, 31, + 26, 17, 0, 0, 43, 44, 54, 64, 74, 84, 85, 86, 0, 0, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 119, 120, 122, 123, 124, + 125, 126, 4, 9, 12, 13, 16, 17, 18, 21, 22, 24, 44, 54, 89, 98, + 0, 27, 84, 0, 0, 27, 44, 54, 33, 44, 54, 89, 0, 0, 27, 35, + 44, 84, 89, 98, 87, 88, 89, 90, 95, 96, 97, 17, 12, 13, 21, 0, + 54, 64, 74, 84, 85, 86, 87, 88, 89, 98, 2, 27, 98, 0, 0, 0, + 86, 87, 88, 0, 39, 41, 43, 33, 43, 27, 29, 31, 41, 43, 27, 29, + 31, 33, 35, 29, 31, 31, 33, 35, 27, 29, 31, 31, 33, 35, 118, 121, + 33, 35, 31, 31, 33, 33, 33, 33, 37, 39, 39, 39, 41, 41, 43, 43, + 43, 43, 29, 31, 33, 35, 37, 27, 35, 35, 29, 31, 27, 29, 13, 21, + 24, 13, 21, 7, 12, 9, 12, 12, 17, 13, 21, 74, 84, 33, 35, 37, + 39, 41, 43, 0, 41, 43, 0, 44, 89, 107, 127, 128, 129, 130, 0, 0, + 87, 88, 0, 0, 41, 43, 2, 27, 2, 2, 27, 29, 33, 0, 0, 0, + 0, 0, 0, 64, 0, 33, 0, 0, 43, 0, 0, 0, +}; + +/* Numeric_Value: 3228 bytes. */ + +RE_UINT32 re_get_numeric_value(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 12; + code = ch ^ (f << 12); + pos = (RE_UINT32)re_numeric_value_stage_1[f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_numeric_value_stage_2[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_numeric_value_stage_3[pos + f] << 3; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_numeric_value_stage_4[pos + f] << 2; + value = re_numeric_value_stage_5[pos + code]; + + return value; +} + +/* Bidi_Mirrored. */ + +static RE_UINT8 re_bidi_mirrored_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_bidi_mirrored_stage_2[] = { + 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +}; + +static RE_UINT8 re_bidi_mirrored_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, + 4, 5, 1, 6, 7, 8, 1, 9, 10, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, + 1, 1, 1, 12, 1, 1, 1, 1, +}; + +static RE_UINT8 re_bidi_mirrored_stage_4[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, + 6, 7, 8, 3, 3, 9, 3, 3, 10, 11, 12, 13, 14, 3, 3, 3, + 3, 3, 3, 3, 3, 15, 3, 16, 3, 3, 3, 3, 3, 3, 17, 18, + 19, 20, 21, 22, 3, 3, 3, 3, 23, 3, 3, 3, 3, 3, 3, 3, + 24, 3, 3, 3, 3, 3, 3, 3, 3, 25, 3, 3, 26, 27, 3, 3, + 3, 3, 3, 28, 29, 30, 31, 32, +}; + +static RE_UINT8 re_bidi_mirrored_stage_5[] = { + 0, 0, 0, 0, 0, 3, 0, 80, 0, 0, 0, 40, 0, 0, 0, 40, + 0, 0, 0, 0, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 24, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 96, 0, 0, 0, 0, 0, 0, 96, + 0, 96, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 30, 63, 98, 188, 87, 248, 15, 250, 255, 31, 60, 128, 245, 207, 255, 255, + 255, 159, 7, 1, 204, 255, 255, 193, 0, 62, 195, 255, 255, 63, 255, 255, + 0, 15, 0, 0, 3, 6, 0, 0, 0, 0, 0, 0, 0, 255, 63, 0, + 121, 59, 120, 112, 252, 255, 0, 0, 248, 255, 255, 249, 255, 255, 0, 1, + 63, 194, 55, 31, 58, 3, 240, 51, 0, 252, 255, 223, 83, 122, 48, 112, + 0, 0, 128, 1, 48, 188, 25, 254, 255, 255, 255, 255, 207, 191, 255, 255, + 255, 255, 127, 80, 124, 112, 136, 47, 60, 54, 0, 48, 255, 3, 0, 0, + 0, 255, 243, 15, 0, 0, 0, 0, 0, 0, 0, 126, 48, 0, 0, 0, + 0, 3, 0, 80, 0, 0, 0, 40, 0, 0, 0, 168, 13, 0, 0, 0, + 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, + 0, 128, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Bidi_Mirrored: 489 bytes. */ + +RE_UINT32 re_get_bidi_mirrored(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_bidi_mirrored_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_bidi_mirrored_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_bidi_mirrored_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_bidi_mirrored_stage_4[pos + f] << 6; + pos += code; + value = (re_bidi_mirrored_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Indic_Positional_Category. */ + +static RE_UINT8 re_indic_positional_category_stage_1[] = { + 0, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_indic_positional_category_stage_2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 0, 0, 0, 0, 0, 0, 9, 0, 10, 11, 12, 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, + 19, 20, 21, 22, 23, 24, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_indic_positional_category_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 2, 3, 4, 5, 0, 6, 0, 0, 7, 8, 9, 5, 0, + 10, 0, 0, 7, 11, 0, 0, 12, 10, 0, 0, 7, 13, 0, 5, 0, + 6, 0, 0, 14, 15, 16, 5, 0, 17, 0, 0, 18, 19, 9, 0, 0, + 20, 0, 0, 21, 22, 23, 5, 0, 6, 0, 0, 14, 24, 25, 5, 0, + 6, 0, 0, 18, 26, 9, 5, 0, 27, 0, 0, 0, 28, 29, 0, 27, + 0, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, + 0, 34, 0, 35, 0, 0, 0, 36, 37, 38, 39, 40, 41, 0, 0, 0, + 0, 0, 42, 43, 0, 44, 45, 46, 47, 48, 0, 0, 0, 0, 0, 0, + 0, 49, 0, 49, 0, 50, 0, 50, 0, 0, 0, 51, 52, 53, 0, 0, + 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 56, 57, 0, 0, 0, + 0, 58, 0, 0, 0, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, + 62, 0, 0, 63, 64, 0, 65, 66, 67, 0, 68, 0, 0, 0, 69, 70, + 0, 0, 71, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 74, 75, + 76, 0, 77, 0, 0, 0, 0, 0, 78, 0, 0, 79, 80, 0, 81, 82, + 0, 0, 83, 0, 84, 70, 0, 0, 1, 0, 0, 85, 86, 0, 87, 0, + 0, 0, 88, 89, 90, 0, 0, 91, 0, 0, 0, 92, 93, 0, 94, 95, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, + 97, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 99, 0, 0, 100, 101, 0, 0, 0, 67, 0, 0, 102, 0, 0, 0, 0, + 103, 0, 104, 105, 0, 0, 0, 106, 67, 0, 0, 107, 108, 0, 0, 0, + 0, 0, 109, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 112, 0, + 6, 0, 0, 18, 113, 9, 114, 115, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 117, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 119, 120, 121, 0, 0, + 0, 0, 0, 122, 123, 0, 0, 0, 0, 0, 124, 125, 0, 0, 0, 0, + 0, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_indic_positional_category_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 1, 2, 8, 5, 9, + 10, 7, 1, 6, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, + 10, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, + 5, 6, 3, 11, 12, 13, 14, 0, 0, 0, 0, 15, 0, 0, 0, 0, + 10, 2, 0, 0, 0, 0, 0, 0, 5, 3, 0, 10, 16, 10, 17, 0, + 1, 0, 18, 0, 0, 0, 0, 0, 5, 6, 7, 10, 19, 15, 5, 0, + 0, 0, 0, 0, 0, 0, 3, 20, 5, 6, 3, 11, 21, 13, 22, 0, + 0, 0, 0, 19, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 8, 2, 23, 0, 24, 12, 25, 26, 0, + 2, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 2, 8, 23, 1, 27, 1, 1, 0, 0, 0, 10, 3, 0, 0, 0, 0, + 28, 8, 23, 19, 29, 30, 1, 0, 0, 0, 15, 23, 0, 0, 0, 0, + 8, 5, 3, 24, 12, 25, 26, 0, 0, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 0, 15, 8, 1, 3, 3, 4, 31, 32, 33, + 20, 8, 1, 1, 6, 3, 0, 0, 34, 34, 35, 10, 1, 1, 1, 16, + 20, 8, 1, 1, 6, 10, 3, 0, 34, 34, 36, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 18, 18, 10, 0, 0, 4, + 18, 37, 6, 38, 38, 1, 1, 2, 37, 1, 3, 1, 0, 0, 18, 6, + 6, 6, 6, 6, 18, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 15, 20, 17, 39, 1, 1, 17, 23, 2, 18, 3, + 0, 0, 0, 8, 6, 0, 0, 6, 3, 8, 23, 15, 8, 8, 8, 0, + 10, 1, 16, 0, 0, 0, 0, 0, 0, 40, 41, 2, 8, 8, 5, 15, + 0, 0, 0, 0, 0, 8, 20, 0, 0, 17, 3, 0, 0, 0, 0, 0, + 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 1, 17, 6, 42, + 43, 24, 25, 2, 20, 1, 1, 1, 1, 10, 0, 0, 0, 0, 10, 0, + 1, 40, 44, 45, 2, 8, 0, 0, 8, 40, 8, 8, 5, 17, 0, 0, + 8, 8, 46, 34, 8, 35, 8, 8, 23, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 10, 39, 20, 0, 0, 0, 0, 11, 40, 1, 17, 6, 3, + 15, 2, 20, 1, 17, 7, 40, 24, 24, 41, 1, 1, 1, 1, 16, 18, + 1, 1, 23, 0, 0, 0, 0, 0, 0, 0, 2, 1, 6, 47, 48, 24, + 25, 19, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 23, 0, 0, 0, 0, 0, 0, + 15, 6, 17, 9, 1, 23, 6, 0, 0, 0, 0, 2, 1, 8, 20, 20, + 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 8, 4, 49, 8, 7, 1, + 1, 1, 24, 17, 0, 0, 0, 0, 1, 16, 50, 6, 6, 1, 6, 6, + 2, 51, 51, 51, 52, 0, 18, 0, 0, 0, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 10, 0, 0, 0, 15, 5, 2, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, + 8, 8, 3, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 6, 0, + 0, 0, 0, 18, 6, 17, 6, 7, 0, 10, 8, 1, 6, 24, 2, 8, + 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 1, 17, 54, 41, 40, 55, 3, 0, 0, 0, 0, + 0, 10, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 15, 2, 0, + 2, 1, 56, 57, 58, 46, 35, 1, 10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 11, 7, 9, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 15, 20, 8, 40, 23, 5, 0, 59, 6, 10, 52, 0, 0, 6, 7, + 0, 0, 0, 0, 17, 3, 0, 0, 20, 23, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 6, 6, 6, 1, 1, 16, 0, 0, 0, 0, + 4, 5, 7, 2, 5, 3, 0, 0, 1, 16, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 1, 6, 41, 38, 17, 3, 16, 0, 0, 0, 0, 0, + 0, 18, 0, 0, 0, 0, 0, 0, 0, 15, 9, 6, 6, 6, 1, 19, + 23, 0, 0, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 0, 8, 5, + 1, 30, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 4, 5, 7, 1, 17, 3, 0, 0, 2, 8, 23, 11, 12, 13, 33, 0, + 0, 8, 0, 1, 1, 1, 16, 0, 1, 1, 16, 0, 0, 0, 0, 0, + 4, 5, 6, 6, 39, 60, 33, 26, 2, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 15, 9, 6, 6, 0, 49, 32, 1, 5, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, + 8, 5, 6, 6, 7, 2, 20, 5, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 10, 20, 9, 6, 1, 1, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 18, 10, 8, 1, 6, 41, 7, 1, 0, 0, +}; + +static RE_UINT8 re_indic_positional_category_stage_5[] = { + 0, 0, 5, 5, 5, 1, 6, 0, 1, 2, 1, 6, 6, 6, 6, 5, + 1, 1, 2, 1, 0, 5, 0, 2, 2, 0, 0, 4, 4, 6, 0, 1, + 5, 0, 5, 6, 0, 6, 5, 8, 1, 5, 9, 0, 10, 6, 1, 0, + 2, 2, 4, 4, 4, 5, 7, 0, 8, 1, 8, 0, 8, 8, 9, 2, + 4, 10, 4, 1, 3, 3, 3, 1, 3, 0, 5, 7, 7, 7, 6, 2, + 6, 1, 2, 5, 9, 10, 4, 2, 1, 8, 8, 5, 1, 3, 6, 11, + 7, 12, 2, 9, 13, 6, 13, 13, 13, 0, 11, 0, 5, 2, 2, 6, + 6, 3, 3, 5, 5, 3, 0, 13, 5, 9, +}; + +/* Indic_Positional_Category: 1842 bytes. */ + +RE_UINT32 re_get_indic_positional_category(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 13; + code = ch ^ (f << 13); + pos = (RE_UINT32)re_indic_positional_category_stage_1[f] << 5; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_indic_positional_category_stage_2[pos + f] << 4; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_indic_positional_category_stage_3[pos + f] << 3; + f = code >> 1; + code ^= f << 1; + pos = (RE_UINT32)re_indic_positional_category_stage_4[pos + f] << 1; + value = re_indic_positional_category_stage_5[pos + code]; + + return value; +} + +/* Indic_Syllabic_Category. */ + +static RE_UINT8 re_indic_syllabic_category_stage_1[] = { + 0, 1, 2, 2, 2, 3, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_indic_syllabic_category_stage_2[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 1, 1, 1, 1, 1, 1, 10, 1, 11, 12, 13, 14, 1, 1, 1, + 15, 1, 1, 1, 1, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 17, 18, 19, 20, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 21, 1, 1, 1, 1, 1, + 22, 23, 24, 25, 26, 27, 28, 29, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_indic_syllabic_category_stage_3[] = { + 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 12, 20, + 21, 15, 16, 22, 23, 24, 25, 26, 27, 28, 16, 29, 30, 0, 12, 31, + 14, 15, 16, 29, 32, 33, 12, 34, 35, 36, 37, 38, 39, 40, 25, 0, + 41, 42, 16, 43, 44, 45, 12, 0, 46, 42, 16, 47, 44, 48, 12, 49, + 46, 42, 8, 50, 51, 52, 12, 53, 54, 55, 8, 56, 57, 58, 25, 59, + 60, 8, 61, 62, 63, 2, 0, 0, 64, 65, 66, 67, 68, 69, 0, 0, + 0, 0, 70, 71, 72, 8, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, + 8, 8, 80, 81, 82, 83, 84, 85, 86, 87, 0, 0, 0, 0, 0, 0, + 88, 89, 90, 89, 90, 91, 88, 92, 8, 8, 93, 94, 95, 96, 2, 0, + 97, 61, 98, 99, 25, 8, 100, 101, 8, 8, 102, 103, 104, 2, 0, 0, + 8, 105, 8, 8, 106, 107, 108, 109, 2, 2, 0, 0, 0, 0, 0, 0, + 110, 90, 8, 111, 112, 2, 0, 0, 113, 8, 114, 115, 8, 8, 116, 117, + 8, 8, 118, 119, 120, 0, 0, 0, 0, 0, 0, 0, 0, 121, 122, 123, + 124, 125, 0, 0, 0, 0, 0, 126, 127, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, + 129, 8, 130, 0, 8, 131, 132, 133, 134, 135, 8, 136, 137, 2, 138, 122, + 139, 8, 140, 8, 141, 142, 0, 0, 143, 8, 8, 144, 145, 2, 146, 147, + 148, 8, 149, 150, 151, 2, 8, 152, 8, 8, 8, 153, 154, 0, 155, 156, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 158, 159, 2, + 160, 161, 8, 162, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 164, 90, 8, 165, 166, 167, 168, 169, 170, 8, 8, 171, 0, 0, 0, 0, + 172, 8, 173, 174, 0, 175, 8, 176, 177, 178, 8, 179, 180, 2, 181, 182, + 183, 184, 185, 186, 0, 0, 0, 0, 187, 188, 189, 190, 8, 191, 192, 2, + 193, 15, 16, 29, 32, 40, 194, 195, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 196, 8, 8, 197, 198, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 199, 8, 200, 201, 202, 203, 0, 0, + 199, 8, 8, 204, 205, 2, 0, 0, 190, 8, 206, 207, 2, 0, 0, 0, + 8, 208, 209, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_indic_syllabic_category_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 0, 4, 0, 0, 0, + 5, 0, 0, 0, 0, 6, 0, 0, 7, 8, 8, 8, 8, 9, 10, 10, + 10, 10, 10, 10, 10, 10, 11, 12, 13, 13, 13, 14, 15, 16, 10, 10, + 17, 18, 2, 2, 19, 8, 10, 10, 20, 21, 8, 22, 22, 9, 10, 10, + 10, 10, 23, 10, 24, 25, 26, 12, 13, 27, 27, 28, 0, 29, 0, 30, + 26, 0, 0, 0, 20, 21, 31, 32, 23, 33, 26, 34, 35, 29, 27, 36, + 0, 0, 37, 24, 0, 18, 2, 2, 38, 39, 0, 0, 20, 21, 8, 40, + 40, 9, 10, 10, 23, 37, 26, 12, 13, 41, 41, 36, 0, 0, 42, 0, + 13, 27, 27, 36, 0, 43, 0, 30, 42, 0, 0, 0, 44, 21, 31, 19, + 45, 46, 33, 23, 47, 48, 49, 25, 10, 10, 26, 43, 35, 43, 50, 36, + 0, 29, 0, 0, 7, 21, 8, 45, 45, 9, 10, 10, 10, 10, 26, 51, + 13, 50, 50, 36, 0, 52, 49, 0, 20, 21, 8, 45, 10, 37, 26, 12, + 0, 52, 0, 53, 54, 0, 0, 0, 10, 10, 49, 51, 13, 50, 50, 55, + 0, 29, 0, 32, 0, 0, 56, 57, 58, 21, 8, 8, 8, 31, 25, 10, + 30, 10, 10, 42, 10, 49, 59, 29, 13, 60, 13, 13, 43, 0, 0, 0, + 37, 10, 10, 10, 10, 10, 10, 49, 13, 13, 61, 0, 13, 41, 62, 63, + 33, 64, 24, 42, 0, 10, 37, 10, 37, 65, 25, 33, 13, 13, 41, 66, + 13, 67, 62, 68, 2, 2, 3, 10, 2, 2, 2, 2, 2, 69, 70, 0, + 10, 10, 37, 10, 10, 10, 10, 48, 16, 13, 13, 71, 72, 73, 74, 75, + 76, 76, 77, 76, 76, 76, 76, 76, 76, 76, 76, 78, 0, 79, 0, 0, + 80, 8, 81, 13, 13, 82, 83, 84, 2, 2, 3, 85, 86, 17, 87, 88, + 89, 90, 91, 92, 93, 94, 10, 10, 95, 96, 62, 97, 2, 2, 98, 99, + 100, 10, 10, 23, 11, 101, 0, 0, 100, 10, 10, 10, 11, 0, 0, 0, + 102, 0, 0, 0, 103, 8, 8, 8, 8, 43, 13, 13, 13, 71, 104, 105, + 106, 0, 0, 107, 108, 10, 10, 10, 13, 13, 109, 0, 110, 111, 112, 0, + 113, 114, 114, 115, 116, 117, 0, 0, 10, 10, 10, 0, 13, 13, 13, 13, + 118, 111, 119, 0, 10, 120, 13, 0, 10, 10, 10, 80, 100, 121, 111, 122, + 123, 13, 13, 13, 13, 91, 124, 125, 126, 127, 8, 8, 10, 128, 13, 13, + 13, 129, 10, 0, 130, 8, 131, 10, 132, 13, 133, 134, 2, 2, 135, 136, + 10, 137, 13, 13, 138, 0, 0, 0, 10, 139, 13, 118, 111, 140, 0, 0, + 2, 2, 3, 37, 141, 142, 142, 142, 143, 0, 0, 0, 144, 145, 143, 0, + 0, 0, 0, 146, 147, 4, 0, 0, 0, 148, 0, 0, 5, 148, 0, 0, + 0, 0, 0, 4, 40, 149, 150, 10, 120, 13, 0, 0, 10, 10, 10, 151, + 152, 153, 154, 10, 155, 0, 0, 0, 156, 8, 8, 8, 131, 10, 10, 10, + 10, 157, 13, 13, 13, 158, 0, 0, 142, 142, 142, 142, 2, 2, 159, 10, + 151, 114, 160, 119, 10, 120, 13, 161, 162, 0, 0, 0, 163, 8, 9, 100, + 164, 13, 13, 165, 158, 0, 0, 0, 10, 166, 10, 10, 2, 2, 159, 49, + 8, 131, 10, 10, 10, 10, 93, 13, 167, 168, 0, 0, 111, 111, 111, 169, + 37, 0, 170, 92, 13, 13, 13, 96, 171, 0, 0, 0, 131, 10, 120, 13, + 0, 172, 0, 0, 10, 10, 10, 86, 173, 10, 174, 111, 175, 13, 35, 176, + 93, 52, 0, 71, 10, 37, 37, 10, 10, 0, 177, 178, 2, 2, 0, 0, + 179, 180, 8, 8, 10, 10, 13, 13, 13, 181, 0, 0, 182, 183, 183, 183, + 183, 184, 2, 2, 0, 0, 0, 185, 186, 8, 8, 9, 13, 13, 187, 0, + 186, 100, 10, 10, 10, 120, 13, 13, 188, 189, 2, 2, 114, 190, 10, 10, + 164, 0, 0, 0, 186, 8, 8, 8, 9, 10, 10, 10, 120, 13, 13, 13, + 191, 0, 192, 67, 193, 2, 2, 2, 2, 194, 0, 0, 8, 8, 10, 10, + 30, 10, 10, 10, 10, 10, 10, 13, 13, 195, 0, 0, 8, 49, 23, 30, + 10, 10, 10, 30, 10, 10, 48, 0, 8, 8, 131, 10, 10, 10, 10, 150, + 13, 13, 196, 0, 7, 21, 8, 22, 17, 197, 142, 145, 142, 145, 0, 0, + 21, 8, 8, 100, 13, 13, 13, 198, 199, 107, 0, 0, 8, 8, 8, 131, + 10, 10, 10, 120, 13, 99, 13, 200, 201, 0, 0, 0, 0, 0, 8, 99, + 13, 13, 13, 202, 67, 0, 0, 0, 10, 10, 150, 203, 13, 204, 0, 0, + 10, 10, 26, 205, 13, 13, 206, 0, 2, 2, 2, 0, +}; + +static RE_UINT8 re_indic_syllabic_category_stage_5[] = { + 0, 0, 0, 0, 0, 11, 0, 0, 33, 33, 33, 33, 33, 33, 0, 0, + 11, 0, 0, 0, 0, 0, 28, 28, 0, 0, 0, 11, 1, 1, 1, 2, + 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 9, 9, + 4, 3, 9, 9, 9, 9, 9, 9, 9, 5, 9, 9, 0, 26, 26, 0, + 0, 9, 9, 9, 8, 8, 9, 9, 0, 0, 33, 33, 0, 0, 8, 8, + 0, 1, 1, 2, 0, 8, 8, 8, 8, 0, 0, 8, 12, 0, 12, 12, + 12, 0, 12, 0, 0, 0, 12, 12, 12, 12, 0, 0, 9, 0, 0, 9, + 9, 5, 13, 0, 0, 0, 0, 9, 12, 12, 0, 12, 8, 8, 8, 0, + 0, 0, 0, 8, 0, 12, 12, 0, 4, 0, 9, 9, 9, 9, 9, 0, + 9, 5, 0, 0, 0, 12, 12, 12, 1, 25, 11, 11, 0, 19, 0, 0, + 8, 8, 0, 8, 9, 9, 0, 9, 0, 12, 0, 0, 0, 0, 9, 9, + 0, 0, 1, 22, 8, 0, 8, 8, 8, 12, 0, 0, 0, 0, 0, 12, + 12, 0, 0, 0, 12, 12, 12, 0, 9, 0, 9, 9, 0, 3, 9, 9, + 0, 9, 9, 0, 0, 0, 12, 0, 0, 14, 14, 0, 9, 5, 16, 0, + 0, 0, 13, 13, 13, 13, 13, 13, 0, 0, 1, 2, 0, 0, 5, 0, + 9, 0, 9, 0, 9, 9, 6, 0, 24, 24, 24, 24, 29, 1, 6, 0, + 12, 0, 0, 12, 0, 12, 0, 12, 19, 19, 0, 0, 9, 0, 0, 0, + 0, 1, 0, 0, 0, 28, 0, 28, 0, 4, 0, 0, 9, 9, 1, 2, + 9, 9, 1, 1, 6, 3, 0, 0, 21, 21, 21, 21, 21, 18, 18, 18, + 18, 18, 18, 18, 0, 18, 18, 18, 18, 0, 0, 0, 0, 0, 28, 0, + 12, 8, 8, 8, 8, 8, 8, 9, 9, 9, 1, 24, 2, 7, 6, 19, + 19, 19, 19, 12, 0, 0, 11, 0, 12, 12, 8, 8, 9, 9, 12, 12, + 12, 12, 19, 19, 19, 12, 9, 24, 24, 12, 12, 9, 9, 24, 24, 24, + 24, 24, 12, 12, 12, 9, 9, 9, 9, 12, 12, 12, 12, 12, 19, 9, + 9, 9, 9, 24, 24, 24, 12, 24, 33, 33, 24, 24, 9, 9, 0, 0, + 8, 8, 8, 12, 6, 0, 0, 0, 12, 0, 9, 9, 12, 12, 12, 8, + 9, 27, 27, 28, 17, 29, 28, 28, 28, 6, 7, 28, 3, 0, 0, 0, + 11, 12, 12, 12, 9, 18, 18, 18, 20, 20, 1, 20, 20, 20, 20, 20, + 20, 20, 9, 28, 12, 12, 12, 10, 10, 10, 10, 10, 10, 10, 0, 0, + 23, 23, 23, 23, 23, 0, 0, 0, 9, 20, 20, 20, 24, 24, 0, 0, + 12, 12, 12, 9, 12, 19, 19, 20, 20, 20, 20, 0, 7, 9, 9, 9, + 24, 24, 28, 28, 28, 0, 0, 28, 1, 1, 1, 17, 2, 8, 8, 8, + 4, 9, 9, 9, 5, 12, 12, 12, 1, 17, 2, 8, 8, 8, 12, 12, + 12, 18, 18, 18, 9, 9, 6, 7, 18, 18, 12, 12, 33, 33, 3, 12, + 12, 12, 20, 20, 8, 8, 4, 9, 20, 20, 6, 6, 18, 18, 9, 9, + 1, 1, 28, 4, 26, 26, 26, 0, 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 2, 2, 26, 0, 0, 0, 30, 31, 0, 0, 11, 11, 11, 11, + 28, 0, 0, 0, 8, 8, 6, 12, 12, 12, 12, 1, 12, 12, 10, 10, + 10, 10, 12, 12, 12, 12, 10, 18, 18, 12, 12, 12, 12, 18, 12, 1, + 1, 2, 8, 8, 20, 9, 9, 9, 5, 0, 0, 0, 33, 33, 12, 12, + 10, 10, 10, 24, 9, 9, 9, 20, 20, 20, 20, 6, 1, 1, 17, 2, + 12, 12, 12, 4, 9, 18, 19, 19, 12, 9, 0, 12, 9, 9, 9, 19, + 19, 19, 19, 0, 20, 20, 0, 0, 0, 0, 12, 24, 23, 24, 23, 0, + 0, 2, 7, 0, 12, 8, 12, 12, 12, 12, 12, 20, 20, 20, 20, 9, + 24, 6, 0, 0, 4, 4, 4, 0, 0, 0, 0, 7, 1, 1, 2, 14, + 14, 8, 8, 8, 9, 9, 5, 0, 0, 0, 34, 34, 34, 34, 34, 34, + 34, 34, 33, 33, 0, 0, 0, 32, 1, 1, 2, 8, 9, 5, 4, 0, + 9, 9, 9, 7, 6, 0, 33, 33, 10, 12, 12, 12, 5, 3, 15, 15, + 0, 0, 4, 9, 0, 33, 33, 33, 33, 0, 0, 0, 1, 5, 4, 25, + 9, 4, 6, 0, 0, 0, 26, 26, 9, 9, 9, 1, 1, 2, 5, 4, + 1, 1, 2, 5, 4, 0, 0, 0, 9, 1, 2, 5, 2, 9, 9, 9, + 9, 9, 5, 4, 0, 19, 19, 19, 9, 9, 9, 6, +}; + +/* Indic_Syllabic_Category: 2448 bytes. */ + +RE_UINT32 re_get_indic_syllabic_category(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 13; + code = ch ^ (f << 13); + pos = (RE_UINT32)re_indic_syllabic_category_stage_1[f] << 5; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_indic_syllabic_category_stage_2[pos + f] << 4; + f = code >> 4; + code ^= f << 4; + pos = (RE_UINT32)re_indic_syllabic_category_stage_3[pos + f] << 2; + f = code >> 2; + code ^= f << 2; + pos = (RE_UINT32)re_indic_syllabic_category_stage_4[pos + f] << 2; + value = re_indic_syllabic_category_stage_5[pos + code]; + + return value; +} + +/* Alphanumeric. */ + +static RE_UINT8 re_alphanumeric_stage_1[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, +}; + +static RE_UINT8 re_alphanumeric_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 13, 13, 26, 27, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 28, 7, 29, 30, 7, 31, 13, 13, 13, 13, 13, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_alphanumeric_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 17, 18, 19, 1, 20, 21, 22, 23, 24, 25, 26, 27, 1, 28, + 29, 30, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 31, + 36, 37, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 40, 1, 41, 42, 43, 44, 45, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 47, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 48, 49, 1, 50, 51, 52, 53, 54, 55, 56, 57, 58, 1, 59, + 60, 61, 62, 63, 64, 31, 31, 31, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 31, 74, 31, 31, 31, 31, 31, 1, 1, 1, 75, 76, 77, 31, 31, + 1, 1, 1, 1, 78, 31, 31, 31, 31, 31, 31, 31, 1, 1, 79, 31, + 1, 1, 80, 81, 31, 31, 31, 82, 83, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 84, 31, 31, 31, 31, 31, 31, 31, 85, 86, 87, 88, + 89, 31, 31, 31, 31, 31, 90, 31, 31, 91, 31, 31, 31, 31, 31, 31, + 1, 1, 1, 1, 1, 1, 92, 1, 1, 1, 1, 1, 1, 1, 1, 93, + 94, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 95, 31, + 1, 1, 96, 31, 31, 31, 31, 31, +}; + +static RE_UINT8 re_alphanumeric_stage_4[] = { + 0, 1, 2, 2, 0, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 7, 0, 0, 8, 9, 10, 11, 5, 12, + 5, 5, 5, 5, 13, 5, 5, 5, 5, 14, 15, 16, 17, 18, 19, 20, + 21, 5, 22, 23, 5, 5, 24, 25, 26, 5, 27, 5, 5, 28, 5, 29, + 30, 31, 32, 0, 0, 33, 0, 34, 5, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 47, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 61, 65, 66, 67, 68, 69, 70, 71, + 16, 72, 73, 0, 74, 75, 76, 0, 77, 78, 79, 80, 81, 82, 0, 0, + 5, 83, 84, 85, 86, 5, 87, 88, 5, 5, 89, 5, 90, 91, 92, 5, + 93, 5, 94, 0, 95, 5, 5, 96, 16, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 97, 2, 5, 5, 98, 99, 100, 100, 101, 5, 102, 103, 78, + 1, 5, 5, 104, 5, 105, 5, 106, 107, 108, 109, 110, 5, 111, 112, 0, + 113, 5, 107, 114, 112, 115, 0, 0, 5, 116, 117, 0, 5, 118, 5, 119, + 5, 106, 120, 121, 0, 0, 0, 122, 5, 5, 5, 5, 5, 5, 0, 123, + 96, 5, 124, 121, 5, 125, 126, 127, 0, 0, 0, 128, 129, 0, 0, 0, + 130, 131, 132, 5, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 134, 5, 78, 5, 135, 107, 5, 5, 5, 5, 136, + 5, 87, 5, 137, 138, 139, 139, 5, 0, 140, 0, 0, 0, 0, 0, 0, + 141, 142, 16, 5, 143, 16, 5, 88, 144, 145, 5, 5, 146, 72, 0, 26, + 5, 5, 5, 5, 5, 106, 0, 0, 5, 5, 5, 5, 5, 5, 106, 0, + 5, 5, 5, 5, 31, 0, 26, 121, 147, 148, 5, 149, 5, 5, 5, 95, + 150, 151, 5, 5, 152, 153, 0, 150, 154, 17, 5, 100, 5, 5, 155, 156, + 5, 105, 157, 82, 5, 158, 159, 160, 5, 138, 161, 162, 5, 107, 163, 164, + 165, 166, 88, 167, 5, 5, 5, 168, 5, 5, 5, 5, 5, 169, 170, 113, + 5, 5, 5, 171, 5, 5, 172, 0, 173, 174, 175, 5, 5, 28, 176, 5, + 5, 121, 26, 5, 177, 5, 17, 178, 0, 0, 0, 179, 5, 5, 5, 82, + 1, 2, 2, 109, 5, 107, 180, 0, 181, 182, 183, 0, 5, 5, 5, 72, + 0, 0, 5, 33, 0, 0, 0, 0, 0, 0, 0, 0, 82, 5, 184, 0, + 5, 26, 105, 72, 121, 5, 185, 0, 5, 5, 5, 5, 121, 78, 0, 0, + 5, 186, 5, 187, 0, 0, 0, 0, 5, 138, 106, 17, 0, 0, 0, 0, + 188, 189, 106, 138, 107, 0, 0, 190, 106, 172, 0, 0, 5, 191, 0, 0, + 192, 100, 0, 82, 82, 0, 79, 193, 5, 106, 106, 157, 28, 0, 0, 0, + 5, 5, 133, 0, 5, 157, 5, 157, 5, 5, 194, 56, 151, 32, 26, 195, + 5, 196, 26, 197, 5, 5, 198, 0, 199, 200, 0, 0, 201, 202, 5, 195, + 38, 47, 203, 187, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 204, 0, + 0, 0, 0, 0, 5, 205, 206, 0, 5, 107, 207, 0, 5, 106, 78, 0, + 208, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 209, + 0, 0, 0, 0, 0, 0, 5, 32, 5, 5, 5, 5, 172, 0, 0, 0, + 5, 5, 5, 146, 5, 5, 5, 5, 5, 5, 187, 0, 0, 0, 0, 0, + 5, 146, 0, 0, 0, 0, 0, 0, 5, 5, 210, 0, 0, 0, 0, 0, + 5, 32, 107, 78, 0, 0, 26, 211, 5, 138, 155, 212, 95, 0, 0, 0, + 5, 5, 213, 107, 176, 0, 0, 0, 214, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 215, 216, 0, 0, 0, 5, 5, 217, 5, 218, 219, 220, 5, + 221, 222, 223, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 224, 225, 88, + 217, 217, 135, 135, 226, 226, 227, 5, 5, 5, 5, 5, 5, 5, 193, 0, + 220, 228, 229, 230, 231, 232, 0, 0, 0, 26, 84, 84, 78, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 138, 0, 5, 33, 5, 5, 5, 5, 5, 5, + 121, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 214, 0, 0, + 121, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_alphanumeric_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 254, 255, 255, 7, 0, 4, 32, 4, + 255, 255, 127, 255, 255, 255, 255, 255, 195, 255, 3, 0, 31, 80, 0, 0, + 32, 0, 0, 0, 0, 0, 223, 188, 64, 215, 255, 255, 251, 255, 255, 255, + 255, 255, 191, 255, 3, 252, 255, 255, 255, 255, 254, 255, 255, 255, 127, 2, + 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 191, 182, 0, 255, 255, + 255, 7, 7, 0, 0, 0, 255, 7, 255, 255, 255, 254, 255, 195, 255, 255, + 255, 255, 239, 31, 254, 225, 255, 159, 0, 0, 255, 255, 0, 224, 255, 255, + 255, 255, 3, 0, 255, 7, 48, 4, 255, 255, 255, 252, 255, 31, 0, 0, + 255, 255, 255, 1, 255, 255, 31, 0, 248, 3, 255, 255, 255, 255, 255, 239, + 255, 223, 225, 255, 207, 255, 254, 255, 239, 159, 249, 255, 255, 253, 197, 227, + 159, 89, 128, 176, 207, 255, 3, 0, 238, 135, 249, 255, 255, 253, 109, 195, + 135, 25, 2, 94, 192, 255, 63, 0, 238, 191, 251, 255, 255, 253, 237, 227, + 191, 27, 1, 0, 207, 255, 0, 2, 238, 159, 249, 255, 159, 25, 192, 176, + 207, 255, 2, 0, 236, 199, 61, 214, 24, 199, 255, 195, 199, 29, 129, 0, + 192, 255, 0, 0, 239, 223, 253, 255, 255, 253, 255, 227, 223, 29, 96, 7, + 207, 255, 0, 0, 238, 223, 253, 255, 255, 253, 239, 227, 223, 29, 96, 64, + 207, 255, 6, 0, 255, 255, 255, 231, 223, 93, 128, 128, 207, 255, 0, 252, + 236, 255, 127, 252, 255, 255, 251, 47, 127, 128, 95, 255, 192, 255, 12, 0, + 255, 255, 255, 7, 127, 32, 255, 3, 150, 37, 240, 254, 174, 236, 255, 59, + 95, 32, 255, 243, 1, 0, 0, 0, 255, 3, 0, 0, 255, 254, 255, 255, + 255, 31, 254, 255, 3, 255, 255, 254, 255, 255, 255, 31, 255, 255, 127, 249, + 255, 3, 255, 255, 231, 193, 255, 255, 127, 64, 255, 51, 191, 32, 255, 255, + 255, 255, 255, 247, 255, 61, 127, 61, 255, 61, 255, 255, 255, 255, 61, 127, + 61, 255, 127, 255, 255, 255, 61, 255, 255, 255, 255, 135, 255, 255, 0, 0, + 255, 255, 63, 63, 255, 159, 255, 255, 255, 199, 255, 1, 255, 223, 15, 0, + 255, 255, 15, 0, 255, 223, 13, 0, 255, 255, 207, 255, 255, 1, 128, 16, + 255, 255, 255, 0, 255, 7, 255, 255, 255, 255, 63, 0, 255, 255, 255, 127, + 255, 15, 255, 1, 192, 255, 255, 255, 255, 63, 31, 0, 255, 15, 255, 255, + 255, 3, 255, 3, 255, 255, 255, 15, 254, 255, 31, 0, 128, 0, 0, 0, + 255, 255, 239, 255, 239, 15, 255, 3, 255, 243, 255, 255, 191, 255, 3, 0, + 255, 227, 255, 255, 255, 255, 255, 63, 0, 222, 111, 0, 128, 255, 31, 0, + 63, 63, 255, 170, 255, 255, 223, 95, 220, 31, 207, 15, 255, 31, 220, 31, + 0, 0, 2, 128, 0, 0, 255, 31, 132, 252, 47, 62, 80, 189, 255, 243, + 224, 67, 0, 0, 255, 1, 0, 0, 0, 0, 192, 255, 255, 127, 255, 255, + 31, 120, 12, 0, 255, 128, 0, 0, 255, 255, 127, 0, 127, 127, 127, 127, + 0, 128, 0, 0, 224, 0, 0, 0, 254, 3, 62, 31, 255, 255, 127, 224, + 224, 255, 255, 255, 255, 63, 254, 255, 255, 127, 0, 0, 255, 31, 255, 255, + 255, 15, 0, 0, 255, 127, 240, 143, 0, 0, 128, 255, 252, 255, 255, 255, + 255, 249, 255, 255, 255, 63, 255, 0, 187, 247, 255, 255, 15, 0, 255, 3, + 0, 0, 252, 40, 255, 255, 7, 0, 255, 255, 247, 255, 0, 128, 255, 3, + 223, 255, 255, 127, 255, 63, 255, 3, 255, 255, 127, 196, 5, 0, 0, 56, + 255, 255, 60, 0, 126, 126, 126, 0, 127, 127, 255, 255, 63, 0, 255, 255, + 255, 7, 255, 3, 15, 0, 255, 255, 127, 248, 255, 255, 255, 63, 255, 255, + 255, 255, 255, 3, 127, 0, 248, 224, 255, 253, 127, 95, 219, 255, 255, 255, + 0, 0, 248, 255, 255, 255, 252, 255, 0, 0, 255, 15, 0, 0, 223, 255, + 252, 252, 252, 28, 255, 239, 255, 255, 127, 255, 255, 183, 255, 63, 255, 63, + 255, 255, 1, 0, 15, 255, 62, 0, 255, 0, 255, 255, 15, 0, 0, 0, + 63, 253, 255, 255, 255, 255, 191, 145, 255, 255, 55, 0, 255, 255, 255, 192, + 111, 240, 239, 254, 31, 0, 0, 0, 63, 0, 0, 0, 255, 1, 255, 3, + 255, 255, 199, 255, 255, 255, 71, 0, 30, 0, 255, 23, 255, 255, 251, 255, + 255, 255, 159, 0, 127, 189, 255, 191, 255, 1, 255, 255, 159, 25, 129, 224, + 179, 0, 255, 3, 255, 255, 63, 127, 0, 0, 0, 63, 17, 0, 255, 3, + 255, 255, 255, 227, 255, 3, 0, 128, 127, 0, 0, 0, 255, 63, 0, 0, + 248, 255, 255, 224, 31, 0, 255, 255, 3, 0, 0, 0, 255, 7, 255, 31, + 255, 1, 255, 67, 255, 255, 223, 255, 255, 255, 255, 223, 100, 222, 255, 235, + 239, 255, 255, 255, 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, + 63, 255, 255, 255, 253, 255, 255, 247, 255, 253, 255, 255, 247, 207, 255, 255, + 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, 255, 251, 255, 15, + 238, 251, 255, 15, +}; + +/* Alphanumeric: 2117 bytes. */ + +RE_UINT32 re_get_alphanumeric(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_alphanumeric_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_alphanumeric_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_alphanumeric_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_alphanumeric_stage_4[pos + f] << 5; + pos += code; + value = (re_alphanumeric_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Any. */ + +RE_UINT32 re_get_any(RE_UINT32 ch) { + return 1; +} + +/* Blank. */ + +static RE_UINT8 re_blank_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_blank_stage_2[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_blank_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_blank_stage_4[] = { + 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 1, 1, 1, 1, 4, 5, 1, 1, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_blank_stage_5[] = { + 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 255, 7, 0, 0, 0, 128, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, +}; + +/* Blank: 169 bytes. */ + +RE_UINT32 re_get_blank(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_blank_stage_1[f] << 3; + f = code >> 13; + code ^= f << 13; + pos = (RE_UINT32)re_blank_stage_2[pos + f] << 4; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_blank_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_blank_stage_4[pos + f] << 6; + pos += code; + value = (re_blank_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Graph. */ + +static RE_UINT8 re_graph_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 4, 8, + 4, 8, +}; + +static RE_UINT8 re_graph_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 7, 7, 7, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 26, 13, 27, 28, 29, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 30, 7, 31, 32, 7, 33, 13, 13, 13, 13, 13, 34, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 35, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 36, +}; + +static RE_UINT8 re_graph_stage_3[] = { + 0, 1, 1, 2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 1, 15, 16, 1, 1, 17, 18, 19, 20, 21, 22, 23, 24, 1, 25, + 26, 27, 1, 28, 29, 1, 1, 1, 1, 1, 1, 30, 31, 32, 33, 34, + 35, 36, 37, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 40, 1, 41, 42, 43, 44, 45, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 47, 48, 48, 48, 48, 48, 48, 48, 48, + 1, 1, 49, 50, 1, 51, 52, 53, 54, 55, 56, 57, 58, 59, 1, 60, + 61, 62, 63, 64, 65, 48, 66, 48, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 48, 76, 48, 48, 48, 48, 48, 1, 1, 1, 77, 78, 79, 48, 48, + 1, 1, 1, 1, 80, 48, 48, 48, 48, 48, 48, 48, 1, 1, 81, 48, + 1, 1, 82, 83, 48, 48, 48, 84, 85, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 86, 48, 48, 48, 87, 88, 89, 90, 91, 92, 93, 94, + 1, 1, 95, 48, 48, 48, 48, 48, 96, 48, 48, 48, 48, 48, 97, 48, + 98, 99, 100, 1, 1, 101, 102, 103, 104, 105, 48, 48, 48, 48, 48, 48, + 1, 1, 1, 1, 1, 1, 106, 1, 1, 1, 1, 1, 1, 1, 1, 107, + 108, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 109, 48, + 1, 1, 110, 48, 48, 48, 48, 48, 111, 112, 48, 48, 48, 48, 48, 48, + 1, 1, 1, 1, 1, 1, 1, 113, +}; + +static RE_UINT8 re_graph_stage_4[] = { + 0, 1, 2, 3, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 4, 5, 6, 2, 2, 2, 7, 8, 1, 9, 2, 10, 11, + 12, 2, 2, 2, 2, 2, 2, 2, 13, 2, 14, 2, 2, 15, 2, 16, + 2, 17, 18, 0, 0, 19, 0, 20, 2, 2, 2, 2, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 30, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 44, 48, 49, 50, 51, 52, 53, 54, + 1, 55, 56, 0, 57, 58, 59, 0, 2, 2, 60, 61, 62, 12, 63, 0, + 2, 2, 2, 2, 2, 2, 64, 2, 2, 2, 65, 2, 66, 67, 68, 2, + 69, 2, 48, 70, 71, 2, 2, 72, 2, 2, 2, 2, 73, 2, 2, 74, + 75, 76, 77, 78, 2, 2, 79, 80, 81, 2, 2, 82, 2, 83, 2, 84, + 3, 85, 86, 87, 2, 88, 89, 2, 90, 2, 3, 91, 80, 17, 0, 0, + 2, 2, 88, 70, 2, 2, 2, 92, 2, 93, 94, 2, 0, 0, 10, 95, + 2, 2, 2, 2, 2, 2, 2, 96, 72, 2, 97, 79, 2, 98, 99, 100, + 101, 102, 3, 103, 104, 3, 105, 106, 2, 2, 2, 2, 88, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 16, 2, 107, 108, 2, 2, 2, 2, 2, + 2, 2, 2, 109, 110, 111, 112, 113, 2, 114, 3, 2, 2, 2, 2, 115, + 2, 64, 2, 116, 76, 117, 117, 2, 2, 2, 118, 0, 119, 2, 2, 77, + 2, 2, 2, 2, 2, 2, 84, 120, 1, 2, 1, 2, 8, 2, 2, 2, + 121, 122, 2, 2, 114, 16, 2, 123, 3, 2, 2, 2, 2, 2, 2, 3, + 2, 2, 2, 2, 2, 84, 2, 2, 2, 2, 2, 2, 2, 2, 84, 0, + 2, 2, 2, 2, 124, 2, 125, 2, 2, 126, 2, 2, 2, 2, 2, 82, + 2, 2, 2, 2, 2, 127, 0, 128, 2, 129, 2, 82, 2, 2, 130, 79, + 2, 2, 131, 70, 2, 2, 132, 3, 2, 76, 133, 2, 2, 2, 134, 76, + 135, 136, 2, 137, 2, 2, 2, 138, 2, 2, 2, 2, 2, 123, 139, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 140, 2, 2, 71, 0, + 141, 142, 143, 2, 2, 2, 144, 2, 2, 2, 105, 2, 145, 2, 146, 147, + 71, 2, 148, 149, 2, 2, 2, 91, 1, 2, 2, 2, 2, 3, 150, 151, + 152, 153, 154, 0, 2, 2, 2, 16, 155, 156, 2, 2, 157, 158, 105, 79, + 0, 0, 0, 0, 70, 2, 106, 56, 2, 123, 83, 16, 159, 2, 160, 0, + 2, 2, 2, 2, 79, 161, 0, 0, 2, 10, 2, 162, 0, 0, 0, 0, + 2, 76, 84, 146, 0, 0, 0, 0, 163, 164, 165, 2, 3, 166, 0, 167, + 168, 169, 0, 0, 2, 170, 145, 2, 171, 172, 173, 2, 2, 0, 2, 174, + 2, 175, 110, 176, 177, 178, 0, 0, 2, 2, 179, 0, 2, 180, 2, 181, + 0, 0, 0, 3, 0, 0, 0, 0, 2, 2, 182, 183, 2, 2, 184, 185, + 2, 98, 123, 76, 2, 2, 140, 186, 187, 79, 0, 0, 188, 189, 2, 190, + 21, 30, 191, 192, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 193, 0, + 0, 0, 0, 0, 2, 110, 79, 0, 2, 2, 194, 0, 2, 82, 161, 0, + 111, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 195, + 0, 0, 0, 0, 0, 0, 2, 74, 2, 2, 2, 2, 71, 0, 0, 0, + 2, 2, 2, 196, 2, 2, 2, 2, 2, 2, 197, 0, 0, 0, 0, 0, + 2, 198, 0, 0, 0, 0, 0, 0, 2, 2, 107, 0, 0, 0, 0, 0, + 2, 74, 3, 199, 0, 0, 105, 200, 2, 2, 201, 202, 203, 0, 0, 0, + 2, 2, 204, 3, 205, 0, 0, 0, 206, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 207, 208, 197, 0, 0, 2, 2, 2, 2, 2, 2, 2, 84, + 2, 209, 2, 2, 2, 2, 2, 179, 2, 2, 210, 0, 0, 0, 0, 0, + 2, 2, 76, 15, 0, 0, 0, 0, 2, 2, 98, 2, 12, 211, 212, 2, + 213, 214, 215, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 216, 2, 2, + 2, 2, 2, 2, 2, 2, 217, 2, 2, 2, 2, 2, 218, 219, 0, 0, + 2, 2, 2, 2, 2, 2, 220, 0, 212, 221, 222, 223, 224, 225, 0, 226, + 2, 88, 2, 2, 77, 227, 228, 84, 124, 114, 2, 88, 16, 0, 0, 229, + 230, 16, 231, 0, 0, 0, 0, 0, 2, 2, 2, 119, 2, 212, 2, 2, + 2, 2, 2, 2, 2, 2, 106, 232, 2, 2, 2, 77, 2, 2, 19, 0, + 88, 2, 193, 2, 10, 233, 0, 0, 234, 0, 0, 0, 235, 0, 158, 0, + 2, 2, 2, 2, 2, 2, 76, 0, 2, 19, 2, 2, 2, 2, 2, 2, + 79, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 206, 0, 0, + 79, 0, 0, 0, 0, 0, 0, 0, 236, 2, 2, 2, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 203, 2, 2, 2, 2, 2, 2, 2, 79, +}; + +static RE_UINT8 re_graph_stage_5[] = { + 0, 0, 0, 0, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, + 255, 255, 255, 252, 240, 215, 255, 255, 251, 255, 255, 255, 255, 255, 254, 255, + 255, 255, 127, 254, 255, 230, 254, 255, 255, 0, 255, 255, 255, 7, 31, 0, + 255, 255, 255, 223, 255, 191, 255, 255, 255, 231, 255, 255, 255, 255, 3, 0, + 255, 255, 255, 7, 255, 63, 255, 127, 255, 255, 255, 79, 255, 255, 31, 0, + 248, 255, 255, 255, 239, 159, 249, 255, 255, 253, 197, 243, 159, 121, 128, 176, + 207, 255, 255, 15, 238, 135, 249, 255, 255, 253, 109, 211, 135, 57, 2, 94, + 192, 255, 63, 0, 238, 191, 251, 255, 255, 253, 237, 243, 191, 59, 1, 0, + 207, 255, 3, 2, 238, 159, 249, 255, 159, 57, 192, 176, 207, 255, 255, 0, + 236, 199, 61, 214, 24, 199, 255, 195, 199, 61, 129, 0, 192, 255, 255, 7, + 239, 223, 253, 255, 255, 253, 255, 227, 223, 61, 96, 7, 207, 255, 0, 255, + 238, 223, 253, 255, 255, 253, 239, 243, 223, 61, 96, 64, 207, 255, 6, 0, + 255, 255, 255, 231, 223, 125, 128, 128, 207, 255, 63, 254, 236, 255, 127, 252, + 255, 255, 251, 47, 127, 132, 95, 255, 192, 255, 28, 0, 255, 255, 255, 135, + 255, 255, 255, 15, 150, 37, 240, 254, 174, 236, 255, 59, 95, 63, 255, 243, + 255, 254, 255, 255, 255, 31, 254, 255, 255, 255, 255, 254, 255, 223, 255, 7, + 191, 32, 255, 255, 255, 61, 127, 61, 255, 61, 255, 255, 255, 255, 61, 127, + 61, 255, 127, 255, 255, 255, 61, 255, 255, 255, 255, 31, 255, 255, 255, 3, + 255, 255, 63, 63, 254, 255, 255, 31, 255, 255, 255, 1, 255, 223, 31, 0, + 255, 255, 127, 0, 255, 255, 15, 0, 255, 223, 13, 0, 255, 255, 255, 63, + 255, 3, 255, 3, 255, 127, 255, 3, 255, 255, 255, 0, 255, 7, 255, 255, + 255, 255, 63, 0, 255, 15, 255, 15, 241, 255, 255, 255, 255, 63, 31, 0, + 255, 15, 255, 255, 255, 3, 255, 199, 255, 255, 255, 207, 255, 255, 255, 159, + 255, 255, 15, 240, 255, 255, 255, 248, 255, 227, 255, 255, 255, 255, 127, 3, + 255, 255, 63, 240, 63, 63, 255, 170, 255, 255, 223, 255, 223, 255, 207, 239, + 255, 255, 220, 127, 0, 248, 255, 255, 255, 124, 255, 255, 223, 255, 243, 255, + 255, 127, 255, 31, 0, 0, 255, 255, 255, 255, 1, 0, 127, 0, 0, 0, + 255, 7, 0, 0, 255, 255, 207, 255, 255, 255, 63, 255, 255, 255, 255, 227, + 255, 253, 3, 0, 0, 240, 0, 0, 255, 127, 255, 255, 255, 255, 15, 254, + 255, 128, 1, 128, 127, 127, 127, 127, 7, 0, 0, 0, 255, 255, 255, 251, + 0, 0, 255, 15, 224, 255, 255, 255, 255, 63, 254, 255, 15, 0, 255, 255, + 255, 31, 255, 255, 127, 0, 255, 255, 255, 15, 0, 0, 255, 63, 255, 0, + 0, 0, 128, 255, 255, 15, 255, 3, 31, 192, 255, 3, 255, 255, 15, 128, + 255, 191, 255, 195, 255, 63, 255, 243, 7, 0, 0, 248, 126, 126, 126, 0, + 127, 127, 255, 255, 63, 0, 255, 255, 255, 63, 255, 3, 127, 248, 255, 255, + 255, 63, 255, 255, 127, 0, 248, 224, 255, 255, 127, 95, 219, 255, 255, 255, + 3, 0, 248, 255, 255, 255, 252, 255, 255, 0, 0, 0, 0, 0, 255, 63, + 255, 255, 247, 255, 127, 15, 223, 255, 252, 252, 252, 28, 127, 127, 0, 62, + 255, 239, 255, 255, 127, 255, 255, 183, 255, 63, 255, 63, 135, 255, 255, 255, + 255, 255, 143, 255, 255, 31, 255, 15, 1, 0, 0, 0, 255, 255, 255, 191, + 15, 255, 63, 0, 255, 3, 0, 0, 15, 128, 0, 0, 63, 253, 255, 255, + 255, 255, 191, 145, 255, 255, 191, 255, 128, 255, 0, 0, 255, 255, 55, 248, + 255, 255, 255, 143, 255, 255, 255, 131, 255, 255, 255, 240, 111, 240, 239, 254, + 255, 255, 15, 135, 255, 0, 255, 1, 127, 248, 127, 0, 255, 255, 63, 254, + 255, 255, 7, 255, 255, 255, 3, 30, 0, 254, 0, 0, 255, 1, 0, 0, + 255, 255, 7, 0, 255, 255, 7, 252, 255, 63, 252, 255, 255, 255, 0, 128, + 3, 0, 255, 255, 255, 1, 255, 3, 254, 255, 31, 0, 255, 255, 251, 255, + 127, 189, 255, 191, 255, 3, 255, 255, 255, 7, 255, 3, 159, 57, 129, 224, + 207, 31, 31, 0, 255, 0, 255, 3, 31, 0, 255, 3, 255, 255, 7, 128, + 255, 127, 31, 0, 15, 0, 0, 0, 255, 127, 0, 0, 255, 195, 0, 0, + 255, 63, 63, 0, 63, 0, 255, 251, 251, 255, 255, 224, 255, 255, 0, 0, + 31, 0, 255, 255, 0, 128, 255, 255, 3, 0, 0, 0, 255, 7, 255, 31, + 255, 1, 255, 243, 127, 254, 255, 255, 63, 0, 0, 0, 100, 222, 255, 235, + 239, 255, 255, 255, 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, + 63, 255, 255, 255, 255, 207, 255, 255, 255, 15, 0, 248, 254, 255, 0, 0, + 159, 255, 127, 0, 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, + 255, 251, 255, 15, 238, 251, 255, 15, 0, 0, 3, 0, 255, 127, 254, 255, + 254, 255, 254, 255, 192, 255, 255, 255, 7, 0, 255, 255, 255, 1, 3, 0, + 255, 31, 15, 0, 255, 63, 0, 0, 0, 0, 255, 1, 31, 0, 0, 0, + 2, 0, 0, 0, +}; + +/* Graph: 2334 bytes. */ + +RE_UINT32 re_get_graph(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_graph_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_graph_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_graph_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_graph_stage_4[pos + f] << 5; + pos += code; + value = (re_graph_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Print. */ + +static RE_UINT8 re_print_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 4, 8, + 4, 8, +}; + +static RE_UINT8 re_print_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 7, 7, 7, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 26, 13, 27, 28, 29, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 30, 7, 31, 32, 7, 33, 13, 13, 13, 13, 13, 34, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 35, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 36, +}; + +static RE_UINT8 re_print_stage_3[] = { + 0, 1, 1, 2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 1, 15, 16, 1, 1, 17, 18, 19, 20, 21, 22, 23, 24, 1, 25, + 26, 27, 1, 28, 29, 1, 1, 1, 1, 1, 1, 30, 31, 32, 33, 34, + 35, 36, 37, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 40, 1, 41, 42, 43, 44, 45, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 47, 48, 48, 48, 48, 48, 48, 48, 48, + 1, 1, 49, 50, 1, 51, 52, 53, 54, 55, 56, 57, 58, 59, 1, 60, + 61, 62, 63, 64, 65, 48, 66, 48, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 48, 76, 48, 48, 48, 48, 48, 1, 1, 1, 77, 78, 79, 48, 48, + 1, 1, 1, 1, 80, 48, 48, 48, 48, 48, 48, 48, 1, 1, 81, 48, + 1, 1, 82, 83, 48, 48, 48, 84, 85, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 86, 48, 48, 48, 87, 88, 89, 90, 91, 92, 93, 94, + 1, 1, 95, 48, 48, 48, 48, 48, 96, 48, 48, 48, 48, 48, 97, 48, + 98, 99, 100, 1, 1, 101, 102, 103, 104, 105, 48, 48, 48, 48, 48, 48, + 1, 1, 1, 1, 1, 1, 106, 1, 1, 1, 1, 1, 1, 1, 1, 107, + 108, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 109, 48, + 1, 1, 110, 48, 48, 48, 48, 48, 111, 112, 48, 48, 48, 48, 48, 48, + 1, 1, 1, 1, 1, 1, 1, 113, +}; + +static RE_UINT8 re_print_stage_4[] = { + 0, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 4, 5, 1, 1, 1, 6, 7, 8, 9, 1, 10, 11, + 12, 1, 1, 1, 1, 1, 1, 1, 13, 1, 14, 1, 1, 15, 1, 16, + 1, 17, 18, 0, 0, 19, 0, 20, 1, 1, 1, 1, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 30, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 44, 48, 49, 50, 51, 52, 53, 54, + 8, 55, 56, 0, 57, 58, 59, 0, 1, 1, 60, 61, 62, 12, 63, 0, + 1, 1, 1, 1, 1, 1, 64, 1, 1, 1, 65, 1, 66, 67, 68, 1, + 69, 1, 48, 70, 71, 1, 1, 72, 1, 1, 1, 1, 70, 1, 1, 73, + 74, 75, 76, 77, 1, 1, 78, 79, 80, 1, 1, 81, 1, 82, 1, 83, + 2, 84, 85, 86, 1, 87, 88, 1, 89, 1, 2, 90, 79, 17, 0, 0, + 1, 1, 87, 70, 1, 1, 1, 91, 1, 92, 93, 1, 0, 0, 10, 94, + 1, 1, 1, 1, 1, 1, 1, 95, 72, 1, 96, 78, 1, 97, 98, 99, + 1, 100, 1, 101, 102, 2, 103, 104, 1, 1, 1, 1, 87, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 16, 1, 105, 106, 1, 1, 1, 1, 1, + 1, 1, 1, 107, 108, 109, 110, 111, 1, 112, 2, 1, 1, 1, 1, 113, + 1, 64, 1, 114, 75, 115, 115, 1, 1, 1, 116, 0, 117, 1, 1, 76, + 1, 1, 1, 1, 1, 1, 83, 118, 1, 1, 8, 1, 7, 1, 1, 1, + 119, 120, 1, 1, 112, 16, 1, 121, 2, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 83, 1, 1, 1, 1, 1, 1, 1, 1, 83, 0, + 1, 1, 1, 1, 122, 1, 123, 1, 1, 124, 1, 1, 1, 1, 1, 81, + 1, 1, 1, 1, 1, 125, 0, 126, 1, 127, 1, 81, 1, 1, 128, 78, + 1, 1, 129, 70, 1, 1, 130, 2, 1, 75, 131, 1, 1, 1, 132, 75, + 133, 134, 1, 135, 1, 1, 1, 136, 1, 1, 1, 1, 1, 121, 137, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 138, 1, 1, 71, 0, + 139, 140, 141, 1, 1, 1, 142, 1, 1, 1, 103, 1, 143, 1, 144, 145, + 71, 1, 146, 147, 1, 1, 1, 90, 8, 1, 1, 1, 1, 2, 148, 149, + 150, 151, 152, 0, 1, 1, 1, 16, 153, 154, 1, 1, 155, 156, 103, 78, + 0, 0, 0, 0, 70, 1, 104, 56, 1, 121, 82, 16, 157, 1, 158, 0, + 1, 1, 1, 1, 78, 159, 0, 0, 1, 10, 1, 160, 0, 0, 0, 0, + 1, 75, 83, 144, 0, 0, 0, 0, 161, 162, 163, 1, 2, 164, 0, 165, + 166, 167, 0, 0, 1, 168, 143, 1, 169, 170, 171, 1, 1, 0, 1, 172, + 1, 173, 108, 174, 175, 176, 0, 0, 1, 1, 177, 0, 1, 178, 1, 179, + 0, 0, 0, 2, 0, 0, 0, 0, 1, 1, 180, 181, 1, 1, 182, 183, + 1, 97, 121, 75, 1, 1, 138, 184, 185, 78, 0, 0, 186, 187, 1, 188, + 21, 30, 189, 190, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 191, 0, + 0, 0, 0, 0, 1, 108, 78, 0, 1, 1, 192, 0, 1, 81, 159, 0, + 109, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 193, + 0, 0, 0, 0, 0, 0, 1, 73, 1, 1, 1, 1, 71, 0, 0, 0, + 1, 1, 1, 194, 1, 1, 1, 1, 1, 1, 195, 0, 0, 0, 0, 0, + 1, 196, 0, 0, 0, 0, 0, 0, 1, 1, 105, 0, 0, 0, 0, 0, + 1, 73, 2, 197, 0, 0, 103, 198, 1, 1, 199, 200, 201, 0, 0, 0, + 1, 1, 202, 2, 203, 0, 0, 0, 204, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 205, 206, 195, 0, 0, 1, 1, 1, 1, 1, 1, 1, 83, + 1, 207, 1, 1, 1, 1, 1, 177, 1, 1, 208, 0, 0, 0, 0, 0, + 1, 1, 75, 15, 0, 0, 0, 0, 1, 1, 97, 1, 12, 209, 210, 1, + 211, 212, 213, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 214, 1, 1, + 1, 1, 1, 1, 1, 1, 215, 1, 1, 1, 1, 1, 216, 217, 0, 0, + 1, 1, 1, 1, 1, 1, 218, 0, 210, 219, 220, 221, 222, 223, 0, 224, + 1, 87, 1, 1, 76, 225, 226, 83, 122, 112, 1, 87, 16, 0, 0, 227, + 228, 16, 229, 0, 0, 0, 0, 0, 1, 1, 1, 117, 1, 210, 1, 1, + 1, 1, 1, 1, 1, 1, 104, 230, 1, 1, 1, 76, 1, 1, 19, 0, + 87, 1, 191, 1, 10, 231, 0, 0, 232, 0, 0, 0, 233, 0, 156, 0, + 1, 1, 1, 1, 1, 1, 75, 0, 1, 19, 1, 1, 1, 1, 1, 1, + 78, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 204, 0, 0, + 78, 0, 0, 0, 0, 0, 0, 0, 234, 1, 1, 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 201, 1, 1, 1, 1, 1, 1, 1, 78, +}; + +static RE_UINT8 re_print_stage_5[] = { + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 127, 255, 255, 255, 252, + 240, 215, 255, 255, 251, 255, 255, 255, 255, 255, 254, 255, 255, 255, 127, 254, + 254, 255, 255, 255, 255, 230, 254, 255, 255, 0, 255, 255, 255, 7, 31, 0, + 255, 255, 255, 223, 255, 191, 255, 255, 255, 231, 255, 255, 255, 255, 3, 0, + 255, 255, 255, 7, 255, 63, 255, 127, 255, 255, 255, 79, 255, 255, 31, 0, + 248, 255, 255, 255, 239, 159, 249, 255, 255, 253, 197, 243, 159, 121, 128, 176, + 207, 255, 255, 15, 238, 135, 249, 255, 255, 253, 109, 211, 135, 57, 2, 94, + 192, 255, 63, 0, 238, 191, 251, 255, 255, 253, 237, 243, 191, 59, 1, 0, + 207, 255, 3, 2, 238, 159, 249, 255, 159, 57, 192, 176, 207, 255, 255, 0, + 236, 199, 61, 214, 24, 199, 255, 195, 199, 61, 129, 0, 192, 255, 255, 7, + 239, 223, 253, 255, 255, 253, 255, 227, 223, 61, 96, 7, 207, 255, 0, 255, + 238, 223, 253, 255, 255, 253, 239, 243, 223, 61, 96, 64, 207, 255, 6, 0, + 255, 255, 255, 231, 223, 125, 128, 128, 207, 255, 63, 254, 236, 255, 127, 252, + 255, 255, 251, 47, 127, 132, 95, 255, 192, 255, 28, 0, 255, 255, 255, 135, + 255, 255, 255, 15, 150, 37, 240, 254, 174, 236, 255, 59, 95, 63, 255, 243, + 255, 254, 255, 255, 255, 31, 254, 255, 255, 255, 255, 254, 255, 223, 255, 7, + 191, 32, 255, 255, 255, 61, 127, 61, 255, 61, 255, 255, 255, 255, 61, 127, + 61, 255, 127, 255, 255, 255, 61, 255, 255, 255, 255, 31, 255, 255, 255, 3, + 255, 255, 63, 63, 255, 255, 255, 1, 255, 223, 31, 0, 255, 255, 127, 0, + 255, 255, 15, 0, 255, 223, 13, 0, 255, 255, 255, 63, 255, 3, 255, 3, + 255, 127, 255, 3, 255, 255, 255, 0, 255, 7, 255, 255, 255, 255, 63, 0, + 255, 15, 255, 15, 241, 255, 255, 255, 255, 63, 31, 0, 255, 15, 255, 255, + 255, 3, 255, 199, 255, 255, 255, 207, 255, 255, 255, 159, 255, 255, 15, 240, + 255, 255, 255, 248, 255, 227, 255, 255, 255, 255, 127, 3, 255, 255, 63, 240, + 63, 63, 255, 170, 255, 255, 223, 255, 223, 255, 207, 239, 255, 255, 220, 127, + 255, 252, 255, 255, 223, 255, 243, 255, 255, 127, 255, 31, 0, 0, 255, 255, + 255, 255, 1, 0, 127, 0, 0, 0, 255, 7, 0, 0, 255, 255, 207, 255, + 255, 255, 63, 255, 255, 255, 255, 227, 255, 253, 3, 0, 0, 240, 0, 0, + 255, 127, 255, 255, 255, 255, 15, 254, 255, 128, 1, 128, 127, 127, 127, 127, + 7, 0, 0, 0, 255, 255, 255, 251, 0, 0, 255, 15, 224, 255, 255, 255, + 255, 63, 254, 255, 15, 0, 255, 255, 255, 31, 255, 255, 127, 0, 255, 255, + 255, 15, 0, 0, 255, 63, 255, 0, 0, 0, 128, 255, 255, 15, 255, 3, + 31, 192, 255, 3, 255, 255, 15, 128, 255, 191, 255, 195, 255, 63, 255, 243, + 7, 0, 0, 248, 126, 126, 126, 0, 127, 127, 255, 255, 63, 0, 255, 255, + 255, 63, 255, 3, 127, 248, 255, 255, 255, 63, 255, 255, 127, 0, 248, 224, + 255, 255, 127, 95, 219, 255, 255, 255, 3, 0, 248, 255, 255, 255, 252, 255, + 255, 0, 0, 0, 0, 0, 255, 63, 255, 255, 247, 255, 127, 15, 223, 255, + 252, 252, 252, 28, 127, 127, 0, 62, 255, 239, 255, 255, 127, 255, 255, 183, + 255, 63, 255, 63, 135, 255, 255, 255, 255, 255, 143, 255, 255, 31, 255, 15, + 1, 0, 0, 0, 255, 255, 255, 191, 15, 255, 63, 0, 255, 3, 0, 0, + 15, 128, 0, 0, 63, 253, 255, 255, 255, 255, 191, 145, 255, 255, 191, 255, + 128, 255, 0, 0, 255, 255, 55, 248, 255, 255, 255, 143, 255, 255, 255, 131, + 255, 255, 255, 240, 111, 240, 239, 254, 255, 255, 15, 135, 255, 0, 255, 1, + 127, 248, 127, 0, 255, 255, 63, 254, 255, 255, 7, 255, 255, 255, 3, 30, + 0, 254, 0, 0, 255, 1, 0, 0, 255, 255, 7, 0, 255, 255, 7, 252, + 255, 63, 252, 255, 255, 255, 0, 128, 3, 0, 255, 255, 255, 1, 255, 3, + 254, 255, 31, 0, 255, 255, 251, 255, 127, 189, 255, 191, 255, 3, 255, 255, + 255, 7, 255, 3, 159, 57, 129, 224, 207, 31, 31, 0, 255, 0, 255, 3, + 31, 0, 255, 3, 255, 255, 7, 128, 255, 127, 31, 0, 15, 0, 0, 0, + 255, 127, 0, 0, 255, 195, 0, 0, 255, 63, 63, 0, 63, 0, 255, 251, + 251, 255, 255, 224, 255, 255, 0, 0, 31, 0, 255, 255, 0, 128, 255, 255, + 3, 0, 0, 0, 255, 7, 255, 31, 255, 1, 255, 243, 127, 254, 255, 255, + 63, 0, 0, 0, 100, 222, 255, 235, 239, 255, 255, 255, 191, 231, 223, 223, + 255, 255, 255, 123, 95, 252, 253, 255, 63, 255, 255, 255, 255, 207, 255, 255, + 255, 15, 0, 248, 254, 255, 0, 0, 159, 255, 127, 0, 150, 254, 247, 10, + 132, 234, 150, 170, 150, 247, 247, 94, 255, 251, 255, 15, 238, 251, 255, 15, + 0, 0, 3, 0, 255, 127, 254, 255, 254, 255, 254, 255, 192, 255, 255, 255, + 7, 0, 255, 255, 255, 1, 3, 0, 255, 31, 15, 0, 255, 63, 0, 0, + 0, 0, 255, 1, 31, 0, 0, 0, 2, 0, 0, 0, +}; + +/* Print: 2326 bytes. */ + +RE_UINT32 re_get_print(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_print_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_print_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_print_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_print_stage_4[pos + f] << 5; + pos += code; + value = (re_print_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Word. */ + +static RE_UINT8 re_word_stage_1[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, + 6, 6, +}; + +static RE_UINT8 re_word_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 26, 13, 27, 28, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 29, 7, 30, 31, 7, 32, 13, 13, 13, 13, 13, 33, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 34, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_word_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 17, 18, 19, 1, 20, 21, 22, 23, 24, 25, 26, 27, 1, 28, + 29, 30, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 31, + 36, 37, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 40, 1, 41, 42, 43, 44, 45, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 47, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 48, 49, 1, 50, 51, 52, 53, 54, 55, 56, 57, 58, 1, 59, + 60, 61, 62, 63, 64, 31, 31, 31, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 31, 74, 31, 31, 31, 31, 31, 1, 1, 1, 75, 76, 77, 31, 31, + 1, 1, 1, 1, 78, 31, 31, 31, 31, 31, 31, 31, 1, 1, 79, 31, + 1, 1, 80, 81, 31, 31, 31, 82, 83, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 84, 31, 31, 31, 31, 85, 86, 31, 87, 88, 89, 90, + 31, 31, 91, 31, 31, 31, 31, 31, 92, 31, 31, 31, 31, 31, 93, 31, + 31, 94, 31, 31, 31, 31, 31, 31, 1, 1, 1, 1, 1, 1, 95, 1, + 1, 1, 1, 1, 1, 1, 1, 96, 97, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 98, 31, 1, 1, 99, 31, 31, 31, 31, 31, + 31, 100, 31, 31, 31, 31, 31, 31, +}; + +static RE_UINT8 re_word_stage_4[] = { + 0, 1, 2, 3, 0, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 7, 8, 6, 6, 6, 9, 10, 11, 6, 12, + 6, 6, 6, 6, 11, 6, 6, 6, 6, 13, 14, 15, 16, 17, 18, 19, + 20, 6, 6, 21, 6, 6, 22, 23, 24, 6, 25, 6, 6, 26, 6, 27, + 6, 28, 29, 0, 0, 30, 0, 31, 6, 6, 6, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 42, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 56, 60, 61, 62, 63, 64, 65, 66, + 15, 67, 68, 0, 69, 70, 71, 0, 72, 73, 74, 75, 76, 77, 78, 0, + 6, 6, 79, 6, 80, 6, 81, 82, 6, 6, 83, 6, 84, 85, 86, 6, + 87, 6, 60, 0, 88, 6, 6, 89, 15, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 90, 3, 6, 6, 91, 92, 30, 93, 94, 6, 6, 95, 96, + 97, 6, 6, 98, 6, 99, 6, 100, 101, 102, 103, 104, 6, 105, 106, 0, + 29, 6, 101, 107, 106, 108, 0, 0, 6, 6, 109, 110, 6, 6, 6, 93, + 6, 98, 111, 80, 0, 0, 112, 113, 6, 6, 6, 6, 6, 6, 6, 114, + 89, 6, 115, 80, 6, 116, 117, 118, 119, 120, 121, 122, 123, 0, 24, 124, + 125, 126, 127, 6, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 129, 6, 96, 6, 130, 101, 6, 6, 6, 6, 131, + 6, 81, 6, 132, 133, 134, 134, 6, 0, 135, 0, 0, 0, 0, 0, 0, + 136, 137, 15, 6, 138, 15, 6, 82, 139, 140, 6, 6, 141, 67, 0, 24, + 6, 6, 6, 6, 6, 100, 0, 0, 6, 6, 6, 6, 6, 6, 100, 0, + 6, 6, 6, 6, 142, 0, 24, 80, 143, 144, 6, 145, 6, 6, 6, 26, + 146, 147, 6, 6, 148, 149, 0, 146, 6, 150, 6, 93, 6, 6, 151, 152, + 6, 153, 93, 77, 6, 6, 154, 101, 6, 133, 155, 156, 6, 6, 157, 158, + 159, 160, 82, 161, 6, 6, 6, 162, 6, 6, 6, 6, 6, 163, 164, 29, + 6, 6, 6, 153, 6, 6, 165, 0, 166, 167, 168, 6, 6, 26, 169, 6, + 6, 80, 24, 6, 170, 6, 150, 171, 88, 172, 173, 174, 6, 6, 6, 77, + 1, 2, 3, 103, 6, 101, 175, 0, 176, 177, 178, 0, 6, 6, 6, 67, + 0, 0, 6, 30, 0, 0, 0, 179, 0, 0, 0, 0, 77, 6, 124, 180, + 6, 24, 99, 67, 80, 6, 181, 0, 6, 6, 6, 6, 80, 96, 0, 0, + 6, 182, 6, 183, 0, 0, 0, 0, 6, 133, 100, 150, 0, 0, 0, 0, + 184, 185, 100, 133, 101, 0, 0, 186, 100, 165, 0, 0, 6, 187, 0, 0, + 188, 189, 0, 77, 77, 0, 74, 190, 6, 100, 100, 191, 26, 0, 0, 0, + 6, 6, 128, 0, 6, 191, 6, 191, 6, 6, 190, 192, 6, 67, 24, 193, + 6, 194, 24, 195, 6, 6, 196, 0, 197, 98, 0, 0, 198, 199, 6, 200, + 33, 42, 201, 202, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 203, 0, + 0, 0, 0, 0, 6, 204, 205, 0, 6, 6, 206, 0, 6, 98, 96, 0, + 207, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 208, + 0, 0, 0, 0, 0, 0, 6, 209, 6, 6, 6, 6, 165, 0, 0, 0, + 6, 6, 6, 141, 6, 6, 6, 6, 6, 6, 183, 0, 0, 0, 0, 0, + 6, 141, 0, 0, 0, 0, 0, 0, 6, 6, 190, 0, 0, 0, 0, 0, + 6, 209, 101, 96, 0, 0, 24, 104, 6, 133, 210, 211, 88, 0, 0, 0, + 6, 6, 212, 101, 213, 0, 0, 0, 214, 0, 0, 0, 0, 0, 0, 0, + 6, 6, 6, 215, 216, 0, 0, 0, 0, 0, 0, 217, 218, 219, 0, 0, + 0, 0, 220, 0, 0, 0, 0, 0, 6, 6, 194, 6, 221, 222, 223, 6, + 224, 225, 226, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 227, 228, 82, + 194, 194, 130, 130, 229, 229, 230, 6, 6, 231, 6, 232, 233, 234, 0, 0, + 6, 6, 6, 6, 6, 6, 235, 0, 223, 236, 237, 238, 239, 240, 0, 0, + 0, 24, 79, 79, 96, 0, 0, 0, 6, 6, 6, 6, 6, 6, 133, 0, + 6, 30, 6, 6, 6, 6, 6, 6, 80, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 214, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, + 6, 6, 6, 6, 6, 6, 6, 88, +}; + +static RE_UINT8 re_word_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 254, 255, 255, 135, 254, 255, 255, 7, + 0, 4, 32, 4, 255, 255, 127, 255, 255, 255, 255, 255, 195, 255, 3, 0, + 31, 80, 0, 0, 255, 255, 223, 188, 64, 215, 255, 255, 251, 255, 255, 255, + 255, 255, 191, 255, 255, 255, 254, 255, 255, 255, 127, 2, 254, 255, 255, 255, + 255, 0, 254, 255, 255, 255, 255, 191, 182, 0, 255, 255, 255, 7, 7, 0, + 0, 0, 255, 7, 255, 195, 255, 255, 255, 255, 239, 159, 255, 253, 255, 159, + 0, 0, 255, 255, 255, 231, 255, 255, 255, 255, 3, 0, 255, 255, 63, 4, + 255, 63, 0, 0, 255, 255, 255, 15, 255, 255, 31, 0, 248, 255, 255, 255, + 207, 255, 254, 255, 239, 159, 249, 255, 255, 253, 197, 243, 159, 121, 128, 176, + 207, 255, 3, 0, 238, 135, 249, 255, 255, 253, 109, 211, 135, 57, 2, 94, + 192, 255, 63, 0, 238, 191, 251, 255, 255, 253, 237, 243, 191, 59, 1, 0, + 207, 255, 0, 2, 238, 159, 249, 255, 159, 57, 192, 176, 207, 255, 2, 0, + 236, 199, 61, 214, 24, 199, 255, 195, 199, 61, 129, 0, 192, 255, 0, 0, + 239, 223, 253, 255, 255, 253, 255, 227, 223, 61, 96, 7, 207, 255, 0, 0, + 238, 223, 253, 255, 255, 253, 239, 243, 223, 61, 96, 64, 207, 255, 6, 0, + 255, 255, 255, 231, 223, 125, 128, 128, 207, 255, 0, 252, 236, 255, 127, 252, + 255, 255, 251, 47, 127, 132, 95, 255, 192, 255, 12, 0, 255, 255, 255, 7, + 255, 127, 255, 3, 150, 37, 240, 254, 174, 236, 255, 59, 95, 63, 255, 243, + 1, 0, 0, 3, 255, 3, 160, 194, 255, 254, 255, 255, 255, 31, 254, 255, + 223, 255, 255, 254, 255, 255, 255, 31, 64, 0, 0, 0, 255, 3, 255, 255, + 255, 255, 255, 63, 191, 32, 255, 255, 255, 255, 255, 247, 255, 61, 127, 61, + 255, 61, 255, 255, 255, 255, 61, 127, 61, 255, 127, 255, 255, 255, 61, 255, + 255, 255, 0, 0, 255, 255, 63, 63, 255, 159, 255, 255, 255, 199, 255, 1, + 255, 223, 31, 0, 255, 255, 15, 0, 255, 223, 13, 0, 255, 255, 143, 48, + 255, 3, 0, 0, 0, 56, 255, 3, 255, 255, 255, 0, 255, 7, 255, 255, + 255, 255, 63, 0, 255, 255, 255, 127, 255, 15, 255, 15, 192, 255, 255, 255, + 255, 63, 31, 0, 255, 15, 255, 255, 255, 3, 255, 3, 255, 255, 255, 159, + 128, 0, 255, 127, 255, 15, 255, 3, 0, 248, 15, 0, 255, 227, 255, 255, + 0, 0, 247, 255, 255, 255, 127, 3, 255, 255, 63, 240, 63, 63, 255, 170, + 255, 255, 223, 95, 220, 31, 207, 15, 255, 31, 220, 31, 0, 48, 0, 0, + 0, 0, 0, 128, 1, 0, 16, 0, 0, 0, 2, 128, 0, 0, 255, 31, + 255, 255, 1, 0, 132, 252, 47, 62, 80, 189, 255, 243, 224, 67, 0, 0, + 255, 1, 0, 0, 0, 0, 192, 255, 255, 127, 255, 255, 31, 248, 15, 0, + 255, 128, 0, 128, 255, 255, 127, 0, 127, 127, 127, 127, 0, 128, 0, 0, + 224, 0, 0, 0, 254, 255, 62, 31, 255, 255, 127, 230, 224, 255, 255, 255, + 255, 63, 254, 255, 255, 127, 0, 0, 255, 31, 0, 0, 255, 31, 255, 255, + 255, 15, 0, 0, 255, 255, 247, 191, 0, 0, 128, 255, 252, 255, 255, 255, + 255, 249, 255, 255, 255, 63, 255, 0, 255, 0, 0, 0, 31, 0, 255, 3, + 255, 255, 255, 40, 255, 63, 255, 255, 1, 128, 255, 3, 255, 63, 255, 3, + 255, 255, 127, 252, 7, 0, 0, 56, 255, 255, 124, 0, 126, 126, 126, 0, + 127, 127, 255, 255, 63, 0, 255, 255, 255, 55, 255, 3, 15, 0, 255, 255, + 127, 248, 255, 255, 255, 255, 255, 3, 127, 0, 248, 224, 255, 253, 127, 95, + 219, 255, 255, 255, 0, 0, 248, 255, 255, 255, 252, 255, 0, 0, 255, 15, + 255, 255, 24, 0, 0, 224, 0, 0, 0, 0, 223, 255, 252, 252, 252, 28, + 255, 239, 255, 255, 127, 255, 255, 183, 255, 63, 255, 63, 0, 0, 0, 32, + 1, 0, 0, 0, 15, 255, 62, 0, 255, 0, 255, 255, 15, 0, 0, 0, + 63, 253, 255, 255, 255, 255, 191, 145, 255, 255, 55, 0, 255, 255, 255, 192, + 111, 240, 239, 254, 255, 255, 15, 135, 127, 0, 0, 0, 255, 255, 7, 0, + 192, 255, 0, 128, 255, 1, 255, 3, 255, 255, 223, 255, 255, 255, 79, 0, + 31, 28, 255, 23, 255, 255, 251, 255, 127, 189, 255, 191, 255, 1, 255, 255, + 255, 7, 255, 3, 159, 57, 129, 224, 207, 31, 31, 0, 191, 0, 255, 3, + 255, 255, 63, 255, 1, 0, 0, 63, 17, 0, 255, 3, 255, 255, 255, 227, + 255, 3, 0, 128, 255, 255, 255, 1, 15, 0, 255, 3, 248, 255, 255, 224, + 31, 0, 255, 255, 0, 128, 255, 255, 3, 0, 0, 0, 255, 7, 255, 31, + 255, 1, 255, 99, 224, 227, 7, 248, 231, 15, 0, 0, 0, 60, 0, 0, + 28, 0, 0, 0, 255, 255, 255, 223, 100, 222, 255, 235, 239, 255, 255, 255, + 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, 63, 255, 255, 255, + 253, 255, 255, 247, 255, 253, 255, 255, 247, 207, 255, 255, 255, 255, 127, 248, + 255, 31, 32, 0, 16, 0, 0, 248, 254, 255, 0, 0, 31, 0, 127, 0, + 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, 255, 251, 255, 15, + 238, 251, 255, 15, +}; + +/* Word: 2214 bytes. */ + +RE_UINT32 re_get_word(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 15; + code = ch ^ (f << 15); + pos = (RE_UINT32)re_word_stage_1[f] << 4; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_word_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_word_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_word_stage_4[pos + f] << 5; + pos += code; + value = (re_word_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* XDigit. */ + +static RE_UINT8 re_xdigit_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_xdigit_stage_2[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 4, + 5, 6, 2, 2, 2, 2, 7, 2, 2, 2, 2, 2, 2, 8, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_xdigit_stage_3[] = { + 0, 1, 1, 1, 1, 1, 2, 3, 1, 4, 4, 4, 4, 4, 5, 6, + 7, 1, 1, 1, 1, 1, 1, 8, 9, 10, 11, 12, 13, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6, 1, 14, 15, 16, 17, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 18, + 1, 1, 1, 1, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 20, 21, 17, 1, 14, 1, 22, 23, 8, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 24, 16, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 25, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_xdigit_stage_4[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 2, 0, 2, 2, 2, 4, + 2, 5, 2, 5, 2, 6, 2, 6, 3, 2, 2, 2, 2, 4, 6, 2, + 2, 2, 2, 3, 6, 2, 2, 2, 2, 7, 2, 6, 2, 2, 8, 2, + 2, 6, 0, 2, 2, 8, 2, 2, 2, 2, 2, 6, 4, 2, 2, 9, + 2, 6, 2, 2, 2, 2, 2, 0, 10, 11, 2, 2, 2, 2, 3, 2, + 2, 5, 2, 0, 12, 2, 2, 6, 2, 6, 2, 4, 0, 2, 2, 2, + 2, 3, 2, 2, 2, 2, 2, 13, +}; + +static RE_UINT8 re_xdigit_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 126, 0, 0, 0, 126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 3, 0, 0, + 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 0, 0, + 0, 0, 255, 3, 0, 0, 0, 0, 192, 255, 0, 0, 0, 0, 0, 0, + 255, 3, 255, 3, 0, 0, 0, 0, 0, 0, 255, 3, 0, 0, 255, 3, + 0, 0, 255, 3, 126, 0, 0, 0, 126, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 192, 255, 0, 192, 255, 255, 255, 255, 255, 255, +}; + +/* XDigit: 425 bytes. */ + +RE_UINT32 re_get_xdigit(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_xdigit_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_xdigit_stage_2[pos + f] << 4; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_xdigit_stage_3[pos + f] << 2; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_xdigit_stage_4[pos + f] << 6; + pos += code; + value = (re_xdigit_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Posix_Digit. */ + +static RE_UINT8 re_posix_digit_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_posix_digit_stage_2[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_posix_digit_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_posix_digit_stage_4[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_posix_digit_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Posix_Digit: 97 bytes. */ + +RE_UINT32 re_get_posix_digit(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_posix_digit_stage_1[f] << 4; + f = code >> 12; + code ^= f << 12; + pos = (RE_UINT32)re_posix_digit_stage_2[pos + f] << 3; + f = code >> 9; + code ^= f << 9; + pos = (RE_UINT32)re_posix_digit_stage_3[pos + f] << 3; + f = code >> 6; + code ^= f << 6; + pos = (RE_UINT32)re_posix_digit_stage_4[pos + f] << 6; + pos += code; + value = (re_posix_digit_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Posix_AlNum. */ + +static RE_UINT8 re_posix_alnum_stage_1[] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, +}; + +static RE_UINT8 re_posix_alnum_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 13, 20, 13, 21, 13, 13, 13, 13, 22, 13, 13, + 13, 13, 13, 13, 13, 13, 23, 24, 13, 13, 25, 13, 13, 26, 27, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 28, 7, 29, 30, 7, 31, 13, 13, 13, 13, 13, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +}; + +static RE_UINT8 re_posix_alnum_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 17, 18, 19, 1, 20, 21, 22, 23, 24, 25, 26, 27, 1, 28, + 29, 30, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 31, + 36, 37, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 1, 40, 1, 41, 42, 43, 44, 45, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 47, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 1, 48, 49, 1, 50, 51, 52, 53, 54, 55, 56, 57, 58, 1, 59, + 60, 61, 62, 63, 64, 31, 31, 31, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 31, 74, 31, 31, 31, 31, 31, 1, 1, 1, 75, 76, 77, 31, 31, + 1, 1, 1, 1, 78, 31, 31, 31, 31, 31, 31, 31, 1, 1, 79, 31, + 1, 1, 80, 81, 31, 31, 31, 82, 83, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 84, 31, 31, 31, 31, 31, 31, 31, 85, 86, 87, 88, + 89, 31, 31, 31, 31, 31, 90, 31, 31, 91, 31, 31, 31, 31, 31, 31, + 1, 1, 1, 1, 1, 1, 92, 1, 1, 1, 1, 1, 1, 1, 1, 93, + 94, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 95, 31, + 1, 1, 96, 31, 31, 31, 31, 31, +}; + +static RE_UINT8 re_posix_alnum_stage_4[] = { + 0, 1, 2, 2, 0, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 7, 0, 0, 8, 9, 10, 11, 5, 12, + 5, 5, 5, 5, 13, 5, 5, 5, 5, 14, 15, 16, 17, 18, 19, 20, + 21, 5, 22, 23, 5, 5, 24, 25, 26, 5, 27, 5, 5, 28, 29, 30, + 31, 32, 33, 0, 0, 34, 0, 35, 5, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 48, 52, 53, 54, 55, 56, 0, + 57, 58, 59, 60, 61, 62, 63, 64, 61, 65, 66, 67, 68, 69, 70, 71, + 16, 72, 73, 0, 74, 75, 76, 0, 77, 0, 78, 79, 80, 81, 0, 0, + 5, 82, 26, 83, 84, 5, 85, 86, 5, 5, 87, 5, 88, 89, 90, 5, + 91, 5, 92, 0, 93, 5, 5, 94, 16, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 95, 2, 5, 5, 96, 97, 98, 98, 99, 5, 100, 101, 0, + 0, 5, 5, 102, 5, 103, 5, 104, 105, 106, 26, 107, 5, 108, 109, 0, + 110, 5, 105, 111, 0, 112, 0, 0, 5, 113, 114, 0, 5, 115, 5, 116, + 5, 104, 117, 118, 0, 0, 0, 119, 5, 5, 5, 5, 5, 5, 0, 120, + 94, 5, 121, 118, 5, 122, 123, 124, 0, 0, 0, 125, 126, 0, 0, 0, + 127, 128, 129, 5, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 131, 5, 109, 5, 132, 105, 5, 5, 5, 5, 133, + 5, 85, 5, 134, 135, 136, 136, 5, 0, 137, 0, 0, 0, 0, 0, 0, + 138, 139, 16, 5, 140, 16, 5, 86, 141, 142, 5, 5, 143, 72, 0, 26, + 5, 5, 5, 5, 5, 104, 0, 0, 5, 5, 5, 5, 5, 5, 104, 0, + 5, 5, 5, 5, 32, 0, 26, 118, 144, 145, 5, 146, 5, 5, 5, 93, + 147, 148, 5, 5, 149, 150, 0, 147, 151, 17, 5, 98, 5, 5, 60, 152, + 29, 103, 153, 81, 5, 154, 137, 155, 5, 135, 156, 157, 5, 105, 158, 159, + 160, 161, 86, 162, 5, 5, 5, 163, 5, 5, 5, 5, 5, 164, 165, 110, + 5, 5, 5, 166, 5, 5, 167, 0, 168, 169, 170, 5, 5, 28, 171, 5, + 5, 118, 26, 5, 172, 5, 17, 173, 0, 0, 0, 174, 5, 5, 5, 81, + 0, 2, 2, 175, 5, 105, 176, 0, 177, 178, 179, 0, 5, 5, 5, 72, + 0, 0, 5, 34, 0, 0, 0, 0, 0, 0, 0, 0, 81, 5, 180, 0, + 5, 26, 103, 72, 118, 5, 181, 0, 5, 5, 5, 5, 118, 0, 0, 0, + 5, 182, 5, 60, 0, 0, 0, 0, 5, 135, 104, 17, 0, 0, 0, 0, + 183, 184, 104, 135, 105, 0, 0, 185, 104, 167, 0, 0, 5, 186, 0, 0, + 187, 98, 0, 81, 81, 0, 78, 188, 5, 104, 104, 153, 28, 0, 0, 0, + 5, 5, 130, 0, 5, 153, 5, 153, 5, 5, 189, 0, 148, 33, 26, 130, + 5, 153, 26, 190, 5, 5, 191, 0, 192, 193, 0, 0, 194, 195, 5, 130, + 39, 48, 196, 60, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 197, 0, + 0, 0, 0, 0, 5, 198, 199, 0, 5, 105, 200, 0, 5, 104, 0, 0, + 201, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 202, + 0, 0, 0, 0, 0, 0, 5, 33, 5, 5, 5, 5, 167, 0, 0, 0, + 5, 5, 5, 143, 5, 5, 5, 5, 5, 5, 60, 0, 0, 0, 0, 0, + 5, 143, 0, 0, 0, 0, 0, 0, 5, 5, 203, 0, 0, 0, 0, 0, + 5, 33, 105, 0, 0, 0, 26, 156, 5, 135, 60, 204, 93, 0, 0, 0, + 5, 5, 205, 105, 171, 0, 0, 0, 206, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 207, 208, 0, 0, 0, 5, 5, 209, 5, 210, 211, 212, 5, + 213, 214, 215, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 216, 217, 86, + 209, 209, 132, 132, 218, 218, 219, 0, 5, 5, 5, 5, 5, 5, 188, 0, + 212, 220, 221, 222, 223, 224, 0, 0, 0, 26, 225, 225, 109, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 135, 0, 5, 34, 5, 5, 5, 5, 5, 5, + 118, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 206, 0, 0, + 118, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_posix_alnum_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 254, 255, 255, 7, 0, 4, 32, 4, + 255, 255, 127, 255, 255, 255, 255, 255, 195, 255, 3, 0, 31, 80, 0, 0, + 32, 0, 0, 0, 0, 0, 223, 188, 64, 215, 255, 255, 251, 255, 255, 255, + 255, 255, 191, 255, 3, 252, 255, 255, 255, 255, 254, 255, 255, 255, 127, 2, + 254, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 191, 182, 0, 255, 255, + 255, 7, 7, 0, 0, 0, 255, 7, 255, 255, 255, 254, 0, 192, 255, 255, + 255, 255, 239, 31, 254, 225, 0, 156, 0, 0, 255, 255, 0, 224, 255, 255, + 255, 255, 3, 0, 0, 252, 255, 255, 255, 7, 48, 4, 255, 255, 255, 252, + 255, 31, 0, 0, 255, 255, 255, 1, 255, 255, 31, 0, 248, 3, 255, 255, + 255, 255, 255, 239, 255, 223, 225, 255, 15, 0, 254, 255, 239, 159, 249, 255, + 255, 253, 197, 227, 159, 89, 128, 176, 15, 0, 3, 0, 238, 135, 249, 255, + 255, 253, 109, 195, 135, 25, 2, 94, 0, 0, 63, 0, 238, 191, 251, 255, + 255, 253, 237, 227, 191, 27, 1, 0, 15, 0, 0, 2, 238, 159, 249, 255, + 159, 25, 192, 176, 15, 0, 2, 0, 236, 199, 61, 214, 24, 199, 255, 195, + 199, 29, 129, 0, 239, 223, 253, 255, 255, 253, 255, 227, 223, 29, 96, 7, + 15, 0, 0, 0, 238, 223, 253, 255, 255, 253, 239, 227, 223, 29, 96, 64, + 15, 0, 6, 0, 255, 255, 255, 231, 223, 93, 128, 128, 15, 0, 0, 252, + 236, 255, 127, 252, 255, 255, 251, 47, 127, 128, 95, 255, 0, 0, 12, 0, + 255, 255, 255, 7, 127, 32, 0, 0, 150, 37, 240, 254, 174, 236, 255, 59, + 95, 32, 0, 240, 1, 0, 0, 0, 255, 254, 255, 255, 255, 31, 254, 255, + 3, 255, 255, 254, 255, 255, 255, 31, 255, 255, 127, 249, 231, 193, 255, 255, + 127, 64, 0, 48, 191, 32, 255, 255, 255, 255, 255, 247, 255, 61, 127, 61, + 255, 61, 255, 255, 255, 255, 61, 127, 61, 255, 127, 255, 255, 255, 61, 255, + 255, 255, 255, 135, 255, 255, 0, 0, 255, 255, 63, 63, 255, 159, 255, 255, + 255, 199, 255, 1, 255, 223, 15, 0, 255, 255, 15, 0, 255, 223, 13, 0, + 255, 255, 207, 255, 255, 1, 128, 16, 255, 255, 255, 0, 255, 7, 255, 255, + 255, 255, 63, 0, 255, 255, 255, 127, 255, 15, 255, 1, 255, 63, 31, 0, + 255, 15, 255, 255, 255, 3, 0, 0, 255, 255, 255, 15, 254, 255, 31, 0, + 128, 0, 0, 0, 255, 255, 239, 255, 239, 15, 0, 0, 255, 243, 0, 252, + 191, 255, 3, 0, 0, 224, 0, 252, 255, 255, 255, 63, 0, 222, 111, 0, + 128, 255, 31, 0, 63, 63, 255, 170, 255, 255, 223, 95, 220, 31, 207, 15, + 255, 31, 220, 31, 0, 0, 2, 128, 0, 0, 255, 31, 132, 252, 47, 62, + 80, 189, 255, 243, 224, 67, 0, 0, 255, 1, 0, 0, 0, 0, 192, 255, + 255, 127, 255, 255, 31, 120, 12, 0, 255, 128, 0, 0, 255, 255, 127, 0, + 127, 127, 127, 127, 0, 128, 0, 0, 224, 0, 0, 0, 254, 3, 62, 31, + 255, 255, 127, 224, 224, 255, 255, 255, 255, 63, 254, 255, 255, 127, 0, 0, + 255, 31, 255, 255, 0, 12, 0, 0, 255, 127, 240, 143, 0, 0, 128, 255, + 252, 255, 255, 255, 255, 249, 255, 255, 255, 63, 255, 0, 187, 247, 255, 255, + 0, 0, 252, 40, 255, 255, 7, 0, 255, 255, 247, 255, 223, 255, 0, 124, + 255, 63, 0, 0, 255, 255, 127, 196, 5, 0, 0, 56, 255, 255, 60, 0, + 126, 126, 126, 0, 127, 127, 255, 255, 63, 0, 255, 255, 255, 7, 0, 0, + 15, 0, 255, 255, 127, 248, 255, 255, 255, 63, 255, 255, 255, 255, 255, 3, + 127, 0, 248, 224, 255, 253, 127, 95, 219, 255, 255, 255, 0, 0, 248, 255, + 255, 255, 252, 255, 0, 0, 255, 15, 0, 0, 223, 255, 192, 255, 255, 255, + 252, 252, 252, 28, 255, 239, 255, 255, 127, 255, 255, 183, 255, 63, 255, 63, + 255, 255, 1, 0, 15, 255, 62, 0, 255, 0, 255, 255, 63, 253, 255, 255, + 255, 255, 191, 145, 255, 255, 55, 0, 255, 255, 255, 192, 111, 240, 239, 254, + 31, 0, 0, 0, 63, 0, 0, 0, 255, 255, 71, 0, 30, 0, 0, 20, + 255, 255, 251, 255, 255, 255, 159, 0, 127, 189, 255, 191, 255, 1, 255, 255, + 159, 25, 129, 224, 179, 0, 0, 0, 255, 255, 63, 127, 0, 0, 0, 63, + 17, 0, 0, 0, 255, 255, 255, 227, 0, 0, 0, 128, 127, 0, 0, 0, + 248, 255, 255, 224, 31, 0, 255, 255, 3, 0, 0, 0, 255, 7, 255, 31, + 255, 1, 255, 67, 255, 255, 223, 255, 255, 255, 255, 223, 100, 222, 255, 235, + 239, 255, 255, 255, 191, 231, 223, 223, 255, 255, 255, 123, 95, 252, 253, 255, + 63, 255, 255, 255, 253, 255, 255, 247, 255, 253, 255, 255, 247, 15, 0, 0, + 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, 255, 251, 255, 15, + 238, 251, 255, 15, 255, 3, 255, 255, +}; + +/* Posix_AlNum: 2089 bytes. */ + +RE_UINT32 re_get_posix_alnum(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_posix_alnum_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_posix_alnum_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_posix_alnum_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_posix_alnum_stage_4[pos + f] << 5; + pos += code; + value = (re_posix_alnum_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Posix_Punct. */ + +static RE_UINT8 re_posix_punct_stage_1[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, +}; + +static RE_UINT8 re_posix_punct_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 11, + 12, 13, 14, 7, 15, 7, 7, 7, 7, 7, 7, 7, 7, 16, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 17, 7, 7, 18, 19, 7, 20, 21, 22, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static RE_UINT8 re_posix_punct_stage_3[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 1, 1, 17, 18, 1, 19, 20, 21, 22, 23, 24, 25, 1, 1, 26, + 27, 28, 29, 30, 31, 29, 29, 32, 29, 29, 29, 33, 34, 35, 36, 37, + 38, 39, 40, 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 41, 1, 1, 1, 1, 1, 1, 42, 1, 43, 44, + 45, 46, 47, 48, 1, 1, 1, 1, 1, 1, 1, 49, 1, 50, 51, 52, + 1, 53, 1, 54, 1, 55, 1, 1, 56, 57, 58, 59, 1, 1, 1, 1, + 60, 61, 62, 1, 63, 64, 65, 66, 1, 1, 1, 1, 67, 1, 1, 1, + 1, 1, 68, 69, 1, 1, 1, 1, 1, 1, 1, 1, 70, 1, 1, 1, + 71, 72, 73, 74, 1, 1, 75, 76, 29, 29, 77, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 10, 1, 78, 79, 80, 29, 29, 81, 82, 83, + 84, 85, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_posix_punct_stage_4[] = { + 0, 1, 2, 3, 0, 4, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 8, 9, 0, 0, 10, + 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 12, 0, 13, 14, 15, 16, + 17, 0, 0, 18, 0, 0, 19, 20, 21, 0, 0, 0, 0, 0, 0, 22, + 0, 23, 14, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 25, + 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 0, 0, 28, + 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 31, + 0, 29, 32, 0, 0, 0, 0, 0, 33, 34, 0, 0, 35, 36, 37, 0, + 0, 0, 38, 0, 36, 0, 0, 39, 0, 0, 0, 40, 41, 0, 0, 0, + 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, 45, + 0, 46, 0, 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 49, 0, 0, 0, 36, 50, 36, 0, 0, 0, 0, 51, 0, 0, + 0, 0, 12, 52, 0, 0, 0, 53, 0, 54, 0, 36, 0, 0, 55, 0, + 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 61, 62, 63, 61, 0, 0, + 64, 65, 66, 0, 67, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 68, 50, 69, 48, 0, 53, 70, 0, 0, + 50, 50, 50, 70, 71, 50, 50, 50, 50, 50, 50, 72, 73, 74, 75, 76, + 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, 0, 27, 0, 0, 0, 0, + 50, 78, 79, 0, 80, 50, 50, 81, 50, 50, 50, 50, 50, 50, 70, 82, + 83, 84, 0, 0, 44, 42, 0, 39, 0, 0, 0, 0, 85, 0, 50, 86, + 61, 87, 88, 50, 87, 89, 50, 61, 0, 0, 0, 0, 0, 0, 50, 50, + 0, 0, 0, 0, 59, 50, 69, 36, 90, 0, 0, 91, 0, 0, 0, 92, + 93, 94, 0, 0, 95, 0, 0, 0, 0, 96, 0, 97, 0, 0, 98, 99, + 0, 98, 29, 0, 0, 0, 100, 0, 0, 0, 53, 101, 0, 0, 36, 26, + 0, 0, 39, 0, 0, 0, 0, 102, 0, 103, 0, 0, 0, 104, 94, 0, + 0, 36, 0, 0, 0, 0, 0, 105, 41, 59, 106, 107, 0, 0, 0, 0, + 1, 2, 2, 108, 0, 0, 0, 109, 79, 110, 0, 111, 112, 42, 59, 113, + 0, 0, 0, 0, 29, 0, 27, 0, 0, 0, 0, 114, 0, 0, 0, 0, + 0, 0, 5, 115, 0, 0, 0, 0, 29, 29, 0, 0, 0, 0, 0, 0, + 0, 0, 116, 29, 0, 0, 117, 118, 0, 111, 0, 0, 119, 0, 0, 0, + 0, 0, 120, 0, 0, 121, 94, 0, 0, 0, 86, 122, 0, 0, 123, 0, + 0, 124, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, + 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 127, 0, 0, 0, 0, 0, + 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, + 0, 0, 0, 98, 0, 0, 0, 129, 0, 110, 130, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 131, 0, 0, 0, 50, 50, 50, 50, 50, 50, 50, 70, + 50, 132, 50, 133, 134, 135, 50, 40, 50, 50, 136, 0, 0, 0, 0, 0, + 50, 50, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 39, + 129, 129, 114, 114, 103, 103, 138, 0, 0, 139, 0, 140, 141, 0, 0, 0, + 50, 142, 50, 50, 81, 143, 144, 70, 59, 145, 38, 146, 147, 0, 0, 148, + 149, 68, 150, 0, 0, 0, 0, 0, 50, 50, 50, 80, 50, 151, 50, 50, + 50, 50, 50, 50, 50, 50, 89, 152, 50, 50, 50, 81, 50, 50, 153, 0, + 142, 50, 154, 50, 60, 21, 0, 0, 116, 0, 0, 0, 155, 0, 42, 0, +}; + +static RE_UINT8 re_posix_punct_stage_5[] = { + 0, 0, 0, 0, 254, 255, 0, 252, 1, 0, 0, 248, 1, 0, 0, 120, + 254, 219, 211, 137, 0, 0, 128, 0, 60, 0, 252, 255, 224, 175, 255, 255, + 0, 0, 32, 64, 176, 0, 0, 0, 0, 0, 64, 0, 4, 0, 0, 0, + 0, 0, 0, 252, 0, 230, 0, 0, 0, 0, 0, 64, 73, 0, 0, 0, + 0, 0, 24, 0, 192, 255, 0, 200, 0, 60, 0, 0, 0, 0, 16, 64, + 0, 2, 0, 96, 255, 63, 0, 0, 0, 0, 192, 3, 0, 0, 255, 127, + 48, 0, 1, 0, 0, 0, 12, 12, 0, 0, 3, 0, 0, 0, 1, 0, + 0, 0, 248, 7, 0, 0, 0, 128, 0, 0, 0, 2, 0, 0, 16, 0, + 0, 128, 0, 12, 254, 255, 255, 252, 0, 0, 80, 61, 32, 0, 0, 0, + 0, 0, 0, 192, 191, 223, 255, 7, 0, 252, 0, 0, 0, 0, 0, 8, + 255, 1, 0, 0, 0, 0, 255, 3, 1, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 24, 0, 56, 0, 0, 0, 0, 96, 0, 0, 0, 112, 15, + 255, 7, 0, 0, 49, 0, 0, 0, 255, 255, 255, 255, 127, 63, 0, 0, + 255, 7, 240, 31, 0, 0, 0, 240, 0, 0, 0, 248, 255, 0, 8, 0, + 0, 0, 0, 160, 3, 224, 0, 224, 0, 224, 0, 96, 0, 0, 255, 255, + 255, 0, 255, 255, 255, 255, 255, 127, 0, 0, 0, 124, 0, 124, 0, 0, + 123, 3, 208, 193, 175, 66, 0, 12, 31, 188, 0, 0, 0, 12, 255, 255, + 255, 255, 255, 7, 127, 0, 0, 0, 255, 255, 63, 0, 0, 0, 240, 255, + 255, 255, 207, 255, 255, 255, 63, 255, 255, 255, 255, 227, 255, 253, 3, 0, + 0, 240, 0, 0, 224, 7, 0, 222, 255, 127, 255, 255, 7, 0, 0, 0, + 255, 255, 255, 251, 255, 255, 15, 0, 0, 0, 255, 15, 30, 255, 255, 255, + 1, 0, 193, 224, 0, 0, 195, 255, 15, 0, 0, 0, 0, 252, 255, 255, + 255, 0, 1, 0, 255, 255, 1, 0, 0, 224, 0, 0, 0, 0, 8, 64, + 0, 0, 252, 0, 255, 255, 127, 0, 3, 0, 0, 0, 0, 6, 0, 0, + 0, 15, 192, 3, 0, 0, 240, 0, 0, 192, 0, 0, 0, 0, 0, 23, + 254, 63, 0, 192, 0, 0, 128, 3, 0, 8, 0, 0, 0, 2, 0, 0, + 0, 0, 252, 255, 0, 0, 0, 48, 255, 255, 247, 255, 127, 15, 0, 0, + 63, 0, 0, 0, 127, 127, 0, 48, 0, 0, 128, 255, 0, 0, 0, 254, + 255, 19, 255, 15, 255, 255, 255, 31, 0, 128, 0, 0, 0, 0, 128, 1, + 0, 0, 255, 1, 0, 1, 0, 0, 0, 0, 127, 0, 0, 0, 0, 30, + 128, 63, 0, 0, 0, 0, 0, 216, 0, 0, 48, 0, 224, 35, 0, 232, + 0, 0, 0, 63, 64, 0, 0, 0, 254, 255, 255, 0, 14, 0, 0, 0, + 0, 0, 31, 0, 0, 0, 32, 0, 48, 0, 0, 0, 0, 0, 0, 144, + 127, 254, 255, 255, 31, 28, 0, 0, 24, 240, 255, 255, 255, 195, 255, 255, + 35, 0, 0, 0, 2, 0, 0, 8, 8, 0, 0, 0, 0, 0, 128, 7, + 0, 224, 223, 255, 239, 15, 0, 0, 255, 15, 255, 255, 255, 127, 254, 255, + 254, 255, 254, 255, 255, 127, 0, 0, 0, 12, 0, 0, 0, 252, 255, 7, + 192, 255, 255, 255, 7, 0, 255, 255, 255, 1, 3, 0, 239, 255, 255, 255, + 255, 31, 15, 0, 255, 255, 31, 0, 255, 0, 255, 3, 31, 0, 0, 0, +}; + +/* Posix_Punct: 1609 bytes. */ + +RE_UINT32 re_get_posix_punct(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_posix_punct_stage_1[f] << 5; + f = code >> 11; + code ^= f << 11; + pos = (RE_UINT32)re_posix_punct_stage_2[pos + f] << 3; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_posix_punct_stage_3[pos + f] << 3; + f = code >> 5; + code ^= f << 5; + pos = (RE_UINT32)re_posix_punct_stage_4[pos + f] << 5; + pos += code; + value = (re_posix_punct_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* Posix_XDigit. */ + +static RE_UINT8 re_posix_xdigit_stage_1[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, +}; + +static RE_UINT8 re_posix_xdigit_stage_2[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_posix_xdigit_stage_3[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_posix_xdigit_stage_4[] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static RE_UINT8 re_posix_xdigit_stage_5[] = { + 0, 0, 0, 0, 0, 0, 255, 3, 126, 0, 0, 0, 126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Posix_XDigit: 97 bytes. */ + +RE_UINT32 re_get_posix_xdigit(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + + f = ch >> 16; + code = ch ^ (f << 16); + pos = (RE_UINT32)re_posix_xdigit_stage_1[f] << 3; + f = code >> 13; + code ^= f << 13; + pos = (RE_UINT32)re_posix_xdigit_stage_2[pos + f] << 3; + f = code >> 10; + code ^= f << 10; + pos = (RE_UINT32)re_posix_xdigit_stage_3[pos + f] << 3; + f = code >> 7; + code ^= f << 7; + pos = (RE_UINT32)re_posix_xdigit_stage_4[pos + f] << 7; + pos += code; + value = (re_posix_xdigit_stage_5[pos >> 3] >> (pos & 0x7)) & 0x1; + + return value; +} + +/* All_Cases. */ + +static RE_UINT8 re_all_cases_stage_1[] = { + 0, 1, 2, 2, 2, 3, 2, 4, 5, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_all_cases_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 10, 11, + 6, 12, 6, 6, 13, 6, 6, 6, 6, 6, 6, 6, 14, 15, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 16, 17, 6, 6, 6, 18, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 19, 6, 6, 6, 20, + 6, 6, 6, 6, 21, 6, 6, 6, 6, 6, 6, 6, 22, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 23, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_all_cases_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 0, 0, 0, 0, 0, 9, 0, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 18, 18, 18, 18, 18, 19, 20, 21, 22, 18, 18, 18, 18, 18, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 21, 34, 18, 18, 35, 18, + 18, 18, 18, 18, 36, 18, 37, 38, 39, 18, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 18, 18, 18, 64, 65, + 66, 66, 11, 11, 11, 11, 15, 15, 15, 15, 67, 67, 18, 18, 18, 18, + 68, 69, 18, 18, 18, 18, 18, 18, 70, 71, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 72, 73, 73, 73, 74, 0, 75, 76, 76, 76, + 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 78, 78, 78, 78, 79, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 82, 83, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 85, 18, 18, 18, + 18, 18, 86, 87, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 88, 89, 82, 83, 88, 89, 88, 89, 82, 83, 90, 91, 88, 89, 92, 93, + 88, 89, 88, 89, 88, 89, 94, 95, 96, 97, 98, 99, 100, 101, 96, 102, + 0, 0, 0, 0, 103, 104, 105, 0, 0, 106, 0, 0, 107, 107, 108, 108, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 110, 111, 111, 111, 112, 112, 112, 113, 0, 0, + 73, 73, 73, 73, 73, 74, 76, 76, 76, 76, 76, 77, 114, 115, 116, 117, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 37, 118, 119, 0, + 120, 120, 120, 120, 121, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 86, 0, 0, + 18, 18, 18, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 69, 18, 69, 18, 18, 18, 18, 18, 18, 18, 0, 123, + 18, 124, 51, 18, 18, 125, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, + 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 11, 11, 4, 5, 15, 15, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 132, 132, 132, 132, 132, 132, 133, 0, 134, 134, 134, 134, 134, 134, 135, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 11, 11, 11, 11, 15, 15, 15, 15, 0, 0, 0, 0, +}; + +static RE_UINT8 re_all_cases_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, + 5, 6, 5, 7, 5, 5, 5, 5, 5, 5, 5, 8, 5, 5, 5, 5, + 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, + 1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 11, + 5, 5, 5, 5, 5, 12, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 13, + 14, 15, 14, 15, 14, 15, 14, 15, 16, 17, 14, 15, 14, 15, 14, 15, + 0, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, + 15, 0, 14, 15, 14, 15, 14, 15, 18, 14, 15, 14, 15, 14, 15, 19, + 20, 21, 14, 15, 14, 15, 22, 14, 15, 23, 23, 14, 15, 0, 24, 25, + 26, 14, 15, 23, 27, 28, 29, 30, 14, 15, 31, 0, 29, 32, 33, 34, + 14, 15, 14, 15, 14, 15, 35, 14, 15, 35, 0, 0, 14, 15, 35, 14, + 15, 36, 36, 14, 15, 14, 15, 37, 14, 15, 0, 0, 14, 15, 0, 38, + 0, 0, 0, 0, 39, 40, 41, 39, 40, 41, 39, 40, 41, 14, 15, 14, + 15, 14, 15, 14, 15, 42, 14, 15, 0, 39, 40, 41, 14, 15, 43, 44, + 45, 0, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 0, 0, 0, 0, + 0, 0, 46, 14, 15, 47, 48, 49, 49, 14, 15, 50, 51, 52, 14, 15, + 53, 54, 55, 56, 57, 0, 58, 58, 0, 59, 0, 60, 61, 0, 0, 0, + 58, 62, 0, 63, 0, 64, 65, 0, 66, 67, 0, 68, 69, 0, 0, 67, + 0, 70, 71, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 73, 0, 0, + 74, 0, 0, 74, 0, 0, 0, 75, 74, 76, 77, 77, 78, 0, 0, 0, + 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 81, 0, + 0, 0, 0, 0, 0, 82, 0, 0, 14, 15, 14, 15, 0, 0, 14, 15, + 0, 0, 0, 33, 33, 33, 0, 83, 0, 0, 0, 0, 0, 0, 84, 0, + 85, 85, 85, 0, 86, 0, 87, 87, 88, 1, 89, 1, 1, 90, 1, 1, + 91, 92, 93, 1, 94, 1, 1, 1, 95, 96, 0, 97, 1, 1, 98, 1, + 1, 99, 1, 1, 100, 101, 101, 101, 102, 5, 103, 5, 5, 104, 5, 5, + 105, 106, 107, 5, 108, 5, 5, 5, 109, 110, 111, 112, 5, 5, 113, 5, + 5, 114, 5, 5, 115, 116, 116, 117, 118, 119, 0, 0, 0, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 0, 14, 15, 129, 14, 15, 0, 45, 45, 45, + 130, 130, 130, 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 131, 131, + 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 14, 15, 14, 15, 14, 15, + 132, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 133, + 0, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 0, 0, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 0, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 0, 136, + 0, 0, 0, 0, 0, 136, 0, 0, 137, 137, 137, 137, 137, 137, 137, 137, + 117, 117, 117, 117, 117, 117, 0, 0, 122, 122, 122, 122, 122, 122, 0, 0, + 0, 138, 0, 0, 0, 139, 0, 0, 140, 141, 14, 15, 14, 15, 14, 15, + 14, 15, 14, 15, 14, 15, 0, 0, 0, 0, 0, 142, 0, 0, 143, 0, + 117, 117, 117, 117, 117, 117, 117, 117, 122, 122, 122, 122, 122, 122, 122, 122, + 0, 117, 0, 117, 0, 117, 0, 117, 0, 122, 0, 122, 0, 122, 0, 122, + 144, 144, 145, 145, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, 0, 0, + 117, 117, 0, 150, 0, 0, 0, 0, 122, 122, 151, 151, 152, 0, 153, 0, + 0, 0, 0, 150, 0, 0, 0, 0, 154, 154, 154, 154, 152, 0, 0, 0, + 117, 117, 0, 155, 0, 0, 0, 0, 122, 122, 156, 156, 0, 0, 0, 0, + 117, 117, 0, 157, 0, 125, 0, 0, 122, 122, 158, 158, 129, 0, 0, 0, + 159, 159, 160, 160, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 0, + 0, 0, 162, 163, 0, 0, 0, 0, 0, 0, 164, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 165, 0, 166, 166, 166, 166, 166, 166, 166, 166, + 167, 167, 167, 167, 167, 167, 167, 167, 0, 0, 0, 14, 15, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 0, 0, 0, 0, 0, 0, + 14, 15, 170, 171, 172, 173, 174, 14, 15, 14, 15, 14, 15, 175, 176, 177, + 178, 0, 14, 15, 0, 14, 15, 0, 0, 0, 0, 0, 0, 0, 179, 179, + 0, 0, 0, 14, 15, 14, 15, 0, 0, 0, 14, 15, 0, 0, 0, 0, + 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 0, 180, + 0, 0, 0, 0, 0, 180, 0, 0, 0, 14, 15, 14, 15, 181, 14, 15, + 0, 0, 0, 14, 15, 182, 0, 0, 14, 15, 183, 184, 185, 186, 0, 0, + 187, 188, 189, 190, 14, 15, 14, 15, 0, 0, 0, 191, 0, 0, 0, 0, + 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, 14, 15, 0, + 193, 193, 193, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 194, 194, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 0, 0, 0, 0, 0, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 0, 0, 0, 0, 0, +}; + +/* All_Cases: 2184 bytes. */ + +static RE_AllCases re_all_cases_table[] = { + {{ 0, 0, 0}}, + {{ 32, 0, 0}}, + {{ 32, 232, 0}}, + {{ 32, 8415, 0}}, + {{ 32, 300, 0}}, + {{ -32, 0, 0}}, + {{ -32, 199, 0}}, + {{ -32, 8383, 0}}, + {{ -32, 268, 0}}, + {{ 743, 775, 0}}, + {{ 32, 8294, 0}}, + {{ 7615, 0, 0}}, + {{ -32, 8262, 0}}, + {{ 121, 0, 0}}, + {{ 1, 0, 0}}, + {{ -1, 0, 0}}, + {{ -199, 0, 0}}, + {{ -232, 0, 0}}, + {{ -121, 0, 0}}, + {{ -300, -268, 0}}, + {{ 195, 0, 0}}, + {{ 210, 0, 0}}, + {{ 206, 0, 0}}, + {{ 205, 0, 0}}, + {{ 79, 0, 0}}, + {{ 202, 0, 0}}, + {{ 203, 0, 0}}, + {{ 207, 0, 0}}, + {{ 97, 0, 0}}, + {{ 211, 0, 0}}, + {{ 209, 0, 0}}, + {{ 163, 0, 0}}, + {{ 213, 0, 0}}, + {{ 130, 0, 0}}, + {{ 214, 0, 0}}, + {{ 218, 0, 0}}, + {{ 217, 0, 0}}, + {{ 219, 0, 0}}, + {{ 56, 0, 0}}, + {{ 1, 2, 0}}, + {{ -1, 1, 0}}, + {{ -2, -1, 0}}, + {{ -79, 0, 0}}, + {{ -97, 0, 0}}, + {{ -56, 0, 0}}, + {{ -130, 0, 0}}, + {{ 10795, 0, 0}}, + {{ -163, 0, 0}}, + {{ 10792, 0, 0}}, + {{ 10815, 0, 0}}, + {{ -195, 0, 0}}, + {{ 69, 0, 0}}, + {{ 71, 0, 0}}, + {{ 10783, 0, 0}}, + {{ 10780, 0, 0}}, + {{ 10782, 0, 0}}, + {{ -210, 0, 0}}, + {{ -206, 0, 0}}, + {{ -205, 0, 0}}, + {{ -202, 0, 0}}, + {{ -203, 0, 0}}, + {{ 42319, 0, 0}}, + {{ 42315, 0, 0}}, + {{ -207, 0, 0}}, + {{ 42280, 0, 0}}, + {{ 42308, 0, 0}}, + {{ -209, 0, 0}}, + {{ -211, 0, 0}}, + {{ 10743, 0, 0}}, + {{ 42305, 0, 0}}, + {{ 10749, 0, 0}}, + {{ -213, 0, 0}}, + {{ -214, 0, 0}}, + {{ 10727, 0, 0}}, + {{ -218, 0, 0}}, + {{ 42282, 0, 0}}, + {{ -69, 0, 0}}, + {{ -217, 0, 0}}, + {{ -71, 0, 0}}, + {{ -219, 0, 0}}, + {{ 42261, 0, 0}}, + {{ 42258, 0, 0}}, + {{ 84, 116, 7289}}, + {{ 116, 0, 0}}, + {{ 38, 0, 0}}, + {{ 37, 0, 0}}, + {{ 64, 0, 0}}, + {{ 63, 0, 0}}, + {{ 7235, 0, 0}}, + {{ 32, 62, 0}}, + {{ 32, 96, 0}}, + {{ 32, 57, 92}}, + {{ -84, 32, 7205}}, + {{ 32, 86, 0}}, + {{ -743, 32, 0}}, + {{ 32, 54, 0}}, + {{ 32, 80, 0}}, + {{ 31, 32, 0}}, + {{ 32, 47, 0}}, + {{ 32, 7549, 0}}, + {{ -38, 0, 0}}, + {{ -37, 0, 0}}, + {{ 7219, 0, 0}}, + {{ -32, 30, 0}}, + {{ -32, 64, 0}}, + {{ -32, 25, 60}}, + {{ -116, -32, 7173}}, + {{ -32, 54, 0}}, + {{ -775, -32, 0}}, + {{ -32, 22, 0}}, + {{ -32, 48, 0}}, + {{ -31, 1, 0}}, + {{ -32, -1, 0}}, + {{ -32, 15, 0}}, + {{ -32, 7517, 0}}, + {{ -64, 0, 0}}, + {{ -63, 0, 0}}, + {{ 8, 0, 0}}, + {{ -62, -30, 0}}, + {{ -57, -25, 35}}, + {{ -47, -15, 0}}, + {{ -54, -22, 0}}, + {{ -8, 0, 0}}, + {{ -86, -54, 0}}, + {{ -80, -48, 0}}, + {{ 7, 0, 0}}, + {{ -116, 0, 0}}, + {{ -92, -60, -35}}, + {{ -96, -64, 0}}, + {{ -7, 0, 0}}, + {{ 80, 0, 0}}, + {{ -80, 0, 0}}, + {{ 15, 0, 0}}, + {{ -15, 0, 0}}, + {{ 48, 0, 0}}, + {{ -48, 0, 0}}, + {{ 7264, 0, 0}}, + {{ 38864, 0, 0}}, + {{ 35332, 0, 0}}, + {{ 3814, 0, 0}}, + {{ 1, 59, 0}}, + {{ -1, 58, 0}}, + {{ -59, -58, 0}}, + {{ -7615, 0, 0}}, + {{ 74, 0, 0}}, + {{ 86, 0, 0}}, + {{ 100, 0, 0}}, + {{ 128, 0, 0}}, + {{ 112, 0, 0}}, + {{ 126, 0, 0}}, + {{ 9, 0, 0}}, + {{ -74, 0, 0}}, + {{ -9, 0, 0}}, + {{ -7289, -7205, -7173}}, + {{ -86, 0, 0}}, + {{ -7235, 0, 0}}, + {{ -100, 0, 0}}, + {{ -7219, 0, 0}}, + {{ -112, 0, 0}}, + {{ -128, 0, 0}}, + {{ -126, 0, 0}}, + {{ -7549, -7517, 0}}, + {{ -8415, -8383, 0}}, + {{ -8294, -8262, 0}}, + {{ 28, 0, 0}}, + {{ -28, 0, 0}}, + {{ 16, 0, 0}}, + {{ -16, 0, 0}}, + {{ 26, 0, 0}}, + {{ -26, 0, 0}}, + {{-10743, 0, 0}}, + {{ -3814, 0, 0}}, + {{-10727, 0, 0}}, + {{-10795, 0, 0}}, + {{-10792, 0, 0}}, + {{-10780, 0, 0}}, + {{-10749, 0, 0}}, + {{-10783, 0, 0}}, + {{-10782, 0, 0}}, + {{-10815, 0, 0}}, + {{ -7264, 0, 0}}, + {{-35332, 0, 0}}, + {{-42280, 0, 0}}, + {{-42308, 0, 0}}, + {{-42319, 0, 0}}, + {{-42315, 0, 0}}, + {{-42305, 0, 0}}, + {{-42258, 0, 0}}, + {{-42282, 0, 0}}, + {{-42261, 0, 0}}, + {{ 928, 0, 0}}, + {{ -928, 0, 0}}, + {{-38864, 0, 0}}, + {{ 40, 0, 0}}, + {{ -40, 0, 0}}, +}; + +/* All_Cases: 2340 bytes. */ + +int re_get_all_cases(RE_UINT32 ch, RE_UINT32* codepoints) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + RE_AllCases* all_cases; + int count; + + f = ch >> 13; + code = ch ^ (f << 13); + pos = (RE_UINT32)re_all_cases_stage_1[f] << 5; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_all_cases_stage_2[pos + f] << 5; + f = code >> 3; + code ^= f << 3; + pos = (RE_UINT32)re_all_cases_stage_3[pos + f] << 3; + value = re_all_cases_stage_4[pos + code]; + + all_cases = &re_all_cases_table[value]; + + codepoints[0] = ch; + count = 1; + + while (count < RE_MAX_CASES && all_cases->diffs[count - 1] != 0) { + codepoints[count] = (RE_UINT32)((RE_INT32)ch + all_cases->diffs[count - + 1]); + ++count; + } + + return count; +} + +/* Simple_Case_Folding. */ + +static RE_UINT8 re_simple_case_folding_stage_1[] = { + 0, 1, 2, 2, 2, 3, 2, 4, 5, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_simple_case_folding_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 10, + 6, 11, 6, 6, 12, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 14, 15, 6, 6, 6, 16, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 17, + 6, 6, 6, 6, 18, 6, 6, 6, 6, 6, 6, 6, 19, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 20, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_simple_case_folding_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 0, 2, 2, 5, 5, 0, 0, 0, 0, + 6, 6, 6, 6, 6, 6, 7, 8, 8, 7, 6, 6, 6, 6, 6, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 8, 20, 6, 6, 21, 6, + 6, 6, 6, 6, 22, 6, 23, 24, 25, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 27, 28, + 29, 30, 1, 2, 31, 32, 0, 0, 33, 34, 35, 6, 6, 6, 36, 37, + 38, 38, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, + 39, 7, 6, 6, 6, 6, 6, 6, 40, 41, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 42, 43, 43, 43, 44, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 45, 45, 45, 46, 47, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 49, 50, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 0, 51, 0, 48, 0, 51, 0, 51, 0, 48, 0, 52, 0, 51, 0, 0, + 0, 51, 0, 51, 0, 51, 0, 53, 0, 54, 0, 55, 0, 56, 0, 57, + 0, 0, 0, 0, 58, 59, 60, 0, 0, 0, 0, 0, 61, 61, 0, 0, + 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 63, 64, 64, 64, 0, 0, 0, 0, 0, 0, + 43, 43, 43, 43, 43, 44, 0, 0, 0, 0, 0, 0, 65, 66, 67, 68, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 23, 69, 33, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 49, 0, 0, + 6, 6, 6, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 6, 7, 6, 6, 6, 6, 6, 6, 6, 0, 70, + 6, 71, 27, 6, 6, 72, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 75, 75, 75, 75, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 76, 76, 76, 76, 76, 76, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_simple_case_folding_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 3, 0, 3, 0, 3, 0, 3, 0, 0, 0, 3, 0, 3, 0, 3, 0, + 0, 3, 0, 3, 0, 3, 0, 3, 4, 3, 0, 3, 0, 3, 0, 5, + 0, 6, 3, 0, 3, 0, 7, 3, 0, 8, 8, 3, 0, 0, 9, 10, + 11, 3, 0, 8, 12, 0, 13, 14, 3, 0, 0, 0, 13, 15, 0, 16, + 3, 0, 3, 0, 3, 0, 17, 3, 0, 17, 0, 0, 3, 0, 17, 3, + 0, 18, 18, 3, 0, 3, 0, 19, 3, 0, 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 20, 3, 0, 20, 3, 0, 20, 3, 0, 3, 0, 3, + 0, 3, 0, 3, 0, 0, 3, 0, 0, 20, 3, 0, 3, 0, 21, 22, + 23, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 0, 0, 0, 0, + 0, 0, 24, 3, 0, 25, 26, 0, 0, 3, 0, 27, 28, 29, 3, 0, + 0, 0, 0, 0, 0, 30, 0, 0, 3, 0, 3, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 31, 0, + 32, 32, 32, 0, 33, 0, 34, 34, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 35, 36, 37, 0, 0, 0, 38, 39, 0, + 40, 41, 0, 0, 42, 43, 0, 3, 0, 44, 3, 0, 0, 23, 23, 23, + 45, 45, 45, 45, 45, 45, 45, 45, 3, 0, 0, 0, 0, 0, 0, 0, + 46, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 0, + 0, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 0, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 0, 48, 0, 0, 0, 0, 0, 48, 0, 0, + 49, 49, 49, 49, 49, 49, 0, 0, 3, 0, 3, 0, 3, 0, 0, 0, + 0, 0, 0, 50, 0, 0, 51, 0, 49, 49, 49, 49, 49, 49, 49, 49, + 0, 49, 0, 49, 0, 49, 0, 49, 49, 49, 52, 52, 53, 0, 54, 0, + 55, 55, 55, 55, 53, 0, 0, 0, 49, 49, 56, 56, 0, 0, 0, 0, + 49, 49, 57, 57, 44, 0, 0, 0, 58, 58, 59, 59, 53, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 61, 62, 0, 0, 0, 0, + 0, 0, 63, 0, 0, 0, 0, 0, 64, 64, 64, 64, 64, 64, 64, 64, + 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 3, 0, 66, 67, 68, 0, 0, 3, + 0, 3, 0, 3, 0, 69, 70, 71, 72, 0, 3, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 73, 73, 0, 0, 0, 3, 0, 3, 0, 0, + 0, 3, 0, 3, 0, 74, 3, 0, 0, 0, 0, 3, 0, 75, 0, 0, + 3, 0, 76, 77, 78, 79, 0, 0, 80, 81, 82, 83, 3, 0, 3, 0, + 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 0, 0, 0, 0, 0, +}; + +/* Simple_Case_Folding: 1624 bytes. */ + +static RE_INT32 re_simple_case_folding_table[] = { + 0, + 32, + 775, + 1, + -121, + -268, + 210, + 206, + 205, + 79, + 202, + 203, + 207, + 211, + 209, + 213, + 214, + 218, + 217, + 219, + 2, + -97, + -56, + -130, + 10795, + -163, + 10792, + -195, + 69, + 71, + 116, + 38, + 37, + 64, + 63, + 8, + -30, + -25, + -15, + -22, + -54, + -48, + -60, + -64, + -7, + 80, + 15, + 48, + 7264, + -8, + -58, + -7615, + -74, + -9, + -7173, + -86, + -100, + -112, + -128, + -126, + -7517, + -8383, + -8262, + 28, + 16, + 26, + -10743, + -3814, + -10727, + -10780, + -10749, + -10783, + -10782, + -10815, + -35332, + -42280, + -42308, + -42319, + -42315, + -42305, + -42258, + -42282, + -42261, + 928, + -38864, + 40, +}; + +/* Simple_Case_Folding: 344 bytes. */ + +RE_UINT32 re_get_simple_case_folding(RE_UINT32 ch) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + RE_INT32 diff; + + f = ch >> 13; + code = ch ^ (f << 13); + pos = (RE_UINT32)re_simple_case_folding_stage_1[f] << 5; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_simple_case_folding_stage_2[pos + f] << 5; + f = code >> 3; + code ^= f << 3; + pos = (RE_UINT32)re_simple_case_folding_stage_3[pos + f] << 3; + value = re_simple_case_folding_stage_4[pos + code]; + + diff = re_simple_case_folding_table[value]; + + return (RE_UINT32)((RE_INT32)ch + diff); +} + +/* Full_Case_Folding. */ + +static RE_UINT8 re_full_case_folding_stage_1[] = { + 0, 1, 2, 2, 2, 3, 2, 4, 5, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, +}; + +static RE_UINT8 re_full_case_folding_stage_2[] = { + 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 10, + 6, 11, 6, 6, 12, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 14, 15, 6, 6, 6, 16, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 17, 6, 6, 6, 18, + 6, 6, 6, 6, 19, 6, 6, 6, 6, 6, 6, 6, 20, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 21, 6, 6, 6, 6, 6, 6, 6, +}; + +static RE_UINT8 re_full_case_folding_stage_3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 0, 2, 2, 5, 6, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 8, 9, 9, 10, 7, 7, 7, 7, 7, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 9, 22, 7, 7, 23, 7, + 7, 7, 7, 7, 24, 7, 25, 26, 27, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 29, 30, + 31, 32, 33, 2, 34, 35, 36, 0, 37, 38, 39, 7, 7, 7, 40, 41, + 42, 42, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, + 43, 44, 7, 7, 7, 7, 7, 7, 45, 46, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 47, 48, 48, 48, 49, 0, 0, 0, 0, 0, + 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 51, 51, 51, 51, 52, 53, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 55, 56, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 0, 57, 0, 54, 0, 57, 0, 57, 0, 54, 58, 59, 0, 57, 0, 0, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 0, 0, 0, 0, 76, 77, 78, 0, 0, 0, 0, 0, 79, 79, 0, 0, + 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 81, 82, 82, 82, 0, 0, 0, 0, 0, 0, + 48, 48, 48, 48, 48, 49, 0, 0, 0, 0, 0, 0, 83, 84, 85, 86, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 25, 87, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 88, 0, 0, + 7, 7, 7, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 44, 7, 44, 7, 7, 7, 7, 7, 7, 7, 0, 89, + 7, 90, 29, 7, 7, 91, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0, + 94, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 96, 96, 96, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 97, 97, 97, 97, 97, 97, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static RE_UINT8 re_full_case_folding_stage_4[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 3, 4, 0, 4, 0, 4, 0, 4, 0, + 5, 0, 4, 0, 4, 0, 4, 0, 0, 4, 0, 4, 0, 4, 0, 4, + 0, 6, 4, 0, 4, 0, 4, 0, 7, 4, 0, 4, 0, 4, 0, 8, + 0, 9, 4, 0, 4, 0, 10, 4, 0, 11, 11, 4, 0, 0, 12, 13, + 14, 4, 0, 11, 15, 0, 16, 17, 4, 0, 0, 0, 16, 18, 0, 19, + 4, 0, 4, 0, 4, 0, 20, 4, 0, 20, 0, 0, 4, 0, 20, 4, + 0, 21, 21, 4, 0, 4, 0, 22, 4, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 23, 4, 0, 23, 4, 0, 23, 4, 0, 4, 0, 4, + 0, 4, 0, 4, 0, 0, 4, 0, 24, 23, 4, 0, 4, 0, 25, 26, + 27, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 0, 0, 0, 0, + 0, 0, 28, 4, 0, 29, 30, 0, 0, 4, 0, 31, 32, 33, 4, 0, + 0, 0, 0, 0, 0, 34, 0, 0, 4, 0, 4, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 35, 0, + 36, 36, 36, 0, 37, 0, 38, 38, 39, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 0, 0, 44, 45, 0, + 46, 47, 0, 0, 48, 49, 0, 4, 0, 50, 4, 0, 0, 27, 27, 27, + 51, 51, 51, 51, 51, 51, 51, 51, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 4, 0, 4, 0, 52, 4, 0, 4, 0, 4, 0, 4, + 0, 4, 0, 4, 0, 4, 0, 0, 0, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 0, 55, 0, 0, 0, 0, 0, 55, 0, 0, + 56, 56, 56, 56, 56, 56, 0, 0, 4, 0, 4, 0, 4, 0, 57, 58, + 59, 60, 61, 62, 0, 0, 63, 0, 56, 56, 56, 56, 56, 56, 56, 56, + 64, 0, 65, 0, 66, 0, 67, 0, 0, 56, 0, 56, 0, 56, 0, 56, + 68, 68, 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, 69, + 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, + 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, + 0, 0, 74, 75, 76, 0, 77, 78, 56, 56, 79, 79, 80, 0, 81, 0, + 0, 0, 82, 83, 84, 0, 85, 86, 87, 87, 87, 87, 88, 0, 0, 0, + 0, 0, 89, 90, 0, 0, 91, 92, 56, 56, 93, 93, 0, 0, 0, 0, + 0, 0, 94, 95, 96, 0, 97, 98, 56, 56, 99, 99, 50, 0, 0, 0, + 0, 0, 100, 101, 102, 0, 103, 104, 105, 105, 106, 106, 107, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 108, 0, 0, 0, 109, 110, 0, 0, 0, 0, + 0, 0, 111, 0, 0, 0, 0, 0, 112, 112, 112, 112, 112, 112, 112, 112, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 4, 0, 114, 115, 116, 0, 0, 4, + 0, 4, 0, 4, 0, 117, 118, 119, 120, 0, 4, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 121, 121, 0, 0, 0, 4, 0, 4, 0, 0, + 4, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 4, 0, 122, 4, 0, + 0, 0, 0, 4, 0, 123, 0, 0, 4, 0, 124, 125, 126, 127, 0, 0, + 128, 129, 130, 131, 4, 0, 4, 0, 132, 132, 132, 132, 132, 132, 132, 132, + 133, 134, 135, 136, 137, 138, 139, 0, 0, 0, 0, 140, 141, 142, 143, 144, + 145, 145, 145, 145, 145, 145, 145, 145, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 0, 0, 0, 0, 0, +}; + +/* Full_Case_Folding: 1824 bytes. */ + +static RE_FullCaseFolding re_full_case_folding_table[] = { + { 0, { 0, 0}}, + { 32, { 0, 0}}, + { 775, { 0, 0}}, + { -108, { 115, 0}}, + { 1, { 0, 0}}, + { -199, { 775, 0}}, + { 371, { 110, 0}}, + { -121, { 0, 0}}, + { -268, { 0, 0}}, + { 210, { 0, 0}}, + { 206, { 0, 0}}, + { 205, { 0, 0}}, + { 79, { 0, 0}}, + { 202, { 0, 0}}, + { 203, { 0, 0}}, + { 207, { 0, 0}}, + { 211, { 0, 0}}, + { 209, { 0, 0}}, + { 213, { 0, 0}}, + { 214, { 0, 0}}, + { 218, { 0, 0}}, + { 217, { 0, 0}}, + { 219, { 0, 0}}, + { 2, { 0, 0}}, + { -390, { 780, 0}}, + { -97, { 0, 0}}, + { -56, { 0, 0}}, + { -130, { 0, 0}}, + { 10795, { 0, 0}}, + { -163, { 0, 0}}, + { 10792, { 0, 0}}, + { -195, { 0, 0}}, + { 69, { 0, 0}}, + { 71, { 0, 0}}, + { 116, { 0, 0}}, + { 38, { 0, 0}}, + { 37, { 0, 0}}, + { 64, { 0, 0}}, + { 63, { 0, 0}}, + { 41, { 776, 769}}, + { 21, { 776, 769}}, + { 8, { 0, 0}}, + { -30, { 0, 0}}, + { -25, { 0, 0}}, + { -15, { 0, 0}}, + { -22, { 0, 0}}, + { -54, { 0, 0}}, + { -48, { 0, 0}}, + { -60, { 0, 0}}, + { -64, { 0, 0}}, + { -7, { 0, 0}}, + { 80, { 0, 0}}, + { 15, { 0, 0}}, + { 48, { 0, 0}}, + { -34, {1410, 0}}, + { 7264, { 0, 0}}, + { -8, { 0, 0}}, + { -7726, { 817, 0}}, + { -7715, { 776, 0}}, + { -7713, { 778, 0}}, + { -7712, { 778, 0}}, + { -7737, { 702, 0}}, + { -58, { 0, 0}}, + { -7723, { 115, 0}}, + { -7051, { 787, 0}}, + { -7053, { 787, 768}}, + { -7055, { 787, 769}}, + { -7057, { 787, 834}}, + { -128, { 953, 0}}, + { -136, { 953, 0}}, + { -112, { 953, 0}}, + { -120, { 953, 0}}, + { -64, { 953, 0}}, + { -72, { 953, 0}}, + { -66, { 953, 0}}, + { -7170, { 953, 0}}, + { -7176, { 953, 0}}, + { -7173, { 834, 0}}, + { -7174, { 834, 953}}, + { -74, { 0, 0}}, + { -7179, { 953, 0}}, + { -7173, { 0, 0}}, + { -78, { 953, 0}}, + { -7180, { 953, 0}}, + { -7190, { 953, 0}}, + { -7183, { 834, 0}}, + { -7184, { 834, 953}}, + { -86, { 0, 0}}, + { -7189, { 953, 0}}, + { -7193, { 776, 768}}, + { -7194, { 776, 769}}, + { -7197, { 834, 0}}, + { -7198, { 776, 834}}, + { -100, { 0, 0}}, + { -7197, { 776, 768}}, + { -7198, { 776, 769}}, + { -7203, { 787, 0}}, + { -7201, { 834, 0}}, + { -7202, { 776, 834}}, + { -112, { 0, 0}}, + { -118, { 953, 0}}, + { -7210, { 953, 0}}, + { -7206, { 953, 0}}, + { -7213, { 834, 0}}, + { -7214, { 834, 953}}, + { -128, { 0, 0}}, + { -126, { 0, 0}}, + { -7219, { 953, 0}}, + { -7517, { 0, 0}}, + { -8383, { 0, 0}}, + { -8262, { 0, 0}}, + { 28, { 0, 0}}, + { 16, { 0, 0}}, + { 26, { 0, 0}}, + {-10743, { 0, 0}}, + { -3814, { 0, 0}}, + {-10727, { 0, 0}}, + {-10780, { 0, 0}}, + {-10749, { 0, 0}}, + {-10783, { 0, 0}}, + {-10782, { 0, 0}}, + {-10815, { 0, 0}}, + {-35332, { 0, 0}}, + {-42280, { 0, 0}}, + {-42308, { 0, 0}}, + {-42319, { 0, 0}}, + {-42315, { 0, 0}}, + {-42305, { 0, 0}}, + {-42258, { 0, 0}}, + {-42282, { 0, 0}}, + {-42261, { 0, 0}}, + { 928, { 0, 0}}, + {-38864, { 0, 0}}, + {-64154, { 102, 0}}, + {-64155, { 105, 0}}, + {-64156, { 108, 0}}, + {-64157, { 102, 105}}, + {-64158, { 102, 108}}, + {-64146, { 116, 0}}, + {-64147, { 116, 0}}, + {-62879, {1398, 0}}, + {-62880, {1381, 0}}, + {-62881, {1387, 0}}, + {-62872, {1398, 0}}, + {-62883, {1389, 0}}, + { 40, { 0, 0}}, +}; + +/* Full_Case_Folding: 1168 bytes. */ + +int re_get_full_case_folding(RE_UINT32 ch, RE_UINT32* codepoints) { + RE_UINT32 code; + RE_UINT32 f; + RE_UINT32 pos; + RE_UINT32 value; + RE_FullCaseFolding* case_folding; + int count; + + f = ch >> 13; + code = ch ^ (f << 13); + pos = (RE_UINT32)re_full_case_folding_stage_1[f] << 5; + f = code >> 8; + code ^= f << 8; + pos = (RE_UINT32)re_full_case_folding_stage_2[pos + f] << 5; + f = code >> 3; + code ^= f << 3; + pos = (RE_UINT32)re_full_case_folding_stage_3[pos + f] << 3; + value = re_full_case_folding_stage_4[pos + code]; + + case_folding = &re_full_case_folding_table[value]; + + codepoints[0] = (RE_UINT32)((RE_INT32)ch + case_folding->diff); + count = 1; + + while (count < RE_MAX_FOLDED && case_folding->codepoints[count - 1] != 0) { + codepoints[count] = case_folding->codepoints[count - 1]; + ++count; + } + + return count; +} + +/* Property function table. */ + +RE_GetPropertyFunc re_get_property[] = { + re_get_general_category, + re_get_block, + re_get_script, + re_get_word_break, + re_get_grapheme_cluster_break, + re_get_sentence_break, + re_get_math, + re_get_alphabetic, + re_get_lowercase, + re_get_uppercase, + re_get_cased, + re_get_case_ignorable, + re_get_changes_when_lowercased, + re_get_changes_when_uppercased, + re_get_changes_when_titlecased, + re_get_changes_when_casefolded, + re_get_changes_when_casemapped, + re_get_id_start, + re_get_id_continue, + re_get_xid_start, + re_get_xid_continue, + re_get_default_ignorable_code_point, + re_get_grapheme_extend, + re_get_grapheme_base, + re_get_grapheme_link, + re_get_white_space, + re_get_bidi_control, + re_get_join_control, + re_get_dash, + re_get_hyphen, + re_get_quotation_mark, + re_get_terminal_punctuation, + re_get_other_math, + re_get_hex_digit, + re_get_ascii_hex_digit, + re_get_other_alphabetic, + re_get_ideographic, + re_get_diacritic, + re_get_extender, + re_get_other_lowercase, + re_get_other_uppercase, + re_get_noncharacter_code_point, + re_get_other_grapheme_extend, + re_get_ids_binary_operator, + re_get_ids_trinary_operator, + re_get_radical, + re_get_unified_ideograph, + re_get_other_default_ignorable_code_point, + re_get_deprecated, + re_get_soft_dotted, + re_get_logical_order_exception, + re_get_other_id_start, + re_get_other_id_continue, + re_get_sterm, + re_get_variation_selector, + re_get_pattern_white_space, + re_get_pattern_syntax, + re_get_hangul_syllable_type, + re_get_bidi_class, + re_get_canonical_combining_class, + re_get_decomposition_type, + re_get_east_asian_width, + re_get_joining_group, + re_get_joining_type, + re_get_line_break, + re_get_numeric_type, + re_get_numeric_value, + re_get_bidi_mirrored, + re_get_indic_positional_category, + re_get_indic_syllabic_category, + re_get_alphanumeric, + re_get_any, + re_get_blank, + re_get_graph, + re_get_print, + re_get_word, + re_get_xdigit, + re_get_posix_digit, + re_get_posix_alnum, + re_get_posix_punct, + re_get_posix_xdigit, +}; diff --git a/lib/regex/_regex_unicode.h b/lib/regex/_regex_unicode.h new file mode 100644 index 000000000..0d2fd62db --- /dev/null +++ b/lib/regex/_regex_unicode.h @@ -0,0 +1,226 @@ +typedef unsigned char RE_UINT8; +typedef signed char RE_INT8; +typedef unsigned short RE_UINT16; +typedef signed short RE_INT16; +typedef unsigned int RE_UINT32; +typedef signed int RE_INT32; + +typedef unsigned char BOOL; +enum {FALSE, TRUE}; + +#define RE_ASCII_MAX 0x7F +#define RE_LOCALE_MAX 0xFF +#define RE_UNICODE_MAX 0x10FFFF + +#define RE_MAX_CASES 4 +#define RE_MAX_FOLDED 3 + +typedef struct RE_Property { + RE_UINT16 name; + RE_UINT8 id; + RE_UINT8 value_set; +} RE_Property; + +typedef struct RE_PropertyValue { + RE_UINT16 name; + RE_UINT8 value_set; + RE_UINT16 id; +} RE_PropertyValue; + +typedef RE_UINT32 (*RE_GetPropertyFunc)(RE_UINT32 ch); + +#define RE_PROP_GC 0x0 +#define RE_PROP_CASED 0xA +#define RE_PROP_UPPERCASE 0x9 +#define RE_PROP_LOWERCASE 0x8 + +#define RE_PROP_C 30 +#define RE_PROP_L 31 +#define RE_PROP_M 32 +#define RE_PROP_N 33 +#define RE_PROP_P 34 +#define RE_PROP_S 35 +#define RE_PROP_Z 36 +#define RE_PROP_ASSIGNED 38 +#define RE_PROP_CASEDLETTER 37 + +#define RE_PROP_CN 0 +#define RE_PROP_LU 1 +#define RE_PROP_LL 2 +#define RE_PROP_LT 3 +#define RE_PROP_LM 4 +#define RE_PROP_LO 5 +#define RE_PROP_MN 6 +#define RE_PROP_ME 7 +#define RE_PROP_MC 8 +#define RE_PROP_ND 9 +#define RE_PROP_NL 10 +#define RE_PROP_NO 11 +#define RE_PROP_ZS 12 +#define RE_PROP_ZL 13 +#define RE_PROP_ZP 14 +#define RE_PROP_CC 15 +#define RE_PROP_CF 16 +#define RE_PROP_CO 17 +#define RE_PROP_CS 18 +#define RE_PROP_PD 19 +#define RE_PROP_PS 20 +#define RE_PROP_PE 21 +#define RE_PROP_PC 22 +#define RE_PROP_PO 23 +#define RE_PROP_SM 24 +#define RE_PROP_SC 25 +#define RE_PROP_SK 26 +#define RE_PROP_SO 27 +#define RE_PROP_PI 28 +#define RE_PROP_PF 29 + +#define RE_PROP_C_MASK 0x00078001 +#define RE_PROP_L_MASK 0x0000003E +#define RE_PROP_M_MASK 0x000001C0 +#define RE_PROP_N_MASK 0x00000E00 +#define RE_PROP_P_MASK 0x30F80000 +#define RE_PROP_S_MASK 0x0F000000 +#define RE_PROP_Z_MASK 0x00007000 + +#define RE_PROP_ALNUM 0x460001 +#define RE_PROP_ALPHA 0x070001 +#define RE_PROP_ANY 0x470001 +#define RE_PROP_ASCII 0x010001 +#define RE_PROP_BLANK 0x480001 +#define RE_PROP_CNTRL 0x00000F +#define RE_PROP_DIGIT 0x000009 +#define RE_PROP_GRAPH 0x490001 +#define RE_PROP_LOWER 0x080001 +#define RE_PROP_PRINT 0x4A0001 +#define RE_PROP_SPACE 0x190001 +#define RE_PROP_UPPER 0x090001 +#define RE_PROP_WORD 0x4B0001 +#define RE_PROP_XDIGIT 0x4C0001 +#define RE_PROP_POSIX_ALNUM 0x4E0001 +#define RE_PROP_POSIX_DIGIT 0x4D0001 +#define RE_PROP_POSIX_PUNCT 0x4F0001 +#define RE_PROP_POSIX_XDIGIT 0x500001 + +#define RE_BREAK_OTHER 0 +#define RE_BREAK_DOUBLEQUOTE 1 +#define RE_BREAK_SINGLEQUOTE 2 +#define RE_BREAK_HEBREWLETTER 3 +#define RE_BREAK_CR 4 +#define RE_BREAK_LF 5 +#define RE_BREAK_NEWLINE 6 +#define RE_BREAK_EXTEND 7 +#define RE_BREAK_REGIONALINDICATOR 8 +#define RE_BREAK_FORMAT 9 +#define RE_BREAK_KATAKANA 10 +#define RE_BREAK_ALETTER 11 +#define RE_BREAK_MIDLETTER 12 +#define RE_BREAK_MIDNUM 13 +#define RE_BREAK_MIDNUMLET 14 +#define RE_BREAK_NUMERIC 15 +#define RE_BREAK_EXTENDNUMLET 16 + +#define RE_GBREAK_OTHER 0 +#define RE_GBREAK_CR 1 +#define RE_GBREAK_LF 2 +#define RE_GBREAK_CONTROL 3 +#define RE_GBREAK_EXTEND 4 +#define RE_GBREAK_REGIONALINDICATOR 5 +#define RE_GBREAK_SPACINGMARK 6 +#define RE_GBREAK_L 7 +#define RE_GBREAK_V 8 +#define RE_GBREAK_T 9 +#define RE_GBREAK_LV 10 +#define RE_GBREAK_LVT 11 +#define RE_GBREAK_PREPEND 12 + +extern char* re_strings[1296]; +extern RE_Property re_properties[147]; +extern RE_PropertyValue re_property_values[1412]; +extern RE_UINT16 re_expand_on_folding[104]; +extern RE_GetPropertyFunc re_get_property[81]; + +RE_UINT32 re_get_general_category(RE_UINT32 ch); +RE_UINT32 re_get_block(RE_UINT32 ch); +RE_UINT32 re_get_script(RE_UINT32 ch); +RE_UINT32 re_get_word_break(RE_UINT32 ch); +RE_UINT32 re_get_grapheme_cluster_break(RE_UINT32 ch); +RE_UINT32 re_get_sentence_break(RE_UINT32 ch); +RE_UINT32 re_get_math(RE_UINT32 ch); +RE_UINT32 re_get_alphabetic(RE_UINT32 ch); +RE_UINT32 re_get_lowercase(RE_UINT32 ch); +RE_UINT32 re_get_uppercase(RE_UINT32 ch); +RE_UINT32 re_get_cased(RE_UINT32 ch); +RE_UINT32 re_get_case_ignorable(RE_UINT32 ch); +RE_UINT32 re_get_changes_when_lowercased(RE_UINT32 ch); +RE_UINT32 re_get_changes_when_uppercased(RE_UINT32 ch); +RE_UINT32 re_get_changes_when_titlecased(RE_UINT32 ch); +RE_UINT32 re_get_changes_when_casefolded(RE_UINT32 ch); +RE_UINT32 re_get_changes_when_casemapped(RE_UINT32 ch); +RE_UINT32 re_get_id_start(RE_UINT32 ch); +RE_UINT32 re_get_id_continue(RE_UINT32 ch); +RE_UINT32 re_get_xid_start(RE_UINT32 ch); +RE_UINT32 re_get_xid_continue(RE_UINT32 ch); +RE_UINT32 re_get_default_ignorable_code_point(RE_UINT32 ch); +RE_UINT32 re_get_grapheme_extend(RE_UINT32 ch); +RE_UINT32 re_get_grapheme_base(RE_UINT32 ch); +RE_UINT32 re_get_grapheme_link(RE_UINT32 ch); +RE_UINT32 re_get_white_space(RE_UINT32 ch); +RE_UINT32 re_get_bidi_control(RE_UINT32 ch); +RE_UINT32 re_get_join_control(RE_UINT32 ch); +RE_UINT32 re_get_dash(RE_UINT32 ch); +RE_UINT32 re_get_hyphen(RE_UINT32 ch); +RE_UINT32 re_get_quotation_mark(RE_UINT32 ch); +RE_UINT32 re_get_terminal_punctuation(RE_UINT32 ch); +RE_UINT32 re_get_other_math(RE_UINT32 ch); +RE_UINT32 re_get_hex_digit(RE_UINT32 ch); +RE_UINT32 re_get_ascii_hex_digit(RE_UINT32 ch); +RE_UINT32 re_get_other_alphabetic(RE_UINT32 ch); +RE_UINT32 re_get_ideographic(RE_UINT32 ch); +RE_UINT32 re_get_diacritic(RE_UINT32 ch); +RE_UINT32 re_get_extender(RE_UINT32 ch); +RE_UINT32 re_get_other_lowercase(RE_UINT32 ch); +RE_UINT32 re_get_other_uppercase(RE_UINT32 ch); +RE_UINT32 re_get_noncharacter_code_point(RE_UINT32 ch); +RE_UINT32 re_get_other_grapheme_extend(RE_UINT32 ch); +RE_UINT32 re_get_ids_binary_operator(RE_UINT32 ch); +RE_UINT32 re_get_ids_trinary_operator(RE_UINT32 ch); +RE_UINT32 re_get_radical(RE_UINT32 ch); +RE_UINT32 re_get_unified_ideograph(RE_UINT32 ch); +RE_UINT32 re_get_other_default_ignorable_code_point(RE_UINT32 ch); +RE_UINT32 re_get_deprecated(RE_UINT32 ch); +RE_UINT32 re_get_soft_dotted(RE_UINT32 ch); +RE_UINT32 re_get_logical_order_exception(RE_UINT32 ch); +RE_UINT32 re_get_other_id_start(RE_UINT32 ch); +RE_UINT32 re_get_other_id_continue(RE_UINT32 ch); +RE_UINT32 re_get_sterm(RE_UINT32 ch); +RE_UINT32 re_get_variation_selector(RE_UINT32 ch); +RE_UINT32 re_get_pattern_white_space(RE_UINT32 ch); +RE_UINT32 re_get_pattern_syntax(RE_UINT32 ch); +RE_UINT32 re_get_hangul_syllable_type(RE_UINT32 ch); +RE_UINT32 re_get_bidi_class(RE_UINT32 ch); +RE_UINT32 re_get_canonical_combining_class(RE_UINT32 ch); +RE_UINT32 re_get_decomposition_type(RE_UINT32 ch); +RE_UINT32 re_get_east_asian_width(RE_UINT32 ch); +RE_UINT32 re_get_joining_group(RE_UINT32 ch); +RE_UINT32 re_get_joining_type(RE_UINT32 ch); +RE_UINT32 re_get_line_break(RE_UINT32 ch); +RE_UINT32 re_get_numeric_type(RE_UINT32 ch); +RE_UINT32 re_get_numeric_value(RE_UINT32 ch); +RE_UINT32 re_get_bidi_mirrored(RE_UINT32 ch); +RE_UINT32 re_get_indic_positional_category(RE_UINT32 ch); +RE_UINT32 re_get_indic_syllabic_category(RE_UINT32 ch); +RE_UINT32 re_get_alphanumeric(RE_UINT32 ch); +RE_UINT32 re_get_any(RE_UINT32 ch); +RE_UINT32 re_get_blank(RE_UINT32 ch); +RE_UINT32 re_get_graph(RE_UINT32 ch); +RE_UINT32 re_get_print(RE_UINT32 ch); +RE_UINT32 re_get_word(RE_UINT32 ch); +RE_UINT32 re_get_xdigit(RE_UINT32 ch); +RE_UINT32 re_get_posix_digit(RE_UINT32 ch); +RE_UINT32 re_get_posix_alnum(RE_UINT32 ch); +RE_UINT32 re_get_posix_punct(RE_UINT32 ch); +RE_UINT32 re_get_posix_xdigit(RE_UINT32 ch); +int re_get_all_cases(RE_UINT32 ch, RE_UINT32* codepoints); +RE_UINT32 re_get_simple_case_folding(RE_UINT32 ch); +int re_get_full_case_folding(RE_UINT32 ch, RE_UINT32* codepoints); diff --git a/lib/regex/test_regex.py b/lib/regex/test_regex.py new file mode 100644 index 000000000..050c3c004 --- /dev/null +++ b/lib/regex/test_regex.py @@ -0,0 +1,3585 @@ +from __future__ import with_statement +import regex +import string +from weakref import proxy +import unittest +import copy +from test.test_support import run_unittest +import re + +# _AssertRaisesContext is defined here because the class doesn't exist before +# Python 2.7. +class _AssertRaisesContext(object): + """A context manager used to implement TestCase.assertRaises* methods.""" + + def __init__(self, expected, test_case, expected_regexp=None): + self.expected = expected + self.failureException = test_case.failureException + self.expected_regexp = expected_regexp + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + if exc_type is None: + try: + exc_name = self.expected.__name__ + except AttributeError: + exc_name = str(self.expected) + raise self.failureException( + "%s not raised" % exc_name) + if not issubclass(exc_type, self.expected): + # let unexpected exceptions pass through + return False + self.exception = exc_value # store for later retrieval + if self.expected_regexp is None: + return True + + expected_regexp = self.expected_regexp + if isinstance(expected_regexp, basestring): + expected_regexp = re.compile(expected_regexp) + if not expected_regexp.search(str(exc_value)): + raise self.failureException('"%s" does not match "%s"' % + (expected_regexp.pattern, str(exc_value))) + return True + +class RegexTests(unittest.TestCase): + PATTERN_CLASS = "<type '_regex.Pattern'>" + FLAGS_WITH_COMPILED_PAT = "cannot process flags argument with a compiled pattern" + INVALID_GROUP_REF = "invalid group reference" + MISSING_GT = "missing >" + BAD_GROUP_NAME = "bad character in group name" + MISSING_GROUP_NAME = "missing group name" + MISSING_LT = "missing <" + UNKNOWN_GROUP_I = "unknown group" + UNKNOWN_GROUP = "unknown group" + BAD_ESCAPE = r"bad escape \(end of pattern\)" + BAD_OCTAL_ESCAPE = r"bad escape \\" + BAD_SET = "unterminated character set" + STR_PAT_ON_BYTES = "cannot use a string pattern on a bytes-like object" + BYTES_PAT_ON_STR = "cannot use a bytes pattern on a string-like object" + STR_PAT_BYTES_TEMPL = "expected str instance, bytes found" + BYTES_PAT_STR_TEMPL = "expected a bytes-like object, str found" + BYTES_PAT_UNI_FLAG = "cannot use UNICODE flag with a bytes pattern" + MIXED_FLAGS = "ASCII, LOCALE and UNICODE flags are mutually incompatible" + MISSING_RPAREN = "missing \\)" + TRAILING_CHARS = "unbalanced parenthesis" + BAD_CHAR_RANGE = "bad character range" + NOTHING_TO_REPEAT = "nothing to repeat" + MULTIPLE_REPEAT = "multiple repeat" + OPEN_GROUP = "cannot refer to an open group" + DUPLICATE_GROUP = "duplicate group" + CANT_TURN_OFF = "bad inline flags: cannot turn flags off" + UNDEF_CHAR_NAME = "undefined character name" + + # assertRaisesRegex is defined here because the method isn't in the + # superclass before Python 2.7. + def assertRaisesRegex(self, expected_exception, expected_regexp, + callable_obj=None, *args, **kwargs): + """Asserts that the message in a raised exception matches a regexp. + + Args: + expected_exception: Exception class expected to be raised. + expected_regexp: Regexp (re pattern object or string) expected + to be found in error message. + callable_obj: Function to be called. + args: Extra args. + kwargs: Extra kwargs. + """ + context = _AssertRaisesContext(expected_exception, self, expected_regexp) + if callable_obj is None: + return context + with context: + callable_obj(*args, **kwargs) + + def assertTypedEqual(self, actual, expect, msg=None): + self.assertEqual(actual, expect, msg) + + def recurse(actual, expect): + if isinstance(expect, (tuple, list)): + for x, y in zip(actual, expect): + recurse(x, y) + else: + self.assertIs(type(actual), type(expect), msg) + + recurse(actual, expect) + + def test_weakref(self): + s = 'QabbbcR' + x = regex.compile('ab+c') + y = proxy(x) + if x.findall('QabbbcR') != y.findall('QabbbcR'): + self.fail() + + def test_search_star_plus(self): + self.assertEqual(regex.search('a*', 'xxx').span(0), (0, 0)) + self.assertEqual(regex.search('x*', 'axx').span(), (0, 0)) + self.assertEqual(regex.search('x+', 'axx').span(0), (1, 3)) + self.assertEqual(regex.search('x+', 'axx').span(), (1, 3)) + self.assertEqual(regex.search('x', 'aaa'), None) + self.assertEqual(regex.match('a*', 'xxx').span(0), (0, 0)) + self.assertEqual(regex.match('a*', 'xxx').span(), (0, 0)) + self.assertEqual(regex.match('x*', 'xxxa').span(0), (0, 3)) + self.assertEqual(regex.match('x*', 'xxxa').span(), (0, 3)) + self.assertEqual(regex.match('a+', 'xxx'), None) + + def bump_num(self, matchobj): + int_value = int(matchobj[0]) + return str(int_value + 1) + + def test_basic_regex_sub(self): + self.assertEqual(regex.sub("(?i)b+", "x", "bbbb BBBB"), 'x x') + self.assertEqual(regex.sub(r'\d+', self.bump_num, '08.2 -2 23x99y'), + '9.3 -3 24x100y') + self.assertEqual(regex.sub(r'\d+', self.bump_num, '08.2 -2 23x99y', 3), + '9.3 -3 23x99y') + + self.assertEqual(regex.sub('.', lambda m: r"\n", 'x'), "\\n") + self.assertEqual(regex.sub('.', r"\n", 'x'), "\n") + + self.assertEqual(regex.sub('(?P<a>x)', r'\g<a>\g<a>', 'xx'), 'xxxx') + self.assertEqual(regex.sub('(?P<a>x)', r'\g<a>\g<1>', 'xx'), 'xxxx') + self.assertEqual(regex.sub('(?P<unk>x)', r'\g<unk>\g<unk>', 'xx'), + 'xxxx') + self.assertEqual(regex.sub('(?P<unk>x)', r'\g<1>\g<1>', 'xx'), 'xxxx') + + self.assertEqual(regex.sub('a', r'\t\n\v\r\f\a\b\B\Z\a\A\w\W\s\S\d\D', + 'a'), "\t\n\v\r\f\a\b\\B\\Z\a\\A\\w\\W\\s\\S\\d\\D") + self.assertEqual(regex.sub('a', '\t\n\v\r\f\a', 'a'), "\t\n\v\r\f\a") + self.assertEqual(regex.sub('a', '\t\n\v\r\f\a', 'a'), chr(9) + chr(10) + + chr(11) + chr(13) + chr(12) + chr(7)) + + self.assertEqual(regex.sub(r'^\s*', 'X', 'test'), 'Xtest') + + self.assertEqual(regex.sub(ur"x", ur"\x0A", u"x"), u"\n") + self.assertEqual(regex.sub(ur"x", ur"\u000A", u"x"), u"\n") + self.assertEqual(regex.sub(ur"x", ur"\U0000000A", u"x"), u"\n") + self.assertEqual(regex.sub(ur"x", ur"\N{LATIN CAPITAL LETTER A}", + u"x"), u"A") + + self.assertEqual(regex.sub(r"x", r"\x0A", "x"), "\n") + self.assertEqual(regex.sub(r"x", r"\u000A", "x"), "\\u000A") + self.assertEqual(regex.sub(r"x", r"\U0000000A", "x"), + "\\U0000000A") + self.assertEqual(regex.sub(r"x", r"\N{LATIN CAPITAL LETTER A}", + "x"), "\\N{LATIN CAPITAL LETTER A}") + + def test_bug_449964(self): + # Fails for group followed by other escape. + self.assertEqual(regex.sub(r'(?P<unk>x)', r'\g<1>\g<1>\b', 'xx'), + "xx\bxx\b") + + def test_bug_449000(self): + # Test for sub() on escaped characters. + self.assertEqual(regex.sub(r'\r\n', r'\n', 'abc\r\ndef\r\n'), + "abc\ndef\n") + self.assertEqual(regex.sub('\r\n', r'\n', 'abc\r\ndef\r\n'), + "abc\ndef\n") + self.assertEqual(regex.sub(r'\r\n', '\n', 'abc\r\ndef\r\n'), + "abc\ndef\n") + self.assertEqual(regex.sub('\r\n', '\n', 'abc\r\ndef\r\n'), + "abc\ndef\n") + + def test_bug_1140(self): + # regex.sub(x, y, u'') should return u'', not '', and + # regex.sub(x, y, '') should return '', not u''. + # Also: + # regex.sub(x, y, unicode(x)) should return unicode(y), and + # regex.sub(x, y, str(x)) should return + # str(y) if isinstance(y, str) else unicode(y). + for x in 'x', u'x': + for y in 'y', u'y': + z = regex.sub(x, y, u'') + self.assertEqual((type(z), z), (unicode, u'')) + z = regex.sub(x, y, '') + self.assertEqual((type(z), z), (str, '')) + z = regex.sub(x, y, unicode(x)) + self.assertEqual((type(z), z), (unicode, unicode(y))) + z = regex.sub(x, y, str(x)) + self.assertEqual((type(z), z), (type(y), y)) + + def test_bug_1661(self): + # Verify that flags do not get silently ignored with compiled patterns + pattern = regex.compile('.') + self.assertRaisesRegex(ValueError, self.FLAGS_WITH_COMPILED_PAT, + lambda: regex.match(pattern, 'A', regex.I)) + self.assertRaisesRegex(ValueError, self.FLAGS_WITH_COMPILED_PAT, + lambda: regex.search(pattern, 'A', regex.I)) + self.assertRaisesRegex(ValueError, self.FLAGS_WITH_COMPILED_PAT, + lambda: regex.findall(pattern, 'A', regex.I)) + self.assertRaisesRegex(ValueError, self.FLAGS_WITH_COMPILED_PAT, + lambda: regex.compile(pattern, regex.I)) + + def test_bug_3629(self): + # A regex that triggered a bug in the sre-code validator + self.assertEqual(repr(type(regex.compile("(?P<quote>)(?(quote))"))), + self.PATTERN_CLASS) + + def test_sub_template_numeric_escape(self): + # Bug 776311 and friends. + self.assertEqual(regex.sub('x', r'\0', 'x'), "\0") + self.assertEqual(regex.sub('x', r'\000', 'x'), "\000") + self.assertEqual(regex.sub('x', r'\001', 'x'), "\001") + self.assertEqual(regex.sub('x', r'\008', 'x'), "\0" + "8") + self.assertEqual(regex.sub('x', r'\009', 'x'), "\0" + "9") + self.assertEqual(regex.sub('x', r'\111', 'x'), "\111") + self.assertEqual(regex.sub('x', r'\117', 'x'), "\117") + + self.assertEqual(regex.sub('x', r'\1111', 'x'), "\1111") + self.assertEqual(regex.sub('x', r'\1111', 'x'), "\111" + "1") + + self.assertEqual(regex.sub('x', r'\00', 'x'), '\x00') + self.assertEqual(regex.sub('x', r'\07', 'x'), '\x07') + self.assertEqual(regex.sub('x', r'\08', 'x'), "\0" + "8") + self.assertEqual(regex.sub('x', r'\09', 'x'), "\0" + "9") + self.assertEqual(regex.sub('x', r'\0a', 'x'), "\0" + "a") + + self.assertEqual(regex.sub(u'x', ur'\400', u'x'), u"\u0100") + self.assertEqual(regex.sub(u'x', ur'\777', u'x'), u"\u01FF") + self.assertEqual(regex.sub('x', r'\400', 'x'), "\x00") + self.assertEqual(regex.sub('x', r'\777', 'x'), "\xFF") + + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\1', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\8', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\9', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\11', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\18', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\1a', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\90', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\99', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\118', 'x')) # r'\11' + '8' + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\11a', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\181', 'x')) # r'\18' + '1' + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\800', 'x')) # r'\80' + '0' + + # In Python 2.3 (etc), these loop endlessly in sre_parser.py. + self.assertEqual(regex.sub('(((((((((((x)))))))))))', r'\11', 'x'), + 'x') + self.assertEqual(regex.sub('((((((((((y))))))))))(.)', r'\118', 'xyz'), + 'xz8') + self.assertEqual(regex.sub('((((((((((y))))))))))(.)', r'\11a', 'xyz'), + 'xza') + + def test_qualified_re_sub(self): + self.assertEqual(regex.sub('a', 'b', 'aaaaa'), 'bbbbb') + self.assertEqual(regex.sub('a', 'b', 'aaaaa', 1), 'baaaa') + + def test_bug_114660(self): + self.assertEqual(regex.sub(r'(\S)\s+(\S)', r'\1 \2', 'hello there'), + 'hello there') + + def test_bug_462270(self): + # Test for empty sub() behaviour, see SF bug #462270 + self.assertEqual(regex.sub('(?V0)x*', '-', 'abxd'), '-a-b-d-') + self.assertEqual(regex.sub('(?V1)x*', '-', 'abxd'), '-a-b--d-') + self.assertEqual(regex.sub('x+', '-', 'abxd'), 'ab-d') + + def test_bug_14462(self): + # chr(255) is not a valid identifier in Python 2. + group_name = u'\xFF' + self.assertRaisesRegex(regex.error, self.BAD_GROUP_NAME, lambda: + regex.search(ur'(?P<' + group_name + '>a)', u'a')) + + def test_symbolic_refs(self): + self.assertRaisesRegex(regex.error, self.MISSING_GT, lambda: + regex.sub('(?P<a>x)', r'\g<a', 'xx')) + self.assertRaisesRegex(regex.error, self.MISSING_GROUP_NAME, lambda: + regex.sub('(?P<a>x)', r'\g<', 'xx')) + self.assertRaisesRegex(regex.error, self.MISSING_LT, lambda: + regex.sub('(?P<a>x)', r'\g', 'xx')) + self.assertRaisesRegex(regex.error, self.BAD_GROUP_NAME, lambda: + regex.sub('(?P<a>x)', r'\g<a a>', 'xx')) + self.assertRaisesRegex(regex.error, self.BAD_GROUP_NAME, lambda: + regex.sub('(?P<a>x)', r'\g<1a1>', 'xx')) + self.assertRaisesRegex(IndexError, self.UNKNOWN_GROUP_I, lambda: + regex.sub('(?P<a>x)', r'\g<ab>', 'xx')) + + # The new behaviour of unmatched but valid groups is to treat them like + # empty matches in the replacement template, like in Perl. + self.assertEqual(regex.sub('(?P<a>x)|(?P<b>y)', r'\g<b>', 'xx'), '') + self.assertEqual(regex.sub('(?P<a>x)|(?P<b>y)', r'\2', 'xx'), '') + + # The old behaviour was to raise it as an IndexError. + self.assertRaisesRegex(regex.error, self.BAD_GROUP_NAME, lambda: + regex.sub('(?P<a>x)', r'\g<-1>', 'xx')) + + def test_re_subn(self): + self.assertEqual(regex.subn("(?i)b+", "x", "bbbb BBBB"), ('x x', 2)) + self.assertEqual(regex.subn("b+", "x", "bbbb BBBB"), ('x BBBB', 1)) + self.assertEqual(regex.subn("b+", "x", "xyz"), ('xyz', 0)) + self.assertEqual(regex.subn("b*", "x", "xyz"), ('xxxyxzx', 4)) + self.assertEqual(regex.subn("b*", "x", "xyz", 2), ('xxxyz', 2)) + + def test_re_split(self): + self.assertEqual(regex.split(":", ":a:b::c"), ['', 'a', 'b', '', 'c']) + self.assertEqual(regex.split(":*", ":a:b::c"), ['', 'a', 'b', 'c']) + self.assertEqual(regex.split("(:*)", ":a:b::c"), ['', ':', 'a', ':', + 'b', '::', 'c']) + self.assertEqual(regex.split("(?::*)", ":a:b::c"), ['', 'a', 'b', 'c']) + self.assertEqual(regex.split("(:)*", ":a:b::c"), ['', ':', 'a', ':', + 'b', ':', 'c']) + self.assertEqual(regex.split("([b:]+)", ":a:b::c"), ['', ':', 'a', + ':b::', 'c']) + self.assertEqual(regex.split("(b)|(:+)", ":a:b::c"), ['', None, ':', + 'a', None, ':', '', 'b', None, '', None, '::', 'c']) + self.assertEqual(regex.split("(?:b)|(?::+)", ":a:b::c"), ['', 'a', '', + '', 'c']) + + self.assertEqual(regex.split("x", "xaxbxc"), ['', 'a', 'b', 'c']) + self.assertEqual([m for m in regex.splititer("x", "xaxbxc")], ['', 'a', + 'b', 'c']) + + self.assertEqual(regex.split("(?r)x", "xaxbxc"), ['c', 'b', 'a', '']) + self.assertEqual([m for m in regex.splititer("(?r)x", "xaxbxc")], ['c', + 'b', 'a', '']) + + self.assertEqual(regex.split("(x)|(y)", "xaxbxc"), ['', 'x', None, 'a', + 'x', None, 'b', 'x', None, 'c']) + self.assertEqual([m for m in regex.splititer("(x)|(y)", "xaxbxc")], + ['', 'x', None, 'a', 'x', None, 'b', 'x', None, 'c']) + + self.assertEqual(regex.split("(?r)(x)|(y)", "xaxbxc"), ['c', 'x', None, + 'b', 'x', None, 'a', 'x', None, '']) + self.assertEqual([m for m in regex.splititer("(?r)(x)|(y)", "xaxbxc")], + ['c', 'x', None, 'b', 'x', None, 'a', 'x', None, '']) + + self.assertEqual(regex.split(r"(?V1)\b", "a b c"), ['', 'a', ' ', 'b', + ' ', 'c', '']) + self.assertEqual(regex.split(r"(?V1)\m", "a b c"), ['', 'a ', 'b ', + 'c']) + self.assertEqual(regex.split(r"(?V1)\M", "a b c"), ['a', ' b', ' c', + '']) + + def test_qualified_re_split(self): + self.assertEqual(regex.split(":", ":a:b::c", 2), ['', 'a', 'b::c']) + self.assertEqual(regex.split(':', 'a:b:c:d', 2), ['a', 'b', 'c:d']) + self.assertEqual(regex.split("(:)", ":a:b::c", 2), ['', ':', 'a', ':', + 'b::c']) + self.assertEqual(regex.split("(:*)", ":a:b::c", 2), ['', ':', 'a', ':', + 'b::c']) + + def test_re_findall(self): + self.assertEqual(regex.findall(":+", "abc"), []) + self.assertEqual(regex.findall(":+", "a:b::c:::d"), [':', '::', ':::']) + self.assertEqual(regex.findall("(:+)", "a:b::c:::d"), [':', '::', + ':::']) + self.assertEqual(regex.findall("(:)(:*)", "a:b::c:::d"), [(':', ''), + (':', ':'), (':', '::')]) + + self.assertEqual(regex.findall(r"\((?P<test>.{0,5}?TEST)\)", + "(MY TEST)"), ["MY TEST"]) + self.assertEqual(regex.findall(r"\((?P<test>.{0,3}?TEST)\)", + "(MY TEST)"), ["MY TEST"]) + self.assertEqual(regex.findall(r"\((?P<test>.{0,3}?T)\)", "(MY T)"), + ["MY T"]) + + self.assertEqual(regex.findall(r"[^a]{2}[A-Z]", "\n S"), [' S']) + self.assertEqual(regex.findall(r"[^a]{2,3}[A-Z]", "\n S"), ['\n S']) + self.assertEqual(regex.findall(r"[^a]{2,3}[A-Z]", "\n S"), [' S']) + + self.assertEqual(regex.findall(r"X(Y[^Y]+?){1,2}( |Q)+DEF", + "XYABCYPPQ\nQ DEF"), [('YPPQ\n', ' ')]) + + self.assertEqual(regex.findall(r"(\nTest(\n+.+?){0,2}?)?\n+End", + "\nTest\nxyz\nxyz\nEnd"), [('\nTest\nxyz\nxyz', '\nxyz')]) + + def test_bug_117612(self): + self.assertEqual(regex.findall(r"(a|(b))", "aba"), [('a', ''), ('b', + 'b'), ('a', '')]) + + def test_re_match(self): + self.assertEqual(regex.match('a', 'a')[:], ('a',)) + self.assertEqual(regex.match('(a)', 'a')[:], ('a', 'a')) + self.assertEqual(regex.match(r'(a)', 'a')[0], 'a') + self.assertEqual(regex.match(r'(a)', 'a')[1], 'a') + self.assertEqual(regex.match(r'(a)', 'a').group(1, 1), ('a', 'a')) + + pat = regex.compile('((a)|(b))(c)?') + self.assertEqual(pat.match('a')[:], ('a', 'a', 'a', None, None)) + self.assertEqual(pat.match('b')[:], ('b', 'b', None, 'b', None)) + self.assertEqual(pat.match('ac')[:], ('ac', 'a', 'a', None, 'c')) + self.assertEqual(pat.match('bc')[:], ('bc', 'b', None, 'b', 'c')) + self.assertEqual(pat.match('bc')[:], ('bc', 'b', None, 'b', 'c')) + + # A single group. + m = regex.match('(a)', 'a') + self.assertEqual(m.group(), 'a') + self.assertEqual(m.group(0), 'a') + self.assertEqual(m.group(1), 'a') + self.assertEqual(m.group(1, 1), ('a', 'a')) + + pat = regex.compile('(?:(?P<a1>a)|(?P<b2>b))(?P<c3>c)?') + self.assertEqual(pat.match('a').group(1, 2, 3), ('a', None, None)) + self.assertEqual(pat.match('b').group('a1', 'b2', 'c3'), (None, 'b', + None)) + self.assertEqual(pat.match('ac').group(1, 'b2', 3), ('a', None, 'c')) + + def test_re_groupref_exists(self): + self.assertEqual(regex.match(r'^(\()?([^()]+)(?(1)\))$', '(a)')[:], + ('(a)', '(', 'a')) + self.assertEqual(regex.match(r'^(\()?([^()]+)(?(1)\))$', 'a')[:], ('a', + None, 'a')) + self.assertEqual(regex.match(r'^(\()?([^()]+)(?(1)\))$', 'a)'), None) + self.assertEqual(regex.match(r'^(\()?([^()]+)(?(1)\))$', '(a'), None) + self.assertEqual(regex.match('^(?:(a)|c)((?(1)b|d))$', 'ab')[:], ('ab', + 'a', 'b')) + self.assertEqual(regex.match('^(?:(a)|c)((?(1)b|d))$', 'cd')[:], ('cd', + None, 'd')) + self.assertEqual(regex.match('^(?:(a)|c)((?(1)|d))$', 'cd')[:], ('cd', + None, 'd')) + self.assertEqual(regex.match('^(?:(a)|c)((?(1)|d))$', 'a')[:], ('a', + 'a', '')) + + # Tests for bug #1177831: exercise groups other than the first group. + p = regex.compile('(?P<g1>a)(?P<g2>b)?((?(g2)c|d))') + self.assertEqual(p.match('abc')[:], ('abc', 'a', 'b', 'c')) + self.assertEqual(p.match('ad')[:], ('ad', 'a', None, 'd')) + self.assertEqual(p.match('abd'), None) + self.assertEqual(p.match('ac'), None) + + def test_re_groupref(self): + self.assertEqual(regex.match(r'^(\|)?([^()]+)\1$', '|a|')[:], ('|a|', + '|', 'a')) + self.assertEqual(regex.match(r'^(\|)?([^()]+)\1?$', 'a')[:], ('a', + None, 'a')) + self.assertEqual(regex.match(r'^(\|)?([^()]+)\1$', 'a|'), None) + self.assertEqual(regex.match(r'^(\|)?([^()]+)\1$', '|a'), None) + self.assertEqual(regex.match(r'^(?:(a)|c)(\1)$', 'aa')[:], ('aa', 'a', + 'a')) + self.assertEqual(regex.match(r'^(?:(a)|c)(\1)?$', 'c')[:], ('c', None, + None)) + + self.assertEqual(regex.findall("(?i)(.{1,40}?),(.{1,40}?)(?:;)+(.{1,80}).{1,40}?\\3(\ |;)+(.{1,80}?)\\1", + "TEST, BEST; LEST ; Lest 123 Test, Best"), [('TEST', ' BEST', + ' LEST', ' ', '123 ')]) + + def test_groupdict(self): + self.assertEqual(regex.match('(?P<first>first) (?P<second>second)', + 'first second').groupdict(), {'first': 'first', 'second': 'second'}) + + def test_expand(self): + self.assertEqual(regex.match("(?P<first>first) (?P<second>second)", + "first second").expand(r"\2 \1 \g<second> \g<first>"), + 'second first second first') + + def test_repeat_minmax(self): + self.assertEqual(regex.match(r"^(\w){1}$", "abc"), None) + self.assertEqual(regex.match(r"^(\w){1}?$", "abc"), None) + self.assertEqual(regex.match(r"^(\w){1,2}$", "abc"), None) + self.assertEqual(regex.match(r"^(\w){1,2}?$", "abc"), None) + + self.assertEqual(regex.match(r"^(\w){3}$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){1,3}$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){1,4}$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){3,4}?$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){3}?$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){1,3}?$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){1,4}?$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){3,4}?$", "abc")[1], 'c') + + self.assertEqual(regex.match("^x{1}$", "xxx"), None) + self.assertEqual(regex.match("^x{1}?$", "xxx"), None) + self.assertEqual(regex.match("^x{1,2}$", "xxx"), None) + self.assertEqual(regex.match("^x{1,2}?$", "xxx"), None) + + self.assertEqual(regex.match("^x{1}", "xxx")[0], 'x') + self.assertEqual(regex.match("^x{1}?", "xxx")[0], 'x') + self.assertEqual(regex.match("^x{0,1}", "xxx")[0], 'x') + self.assertEqual(regex.match("^x{0,1}?", "xxx")[0], '') + + self.assertEqual(bool(regex.match("^x{3}$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{1,3}$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{1,4}$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{3,4}?$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{3}?$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{1,3}?$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{1,4}?$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{3,4}?$", "xxx")), True) + + self.assertEqual(regex.match("^x{}$", "xxx"), None) + self.assertEqual(bool(regex.match("^x{}$", "x{}")), True) + + def test_getattr(self): + self.assertEqual(regex.compile("(?i)(a)(b)").pattern, '(?i)(a)(b)') + self.assertEqual(regex.compile("(?i)(a)(b)").flags, regex.A | regex.I | + regex.DEFAULT_VERSION) + self.assertEqual(regex.compile(u"(?i)(a)(b)").flags, regex.I | regex.U + | regex.DEFAULT_VERSION) + self.assertEqual(regex.compile("(?i)(a)(b)").groups, 2) + self.assertEqual(regex.compile("(?i)(a)(b)").groupindex, {}) + + self.assertEqual(regex.compile("(?i)(?P<first>a)(?P<other>b)").groupindex, + {'first': 1, 'other': 2}) + + self.assertEqual(regex.match("(a)", "a").pos, 0) + self.assertEqual(regex.match("(a)", "a").endpos, 1) + + self.assertEqual(regex.search("b(c)", "abcdef").pos, 0) + self.assertEqual(regex.search("b(c)", "abcdef").endpos, 6) + self.assertEqual(regex.search("b(c)", "abcdef").span(), (1, 3)) + self.assertEqual(regex.search("b(c)", "abcdef").span(1), (2, 3)) + + self.assertEqual(regex.match("(a)", "a").string, 'a') + self.assertEqual(regex.match("(a)", "a").regs, ((0, 1), (0, 1))) + self.assertEqual(repr(type(regex.match("(a)", "a").re)), + self.PATTERN_CLASS) + + # Issue 14260. + p = regex.compile(r'abc(?P<n>def)') + p.groupindex["n"] = 0 + self.assertEqual(p.groupindex["n"], 1) + + def test_special_escapes(self): + self.assertEqual(regex.search(r"\b(b.)\b", "abcd abc bcd bx")[1], 'bx') + self.assertEqual(regex.search(r"\B(b.)\B", "abc bcd bc abxd")[1], 'bx') + self.assertEqual(regex.search(r"\b(b.)\b", "abcd abc bcd bx", + regex.LOCALE)[1], 'bx') + self.assertEqual(regex.search(r"\B(b.)\B", "abc bcd bc abxd", + regex.LOCALE)[1], 'bx') + self.assertEqual(regex.search(ur"\b(b.)\b", u"abcd abc bcd bx", + regex.UNICODE)[1], u'bx') + self.assertEqual(regex.search(ur"\B(b.)\B", u"abc bcd bc abxd", + regex.UNICODE)[1], u'bx') + + self.assertEqual(regex.search(r"^abc$", "\nabc\n", regex.M)[0], 'abc') + self.assertEqual(regex.search(r"^\Aabc\Z$", "abc", regex.M)[0], 'abc') + self.assertEqual(regex.search(r"^\Aabc\Z$", "\nabc\n", regex.M), None) + + self.assertEqual(regex.search(ur"\b(b.)\b", u"abcd abc bcd bx")[1], + u'bx') + self.assertEqual(regex.search(ur"\B(b.)\B", u"abc bcd bc abxd")[1], + u'bx') + self.assertEqual(regex.search(ur"^abc$", u"\nabc\n", regex.M)[0], + u'abc') + self.assertEqual(regex.search(ur"^\Aabc\Z$", u"abc", regex.M)[0], + u'abc') + self.assertEqual(regex.search(ur"^\Aabc\Z$", u"\nabc\n", regex.M), + None) + + self.assertEqual(regex.search(r"\d\D\w\W\s\S", "1aa! a")[0], '1aa! a') + self.assertEqual(regex.search(r"\d\D\w\W\s\S", "1aa! a", + regex.LOCALE)[0], '1aa! a') + self.assertEqual(regex.search(ur"\d\D\w\W\s\S", u"1aa! a", + regex.UNICODE)[0], u'1aa! a') + + def test_bigcharset(self): + self.assertEqual(regex.match(ur"(?u)([\u2222\u2223])", u"\u2222")[1], + u'\u2222') + self.assertEqual(regex.match(ur"(?u)([\u2222\u2223])", u"\u2222", + regex.UNICODE)[1], u'\u2222') + self.assertEqual(u"".join(regex.findall(u".", + u"e\xe8\xe9\xea\xeb\u0113\u011b\u0117", flags=regex.UNICODE)), + u'e\xe8\xe9\xea\xeb\u0113\u011b\u0117') + self.assertEqual(u"".join(regex.findall(ur"[e\xe8\xe9\xea\xeb\u0113\u011b\u0117]", + u"e\xe8\xe9\xea\xeb\u0113\u011b\u0117", flags=regex.UNICODE)), + u'e\xe8\xe9\xea\xeb\u0113\u011b\u0117') + self.assertEqual(u"".join(regex.findall(ur"e|\xe8|\xe9|\xea|\xeb|\u0113|\u011b|\u0117", + u"e\xe8\xe9\xea\xeb\u0113\u011b\u0117", flags=regex.UNICODE)), + u'e\xe8\xe9\xea\xeb\u0113\u011b\u0117') + + def test_anyall(self): + self.assertEqual(regex.match("a.b", "a\nb", regex.DOTALL)[0], "a\nb") + self.assertEqual(regex.match("a.*b", "a\n\nb", regex.DOTALL)[0], + "a\n\nb") + + def test_non_consuming(self): + self.assertEqual(regex.match(r"(a(?=\s[^a]))", "a b")[1], 'a') + self.assertEqual(regex.match(r"(a(?=\s[^a]*))", "a b")[1], 'a') + self.assertEqual(regex.match(r"(a(?=\s[abc]))", "a b")[1], 'a') + self.assertEqual(regex.match(r"(a(?=\s[abc]*))", "a bc")[1], 'a') + self.assertEqual(regex.match(r"(a)(?=\s\1)", "a a")[1], 'a') + self.assertEqual(regex.match(r"(a)(?=\s\1*)", "a aa")[1], 'a') + self.assertEqual(regex.match(r"(a)(?=\s(abc|a))", "a a")[1], 'a') + + self.assertEqual(regex.match(r"(a(?!\s[^a]))", "a a")[1], 'a') + self.assertEqual(regex.match(r"(a(?!\s[abc]))", "a d")[1], 'a') + self.assertEqual(regex.match(r"(a)(?!\s\1)", "a b")[1], 'a') + self.assertEqual(regex.match(r"(a)(?!\s(abc|a))", "a b")[1], 'a') + + def test_ignore_case(self): + self.assertEqual(regex.match("abc", "ABC", regex.I)[0], 'ABC') + self.assertEqual(regex.match(u"abc", u"ABC", regex.I)[0], u'ABC') + + self.assertEqual(regex.match(r"(a\s[^a]*)", "a bb", regex.I)[1], + 'a bb') + self.assertEqual(regex.match(r"(a\s[abc])", "a b", regex.I)[1], 'a b') + self.assertEqual(regex.match(r"(a\s[abc]*)", "a bb", regex.I)[1], + 'a bb') + self.assertEqual(regex.match(r"((a)\s\2)", "a a", regex.I)[1], 'a a') + self.assertEqual(regex.match(r"((a)\s\2*)", "a aa", regex.I)[1], + 'a aa') + self.assertEqual(regex.match(r"((a)\s(abc|a))", "a a", regex.I)[1], + 'a a') + self.assertEqual(regex.match(r"((a)\s(abc|a)*)", "a aa", regex.I)[1], + 'a aa') + + # Issue 3511. + self.assertEqual(regex.match(r"[Z-a]", "_").span(), (0, 1)) + self.assertEqual(regex.match(r"(?i)[Z-a]", "_").span(), (0, 1)) + + self.assertEqual(bool(regex.match(ur"(?iu)nao", u"nAo")), True) + self.assertEqual(bool(regex.match(ur"(?iu)n\xE3o", u"n\xC3o")), True) + self.assertEqual(bool(regex.match(ur"(?iu)n\xE3o", u"N\xC3O")), True) + self.assertEqual(bool(regex.match(ur"(?iu)s", u"\u017F")), True) + + def test_case_folding(self): + self.assertEqual(regex.search(ur"(?fiu)ss", u"SS").span(), (0, 2)) + self.assertEqual(regex.search(ur"(?fiu)SS", u"ss").span(), (0, 2)) + self.assertEqual(regex.search(ur"(?fiu)SS", + u"\N{LATIN SMALL LETTER SHARP S}").span(), (0, 1)) + self.assertEqual(regex.search(ur"(?fi)\N{LATIN SMALL LETTER SHARP S}", + u"SS").span(), (0, 2)) + + self.assertEqual(regex.search(ur"(?fiu)\N{LATIN SMALL LIGATURE ST}", + u"ST").span(), (0, 2)) + self.assertEqual(regex.search(ur"(?fiu)ST", + u"\N{LATIN SMALL LIGATURE ST}").span(), (0, 1)) + self.assertEqual(regex.search(ur"(?fiu)ST", + u"\N{LATIN SMALL LIGATURE LONG S T}").span(), (0, 1)) + + self.assertEqual(regex.search(ur"(?fiu)SST", + u"\N{LATIN SMALL LETTER SHARP S}t").span(), (0, 2)) + self.assertEqual(regex.search(ur"(?fiu)SST", + u"s\N{LATIN SMALL LIGATURE LONG S T}").span(), (0, 2)) + self.assertEqual(regex.search(ur"(?fiu)SST", + u"s\N{LATIN SMALL LIGATURE ST}").span(), (0, 2)) + self.assertEqual(regex.search(ur"(?fiu)\N{LATIN SMALL LIGATURE ST}", + u"SST").span(), (1, 3)) + self.assertEqual(regex.search(ur"(?fiu)SST", + u"s\N{LATIN SMALL LIGATURE ST}").span(), (0, 2)) + + self.assertEqual(regex.search(ur"(?fiu)FFI", + u"\N{LATIN SMALL LIGATURE FFI}").span(), (0, 1)) + self.assertEqual(regex.search(ur"(?fiu)FFI", + u"\N{LATIN SMALL LIGATURE FF}i").span(), (0, 2)) + self.assertEqual(regex.search(ur"(?fiu)FFI", + u"f\N{LATIN SMALL LIGATURE FI}").span(), (0, 2)) + self.assertEqual(regex.search(ur"(?fiu)\N{LATIN SMALL LIGATURE FFI}", + u"FFI").span(), (0, 3)) + self.assertEqual(regex.search(ur"(?fiu)\N{LATIN SMALL LIGATURE FF}i", + u"FFI").span(), (0, 3)) + self.assertEqual(regex.search(ur"(?fiu)f\N{LATIN SMALL LIGATURE FI}", + u"FFI").span(), (0, 3)) + + sigma = u"\u03A3\u03C3\u03C2" + for ch1 in sigma: + for ch2 in sigma: + if not regex.match(ur"(?fiu)" + ch1, ch2): + self.fail() + + self.assertEqual(bool(regex.search(ur"(?iuV1)ff", u"\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)ff", u"\uFB01\uFB00")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)fi", u"\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)fi", u"\uFB01\uFB00")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)fffi", u"\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)f\uFB03", + u"\uFB00\uFB01")), True) + self.assertEqual(bool(regex.search(ur"(?iuV1)ff", u"\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)fi", u"\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)fffi", u"\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)f\uFB03", + u"\uFB00\uFB01")), True) + self.assertEqual(bool(regex.search(ur"(?iuV1)f\uFB01", u"\uFB00i")), + True) + self.assertEqual(bool(regex.search(ur"(?iuV1)f\uFB01", u"\uFB00i")), + True) + + self.assertEqual(regex.findall(ur"(?iuV0)\m(?:word){e<=3}\M(?<!\m(?:word){e<=1}\M)", + u"word word2 word word3 word word234 word23 word"), [u"word234", + u"word23"]) + self.assertEqual(regex.findall(ur"(?iuV1)\m(?:word){e<=3}\M(?<!\m(?:word){e<=1}\M)", + u"word word2 word word3 word word234 word23 word"), [u"word234", + u"word23"]) + + self.assertEqual(regex.search(ur"(?fi)a\N{LATIN SMALL LIGATURE FFI}ne", + u" affine ").span(), (2, 8)) + self.assertEqual(regex.search(ur"(?fi)a(?:\N{LATIN SMALL LIGATURE FFI}|x)ne", + u" affine ").span(), (2, 8)) + self.assertEqual(regex.search(ur"(?fi)a(?:\N{LATIN SMALL LIGATURE FFI}|xy)ne", + u" affine ").span(), (2, 8)) + self.assertEqual(regex.search(ur"(?fi)a\L<options>ne", u"affine", + options=[u"\N{LATIN SMALL LIGATURE FFI}"]).span(), (0, 6)) + self.assertEqual(regex.search(ur"(?fi)a\L<options>ne", + u"a\N{LATIN SMALL LIGATURE FFI}ne", options=[u"ffi"]).span(), (0, 4)) + + def test_category(self): + self.assertEqual(regex.match(r"(\s)", " ")[1], ' ') + + def test_not_literal(self): + self.assertEqual(regex.search(r"\s([^a])", " b")[1], 'b') + self.assertEqual(regex.search(r"\s([^a]*)", " bb")[1], 'bb') + + def test_search_coverage(self): + self.assertEqual(regex.search(r"\s(b)", " b")[1], 'b') + self.assertEqual(regex.search(r"a\s", "a ")[0], 'a ') + + def test_re_escape(self): + p = "" + self.assertEqual(regex.escape(p), p) + for i in range(0, 256): + p += chr(i) + self.assertEqual(bool(regex.match(regex.escape(chr(i)), chr(i))), + True) + self.assertEqual(regex.match(regex.escape(chr(i)), chr(i)).span(), + (0, 1)) + + pat = regex.compile(regex.escape(p)) + self.assertEqual(pat.match(p).span(), (0, 256)) + + def test_constants(self): + if regex.I != regex.IGNORECASE: + self.fail() + if regex.L != regex.LOCALE: + self.fail() + if regex.M != regex.MULTILINE: + self.fail() + if regex.S != regex.DOTALL: + self.fail() + if regex.X != regex.VERBOSE: + self.fail() + + def test_flags(self): + for flag in [regex.I, regex.M, regex.X, regex.S, regex.L]: + self.assertEqual(repr(type(regex.compile('^pattern$', flag))), + self.PATTERN_CLASS) + + def test_sre_character_literals(self): + for i in [0, 8, 16, 32, 64, 127, 128, 255]: + self.assertEqual(bool(regex.match(r"\%03o" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"\%03o0" % i, chr(i) + "0")), + True) + self.assertEqual(bool(regex.match(r"\%03o8" % i, chr(i) + "8")), + True) + self.assertEqual(bool(regex.match(r"\x%02x" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"\x%02x0" % i, chr(i) + "0")), + True) + self.assertEqual(bool(regex.match(r"\x%02xz" % i, chr(i) + "z")), + True) + + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.match(r"\911", "")) + + def test_sre_character_class_literals(self): + for i in [0, 8, 16, 32, 64, 127, 128, 255]: + self.assertEqual(bool(regex.match(r"[\%03o]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\%03o0]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\%03o8]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\x%02x]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\x%02x0]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\x%02xz]" % i, chr(i))), True) + + self.assertRaisesRegex(regex.error, self.BAD_OCTAL_ESCAPE, lambda: + regex.match(r"[\911]", "")) + + def test_bug_113254(self): + self.assertEqual(regex.match(r'(a)|(b)', 'b').start(1), -1) + self.assertEqual(regex.match(r'(a)|(b)', 'b').end(1), -1) + self.assertEqual(regex.match(r'(a)|(b)', 'b').span(1), (-1, -1)) + + def test_bug_527371(self): + # Bug described in patches 527371/672491. + self.assertEqual(regex.match(r'(a)?a','a').lastindex, None) + self.assertEqual(regex.match(r'(a)(b)?b','ab').lastindex, 1) + self.assertEqual(regex.match(r'(?P<a>a)(?P<b>b)?b','ab').lastgroup, + 'a') + self.assertEqual(regex.match("(?P<a>a(b))", "ab").lastgroup, 'a') + self.assertEqual(regex.match("((a))", "a").lastindex, 1) + + def test_bug_545855(self): + # Bug 545855 -- This pattern failed to cause a compile error as it + # should, instead provoking a TypeError. + self.assertRaisesRegex(regex.error, self.BAD_SET, lambda: + regex.compile('foo[a-')) + + def test_bug_418626(self): + # Bugs 418626 at al. -- Testing Greg Chapman's addition of op code + # SRE_OP_MIN_REPEAT_ONE for eliminating recursion on simple uses of + # pattern '*?' on a long string. + self.assertEqual(regex.match('.*?c', 10000 * 'ab' + 'cd').end(0), + 20001) + self.assertEqual(regex.match('.*?cd', 5000 * 'ab' + 'c' + 5000 * 'ab' + + 'cde').end(0), 20003) + self.assertEqual(regex.match('.*?cd', 20000 * 'abc' + 'de').end(0), + 60001) + # Non-simple '*?' still used to hit the recursion limit, before the + # non-recursive scheme was implemented. + self.assertEqual(regex.search('(a|b)*?c', 10000 * 'ab' + 'cd').end(0), + 20001) + + def test_bug_612074(self): + pat = u"[" + regex.escape(u"\u2039") + u"]" + self.assertEqual(regex.compile(pat) and 1, 1) + + def test_stack_overflow(self): + # Nasty cases that used to overflow the straightforward recursive + # implementation of repeated groups. + self.assertEqual(regex.match('(x)*', 50000 * 'x')[1], 'x') + self.assertEqual(regex.match('(x)*y', 50000 * 'x' + 'y')[1], 'x') + self.assertEqual(regex.match('(x)*?y', 50000 * 'x' + 'y')[1], 'x') + + def test_scanner(self): + def s_ident(scanner, token): return token + def s_operator(scanner, token): return "op%s" % token + def s_float(scanner, token): return float(token) + def s_int(scanner, token): return int(token) + + scanner = regex.Scanner([(r"[a-zA-Z_]\w*", s_ident), (r"\d+\.\d*", + s_float), (r"\d+", s_int), (r"=|\+|-|\*|/", s_operator), (r"\s+", + None), ]) + + self.assertEqual(repr(type(scanner.scanner.scanner("").pattern)), + self.PATTERN_CLASS) + + self.assertEqual(scanner.scan("sum = 3*foo + 312.50 + bar"), (['sum', + 'op=', 3, 'op*', 'foo', 'op+', 312.5, 'op+', 'bar'], '')) + + def test_bug_448951(self): + # Bug 448951 (similar to 429357, but with single char match). + # (Also test greedy matches.) + for op in '', '?', '*': + self.assertEqual(regex.match(r'((.%s):)?z' % op, 'z')[:], ('z', + None, None)) + self.assertEqual(regex.match(r'((.%s):)?z' % op, 'a:z')[:], ('a:z', + 'a:', 'a')) + + def test_bug_725106(self): + # Capturing groups in alternatives in repeats. + self.assertEqual(regex.match('^((a)|b)*', 'abc')[:], ('ab', 'b', 'a')) + self.assertEqual(regex.match('^(([ab])|c)*', 'abc')[:], ('abc', 'c', + 'b')) + self.assertEqual(regex.match('^((d)|[ab])*', 'abc')[:], ('ab', 'b', + None)) + self.assertEqual(regex.match('^((a)c|[ab])*', 'abc')[:], ('ab', 'b', + None)) + self.assertEqual(regex.match('^((a)|b)*?c', 'abc')[:], ('abc', 'b', + 'a')) + self.assertEqual(regex.match('^(([ab])|c)*?d', 'abcd')[:], ('abcd', + 'c', 'b')) + self.assertEqual(regex.match('^((d)|[ab])*?c', 'abc')[:], ('abc', 'b', + None)) + self.assertEqual(regex.match('^((a)c|[ab])*?c', 'abc')[:], ('abc', 'b', + None)) + + def test_bug_725149(self): + # Mark_stack_base restoring before restoring marks. + self.assertEqual(regex.match('(a)(?:(?=(b)*)c)*', 'abb')[:], ('a', 'a', + None)) + self.assertEqual(regex.match('(a)((?!(b)*))*', 'abb')[:], ('a', 'a', + None, None)) + + def test_bug_764548(self): + # Bug 764548, regex.compile() barfs on str/unicode subclasses. + class my_unicode(str): pass + pat = regex.compile(my_unicode("abc")) + self.assertEqual(pat.match("xyz"), None) + + def test_finditer(self): + it = regex.finditer(r":+", "a:b::c:::d") + self.assertEqual([item[0] for item in it], [':', '::', ':::']) + + def test_bug_926075(self): + if regex.compile('bug_926075') is regex.compile(u'bug_926075'): + self.fail() + + def test_bug_931848(self): + pattern = u"[\u002E\u3002\uFF0E\uFF61]" + self.assertEqual(regex.compile(pattern).split("a.b.c"), ['a', 'b', + 'c']) + + def test_bug_581080(self): + it = regex.finditer(r"\s", "a b") + self.assertEqual(it.next().span(), (1, 2)) + self.assertRaises(StopIteration, lambda: it.next()) + + scanner = regex.compile(r"\s").scanner("a b") + self.assertEqual(scanner.search().span(), (1, 2)) + self.assertEqual(scanner.search(), None) + + def test_bug_817234(self): + it = regex.finditer(r".*", "asdf") + self.assertEqual(it.next().span(), (0, 4)) + self.assertEqual(it.next().span(), (4, 4)) + self.assertRaises(StopIteration, lambda: it.next()) + + def test_empty_array(self): + # SF buf 1647541. + import array + for typecode in 'cbBuhHiIlLfd': + a = array.array(typecode) + self.assertEqual(regex.compile("bla").match(a), None) + self.assertEqual(regex.compile("").match(a)[1 : ], ()) + + def test_inline_flags(self): + # Bug #1700. + upper_char = unichr(0x1ea0) # Latin Capital Letter A with Dot Below + lower_char = unichr(0x1ea1) # Latin Small Letter A with Dot Below + + p = regex.compile(upper_char, regex.I | regex.U) + self.assertEqual(bool(p.match(lower_char)), True) + + p = regex.compile(lower_char, regex.I | regex.U) + self.assertEqual(bool(p.match(upper_char)), True) + + p = regex.compile('(?i)' + upper_char, regex.U) + self.assertEqual(bool(p.match(lower_char)), True) + + p = regex.compile('(?i)' + lower_char, regex.U) + self.assertEqual(bool(p.match(upper_char)), True) + + p = regex.compile('(?iu)' + upper_char) + self.assertEqual(bool(p.match(lower_char)), True) + + p = regex.compile('(?iu)' + lower_char) + self.assertEqual(bool(p.match(upper_char)), True) + + self.assertEqual(bool(regex.match(r"(?i)a", "A")), True) + self.assertEqual(bool(regex.match(r"a(?i)", "A")), True) + self.assertEqual(bool(regex.match(r"(?iV1)a", "A")), True) + self.assertEqual(regex.match(r"a(?iV1)", "A"), None) + + def test_dollar_matches_twice(self): + # $ matches the end of string, and just before the terminating \n. + pattern = regex.compile('$') + self.assertEqual(pattern.sub('#', 'a\nb\n'), 'a\nb#\n#') + self.assertEqual(pattern.sub('#', 'a\nb\nc'), 'a\nb\nc#') + self.assertEqual(pattern.sub('#', '\n'), '#\n#') + + pattern = regex.compile('$', regex.MULTILINE) + self.assertEqual(pattern.sub('#', 'a\nb\n' ), 'a#\nb#\n#') + self.assertEqual(pattern.sub('#', 'a\nb\nc'), 'a#\nb#\nc#') + self.assertEqual(pattern.sub('#', '\n'), '#\n#') + + def test_ascii_and_unicode_flag(self): + # Unicode patterns. + for flags in (0, regex.UNICODE): + pat = regex.compile(u'\xc0', flags | regex.IGNORECASE) + self.assertEqual(bool(pat.match(u'\xe0')), True) + pat = regex.compile(u'\w', flags) + self.assertEqual(bool(pat.match(u'\xe0')), True) + + pat = regex.compile(u'\xc0', regex.ASCII | regex.IGNORECASE) + self.assertEqual(pat.match(u'\xe0'), None) + pat = regex.compile(u'(?a)\xc0', regex.IGNORECASE) + self.assertEqual(pat.match(u'\xe0'), None) + pat = regex.compile(u'\w', regex.ASCII) + self.assertEqual(pat.match(u'\xe0'), None) + pat = regex.compile(u'(?a)\w') + self.assertEqual(pat.match(u'\xe0'), None) + + # String patterns. + for flags in (0, regex.ASCII): + pat = regex.compile('\xc0', flags | regex.IGNORECASE) + self.assertEqual(pat.match('\xe0'), None) + pat = regex.compile('\w') + self.assertEqual(pat.match('\xe0'), None) + + self.assertRaisesRegex(ValueError, self.MIXED_FLAGS, lambda: + regex.compile('(?au)\w')) + + def test_subscripting_match(self): + m = regex.match(r'(?<a>\w)', 'xy') + if not m: + self.fail("Failed: expected match but returned None") + elif not m or m[0] != m.group(0) or m[1] != m.group(1): + self.fail("Failed") + if not m: + self.fail("Failed: expected match but returned None") + elif m[:] != ('x', 'x'): + self.fail("Failed: expected \"('x', 'x')\" but got %s instead" % + repr(m[:])) + + def test_new_named_groups(self): + m0 = regex.match(r'(?P<a>\w)', 'x') + m1 = regex.match(r'(?<a>\w)', 'x') + if not (m0 and m1 and m0[:] == m1[:]): + self.fail("Failed") + + def test_properties(self): + self.assertEqual(regex.match('(?i)\xC0', '\xE0'), None) + self.assertEqual(regex.match(r'(?i)\xC0', '\xE0'), None) + self.assertEqual(regex.match(r'\w', '\xE0'), None) + self.assertEqual(bool(regex.match(ur'(?u)\w', u'\xE0')), True) + + # Dropped the following test. It's not possible to determine what the + # correct result should be in the general case. +# self.assertEqual(bool(regex.match(r'(?L)\w', '\xE0')), +# '\xE0'.isalnum()) + + self.assertEqual(bool(regex.match(r'(?L)\d', '0')), True) + self.assertEqual(bool(regex.match(r'(?L)\s', ' ')), True) + self.assertEqual(bool(regex.match(r'(?L)\w', 'a')), True) + self.assertEqual(regex.match(r'(?L)\d', '?'), None) + self.assertEqual(regex.match(r'(?L)\s', '?'), None) + self.assertEqual(regex.match(r'(?L)\w', '?'), None) + + self.assertEqual(regex.match(r'(?L)\D', '0'), None) + self.assertEqual(regex.match(r'(?L)\S', ' '), None) + self.assertEqual(regex.match(r'(?L)\W', 'a'), None) + self.assertEqual(bool(regex.match(r'(?L)\D', '?')), True) + self.assertEqual(bool(regex.match(r'(?L)\S', '?')), True) + self.assertEqual(bool(regex.match(r'(?L)\W', '?')), True) + + self.assertEqual(bool(regex.match(ur'(?u)\p{Cyrillic}', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)(?iu)\p{Cyrillic}', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{IsCyrillic}', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{Script=Cyrillic}', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{InCyrillic}', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{Block=Cyrillic}', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:Cyrillic:]]', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:IsCyrillic:]]', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:Script=Cyrillic:]]', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:InCyrillic:]]', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:Block=Cyrillic:]]', + u'\N{CYRILLIC CAPITAL LETTER A}')), True) + + self.assertEqual(bool(regex.match(ur'(?u)\P{Cyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\P{IsCyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\P{Script=Cyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\P{InCyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\P{Block=Cyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{^Cyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{^IsCyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{^Script=Cyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{^InCyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{^Block=Cyrillic}', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:^Cyrillic:]]', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:^IsCyrillic:]]', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:^Script=Cyrillic:]]', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:^InCyrillic:]]', + u'\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(ur'(?u)[[:^Block=Cyrillic:]]', + u'\N{LATIN CAPITAL LETTER A}')), True) + + self.assertEqual(bool(regex.match(ur'(?u)\d', u'0')), True) + self.assertEqual(bool(regex.match(ur'(?u)\s', u' ')), True) + self.assertEqual(bool(regex.match(ur'(?u)\w', u'A')), True) + self.assertEqual(regex.match(ur"(?u)\d", u"?"), None) + self.assertEqual(regex.match(ur"(?u)\s", u"?"), None) + self.assertEqual(regex.match(ur"(?u)\w", u"?"), None) + self.assertEqual(regex.match(ur"(?u)\D", u"0"), None) + self.assertEqual(regex.match(ur"(?u)\S", u" "), None) + self.assertEqual(regex.match(ur"(?u)\W", u"A"), None) + self.assertEqual(bool(regex.match(ur'(?u)\D', u'?')), True) + self.assertEqual(bool(regex.match(ur'(?u)\S', u'?')), True) + self.assertEqual(bool(regex.match(ur'(?u)\W', u'?')), True) + + self.assertEqual(bool(regex.match(ur'(?u)\p{L}', u'A')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{L}', u'a')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{Lu}', u'A')), True) + self.assertEqual(bool(regex.match(ur'(?u)\p{Ll}', u'a')), True) + + self.assertEqual(bool(regex.match(ur'(?u)(?i)a', u'a')), True) + self.assertEqual(bool(regex.match(ur'(?u)(?i)a', u'A')), True) + + self.assertEqual(bool(regex.match(ur'(?u)\w', u'0')), True) + self.assertEqual(bool(regex.match(ur'(?u)\w', u'a')), True) + self.assertEqual(bool(regex.match(ur'(?u)\w', u'_')), True) + + self.assertEqual(regex.match(ur"(?u)\X", u"\xE0").span(), (0, 1)) + self.assertEqual(regex.match(ur"(?u)\X", u"a\u0300").span(), (0, 2)) + self.assertEqual(regex.findall(ur"(?u)\X", + u"a\xE0a\u0300e\xE9e\u0301"), [u'a', u'\xe0', u'a\u0300', u'e', + u'\xe9', u'e\u0301']) + self.assertEqual(regex.findall(ur"(?u)\X{3}", + u"a\xE0a\u0300e\xE9e\u0301"), [u'a\xe0a\u0300', u'e\xe9e\u0301']) + self.assertEqual(regex.findall(ur"(?u)\X", u"\r\r\n\u0301A\u0301"), + [u'\r', u'\r\n', u'\u0301', u'A\u0301']) + + self.assertEqual(bool(regex.match(ur'(?u)\p{Ll}', u'a')), True) + + chars_u = u"-09AZaz_\u0393\u03b3" + chars_b = "-09AZaz_" + word_set = set("Ll Lm Lo Lt Lu Mc Me Mn Nd Nl No Pc".split()) + + tests = [ + (ur"(?u)\w", chars_u, u"09AZaz_\u0393\u03b3"), + (ur"(?u)[[:word:]]", chars_u, u"09AZaz_\u0393\u03b3"), + (ur"(?u)\W", chars_u, u"-"), + (ur"(?u)[[:^word:]]", chars_u, u"-"), + (ur"(?u)\d", chars_u, u"09"), + (ur"(?u)[[:digit:]]", chars_u, u"09"), + (ur"(?u)\D", chars_u, u"-AZaz_\u0393\u03b3"), + (ur"(?u)[[:^digit:]]", chars_u, u"-AZaz_\u0393\u03b3"), + (ur"(?u)[[:alpha:]]", chars_u, u"AZaz\u0393\u03b3"), + (ur"(?u)[[:^alpha:]]", chars_u, u"-09_"), + (ur"(?u)[[:alnum:]]", chars_u, u"09AZaz\u0393\u03b3"), + (ur"(?u)[[:^alnum:]]", chars_u, u"-_"), + (ur"(?u)[[:xdigit:]]", chars_u, u"09Aa"), + (ur"(?u)[[:^xdigit:]]", chars_u, u"-Zz_\u0393\u03b3"), + (ur"(?u)\p{InBasicLatin}", u"a\xE1", u"a"), + (ur"(?u)\P{InBasicLatin}", u"a\xE1", u"\xE1"), + (ur"(?iu)\p{InBasicLatin}", u"a\xE1", u"a"), + (ur"(?iu)\P{InBasicLatin}", u"a\xE1", u"\xE1"), + + (r"(?L)\w", chars_b, "09AZaz_"), + (r"(?L)[[:word:]]", chars_b, "09AZaz_"), + (r"(?L)\W", chars_b, "-"), + (r"(?L)[[:^word:]]", chars_b, "-"), + (r"(?L)\d", chars_b, "09"), + (r"(?L)[[:digit:]]", chars_b, "09"), + (r"(?L)\D", chars_b, "-AZaz_"), + (r"(?L)[[:^digit:]]", chars_b, "-AZaz_"), + (r"(?L)[[:alpha:]]", chars_b, "AZaz"), + (r"(?L)[[:^alpha:]]", chars_b, "-09_"), + (r"(?L)[[:alnum:]]", chars_b, "09AZaz"), + (r"(?L)[[:^alnum:]]", chars_b, "-_"), + (r"(?L)[[:xdigit:]]", chars_b, "09Aa"), + (r"(?L)[[:^xdigit:]]", chars_b, "-Zz_"), + + (r"\w", chars_b, "09AZaz_"), + (r"[[:word:]]", chars_b, "09AZaz_"), + (r"\W", chars_b, "-"), + (r"[[:^word:]]", chars_b, "-"), + (r"\d", chars_b, "09"), + (r"[[:digit:]]", chars_b, "09"), + (r"\D", chars_b, "-AZaz_"), + (r"[[:^digit:]]", chars_b, "-AZaz_"), + (r"[[:alpha:]]", chars_b, "AZaz"), + (r"[[:^alpha:]]", chars_b, "-09_"), + (r"[[:alnum:]]", chars_b, "09AZaz"), + (r"[[:^alnum:]]", chars_b, "-_"), + (r"[[:xdigit:]]", chars_b, "09Aa"), + (r"[[:^xdigit:]]", chars_b, "-Zz_"), + ] + for pattern, chars, expected in tests: + try: + if chars[ : 0].join(regex.findall(pattern, chars)) != expected: + self.fail("Failed: %s" % pattern) + except Exception, e: + self.fail("Failed: %s raised %s" % (pattern, repr(e))) + + self.assertEqual(bool(regex.match(ur"(?u)\p{NumericValue=0}", u"0")), + True) + self.assertEqual(bool(regex.match(ur"(?u)\p{NumericValue=1/2}", + u"\N{VULGAR FRACTION ONE HALF}")), True) + self.assertEqual(bool(regex.match(ur"(?u)\p{NumericValue=0.5}", + u"\N{VULGAR FRACTION ONE HALF}")), True) + + def test_word_class(self): + self.assertEqual(regex.findall(ur"(?u)\w+", + u" \u0939\u093f\u0928\u094d\u0926\u0940,"), + [u'\u0939\u093f\u0928\u094d\u0926\u0940']) + self.assertEqual(regex.findall(ur"(?u)\W+", + u" \u0939\u093f\u0928\u094d\u0926\u0940,"), [u' ', u',']) + self.assertEqual(regex.split(ur"(?uV1)\b", + u" \u0939\u093f\u0928\u094d\u0926\u0940,"), [u' ', + u'\u0939\u093f\u0928\u094d\u0926\u0940', u',']) + self.assertEqual(regex.split(ur"(?uV1)\B", + u" \u0939\u093f\u0928\u094d\u0926\u0940,"), [u'', u' \u0939', + u'\u093f', u'\u0928', u'\u094d', u'\u0926', u'\u0940,', u'']) + + def test_search_anchor(self): + self.assertEqual(regex.findall(r"\G\w{2}", "abcd ef"), ['ab', 'cd']) + + def test_search_reverse(self): + self.assertEqual(regex.findall(r"(?r).", "abc"), ['c', 'b', 'a']) + self.assertEqual(regex.findall(r"(?r).", "abc", overlapped=True), ['c', + 'b', 'a']) + self.assertEqual(regex.findall(r"(?r)..", "abcde"), ['de', 'bc']) + self.assertEqual(regex.findall(r"(?r)..", "abcde", overlapped=True), + ['de', 'cd', 'bc', 'ab']) + self.assertEqual(regex.findall(r"(?r)(.)(-)(.)", "a-b-c", + overlapped=True), [("b", "-", "c"), ("a", "-", "b")]) + + self.assertEqual([m[0] for m in regex.finditer(r"(?r).", "abc")], ['c', + 'b', 'a']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)..", "abcde", + overlapped=True)], ['de', 'cd', 'bc', 'ab']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r).", "abc")], ['c', + 'b', 'a']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)..", "abcde", + overlapped=True)], ['de', 'cd', 'bc', 'ab']) + + self.assertEqual(regex.findall(r"^|\w+", "foo bar"), ['', 'foo', + 'bar']) + self.assertEqual(regex.findall(r"(?V1)^|\w+", "foo bar"), ['', 'foo', + 'bar']) + self.assertEqual(regex.findall(r"(?r)^|\w+", "foo bar"), ['bar', 'foo', + '']) + self.assertEqual(regex.findall(r"(?rV1)^|\w+", "foo bar"), ['bar', + 'foo', '']) + + self.assertEqual([m[0] for m in regex.finditer(r"^|\w+", "foo bar")], + ['', 'foo', 'bar']) + self.assertEqual([m[0] for m in regex.finditer(r"(?V1)^|\w+", + "foo bar")], ['', 'foo', 'bar']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)^|\w+", + "foo bar")], ['bar', 'foo', '']) + self.assertEqual([m[0] for m in regex.finditer(r"(?rV1)^|\w+", + "foo bar")], ['bar', 'foo', '']) + + self.assertEqual(regex.findall(r"\G\w{2}", "abcd ef"), ['ab', 'cd']) + self.assertEqual(regex.findall(r".{2}(?<=\G.*)", "abcd"), ['ab', 'cd']) + self.assertEqual(regex.findall(r"(?r)\G\w{2}", "abcd ef"), []) + self.assertEqual(regex.findall(r"(?r)\w{2}\G", "abcd ef"), ['ef']) + + self.assertEqual(regex.findall(r"q*", "qqwe"), ['qq', '', '', '']) + self.assertEqual(regex.findall(r"(?V1)q*", "qqwe"), ['qq', '', '', '']) + self.assertEqual(regex.findall(r"(?r)q*", "qqwe"), ['', '', 'qq', '']) + self.assertEqual(regex.findall(r"(?rV1)q*", "qqwe"), ['', '', 'qq', + '']) + + self.assertEqual(regex.findall(".", "abcd", pos=1, endpos=3), ['b', + 'c']) + self.assertEqual(regex.findall(".", "abcd", pos=1, endpos=-1), ['b', + 'c']) + self.assertEqual([m[0] for m in regex.finditer(".", "abcd", pos=1, + endpos=3)], ['b', 'c']) + self.assertEqual([m[0] for m in regex.finditer(".", "abcd", pos=1, + endpos=-1)], ['b', 'c']) + + self.assertEqual([m[0] for m in regex.finditer("(?r).", "abcd", pos=1, + endpos=3)], ['c', 'b']) + self.assertEqual([m[0] for m in regex.finditer("(?r).", "abcd", pos=1, + endpos=-1)], ['c', 'b']) + self.assertEqual(regex.findall("(?r).", "abcd", pos=1, endpos=3), ['c', + 'b']) + self.assertEqual(regex.findall("(?r).", "abcd", pos=1, endpos=-1), + ['c', 'b']) + + self.assertEqual(regex.findall(r"[ab]", "aB", regex.I), ['a', 'B']) + self.assertEqual(regex.findall(r"(?r)[ab]", "aB", regex.I), ['B', 'a']) + + self.assertEqual(regex.findall(r"(?r).{2}", "abc"), ['bc']) + self.assertEqual(regex.findall(r"(?r).{2}", "abc", overlapped=True), + ['bc', 'ab']) + self.assertEqual(regex.findall(r"(\w+) (\w+)", + "first second third fourth fifth"), [('first', 'second'), ('third', + 'fourth')]) + self.assertEqual(regex.findall(r"(?r)(\w+) (\w+)", + "first second third fourth fifth"), [('fourth', 'fifth'), ('second', + 'third')]) + + self.assertEqual([m[0] for m in regex.finditer(r"(?r).{2}", "abc")], + ['bc']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r).{2}", "abc", + overlapped=True)], ['bc', 'ab']) + self.assertEqual([m[0] for m in regex.finditer(r"(\w+) (\w+)", + "first second third fourth fifth")], ['first second', + 'third fourth']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)(\w+) (\w+)", + "first second third fourth fifth")], ['fourth fifth', + 'second third']) + + self.assertEqual(regex.search("abcdef", "abcdef").span(), (0, 6)) + self.assertEqual(regex.search("(?r)abcdef", "abcdef").span(), (0, 6)) + self.assertEqual(regex.search("(?i)abcdef", "ABCDEF").span(), (0, 6)) + self.assertEqual(regex.search("(?ir)abcdef", "ABCDEF").span(), (0, 6)) + + self.assertEqual(regex.sub(r"(.)", r"\1", "abc"), 'abc') + self.assertEqual(regex.sub(r"(?r)(.)", r"\1", "abc"), 'abc') + + def test_atomic(self): + # Issue 433030. + self.assertEqual(regex.search(r"(?>a*)a", "aa"), None) + + def test_possessive(self): + # Single-character non-possessive. + self.assertEqual(regex.search(r"a?a", "a").span(), (0, 1)) + self.assertEqual(regex.search(r"a*a", "aaa").span(), (0, 3)) + self.assertEqual(regex.search(r"a+a", "aaa").span(), (0, 3)) + self.assertEqual(regex.search(r"a{1,3}a", "aaa").span(), (0, 3)) + + # Multiple-character non-possessive. + self.assertEqual(regex.search(r"(?:ab)?ab", "ab").span(), (0, 2)) + self.assertEqual(regex.search(r"(?:ab)*ab", "ababab").span(), (0, 6)) + self.assertEqual(regex.search(r"(?:ab)+ab", "ababab").span(), (0, 6)) + self.assertEqual(regex.search(r"(?:ab){1,3}ab", "ababab").span(), (0, + 6)) + + # Single-character possessive. + self.assertEqual(regex.search(r"a?+a", "a"), None) + self.assertEqual(regex.search(r"a*+a", "aaa"), None) + self.assertEqual(regex.search(r"a++a", "aaa"), None) + self.assertEqual(regex.search(r"a{1,3}+a", "aaa"), None) + + # Multiple-character possessive. + self.assertEqual(regex.search(r"(?:ab)?+ab", "ab"), None) + self.assertEqual(regex.search(r"(?:ab)*+ab", "ababab"), None) + self.assertEqual(regex.search(r"(?:ab)++ab", "ababab"), None) + self.assertEqual(regex.search(r"(?:ab){1,3}+ab", "ababab"), None) + + def test_zerowidth(self): + # Issue 3262. + self.assertEqual(regex.split(r"\b", "a b"), ['a b']) + self.assertEqual(regex.split(r"(?V1)\b", "a b"), ['', 'a', ' ', 'b', + '']) + + # Issue 1647489. + self.assertEqual(regex.findall(r"^|\w+", "foo bar"), ['', 'foo', + 'bar']) + self.assertEqual([m[0] for m in regex.finditer(r"^|\w+", "foo bar")], + ['', 'foo', 'bar']) + self.assertEqual(regex.findall(r"(?r)^|\w+", "foo bar"), ['bar', 'foo', + '']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)^|\w+", + "foo bar")], ['bar', 'foo', '']) + self.assertEqual(regex.findall(r"(?V1)^|\w+", "foo bar"), ['', 'foo', + 'bar']) + self.assertEqual([m[0] for m in regex.finditer(r"(?V1)^|\w+", + "foo bar")], ['', 'foo', 'bar']) + self.assertEqual(regex.findall(r"(?rV1)^|\w+", "foo bar"), ['bar', + 'foo', '']) + self.assertEqual([m[0] for m in regex.finditer(r"(?rV1)^|\w+", + "foo bar")], ['bar', 'foo', '']) + + self.assertEqual(regex.split("", "xaxbxc"), ['xaxbxc']) + self.assertEqual([m for m in regex.splititer("", "xaxbxc")], + ['xaxbxc']) + + self.assertEqual(regex.split("(?r)", "xaxbxc"), ['xaxbxc']) + self.assertEqual([m for m in regex.splititer("(?r)", "xaxbxc")], + ['xaxbxc']) + + self.assertEqual(regex.split("(?V1)", "xaxbxc"), ['', 'x', 'a', 'x', + 'b', 'x', 'c', '']) + self.assertEqual([m for m in regex.splititer("(?V1)", "xaxbxc")], ['', + 'x', 'a', 'x', 'b', 'x', 'c', '']) + + self.assertEqual(regex.split("(?rV1)", "xaxbxc"), ['', 'c', 'x', 'b', + 'x', 'a', 'x', '']) + self.assertEqual([m for m in regex.splititer("(?rV1)", "xaxbxc")], ['', + 'c', 'x', 'b', 'x', 'a', 'x', '']) + + def test_scoped_and_inline_flags(self): + # Issues 433028, 433024, 433027. + self.assertEqual(regex.search(r"(?i)Ab", "ab").span(), (0, 2)) + self.assertEqual(regex.search(r"(?i:A)b", "ab").span(), (0, 2)) + self.assertEqual(regex.search(r"A(?i)b", "ab").span(), (0, 2)) + self.assertEqual(regex.search(r"A(?iV1)b", "ab"), None) + + self.assertRaisesRegex(regex.error, self.CANT_TURN_OFF, lambda: + regex.search(r"(?V0-i)Ab", "ab", flags=regex.I)) + + self.assertEqual(regex.search(r"(?V0)Ab", "ab"), None) + self.assertEqual(regex.search(r"(?V1)Ab", "ab"), None) + self.assertEqual(regex.search(r"(?V1-i)Ab", "ab", flags=regex.I), None) + self.assertEqual(regex.search(r"(?-i:A)b", "ab", flags=regex.I), None) + self.assertEqual(regex.search(r"A(?V1-i)b", "ab", + flags=regex.I).span(), (0, 2)) + + def test_repeated_repeats(self): + # Issue 2537. + self.assertEqual(regex.search(r"(?:a+)+", "aaa").span(), (0, 3)) + self.assertEqual(regex.search(r"(?:(?:ab)+c)+", "abcabc").span(), (0, + 6)) + + def test_lookbehind(self): + self.assertEqual(regex.search(r"123(?<=a\d+)", "a123").span(), (1, 4)) + self.assertEqual(regex.search(r"123(?<=a\d+)", "b123"), None) + self.assertEqual(regex.search(r"123(?<!a\d+)", "a123"), None) + self.assertEqual(regex.search(r"123(?<!a\d+)", "b123").span(), (1, 4)) + + self.assertEqual(bool(regex.match("(a)b(?<=b)(c)", "abc")), True) + self.assertEqual(regex.match("(a)b(?<=c)(c)", "abc"), None) + self.assertEqual(bool(regex.match("(a)b(?=c)(c)", "abc")), True) + self.assertEqual(regex.match("(a)b(?=b)(c)", "abc"), None) + + self.assertEqual(regex.match("(?:(a)|(x))b(?<=(?(2)x|c))c", "abc"), + None) + self.assertEqual(regex.match("(?:(a)|(x))b(?<=(?(2)b|x))c", "abc"), + None) + self.assertEqual(bool(regex.match("(?:(a)|(x))b(?<=(?(2)x|b))c", + "abc")), True) + self.assertEqual(regex.match("(?:(a)|(x))b(?<=(?(1)c|x))c", "abc"), + None) + self.assertEqual(bool(regex.match("(?:(a)|(x))b(?<=(?(1)b|x))c", + "abc")), True) + + self.assertEqual(bool(regex.match("(?:(a)|(x))b(?=(?(2)x|c))c", + "abc")), True) + self.assertEqual(regex.match("(?:(a)|(x))b(?=(?(2)c|x))c", "abc"), + None) + self.assertEqual(bool(regex.match("(?:(a)|(x))b(?=(?(2)x|c))c", + "abc")), True) + self.assertEqual(regex.match("(?:(a)|(x))b(?=(?(1)b|x))c", "abc"), + None) + self.assertEqual(bool(regex.match("(?:(a)|(x))b(?=(?(1)c|x))c", + "abc")), True) + + self.assertEqual(regex.match("(a)b(?<=(?(2)x|c))(c)", "abc"), None) + self.assertEqual(regex.match("(a)b(?<=(?(2)b|x))(c)", "abc"), None) + self.assertEqual(regex.match("(a)b(?<=(?(1)c|x))(c)", "abc"), None) + self.assertEqual(bool(regex.match("(a)b(?<=(?(1)b|x))(c)", "abc")), + True) + + self.assertEqual(bool(regex.match("(a)b(?=(?(2)x|c))(c)", "abc")), + True) + self.assertEqual(regex.match("(a)b(?=(?(2)b|x))(c)", "abc"), None) + self.assertEqual(bool(regex.match("(a)b(?=(?(1)c|x))(c)", "abc")), + True) + + self.assertEqual(repr(type(regex.compile(r"(a)\2(b)"))), + self.PATTERN_CLASS) + + def test_unmatched_in_sub(self): + # Issue 1519638. + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "xy"), 'y-x') + self.assertEqual(regex.sub(r"(?V1)(x)?(y)?", r"\2-\1", "xy"), 'y-x-') + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "x"), '-x') + self.assertEqual(regex.sub(r"(?V1)(x)?(y)?", r"\2-\1", "x"), '-x-') + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "y"), 'y-') + self.assertEqual(regex.sub(r"(?V1)(x)?(y)?", r"\2-\1", "y"), 'y--') + + def test_bug_10328 (self): + # Issue 10328. + pat = regex.compile(r'(?mV0)(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\n])\Z)') + self.assertEqual(pat.subn(lambda m: '<' + m.lastgroup + '>', + 'foobar '), ('foobar<trailing_ws>', 1)) + self.assertEqual([m.group() for m in pat.finditer('foobar ')], [' ', + '']) + pat = regex.compile(r'(?mV1)(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\n])\Z)') + self.assertEqual(pat.subn(lambda m: '<' + m.lastgroup + '>', + 'foobar '), ('foobar<trailing_ws><no_final_newline>', 2)) + self.assertEqual([m.group() for m in pat.finditer('foobar ')], [' ', + '']) + + def test_overlapped(self): + self.assertEqual(regex.findall(r"..", "abcde"), ['ab', 'cd']) + self.assertEqual(regex.findall(r"..", "abcde", overlapped=True), ['ab', + 'bc', 'cd', 'de']) + self.assertEqual(regex.findall(r"(?r)..", "abcde"), ['de', 'bc']) + self.assertEqual(regex.findall(r"(?r)..", "abcde", overlapped=True), + ['de', 'cd', 'bc', 'ab']) + self.assertEqual(regex.findall(r"(.)(-)(.)", "a-b-c", overlapped=True), + [("a", "-", "b"), ("b", "-", "c")]) + + self.assertEqual([m[0] for m in regex.finditer(r"..", "abcde")], ['ab', + 'cd']) + self.assertEqual([m[0] for m in regex.finditer(r"..", "abcde", + overlapped=True)], ['ab', 'bc', 'cd', 'de']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)..", "abcde")], + ['de', 'bc']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)..", "abcde", + overlapped=True)], ['de', 'cd', 'bc', 'ab']) + + self.assertEqual([m.groups() for m in regex.finditer(r"(.)(-)(.)", + "a-b-c", overlapped=True)], [("a", "-", "b"), ("b", "-", "c")]) + self.assertEqual([m.groups() for m in regex.finditer(r"(?r)(.)(-)(.)", + "a-b-c", overlapped=True)], [("b", "-", "c"), ("a", "-", "b")]) + + def test_splititer(self): + self.assertEqual(regex.split(r",", "a,b,,c,"), ['a', 'b', '', 'c', '']) + self.assertEqual([m for m in regex.splititer(r",", "a,b,,c,")], ['a', + 'b', '', 'c', '']) + + def test_grapheme(self): + self.assertEqual(regex.match(ur"(?u)\X", u"\xE0").span(), (0, 1)) + self.assertEqual(regex.match(ur"(?u)\X", u"a\u0300").span(), (0, 2)) + + self.assertEqual(regex.findall(ur"(?u)\X", + u"a\xE0a\u0300e\xE9e\u0301"), [u'a', u'\xe0', u'a\u0300', u'e', + u'\xe9', u'e\u0301']) + self.assertEqual(regex.findall(ur"(?u)\X{3}", + u"a\xE0a\u0300e\xE9e\u0301"), [u'a\xe0a\u0300', u'e\xe9e\u0301']) + self.assertEqual(regex.findall(ur"(?u)\X", u"\r\r\n\u0301A\u0301"), + [u'\r', u'\r\n', u'\u0301', u'A\u0301']) + + def test_word_boundary(self): + text = u'The quick ("brown") fox can\'t jump 32.3 feet, right?' + self.assertEqual(regex.split(ur'(?V1)\b', text), [u'', u'The', u' ', + u'quick', u' ("', u'brown', u'") ', u'fox', u' ', u'can', u"'", u't', + u' ', u'jump', u' ', u'32', u'.', u'3', u' ', u'feet', u', ', + u'right', u'?']) + self.assertEqual(regex.split(ur'(?V1w)\b', text), [u'', u'The', u' ', + u'quick', u' ', u'(', u'"', u'brown', u'"', u')', u' ', u'fox', u' ', + u"can't", u' ', u'jump', u' ', u'32.3', u' ', u'feet', u',', u' ', + u'right', u'?', u'']) + + text = u"The fox" + self.assertEqual(regex.split(ur'(?V1)\b', text), [u'', u'The', u' ', + u'fox', u'']) + self.assertEqual(regex.split(ur'(?V1w)\b', text), [u'', u'The', u' ', + u' ', u'fox', u'']) + + text = u"can't aujourd'hui l'objectif" + self.assertEqual(regex.split(ur'(?V1)\b', text), [u'', u'can', u"'", + u't', u' ', u'aujourd', u"'", u'hui', u' ', u'l', u"'", u'objectif', + u'']) + self.assertEqual(regex.split(ur'(?V1w)\b', text), [u'', u"can't", u' ', + u"aujourd'hui", u' ', u"l'", u'objectif', u'']) + + def test_line_boundary(self): + self.assertEqual(regex.findall(r".+", "Line 1\nLine 2\n"), ["Line 1", + "Line 2"]) + self.assertEqual(regex.findall(r".+", "Line 1\rLine 2\r"), + ["Line 1\rLine 2\r"]) + self.assertEqual(regex.findall(r".+", "Line 1\r\nLine 2\r\n"), + ["Line 1\r", "Line 2\r"]) + self.assertEqual(regex.findall(r"(?w).+", "Line 1\nLine 2\n"), + ["Line 1", "Line 2"]) + self.assertEqual(regex.findall(r"(?w).+", "Line 1\rLine 2\r"), + ["Line 1", "Line 2"]) + self.assertEqual(regex.findall(r"(?w).+", "Line 1\r\nLine 2\r\n"), + ["Line 1", "Line 2"]) + + self.assertEqual(regex.search(r"^abc", "abc").start(), 0) + self.assertEqual(regex.search(r"^abc", "\nabc"), None) + self.assertEqual(regex.search(r"^abc", "\rabc"), None) + self.assertEqual(regex.search(r"(?w)^abc", "abc").start(), 0) + self.assertEqual(regex.search(r"(?w)^abc", "\nabc"), None) + self.assertEqual(regex.search(r"(?w)^abc", "\rabc"), None) + + self.assertEqual(regex.search(r"abc$", "abc").start(), 0) + self.assertEqual(regex.search(r"abc$", "abc\n").start(), 0) + self.assertEqual(regex.search(r"abc$", "abc\r"), None) + self.assertEqual(regex.search(r"(?w)abc$", "abc").start(), 0) + self.assertEqual(regex.search(r"(?w)abc$", "abc\n").start(), 0) + self.assertEqual(regex.search(r"(?w)abc$", "abc\r").start(), 0) + + self.assertEqual(regex.search(r"(?m)^abc", "abc").start(), 0) + self.assertEqual(regex.search(r"(?m)^abc", "\nabc").start(), 1) + self.assertEqual(regex.search(r"(?m)^abc", "\rabc"), None) + self.assertEqual(regex.search(r"(?mw)^abc", "abc").start(), 0) + self.assertEqual(regex.search(r"(?mw)^abc", "\nabc").start(), 1) + self.assertEqual(regex.search(r"(?mw)^abc", "\rabc").start(), 1) + + self.assertEqual(regex.search(r"(?m)abc$", "abc").start(), 0) + self.assertEqual(regex.search(r"(?m)abc$", "abc\n").start(), 0) + self.assertEqual(regex.search(r"(?m)abc$", "abc\r"), None) + self.assertEqual(regex.search(r"(?mw)abc$", "abc").start(), 0) + self.assertEqual(regex.search(r"(?mw)abc$", "abc\n").start(), 0) + self.assertEqual(regex.search(r"(?mw)abc$", "abc\r").start(), 0) + + def test_branch_reset(self): + self.assertEqual(regex.match(r"(?:(a)|(b))(c)", "ac").groups(), ('a', + None, 'c')) + self.assertEqual(regex.match(r"(?:(a)|(b))(c)", "bc").groups(), (None, + 'b', 'c')) + self.assertEqual(regex.match(r"(?:(?<a>a)|(?<b>b))(?<c>c)", + "ac").groups(), ('a', None, 'c')) + self.assertEqual(regex.match(r"(?:(?<a>a)|(?<b>b))(?<c>c)", + "bc").groups(), (None, 'b', 'c')) + + self.assertEqual(regex.match(r"(?<a>a)(?:(?<b>b)|(?<c>c))(?<d>d)", + "abd").groups(), ('a', 'b', None, 'd')) + self.assertEqual(regex.match(r"(?<a>a)(?:(?<b>b)|(?<c>c))(?<d>d)", + "acd").groups(), ('a', None, 'c', 'd')) + self.assertEqual(regex.match(r"(a)(?:(b)|(c))(d)", "abd").groups(), + ('a', 'b', None, 'd')) + + self.assertEqual(regex.match(r"(a)(?:(b)|(c))(d)", "acd").groups(), + ('a', None, 'c', 'd')) + self.assertEqual(regex.match(r"(a)(?|(b)|(b))(d)", "abd").groups(), + ('a', 'b', 'd')) + self.assertEqual(regex.match(r"(?|(?<a>a)|(?<b>b))(c)", "ac").groups(), + ('a', None, 'c')) + self.assertEqual(regex.match(r"(?|(?<a>a)|(?<b>b))(c)", "bc").groups(), + (None, 'b', 'c')) + self.assertEqual(regex.match(r"(?|(?<a>a)|(?<a>b))(c)", "ac").groups(), + ('a', 'c')) + + self.assertEqual(regex.match(r"(?|(?<a>a)|(?<a>b))(c)", "bc").groups(), + ('b', 'c')) + + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(?<b>c)(?<a>d))(e)", + "abe").groups(), ('a', 'b', 'e')) + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(?<b>c)(?<a>d))(e)", + "cde").groups(), ('d', 'c', 'e')) + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(?<b>c)(d))(e)", + "abe").groups(), ('a', 'b', 'e')) + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(?<b>c)(d))(e)", + "cde").groups(), ('d', 'c', 'e')) + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(c)(d))(e)", + "abe").groups(), ('a', 'b', 'e')) + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(c)(d))(e)", + "cde").groups(), ('c', 'd', 'e')) + + # Hg issue 87. + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(c)(?<a>d))(e)", + "abe").groups(), ("a", "b", "e")) + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(c)(?<a>d))(e)", + "abe").capturesdict(), {"a": ["a"], "b": ["b"]}) + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(c)(?<a>d))(e)", + "cde").groups(), ("d", None, "e")) + self.assertEqual(regex.match(r"(?|(?<a>a)(?<b>b)|(c)(?<a>d))(e)", + "cde").capturesdict(), {"a": ["c", "d"], "b": []}) + + def test_set(self): + self.assertEqual(regex.match(r"[a]", "a").span(), (0, 1)) + self.assertEqual(regex.match(r"(?i)[a]", "A").span(), (0, 1)) + self.assertEqual(regex.match(r"[a-b]", r"a").span(), (0, 1)) + self.assertEqual(regex.match(r"(?i)[a-b]", r"A").span(), (0, 1)) + + self.assertEqual(regex.sub(r"(?V0)([][])", r"-", "a[b]c"), "a-b-c") + + self.assertEqual(regex.findall(ur"[\p{Alpha}]", u"a0"), [u"a"]) + self.assertEqual(regex.findall(ur"(?i)[\p{Alpha}]", u"A0"), [u"A"]) + + self.assertEqual(regex.findall(ur"[a\p{Alpha}]", u"ab0"), [u"a", u"b"]) + self.assertEqual(regex.findall(ur"[a\P{Alpha}]", u"ab0"), [u"a", u"0"]) + self.assertEqual(regex.findall(ur"(?i)[a\p{Alpha}]", u"ab0"), [u"a", + u"b"]) + self.assertEqual(regex.findall(ur"(?i)[a\P{Alpha}]", u"ab0"), [u"a", + u"0"]) + + self.assertEqual(regex.findall(ur"[a-b\p{Alpha}]", u"abC0"), [u"a", + u"b", u"C"]) + self.assertEqual(regex.findall(ur"(?i)[a-b\p{Alpha}]", u"AbC0"), [u"A", + u"b", u"C"]) + + self.assertEqual(regex.findall(ur"[\p{Alpha}]", u"a0"), [u"a"]) + self.assertEqual(regex.findall(ur"[\P{Alpha}]", u"a0"), [u"0"]) + self.assertEqual(regex.findall(ur"[^\p{Alpha}]", u"a0"), [u"0"]) + self.assertEqual(regex.findall(ur"[^\P{Alpha}]", u"a0"), [u"a"]) + + self.assertEqual("".join(regex.findall(r"[^\d-h]", "a^b12c-h")), + 'a^bc') + self.assertEqual("".join(regex.findall(r"[^\dh]", "a^b12c-h")), + 'a^bc-') + self.assertEqual("".join(regex.findall(r"[^h\s\db]", "a^b 12c-h")), + 'a^c-') + self.assertEqual("".join(regex.findall(r"[^b\w]", "a b")), ' ') + self.assertEqual("".join(regex.findall(r"[^b\S]", "a b")), ' ') + self.assertEqual("".join(regex.findall(r"[^8\d]", "a 1b2")), 'a b') + + all_chars = u"".join(unichr(c) for c in range(0x100)) + self.assertEqual(len(regex.findall(ur"(?u)\p{ASCII}", all_chars)), 128) + self.assertEqual(len(regex.findall(ur"(?u)\p{Letter}", all_chars)), + 117) + self.assertEqual(len(regex.findall(ur"(?u)\p{Digit}", all_chars)), 10) + + # Set operators + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{ASCII}&&\p{Letter}]", + all_chars)), 52) + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{ASCII}&&\p{Alnum}&&\p{Letter}]", + all_chars)), 52) + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{ASCII}&&\p{Alnum}&&\p{Digit}]", + all_chars)), 10) + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{ASCII}&&\p{Cc}]", + all_chars)), 33) + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{ASCII}&&\p{Graph}]", + all_chars)), 94) + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{ASCII}--\p{Cc}]", + all_chars)), 95) + self.assertEqual(len(regex.findall(ur"(?u)[\p{Letter}\p{Digit}]", + all_chars)), 127) + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{Letter}||\p{Digit}]", + all_chars)), 127) + self.assertEqual(len(regex.findall(ur"(?u)\p{HexDigit}", all_chars)), + 22) + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{HexDigit}~~\p{Digit}]", + all_chars)), 12) + self.assertEqual(len(regex.findall(ur"(?uV1)[\p{Digit}~~\p{HexDigit}]", + all_chars)), 12) + + self.assertEqual(repr(type(regex.compile(r"(?V0)([][-])"))), + self.PATTERN_CLASS) + self.assertEqual(regex.findall(r"(?V1)[[a-z]--[aei]]", "abc"), ["b", + "c"]) + self.assertEqual(regex.findall(r"(?iV1)[[a-z]--[aei]]", "abc"), ["b", + "c"]) + self.assertEqual(regex.findall("(?V1)[\w--a]","abc"), ["b", "c"]) + self.assertEqual(regex.findall("(?iV1)[\w--a]","abc"), ["b", "c"]) + + def test_various(self): + tests = [ + # Test ?P< and ?P= extensions. + ('(?P<foo_123', '', '', regex.error, self.MISSING_GT), # Unterminated group identifier. + ('(?P<1>a)', '', '', regex.error, self.BAD_GROUP_NAME), # Begins with a digit. + ('(?P<!>a)', '', '', regex.error, self.BAD_GROUP_NAME), # Begins with an illegal char. + ('(?P<foo!>a)', '', '', regex.error, self.BAD_GROUP_NAME), # Begins with an illegal char. + + # Same tests, for the ?P= form. + ('(?P<foo_123>a)(?P=foo_123', 'aa', '', regex.error, + self.MISSING_RPAREN), + ('(?P<foo_123>a)(?P=1)', 'aa', '1', repr('a')), + ('(?P<foo_123>a)(?P=0)', 'aa', '', regex.error, + self.BAD_GROUP_NAME), + ('(?P<foo_123>a)(?P=-1)', 'aa', '', regex.error, + self.BAD_GROUP_NAME), + ('(?P<foo_123>a)(?P=!)', 'aa', '', regex.error, + self.BAD_GROUP_NAME), + ('(?P<foo_123>a)(?P=foo_124)', 'aa', '', regex.error, + self.UNKNOWN_GROUP), # Backref to undefined group. + + ('(?P<foo_123>a)', 'a', '1', repr('a')), + ('(?P<foo_123>a)(?P=foo_123)', 'aa', '1', repr('a')), + + # Mal-formed \g in pattern treated as literal for compatibility. + (r'(?<foo_123>a)\g<foo_123', 'aa', '', repr(None)), + (r'(?<foo_123>a)\g<1>', 'aa', '1', repr('a')), + (r'(?<foo_123>a)\g<!>', 'aa', '', repr(None)), + (r'(?<foo_123>a)\g<foo_124>', 'aa', '', regex.error, + self.UNKNOWN_GROUP), # Backref to undefined group. + + ('(?<foo_123>a)', 'a', '1', repr('a')), + (r'(?<foo_123>a)\g<foo_123>', 'aa', '1', repr('a')), + + # Test octal escapes. + ('\\1', 'a', '', regex.error, self.INVALID_GROUP_REF), # Backreference. + ('[\\1]', '\1', '0', "'\\x01'"), # Character. + ('\\09', chr(0) + '9', '0', repr(chr(0) + '9')), + ('\\141', 'a', '0', repr('a')), + ('(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\119', 'abcdefghijklk9', + '0,11', repr(('abcdefghijklk9', 'k'))), + + # Test \0 is handled everywhere. + (r'\0', '\0', '0', repr('\0')), + (r'[\0a]', '\0', '0', repr('\0')), + (r'[a\0]', '\0', '0', repr('\0')), + (r'[^a\0]', '\0', '', repr(None)), + + # Test various letter escapes. + (r'\a[\b]\f\n\r\t\v', '\a\b\f\n\r\t\v', '0', + repr('\a\b\f\n\r\t\v')), + (r'[\a][\b][\f][\n][\r][\t][\v]', '\a\b\f\n\r\t\v', '0', + repr('\a\b\f\n\r\t\v')), + (r'\c\e\g\h\i\j\k\o\p\q\y\z', 'ceghijkopqyz', '0', + repr('ceghijkopqyz')), + (r'\xff', '\377', '0', repr(chr(255))), + + # New \x semantics. + (r'\x00ffffffffffffff', '\377', '', repr(None)), + (r'\x00f', '\017', '', repr(None)), + (r'\x00fe', '\376', '', repr(None)), + + (r'\x00ff', '\377', '', repr(None)), + (r'\t\n\v\r\f\a\g', '\t\n\v\r\f\ag', '0', repr('\t\n\v\r\f\ag')), + ('\t\n\v\r\f\a\g', '\t\n\v\r\f\ag', '0', repr('\t\n\v\r\f\ag')), + (r'\t\n\v\r\f\a', '\t\n\v\r\f\a', '0', repr(chr(9) + chr(10) + + chr(11) + chr(13) + chr(12) + chr(7))), + (r'[\t][\n][\v][\r][\f][\b]', '\t\n\v\r\f\b', '0', + repr('\t\n\v\r\f\b')), + + (r"^\w+=(\\[\000-\277]|[^\n\\])*", + "SRC=eval.c g.c blah blah blah \\\\\n\tapes.c", '0', + repr("SRC=eval.c g.c blah blah blah \\\\")), + + # Test that . only matches \n in DOTALL mode. + ('a.b', 'acb', '0', repr('acb')), + ('a.b', 'a\nb', '', repr(None)), + ('a.*b', 'acc\nccb', '', repr(None)), + ('a.{4,5}b', 'acc\nccb', '', repr(None)), + ('a.b', 'a\rb', '0', repr('a\rb')), + # The new behaviour is that the inline flag affects only what follows. + ('a.b(?s)', 'a\nb', '0', repr('a\nb')), + ('a.b(?sV1)', 'a\nb', '', repr(None)), + ('(?s)a.b', 'a\nb', '0', repr('a\nb')), + ('a.*(?s)b', 'acc\nccb', '0', repr('acc\nccb')), + ('a.*(?sV1)b', 'acc\nccb', '', repr(None)), + ('(?s)a.*b', 'acc\nccb', '0', repr('acc\nccb')), + ('(?s)a.{4,5}b', 'acc\nccb', '0', repr('acc\nccb')), + + (')', '', '', regex.error, self.TRAILING_CHARS), # Unmatched right bracket. + ('', '', '0', "''"), # Empty pattern. + ('abc', 'abc', '0', repr('abc')), + ('abc', 'xbc', '', repr(None)), + ('abc', 'axc', '', repr(None)), + ('abc', 'abx', '', repr(None)), + ('abc', 'xabcy', '0', repr('abc')), + ('abc', 'ababc', '0', repr('abc')), + ('ab*c', 'abc', '0', repr('abc')), + ('ab*bc', 'abc', '0', repr('abc')), + + ('ab*bc', 'abbc', '0', repr('abbc')), + ('ab*bc', 'abbbbc', '0', repr('abbbbc')), + ('ab+bc', 'abbc', '0', repr('abbc')), + ('ab+bc', 'abc', '', repr(None)), + ('ab+bc', 'abq', '', repr(None)), + ('ab+bc', 'abbbbc', '0', repr('abbbbc')), + ('ab?bc', 'abbc', '0', repr('abbc')), + ('ab?bc', 'abc', '0', repr('abc')), + ('ab?bc', 'abbbbc', '', repr(None)), + ('ab?c', 'abc', '0', repr('abc')), + + ('^abc$', 'abc', '0', repr('abc')), + ('^abc$', 'abcc', '', repr(None)), + ('^abc', 'abcc', '0', repr('abc')), + ('^abc$', 'aabc', '', repr(None)), + ('abc$', 'aabc', '0', repr('abc')), + ('^', 'abc', '0', repr('')), + ('$', 'abc', '0', repr('')), + ('a.c', 'abc', '0', repr('abc')), + ('a.c', 'axc', '0', repr('axc')), + ('a.*c', 'axyzc', '0', repr('axyzc')), + + ('a.*c', 'axyzd', '', repr(None)), + ('a[bc]d', 'abc', '', repr(None)), + ('a[bc]d', 'abd', '0', repr('abd')), + ('a[b-d]e', 'abd', '', repr(None)), + ('a[b-d]e', 'ace', '0', repr('ace')), + ('a[b-d]', 'aac', '0', repr('ac')), + ('a[-b]', 'a-', '0', repr('a-')), + ('a[\\-b]', 'a-', '0', repr('a-')), + ('a[b-]', 'a-', '0', repr('a-')), + ('a[]b', '-', '', regex.error, self.BAD_SET), + + ('a[', '-', '', regex.error, self.BAD_SET), + ('a\\', '-', '', regex.error, self.BAD_ESCAPE), + ('abc)', '-', '', regex.error, self.TRAILING_CHARS), + ('(abc', '-', '', regex.error, self.MISSING_RPAREN), + ('a]', 'a]', '0', repr('a]')), + ('a[]]b', 'a]b', '0', repr('a]b')), + ('a[]]b', 'a]b', '0', repr('a]b')), + ('a[^bc]d', 'aed', '0', repr('aed')), + ('a[^bc]d', 'abd', '', repr(None)), + ('a[^-b]c', 'adc', '0', repr('adc')), + + ('a[^-b]c', 'a-c', '', repr(None)), + ('a[^]b]c', 'a]c', '', repr(None)), + ('a[^]b]c', 'adc', '0', repr('adc')), + ('\\ba\\b', 'a-', '0', repr('a')), + ('\\ba\\b', '-a', '0', repr('a')), + ('\\ba\\b', '-a-', '0', repr('a')), + ('\\by\\b', 'xy', '', repr(None)), + ('\\by\\b', 'yz', '', repr(None)), + ('\\by\\b', 'xyz', '', repr(None)), + ('x\\b', 'xyz', '', repr(None)), + + ('x\\B', 'xyz', '0', repr('x')), + ('\\Bz', 'xyz', '0', repr('z')), + ('z\\B', 'xyz', '', repr(None)), + ('\\Bx', 'xyz', '', repr(None)), + ('\\Ba\\B', 'a-', '', repr(None)), + ('\\Ba\\B', '-a', '', repr(None)), + ('\\Ba\\B', '-a-', '', repr(None)), + ('\\By\\B', 'xy', '', repr(None)), + ('\\By\\B', 'yz', '', repr(None)), + ('\\By\\b', 'xy', '0', repr('y')), + + ('\\by\\B', 'yz', '0', repr('y')), + ('\\By\\B', 'xyz', '0', repr('y')), + ('ab|cd', 'abc', '0', repr('ab')), + ('ab|cd', 'abcd', '0', repr('ab')), + ('()ef', 'def', '0,1', repr(('ef', ''))), + ('$b', 'b', '', repr(None)), + ('a\\(b', 'a(b', '', repr(('a(b',))), + ('a\\(*b', 'ab', '0', repr('ab')), + ('a\\(*b', 'a((b', '0', repr('a((b')), + ('a\\\\b', 'a\\b', '0', repr('a\\b')), + + ('((a))', 'abc', '0,1,2', repr(('a', 'a', 'a'))), + ('(a)b(c)', 'abc', '0,1,2', repr(('abc', 'a', 'c'))), + ('a+b+c', 'aabbabc', '0', repr('abc')), + ('(a+|b)*', 'ab', '0,1', repr(('ab', 'b'))), + ('(a+|b)+', 'ab', '0,1', repr(('ab', 'b'))), + ('(a+|b)?', 'ab', '0,1', repr(('a', 'a'))), + (')(', '-', '', regex.error, self.TRAILING_CHARS), + ('[^ab]*', 'cde', '0', repr('cde')), + ('abc', '', '', repr(None)), + ('a*', '', '0', repr('')), + + ('a|b|c|d|e', 'e', '0', repr('e')), + ('(a|b|c|d|e)f', 'ef', '0,1', repr(('ef', 'e'))), + ('abcd*efg', 'abcdefg', '0', repr('abcdefg')), + ('ab*', 'xabyabbbz', '0', repr('ab')), + ('ab*', 'xayabbbz', '0', repr('a')), + ('(ab|cd)e', 'abcde', '0,1', repr(('cde', 'cd'))), + ('[abhgefdc]ij', 'hij', '0', repr('hij')), + ('^(ab|cd)e', 'abcde', '', repr(None)), + ('(abc|)ef', 'abcdef', '0,1', repr(('ef', ''))), + ('(a|b)c*d', 'abcd', '0,1', repr(('bcd', 'b'))), + + ('(ab|ab*)bc', 'abc', '0,1', repr(('abc', 'a'))), + ('a([bc]*)c*', 'abc', '0,1', repr(('abc', 'bc'))), + ('a([bc]*)(c*d)', 'abcd', '0,1,2', repr(('abcd', 'bc', 'd'))), + ('a([bc]+)(c*d)', 'abcd', '0,1,2', repr(('abcd', 'bc', 'd'))), + ('a([bc]*)(c+d)', 'abcd', '0,1,2', repr(('abcd', 'b', 'cd'))), + ('a[bcd]*dcdcde', 'adcdcde', '0', repr('adcdcde')), + ('a[bcd]+dcdcde', 'adcdcde', '', repr(None)), + ('(ab|a)b*c', 'abc', '0,1', repr(('abc', 'ab'))), + ('((a)(b)c)(d)', 'abcd', '1,2,3,4', repr(('abc', 'a', 'b', 'd'))), + ('[a-zA-Z_][a-zA-Z0-9_]*', 'alpha', '0', repr('alpha')), + + ('^a(bc+|b[eh])g|.h$', 'abh', '0,1', repr(('bh', None))), + ('(bc+d$|ef*g.|h?i(j|k))', 'effgz', '0,1,2', repr(('effgz', + 'effgz', None))), + ('(bc+d$|ef*g.|h?i(j|k))', 'ij', '0,1,2', repr(('ij', 'ij', + 'j'))), + ('(bc+d$|ef*g.|h?i(j|k))', 'effg', '', repr(None)), + ('(bc+d$|ef*g.|h?i(j|k))', 'bcdd', '', repr(None)), + ('(bc+d$|ef*g.|h?i(j|k))', 'reffgz', '0,1,2', repr(('effgz', + 'effgz', None))), + ('(((((((((a)))))))))', 'a', '0', repr('a')), + ('multiple words of text', 'uh-uh', '', repr(None)), + ('multiple words', 'multiple words, yeah', '0', + repr('multiple words')), + ('(.*)c(.*)', 'abcde', '0,1,2', repr(('abcde', 'ab', 'de'))), + + ('\\((.*), (.*)\\)', '(a, b)', '2,1', repr(('b', 'a'))), + ('[k]', 'ab', '', repr(None)), + ('a[-]?c', 'ac', '0', repr('ac')), + ('(abc)\\1', 'abcabc', '1', repr('abc')), + ('([a-c]*)\\1', 'abcabc', '1', repr('abc')), + ('^(.+)?B', 'AB', '1', repr('A')), + ('(a+).\\1$', 'aaaaa', '0,1', repr(('aaaaa', 'aa'))), + ('^(a+).\\1$', 'aaaa', '', repr(None)), + ('(abc)\\1', 'abcabc', '0,1', repr(('abcabc', 'abc'))), + ('([a-c]+)\\1', 'abcabc', '0,1', repr(('abcabc', 'abc'))), + + ('(a)\\1', 'aa', '0,1', repr(('aa', 'a'))), + ('(a+)\\1', 'aa', '0,1', repr(('aa', 'a'))), + ('(a+)+\\1', 'aa', '0,1', repr(('aa', 'a'))), + ('(a).+\\1', 'aba', '0,1', repr(('aba', 'a'))), + ('(a)ba*\\1', 'aba', '0,1', repr(('aba', 'a'))), + ('(aa|a)a\\1$', 'aaa', '0,1', repr(('aaa', 'a'))), + ('(a|aa)a\\1$', 'aaa', '0,1', repr(('aaa', 'a'))), + ('(a+)a\\1$', 'aaa', '0,1', repr(('aaa', 'a'))), + ('([abc]*)\\1', 'abcabc', '0,1', repr(('abcabc', 'abc'))), + ('(a)(b)c|ab', 'ab', '0,1,2', repr(('ab', None, None))), + + ('(a)+x', 'aaax', '0,1', repr(('aaax', 'a'))), + ('([ac])+x', 'aacx', '0,1', repr(('aacx', 'c'))), + ('([^/]*/)*sub1/', 'd:msgs/tdir/sub1/trial/away.cpp', '0,1', + repr(('d:msgs/tdir/sub1/', 'tdir/'))), + ('([^.]*)\\.([^:]*):[T ]+(.*)', 'track1.title:TBlah blah blah', + '0,1,2,3', repr(('track1.title:TBlah blah blah', 'track1', + 'title', 'Blah blah blah'))), + ('([^N]*N)+', 'abNNxyzN', '0,1', repr(('abNNxyzN', 'xyzN'))), + ('([^N]*N)+', 'abNNxyz', '0,1', repr(('abNN', 'N'))), + ('([abc]*)x', 'abcx', '0,1', repr(('abcx', 'abc'))), + ('([abc]*)x', 'abc', '', repr(None)), + ('([xyz]*)x', 'abcx', '0,1', repr(('x', ''))), + ('(a)+b|aac', 'aac', '0,1', repr(('aac', None))), + + # Test symbolic groups. + ('(?P<i d>aaa)a', 'aaaa', '', regex.error, self.BAD_GROUP_NAME), + ('(?P<id>aaa)a', 'aaaa', '0,id', repr(('aaaa', 'aaa'))), + ('(?P<id>aa)(?P=id)', 'aaaa', '0,id', repr(('aaaa', 'aa'))), + ('(?P<id>aa)(?P=xd)', 'aaaa', '', regex.error, self.UNKNOWN_GROUP), + + # Character properties. + (ur"\g", u"g", '0', repr(u'g')), + (ur"\g<1>", u"g", '', regex.error, self.INVALID_GROUP_REF), + (ur"(.)\g<1>", u"gg", '0', repr(u'gg')), + (ur"(.)\g<1>", u"gg", '', repr((u'gg', u'g'))), + (ur"\N", u"N", '0', repr(u'N')), + (ur"\N{LATIN SMALL LETTER A}", u"a", '0', repr(u'a')), + (ur"\p", u"p", '0', repr(u'p')), + (ur"\p{Ll}", u"a", '0', repr(u'a')), + (ur"\P", u"P", '0', repr(u'P')), + (ur"\P{Lu}", u"p", '0', repr(u'p')), + + # All tests from Perl. + ('abc', 'abc', '0', repr('abc')), + ('abc', 'xbc', '', repr(None)), + ('abc', 'axc', '', repr(None)), + ('abc', 'abx', '', repr(None)), + ('abc', 'xabcy', '0', repr('abc')), + ('abc', 'ababc', '0', repr('abc')), + + ('ab*c', 'abc', '0', repr('abc')), + ('ab*bc', 'abc', '0', repr('abc')), + ('ab*bc', 'abbc', '0', repr('abbc')), + ('ab*bc', 'abbbbc', '0', repr('abbbbc')), + ('ab{0,}bc', 'abbbbc', '0', repr('abbbbc')), + ('ab+bc', 'abbc', '0', repr('abbc')), + ('ab+bc', 'abc', '', repr(None)), + ('ab+bc', 'abq', '', repr(None)), + ('ab{1,}bc', 'abq', '', repr(None)), + ('ab+bc', 'abbbbc', '0', repr('abbbbc')), + + ('ab{1,}bc', 'abbbbc', '0', repr('abbbbc')), + ('ab{1,3}bc', 'abbbbc', '0', repr('abbbbc')), + ('ab{3,4}bc', 'abbbbc', '0', repr('abbbbc')), + ('ab{4,5}bc', 'abbbbc', '', repr(None)), + ('ab?bc', 'abbc', '0', repr('abbc')), + ('ab?bc', 'abc', '0', repr('abc')), + ('ab{0,1}bc', 'abc', '0', repr('abc')), + ('ab?bc', 'abbbbc', '', repr(None)), + ('ab?c', 'abc', '0', repr('abc')), + ('ab{0,1}c', 'abc', '0', repr('abc')), + + ('^abc$', 'abc', '0', repr('abc')), + ('^abc$', 'abcc', '', repr(None)), + ('^abc', 'abcc', '0', repr('abc')), + ('^abc$', 'aabc', '', repr(None)), + ('abc$', 'aabc', '0', repr('abc')), + ('^', 'abc', '0', repr('')), + ('$', 'abc', '0', repr('')), + ('a.c', 'abc', '0', repr('abc')), + ('a.c', 'axc', '0', repr('axc')), + ('a.*c', 'axyzc', '0', repr('axyzc')), + + ('a.*c', 'axyzd', '', repr(None)), + ('a[bc]d', 'abc', '', repr(None)), + ('a[bc]d', 'abd', '0', repr('abd')), + ('a[b-d]e', 'abd', '', repr(None)), + ('a[b-d]e', 'ace', '0', repr('ace')), + ('a[b-d]', 'aac', '0', repr('ac')), + ('a[-b]', 'a-', '0', repr('a-')), + ('a[b-]', 'a-', '0', repr('a-')), + ('a[b-a]', '-', '', regex.error, self.BAD_CHAR_RANGE), + ('a[]b', '-', '', regex.error, self.BAD_SET), + + ('a[', '-', '', regex.error, self.BAD_SET), + ('a]', 'a]', '0', repr('a]')), + ('a[]]b', 'a]b', '0', repr('a]b')), + ('a[^bc]d', 'aed', '0', repr('aed')), + ('a[^bc]d', 'abd', '', repr(None)), + ('a[^-b]c', 'adc', '0', repr('adc')), + ('a[^-b]c', 'a-c', '', repr(None)), + ('a[^]b]c', 'a]c', '', repr(None)), + ('a[^]b]c', 'adc', '0', repr('adc')), + ('ab|cd', 'abc', '0', repr('ab')), + + ('ab|cd', 'abcd', '0', repr('ab')), + ('()ef', 'def', '0,1', repr(('ef', ''))), + ('*a', '-', '', regex.error, self.NOTHING_TO_REPEAT), + ('(*)b', '-', '', regex.error, self.NOTHING_TO_REPEAT), + ('$b', 'b', '', repr(None)), + ('a\\', '-', '', regex.error, self.BAD_ESCAPE), + ('a\\(b', 'a(b', '', repr(('a(b',))), + ('a\\(*b', 'ab', '0', repr('ab')), + ('a\\(*b', 'a((b', '0', repr('a((b')), + ('a\\\\b', 'a\\b', '0', repr('a\\b')), + + ('abc)', '-', '', regex.error, self.TRAILING_CHARS), + ('(abc', '-', '', regex.error, self.MISSING_RPAREN), + ('((a))', 'abc', '0,1,2', repr(('a', 'a', 'a'))), + ('(a)b(c)', 'abc', '0,1,2', repr(('abc', 'a', 'c'))), + ('a+b+c', 'aabbabc', '0', repr('abc')), + ('a{1,}b{1,}c', 'aabbabc', '0', repr('abc')), + ('a**', '-', '', regex.error, self.MULTIPLE_REPEAT), + ('a.+?c', 'abcabc', '0', repr('abc')), + ('(a+|b)*', 'ab', '0,1', repr(('ab', 'b'))), + ('(a+|b){0,}', 'ab', '0,1', repr(('ab', 'b'))), + + ('(a+|b)+', 'ab', '0,1', repr(('ab', 'b'))), + ('(a+|b){1,}', 'ab', '0,1', repr(('ab', 'b'))), + ('(a+|b)?', 'ab', '0,1', repr(('a', 'a'))), + ('(a+|b){0,1}', 'ab', '0,1', repr(('a', 'a'))), + (')(', '-', '', regex.error, self.TRAILING_CHARS), + ('[^ab]*', 'cde', '0', repr('cde')), + ('abc', '', '', repr(None)), + ('a*', '', '0', repr('')), + ('([abc])*d', 'abbbcd', '0,1', repr(('abbbcd', 'c'))), + ('([abc])*bcd', 'abcd', '0,1', repr(('abcd', 'a'))), + + ('a|b|c|d|e', 'e', '0', repr('e')), + ('(a|b|c|d|e)f', 'ef', '0,1', repr(('ef', 'e'))), + ('abcd*efg', 'abcdefg', '0', repr('abcdefg')), + ('ab*', 'xabyabbbz', '0', repr('ab')), + ('ab*', 'xayabbbz', '0', repr('a')), + ('(ab|cd)e', 'abcde', '0,1', repr(('cde', 'cd'))), + ('[abhgefdc]ij', 'hij', '0', repr('hij')), + ('^(ab|cd)e', 'abcde', '', repr(None)), + ('(abc|)ef', 'abcdef', '0,1', repr(('ef', ''))), + ('(a|b)c*d', 'abcd', '0,1', repr(('bcd', 'b'))), + + ('(ab|ab*)bc', 'abc', '0,1', repr(('abc', 'a'))), + ('a([bc]*)c*', 'abc', '0,1', repr(('abc', 'bc'))), + ('a([bc]*)(c*d)', 'abcd', '0,1,2', repr(('abcd', 'bc', 'd'))), + ('a([bc]+)(c*d)', 'abcd', '0,1,2', repr(('abcd', 'bc', 'd'))), + ('a([bc]*)(c+d)', 'abcd', '0,1,2', repr(('abcd', 'b', 'cd'))), + ('a[bcd]*dcdcde', 'adcdcde', '0', repr('adcdcde')), + ('a[bcd]+dcdcde', 'adcdcde', '', repr(None)), + ('(ab|a)b*c', 'abc', '0,1', repr(('abc', 'ab'))), + ('((a)(b)c)(d)', 'abcd', '1,2,3,4', repr(('abc', 'a', 'b', 'd'))), + ('[a-zA-Z_][a-zA-Z0-9_]*', 'alpha', '0', repr('alpha')), + + ('^a(bc+|b[eh])g|.h$', 'abh', '0,1', repr(('bh', None))), + ('(bc+d$|ef*g.|h?i(j|k))', 'effgz', '0,1,2', repr(('effgz', + 'effgz', None))), + ('(bc+d$|ef*g.|h?i(j|k))', 'ij', '0,1,2', repr(('ij', 'ij', + 'j'))), + ('(bc+d$|ef*g.|h?i(j|k))', 'effg', '', repr(None)), + ('(bc+d$|ef*g.|h?i(j|k))', 'bcdd', '', repr(None)), + ('(bc+d$|ef*g.|h?i(j|k))', 'reffgz', '0,1,2', repr(('effgz', + 'effgz', None))), + ('((((((((((a))))))))))', 'a', '10', repr('a')), + ('((((((((((a))))))))))\\10', 'aa', '0', repr('aa')), + + # Python does not have the same rules for \\41 so this is a syntax error + # ('((((((((((a))))))))))\\41', 'aa', '', repr(None)), + # ('((((((((((a))))))))))\\41', 'a!', '0', repr('a!')), + ('((((((((((a))))))))))\\41', '', '', regex.error, + self.INVALID_GROUP_REF), + ('(?i)((((((((((a))))))))))\\41', '', '', regex.error, + self.INVALID_GROUP_REF), + + ('(((((((((a)))))))))', 'a', '0', repr('a')), + ('multiple words of text', 'uh-uh', '', repr(None)), + ('multiple words', 'multiple words, yeah', '0', + repr('multiple words')), + ('(.*)c(.*)', 'abcde', '0,1,2', repr(('abcde', 'ab', 'de'))), + ('\\((.*), (.*)\\)', '(a, b)', '2,1', repr(('b', 'a'))), + ('[k]', 'ab', '', repr(None)), + ('a[-]?c', 'ac', '0', repr('ac')), + ('(abc)\\1', 'abcabc', '1', repr('abc')), + ('([a-c]*)\\1', 'abcabc', '1', repr('abc')), + ('(?i)abc', 'ABC', '0', repr('ABC')), + + ('(?i)abc', 'XBC', '', repr(None)), + ('(?i)abc', 'AXC', '', repr(None)), + ('(?i)abc', 'ABX', '', repr(None)), + ('(?i)abc', 'XABCY', '0', repr('ABC')), + ('(?i)abc', 'ABABC', '0', repr('ABC')), + ('(?i)ab*c', 'ABC', '0', repr('ABC')), + ('(?i)ab*bc', 'ABC', '0', repr('ABC')), + ('(?i)ab*bc', 'ABBC', '0', repr('ABBC')), + ('(?i)ab*?bc', 'ABBBBC', '0', repr('ABBBBC')), + ('(?i)ab{0,}?bc', 'ABBBBC', '0', repr('ABBBBC')), + + ('(?i)ab+?bc', 'ABBC', '0', repr('ABBC')), + ('(?i)ab+bc', 'ABC', '', repr(None)), + ('(?i)ab+bc', 'ABQ', '', repr(None)), + ('(?i)ab{1,}bc', 'ABQ', '', repr(None)), + ('(?i)ab+bc', 'ABBBBC', '0', repr('ABBBBC')), + ('(?i)ab{1,}?bc', 'ABBBBC', '0', repr('ABBBBC')), + ('(?i)ab{1,3}?bc', 'ABBBBC', '0', repr('ABBBBC')), + ('(?i)ab{3,4}?bc', 'ABBBBC', '0', repr('ABBBBC')), + ('(?i)ab{4,5}?bc', 'ABBBBC', '', repr(None)), + ('(?i)ab??bc', 'ABBC', '0', repr('ABBC')), + + ('(?i)ab??bc', 'ABC', '0', repr('ABC')), + ('(?i)ab{0,1}?bc', 'ABC', '0', repr('ABC')), + ('(?i)ab??bc', 'ABBBBC', '', repr(None)), + ('(?i)ab??c', 'ABC', '0', repr('ABC')), + ('(?i)ab{0,1}?c', 'ABC', '0', repr('ABC')), + ('(?i)^abc$', 'ABC', '0', repr('ABC')), + ('(?i)^abc$', 'ABCC', '', repr(None)), + ('(?i)^abc', 'ABCC', '0', repr('ABC')), + ('(?i)^abc$', 'AABC', '', repr(None)), + ('(?i)abc$', 'AABC', '0', repr('ABC')), + + ('(?i)^', 'ABC', '0', repr('')), + ('(?i)$', 'ABC', '0', repr('')), + ('(?i)a.c', 'ABC', '0', repr('ABC')), + ('(?i)a.c', 'AXC', '0', repr('AXC')), + ('(?i)a.*?c', 'AXYZC', '0', repr('AXYZC')), + ('(?i)a.*c', 'AXYZD', '', repr(None)), + ('(?i)a[bc]d', 'ABC', '', repr(None)), + ('(?i)a[bc]d', 'ABD', '0', repr('ABD')), + ('(?i)a[b-d]e', 'ABD', '', repr(None)), + ('(?i)a[b-d]e', 'ACE', '0', repr('ACE')), + + ('(?i)a[b-d]', 'AAC', '0', repr('AC')), + ('(?i)a[-b]', 'A-', '0', repr('A-')), + ('(?i)a[b-]', 'A-', '0', repr('A-')), + ('(?i)a[b-a]', '-', '', regex.error, self.BAD_CHAR_RANGE), + ('(?i)a[]b', '-', '', regex.error, self.BAD_SET), + ('(?i)a[', '-', '', regex.error, self.BAD_SET), + ('(?i)a]', 'A]', '0', repr('A]')), + ('(?i)a[]]b', 'A]B', '0', repr('A]B')), + ('(?i)a[^bc]d', 'AED', '0', repr('AED')), + ('(?i)a[^bc]d', 'ABD', '', repr(None)), + + ('(?i)a[^-b]c', 'ADC', '0', repr('ADC')), + ('(?i)a[^-b]c', 'A-C', '', repr(None)), + ('(?i)a[^]b]c', 'A]C', '', repr(None)), + ('(?i)a[^]b]c', 'ADC', '0', repr('ADC')), + ('(?i)ab|cd', 'ABC', '0', repr('AB')), + ('(?i)ab|cd', 'ABCD', '0', repr('AB')), + ('(?i)()ef', 'DEF', '0,1', repr(('EF', ''))), + ('(?i)*a', '-', '', regex.error, self.NOTHING_TO_REPEAT), + ('(?i)(*)b', '-', '', regex.error, self.NOTHING_TO_REPEAT), + ('(?i)$b', 'B', '', repr(None)), + + ('(?i)a\\', '-', '', regex.error, self.BAD_ESCAPE), + ('(?i)a\\(b', 'A(B', '', repr(('A(B',))), + ('(?i)a\\(*b', 'AB', '0', repr('AB')), + ('(?i)a\\(*b', 'A((B', '0', repr('A((B')), + ('(?i)a\\\\b', 'A\\B', '0', repr('A\\B')), + ('(?i)abc)', '-', '', regex.error, self.TRAILING_CHARS), + ('(?i)(abc', '-', '', regex.error, self.MISSING_RPAREN), + ('(?i)((a))', 'ABC', '0,1,2', repr(('A', 'A', 'A'))), + ('(?i)(a)b(c)', 'ABC', '0,1,2', repr(('ABC', 'A', 'C'))), + ('(?i)a+b+c', 'AABBABC', '0', repr('ABC')), + + ('(?i)a{1,}b{1,}c', 'AABBABC', '0', repr('ABC')), + ('(?i)a**', '-', '', regex.error, self.MULTIPLE_REPEAT), + ('(?i)a.+?c', 'ABCABC', '0', repr('ABC')), + ('(?i)a.*?c', 'ABCABC', '0', repr('ABC')), + ('(?i)a.{0,5}?c', 'ABCABC', '0', repr('ABC')), + ('(?i)(a+|b)*', 'AB', '0,1', repr(('AB', 'B'))), + ('(?i)(a+|b){0,}', 'AB', '0,1', repr(('AB', 'B'))), + ('(?i)(a+|b)+', 'AB', '0,1', repr(('AB', 'B'))), + ('(?i)(a+|b){1,}', 'AB', '0,1', repr(('AB', 'B'))), + ('(?i)(a+|b)?', 'AB', '0,1', repr(('A', 'A'))), + + ('(?i)(a+|b){0,1}', 'AB', '0,1', repr(('A', 'A'))), + ('(?i)(a+|b){0,1}?', 'AB', '0,1', repr(('', None))), + ('(?i))(', '-', '', regex.error, self.TRAILING_CHARS), + ('(?i)[^ab]*', 'CDE', '0', repr('CDE')), + ('(?i)abc', '', '', repr(None)), + ('(?i)a*', '', '0', repr('')), + ('(?i)([abc])*d', 'ABBBCD', '0,1', repr(('ABBBCD', 'C'))), + ('(?i)([abc])*bcd', 'ABCD', '0,1', repr(('ABCD', 'A'))), + ('(?i)a|b|c|d|e', 'E', '0', repr('E')), + ('(?i)(a|b|c|d|e)f', 'EF', '0,1', repr(('EF', 'E'))), + + ('(?i)abcd*efg', 'ABCDEFG', '0', repr('ABCDEFG')), + ('(?i)ab*', 'XABYABBBZ', '0', repr('AB')), + ('(?i)ab*', 'XAYABBBZ', '0', repr('A')), + ('(?i)(ab|cd)e', 'ABCDE', '0,1', repr(('CDE', 'CD'))), + ('(?i)[abhgefdc]ij', 'HIJ', '0', repr('HIJ')), + ('(?i)^(ab|cd)e', 'ABCDE', '', repr(None)), + ('(?i)(abc|)ef', 'ABCDEF', '0,1', repr(('EF', ''))), + ('(?i)(a|b)c*d', 'ABCD', '0,1', repr(('BCD', 'B'))), + ('(?i)(ab|ab*)bc', 'ABC', '0,1', repr(('ABC', 'A'))), + ('(?i)a([bc]*)c*', 'ABC', '0,1', repr(('ABC', 'BC'))), + + ('(?i)a([bc]*)(c*d)', 'ABCD', '0,1,2', repr(('ABCD', 'BC', 'D'))), + ('(?i)a([bc]+)(c*d)', 'ABCD', '0,1,2', repr(('ABCD', 'BC', 'D'))), + ('(?i)a([bc]*)(c+d)', 'ABCD', '0,1,2', repr(('ABCD', 'B', 'CD'))), + ('(?i)a[bcd]*dcdcde', 'ADCDCDE', '0', repr('ADCDCDE')), + ('(?i)a[bcd]+dcdcde', 'ADCDCDE', '', repr(None)), + ('(?i)(ab|a)b*c', 'ABC', '0,1', repr(('ABC', 'AB'))), + ('(?i)((a)(b)c)(d)', 'ABCD', '1,2,3,4', repr(('ABC', 'A', 'B', + 'D'))), + ('(?i)[a-zA-Z_][a-zA-Z0-9_]*', 'ALPHA', '0', repr('ALPHA')), + ('(?i)^a(bc+|b[eh])g|.h$', 'ABH', '0,1', repr(('BH', None))), + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'EFFGZ', '0,1,2', repr(('EFFGZ', + 'EFFGZ', None))), + + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'IJ', '0,1,2', repr(('IJ', 'IJ', + 'J'))), + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'EFFG', '', repr(None)), + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'BCDD', '', repr(None)), + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'REFFGZ', '0,1,2', repr(('EFFGZ', + 'EFFGZ', None))), + ('(?i)((((((((((a))))))))))', 'A', '10', repr('A')), + ('(?i)((((((((((a))))))))))\\10', 'AA', '0', repr('AA')), + #('(?i)((((((((((a))))))))))\\41', 'AA', '', repr(None)), + #('(?i)((((((((((a))))))))))\\41', 'A!', '0', repr('A!')), + ('(?i)(((((((((a)))))))))', 'A', '0', repr('A')), + ('(?i)(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))', 'A', '1', + repr('A')), + ('(?i)(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))', 'C', '1', + repr('C')), + ('(?i)multiple words of text', 'UH-UH', '', repr(None)), + + ('(?i)multiple words', 'MULTIPLE WORDS, YEAH', '0', + repr('MULTIPLE WORDS')), + ('(?i)(.*)c(.*)', 'ABCDE', '0,1,2', repr(('ABCDE', 'AB', 'DE'))), + ('(?i)\\((.*), (.*)\\)', '(A, B)', '2,1', repr(('B', 'A'))), + ('(?i)[k]', 'AB', '', repr(None)), + # ('(?i)abcd', 'ABCD', SUCCEED, 'found+"-"+\\found+"-"+\\\\found', repr(ABCD-$&-\\ABCD)), + # ('(?i)a(bc)d', 'ABCD', SUCCEED, 'g1+"-"+\\g1+"-"+\\\\g1', repr(BC-$1-\\BC)), + ('(?i)a[-]?c', 'AC', '0', repr('AC')), + ('(?i)(abc)\\1', 'ABCABC', '1', repr('ABC')), + ('(?i)([a-c]*)\\1', 'ABCABC', '1', repr('ABC')), + ('a(?!b).', 'abad', '0', repr('ad')), + ('a(?=d).', 'abad', '0', repr('ad')), + ('a(?=c|d).', 'abad', '0', repr('ad')), + + ('a(?:b|c|d)(.)', 'ace', '1', repr('e')), + ('a(?:b|c|d)*(.)', 'ace', '1', repr('e')), + ('a(?:b|c|d)+?(.)', 'ace', '1', repr('e')), + ('a(?:b|(c|e){1,2}?|d)+?(.)', 'ace', '1,2', repr(('c', 'e'))), + + # Lookbehind: split by : but not if it is escaped by -. + ('(?<!-):(.*?)(?<!-):', 'a:bc-:de:f', '1', repr('bc-:de')), + # Escaping with \ as we know it. + ('(?<!\\\):(.*?)(?<!\\\):', 'a:bc\\:de:f', '1', repr('bc\\:de')), + # Terminating with ' and escaping with ? as in edifact. + ("(?<!\\?)'(.*?)(?<!\\?)'", "a'bc?'de'f", '1', repr("bc?'de")), + + # Comments using the (?#...) syntax. + + ('w(?# comment', 'w', '', regex.error, self.MISSING_RPAREN), + ('w(?# comment 1)xy(?# comment 2)z', 'wxyz', '0', repr('wxyz')), + + # Check odd placement of embedded pattern modifiers. + + # Not an error under PCRE/PRE: + # When the new behaviour is turned on positional inline flags affect + # only what follows. + ('w(?i)', 'W', '0', repr('W')), + ('w(?iV1)', 'W', '0', repr(None)), + ('w(?i)', 'w', '0', repr('w')), + ('w(?iV1)', 'w', '0', repr('w')), + ('(?i)w', 'W', '0', repr('W')), + ('(?iV1)w', 'W', '0', repr('W')), + + # Comments using the x embedded pattern modifier. + ("""(?x)w# comment 1 +x y +# comment 2 +z""", 'wxyz', '0', repr('wxyz')), + + # Using the m embedded pattern modifier. + ('^abc', """jkl +abc +xyz""", '', repr(None)), + ('(?m)^abc', """jkl +abc +xyz""", '0', repr('abc')), + + ('(?m)abc$', """jkl +xyzabc +123""", '0', repr('abc')), + + # Using the s embedded pattern modifier. + ('a.b', 'a\nb', '', repr(None)), + ('(?s)a.b', 'a\nb', '0', repr('a\nb')), + + # Test \w, etc. both inside and outside character classes. + ('\\w+', '--ab_cd0123--', '0', repr('ab_cd0123')), + ('[\\w]+', '--ab_cd0123--', '0', repr('ab_cd0123')), + ('\\D+', '1234abc5678', '0', repr('abc')), + ('[\\D]+', '1234abc5678', '0', repr('abc')), + ('[\\da-fA-F]+', '123abc', '0', repr('123abc')), + # Not an error under PCRE/PRE: + # ('[\\d-x]', '-', '', regex.error, self.BAD_CHAR_RANGE), + (r'([\s]*)([\S]*)([\s]*)', ' testing!1972', '3,2,1', repr(('', + 'testing!1972', ' '))), + (r'(\s*)(\S*)(\s*)', ' testing!1972', '3,2,1', repr(('', + 'testing!1972', ' '))), + + # + # Post-1.5.2 additions. + + # xmllib problem. + (r'(([a-z]+):)?([a-z]+)$', 'smil', '1,2,3', repr((None, None, + 'smil'))), + # Bug 110866: reference to undefined group. + (r'((.)\1+)', '', '', regex.error, self.OPEN_GROUP), + # Bug 111869: search (PRE/PCRE fails on this one, SRE doesn't). + (r'.*d', 'abc\nabd', '0', repr('abd')), + # Bug 112468: various expected syntax errors. + (r'(', '', '', regex.error, self.MISSING_RPAREN), + (r'[\41]', '!', '0', repr('!')), + # Bug 114033: nothing to repeat. + (r'(x?)?', 'x', '0', repr('x')), + # Bug 115040: rescan if flags are modified inside pattern. + # If the new behaviour is turned on then positional inline flags + # affect only what follows. + (r' (?x)foo ', 'foo', '0', repr('foo')), + (r' (?V1x)foo ', 'foo', '0', repr(None)), + (r'(?x) foo ', 'foo', '0', repr('foo')), + (r'(?V1x) foo ', 'foo', '0', repr('foo')), + (r'(?x)foo ', 'foo', '0', repr('foo')), + (r'(?V1x)foo ', 'foo', '0', repr('foo')), + # Bug 115618: negative lookahead. + (r'(?<!abc)(d.f)', 'abcdefdof', '0', repr('dof')), + # Bug 116251: character class bug. + (r'[\w-]+', 'laser_beam', '0', repr('laser_beam')), + # Bug 123769+127259: non-greedy backtracking bug. + (r'.*?\S *:', 'xx:', '0', repr('xx:')), + (r'a[ ]*?\ (\d+).*', 'a 10', '0', repr('a 10')), + (r'a[ ]*?\ (\d+).*', 'a 10', '0', repr('a 10')), + # Bug 127259: \Z shouldn't depend on multiline mode. + (r'(?ms).*?x\s*\Z(.*)','xx\nx\n', '1', repr('')), + # Bug 128899: uppercase literals under the ignorecase flag. + (r'(?i)M+', 'MMM', '0', repr('MMM')), + (r'(?i)m+', 'MMM', '0', repr('MMM')), + (r'(?i)[M]+', 'MMM', '0', repr('MMM')), + (r'(?i)[m]+', 'MMM', '0', repr('MMM')), + # Bug 130748: ^* should be an error (nothing to repeat). + # In 'regex' we won't bother to complain about this. + # (r'^*', '', '', regex.error, self.NOTHING_TO_REPEAT), + # Bug 133283: minimizing repeat problem. + (r'"(?:\\"|[^"])*?"', r'"\""', '0', repr(r'"\""')), + # Bug 477728: minimizing repeat problem. + (r'^.*?$', 'one\ntwo\nthree\n', '', repr(None)), + # Bug 483789: minimizing repeat problem. + (r'a[^>]*?b', 'a>b', '', repr(None)), + # Bug 490573: minimizing repeat problem. + (r'^a*?$', 'foo', '', repr(None)), + # Bug 470582: nested groups problem. + (r'^((a)c)?(ab)$', 'ab', '1,2,3', repr((None, None, 'ab'))), + # Another minimizing repeat problem (capturing groups in assertions). + ('^([ab]*?)(?=(b)?)c', 'abc', '1,2', repr(('ab', None))), + ('^([ab]*?)(?!(b))c', 'abc', '1,2', repr(('ab', None))), + ('^([ab]*?)(?<!(a))c', 'abc', '1,2', repr(('ab', None))), + # Bug 410271: \b broken under locales. + (r'\b.\b', 'a', '0', repr('a')), + (ur'(?u)\b.\b', u'\N{LATIN CAPITAL LETTER A WITH DIAERESIS}', '0', + repr(u'\xc4')), + (ur'(?u)\w', u'\N{LATIN CAPITAL LETTER A WITH DIAERESIS}', '0', + repr(u'\xc4')), + ] + + for t in tests: + excval = None + try: + if len(t) == 4: + pattern, string, groups, expected = t + else: + pattern, string, groups, expected, excval = t + except ValueError: + fields = ", ".join([repr(f) for f in t[ : 3]] + ["..."]) + self.fail("Incorrect number of test fields: (%s)" % fields) + else: + group_list = [] + if groups: + for group in groups.split(","): + try: + group_list.append(int(group)) + except ValueError: + group_list.append(group) + + if excval is not None: + self.assertRaisesRegex(expected, excval, regex.search, + pattern, string) + else: + m = regex.search(pattern, string) + if m: + if group_list: + actual = repr(m.group(*group_list)) + else: + actual = repr(m[:]) + else: + actual = repr(m) + + self.assertEqual(actual, expected) + + def test_replacement(self): + self.assertEqual(regex.sub("test\?", "result\?\.\a\q\m\n", "test?"), + "result\?\.\a\q\m\n") + self.assertEqual(regex.sub(r"test\?", "result\?\.\a\q\m\n", "test?"), + "result\?\.\a\q\m\n") + + self.assertEqual(regex.sub('(.)', r"\1\1", 'x'), 'xx') + self.assertEqual(regex.sub('(.)', regex.escape(r"\1\1"), 'x'), r"\1\1") + self.assertEqual(regex.sub('(.)', r"\\1\\1", 'x'), r"\1\1") + self.assertEqual(regex.sub('(.)', lambda m: r"\1\1", 'x'), r"\1\1") + + def test_common_prefix(self): + # Very long common prefix + all = string.ascii_lowercase + string.digits + string.ascii_uppercase + side = all * 4 + regexp = '(' + side + '|' + side + ')' + self.assertEqual(repr(type(regex.compile(regexp))), self.PATTERN_CLASS) + + def test_captures(self): + self.assertEqual(regex.search(r"(\w)+", "abc").captures(1), ['a', 'b', + 'c']) + self.assertEqual(regex.search(r"(\w{3})+", "abcdef").captures(0, 1), + (['abcdef'], ['abc', 'def'])) + self.assertEqual(regex.search(r"^(\d{1,3})(?:\.(\d{1,3})){3}$", + "192.168.0.1").captures(1, 2), (['192', ], ['168', '0', '1'])) + self.assertEqual(regex.match(r"^([0-9A-F]{2}){4} ([a-z]\d){5}$", + "3FB52A0C a2c4g3k9d3").captures(1, 2), (['3F', 'B5', '2A', '0C'], + ['a2', 'c4', 'g3', 'k9', 'd3'])) + self.assertEqual(regex.match("([a-z]W)([a-z]X)+([a-z]Y)", + "aWbXcXdXeXfY").captures(1, 2, 3), (['aW'], ['bX', 'cX', 'dX', 'eX'], + ['fY'])) + + self.assertEqual(regex.search(r".*?(?=(.)+)b", "ab").captures(1), + ['b']) + self.assertEqual(regex.search(r".*?(?>(.){0,2})d", "abcd").captures(1), + ['b', 'c']) + self.assertEqual(regex.search(r"(.)+", "a").captures(1), ['a']) + + def test_guards(self): + m = regex.search(r"(X.*?Y\s*){3}(X\s*)+AB:", + "XY\nX Y\nX Y\nXY\nXX AB:") + self.assertEqual(m.span(0, 1, 2), ((3, 21), (12, 15), (16, 18))) + + m = regex.search(r"(X.*?Y\s*){3,}(X\s*)+AB:", + "XY\nX Y\nX Y\nXY\nXX AB:") + self.assertEqual(m.span(0, 1, 2), ((0, 21), (12, 15), (16, 18))) + + m = regex.search(r'\d{4}(\s*\w)?\W*((?!\d)\w){2}', "9999XX") + self.assertEqual(m.span(0, 1, 2), ((0, 6), (-1, -1), (5, 6))) + + m = regex.search(r'A\s*?.*?(\n+.*?\s*?){0,2}\(X', 'A\n1\nS\n1 (X') + self.assertEqual(m.span(0, 1), ((0, 10), (5, 8))) + + m = regex.search('Derde\s*:', 'aaaaaa:\nDerde:') + self.assertEqual(m.span(), (8, 14)) + m = regex.search('Derde\s*:', 'aaaaa:\nDerde:') + self.assertEqual(m.span(), (7, 13)) + + def test_turkic(self): + # Turkish has dotted and dotless I/i. + pairs = u"I=i;I=\u0131;i=\u0130" + + all_chars = set() + matching = set() + for pair in pairs.split(";"): + ch1, ch2 = pair.split("=") + all_chars.update((ch1, ch2)) + matching.add((ch1, ch1)) + matching.add((ch1, ch2)) + matching.add((ch2, ch1)) + matching.add((ch2, ch2)) + + for ch1 in all_chars: + for ch2 in all_chars: + m = regex.match(ur"(?iu)\A" + ch1 + ur"\Z", ch2) + if m: + if (ch1, ch2) not in matching: + self.fail("%s matching %s" % (repr(ch1), repr(ch2))) + else: + if (ch1, ch2) in matching: + self.fail("%s not matching %s" % (repr(ch1), + repr(ch2))) + + def test_named_lists(self): + options = [u"one", u"two", u"three"] + self.assertEqual(regex.match(ur"333\L<bar>444", u"333one444", + bar=options).group(), u"333one444") + self.assertEqual(regex.match(ur"(?i)333\L<bar>444", u"333TWO444", + bar=options).group(), u"333TWO444") + self.assertEqual(regex.match(ur"333\L<bar>444", u"333four444", + bar=options), None) + + options = ["one", "two", "three"] + self.assertEqual(regex.match(r"333\L<bar>444", "333one444", + bar=options).group(), "333one444") + self.assertEqual(regex.match(r"(?i)333\L<bar>444", "333TWO444", + bar=options).group(), "333TWO444") + self.assertEqual(regex.match(r"333\L<bar>444", "333four444", + bar=options), None) + + self.assertEqual(repr(type(regex.compile(r"3\L<bar>4\L<bar>+5", + bar=["one", "two", "three"]))), self.PATTERN_CLASS) + + self.assertEqual(regex.findall(r"^\L<options>", "solid QWERT", + options=set(['good', 'brilliant', '+s\\ol[i}d'])), []) + self.assertEqual(regex.findall(r"^\L<options>", "+solid QWERT", + options=set(['good', 'brilliant', '+solid'])), ['+solid']) + + options = [u"STRASSE"] + self.assertEqual(regex.match(ur"(?fiu)\L<words>", + u"stra\N{LATIN SMALL LETTER SHARP S}e", words=options).span(), (0, + 6)) + + options = [u"STRASSE", u"stress"] + self.assertEqual(regex.match(ur"(?fiu)\L<words>", + u"stra\N{LATIN SMALL LETTER SHARP S}e", words=options).span(), (0, + 6)) + + options = [u"stra\N{LATIN SMALL LETTER SHARP S}e"] + self.assertEqual(regex.match(ur"(?fiu)\L<words>", u"STRASSE", + words=options).span(), (0, 7)) + + options = ["kit"] + self.assertEqual(regex.search(ur"(?iu)\L<words>", u"SKITS", + words=options).span(), (1, 4)) + self.assertEqual(regex.search(ur"(?iu)\L<words>", + u"SK\N{LATIN CAPITAL LETTER I WITH DOT ABOVE}TS", + words=options).span(), (1, 4)) + + self.assertEqual(regex.search(ur"(?fiu)\b(\w+) +\1\b", + u" stra\N{LATIN SMALL LETTER SHARP S}e STRASSE ").span(), (1, 15)) + self.assertEqual(regex.search(ur"(?fiu)\b(\w+) +\1\b", + u" STRASSE stra\N{LATIN SMALL LETTER SHARP S}e ").span(), (1, 15)) + + self.assertEqual(regex.search(r"^\L<options>$", "", options=[]).span(), + (0, 0)) + + def test_fuzzy(self): + # Some tests borrowed from TRE library tests. + self.assertEqual(repr(type(regex.compile('(fou){s,e<=1}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(fuu){s}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(fuu){s,e}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(anaconda){1i+1d<1,s<=1}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(anaconda){1i+1d<1,s<=1,e<=10}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(anaconda){s<=1,e<=1,1i+1d<1}'))), + self.PATTERN_CLASS) + + text = 'molasses anaconda foo bar baz smith anderson ' + self.assertEqual(regex.search('(znacnda){s<=1,e<=3,1i+1d<1}', text), + None) + self.assertEqual(regex.search('(znacnda){s<=1,e<=3,1i+1d<2}', + text).span(0, 1), ((9, 17), (9, 17))) + self.assertEqual(regex.search('(ananda){1i+1d<2}', text), None) + self.assertEqual(regex.search(r"(?:\bznacnda){e<=2}", text)[0], + "anaconda") + self.assertEqual(regex.search(r"(?:\bnacnda){e<=2}", text)[0], + "anaconda") + + text = 'anaconda foo bar baz smith anderson' + self.assertEqual(regex.search('(fuu){i<=3,d<=3,e<=5}', text).span(0, + 1), ((0, 0), (0, 0))) + self.assertEqual(regex.search('(?b)(fuu){i<=3,d<=3,e<=5}', + text).span(0, 1), ((9, 10), (9, 10))) + self.assertEqual(regex.search('(fuu){i<=2,d<=2,e<=5}', text).span(0, + 1), ((7, 10), (7, 10))) + self.assertEqual(regex.search('(?e)(fuu){i<=2,d<=2,e<=5}', + text).span(0, 1), ((9, 10), (9, 10))) + self.assertEqual(regex.search('(fuu){i<=3,d<=3,e}', text).span(0, 1), + ((0, 0), (0, 0))) + self.assertEqual(regex.search('(?b)(fuu){i<=3,d<=3,e}', text).span(0, + 1), ((9, 10), (9, 10))) + + self.assertEqual(repr(type(regex.compile('(approximate){s<=3,1i+1d<3}'))), + self.PATTERN_CLASS) + + # No cost limit. + self.assertEqual(regex.search('(foobar){e}', + 'xirefoabralfobarxie').span(0, 1), ((0, 6), (0, 6))) + self.assertEqual(regex.search('(?e)(foobar){e}', + 'xirefoabralfobarxie').span(0, 1), ((0, 3), (0, 3))) + self.assertEqual(regex.search('(?b)(foobar){e}', + 'xirefoabralfobarxie').span(0, 1), ((11, 16), (11, 16))) + + # At most two errors. + self.assertEqual(regex.search('(foobar){e<=2}', + 'xirefoabrzlfd').span(0, 1), ((4, 9), (4, 9))) + self.assertEqual(regex.search('(foobar){e<=2}', 'xirefoabzlfd'), None) + + # At most two inserts or substitutions and max two errors total. + self.assertEqual(regex.search('(foobar){i<=2,s<=2,e<=2}', + 'oobargoobaploowap').span(0, 1), ((5, 11), (5, 11))) + + # Find best whole word match for "foobar". + self.assertEqual(regex.search('\\b(foobar){e}\\b', 'zfoobarz').span(0, + 1), ((0, 8), (0, 8))) + self.assertEqual(regex.search('\\b(foobar){e}\\b', + 'boing zfoobarz goobar woop').span(0, 1), ((0, 6), (0, 6))) + self.assertEqual(regex.search('(?b)\\b(foobar){e}\\b', + 'boing zfoobarz goobar woop').span(0, 1), ((15, 21), (15, 21))) + + # Match whole string, allow only 1 error. + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foobar').span(0, 1), + ((0, 6), (0, 6))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xfoobar').span(0, + 1), ((0, 7), (0, 7))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foobarx').span(0, + 1), ((0, 7), (0, 7))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'fooxbar').span(0, + 1), ((0, 7), (0, 7))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foxbar').span(0, 1), + ((0, 6), (0, 6))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xoobar').span(0, 1), + ((0, 6), (0, 6))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foobax').span(0, 1), + ((0, 6), (0, 6))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'oobar').span(0, 1), + ((0, 5), (0, 5))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'fobar').span(0, 1), + ((0, 5), (0, 5))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'fooba').span(0, 1), + ((0, 5), (0, 5))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xfoobarx'), None) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foobarxx'), None) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xxfoobar'), None) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xfoxbar'), None) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foxbarx'), None) + + # At most one insert, two deletes, and three substitutions. + # Additionally, deletes cost two and substitutes one, and total + # cost must be less than 4. + self.assertEqual(regex.search('(foobar){i<=1,d<=2,s<=3,2d+1s<4}', + '3oifaowefbaoraofuiebofasebfaobfaorfeoaro').span(0, 1), ((6, 13), (6, + 13))) + self.assertEqual(regex.search('(?b)(foobar){i<=1,d<=2,s<=3,2d+1s<4}', + '3oifaowefbaoraofuiebofasebfaobfaorfeoaro').span(0, 1), ((34, 39), + (34, 39))) + + # Partially fuzzy matches. + self.assertEqual(regex.search('foo(bar){e<=1}zap', 'foobarzap').span(0, + 1), ((0, 9), (3, 6))) + self.assertEqual(regex.search('foo(bar){e<=1}zap', 'fobarzap'), None) + self.assertEqual(regex.search('foo(bar){e<=1}zap', 'foobrzap').span(0, + 1), ((0, 8), (3, 5))) + + text = ('www.cnn.com 64.236.16.20\nwww.slashdot.org 66.35.250.150\n' + 'For useful information, use www.slashdot.org\nthis is demo data!\n') + self.assertEqual(regex.search(r'(?s)^.*(dot.org){e}.*$', text).span(0, + 1), ((0, 120), (120, 120))) + self.assertEqual(regex.search(r'(?es)^.*(dot.org){e}.*$', text).span(0, + 1), ((0, 120), (93, 100))) + self.assertEqual(regex.search(r'^.*(dot.org){e}.*$', text).span(0, 1), + ((0, 119), (24, 101))) + + # Behaviour is unexpected, but arguably not wrong. It first finds the + # best match, then the best in what follows, etc. + self.assertEqual(regex.findall(r"\b\L<words>{e<=1}\b", + " book cot dog desk ", words="cat dog".split()), ["cot", "dog"]) + self.assertEqual(regex.findall(r"\b\L<words>{e<=1}\b", + " book dog cot desk ", words="cat dog".split()), [" dog", "cot"]) + self.assertEqual(regex.findall(r"(?e)\b\L<words>{e<=1}\b", + " book dog cot desk ", words="cat dog".split()), ["dog", "cot"]) + self.assertEqual(regex.findall(r"(?r)\b\L<words>{e<=1}\b", + " book cot dog desk ", words="cat dog".split()), ["dog ", "cot"]) + self.assertEqual(regex.findall(r"(?er)\b\L<words>{e<=1}\b", + " book cot dog desk ", words="cat dog".split()), ["dog", "cot"]) + self.assertEqual(regex.findall(r"(?r)\b\L<words>{e<=1}\b", + " book dog cot desk ", words="cat dog".split()), ["cot", "dog"]) + self.assertEqual(regex.findall(ur"\b\L<words>{e<=1}\b", + u" book cot dog desk ", words=u"cat dog".split()), [u"cot", u"dog"]) + self.assertEqual(regex.findall(ur"\b\L<words>{e<=1}\b", + u" book dog cot desk ", words=u"cat dog".split()), [u" dog", u"cot"]) + self.assertEqual(regex.findall(ur"(?e)\b\L<words>{e<=1}\b", + u" book dog cot desk ", words=u"cat dog".split()), [u"dog", u"cot"]) + self.assertEqual(regex.findall(ur"(?r)\b\L<words>{e<=1}\b", + u" book cot dog desk ", words=u"cat dog".split()), [u"dog ", u"cot"]) + self.assertEqual(regex.findall(ur"(?er)\b\L<words>{e<=1}\b", + u" book cot dog desk ", words=u"cat dog".split()), [u"dog", u"cot"]) + self.assertEqual(regex.findall(ur"(?r)\b\L<words>{e<=1}\b", + u" book dog cot desk ", words=u"cat dog".split()), [u"cot", u"dog"]) + + self.assertEqual(regex.search(r"(\w+) (\1{e<=1})", "foo fou").groups(), + ("foo", "fou")) + self.assertEqual(regex.search(r"(?r)(\2{e<=1}) (\w+)", + "foo fou").groups(), ("foo", "fou")) + self.assertEqual(regex.search(ur"(\w+) (\1{e<=1})", + u"foo fou").groups(), (u"foo", u"fou")) + + self.assertEqual(regex.findall(r"(?:(?:QR)+){e}","abcde"), ["abcde", + ""]) + self.assertEqual(regex.findall(r"(?:Q+){e}","abc"), ["abc", ""]) + + # Hg issue 41. + self.assertEqual(regex.match(r"(?:service detection){0<e<5}", + "servic detection").span(), (0, 16)) + self.assertEqual(regex.match(r"(?:service detection){0<e<5}", + "service detect").span(), (0, 14)) + self.assertEqual(regex.match(r"(?:service detection){0<e<5}", + "service detecti").span(), (0, 15)) + self.assertEqual(regex.match(r"(?:service detection){0<e<5}", + "service detection"), None) + self.assertEqual(regex.match(r"(?:service detection){0<e<5}", + "in service detection").span(), (0, 20)) + + # Hg issue 109. + self.assertEqual(regex.fullmatch(r"(?:cats|cat){e<=1}", + "cat").fuzzy_counts, (0, 0, 1)) + self.assertEqual(regex.fullmatch(r"(?e)(?:cats|cat){e<=1}", + "cat").fuzzy_counts, (0, 0, 0)) + + self.assertEqual(regex.fullmatch(r"(?:cat|cats){e<=1}", + "cats").fuzzy_counts, (0, 1, 0)) + self.assertEqual(regex.fullmatch(r"(?e)(?:cat|cats){e<=1}", + "cats").fuzzy_counts, (0, 0, 0)) + + self.assertEqual(regex.fullmatch(r"(?:cat){e<=1} (?:cat){e<=1}", + "cat cot").fuzzy_counts, (1, 0, 0)) + + def test_recursive(self): + self.assertEqual(regex.search(r"(\w)(?:(?R)|(\w?))\1", "xx")[ : ], + ("xx", "x", "")) + self.assertEqual(regex.search(r"(\w)(?:(?R)|(\w?))\1", "aba")[ : ], + ("aba", "a", "b")) + self.assertEqual(regex.search(r"(\w)(?:(?R)|(\w?))\1", "abba")[ : ], + ("abba", "a", None)) + self.assertEqual(regex.search(r"(\w)(?:(?R)|(\w?))\1", "kayak")[ : ], + ("kayak", "k", None)) + self.assertEqual(regex.search(r"(\w)(?:(?R)|(\w?))\1", "paper")[ : ], + ("pap", "p", "a")) + self.assertEqual(regex.search(r"(\w)(?:(?R)|(\w?))\1", "dontmatchme"), + None) + + self.assertEqual(regex.search(r"(?r)\2(?:(\w?)|(?R))(\w)", "xx")[ : ], + ("xx", "", "x")) + self.assertEqual(regex.search(r"(?r)\2(?:(\w?)|(?R))(\w)", "aba")[ : ], + ("aba", "b", "a")) + self.assertEqual(regex.search(r"(?r)\2(?:(\w?)|(?R))(\w)", "abba")[ : + ], ("abba", None, "a")) + self.assertEqual(regex.search(r"(?r)\2(?:(\w?)|(?R))(\w)", "kayak")[ : + ], ("kayak", None, "k")) + self.assertEqual(regex.search(r"(?r)\2(?:(\w?)|(?R))(\w)", "paper")[ : + ], ("pap", "a", "p")) + self.assertEqual(regex.search(r"(?r)\2(?:(\w?)|(?R))(\w)", + "dontmatchme"), None) + + self.assertEqual(regex.search(r"\(((?>[^()]+)|(?R))*\)", "(ab(cd)ef)")[ + : ], ("(ab(cd)ef)", "ef")) + self.assertEqual(regex.search(r"\(((?>[^()]+)|(?R))*\)", + "(ab(cd)ef)").captures(1), ["ab", "cd", "(cd)", "ef"]) + + self.assertEqual(regex.search(r"(?r)\(((?R)|(?>[^()]+))*\)", + "(ab(cd)ef)")[ : ], ("(ab(cd)ef)", "ab")) + self.assertEqual(regex.search(r"(?r)\(((?R)|(?>[^()]+))*\)", + "(ab(cd)ef)").captures(1), ["ef", "cd", "(cd)", "ab"]) + + self.assertEqual(regex.search(r"\(([^()]+|(?R))*\)", + "some text (a(b(c)d)e) more text")[ : ], ("(a(b(c)d)e)", "e")) + + self.assertEqual(regex.search(r"(?r)\(((?R)|[^()]+)*\)", + "some text (a(b(c)d)e) more text")[ : ], ("(a(b(c)d)e)", "a")) + + self.assertEqual(regex.search(r"(foo(\(((?:(?>[^()]+)|(?2))*)\)))", + "foo(bar(baz)+baz(bop))")[ : ], ("foo(bar(baz)+baz(bop))", + "foo(bar(baz)+baz(bop))", "(bar(baz)+baz(bop))", + "bar(baz)+baz(bop)")) + + self.assertEqual(regex.search(r"(?r)(foo(\(((?:(?2)|(?>[^()]+))*)\)))", + "foo(bar(baz)+baz(bop))")[ : ], ("foo(bar(baz)+baz(bop))", + "foo(bar(baz)+baz(bop))", "(bar(baz)+baz(bop))", + "bar(baz)+baz(bop)")) + + rgx = regex.compile(r"""^\s*(<\s*([a-zA-Z:]+)(?:\s*[a-zA-Z:]*\s*=\s*(?:'[^']*'|"[^"]*"))*\s*(/\s*)?>(?:[^<>]*|(?1))*(?(3)|<\s*/\s*\2\s*>))\s*$""") + self.assertEqual(bool(rgx.search('<foo><bar></bar></foo>')), True) + self.assertEqual(bool(rgx.search('<foo><bar></foo></bar>')), False) + self.assertEqual(bool(rgx.search('<foo><bar/></foo>')), True) + self.assertEqual(bool(rgx.search('<foo><bar></foo>')), False) + self.assertEqual(bool(rgx.search('<foo bar=baz/>')), False) + + self.assertEqual(bool(rgx.search('<foo bar="baz">')), False) + self.assertEqual(bool(rgx.search('<foo bar="baz"/>')), True) + self.assertEqual(bool(rgx.search('< fooo / >')), True) + # The next regex should and does match. Perl 5.14 agrees. + #self.assertEqual(bool(rgx.search('<foo/>foo')), False) + self.assertEqual(bool(rgx.search('foo<foo/>')), False) + + self.assertEqual(bool(rgx.search('<foo>foo</foo>')), True) + self.assertEqual(bool(rgx.search('<foo><bar/>foo</foo>')), True) + self.assertEqual(bool(rgx.search('<a><b><c></c></b></a>')), True) + + def test_copy(self): + # PatternObjects are immutable, therefore there's no need to clone them. + r = regex.compile("a") + self.assert_(copy.copy(r) is r) + self.assert_(copy.deepcopy(r) is r) + + # MatchObjects are normally mutable because the target string can be + # detached. However, after the target string has been detached, a + # MatchObject becomes immutable, so there's no need to clone it. + m = r.match("a") + self.assert_(copy.copy(m) is not m) + self.assert_(copy.deepcopy(m) is not m) + + self.assert_(m.string is not None) + m2 = copy.copy(m) + m2.detach_string() + self.assert_(m.string is not None) + self.assert_(m2.string is None) + + # The following behaviour matches that of the re module. + it = regex.finditer(".", "ab") + it2 = copy.copy(it) + self.assertEqual(it.next().group(), "a") + self.assertEqual(it2.next().group(), "b") + + # The following behaviour matches that of the re module. + it = regex.finditer(".", "ab") + it2 = copy.deepcopy(it) + self.assertEqual(it.next().group(), "a") + self.assertEqual(it2.next().group(), "b") + + # The following behaviour is designed to match that of copying 'finditer'. + it = regex.splititer(" ", "a b") + it2 = copy.copy(it) + self.assertEqual(it.next(), "a") + self.assertEqual(it2.next(), "b") + + # The following behaviour is designed to match that of copying 'finditer'. + it = regex.splititer(" ", "a b") + it2 = copy.deepcopy(it) + self.assertEqual(it.next(), "a") + self.assertEqual(it2.next(), "b") + + def test_format(self): + self.assertEqual(regex.subf(r"(\w+) (\w+)", "{0} => {2} {1}", + "foo bar"), "foo bar => bar foo") + self.assertEqual(regex.subf(r"(?<word1>\w+) (?<word2>\w+)", + "{word2} {word1}", "foo bar"), "bar foo") + + self.assertEqual(regex.subfn(r"(\w+) (\w+)", "{0} => {2} {1}", + "foo bar"), ("foo bar => bar foo", 1)) + self.assertEqual(regex.subfn(r"(?<word1>\w+) (?<word2>\w+)", + "{word2} {word1}", "foo bar"), ("bar foo", 1)) + + self.assertEqual(regex.match(r"(\w+) (\w+)", + "foo bar").expandf("{0} => {2} {1}"), "foo bar => bar foo") + + def test_fullmatch(self): + self.assertEqual(bool(regex.fullmatch(r"abc", "abc")), True) + self.assertEqual(bool(regex.fullmatch(r"abc", "abcx")), False) + self.assertEqual(bool(regex.fullmatch(r"abc", "abcx", endpos=3)), True) + + self.assertEqual(bool(regex.fullmatch(r"abc", "xabc", pos=1)), True) + self.assertEqual(bool(regex.fullmatch(r"abc", "xabcy", pos=1)), False) + self.assertEqual(bool(regex.fullmatch(r"abc", "xabcy", pos=1, + endpos=4)), True) + + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "abc")), True) + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "abcx")), False) + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "abcx", endpos=3)), + True) + + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "xabc", pos=1)), + True) + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "xabcy", pos=1)), + False) + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "xabcy", pos=1, + endpos=4)), True) + + def test_hg_bugs(self): + # Hg issue 28. + self.assertEqual(bool(regex.compile("(?>b)", flags=regex.V1)), True) + + # Hg issue 29. + self.assertEqual(bool(regex.compile(r"^((?>\w+)|(?>\s+))*$", + flags=regex.V1)), True) + + # Hg issue 31. + self.assertEqual(regex.findall(r"\((?:(?>[^()]+)|(?R))*\)", + "a(bcd(e)f)g(h)"), ['(bcd(e)f)', '(h)']) + self.assertEqual(regex.findall(r"\((?:(?:[^()]+)|(?R))*\)", + "a(bcd(e)f)g(h)"), ['(bcd(e)f)', '(h)']) + self.assertEqual(regex.findall(r"\((?:(?>[^()]+)|(?R))*\)", + "a(b(cd)e)f)g)h"), ['(b(cd)e)']) + self.assertEqual(regex.findall(r"\((?:(?>[^()]+)|(?R))*\)", + "a(bc(d(e)f)gh"), ['(d(e)f)']) + self.assertEqual(regex.findall(r"(?r)\((?:(?>[^()]+)|(?R))*\)", + "a(bc(d(e)f)gh"), ['(d(e)f)']) + self.assertEqual([m.group() for m in + regex.finditer(r"\((?:[^()]*+|(?0))*\)", "a(b(c(de)fg)h")], + ['(c(de)fg)']) + + # Hg issue 32. + self.assertEqual(regex.search("a(bc)d", "abcd", regex.I | + regex.V1).group(0), "abcd") + + # Hg issue 33. + self.assertEqual(regex.search("([\da-f:]+)$", "E", regex.I | + regex.V1).group(0), "E") + self.assertEqual(regex.search("([\da-f:]+)$", "e", regex.I | + regex.V1).group(0), "e") + + # Hg issue 34. + self.assertEqual(regex.search("^(?=ab(de))(abd)(e)", "abde").groups(), + ('de', 'abd', 'e')) + + # Hg issue 35. + self.assertEqual(bool(regex.match(r"\ ", " ", flags=regex.X)), True) + + # Hg issue 36. + self.assertEqual(regex.search(r"^(a|)\1{2}b", "b").group(0, 1), ('b', + '')) + + # Hg issue 37. + self.assertEqual(regex.search("^(a){0,0}", "abc").group(0, 1), ('', + None)) + + # Hg issue 38. + self.assertEqual(regex.search("(?>.*/)b", "a/b").group(0), "a/b") + + # Hg issue 39. + self.assertEqual(regex.search(r"(?V0)((?i)blah)\s+\1", + "blah BLAH").group(0, 1), ("blah BLAH", "blah")) + self.assertEqual(regex.search(r"(?V1)((?i)blah)\s+\1", "blah BLAH"), + None) + + # Hg issue 40. + self.assertEqual(regex.search(r"(\()?[^()]+(?(1)\)|)", + "(abcd").group(0), "abcd") + + # Hg issue 42. + self.assertEqual(regex.search("(a*)*", "a").span(1), (1, 1)) + self.assertEqual(regex.search("(a*)*", "aa").span(1), (2, 2)) + self.assertEqual(regex.search("(a*)*", "aaa").span(1), (3, 3)) + + # Hg issue 43. + self.assertEqual(regex.search("a(?#xxx)*", "aaa").group(), "aaa") + + # Hg issue 44. + self.assertEqual(regex.search("(?=abc){3}abc", "abcabcabc").span(), (0, + 3)) + + # Hg issue 45. + self.assertEqual(regex.search("^(?:a(?:(?:))+)+", "a").span(), (0, 1)) + self.assertEqual(regex.search("^(?:a(?:(?:))+)+", "aa").span(), (0, 2)) + + # Hg issue 46. + self.assertEqual(regex.search("a(?x: b c )d", "abcd").group(0), "abcd") + + # Hg issue 47. + self.assertEqual(regex.search("a#comment\n*", "aaa", + flags=regex.X).group(0), "aaa") + + # Hg issue 48. + self.assertEqual(regex.search(r"(?V1)(a(?(1)\1)){1}", + "aaaaaaaaaa").span(0, 1), ((0, 1), (0, 1))) + self.assertEqual(regex.search(r"(?V1)(a(?(1)\1)){2}", + "aaaaaaaaaa").span(0, 1), ((0, 3), (1, 3))) + self.assertEqual(regex.search(r"(?V1)(a(?(1)\1)){3}", + "aaaaaaaaaa").span(0, 1), ((0, 6), (3, 6))) + self.assertEqual(regex.search(r"(?V1)(a(?(1)\1)){4}", + "aaaaaaaaaa").span(0, 1), ((0, 10), (6, 10))) + + # Hg issue 49. + self.assertEqual(regex.search("(?V1)(a)(?<=b(?1))", "baz").group(0), + "a") + + # Hg issue 50. + self.assertEqual(regex.findall(ur'(?fi)\L<keywords>', + u'POST, Post, post, po\u017Ft, po\uFB06, and po\uFB05', + keywords=['post','pos']), [u'POST', u'Post', u'post', u'po\u017Ft', + u'po\uFB06', u'po\uFB05']) + self.assertEqual(regex.findall(ur'(?fi)pos|post', + u'POST, Post, post, po\u017Ft, po\uFB06, and po\uFB05'), [u'POS', + u'Pos', u'pos', u'po\u017F', u'po\uFB06', u'po\uFB05']) + self.assertEqual(regex.findall(ur'(?fi)post|pos', + u'POST, Post, post, po\u017Ft, po\uFB06, and po\uFB05'), [u'POST', + u'Post', u'post', u'po\u017Ft', u'po\uFB06', u'po\uFB05']) + self.assertEqual(regex.findall(ur'(?fi)post|another', + u'POST, Post, post, po\u017Ft, po\uFB06, and po\uFB05'), [u'POST', + u'Post', u'post', u'po\u017Ft', u'po\uFB06', u'po\uFB05']) + + # Hg issue 51. + self.assertEqual(regex.search("(?V1)((a)(?1)|(?2))", "a").group(0, 1, + 2), ('a', 'a', None)) + + # Hg issue 52. + self.assertEqual(regex.search(r"(?V1)(\1xx|){6}", "xx").span(0, 1), + ((0, 2), (2, 2))) + + # Hg issue 53. + self.assertEqual(regex.search("(a|)+", "a").group(0, 1), ("a", "")) + + # Hg issue 54. + self.assertEqual(regex.search(r"(a|)*\d", "a" * 80), None) + + # Hg issue 55. + self.assertEqual(regex.search("^(?:a?b?)*$", "ac"), None) + + # Hg issue 58. + self.assertRaisesRegex(regex.error, self.UNDEF_CHAR_NAME, lambda: + regex.compile("\\N{1}")) + + # Hg issue 59. + self.assertEqual(regex.search("\\Z", "a\na\n").span(0), (4, 4)) + + # Hg issue 60. + self.assertEqual(regex.search("(q1|.)*(q2|.)*(x(a|bc)*y){2,}", + "xayxay").group(0), "xayxay") + + # Hg issue 61. + self.assertEqual(regex.search("(?i)[^a]", "A"), None) + + # Hg issue 63. + self.assertEqual(regex.search(u"(?iu)[[:ascii:]]", u"\N{KELVIN SIGN}"), + None) + + # Hg issue 66. + self.assertEqual(regex.search("((a|b(?1)c){3,5})", "baaaaca").group(0, + 1, 2), ('aaaa', 'aaaa', 'a')) + + # Hg issue 71. + self.assertEqual(regex.findall(r"(?<=:\S+ )\w+", ":9 abc :10 def"), + ['abc', 'def']) + self.assertEqual(regex.findall(r"(?<=:\S* )\w+", ":9 abc :10 def"), + ['abc', 'def']) + self.assertEqual(regex.findall(r"(?<=:\S+? )\w+", ":9 abc :10 def"), + ['abc', 'def']) + self.assertEqual(regex.findall(r"(?<=:\S*? )\w+", ":9 abc :10 def"), + ['abc', 'def']) + + # Hg issue 73. + self.assertEqual(regex.search(r"(?:fe)?male", "female").group(), + "female") + self.assertEqual([m.group() for m in + regex.finditer(r"(fe)?male: h(?(1)(er)|(is)) (\w+)", + "female: her dog; male: his cat. asdsasda")], ['female: her dog', + 'male: his cat']) + + # Hg issue 78. + self.assertEqual(regex.search(r'(?<rec>\((?:[^()]++|(?&rec))*\))', + 'aaa(((1+0)+1)+1)bbb').captures('rec'), ['(1+0)', '((1+0)+1)', + '(((1+0)+1)+1)']) + + # Hg issue 80. + self.assertRaisesRegex(regex.error, self.BAD_ESCAPE, lambda: + regex.sub('x', '\\', 'x'), ) + + # Hg issue 82. + fz = "(CAGCCTCCCATTTCAGAATATACATCC){1<e<=2}" + seq = "tcagacgagtgcgttgtaaaacgacggccagtCAGCCTCCCATTCAGAATATACATCCcgacggccagttaaaaacaatgccaaggaggtcatagctgtttcctgccagttaaaaacaatgccaaggaggtcatagctgtttcctgacgcactcgtctgagcgggctggcaagg" + self.assertEqual(regex.search(fz, seq, regex.BESTMATCH)[0], + "tCAGCCTCCCATTCAGAATATACATCC") + + # Hg issue 83. + self.assertEqual(regex.findall(r"c..+/c", "cA/c\ncAb/c"), ['cAb/c']) + + # Hg issue 85. + self.assertEqual(repr(regex.sub(ur"(?u)(\w+)", ur"[\1]", + u'\u0905\u0928\u094d\u200d\u0928 \u0d28\u0d4d\u200d \u0915\u093f\u0928', + regex.WORD)), + repr(u'[\u0905\u0928\u094d\u200d\u0928] [\u0d28\u0d4d\u200d] [\u0915\u093f\u0928]')) + + # Hg issue 88. + self.assertEqual(regex.match(r".*a.*ba.*aa", "ababba"), None) + + # Hg issue 87. + self.assertEqual(regex.match(r'(?<x>a(?<x>b))', "ab").spans("x"), [(1, + 2), (0, 2)]) + + # Hg issue 91. + # Check that the replacement cache works. + self.assertEqual(regex.sub(r'(-)', lambda m: m.expand(r'x'), 'a-b-c'), + 'axbxc') + + # Hg issue 94. + rx = regex.compile(r'\bt(est){i<2}', flags=regex.V1) + self.assertEqual(rx.search("Some text"), None) + self.assertEqual(rx.findall("Some text"), []) + + # Hg issue 95. + self.assertRaisesRegex(regex.error, self.MULTIPLE_REPEAT, lambda: + regex.compile(r'.???')) + + # Hg issue 97. + self.assertEqual(regex.escape(u'foo!?'), u'foo\\!\\?') + self.assertEqual(regex.escape(u'foo!?', special_only=True), u'foo!\\?') + + self.assertEqual(regex.escape('foo!?'), 'foo\\!\\?') + self.assertEqual(regex.escape('foo!?', special_only=True), + 'foo!\\?') + + # Hg issue 100. + self.assertEqual(regex.search('^([^z]*(?:WWWi|W))?$', + 'WWWi').groups(), ('WWWi', )) + self.assertEqual(regex.search('^([^z]*(?:WWWi|w))?$', + 'WWWi').groups(), ('WWWi', )) + self.assertEqual(regex.search('^([^z]*?(?:WWWi|W))?$', + 'WWWi').groups(), ('WWWi', )) + + # Hg issue 101. + pat = regex.compile(r'xxx', flags=regex.FULLCASE | regex.UNICODE) + self.assertEqual([x.group() for x in pat.finditer('yxxx')], ['xxx']) + self.assertEqual(pat.findall('yxxx'), ['xxx']) + + raw = 'yxxx' + self.assertEqual([x.group() for x in pat.finditer(raw)], ['xxx']) + self.assertEqual(pat.findall(raw), ['xxx']) + + pat = regex.compile(r'xxx', flags=regex.FULLCASE | regex.IGNORECASE | + regex.UNICODE) + self.assertEqual([x.group() for x in pat.finditer('yxxx')], ['xxx']) + self.assertEqual(pat.findall('yxxx'), ['xxx']) + + raw = 'yxxx' + self.assertEqual([x.group() for x in pat.finditer(raw)], ['xxx']) + self.assertEqual(pat.findall(raw), ['xxx']) + + # Hg issue 106. + self.assertEqual(regex.sub('(?V0).*', 'x', 'test'), 'x') + self.assertEqual(regex.sub('(?V1).*', 'x', 'test'), 'xx') + + self.assertEqual(regex.sub('(?V0).*?', '|', 'test'), '|t|e|s|t|') + self.assertEqual(regex.sub('(?V1).*?', '|', 'test'), '|||||||||') + + # Hg issue 112. + self.assertEqual(regex.sub(r'^(@)\n(?!.*?@)(.*)', + r'\1\n==========\n\2', '@\n', flags=regex.DOTALL), '@\n==========\n') + + # Hg issue 109. + self.assertEqual(regex.match(r'(?:cats|cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.match(r'(?e)(?:cats|cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.match(r'(?b)(?:cats|cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + + self.assertEqual(regex.match(r'(?:cat){e<=1}', 'caz').fuzzy_counts, + (1, 0, 0)) + self.assertEqual(regex.match(r'(?e)(?:cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.match(r'(?b)(?:cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + + self.assertEqual(regex.match(r'(?:cats){e<=2}', 'c ats').fuzzy_counts, + (1, 1, 0)) + self.assertEqual(regex.match(r'(?e)(?:cats){e<=2}', + 'c ats').fuzzy_counts, (0, 1, 0)) + self.assertEqual(regex.match(r'(?b)(?:cats){e<=2}', + 'c ats').fuzzy_counts, (0, 1, 0)) + + self.assertEqual(regex.match(r'(?:cats){e<=2}', + 'c a ts').fuzzy_counts, (0, 2, 0)) + self.assertEqual(regex.match(r'(?e)(?:cats){e<=2}', + 'c a ts').fuzzy_counts, (0, 2, 0)) + self.assertEqual(regex.match(r'(?b)(?:cats){e<=2}', + 'c a ts').fuzzy_counts, (0, 2, 0)) + + self.assertEqual(regex.match(r'(?:cats){e<=1}', 'c ats').fuzzy_counts, + (0, 1, 0)) + self.assertEqual(regex.match(r'(?e)(?:cats){e<=1}', + 'c ats').fuzzy_counts, (0, 1, 0)) + self.assertEqual(regex.match(r'(?b)(?:cats){e<=1}', + 'c ats').fuzzy_counts, (0, 1, 0)) + + # Hg issue 115. + self.assertEqual(regex.findall(r'\bof ([a-z]+) of \1\b', + 'To make use of one of these modules'), []) + + # Hg issue 125. + self.assertEqual(regex.sub(r'x', r'\g<0>', 'x'), 'x') + + # Unreported issue: no such builtin as 'ascii' in Python 2. + self.assertEqual(bool(regex.match(r'a', 'a', regex.DEBUG)), True) + + # Hg issue 131. + self.assertEqual(regex.findall(r'(?V1)[[b-e]--cd]', 'abcdef'), ['b', + 'e']) + self.assertEqual(regex.findall(r'(?V1)[b-e--cd]', 'abcdef'), ['b', + 'e']) + self.assertEqual(regex.findall(r'(?V1)[[bcde]--cd]', 'abcdef'), ['b', + 'e']) + self.assertEqual(regex.findall(r'(?V1)[bcde--cd]', 'abcdef'), ['b', + 'e']) + + # Hg issue 132. + self.assertRaisesRegex(regex.error, '^unknown property at position 4$', + lambda: regex.compile(ur'\p{}')) + + # Issue 23692. + self.assertEqual(regex.match('(?:()|(?(1)()|z)){2}(?(2)a|z)', + 'a').group(0, 1, 2), ('a', '', '')) + self.assertEqual(regex.match('(?:()|(?(1)()|z)){0,2}(?(2)a|z)', + 'a').group(0, 1, 2), ('a', '', '')) + + # Hg issue 137: Posix character class :punct: does not seem to be + # supported. + + # Posix compatibility as recommended here: + # http://www.unicode.org/reports/tr18/#Compatibility_Properties + + # Posix in Unicode. + chars = u''.join(unichr(c) for c in range(0x10000)) + + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:alnum:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)[\p{Alpha}\p{PosixDigit}]+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:alpha:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)\p{Alpha}+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:ascii:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)[\p{InBasicLatin}]+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:blank:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)[\p{gc=Space_Separator}\t]+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:cntrl:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)\p{gc=Control}+''', chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:digit:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)[0-9]+''', chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:graph:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)[^\p{Space}\p{gc=Control}\p{gc=Surrogate}\p{gc=Unassigned}]+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:lower:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)\p{Lower}+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:print:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?uV1)[\p{Graph}\p{Blank}--\p{Cntrl}]+''', chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:punct:]]+''', + chars))), + repr(u''.join(regex.findall(ur'''(?uV1)[\p{gc=Punctuation}\p{gc=Symbol}--\p{Alpha}]+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:space:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)\p{Whitespace}+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:upper:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)\p{Upper}+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:word:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)[\p{Alpha}\p{gc=Mark}\p{Digit}\p{gc=Connector_Punctuation}\p{Join_Control}]+''', + chars)))) + self.assertEqual(repr(u''.join(regex.findall(ur'''(?u)[[:xdigit:]]+''', + chars))), repr(u''.join(regex.findall(ur'''(?u)[0-9A-Fa-f]+''', + chars)))) + + # Posix in ASCII. + chars = ''.join(chr(c) for c in range(0x100)) + + self.assertEqual(repr(''.join(regex.findall(r'''[[:alnum:]]+''', + chars))), repr(''.join(regex.findall(r'''[\p{Alpha}\p{PosixDigit}]+''', + chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:alpha:]]+''', + chars))), repr(''.join(regex.findall(r'''\p{Alpha}+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:ascii:]]+''', + chars))), repr(''.join(regex.findall(r'''[\x00-\x7F]+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:blank:]]+''', + chars))), repr(''.join(regex.findall(r'''[\p{gc=Space_Separator}\t]+''', + chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:cntrl:]]+''', + chars))), repr(''.join(regex.findall(r'''\p{gc=Control}+''', + chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:digit:]]+''', + chars))), repr(''.join(regex.findall(r'''[0-9]+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:graph:]]+''', + chars))), repr(''.join(regex.findall(r'''[^\p{Space}\p{gc=Control}\p{gc=Surrogate}\p{gc=Unassigned}]+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:lower:]]+''', + chars))), repr(''.join(regex.findall(r'''\p{Lower}+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:print:]]+''', + chars))), repr(''.join(regex.findall(r'''(?V1)[\p{Graph}\p{Blank}--\p{Cntrl}]+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:punct:]]+''', + chars))), repr(''.join(regex.findall(r'''(?V1)[\p{gc=Punctuation}\p{gc=Symbol}--\p{Alpha}]+''', + chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:space:]]+''', + chars))), repr(''.join(regex.findall(r'''\p{Whitespace}+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:upper:]]+''', + chars))), repr(''.join(regex.findall(r'''\p{Upper}+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:word:]]+''', + chars))), repr(''.join(regex.findall(r'''[\p{Alpha}\p{gc=Mark}\p{Digit}\p{gc=Connector_Punctuation}\p{Join_Control}]+''', chars)))) + self.assertEqual(repr(''.join(regex.findall(r'''[[:xdigit:]]+''', + chars))), repr(''.join(regex.findall(r'''[0-9A-Fa-f]+''', chars)))) + + # Hg issue 138: grapheme anchored search not working properly. + self.assertEqual(repr(regex.search(ur'(?u)\X$', u'ab\u2103').group()), + repr(u'\u2103')) + + # Hg issue 139: Regular expression with multiple wildcards where first + # should match empty string does not always work. + self.assertEqual(regex.search("([^L]*)([^R]*R)", "LtR").groups(), ('', + 'LtR')) + + # Hg issue 140: Replace with REVERSE and groups has unexpected + # behavior. + self.assertEqual(regex.sub(r'(.)', r'x\1y', 'ab'), 'xayxby') + self.assertEqual(regex.sub(r'(?r)(.)', r'x\1y', 'ab'), 'xayxby') + + # Hg issue 141: Crash on a certain partial match. + self.assertEqual(regex.fullmatch('(a)*abc', 'ab', + partial=True).span(), (0, 2)) + self.assertEqual(regex.fullmatch('(a)*abc', 'ab', + partial=True).partial, True) + + # Hg Issue #143: Partial matches have incorrect span if prefix is '.' + # wildcard. + self.assertEqual(regex.search('OXRG', 'OOGOX', partial=True).span(), + (3, 5)) + self.assertEqual(regex.search('.XRG', 'OOGOX', partial=True).span(), + (3, 5)) + self.assertEqual(regex.search('.{1,3}XRG', 'OOGOX', + partial=True).span(), (1, 5)) + + # Hg issue 144: Latest version problem with matching 'R|R'. + self.assertEqual(regex.match('R|R', 'R').span(), (0, 1)) + + # Hg issue 146: Forced-fail (?!) works improperly in conditional. + self.assertEqual(regex.match(r'(.)(?(1)(?!))', 'xy'), None) + + # Groups cleared after failure. + self.assertEqual(regex.findall(r'(y)?(\d)(?(1)\b\B)', 'ax1y2z3b'), + [('', '1'), ('', '2'), ('', '3')]) + self.assertEqual(regex.findall(r'(y)?+(\d)(?(1)\b\B)', 'ax1y2z3b'), + [('', '1'), ('', '2'), ('', '3')]) + + # Hg issue 147: Fuzzy match can return match points beyond buffer end. + self.assertEqual([m.span() for m in + regex.finditer(r'(?i)(?:error){e}', 'regex failure')], [(0, 5), (5, + 10), (10, 13), (13, 13)]) + self.assertEqual([m.span() for m in + regex.finditer(r'(?fi)(?:error){e}', 'regex failure')], [(0, 5), (5, + 10), (10, 13), (13, 13)]) + + # Hg issue 151: Request: \K. + self.assertEqual(regex.search(r'(ab\Kcd)', 'abcd').group(0, 1), ('cd', + 'abcd')) + self.assertEqual(regex.findall(r'\w\w\K\w\w', 'abcdefgh'), ['cd', + 'gh']) + self.assertEqual(regex.findall(r'(\w\w\K\w\w)', 'abcdefgh'), ['abcd', + 'efgh']) + + self.assertEqual(regex.search(r'(?r)(ab\Kcd)', 'abcd').group(0, 1), + ('ab', 'abcd')) + self.assertEqual(regex.findall(r'(?r)\w\w\K\w\w', 'abcdefgh'), ['ef', + 'ab']) + self.assertEqual(regex.findall(r'(?r)(\w\w\K\w\w)', 'abcdefgh'), + ['efgh', 'abcd']) + + # Hg issue 153: Request: (*SKIP). + self.assertEqual(regex.search(r'12(*FAIL)|3', '123')[0], '3') + self.assertEqual(regex.search(r'(?r)12(*FAIL)|3', '123')[0], '3') + + self.assertEqual(regex.search(r'\d+(*PRUNE)\d', '123'), None) + self.assertEqual(regex.search(r'\d+(?=(*PRUNE))\d', '123')[0], '123') + self.assertEqual(regex.search(r'\d+(*PRUNE)bcd|[3d]', '123bcd')[0], + '123bcd') + self.assertEqual(regex.search(r'\d+(*PRUNE)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'\d+?(*PRUNE)bcd|[3d]', '123bcd')[0], + '3bcd') + self.assertEqual(regex.search(r'\d+?(*PRUNE)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'\d++(?<=3(*PRUNE))zzd|[4d]$', + '123zzd')[0], '123zzd') + self.assertEqual(regex.search(r'\d++(?<=3(*PRUNE))zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'\d++(?<=(*PRUNE)3)zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'\d++(?<=2(*PRUNE)3)zzd|[3d]$', + '124zzd')[0], 'd') + + self.assertEqual(regex.search(r'(?r)\d(*PRUNE)\d+', '123'), None) + self.assertEqual(regex.search(r'(?r)\d(?<=(*PRUNE))\d+', '123')[0], + '123') + self.assertEqual(regex.search(r'(?r)\d+(*PRUNE)bcd|[3d]', + '123bcd')[0], '123bcd') + self.assertEqual(regex.search(r'(?r)\d+(*PRUNE)bcd|[3d]', + '123zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=3(*PRUNE))zzd|[4d]$', + '123zzd')[0], '123zzd') + self.assertEqual(regex.search(r'(?r)\d++(?<=3(*PRUNE))zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=(*PRUNE)3)zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=2(*PRUNE)3)zzd|[3d]$', + '124zzd')[0], 'd') + + self.assertEqual(regex.search(r'\d+(*SKIP)bcd|[3d]', '123bcd')[0], + '123bcd') + self.assertEqual(regex.search(r'\d+(*SKIP)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'\d+?(*SKIP)bcd|[3d]', '123bcd')[0], + '3bcd') + self.assertEqual(regex.search(r'\d+?(*SKIP)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'\d++(?<=3(*SKIP))zzd|[4d]$', + '123zzd')[0], '123zzd') + self.assertEqual(regex.search(r'\d++(?<=3(*SKIP))zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'\d++(?<=(*SKIP)3)zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'\d++(?<=2(*SKIP)3)zzd|[3d]$', + '124zzd')[0], 'd') + + self.assertEqual(regex.search(r'(?r)\d+(*SKIP)bcd|[3d]', '123bcd')[0], + '123bcd') + self.assertEqual(regex.search(r'(?r)\d+(*SKIP)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=3(*SKIP))zzd|[4d]$', + '123zzd')[0], '123zzd') + self.assertEqual(regex.search(r'(?r)\d++(?<=3(*SKIP))zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=(*SKIP)3)zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=2(*SKIP)3)zzd|[3d]$', + '124zzd')[0], 'd') + + # Hg issue 152: Request: Request: (?(DEFINE)...). + self.assertEqual(regex.search(r'(?(DEFINE)(?<quant>\d+)(?<item>\w+))(?&quant) (?&item)', + '5 elephants')[0], '5 elephants') + + # Hg issue 150: Have an option for POSIX-compatible longest match of + # alternates. + self.assertEqual(regex.search(r'(?p)\d+(\w(\d*)?|[eE]([+-]\d+))', + '10b12')[0], '10b12') + self.assertEqual(regex.search(r'(?p)\d+(\w(\d*)?|[eE]([+-]\d+))', + '10E+12')[0], '10E+12') + + self.assertEqual(regex.search(r'(?p)(\w|ae|oe|ue|ss)', 'ae')[0], 'ae') + self.assertEqual(regex.search(r'(?p)one(self)?(selfsufficient)?', + 'oneselfsufficient')[0], 'oneselfsufficient') + + # Hg issue 156: regression on atomic grouping + self.assertEqual(regex.match('1(?>2)', '12').span(), (0, 2)) + + # Hg issue 157: regression: segfault on complex lookaround + self.assertEqual(regex.match(r'(?V1w)(?=(?=[^A-Z]*+[A-Z])(?=[^a-z]*+[a-z]))(?=\D*+\d)(?=\p{Alphanumeric}*+\P{Alphanumeric})\A(?s:.){8,255}+\Z', + 'AAaa11!!')[0], 'AAaa11!!') + + # Hg issue 158: Group issue with (?(DEFINE)...) + TEST_REGEX = regex.compile(r'''(?smx) +(?(DEFINE) + (?<subcat> + ^,[^,]+, + ) +) + +# Group 2 is defined on this line +^,([^,]+), + +(?:(?!(?&subcat)[\r\n]+(?&subcat)).)+ +''') + + TEST_DATA = ''' +,Cat 1, +,Brand 1, +some +thing +,Brand 2, +other +things +,Cat 2, +,Brand, +Some +thing +''' + + self.assertEqual([m.span(1, 2) for m in + TEST_REGEX.finditer(TEST_DATA)], [((-1, -1), (2, 7)), ((-1, -1), (54, + 59))]) + + # Hg issue 161: Unexpected fuzzy match results + self.assertEqual(regex.search('(abcdefgh){e}', + '******abcdefghijklmnopqrtuvwxyz', regex.BESTMATCH).span(), (6, 14)) + self.assertEqual(regex.search('(abcdefghi){e}', + '******abcdefghijklmnopqrtuvwxyz', regex.BESTMATCH).span(), (6, 15)) + + # Hg issue 163: allow lookarounds in conditionals. + self.assertEqual(regex.match(r'(?:(?=\d)\d+\b|\w+)', '123abc').span(), + (0, 6)) + self.assertEqual(regex.match(r'(?(?=\d)\d+\b|\w+)', '123abc'), None) + self.assertEqual(regex.search(r'(?(?<=love\s)you|(?<=hate\s)her)', + "I love you").span(), (7, 10)) + self.assertEqual(regex.findall(r'(?(?<=love\s)you|(?<=hate\s)her)', + "I love you but I don't hate her either"), ['you', 'her']) + + # Hg issue #180: bug of POSIX matching. + self.assertEqual(regex.search(r'(?p)a*(.*?)', 'aaabbb').group(0, 1), + ('aaabbb', 'bbb')) + self.assertEqual(regex.search(r'(?p)a*(.*)', 'aaabbb').group(0, 1), + ('aaabbb', 'bbb')) + self.assertEqual(regex.sub(r'(?p)a*(.*?)', r'\1', 'aaabbb'), 'bbb') + self.assertEqual(regex.sub(r'(?p)a*(.*)', r'\1', 'aaabbb'), 'bbb') + + def test_subscripted_captures(self): + self.assertEqual(regex.match(r'(?P<x>.)+', + 'abc').expandf('{0} {0[0]} {0[-1]}'), 'abc abc abc') + self.assertEqual(regex.match(r'(?P<x>.)+', + 'abc').expandf('{1} {1[0]} {1[1]} {1[2]} {1[-1]} {1[-2]} {1[-3]}'), + 'c a b c c b a') + self.assertEqual(regex.match(r'(?P<x>.)+', + 'abc').expandf('{x} {x[0]} {x[1]} {x[2]} {x[-1]} {x[-2]} {x[-3]}'), + 'c a b c c b a') + + self.assertEqual(regex.subf(r'(?P<x>.)+', r'{0} {0[0]} {0[-1]}', + 'abc'), 'abc abc abc') + self.assertEqual(regex.subf(r'(?P<x>.)+', + '{1} {1[0]} {1[1]} {1[2]} {1[-1]} {1[-2]} {1[-3]}', 'abc'), + 'c a b c c b a') + self.assertEqual(regex.subf(r'(?P<x>.)+', + '{x} {x[0]} {x[1]} {x[2]} {x[-1]} {x[-2]} {x[-3]}', 'abc'), + 'c a b c c b a') + +if not hasattr(str, "format"): + # Strings don't have the .format method (below Python 2.6). + del RegexTests.test_format + del RegexTests.test_subscripted_captures + +def test_main(): + run_unittest(RegexTests) + +if __name__ == "__main__": + test_main() -- GitLab