From 547e704257952eed1f981f5075297f1a353e0153 Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 09:57:58 +0200 Subject: [PATCH 01/10] [tests] add pytest infrastructure and synthetic COCO data generator conftest.py isolates the global state of cocopp (module globals, testbed settings, rcParams, numpy RNG and error settings, cwd) around each test, seeds numpy, runs each test in a fresh working directory, and redirects package-relative files into a temporary copy of the package such that tests never write into the source tree (the session fails otherwise). Tests needing the online archives are marked 'network' and only run with --run-network, tests running the full pipeline are marked 'slow'. synthdata.py writes deterministic synthetic data (bbob in the current and pre-2016 format, bbob-noisy, bbob-constrained with g-evaluations, bbob-largescale, bbob-mixint, bbob-boxed and bbob-biobj) and a tiny hand-crafted data set with hand-computable runtimes. pipeline.py runs cocopp.main in-process and defines the scenarios shared by integration and regression tests, snapshots.py takes and compares normalized golden-file snapshots of the text output. --- tests/conftest.py | 379 +++++++++++++++++++++++++++++++++++++++++++++ tests/pipeline.py | 130 ++++++++++++++++ tests/snapshots.py | 167 ++++++++++++++++++++ tests/synthdata.py | 298 +++++++++++++++++++++++++++++++++++ 4 files changed, 974 insertions(+) create mode 100644 tests/conftest.py create mode 100644 tests/pipeline.py create mode 100644 tests/snapshots.py create mode 100644 tests/synthdata.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..74a0460 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,379 @@ +"""Shared fixtures for the `cocopp` test suite. + +The package keeps a lot of state in module globals (``genericsettings``, +``testbedsettings.current_testbed``, ``bestalg.bestAlgorithmEntries``, +plotting defaults changed by ``config.config()``, ...) and writes files +next to its own sources (extracted reference algorithms and pickle +caches). The fixtures here + +- force the non-interactive ``Agg`` matplotlib backend, +- redirect all package-relative file access (`toolsdivers.path_in_package`) + to a throw-away copy of the package so that tests never write into the + source tree, +- snapshot and restore the module globals of all ``cocopp`` modules, + the testbed class settings, matplotlib's ``rcParams``, the global numpy + random state and the working directory around *every* test, +- seed numpy's global random number generator before every test, +- provide deterministic synthetic COCO data sets (see `synthdata`). + +Markers: + +- ``slow``: runs the full post-processing pipeline (seconds to minutes). + Deselect with ``-m "not slow"``. +- ``network``: needs internet access to the official COCO data archives. + These tests are skipped unless ``--run-network`` is given. +""" + +import contextlib +import copy +import os +import shutil +import sys +import types +import warnings + +os.environ.setdefault("MPLBACKEND", "Agg") + +import matplotlib + +matplotlib.use("Agg") + +import numpy as np +import pytest + +sys.path.insert(0, os.path.dirname(__file__)) +import synthdata # noqa: E402 + +with warnings.catch_warnings(): + warnings.simplefilter("ignore") + import cocopp # noqa: E402 + from cocopp import testbedsettings, toolsdivers # noqa: E402 + +PACKAGE_DIR = os.path.dirname(os.path.abspath(cocopp.__file__)) +REPO_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SEED = 20240521 + + +def pytest_addoption(parser): + parser.addoption("--run-network", action="store_true", default=False, help="run tests that download data from the official COCO archives") + parser.addoption( + "--update-snapshots", action="store_true", default=False, help="rewrite the golden output snapshots in tests/snapshots instead of comparing" + ) + + +def pytest_configure(config): + config.addinivalue_line("markers", "slow: runs (parts of) the full post-processing pipeline") + config.addinivalue_line("markers", "network: needs access to the official online COCO data archives") + + +def pytest_collection_modifyitems(config, items): + if config.getoption("--run-network"): + return + skip = pytest.mark.skip(reason="needs --run-network") + for item in items: + if "network" in item.keywords: + item.add_marker(skip) + + +# -------------------------------------------------------------------------- +# keep the source tree clean +# -------------------------------------------------------------------------- + + +@pytest.fixture(scope="session") +def package_copy(tmp_path_factory): + """a throw-away copy of the installed `cocopp` package folder. + + Its parent folder can be put on ``PYTHONPATH`` for subprocesses. + """ + root = tmp_path_factory.mktemp("pkgcopy") + shutil.copytree( + PACKAGE_DIR, + root / "cocopp", + ignore=shutil.ignore_patterns("__pycache__", ".extracted_*", "*.pickle"), + ) + return root / "cocopp" + + +@pytest.fixture(scope="session", autouse=True) +def _redirect_package_files(package_copy): + """make `toolsdivers.path_in_package` point into `package_copy`. + + The reference algorithms in ``refalgs/`` are extracted (and pickled) + next to the archives, i.e. inside the package, on first use. + """ + original = toolsdivers.path_in_package + + def path_in_package(sub_path=""): + return os.path.join(str(package_copy), sub_path) + + toolsdivers.path_in_package = path_in_package + yield + toolsdivers.path_in_package = original + + +def _listing(folder): + return set(os.listdir(folder)) if os.path.isdir(folder) else set() + + +@pytest.fixture(scope="session", autouse=True) +def _assert_source_tree_untouched(): + """fail the session if tests created files in the package or repository root. + + Tests run in a temporary working directory (see `_isolate_global_state`) + and package files are redirected (see `_redirect_package_files`), hence + new files would indicate a test, or a code path, writing to an + unexpected place. + """ + folders = [PACKAGE_DIR, os.path.join(PACKAGE_DIR, "refalgs"), REPO_DIR] + before = {f: _listing(f) for f in folders} + yield + new = {f: sorted(_listing(f) - before[f] - {".pytest_cache"}) for f in folders} + new = {f: n for f, n in new.items() if n} + assert not new, "tests created files in the source tree: %s" % new + + +# -------------------------------------------------------------------------- +# global state isolation +# -------------------------------------------------------------------------- + +_SKIP_TYPES = (types.ModuleType, types.FunctionType, types.BuiltinFunctionType, type) +# containers whose content is restored in place, other objects are only rebound +_CONTAINERS = (dict, list, set, np.ndarray) +_NO_COPY = object() + + +def _snapshot_module(mod): + """return ``{name: (original_object, deep_copy_or__NO_COPY)}``. + + Only containers are deep-copied. Other objects (e.g. the archives or + the current testbed) are only remembered by reference: they are + often shared between modules (``from .archiving import + official_archives``) and restoring their content would break the + identity of objects referenced from elsewhere. + """ + state = {} + for key, val in vars(mod).items(): + if key.startswith("__") or isinstance(val, _SKIP_TYPES): + continue + saved = _NO_COPY + if isinstance(val, _CONTAINERS): + try: + saved = copy.deepcopy(val) + except Exception: + pass + state[key] = (val, saved) + return state + + +def _restore_content(obj, saved): + """restore the content of container `obj` in place from its deep copy `saved`. + + Like this, the identity of containers that are shared between modules + (e.g. via ``from module import name``) is preserved. + """ + if saved is _NO_COPY or type(obj) is not type(saved): + return + if isinstance(obj, np.ndarray): + if obj.shape == saved.shape and obj.flags.writeable: + obj[...] = saved + elif isinstance(obj, dict): + obj.clear() + obj.update(saved) + elif isinstance(obj, list): + obj[:] = saved + elif isinstance(obj, set): + obj.clear() + obj.update(saved) + + +def _restore_module(mod, state): + for key in [k for k, v in vars(mod).items() if not k.startswith("__") and k not in state and not isinstance(v, _SKIP_TYPES)]: + delattr(mod, key) # added during the test + for key, (original, saved) in state.items(): + _restore_content(original, saved) + setattr(mod, key, original) + + +def _testbed_classes(): + return [c for c in vars(testbedsettings).values() if isinstance(c, type) and issubclass(c, testbedsettings.Testbed)] + + +@contextlib.contextmanager +def isolated_cocopp_state(workdir, seed=SEED): + """context manager to snapshot and restore the global state of `cocopp`. + + Restores module globals of all ``cocopp`` modules, the testbed class + settings, matplotlib's ``rcParams``, numpy's random state and error + settings, and the working directory. Within the context, numpy's + global RNG is seeded with `seed`, `cocopp` never opens a web browser, + and the working directory is `workdir`, because the package writes + output (e.g. ``ppdata``) relative to the current folder. + """ + modules = {name: mod for name, mod in sys.modules.items() if name == "cocopp" or name.startswith("cocopp.")} + states = {name: _snapshot_module(mod) for name, mod in modules.items()} + class_settings = {cls: (cls.__dict__["settings"], copy.deepcopy(cls.__dict__["settings"])) for cls in _testbed_classes() if "settings" in cls.__dict__} + rc = matplotlib.rcParams.copy() + rng_state = np.random.get_state() + np_err = np.geterr() + cwd = os.getcwd() + cocopp.genericsettings.interactive_mode = False # never open a web browser + np.random.seed(seed) + os.chdir(str(workdir)) + try: + yield + finally: + os.chdir(cwd) + import matplotlib.pyplot as plt + + plt.close("all") + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + matplotlib.rcParams.update(rc) + np.random.set_state(rng_state) + np.seterr(**np_err) + for cls, (original, saved) in class_settings.items(): + _restore_content(original, saved) + cls.settings = original + for name, state in states.items(): + _restore_module(modules[name], state) + + +@pytest.fixture(autouse=True) +def _isolate_global_state(tmp_path_factory): + """run every test in `isolated_cocopp_state` within a fresh working directory""" + with isolated_cocopp_state(tmp_path_factory.mktemp("cwd")): + yield + + +# -------------------------------------------------------------------------- +# synthetic data +# -------------------------------------------------------------------------- + + +@pytest.fixture(scope="session") +def synth_root(tmp_path_factory): + return tmp_path_factory.mktemp("synthdata") + + +@pytest.fixture(scope="session") +def bbob_alg_a(synth_root): + """full synthetic ``bbob`` experiment (24 functions, 5 dimensions, 15 instances)""" + return synthdata.write_bbob(str(synth_root / "bbob"), "SYNTH-A", seed=1) + + +@pytest.fixture(scope="session") +def bbob_alg_b(synth_root): + """a second, faster synthetic ``bbob`` "algorithm" on the same problems""" + return synthdata.write_bbob(str(synth_root / "bbob"), "SYNTH-B", seed=2, speed=2.0) + + +@pytest.fixture(scope="session") +def bbob_alg_c(synth_root): + """a third, slower synthetic ``bbob`` "algorithm" """ + return synthdata.write_bbob(str(synth_root / "bbob"), "SYNTH-C", seed=3, speed=0.6) + + +@pytest.fixture(scope="session") +def bbob_small(synth_root): + """a small synthetic ``bbob`` experiment: f1, f2, f8, f15 in 2-D and 5-D""" + return synthdata.write_bbob(str(synth_root / "small"), "SMALL", functions=(1, 2, 8, 15), dims=(2, 5), seed=11) + + +@pytest.fixture(scope="session") +def bbob_small_b(synth_root): + """a second "algorithm" on the problems of `bbob_small`, twice as fast""" + return synthdata.write_bbob(str(synth_root / "small"), "SMALL-B", functions=(1, 2, 8, 15), dims=(2, 5), seed=13, speed=2.0) + + +@pytest.fixture(scope="session") +def bbob_old_format(synth_root): + """small synthetic data in the pre-2016 layout without suite/data_format""" + return synthdata.write_bbob(str(synth_root / "old"), "OLDFMT", functions=(1, 2), dims=(2, 5), seed=12, old_format=True) + + +@pytest.fixture(scope="session") +def biobj_alg(synth_root): + """full synthetic ``bbob-biobj`` experiment (55 functions, 2/3/5-D, 10 instances)""" + return synthdata.write_biobj(str(synth_root / "biobj"), "SYNTH-BI", seed=21) + + +@pytest.fixture(scope="session") +def biobj_alg_b(synth_root): + return synthdata.write_biobj(str(synth_root / "biobj"), "SYNTH-BI-B", seed=22, speed=2.0) + + +@pytest.fixture(scope="session") +def bbob_noisy(synth_root): + return synthdata.write_bbob(str(synth_root / "noisy"), "SYNTH-NOISY", functions=range(101, 131), dims=(2, 5, 20), seed=31, suite="bbob-noisy") + + +@pytest.fixture(scope="session") +def bbob_constrained(synth_root): + """constrained data with g-evaluations, see `synthdata.write_bbob`""" + return synthdata.write_bbob(str(synth_root / "cons"), "SYNTH-CONS", functions=range(1, 55), dims=(2, 5), seed=32, suite="bbob-constrained") + + +@pytest.fixture(scope="session") +def bbob_largescale(synth_root): + return synthdata.write_bbob(str(synth_root / "ls"), "SYNTH-LS", dims=(20, 40), seed=33, suite="bbob-largescale", budget_multiplier=50) + + +@pytest.fixture(scope="session") +def bbob_mixint(synth_root): + return synthdata.write_bbob(str(synth_root / "mixint"), "SYNTH-MIXINT", dims=(5, 10), seed=34, suite="bbob-mixint") + + +@pytest.fixture(scope="session") +def bbob_boxed(synth_root): + return synthdata.write_bbob(str(synth_root / "boxed"), "SYNTH-BOXED", dims=(2, 5), seed=35, suite="bbob-boxed") + + +@pytest.fixture(scope="session") +def pipeline_run(request, tmp_path_factory): + """return a function ``run(scenario_name)`` returning a cached `pipeline.PipelineRun`. + + Scenarios are defined in `pipeline.SCENARIOS`; each is run at most + once per test session. + """ + import pipeline + + cache = {} + + def run(name): + if name not in cache: + fixtures, options, background = pipeline.SCENARIOS[name] + folders = [request.getfixturevalue(f) for f in fixtures] + background = [request.getfixturevalue(f) for f in background] + + def setup(): + if background: + cocopp.genericsettings.background = {None: background} + + args = " ".join(list(options) + folders) + cache[name] = pipeline.run_main(args, tmp_path_factory.mktemp("run-" + name), setup=setup) + return cache[name] + + return run + + +@pytest.fixture +def quiet(): + """silence the (many) warnings the package emits while loading data""" + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + yield + + +def load(path): + """load a `DataSetList` without the package's chatter""" + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + return cocopp.pproc.DataSetList(str(path)) + + +@pytest.fixture +def dsl_small(bbob_small): + """freshly loaded `DataSetList` of `bbob_small` (8 data sets)""" + testbedsettings.reset_current_testbed() + return load(bbob_small) diff --git a/tests/pipeline.py b/tests/pipeline.py new file mode 100644 index 0000000..6808f5b --- /dev/null +++ b/tests/pipeline.py @@ -0,0 +1,130 @@ +"""Helpers to run the full post-processing pipeline (`cocopp.main`) in tests.""" + +import contextlib +import fnmatch +import io +import os +import re +import time +import warnings + +import numpy as np + +import cocopp + +from conftest import SEED, isolated_cocopp_state + +# the output sub-folder names end with a time stamp like _101509h5934 +TIMESTAMP_RE = re.compile(r"_\d{6}h\d{4}") + + +class PipelineRun: + """result of a single `cocopp.main` call, see `run_main`""" + + def __init__(self, workdir, outdir, result, stdout, duration): + self.workdir = str(workdir) + self.outdir = os.path.join(self.workdir, outdir) + self.result = result + self.stdout = stdout + self.duration = duration + + @property + def files(self): + """sorted list of all output files relative to `outdir`""" + return sorted( + os.path.relpath(os.path.join(root, f), self.outdir).replace(os.sep, "/") + for root, _, files in os.walk(self.outdir) + for f in files + ) + + def glob(self, pattern): + """output files matching `pattern`, e.g. ``'*/pptable_*.tex'``, with time stamps removed""" + return [f for f in self.files if fnmatch.fnmatch(TIMESTAMP_RE.sub("", f), pattern)] + + @property + def subfolders(self): + """the output sub-folders without time stamps""" + return sorted(TIMESTAMP_RE.sub("", d) for d in os.listdir(self.outdir) if os.path.isdir(os.path.join(self.outdir, d))) + + def path(self, relative_name): + """absolute path of an output file given without time stamps""" + matches = [f for f in self.files if TIMESTAMP_RE.sub("", f) == relative_name] + assert len(matches) == 1, (relative_name, matches) + return os.path.join(self.outdir, matches[0]) + + def read(self, relative_name): + with open(self.path(relative_name), encoding="utf-8", errors="replace") as f: + return f.read() + + +# modules whose text output depends on the state of numpy's global random number generator +RESEEDED_FUNCTIONS = [("cocopp.pptable", "main"), ("cocopp.compall.pptables", "main"), ("cocopp.compall.pprldmany", "main")] + + +@contextlib.contextmanager +def reseeded_entry_points(functions=RESEEDED_FUNCTIONS, seed=SEED): + """reseed numpy's global RNG whenever one of `functions` is called. + + Without this, the bootstrapped dispersion values in the tables depend + on ``PYTHONHASHSEED``: `rungenericmany` iterates over function groups in + `set` order before, consuming a data dependent number of random numbers. + """ + import importlib + + originals = [] + for module_name, name in functions: + module = importlib.import_module(module_name) + original = getattr(module, name) + + def wrapper(*args, __original=original, **kwargs): + np.random.seed(seed) + return __original(*args, **kwargs) + + originals.append((module, name, original)) + setattr(module, name, wrapper) + try: + yield + finally: + for module, name, original in originals: + setattr(module, name, original) + + +def run_main(args, workdir, setup=None, outdir="out"): + """run ``cocopp.main('-o outdir ' + args)`` in `workdir` with isolated global state. + + `setup` is called within the isolated state before the run, e.g. to + change `cocopp.genericsettings`. Numpy's global RNG is reseeded on + entry of the table modules, see `reseeded_entry_points`. Return a + `PipelineRun`. + """ + stdout = io.StringIO() + os.makedirs(str(workdir), exist_ok=True) + with isolated_cocopp_state(workdir): + if setup: + setup() + t0 = time.time() + with warnings.catch_warnings(), contextlib.redirect_stdout(stdout), reseeded_entry_points(): + warnings.simplefilter("ignore") + result = cocopp.main("--no-interactive -o %s %s" % (outdir, args)) + duration = time.time() - t0 + return PipelineRun(workdir, outdir, result, stdout.getvalue(), duration) + + +# name: (data fixtures, extra command line options, background data fixtures) +SCENARIOS = { + "bbob-1": (["bbob_alg_a"], [], []), + "bbob-2": (["bbob_alg_a", "bbob_alg_b"], [], []), + "bbob-3": (["bbob_alg_a", "bbob_alg_b", "bbob_alg_c"], [], []), + "bbob-1-expensive": (["bbob_alg_a"], ["--expensive"], []), + "bbob-1-conv": (["bbob_alg_a"], ["--conv"], []), + "bbob-1-background": (["bbob_alg_a"], [], ["bbob_alg_b", "bbob_alg_c"]), + "bbob-old-format": (["bbob_old_format"], [], []), + "bbob-small-include-single": (["bbob_small", "bbob_old_format"], ["--include-single"], []), + "biobj-1": (["biobj_alg"], [], []), + "biobj-2": (["biobj_alg", "biobj_alg_b"], [], []), + "noisy-1": (["bbob_noisy"], [], []), + "constrained-1": (["bbob_constrained"], [], []), + "largescale-1": (["bbob_largescale"], [], []), + "mixint-1": (["bbob_mixint"], [], []), + "boxed-1": (["bbob_boxed"], [], []), +} diff --git a/tests/snapshots.py b/tests/snapshots.py new file mode 100644 index 0000000..03f73db --- /dev/null +++ b/tests/snapshots.py @@ -0,0 +1,167 @@ +"""Golden-file snapshots of the text output of the post-processing. + +A snapshot of a `pipeline.PipelineRun` consists of + +- the list of all output files (including figures), +- the content of all generated text files (``.tex``, ``.html``, ``.js``), +- the list of broken relative links in the generated html files, + +normalized such that it does not depend on time stamps, temporary +folders or the package version. Snapshots are stored gzipped as +``tests/snapshots/.json.gz`` and are (re)written with +``pytest --update-snapshots``. +""" + +import difflib +import gzip +import json +import os +import re + +import cocopp + +from pipeline import TIMESTAMP_RE + +SNAPSHOT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "snapshots") +TEXT_EXTENSIONS = (".tex", ".html", ".js") +# copied verbatim from the package's static folder +STATIC_FILES = ("sorttable.js", "pprldflex.js", "pprldflex.css", "pprldflex.html") +_LINK_RE = re.compile(r"""(?:href|src)\s*=\s*["']([^"'#]+)""", re.IGNORECASE) + + +_HV_HASHES_RE = re.compile(r"hv-hashes inconsistent: (.*?) found!") + + +def _sorted_hashes(match): + """the order of the hashes depends on PYTHONHASHSEED (set iteration order)""" + return "hv-hashes inconsistent: %s found!" % " and ".join(sorted(match.group(1).split(" and "))) + + +_SIDEPANEL_RE = re.compile(r"(\\providecommand\{\\perfprofsidepanel\}\{\{(?:\\tiny\{\})?)(.*?)(\}\}\n)", re.DOTALL) + + +def _sorted_sidepanel(match): + """the order of (nearly) tied algorithms in the pprldmany legend depends on + floating point summation order, which depends on PYTHONHASHSEED""" + entries = sorted(e.strip() for e in match.group(2).split("\\vfill")) + return match.group(1) + "\n\\vfill ".join(entries) + match.group(3) + + +def normalize(text, replacements): + """remove run-specific parts from `text`. + + Each replacement ``(old, new)`` is also applied to the LaTeX-escaped + variants of ``old`` as written by the package. + """ + for old, new in replacements: + for variant in (old, old.replace("_", "\\_"), old.replace("_", " ")): + text = text.replace(variant, new) + text = TIMESTAMP_RE.sub("_", text) + text = _HV_HASHES_RE.sub(_sorted_hashes, text) + text = _SIDEPANEL_RE.sub(_sorted_sidepanel, text) + return text.replace(cocopp.__version__, "") + + +def broken_links(run): + """return sorted list of ``"page -> link"`` for relative links that don't resolve""" + res = set() + for name in run.files: + if not name.endswith(".html"): + continue + path = os.path.join(run.outdir, name) + with open(path, encoding="utf-8", errors="replace") as f: + links = _LINK_RE.findall(f.read()) + for link in links: + if ":" in link.split("/")[0]: # http:, file:, javascript: ... + continue + if not os.path.exists(os.path.normpath(os.path.join(os.path.dirname(path), link))): + res.add("%s -> %s" % (TIMESTAMP_RE.sub("_", name), TIMESTAMP_RE.sub("_", link))) + return sorted(res) + + +def take(run, replacements=()): + """return the snapshot `dict` of a `pipeline.PipelineRun`""" + replacements = list(replacements) + [(run.workdir, "")] + text = {} + for name in run.files: + if name.endswith(TEXT_EXTENSIONS) and os.path.basename(name) not in STATIC_FILES: + with open(os.path.join(run.outdir, name), encoding="utf-8", errors="replace") as f: + text[normalize(name, replacements)] = normalize(f.read(), replacements) + return { + "files": sorted(normalize(name, replacements) for name in run.files), + "text": text, + "broken_links": broken_links(run), + } + + +def path(scenario): + return os.path.join(SNAPSHOT_DIR, scenario + ".json.gz") + + +def save(scenario, snapshot): + os.makedirs(SNAPSHOT_DIR, exist_ok=True) + data = json.dumps(snapshot, indent=0, sort_keys=True).encode("utf-8") + # mtime=0 makes the gzip file itself reproducible + with open(path(scenario), "wb") as f, gzip.GzipFile(fileobj=f, mode="wb", mtime=0) as gz: + gz.write(data) + + +def load(scenario): + with gzip.open(path(scenario), "rb") as f: + return json.loads(f.read().decode("utf-8")) + + +def diff(expected, actual, max_lines=60): + """return a human readable description of the differences or ``""``""" + out = [] + for key in ("files", "broken_links"): + missing = sorted(set(expected[key]) - set(actual[key])) + added = sorted(set(actual[key]) - set(expected[key])) + if missing or added: + out.append("%s: %d missing, %d new" % (key, len(missing), len(added))) + out += [" - " + m for m in missing[:10]] + [" + " + a for a in added[:10]] + changed = sorted(n for n in set(expected["text"]) & set(actual["text"]) if expected["text"][n] != actual["text"][n]) + if changed: + out.append("%d text file(s) differ: %s" % (len(changed), ", ".join(changed[:10]) + (" ..." if len(changed) > 10 else ""))) + name = changed[0] + udiff = difflib.unified_diff( + expected["text"][name].splitlines(), actual["text"][name].splitlines(), "expected/" + name, "actual/" + name, lineterm="", n=1 + ) + out += list(udiff)[:max_lines] + return "\n".join(out) + + +def _main(argv): + """command line interface to review snapshots:: + + python tests/snapshots.py diff OLD.json.gz NEW.json.gz + python tests/snapshots.py extract SNAPSHOT.json.gz FOLDER + + ``extract`` writes all text files of a snapshot into FOLDER, such that + two extracted snapshots can be compared with any diff tool, e.g. after + ``jj file show -r @- tests/snapshots/bbob-1.json.gz > old.json.gz``. + """ + def read(name): + with gzip.open(name, "rb") as f: + return json.loads(f.read().decode("utf-8")) + + if len(argv) == 3 and argv[0] == "diff": + print(diff(read(argv[1]), read(argv[2]), max_lines=10**6) or "no differences") + elif len(argv) == 3 and argv[0] == "extract": + snapshot = read(argv[1]) + for name, text in snapshot["text"].items(): + target = os.path.join(argv[2], *name.split("/")) + os.makedirs(os.path.dirname(target), exist_ok=True) + with open(target, "w", encoding="utf-8") as f: + f.write(text) + for key in ("files", "broken_links"): + with open(os.path.join(argv[2], "_%s.txt" % key), "w", encoding="utf-8") as f: + f.write("\n".join(snapshot[key]) + "\n") + else: + print(_main.__doc__) + + +if __name__ == "__main__": + import sys + + _main(sys.argv[1:]) diff --git a/tests/synthdata.py b/tests/synthdata.py new file mode 100644 index 0000000..76a60fa --- /dev/null +++ b/tests/synthdata.py @@ -0,0 +1,298 @@ +"""Deterministic generator for synthetic COCO experiment data. + +The real data archives are several MB per algorithm and live online. For +offline, fast and reproducible tests we instead write small, *synthetic* +data folders that follow the on-disk formats written by the COCO +experiment loggers closely enough for `cocopp` to read them: + +- single-objective ``bbob`` (and ``bbob-noisy``) data in the "new" + (``data_format = 'bbob-new2'``, constraints evaluations in the second + column) and the "old" (no ``data_format``) layouts, consisting of + ``.info`` index files and ``.dat``/``.tdat`` data files; +- bi-objective ``bbob-biobj`` data (``indicator = 'hyp'``). + +Every generator takes a ``seed`` and only uses its own +`numpy.random.Generator`, hence the produced files are byte-identical +for the same arguments, independently of the global numpy random state. + +The simulated "optimizer" improves the best-so-far Delta-f value by a +random factor at random evaluation increments. Function and dimension +determine the convergence speed, such that some (function, dimension) +pairs are solved in all trials, some in a few and some in none, which +exercises the success/failure code paths of the post-processing. +""" + +import os + +import numpy as np + +BBOB_DIMS = (2, 3, 5, 10, 20) +BBOB_FUNCTIONS = tuple(range(1, 25)) +BBOB_INSTANCES = tuple(range(1, 16)) +BIOBJ_DIMS = (2, 3, 5) +BIOBJ_FUNCTIONS = tuple(range(1, 56)) +BIOBJ_INSTANCES = tuple(range(1, 11)) + +_PRECISION = 1e-8 + + +def _log_spaced_evals(last, per_decade=10): + """return sorted unique integer evaluations ``1, 2, ...`` up to `last` + + roughly log-uniformly spaced, always including 1 and `last`.""" + if last <= 1: + return [1] + n = int(np.ceil(np.log10(last) * per_decade)) + 1 + evals = np.unique(np.floor(np.logspace(0, np.log10(last), n)).astype(int)) + evals = [int(e) for e in evals if e < last] + return evals + [int(last)] + + +def _trajectory(rng, f0, rate, budget, final=_PRECISION / 100): + """simulate a monotone best-so-far trajectory. + + Return two lists ``evals`` and ``fvals`` (Delta f), where + ``fvals[i]`` is the best-so-far value from evaluation ``evals[i]`` on. + The first evaluation is always 1. The run stops when the `budget` is + exhausted or `final` is reached; the last entry is the last + evaluation. + """ + evals, fvals = [1], [f0] + e, f = 1, f0 + while True: + e += int(rng.geometric(rate)) + if e > budget: + break + f *= 10 ** -rng.uniform(0.05, 0.6) + evals.append(e) + fvals.append(f) + if f < final: + break + if evals[-1] < budget and fvals[-1] >= final: # unsuccessful: ran into the budget + evals.append(int(budget)) + fvals.append(fvals[-1]) + return evals, fvals + + +def _bbob_target_crossings(evals, fvals, nb_per_decade=5): + """return indices into `evals` at which a new ``10**(i/5)`` target is reached, + + as written into ``.dat`` files by the bbob logger. Index 0 is always + included and the final index is included when it reaches below the + final precision.""" + idx = [0] + level = np.floor(np.log10(fvals[0]) * nb_per_decade) + for i in range(1, len(fvals)): + new_level = np.floor(np.log10(max(fvals[i], 1e-300)) * nb_per_decade) + if new_level < level: + idx.append(i) + level = new_level + return idx + + +def _fmt(x): + return "%+.9e" % x + + +def _bbob_line(e, df, fopt, dim, rng, old_format, g_per_f=0): + # the real loggers write all ``dim`` coordinates, two suffice to keep files small + x = " ".join("%+.4e" % v for v in rng.uniform(-4, 4, min(dim, 2))) + second = _fmt(df + fopt) if old_format else "%d" % (g_per_f * e) + return "%d %s %s %s %s %s\n" % (e, second, _fmt(df), _fmt(df + fopt), _fmt(df + fopt), x) + + +def _rate(rng, fun, dim, speed): + """return the per-evaluation improvement probability for one trial on ``(fun, dim)``. + + The expected number of evaluations per improvement step is about + ``c_f * dim**1.1 / speed`` where ``c_f`` grows with ``fun % 8`` from + 1.5 to about 12, such that with the default budget of ``200 * dim`` + the easy functions are always and the hard ones rarely solved. + """ + c_f = 1.5 * 1.35 ** (fun % 8) + return min(1.0, speed / (c_f * dim**1.1 * 10 ** rng.uniform(-0.3, 0.3))) + + +def write_bbob( + root, + alg_name="SYNTH-A", + functions=BBOB_FUNCTIONS, + dims=BBOB_DIMS, + instances=BBOB_INSTANCES, + seed=1, + budget_multiplier=200, + speed=1.0, + suite="bbob", + old_format=False, + with_tdat=True, + comment=None, + g_per_f=None, +): + """write a full synthetic single-objective experiment into ``root/alg_name``. + + Return the path of the algorithm folder. `speed` scales the + convergence speed (larger is faster), such that different "algorithms" + can be created from the same function. Only the generator seeded with + `seed` is used. + + ``old_format=True`` writes the pre-2016 layout without ``suite``, + ``data_format`` and ``logger`` in the header (second column is the + current f-value instead of constraints evaluations). + + `g_per_f` is the number of constraints evaluations per f-evaluation, + by default ``1 + (fun - 1) % 6`` for the ``bbob-constrained`` suite + and zero otherwise. + """ + rng = np.random.default_rng(seed) + folder = os.path.join(root, alg_name) + os.makedirs(folder, exist_ok=True) + comment = comment if comment is not None else "%% %s: synthetic data, seed=%d" % (alg_name, seed) + for fun in functions: + g = g_per_f if g_per_f is not None else (1 + (fun - 1) % 6 if suite == "bbob-constrained" else 0) + data_dir = "data_f%d" % fun + os.makedirs(os.path.join(folder, data_dir), exist_ok=True) + info_lines = [] + for dim in dims: + budget = int(budget_multiplier * dim) + base = "bbobexp_f%d_DIM%d" % (fun, dim) + dat = open(os.path.join(folder, data_dir, base + ".dat"), "w") + tdat = open(os.path.join(folder, data_dir, base + ".tdat"), "w") if with_tdat else None + trial_infos = [] + for inst in instances: + fopt = float(np.round(rng.uniform(-400, 400), 2)) + f0 = 10 ** rng.uniform(0.5, 3) + rate = _rate(rng, fun, dim, speed) + evals, fvals = _trajectory(rng, f0, rate, budget) + header = ( + "%% f evaluations | g evaluations | best noise-free fitness - Fopt (%.12e) + sum g_i+ |" + " measured fitness | best measured fitness or single-digit g-values | x1 | x2...\n" % fopt + ) + dat.write(header) + for i in _bbob_target_crossings(evals, fvals): + dat.write(_bbob_line(evals[i], fvals[i], fopt, dim, rng, old_format, g)) + if tdat: + tdat.write(header) + j = 0 + for e in _log_spaced_evals(evals[-1]): + while j + 1 < len(evals) and evals[j + 1] <= e: + j += 1 + tdat.write(_bbob_line(e, fvals[j], fopt, dim, rng, old_format, g)) + trial_infos.append("%d:%d|%.1e" % (inst, evals[-1], fvals[-1] - _PRECISION)) + dat.close() + if tdat: + tdat.close() + if old_format: + head = "funcId = %d, DIM = %d, Precision = %.3e, algId = '%s'" % (fun, dim, _PRECISION, alg_name) + else: + head = ( + "suite = '%s', funcId = %d, DIM = %d, Precision = %.3e, algId = '%s'," + " coco_version = '2.6.0', logger = 'bbob', data_format = 'bbob-new2'" % (suite, fun, dim, _PRECISION, alg_name) + ) + info_lines += [head, comment, "%s/%s.dat, " % (data_dir, base) + ", ".join(trial_infos)] + with open(os.path.join(folder, "bbobexp_f%d.info" % fun), "w") as f: + f.write("\n".join(info_lines) + "\n") + return folder + + +def write_biobj( + root, + alg_name="SYNTH-BI", + functions=BIOBJ_FUNCTIONS, + dims=BIOBJ_DIMS, + instances=BIOBJ_INSTANCES, + seed=1, + budget_multiplier=100, + speed=1.0, +): + """write a synthetic ``bbob-biobj`` experiment into ``root/alg_name``. + + The layout follows the 2016 biobj logger: one ``.info`` file per + dimension in a ``dXX`` folder; each data line references a ``.dat`` + file in a ``folder`` subdirectory. Return the algorithm folder. + """ + rng = np.random.default_rng(seed) + folder = os.path.join(root, alg_name) + for dim in dims: + dim_dir = os.path.join(folder, "d%02d" % dim) + group = "all-functions" + os.makedirs(os.path.join(dim_dir, group), exist_ok=True) + info = [ + "algorithm = '%s', indicator = 'hyp', folder = '%s', coco_version = '2.6.0'" % (alg_name, group), + "%% %s: synthetic bi-objective data, seed=%d" % (alg_name, seed), + ] + for fun in functions: + budget = int(budget_multiplier * dim) + base = "bbob-biobj_f%02d_d%02d_hyp" % (fun, dim) + dat = open(os.path.join(dim_dir, group, base + ".dat"), "w") + tdat = open(os.path.join(dim_dir, group, base + ".tdat"), "w") + trial_infos = [] + for inst in instances: + # reference values are a property of the problem instance, not of the algorithm + ref = np.random.default_rng((fun, dim, inst)).uniform(0.5, 1.0) + hdr = "%%\n%% index = %d, name = synthetic_f%02d_i%02d_d%02d\n%% instance = %d, reference value = %.15e\n" % ( + fun * 100 + inst, fun, inst, dim, inst, ref) + f0 = 10 ** rng.uniform(-0.5, 0.5) + rate = _rate(rng, fun, dim, speed) + evals, fvals = _trajectory(rng, f0, rate, budget, final=1e-6) + dat.write(hdr + "% function evaluation | indicator value | target hit\n") + for i in _bbob_target_crossings(evals, fvals, nb_per_decade=10): + target_hit = 10 ** (np.floor(np.log10(max(fvals[i], 1e-300)) * 10) / 10) + dat.write("%d\t%.15e\t%.15e\n" % (evals[i], fvals[i], target_hit)) + tdat.write(hdr + "% function evaluation | indicator value\n") + j = 0 + for e in _log_spaced_evals(evals[-1]): + while j + 1 < len(evals) and evals[j + 1] <= e: + j += 1 + tdat.write("%d\t%.15e\n" % (e, fvals[j])) + trial_infos.append("%d:%d|%.1e" % (inst, evals[-1], fvals[-1])) + dat.close() + tdat.close() + # the file name is relative to `folder` given in the header + info.append("function = %d, dim = %d, %s.dat, " % (fun, dim, base) + ", ".join(trial_infos)) + with open(os.path.join(dim_dir, "synthetic_d%02d_hyp.info" % dim), "w") as f: + f.write("\n".join(info) + "\n") + return folder + + +# hand-crafted tiny data set with hand-computable results, see `write_tiny_bbob` +TINY_TRIALS = { + # instance: [(f-evaluations, g-evaluations, Delta f), ...]; the last line is the final evaluation + 1: [(1, 0, 95.0), (10, 0, 0.9), (50, 0, 5e-9)], + 2: [(1, 0, 45.0), (20, 0, 4.5), (100, 0, 0.45)], + 3: [(1, 0, 9.5), (5, 0, 9e-4), (30, 0, 9e-10)], +} + + +def write_tiny_bbob(root, alg_name="TINY", trials=None, suite="bbob", fun=1, dim=2): + """write a single ``(fun, dim)`` data set with given `trials` into ``root/alg_name``. + + `trials` maps instance numbers to lists of ``(evals, g_evals, Delta_f)`` + tuples, or is a list of ``(instance, rows)`` pairs (to repeat + instances). Each tuple is written to both the ``.dat`` and the ``.tdat`` + file such that the expected `DataSet` content can be computed by hand. + Return the path of the ``.info`` file. + """ + trials = TINY_TRIALS if trials is None else trials + trials = list(trials.items()) if isinstance(trials, dict) else list(trials) + folder = os.path.join(root, alg_name) + os.makedirs(os.path.join(folder, "data_f%d" % fun), exist_ok=True) + base = "data_f%d/bbobexp_f%d_DIM%d" % (fun, fun, dim) + fopt = 10.0 + lines = [] + for inst, rows in trials: + lines.append("%% f evaluations | g evaluations | best noise-free fitness - Fopt (%.12e) + sum g_i+ | ...\n" % fopt) + for e, g, df in rows: + lines.append("%d %d %s %s %s +0.0000e+00\n" % (e, g, _fmt(df), _fmt(df + fopt), _fmt(df + fopt))) + for ext in (".dat", ".tdat"): + with open(os.path.join(folder, base + ext), "w") as f: + f.write("".join(lines)) + infos = ", ".join("%d:%d|%.1e" % (inst, rows[-1][0], rows[-1][2] - _PRECISION) for inst, rows in trials) + info_file = os.path.join(folder, "bbobexp_f%d.info" % fun) + with open(info_file, "w") as f: + f.write( + "suite = '%s', funcId = %d, DIM = %d, Precision = 1.000e-08, algId = '%s', coco_version = '2.6.0'," + " logger = 'bbob', data_format = 'bbob-new2'\n" % (suite, fun, dim, alg_name) + ) + f.write("%% %s: hand-crafted test data\n" % alg_name) + f.write("%s.dat, %s\n" % (base, infos)) + return info_file From 2cac9d06254f83468236f65e263eb07503114a99 Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 09:57:58 +0200 Subject: [PATCH 02/10] [tests] add unit tests Unit tests for the statistics and formatting helpers (toolsstats, toolsdivers, pptex), data reading and alignment (readalign, DataSet on a hand-crafted data set with hand-computed runtimes, DataSetList and the dictAlg helpers), target values, reference algorithm generation, the settings modules (testbedsettings, genericsettings, config, dataformatsettings), findfiles, captions, ppfig helpers, local data archives, reduce_size, sanitycheck and the package interface. Many expected values are characterization values pinning the current behavior. Known bugs are documented as strict xfail tests. --- tests/unit/test_archiving_local.py | 164 +++++++ tests/unit/test_cococommands_and_html.py | 94 ++++ tests/unit/test_package.py | 96 ++++ tests/unit/test_pproc_dataset.py | 362 +++++++++++++++ tests/unit/test_pproc_lists.py | 419 ++++++++++++++++++ tests/unit/test_pptex.py | 86 ++++ tests/unit/test_readalign.py | 124 ++++++ .../unit/test_reduce_size_and_sanitycheck.py | 100 +++++ tests/unit/test_settings_and_helpers.py | 319 +++++++++++++ tests/unit/test_synthdata.py | 63 +++ tests/unit/test_toolsdivers.py | 320 +++++++++++++ tests/unit/test_toolsstats.py | 282 ++++++++++++ 12 files changed, 2429 insertions(+) create mode 100644 tests/unit/test_archiving_local.py create mode 100644 tests/unit/test_cococommands_and_html.py create mode 100644 tests/unit/test_package.py create mode 100644 tests/unit/test_pproc_dataset.py create mode 100644 tests/unit/test_pproc_lists.py create mode 100644 tests/unit/test_pptex.py create mode 100644 tests/unit/test_readalign.py create mode 100644 tests/unit/test_reduce_size_and_sanitycheck.py create mode 100644 tests/unit/test_settings_and_helpers.py create mode 100644 tests/unit/test_synthdata.py create mode 100644 tests/unit/test_toolsdivers.py create mode 100644 tests/unit/test_toolsstats.py diff --git a/tests/unit/test_archiving_local.py b/tests/unit/test_archiving_local.py new file mode 100644 index 0000000..7314a85 --- /dev/null +++ b/tests/unit/test_archiving_local.py @@ -0,0 +1,164 @@ +"""Unit tests for `cocopp.archiving` with *local* archives only. + +Nothing here touches the network or the user's cache folder: +`ArchivesLocal.register`, which would record created archives in the +user's cache, is patched. +""" + +import os +import tarfile + +import pytest + +from cocopp import archiving + + +@pytest.fixture +def registered(monkeypatch): + calls = [] + monkeypatch.setattr(archiving.ArchivesLocal, "register", classmethod(lambda cls, folder: calls.append(folder))) + return calls + + +@pytest.fixture +def archive_folder(tmp_path, bbob_small, bbob_old_format): + """a folder with three zipped "data sets" in sub-folders""" + root = tmp_path / "my-archive" + for sub, src, name in [("2020", bbob_small, "SMALL_alg.tgz"), ("2021", bbob_old_format, "OLD_alg.tgz"), ("2021", bbob_small, "SMALL2.tgz")]: + (root / sub).mkdir(parents=True, exist_ok=True) + with tarfile.open(str(root / sub / name), "w:gz") as tar: + tar.add(src, arcname=os.path.basename(src)) + (root / "README.md").write_text("ignored") + return root + + +@pytest.fixture +def archive(archive_folder, registered): + return archiving.create(str(archive_folder)) + + +class TestCreateAndRead: + def test_create(self, archive, archive_folder, registered): + assert isinstance(archive, archiving.COCODataArchive) + assert sorted(archive) == ["2020/SMALL_alg.tgz", "2021/OLD_alg.tgz", "2021/SMALL2.tgz"] + assert registered == [str(archive_folder)] + assert (archive_folder / archiving.default_definition_filename).exists() + assert archive.remote_data_path is None + assert archive.local_data_path == str(archive_folder) + + def test_definition_file(self, archive, archive_folder): + defs = archiving.read_definition_file(str(archive_folder)) + assert sorted(d[0] for d in defs) == sorted(archive) + name, hash_, size = [d for d in defs if d[0] == "2020/SMALL_alg.tgz"][0] + assert hash_ == archiving._hash(str(archive_folder / "2020" / "SMALL_alg.tgz")) + assert isinstance(size, int) + + def test_get_returns_existing_archive(self, archive, archive_folder): + same = archiving.get(str(archive_folder)) + assert list(same) == list(archive) + assert archiving.COCODataArchive.is_archive(str(archive_folder)) + assert not archiving.COCODataArchive.is_archive(str(archive_folder / "2020")) + + def test_not_an_archive(self, tmp_path): + with pytest.raises(ValueError, match="seems not to"): + archiving.COCODataArchive(str(tmp_path)) + with pytest.raises(ValueError): + archiving.get(None) + + def test_recreate_makes_backup(self, archive, archive_folder, registered): + archiving.create(str(archive_folder)) + backups = [f for f in os.listdir(archive_folder) if f.startswith("._" + archiving.default_definition_filename)] + assert len(backups) == 1 + + def test_empty_archive(self, tmp_path, registered): + with pytest.warns(UserWarning, match="no data found"): + assert archiving.create(str(tmp_path)) is None + + def test_unusual_file_warns(self, tmp_path, registered): + (tmp_path / "data.abc").write_text("x") + with pytest.warns(UserWarning, match="unusual file"): + arch = archiving.create(str(tmp_path)) + assert list(arch) == ["data.abc"] + + +class TestFindAndGet: + def test_find(self, archive): + assert archive.find("small") == ["2020/SMALL_alg.tgz", "2021/SMALL2.tgz"] + assert archive.find("2021", "small") == ["2021/SMALL2.tgz"] + assert archive("old") == ["2021/OLD_alg.tgz"] + assert archive.found == ["2021/OLD_alg.tgz"] + + def test_get(self, archive, archive_folder): + path = archive.get("OLD") + assert path == str(archive_folder / "2021" / "OLD_alg.tgz") + assert archive.get(0) == archive.full_path(archive[0]) + archive.find("OLD") + assert archive.get() == path # first of last found + assert archive.contains(path) and archive.contains("2021/OLD_alg.tgz") + assert not archive.contains("nothing") + + def test_get_ambiguous_or_missing(self, archive): + with pytest.raises(ValueError, match="multiple matches"): + archive.get("SMALL") + with pytest.raises(ValueError, match="no match"): + archive.get("nonexisting") + with pytest.raises(ValueError): + archive.get("OLD", remote="yes") + + def test_get_all_first_and_extended(self, archive, archive_folder): + assert len(archive.get_all("small")) == 2 + assert archive.get_first(["small"]) == str(archive_folder / "2020" / "SMALL_alg.tgz") + assert archive.get_first(["nothing"]) is None + archive.find("OLD") + assert archive.get_found() == [str(archive_folder / "2021" / "OLD_alg.tgz")] + assert archive.get_extended(["small!"]) == [str(archive_folder / "2020" / "SMALL_alg.tgz")] + assert len(archive.get_extended("small*")) == 2 + assert archive.get_extended(["20.*SMALL"]) == archive.get_all("small") + assert archive.get_extended([str(archive_folder)]) == [str(archive_folder)] # existing path + with pytest.raises(ValueError): + archive.get_extended(["nonexisting"], remote=False) + + def test_downloaded_and_hash_check(self, archive, archive_folder): + assert sorted(archive.downloaded) == sorted(archive) + assert archive.consistency_check_data() == (3, 3) + path = archive_folder / "2021" / "OLD_alg.tgz" + with open(path, "ab") as f: + f.write(b"corrupt") + with pytest.raises(ValueError, match="wrong checksum"): + archive.get("OLD") + + def test_missing_file_with_remote_false(self, archive, archive_folder): + os.remove(archive_folder / "2021" / "OLD_alg.tgz") + assert archive.get("OLD", remote=False) == "" + + def test_get_found_with_several_matches(self, archive): + archive.find("small") + assert len(archive.get_found()) == 2 + + def test_update_without_url_warns(self, archive): + with pytest.warns(UserWarning, match="has no remote URL"): + archive.update() + + +class TestHelpers: + def test_url_helpers(self): + assert archiving._is_url("https://x") and archiving._is_url("HTTP://x") and not archiving._is_url("/x") + folder = archiving._url_to_folder_name("https://numbbo.github.io/data-archive/data-archive/bbob") + assert folder == os.path.join(archiving.default_archive_location, "da", "bbob") + + def test_str_to_list(self): + assert archiving._str_to_list("a") == ["a"] + assert archiving._str_to_list(("a",)) == ("a",) + with pytest.raises(ValueError): + archiving._str_to_list(3) + + def test_definition_file_names(self, tmp_path): + assert archiving._definition_file_to_read(str(tmp_path)) == str(tmp_path / archiving.default_definition_filename) + assert archiving._definition_file_to_write(str(tmp_path)) == str(tmp_path / archiving.default_definition_filename) + assert archiving._definition_file_to_write(str(tmp_path), "x.txt") == str(tmp_path / "x.txt") + with pytest.warns(UserWarning): + archiving._definition_file_to_write(str(tmp_path / "other.txt")) + + def test_repr_definitions_is_sorted(self): + s = archiving._repr_definitions([("b/x", "h", 1), ("a/y", "h", 2)]) + assert s.index("a/y") < s.index("b/x") diff --git a/tests/unit/test_cococommands_and_html.py b/tests/unit/test_cococommands_and_html.py new file mode 100644 index 0000000..a44347f --- /dev/null +++ b/tests/unit/test_cococommands_and_html.py @@ -0,0 +1,94 @@ +"""Unit tests for `cocopp.cococommands` (``load``, ``load2``, +``DataWithFewSuccesses``) and the html/LaTeX description helpers +`cocopp.htmldesc` and `cocopp.preparetexforhtml`. +""" + +import numpy as np +import pytest + +import cocopp +from cocopp import cococommands, htmldesc, preparetexforhtml, pproc, testbedsettings + +from conftest import load + + +class TestDataWithFewSuccesses: + @pytest.fixture + def few(self, bbob_small, quiet): + testbedsettings.reset_current_testbed() + return cococommands.DataWithFewSuccesses(bbob_small, minsuccesses=9) + + def test_attributes(self, few, bbob_small): + dsl = load(bbob_small) + assert few.trials == [15] * 8 + assert few.successes == [ds.detSuccesses([1e-8])[0] for ds in dsl] + assert few.evaluations == pytest.approx([np.sum(ds.maxevals) / 15 for ds in dsl]) + assert few.input_parameters["minsuccesses"] == 9 + + def test_all_and_budget_left(self, few): + expected = {(f, d): v for (f, d), v in few.all.items()} + assert all(v[1] < 9 for v in expected.values()) + assert all(v[2] == 15 for v in expected.values()) + assert list(few.all) == sorted(few.all) + assert few.with_budget_left == few.all # budget multiplier is inf + few.budget_multiplier = 0 + assert few.with_budget_left == {} + assert few.current == few.all + + def test_minsuccesses_and_threshold_changes(self, few): + n = len(few.all) + few.minsuccesses = 16 + assert len(few.all) == 8 + few.minsuccesses = 9 + few.compute_successes(1e2) # easy target: everything solved + assert len(few.all) == 0 < n or n == 0 + + def test_from_datasetlist(self, bbob_small, quiet): + dsl = load(bbob_small) + few = cococommands.DataWithFewSuccesses(dsl, minsuccesses=16, exclude=[(1, 2)]) + assert (1, 2) not in few.all and len(few.all) == 7 + + @pytest.mark.xfail(reason="known bug: print and __len__ use a non-existing `result` attribute", raises=AttributeError, strict=True) + def test_len(self, few): + len(few) + + +def test_true_number_of_trials(dsl_small): + assert cococommands.true_number_of_trials(dsl_small[0]) == 15 + + +class TestLoad2: + def test_single_algorithm(self, bbob_small, quiet): + testbedsettings.reset_current_testbed() + dsl = cocopp.load2(bbob_small) + assert isinstance(dsl, pproc.DataSetList) and len(dsl) == 8 + + def test_filters(self, bbob_small, quiet): + testbedsettings.reset_current_testbed() + dsl = cocopp.load2(bbob_small, keep=lambda ds: ds.funcId < 10, remove=lambda ds: ds.funcId == 2) + assert sorted({ds.funcId for ds in dsl}) == [1, 8] + + def test_several_algorithms(self, bbob_small, bbob_small_b, quiet): + testbedsettings.reset_current_testbed() + res = cocopp.load2([bbob_small, bbob_small_b]) + assert sorted(res) == [2, 5] + assert list(res[5]) == [bbob_small, bbob_small_b] + assert len(cocopp.load2([bbob_small, bbob_small_b], flat_list=True)) == 16 + + +class TestHtmlDescriptions: + def test_get_value(self): + text = htmldesc.getValue("##bbobppfigdimlegendrlbased##") + assert text and "##" not in text + assert htmldesc.getValue("##not-a-key##") == "" + assert len(htmldesc.descriptions) == 102 + + def test_prepare_tex_for_html(self, tmp_path, quiet): + out = tmp_path / "latex_commands_for_html.tex" + preparetexforhtml.main(str(out)) + text = out.read_text() + assert text.count("\\begin{document}") == 1 and text.rstrip().endswith("\\end{document}") + # every scenario gets the same set of caption commands, each preceded by its html key + for scenario in ["fixed", "rlbased", "biobjfixed", "bbobboxedfixed"]: + assert "\\providecommand{\\bbobppfigdimlegend%s}" % scenario in text + assert "\\#\\#bbobppfigdimlegend%s\\#\\#" % scenario in text diff --git a/tests/unit/test_package.py b/tests/unit/test_package.py new file mode 100644 index 0000000..ec8f221 --- /dev/null +++ b/tests/unit/test_package.py @@ -0,0 +1,96 @@ +"""Tests of the package-level interface of `cocopp`. + +These pin the public API so that a restructuring does not silently drop +names that users (and notebooks) rely upon. +""" + +import importlib +import pkgutil + +import pytest + +import cocopp + +ALL_MODULES = sorted( + name + for _, name, _ in pkgutil.walk_packages(cocopp.__path__, "cocopp.") + # __main__ runs the CLI, the other two are scripts needing the network or local data + if name not in ("cocopp.__main__", "cocopp.firstsession", "cocopp.pprldistr2009_hardestRLB") +) + + +def test_module_list_is_complete(): + # if this fails, a module was added or removed: update the tests accordingly + assert len(ALL_MODULES) == 47 + for name in ["cocopp.pproc", "cocopp.compall.pprldmany", "cocopp.comp2.ppscatter", "cocopp.rungeneric"]: + assert name in ALL_MODULES + + +@pytest.mark.parametrize("name", ALL_MODULES) +def test_all_modules_import(name, quiet): + importlib.import_module(name) + + +def test_interface(): + assert sorted(cocopp.Interface.dir()) == ["archives", "config", "genericsettings", "load", "main", "remote_archives"] + assert all(hasattr(cocopp, name) for name in cocopp.Interface.dir()) + assert cocopp.Interface.main is cocopp.main + assert cocopp.load is cocopp.cococommands.load + + +def test_public_names(): + for name in ["set_seed", "archives", "data_archive", "archiving", "config", "genericsettings", "load", "load2", "info", "systeminfo", "pproc", "testbedsettings", "toolsdivers", "toolsstats", "bestalg", "__version__"]: + assert hasattr(cocopp, name), name + assert cocopp.archives is cocopp.archiving.official_archives + assert cocopp.data_archive is cocopp.archives.all + + +def test_version(): + assert cocopp.__version__ == cocopp._version.__version__ + assert cocopp.__version__[0].isdigit() + + +def test_set_seed_is_numpy_seed(): + import numpy as np + + cocopp.set_seed(3) + a = np.random.rand() + np.random.seed(3) + assert a == np.random.rand() + + +@pytest.fixture +def no_sys_argv(monkeypatch): + """`cocopp.main` falls back to ``sys.argv[1:]``, which hold the pytest arguments""" + monkeypatch.setattr("sys.argv", ["cocopp"]) + + +def test_main_without_arguments_prints_usage(capsys, no_sys_argv): + cocopp.main([]) + assert "Main routine for post-processing data from COCO" in capsys.readouterr().out + + +def test_main_help(capsys, no_sys_argv): + assert cocopp.main("-h") is None + assert "-h, --help" in capsys.readouterr().out + + +def test_main_unknown_option(capsys, no_sys_argv): + assert cocopp.main("--no-such-option x") == 2 + assert "For help use -h or --help" in capsys.readouterr().err + + +def test_rungeneric_main_unknown_option(capsys): + assert cocopp.rungeneric.main("--no-such-option x") == 2 + assert "For help use -h or --help" in capsys.readouterr().err + + +def test_rungeneric_main_help(capsys): + assert cocopp.rungeneric.main("-h") is None + assert "--output-dir" in capsys.readouterr().out + + +def test_systeminfo(capsys): + cocopp.systeminfo() + out = capsys.readouterr().out + assert "Numpy" in out and "Matplotlib" in out and "cocopp " + cocopp.__version__ in out diff --git a/tests/unit/test_pproc_dataset.py b/tests/unit/test_pproc_dataset.py new file mode 100644 index 0000000..0bb8172 --- /dev/null +++ b/tests/unit/test_pproc_dataset.py @@ -0,0 +1,362 @@ +"""Unit tests of `cocopp.pproc.DataSet` on a tiny hand-crafted data set. + +The data (see `synthdata.TINY_TRIALS`) consist of three trials on f1 in +2-D with the following best-so-far Delta f values:: + + instance 1: eval 1 -> 95, eval 10 -> 0.9, eval 50 -> 5e-9 (solved) + instance 2: eval 1 -> 45, eval 20 -> 4.5, eval 100 -> 0.45 (not solved) + instance 3: eval 1 -> 9.5, eval 5 -> 9e-4, eval 30 -> 9e-10 (solved) + +hence all expected values below can be (and were) computed by hand. +""" + +import os +import pickle + +import numpy as np +import pytest + +import cocopp +from cocopp import genericsettings, pproc, testbedsettings + +import synthdata + +nan = np.nan +TARGETS = [100, 10, 1, 0.1, 1e-3, 1e-8] +EXPECTED_EVALS = [[1, 1, 1], [10, 20, 1], [10, 100, 5], [50, nan, 5], [50, nan, 5], [50, nan, 30]] +EXPECTED_ERT = [1, 31 / 3, 115 / 3, 155 / 2, 155 / 2, 180 / 2] +EXPECTED_SUCCESSES = [3, 3, 3, 2, 2, 2] + + +def assert_nan_equal(a, b): + np.testing.assert_array_equal(np.asarray(a, dtype=float), np.asarray(b, dtype=float)) + + +@pytest.fixture +def tiny(tmp_path, quiet): + testbedsettings.reset_current_testbed() + info = synthdata.write_tiny_bbob(str(tmp_path)) + dsl = pproc.DataSetList(info) + assert len(dsl) == 1 + return dsl[0] + + +def _load_tiny(tmp_path, name, trials, **kwargs): + testbedsettings.reset_current_testbed() + return pproc.DataSetList(synthdata.write_tiny_bbob(str(tmp_path), name, trials=trials, **kwargs))[0] + + +class TestReading: + def test_header_attributes(self, tiny): + assert (tiny.funcId, tiny.dim, tiny.algId) == (1, 2, "TINY") + assert tiny.precision == 1e-8 + assert tiny.suite_name == "bbob" + assert tiny.get_data_format() == "bbob-new2" + assert tiny.comment == "% TINY: hand-crafted test data" + assert not tiny.isBiobjective() + assert not tiny.has_constraints + assert repr(tiny) == "DataSet(TINY on f1 2-D)" + + def test_loading_sets_current_testbed(self, tiny): + assert isinstance(testbedsettings.current_testbed, testbedsettings.GECCOBBOBTestbed) + + def test_run_information_from_info_file(self, tiny): + assert tiny.instancenumbers == [1, 2, 3] + assert tiny.readmaxevals == [50, 100, 30] + # f - ftarget as written with one digit precision by `write_tiny_bbob` + assert tiny.readfinalFminusFtarget == [-5e-9, 0.45, -9.1e-9] + assert tiny.isFinalized == [True, True, True] + assert tiny.dataFiles == [os.path.join("data_f1", "bbobexp_f1_DIM2.dat")] + + def test_maxevals_and_finalfunvals(self, tiny): + assert list(tiny.maxevals) == [50, 100, 30] + assert tiny.max_eval == 100 == tiny.mMaxEvals() + assert list(tiny.finalfunvals) == pytest.approx([5e-9, 0.45, 9e-10]) + assert tiny.nbRuns() == tiny.nbRuns_raw == 3 + + def test_evals_first_row_and_targets(self, tiny): + assert list(tiny.evals[0, 1:]) == [1, 1, 1] + assert tiny.evals[0, 0] == pytest.approx(100) # 10**(i/5) just above 95 + assert all(np.diff(tiny.evals[:, 0]) < 0) + # targets are on the 10**(i/5) grid, the last is cut just below precision 1e-8 + grid = np.log10(tiny.evals[:-1, 0]) * 5 + assert grid == pytest.approx(np.round(grid)) + assert tiny.evals[-1, 0] == pytest.approx(1e-8 / 1.001) + assert all(tiny.evals[:, 0] == tiny.target) + + def test_funvals(self, tiny): + assert_nan_equal( + tiny.funvals, + [ + [1, 95, 45, 9.5], + [5, 95, 45, 9e-4], + [10, 0.9, 45, 9e-4], + [20, 0.9, 4.5, 9e-4], + [30, 0.9, 4.5, 9e-10], + [50, 5e-9, 4.5, 9e-10], + [100, 5e-9, 0.45, 9e-10], + ], + ) + + def test_consistency_check(self, tiny): + with pytest.warns(UserWarning, match="less than 15 instances"): + assert tiny.consistency_check() is False + + +class TestRuntimes: + def test_detEvals(self, tiny): + for res, expected in zip(tiny.detEvals(TARGETS), EXPECTED_EVALS): + assert_nan_equal(res, expected) + + def test_detEvals_order_follows_input(self, tiny): + rev = tiny.detEvals(TARGETS[::-1]) + for res, expected in zip(rev, EXPECTED_EVALS[::-1]): + assert_nan_equal(res, expected) + + def test_detEvals_too_difficult_target(self, tiny): + assert all(np.isnan(tiny.detEvals([1e-12])[0])) + + def test_detEvals_complement_unsuccessful(self, tiny): + assert list(tiny.detEvals([1e-8], complement_unsuccessful=-1)[0]) == [50, -100, 30] + assert list(tiny.detEvals([1e-8], complement_unsuccessful=lambda m: m + 1)[0]) == [50, 101, 30] + assert np.isnan(tiny.detEvals([1e-8])[0][1]) # data are not changed + + def test_detEvals_by_instance(self, tiny): + assert list(tiny.detEvals([10], instance=2)[0]) == [20] + res = tiny.detEvals_by_instance([10, 1]) + assert list(res) == [1, 2, 3] + assert [list(v[1]) for v in res.values()] == [[10], [100], [5]] + with pytest.raises(ValueError): + tiny.detEvals([10], instance=7) + + def test_detEvals_bootstrap_draws_from_data(self, tiny): + res = tiny.detEvals([1], bootstrap=True)[0] + assert len(res) == 3 and set(res) <= {10, 100, 5} + + def test_detEvals2_agrees(self, tiny): + for a, b in zip(tiny._detEvals2(TARGETS), tiny.detEvals(TARGETS)): + assert_nan_equal(a, b) + + def test_detERT_and_ert(self, tiny): + assert tiny.detERT(TARGETS) == pytest.approx(EXPECTED_ERT) + assert tiny.detERT([1e-12]) == [np.inf] + assert len(tiny.ert) == len(tiny.target) + assert tiny.ert[0] == 1 + + def test_detSuccesses(self, tiny): + assert tiny.detSuccesses(TARGETS) == EXPECTED_SUCCESSES + assert list(tiny.detSuccessRates(TARGETS)) == pytest.approx(np.array(EXPECTED_SUCCESSES) / 3) + assert tiny.successes_by_instance() == {1: 1, 2: 0, 3: 1} + + def test_detAverageEvals(self, tiny): + # sum(evals of successes + maxevals of failures) / nbRuns == ERT * success rate + expected = [1, 31 / 3, 115 / 3, 155 / 3, 155 / 3, 180 / 3] + assert list(tiny.detAverageEvals(TARGETS)) == pytest.approx(expected) + + def test_generateRLData(self, tiny): + res = tiny.generateRLData([10, 1e-8]) + assert list(res[10][1:]) == [10, 20, 1] + assert_nan_equal(res[1e-8][1:], [50, nan, 30]) + + def test_median_evals(self, tiny): + assert_nan_equal(tiny.median_evals([10, 1, 1e-8]), [10, 10, 50]) + assert tiny.median_evals()[0] == 1 + + def test_number_of_better_runs(self, tiny): + assert tiny._number_of_better_runs(1, 10) == 1.5 # 5 < 10, 10 == 10 counts 1/2 + assert tiny._number_of_better_runs([1, 1e-8], np.nan) == [3, 2] + + def test_info_str(self, tiny): + s = tiny.info_str() + assert "Algorithm: TINY" in s and "Number of trials: 3" in s + assert "min / max number of evals per trial: 30 / 100" in s + assert " 1.0e+01 | 0 0 5 10 10 | 5.2 3" in s + + +class TestInstances: + def test_instance_bookkeeping(self, tiny): + assert tiny.createDictInstance() == {1: [0], 2: [1], 3: [2]} + assert tiny.createDictInstanceCount() == {1: 1, 2: 1, 3: 1} + assert tiny.instance_index_lists() == {1: [0], 2: [1], 3: [2]} + assert tiny.trial_count_by_instance == {1: 1, 2: 1, 3: 1} + assert tiny.instance_multipliers == (1, 1, 1) + assert tiny.instancenumbers_balanced == [1, 2, 3] + assert not tiny._need_balancing + assert tiny._instance_repetitions == 0 + + def test_budget_effective_estimates(self, tiny): + assert tiny.budget_effective_estimates == {1: 50, 2: 100, 3: 30} + assert tiny._budget_estimates == {1: 50, 2: 100, 3: 30} + + def test_splitByTrials(self, tiny): + evals, funvals = tiny.splitByTrials() + assert set(evals) == {1, 2, 3} + assert evals[2].shape == (len(tiny.evals), 1, 1) + assert tiny.splitByTrials("evals").keys() == evals.keys() + assert tiny.splitByTrials("funvals")[3][0, 0, 0] == 9.5 + + def test_balancing_repeated_instances(self, tmp_path, quiet): + trials = [(1, synthdata.TINY_TRIALS[1]), (1, synthdata.TINY_TRIALS[2]), (2, synthdata.TINY_TRIALS[3])] + ds = _load_tiny(tmp_path, "REP", trials) + assert ds.instancenumbers == [1, 1, 2] + assert ds.instance_multipliers == (1, 1, 2) + assert ds.instancenumbers_balanced == (1, 1, 2, 2) + assert ds.nbRuns() == 4 and ds.nbRuns_raw == 3 + assert list(ds.maxevals) == [50, 100, 30, 30] + assert_nan_equal(ds.evals[-1, 1:], [50, nan, 30, 30]) + assert ds.detERT([1e-8]) == pytest.approx([(50 + 100 + 30 + 30) / 3]) + assert ds.detSuccesses([1e-8]) == [3] + assert ds.detSuccesses([1e-8], raw_values=True) == [2] + + def test_balancing_can_be_switched_off(self, tmp_path, quiet, monkeypatch): + trials = [(1, synthdata.TINY_TRIALS[1]), (1, synthdata.TINY_TRIALS[2]), (2, synthdata.TINY_TRIALS[3])] + ds = _load_tiny(tmp_path, "REP", trials) + monkeypatch.setattr(genericsettings, "balance_instances", False) + assert ds.nbRuns() == 3 + assert ds.detERT([1e-8]) == pytest.approx([180 / 2]) + + def test_evals_appended(self, tmp_path, quiet, monkeypatch): + trials = [(1, synthdata.TINY_TRIALS[2]), (1, synthdata.TINY_TRIALS[1]), (2, synthdata.TINY_TRIALS[3])] + ds = _load_tiny(tmp_path, "APP", trials) + assert not ds.evals_are_appended # too few remaining trials by default + monkeypatch.setattr(genericsettings, "appended_evals_minimal_trials", 2) + assert ds.evals_are_appended + app = ds.evals_appended + assert app.shape == (len(ds._evals), 3) + # instance 1: the failed first run (100 evals) is restarted with the second run + assert app[-1, 1] == 100 + 50 + + +class TestSimulatedRestarts: + def test_all_successful_target_reproduces_data(self, tiny): + sim = tiny.evals_with_simulated_restarts([1], samplesize=3)[0] + assert sorted(sim) == [5, 10, 100] + + def test_restarts_add_unsuccessful_runs(self, tiny): + sim = tiny.evals_with_simulated_restarts([1e-8], samplesize=300)[0] + assert len(sim) == 300 + # each simulated run = k * 100 (failed trial) + 50 or 30 (successful trial) + assert np.all(np.isin(sim % 100, [30, 50])) + + def test_default_samplesize(self, tiny): + sim = tiny.evals_with_simulated_restarts([1e-8])[0] + assert len(sim) == 45 # smallest multiple of nbRuns() >= 45 + + def test_unsolved_target_gives_nan(self, tiny): + assert all(np.isnan(tiny.evals_with_simulated_restarts([1e-12], samplesize=6)[0])) + + def test_within_instance_restarts(self, tiny): + sim = tiny.evals_with_simulated_restarts([1e-8], samplesize=30, instances_are_uniform=False)[0] + assert len(sim) == 30 + assert np.sum(np.isnan(sim)) == 10 # instance 2 never succeeds + assert set(sim[np.isfinite(sim)]) == {50, 30} # no restarts across instances + + def test_within_instance_restarts_singular_instances(self, tiny): + # with one trial per instance and default samplesize, the data are returned as is, + # with unsuccessful runs as nan + sim = tiny.evals_with_simulated_restarts([1e-8], instances_are_uniform=False)[0] + assert sim[0] == 50 and np.isnan(sim[1]) and sim[2] == 30 + + def test_percentiles(self, tiny): + res = tiny.percentiles([10, 1e-12], prctiles=50) + assert res[0] == 10 and np.isnan(res[1]) + assert len(tiny.percentiles([1], (10, 90))[0]) == 2 + + def test_bootstrap_sample_size(self, tiny): + assert tiny.bootstrap_sample_size(10) == 12 + assert tiny.bootstrap_sample_size(0) == 3 + + +class TestMisc: + def test_equality(self, tiny, tmp_path, quiet): + other = _load_tiny(tmp_path / "x", "TINY", synthdata.TINY_TRIALS) + assert tiny == other and not tiny != other + assert tiny._data_differ(other) == [] + other3 = _load_tiny(tmp_path / "y", "TINY", {1: synthdata.TINY_TRIALS[1], 2: synthdata.TINY_TRIALS[3]}) + assert tiny._data_differ(other3) # different data + other.algId = "OTHER" + assert tiny != other + + def test_pickle_roundtrip(self, tiny): + ds = pickle.loads(pickle.dumps(tiny)) + assert ds == tiny and not ds._data_differ(tiny) + assert ds.detERT(TARGETS) == pytest.approx(EXPECTED_ERT) + + def test_pickle_to_file(self, tiny, tmp_path): + tiny.pickle(str(tmp_path)) + assert os.path.exists(tmp_path / "ppdata_f001_02.pickle.gz") + + def test_argsort(self, tiny): + # columns sorted from best to worst: instance 3 (30 evals), 1 (50), 2 (failed) + assert list(tiny._argsort()) == [3, 1, 2] + + def test_largest_finite_index(self): + f = pproc.DataSet._largest_finite_index + assert f(np.array([1.0, 2, nan, nan])) == 1 + assert f(np.array([nan, nan])) == -1 + assert f(np.array([1.0, 2, 3])) == 2 + + def test_plot_smoke(self, tiny): + ax = tiny.plot() + assert "F1 in dimension 2" in ax.get_title() + with pytest.raises(ValueError): + tiny.plot(smallest_target=1e6) + + def test_number_of_constraints_not_available_for_bbob(self, tiny): + with pytest.raises(NotImplementedError): + tiny.number_of_constraints + + +def test_constrained_evaluations_are_summed(tmp_path, quiet): + trials = {1: [(1, 2, 95.0), (10, 30, 0.9), (50, 80, 5e-9)], 2: [(1, 2, 45.0), (20, 40, 4.5), (100, 150, 0.45)]} + ds = _load_tiny(tmp_path, "CONS", trials, suite="bbob-constrained") + assert testbedsettings.current_testbed.name == "bbob-constrained" + assert ds.has_constraints + assert ds.readmaxevals == [50, 100] # f-evaluations only + assert list(ds.maxevals) == [130, 250] # f + g from the last .tdat line + assert list(ds.evals[0, 1:]) == [3, 3] + assert list(ds.detEvals([1])[0]) == [40, 250] + assert list(ds.evals_function[0, 1:]) == [1, 1] + assert list(ds.evals_constraints[0, 1:]) == [2, 2] + assert ds.number_of_constraints == 1 + + +def test_constrained_evaluation_weights(tmp_path, quiet, monkeypatch): + trials = {1: [(1, 2, 95.0), (10, 30, 0.9), (50, 80, 5e-9)]} + monkeypatch.setattr(genericsettings, "weight_evaluations_constraints", [1, 0]) + ds = _load_tiny(tmp_path, "CONS0", trials, suite="bbob-constrained") + assert not ds.has_constraints # g-evaluations are ignored entirely + assert list(ds.detEvals([1])[0]) == [10] + + +def test_instances_of_interest_filter(tmp_path, quiet, monkeypatch): + monkeypatch.setitem(testbedsettings.GECCOBBOBTestbed.settings, "instancesOfInterest", [1, 3]) + ds = _load_tiny(tmp_path, "TINY", synthdata.TINY_TRIALS) + assert ds.instancenumbers == [1, 3] + assert_nan_equal(ds.detEvals([1e-8])[0], [50, 30]) + + +def test_old_data_format(bbob_old_format, quiet): + testbedsettings.reset_current_testbed() + dsl = pproc.DataSetList(bbob_old_format) + assert len(dsl) == 4 + ds = dsl[0] + assert ds.get_data_format() is None + assert ds.suite_name == "bbob" + assert ds.instancenumbers == list(range(1, 16)) + assert ds.evals.shape[1] == 16 + + +def test_suite_name_fallbacks(tiny): + del tiny.suite + assert tiny.suite_name == "bbob" + tiny.funcId = 101 + assert tiny.suite_name == "bbob-noisy" + tiny.suite = "sbox-cost" + assert tiny.suite_name == "bbob-boxed" + + +def test_cococommands_load(tiny, tmp_path, quiet): + info = synthdata.write_tiny_bbob(str(tmp_path / "load")) + dsl = cocopp.load(info) + assert len(dsl) == 1 and dsl[0] == tiny diff --git a/tests/unit/test_pproc_lists.py b/tests/unit/test_pproc_lists.py new file mode 100644 index 0000000..0f3c737 --- /dev/null +++ b/tests/unit/test_pproc_lists.py @@ -0,0 +1,419 @@ +"""Unit tests for `DataSetList`, the ``dictAlg*`` helpers, target value +classes and other module-level functions of `cocopp.pproc`, and for the +reference ("best") algorithm construction in `cocopp.bestalg`. +""" + +import os +import pickle + +import numpy as np +import pytest + +import cocopp +from cocopp import bestalg, pproc, testbedsettings + +import synthdata +from conftest import load + + +class TestParseInfo: + def test_parseinfo(self): + s = "suite = 'bbob', funcId = 1, DIM = 2, Precision = 1.000e-08, algId = 'A, B', coco_version = '2.6'" + assert pproc.parseinfo(s) == [ + ("suite", "bbob"), + ("funcId", 1), + ("DIM", 2), + ("Precision", 1e-08), + ("algId", "A, B"), + ("coco_version", "2.6"), + ] + + def test_parseinfo_quotes_inside_values(self): + assert pproc.parseinfo("algId = 'it's'") == [("algId", "it's")] + assert pproc.parseinfo('algId = "x"') == [("algId", "x")] + + def test_parseinfo_biobj_data_line(self): + res = dict(pproc.parseinfo("function = 2, dim = 5, f.dat, 1:500|6.1e-003")) + assert res == {"function": 2, "dim": 5} + + def test_parseinfoold(self): + assert pproc.parseinfoold("funcId = 1, algId = 'x'") == [("algId", "x"), ("funcId", 1)] + + +def test_align_list(): + assert pproc.align_list(["a", "b"], [1, 1, 2]) == ["a", "a", "b"] + + +def test_set_unique_algId(): + class D: + def __init__(self, a): + self.algId = a + + taken = [D("A"), D("B")] + new = [D("A"), D("C"), D("B")] + pproc.set_unique_algId(new, taken) + assert [d.algId for d in new] == ["A 2", "C", "B 2"] + new = [D("A")] + pproc.set_unique_algId(new, None, taken_ids=["A", "A 2"]) + assert new[0].algId == "A 3" + + +class TestTargetValues: + def test_target_values(self): + targets = [10**i for i in np.arange(2, -8.1, -0.2)] + tv = pproc.TargetValues(targets) + assert tv() == targets and tv((1, 20)) == targets + assert len(tv) == len(targets) + assert tv.short_info == "absolute targets" + assert tv.label_name() == "Df" + + def test_sorted_and_labels(self): + tv = pproc.TargetValues([1e-8, 10, 0.1]) + assert tv() == [10, 0.1, 1e-8] + assert tv.labels() == ["10", "0.1", "1e-8"] + assert tv.loglabels() == ["1", "-1", "-8"] + assert tv.label(0) == "10" and tv.loglabel(2) == "-8" + + def test_discretize(self): + tv = pproc.TargetValues([1.1e-3, 9e-2], discretize=True) + assert tv() == pytest.approx([10**-1, 10**-3]) # rounded to the 10**(i/5) grid + assert pproc.TargetValues([3.0])(discretize=True) == pytest.approx([10**0.4]) + + def test_cast_and_as_target_values(self): + tv = pproc.TargetValues([1, 2]) + assert pproc.TargetValues.cast(tv) is tv + assert pproc.TargetValues.cast([1, 2])() == [2, 1] + assert pproc.asTargetValues(tv) is tv + assert pproc.asTargetValues([1])() == [1] + rl = pproc.RunlengthBasedTargetValues([1, 10]) + assert pproc.asTargetValues(rl) is rl + with pytest.raises(NotImplementedError): + pproc.asTargetValues("x") + + +class TestRunlengthBasedTargetValues: + @pytest.fixture + def reference(self, bbob_alg_a, bbob_alg_b, quiet): + testbedsettings.reset_current_testbed() + dsl, algs, dict_alg = pproc.processInputArgs([bbob_alg_a, bbob_alg_b]) + return bestalg.generate(dict_alg, "REF") + + def test_basic_attributes(self): + rl = pproc.RunlengthBasedTargetValues([10, 0.5, 3]) + assert rl.run_lengths == [0.5, 3, 10] + assert len(rl) == 3 + assert rl.labels() == ["0.5", "3", "10"] + assert rl.loglabel(0) == "-0.30" + assert rl.label_name() == "RL/dim" + assert pproc.RunlengthBasedTargetValues([1], times_dimension=False).label_name() == "RL" + assert pproc.RunlengthBasedTargetValues.cast(rl) is rl + assert pproc.RunlengthBasedTargetValues.cast([1, 2]).run_lengths == [1, 2] + + def test_requires_fun_dim(self, reference): + with pytest.raises(ValueError): + pproc.RunlengthBasedTargetValues([1], reference_data=reference)() + + def test_targets_from_reference_data(self, reference): + run_lengths = [0.5, 1.2, 3, 10, 50] + rl = pproc.RunlengthBasedTargetValues(run_lengths, reference_data=reference, force_different_targets_factor=1) + for fun_dim in [(1, 5), (6, 2), (21, 20)]: + targets = rl(fun_dim) + ref = reference[fun_dim[::-1]] + assert len(targets) == len(run_lengths) + assert all(np.diff(targets) <= 0) and targets[-1] >= 1e-8 + # recompute: the best target reached by the reference within rl * dim, divided by 10**0.2 + for t, r in zip(targets, run_lengths): + idx = np.nonzero(ref.ert[ref.target >= 1e-8] <= max(1, r * fun_dim[1]))[0] + expected = ref.target[0] if not len(idx) else max(1e-8, (1 + 1e-9) * ref.target[idx[-1]] / 10**0.2) + assert t == pytest.approx(expected) + assert rl.reference_algorithm == "REF" + + def test_forced_different_targets(self, reference): + rl = pproc.RunlengthBasedTargetValues([1, 1.01, 1.02], reference_data=reference) + targets = rl((1, 5)) + assert targets[1] == pytest.approx(targets[0] / 10**0.04) + + def test_unique_and_discretized_targets(self, reference): + rl = pproc.RunlengthBasedTargetValues([1, 1.01, 1.02, 1e9], reference_data=reference, force_different_targets_factor=1, unique_target_values=True) + targets = rl((1, 5)) + assert len(targets) == len(set(targets)) < 4 + disc = pproc.RunlengthBasedTargetValues([1, 10], reference_data=reference)((1, 5), discretize=True) + assert np.log10(disc) * 5 == pytest.approx(np.round(np.log10(disc) * 5)) + + def test_shipped_reference_algorithm(self, quiet): + cocopp.config.config("bbob") + rl = pproc.RunlengthBasedTargetValues([0.5, 1.2, 3, 10, 50]) + t = rl(fun_dim=(1, 20)) + assert 6.30957345e01 <= t[0] <= 6.30957346e01 # from the package doctest + assert t[-1] == 1e-8 + assert rl.reference_algorithm == "refalgs/best2009-bbob.tar.gz" + + +class TestDataSetList: + def test_load_folder(self, dsl_small): + assert len(dsl_small) == 8 + assert isinstance(dsl_small, pproc.DataSetList) + # sorted by dimension, then function + assert [(ds.dim, ds.funcId) for ds in dsl_small] == [(d, f) for d in (2, 5) for f in (1, 2, 8, 15)] + assert all(ds.algId == "SMALL" for ds in dsl_small) + assert dsl_small.current_testbed is testbedsettings.current_testbed + + def test_from_datasets_and_empty(self, dsl_small): + assert pproc.DataSetList() == [] + assert pproc.DataSetList(list(dsl_small)) == dsl_small + + def test_append_rejects_non_datasets(self, dsl_small): + dsl = pproc.DataSetList() + with pytest.warns(UserWarning): + dsl.append(3) + with pytest.raises(ValueError): + dsl.append(3, check_data_type=True) + + def test_append_same_dataset_twice_is_skipped(self, dsl_small): + with pytest.warns(UserWarning, match="found twice"): + dsl_small.append(pickle.loads(pickle.dumps(dsl_small[0]))) + assert len(dsl_small) == 8 + + def test_append_merges_trials(self, tmp_path, quiet): + """two index entries of the same algorithm/function/dimension are merged""" + a = synthdata.write_tiny_bbob(str(tmp_path / "a"), "M", trials={1: synthdata.TINY_TRIALS[1]}) + b = synthdata.write_tiny_bbob(str(tmp_path / "b"), "M", trials={2: synthdata.TINY_TRIALS[2], 3: synthdata.TINY_TRIALS[3]}) + testbedsettings.reset_current_testbed() + dsl = pproc.DataSetList([a, b]) + assert len(dsl) == 1 + ds = dsl[0] + assert ds.instancenumbers == [1, 2, 3] + assert list(ds.maxevals) == [50, 100, 30] + assert ds.detERT([1, 1e-8]) == pytest.approx([115 / 3, 90]) + assert ds.indexFiles == [a, b] + + def test_append_merges_file_lists_once(self, tmp_path, quiet): + a = synthdata.write_tiny_bbob(str(tmp_path / "a"), "M", trials={1: synthdata.TINY_TRIALS[1]}) + b = synthdata.write_tiny_bbob(str(tmp_path / "b"), "M", trials={2: synthdata.TINY_TRIALS[2]}) + testbedsettings.reset_current_testbed() + ds = pproc.DataSetList([a, b])[0] + assert ds.indexFiles == [a, b] + assert len(ds.dataFiles) == 2 + + def test_missing_input_warns(self, tmp_path, capsys): + with pytest.warns(UserWarning, match="not found"): + dsl = pproc.DataSetList([str(tmp_path / "nothing.xyz")]) + assert len(dsl) == 0 + + def test_dict_by(self, dsl_small): + assert sorted(dsl_small.dictByDim()) == [2, 5] + assert sorted(dsl_small.dictByFunc()) == [1, 2, 8, 15] + assert list(dsl_small.dictByAlg()) == [("SMALL", "")] + assert dsl_small.by("dim").keys() == dsl_small.dictByDim().keys() + assert sorted(dsl_small.dictByDimFunc()[5]) == [1, 2, 8, 15] + assert list(dsl_small.dictByNoise()) == ["noiselessall"] + assert sorted(dsl_small.dictByParam("dim")) == [2, 5] + assert list(dsl_small.dictByParam("nonexisting")) == [None] + + def test_function_groups(self, dsl_small): + groups = dsl_small.dictByFuncGroup() + assert {k: sorted({ds.funcId for ds in v}) for k, v in groups.items()} == {"separ": [1, 2], "lcond": [8], "multi": [15]} + assert list(dsl_small.getFuncGroups().items()) == [ + ("separ", "Separable functions"), + ("lcond", "Misc. moderate functions"), + ("multi", "Multi-modal functions"), + ] + assert not dsl_small.isBiobjective() + + def test_filter_and_filtered(self, dsl_small): + unimodal = dsl_small.filtered(lambda ds: ds.funcId < 10) + assert isinstance(unimodal, pproc.DataSetList) and len(unimodal) == 6 + assert dsl_small.filtered(None) == [] and len(dsl_small.filtered(True, list)) == 8 + removed = dsl_small.filter(lambda ds: ds.dim == 2, verbose=2) + assert len(removed) == 4 and len(dsl_small) == 4 + dsl_small.remove_if(lambda ds: ds.funcId == 15) + assert sorted(ds.funcId for ds in dsl_small) == [1, 2, 8] + assert dsl_small.filter(None) is None and len(dsl_small) == 3 + + def test_sort(self, dsl_small): + dsl_small.sort(key1="funcId", key2="dim") + assert [(ds.funcId, ds.dim) for ds in dsl_small][:3] == [(1, 2), (1, 5), (2, 2)] + + def test_info(self, dsl_small, capsys): + dsl_small.info("all") + out = capsys.readouterr().out + assert "8 data set(s)" in out and "Algorithm(s): SMALL" in out + assert "4 Functions with IDs 1, 2, 8, 15" in out + assert "Dimension(s): 2, 5" in out + assert "Max evals: [400, 1000]" in out + pproc.DataSetList().info() + assert capsys.readouterr().out == "[]\n" + + def test_get_all_data_lines_and_best_lines(self, dsl_small): + lines, scores = dsl_small.get_all_data_lines(1e-1, 1, 5) + assert len(lines) == 1 and scores == dsl_small[4].detERT([1e-1]) + with pytest.warns(UserWarning): + best, best_scores = dsl_small.det_best_data_lines([10, 1e-1], 1, 5) + assert list(best_scores) == dsl_small[4].detERT([10, 1e-1]) + + @pytest.mark.xfail(reason="known bug: np.sort on dict_values in det_best_data", strict=True) + def test_det_best_data(self, dsl_small): + dsl_small.det_best_data([10, 1e-1], 1, 5) + + @pytest.mark.xfail(reason="known bug: dict.values()[0] in run_length_distributions (py2 code)", raises=TypeError, strict=True) + def test_run_length_distributions_single_alg(self, dsl_small): + dsl_small.run_length_distributions(5, [10, 1e-1]) + + def test_run_length_distributions(self, dsl_small): + rld, left = dsl_small.run_length_distributions(5, [10, 1e-1], flatten_output_dict=False) + data, solved, processed = rld[("SMALL", "")] + assert processed == [1, 2, 8, 15] + assert len(data) == 4 * 2 * 15 + assert np.all(np.diff(data[np.isfinite(data)]) >= 0) + # the left envelope of a single algorithm is its data, with inf instead of nan + np.testing.assert_array_equal(left, np.where(np.isnan(data), np.inf, data)) + + def test_reference_values_hash(self, dsl_small, biobj_alg, quiet): + assert dsl_small.get_reference_values_hash() is None + testbedsettings.reset_current_testbed() + dsl = load(biobj_alg) + h = dsl.get_reference_values_hash() + assert isinstance(h, str) and len(h) == 16 + assert h == load(biobj_alg).get_reference_values_hash() + + def test_is_same_list_assert(self, dsl_small, bbob_small): + dsl_small._is_same_list_assert(load(bbob_small)) + + +class TestDictAlg: + @pytest.fixture + def dict_alg(self, bbob_alg_a, bbob_alg_b, quiet): + testbedsettings.reset_current_testbed() + dsl, algs, dict_alg = pproc.processInputArgs([bbob_alg_a, bbob_alg_b]) + return dsl, algs, dict_alg + + def test_processInputArgs(self, dict_alg, bbob_alg_a, bbob_alg_b): + dsl, algs, d = dict_alg + assert algs == [bbob_alg_a, bbob_alg_b] + assert list(d) == algs + assert len(dsl) == 240 + assert all(ds._data_folder == bbob_alg_a for ds in d[bbob_alg_a]) + assert set(testbedsettings.reference_values) == {"SYNTH-A", "SYNTH-B"} + + def test_processInputArgs_errors(self, tmp_path): + with pytest.raises(Exception, match="could not be found"): + pproc.processInputArgs([str(tmp_path / "missing")]) + single = tmp_path / "a.info" + single.write_text("") + with pytest.warns(UserWarning, match="single file"): + assert pproc.processInputArgs([str(single)])[0] == [] + + def test_same_algorithm_twice_gets_unique_names(self, bbob_small, quiet): + testbedsettings.reset_current_testbed() + dsl, algs, d = pproc.processInputArgs([bbob_small, bbob_small + os.sep]) + assert algs == [bbob_small] # trailing separators are stripped + assert {ds.algId for ds in dsl} == {"SMALL", "SMALL 2"} + + def test_by_dim(self, dict_alg): + _, algs, d = dict_alg + by_dim = pproc.dictAlgByDim(d) + assert sorted(by_dim) == [2, 3, 5, 10, 20] + assert list(by_dim[5]) == algs and len(by_dim[5][algs[0]]) == 24 + assert pproc.DictAlg(d).by_dim().keys() == by_dim.keys() + by_dim2 = pproc.dictAlgByDim2(d) + assert {k: {a: len(v) for a, v in x.items()} for k, x in by_dim2.items()} == { + k: {a: len(v) for a, v in x.items()} for k, x in by_dim.items() + } + + def test_by_fun_noise_group(self, dict_alg): + _, algs, d = dict_alg + assert sorted(pproc.dictAlgByFun(d)) == list(range(1, 25)) + assert list(pproc.dictAlgByNoi(d)) == ["noiselessall"] + groups = pproc.dictAlgByFuncGroup(d) + assert sorted(groups) == ["hcond", "lcond", "mult2", "multi", "separ"] + assert len(groups["separ"][algs[1]]) == 5 * 5 + + def test_missing_dimension_is_filled_with_empty_list(self, bbob_small, bbob_alg_a, quiet): + testbedsettings.reset_current_testbed() + _, algs, d = pproc.processInputArgs([bbob_small, bbob_alg_a]) + by_dim = pproc.dictAlgByDim(d) + assert len(by_dim[20][bbob_small]) == 0 and len(by_dim[20][bbob_alg_a]) == 24 + + +class TestGetDataSetList: + def test_pickle_cache_for_archives(self, tmp_path, bbob_small, quiet, monkeypatch): + import tarfile + + archive = str(tmp_path / "SMALL.tgz") + with tarfile.open(archive, "w:gz") as tar: + tar.add(bbob_small, arcname="SMALL") + monkeypatch.chdir(tmp_path) + testbedsettings.reset_current_testbed() + dsl1 = pproc.get_DataSetList(archive) + assert pproc._pickle_file_read == "" + assert os.path.exists(archive + ".pickle") + assert os.path.isdir(tmp_path / ".extracted_SMALL") + dsl2 = pproc.get_DataSetList(archive) + assert pproc._pickle_file_read == archive + ".pickle" + dsl2._is_same_list_assert(dsl1) + monkeypatch.setattr(pproc, "_ignore_pickle_files", True) + dsl3 = pproc.get_DataSetList(archive) + assert pproc._pickle_file_read == "" + dsl3._is_same_list_assert(dsl1) + + def test_folders_are_not_pickled(self, bbob_small, quiet): + testbedsettings.reset_current_testbed() + dsl = pproc.get_DataSetList(bbob_small) + assert len(dsl) == 8 + assert not os.path.exists(bbob_small + ".pickle") + + +class TestBestAlg: + @pytest.fixture + def algs(self, bbob_alg_a, bbob_alg_b, quiet): + testbedsettings.reset_current_testbed() + return pproc.processInputArgs([bbob_alg_a, bbob_alg_b]) + + def test_best_ert_is_minimum_of_algorithm_erts(self, algs): + dsl, names, dict_alg = algs + best = bestalg.generate(dict_alg, "BEST") + assert len(best) == 24 * 5 + targets = [100, 10, 1, 1e-1, 1e-3, 1e-5, 1e-7, 1e-8] + for (dim, fun), ba in list(best.items())[::7]: + erts = [pproc.DataSetList(dict_alg[a]).dictByDimFunc()[dim][fun][0].detERT(targets) for a in names] + assert ba.detERT(targets) == pytest.approx(np.min(erts, axis=0)) + assert (ba.dim, ba.funcId, ba.algId) == (dim, fun, "BEST") + assert set(ba.best_algorithm_data) <= set(names) + assert repr(ba) == "{alg: BEST, F%d, dim: %d}" % (fun, dim) + assert ba.comment == "Combination of " + ", ".join(names) + + def test_best_detEvals_comes_from_best_algorithm(self, algs): + dsl, names, dict_alg = algs + ba = bestalg.generate(dict_alg, "BEST")[(5, 1)] + evals, alg_names, _ = ba.detEvals([1e-1, 1e-12]) + source = pproc.DataSetList(dict_alg[alg_names[0]]).dictByDimFunc()[5][1][0] + np.testing.assert_array_equal(evals[0], source.detEvals([1e-1])[0]) + assert alg_names[1] is None and np.all(np.isnan(evals[1])) + assert ba.get_success_ratio(1e-1) == (np.sum(np.isfinite(evals[0])), 15) + + def test_custom_generate_roundtrip(self, algs, tmp_path, monkeypatch, bbob_alg_a, bbob_alg_b): + monkeypatch.chdir(tmp_path) + bestalg.custom_generate([bbob_alg_a, bbob_alg_b], "myref") + assert os.path.exists(tmp_path / "myref.tar.gz") + assert os.path.exists(tmp_path / "myref" / "bbob-bestalg.info") + testbedsettings.reset_current_testbed() + entries = bestalg.load_reference_algorithm(str(tmp_path / "myref"), force=True, relative_load=False) + direct = bestalg.generate(algs[2], "BEST") + targets = [10, 1e-1, 1e-8] + for key in [(2, 1), (5, 8), (20, 24)]: + # the written ERTs are rounded to integers + assert entries[key].detERT(targets) == pytest.approx(direct[key].detERT(targets), abs=0.5) + + def test_load_reference_algorithm(self, quiet): + bestalg.reset_reference_algorithm() + assert bestalg.bestAlgorithmEntries == {} + cocopp.config.config("bbob") + entries = bestalg.load_reference_algorithm("refalgs/best2009-bbob.tar.gz") + assert len(entries) == 24 * 6 + assert bestalg.load_reference_algorithm("anything") is entries # cached unless forced + assert bestalg.load_reference_algorithm("", force=True) is None + + def test_get_used_instance_list(self): + assert bestalg.get_used_instance_list([[1, 2], [1, 2]]) == "1,2" + assert bestalg.get_used_instance_list([[1, 2], [3]]) == "[1,2],[3]" + assert bestalg.get_used_instance_list([]) is None diff --git a/tests/unit/test_pptex.py b/tests/unit/test_pptex.py new file mode 100644 index 0000000..cb6eead --- /dev/null +++ b/tests/unit/test_pptex.py @@ -0,0 +1,86 @@ +"""Unit tests for the LaTeX/number formatting helpers in `cocopp.pptex`. + +Expected strings are characterization values of the current behavior; +they end up verbatim in the generated tables. +""" + +import numpy as np +import pytest + +from cocopp import pptex + +inf = np.inf + +# value: writeFEvals, writeFEvals2, writeFEvals2(.., 3), writeFEvals2(.., isscientific=True), +# writeFEvalsMaxSymbols(.., 3), writeFEvalsMaxSymbols(.., 5), +# writeFEvalsMaxPrec(.., 2), writeFEvalsMaxPrec(.., 3, maxfloatrepr=1e3) +FORMATS = [ + (1, ["1", "1", "1", "1.0e0", "1", "1", "1", "1"]), + (7, ["7", "7", "7", "7.0e0", "7", "7", "7.0", "7.00"]), + (12.5, ["12", "12", "12.5", "1.2e1", "12.5", "12.5", "12", "12.5"]), + (123.456, ["120", "120", "123", "1.2e2", "123", "123.46", "123", "123"]), + (1234.5, ["1200", "1200", "1230", "1.2e3", "1e3", "1234.5", "1234", "1.23e3"]), + (102345, ["1e5", "1.0e5", "1.02e5", "1.0e5", "1e5", "1.02e5", "1.0e5", "1.02e5"]), + (1e7, ["1e7", "1.0e7", "1.00e7", "1.0e7", "1e7", "1.00e7", "1.0e7", "1.00e7"]), + (0.5, ["0.5", "0.5", "0.5", "5.0e-1", "0.5", "0.5", "0.50", "0.500"]), + (0.0123, ["0.012", "0.012", "0.0123", "1.2e-2", "0.01", "0.0123", "0.01", "0.012"]), + (inf, 8 * [r"$\infty$"]), +] + + +@pytest.mark.parametrize("val, expected", FORMATS) +def test_number_formats(val, expected): + got = [ + pptex.writeFEvals(val), + pptex.writeFEvals2(val), + pptex.writeFEvals2(val, 3), + pptex.writeFEvals2(val, isscientific=True), + pptex.writeFEvalsMaxSymbols(val, 3), + pptex.writeFEvalsMaxSymbols(val, 5), + pptex.writeFEvalsMaxPrec(val, 2), + pptex.writeFEvalsMaxPrec(val, 3, maxfloatrepr=1e3), + ] + assert got == expected + + +@pytest.mark.parametrize("val, as_int, as_float", [(5, "5", "5"), (123456, "1.2e5", "123456"), (12345678901234, "1e13", "12345678901234")]) +def test_writeFEvals2_maxdigits(val, as_int, as_float): + assert pptex.writeFEvals2(val, maxdigits=5) == as_int + # quirk: maxdigits is only honored for `int` input + assert pptex.writeFEvals2(float(val), maxdigits=5) == as_float + + +@pytest.mark.parametrize("func", [pptex.writeFEvals2, lambda x: pptex.writeFEvalsMaxSymbols(x, 3), lambda x: pptex.writeFEvalsMaxPrec(x, 2)]) +def test_nan_is_rejected(func): + with pytest.raises(AssertionError): + func(np.nan) + + +def test_numtotext(): + assert [pptex.numtotext(n) for n in (0, 25, 26, 51, 52, 53, 104, 2755)] == ["A", "Z", "a", "z", "AA", "AB", "BA", "zz"] + with pytest.raises(Exception): + pptex.numtotext(2756) + + +def test_labels_colors_markers(): + assert pptex.writeLabels("a_b_c") == r"a\_b\_c" + assert pptex.color_to_latex("k") == r"\color{Black}" + assert pptex.color_to_latex("0.5") == r"\color[gray]{0.5}" + assert pptex.color_to_latex("#123456") == "{}" + assert pptex.marker_to_latex("o") == r"$\circ$" + assert pptex.marker_to_html("o") == "○" + + +def test_table_latex(): + table = [["a", "b"], ["c", "d"]] + # quirk: extra_eol is also appended to the last line, which has no \\ + assert pptex.tableLaTeX(table, spec="cc", extra_eol=[r"\hline"]) == "\\begin{tabular}{cc}\na & b\\\\\\hline\nc & d\\hline\n\\end{tabular}" + # without the end of the tabular, the last line is terminated with \\ + assert pptex.tableLaTeX(table, spec="cc", add_begin_tabular=False, add_end_tabular=False) == "a & b\\\\\nc & d\\\\" + assert pptex.tableXLaTeX(table, spec="cc") == "\\begin{tabularx}{1.0\\textwidth}{cc}\na & b\\\\\nc & d\n\\end{tabularx}" + assert pptex.tableLaTeXStar(table, width="5cm", spec="cc") == "\\begin{tabular*}{5cm}{cc}\na & b\\\\\nc & d\n\\end{tabular*}" + + +def test_wrong_input_size_error(): + # quirk: __str__ returns the repr of the message + assert str(pptex.WrongInputSizeError("arr", (2, 3), (3, 3))) == repr("The size of arr is (2, 3). One dimension must be of length (3, 3)!") diff --git a/tests/unit/test_readalign.py b/tests/unit/test_readalign.py new file mode 100644 index 0000000..ea67ea4 --- /dev/null +++ b/tests/unit/test_readalign.py @@ -0,0 +1,124 @@ +"""Unit tests for `cocopp.readalign` (reading and aligning raw data files).""" + +import numpy as np +import pytest + +from cocopp import dataformatsettings, readalign, testbedsettings, pproc + +nan = np.nan + + +@pytest.fixture +def bbob_format(monkeypatch): + """set the module globals `readalign` relies upon""" + monkeypatch.setattr(dataformatsettings, "current_data_format", dataformatsettings.BBOBNewDataFormat()) + monkeypatch.setattr(testbedsettings, "current_testbed", testbedsettings.GECCOBBOBTestbed(pproc.TargetValues)) + + +def _write(path, text): + path.write_text(text) + return str(path) + + +class TestSplit: + def test_split_trials(self, tmp_path): + fn = _write( + tmp_path / "a.dat", + "% instance = 1, reference value = 0.5\n1 0 10 0 0\n5 0 1 0 0\n" + "% instance = 2, reference value = 0.25\n1 0 Inf 0 0\n2 0 -inf nan NaN\n", + ) + data, algs, refs, succ = readalign.split([fn]) + assert len(data) == 2 + np.testing.assert_array_equal(data[0], [[1, 0, 10, 0, 0], [5, 0, 1, 0, 0]]) + assert data[1][0, 2] == np.inf and data[1][1, 2] == -np.inf and np.isnan(data[1][1, 3]) + assert refs == {1: 0.5, 2: 0.25} + assert algs == [] and succ == [] + + def test_split_with_index_filter(self, tmp_path): + fn = _write(tmp_path / "a.dat", "%\n1 0 1 0 0\n%\n1 0 2 0 0\n%\n1 0 3 0 0\n") + data = readalign.split([fn], idx_to_load=[True, False, True])[0] + assert [d[0, 2] for d in data] == [1, 3] + + def test_split_reads_several_files(self, tmp_path): + f1 = _write(tmp_path / "a.dat", "%\n1 0 1 0 0\n") + f2 = _write(tmp_path / "b.dat", "%\n1 0 2 0 0\n") + assert len(readalign.split([f1, f2])[0]) == 2 + + def test_split_best_algorithm_columns(self, tmp_path): + fn = _write(tmp_path / "best.dat", "% algorithm type = best\n1 0 1 0 0 ALG1 3 15\n2 0 0.5 0 0 ALG2 15 15\n") + data, algs, refs, succ = readalign.split([fn]) + assert algs == ["ALG1", "ALG2"] + assert succ == [[3, 15], [15, 15]] + assert data[0].shape == (2, 5) + + def test_split_invalid_number_and_incomplete_line(self, tmp_path): + # with `dim` given, lines must have dim + 5 entries + fn = _write(tmp_path / "a.dat", "%\n1 0 x 0 0 7\n1 2\n") + with pytest.warns(UserWarning): + data = readalign.split([fn], dim=1) + assert len(data[0]) == 1 and data[0][0].shape == (1, 6) and np.isnan(data[0][0][0, 2]) + + def test_openfile_missing(self, tmp_path): + with pytest.raises(IOError): + readalign.openfile(str(tmp_path / "missing.dat")) + + +def test_is_close(): + assert readalign.is_close(1.0, 1.0 + 1e-12) + assert not readalign.is_close(1.0, 1.001) + assert readalign.is_close(0.0, 1e-12, abs_tol=1e-10) + + +class TestAlignment: + def test_horizontal_alignment(self, bbob_format): + # two trials: Delta f (column 2) over evaluations (column 0) + t1 = np.array([[1, 0, 50.0], [10, 0, 2.0], [20, 0, 0.05]]) + t2 = np.array([[1, 0, 9.0], [3, 0, 0.5]]) + evals, maxevals, finalf = readalign.align_data(readalign.HMultiReader([t1, t2]), 0, 2) + assert list(maxevals) == [20, 3] + assert list(finalf) == [0.05, 0.5] + # first row: smallest 10**(i/5) not smaller than the largest initial f-value + assert evals[0, 0] == pytest.approx(10 ** (np.ceil(np.log10(50) * 5) / 5)) + assert list(evals[0, 1:]) == [1, 1] + # rows are monotonously decreasing targets with non-decreasing evals + assert np.all(np.diff(evals[:, 0]) < 0) + e = np.where(np.isnan(evals[:, 1:]), np.inf, evals[:, 1:]) + assert np.all(np.diff(e, axis=0) >= 0) + # the last row is the smallest 10**(i/5) target reached, i.e. the one just above 0.05 + assert evals[-1, 0] == pytest.approx(10 ** (-6 / 5)) + assert evals[-1, 1] == 20 and np.isnan(evals[-1, 2]) + + def test_vertical_alignment(self, bbob_format): + t1 = np.array([[1, 0, 50.0], [10, 0, 2.0], [20, 0, 0.05]]) + t2 = np.array([[1, 0, 9.0], [3, 0, 0.5]]) + funvals, maxevals, finalf = readalign.align_data(readalign.VMultiReader([t1, t2]), 0, 2) + np.testing.assert_array_equal(funvals, [[1, 50, 9], [3, 50, 0.5], [10, 2, 0.5], [20, 0.05, 0.5]]) + assert list(maxevals) == [20, 3] + + def test_negative_fvalues(self, bbob_format): + t1 = np.array([[1, 0, 5.0], [4, 0, -2.0]]) + evals, _, finalf = readalign.align_data(readalign.HMultiReader([t1]), 0, 2) + assert finalf[0] == -2 + assert evals[-1, 0] <= 0 and evals[-1, 1] == 4 + + def test_rewind_reader(self, bbob_format): + t1 = np.array([[1, 7, 5.0], [4, 8, 0.1]]) + reader = readalign.HMultiReader([t1]) + e1 = readalign.align_data(reader, 0, 2)[0] + e2 = readalign.align_data(reader, 1, 2, rewind_reader=True)[0] + assert list(e1[:, 1]) == [1, 4] and list(e2[:, 1]) == [7, 8] + + def test_align_data_type_check(self): + with pytest.raises(TypeError): + readalign.align_data([np.zeros((2, 3))], 0, 2, rewind_reader=True) + + def test_empty_arrays_are_skipped(self, bbob_format): + assert len(readalign.HMultiReader([np.zeros((0, 3)), np.ones((2, 3))])) == 1 + with pytest.raises(ValueError): + readalign.MultiReader.SingleReader([]) + + def test_array_alignment_merges_columns(self, bbob_format): + a = np.array([[1, 50.0, 9.0], [3, 50, 0.5]]) + b = np.array([[1, 7.0], [2, 1.0]]) + res = readalign.alignArrayData(readalign.VArrayMultiReader([a, b])) + np.testing.assert_array_equal(res, [[1, 50, 9, 7], [2, 50, 9, 1], [3, 50, 0.5, 1]]) diff --git a/tests/unit/test_reduce_size_and_sanitycheck.py b/tests/unit/test_reduce_size_and_sanitycheck.py new file mode 100644 index 0000000..867dc89 --- /dev/null +++ b/tests/unit/test_reduce_size_and_sanitycheck.py @@ -0,0 +1,100 @@ +"""Unit tests for the data-file utilities `cocopp.reduce_size` and `cocopp.sanitycheck`.""" + +import os +import warnings + +import pytest + +from cocopp import reduce_size as rs +from cocopp import sanitycheck +from cocopp.ppfig import Usage + +import synthdata + +HEADER = "% f evaluations | g evaluations | best noise-free fitness - Fopt | ...\n" + + +class TestReduceSize: + def test_target_hit(self): + hit = rs.TargetHit(20, 1e-8) + assert hit("1 0 100 x\n") + assert not hit("2 0 100 x\n") # same f-value: no new target + assert hit("3 0 50 x\n") + assert not hit("4 0 49 x\n") # within the same 10**(1/20) band + assert hit("5 0 1e-9 x\n") # final target + assert not hit("6 0 1e-12 x\n") # nothing after the final target + assert hit("1 0 100 x\n") # evaluations decreased: new run, reset + + def test_reduce_dat(self): + lines = [HEADER] + ["%d 0 %g 0 0\n" % (e, f) for e, f in [(1, 100), (2, 100), (3, 99), (4, 50), (5, 49), (6, 48)]] + res = rs.reduce_dat(lines) + assert res[0] == HEADER + # lines hitting a new 10**(-1/20) target band are kept, others are overwritten + # by the next line, hence the last line is always kept + assert [line.split()[0] for line in res[1:]] == ["1", "4", "6"] + + def test_reduce_tdat_keeps_first_and_last(self): + lines = [HEADER] + ["%d 0 1 0 0\n" % e for e in range(1, 9)] + [HEADER] + ["%d 0 1 0 0\n" % e for e in (1, 2, 3)] + res = rs.reduce_tdat(lines) + evals = [line.split()[0] if not line.startswith("%") else "%" for line in res] + assert evals == ["%", "1", "8", "%", "1", "3"] + + def test_remove_x(self): + lines = [HEADER, "1 0 3 4 5 6 7 8\n"] + assert rs.remove_x(lines) == [HEADER, "1 0 3 4 5\n"] + + @pytest.mark.parametrize( + "name, dat, tdat, x", + [("f_DIM2.dat", True, False, False), ("f_DIM10.tdat", False, True, True), ("f_DIM10.mdat", False, False, True), ("f.info", False, False, False)], + ) + def test_conditions(self, name, dat, tdat, x): + assert rs.reduce_dat_condition(name) == dat + assert rs.reduce_tdat_condition(name) == tdat + assert rs.remove_x_condition(name) == x + + def test_main_on_synthetic_data(self, tmp_path, quiet): + import cocopp + + folder = synthdata.write_bbob(str(tmp_path), "RED", functions=(1,), dims=(2, 10), seed=5) + before = cocopp.pproc.DataSetList(folder) + rs.main(folder) + reduced = [d for d in os.listdir(tmp_path) if d.startswith("RED-")] + assert len(reduced) == 1 + red_folder = os.path.join(str(tmp_path), reduced[0]) + size = lambda f: sum(os.path.getsize(os.path.join(r, n)) for r, _, ns in os.walk(f) for n in ns) # noqa: E731 + assert size(red_folder) < size(folder) + after = cocopp.pproc.DataSetList(red_folder) + # the reduction must not change the post-processed data + for ds0, ds1 in zip(before, after): + assert not ds0._data_differ(ds1) + assert list(ds0.maxevals) == list(ds1.maxevals) + + +class TestSanityCheck: + def test_is_correct_instances(self): + assert sanitycheck.is_correct_instances(list(range(1, 16))) + assert sanitycheck.is_correct_instances(3 * [1, 2, 3, 4, 5]) + assert not sanitycheck.is_correct_instances([1, 2, 3]) + + def test_checkinfofile_ok(self, bbob_small, capsys): + sanitycheck.checkinfofile(os.path.join(bbob_small, "bbobexp_f1.info")) + out = capsys.readouterr().out + # quirk: the reported line numbers are 0-based + assert "entry l0-2 is ok" in out and "entry l3-5 is ok" in out + + def test_checkinfofile_wrong_instances(self, tmp_path): + info = synthdata.write_tiny_bbob(str(tmp_path)) + with pytest.raises(Usage, match="instances"): + sanitycheck.checkinfofile(info, verbose=False) + + def test_checkinfofile_missing_data(self, tmp_path): + info = synthdata.write_tiny_bbob(str(tmp_path)) + os.remove(os.path.join(os.path.dirname(info), "data_f1", "bbobexp_f1_DIM2.tdat")) + with pytest.raises(IOError): + sanitycheck.checkinfofile(info, verbose=False) + + @pytest.mark.xfail(reason="known bug: sanitycheck.main uses rungeneric.shortoptlist which does not exist", raises=AttributeError, strict=True) + def test_main(self, bbob_small): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + sanitycheck.main([bbob_small]) diff --git a/tests/unit/test_settings_and_helpers.py b/tests/unit/test_settings_and_helpers.py new file mode 100644 index 0000000..d67d29b --- /dev/null +++ b/tests/unit/test_settings_and_helpers.py @@ -0,0 +1,319 @@ +"""Unit tests for settings modules (`testbedsettings`, `genericsettings`, +`config`, `dataformatsettings`), `findfiles`, `captions` and small helpers +of `ppfig`. +""" + +import os +import tarfile +import zipfile + +import numpy as np +import pytest + +import cocopp +from cocopp import captions, config, dataformatsettings, findfiles, genericsettings, ppfig, pproc +from cocopp import testbedsettings as tbs + +# suite name -> (testbed class name, .name, first and last function, instances_are_uniform, has_constraints) +TESTBEDS = { + "bbob": ("GECCOBBOBTestbed", "bbob", 1, 24, True, False), + "bbob-noisy": ("GECCOBBOBNoisyTestbed", "bbob", 101, 130, True, False), # quirk: name is 'bbob' + "bbob-biobj": ("GECCOBiObjBBOBTestbed", "bbob-biobj", 1, 55, False, False), + "bbob-biobj-ext": ("GECCOBiObjExtBBOBTestbed", "bbob-biobj-ext", 1, 92, False, False), + "bbob-constrained": ("CONSBBOBTestbed", "bbob-constrained", 1, 54, True, True), + "bbob-largescale": ("BBOBLargeScaleTestbed", "bbob-largescale", 1, 24, True, False), + "bbob-mixint": ("GECCOBBOBMixintTestbed", "bbob-mixint", 1, 24, True, False), + "bbob-biobj-mixint": ("GECCOBBOBBiObjMixintTestbed", "bbob-biobj-mixint", 1, 92, False, False), + "bbob-JOINED-bbob-largescale": ("BBOBLargeScaleJOINEDTestbed", "bbob", 1, 24, True, False), + "bbob-boxed": ("SBOXCOSTTestbed", "bbob-boxed", 1, 24, True, False), + "sbox-cost": ("SBOXCOSTTestbed", "bbob-boxed", 1, 24, True, False), + "bbob-JOINED-bbob-boxed": ("SboxCostJOINEDTestbed", "bbob and bbob-boxed", 1, 24, True, False), +} + + +class TestTestbedSettings: + def test_all_suites_are_tested(self): + assert set(TESTBEDS) == set(tbs.suite_to_testbed) + + @pytest.mark.parametrize("suite", sorted(TESTBEDS)) + def test_load_current_testbed(self, suite): + cls, name, first, last, uniform, constraints = TESTBEDS[suite] + tb = tbs.load_current_testbed(suite, pproc.TargetValues) + assert tb is tbs.current_testbed + assert type(tb).__name__ == cls == tbs.get_testbed_from_suite(suite) + assert tbs.SuiteClass(suite) is type(tb) + assert (tb.name, tb.first_function_number, tb.last_function_number) == (name, first, last) + assert tb.instances_are_uniform is uniform and tb.has_constraints is constraints + assert tb.string_evals == ("evaluations" if constraints else "$f$-evaluations") + assert tb.string_evals_short == ("evals" if constraints else "FEvals") + # target value attributes are converted to the given class + assert isinstance(tb.pprldmany_target_values, pproc.TargetValues) + + def test_unknown_suite(self): + with pytest.raises(ValueError): + tbs.get_testbed_from_suite("bbob-unknown") + with pytest.raises(ValueError): + tbs.load_current_testbed("bbob-unknown", pproc.TargetValues) + + def test_reset(self): + tbs.load_current_testbed("bbob", pproc.TargetValues) + tbs.reset_current_testbed() + assert tbs.current_testbed is None + + def test_reference_values(self): + tbs.reset_reference_values() + assert tbs.get_reference_values(None) is None and tbs.get_first_reference_values() is None + tbs.update_reference_values("A", "h1") + tbs.copy_reference_values("A", "B") + assert tbs.get_reference_values("B") == "h1" and tbs.get_reference_values(None) == "h1" + with pytest.warns(UserWarning, match="different"): + tbs.update_reference_values("C", "h2") + assert tbs.get_reference_values(None) == {"h1", "h2"} + assert tbs.get_first_reference_values() == "h1" + tbs.reset_reference_values() + assert tbs.reference_values == {} + + def test_info_and_short_names(self): + tb = tbs.load_current_testbed("bbob", pproc.TargetValues) + assert tb.info(1) == "F1 Sphere" + assert tb.info().startswith("Testbed of 24 noiseless functions") + names = tbs.get_short_names("bbob-benchmarkshortinfos.txt") + assert sorted(names) == list(range(1, 25)) and names[1] == "Sphere" + with pytest.warns(UserWarning): + assert tbs.get_short_names("nonexisting.txt") is None + + def test_constraints(self): + assert [tbs.CONSBBOBTestbed.number_of_constraints(10, f) for f in range(1, 8)] == [1, 3, 9, 16, 24, 54, 1] + assert tbs.CONSBBOBTestbed.number_of_constraints(10, 4, active_only=True) == 11 + assert tbs.CONSBBOBTestbed.constraint_category(5) == "6ndiv4+9" + assert tbs.CONSBBOBTestbed.constraint_category(5, active_only=True) == "6+n" + with pytest.raises(NotImplementedError): + tbs.Testbed.number_of_constraints(10, 1) + + def test_filter_bbob_and_largescale(self): + class DS: + def __init__(self, suite): + self.suite_name = suite + + tb = tbs.load_current_testbed("bbob", pproc.TargetValues) + dsl = [DS("bbob"), DS("bbob-largescale")] + tb.filter(dsl) + assert [ds.suite for ds in dsl] == ["bbob-largescale"] * 2 + dsl = [DS("bbob"), DS("bbob-boxed")] + tb.filter(dsl) + assert [ds.suite for ds in dsl] == ["bbob-JOINED-bbob-boxed"] * 2 + with pytest.raises(ValueError): + tb.filter([DS("bbob-biobj")]) + + +class TestConfig: + @pytest.mark.parametrize( + "name, first_digit, after", + [(".1bb", 0, 2), ("s-.2bbb", 1, 4), ("ss1.23bbbb", 2, 6), ("sss-2e0bbbbb", 3, 7), ("ssss1.2e-3bbbbbb", 4, 10), ("abc", -1, 0)], + ) + def test_index_after_float(self, name, first_digit, after): + assert config._index_after_float(name, True) == first_digit + assert config._index_after_float(name) == after + + def test_line_style_mapping(self): + names = ["1.0-CMA", "2.0-CMA", "BFGS", "0.5-CMA"] + assert config.map_indices_to_line_styles(names) == {0: 0, 1: 0, 2: 2, 3: 0} + styles = ["s0", "s1", "s2", "s3"] + # styles of the indices [0, 1, 3] are reassigned in the sorted order of 0.5, 1.0, 2.0 + assert config.sorted_line_styles(styles, names, [0, 1, 3]) == ["s1", "s3", "s2", "s0"] + + def test_str_to_colormap(self): + colors = list(config._str_to_colormap("viridis..5", 3)) + assert len(colors) == 3 and all(c.startswith("#") for c in colors) + assert colors[0] != colors[-1] + + def test_config_line_styles_parameter_sweep(self, monkeypatch): + monkeypatch.setattr(genericsettings, "parameter_sweep", True) + monkeypatch.setattr(genericsettings, "_current_args", ["a1.0x", "a2.0x", "b3.0y"]) + before = [dict(s) for s in genericsettings.line_styles] + config.config_line_styles() + after = genericsettings.line_styles + assert after[0]["color"] != before[0]["color"] # colors from the colormap + assert after[0]["marker"] == after[1]["marker"] # same algorithm family, same marker + assert after[2]["marker"] != after[0]["marker"] + + @pytest.mark.xfail(reason="parameter sweep fails for algorithm names without a float", raises=ValueError, strict=True) + def test_config_line_styles_parameter_sweep_name_without_float(self, monkeypatch): + monkeypatch.setattr(genericsettings, "parameter_sweep", True) + monkeypatch.setattr(genericsettings, "_current_args", ["a1.0x", "a2.0x", "b"]) + config.config_line_styles() + + def test_config_sets_testbed_and_bootstrap_size(self): + config.config("bbob-biobj") + assert tbs.current_testbed.name == "bbob-biobj" + assert genericsettings.simulated_runlength_bootstrap_sample_size == pytest.approx(10 + 990 / (1 + 10 * genericsettings.in_a_hurry)) + from cocopp.compall import pprldmany + + assert pprldmany.x_limit == genericsettings.xlimit_pprldmany + + def test_config_expensive_uses_runlength_based_targets(self, monkeypatch, quiet): + from cocopp import ppfigdim, pprldistr + + monkeypatch.setattr(genericsettings, "isExpensive", True) + config.config("bbob") + assert genericsettings.runlength_based_targets + assert genericsettings.maxevals_fix_display == genericsettings.xlimit_expensive + tb = tbs.current_testbed + assert tb.scenario == tbs.scenario_rlbased + for attr in ["pprldmany_target_values", "ppfigdim_target_values", "pptable_targetsOfInterest", "ppfigs_ftarget"]: + assert isinstance(getattr(tb, attr), pproc.RunlengthBasedTargetValues), attr + assert ppfigdim.xlim_max == genericsettings.xlimit_expensive + assert pprldistr.runlen_xlimits_max == genericsettings.xlimit_expensive / 2 + + def test_global_state_is_restored_after_previous_test(self): + """the conftest fixture must undo `config` side effects of other tests""" + from cocopp import ppfigdim + + assert genericsettings.isExpensive is False + assert ppfigdim.xlim_max is None + + +class TestGenericSettings: + def test_default_settings_copy(self): + assert genericsettings.default_settings is not genericsettings + assert genericsettings.default_settings.outputdir == "ppdata" + assert cocopp.toolsdivers.diff_attr(genericsettings.default_settings, genericsettings) == [ + ["interactive_mode", True, False] # set by conftest + ] + + def test_instances_of_interest(self): + assert genericsettings.instancesOfInterest2009 == {i: 3 for i in range(1, 6)} + assert all(isinstance(d, dict) for d in genericsettings.instancesOfInterest) + + +def test_data_format_mapping(): + m = dataformatsettings.data_format_name_to_class_mapping + assert m[None] is m["bbob"] is dataformatsettings.BBOBOldDataFormat + assert m["bbob-new2"] is dataformatsettings.BBOBNewDataFormat + biobj = m["bbob-biobj"]() + assert (biobj.evaluation_idx, biobj.function_value_idx) == (0, 1) + new = m["bbob-new"]() + assert (new.evaluation_idx, new.evaluation_constraints_idx, new.function_value_idx) == (0, 1, 2) + + +class TestFindFiles: + def test_recognized_filetypes(self, tmp_path): + assert findfiles.is_recognized_repository_filetype(str(tmp_path)) + for name in ["a.tgz", "a.tar.gz", "a.tar", "a.zip"]: + assert findfiles.is_recognized_repository_filetype(name) + assert findfiles.is_recognized_repository_filetype2(name) + assert not findfiles.is_recognized_repository_filetype("a.info") + assert not findfiles.is_recognized_repository_filetype2(str(tmp_path)) + + def test_main_lists_info_files(self, bbob_small): + found = findfiles.main(bbob_small) + assert sorted(os.path.basename(f) for f in found) == ["bbobexp_f1.info", "bbobexp_f15.info", "bbobexp_f2.info", "bbobexp_f8.info"] + + def test_main_warns_on_empty_folder(self, tmp_path): + with pytest.warns(UserWarning, match="Could not find"): + assert findfiles.main(str(tmp_path)) == [] + + @pytest.mark.parametrize("kind", ["tgz", "zip"]) + def test_archives_are_extracted(self, tmp_path, bbob_small, kind): + name = str(tmp_path / ("SMALL." + kind)) + if kind == "tgz": + with tarfile.open(name, "w:gz") as tar: + tar.add(bbob_small, arcname="SMALL") + else: + with zipfile.ZipFile(name, "w") as z: + for root, _, files in os.walk(bbob_small): + for f in files: + p = os.path.join(root, f) + z.write(p, os.path.relpath(p, os.path.dirname(bbob_small))) + found = findfiles.main(name) + assert len(found) == 4 + assert all(os.sep + ".extracted_SMALL" + os.sep in f for f in found) + assert findfiles.get_directory(name, False) == str(tmp_path / ".extracted_SMALL") + + def test_hash(self): + assert findfiles.hash("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d" + assert findfiles.hash("abc", 4) == "a999" and findfiles.hash("abc", 0) == "" + + def test_output_directory_sub_folder(self, monkeypatch): + monkeypatch.setattr(tbs, "current_testbed", None) + assert findfiles.get_output_directory_sub_folder("data/ALG.tgz", addtime=False) == "ALG" + assert findfiles.get_output_directory_sub_folder(["a/LONGNAME", "b/X"], addtime=False) == "LONGN_X" + assert findfiles.get_output_directory_sub_folder(["a", "b"], addhash=3, addtime=False) == "a_b_" + findfiles.hash("ab", 3) + args = ["a%d" % i for i in range(9)] + assert findfiles.get_output_directory_sub_folder(args, addtime=False).endswith("a6_et_al") + tbs.load_current_testbed("bbob-biobj", pproc.TargetValues) + assert findfiles.get_output_directory_sub_folder("x/ALG", addtime=False) == "biobj_ALG" + assert len(findfiles.get_output_directory_sub_folder("ALG")) == len("biobj_ALG_") + len("0101h0101") + 2 + with pytest.raises(ValueError): + findfiles.get_output_directory_sub_folder([]) + + +class TestCaptions: + def test_all_keywords_are_replaced(self, quiet): + config.config("bbob") + for key in captions.replace_dict: + res = captions.replace("x " + key + " y") + assert "!!" not in res, key + assert captions.replace("!!THE-REF-ALG!!") == "the best algorithm from BBOB 2009" + assert captions.replace("!!TOTAL-NUM-OF-FUNCTIONS!!") == "24" + assert captions.replace("!!NUM-OF-TARGETS-IN-ECDF!!") == "51" + + def test_html_replacements(self): + config.config("bbob") + assert captions.replace("!!DF!! 2-D a-b", html=True) == "Δf 2different dimensions a−b" + + def test_unknown_keyword_warns(self): + with pytest.warns(UserWarning, match="!!"): + captions.replace("!!UNKNOWN!!") + + def test_reference_algorithm_text(self): + config.config("bbob-constrained") + assert captions.get_reference_algorithm_text(False) == "" + with pytest.raises(NotImplementedError): + captions.get_reference_algorithm_text(True) + + +class TestPpfigHelpers: + def test_consecutive_numbers(self): + assert ppfig.consecutiveNumbers([0, 1, 2, 4, 5, 7, 8, 9]) == "0-2, 4, 5, 7-9" + assert ppfig.consecutiveNumbers([0, 1, 2, 4, 5, 7, 8, 9], "f") == "f0-f2, f4, f5, f7-f9" + assert ppfig.groupByRange([1, 2, 5]) == [[1, 2], [5]] + + def test_enum(self): + e = ppfig.enum("A", "B", C=7) + assert (e.A, e.B, e.C) == (0, 1, 7) + + def test_discretize_limits(self): + lo, hi = ppfig.discretize_limits((1, 70)) + assert lo == pytest.approx(10**-0.2) + assert hi == pytest.approx(100 * (1 + 1e-6)) + assert ppfig.discretize_limits((1, 20))[1] == pytest.approx(30 * (1 + 1e-6)) + + def test_marker_positions(self): + x = np.logspace(0, 4, 50) + y = np.linspace(0, 1, 50) + xp, yp = ppfig.marker_positions(x, y, 3, 10, ax_limits=(1, 1e4, 0, 1)) + assert len(xp) == len(yp) <= 11 + assert xp[-1] == x[-1] and set(xp) <= set(x) + + def test_plot_unif_log_x_markers(self): + lines = ppfig.plotUnifLogXMarkers(np.logspace(0, 3, 20), np.linspace(0, 1, 20), 3, marker="o", label="x") + assert len(lines) == 3 + + def test_save_figure(self, tmp_path): + import matplotlib.pyplot as plt + + plt.plot([1, 2], [3, 4]) + ppfig.save_figure(str(tmp_path / "fig")) # default formats from genericsettings + assert (tmp_path / "fig.pdf").exists() and (tmp_path / "fig.svg").exists() + ppfig.save_figure(str(tmp_path / "fig2"), format="png") + assert (tmp_path / "fig2.png").exists() + + def test_html_links(self, tmp_path): + (tmp_path / "f.html").write_text("") + assert ppfig.add_link(str(tmp_path), "", "f.html", "label") == 'label
\n' + assert ppfig.add_link(str(tmp_path), "", "missing.html", "label") == "" + assert ppfig.add_link(str(tmp_path), "sub", "x.html", "l", " ", True, 5) == ' l
\n' + assert ppfig.format_link_list_entry_in_html("x") == "x
\n" + assert ppfig.add_image("a.svg", True) == '' + assert ppfig.add_image("a.svg", False, 10) == '' diff --git a/tests/unit/test_synthdata.py b/tests/unit/test_synthdata.py new file mode 100644 index 0000000..b7ed143 --- /dev/null +++ b/tests/unit/test_synthdata.py @@ -0,0 +1,63 @@ +"""Sanity checks of the synthetic test data (`synthdata`) themselves. + +If these fail, the data generator rather than `cocopp` is likely broken. +Note that `DataSetList` only *prints* I/O errors, hence a broken +generator can silently lead to empty data sets. +""" + +import filecmp +import os + +import numpy as np +import pytest + +from cocopp import testbedsettings + +import synthdata +from conftest import load + + +@pytest.mark.parametrize( + "fixture, n_datasets, n_runs, suite", + [ + ("bbob_alg_a", 24 * 5, 15, "bbob"), + ("bbob_small", 4 * 2, 15, "bbob"), + ("bbob_old_format", 2 * 2, 15, "bbob"), + ("biobj_alg", 55 * 3, 10, "bbob-biobj"), + ], +) +def test_fixture_data_load_completely(request, fixture, n_datasets, n_runs, suite): + testbedsettings.reset_current_testbed() + dsl = load(request.getfixturevalue(fixture)) + assert len(dsl) == n_datasets + assert all(ds.nbRuns() == n_runs for ds in dsl) + assert {ds.suite_name for ds in dsl} == {suite} + assert testbedsettings.current_testbed.name == suite + + +def test_success_rates_are_mixed(bbob_alg_a): + """the data should exercise both solved and unsolved code paths in all dimensions""" + testbedsettings.reset_current_testbed() + dsl = load(bbob_alg_a) + for dim, dsl_dim in dsl.dictByDim().items(): + succ = [ds.detSuccesses([1e-8])[0] for ds in dsl_dim] + assert max(succ) == 15 and min(succ) < 5, (dim, succ) + assert any(0 < s < 15 for s in succ) + + +def test_generator_is_deterministic(tmp_path): + np.random.seed(1) + a = synthdata.write_bbob(str(tmp_path / "1"), "X", functions=(1, 7), dims=(2, 3), seed=3) + np.random.seed(2) # the global state must not matter + b = synthdata.write_bbob(str(tmp_path / "2"), "X", functions=(1, 7), dims=(2, 3), seed=3) + c = synthdata.write_bbob(str(tmp_path / "3"), "X", functions=(1, 7), dims=(2, 3), seed=4) + files = sorted(os.path.relpath(os.path.join(r, f), a) for r, _, fs in os.walk(a) for f in fs) + match, mismatch, errors = filecmp.cmpfiles(a, b, files, shallow=False) + assert not mismatch and not errors and len(match) == len(files) == 2 + 2 * 2 * 2 + assert filecmp.cmpfiles(a, c, files, shallow=False)[1] # another seed gives other data + + +def test_tiny_data_files(tmp_path): + info = synthdata.write_tiny_bbob(str(tmp_path)) + lines = open(info).read().splitlines() + assert len(lines) == 3 and lines[2].endswith("1:50|-5.0e-09, 2:100|4.5e-01, 3:30|-9.1e-09") diff --git a/tests/unit/test_toolsdivers.py b/tests/unit/test_toolsdivers.py new file mode 100644 index 0000000..751af8d --- /dev/null +++ b/tests/unit/test_toolsdivers.py @@ -0,0 +1,320 @@ +"""Unit tests for `cocopp.toolsdivers`. + +Many expected values below are *characterization* values: they pin the +current behavior (including some quirks, marked as such) so that a +refactoring does not change the produced tables and figures by accident. +""" + +import os + +import numpy as np +import pytest + +import cocopp +from cocopp import toolsdivers as td + + +class TestStringLists: + def test_stringlist_from_str_and_list(self): + assert td.StringList("ab bc") == ["ab", "bc"] + assert td.StringList(["x", "y"]).as_string == " x y " + assert td.StringList(None) == [] + assert td.StringList("") == [] + + def test_strlist_find_substring_and_regex(self): + s = td.StrList(["abc", "bcd", "cde", " cde"]) + assert s("bc") == ["abc", "bcd"] + assert s("a", "b") == ["abc"] + assert s(["a", "b"]) == ["abc"] + assert s(".c") == ["bcd", " cde"] + assert s(".*c") == ["abc", "bcd", "cde", " cde"] + assert s("BC") == ["abc", "bcd"] # case insensitive + + def test_strlist_find_by_index(self): + s = td.StrList(["abc", "bcd", "cde"]) + assert s(1) == ["bcd"] + assert s([0, 2]) == ["abc", "cde"] + assert s.found == ["abc", "cde"] + assert isinstance(s.found, td.StrList) + + def test_strlist_find_with_leading_strs(self): + s = td.StrList(["2009/BFGS", "2010/IPOP"]) + assert s.find("bbob/2009", leading_strs=["bbob/"]) == ["2009/BFGS"] + # a trailing part of the leading string is removed as well + assert s.find("ob/2010", leading_strs=["bbob/"]) == ["2010/IPOP"] + + def test_strlist_find_indices_and_print(self, capsys): + s = td.StrList(["abc", "bcd", "cde"]) + assert s.find_indices("bc") == [0, 1] + s.find("abc") + found_before = list(s.found) + s.print("d") + out = capsys.readouterr().out + assert " 1: 'bcd'" in out and " 2: 'cde'" in out + assert list(s.found) == found_before # print does not change found + + def test_strlist_as_string(self): + assert td.StrList("a b").as_string == " a b " + + +def test_algorithm_list_ordered_dict(): + names = ["b", "a", "c"] + d = dict(zip(names[::-1], [1, 2, 3])) + res = td.AlgorithmList(names).ordered_dict(d) + assert list(res) == names + assert res == d + + +def test_algorithm_list_ordered_dict_appends_unknown_keys_sorted(): + with pytest.warns(UserWarning): + res = td.AlgorithmList(["b"]).ordered_dict({"z": 1, "b": 2, "a": 3}) + assert list(res) == ["b", "a", "z"] + + +class TestFolderContexts: + def test_infolder(self, tmp_path): + before = os.getcwd() + with td.Infolder(str(tmp_path)): + assert os.path.samefile(os.getcwd(), tmp_path) + assert os.getcwd() == before + + def test_infolder_gone_with_the_wind(self): + before = os.getcwd() + with td.InfolderGoneWithTheWind() as _: + tmp = os.getcwd() + assert tmp != before + open("some_file", "w").close() + assert os.getcwd() == before + assert not os.path.exists(tmp) + + +def test_equals_approximately(): + assert td.equals_approximately(1.0, 1.0 + 1e-13) + assert td.equals_approximately(-1.0, -1.0 - 1e-13) + assert not td.equals_approximately(1.0, 1.001) + assert td.equals_approximately(1e20, 1e20 * (1 + 1e-13)) + + +def test_less_ignores_nan_without_warning(): + a = np.array([1.0, np.nan, 3.0]) + with np.errstate(invalid="raise"): + res = td.less(a, 2.0) + assert list(res) == [True, False, False] + + +def test_diff_attr(): + class A: + pass + + a, b = A(), A() + a.x, b.x = 1, 1 + a.y, b.y = 1, 2 + a._z, b._z = 1, 2 + a.only_a = 3 + assert td.diff_attr(a, b) == [["y", 1, 2]] + + +class TestFileHelpers: + def test_prepend_to_file(self, tmp_path): + fn = tmp_path / "f.tex" + fn.write_text("old1\nold2\n") + td.prepend_to_file(str(fn), ["new1", "new2"]) + assert fn.read_text() == "new1\nnew2\nold1\nold2\n" + + def test_prepend_to_new_file(self, tmp_path): + fn = tmp_path / "new.tex" + td.prepend_to_file(str(fn), ["a"]) + assert fn.read_text() == "a\n" + + def test_prepend_to_file_truncates(self, tmp_path, capsys): + fn = tmp_path / "f.tex" + fn.write_text("".join("%d\n" % i for i in range(10))) + td.prepend_to_file(str(fn), ["x"], maxlines=2, warn_message="too long") + assert fn.read_text().splitlines() == ["x", "0", "1", "2", "3"] + assert "too long" in capsys.readouterr().out + + def test_replace_in_file(self, tmp_path, capsys): + fn = tmp_path / "f.txt" + fn.write_text("abc\nxbx\n") + td.replace_in_file(str(fn), "b", "B") + assert fn.read_text() == "aBc\nxBx\n" + td.replace_in_file(str(tmp_path / "missing"), "a", "b") + assert "does not exist" in capsys.readouterr().out + + def test_truncate_latex_command_file(self, tmp_path): + fn = tmp_path / "cmd.tex" + lines = ["\\providecommand{\\c%d}{}\n" % i for i in range(10)] + fn.write_text("".join(lines)) + td.truncate_latex_command_file(str(fn), keeplines=3) + # keeps keeplines + 1 lines, then stops at the next \providecommand + assert fn.read_text() == "".join(lines[:4]) + + def test_truncate_latex_command_file_creates_file(self, tmp_path): + fn = tmp_path / "cmd.tex" + td.truncate_latex_command_file(str(fn)) + assert fn.exists() and fn.read_text() == "" + + +@pytest.mark.parametrize( + "name, p0, p1, p2, p3", + [ + ("../data/BFGS_noiseless.tgz", "data/BFGS_noiseless.tgz", "BFGS_noiseless", "BFGS_noiseless", "BFGS_"), + ("./x/y/BIPOP-CMA-ESdata.tar.gz", "x/y/BIPOP-CMA-ESdata.tar.gz", "BIPOP-CMA-ES", "y/BIPOP-CMA-ES", "BIPOP-CMA-ES"), + # quirk: strip_pathname2 does not remove a .zip extension + (" a/b/c.zip ", "a/b/c.zip", "c", "b/c.zip", "c"), + ("a/.extracted_foo/bar", "a/.extracted_foo/bar", "bar", ".extracted_foo/bar", "bar"), + ], +) +def test_strip_pathname(name, p0, p1, p2, p3): + name = name.replace("/", os.sep) + assert td.strip_pathname(name) == p0 + assert td.strip_pathname1(name) == p1 + assert td.strip_pathname2(name) == p2 + assert td.strip_pathname3(name) == p3 + + +def test_str_to_latex(): + assert td.str_to_latex(r"a_b^c%d~e#f\g") == r"a b\^\,c\%d\ensuremath{\sim}e\#f\textbackslash{}g" + + +def test_number_of_digits_not_implemented(): + with pytest.raises(NotImplementedError): + td.number_of_digits(3.0) + + +# value, num2str(v), num2str(v, 1), num2str(v, 3), num2str(v, 2, force_rounding=True) +NUM2STR = [ + (0, "0", "0", "0", "0"), + (1, "1", "1", "1", "1"), + (-1, "-1", "-1", "-1", "-1"), + (0.5, "0.5", "0.5", "0.5", "0.5"), + (12345, "12345", "1e4", "12345", "12000"), + (1234.5, "1234", "1e3", "1234", "1200"), + (123.45, "123", "123", "123", "120"), + (12.345, "12", "12", "12.3", "12"), + (1.2345, "1.2", "1", "1.23", "1.2"), + (0.12345, "0.12", "0.1", "0.123", "0.12"), + (0.012345, "0.012", "0.01", "0.0123", "0.012"), + (0.0012345, "1.2e-3", "1e-3", "1.23e-3", "1.2e-3"), + (1e-8, "1e-8", "1e-8", "1e-8", "1e-8"), + (3.3e-12, "3.3e-12", "3e-12", "3.3e-12", "3.3e-12"), + (1e5, "1e5", "1e5", "1e5", "1e5"), + (1.5e6, "1.5e6", "2e6", "1.5e6", "1.5e6"), + (123456789, "1.2e8", "1e8", "1.23e8", "1.2e8"), + (-0.0234, "-0.023", "-0.02", "-0.0234", "-0.023"), + (99.9, "100", "100", "99.9", "100"), + (0.999, "1.0", "1", "0.999", "1.0"), + (2.0, "2", "2", "2", "2"), + (100, "100", "100", "100", "100"), +] + + +@pytest.mark.parametrize("val, s2, s1, s3, s2forced", NUM2STR) +def test_num2str(val, s2, s1, s3, s2forced): + assert td.num2str(val) == s2 + assert td.num2str(val, 1) == s1 + assert td.num2str(val, 3) == s3 + assert td.num2str(val, 2, force_rounding=True) == s2forced + + +def test_num2str2_unused_helper(): + assert td._num2str2(0.99, 1) == "1e0" # the "poor" formatting mentioned in its docstring + assert td._num2str2(12345.6) == "12346" + assert td._num2str2(0.00123, 2, True) == "+0.0012" + + +@pytest.mark.parametrize( + "s, latex, html", + [ + ("1e-8", "10^{-8}", "10-8"), + ("-1e-12", "-10^{-12}", "-10-12"), + ("2.3e5", r"2.3\times10^{5}", "2.3 x 105"), + ("12", "12", "12"), + ], +) +def test_number_to_latex_and_html(s, latex, html): + assert td.number_to_latex(s) == latex + assert td.number_to_html(s) == html + + +@pytest.mark.parametrize( + "ratio, html, tex, html_unsigned", + [ + (0, "÷inf", r"\div inf", "÷inf"), + (0.1, "÷10", r"\div 10", "÷10"), + (0.4, "÷2.5", r"\div 2.5", "÷2.5"), + (0.5, "-50%", r"-50\%", "-50%"), + (0.9, "-10%", r"-10\%", "-10%"), + (1, "0%", r"0\%", "0%"), + (1.01, "+1%", r"+1\%", "1%"), + (1.5, "+50%", r"+50\%", "50%"), + (1.99, "+99%", r"+99\%", "99%"), + (1.995, "×2.0", r"\times 2.0", "×2.0"), + (2, "×2", r"\times 2", "×2"), + (123.4, "×123", r"\times 123", "×123"), + ], +) +def test_ratio2str(ratio, html, tex, html_unsigned): + assert td.ratio2html(ratio) == html + assert td.ratio2tex(ratio) == tex + assert td.ratio2html(ratio, signed=False) == html_unsigned + + +def test_ratio2str_without_div_or_times(): + assert td._ratio2str(0.1, formdiv=None) == "-90%" + assert td._ratio2str(5, formtimes=None) == "+400%" + + +def test_html_subsup(): + assert td.html_subsup(".5", "3", "x", "y") == "x.5y3" + + +@pytest.mark.parametrize( + "succ, digit", + [ + (0, None), + (0.04, 0), # quirk: rounds to a ".0" subscript although 0 < succ + (0.05, 0), + (0.5, 5), + (0.94, 9), + (0.95, None), + (1, None), + ], +) +def test_prepend_succ_str(succ, digit): + tex, html, indent = td.prepend_succ_str(succ, "T", "H") + if digit is None: + assert (tex, html, indent) == ("T", "H", "") + else: + assert tex == r"$\!\!\!\!{}_{.%d}$T" % digit + assert html == td.html_subsup(".%d" % digit, "") + "H" + assert indent == 'style="text-indent: -0.5em;"' + + +def test_get_version_label_without_reference_values(monkeypatch): + monkeypatch.setattr(cocopp.testbedsettings, "reference_values", {}) + assert td.get_version_label() == "v" + cocopp.__version__ + + +def test_get_version_label_with_reference_values(monkeypatch): + monkeypatch.setattr(cocopp.testbedsettings, "reference_values", {"A": "abc"}) + assert td.get_version_label() == "v%s, hv-hash=abc" % cocopp.__version__ + assert td.get_version_label("A") == "v%s, hv-hash=abc" % cocopp.__version__ + monkeypatch.setattr(cocopp.testbedsettings, "reference_values", {"A": "abc", "B": "def"}) + label = td.get_version_label() + assert label.startswith("v%s, hv-hashes inconsistent:" % cocopp.__version__) + assert label.endswith("found!") and "abc" in label and "def" in label + + +def test_path_in_package_is_redirected_by_conftest(package_copy): + # the test suite redirects package files into a copy to keep the source tree clean + assert td.path_in_package("refalgs") == os.path.join(str(package_copy), "refalgs") + assert os.path.isdir(td.path_in_package("refalgs")) + + +def test_legend_sets_framealpha(): + import matplotlib.pyplot as plt + + plt.plot([1, 2], label="x") + td.legend() + assert plt.gca().get_legend().get_frame().get_alpha() == pytest.approx(0.2) diff --git a/tests/unit/test_toolsstats.py b/tests/unit/test_toolsstats.py new file mode 100644 index 0000000..aca55e2 --- /dev/null +++ b/tests/unit/test_toolsstats.py @@ -0,0 +1,282 @@ +"""Unit tests for `cocopp.toolsstats` (statistics and bootstrapping helpers). + +Where a function draws random numbers, either properties that hold for +every draw are tested or numpy's global random state is fixed (the +autouse fixture in ``conftest.py`` seeds it before each test). +""" + +import math + +import numpy as np +import pytest + +from cocopp import toolsstats as ts + +nan, inf = np.nan, np.inf + + +class TestSuccessPerformance: + def test_sp1_all_successful(self): + assert ts.sp1([1, 2, 3]) == (2.0, 1.0, 3) + + def test_sp1_with_maxvalue(self): + # data >= maxvalue count as unsuccessful: mean(successful) / success rate + res = ts.sp1([1, 2, 3, 10], maxvalue=5) + assert res == pytest.approx((2.0 / 0.75, 0.75, 3)) + + def test_sp1_with_issuccessful_and_nan(self): + res = ts.sp1([1, nan, 3, 4], issuccessful=[True, True, False, True]) + # the nan entry is removed first, then 2 of 3 remaining are successful + assert res == pytest.approx((2.5 / (2 / 3), 2 / 3, 2)) + + def test_sp1_no_success_and_empty(self): + assert ts.sp1([5, 6], maxvalue=1) == (inf, 0.0, 0) + res = ts.sp1([nan, nan]) + assert all(np.isnan(r) for r in res) + + def test_sp_is_ert(self): + # sum of all evaluations / number of successes + assert ts.sp([1, 2, 3, 10], maxvalue=5) == pytest.approx((16 / 3, 0.75, 3)) + assert ts.sp([1, 2, 3]) == (2.0, 1.0, 3) + + def test_sp_no_success(self): + assert ts.sp([5, 6], maxvalue=1) == (inf, 0.0, 0) + assert ts.sp([5, 6], maxvalue=1, allowinf=False) == (11, 0.0, 0) + + def test_sp_issuccessful_with_nan_regression(self): + # regression test for the bug fixed in July 2025 (sorting broke the + # alignment between data and issuccessful when data contained nan) + res = ts.sp([10, nan, 1, 5], issuccessful=[False, True, True, True]) + assert res == pytest.approx((16 / 2, 2 / 3, 2)) + + @pytest.mark.parametrize("func", [ts.sp, ts.sp1]) + def test_input_validation(self, func): + with pytest.raises(Exception): + func(3) + with pytest.raises(Exception): + func([1, 2], issuccessful=3) + with pytest.raises(Exception): + func([1, 2], issuccessful=[True]) + + +class TestPrctile: + def test_docstring_example(self): + pp = ts.prctile([2, 3, nan, 1], [5, 25, 50, 75, 95], ignore_nan=False) + assert pp[:3] == [1.0, 1.5, 2.5] + assert np.isnan(pp[3]) and np.isnan(pp[4]) + + @pytest.mark.parametrize("n", [1, 2, 5, 15, 100]) + def test_agrees_with_numpy_hazen(self, n): + rng = np.random.default_rng(n) + x = rng.lognormal(size=n) + p = [0, 2, 10, 25, 50, 75, 90, 98, 100] + assert ts.prctile(x, p) == pytest.approx(np.percentile(x, p, method="hazen")) + + def test_scalar_and_out_of_range(self): + x = [3, 1, 2] + assert ts.prctile(x, 50) == [2] + assert ts.prctile(x, [-10, 110]) == [1, 3] + + def test_ignores_none_and_nan_by_default(self): + assert ts.prctile([None, 1, nan, 3], [50]) == [2.0] + + def test_empty(self): + assert all(np.isnan(ts.prctile([], [10, 50]))) + + def test_infinite_values_use_proximity(self): + assert ts.prctile([1, 2, inf, inf], [60]) == [inf] + assert ts.prctile([1, 2, inf, inf], [40]) == [2] + + def test_issorted_skips_sorting(self): + # documents that issorted=True trusts the caller + assert ts.prctile([3, 1, 2], [0], issorted=True) == [3] + + +class TestRandint: + def test_randint_derandomized_docstring(self): + np.random.seed(1) + assert [int(i) for i in ts.randint_derandomized(0, 4, 6)] == [3, 2, 0, 1, 0, 2] + + @pytest.mark.parametrize("low, high, size", [(0, 5, None), (2, 7, 23), (10, None, 37), (0, 1, 3)]) + def test_each_value_once_per_chunk(self, low, high, size): + res = ts.randint_derandomized(low, high, size) + lo, hi = (0, low) if high is None else (low, high) + n = hi - lo + assert len(res) == (n if size is None else size) + assert res.min() >= lo and res.max() < hi + for i in range(0, len(res) - n + 1, n): + assert sorted(res[i : i + n]) == list(range(lo, hi)) + + def test_randint(self): + r = ts.randint(5, 1000) + assert r.shape == (1000,) and r.min() >= 0 and r.max() <= 4 + assert np.all(r == np.floor(r)) + + def test_fix_data_number(self): + data = [1, 2, 4] + for n in [1, 3, 4, 14]: + assert len(ts.fix_data_number(data, n)) == n + assert ts.fix_data_number(data, 14)[2] == data[2] + assert list(ts.fix_data_number(np.arange(10), 4, last_elements_randomized=False)) == [0, 1, 2, 3] + with pytest.warns(UserWarning): + ts.fix_data_number(data, 5, warn=True) + + +class TestSimulatedEvals: + def test_docstring_example(self): + np.random.seed(4) + res = ts.simulated_evals(np.hstack([[1], [2, 4, 2, 6, 100]]), 5, 13) + assert [int(i) for i in res[:3]] == [1, 1, 3] + + def test_all_successful_returns_resampled_data(self): + evals = np.array([3.0, 5, 7]) + res = ts.simulated_evals(evals, 0, 9, return_as=np.asarray) + assert sorted(res) == sorted(3 * list(evals)) # derandomized: each exactly 3 times + + def test_all_unsuccessful(self): + for return_as in (sorted, list, np.asarray, np.array, tuple): + res = ts.simulated_evals([1, 2], 2, 4, return_as=return_as) + assert len(res) == 4 and all(np.isnan(res)) + + def test_sums_contain_exactly_one_successful_run(self): + succ, fail = [1, 10], [1000, 100000] + res = ts.simulated_evals(succ + fail, len(fail), 200, return_as=np.asarray) + # every value = (sum of failures) + (one success); success evals are < 1000 + assert np.all(np.isin(res % 1000, succ)) + + def test_negative_convention(self): + # nfails=-1: unsuccessful runs are given as negative values in any position + res = ts.simulated_evals(np.array([-1000.0, 1, -100000, 10]), -1, 200, return_as=np.asarray) + assert np.all(res > 0) + assert np.all(np.isin(res % 1000, [1, 10])) + + def test_inconsistent_nfails(self): + with pytest.raises(ValueError): + ts.simulated_evals([1, 2], 3) + + def test_return_sorted_by_default(self): + res = ts.simulated_evals([5, 1, 3], 0, 9) + assert res == sorted(res) + + +class TestDraw: + def test_drawSP_properties(self): + prc, values = ts.drawSP([1, 2], [10, 20], [50], samplesize=100) + assert len(values) == 100 and values == sorted(values) + # each value is a (possibly empty) sum of failures plus one success + assert all(v - 1 in _sums_of([10, 20], 50) or v - 2 in _sums_of([10, 20], 50) for v in values) + assert prc == ts.prctile(values, [50], issorted=True) + + def test_drawSP_no_success_raises(self): + with pytest.raises(NotImplementedError): + ts.drawSP([], [10], [50]) + + def test_draw_bootstrap_of_sp1(self): + data = np.arange(1, 11) + prc, values = ts.draw(data, [0, 50, 100], samplesize=200) + assert len(values) == 200 + assert values == sorted(values) + assert 1 <= prc[0] <= prc[1] <= prc[2] <= 10 + assert prc[1] == pytest.approx(5.5, abs=1.0) + + def test_draw_is_reproducible_with_seed(self): + np.random.seed(5) + r1 = ts.draw(np.arange(10), [50], samplesize=50) + np.random.seed(5) + r2 = ts.draw(np.arange(10), [50], samplesize=50) + assert r1 == r2 + + def test_drawSP_from_dataset_new_not_implemented(self): + with pytest.raises(NotImplementedError): + ts.drawSP_from_dataset_new(None, 1, None) + + +def _sums_of(values, max_terms): + """all sums of up to `max_terms` elements drawn with replacement from `values`""" + sums = {0} + for _ in range(max_terms): + sums |= {s + v for s in sums for v in values if s + v <= max_terms * max(values)} + return sums + + +class TestRankSum: + def test_ranksum_statistic_against_brute_force(self): + rng = np.random.default_rng(1) + x, y = rng.integers(0, 10, 7), rng.integers(0, 10, 9) + expected = sum((yj < xi) + 0.5 * (yj == xi) for xi in x for yj in y) + assert ts.ranksum_statistic(x, y) == expected + + def test_rankdata_with_ties(self): + assert list(ts.rankdata([0, 2, 2, 3])) == [1.0, 2.5, 2.5, 4.0] + assert list(ts.rankdata([[3, 1], [2, 2]])) == [4.0, 1.0, 2.5, 2.5] + + def test_fastsort(self): + s, i = ts.fastsort(np.array([3, 1, 2])) + assert list(s) == [1, 2, 3] and list(i) == [1, 2, 0] + + @pytest.mark.parametrize("z", [-7, -3, -1.5, -0.2, 0, 0.3, 1.0, 1.96, 2.5, 4, 8]) + def test_zprob_is_normal_cdf(self, z): + assert float(ts.zprob(np.array(z))) == pytest.approx(0.5 * (1 + math.erf(z / math.sqrt(2))), abs=1e-6) + + def test_ranksumtest(self): + x = [1, 2, 3, 4, 5] + y = [6, 7, 8, 9, 10] + z, p = ts.ranksumtest(x, y) + n1, n2 = 5, 5 + expected_z = (15 - n1 * (n1 + n2 + 1) / 2) / math.sqrt(n1 * n2 * (n1 + n2 + 1) / 12) + assert z == pytest.approx(expected_z) + assert p == pytest.approx(2 * (1 - 0.5 * (1 + math.erf(abs(expected_z) / math.sqrt(2)))), abs=1e-6) + z2, p2 = ts.ranksumtest(y, x) + assert z2 == pytest.approx(-z) and p2 == pytest.approx(p) + + def test_ranksumtest_identical_samples(self): + z, p = ts.ranksumtest([1, 2, 3], [1, 2, 3]) + assert z == 0 and p == pytest.approx(1.0) + + +class TestBestAlgIndices: + def test_from_ert_arrays(self): + erts = np.array([[1, 5, inf], [2, 3, inf]]) + idx = ts.best_alg_indices(ert_ars=erts, median_finalfunvals=[0.5, 0.1]) + assert list(idx) == [0, 1, 1] # last target: no ERT, best median final f-value + + +def test_sliding_window_data(): + np.random.seed(2) + data = np.array([1.0, 3, 2, 5, inf, 4, 6]) + smooth, stats = ts.sliding_window_data(data, width=3, number_of_stats=2) + assert isinstance(smooth, np.ndarray) and len(smooth) == len(data) + assert smooth[0] == np.median([1, 3]) + assert smooth[4] == inf # non-finite data are kept + assert smooth[3] == np.median([2, 5]) # non-finite neighbors are ignored + assert all(len(s[1]) == 7 for s in stats) + + +def test_sliding_window_data_small_width_is_identity(): + data = [1, 2, 3] + assert ts.sliding_window_data(data, width=1) == (data, []) + + +@pytest.mark.xfail(reason="known bug: default number_of_stats=0 divides by zero", raises=ZeroDivisionError, strict=True) +def test_sliding_window_data_default_number_of_stats(): + ts.sliding_window_data([1.0, 2, 3, 4], width=2) + + +def test_sliding_window_data_too_wide_window_warns(): + with pytest.warns(UserWarning, match="width 3 should be smaller than the number of data 2"): + ts.sliding_window_data([1.0, 2], width=3, number_of_stats=1) + + +def test_equals_and_in_approximately(): + assert ts.equals_approximately(1.0, 1.0 + 1e-12) + assert ts.equals_approximately(1e-20, 0) # absolute tolerance + assert not ts.equals_approximately(1.0, 1.1) + assert ts.in_approximately(2.0, [1, 2 + 1e-12, 3]) + assert not ts.in_approximately(2.5, [1, 2, 3]) + + +def test_evals_container(): + e = ts.Evals([1, 5], [2, 3]) + assert e() == [1, 1, 5, 5, 5] + with pytest.raises(NotImplementedError): + e.sort() From a77680764904633590b0bdaa85ded746dcda9c07 Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 10:40:31 +0200 Subject: [PATCH 03/10] [tests] add integration tests of the post-processing pipeline test_modules.py calls the figure and table modules directly on small synthetic data, test_pipeline.py runs cocopp.main on many scenarios (single, two and three algorithms, expensive and convergence settings, background algorithms, --include-single, output options, bbob-biobj, bbob-noisy, bbob-constrained, bbob-largescale, bbob-mixint, bbob-boxed, old data format) and checks the produced files, the validity of figures, and that table entries agree with the data. --- tests/integration/test_modules.py | 223 +++++++++++++++++++++++ tests/integration/test_pipeline.py | 280 +++++++++++++++++++++++++++++ 2 files changed, 503 insertions(+) create mode 100644 tests/integration/test_modules.py create mode 100644 tests/integration/test_pipeline.py diff --git a/tests/integration/test_modules.py b/tests/integration/test_modules.py new file mode 100644 index 0000000..fb21ea3 --- /dev/null +++ b/tests/integration/test_modules.py @@ -0,0 +1,223 @@ +"""Integration tests calling the figure and table modules directly. + +`cocopp.main` calls these modules in a fixed sequence; calling them +individually on small data sets localizes failures and covers modules +that the pipeline does not (or no longer) use. +""" + +import os +import warnings + +import numpy as np +import pytest + +from cocopp import algportfolio, bestalg, config, genericsettings, ppconverrorbars, ppfig, ppfigdim, ppfigparam, pplogloss, pprldistr +from cocopp import ppsingle, pptable, pproc, testbedsettings, toolsstats +from cocopp.comp2 import ppfig2, pprldistr2, ppscatter +from cocopp.compall import ppfigs, ppperfprof, pprldmany, pptables + + +@pytest.fixture +def two_algs(bbob_small, bbob_small_b, quiet): + """``(dsList, sortedAlgs, dictAlg)`` of two small synthetic algorithms, configured for 'bbob'""" + testbedsettings.reset_current_testbed() + res = pproc.processInputArgs([bbob_small, bbob_small_b]) + config.config("bbob") + return res + + +@pytest.fixture +def one_alg(two_algs): + dsl, algs, dict_alg = two_algs + return dict_alg[algs[0]] + + +@pytest.fixture +def out(tmp_path): + return str(tmp_path) + + +def produced(folder): + return sorted(os.listdir(folder)) + + +def single_algorithm_pages(out, dsl): + """write the html skeleton pages which `rungeneric1` creates before calling the modules""" + ppfig.save_single_functions_html(os.path.join(out, "index1"), "title", htmlPage=ppfig.HtmlPage.ONE, function_groups=dsl.getFuncGroups()) + ppfig.save_single_functions_html( + os.path.join(out, "pptable"), dimensions=sorted(dsl.dictByDim()), htmlPage=ppfig.HtmlPage.PPTABLE, parentFileName="index1" + ) + + +def many_algorithms_pages(out, dict_alg, algs): + """write the html skeleton pages which `rungenericmany` creates before calling the modules""" + groups = dict_alg[algs[0]].getFuncGroups() + ppfig.save_single_functions_html(os.path.join(out, "index"), "", htmlPage=ppfig.HtmlPage.MANY, function_groups=groups) + ppfig.save_single_functions_html( + os.path.join(out, "pptables"), + "", + dimensions=sorted(pproc.dictAlgByDim(dict_alg)), + htmlPage=ppfig.HtmlPage.PPTABLES, + function_groups=groups, + parentFileName="index", + ) + + +class TestSingleAlgorithmModules: + def test_pptable(self, one_alg, out, tmp_path): + single_algorithm_pages(out, one_alg) + latex = str(tmp_path / "cmds.tex") + pptable.main(one_alg, (2, 5), out, latex) + tables = [f for f in produced(out) if f.startswith("pptable_")] + assert tables == sorted("pptable_f%03d_%02dD.tex" % (f, d) for f in (1, 2, 8, 15) for d in (2, 5)) + assert "pptable_f001_05D" not in open(os.path.join(out, "pptable.html")).read() # html gets the table itself + assert " 0 for z, p in res) # positive z means the first data set is better + res2 = toolsstats.significancetest(slow, fast, [1e-1, 1e-8]) + assert [z for z, p in res2] == pytest.approx([-z for z, p in res]) + + def test_significance_all_best_vs_other(self, two_algs): + dsl, algs, dict_alg = two_algs + datasets = [dict_alg[a].dictByDimFunc()[5][8][0] for a in algs] + targets = [10, 1e-1, 1e-8] + res, best = toolsstats.significance_all_best_vs_other(datasets, targets) + assert len(res) == len(best) == len(targets) + assert list(best) == list(toolsstats.best_alg_indices(datasets=datasets, targets=targets)) + + def test_against_reference_algorithm(self, two_algs): + dsl, algs, dict_alg = two_algs + ref = bestalg.load_reference_algorithm(testbedsettings.current_testbed.reference_algorithm_filename, force=True) + ds = dict_alg[algs[0]].dictByDimFunc()[5][1][0] + res = toolsstats.significancetest(ref[(5, 1)], ds, [10, 1e-8]) + assert len(res) == 2 and all(0 <= p <= 1 for z, p in res) + + +def test_rungeneric1_and_rungenericmany_directly(two_algs, tmp_path, monkeypatch): + """the sub-routines of `cocopp.main` can be called directly""" + from cocopp import rungeneric1, rungenericmany + + dsl, algs, dict_alg = two_algs + monkeypatch.chdir(tmp_path) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + dict_alg1 = rungeneric1.main(algs[0], "out1") + testbedsettings.reset_current_testbed() + dict_alg2 = rungenericmany.main(algs, "out2") + # quirk: the returned dictionaries are keyed by (algId, comment) instead of input path + assert list(dict_alg1) == [("SMALL", "")] and list(dict_alg2) == [("SMALL", ""), ("SMALL-B", "")] + assert os.path.exists(tmp_path / "out1" / "index.html") and os.path.exists(tmp_path / "out2" / "index.html") diff --git a/tests/integration/test_pipeline.py b/tests/integration/test_pipeline.py new file mode 100644 index 0000000..dddf275 --- /dev/null +++ b/tests/integration/test_pipeline.py @@ -0,0 +1,280 @@ +"""Integration tests of the full post-processing pipeline (`cocopp.main`). + +The scenarios from `pipeline.SCENARIOS` run once per session (see the +``pipeline_run`` fixture). The tests here check properties of the output +which a golden-file comparison cannot express well: that files are valid, +that the expected kinds of output exist, and that the numbers in the +tables agree with the data. +""" + +import os +import re +import xml.etree.ElementTree as ET + +import pytest + +import cocopp +from cocopp import bestalg, pproc + +import pipeline + +pytestmark = pytest.mark.slow + +# scenario: number of foreground algorithms +NUM_ALGS = {name: len(fixtures) for name, (fixtures, _, _) in pipeline.SCENARIOS.items()} + + +@pytest.mark.parametrize("scenario", sorted(pipeline.SCENARIOS)) +class TestAllScenarios: + def test_run_completed(self, scenario, pipeline_run): + run = pipeline_run(scenario) + assert "ALL done" in run.stdout + assert isinstance(run.result, dict) + assert all(len(dsl) > 0 for dsl in run.result.values()) + + def test_top_level_files(self, scenario, pipeline_run): + run = pipeline_run(scenario) + assert "index.html" in run.files and "cocopp_commands.tex" in run.files + commands = run.read("cocopp_commands.tex") + assert "\\providecommand{\\numofalgs}{%d}" % NUM_ALGS[scenario] in commands + assert "Data produced with COCO v" + cocopp.__version__ in commands + index = run.read("index.html") + for folder in run.subfolders: + assert folder.split("_")[0] in index + + def test_figures_are_valid(self, scenario, pipeline_run): + run = pipeline_run(scenario) + pdfs = [f for f in run.files if f.endswith(".pdf")] + svgs = [f for f in run.files if f.endswith(".svg")] + assert pdfs and len(svgs) == len(pdfs) + for f in pdfs: + with open(os.path.join(run.outdir, f), "rb") as fh: + assert fh.read(5) == b"%PDF-", f + for f in svgs[:: max(1, len(svgs) // 50)]: # parsing all takes too long + assert ET.parse(os.path.join(run.outdir, f)).getroot().tag.endswith("svg"), f + + def test_text_files_are_not_empty(self, scenario, pipeline_run): + run = pipeline_run(scenario) + empty = [f for f in run.files if f.endswith((".tex", ".html")) and os.path.getsize(os.path.join(run.outdir, f)) == 0] + assert not empty + + +class TestSingleAlgorithm: + SCENARIO = "bbob-1" + + @pytest.fixture + def run(self, pipeline_run): + return pipeline_run(self.SCENARIO) + + @pytest.fixture + def datasets(self, run): + (dsl,) = run.result.values() + return dsl + + def test_inventory(self, run): + assert run.subfolders == ["SYNTH-A"] + assert len(run.glob("SYNTH-A/pptable_f*_*D.tex")) == 24 * 5 + assert len(run.glob("SYNTH-A/ppfigdim_f*.pdf")) == 24 + assert len(run.glob("SYNTH-A/pprldistr_*D_*.pdf")) == 2 * 6 # dimensions of interest x function groups + assert len(run.glob("SYNTH-A/pplogloss_*D_*.pdf")) == 2 * 6 + assert len(run.glob("SYNTH-A/pploglosstable_*D_noiselessall.tex")) == 2 + assert len(run.glob("SYNTH-A/pprldmany-single-functions/pprldmany_f*.pdf")) == 24 + assert len(run.glob("SYNTH-A/pprldmany-single-functions/pprldmany_gr_*_*D.pdf")) == 5 * 5 + for page in ["index1.html", "ppfigdim.html", "pptable.html", "pprldistr.html", "pplogloss.html", "pprldflex.html"]: + assert run.glob("SYNTH-A/" + page), page + assert not run.glob("*ppconv*") and not run.glob("*ppscatter*") + + def test_result_datasets(self, datasets, bbob_alg_a): + assert len(datasets) == 24 * 5 + assert {ds.algId for ds in datasets} == {"SYNTH-A"} + fresh = pproc.DataSetList(bbob_alg_a) + for ds, ds0 in zip(sorted(datasets, key=lambda d: (d.dim, d.funcId)), fresh): + assert ds == ds0 and not ds._data_differ(ds0) + + def test_pptable_success_counts(self, run, datasets): + """the last two columns of the algorithm row are #successes and #trials at the final target""" + cocopp.config.config("bbob") # DataSet methods need the current testbed + for ds in datasets: + table = run.read("SYNTH-A/pptable_f%03d_%02dD.tex" % (ds.funcId, ds.dim)) + row = table.strip().splitlines()[-1] + m = re.search(r"& (\d+) & /(\d+)\\\\$", row) + assert m, row + assert (int(m.group(1)), int(m.group(2))) == (ds.detSuccesses([1e-8])[0], ds.nbRuns()), (ds, row) + + def test_pptable_first_ert_ratio(self, run, datasets): + """the first entry of the algorithm row is the ERT ratio to the reference algorithm for target Delta f = 10""" + cocopp.config.config("bbob") + reference = bestalg.load_reference_algorithm("refalgs/best2009-bbob.tar.gz", force=True) + checked = 0 + for ds in datasets: + if ds.dim not in (5, 20): + continue + row = run.read("SYNTH-A/pptable_f%03d_%02dD.tex" % (ds.funcId, ds.dim)).strip().splitlines()[-1] + m = re.match(r"^ & (?:\$[^$]*\$)?(\d+)\s*&\s*(\.\d+)?\$", row) + if not m: # e.g. infinite ERT or another format + continue + value = float(m.group(1) + (m.group(2) or "")) + ratio = ds.detERT([10])[0] / reference[(ds.dim, ds.funcId)].detERT([10])[0] + assert value == pytest.approx(ratio, rel=0.06, abs=0.06), (ds, row) + checked += 1 + assert checked >= 20 + + def test_latex_commands(self, run): + commands = run.read("cocopp_commands.tex") + assert "\\providecommand{\\algname}{SYNTH-A{}}" in commands + assert re.search(r"\\providecommand\{\\algfolder\}\{SYNTH-A_\d{6}h\d{4}/\}", commands) + assert "the best algorithm from BBOB 2009" in commands + + def test_html_pages_link_figures(self, run): + ppfigdim = run.read("SYNTH-A/ppfigdim.html") + assert all("ppfigdim_f%03d.svg" % f in ppfigdim for f in range(1, 25)) + pptable = run.read("SYNTH-A/pptable.html") + assert pptable.count("= 2 + + +def test_expensive_setting_uses_budget_based_targets(pipeline_run): + run = pipeline_run("bbob-1-expensive") + default = pipeline_run("bbob-1") + assert run.glob("SYNTH-A/pptable_f001_05D.tex") + commands = run.read("cocopp_commands.tex") + assert "budget" in commands.lower() or "runlength" in commands.lower() + # tables differ from the default fixed-target setting + assert run.read("SYNTH-A/pptable_f001_05D.tex") != default.read("SYNTH-A/pptable_f001_05D.tex") + + +def test_convergence_plots(pipeline_run): + run = pipeline_run("bbob-1-conv") + assert len(run.glob("SYNTH-A/ppconv_f*.pdf")) == 24 + assert "ppconv.html" in [os.path.basename(f) for f in run.files] + + +class TestComparisons: + def test_two_algorithms(self, pipeline_run, bbob_alg_a, bbob_alg_b): + run = pipeline_run("bbob-2") + assert run.subfolders == ["SYNTH_SYNTH"] + # quirk: the returned dict is keyed by (algId, comment), not by the input path + assert list(run.result) == [("SYNTH-A", ""), ("SYNTH-B", "")] + assert len(run.glob("SYNTH_SYNTH/pptables_f*_*D.tex")) == 24 * 5 + assert len(run.glob("SYNTH_SYNTH/ppscatter_f*.pdf")) == 24 + assert len(run.glob("SYNTH_SYNTH/ppfigs_f*.pdf")) == 24 + assert len(run.glob("SYNTH_SYNTH/pplogabs_*D_*.pdf")) == 2 * 7 + assert len(run.glob("SYNTH_SYNTH/pprldmany_*D_*.pdf")) == 5 * 6 + assert len(run.glob("SYNTH_SYNTH/pprldmany-single-functions/pprldmany_f*_*D.pdf")) == 24 * 5 + assert run.glob("SYNTH_SYNTH/pprldistr2.html") + + def test_two_algorithms_tables_have_a_row_per_algorithm(self, pipeline_run): + run = pipeline_run("bbob-2") + table = run.read("SYNTH_SYNTH/pptables_f001_05D.tex") + assert len([line for line in table.splitlines() if line.endswith("/15\\\\")]) == 2 + assert "SYNTH-A" in run.read("SYNTH_SYNTH/pptables.html") and "SYNTH-B" in run.read("SYNTH_SYNTH/pptables.html") + + def test_significance_markers_in_tables(self, pipeline_run): + """B is twice as fast as A, hence some ERT differences must be significant""" + run = pipeline_run("bbob-2") + tables = "".join(open(os.path.join(run.outdir, f)).read() for f in run.files if "/pptables_f" in f) + assert "\\star" in tables or "downarrow" in tables or "uparrow" in tables + + def test_three_algorithms(self, pipeline_run): + run = pipeline_run("bbob-3") + assert run.subfolders == ["SYNTH_SYNTH_SYNTH"] + assert len(run.glob("SYNTH_SYNTH_SYNTH/pptables_f*_*D.tex")) == 24 * 5 + # scatter plots and pairwise ECDF comparisons are only made for two algorithms + assert not run.glob("*ppscatter*") and not run.glob("*pplogabs*") + + def test_include_single(self, pipeline_run): + run = pipeline_run("bbob-small-include-single") + assert run.subfolders == ["OLDFMT", "SMALL", "SMALL_OLDFM"] + assert run.glob("SMALL/index1.html") and run.glob("OLDFMT/index1.html") + assert run.glob("SMALL_OLDFM/index.html") + # comparison tables only for the functions both algorithms have data for + assert len(run.glob("SMALL_OLDFM/pptables_f*_*D.tex")) == 2 * 2 + 2 * 2 + + def test_background_algorithms(self, pipeline_run): + run = pipeline_run("bbob-1-background") + assert run.subfolders == ["SYNTH", "SYNTH-A"] + assert len(run.result) == 3 # foreground and background algorithms + assert run.glob("SYNTH/pprldmany_05D_noiselessall.pdf") + + +class TestOtherSuites: + def test_biobj_single(self, pipeline_run): + run = pipeline_run("biobj-1") + assert run.subfolders == ["SYNTH-BI"] + assert len(run.glob("SYNTH-BI/pptable_f*_*D.tex")) == 55 * 3 + assert len(run.glob("SYNTH-BI/ppfigdim_f*.pdf")) == 55 + assert run.glob("SYNTH-BI/pprldmany-single-functions/pprldmany_gr_all-functions_*D.pdf") + assert "best 2016" in run.read("cocopp_commands.tex") or "BBOB 2016" in run.read("cocopp_commands.tex") + + def test_biobj_two(self, pipeline_run): + run = pipeline_run("biobj-2") + assert run.subfolders == ["biobj_SYNTH_SYNTH"] + assert len(run.glob("biobj_SYNTH_SYNTH/ppscatter_f*.pdf")) == 55 + assert len(run.glob("biobj_SYNTH_SYNTH/pptables_f*_*D.tex")) == 55 * 3 + + def test_noisy(self, pipeline_run): + run = pipeline_run("noisy-1") + assert len(run.glob("SYNTH-NOISY/pptable_f1*_*D.tex")) == 30 * 3 + assert run.glob("SYNTH-NOISY/pprldistr_05D_nzall.pdf") + assert run.glob("SYNTH-NOISY/pplogloss_20D_nzall.pdf") + + def test_constrained(self, pipeline_run): + run = pipeline_run("constrained-1") + assert len(run.glob("SYNTH-CONS/pptable_f*_*D.tex")) == 54 * 2 + assert len(run.glob("SYNTH-CONS/ppfigcons1_*_d*.pdf")) == 9 * 2 + assert run.glob("SYNTH-CONS/pprldistr_05D_all m=0n+1.pdf") # quirk: blanks and '=' in file names + (dsl,) = run.result.values() + assert all(ds.has_constraints for ds in dsl) + + @pytest.mark.parametrize("scenario, folder, dims", [("largescale-1", "SYNTH-LS", (20, 40)), ("mixint-1", "SYNTH-MIXINT", (5, 10)), ("boxed-1", "SYNTH-BOXED", (2, 5))]) + def test_single_suites(self, pipeline_run, scenario, folder, dims): + run = pipeline_run(scenario) + assert run.subfolders == [folder] + assert len(run.glob(folder + "/pptable_f*_*D.tex")) == 24 * len(dims) + assert len(run.glob(folder + "/ppfigdim_f*.pdf")) == 24 + + def test_old_format(self, pipeline_run): + run = pipeline_run("bbob-old-format") + assert run.subfolders == ["OLDFMT"] + assert len(run.glob("OLDFMT/pptable_f*_*D.tex")) == 2 * 2 + + +@pytest.mark.parametrize("option, present, absent", [ + ("--tab-only", ["pptable_f001_05D.tex"], ["ppfigdim_f001.pdf", "pprldistr_05D_noiselessall.pdf"]), + ("--fig-only", ["ppfigdim_f001.pdf"], ["pptable_f001_05D.tex", "pprldistr_05D_noiselessall.pdf"]), + ("--rld-only", ["pprldistr_05D_noiselessall.pdf"], ["pptable_f001_05D.tex", "ppfigdim_f001.pdf"]), + ("--no-rld-single-fcts", ["pptable_f001_05D.tex"], ["pprldmany-single-functions/pprldmany_f001.pdf"]), +]) +def test_output_options(option, present, absent, bbob_small, tmp_path): + run = pipeline.run_main(option + " " + bbob_small, tmp_path) + for f in present: + assert run.glob("SMALL/" + f), (option, f) + for f in absent: + assert not run.glob("SMALL/" + f), (option, f) + + +def test_output_is_deterministic(bbob_small, tmp_path): + import snapshots + + s1 = snapshots.take(pipeline.run_main(bbob_small, tmp_path / "a")) + s2 = snapshots.take(pipeline.run_main(bbob_small, tmp_path / "b")) + assert s1["files"] == s2["files"] + assert not snapshots.diff(s1, s2) + + +def test_main_twice_in_same_session(bbob_small, bbob_old_format, tmp_path): + """state must not leak between two calls in the same session (test.py subtest 19)""" + import contextlib + import io + import warnings + + from conftest import isolated_cocopp_state + + # the RNG is reseeded on entry of the table modules, otherwise their dispersion values differ + with isolated_cocopp_state(tmp_path), warnings.catch_warnings(), contextlib.redirect_stdout(io.StringIO()), pipeline.reseeded_entry_points(): + warnings.simplefilter("ignore") + results = [cocopp.main("--no-interactive -o out%d %s" % (i, folder)) for i, folder in enumerate([bbob_small, bbob_old_format, bbob_small])] + assert [list(r) for r in results] == [[("SMALL", "")], [("OLDFMT", "")], [("SMALL", "")]] + first, third = (pipeline.PipelineRun(tmp_path, "out%d" % i, None, "", 0) for i in (0, 2)) + assert [pipeline.TIMESTAMP_RE.sub("", f) for f in first.files] == [pipeline.TIMESTAMP_RE.sub("", f) for f in third.files] + for name in ["SMALL/pptable_f001_05D.tex", "SMALL/pptable_f015_02D.tex", "SMALL/pprldmany-single-functions/pprldmany_f008.tex"]: + assert first.read(name) == third.read(name), name From 2a2b1227b3ab2dd3b755f6b530a9fea9f802a4d1 Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 10:40:31 +0200 Subject: [PATCH 04/10] [tests] add smoke tests of the command line entry points Run python -m cocopp, cocopp.main from python -c, cocopp.reduce_size and findfiles in subprocesses that import a temporary copy of the package. --- tests/smoke/test_cli.py | 130 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 tests/smoke/test_cli.py diff --git a/tests/smoke/test_cli.py b/tests/smoke/test_cli.py new file mode 100644 index 0000000..416928e --- /dev/null +++ b/tests/smoke/test_cli.py @@ -0,0 +1,130 @@ +"""Smoke tests of the command line entry points, run in subprocesses. + +The subprocesses import the throw-away copy of the package (see the +``package_copy`` fixture) by putting it first on ``PYTHONPATH``, hence +they cannot write into the source tree either. +""" + +import os +import subprocess +import sys + +import pytest + +import cocopp + + +@pytest.fixture +def run_python(package_copy, tmp_path): + """return a function running ``python args...`` in `tmp_path`, returning the `CompletedProcess`""" + env = dict(os.environ) + env["PYTHONPATH"] = os.pathsep.join([str(package_copy.parent)] + [p for p in env.get("PYTHONPATH", "").split(os.pathsep) if p]) + env["MPLBACKEND"] = "Agg" + + def run(*args, timeout=600, env_update=None): + return subprocess.run( + [sys.executable] + list(args), cwd=str(tmp_path), env=dict(env, **(env_update or {})), capture_output=True, text=True, timeout=timeout + ) + + return run + + +def test_subprocess_uses_package_copy(run_python, package_copy): + res = run_python("-c", "import cocopp; print(cocopp.__file__)") + assert res.returncode == 0, res.stderr + assert res.stdout.strip().startswith(str(package_copy)) + + +def test_import_is_quiet_and_fast(run_python): + res = run_python("-c", "import time; t = time.time(); import cocopp; print('%.2f' % (time.time() - t))") + assert res.returncode == 0, res.stderr + assert res.stdout.splitlines()[-1] and float(res.stdout.splitlines()[-1]) < 20 + assert len(res.stdout.splitlines()) == 1 # nothing else printed on import + + +def test_version(run_python): + res = run_python("-c", "import cocopp; print(cocopp.__version__)") + assert res.stdout.strip() == cocopp.__version__ + + +def test_cli_without_arguments_prints_usage(run_python): + res = run_python("-m", "cocopp") + assert res.returncode == 0, res.stderr + assert "Main routine for post-processing data from COCO" in res.stdout + + +def test_cli_help(run_python): + res = run_python("-m", "cocopp", "--help") + assert res.returncode == 0 and "--output-dir" in res.stdout + + +def test_cli_unknown_option(run_python): + res = run_python("-m", "cocopp", "--no-such-option", "x") + assert "For help use -h or --help" in res.stderr + + +def test_cli_missing_data(run_python, tmp_path): + res = run_python("-m", "cocopp", "--no-interactive", str(tmp_path / "does-not-exist")) + assert res.returncode != 0 + assert "does-not-exist" in res.stderr + + +@pytest.mark.slow +def test_cli_postprocess_single(run_python, bbob_small, tmp_path): + res = run_python("-m", "cocopp", "--no-interactive", "-o", "out", bbob_small) + assert res.returncode == 0, res.stderr[-3000:] + assert "ALL done" in res.stdout + assert (tmp_path / "out" / "index.html").exists() + (folder,) = [d for d in os.listdir(tmp_path / "out") if os.path.isdir(tmp_path / "out" / d)] + assert folder.startswith("SMALL_") + assert (tmp_path / "out" / folder / "pptable_f001_05D.tex").exists() + + +@pytest.mark.slow +def test_cli_postprocess_two(run_python, bbob_small, bbob_small_b, tmp_path): + res = run_python("-m", "cocopp", "--no-interactive", "--no-svg", "-o", "out", bbob_small, bbob_small_b) + assert res.returncode == 0, res.stderr[-3000:] + (folder,) = [d for d in os.listdir(tmp_path / "out") if os.path.isdir(tmp_path / "out" / d)] + assert folder.startswith("SMALL_SMALL_") + assert (tmp_path / "out" / folder / "pptables_f001_05D.tex").exists() + + +@pytest.mark.slow +def test_python_api_main_twice(run_python, bbob_small, bbob_old_format): + """corresponds to subtest 19 of the legacy ``cocopp/test.py``""" + script = "import cocopp; cocopp.main(%r); cocopp.main(%r)" % ("--no-interactive -o a " + bbob_small, "--no-interactive -o b " + bbob_old_format) + res = run_python("-c", script) + assert res.returncode == 0, res.stderr[-3000:] + assert res.stdout.count("ALL done") == 2 + + +def test_reduce_size_cli(run_python, tmp_path): + import synthdata + + folder = synthdata.write_bbob(str(tmp_path / "data"), "RED", functions=(1,), dims=(10,), seed=5) + res = run_python("-m", "cocopp.reduce_size", "--help") + assert res.returncode == 0 and "reduce_size" in res.stdout + res = run_python("-m", "cocopp.reduce_size", folder) + assert res.returncode == 0, res.stderr + assert len([d for d in os.listdir(tmp_path / "data") if d.startswith("RED-")]) == 1 + res = run_python("-m", "cocopp.reduce_size") + assert res.returncode != 0 and "needs a folder name" in res.stderr + + +def test_findfiles_from_shell(run_python, bbob_small): + res = run_python("-c", "from cocopp.findfiles import main; print(len(main(%r)))" % bbob_small) + assert res.returncode == 0 and res.stdout.strip() == "4" + + +@pytest.mark.xfail(reason="the order of inconsistent hv-hashes in the version label depends on PYTHONHASHSEED", strict=True) +def test_version_label_independent_of_hash_seed(run_python): + script = ( + "import cocopp; cocopp.testbedsettings.reference_values = {'A': 'a1b2c3', 'B': 'z9y8x7', 'C': 'k5l6m7'};" + " print(cocopp.toolsdivers.get_version_label())" + ) + labels = set() + for seed in range(8): + res = run_python("-c", script, env_update={"PYTHONHASHSEED": str(seed)}) + assert res.returncode == 0, res.stderr + labels.add(res.stdout.strip()) + assert len(labels) == 1, labels From 14ea6da01c9c66295cebb0ac4502fd7cc2fa411c Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 10:40:31 +0200 Subject: [PATCH 05/10] [tests] add tests using the online data archives Port the scenarios of the legacy python -m cocopp.test runner, including its doctests and pickle regression test. Skipped unless --run-network. --- tests/network/test_archives_online.py | 147 ++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 tests/network/test_archives_online.py diff --git a/tests/network/test_archives_online.py b/tests/network/test_archives_online.py new file mode 100644 index 0000000..745bb13 --- /dev/null +++ b/tests/network/test_archives_online.py @@ -0,0 +1,147 @@ +"""Tests that need the official online COCO data archives. + +These are skipped unless pytest is called with ``--run-network``. They +port the scenarios of the legacy test runner ``python -m cocopp.test`` +(which is still used in CI) to pytest. Downloaded data are cached in +the user's cocopp cache folder (as with any use of `cocopp.archives`). + +The full post-processing runs of the legacy "all" tests are additionally +marked ``slow``. +""" + +import os +import subprocess +import sys + +import pytest + +import cocopp +from cocopp import archiving, genericsettings, pproc + +import pipeline + +pytestmark = pytest.mark.network + + +class TestOfficialArchives: + def test_archive_names(self): + names = cocopp.archives.names + for suite in ["all", "bbob", "bbob-noisy", "bbob-biobj", "bbob-largescale", "bbob-mixint", "bbob-constrained"]: + assert suite in names + + def test_find(self): + found = cocopp.archives.bbob.find("bfgs") + assert "2009/BFGS_ros_noiseless.tgz" in found + assert cocopp.archives.bbob.find("bfgs") == cocopp.archives.bbob.find(".*bfgs") + assert len(cocopp.archives.bbob.find("bfgs")) > len(cocopp.archives.bbob.find("bfgs.*")) + assert cocopp.archives.all("bbob/2009/BFGS_ros") == ["bbob/2009/BFGS_ros_noiseless.tgz"] + + def test_names_are_unique(self): + bbob = cocopp.archives.bbob + for name in bbob: + assert bbob.count(name) == 1 and len(bbob.find(name)) == 1, name + + def test_get_extended_patterns(self): + arch = cocopp.archives.all + assert len(arch.get_extended(["bbob/2009/BFGS_ros!"], remote=False)) == 1 + assert len(arch.get_extended(["bbob/2009/.*BFGS"], remote=False)) >= 1 + + def test_remote_archives_list(self): + urls = cocopp.remote_archives() + assert len(urls) > 0 and all(u.startswith("http") for u in urls) + + def test_get_remote_archive_by_url(self): + url = "https://cma-es.github.io/lq-cma/data-archives/lq-gecco2019" + arch = archiving.get(url).update() + assert len(arch) == 4 + assert arch.remote_data_path.split("//", 1)[1] == url.split("//", 1)[1] + + +def _run_official(args, tmp_path): + return pipeline.run_main(args, tmp_path) + + +@pytest.mark.slow +@pytest.mark.parametrize( + "args", + [ + "bbob/2009/BFGS_ros_noiseless", # legacy subtest 1 + "RANDOMSEARCH-4_Auger_bbob-biobj.tgz", # legacy subtest 2 + ], +) +def test_postprocess_archived_data(args, tmp_path): + run = _run_official(args, tmp_path) + assert "ALL done" in run.stdout + assert len(run.result) == 1 + + +@pytest.mark.slow +def test_postprocess_constrained_archive_by_url(tmp_path): + """legacy ``tmp_test_constrained``""" + url = "https://numbbo.github.io/cocopp-playground/bbob-constrained/testdata/bbob-constrained" + data_path = archiving.get(url).get("fmincon_consbbob") + run = _run_official(data_path, tmp_path) + assert "ALL done" in run.stdout + + +@pytest.mark.slow +@pytest.mark.parametrize( + "args", + [ + "BIPOP-CMA-ES_hansen_noiseless MCS_huyer_noiseless 2009/NEWUOA_ros_noiseless.tgz RANDOMSEARCH_auger_noiseless.tgz BFGS_ros_noiseless.tgz", + "SMAC-BBOB_hutter_noiseless.tgz lmm-CMA-ES_auger_noiseless.tgz", + "--include-single DE-PSO_garcia-nieto_noiseless.tgz VNS_garcia-martinez_noiseless.tgz", + "--expensive VNS_garcia-martinez_noiseless.tgz", + "RANDOMSEARCH-4_Auger_bbob-biobj.tgz RANDOMSEARCH-100_Auger_bbob-biobj.tgz", + "NSGA-II-MATLAB_Auger_bbob-biobj.tgz RANDOMSEARCH-4_Auger_bbob-biobj.tgz RANDOMSEARCH-100_Auger_bbob-biobj.tgz", + "MCS_huyer_noisy.tgz BFGS_ros_noisy.tgz", + "test/RS-4.zip", + "bbob/2009/BFGS! bbob-largescale/2019/LBFGS!", + "NSGA-II! 2019/IBEA!", + "constrained/2022/RandomSearch-5", + ], + ids=lambda a: a[:40], +) +def test_legacy_all_scenarios(args, tmp_path): + """the "all" scenarios (subtests 3-20) of the legacy ``python -m cocopp.test all``""" + run = _run_official(args, tmp_path) + assert "ALL done" in run.stdout + + +@pytest.mark.slow +def test_legacy_main_twice_in_one_process(package_copy, tmp_path): + """legacy subtest 19: post-process archived data twice in the same python session""" + script = "import cocopp; cocopp.main('--no-interactive slsqp!'); cocopp.main('--no-interactive abc*')" + env = dict(os.environ, PYTHONPATH=str(package_copy.parent), MPLBACKEND="Agg") + res = subprocess.run([sys.executable, "-c", script], cwd=str(tmp_path), env=env, capture_output=True, text=True, timeout=3600) + assert res.returncode == 0, res.stdout[-3000:] + res.stderr[-3000:] + assert res.stdout.count("ALL done") == 2 + + +def test_pickle_regression(quiet, monkeypatch): + """legacy ``do_pickle_regression_test``: data read from a pickle file equal data read from the archive""" + monkeypatch.setattr(genericsettings, "pretend_initial_solution_was_evaluated", False) + name = cocopp.archives.all.get_extended("strained/2022/al3-cma")[0] + dsl1 = pproc.get_DataSetList(name) + if not pproc._pickle_file_read: + dsl1 = pproc.get_DataSetList(name) # second call reads the pickle written by the first + assert pproc._pickle_file_read + old, pproc._ignore_pickle_files = pproc._ignore_pickle_files, True + try: + dsl2 = pproc.get_DataSetList(name) + finally: + pproc._ignore_pickle_files = old + assert not pproc._pickle_file_read + dsl2._is_same_list_assert(dsl1) + + +@pytest.mark.slow +def test_package_doctests(package_copy): + """run the doctests of all modules like ``python -m cocopp.test doctest`` (the CI does this) + + The doctests run in the throw-away package copy because they change + into the package folder and may write files there. + """ + env = dict(os.environ, PYTHONPATH=str(package_copy.parent), MPLBACKEND="Agg") + res = subprocess.run([sys.executable, "-m", "cocopp.test", "doctest"], cwd=str(package_copy.parent), env=env, capture_output=True, text=True, timeout=1800) + assert res.returncode == 0, res.stdout[-5000:] + res.stderr[-5000:] From db5c37e57b7c527fcb3b2df17e36a0f634b7313a Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 10:40:31 +0200 Subject: [PATCH 06/10] [tests] add golden-file regression tests of the pipeline output Snapshots of the normalized text output (all LaTeX and html files, the list of produced files and of broken html links) of 15 pipeline scenarios on deterministic synthetic data. Rewrite them after intended changes with pytest tests/regression --update-snapshots. Verified to be reproducible across processes with different PYTHONHASHSEED values. --- tests/regression/test_snapshots.py | 45 ++++++++++++++++++ tests/snapshots/bbob-1-background.json.gz | Bin 0 -> 66513 bytes tests/snapshots/bbob-1-conv.json.gz | Bin 0 -> 29999 bytes tests/snapshots/bbob-1-expensive.json.gz | Bin 0 -> 24895 bytes tests/snapshots/bbob-1.json.gz | Bin 0 -> 29679 bytes tests/snapshots/bbob-2.json.gz | Bin 0 -> 59203 bytes tests/snapshots/bbob-3.json.gz | Bin 0 -> 75315 bytes tests/snapshots/bbob-old-format.json.gz | Bin 0 -> 6938 bytes .../bbob-small-include-single.json.gz | Bin 0 -> 23288 bytes tests/snapshots/biobj-1.json.gz | Bin 0 -> 33509 bytes tests/snapshots/biobj-2.json.gz | Bin 0 -> 49762 bytes tests/snapshots/boxed-1.json.gz | Bin 0 -> 13437 bytes tests/snapshots/constrained-1.json.gz | Bin 0 -> 25417 bytes tests/snapshots/largescale-1.json.gz | Bin 0 -> 13097 bytes tests/snapshots/mixint-1.json.gz | Bin 0 -> 13736 bytes tests/snapshots/noisy-1.json.gz | Bin 0 -> 24368 bytes 16 files changed, 45 insertions(+) create mode 100644 tests/regression/test_snapshots.py create mode 100644 tests/snapshots/bbob-1-background.json.gz create mode 100644 tests/snapshots/bbob-1-conv.json.gz create mode 100644 tests/snapshots/bbob-1-expensive.json.gz create mode 100644 tests/snapshots/bbob-1.json.gz create mode 100644 tests/snapshots/bbob-2.json.gz create mode 100644 tests/snapshots/bbob-3.json.gz create mode 100644 tests/snapshots/bbob-old-format.json.gz create mode 100644 tests/snapshots/bbob-small-include-single.json.gz create mode 100644 tests/snapshots/biobj-1.json.gz create mode 100644 tests/snapshots/biobj-2.json.gz create mode 100644 tests/snapshots/boxed-1.json.gz create mode 100644 tests/snapshots/constrained-1.json.gz create mode 100644 tests/snapshots/largescale-1.json.gz create mode 100644 tests/snapshots/mixint-1.json.gz create mode 100644 tests/snapshots/noisy-1.json.gz diff --git a/tests/regression/test_snapshots.py b/tests/regression/test_snapshots.py new file mode 100644 index 0000000..dbdd30f --- /dev/null +++ b/tests/regression/test_snapshots.py @@ -0,0 +1,45 @@ +"""Golden-file regression tests of the complete post-processing output. + +Each scenario from `pipeline.SCENARIOS` is run once (cached for the test +session) on deterministic synthetic data with a fixed random seed, and +the normalized text output (all LaTeX tables and commands, html pages, +the list of produced files and of broken html links) is compared to +the stored snapshot in ``tests/snapshots``. + +These tests are the safety net for refactoring: any change in the +produced numbers or formatting shows up as a diff. If a change is +intended, rewrite the snapshots with:: + + pytest tests/regression --update-snapshots + +and review the changes of the (gzipped) snapshots with, e.g., +``python tests/snapshots.py`` (see `snapshots.diff`). +""" + +import os + +import pytest + +import pipeline +import snapshots + +pytestmark = pytest.mark.slow + + +@pytest.mark.parametrize("scenario", sorted(pipeline.SCENARIOS)) +def test_snapshot(scenario, pipeline_run, synth_root, request): + run = pipeline_run(scenario) + actual = snapshots.take(run, replacements=[(str(synth_root), "")]) + if request.config.getoption("--update-snapshots"): + snapshots.save(scenario, actual) + pytest.skip("snapshot written") + if not os.path.exists(snapshots.path(scenario)): + pytest.fail("no snapshot for %s, run pytest with --update-snapshots" % scenario) + expected = snapshots.load(scenario) + differences = snapshots.diff(expected, actual) + assert not differences, "output of scenario %s changed:\n%s" % (scenario, differences) + + +def test_no_orphaned_snapshots(): + stored = {f[: -len(".json.gz")] for f in os.listdir(snapshots.SNAPSHOT_DIR) if f.endswith(".json.gz")} + assert stored <= set(pipeline.SCENARIOS) diff --git a/tests/snapshots/bbob-1-background.json.gz b/tests/snapshots/bbob-1-background.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..ad155bb05a549d8f1cac335a6a9e0803027e9f54 GIT binary patch literal 66513 zcmeFY1#Dc+x~`eTjwyzi-DZxNnVB)B7~9OuZH(D5Gcz+Y#>{R*%*@Qp!~~v46IYT#Cap?`n-dNlO?G%JRI({cpnuCj0gn*yez52 z{3dWm^anTrQYI2!Cf+ZGU#ytX1R;r;nO1mtmigbFPtN$<+LPWGGCcXyN4zj@sD-O=m3?$`jm?=C|-U#^}XPEPdQK&*IgJZ&AV$%y0Q(6I|UyVK%e zl+I?rm=~v}oUu*8R&D9iLf?uZpXO=DqFiGd{pqP#W#QJwn}&1p`4fBSb(%+Gllv#= zwb#?mo;$VJ1C9#`HU6N$Z?3E$2hL_9gvVDY7_3ztok=JtLwE%zs3I;1<@Apw z;~!K^ewHu`%mA|fv+iOpKjn16l5s*6lc*AAi5bAV$#*BH_ikCPtF0YFP~*MMeq~2z zM-rG0XO|aCF2gg0{fWiqHudQMZ^{IS1WxgcVSn=Hd$QzHkMQ$uz?Ch$4i)Jn23l}- z;L+}5sv~_Hiy)Tvb?zCemA^91+#Smx5^UX2ua&Kj@pykehz zuwSsPnGeg+DCyaVbIsE=*)Z8=r3|)l(>w7wCIQbN5=wL>g?`{Hyv#4wYraX8Np^ zF3SwnOiGOf`lLA4QESKzC2)suz&^@=bFdnB=zq{xjr$MAs&W58Pc`m87^%kn2Lsi( z{{UQ#`xjMd1*3#cF{dN2u?#dpJ=7l%D?f0weOf<<`}u!euzd~})k7`tZq3>D>Awno zpXzj_itha|NHV}%c-2`~ODB+JQJ)h>lbwB0t(iKEq{jb=`MA+v^cyiHXRW*s z1Sz*7GHyh?#{`g_@Sld1<@j$kBar1^ZMDcv*#SLFF0mK1O7?XB#DPxf9cH@Np}!`f zNp5@ikL3*M`I=@~F4AaYHe}=0!j&!Lx_Z2Xw?HxflnK$qpJ z42JS39Gh|yJyMUM)`%U=i&Z}gDf~3Ka{T!?G9F;S>S>v)xP}wDU7b|THu?j{xD0`L zHV|MD`yWvPSo|x~0E>Tx8DR0R)B-I26$gODzj6hz_+MPt`(#y?V^K5rn?L>-Yyv(m zGCiXVo=X$<(EGr5LjFG@7(JI}=%Md=pAPdg%Kz!W@>)6t)J#3thcv{NI0kWRle76G zd>g(f_~f&aA}EmzOUW!bEEB^Nn=_h6!E81xlfo36J!(k7tUfH0z!aN1x=q0h8kWgm zP%6%9cmAqJ9ufv2+V4ZWB@k_wHOQf0h)cnQhz5ZE&$^?DwPd1&mfD4_A@!{@|7>h4`r&WHuF<$RpcLO(ahO!XW#bd=SQ8 zsAH%EYB(hY>M0u0ckHkX)>3S%quJBIf#5%1V!>@Qq752QL=7kqMrI4(3;XZ{e)``E zL?HNp_uQBP1qpoNe4w1=IJM_i=U^(f*xagApxoKmiM1 zSnqwDehOI9 z=JiK8Y}1a~%AyEXOJ=0toPWRq&iRXx91i$CW9&fDkCAdj;G8HEMVdfg=ww))L78Mzt~E z<=A2)=WJvgY7U3JVKcrOvgg60Ps}}tb7P;h31&xs33Rm?xi~QuY%jji-CQYyRLBysp7{vU)dH_^0z#2Xa;SK>bI%_Qex?t9{1QRKKH zL3%D9m$;{6#N&Suth4!A0v`7<{%4{>=KAu~j0t;^Elz7!?^4qLT!)})=8Ep7Rn*L$ z-Z|;s-^<8e6{~o}eGYV0YCcqY4lLt)uV^JIMcOU;uX&%UWj5cukSBxqUqt2bmG*BP z%x)YJ<$0w~Do7HP3<={12|Ni$bDoV5xi*q~tReMOkLjS11SaIr zOHU@|?~i=HHllp2VewQ?>7bDXCKS+1PbV7fk1$;u0Um4MJ=Fs{XrzG&`Sc@GiADz_ zHP=R*k2ScS>Jc3@;=qJl`jP3xor96fYa`HO4G&0t%(CL2bMW!1+%}qQhtF~c2;4FK zJT>~TGV-A%@uMR>#J&#H#Q*(*Kue;}```yUP*d&jdF}v-JEo{pBf^!D|Gr=+4S9+r7Q*`=+uJiypet8^H6SM#oc`aT;k0=#@fG zmk2%0gM)U}+xc)clK95+^!WA|+4;QIm-TRL&C|;5{xEXF@#s`Nwbv)bN1&w@i7|4q zLhxhp&R7cr@(`qVy0!-olnbw@O|MMYoSC@9re3T&Gj^sxR^xd*D>e0WnN}yg zvA|S1awdg)rL_UjD_>$uI4O(TXZpM%a}M1axB65o7RTRSQbi9@MxL@1LV-|%#SMwr zZR(?EHiGX*^3!&aPG$D;0Z>{`W z54yIt5LW~})bBg#rBG;4p9etw^}ax%sPp+`jlD$dZD!q#2jt+1=|z%`wN`d*U-x|o zTE(M-$xoA6t0s98=gmreEGXY$v8}vsQS$d{1)A}_cvKR>x2;U5#$0yyKy#x_IDq>^ z)5DCe9Y2mg*=L_G7FuSyQLVPr?CvQ)h-{8T%VRWDe4Wm3! zn7qZ}GZc5gMbpfO&j49Bu~`0GS+MDqiQ@r4`!$S@Hy50NEMnguROw(Wa|FMA1fS;w zb?F2(#Rc)e1!0!%s z@-s{C@<{Kh8`td^*VQm#C}(a2f3YE1JyxqQ8k+7%MZ(yc?7LalZ)?JlHO3KlINvgL zBhV@<>~z8C-Bau70)DH#xUB8E-&?9@e`Z)4pigMbU0B!W(xB^+rP=Lrf@iBB>hRggR03r}-%Ct&o{MhBXmXZdJmp~;n@6m^Ly z&9p~-JyDPYDy*QVYi-Y^)E&h8*6gYfyDmugdiP>#hl%^JehOZ-%y`*_MWaTll^0J9 zYJTQP(w?u< zrFZGXK^%U2I(2)Uwq#JRKpH|gJ3lzSxxBsFWN~DD0J)>Voo?Ri*R+(68TI6Jr_-F- zfW0YB_ijDfw+WzZ$oV5o`XHz)*zh_5V7eYuP2Rbx`MHgxX;&nZ5umjWxNw72t^z7| zY3afgS>!2}xPqY>M&6F~=2WG8by!RY4xOR{At+GDt*6~ml>xVn=Cn{Di$T}^Liqm4 zvXmq@_2JmQx&8N}Vmm(lmoB$pHmv|0?R0V|EZndepK4X>9_f3B`-L|gM9kcSL#Yu$ z1ii^Vp!h~!^X#mN-;N^-wa$5J-?lyTsl5kxa}y4vPYX>%1SXPSPa3^%mSyIWmEMQf z6PjoGd7pF9jaF#Qss`wztAWI|Krp&@S_iMEdzj5ZwExS`VH?`GD)px)Wa%HGD86Q^ zkw#X+pBwe&LDh*?22ueVb0)xYHhMG@V7u!p*RnR28dgzAu)0yX>;kG(q?L0Wz85f{ ztfE!*W2~2^-?3Ubw{$bTuwx2(0bQe)jHz~bnfBGXsWuU>dAvNA z@(0QmxZ3KC&xEd~yll>3zE#DfV(LL-YgfG1{P5;^dtnFCX9hK&&c1nobSw?r2?XIY zjy*;2JmKj^R=?qRAKZC8dU$s2wc<3TfX{NcA8sp4RbB^Nj#R|h0}Qrb>e@3;tlBbw zuQlHMe&tgh-gcdBscVq3NMO_;A)v%Krk} zzs{Id2Vz47%dGoNwbJ)|SJ#x0Caa0y_pwRqo9#eNWYCxXlzQJUAWDhdaB>u+Oolq)P-NNZ zvIG}zp0g;Oxr;E-o`imgZ`sxto|c@Pzp{x$-{|_S@>^!bT4;FN7;<(lsnpuL9oX2=O zIkpV9(G-rFCGyZPb#_7YhIy&bkH1S4O=7c5b0Rt}!Md_NAM;@R5wJd|w=~8w<8nDR zr`NKxYn)L|*P1DWrB&|Sc(}G|<#k*2fJvQ91C4o=y7VOb(;dftMaIpkA>)?l66kNv zuD3I06_}5fIosHQ@CPyX=N%2nVf&c)cEvUkhE_{#*KX!qiKb~!@k34`)-ax$SuU`|GbZELs zK)f0rYyzIfH?g(E1~r+~5BE3Ed-53w0%@S?*W0bo$rGk$C1k~x{P!_r~Tu9a99*_%5h)75# z>V}XVIFUUNz;U%{;8@}GgS=>iK3<|$J}L%IoPk~VOwU@X{^z<8iH~KxXJ;W)&+(1R zv_g%vFY*Dqw@^EM+t?=b2vXu3v(#@(h9E}V9P-25WRu7sbVSg^~pmu0G?QW+C zhcDll(lgLIlh8u(fzDcQtVS<$R&(C6>Tgb~k8i3Bj%fjN{D;_St0;bXvmNXdHYee= ziBO8OubEquNPasjo3ESJM(SMj1e7BK4I|>im(dN+s$PHW&oAuT)M?j9 zp2`ZpW3O%8U0DLsueN*MlnjD9Iu@Ue?C)+5S4!1F6_a>5a&5r93Wpcqv(@g_+S)*( zqE)M~<5mi5094T1*t^L9aZh^L@>yXQ*z@L4@}Bl92AF#83&D6$qfPJ1{(Lqhx(UNt)aWa zbacJ$x*w{;XelQ^E4#w=>8T&QzsjRdW-Ko!^X-(vR5_lzY$%5bzZ|3nL~hhqyxE-( zbS*LLtdvGJbYJ|n`p2@9sX4{qdY#t>xJd2CPSyW`V+ z?E#1|@e3VFRmA-G*|*Q9d)vc>S7R9wBX)#c+wm(*0hDUP?rTA?H3rPC?qH9Q%^rU^ zW_kv7*W%Ar_6lt}u_{&ttkgGL`R*FuE_G)OoUYN|!{hIpl8|p)_x*f0mLzrop)(ts zkg*VSPIi+Amyk;k0T&NA@E+s|#;E=GNg;oKFD@90ADyzL3kiuZr*ugF;pYO~g(F(Bka3j}k)xt@@7e@~tW0)A0&1_SggZOzp@XW@^I8 zjeJk1dXS0Vh@C>p#jajC^Jl@RvsvBUb8EF92edjMr;ATtb;>qwas;+2n`QAo9B=f0 z5pZaV-f@b0+&rM04x{Spq4{I@MI9J;SEc8QV6$=Akrk}|S$G@{@(C8(udSge_P?D81w?3xDlY|mM0dy6NjOi$XpzT5&sp(SIXd6H6nk`nu9 zvF4~5_AU$UZXJTdB8EdtAg6XHr)N(ZpZB=A{(E8VuH^)YX}0)8miUBFWR_rLRsg=B zAHLuhUSB9)Uko=$1UE?9YZ&Zn7>#{omVM;9_w2t5=Cj1-g(9m2BdY@N_5ASlzVLQK z@pfal`6IZ+8C&tl6xG`dMKese# z$>Vv)_IxC(i1OVnL)5G*a$}zn@7VjzsOQex{Qk8x@XSTpeNK5)fD)Snc22VC5rvND zpDhTbBG&eXOzV(T>)XbmbkntDlU(;qqHS`lZMuIc{YK5V4K7$UVE2w(w-ZW5hyO^X z{}HJcFDz#8KNGk__vO21QfA>wwc26lD22RH zDpviE(6aeq)oi+*LMAf(OW8O6QJ!eK@t^Ad!(O~;mBwp?hez+>j29hz$rrq{naG;G z2tfuexw%Y`^cb6O@@#8rnWGQ!ez*Z2)o&nrq!xqs!aGKy^16F` zE~Q0ZCxC9+b*G7p)7xI_L5(z_b|oq`^IfHxt*cr4@=PVFXv<+OuJV&u*88hh>!vN4 zRxS(O5%yfKg_cWGe@JGWZTM8vu?67;_Pk?w%!gsPkMM8T;2^mMSU_A56Gf?!LSurU zWSroFAQYJ*lo2!_P6QB#XQ+T;q~IL&myk^2_o(ofc9RW%Xbzy!_*Rc5B#S1rA_&8( z2;&4z^hbo~51zjQYJh^v*S~~x5`XFk3dwA8sg1|RYAoNP1Yt|sAVO37CcHTz#MDYf z?7thkl?`l*2lEawGp#S2dD;-E6^geo)Z4aXKs3Q#IKf^nL3rU#bvw>IIAWB~$lNgh z`(rBPg@}pM<1H{%nTGH5Lj15opf2){_Q$^u4e!rrHdSs>U)MH z@%@^Y>~%x0-n_R*sg4c4q0o2zyCu_~=3DWc7VNf!{^)?WJ>o5a(oOO4lJsMz9qhz6 z*n2B!0r`$&$xnP(u)kv=pN+|MgCY+EKUIzBLA2ts{j)iDoG-#)1pRGDt^Y`>{|)&_ zrPs|qL7vzdrP@$jjdeg~120 z!RaDpYRE@l_U!(0JMmfm+d@agQZ0NrDA5PR6{0p3FOn@@JyZ21h#VKi>Gyx!qALz0TjZu@QXR`tmcJcv zNvhdQv2sezNw&O8@zUOZIqgW#rTE=64l1>3fWT<=B3_H=+$VEu9k?>aehV*SM;H1G zor{c95#G46XX4cP-t=Z>J!54VO4Zk`M;H6{H;H|ZTMyG~?hKho6*@df1R>rnJV6(Q zU-@)S`9k)$)SpiP{RBqOiUR~Es{?EFXY_RVeEh(*sjC&*j`y88%8G?6h8lgsT_v~& z0qsqlKlH?N%l3^=7AaGJA6mO*V`ata%#eYjQTXNh)Qw4V)$4WBSlPoJ_0$za(7S1j z5s2aK|K{z+NeMrCn(xX_-P4_l})EgFdRP)mpUGI z^i{;`4B~JR>8Vq&@vWasP6JfkpA<~8q$!i*PNFMn9p8oL2&>DiN zc;a+0bP}X#b~$D$S|t~vpKe(16fzpI;u04(HCgDHnpYRhSNtL`R$rs&u9`v$F>gr? zFBsYTwYEXJb-MNO3&}zEx&vJq<*ed_YS~qttkaD|uY&$68LC$Xa-B4lp#yC_PmL_> zG*-h5g&+e}jJ~q+O|$XaPcjul-0KdA+~*F($tG`<>B+M?Mq0c~9wG^=kqB~U-T4Nl z!pv)3iI~%fn>uOYp(l>mSIKXjTELOX3Nq9=As0sk)jIJ4Eq%cEyEgc_CU`%~Jrik8 zci0f3srWz%(XJ>yKBFHwGAC81eC2?W^08F4-;*NPv)fzyAELf|Lq_P>ou zD-P5nM$lhw%YqZeS;{_>+T$HGRu0^@lCIQ|f_G7-Bf0hx^nc6VTsW?gge?nyj;?GX z__!81|3r}zc_LRpb!J#Xre?Xk2e8?0vIg5_v@v^8$rxY9BAGJ|C1t9J17(ak6u+&? ztaUiYrZa;sfXN(Rje29aXeC)`hIV07^?!X&kG;fim~tE$l<)U+up&lU6lcS?d?_5< zogQl(jj{X+d36#|TE?f=aWcs(Zu~@|#3?e@cw~x$;ny>2TFBAzG8ryCJUa2U4(3ri z#&VfVS?8IT$R6JfgVT}=Wb{}RH!Zc*e2+`{f4E@?Gu5=_Lw;H zHe_&ZcRihxVLVF}BrR_e&zZM5Ev=(@v@`8ZUaDnf#ZGIGKf;BE##BYTTi|dNpD<;H zM zQ`~(-1n_NlNzpd&RT6w_F`J}O^}{+J@YOyZaZ$BkiPPKegcaP=_c~4Gu^= zgxUkbYJkX_9gwQO2YyacgAFJcz+lGWt=PG`PgtYOeK9?`M>8x#*&Dv%ZS)}C3?snD^#;UA=nO12cA;aW%p7ICgAN5Zr+PFr}{^VFF zuvHcbC|RyV+qHcl(BNyf6ZOSdWtCrbCs>#&bLL8Pal{J5Lt%E=m26}%sbmOPb|YSc zHFA}^`dNBJo72ntNTEn!=(vt)Cx|H`0Tckds0wNVIYwkOhc@KOYcMx^J~Fdn(q$X- zYj@kx`e9LZMNGsD+-!HVq`Hiwif#n`K0PDdUqPkXH>k;Y>j^X1n=Oucdy ztYHDKh#D99p&zdjWmG)TrxpR1P7H#qFBCssU=}|{F9j;PiHq$#5fVF+W3AA!(_C#{ zeZaMSK?gztH#)?LsRK8C4Yrwn!gL%ns7%rV>(MS^e9KjnMjeRnvk3iFzRz@E`{Gqc z`-H7)UtGYeu>1)Lj+GH@<>XqyxZY_J00KTYQnQYRRZwPZK4+o+&e;Cm9$b8e(QVw3 zhw7=thZ*}4#IlYszj)%O zzES7MKO&pUqMe_k2w8_$Zho|fw2`PGkTUfMmZ}#1yuM%O`Kk!gbJq!HwuGh%L7JoQ zl(+8V405qOm0*kR#;nX{`-;;ljirZc2p<-8a*hOoT!w2FIH9Zjq2`3N4(bxywNU>e zXVz!y>QO0-|FLu`Yfe+PqCxAQKrrI3Py;+=dD@)i77Y50E3&`r&boNuF@G2mEdy@Y{jDtcAILM+b#l zyJpJ4t;4GI!N$ZSHKM@#{YT4QktQZVvO-3pu@mR=Z(3pAX`^R}#eCV9Rq{n;r>G_J zo#I8#;jjrq3;ag9%)3Pa^wCjV#?xnw<+`i2;mG)=4)t_>f*InF2|uil$V4q<8}kLCA~6gNS9ZK28`*yi`)|T>=~}d@_i7R zM_-kPSNleZRLi1?N?)X;fOpiStjSi|5bDaO6&LA*92y*2_EYKNqf7%$(NO3P2i>5R zPCjgLR(hZZG~c+Wu;WQ|+zgk5WIXMK1x~$*CoPyR5fDA zhijq|+>wmYijIybincKR-y;QQB7UPI43>vX!w0w~ME3mgvi-c>kh(T`XMWEk>i+DK zE3$|9F>*$tdZEtzc)UtBBt!72Adk^Rks$NYmkno5D?|=)r3+P&9WO z!G0(+f%w=w>H;`-qp@0^D| zTVSdebso8e6*3%&Ioh4XfVA}|Onb*0H|mAtQbKuh_ZGMZvnt+g4}?#(bIhT?E%1^1 z{O91zE)VIKKhR{65v@~}hAx>>uJ*^yRI~iXmA(#Z*|H{Rns$s#tXv6_N|}u= zH~w*)C%=^ODL+SE5(6jRl)RrC79z&J;jDXWu%*GvI>ni()QY5*n!yj%0*;@M8Vrb0;zU6BYWj!~GH4t6o`WVGE`iqOsNMWs8lVH+9CO3VFjN{wBK z{Dd9$3=E>HVCWlEX`r)CE6cJiTMhNSFEt7mtc1~*}M^&&&^(3Db)}Mp-p%#v7MS@|Q zMQzx1PX-aMTd*7!Yk&t!4Ii5g8$&{@wfRZpkb;qSc2;5N7CTtQMh)1cj?GuFzvtBW zqw!pWl6S~E<$RuWI;byr`imp10nbI z3ESFoyHXP%IA%GzX{j<2EUyzQF>}#eZc2L}rK!rGA7hsM@gbP1Jo8$}g|;Q#@9{LR zbb%YUd(zLzV?ay_r%-N;*Wgm`Kwred@mc^dS5XWD@>xvyT6xmKX@tRu(>Dv^sfGPe zb1~;mw-(gPuv{wyQRU<3UYKvHy3^K^;)e8?#Rk!9&zTt!31%G?v6U=#$S6ful7$t7 z&k7!Qf+JZpJ;?Rlg3&?#?52<6?-(HLz6f#DET@gQ@Ez{<-ioz+9i<~IJ@@RkI|Xly z28i3NmY{3H1TVgOG`#bZNj$M_7r!WhnqR!jDLoaDS0UF|AsA=*h(gOG$G38Rdq4$i z0uiC`3b9@1>iW}A#f^{uSxMv?L2_i&^L}vaaSEA>V^yMO*D|&OKI8S|=v^%6n7adq z4y3;MU78rQQ6em?3;=CN^}4oDI4YK~cMXb=B)AnnI8gHhJ4-3~(_5U5rh$hBa!`OR zqdirQ7b2X zaT0gda0z13V--XRsk6XL5T1gQ6SVTzLX0A^DII}XBtW}-tu1#1rF4>JOrr0^B+M+< z(#=E&B0)SaVJn2>rEh}2MD*_lvkBJ;QUc=1BrooCE`Un%iFYt|#6yeM7cXGCrs{1- zw(>F>NJO7H(}m4WyX$AO=bxAhCH+z*v3@(j)|?~6h#V&s6=C9$^25>9(&9v4aiZpO zM0@q%PwG$xyV3x>G?`ITGv#MZ+RaESCvKz@{h!|yqrVKtNM_Mb+JSE{wC+OkwY)#J zILz(1!B3dEYtC8EpJIp-bS9ct7!Y!&lYs>r&iD>RWISk%WSB5bgq%i;aqe{O>6mDA z1``rUBcpn7BEBzS%RxRwdKoKsoRf!-6;s z!$*MgXeEKoOmg8xsHvJc2N)4{K*l0#@ckWOlw&ABMo>>pn^k)MD7j?N)9zrFY{Y(e zrE}rayiRcJEU=G>wa?7B`n$_Md#Uj%Op_&!5JNHjSBNW(<8qbYU=o|M{>beuF};(E z$2B!Gb>p$d{^FJXb&W2UV$of9Y zRa!b(zMtZOLql3+wP&-);%KmvjPK+Jo>jXD9uT+xE{S?PQxzWAec(lI?8G~ z&@CvbzOw#^B`)a*HIAm`ykHs$r;tHoG3P?E6c?m_JkT?3Au%i89f?3i|9wHWUGI4u zQ5F-pmYaS)Ubo**H(O3p8piduF9E7wQFB05F$S<8OS*}Koc89D#-MBBY-?2oo<<-E zZo`>g* z2K^JWVVgc3Aq8fz)a(k|MWVL}TpGTGi>ziV^Gt7WpKcz$(5OCTwUDB?C-haTQFXsG z|1z1({OxGjw2Fh6qwDG`WBxDSu?w@#xRcXMN*=DhY%gVH%KAnpCqanbpNIr1#^UI3 zcDZO#j2PqQVH`Yl)=T`<^pq6vkqaXt4ir+q@g?U};5Vck93i%w5LeH>c)WkdFqbhk zlDlV*z)o9toQhlA9GSp=92b%iv{q3tW06MS2ji@!_5lvrgi>q}ekKNmi+=L+<-LsY z*ogu&wd@$O(#BZm280eSo`$J|UCibF1bEl0G9?{W0nbI!e82hUUgS+e%FxyF7JFXK z$7}P)g~MRj-b5`+pj4}yCLByCRxgny3%;QFTF7)t%HFv7h3j=eL$hbdHF7aMt@|9( z5@qz{lQ}%FV-+{uQQ@2*V%u)RsyTi(A?S;X?W-eGYsx4eL0b|eutmaN{?@8;e*z7| zc5OZ-SQJ)PgdO2ijw22Xy3yLlun^x43XdgQ2^}TTYxwZr_g~G1&l{NHA~ zU&mS>#HU$+BAk#FJt=cAPSh*Ol#Zra_MEG@Hc28VgS7uVA!x^SMU!8qhJ{5e&yG05 zrnebqV>h)hP#WI(%F>%^49z5{7zPF`Z{11A)&nnISS)2GWk$T{ex--RwmSI+P#{p} zI!{cA4{2j2a)W5%f4dxnu;~5geP@ZmO)pv1>RwKj{nN&xxL!v5I{!=Mcdas;a54k|~ zoB|lQ^&c%i%4Qo=B=iWmSWcU1UG*?E&%%m9K3huZb=6WJiD6dH5-v`hY&H=WZYZ? zM=96MS#u*YW9M!WfNcJu{K~FfbQwiRiYWIC{m^-Tt<9X8gKs8xD}PhwN_Qc54hgo$ zOAA$eKmfp}&*QxF+zO7K^wTDGqICCKMy2&yuomXT*o3hUhG|4qKb1bv2Ws z$dR8wBW#E-gU0htoQvgn)w;yc&!>7v>%@B3`YmVdkW>M8y72{n_6WBW;u4D?D3dHsbUN>dltqI>l(QmV_qvLt1l=JY*n!16taSKv%CYqY2?XN^T@(V3N&b8wv=>H zeg79RN#wkNmNhh^JD~UC4@Vmni9T6Wi4}WxTW+8j-9EYm8~;x@W{8nlB?(meUtucu zBEG~dmR7?>jx;eJK}5ekI`+-&2rhYP{?w0SWThhJ4}sJ7gb(;i{@pqlZtR1Wrlp9E z1L0A#x!KjBGm%5+AI;I$VFdc;#&WH71k< z2H+N8wtYP}y{=X>N7JH-SkfByyNa$0gl#Gykh~?#AaZB)Rcg6dcBQHyQbfktQvY%4 zyGu4lr;!KmSL)HFhGZk1QdNe6}B5Y7@}S65&>hKsOhr*g%Efh54J;@kv)^w?OC}Tl9a7AP>r0wg(n0dB6>uAjHPHwCNt5X zPZVpuT+8T5gh{idw3m53p--$XRqWnE>X`dHW+pHc-9)TIoh+o%Cd(6BhaToxY$@B~ z3=AhB6Ax>d1^_&dw#Jz$^r>)IH0q@1hTBr02gG&?qsqz0Q^mhkGPGpg2FGN7OVjW9 z@jZ4!xl@iGRCep8MRTx%EvN5aWs8guj;`oLY)Y_IQ5K(tF6JuCG@n%UBNt12`d~ zp+8F#*-JvA@3XiIVqMovy9ZjS@!)Hs-)JRuM^Bg%%VbjFU1M5y8|1ffxM1~pKUYV-t@nz*TG<+AqmhoQys3^%GCGjiw< zslFsBXFE5Sa~I%t)6-q+_|xv>aFcNXu5YaP@pmyZ(*uK3OjHP*EJxdcYzoi7?LBxC zPOzOqmMaXIF2y}XSfo6Cx}>*US7i63eD?3vt^NCUmWvArUr)cL%SSU(z`5BF1eAGs zid@91I%;!k*Qw~x`uQl$mdW7y#-Pk()rD= z9S{?0$CXBpi{`5 z>$U|!zT{~xz+PANIGUN$3nnsokjPi1ghWF_q|BV{RK!HKw*ZSz>|6DBMw4ajDd&B< zWK6J7R{-Y6em=>9%`hY*&QQy3AQ*gd;0*W(LQxL-z%EajrlZ_^RYPH}bN zfo~|jp}ULhPs$jKY}JZgIh26)LBCqn-kbBq_=({ZxKD?Fj!?0FtVNGaa1Kb$Ng0?J zoHQl(BT9pZAjPSXxH>??hHTPQb=+PnZN}oAEd429t0(+Ss#Ag92O^T_KxIZYRK5nALV`xs`nj!wnK$!e$`2vDdVvEg$iFF_>Adr>9OEF!^@)@Z__9b7GM z7M^!TNe0{rbTn&{aVH*=o}MD>5C!#0uBL+tP%7V1z6!bV5*0OR5s^UlM}E7 zGs5>@xm)^p4z?rTFUubLpl1$a^JI4C3qAVgMIP>991NQ0FCOJZ1#(ZuFPA(}6e%Oy zl6dD~WXDmm-G#F*PAAmSMU~mf(!-rek)42wUvKo4VUTY!!@NtimQJZb`x;R)NqNg( zVi$2xeKIq~5SYs@UL4&8%qlbt2E$JV*oRmI)LGFji>9;_! z^5={}PAv?3qL(^R8GSNZGIver*@iud&w~CeZ=I|>&aF$+LPVY-z^EL`T&h{~5%)qd zXU|U=wLGsbQlG4qRk7aA5uDtVCHw=x&dYAsA^t$|49)gT_*hm3ijynZZ~j5#ZydRb zQtSW^RE^VE(v9ewHF%hnqQ$d|tl2QeI(6J)qjs~dfg?W64N=@*%?eK~6xT(!kyZYJ z?1i%cyJM&+k~M5FEiy23DX!w$`2 zXgRerWC%4k2`BA+a75liLCLcN@&-~PDqqF*kWbTvpzbLQQ`W|s4<<@y_Xq7zVHo?| zl-KhO{PxO19+`{2r-@~m9yK?Y*#7s>hjJVo$KQ}Crg}ES6Q_l*71;x)sPXfbur^li(bD3aAVr9c35Grj! zeSZQ!O$Qnn&@eqb!Qf#?z)^js#@iKJsFjo7(tEI$s> z(_=aLe=zq>(UpDM-e{#Nwo{c<>{M*q#)@s*u8N(Cv0~e{Z6_$>&f20rc(AzCcZ@J z)X%i#ar+PhZ^?hdxU0;nVj6)X3x$gg=FerJ_0P&%lsOD7hJF6!z%%jx!k=4E>F~ri zzvH4qsO$+Mp8E3DvXCO(g8t+#*STv10!8p)o@3Dws7`EH@&>I-6%d6sqc9;3i}q)} zt~-Qi6vnvPxNy1cwBTz4vwXqJH!U6lM|Z1JdwF7#Fhimy)$0UKg<<98qUfXDlE-8k zm6p3KmhZJo{VONV=`C;idH( zo_sf?(=?5a4n7c4Vt0IAPyhl-JkD4}?Jtz;MCYs<~cs3V8#)t$1ZQwo|YqUgBBw|=m5=!Lv2GACJSjMc{YVg zh@ZvwEA@H4JzwYubbj5zPt!N9_B)TRVKq+nX8Kq9cHru+M$$orzo=(J`bX@^XGWgOa*=18gTUCv<{w(#9ZbUx14{{dztuO`JF%?5XR~=VUj_ z*Kyq%@c+PfSGA*|>R@`A`!^SN_SW>6{|jrYs{gn-O80TK_a*SsKiani?-M>SC-X0O z?dZvqTrIsDuV?M^+5FpSPE%m_?sc3%Q}^ib{Ja|q^=n1zg#)|WCkhAk!d#)u3_0i% zSAw8@pa{HWfjPusx^2&@Od2+xsDUL0!Rp4ry_7i{H zDhK(AYSweIXXu1cO|c?-HRn}}DBRNEi|S3MwH*vs25kec%-law$h}5MNpbUy`U-N8 zH~pLYJ|1k%>9Vvce9yb;kJ}D+`Zpx}+#Iby>0S`E_R58~f|luxvfCqmXU613qU$FF z=!~7ag8ymUB+#JV|p8@KX?W7`=W zHq$+)-LD;eNw_^9*0rC$`v#os(R?y?_1AYzTaD~d(!|8st=;ZF)Ap?OYcECgfwi?~ zeP__Ja;)qXSfcAplcNjQc0eYvWPrIat)tkzKR*}v5@wFMlW?dV@et6g-r)=T!Tue= zX0ic^h`aEpsVU!2vdry{KnGnk%MB?c^F!WhrSZT&3u}wahl1y_^vQ|y<4Ay_N3L!$ z6DhHCwJk#q3Li_*O_#fEd;}|Z`4d|CVwc&^Q(?0=`b14_$dD=1L#z%?s#d%x3Uhz! zo9i|TRw5VV-yN<8)-d9%S+|48N}-mH^|^tG1FCJ{>;4+JrR#&ktNLpHe1Ch!(BXac zI_m3nr(4?j|H9CxYd5@a*L>gJ_A)0s9^d;%KQYKB7dk#yjlLOt8&;(ox-ej$ZMPQR zWijyo1tvoJY>)Q9o!fqCygXmShm?b=)G4RFUny(rceVY9ZoHuF`RMcv7eeWH+jz(w zbse_f@5O&O>FqKAKfDyX)X>5B00wq~E;DikS5w(R{ncttN{3bC)<&z|>N&lwef%4Ca44mZ^THW*suq(KEG9GC$Tx6x;b8|>k5lr0g%OfGui!)P&j_~;V zi(|?2-LggKY&HDZa%#Mp&#_C-er7>gHE2aBe)x}zC6e(Io+0q8@m@Q%E|8_XA4RxS zCjEXy!Dpz~XDy->eQ^F9{)MhzNb;xZcKFNAo;={=OKIcLj86xel)7c%2qdx#*?59X z>I2laXMwPL{F)^8^Rt3yg+K+j!7q0}o$I^)Kfx&C-EBp|*GOXSigsvSeQW&q##6>n z#F>1EropO#B>bAJiR6|NW3nEypun*HE_0ednyRfz zIcY$Oc~#8ojm&w8W3=pbhPVku)^Z9_bjAUWv}zbJ{#1xbtR56EbhN2owlZ5-JHMxB zO4iP&f$xD-(cV384L&z5JfglrpW4Kg`gC7c+fI%LY%9T%FQyQCxnu2o{`2s{C%+em zD2i&K=vfv`iXNv(o>N`1pt=@Xv%<7xs+a&(QvUPAj36FLa!rHrmz?}~#TSMB-xOq< zf85hZqLlg!+ZSQru&pB|7DDbX)pvkutCFRe73?GSy1M+?`w5XnFXXVOgZ#37nhZL%5=i0+CF<`oL`|9guwo{#c_Uhx(r@SVkO5JHZJv= z>WGr~^je8b+V%Rl1{W+ed5m=n3DHKEcZF)JlKUNsL#4Ao%x3YOIJy`_K}JA&t^W$l^(4 z_HnPw4vGe;3yr;32{;#iCBZg+HnJKH$4AEg7t|1~Tf-e#^!KKksDdk9 zkSd5R_7+UZOVJtE6K3?O4#x}8T3)a=0x9l3I<2le4xkF3hx07*sK zbevpZ(f2T=PWwa)>RQ$}>ZyyEA2~+T&P}$^H>)u~P=rEl2bZcJT|hSMN~A7er74(B z(ajdmPp6VDq7s2AlSrPbv0ulHIJU-36zs1qujNnf&YKf?dj4_hzY_zV-7LJ&!_AII zRu9|hZAL4F2Dp2YXZV9f!ZFRyv@kL|Cy53IuYocdyprsZ$;nzkcEpnmxVM1vEp~$5 z{+V`HEuxGRQ~50~Aq>gw5g9$Ph8q>PJ6NS&)CUVpmVIitYQrXT#3@VpZmUY+FUA!; zc}TGMHsn?eCi_+Dp;7pL$oPOOrsRBhO?J~yerJ-AYbzxNT4R_X_LNpd z3WbBe@zK3^l3CTXI8Cl_DcQWr-T_u~U&Nf^dx!rfPtf;$aq2D+!8!cxM&UdmuS7|N zQWn*}vbbrZm5%pF$jW(QY>dp38i#Bwoq^@up-XI$brtHEXEQLa?{AwY!uSMJewTes zEoo<+N0f2%*TaE%PNz~N?BgdQJTx6yn;kagS^d4S=N_- zhHi#sYIBILgyAuaV22jQwq|_2Q@lXLRa`EUa&t+tD}mJGVhjBwiyryemXa;^?tc#m5vksD2#y)N}WS9XP#;_-$SgSrL`m?;}%LW#q`?%uWgbn{5}m+ zK;;tk^1}Rq;<$3;Egk*&V(_2&buTS5eQPRMT0^y(63;kJu<_gC<1mv+c4_^&*_JH5 zA17Kz>a9-Y>1@LUaN>E{<<{*gzXt*HlJ3AU6_{mFQfh_WcZh+1y;(F!4Su)T&u5wI zD*KWIvmjd*B^{tTbYME2p=#tSK~U#W)=7un;Wbn#7M4k4$>=*I{f<>;4{85|_k*aF zr>p^BA?#g99!o;Wg^Ezz)w_~5V_L;4Y70O5>#>I%#*0gORq{*ItvTV%>D3qV86q1b z&jG_~uOod)2;cJVCDSPv6T}uon?+Mky_R}e2j{RsI;b%D7O~v&4dUwdSP*h?#idS< z@}8+INq0(1P9nKX2=`08GW+EeK%AluoM?mVOaEUi#xT*x!_*Rpo!R zHvSbEBPkJ29bQ*bd|RRdoe)15v|F-ZE_6QA5jwFYRKyJ}6iO<9$UqmQPLZHg$D>%k zgd%;%vBd1bS?0VhtiMp#R9xLRhm~-`k5W2XZ?zkC6-rMs%wHO|!mNaLPAqHp#R)Ku zRs;x`%hUep_!}+3{ZXpwl#XCPld34T*F7?=t z(4F9fAZ}>A7k{W`k~TFTRiXj&_^h6DNFm9VIN_cV_*E~4J&x5D$Aby(FQyWk6g}bj zrVcfZ^$8nK&ow-;%&Czt+MQA z(+;5A^kkx!nyS9~dylpF*C&a3AfOt0D7-4jGy}HL)o=*vrM+8kWfOeRg_ru=LVTM1ImTC*| z%&o1U{cE_eG7F)`0P0VCbYKeApK<_^_|F(<5!C7tMHD1v*m^P|;m|*nhQ&@avM`wx zleAah%jjkTt?=9U9)uYaw8@Mmn1z)9$EfA!=NYiYHf?^`vjb;Ewi3w@k=RmtDK@LS z?p4+dzK~^FBHlS@6|%1+cmU`fRxS~l&60RZXrQ`_!Kms_CF_QaB3PV=-Q<$B@AlY~ z>BU&CWqPc!?z9=JX{*XsrpJE)!DyTN;;4ht$5wJu`7ze zjg+i^=L_#qJ%1MMrYNyE_V-asASD~Q>y;XRbIS2ElqkF;yPJpzLueQSLa?f21AH?{ zmZjQ)?fwj(9-k*&Zsed%?b0S|UY)O*#7&@ju8E$;gb0yFeIux0`90J3XN3sVHLkAZ zsY&@7JdJvp+-?p?7lhz=XmhrVC2=Xv8x&$ma?G29n8U}*64Vw(;IYjw=i}x+R1A!f zvg6^Oht!fnsxovQo=zS4=6+)$kRb7G?k6rvLU~{z?l6ly6BR#CtmP4RLU-Sz%OK3R z&^&&|>tm|yxf)tRJ?uI$IK`io**un{fV|w=@+=5j&!MU9^9W1LPz``CpG>pr#M}gLT zSEq8YplDC=Zbg1i;+c`3yq$bF>}_qH`D$pEO4WPQH8KsX?;9UOgtQSDfno)6DZ|if z!0)~pMb)U<0fjA(0T`ScDD^VqU2U9B8 zOHYV$XzzGQb@6_A?piYuHXwq4USG5@rNqC}h7Q;gY4!Plj%AlsZ1xT@g*+-2gS2=; zG-$UQ=p+3r{k6<;9Kgs##duhtMg7uLUt80mAY%N7CogPgmz7Tsu^_Tv-C7Kw%w`Vw&^HK@!q zI7ISQOS?OV-@;t-NDCv|jlD-BOw3IcKsHPU_8*$MCH2_pX{o-&e5mqI<%0SA> zj_0vm9AXqBJ%ObPx*xjgG2}mof(LmK<*`#U?XR2WX z?bKzImueC4OQ`j-`?Gwdh8S6+anY~W2L1v{M_%0aJB^_?aV+Rq&e8V+;)-L=(FT$y z@rCj=u4JZFGDM*doM6fn-xr*GS{T9u4Oh}b1PBlx# z`0+CrJ9O2%2C+9OPhzU7{-m38m`JOpL}Fx%C&nUP>4+-wSU)LSTbx=`_SYm}Tr`0K zI_OZAQ>KhD=iUqj;E8Co#L*}#wbs&wHPM}dg zv2!Y<3_z>|k;e$6qgS2K^)G01V-~Kd{2NI0WpoqH}_B zdOV5>z^443w~CHYz2!~y1WDpVwG!`|qrkrB)ZlBi@=|_xmU1{_<%O~!9Tr}><#?72 zM@m42*NDosMAUD^TA82`YN{rUI_pf=#Xj}71Zm21MVBBwyf%Qs(}-?-=WeU*ozGAvd`Wc{-lX8)qIIJZ)1 zELpH*MM9QxY}lL_;M1d6Km@^ms7@N^T*YseKonT96c@vF&ktBqpnz=CVjh7|Pyww)DuGeMDGP+y3@fYdmR(uoQ6G*Yl#G~j221LJfg z)45&wvBBVeRkuaGH*H`wzo`?`pSmQPIB!LO45g@J<|I`GwxFTl-1r)KRb+_a_%KSd zY*b$&XKL}nuhAVSnMh|hncdB}f*{xPOQXf#bfpf`+UH1KT!Q>!J*ea>>45wzMsG(= z9;q5|mP$BH!q=LFOQkK=K4JW|P=c?Up;y>D#WIQBL@7Cj!J2H?ckF|zCP8-9=_>rk zN`Arf>@4WmSLOmIMv)NRH76t+LExMy>S%Mr2e6Gcq^tZ^opKAkpOz1?>Kw%~? z^z%?-q7sYzwQ*76KVZbExxY$~9xM#h)?dBe=}*JxNBg7bNgJ+vt@`Uhzo$E zLNO4S1@nrW?Z;udR0e!Xdg5c(%bIfz(X=fkM5kv=(A;;r(7X}qgw+%IcXL(w=cBSj z=c=-uinH9tzUS20N5bw+_E}a?+tO+j}_2-tNc_1Xy zcmg9&__QxBA z5r!jdwkcR-Iaca6qXf9iNEaWwBpC+IN_wEQf8#C!rQe>WHTpdHhG_oZsKlEXvVZ-8QmHMnBb*(Q2U;SPZXCaVkZ919@J>fkrpzS}8#S=iN>6_- zO4u@51`ttf!I1!oWaNo1aPB-Esho zyK7$2Z>mu>Qe0i~gYUAFSzSRgYo$NTFhE3MvQ&5!liSlkG|MjexUCf5h%)r|uHii! z)^$vHnuf#n$q6B$;?&!+_Ytr-jZnHe-?Nm55~$uA|5_F+0iad)_4QTm!n1-Ud_lFC zF%adId0J>iy>~dcaVWFLA`dQPY1*Q2em|OUre9MD@&S>MmSCL~ZE!?6?wE zD^zgsL9kaRh&hk!dY8vZy^L(;@MC<|VJ+vF$vJldd3lnDrAV?IjG(3n8OIu1%qcP+ zJ+%i(ZFXA7CM_dEWi@tld+t=+9LMLK>BG z`5Wj65@jP4K&2)SNrW0mB$y8^7Yc*V<&8^F7iBXEjPX;`3xogCqI+zGpR})bu5Mm1)JFmzo6M=g#=8BQFisSMD-YSTkYOD)M$Mj+Y zy2xNYil+Kj##+lZq^WA~T$15|Ml84iDl$<1D**f>oOw}oqH%S0ZKYppdD)_l34|rd z^NHWNY8U?GEWENeBYqf=25NYz$+H;S!X(5<%s4EjO<{_E{TfEr9#d~rM#+D!Vm>p? zG2NyQow=D}O&jkosbx#^DA~w)ug3)uD~NiMs9!YLC%%9@L>%Ltm$Q06CdkUX1CIDh z<;ww~v$XaHQDiG?0MA!(d! z=Y4P`G_OEoD>8Mbi>ZWGhMS_8q_h-Ed>*qt;2s;wwWP~$Nr?X%^JNxU6E`=z zrZ50l5M+T-ABp;11_h{liF z2wof_p-^mrO`h88AONJADc#g7LeM#T%SoF?8y`ofhLI0)e(OaX!61|{sZ>169PyXx zf?3w_asKr*FZ?9J$c2Q0bQ)r!rKqh>T#RgZlArUNlqF9v%Q82dM4vf$ZD0JZ6@zGY zN{2e#l^P`rI$fZZlDWF_vkg}zbJB{5kfBwHenjUDv59o!D*1_$EETau9g*f_3ji}JydR% zVkr^okK#OfVODqRDU=-;S&DFBF)Wz3PV~RYn$=MeY6aP47(OO6704&~;|qW+Rg+PM z{uXsScxP#EcZX98M2iZB3I4qq0+)OyBSAj7GobKo14jKfCeCy-%wyDX!Cz~T>R(Ta z2dtRBz#!xu%SmyR&+q7+IYfcA$$54R&g{nZAt$}y^IG_PQfYh z#@7=U%;dvT#Vg(!CCy)P;ckCd=v3VV_Ccj^Jb=DBBPKQ$LDaqCI9@76MIDy$Wb4Q- zJR_VDABUCf`T1GA0T<0eGOcm_ahBRovG5@sQwRnY_!T8kh=xpCQyoR<58c3SED5z` zMdvK@zL_j(hCB9@cy-WepMi_vfRAs?CE?6cW<$Ay_V`hSLLWl4u&9UBei!k;ke{oh zRf#2aLN9huY^tYbwg_5tW0Mr>;{YJ&a#*!ARnE-t~bBV7R{WcSj=*EjsBTsWQ?>(-6rT~Ej&d{tE+9mQUG*Z6&R+zp*nMFB- zlk8UEZV>g4T$~j&QI1$V^L@~OW6Fe`P9%{q0yCvKf=mLH#N7V*Y6{)t^J7r*iu-TB zvgHWjiI~pwAQd#mKFnoO8b*o_W)bvuEbQ_ur~8zU4F7LA!~D1L&r#&aJgHNJbzlz- zLIV5+(5mISTheYz6+a@Eb$Au0odAL)GZPlPrNS-HyJgkxa9cnmQAS^Iv-q zwG0(bu$595!k!Z|Q@gIz7x0WusShlpoT8}~Vk4pfLQ?1|Xk!%4dHG>?7G^9;Az^_S z39`6*5wx|&EhJgo=6u>KO9rTlX?PJ%TNMqB>wBY(#T+vx^$9`>-j71v5SqmPPRtl% zy<8Kh_GmM)htmL5AD6?QN@LE=mK7G~DdKP-7Kd}s)FIL$;H+MKUxglP06P_N__>3K zZUd^f7`nFvNs$1R47#M8j1<>)#EPWFmprns4&p2(0LSN*{`ux5t*_f?%iVck-hV}Y zg9jL1fNT1mhwEoisg^fb?Z(cr4KpL3>4i1>+CtSfYh-JhLohIk1=dMG3CH3f#HzIw zCe-vXT8Z!{@fvlL4oQlp3YjSB&N>>=IUtZWa9IVJPhIbm-3S+5AF>@6pe&QLCd?Ym z@Nr0n9c1mW8d*s0sW+-C6`jUwpyY0+N;hQtTTbxJDCDxXP^^{&zYqFEA*VS1$(z%E zGr#{Zy%-1Xu*dRre>^A|HEPr>h>Hib1`kdP&L+ke6*kOI=kAZKEZM0*#){O4eu345 z2q{mvU1O_3Bsf8g4xvUr#@Miw0B0oLxqo@M0XqAreIrHFC#Z_Wfo>Wgj5GVvh}xJB z`L*EedWjSLTQ$5E%kB20&=a{#*&COSQ@-%jsI0IP4jZA>CpwZBSx6 z1w75tSO=KI88>)hboP~?$RD!|0MU$`FYRoHAER@+Qu8`(D5_p5-xVwGM5~=jm1b1f zs((_&q?A-w!K4~gBNJ6e8Zc1Q_a-!QjRilP-CzFgjWn8NIMtvkq5aeNWX>Ko_jjQb zRJv^_>B8%VvHqZOlic_dwKa6&LWh`XMyN+3Sa?tcXdcamSs0=Ml zpqR04Wi#-siYSJ(=X(lYso{}aUDPfvf=Ng?_+f3enhgKpqW`7<_z#r&H|}s!GYd5C z-tFEhNMASFNbf*}F_fvwyzP=vR=bpz$dZ&5M37{xuyL_LfH{bl^=ZMb%xS9H7$=L; zfzLk*f*x{TEu-J=sue-g*T$B!cI_$pI|dVIAF%30k+#;MU{72a#)Lw1$PU|nr|Dh2 zi@n(OR7Renwh6aXbQ*emfV{Xgss=AYq{0&hA1`3VPlFlXGl+Uu5moSH{7${@#C;nU z!YIPz@H~%MoMcy888%Z%9^3>Im? zg30rwJ46Q20X;Gp1IEnAU@O_$2p{)(>8FJV{E5(r2`|I&NHW4XwN~e!aKl^=1J#wG zld~%Xa7Q?P{i}k(2)NP1NG~)SuKG|W1sHi3IG#1ZFgPnmZ}ADlf^=M5b*KKHl#qW7 zP%sq+1;e0z35#Xe?-)#fA2e?H75YadV8@NPbU5f6_zgiMH70ksmmQe~Dbp|O2Z*6G zOzN9w;t<-fWvIq9p%uWN&h{T^<3nFtj|WQ3QlN8KOt=#yBO&Cf3n?5r#Tu|+XeFxq zx{MYdo5#G~5U*9V2A>XV;?677;c0Cg|MaEll(pJ5t);1`dwQ94>H)gfAZZC&nZLKn zTBOwZ3rawdVwPx>ier0V!2N-&mN?2XtX(uz8XB$?&6sm}uQFT3E@N^bCYBdA|Jpn7 zg~1vNYG6{gD|x`FL_1N@xe(Zla9f`Fqjinurx-JmLr?R5vR&a|KyEJXbcWW6fn#1* zpT{D_*JQ__n8>yjAuDr%nh?vSBrMP3OFkm>8t4wu8ZUdD?{?JZ%Vo8d~7Ecb0T?N$-=NdWuaX>1-pc)ga?j?NNB?< zs;8zS*l6EFnhLVKoEIu|zf4ylYd;?qKz`|)wzAUu9bDw(f>|AFVeUT1>7bN7=yn%V zT}p$^gByRidRXU}yO{jkA|Eg#k^wPT9R#C5EffG5;#{U40S}16e>3C_J;W=v;ccpp zatvNH#7;T;5?*qMBh|D`Ri9@e!`q61F1f}>-CJep!a#avU~7nSXZYlhlkmghF%N%x zc;n*J#QM&(pH@I}WZ5DXetCmIE+LDWUqoFq=44+Nh4rt25O=jgTOHX}KLpVkps0{t zidh;lz7jIIS52r0bPwjvu7ife0B@&v<4v+vMF7YTH@82~MY0A&7_@BbJn)#9xQ5+= zcLk|qoo>_Voz8N#XhBltIL_0$A@}a{afpA@QMZ7YaR3Bp+NWdD>7m<5YMgvS?MCXi zQz~%?T&ROYmTdE9u!dq=lz;7rpkWypTD#Z#NA;OYt^czwce(62+XZh8T8v4(_NeL$ zB-h6=tJRQVX-CA0PV9&R3IGopEI*EAE;jC7fTQ6iaT(q>+pV^lZEc({`%kl#J)NZ` zoG<53;f3=La-E*<4@tg0vpDT|_m#HK{-sI26&HLdq}$PcF(8FNRuUv zZ?27>kJ3t?^7VC{_6+Xkocv7BPM`1a8xe`APi+<}DZU3lEV_61>(|crM&H+m{p{{} znTzB7-TunO_U1;MxVyR^!&vht4+p&p7yB1))0=%9_vChqo8i7~kM~b;7To8@r%u<4 z!f8{gITDM-`l%-=#j*+?h5qD=ZvWxA&grE6H=; z=UcALOBmSrAnKUa04RT_V| z%$l)6e0X@{k=<#t;5`DT&gadnbqC;FANIqqHcA@(^;wR!Z*?|q*8yw?y{_%Rd(+Nt za^?EDy>2@F%>yi~TCAQ#7^I4y?mIV+M;24I9SqokuE*1K7pdhp(HHNAEyWEyn(Y^N z8||GiazrKTdu|(nh!xM6FdZT(8wTBt?C+Y&$xp{ld*uGdSZ~AglLJUd5F3Ksjj1^L zE<$NhbPMyUc9y`8gBE}CZx_3$9l8h2I1j;gsh^|zc>#+D7aFP;k25wCAE!}2+RB3( zRh7TXHNp3MIk-qucI9V^&AOpftM|n8aqrabDqByj#j;bfq_B=;trwkHxGh+yt7BQ8 z!dmTuIa2>EV+xjM z3dWia@izlv=|3~lJaTfO zP`u{FcdA9-7i*{Qo|NyNx4oEQFA$&v@Zm0aKCb$g&2NC@nOg1G+P>4BB^5Swx)Aw1 zLzj)2Uc1!WSnawKbT)$x(?y*4%2@ucrnw+hIsQP^-piC3Y)f*(>r`{K$93=wP-v&3UN6%CIIgX1qU|8Z20PBrhW8D=D=F);GT>&W<&O zj^6k_(Am;y-heU|8}&uVbg{MCPa=yHHJrjCWF&dWTS;tq^MJOrlmT~5QeweM?aySgO%<-Lm(Zr}|v8xTp_2T)ymZpUS zRq4;Mrsb2{4FV!p=xq1m;5F^^=J>AD z-u~U|5Fs-V^c3A0kogc?`EfnCG11}iIdC{`R<#ncQnrxXM*E}CrZH}DYx}ShPQ{ez zZ<6X(W$r$rg^$`BkJt6_o=jEWg-vwQR`o|{rs}PGez& zFum8|Q@_ZE2Qze!*&JMwg`w_&7e=FRLXWC1H$FtnCGAiyk#7E(?)-S(`_HnsR=G^> z*0Xk>%scJ&x7ME>FV@I62fY?f3{4ZypCiZ8U@|>D9GSS0E}#^6U=?lX$DRt7-(^73 zXEGPhd2Apl4*WV0-(e~)Gqhw+sh#?(7Q(=&yHCuGa^qX6&wFWax6G7GP0(?pFFN2Q z!o&+6&^MS0JScrXygQ>fkV0WYVpg536&3Sd^L5Rr6(HOm__IIqwD@wf<&05x?#f|` zOjLpWeS<}S1MgP?lksZ{im3Vxwms-eBi&+Jw8$#UlosoUYbAaV25p+c!V*!%kMA2y zl^dCCOd8(Se6~iHz<0H9sDzuVkY7-%1<|EtlVYnpQ(C+q45tsgX}WJ-GcAD~F1m&j zjl>lMfVgbm!}c?6z{fjXc6aCd%WOonL7#HKxbiK&028u8wd3ky}AGb$N$^GP=y#BN>AzQh)YZ6huQ$S%XWas;ya)x)of#c>0 zN?u|Wsgnt{J_%`XHURycW+iX1CR$+)wFZSWSQ~(7bF7n=8lufWh4ly)o}e>RVQaB8 zDRJ&3WP4h|D=opk=72s&7?cB4)Jam||IGMqPs?znWzg5$*XJ1esev|0Dw-xG&7Jhg zus>p=oz%Zbde6d@&?$$YIh3l?(A_5S4kfxJm7GhW5Y){P{$I5q3hn3{ zeMS{ImTD`byRz^u4y7ffkp7uXZ=ciChXRX6PW8WPA%$L0O#Y;jDc za`cJBV`8D8134A6ng(#r-ZwB*TZfqeQ9%nR)+c=p968pqRv_UzYgAAJiZ!UOL0cF^ zTWyU$-5_Vlbtt3>vf(41OKE6KccUfT))L%q4hV6C2|Yk1njj^~kP2Z>`uiUk4BJ`; z+s%C;j-j6##1o`s8B!7KNy%=spN#DV$L8ejIzdzH6F~+^vYPdm2wbXo#d>fYhw+k2xYTUp)A?mSKKNNrDNd!wZ% z>zCF_cvpkA_t6-ge4TC&x9>4YbRW)4uMH3P`;Dvw-T01YW#)U!i7FW)_1AleDjg9P zdmkqVP!9<07Rb8vX2zu%89c18eC@oQ$J(z~nvX|&pZ!HxWPyLkdH;~>)5Meyed(Ie46ggP+?2!f|L8Kf zHcjdL@iPMx|2FpDoF1T9KTrMRZthY3Wn-ErSvw!F0zuytDx+9_gDe2rfGWaY1_m^nOarOVR z$N!Mj+vLPrA74lJ>9YA{HoDK_8;3_rYi%?5-l$$%OG;`lGCJKfygzPR4}5)}4%phK zPxL=p)UW?@mAhW#ac9W_`bqcE_J3LA_WHDPSc_hZ+&#ZN+0C^+dHOQ5+ef{@tg<|A zge=ijlsE;~NvD554Q4DdB$#{GBHRgx5yUW#vSp3bwKr^@3kcXU0}Q8vAa-`_O?t?k*;=UcZn?>*3;adm!ldc5tP z+UVG?P8N>Kbv#`?EpCzHXT$Qn+;g8cvSnYDDwyTE=RCJt@XfV791pA3l9zOwOFJ%L1*z%BAzIjWZTqtRFwzW4M*;=;W{cUrOFvnHW=)}GiYp;?K z`z(S%yfv?<$F?+9rW)1hz0vO+r*AX(t5K|HW*Y0$bsl21PGU-9Q3Z3jG7Ma)imuH4 zr)H9C)9AIil+ORf@WYiE=1NU+WfnO%(^;Qx^APiowK0!co=I!T=P;3SJWkwCrWr5R zOf_s^F8nWsdR0D$yHvC*s3&88h=<4B)kn!+qrFt2rPN=c*;|#48=bFpJY3WDu1Bb5 zVPmlFowxq3ywzii(Ea55EN4L-UTdi;A5ML#eSZbs#lq))+ph+e3edvVbmdF0!fnIA zC#4vKZP>t4U}G7vypY^d&S0Wxc>G@s2oN>|==t>7$%;&664D7zn-?VnV=}^^P)9E-y0Cu*&?Gl@!bRNDr_}F{$ zy1WFiVNdo02Ua^lZS%UFK6rRP(tYltFL=w>5rZ&?+7F&NP<3F=q6#BxL*jzaK zzKVX7Ji`@z95jA#yakk6zUunE8oxSxUr#^d!q)8htb~25N+dj-W|0aCEj-u*(-(V|z z7)48U|8Irh{i)Ob8YeCnWMdPGX2###i-+n*AI*vCogAJL5un+RK@m@<@C82Fl^u;b z4&|37muQPKGS*A`X}rmtX@yX)_;42h5&l*VZo-gQikwNDF1;X$9j?P>pI`F{{031@}O{P9*8aF7D; zojK*M!@Plw1$AG!9{CF<^W!mxQlO14MoS6XUDbK1jlR58OCh5v=`Zijc|_q;MNf6g z<35zWJ8&63m&2FoJ$h@mQtq>k_J7Lp@MLZAzB97(c63>J6=O=z=Cj?pftBk>y(QsZ zZM!bOUVHVJhO^eauaxDz-B@k=z@cz{yO?4)6u2Q~7X3UMn2isT7b4?r+o^`X@B!}L;&OlSeq_zt>>6cvSgb(R!?L-gwfAB6 z1T;Oq$*A>xi$&8C?R{x@qIQSH=PnN~H{Q$Pro)$|<$e3~nw#0tgS0O-i#c~y3F1gZ$$!q*xr9wWPF@F9=4D2vQ6x8TJkL6#xo_7Zp)-pR!Ga%)+Tvt zk9X?Um#|f3kcT~m9@-h=mvH1+S}q6WTA2FR%qH^d*rrl}yX$)8)lW=k2#JT0TXUb{ zEA7?8?cFN2P6oEf7Hca5(4_l8_L{Z}(lX!9YwT*7kz{_Yj7q^v$9~r%`TDI*T!$dM zPvmvS(EN`m3H4N{5yUeOGKM@_>7}Vhn+aPs=7pISIz~Jdm0y@Zmx~9^L`9~CM60kf z#}$SSxHmaHy^!AHi~mJjW%dg2MI?9kN6*UY!$AIhkYAf#u6{P#HO-vaH`n%MA4*v^ zAH%6MX3W0%@r@ERWyg!_aJ%kuC83CK$>MPc+7{(AsXDud^vtd=t+J3K^$n7iRP^$) z3-|{r@yV=W(-}JHOXf58U zSGNgxyPBxgq*wNS0DLb8WGb&y_H~IRUyOiouGLCS1xn*CnJn+aXHRyIfh{i!yVO=^ ztn3Q|G)}G4>qcw)%7Plsi`GU{P0S$b&K~E8V&$w}@OzuVb?0RH^{vD;D?l*r0(6#p zb}x1{fYihba1%c?rzMss3mRa|9cT2MxjXBU7vW`olbXMyxMNC~@z4S~? zl7t3bTwMf7Y+adgY+~dxTrH~BjW3)@{wt;#4#oKn;0GL}!jp);aSgibAg)f%CI*4A z)FOxD>@y-)1m{?lPJHP!>n6-NCwcq!E|0UomPXpjfHN9M$>I)u%^!wLCQ3a4U7FLU zxz1ANt36m~JSz-jnu8{JHw;;~o(ghTtMISJAK*o6!#c9D?HZWxvptuYm_Z+P^sj}1 zTYNO|A4l!bAAgV@mfO?0P;g)rU@enmm@*HE;i;@fZ^20Xs(qqTW5tbEw3GYB^%$7A&UOo zUFG`n@w-QPv7y;HM-wO+t1#Tqh@=^JX?01hAWx$NhtuMvpRcsTwT2LTmK!@vCx z>C~pHqd6~AcqiXP9}w1)CRars`mdZ8qY9NALdD0!QpN>s*@s_VQ|gD+Au(f;{O&P# zUa~dcF8{7alhIpYy==`IT#>coug>sZ)Miq2;Ga297xbomfm0#OtNd9UO&U6J@=|Rv zK%KuwX7(X|9Ojk!qE14TCM^uzJ0*BgixvtEMTD~TAShU*QL3UHBiX>ScQsjWAQ*%2 z(MdoC(9zD7;62GGuMdZrTQ_nZ%-3DWD60*pbkI4N&wAcHXLk?}SMQTGP4G4e0bH1s zyf>N-?kQ)d8U;e>1wKPug#}rMmktt<@#ni!8UK*_KLDFRWWPovDB|Oe6SxBCR8XKy zP`Eu16hTY0EABxSA1$ykskspvm1JQ(41vy0F>kxebTVo}t*y38bHiB@LZ?dzEfD_W7ZAetX z8DEe&5fnmP8|*TlMQTyly$}?}pyNfihLJ1GRKVF2u0pFFaQ@|EelWVP70kJU zTtTDDXoHmPDQqW9q?;8`I|be^f?c@0jDMSq;-EaKSfdKA88p+HSD?=0TGXEAR7KtNZk_AE9`IrwF1W}~XnOuJowW4}H z{bn-?wD!68tX`Fw4#_Gpse9zEjAS`ivQKDmhEZvz9y;Nf-(he-gJRStG0#i zX1KONfAR&&Gjhxxv!ld<#!y4G9bdq9J6x9F2Cf-NGX{Vi%GETS9u!qAkj%=97sDt^ za~#Xnc+8-*ss-E=h#sY9p8s^ESo>l!BODJT2Wp{9V!O)BRR(b~+8U}A*l#G8^t7Wp zj%v1~Y>6~SN>*%W)s@cO4A+1&lO6nE!^KretgfpH8SmH>kYG85BkiL_9Tkpv@QQ7L z+|KmMr#alIRIGt!t;`MuweCK@!wP{0KqjbBW&o2cZf9iG1%4ke23XA%7jSBV2de_v z3&6GWjgX$2btbhlt7Kv)LIR*h%}hnix>OWuS~XngWVp?4QGg|??!>u7CwF1A&aGEl z8^vf!6tGya?4Qfq>~=kE4Fu?c+j#XYooOv*8Gi0)gO9H$h7x9F40zlLx3~Ogg)Ck@K+NrD}LGmAWGg8*9 z9TzYp?{_&DQT{rLU^I9O+aU;0;NxaFJobuAa@$R)Tp5P#4iON9%Eb;5zZ|fk?W*8D zR188I7?GEi46@K&U}br}gfhK>SmrU%%F953$?ZqP9K9ix8!`8FIY=Va@Za3!(%%ED za}L|gXBN7|24;8X>c{{yp7WV@3uX)qOI2-!~h)XuU7REjxkXF+zh`XfLCc@vD)S70ZK)udbh1%fU$9 z2PB@_wu)RRISmIu9@JZJZW*tdk!;VM?)E?%@v0gAwl>Mrj$?L8H^-+W9~Pl*m2Yc!Vauv49O*wGR^g0fleZp#nG zWuvUqXiA1Ul*`gr*c=SW8tejIM@HH$Ouhn{GZj&Y?#eRU1ho6Wu!epPUyD)`ut*gv zKxGQ{<2^75Q*y!s6e~cbMur-X3#NA-Wo9ie9?3JJL(3X;TKCf_C>qk;gi)t6lWkOFW8CgXw2@{J1&+{aa zN=95pmiGfSZjQJtRi4O)@RoC0HP$BCgO5Im30>#;|B+ zX=))l0qVn z9n5#NqS|`3T}aq2>qAyDt=oOCE2gY+rm_XWJ8uB1BNWpfz1=B4Eq}x#~aA+1SSY1+bs4TP5c0*cC4KZp2;VlWU z!Y~V8!gWN>ZJR1PRiGx;ChuQ6=UC%M!JmnV*jY}Z2{@Md0UU!Y+Kois3ta(HBn{LS zLCDfQ_r|?SuybOARjhx8N>m4NszwklV8li5v7%0qbW7yLDvdY|)`ttc*-d-jWAJu5 z(LwHI_ILvuL6N>9&G5W~rjyiH$_d>k^qA036D26I$2x{Dq|Dxh8Op4#A*ruewy%i= zL1%wDIk~^Ucyclkj-eAztM8wF_qWb9|ncBD9_8DDTl~0w3rvX8o zQSLs4FEvkKWuHa-UEFzPlb%5V$o%Cb>7|s5(&9x-~fmG@WpdH8vhcY4INN8n{3@gaR3$2NjArus@O+xp8 z5P4|HIGtN=qFq5~1OUFHsm#D{TZyjUDew*uE*8l^9(T6s(gB1NuGYnQ#w;J(XWS*2 z8+xUd0GBbZdc~RtLm|+}rff~7rmXC5{czyZ>E;0xEC~fv+F${YRo)dU ztT7Iu0L&c5SbUue4`fXa9~!OSrXl2(W-f5rmo16KJWqfm7FKvJv_j8Cp^f5M4NmSr z9XhO*Z7@xg2P&y6!~>$txf5bC)c1qDmXp*2D+?4#N%5>OP-umLLMsduT4A8j?nR)` z*G5|dJ`=;jxMc!|#5f^Rt-&ij^&nz8E)yn@N+jh)a!|2QLkYe(O6T3;U2&*#gEC@^ zumNgLj@|8w{IJ0ZQ-*yBn!IF?v6XpVog{BtYzoyGbpZ)!sX*+yW3ET-Yh?hjMEW*p zUy|HXJ%;ihljhymA`A^9(hR&zF#xbK=woT&uh4IRhWCO6AZW>U=8>jk4(Kr}Los+n zXGr<%KG6<}C3KC*Mnef9nNci(J1TL~wE-r_C5hEd-nw?fhsU)-1F8hv5+~=t?T!mM z4A*)~C8Q%~WNqxKbj{Y@D6+OdiIdBy-P07U4`@z|A9O#@~5 zU`Ch_=44?6!ShoxyuHA>3>y#uXK;v+s@Ow8M6DoU0cl3hr8A1|u}DrB1hD{%xex-L zaBjH<$$Z>ZWv*9FJusRBcaGB0_>c^^qNS=@evj`;O8Xj|wd@O2tor10WM%8Ru1r9w z``U$1`d4rJS6`g zH@2BRyX244Y@LE+Cd3nJhaky!*i-f{?1RNhah29n&7HC^u=6&Zg<)QIRH7qM6jl`2N=b;q;A9y`;I%NYD73BGuFwY*J*XYiqH&OrKk9Z0&O+?N z&6-WQI>(gZyJX$ePAyA#?H;_wW4+)C=hrQX|2**Tq+oii&^YeWpy}NRH)jM z^|*wKsq?5syFyV$P*&>)#>M6st8I&HY{mdPjIlu)Y>yFn9YM*gG89^6Vz^x^V@V|? zi?UI>{|{-Eg+pPMRAD*VzAXr@=e2iBR+&$*C)#o({gyKjq}pPktjPn#*PYs9K6F?o zONkRrZ8w-pXZ#_DOD;~Q^GcXhSKOA-l_el6vAq{~j z+Ucl-hTkOTgk*-IRD*7?b8Vd!|iSMdl ztQ?UprI>3U%0eIwUrNZd4l-UC6x!%AhqVP+77@t6Bj!TI52gi}aMiTfU`%?sH^-!! zY|E8^+8Sl=;3%;~Z(K5Px@u$rTgZl%Z%JwHrl!$_iSyGGoJ=Y0#Olb2R#9o5nRR`R zw8Ijttu|qVZESbj4FRvIy>_r2(<@?xMqF5Zau=59T*V4|)vAcSN?q*LRO;w`AZdp* z6eo~n^EQ|PA~9^x$>`Y}##d0zu$W~jz0Nur8nag#_QGM_4ApXWh!4~X=p^a}jtvz8 z_@d~0N?y6<RBNemiBTk&L$t7p&`Myr94uEMO7e zql~IN91PtNkRmY9c9K*r8EHaNiHRw&%e|w>0hPdnxWA(k+>(0vPF9bB6&BJUqO?fw zAn+o!gPp8S);ejOtdS*i6DlnW1d!Per6crwnYln->j^vN+H~IVVy{&m8-VGuLRDGK zORV?GW+4q5=(NMgOX=M49iF$Eb(fj)ikw-><5q(5PxJAv=9DypPbNB^?0&~({hHh0v1Ssji7BYS*e&+UL>2&7<17;n|#q^!uv&;f~&goLpq zE9zz5lhP$^?rEa$6L!TnBX~J#{{|Q$1h=S!63G3fD#tbj`CY=Yh$d$t#Ys?+H9;fCU>A1bWHRlLw)9TJRj+>Zu7IF3&6|+A--ICSqU- z3yR{My%T3OvaqB`mQ$ozhP|(a(8P1pG%eLY@0MzYYQW}CCe@7f1wnwrHH^!N)tGm; zU`Kb0@kW=mwyY|0w{(3XhTr7Nr_y!4lRbWRz65l||bXS&_56y=IFA z1NC_))^}7SZ?)FO7D%PW=OtT*ZZ`aOk7CidAoPdLFX)Js`g%6)+I%w z_j&Nfz9U&dhS<6XMo?@)G}+FCB8EjRVIxD4^4D&WF{$>1U{y&TzXZ>i5g}tH^6YBF zvIrw8XQ1&_-1~qqf@w%&By$L8WralaMus&e01unQ3lM~P)#!!s0&2hTV2XgYHR|Ti zlu zD^HKMcFLGhYNRPhwzhM+2vs{r))pwO266! zT(^Ct8QL!;?g5G8+~(gdGQmSoJ_=J=)}|N z`={SMpW=v}ojz@T^39LWzYah7sL34t=dkxne{egh-h>O{YSbTGH4jz4SFPu!v-H*M ziR$mw#Cx#xBs7cveb8a%n>#zb9(SmC&iv;Ur|>_D1h|m>5rwwAtU?-Pc%d?SE7%9( zM_U^e{%`6zx5@a%u3XL^Kn<;W?F^94c3o^T*(=x3&fe$b-q;cVrml9jm51Szd*fn+ z|2A~@Fen4z&s1JgiQd@MLJLy0X$7VzeczCT)L~t3OikpRn|h+iB=@H6voMc@D;SeR zg(DHigv{^jWYE$=XeqBmqQc8W#a;kQt7bz3FBZU%F2NDpVJfzFte2@0z1*edVhVS+ zMyibh2409%o4iQ1O})Tt@PzCZ=s7UYmQ=~Tad;n2zywI=b}5)mT_WAo&YGEVt;K=6 z6Vh#rEJ*AI*9yILb|K9|&<*&w0F&;nB?l!*4hu%c9f(qvd6IGIg3&@-!)PP3HVg~t z2@LOt7*URS3HnH08)G0of2as@9(Z)wP=7Rs|Inp-2msv@Q6oT>a^5MJB53E9J;$6S z8g-{L?AYK^hkaIuO_z8`Xgt`(4x>#IuxuWz9F2@Jx@a;znLMAgD^QNMrt+e>!fAH` zSf`n8%F6^Wkx&2&?KiOOgy6s+gU0~pjF2Bl(Ow8(0O$Q;;S7kiesAId+)LcHNDd*t z*Q+Xlsl#p?%VY|G6RNV|y3`s;)XEmE1jf8HZQ~!T5*WEjHY++8CTm|jU{y4+wnh~d z$(#hM)h*Le!-37@RmpN_R#^uiBKdhuoTZUXJIny$wD_T{JXi@qw`59%lBpCjr?8Z6 zQ0;*-;hTJ)yI7sSlW$_F49f_UiSa{JwQ=eW#{^U3K$Mbrzb+c2Ypw44L^?Sdq+^VZ zZ5~cj`_>F4CvMNVeLJebFhRf}Xe_}-Ww1m@kh@77_O7;fyq%df#NHQ)Hg;4kDo1%Q?57ob^4gv3^u=qq?(qG7PNdy(wx{e6N>I>0A*+!1wabA4H6`@n)% zb#CUW+G^4WCH;z4XC*0jR`l$w0R<&t7UgB5b0h5t>7`Mlh^#39A(gI1&4Jk4ZU!0- zh*ndgrOe7$%D~C+ay+3lPfd;1qupu^K!l?v89ixbA+UK2g}^Q?!%vhOj#idlKqLbN zAuB`%^D+o=EsO4KlY(nCh1w~~O&s>+h zNr(;i_Ts+4cvG2nMe>18!BV;gy1c3FG6|8KVYNi3=HsV!jwji7*i-GCUdsE`K8@zf z91a(iPZEg4vbtJXwUi(TFk#g&Bol~A=F}$=!eQ(M^Dxvd7lcdY8zUA(~zH+SV(_ub*Vta+c@9j6>FrI@?CB!0HZ#cPVZ zQk)y1nu{`1axHXIFaX*u^d@?4PFO_|T<#llp-K}MT9ly8N|s2)Rwge@JZj@uUKo^B zM>&+yEI6n3BsJR*VRn+Uh8%9(gvuR@OFY{tl{>tnQlnsw0Jvm(WMvMC?Gl%Wf)rRP zVkXSXAM29SS1ZINJ4)w?icXQAG^=Scce}%!W=C~O?98N!ln&MKE0qZD?C zHw=jYMOx-sRI<`x8*drm1n^KM83C-YZb`@U08a~t?S9lP8N-%zHngv2N$6+2YEFul zE(}Lm`Ad{;xS;pQOGJq(i)5JS;p$1yBXH4r63VhE!9CnufC1}^&P5)?2RvNO6$J~l z@Lc3UXPO7Bx$>H|EjkxIXD%}N3U^-W447DvBw{H-<7BZ&VZM_Fnd?;>Su7Gr$><3{ z?+IR&1&eM`U$Ek<&)Rsrs_pay!7Or%mZ!lyhaP|29ekEW>-C~X!sf|Rov!9Mhej`| zIVt%+N&?52N(!iewJ({xrjlx6J0v?WUK>+V1ZkUQa%+4rIm{Lf>XMAmoP`gNOAOGQ zcEBCV;WW@MAQXi_JTQN$Z0x%hHY!nfF2vI~8<@MgOpq=N&6kOcFzl zM0bf3>E%ayRURIu_DQ36cTmIPoX>?^4$~Fc9eL-9)O78%?T9qkX&>~8lBB^dr|K({G$AsTZUI>M(Z8>1hixDm|P98ioLd5v=h}}3m8=t<4~EM<;WHGc`9w}c326C zm8dvRAAydnM8)2?vLiJ)(UWc~?i+Z1ZMP%5O8ASF+h>0VvC*Ci6!T_)iQC#0tMCX{ z{+MrZV#j^bzk1uh`r@qrr1|;TX@8r%Z(dS6l=q34W+-wd7$L798Y5&DR3NN?0;RRg zsQ_TK7E%luW)Jg1@+Z!$|~-%2w0(JuhV-vQ)MlU)e!`>oeWJ1u8(6w$o?L#&iH0(Pq9$A&>TW2Unv>R-2 zM6Z{Mbg+oGDY-rww4xxsH+8ZHLuoQ37qZrZJD;64ldM^Ml zR2|F`<9s$}F!4?=b5tuBvJmp{6uDFLj{%mhn!L)EZY>rBQa0HRSQ*jNoq7b2fg#<% zo`bMr*J)3^Wu4s9l_~PN+szxUP?tWna|O+04o5(yAw!8Gp|UJ1)Izstt$HkTi`E$b z28-XYE8ciGJZ4{ex@S*vsWzcgmx{PSvSYGHv^717TqVd^^D?Ei~2e*16Vy6Kc)(1wJDJt z&$Ufjt9HNvI>YwZHFqGUp zD1RyUs*W$O>^}KQxiTOau7;$^P&wY1(vtz2`7Y zkq{7L8bFwq8PvcssAVuh>`X9TrbB>LLX+AG;WNRq-2(+8$fsZ}&e99+7`%+U(Ade- zEjOU`E5;=a!sS&8c8T2x9vMo|3z(gvkfH^*6oEyXmQn#sa7XYD&@kf}SHhc@17W7*uxjbWv_Y*Z>bx{L&dbi7wQSvX z`fBcUyu0DAR?b@E3W%qSay#7yukUQ6;mCU8k_E$Z<{6h#Fq~>!P`Ffm$?<9}FOY7T zQJT`+6+13%#zdGjgCbm0l;ppSzhG+Jlkpw<+Qh#>;y0#weAE*Xlgz(yuXfOmliU?3 zPn29JdZ%`{csX1oML^*!+p5__CpxQ?#&w&;rqqmBx=G(W;T=ct(|QcE9z#-(RgC>y zs;@NbD<$<6V%%J%`Z~)8ZE^%No&258tDp7NC-v2pEmliRZ9n_d$;tKg_-CVUZZ0o= z;**n!$PAr$T7CcYyXWH~*JiAzPn#co^W*cc!;d~{TK?y-_e+0pJF4DDb2IRQuV#-_f3K!hvP;j7OWnT@TFiWdXQ$WWJ{8Zy|2*_WF35#qoMf5!GFCAZ zLXin^$YcZ|xDRw^a>v~2B-1EYAcH+hLOTBY8WHIg&V zE+jkTCE{{wZ>R=k{S0)4#JbSDB-5$IevL||{fxv^u9BUQ6?J1zL3Tp(qAXclf)9`8)z+KjUeDhP zo#IIZ1=uW?X=M@ZY)IuO(cz#g!y?28a3Tz1r|IC1$)~4-F9?S&=M)VqMadc9^K;5w zLPHS&EU{`5r2#hT?jSFk6Sa^B+NmtmsjfECGFga5C<~2{4bXTQ@C8|AWwOu;(SXre zv${$LcP$KquIW@hx~{_ALi~A;OK^#xCJMzFoLy~d7e62Up6N+<*AMLni%0#ip)qsE3n!ae9f=Syu(R|ECVDl9orO01*x9hH}A zP_VshS_{fcbrIU-PKsKn0m(KR3Y05RGA6A~(suTtn%1U9tVO~f49M^}HN^YeCvcp~ z%fvrJyfkO&3ll6(a{EBsfQ|2%XB8KiEN{oG$ve^LRc(}IEK1L7AO~mqGvhm|KqRM# zbjd1ON^o219Y7_q(zu;`R|1v9s)$tz-Hg|1jUEa};d{UiT|_QnH+q$GWMFH+0R?)M zmm~COc{%vHYjX|vF774wu1gc+gd$XJk1{C^075)W1S>S+jV#m-1VV-h(zF*wJg_rM zByS*w5MbPC?IfU8v=nHk9DAbM@>DR)OJQJ0fSzn=58P#kQF*(>W|Ts3)utQpou+mX z=%o&uoGRdM`J#voF5$<;GsDkY{nTZaNKo{xrR*YyxpHN;2gAbTQ)AsShI%c7j&kuR z(-Q;-FQun9LdS&Rs#uS-z942aHi_3d8_QU)!td=cUz`-P zSxRe#tg81;EA)vRlFp{5oQFT?vcikJGfNd-TBY$MnJ#z+^us?_>aVi~b~v~PFDB*-j8K13(W*m6o1s@1GzI0kyI0_!1*i^8q2 zOrR+T*`gg67g@EW(x>E4n%x?wV3%nI;wz0#sC|)iTVkY5(Bd*mjNs!K(6u6Y-wKuh zwBji;PDt-;PFdq}omu>xqTAh^GCN2w+oCpMG5Nz`)lG%-F(S7oC@tjHHA+UVi#{IM zcT!?RB%>10Dn8q3A`#u5_&q7u$4h+Za+?km?Uc0-Ey0r0NK(LP+V>!K>+#giC3`&Q z(Ox?B9D+BfmgZEY$?HyCh6v~#COc&p=2Ug+jn&5bWN++3p{;qadMETYTvysU&5P|GTWfP_%{z{U&VER3v9C`^&w5BZ9?9PlD> z@+KIwOx3GXT|8vNgf42lWaS9~yi=i1KG?zOF1=Oc9H03vZ6H=&>qXhm&=D?ui4He_ z?TgwVV-_=@=~NT&%(N7e5Qr8kY-Og(SEn2m<>tkBr@&o$e=`YqK=L&aFp6ZDgDjM< z)>~oi4am$_%}$oK6ZC#rNYYxn9}Zo1O2Oj!XL%_-YI#Ea`hp>pd~a7yyJqP%UK`LQX?~2)!J~;s2w7dw9!9cD-*Y0q6^SIb7qZZi5KCSOk*$O!v&PA2 zU^JN0|Iw}R9vEvF_NJP-m*2;h`8^l7H8Ba#b^Z~1lk(JR}wqAy( zWvv4l+NBM4h~5*lOSb~)S8k~47_yMgU3v=HX(;K^QL>LT%UD4QTVsDBP_UbHxI-|Q9sYkyZ>$7XZ-W8!X-;rm^(v*;~6pgJs zv}ji;qL7wK-pINgDlDmhUnmOdTZLZcyx=*XeT2HAS*oru0Lx%MOi*xyp@%S#{lNVK zr|&R86LN9y{1|?>WVU!u5St-Q^&B^7muYFCx6Zn3&fqp$@@L#>hZQ?B4We^HW=eV0 z!miW48kl9$nT9Ty_AoI~sTuh6t0%Trl1|R$W^3%Ulg`Yb`dw{Boour)kvW_Ma+{h! zE|G*%2e(32kDn^Q{N|n`l_C*LLf=6L=3DP9Vn}ptL1+pS4U@xsJwjAH7DN|ECJ=HX% zjkcY-_K*y?s4V6S9bQ$WOWA(g4ZJ+)mqZUz7W0wovRYG-^IjFW$?a|_u@D*a&2E%r zY!xxS4KL+aK89{vG}%JP zWt>5+domF%dc&@Gzkn8O+fIS^_ql&+#TCM*aN2|g)w zu6=JGlDbWetYIcwjhMXUo~l?C^aQKMyj73Sd+1JKSDqZuGn` zr|DKj05kJ)Ky7kwUw%j-jHpX+cc3WRyK)*dJfJM52nv%3Sxot|*zlvEBF02GFCZBi zF$z7=T2S^h$xG5sMq{82$l$zKSuKQUo{JGboQ)d{H6NxYm;G-HQC$pf1QT!f{-073>JR$FUe|9~)4hP;0mn5k7-Nw+n8O{y(>;~$ov zYEG8b-Vs+|djk11K{1ngzlb#pn6YFfuo71SyE(Q*{`fxw-!SS_bKe?SMp_VR z!o2aYXJqiDqBltV28-XYE8b{{+FgU5DCODCV!cKX>%C7$+btValLvDb_vM5RQ@vZ3 z8|@~W`)issjsvnI>>Qf-K>T`x~r*Vp5pjlQ|Ly!a_dwUdd!483?-egE{k=VR&I4EFSC z^P_KmeExO#(MQny?4QHlFa5#osCpA_iK|h6aMe6j{a&>m#A%|;SF=Z|zgH91nibE6 zN5Ae5M>q2go}FHgJ5@ZZ{PWP01Sk*^)jyLk+%5~Y%h-_PA7|O)cJc8CGmdrb*}<$f zlatL*1X_-0RvZyl--!lrENi2jNZ2;O~40$=U5J4`{6Glc~vR?WYq3 zCK(Xa6zdZ<#PauL30MM+HMa@?UER>u%)t22l|YDfI?;F|uDy4fr$>&`R}Dl?g8e8U z#xx;9PJ|~#PG$$jb7V~92stOj!j|D)k_V9}A-^B|i=NCfO}UqwR0fI>SU_!qTJK@O z&uX*yE0PJx!!R6#<3GKLGN6rZOg+-JtEgtiJ0CPBl^9nozh|X9lqQH+$@qY+5ti~; zL$cssctPlpG(x*3ydcX$vSu%mkeaX+2JCp*w zffVC5*!9Der37Qv>9YKFki%xQ0nNYjOfWR$%SL|DWEox(aK^%Op)F3!5lY2|watlw zcbySwI4u3vh^dj}itUKiYqeh~jKu9u!_bNTQi~|mFq}}sh*n`{8b-W1J<%|%lJY#T z6T@(<%q{XeXL%rAuE~|>wnX`Iihwg}cKP6pbCTM1iFF8#ev8?-)blltUB(enqn^bP z?`2*hKn8VL^Grd@beK#xyc+B9;mWPpGOMCA6$#X`Z_ovt0Xs|Lfz;$)yX17%-Bd~v z)Cvs{7O<))dAM`7b~iVbrkcsHD*^MTb#k1HI;X{BT^olUZGnL@ml!B_Q)^0ZK2RHj3X{jw2~>O3k$bhgr}ijd|FkG7#5Vapa2FhrgsA3SyycQS_5;m#MVj=Mq z7)Dw8b@PUn;dx8mm;@4xH#=1`67NIr!*$_G3I*@wIBI%D9)#^E_GrR@RY@=@dF;{7 zCH!(%2qX$U`jSyrRyj9g*DzrXM4~FnwXF4SqRNRo*{W&oc_|zcV~a2@Q%8cH9k~*9 z){tEJ;a6?vPRhy~4<{Hf1%`W)qOskk^#E2Fl~Gy=<1$)S z5}*;hu)53&qchs+{m8-$TA5j*E3|U;Lr`K?qHs0b+e<*N@@~^A1kNt&!@c%gAR{F% zzFQO}?I_i}M2DSnpp&a;%bRGM6g|5nGC@$YPal^>aN8G=nTXvL z{)#fXEMYC0LF+^k)3LA?&0B*qd5P#`K-W!1?0(d{YqX>nC66X~y}K)hzY_FVw06lC zMU+Ls?66xmpV20}ntgXAXTau;Gy1;6nF(;zX;Gio))gq~Yt=9qdO*AYL~kDkFJK{K zj~@;wpvmCGk_j8gX_H_Q=_jD+J?t>I3ounIYZ;-mCz)7AD-3@Uun`ufX`va5TFF4M zf!vA~LM- zM%z?zXuNex@F-SgG4gdrn`@m>SQJ;y`W&vsm>Q5o?!g8Frszl=<~dAUMCVMVT|Tk(bLtObWwhO`tJeXos3WBIKK8 zSv9zf*P4`;r82WDB<)G)U^iL__QrFO_yRt-No6mU|ybf%PAwEAhlJ2eKiP?hk;-r=xQ zRyd)ffx23QBUeIJxn#h3v#N?(byHl`Buj6nE0+{kc{zurTQ*=Q&EwwO@;j8zi2!h< zC^}3%WuoMsjP#i#^vV-!y+$GFv*?XU&5>jrlcJ;A>;>tws#TNCw3M|XzQvq}c!-1& z$u@vSFQvReo_n;{h+j+_>G1RB~T83w#nAWT)C zM_iAI4%A+#ZUn7asBYlAO7oEFhG)+@yT)imk@Nbyy9hh4zr%wc6LwRP@P3!+(@HzM zmYMLPt%*=@Dwm^{*f_*5VEJTb>z?+)h%EXsVk7XenbV9&Xr7iA(ETr0@ zV+#wclJe|c4aF#cRjmQ6TZF~X3AQA}+ZqLO@PN@RxdT#|)$FcooQ$>wuv1jdY|Fb% z1f;TkM)LsWwo7@4#LG2%m`CB+sYZkiieP~bLku$CJL~;PJ#b>ZV!LjiXNGIu$#m8&yh8D`Tg9)p@3g@3^lsI!tUF%b9(} zhYPwZrJ+RjxenJ)$~3ey3ZW|!Or_X!fy@GvxGzmY(0S!p8tv5YPLyIIewGuJD`^9) z#3@iRVrmY1L?%x~Z%q8KwGqnGg*RIARPWdi>pcTbZ#12puu3g7kO;cHP38i6ot@b< zR|v};3xqo+XB{@@Ng0}fPGe3@{?Sa_LejOy`;j-+h%oPA5N?N(JQ3C_-j}N;g~~!sYtSGihtdR2 z2!v_kNiiqh;&76L2qKo7ctEDy~DLJJu{5BH*HCAI#w8Y>OhVBvc{c z&WZ?w{cN}IWR#rhgras|D!#E}#%xT)V;lQ{1jzyhWQ^IyhD|Ii@wUR6D=n_Ma+1RH zNuK$9-owM?3;2d3C;XWf*o40)M&`%tpxd+2vQ9PD%4l>Y%xM&(tE^6&rH;fCC?5fS zCJ|4N2_o9nNSjOy^}yB7JFRX<5BkBYycpZ~Z+hoW?{TwZ^O#(O+6gKENb)Qe)2wvlIkxkoXN2zhPIr zaWBPb*M-wc!7R!SNil1H_zSR1kX_BP0fU%nRv;%6(7J9@7?hfNcY;RWJ26=*19R7X z^|MlR67+SR{-IO8%chSrvg+|}6>wC*&eGkRPV%giEa@c8@p0;|JLzYg^rB9-mm420 z$^Gn4Cnwj}C+35QG>P|=w2bbqp z;S0O{j2Kg_VQJ4SPkA*Z%=x^oL}6Y-}DDpqfe{JseDtt`n3v| zay9IYns4-f=~b_Kqnj%31DCz?(d{s_^5%Bfzj{|ad-lV#6AXv1`s;`Hy{jtd2K}4f z=hf)_;KNmQelZBoG+%prd-b~MN7aAxX%%|;rvLVBZy0o`@L!j`t5MU%x_a@xKZ@+g zuo+er9={&;UiW&ffi=>oHyTypiO`Y$+iG-qesK||7xAt89^?Xzfu6ieL`tSNT zRkQT3hyClDPpk8*H`V#;*MrOOo&L1N4}+CfAC3HGW-mvL)HJy=B zX!XsT{&+sa_&%-%!|L+(;--In(SJRD^V86ot2g7nOeY1@$0s-ap!@uFp%1?Gi{?Ai zUxhXO>#M=wW^^+=zrOB;nGD;by69cKyLmtQeA4^y^Yh`TdK*69JAeH?EWx|Muz&OZ za{TL?^WnSRO*7HG))Gv9T7B|Sux8a4Z%>+C69#lX>`m7(45;bv&EUEk4nEY?SmHO6 z-~9A`!`%$~=NHWn&u_x^3SSRX*gWyquq)0lKAnAMqPeYJ=7~PnEc4`?eHJF^tT~xY z=McYe&5y62Oh$0od(%H}@A%h)t2ejfb91-fT1TMi@Vl@O^?nH-nhtH!hw<47lQ{gfY5wwj`154noCo#hd{~VS z#iXgwaCr23GP_N0uh09#5B*U%isx59pNwuVtDEq|sQM6=Yy8e2{9|~(f}^U}AOB?V zs`u-tYSJRB>XVC`FRJRgYF5uF{YCR*ZZ_Ziy!|}=H%#Vl#d{0SPoKN%|9CY0W@BAk z3_jeSw&~>m{Q0EY&jwdvQw;}K{V;FCVQ;bs^UpR{({y>lsX_f|T|I5O!JfXTM*VkJ z{kQ$zroHtcY}@L?U>K%ue5RWx!~HORY_=r#`~Gd%TjP7G8sAfs9X+_Z_;oV4i{`Ev zHv_5)hwXK9B3fSr{Dea$>~gu`RQ%Y%6?M#RejU@_n-e({rTm~#%J>C z?Qd`4kLr`^<;$@=oLr~B;UB;KpTGU_I{fdC->_EUxBu_$`J2tpnS*%l_wj?x6Tsmg zK}UQ!{`3B#f7J_D!OL)UJ)KNx_+gNRuY3RHx0f&9_Af3b%}!_4&i$?CKmPJlc1TFG&`-mlDPVCEm%Of>4dI57@HeebWZqrL9t>-Znovj)*ZcZA{KSdJC*e>FJJ!Y z?*D)LRs8J=Att%i3fv;=E}6?Ei1?Tbtv^tt`LKuc)x; z4QbV6C*D9?(-C^wj_8NCTkGA4(X69H7Ny!IS=_2pt2Gk$w=a-ckF3nZ%>xu@k7v8b z(=CxmWF`{#fphOY*YGjHW8{IyvflM_SaF=zj|#ySDl&uWJDE1yl&N%Sve^E&1{;RS zro{}S!!SVy;ucO8mnI=0NYKmNc9{R)``hVqx%dzi^Wh%pOzzylbD9cpqzn~p%q zM2lhQbH=D+M_arB+)>gevW`IYEvAmxomv7S7xzzIwsrDyNv`V7)n+V3(8Mt8Ax4Y< zh(H(yo~gjolGNbgs{+S?woK{*9wR4@P8^c5j;!)Sy>DXBBG0ml%yocE6+Pf&%+6KE z=E=6g+g^*QRN*`6(vQrqX@b%%O<}fM+eYZVH-FxXJSVp zvYYT-DN&Vz^j4%E6II#~=-f>wWe%|_Q46SaEnv_vhz9il(*is#Yl+S%(Pva(VwoRV z9!IJUH*U=Nc3DzU8Peo#a$9l~1S4&g&X!}QNIzcYKe3|^2s&HynD|jzl~p@JBgqv7 zJ%mBrYwW0C1cvbnT~+8nbVdQ5^HD7#Ff9^J=324x%As>AW>Lj|jB<#Bv^jFd$zZ=a zUNa1wur)hcEeru+vVczLqXGHnoPF@A4~&m$nzR6|7{=p9WM5Sp6F{RmIuP{y4eCJ{ z4F-M=Xw@H_N zH$xmU$O@cp`c{8xO`Iq+wQSw2E}Nn_j*a5b4ev`J(y;dvh>TN#&aW#ujo)hhv!*`Oca#jb_u&dP0B7(vFJ524Cp z2z0=3U-gp)AIM=4aYWiB2>MkZ*a$iuj$_dYr$C6Ig2EyJIjE#8F7FyT8?a-%^&^Dd zkkKn?%DMq`mZB}}2GB`t#uUAqtm0n3V_0Od8)@LtEVf5Z<;4}{Fpwz6MBJhHAegP9 z!O(nY%7P8F41^}F)o7+nRX&X05oH%i_O~TY_3n(9L?U|1qDs_4Y2wRiB+%XOnV}l0 zqc3$gq2T(dq_c>s=yH6Ut271?#?ixqkRW=;Q9MwiP^1Ghj!Xrn5*hT&z^&1B!5-uH z7k>ymv?VhN4)=}i?0%&R!3?rn#m`W?r@2VYl6WtyW@R_f4I!PLHW-y?sGk`fFKiP`q~axRy&-DMw~^evl$?7RQ-OmV%s7b zLcv@kX{tww4B`XNUAtFT2zz3qG&)u0?l!LA;rN`8<)U>Wl#f4s?%6Ol1h`sLBMQ87CE-x z|KW!PkgSAdr8bh!6-%8Dbein)4B3@nMcqI8uyDRDkVHo@LrOEaO=Q%MJ8)@936wCnav;?* zK~NYd)hfYSTFhFS1inm12y`2+ViYN@=oY)8CfG{Y1!|pflT5H42CMXlFIkjl5)?dz zg~_x5(_&DPSb5LvmP+Ihnma0DG=8Wfh_@KI&$P;7^I~*Co6MS2Vr+f_kTp#eHHuD> zpRVGM+cw6AS_2t9f20bqNEcu*j_l|W<0Lh#}_sE41GF_mvq87-4Ciy@OyWR@ZoekEp>q_x(0k1Gf+ zYcPI0vH*$a=C*)y+Eg=YCx|owAH=-$n2U@hhG@{`wqPom7f3Lrl{+xa+@c3)E=*OP z9;)LvS9Y|O4z)9MM>J%fXz%$SKnPnKlzH3A%9W>5?6T4uEdOwntW?<)n)%}> zkrY;?nwLDSWxQ%z%A&QM#>H<%9byR6&M=Y~2WW*6YQI>xAxR;aVHnMfh%cCm0akkH zswh<|2aoI6xM0vrK;l$L*Ly4Z$rz4CfR0H7qYYk+w3`+|p3v;3Md42iA*U($1^lEU z4z0Gk}fmNfYmQ=|W>5>h0QPW`g1=<5Zg9YW57aSJ~z(oOojp`V~B!}=d znMC|O%J09cEEGMvM)}V=Zhb7>-A!R4MfTs2sSp~AYr!r-XY6qY_C^?X)%?fr5PygH z&E@*zN%KU0`uW-$tm-b@uSyh1l`fDRtg!;eF$QwOGO56U%J7l{14gEtIbJMD=#x2~ zZ*fP^oC?mbqFhtDHw_hdA{a-krK83QI|(ydj5`ZJOv=$iusV!njXTl|^^g>OaA#>E zK8TTx*<4hBG($5z!172{ISeIJ42^X&6eUx_y89+~)~?tYSTj1Z>ar*Rgr)IyktOA- z!tiAVhsw3<+uks8x&yi{Y8c@lN1T#M>lzjnZN(v)o(UCPl ztJC?Sog|g68_kwgTEe7-*}2RxDa(Fk}W2ErR^ZU==mog^2NcW?Hh{xX@?>j9@M`W{j83 zaD@M79BA%yr0LPCm53kbWp+XM{*6Sh1)h?lZzSUUY|`a*AF{)cl^yGLQnE87Ls`d+ zL+OY>a2xFhTb1ugsX9F=4=tS*xiKljX~ zS3YBbu0|Vu<$N1SV1%QyCJVMo7i={%xcVXH$*&uFXaXw5Y+t6>E?r1Ve?ZObYF zHw>D&5&H?AvN1&gNznr8wZ{64g zR+7myiqzZp~{5H+%Qz;%H(5$P#G^<7ReOKLheA1+LVXNjD`->f<|-% z7Dc7I1}H_k->S-mhV*?+Z0JJF0n~X!8}YIBQ^10&iphLB+Wlkq=N7wi7$tj{+x&liMqZ25)hv&F`qnm&#bDU5K3!67p#baBk zLNy2*jhQ!a*J|)^XB9}v0Tw2(3bm{ZE+=MI6}NHX@W5ppl`1)Uv>0Ke&FfVm=uMyB zF-MHJoB3VrD?&`NXH_FTJI3{wjGuu`w)FB0>1Aj;Yr!foLc zqybnpjP-2ciDU5@U6gl=6-9f%8V7d|SsaHBF*A{TMX=Myc z*DHmVJq?SF6ljK6q+8x7&|^s7p>@KPiNYi@gb9v$q8U#b?bL>r^P=28Xl9hWhy01o zmarweKMPITJD2Zk(q%qUXCp(jgbJ693{|w~^JJJICNB`ey_U6K3ch*n6Pq%k)<+bQHUw>&%gxEl$YWIwSa z!mBoW)_sa~|8zdyNj6pd2f?jTdu~C{n4<-S96il)1Ga7B7Cf3u1u+GMbOzdbYsEnvjaWx zu^v)okba2ADBY|~rcp$4AYa@R#>`j*Gh;;}$r5D*0A2l(l=g6jRC%DhQ{>CVSXto6 z0Z~)s@F?tLa}u>iIQrb!n~XFn?XLuW24P>1Tl#hKG0n^=&P zHeysbdkwk|g;R*4Y_md;xG}S3??w~*F<16(v_8>Q*}FNct7_v5F_ESdh9NUHW;#)} zc<^k?HK;wf1EbGE2(Z$qa-iLo_8tleJ6|`-SSydML+RJd@T1EJPh}P1sK|NNDh|Nq z*ZH-QN&3%P9bJ|6|6jyeW!>a78|D%uzU8`6^iIEI+-Is7RN?T+Q*~ikyxB4Dz7btqtaPLl9<#u z!Mvn1PYFDNmhm!Z878bol_SlgNaI&}=0Tkn_7Y1JQjsB~Xq5z*kcNBMAd3KqrE92bj+ay8%ICSL&M{o& z80StNnH=uUkb#rQE}O23?&O@=9nHv{&6%YX&GJzenou+axM26w3um~ZW?u>C5;cIz z&;YCg^G#3{fD(kDZK=@sx~NcFEnRumj}HHy$wJ%p2nSz2104;$jl=joHv4LbcPrI= zS45cv*d;HMkl6Sokn+E+I>cCDOx*ST6>V z=7#H`l$0DoT$)xm(@OU`d9BspD z0l}4Yo?B-%1=g@qQoV1wITLPokV%zTks+~Qm6-)WI`AaVKz42po+db&R4TM8F13fo znQr_cutTh(K=hFgu`*GV zRfZ@tfx6tFP$|^mb|^VES}CHyrc-!n$)?@7vhwb zR%s&4b3|B!)GE9JnR&SZn@S1=k~T1FK!=}8tX%waU1CzP`?kW0Manfz3e$+}GJfWP zN2a^?T%?dJX;w?uso1>M(wydbQZCDO_H>3DN~BMfY1!#t(xp_|S>&X2av8z0WC<>M zgSL4D54>O=xh+?b8A>e+WTYs9j9yh^H1JFlSH}zzf)p_ppE%jl9ZWpnd9sdNo4@ zAE{=MA7{o10i@~qQ5T$y_jJh?l*xjhE!J6{<@n3P2$n3;q1>SnBDBC8r08}aiHQY$ zW+^Ilzb%}EjvRSAz_THP$aFSGj*lDh2|$ZS&*_Gmw2q=qhAwY#X)032)dLx#YBA{d zC*|?D7spOJ&ExVkN#;3{3>G?Zv|(d~BFtch1{&wWI)8kwQ$lqC1j|y}P)j$KiJ|Cx z2^K++#}vT;M;3|{Te^2l?2YJ2CC&Py;7JAQcIX)HTt#MPaR~mIC3x#0@WZOEJ_O1#LBc!oAY7a%pT=3su8ynq}n#SO~?Of$7#Tj^8lSpp9KQ6ROD* zktJ&?T)A`d7^yDU7$C2pSlXP8u+;SSTo`-R{KxM+ zf1mly<@)1EbFsh4d>wd&s=IL8DoyH#LA4R7mj-OxAsk0(OoT?`R8oT)!YBhm?8aCo zQKAGZn!pw>*%G94h?PT}CZ#I%DnYj}wnfWvkZ6T zsCI?>HEUlfOF>YaNU3 zpB&zLM#tDPNct$DWmt?zWQdvQ0BkJnaDz{+QgcQb?9?zu#aEJMW&%4eH0V*1=b$67 zpdL8~fEh3fO_Txs5U?$nP%sfH$iX1&J=32#8m)?&4Mo@fvSQSPrMhA>y0fOue_}x< z_GoeRQVftU=n`#`L^`8vT0amSfdX}rG#Ru+OM&p7u^@6_HL5l$w1c+L?B2On%67qV zB=b@J{v2tqli$v-BAQ3%(-ZF;{(jo&G0!UEvkG!4s^j0<*e; z&&uNo4$2)r>p;gvRa)|gLBTPj&#}X_slXr{Q^q(jM+b!j6=PsNk5wnpE;5@AbKLGka#*H#0qVMUUPrRlo2Y{lbKmXs2M1z1f%wq;g?ra)yp{;7EVa zL{UW^pvv+Zrb8(qL=-M7A+YXDcLg@e>c$F5l035`_uNMKi`-81U$J}s89`R^Z#U32 z+^KT+k_Y!9DuJp}ib;*UowbD(lZjxOW-N|?9(l^v7#Ot10Bt~JHQMt{6=0JTROJX` zRA3*h)b1rG^IV!}LADt@ww*e!=*|c1=;l+y&RG7$71?L)i2gG@@Q5Woak9jV42hSZ z#Dif_G<{K`$xmyT@)8`l_BH`6mkx8%Y$XS7IYK@3VLXPRt6g<g-t25QxEH2WYL~ zeo^3!4cc{13Q+#JRknPEVh64>IbOZ!77R)%D?Tdb3@hA`WdJER)N7&q#I`ClG2D4F zvzElt@!rwWawZbRUSxH<497mf+D)8pQZ~-PFOYV;l@}MKgO!B%+8?a2CyxAY}nd-%GVUqV9sl4e}AEh~2Ehrgx79 z>MrDcI}a`b6S@*zHbALHn@WP?ILNFBcdIMG1}-SZ;0kIorv%lufl(ZSu^3yY5?RI{j57il(ZJ^E`sF!4JDIRVE&8&>xGTb z1{~->0s$jS@Cz!U4~85@aSjuDI8KjksV)7>d)v^dsVIq#rz7$|Cxe zGbSa2F0pb(juq-?Te5gYlBF!FI=-NfRzIp;YO~UXg+5K$4!q zrlzQX3eiLBtD|mhxT8x@BfJ{M|9DlVzz?p}QD{X^*>MJ}3J~U=yM{@uERO^VGTjd$ z;FER+Y9J-dZd|IrilUj@tjz24O&8@kg0Nl3`=3ZM7je&~Y!iiAWC*ndg<61ZM>yKN zhzo}%^;&R71vNCN(ftzeKG|_g%dF&I+u^O|3H(MZF)paO7^f`?=5{LnG3RQ3DpdR=-$FDR*2EyRwhvus0>vA#@f*OJ7F2v2SqUf@);^A4ak)& zv5Mh3Cs;6XeA5!L>8p#7Ow-gItlPnk=VkH3r}rLFk0OeS@6X#dZCbG$b`e(SLF^gp zKS-5ak9C(E2w{}pF|N&Qe!|f{a>>$k&LFHgrgPB#ph|)n0}Ae*5WwH^BP|*V2~2&e zID1HEuU1t~(Q&CHJD38^k^rSEh$XP0hzc9(ky7Y(dJGIy_YH~iA!W)_gB(Xf8e$w3 z)q7Ml5zXK^ng^v9(hO|75W2D8I;#9gjUuD<(Bu6bNk%}q9g<<3BemOdupmdO3`SjI z-A$`vkSXg!dMbs>;hr!|gN1iXIaD=$Ax-39)iF4#J_rWM{)eujF7#b08V+r4lD=2C*~;RJ`bUVg{9#NV5(&o|l;@ zs8%!G=GY^#%noFMP@j!9h3&L#)^wH2E&moPcZ`m6o_Tl37*_ah{AebnHy(==F73l5A4C z^y)-$RvF?fQD#tuB`7lunp&nEN108)^eV$ak!EJYLwuA>lS-OZHiw9~+q-0geRGUngZmhz{$E#!>vkoVAHt7IoaU@ceZ8SiwsB`lX#?ANTVCi7rOIla z6F=Y_OHtwllMQHfFkVKVBwfAU1a2siB6(JcakjB^!R^t=a@gVL=+VS)RJYz&qQ3YB&=dAG~b3zF=rZcLU4TP!jdbIL_K>P?|;sw-`T*U+9+ zQASu1*oVJNCd#qOkYj_U2Vy612U3KY$@bT+<$k*!|a5|af#scyp~ zmv%swgsSOwLDECzjH9ATmk)ItN>dh}#TCHFm)uQSrO7_ek$nkr`zQsPXD-l&eq1uJ zdx<}MOHfB?i#GqOIRx)ITro%EZ=Ix+SYt4kp#WH?Cm`5QGtZc^ii|Kvfpp?-8ph*P zH#3E0shduPj!ebl2z4V!ZrAZc1}9ctcD}Qt0*FcefrvnJqD+Y)a{^TnpfH2;fjtO? zv}oaQ31D6moF!c4wEF9$hS`EyDo5^Tjr-6xtrCU+zbZ2GY!7}Vo$)zE1oa3s_=Z{n zGt3B|A<7+QeLBwDGAmJ174pgW*6gh0nqSm``DZODuolUMYv z7?sFH=6Mp3Pde~IFf%4mJ$DeR%=P-_jGSmnSVWL2*dlYB|2fN`| zm$7K7InyYbFR)I9b@y(r3dGQipzL&-$7s=g6Yce@G73U<3NvPsboRRcR#}KQO=BRt zRI8worIQwH%+b*4Q+Mc4x&~Z|tP{J4pml}h-GTO&X*A27!DNCQW-0ZugcjV^Lksxj z!cvSr63UEC%JJ5DX1v!0zz$Jnr6PO1GQDbuW-M7z@Z;*;8BF|ZI}fZB#{@dU44Z3bPvy=DyTJ?+$Z>k8Y~d2^TY##eE@Q-o-Mt=mX>o>9f#eLj>ynB4orZp1Q>_ITMCFX z{dXJrc{B9x5GOsplNW=OP9cUa(N`C-GS)(p6c|Xx$ahsqWih~$A?}TzhY!(F=zHsU zAchuGru27BRZDX8ezLq;QaO&0<{UinqkTy<+>*e7!8 zI{Dk8xM~_kOvgMXYc%c(fsK>BB{2WtM}FQ6y*rFWCFmxY0JX~yl$(CrFSIetFaqkQc3fc7@28jI&>tYABE zJ%kbra!|YcJx6}t49)kZQoI2Po&7{fP(zWuV~j1?%gK$&mn!J*)Sa6&(YdKA>Fl&z zxzn2cG^IIox}DkGOSt0gx1wIvspu^t*U1X%d~v?Gz5VO?;`(}ebFtbhqub@;=lR8K z^9w)Wn>UL$v-69~^Xct+zPMSo>DynC9g2Yzev~gdm;0^lU*ux)=A-}P^~^uH zzx#uiFZ>m&FQ5PE+lQ0O#d5NKH=CR&b~5Q)y`C=L&ep5R39J6sr++=QXJ`E1{Nc$z zz<#pdva{DWcR~I1RxPaW&$DG1cK6BueRaN^->z5lf1mk3UtUhH=T{#;-Gy;ii{+<3 zET&4!#ZvB?cU;L}j&gbmK?Q-${b``GpEjxXFHhHp|EN7Rqw}GF?rs=5ODv{WbIRpC-Y=G|zc4pI$Hg zZLg=xpZuqde*3W6x3`z`w};F9;+NamdD!%RU*CAKnS{+fdGPx6iyvOSeob!e&feVo z;~(_o_3Nv}+rNf>L?@Fle)jN-;qOrI^=6^Bi`ZPyEcIy!eGc9w?Be&U{Y+<*AFrl2 z;XU3i=NI+zg!RAhduKhH1WUg34}Csa`Mnh0viZdCX6+fC&oAq5w4SU!UcXsfttRJ- z<#M*V4a>V;_@Ue1?>^UR^49OjdT)funw1Sp<%MwaP5r&LbA^HRpzG-`UejE+*0sG| z>+fIBwYYg3wuay8e)|ut-r3~o)zt5Wm0!Wt3zK8oapZfi^Ui;hl-*~<5 z-{{&a;aUAYVe2mZt(NWgsTbzt_HlJqd5H-7`RU@uzxHx*GjGyXns#>nHm3$5-EebN8w8|JdrTJM7!3f1~wuJL=`js(Sgd zH|qQ4)PJ`9peL&8Pt{q8&0sB^dhxtB_+Mt>P5!W)&2F|M-IHg}1@Dcl_4Q`R(--0Y zB9rYOFTBOTJpNvXBcD8f`I7hF_T|EFtM$RNac-aT{Cs)olI4Y(K8zl{@mYedDF#U||H` zezYO&ygi6rdg~WfDGc%p_R0U$o4w^PcQLiTz3|;^da=Fm&Ft;`W`AjbiW;x&!~A0X zZhwCHub)i)m!<#z{~3-xpErN;|GNr*e|_Vn^!e<{y8xGe^`7B${cd^vX>q%T09!U= zchIm38+Hm#cgn$W0vxBnab|Gb=k03xfxlh7KR>@aI3-5Br{=5rL-XCQ`{mdD^6P&2 zb-xtdFMDJvT=wI7`OWNd{iy~BPoLbiHq@`%?bq%0t9tr%zx>DAFAbQ{4Dvtv*n%{E zw+9T`*ai-3JZvAf!$~|q0O8d8^9HQhPO`)QF5WK#rVvn@(;BsT@ZsI;X5tS6^Y!eD z2Ef@r)ij*!>xp-e$Nb`90~Vd_hrN0?U+r!cz>7ryg902io3$P;tmtgD3M2fj=9d${ z9#>bh03l9qCTC7f9!y(E&EjTudNF@HUk8|~f&U)Xr~K)84GrAPH&cID_?yqJZ{K}d z&Klh9E&y-04SKkraI*;h>_5G@nAeNs-{ke{-%S?FNxc!O{UA)2AkWUKkLQqWV!gz`i7ewA3oe6NLhnA z;fK?;-(LR70wUu_Jn(yFdi8K~yZuC74_$h_GkG-G!eVEG4<8=-dUi3Nw)gk>;^yLg zEeE>|*G8P?EfA~y=$jPv`FiR<;Oeg4T=i=ZqT43sQN9`K$W52&q} zdb0L|S9?gWhiT2`^-mUWW*=9RlYneZ9#3xlEBejl|E5|PV}OF|CH^U>+uf+v-pnuO zGipP9@LP8BVX>@%uW9qRUX5&_avE@McJ$)r>SOb+S3$Yd)39pd#qc~xL+cJfjQtg3lmT|Ma%llv3iy-|q;CdDCxFQbM zum0B7--^28FZLoYNci}<1VeZyxrepp5Lisa%Bim<_?-uL&QQj$o;(W`p)?T@pNpFx6ll!)hS$;CF+% z<;TV)Sshrg+6K>m2|BBrU9y=zm_sjRUY?pjy#GV3YI-u#Y|_?l`mSDQ|DJEW*VNqc zbenq+as!&Pu;0I}?0_NN+B#o_J+T$x76{TlA(J^N`wWZ~poF#_&f%8b-EHzmf7!n; zZoG2cq<45zcvJt;pTstK9p2(MX(0QzXyt=k%x>rB>wY%KNY2dis`mY2YGE?&F1F5e zA>qjz=6aWH*YR&Mt!X!=uF=i@ckkEdAsZryV|_qAQ2)D+v(ewp&rg0iIrU=L zw$9$=X+L{_{ex=~p2rJYc)IbFsT|7`CzpjQ_$CN+?^L08_`dgsJD{?WtM z*2aXP&FLlNW6frhm-Ti3_w{SyoRd?W8!W$?aCKT2OrHinwB>>Br}M|-UmgZ! zdD@(&>%+9)v#WV{qzq4_Nm0x_}J?pXqR!NZZ?ZFrIj{tkwF&qL7j(Qr_<`CS$N-6?ThB!GKP1Z~fW zK<)WRaCS)sXG2+rcX?*NoV{CLUxjLn$048b>(@7r|M=p`^RMf#`S{xxubxc4d-Cmz ze}4U&i&+C51=#90lV{(5_v*!Wul&#JG~QY;8%wqd^1R(1uYWTz(@(RH9~R4tRd1Rv zY5_mI=8vyl{rU98fBWx$`b!w(&k5c}~z&pO=ep#tt_7hs_3m{ADw$x#GuXKm6&>uO>hK@XX(2 zwOFp}t$6mo{j-1l_{-+EVa>kWEZXCz-#`DCdJ!3YpZXg1b+FEl|H!|7R_AZ~N4}X{ zOxIKYJMMq?UvA#4ZhznW`|))0&I_l1jDFN){~`Tj^?v>M%jwsT-z>jw?(}1$A@+(D qyI&_lQs0ps-12b5{-)tgx2y7Hy~lp@Pj~-x_x}JJbJD(8oD2ZS7u^Q{ literal 0 HcmV?d00001 diff --git a/tests/snapshots/bbob-1-conv.json.gz b/tests/snapshots/bbob-1-conv.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..478bc58000e6966282efb7f4cda0a5c8244ac13f GIT binary patch literal 29999 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 Pzuo;mIri~d<{|_Dxm1S0 literal 0 HcmV?d00001 diff --git a/tests/snapshots/bbob-1.json.gz b/tests/snapshots/bbob-1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..d154b7b07b651405a87d6135808ca0fa560f8b45 GIT binary patch literal 29679 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

GJib**pHB@fUwmdb{#<>31(9SyE{sMZET%R5YLM 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 pqLd3_h{5j&xNYN*3NJjCQ7RF^}GsOeps0PL7L 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 z z`*G*E(yoOJE9Pof02jhgi(>54#&C~bYt&WoHe00;^OPz~TTMpDmqaLNJCrM~ZTn=% zLq{lWom`mSB)@esZ*tJo8#byiY+7O~rz~@52Z+3u=uwGcbV{%YwIU9~Pa**>kBUU zlIwxk@l%mu4SG#&x?+#lcjF*mjEC|nTDHT8QGv6uUx8mrMRB}EQ5-VORoE&rC{3}l zE*+GnJt<8)fc8LEIkTfuQfMS!oXY;B8I{HqGZ?8PbVpMMK(6$a6IIEOomQq2kOgAH z%0)eKNDV6&{gZbME2qNBi`0XlXX8qA=!zy<3`3=^ zGhE@5HZFV!`Oz%m>v`F$!Q>pfN2isZdAgHRT|(JLJdCM7JLh?KmHE-BLpj;-{) zQv(F9s&F}D2K4rx%Y?jltrw&u-F4YiUdg!oG_*qI*c zd=J+69&lP$flBm^$Drj<&}#4nWf>oaJ!;A0ITEzFAD&W=<%R>U7;fhIL#hEjLU9~P z2`7beQQwBs$y6Vx-eEj>GO=q%<>hlRE1bA!eBcfY3rLuiwyjAml1>J+(|Bv>Io`Th zJ$4KELgURzK+U(gJTg1P9E4g{_BGMMT8VxQix&L0HnC*7*=%YbOn zK}p(()20*%KEpA)?9FblU#_(YqV}^o zbc(4WMd7A~)`xH@q9U-b9L_6;RBV<$e$mN{sh8;^jU}9cl3dZ}s?^w{Bb^Q5>%w3H zwaxB77o51;B(qgNvXZsg%%)uFBBa7afK!e>X?=uNxJileXD!5^oHmN))TqWZmE8{8 zFe6`<-d2iM7kk7rtWu1{U?gsXhg_Z@42x$^6!zAzhge@{DKcKp1K&QS7M>anW?g+B zj5gFR(yHt@FZdd&YZO$Da69r2bNNEuitAUh<}o+4+*7MB2l?B%f{&T*@rG*<(1qtQ zDZ9+5)anCD$^*qK`7ym$ihd6_-RK>rh#T-eJApMPV?1{dbFms*DHP@aN9AafKrvy^ zO*csqcjT2yuLZDdOA{bw#WV3nUE>*xu}_Bjy&Ybogl07{E@HbN_zF6c>eav{-)*K| zfZQkMh$TNo{YV%1!q)dt=Nmi(x~k-XmHpGCEFED``Uo0qzgGCwsoj$nqe`)LRDpR) zk#14cgo2oLR!xKQ6(PSic(rAlJ(gUJCP(m4l)B>j$`1uq@yutI;nfXt4yAbNY4|Q~ zVHRRLSPvdt8(;iBz(~wH2Vb4dV8{<;)pOciIR}^HKx@($KXy-1f>D*GL zb4weRUa3CSwZZFku0UDCN?J$|){})U2UlWi zg=GPKk@C&(9wLOP3jH!`>(6CW)`P$zue+2rVP$(o6Vbak^A;D}^|V?Tn#YS~Kw&C# z^I+<9ofr!f1wMa~>zP4=F_#Ks4y3J+D9x&9>my19ZUQZfnJDM45T#KcFuLjdl`jQf zV2xt0(T8hPAAY+z8Vg^#D%@SIk>~6F#cGEyVsoCPLGp!;yy992grC58*T~+^NbAEcQhMz2~*?N1t-qhT% z#}svh1)^+fhNLCwQA(t+L@lwsQ;L;@5K>PWCPb^|_FQ#2&BU&Y#0pRFX(iPStpf&% z$db8?3T0%0Rs-pkXEf&$@?4ChPli0HDgdXzwvSS4)Db{Ro8V^#$QQaHQE0VA-CSH{ zm&b>k0o^@DJRu*&3UnVN&14h(CRRCLPw_XZNMns_@Jewibf{!Y2m;C?w^GamVo+9x zkag_$-yiwe9P|v>1v0F|A*b`SphZ!%55vq_rVN>7euY>hbSqVYE}=ITB?l#Lhu(9) zlh(MAi(Z5Mv;YGcPuqnjXNFpY&{MP(<&(DI+5wj5KdG{>J{Kz@a!MDgVst1c6tNpn zbD@LFjUucHE@IFTxRn?kYRW&H>2mUh!PX@a2W1ULS)x}`@5Fu-#v?az{p^*DhHPTR zT&4^Setu-G)+2?C0cML(0SS>^X#r>4 zM`)`geTV2uTR}4A|8<|fp8!WEW#IjBaxYiZebp|)7M~0N`Q_$-2)$1@FYgUsqIX5DX z8wN5p6iMO=x+AQy3Kip1R2e$lFt%bKhsgu`k_x>XHt;GXc82+0>S`vTWFbRRSy?HW z1esUT!4|GkR%<19eJS=h{qp>c$Q0B)>W~k2%XI!B6?-;0<2#38PFoA{aZz7mch_5% zyfspf%Is%hxpt+h?63Az2(ejwD97?T99TSz?3b8jRq?YF= z^}xQIEp<@wX+gdo?YBUM#kd;Js6;fxly#$&zGMX?hZ^Uied1CSd&K~zi-PC&F~HXO zW!$zbuaD)Bkun+HY6CpCDC;WZnXIE6^3G|*P)!OsoEK|sFV@&zB_kjz!%uQr5QuUD zMT;)x;z&ctsRW)Cbc=2nVK%2Z zT1!l}fmX>IUUo(P#wS+Lips8JdS=ok-o>O9e^y4k;IwiXpd_yXB+!ylLF0_z#J&Y8 z1wkjcT9GPbo7bp7uOTXN7udj7%(+bT#kxmCo|G_srTh@j##W|0tIpPHgmXwd$uCl_ zmYEttVkl+n5=s|TvhZa=;g$MAQe?kM?Gz>JuZ>T3*mKNVPjW@)7D!iu$cf^uLP_T> zH`CIBrKPW;vpRE673LmL%kUSJl?GIfMn;Uf6(gf^CNo4eT7p|Kchk8?hwCp1ZCUH7 z3$8D#an(5cYtmAHnK|9o|igo}Emoa`GyP=cIU_KAMBQGp}U z395*--f9}VBd^sXt7u;x6+fO>PfPC5(7Y-*G0I^Q=d{5E4^~9{c*Q)CODg4vO&=Hv zdF9V2w1LVuj01NS!=5=3p3|9l^SEir?H|26Qe)LSB0!`wUYoTci9C|Dx22?Aori%dTUjAhP zAXU}$iFCQ$#m$@$V2w(^mg{)}0UT)VW#;9xROV*?WVsw4c5eT1wCiso=qH^d= zw6kf-L=nA`M?x#ms4PAT#g2-zDc4K5;-)7l0@a~PgnV_lb8`_aS|jC7s1zxCGTqJ^ zu{xWi-PeRvl0{IW-hX-0;dH@9VGo@c;i0SzC{nxd6?K8ArO>uleWG^59JhRfc>2nF za?xXGF9Ihk-BD*bET6$C_%3s8TH;MnVh=)8wyC1mIuoZX<`H@5s)be6N?s{SE#{AO zR?vlFHyBKdtc-iI)2+Zc!UpvoD7ylQZ4b-QYmB#X2s5NsICSNnTa-^i77Wn?#vony z&NrkcTGX`sEiXpuk&@osvRcxmN{=HI9tTjDMtD#X*LjaxvQn?bOtX@Z4!|>O^-^4F zEUHbxSD03*8;w2(R{6!%rkd>XMU&dwO1R{mafMX72h7giNV2W46c!P=k;9p0AmF=t zgfjbos*lMxzy%e&o$4Z)Y!rYp%H~Z)va5O+6zgFT_q%pF#O1AP9(?)g%61wy${p9a zsCBHvR#JqzmP&9+1T{zqNjW2_MLGz8a-PylNLo_uVW+OmNx>BCTowPW;177-5J@I4 zv7TaSiVEeAOAcxHriw1Ly`kU__;`^uTB+20g>Z0BS6DjrjgrnM4eIYydCxYvH-u$B zb#s_AdQYkZRjBPfE^!U4a5ta&G@w?x9nwOPZMr-zZ+=mNBon&Nc=;<=zZT@lp4U#- z*Is;4#GSAG{nf?ky6?CmlY!BE@jCB|HQpB}vXY4$z0y$!rxbXD;n8W2Ov)pN%wLPV z7{8dtByox~1jvyApWPOC94Ka+;&Qhu9Qz`!7QO|Vs^EbjjNLP#{MZ7cT(xf zutaVVaz77Rq(&CQjP?8z{!`VI$@ao!?N*@4zwNZUN~q@~OciMStevlKt| zQ?!pN`+a5cfwel}HtRT7{;M>xevP1Ra}g;f8C10h8cohF#=bZ$khRG%A0qnHj^bWI zE*r4nkLHb_qnUqNm2v{C4LFon6mziEevrLa#LpDpufrh17P3wkk1IhCh>n!TS-E10 zN(2NtNJ>#E^x~+kzB4h$v8Z#iV(}OC9bYxrBRW#BvcV){n!ifcz(xx&V~tqOmju%a z>qAN$Z)5yX#Y5!Ekv^7~8ng~xO}Uo3QsWhc_O`}ft67(NF6=GMEej%!jriR<(~mW# zUk#KdrF@oC3a^AGi3H>?gs78!PRlSx5E zn22^W9M*)qZBW2P)rPAU0pt8^r9*&{LkLl&>oU&nnK1JpxUg? zQbp+%#g_ri71z=BhhYR~mK!C3Z_y%-nHF)};u=-9es$$~uN5>v?GR6J$_@pS(vM)2 z*hqvu?GlI0x)J$;nBJTp|Ku(}HIT>l9%@=;xoBD>x{5{S6k`{@5ncj2UEH&v2vJ!1X$w#G3j#jo3qtaWsir~ z++WXyjs1Z&R;bvc!yvY8tYAU>jVJ_W9qQQAMJ$weW^jPm$t2D@3m22vjlcS8jNWez zRn|-kR5cAecawYAD5J&QE`+PW)xH3znCXbeEu^T@XOL0>!6`{@KnCxk9gI+3oC1=P zyM{5i#(EadB-FiO?0TAFAWe(kRtV>|qJXWzfgO~^UgAoHQdpTpG;Ef_xJza;jJ{a3jYP%ZDeQUJ?Oj`~xYKB>r_t96X4|MctxI^B9H7>_1g+wVSg+w8V z5>1OyL|0eyLA1Ja2hz^MAB7bfXW`Sd_K~u)cGgids_H-&wR)J1dCO8L#M_wDR_NHt zw!#uM$;K6yv>eZs_Bh!QRweVmow_-YZXsqJN1t7Y4Bw6`-ae~CPbxa z?OyG6PI+8g*rnBxGEMIr8f5{h9983R%7M6`B-#(;gYv+n)ME+E^^E+2GgHg!jn?Bd z-u&1_TJNp}91m0^&ZCJ%F7>RXLpxzx4WLl3k&EOQKb`v92c4&)RQXCvbOh>9xuKqbu!ITND8vE7_<5A=u2x*DOt?AqB%S^ z>H-Kb*Kc6E10Jwn{RY-pjGcwr%E$(IEm^trPYnA}0n!>1A+!)>>)q6PNBpL_idSYU zqmqH1&)0W7^853l4WuA*Pc{6>nhfJ`6h>K8l6Z5Xa31d`GY7*KAN^qTIsbuh2gZB4 zxaoJy|CWn!qhet1cY zw+EJMluv)fpH#!Hv=900(d6?dRkaxQk_lLBVIeQuTNCH|i6##%uOC`|cE;tf+moI|*_)k8X(^B+$p7XAEm44+3`ZaQ}Pg4!`Z6`UBM9j*=QRfnws8nTK z9Y}9XfaY-L4ypV!z0;ZFK9}RC%dM4BrRTNM^|eASblr#Moyymi8_e*dTL%iD>%;Y{ zCTbKdp=00Uf|3S|g7jm3?g{`ibG_69Y&V~zC~_Tn8zT_Cf_P(f zh2a6if{~31l*nTpxhk>Z63`%rJ1lcPN+|h}O0Jm77ez@;hIQPd+110IP+;<970__Nl_`N2S@PsFcD&C3!ni(sHsy<%$wW3lwWWip52*$n!RLOPs1dpg}Yf z3OHq%V+v+al!39&EdCoZ?$t)Aj%izq1`n4!B=O#`oL@j^XaCGQF z4@sXiObu+Au%z{jtH%&NwoX{82xAtl(9{hNAfGht&<+>!pEP1}9WE5g9QE-A2#3+P zNQG}v;()arzZ3yLshmt+2Z#{n;FL$|IO>o5E(9lrVXncuEjT67URtPC+c2Ex%=-sR-<@2E3yhs~Zo8CiB6bqeQa1UmvgE>vy;DJpv8+r@)9BCITgaE$t_0=|CO)m?)V% ztbSIR!kT7la{W<4;oHk0J35C5ceqUlHFyl7F7bIVAeP<@VYok^f_vfy=8e0$^Yt?* zkv89pojXovgVMpitt|+Hbw%L%Wg07`dmV!-+I0&(QKj&(N8%!`uzCSuI-}W#P~Fl- zo&;v&JpwIMCUfEr7F(s1@VeoG7LW+9vQ z78g|y@^a!vf}JLY1&rCK>DlP*eIs8xMi37Ld)Ql#+YKrMDKph*v?c11R@ z$g&Mg```lkHwNaeBgE`2a^k9cA;nrGv|&LAxx24FG@#)!jM{PNX@R{+r<}p2P#f_P;m=L#SyTM0mVR{a{`Od zqRasbY``QD)WN-lAXH7qW4o>Xbq?0O-E080wM~FG0m)yd8t*p-@&dEeMxUneWfSZ} z$MOMO-F{eBJL#`@c~D(d3TymqKaA)jwiN`z)V|@B8U0&RIuL#QZ2v;ZRG)Brz`%N6 z1KPW|frYfDB51uGy)UW?xMOZx^C>nYW$H=lSr1ZAfe&%SV2OE<1$?q2%f-+FYQSh* z8w1HbvYFkR-q&B6$aHjZM46L>qYK+`VTEUq&fB?D4rgp@Z4v`K`G}(}$%(XLK<3lU zw80@(yQ`h;fHXsBfJ-*gx!N(@%bD8Lhvu7oNgzV%_?P}Ep^-YOrcX?%4w9pm37Sg> z43TgefLM0-`{1a#0tKdtMzw@jaAwf#Jb>DYWST%nYhV=s{-6h%R;Xh`Z7{I6KQnEh z4hJEE-Ql686^c+kV?-RC_ibZ=WwS|v(#j$jq;3hY7*B(@LA&im!x~HteS>?LRXGd> za~QT80Tz^Gch?rx!>y2I>mCPSpETAbgHsoWZnKLwiEdzhW*MtLqdAm}wmRnO==xsA zE$Od#*)A+m*fl=kKWxDOwI=J2-|RD7V&3TucGAc05JqQEg31Em$sBm<{EFOavt zM1UO&w>ptw3<}T0eM4)s9HG6Qikc8(6I4dAjh_pH2P#0Lu2B>->X8-6)` zpMc{Q-Daz-+vH}VHS1E4*B@;eg#i>++Aw`bXTT)3mdoIVjZoPr`h{*1trxGW6HrtV zXf?v0_R0H6^@d;9>1(2(u0HFu(~&*`6PMQ93fM?+EUQQ5$?lZhbzm|BQBZc=i?Wj* zlpXqv2gX1WMuEv%FfgJMXuCFnk(V?}G2gGwK#fwnOzCpJ6Ev_k6&3?{?6|s{!Q6J< zK(#JEZaZ&qKt8xC34$XzsacO%a}i+G0CiVSW%*FL=c(omP~dXphJ^s0=bq+fw{hrX*t7e&5pWONg=~k=FWr4gv@Sokk;}9>S_aPon8u1 zIkKeGv(`o!+HT z+N&Nh-P?FUnA)F)^)@h2P2{@ej%r}A^!4e42 zjlg4d3P1G=_8vEDifX zWpc>6#CYlr;ifi)quYICtI-h7G&)%=V=HHfq+NqCc8sl@yqR`QYfvL z44b&E%^Lu@84uj3kd0e<=%O z5wEI`s6Lr78!dXxg86Qv&gKvsY~$%nq-|5@a$HUnJ{Xn*4uKeK9MOl>IB0YZKzeWh z0^|VHgh?o_2`o&B5j43zqR=e-&{X)uCB3--wX0l!;?}rp%hnc80+b73NE2vWs7R=O zPFY}a!4`AeZU? zdaJB3(W|WRXamzO2{Svt(rv9u3Ol`A#g%KblrjNGWClByX@hfFFAC3|kis)?`~%oI z)pnqHkB|j*vmz#eR=c8@^>=!;N$s`YP2cY{Q7o!*mT}wCn_JL~zgiMr9)@U=xohco zxgd+u6vEW+_=X4+M|ESYmSM`ZzN+$oV-{T%ddLGBet@td&n@mw1uJiP+BHm3GqkDC zRPUgt%tcWHj{8;f7zH|Rc1`)$D1oYSAG6dVK%)>3tTm%_05KK7oIcE`*~C5)Gjf5_ z0kWVuo)9pjyUZmBZi%2-;$_qRswd*qsxMOA^P)XFk_8YtOjlby+iuUPVItie96-#p zi9rH8ZQ&87U1l|=K5tTcs>~o>Ge`a)eZ;0|ZnwSpjD8l45l$Uw{LsmyChrkp2E3Crq*i>s1XH`_wLDHTN=r)f%d2PUxXD($p9DQpdE zv-)rw{FK}VSwJ0=Wm8~2DgrJwLPh-$m{S*=-8eHkfZd36wPkkW*a%$V*2(Pz2$XfB zO=785XsiShl6Iw&Nk9@9=`;*et;g=fh^!gbN5}PHM06M3?%7FKM+Ta;lPM~s~Re`S6`#< zyn!lA-*l8PK?g|h9R{^$5CSx6V~P;eGt0)nXhGR@TS+ejW?NzCe zu2hzgUWRiMDU(Kxh3d`4ji$`hSu;i{$l(ZpRPD?)LP?pz;>^P0NG*Y+-AQ=u2%2gu zSX%bDpg)&jYo8PyGt~4g0!bCl1T8$(1tv=|3D7wifmLy2X8mO?Kj%%i6`!L{Y_%-s zsMzYTV#cJ#Bvs|$hh1V(GOSvN33o~V3S*J=3Rh(8;*q$aq?84#ywimRvzEG5HXDNl z(>5sdU&Mkd8HZh!;SY_)C#|~RM4zy171nY|MxTsxNe0c-X)BrYgiI3^S6xf3#=6yn z=?I$@VPjR`?^7^rT&#IFu44d^4f-Q`JmM#ornieNBz$6O5a*NT>$M9Q(B(OHDIviWf8j5LyFb zkGm3yxNZ%K)3qvMH%8uI%f+}eGV~_tiuyiyJf3X}Dy|QQvh4A|SpwzNeu!=LY&yOP z%p$BBb0kj(tDY-^3`hU6hG75LA@x#csM9Tgv)X3Qmp0khu`RgJ3*&Y+za;j*eap{z z({05i8_sL>*yq+{G-N)B9t_pwG_9xA(CW>WHedd5YDC3yES+rY(7`}tO=g|%syed= z9EjZj1dXc-_$=@Yw=BgnM;D;GsDdfz?TMa7{u2%lQ+?7_+k83^P#%u0mVBx8l;S5Y za=p5$nl!;C^e zVgK8={G2!KFL?u`Nea=WlMxK3OcPPPuMsu?h$ZN{mb0_SZs3Y5Psvt?Xu*QxnBvtWT;R!ej zxa`rYDWN*5D!{F49?;kx&1EMtOK%G?M-|dZ{aJ{+3IJ8kJdftlX7lGfTCcruksSE% zCXPM?3qSvX4|sDE-T3pxa_()?APSQpyj}Ur`*1ElWgi#8diy*2A}B#=M(3**^M6s7z}Z?o|iN7tcI{1@Mc^41+2Yu`)m zW8OXpjqrYb5= zV;RLpdgmJxh9*X(H-GBf`e|ZJkomulzfYan4F9p6hzT49`x?!z!>yo0wOteY`@xSz zv+X1QoXq23lP1Cc`TXzY(pv|shsUjGw~FG&A7k%fd*P*?!3SF0&-tuB2I-w~{^tCR z@!{mR%eU`-e)G$VZ8I#sBhivm!eq-4sj-DP!^`uFm&wY%^}|JeR%eC1H+;y!iOo{( zKXAk4X76@at}^?;m+sF4*@tg(W!ptZZ?!V^cxUj18Oi<4Z5-Wi64B#pG`*M^r->2! zOF#C*xzBstMlpx5K9)1T@ssrMow1CgwQ+X#=FGsR=^8;|BzMur(C`vt72MvXyiYKH zZ-_@#HqS-its@S0?Zxl;(JIeg?Dl4}3~tNYefeqQ&jr$Rybk%bF$8iO&#td8-(Fo` zYf-y`oB2OJ(aH7oD!TpiQP_hG(cV8gZaxQz>)A%{HZkjvZS_X0VO56~GWjiajieEzGl{~NASfZh9wDvx6O0zEPYY|=L`OC4Q z@K&G(M|F<=O}jTU&Q>1B1t9<>#yaBX*_@cdE+FywEDHIu<0uTW{m4$~F#G*{_#Bep)6vY}RO^-8iSPJdk2`X5 z9>w8u6@B#MGk(Crsdyg5^U0Psy53LV^g28?9v{W8IbtXGL0V~aCUN{5W8p8wHAo_k zv4SW)UwjBQN$80;k%XD>)z7{A#22kk&tLPcdRXx@k9dPH66eV0BesG!5u3i|ixCT4 zJ%}rm4WCW^7w^M^oc7~SSFc}fA5H$xF}KxbuRXr()Z4Xs`O-9BzO1!+6MOt@4~sOL@2F2#QogB_+PR?#pHhgEKQ zdhzlluCMlG#Bnbz%?)Gc4Ae~op~9nwwzs;-!^w#7WIHod4~w1JY{?&P;_iw@95WyJxBSHxf7-Tnyz{-quH%h= z8-$0h!f2Ms93O*4dUwz^{pX`CKg9fh|05JtpYsp=zboiU zeEEVeU-0D%zKn`52kolp_AZTI`OEZCnscUS+X6xbXR6D?tURI9k^}E$&R}trsYz#tW>$`{IWFEnN@GoE<`Niz!l=_0rdQlX&D6yHZuzva!PQY;Mep+80(xWC@qaZxGL zIB$i@d6VhQ4+G|#kZ>@IMIbkcujKmr8zFT{RM@P9xXFJwI^M~iLB0XPmF=5p8m#!y zROaKBeiC4_A4?*6*+e0Z*;x7EZF-j+XN%(B3$37#zoq1S&@hXi4`UUOCX;|?eEpjzGW5m(N!WrfmFAlD(o|o#(B$L2%ky^HR#&O159o58e zPKTD9?iksO5~Eu z%(h~u?DZ0%PZhVlK}}GenY=xGH{%BZRI2JFvUyT@YsgEJjTuX&?_@K{He0M>;|1}@ zKqxWyHYhKAKTYn}M#>u}2W56mo9hSio9M=WNQ{Zl=^7`-hA)vL7yrZx+I69*kz4#; zklTH$>?=<8_j8mp4g_im<=^2x=G#7bU(`IvsWoem%iRi!D1M_bJ(?M(;_!2u;lxOS z+b~!Lz7C;2a+EbbMzPf9df9Y28QB?$^rn^!qoZ*3kgatkh>IKsnFhaxb8#CA5X3#& z0TAb(DcWbonShe1+q0{(;WRSM%!D`5s)!mo5+ARmohzg7oG9g*z9NxtGmi0%6!x_; z+Z|s8HNdiEjm!n&dVN@%Ik`eL40XizAH-H;7~D;ri#8xD4WC zH(<%2b1cYckwBEyp?kgA*-nL*88))rtLjGUtxJZEqrz%;CZrobxb<_JmcY^s?edKl z+nN*&X>tJ-4eJj(;wgFvyH9~e-1y=AE+$w?+%&&(O`l5UaYZzHPVD8$Y_Bf?x@Yo_*MQO z5rkh*K}s`{vh+9)UdSuVZ&mEydNH4Tfw*l79r^Pjhg0bGflr)2)b^7L)FTQ5bbr=g0C-AKXVNb2oxSG$>*LH9)?V!)rX7(@t;<{U|ok9 zKdT&*ya-1#jR4NYm6en_RKAqTo;j55UgFj_JlpOi`|D7+i17XJKh5sa^-6e#oCveY zi|g>@r_0le7jn5LuP?7ojbBb*U;c3Y)xysnPvYVBm2v*&m#fQPuK4%TS|~~XGPsqG zIF6FL4ZQxUVWju|11I~7q&Cd4yd>p5KV4n@I=%d_|Ni+m(J127KTM}r4;#PKTRbzK zipcleAb@CH_ zaV~Aie9ED*@KTTehWT&*5#A)5@3a3u@r=9JU-D_F@Z@lud~%9LC(peXCpYnn4C*`S g+f!iU1=wPOyTv@0sPWa`wtw6HKT+e+W9%La0JAWaf&c&j literal 0 HcmV?d00001 diff --git a/tests/snapshots/bbob-3.json.gz b/tests/snapshots/bbob-3.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..6cb0283ff21b837e2a4ee0998a6dbca0845ca52c GIT binary patch literal 75315 zcmeFXWl*Hewk`;bOXKb|+BEJ?15M-JxLf1y?$AKv?k^Eygwh_3=M4!8Q2&t9BpkN&O59f3B}_W zU%Y)`7VEN>J2_w9r@?+Qgf$W=lQZBw|l=>Gu}TZ{f3SUhEEyq;d1UT$Z`ZtLzg zU}(o*l^z|aU)&fswR{9UnR&LGCJPTGuB%|XM!m`7qz}7l&!vCn9iB@ofx?vkf+HyG zAGi+-{3Cb`j!UH3)T_PeuL1vshyI#Q$G(@zk7s;b<$(3-|AJ5oSTE^x%zv4TJmus1 zF9;Kht~QOGj&=8$=@o!=EAOM9?`>&qwXD2M{yUYu_tD^eCgGB+jrLZ{e?_S5baZ^T zUj|s$_C7j%_e*W7rRClJf~(EHQ@tDS>~y?+XH@~#z1~^xUg>SMJioL4jVkzMl40J} zCb!cO={{4d1h6jReS~w$*QBu3^6ymYTP*|ctoI1KhdJV%^&Vrxch(tS)Bml?`3SbC z)lo~k_OKr}xm>`$ROnx*6te#Z1g}>ot?frfF+*Q0iO%HT(cbDVtPbVcXHyy$tNhV@Z0SI817%?t;Yghx!ZkG zV!PUj2uDZNOF&7ixAFg<0&(L5b}I~>6HH>Fy?dS991I51 zHQC*Y4g6BEfo%9M0BPd`PAd$(6HBTtoV6h zSpXy`>x6A>`9l(h(h=~#0P;{e{$GI<-JI!Bvf3?^%Nxg;5a{Oh;CWy`PcN&>)79(2 zqOpDWuO&*~%(M{5^aXHtRu=8U_3XTLpw{`=HXlZ4m|0pCdNaE@aNQF0e7UH*12T01Z`#E5yskHJ*9nxgi729G&4EF5KZzCO)dErECVHTg4#^t>Jye8;nK_*2JyQBS&NQQy3r zoc>w_9+$kGUGHBU+&Q-zFIDhgI!#nfZOM`f@%r364II22g^2bp6?2h-z@U|dk+zfl z-J92YN!GlPF0hlsy^DjFyE}M}QebRl|NLnvo@)qn3vRMr8sCu_w|yXFU%PZ=>1g-K zb=7^Vj2Pdf$?teIi9>#TF^uO*J_0cPT&qegH*fTU_H}p9AURDR@!GALb)NdkH&pAO zl-Bud1=fpSI!cZQyFDE&+%4>%KHW^*S==}3?ZuDmFUCyy%3ul!3h4Ndr^>u+FQ(cu zL<3}xqPJdwy(JIcE;lY7H-{H0FO+$yo#|^j1lx_CvD>;o+?a6IBW?j4lJY(`W@ijd z*a#P`2M<$+@~AM6B0U`*(baco(T7nSrt!_3&!}UhsF=39F}9n}X^$YrigQx=A|YAE zsIBtmofas7chBrrMpu@0r%-yqGv!bWb@Qcf<8z=7_3dnsbyEHH5aAqT(}qm4UUyq% z$ZXk+jvB?u!nuxG-E7&lj+)WQ!n%%{<80Y;TjgP)8j`CHgG3b$pj8t~_v1LRniiPl z&BC<;CZ-am*^Jq63N0Z4nn<)dNN!7-Ur#i<-zl3NIE*CsWZNfGxKO(CT2~rfgi|xm zU4mIUMA@>sStL_GPdt;45Yh_)0wD0po;XM^>-FJ$;C#0(zHVd%=*ooQC-3=mwgskk z>z^tTBFXN|9+IpZxwdlf03TkTgCc13XVvE3z#knvGU;Y6l!bv%Jn>Ua;+hh&6P}3MU9|GX_llgGBUPT zcC!E0F685!8uH-4suk&6xhbbsb+s^Zwlp^u)J$Ecx7oohl=<$v9`!F*`>eduvfI0v zgRN-!tqi@QFYW2Fn;Ea%0@AK&dgo3rKhp%)gc1xcBmPYF)jr!!qja58`<`x1+-JUw zjWb^ji|Wco77j+PPZq^jWjnbAe4hJ^lSnMXE#!5J zr!9G<^WU1%j~w{jSvp#!nk0SQeLWHnftR~a4!qgcq-mMOS~``JF5qQG>Mntf7b`m_`>y4ewqu6dY+NR{vp32Z_m3|ePSi7;u+ueIw*5cHVz#szkMJ@bB5Ar^c5 zGlTWbm;d?H%fZEm!`(H@YmaG!C6f}@*?||dLD{mhzYs^*!kw){tfQUPC1civsJ}%Rw9)GLuMHhJ0cXwgrx^2J96d+&O=)=qaI6Hd- z9yoa{`u=%L)e-`kP=CywVkYU3@wwiY&DN2dI(^o7nbH6l0`=s4vM)UzUQeIJxhAjO zUzXnn14zc_$ zF97*D-$&cs!>ZI(Mp*F6vRAl#CjoQ+mf(g1g$$R+`{TgEC+~TYVLnrgDs>qS1W27! zZjnySMmq4o0Ra}k(3ifu;&d%i?`xlz8|xYG3oy{36_AY)&eNGOE;q##ZZib5 zJ(|4`>}qZ0m)nw;FizsUz8#D6Yy~~uf$}@jbctk>8-F;6cD;qRP`e*}zio{p>h;J~e*00hRB|;b*~lEZvHs z_UL7`^Dwd~3}10iw`Fv-3v{-p6y)s6jt~SPt3f&H#aF)(u`wA%M)-7n@uitc8M)(Ql`|P5s;^O%1w<@O|U(oXwqD?q{ zCA_EF9RR_Lr0YafO@jP%;O;3DgRqb2Taabb0HW)}mHaZW_zn1E@c;%L*pGCuW?(){ zp2!2HqV(i^Q`)(CN_Xg)``8Y4+6SC%j(hnklLA8PFCP5|?`q}!w3!CW%k=6_T(vs}2wbz};9%UV#ZcqK#i!F9CtdT7(|sslVdP9mlp{ISjE_q=&}(b)B} z%6{y6egPKyN_62P8h{hpr!7WCkL0P}1T;VjZymVuovo8>Z$1Ihq#bS!!1x5gXKRo4 zGd0iGV?%MtQI6-gsI-v#sdM?us)0UWRph9p@?e9cyLGy~by+*7sdjqek_-Nl%cv#r zx25MngXmyG;LR$_-m0RjH8ZfP;{|`Ffo+kBc8SWkTw0-0dRRR{#DhEceMk=#fp zz(Gf3uoe2CHR!i9!l*L>{uxHX83yZLgrDWoKPsii)ssuKlT&IK6pa(#=C||0dP`4c{#+;lG_g=@w?YM>X0n?M29R?b1G<&Hfnz z(-eYQ^#k=JgiJZKOa^3%IzkFJ6mty*vpuZ7A(j3va(BN}cR#*gSfXDT8wBPT$p3DF zG{hXym=ziIH9&$=e^Z}_Qr|kMit$3 zw#tc7%eLQ^d48{&y4OeVo4?wD$jY z4jXk2d!HDIXBh7@iEfGNePXP)F>-xLY1A*`!5M{=ZH8grVVq77dY)AaGKrW zZm0`vL`$_ldy#C|-XYO_8!>jPJ8D8M0zA7tGR2)6TM|&&we5Q8vVI0$pQKOO3f{av z>s2zov3q;ppOxbDZY@^U?a}F-w~XRys69JOsdSFFj=phq@OQOLN9Z2IVv4LioE!}O zJZZumGM$_20;Tx?F9)8d(ot!ila7QsFI8w%-@pS|UA`?x083HXTb?ot0`{~)GlDhQ zK=C;|S)(#@`rx_0-lP$2*&}Dz8;_YHnVr}pb&6Per)St*QU>8g#pjl72q=(=WsO43 zfB!>C9FXysnEWsCpLHA4|5?}IUv&k6e^E{TM!opE-I*rSpxk`(bJj@okf@@XKKAc; zylI0jb9e^FW(0gPMt>>I4!HmFCG~Ii+y2(L3;PqvKR9jw;xPZy;Kn}WBm6RG^PzMiHMDu9xJ6-snI8V^;Uqi8<43`A*PldU2O8c&6;+{NP|j%vNwpD9!76qiHJ3 z2S^VBj{!PgFK2MychUp<_ao_#$#X0F15ELSxdLxyV~IW~;G4PUgR+&1F)!Yesyc9H zRki@|c4qGFTIlVm>jgD4TSs=PGAaw?z@h_msof|O2gwMgw|jYd@ucg5z{V~8}SsraA0?9BjNFEXfsb_!jyLy1$w| zS-$y^Fl0^dF?9k@g%0JBvt$mtLcv{8G`38|RdZBwle)6LSwdS*{s5m0U9cM~$lSsH z_2piB9o5f{C}i0p}3ju&|uuxIT{F4&VP z%d6qFfBzBF>-Bgbel5G;Zp3yjU$<*@PaN0sw)2|dbI|h{Jww=-&BI7k8RHNmZz||VehnRNRe`;DK-c@PpmWBEqBR`ZHHNf8 z99bS5SLwS;xGYLjB1r25x>g<0Q(rmTZO*GlJ8qGFsX7mfF zCwQnQG~*A6;(u01Iz~y>Ef(+$6=+z_W*E(ubC~0#u$Ret*jN*CPNZ@Qc)4zr^jI}n zBxpJ#x;$##4~E{n@5%-`R6DN8R~EJowLujRr=Arad!jdGMw1JQj`!MSo8T4tvOxw_ zfu=>GxRIvH1ZeAS%%6LEf-Ua7E3rR$5|}r(>M^v z31EJLo_1&WWiq_%eSA3DTKPMhQ##XRwsbPIv)cIuGuyqgLp;5lZ}!+%^j?4?5s$ab zx3Bm6rx#8R58#c66O%W+DqmpwHo8zc*v;z&?3N`WwRK!aR`p`{w(3Un_7YlrLQyBK z_kK<2o8B(imBB1i8O5|EQ}r`GTds1G5lj)>GM7wqYnzcF3x*kg1D?I%|4r!HFhp(&~j~m6e z_!D#7OI_*ho&3IBQ#|9!eDU#E&Rqg;Csq#p=@}2vY~Dxv2dF1UbtlX(YBWQDIBPj2 zV{qRc>xR`0w?N3q*xcSqxvw)QJT!0=}KdiJyg zd^+0`A3I;ncQ;gsQPLg>|IKrN%2M1O=S28%JCMT<5+M@yN|>;F#0-M860$t_b3U4vj` zFk)sV=*C0#8ir+l|B`t7{WS%a8FxM1J0>ouZ=N*n(@q^&*HJhqzK+ZBKB#z0=*$Gy z#+ck?SXlcD!=0YGnWR;$v0Dsl$nDNJ>1{))=-W0>uV86SXo5X`PwkeJG4##rW^L=6 z-TSeSP*#WU`|oG>^g16L3brr0ZV!>Cal2b!j=S3df2pFbE7OxJK3N6GO-A|fx;x)L zxayA&OquI52ZPg>Q}3?T@JYEqLpl#b(O;k3v3#B z+m)G{aI}r?a>T7#S9xd48R17RVG^6#WfE20rG1ZU(ER~$W#PGPpLur4%T(uOs2K1R zuy5sZy|U*zbP^H0MMaU)A)f@gr0;kJzZJd#FREn(&%|3^RjPnIKx94wpZnvN zi0BZc`?(e)w8q=%w|LlCp9JI&ZFZnG`SxdZSKqCj2~n~Y(8(bQJv&p*!0RwNqc9{0 z!VH<2c3@|8Wfylbw*fJq%dMUH>gqCC*JT~o`M7orUxgbS*z0t)=Ma*K^+2%L2ALyi zxd++shzyhFYT%QLg3v=ytlp-Wdvox}%LV&jDP%gg%8~7JNmeK-#?djB2n(p!A8g3CF1Se_(FR*r zOJz$MBGw?Auly+$tP!9Sq5p!)F#q99yr?8xrbqIF@~^yREDfxrLtihF#5_!xLcDJ* zp9kU4;;5U3k-9SMT82aH*(j;3CBiew^|!>0PsCFlD{=9PnF5HlA*G<^u=`0C{1tIG zc2yK;oMvoHBI+%E+r(xDKZ8t)rbDrp9)~!yS{)(vJAIRQEHoyKtRIi2jV;zSvh_bF z%0-&+6gD&^)pY%|5ozc(kN>c2M=Y0q27yz6PG6-_OwdgQ@Wazb=b;Z3y>asx$jMWS z9;|IcQL7T~d(Kr=k1Sp&X+^5HOD*dgj!r{R3(bX1M(BrBULJPV2Q#l*sE3{!1nGa; z8)P;FOKU_jRtPQ`Mk3AF?rU0sne&D@QHV#15`T4=rikLK2!34X7U|^`{C38Xeqh0m zyTG)v$Dw|88k7|r-B1ia)4iQJ7hMZXN7S%d!r#+5UrHN;l0L(4m5^<`ndZsnH>BSf z4ZO>&%uC=@4ipTPo(%SOy!AYURkf}ZPQS|@;ySd;WjzrwNeUWW79|%hx2m95Z(Iz^ zGa0L8(;idBV$w1t{o0+8w0e^OXA&mUyVe#x=Pooy{tefoOin0VN#2L*-bny?J$0zl zAGyoh*USBRC9;h8+#B$fygC#3`_3q#=Yb)&!<lD&=VmsV zmT(vG(5`3)jp2iO7OG+z#z!74bugMry7mhj+>k=6Z}Hi2{Hy9!fd10C>-kQHP$Bh9 zYlIV@rmG?WX<$posm2+VEcBGUkqq^yk&A8**|gnr$~LoMFl3*wHx1bbn@-Q+=&lMq z|HIA$DA==(nskbTJo>1*k8r<_`ELgOVx4-t%y?(b@;;_hs4QRNWI!LR_4wCet)nwd zrfVdTK_sJ2KLCa%3mM#(qdUMd6iEPyHbq(HUr@$5WL21d4CbWD_46I~zv#n=edv2e zBoju6{e*9aT||7~ES+>|y*fka(8vy)(G%D4nJGd1Fq`seZ&S_Uaa`8R4T+}>yg||~ zPVCg&AGM%zluubBx;To#M`RL~Nh^JBhsp&VM%-B2kHYszMn_I|_cj@gZJb+HrjV1A z^GB53emH^qo_E!|TLVf!oNe37oYnNB?9(zv@ai4bMa*E0d#LZVkdqWgI9nMegc<=`OxZUoL z=qeYYwPKIB$h6(@u#F_1nvVjd*Wc221b$g+FE;uubNU+|yOa)%=D#Ku@HDNXb;AI- zK0;yfr!44no-hiVbqMa&IgcICnvTy)BD4&vD2u2>F`;B4=S88-cHLThR2&H}UQ=mY z{{(wJWr^I1XwY%KUOc^{x_M>8}F1yK1GWrvd{G;au{>f&6&1W;DB-*4S zb8T8Vtu@XSg23_m$KSITWqq+YscVM^%F~Jxi!H8@H=BR{oGsNUU-F&Kz68|w73Qla zM%A*`z<@rhxPDAA;7IoPBHGuBG)@g~rVn2plK_?Nf+ zOi1Ja%2s_!jx_AbQY#MTw+!s$+@rSl>tcE*$k|a^xf*DC#^`QNu{!g$JETq96&u-- zk7|T{u_?Nr0tVFEEybM&)P1b#Gcz~mW(f;M*FUy7sMHCQj5#*MK~xi0((3qOOOdUU zd8k<@pNyRB6nP*=I>=~6l2}LwlexM$*VALi>c?wz<|MX|&CPI>fd(*Vg{D+WVUtR9 zN;qAXc=mEL@K(OGy4|x?^Be&xvLM?g`AC=5++fRnVbvvhiYzoNzVB8fXR231WA*U) ziJH*ked#b-vxgc0`WnO0i+S6JWAVM3gB@!vtcHx%vW6eoVbVfvntEbDF zfa&IkW zIgiuPT*@XCTd{$Ghg*#@3QZcpvAh|Oqcwn>P&7OaC~hV-KNmxK&>rG+ z$I|nnL>Lg`oR;`Q>*fNS(zaG9{4`7HsLgIi`>Zn^rmz!hacV{%Ht`)L{RtHNlWtI~!R-U0h6- zbxs|@*qm1%Cw_1D$z7z2CBw|woN!4>rLTxNqWb5dD082R$;I(~Dx38pT=zJFvlwA5 z<3R)1#RybBH7!;myf|by1QPOYokFY=6xNOYLr-)3@_Q5PvE|)@t}iga(->zf$I`@A zm>TOuZ2ehv&CvQ%yr*@`Gg&WxnAomLgii!yI5^RQ}C@+|f>HJDNMaCde%qGj^P1U2Rz2O&; zAJx9}dKoHu`~EVH!Ky8=d~|)03*79_wRL7p(;cYCyJy~HD1c<``{iuN@T`eeYL7+a zEQ6_tOk~Vbt|-5RXbv4L(dkZj*4e#gR@7?l1oksAI6Ww|2pn07RvuFq!z` z>-Mhx5=@=gtf;m=pjF}GxYf3E|4Nm6PgXHMzl6(EJOX-MlZjT$s?))0TOvE}=45MP zlATEE)0KR?`7*{p>$VNC?OI+!53v|Ob{@CO;NXNYGM^6ZY0gf*bA5$uYEHJ2Zdzrt zvQ}LQ2GH7mr3WM7B3>vACYWIi%EsVLhQ=JZA)ERjdDTM+r84ngh_Z- zg1+^e6`u3MFo862gMc)NHmEfEj55lI-6TbLOiuNO^fphWd5 zFMyvYE;2ykXInsuAZn_OJk(b8ToY!^j~KXm9~R%vX#N7Eeg2W#Zp#{e%enyGJk!M< zx;-T_Cv5)w=FIT2IF@a{$^Kn^bL*A0BoKviIXn9$qa{DRRlTeDNBFjCe0?{%SzY6f zHDo&qXm>}8Xp@h&k(&|XflYE3M)XQ1^j~*iW#H2d5sGAJ=1AC0?oFaX!(3u(F^+@- zGXh;h8-=>t3ArTPjlYY8Z+7@ozS`#fgu2G&#`=5;KYZ&AU3l_D8;i1v?-rk*o5+5; zMsciz{s7lvpvw_8do&gIqdw_VDwXn%r81=&-`J35#inLxN)8f>29{wheSbox2CvoU zC>nvfJJe78PAG8Lt6=e&4~2BWB-_?_E{uSpa zr4q$;+SwCwGcC?KKwc#Fs+h+giENp|7jpJ+X#Q`*jC|M+^oT3@<>PqB5r!`Mak18F z*jIp9E6ZU+yPHpX4%kwl*-Go~Robm?Yq5rJ$MpExc34*S)0Lz!yb1I3>wn&+i?n3J zZTVAnQI^OSeEpy#H-wKqek?{dS*4uCYT!#~Hb4465V9L01;g`%3=^H!%A+v?XK7nn zYb*70fNDs0j@!aD#NdQzi52;~PZG)kMh3|JIkH-P#@)TVe-D-^VmrpEjR3^+XnB%H_&m8?snl{t66kubEmZ-MRKms`TkA;bNTE=lfB z%RD~F6S)0`h{-0>Ff}N3uQG2WpVfXNVRJ9GHzB_nEqrFtOUNshrv1V5% zP}Vxx39+ucJmmX?GLsyh?Y=VK$hj0oXSzhnvjRo^Q6iE3t0E?UT;}c35uO6|uHbtM zW^2#@@|gH|rjvUub)RMn%LlnuL56P{7H)~$&yw>2c&#S^a9g#jIR_?5|y5)k+ zxzEPqe%cEl7h77XD(G#qs6%@L+c*Y@m2+zP&F%#*7b66FMaL*9n^DP^$$ur8V7&S_ z8m`&zvya~3u*gjmMO8?(t<;j!W~XI6ESB&fyB?-(DzkDVVPI6dJL1U+LWaqF_A@kh z4oR$5aTe#EzW*V|XyXOdOWjq&a}JYiR|ToOegm7~Vi8Pidno<`PNru0;nY@8i`iSE z#*cm=ptcn`uI4_o{7i=(m8`G#uqdA>g8xf8VykJ4m+tx87m#DDii*vcwH*cNuOv8u zs9GuL=|&DqFS)HXKS=uTKlJ+(JFP7xypCQ%<_9h?yoT&0YA6DjTupYfyRMJ@g^_NH4p=h$8Hjf!G_qYRqW_88(-GvR4tNeM`JICc*d>th2-2EUISW zrVGy#&J1{Sv<}~z%I)aR1%^(;N~IcI=)2j`_2LqhE(yH{T{M){p!JDHjjgV}9$7BB zY&*=-c;b_(ZfQ25i*Z1r!)Haz(Huue6Q^|(NE4eDc9xvBA(bw+(tS&Fp#{KeoAdE8 zSyGsQT4N5d6g=0-~XctVZ=-5oifHzaBn%!QQxD2 z=DFC69UsHzuB=Txr&pXZKU;8IZ<}loh+1e?*cGL!QGVfK^CBui*j#T$trHr7nU(vI zB}`WP*9ms!>u{6?s8Ma$+F0+mG`bTD877X-cWhTiOV=tjP(LRtp|$mtP9BQl+1&{G zimWa=k2~4a^LD8S4U9grL7f^*wbydlo$g%qJ=8a8pE`fiZ_j?RQLV8#TGS_=)G-NQJWT+YPofcSNBsiP_tW`s5KZ^Ts4qPXx3 z2A{tpiJWr3KxHl)`oaz?jC)~(!v{Awk+Exj5L8(AHMV@rlP3*7Lm)sPj5#^@5+To1 zxL%oPowWAEbgZSrrUJ33`*5kvLGAU8^2Tf!rg77eIhSp$FL)~}qeGAfXtl*r&eGK7 zctZ(#xz3OGW^}Cd_U>*^Qa)%fq_ODj?X6{GI_njo9kW0mqiZV<%gew~cEbzcL--UI zi*)%dl5L%MlU|GhNV*U=N22*1mMG*?{awDL0-b`O~ZDY3nOMZc-s9TIt`1xX<&5e`A|52bWW zHsAWpy3Gz@MB18|_Yxf<8U>1-(guwefZPB^>w9|ELUne&rji@rr>arTW;H1U?R^8s*1xPM?8)~(mbi|)_$moH`KmFY^%=>yF-kVWTh?>Mq{b1^@fOIDzj zDCj)ylvy<89R&OASY-Jh81Nk(Pt2=_R4ys6j(_r#%o9y5^eiM{jwj(jmUP^?P6mrs=9LWJh4iN+_>GgMe)ek*IKI~ zZEeK?Js)u8O)kctD+7`mm06m`SVxO?cYKOOzHX}ee7Pk3oXxs~=%jzd(NxPip=B3+ zN3z7~#z!Qek@T!yIcD8Pk@j)&yKuwaL-2Gx*_@lE{tW+gdzQD!7P~xA6=`Z+0fA~OatvC3#2 z^rC&V($j8_XqtQjQOUi1l*+x#3nG|B=|%=chBN&6)>jE$x#`L5;hq;O8Lwb48(KXh zik-h=SE^kf8yd=XvMBQDEX)}J%1pdy8~14KO%^2)0lGzg7>?|-9HH>rdPVKbNV}p3 z5_!xtjN@Dg&o9E3b-TR4f}VaxTM4e9nNGpr1dCQnRGka7oI1hP?fZ{0sz_Ra$#8DK zDZ;r!`-nvUaw8LHSEd=Kt*PTAA4FIkaSA5)QFU7kEL-*{Tc>aJ_jHf(xstK8vp+Y- z5f9M>D>K-I>=t`PV-%}7Nju{HrBNJqL&p9R{StE>{TUK z{R&?aJwV98d8fQLdg&iAsp!5;Xpl!XP zRa`Y#3z$n9V;A^6k)lQxfoor#dY3$*2-_|A!f*+{VC1y8$E@L9z=_$<;bH4UiMflMU5#+ zig@x=Y|`7hSfQ4r0V)u7jJN)43+7EqfhdBtp@_q>zEOCF`Qs=Qumuj|g99Ak7KOYfs9a%U(V6$f~eIa75gDM?IK|IjN{lkBzIUp$qBg0<%o+PXXAg>ce&AsOo zfg3Y}HVI`^znF$m`iA8X7r9Kv_tePKn&Qc|ghny@H75M~1kuKphra8|U@ec{3;II) z^zykCEO9Hr0%3s(N>h?3V1d(rOrGs7R6chimD~j-lB?6J$&t0tmA>j`oxi6fG2mK9 z8{M#_vJG82F`!IB;pQs|SD9qHI$+_@xpswAjc{A*rp`EgReeiMJ5okp7AA}^-Dob- zQs=k*Ov#qCf49ZNx6mCoz8T2pc=gEZa{<|ao*M=Wl0YTFelwmYr?6eM8X?95KIQO>V7~=_QdjZAv_z?uT8v2>S)7c2Xb_A< z)cdE_33r98AZZ}_sjOQX_RWz@$q*u-hnDEhW^9E&J?ZYkysss?f`fCdDy?N0#>_xIK*8twCDQ@F0SbJMnS|{rp<&qxv^v0>0`+!{Ri}gXpC^bSsVBY zDte(9oKKEP2`?5%rL!n{`Ksf%*$*q6!~FfaBPB$sXZKVD=Ikw#pVa#b-MMMLQ0rsQ z$eLsb2CJmx5;UTwjjcY1SfkefA#Utcivx%$&6~SxOY6GH;@w2@@PdO)Atm4rjU*+fg@v#k>xs=>+gPf#w%!3I5GuEhv5?K;s3wxFh*6vuO zeQ{Z$o5q2ZbTuEki#9xf@gd#hQenkQ`^F4gl*8c9Jn>7eCb1r4Ymqb#a@xTyd)$$v z<%h-h9C!zOGSZ~JrnsG|_v;rRL8q$=&np>$p!aKZY14f2D$_5BSqt|PT6xoQ9hWE$ zRo})?S+y!8`go32ZYcitv2LVn-*)mR&3meM=syy+pVvR=dH zk3}(DW2tbUft%y0E5VC-kh#0RD=N9g!6_L$jiytKlXm&%Ci@=up^0j6uL7N|j zeIQ?<6KOx))n9J2f=2YyPWLn1d9Sm;d<2c##0XklclBy*KFY~YiKTw_1WP2v29}ld z)~SV6arjjZFxFs|Sx>_@4(mQFBTmO#>Gc5*p^V&31tQ?*?$n)LS)U-Fbh@0uC| za^@+2h|yZrs*ne$pi_sT02sn|rKR((W;GR^%r&sIHJQR0)K)kjaWo&$1baUj{1gkv zQ(!4QlljJW1oTgc9L9j0=X0az;S2V;A|T)LXjskA>iwQ8b^797-DuC`{B`p^SFFFI zP3;e0^$_|q{nuYT2W2qC8gKR-5WU30nmnMrqw%#WNI&G!sh;R+7TEeqb#G@a*(ht* zfD3LnR%~ee(_p7BvLYGBIC+R4i zTM<(=PuuH^4La*!y%B#c@SQpP^x=>ZHgxSWHXoZUYv%Jdx>`Fyh?=%H0Hq%vgS~|j z)0#W_$sYvrMe7IJk*s`1YbiJukkm;STMMDSH9?x}Sd;Bsf37)yVi#H`iINW{x6${2 zM@08F72#TXj=RYh5z}P36NeIwz*|W(lpMQ3 zv2GR-Im@=8$mIFC=+E-Ol6tq6Vb0*_RMBB$CUN+AE^QiN16v-DCRu*#MblE!Q zn&0j6k4l3dB@R`~NV_qt!}(Ubnli(0aX7aSpK4KLE_I9ITs+~@!^zfm*;Wn5~W#%%J)JkQSR)pj&{*gH1rqC9GLc8687B zkc~0%$iOu5Fzn>*=AXmVNIHRxXA0jAWh%!}TQ{l|iVnId+RV?4gbNn3UbEzX$d)#jjfe7-QJ@9;|2u#_9yG zC!pgBHDXhqu34tQgmoe3F^8X-IY8^W{+OOT;g)5Uo3u8+&!a^cnI&7vKE8_5S(b5v zPPFNvLDzh7?L+4hM^s1{+D@8R&e7$wckKT0L#&r!Wm>-@Z~~gdZpQDk&-$Hy=w6W^ zeE%n2_$~P)l#$wv^S9qW*s%%IspU~K(WD%;92$cde)oJG(A9xYa?5EOYgOUakj_Pv z-4s8+}1kOw&L~SffB>(3VA8^7$-A zS5v+Acb*foC1Uc-Nf>+Eqp_S;tsXL3#glnOT(4+};MW`Ca^#qHH@`AjS&cFHn#w*Z zmy_gdOjIKkv~FSN@AC|r-VH%A$Ln!YwDbs^b~#UpKuX!W+~&*VVCzFHe433cQrrId za5ok~9Gqm%-i;AS7e=M4T(2V<@6W$1l@Howxt8$&+)Fw=`T)!i(SETbzvTgD7c zo4Ypws7O9<%#U)*i0W=VAD8f}QJz~04c&zDkeymFM@5{ew3ma!QIkKOeTqk@#HtSC z0+n!6&dOBdwnkE8K{e7wQXhY{WiH?{^P*)O4O1HSuEyz@*O;2YK8pOBGVvR(lp+T! z$gvt-$`dV~mg&&8s|Q49iJBKtt!6R=LAk;*ly{DIfb{vJNWWakHzMOZfu5tI6PBXT z7?Qwx1hN8OVC;w{+?vPGW@Dc&)TeQ(TLq?&GF%E+xS!l1<85!~6f zEyOk5tW830CstKDUk8R5SugL>&i!rktw{OcHy!PLJ;}iNhZ61+6P6iQOUUhfE@W4z2G%{>`=ac8 zmmkBSLjUk%hef66oL3s(q9AT)-zuL-lTQnQoCYtEKL~Bi1G`03C@9FhH*d(yeD)(^ zf5&Gym3{ueGz_l+d>(jWon)(|6ujZDX+2OdW1V$Gc};MW!!6b;5Oc5*uw%%b?Aa7Q z2%FZN(px@$x&UhPsYOW$uOMHe*vPeZw3DO5K{0R}aG(L}CG4*xpA#4@Lb_K&$^zvB7}) z2-w(YLRgVtc*`U_}fWaMk1-lEYe(n9`3_OodhpQ8SyNjgi!o(^{h}(-6};8xe-U z%RSH`dPl>FmDHrxi95DrJMhNlZwFg9LPqudDFt4UB)h^YbY=hW`eQSAdkdS(8(D2j zzN>u(AsD>Xvm49$C}YzecR;kMfQyBKvc9Q)CPnfUp72V?6?Z?)ort&esUj|#$~`rA zvJ7}T`YfD^zn@NM>;m6f66}i_RXN|bpy%M7s+^;=>Cd+lQR@j}x%A!6Hx*sP1vH9Y z%YD%Isqc?h*m4v_mum`4puTfpuskRo2bPOd*#aPg6k?RbwX14R7z+@LB`=C=S8r^6 ztD5SN##Kz9gFG3B^&|mn+l$^3$Z8y`gHaMbM0zNK6Yk;iF37_5AXC$YXoZR>!V~dT z%E~>4>uA%4Ipi9Jop{1Q4@ih(id2U)UttWm0!w1ch7>lKW#+?%du_VBJJ?UFGAz;} zpsre#98$}JeS>DvyUQE(5Y@qULL+ITTAV~gt8ab6twi8^tp{7aW$F8(5!c$e>g5yq zzSy^LKZF{Mv8iDIk4U%L86lP_Wi)MoVJO}jFqtx;5!3VxIQhHHIskG-Mf! zoII@s<5(8JwtT{zkWJjo9&ll6=#~{)96}7RIpA3KgsUW>;K7{6+hwV4ZjZj};}cgg zMc{#e2W;v=-Ht_F3pvmGCorPP`@0;>xj$<53}HBiwt6OXDP-Q=yhz$TnR;z%* zS_O0@N`9dCqT~k|ncfAhj{iPu6;deWNU_0K1zl@*z-ePx)G9<)Z*x{3@j763i$X6{ zdo0_uN+G5pT0vIe$kJ*8Ip~x16mUT{2>MOfnpWum%R+eJ&T5uWS4 zbadr#ryyU|s(mpVh=irmA<(h*__uApPMoI;iOaJ_rg(x}MaH^yY6W$v6&9sd^6Nwf zDN&Nul~@BZDbal8hk0tnMJMah9L>W}TA+o9=NcjqD@9c;IkY~~BF|xk@;oawB*C_1 zSR0RVIP$PitO$(_2O420t>k^;=e1FC40D{+CXp!T7bHjHG7u|Ur!RaYYs z+}rGvCmT|JMZ0N#9d&#g_<;Q&@^~{L3=&tpnuloFg^C$#3yT8sGTjQdEwaFt*&OF@ z5qwb5c$)KB)-|#nkT!+QBoskOw&M^_n>bnnbxUtzQ&}OC0+bpVGRPC~tw5&&b4!Y& zkWST>gLHVVO9*!0j)Jh`BUbF>-sh2r#CInBVSCrWR)M)eZ#^A2=deZ}Zbt9`Pl#}j z$72~P6w0afvCMv#u$`NI2bIfx@L8ugBgw!K)vg#Xdkm$}<2cQ#4-7(1cb~i%hP(aD}9x0}s~RaxMf)<);Cz zxO*oy>e_?O87KuHB(2Pe&Fg=;bHO_grTa6>==HqKIPHV##oQ;L*pMw}PsEDCEg{V# zZk{y+`B}rNOBYpA-k~e)^Xz2_#nm&wPr&MuQvIfZ2TV#? zoqTO)M3qZk#t9SFEy);R9%SORU}WO8t7<#P3QW57OJke%od1?YT!22 zL3!NHokO2ukzquvZw}p2H7Ri0MRX9&M#JhT&r#X@f>?E8#db*SIBtx#NQEYYU#J{y zdL}6Vwu=NHUDu-(rK=hp+xJ;>^!putMwUZ9(RKIF*eb|HTBur;M1rd`^d&6CYr~6& ztL4l{+qiJk5Yx#zj*>fE%TYq_2@c!Oc(uUt4qj&Q)SQ9+N+_Y~)Tna@q9cLpoDG4B zu_btIB+^(KAaopDU|X7!k@mw~HO@8%kPtgI(Zu2tNHoQT&G3)k@E2I8a4hRb1mtBB z#}4xgljb>{fB{8WonTSclqmS%MTwngEIbH4T0d3K3KgHl;K&?;}wu`c2mf+Ai&_X9xxsDlR2;KmB!zk9usQ z88R$7hnC=q{yN3j5F2?K3a}p<(tb~4)z#y!&RI{z(k8MoxMIq(+XAAzwzLUM-=bnu z*BVL+Tn-`hWQf^r*5w#m)Jm&{D6rP2_d=|`W@U=zS&*(9ab;SrvB^17)n}n7fWx}2 z2i0hXhT`!eG_&?BTv6FpqeArF4>^jabk8qyVq29?=y0bYUv3Ewg*D*-JpeAR=a(JM zGT)A18sQ4-kja&k_N@@#o5Nwl7>u*!*iaUu@$DixYK8CF_(3M+&Kbgf*$F6P^)w5! z(_ZfOS8`=c5Vc|U9mcWi^gywVz|#=SC^&8bm7%$;U?Aa%>hOTFG-; ztE|Hc3Mq&0Uk;IM+{_h$xSU6ndav=W7|?7=+-Tx&pP%EZv@{J>NGI`OpEmh{i_&1& za331t2F>kmY6eTvdiOjba4z@?6C#NsSkno`$hN|Su;>l_J*aZCrK+F)#g7 z&+k%GG5{);#h~xy1}gOBvIlCM4#44dfBK@h>=WNKnH=nb!?_wr3iMQb7S*Uq{?VHy zv92hm1qVws8;6_1*oKM*%uNTJ!aySRfRA`J-%Ifys=F8TVy+ASDC6wgl|}T83pU}dx6a}3d|3rueqH=c7bcY2I)~=Wp+p35`-`$*a?cu`JmlyVVI+drSWIe zOuQkCix#4p1pw=tM%&^9^3jXNhO5Cr(=0MY3!{ypRbZ21%)hLVz zc@4=c3>YH03IpaG?X4;UrXh{38*u&j?jBlM!9r&XWamU+D)#S=#<)0aTG13&F`>G6 zqPK=c@q|^^y0-NR-dbfjuF>7W+8p$|#|h5Uk%uISM4f<(D4`+j5hcrJIPYZH3{TFBvKgLYlywrf4??_giSCk;-L8QHqc zx_jKpn9xIot&EGi0XLJx0rXfw^heKX(suHm=Z>I--U#&FAa0T%V3oqTV5@afBIb0T#g8hDn}G#EE*E_5=YGE#o$15i9yy)+y+_i zroPxylqf>)-2+Gzg&q$LJ8h8l{&TisX*zj_eyE0^AkzZC5_Y;gGKqjeqiDiwd9`qV zC4XQ8R@l`*h+2G3aNB%WOOY||_x$^lD&V!vI`4S*#p<*vJ`3f$8d9r42CF{ zt3+ea+HStm_x8?ZY3#lU-H0sQw3Av!wc?<5TP36V&>Mi_ZT5~rz!DC{s3T);6)I^m z1DIFd(K73lHdJpah^Y7+$l5qzV;$QXRHaqDu5eFtafJ4kbc!p>2Eskf#Z&ON%H(_* zBP;wvCDIZ)jSIi*WrvU)s&)2DkUA#UwjlPz;`1(Zf6x`t#6v^ZwYA3gmyiMLV1E;k zSgE~&zS_L@v;!OR6{a2K(~hPQR40QLOgU61BPK7l&KWy!HpcQE88ST$+Lrd$shszI z@_@WD1XYI33EQIQ)4m4U8nUo4x=gFra*(mgoU9DXt_tm}B-iXglWRadHx%aui6Gkk zeXQ0~&U67SF1vWCSJfR#52YR>I$NWZ$D87)zER#H*x+lNqXjNLDQwcg06K%1K_uSW z6|^(Ott}c&Fa*#08xZr}2}bq$i}Z1uV{C7&b-uJWL%NQ(sa0XG;r;~PHAt$3X%>&XinK0zLUn3KCHfXR4_(j_pZln;JeAin?&M z9k2&;ixq0i@-Vg~jn0_>xAe3f6jfj`w=7gt?)yGQAFFo#3Pz_TWfjGbdy;1J%6fb7 zc2EQzb7U;egb->QX?_xYn+DdMBZ#5+a5h~>?^;cxsTqf^{H1}giPxHrV5_Nl9}Ge1 zv80fnid{HRf3m*N(L;IfT4@8eHI_6qd3QDijg0%va$ma2O2Mx{cJ&{qEfu1UaTr9Y zE^kDsE@CT_P;#<%@f2i|%+`P`)dlt;5Dq)^>7Rb@V%bSoWO*RCoAcMVbAmbN5B2>( zxlyJoM(er45l&Pe;(fxg(I!Za*jIoqRxSPETcNUm>BSWnRtfia+SVO~TTGC;NC@7P zZi3Va3nE=k=8h1uJwfw~7c7&3KZktq$Sx*1BjfydGRgJZBE8U&R zh&J9z3|Mi0FjuB(FHdnrihh$(MzKi8D8_rK8{@zxZ03?U7Uh!eD)(*3A$1Y`EwZAE zWz~G9AdRBL42YrpbZqq*L@ZK#XM5mh03BPl0Hqsn&lgwwIh2$+t8;3%Bw%V4qHz}t zd1L4BT&RO(50qy}kUaqr@5?bH6}Mt(H9_k=Q~=4?U|%VmT6D%nU%E!IGy2-R*yw=N zLA1`5r@vhB;dYbO?qIs$90yz4+g(;yxysc3gxXL&F~yT)eW22*A;??`%eiqoUQWLw z956#istg^;s?n_Rq7+>;PL!ep1eVb~5rloy(pDv-d(!F`?k!DcecFTifBLXeCUS1E zVi|*Tjuw3=o!1sM-60N82)fxgA1f4WF=%UIK5^~J?EsleceDOFGM9)4(&gw2BD@Vy zK0_O{B+u3}41xT9puE4xIs{o-D;}t1G9g^>?Dv+DV_l@o+j6e1%D!DKZw| zB5o^&dm}g@d!*vh={37>`9)%@&BbCX$YDbOHl-VK*`YvYmw>yydJmgg@I)^3qZsbgrNcyvuqpTsuYHDC_d^H9@A4^Q{>XA zAv$ZzA)}0^En!IS+2$i+-+9K63!Gw@^q#`)(yo7`axkk(HbpNg2Qw6fD)gP1%Gi&| zMNMT1C($=p{0+b58#g&8JaA(@0J3IiCnie~AGlGKF$8{Sk7A0sge`|@m{Bpz^=jy* z^75wd?W7ta9Vnvehv--}M3<){Q)7t4Mm0oxs8(slSj)vj{JZSN2L={p|EqDRn2Hx~ z7xK4l6K8V$bVMN# z3fk0P!?aa#`j(vMUPjO?c17U{ARc%xFhd?zj(AtNx4||BL5qIaQiKt6q{J(MAa<%D zLLP&;O3V;K=@4tM!nJbr$xNq^c7mDE?BEf}k$7X*#kMs4KgKz1G;l2dO= z&M8~;HA{@7<$-{JrOdLpmaSC3<}{ic5hqfQCaj5MIOS;4n#YCBrBU#8(nv1fWb?4? zJ|4=hQ>2c~4Li`OpW6C3Ns$KFGf?08a8nKC)f!#vg4$SrpwJh3ERN;+bc7MsZ5{Dy zM#V`UC1s9~ZYccy2uWF4w%&-{77X;zsRi2+d!C;FS=j-%4f49d-G<_q71}af!rL@( zQH@Tn7Z$kU6o!%!jONB9(P-tGO6wbuZ{7&HV$z^%!r@PjaH%w3ri!I+*Vd@94fhqV z(c5C(jJ#s}wI-#7&z40 zz@$icV>oDUv>14(50om@*tLjK9Z`4A01xm$s(=R)alf5`4@5$qZ&p%+|39k`$ZA-#wz$x?>CKaLQox!OvOknKy=&8xudY;CWsSZg5k! z0_7cophe@BJDSc%d#kB7+oE{Ua>FA!@6qFCBPL%anj5fsAP$}J{^k%{oiJF>8F7Pg zQizVs#yD-62ytjI&JX}NZj3YPTG(4xKT&`kc@uFneP+wlk$u5H4;_X>a(58sUDAZb z*u)Z{i{0BO8=m*ez%Dju{I@q_89do}k6Du5-&MH3?ep?}ULj_slC+eQqT0KuyTB3M zd{g%gibsp0(a=T584QYje61*LbpeM`1L2Mk!u~#MdwX4Qv&xqT)gql#F}DXNI-+(c zJfwEyWyjRc3Ja|y07v#TUMNb4Pi-F;U?gWl$Tn(D5 z^i-d8v=U*d*{zgxz0`$au(7;RGx>w2E)4hME+6$aA}F==_0fl04?C35;tx`B93wLo zlV;X4rcGTf@=&C-Hekw67{ts{{PK}{54ZV9L8k@bO;a@!8*e!ZL|qc5#x8bv5(9z( zI|!i3v;~1p$)S$is*;;oKn>Xuyv?osX}q0dv_m$fa63me(5k3Gt`%TmKn$fhi~c(L zLfKZmv>6yFmd}k*4c7uSnp@i8hdSv(Jrb`t4s52=VTm|$X}?ji1c?!Kn|KJV!L6>E z!$etTiAb1P5|(s;&u{iUWR?PWS!O8&C(A5FLk}Xe1Q22zn1UekKXFTCS!juZDIjRZ zxh=2TFf0P+nFcN%c=dT%4RH)DTtREf`zZC-(dUKYJ&NJ=%D@$7@6_*J68Th~J+bx9 zRLDHBI5cgotY#xqH04N!_3YLC^-a5&V)Ih$Mk%ut2yg0o8gaGHw)*py3PcVF%16z) zB?=X?PQ-4rv6NA0NUl)I9W*^Afh}35GDTh6bifwkEt3GNOOvr_dv-#%(`02Wu5457 z-vqsGsVk!`qBzwM8)()SeD{<1|Ji%j9!ZkpTJZb+iqfMAafcmrd%2k}=MtBcC~$$5 zuAtSI1qMqrXKKiTdb(%3d&Db{^WWDaBeE(ZGb=O9ACWb4)ugC?L}s~1c(|J#J9ds* zgekZ~t0h9)%_2M!OATB=(buAM&Xa_07U={wYY^vAg(S%BB%hhn3Ln_^a9rwcVIPKw zfY*CuY8y7@l=;m~b37~#zPFsl*Ymk=!&`Fs%N$}~{`p~)H z!{&kyne(+k4O((KS81`C^PTsKeX*7U^LKNC?D+C>kXSJqr zOjbCi#VW8;85!g!?WnOyh>N8G~@#T*#bYBG9H=LC*SJyP0rGfWxhL27SNjCEQV;eB5zX0SZA$L__SYUT#PnGB=?AL61ZA{`t_m!=r6O%bWQ zW4sTj1;)b|>07l2c>bnh-z4gj#wglTmRIGGtv*a%&n?dMLomsXg_^YN!F?Q#)Vr@o z0~hkcfnCs#m>2sY^PxXPFF~9Zaww>A*Z&2+9$f(0sz-MK(*)IGUru}6WtSlKqK761 z;rgjbwO$-_svlS|lKKg92dmoJspU;B#QLdG0bQ9p%}Fq#VD-uGU5~I)+>IlfXC}wg z6n$`TI$eNYta4q^ryV;hL3S#xU<#Dy+|i_nK~0jLk+0v7Pmn?#-N zOO6jrAeS7vm*3EVDWzE|*qk6&!i`ScG4oat-5d!D-Kj&P#RD<6Xzb=v=cvA@Le9kU zZXKYu8kE1sioLOt^n$sJNaMyG?7Zk;?~@Yd)xbeQbs#~$O-0U?g1SUXX+TY=TpLFu zpD?YRQv-n}wXRlZ7w7F(9{q{9*zyTNYUV=$zYy_JIM%X54yp zQ1r%r>@kQbd?}*j!@EJHQ>&HDJ3Tv2hS%Tv>y=I^t#p1C`#d`yGzYcU2_e_QTJ}A= zvlK2W3#$@hPO30aG|AzWSg=#L>_>&MS($LW%d8F6C+>2HME?B5zG-<*MB0{l9ze1}F+pZk2e% zAMJTg5zhP80)V5kks5a0VL9;0NF5^b@m1S5YI|w+h>>kNWTFd(;MkDnNrV8C?mAJi z=9~2l7#LM_(y(XpbN-~~Iv({VXD{M)l98`DyxK_IFM>_}c579RUX#5l)A95BqCQ1f zn*8^TxtKDWj8LYZoUDh5@YtuIQhWm-%7d3 zTq$_Y@g{$+Bbk7 zG7(ZwU-s(>P=3|vSacUt6Lti2tnOccpoGM#C$ub=$*Y@8C26o@ER}-)*&KR0OM$9N z6aI)jisL-Z1#7(KKI0qBB_BH!o)Q~PMznH;%O_OggTXQ>pGXM(|LSSMnYeA zgGPUI!}I512x_|iJbc>8%)@7WJ|Iac#3>?5p;UsT{=l*naL&ptm1-B)R+{rI;3P67 z;Hf-5OsP0LLJB;GM_Ar1)r$ur9E`VCU|8VXDun^yPPPNLOXeYzW%KOv=#t4J6lrjO zI8pho+!Z|}MX=dCU4fkj%oEsk`ic%pHG+}>~g2M9ZiQI2t(ebartg4=Gi zBrpkdueFW5C?M6*bdVXurXZKuUztul8|=hbV5(h<`Nvivcoj!6!6~GP>wYpOU!|q6 z{d;VaN%aO_W`1IcrEcn~MkMOX@Gp;1lxD7+i+Ot>oL_}&Q=mE9v~t~WDyJG?O1RS~sq!Kux}E1V zCaiZA@RJk|%4cvpCq{w@+TfV-0!9aD4BXCbohXGSv*&Uw>V|IXiG{Vn?2ApV)~kfK zbVON&8lgpGt!84xb@j^z#8ny%s85LN1-)yrEN5^JIA=2q$L$sno9OsbM-VAOJjGvP zjg|Tq*&{?dW2mk*w2w`OtvRziu~oMw`m#L>QYojUC%Z?evMf*WhuBOaVu2?ZNMbDv z1jS*pna-UWKHf!J`UffLT&E;0mUZij=F{lR%|#Wqv{5OQbI3k(d6aDwz#Ya$x+(1bMiru0Z%NR5xo*y>^h_KK?+gt!qG@lBFQ)rgK*ABzc zr-A_#$cGPc;unDrDJagpliR0kb&wp9%p47O=i4!QrZ0+`<~TvikrXZmvdl2@ z1Y~(7SHl|hhuR%AfYjF$R(8WI?MOPgCFRpQCXTWG99-v(zU0|3-VApU#fV&MEk1rv z=`yXAU_D`(HWbtdIYd5&G1#3sF|X@RsE1tuV!~=^(d~pdh@wQR#)+16<=8bNA+nP@ zoa$sbZqYf*dPCoSSG} z&<&_~dFEXh2$e55jah|ReXL%h$1fnXtF?orqY7>42?o;hBS?hoixcOxsPn}ZynED zXyZ&TUuiLOq>>psVK_#~tU+I?z06Z5|1;({t^t=yusCGs=2$gq;z_D^tcp@vvU+rr zfj%=#W33izV}{W+y1K%OO=>p{Q7OUvpX0pU?fnr$OHe<>Q~rzwzhBWBzRz{|oLT5t ziQ2dco2S@4$u#e{$D|jlBE4LoVO&QWBF#qgMS890eUrL2Eo;^T4+}RK^RzA{862iyv@aEMZiS$zBCoZ$6lU9}bG3C8zH2%ngIH^liN2>Mo1sgT59I__ zjV_UIGttA%1g(s}w6TW|a3O!m11mB3)PKy^-4>jfQgT5`xZZ<%a`4gk5~O_dmTI$) zl+QCBXsg%MMjbQO>JAq3!T8G#i9TDb4Wy6|>Zexhk9|RjrfOuSF%!T^Ytj-`Pd~7 zy+=o!4hh4fJ+|)iU^fj%Q?s@X8KSbLxy?o%Au#WdWFab9Dn$_oKuk9GD#Z$0+(R_G zRAMvFN!dV-?u}GIU&Vs(JhIMWf+KHrs`9D3t`1+gyMf^f2j3Mg(T@{F{~|bO5mxAw zeCLOf5F404Q}Gk!MTZ-`+79=x_Ij2aB13Ed%!n&S6VlVOqRTl?<0gU

~Ck(=S-I z%JXv!DHgAX+1F}b>qM?s@cI2wWE zyb}~}%tvAqNa%2bJ?CB0o39cg#1QKFDj{wnI^DPrDR5N5dPYl-a*Qc-V(SD+(OS$c zmX`yi*W4_h6;>ORjo(wwvg#$9ND}#a6&J2+62)p>Zz=n*?Rnge!vuhG(t8AMd_wFr z{pctF6ClE=dN+2K+v(VHGE~V!OK?5z^(O_B z*>J)qe?d{#I2hJG;w14pA^&=m2*HOS=th;gabysc>p9)N8K!f~);i6QNUsZH(DfPm ztnSaDK-z1D39rLkD`v<(lu|v4;{Szk?Gdh6#)v`}B`ShX@*87BAHJ104j;Y+f!Rlm z0*K5$T_+6bW%S!2hrNX&`ZzWRGI`e1lOdR~Ow!;X6<1lgr?f{PCYYKQ!#Y1ITG$%( z^vVi=Bolxc!0*A(qTAuZ*w{>5V@uoJw-j)Grs!{wXd%mZA;)7$>>1nAElFOO0_!^5 zhvuEC%=PM#*BRFbJS`qo&c5ix|4gd#%+z&ljXfHvF&2_;qKXrOJlbkYj!s|K5q0!N z?%-imW|_^JET!|s@p<%Da5KM&_Ub;s`^|ZL3?T?}stnO0HP$&>}N+ z%^ff%5m)YDTlwq;!Z_REtsUDU&fHMxzc(79WqhB(g@z#bbcZbY$UbQ61*>;h$jEkf zK_QWxZDN|}B`93OsA@2iV0{>|fFD48o^6Er1f6}su{&Y$f)9P5FunIau+=4h)MA4Y z5u60ULoDa6cHQ44+FUxSAmM1&l#exGDhf4ZBRh{gM4bam?+|gF!}3c7KJqfyZ%p|h z!s6~AAU_@Pg6}ok3HbvYf+`D$vFqB7G3W`k!(lTX8v|z-0zp+d8R`05pWs#7m40cY z2&rPEhv0YYnnWRhmJ@rHp|*?B(~{704tQ|Pq1c}N$)8qP>i0jEe|#5dZ+~wPuP2u zm)p%0%tADh22Qm>{f|UoJI%7*aD)}g)wulFNu73EDf%R9jKzmXBs8ZGa!5g()C`X4 zMNl({DcY2l`HY!KHci4YSV|LuiBRqa*>y7~fjy8}?s46hZ-78Si@@}QScbbE*C-(1 zfrA&mBGNa~9}PbpV{`lIv|tGdnCJE_%C{7MkgN>_!Fs=Du=UN#EqssjZV|b<$7l)R z*{ja&MX{Q-K?}Zv-iWo4Vz513AQsUD4jWVDR&)Vq(jLX@dTjf)M^O-LFc(?(HuRkf z+bPOcN8>R4k1d zMs9Efm)lKnzly`j;nfPV5ghD*a3Xt6Our%~G$Wi?9Qa^N7K_J3{*Ltv#a~W#oZ<`l zF&Aqqqjlkp80})G0Yg2q(iaXN`;cruRxKK9*s6_jBmvC#HLr0`gU-S{oNq)h$>;SR z5+PTA(b-M!#o^Q|Addw3#hpX9$Mg#_*j^q0y?jbR*6=!4$et zxtCixp(ckL>Iyf`Mno;lcDj>TD`S~g4%T>6Ky{G*xhS=hv<_~tq+!e zZd@Rfy-FUbGu(lp4DN^?yu-wYc#y$r3Em0w5TNyvBRkAPnAOKp5q31@>@W`z0(t=g zxuemD*zVkPdWEDj>)ML>=m}KkhLz-VcEn8!%OT{PR2_;JPv#!MqoCIPlv7B${VB)m z)3*)IItzM(lRngtyIS7^m`SoXi*|Yo&jk?DSZj?@!|7UI$k`Yv;sG?+UN^PIB<28b zO`$k<$l;luD4zmX;RdRGu~phLUAO`_h|+hEVUKdLPkm%5h{SydsS@^sG}j;xJN^4| zv_gcm_thG(7SiggXci1`9;-Q@y1ZnmslQ+48JZ7vm?~&K6eOgA>U7z`#2O)D{n5)- z3Rd@?VLp1%q(N{|uT4VTW*A7fpP_C6mmm>Z1GogM-9vjZU^obBy(V$sQlz)9t=Pyu zV#5ZCy*@1atR}G(1-~OCuwh#kLJ{P}K8g>B`b?psvXABylMnIAd4QATqv$h)0zI7c z&EPH^Vvg8%(wq6Wt_Fr+%?d@M{zBLjbis;USyRs*RY)j61(4h!n!g3t6cudNkU=fy zfZwjCmej)allFQPK~@??^gd~2?6Z&cWhIg78Kyon9Hl=1>Z2uf?gqi*7~RlyV+Fhf z`E^WDXX|Guw&oRV*pm=a&bp3moFLQ@CVFyHAS6>Q?qpjEZg&-20OkNt4BstAy5ijE zNT>nTU|OM$M6U`PrP|E_g()7-0i|pX@pg?E95h+Awe{gm;HqY^_4SJLo8p*_0d_#W zXh@u7=UJG4>r+(6%`hPnve2OD=C$(XnksSQq8lX!Xi$=(RP8+2sp!Tw+7j2#kn1B_ z=`+KqFsn>h>=AX9+t?bQk2$f4$THq!JHyR9DHb4I22mCAsVIJBYcQ)1*yz=+>CfF=J9J530g2kDRO8rMz zT=HhZaZ!3M-}nVNgky9p3EQOgePJ@BxQW6u7cT26vDBT z4;M1OT8NOB9+V-HX-gmTSf^)1+yZiJdZ5B+^#Ju5^Wp9VRog`@P)EDGd5?i?>Vxc| z>aMHMi|p~5Zr`g-g&c6w;LpD;CMrpy@$eW(eWn(NWQcI}je{#wQ=hnu7V#Bcf)hJZ z5r{@U?=bAyz>weD zXw{{s{5{p)W}SFAK@w1E(C!tB#S1LYG;6WUw@&k~eUl&*3SR-@X}5i+ zrU8yAQECZiur?7N>(wNI4AK<733V;B1}_f2*|j=peKn}|F5x;i3WsQS^V_6?>9)5C z*A+)^xtK5#U;`|$z`gLS;#SdOs=Wy$e0`}z&rRT}H!6mjo6wG1{`Y6HWM7^WWXMS& zL$!c6`1Ifj;zq}GPQeI5y>y2&m0KX_hTW0@L~1~;3r@t7H)yMc$atMxhgatYu`~^M zT$t1=(aCR~Hb`__AWAw1G89U~ro8+}F;FyNRk2`0*b6)UDKK;pQx4OsID2##G+Ch@MDzo@_IR=0E z`A7=;M^xej0laxQ7fM2q1x(jJob#OTNAAesWrjzgtev84i0Zn1@M<$P#5^|hjExRu z6F7Aq8@iSqX=7Q=wfol*2=Vz}y?*`ex4Xal_}fq4y#1ZLem!9mc@V$4`{!T%({FY_ z$hb}Y{8!^&{q|pf^N;zj{$~8 zr!%e=arFtvT>J4kg@{PIWO^OczHH$uw(vn5;{Lwl%6T6e_Gv8r!8Vvk*cmos^8esi z+R@$(6Uea-B}#MC8$MP^-JvoxTUIpa-ll3a>)H=Vn;790j!8xr0NLLrDZWih5~P)P z*F4KR=ag6ItS}WHy9TSqd8rkzaGW-<8pe5?Rx!?ldZZLvg2BQL641#&Tc-3}JPa8y z2Gf1UF5Y1*f?l@Py;~jC$VZ9KF{*GMq~;OQyng?U6YkXbe5GSKtf$1%CNhK}<->f} z?uo_h@z1o>T>|s&VdM5O{`}a1bXHX~Cmz(yu$r$zQr@4}sS(PCpuqKCH1J%bLN|rLeueO=Tt5j4C12_ciGAdPBrhO& z9+~4JX@UUR)sbn31=5~4YT^Vq;i$=}Ab7B!a7`<<=}rmR*-E`2h%*%be1jNd`uXKb z!(4m{4@eewS_wE{dC*?^^J##fDe$h79tsSeOO0_yV2RVN%U~t2bQJKuxa2wv6ccec z78=otLX^w**X^DzCK!+9M#L?FBZp?S9(@$|fDMK1_y!x*wF6M`))7+PJM~CGs7|P) zJgXfrzfa*td=imB{d}^?7@W2vtfVw;R zq}b}@dJ6X}x(SqHp13nOhVllXKH0Gj8YxAl5w=>^hh2dj>#_MG8Dem(5{4xzM9H z9Zx)M(Bj_x)HC=*u)tG|3$YmZOt&*sBT?c8rWO>dUD-RijWSZ87PcMeKU=%$M#NE+ zz#14%rRoT%mo}wX4zX+P*kPdJy6Sb1^BIhF1Xs5Minz4(fY=8VVRQmTF*t#uBtD=> zgZh9XWgnFSP2+i^a@-mzN@1%0c||QTxU!b|m^dZm2`cjP8^cCU6TmT2aKJH7v=7|M zZr#M@)_t$tx|9{r{h<#j*)&At^0=CBXtXY1vc1uI+{|7zs0O0#m9v72BHPYAVTQ=A40 zTp)s?T_Z9IVS@Bl_KGfa4XA6hm~yN&RcTkYlP)zE>~>*dP8Jj!go)}W`eM47kZfht{Z1%HM)H&W)dLzScuRdSgWp^timK0FD1?nLV^ z7vJT2eEH1?N08%am^EmfTp4DM!}fDOKMg2z!@CdGpmoxdb-jr&pdm0-_a;JlM{A$n zL`iwe%7Hi3PEArN($m-KNkHhtE^Hf5W-4MqvAS`PtTF^8yb#8Jr0IY-k9ysehSp&8m5 znXRJLp9ZEDp>QmY7eAv;jYxSjS~`38(Vu1GYbXs=_Ih>hrwIyQipw ztlFQi`c9)&TIUoS%iuGnF!&-(VPY8~#Ng66jZTqML!TcEgw_3Vvsw!#UpE>UgH!8N z!;9!fOO^;HUQF^C=LYi}NzZEvL?hj4Z2Ee0b5%Q89~O>CusO}*pq^K;1ABOLS9B9`54x=|xpq|y2DcoJ0RB*faYv9GWo zZZV}OEv=VL>-y}txt8yVs^v!b*NC}>>udwNdQ79wdVsd7+T{0HHKy9t;}k+VD|T*u zCTIzxR7=+2kV>PG^+l;71ari=F;o*`obDJ4^~~5O@RtDEHSE*J9eN zZzcwndiC}EJx+zWbg(U^P83M8K*Y5{iMQ*C&Z(4la-5f(EF0Dy+T-^W>jS1<1Xg3l z1oZ?~yjk}WLKVry(;w5g;Q=LwrquLa#JYPCZrA+5 zDWz(@=>lkN)2iA@BPMDJ*YkYOe4{m)FKx#~tp>bo7zKVqHJWHXzzg`C!bR#6ov6eh zSWUzptXy`2dX={U1hHOl2a5YTqIRm6+Sr5+wM-!=jA!Shr&AxS%L1nB=Qv!&x?Iar zOUU#8g>}tXmz7~%w3_duS3YCWX2M1O{dL-BE8NdQHmdczFPq$vnwzbkrg7 zDG$#BMILgLqTFN-$U(yKsz=9jz|HjRw-@Th{mwf=l(h}JchOT?RV3?k;lw$@~YH=P0B_Wv(Z7nz$6<(gzIO3C*$y2)E*1Toe zRKXc2XPeSMsaMLxUJEc+rc-I*I!;5hzM}alS_5g}1GFdhbq@;hq_Gd74DE?srDe?m z9{{XCQ@^7A8tS2&mItT*o(G!j{~(lQr_};10AfeC3i{dPY-e+^aV9yn;~JDP$xrf#UV8MYBXPi?3BEzq;m=DYoF# zqhVokfeE~@kiVrndQR-Xs-0StHug%_Fc;aqRt9lf+6SniIgHbnlKg!g(>aFdb4)+n zoVlyzN{%dv9L}k)i8s4%!Z<_CEAJcHcK0nuQ{Zu$Uo8Uhf_W1{VAD$$#K*2YG`Q)R zHs@S`1&f`y&+C|nt|SS7B*msuuW4a_-ZM52$t0w5AE||s3wPZ(U~bDT^Z#D2In&^t zMxYvRuF%+5=F&Zl2<7*5QQ2*APq#-J0;jyY6l{M9wgqQ{7)?nuIJGQz+s?y8DU}(6 z@}_hUWC}49aV^58cN9}V7LPO40(Na-16$h4kAguPwDME+Lb6WY?W3tj1DkW^;^Jy7 zbOGN<&B2BZB>;uwisGC z#$t<#zRwVHAe7U{P%AI&l*^GtBXXgW`1opV*z*HehD!;!s5+LSPw52NXD1Gne1_s6 zF}YwViqU5(o^KJq3|I*<*X7x z>dJf#X;0mha!D`kSzi@MBy(RSmNWdjLS{*X=6<+PK+dJx!BQ5CVto|0wu||&g&%*A zuoMuhXmx|nIi2&=5u8#wL;TRXrkT(cArC@UA$W24f??NQexy?*01Npa)N?1ztDVv@ zrv{}A2_v|T5Q;)LT^Cx`OhtXg%*K|D;>Nn8yDdLNUXbntAE&NVr>dqjlpjwVB2L_o z#Qxo5g;ii-a!FB5>w7>#HL-&_xpFc^mRbbFZ8&-$SD1R)#O9=oVyNY;IN{NSK%aH9 zi6ubedWm2xwGQd0eoQGcH-K}vlstGrx9ajXJ?^4gwq8-8aIL8a&Jib^qe-1U;NT+} zv6xS{He&FJxTxyO=(`dNVOAUyR|gX!H*}ayA?+EYshE@dS<+(ZjFdE`UBWnoP+TQS z+^bPEhKcgBrD^v9-QjK~Rg#QS17>TiE+2F%kVt;n!D@{Jye}x7(>Ib%C7{F+xb{RH zzo*nI;So^QCUugA1SW$sz<`@X10k({noYEsgGegY)HoZfrmkYrW^y9`OR3JF*lvPu z~y-leL<%%0r%YrIQ7DWbR@LYvy|D7*w&<1{2Snj(Qv z=~+%!kCA5vp*dKKO5+=;Lw$>Sm%2_G)n+wXH6|5`6;Y*AfglWQ#87Q+3B^ES-$$oI zG@^PBR2|CgxjMxmaRjv0Vr_^Ohxjt-);Jnlr0X?Dyc#5HLBz*py=m-(%Oy_&@NlvNFW=rrP`1VorzTJ&qUIJV_u$GjhfXrz;r=$IWH5-8#UHK zOH4jFP4!3PKMJF9kTPV7RoR10Q9^cdwkWa>pwj$qBo{=bqU+oCWTIi&dTQ82+)760 z`|xmU#q4C$t~lyKxT2QBj3V8v=_sHWLw(axpmMJUyIJFVKnT}cuCBA50#*mCof_W* znn0@8^9u#`laV`-)dG{GSWo2&{9+$l!emlU~>^g@7Ky66kZ96_&&udfe zYN=p8?#k63>brLPEo~S8r`Dduu)(iteo!dK*wqKB@Z}5%pt@Hm-<>qqA~v`|70YPB_QE!7ujaQY=Vf-A+w_`rW!L)+?++3yH$YjR?hn?ev|ZI+ z?gq04j&PlwFP=M5Kq5{avz)7U;(SqbEVgeBz>W>V8mvJY(`Az5gMcl zP6es7hGASgH<5V#`SSI-MIXi&Hq01^uJnj#LjK$t4bQ?wZJPiqt(-6lrj@CKVEwqKTwuV4*Oh-XuI_kgjckkJ0Jt&lJTL{S#6w7&^ zV-#&+Z9ZB_@v*>rh)oLaJ@Wy~xnMjVb^)YxaAX*FeOobw6K6!MxzC*k48kWe zfcdTKaRO`-t0{d;tz`Y-$*0R;`--dh^i|^F;-Ye8RLxn_TwYTsjy*i)3*fhj5T3n& zFcjkn)YHjgByP?g**>1ba)@0+5GC5D?pj`1yPdmo5Mss#9VJqZo!`z%F>ngVTh0E+ zyQrUu&lnTKmYU~Q2#t`rEbPeHKNkRfqUVy(T;Rwkau$6ic8cU{pUXkWp;Gz{StE== zu>_cqA=Z>y-G>_zrWu|>*URI%$SbpE9g$$C2qP806wW96`0STS2XJjU9y}u z=2Z#SbgrM~_ldbd{;pdA4j~_5Gjx_*?2WVKfX+8JgxKQdZhck&Q;T6^kIiCxUP+G7 zQ<}YZT$lhboMPQr?4)c7X*$9wm2ZDtp@|q3xfYt}yF+u1*{moArW%$|3D}NY#uCD| z<<8PrjUd?#ghOZ@MY2206LJpSKsbcUBULEV|2#Eh?v3T7GAsu?NDlbge?knt_MbG^ zy-%te2EO+JIj&FWnNQpd$jRdxFy>#nQ|t~tPo>~l)X&wgjfX_zuTkFCq*_J~N)PhD zDnLank+c;BV+>s9AqKy$4|C_S&2iA$u^qObv@k+1mIVn3t-*;H3apuKcogO&GqzBe zDz6zn&!xR`6z(*9dWiU_jnVS7ACApAHYZx%EK9AK_cm}EXum_|q2xao4vG)SPS2;< zD`H?s5}Lv(D>BipolJoc_q`i<|C(#GeO8n;I?pB8elAcn7w`ZZedode`wp{`xhzD^ zE#9ibEC|ihkb#P(p=%5Tdb-DTLvm2)AOaL$VkO*+ZOzI9@7Y5652bP%7gRxOPi#$^ zTBPn)3<5>IWxN7WwhGhEE1t%i&i=aMY1i4C_FC5g5_7EtyMJzRBa66Qb7+DVvY??@ zkx}VY6L06vpc95dcQ|yRPQvJ85J`T-;zFKeP$%-_`h-lMaG+Z{eInhz1gE$-reM{I@aMdETpo4m09EDFVpRN?aTqCf-U2+WhR5RV6w+K@tAvJZy)Ss1 z%~ki@3N6ae>0C6aeUAfuB;07L5nIXXmiR8!GZvA`~CWkW=0mE2`g+{}7V<>5qeu-w65C7&fX8+R zcbY|K6gIf@um{PT(8L4+DK?==JrNgRjRA{>2pbWjF+{+*^Bw${n%vI*4^yO+>JL~7 z)ut-g%V$i|b$ZfurY>3aS=7r(jTFr4-SDWsY1EpOhFT$8Fre)@slM6(#Dd8^zk`i_ z565~yQ04f3QLDLr%MFG~#{-5cqq44r10ZHNZp?!^0p_k!au8ARSXc*QnbhwMIj@*^ z9-@{OW{5Qnbgs%u<2*b^<7`b`?@CrMuZelQ9U|MSw~ag`YeF8niJaz{EpF*Sc6k0|B+8lgSgdAMv0y{k_p$%v;oa8{?|yMrg(+Ws`k{wkj(`98&mUeI zz(<8WqJxWemq2{tWMj_kQ<-;*ljTFt*&>>E1!s%s(I;-lydxcqXCYkh@pu@x84(0V zIo_#)0z)V!Qp8>rtY9f%i_XAQuB>9c<_3t@iR9Xtk-Q|pbbD?PSJdUCxoY}$47bq* zSgI?JUe#I~+BJwbJ{VgeSTR2`ev$}}Sf~gFb|$SVZvM=hq@Dmm3{?o$CHh%UVkwsdyOfm6<}{+c3R$qqD`o2qnt*`ZHVg>GuN-a=6z!puk(giKvPzD zgLGZjU@tr>fI_G)yVW(Qlc$VRj7N=mY1|x-LQMmjC{){_P9#@%65~i%q(_ zT52)>dgaCj7WXPmsX$i>?B%G32hzO~x{ilVRaaKHnR<8#uvhxR@gywjHspBw@xwTN zN4I2Jsnl~ae(E)TDIULs8;*a^b#&f;TxnI(ef)G_Xs~|#bh!RCbshiq28g3<7N^R1 zWFzTXgtDl_(X4UH2=8py0lmmLFKufXKK{~H1MQrDKxj2kxS^L-fE$=3P(f2enaknl zfA#wHx8Lsm?&EJiee?Er5MIBY(471;es%ZHzxt=&?4YCZCj9(Y<6r&uUw`wD`LF(F z_k925!~K6ey#Maw-B)>>?>;`fe>Yxr_m{ha3vpJk{fGS}cmM5fqVJx$HedRW4<9~$ zT7Kc@KmT?&sOF}OKg%QWV@ymQfNAyBuYF%Wi;a;_YK&Z<()!Ew`0|@Eop?xMuv7OI zAHxVw`>cz-jBQZEwBsS$U_NBLT_X(SPpUwgNMF@-`9g&?hyxjO8gB-~?zSS&;@<=B z-RLls7V~SZG0_GToBT`A%01n=P}-JcNXdBTI7hJUDU0tYq&$VK;!?ObJh3hXrehFl=OjImx*K>LFUEM3wCsIe-+8<`6ayrK3st-aNf!7>Y*&t@>K~fdX;R zQBZBi8yQ@rNlf81kO-xYrFp}Sw$rBsXNes=GRMyySR-RQ?_)bd!E)lSmM%GhLH5A| zVYyL!pWc4XZ#f05r`R$2LD1{2vqf`?V@tQPR@*T|?5|v1GC_;^z!ARw(5_M8O^Xe1 zo>vR@x;6JEVwB~`Wa3GZ$$T6PG9LpYM?9`PnNn~i%9OyBDN}k0sdC6MbBs7K1(K`b zl_E4_H^HDG#GM%{_zqfLP{K=(m^^TUoE42Hob#YxXbNKK5jTk7)V)GOc}{lS0RGuL zg+y^@2g?JOtB#i1t>l~}$`s+mVWmlhyz>0uqGKYBV)YP1Ejc_5^$rfu=&)$VZJ>~2 z1UYKzAa`yn=kmyorHLp?eC(+fV@-0mn!fsc-?T=yV(L=Dg` z%I^rP>Mjxq1v^VB4uLF!?JR*;3TMA&deyyW+Xl!-gFsrR-rV0~cXJF7nJe?3ur%uLv18@JaB(_Ovck?=2b_ zDYZ~y!B|{n=X5)l2t~ebA_^-WfB-YQyWYlYiWxtxv}YBuZLs z`JQZBPI69JI2H?h996aAZA409K!W2r6aYi`QaX0j08S=kRd!&;H+eQW)-XAKDWJ%>1gz_Xuy zv8VV272p;j2P4mIp~(IME%mf|rhs76?em(>hcbApaX2I@5>f*bR4bPh=_WCurU_C|Wt3_t1z=4$+6XU+P>q)yx{rNk&~v~X zXW!r|E2JeIw6Ar2ig1GB_!sg5T+`cCnn*25QwMvl>rm30<*ETR5x2Y-e*E6+2hhvG z{=)jlBy(-mRfW-**6ESgzoIEcnNTe(b=&?-dhU8?d8JLoeoUn@85gCHI&T}yMp7-F z?e(KNTgdBVx)4EvHUO@o)0LLisDf9*IvikDr>l`R24Ba3oOXUvFUx4y%g|J6H$(6} zJS$K(K2OISd6?V5S5gRHx%75dU;Wzm<+B_L_~cN)Cx_x|^5U1kDTDli=44>UjCV4% z^yF3LJ`I@!IYx?b-52v()J0I7E_8AxVj6_v1jp4i2$wfzX`d5Bc!-7xwRUPE)^nSsyi?E9 zNZPnw963xh|H|1%_G@e~-)!1BAP?68mg|)s#c5)Ck=9pRs5-#I-YAxHVMux23%kX zPut$-ywji1zDkCDDCJHaDJD!gxv}E`Q&s0=yd7;gYR)06X~PTYw*t-{Zbm;*k$F36 z<<73rKi^fnybk4zYUpq*ednXRIy42JKRKMm4yDgoP9~D7g(93R9`+bYRZcKQ)lgr_ z8?@T~M0!PIc~hg!CkPwG)3_7jp=ur$4p_WxJ#TkeDbJQ5(shvl0IVWlV8XP_8f_N+~Dj zRu8tM0Q@!$&glgmg`pl3=(Xv?I{Ks6K z2ggq9Q$xD$ig}$?9i8ZrY=-}-7+HO>%xC+T&Mnd|6C4?-5Fyr1kOn8;;E;h-m`5H* z9SxB`21)hM4rnu5v@oCuouH8zt2*miv(|8Pf>3A4YF%5ffRdeb_5zn_vJ3Q7 zM=KT)Wy5M7^I`!)orawG?^5ldEEHHH$R;7ZD7$Q+p!f$e|vNqT-H6!A|K%Vw^;)tAwn)R$1GnA7klS z^g81h+m@~mXAR{DSQz@ylt(1*lhFL)rjFTv5P#>N~>-*fA==X#as9>Pv@hNUG z(8NiKwB;6L{=VXJKzrkd0?*nQ`Z_+?UU65}jV->~C^g1lDVjsUb~!ffDnt@3>clcd z^*2z(23l54<(b!Mks-wptEKkYq4v;5YCl#;?VnSS*~B40D4mMYS0&L+oLMfMZ5B?A z7_k$l}zSFF6S-E^HG7z@M_ z4S74niG;giy(Thi&T{!e>Z&;#5Sa@!%a-lk)KwNYpFi^uD|9 zU%)q?;xGmJRA4uGL(MlKjnKfBOUhqCY|-rVOt0j)L-KYwNh+i@DY*I!2?F~13{lmE zlMrcg;UvWC8xwap2}$yCnn+P!22LW9=3PCwNI5Byf_7#$Y(3 z523VQzDWneg-9r*eI&H9rO!44A;$aX10fDWQ||d_F;NmKI<;vd>?`jmIO8Owx52({ zfRl_pL{g17D6emCY_XTIq*HW9n&2eV6RC!&z*7r5==%1BCb%_0Jm&q3UH;`6`xbCJ zK$uNd5A${?^sSWJfF%+F*I*CuYF#g4&pBA$`A_DM*3W@fBIQ3!A3;tmvBaKF&cu&B zUngarOB~>gA7LX{7a0)LVd`^May|9LfCyu>Imn=L_a^O%UgQNig=$o8z+-s3K4K!P z_jnk;r&^OmI#B>ehbXP@BC$}c$zoLKTLC+%>g_-n2xTaYt`}q4p)eMskF5ehNqpU9 z6rB?2`E+p#K7r<8`MS%LHNFfehJ@xCK=AGVX}xVhn|dkHWZ@U2T7tM| zNliQxI>AbfK$@K6#Uc!dN$cwkYe_o`jc~b{MmUbT@K|4jKq=P<4_lfYt%7WjK_1IcEv6hVuN^cRJkx>8d(Fg2j2=*FF(Fq;Zh4%;>!wuIA8m{4=KFIfLH z0gWTXathGdgU0X$>Hq;m4%onpuBs#JFk)!Jm#XFWx5Ji11ImOgNxigPaZoHV;LOeN z`l;wf0(3cC4@4mK@eqaLm^$6gI)y@rs$0!|p;6ZT!D0;8jSR0xeCzVL1ti^DREx*n zJgo=1xSUZVv7azfs8{Ir3L}Z93H|cMK5lrlA<%`TKo`4!E=c49x=@|^HhCNN0bLC2 zqzfSj^#NUEFQjveIhaj{sZWrXrAlAH@28_7%w+`D(lSE5U@2PyoE`O2xmthEno!Kk zab^QWEOxGsy(H=)>>Y+GnW<2!AMGk8wT_IUAVU2aOXWth3jLm}aI6;}^gUTV#|PA+ zGj<$%?1G5pV-CTpp(PBkX}<$Ph$mf7CF;1_(hL!VPn50^%erm47RNEFr4#*Y2V-TS zpw+X^gF=aqxi#pZErnO>YAHRtun_5F4(mal*R*J3PPdsuKHN|7$Mt&Nw9xamTXd3j zgs6-KbYc~ml*;q2rX)N+42%#aq|B(BYFTYc9P{SW!fB?s6Rx*bFf&DxS~C`>bDXTB zzShbhCkxT)r-s$5vlGsik40vy2W%`=HP_>y2jzCaO+3TX=nZ149h8Vq0`)uG{n?^Ozd<|vyG0m zky0Eray<&RD^OVl#8DfRH=s1?38?!Q1C@HH6F1a=+hPWLM#^A}-@k%Jt&H=tgn@G{-+ zU?~QxSltW@vmpw| zuGnY3tt|4PmY)=w`pF>CbjSFXAr@~pi3@+e4mqlno&TB+;Md~e)McL=oefE@yKG)N zfAja0<5PW(r@WP_X{c^!G%jAxbdCHYfOx;>}oVTD%@7N>xHpQMybt=T(;f6BCI9;yc zDE6XljIA*o>a)K?Xo^Eu#ebjr%0?dbItT>T>W#oBwEzUP6*(mD02+f;$CyCX@OYnK zMFu8bVO4+ao%d>vY_+FB8f^6XPPK(l4AuN4?DPbI@*34@0+SRQF;Sl^2y9d*)=(cx zyY?47_w3^T5;&HW;aC)Cwd&Q>ZI~at_`s|I?tKE~G~oJAkoyxmNQ*u5(CHKL@DsNb zC?DmtXMts34Jkp59E`sb2pD-*QipN|SiG4gQ=ULb4f?sgvkw~x7X})iPJOAtw*+Gm z2d8v~NQPU_P!4rzIze$`kv(tSSbMyB)$YO3JXfnlgJ}j;>Lv)zo`-Nz9?OFefI zjn#VY47jZ@dWf?M?C*b>@AG(c*B}h3r%yl#OnYt-4_rG|>*}`z zIAtTI+fD^jQ;SI0RkTf;;h>|cYJ?~aLgC~Kg%8SfT*^R6h(wBkq6w>t1v^F6eq2yo zEY$rXV;8ARG5U}=)c{Vbz#RJekT@8U6N$_Dl@p1>CE9mOBrZu3kDb_4C5*Q9ipe82 zDL~@RYJWl3#LI9VXecIgXy1H`;@>6sLp=eTOIs979~@IZ67a)-rn+uN>Q?E4g0C4Lh1+3EIqt z^4hp=96pcoLsMuHwAz)lXJ$e)sm^#gI!o`Uu7=u3vd0pz_;BdUA3z`uMMH#j$|)}u z^?)I7MYvbRf#S9{EzkzLAWzKkMAin_FQo+A;u?`BcZo7Jo(0JFnNipEp5RoUrjm@* zHNb?ia#qdv>fo$aFcmnnN}#HR@K#d^DMi|YOks=}YgUl*j|tzPfuDjy7j*z3Hgvkn z+q)G8hp7f-@dt+rY4)iG^&1yRS2Cn(n)PIZ`OSr6*J=`}#mPK-9Yy6e9geLpFfbl8 z>IhF?GrnqDAuQ$8U<%woKX&c3W1(9JRLZ*b212Y52w5$ly+cAMDY}pl7&T#BP)Od) z#YY5*D$yso4FfreAVia$iw2SiIJsU^6TP9Vs( zz@MZDS}px6hZV}$cX1ie3Fc6jF?N1a4MiIac$Fb$n{=GQe1}tEciEwOY2S)rOq0ym z*a??hoCw*!|#<xo#wl4cv5nJwS_%k;2W~bOVvKu(0z+>aa$GrvL0w`MN@YHlMwC zv|WpztWcm@e>-pI*IBBk6*bTwcRlt7Yf@p~j*GP7l+IzNbPf-CAALB@fa^aY(@4sG4BB8z{SKPRp#Fu;CP`46SGR2PVNYZ^u-GqkFn7VC;@ zgn%<));q*QCPnHzM8oPK;_YIg5ExqE{-HD)=&lJAf+T5h;yY&;*Km0|r~s@$fzWK= zeRpmCgKJ`D?VKYhZ=o^&nqdbfD%_oE9n)b=AISuk?@Ih7>KF?d|JYZtCcqxB+4a0| zRkw&?=dm*H$I6beE_*^mIuFtwZ)kE2asl@9jp!*O^>E*JL*>Jl9}_P{Ky#u#?=b@E zbH~+Y&<=#W+3|iL8AS26GE(`#{Ck zurRCV22rGeC`~BEL?7Q8?KMNR>niowc!p5BK0y9^nIH;%Ywf;eq^_ zbcPr>#3FqZ+F0jc6c{eTLgZy=KaW^Q+8zthvV^g@s$`ga^ERG6dtCR*=a$GCR7OI# z=D5p;^Eh_vfCR}fZZ5lAKz!DEe9qWK%XejwpamUi3pl*9icz32r`KbU;D=jip>a>0 zVd*Ccj+jkc*z*esd;mVd6&hx8b&D6gSa?ma%<{o2VLT znQ!A5C;|2WyarAIcVKJIGhAD`goa(M0aXMGNZp5xSWPtFEAW^mM41XaMsFGpUJse9 zV>q}T&nTXu0&b7D>2^ZN0Rr*XVK1KZ=>C6z( zro0ZZT7?*I6$$_=Y6E1*s{#ZsA`}?4+_gc`4MTs+-NG3%!gY`A6*J^w+_Rs3GGauj zxAfrpf^{F)*dgbH5UNXW;KRT4T;&alVt{ez)TP-k$3_&6d%8s2U{Gks`5L>B1Hg&y zbn++TaeXr0r0pfAvnTy=TGP#sa5B4esug86%V zt|(!BUo zAO8<2XOljqEQZxO<`WW$oY;JpY0=n;L@u?p4vP7QzzKknKErAkD-t7ND`sPzyaTB+&n1;LBj zHkWO$$x*<~0zzQW>+AD+X8|~4iUSq~;HxG*1?`Yc4H)-=N{`vRR?Lw&XFsKu|Lz z34%>Q&9MIVaPyAb=0Z%kLWW8mwEcVBAxEGD^*|UqXBgL*7FU+zLDJ;SSOtNB@6Ai+5ZXf8 zQbHusk@aFS`mk8AA?)Q+e)90{>xXy0xGHUuuRi_I!)QsDSJ}f$1LLR+jN5fMIkeE? zSe0~uaZz(_=K|ws(0iY3Jr8O9Cw5@msEz8At+%DD+wJ7gQr;9b{~BZd{0A2tr?5s2 zzH0Np(NqepnrntbxpS!XLMkAn5MwkCGEfuY$#f1V)=o^y9QZ_Skg-Ft&cmV* z9s5b;rkG+&Xd-YECLs0dlQH8KP>hZJqf`b`rEWWPNOt{cu0T?fOPc>YC9In~6ev@5 z%2;Rauqf%Mbvu*-Np%8QN7JQq^zK;J>N_R^nq_By)J!v?X8bKMFy}CH`>=| zU`Vt(_^V=SC*9_(=oWWN8WP&}NdulTrh4h~3gYz{t`JTWUwIR^JA7mQk2DCfU0|Ob z7Ufc@B<}x$SQr<&0t~cTqHTwUsYeBF>cSoHUY*)>j!%&L6XO1ap83QR97Y?PIF5?H zMcQfrHD19bdOm@uNCVE+1Z<6l7|O)@VLN^25RyywcHtZxI*$#&H8$AH!*QoH?im}U zS6lgVj$?B@(}f!}{vbUZ!%hcjR@59*RySiOIyyF5Jtf;Kt6RZ<7Xts*d$sQyCV7-m zkN&aJhHvKX{Mt!cMaNi=B;R$5lVR_DSU}f;t&Hz?5y9S5vTUq}jGFF82JK%eDtM`u zyuI*4bZs2<0hJp?aH;RZ(vt8<&NbJ;3p(sC3R~d|I{7bZjCCF;a-~PqRs$tyHBh*r zZeL*7O1E$6rEV>nc+PL4<4zCsJPz{tU%h_)?YFzX`}o^W-@N@DhS#qj|Ml)S|MT1X zFF)OXb@$=BcjF_v`@7FSe*5O#BWm*D{WovkoD=fOR^yL*r zr@M7O;G6q5AHVyMzvZj%K0Lhp!`-ic{Xc&F8grs{_jiB#H*eqPYsR1U^>^>S9LI6@@YPT6@+iN0`125jqt?%$B%dUiad~qukSv7^XBc_d`mw4F@Nunx4}W<0bT>ZwFF!nd`{}25Z{B@%_vXtl-+z;T&cpnTe|mrS`TaM){BZyF&3Lmu z|9SpH9(2BWAI4GN&3EKu{?@O)df461Jik|W??2pq^WED|58u9h_;UBjpXR~5`)c=x z`KCa4_4?Dp+x&Eo#*hyFVf;DsKjmlocfWuC{?o@#AKrZX?R~zJd3oHuy?^(IPk;RQ zvuX7C@85j*c=vVw@%L}O{9}Fue|Z1l;nN?#+5PjUHy{3R|7pC@_Xm$)`rEso{EaDu zUwr*~Ts3(@Z$8}5&oNJE9Pg+1-`;(A|EJ+@2LX*YfBf601^4O0!<)C`U%vU2mskGj z`4)~>{9RrZZ{GfN|8piN@BCw)0KV}tPw&}h`6f+-<~%sWU)V%so@VgP{Z|if4p;n_ z@85m(-R`}4TyF=jz&PMPv?YK3~k0-x1$UhzKel-q568+-t ztj(f%KYara^m+i~(jX3xTyVqv^0pl9@{r#s;_aBD4UynZ}zxVkapI()3^M*Js zy}WjQ|Nd=W9(SMTE%$MSe>0v6e*fqH{^#$% z%>VcOpK%EJU;n@F-h6fWy7;5G?k~Fw$16bc|Kxb_i`}n3zI}LipSOiC@;3IX>6Yfd z%;(~l_y7B!zxd+ohqrIX4{83tLH_o4`LBNV#TVmlGk!t-?a#jdf2Y6O#rJnO(D#3v zujTQ2J-i+VJN&?B8ov1A)$ZEechmA(T3j1xe{J45e{#3H6qaB6bSYlBG|JY9_m}Q0#gos| z^7C>2{A(WX!Q#U_-Ic$cS3v&ncy=$p?Pxak{I^j$_iecRy14v0c>KC>{B;5=zRsTe zIyf2Kny-Vf_`dzOk$ArC%(D6WD=#K4UQAy6s3eQ`43?{5d9B&w+C5(8bo`z@y*%;c zjd<=&$8hC6JGeAGUizBT$-{koAOH4?FaGNBzkmMzcYhwz_kYW$ul{nP__*wWpYFE% z)FJ<=!z*`n`%zr?m)(VRr%qTrNIQS(q^A|WynfJ2#}u6I=FeOoo>t4H1)S`{YXRrK z8lHPM;L=bqUV1^IR-Qsfr$}0yB7m1}>T@RvF1~}8C(*NC!sm~pvs=yaJ$>%jz@smT z%V$U@XQEynge*UQdM2Mg7|!lZ&&~m8^>^%-;klF9cf6d$TV$6OrJV`o>|D&SjL%+6 zcqSUy4|u=OJ9_fi!p-NkG@4(rZAPT?v3iFMdA4 z<&*5>E<*WXgawBYuiNl{@R$8%ysr2#l2cd>=u(umg8^*$Ht?k|3+QB>%cU?2R)<;q z9JnSI!MJPd{wZgnD>&b0R+~)0B`D75(tUjnMS~V7TKIldyg?U_k#zAGiKnR@zp1J%9+Wgo#h9`&uRD z3`+e3beumu@G?$uyv9zyB*$WQt+soB;S`@fc%FfPR}inb4j4=8vJz=}*}AdijdEQo zl!{LuIDrHZ&%X%MD!`M|>e<8O-~&gX^|H#Io_6~)6{h&y$#6RGV@wb)zdR> zt`RNp@xv!Y3c_n2PLIPr;R^6oiUxoG><;@hXJ`6z`cA;5cj(+&KSHY0;=RyIhYG{~ z+TAOVE@6*P5I{V5H_udnp2~Xve|zuRB*~Fnhknq%Mz;qV9k5bD4J}Lzb{Q6rj0ek{&>d*R?j2r|+<5jt z2GZ%YOgOU9vXUr!>7ULvm9?p|#8xdfljQQ7u-r_V!d=pQ)t+9RHxYbTS=Jn(yDg=p4AA#D)Sn`O_!-9j-gf#yJEB!!SerQRy* zHDw`3EUpb?N`8!E<}`WJIn`J$w%OB#Ie*8F4NEGC95lp}{}iGYmaJiU1ZMwPI^v!8 z>?t2qz1<^u#88z^ww47A`-CPmFBqB1&jIua#Xp}Y^*sg(#Lpw^|7piVM7WWK%u*1@P$@8Kf~|U5 zx0Oz;WeO_khWFrjryX)YEF98;#uM{keUYt1WFTWzw6%LLW=D!}fRKffZnNIEP1bjpR)i>+Zf{N+ebum!9} zwooB_AxKi)#(aS(i!AiG1sAe>f5=jz2P4FQI2{~#od>VO3ONmiN)3g;pXUqpx{wu@ zA;d|RsG8+XCLO+8#u3l9ZI<{STeuw&3R3{-HL+ioV0wPC`FfHkq1J6q{8LtqTvXZWV{LtF*_hMZ)#>WJMz6mc;01rurS@?Bd{Dns4UR@oNQmt(y`WuQ5^K$ z=Jv}y75TTfuwj?^jb=BuCgOFQI2joZEqZ|O zpUEO4I+aCqLa9&ie5@=rifank>XQ`H(cQ|{v7?rvR#TnMe)LCOmkb9Bh>@5qA|g9e z``E3!u1HOYpV*ALk7vI)V!FN~%a90j3BOMV=p7hJ(n&SGKb{;j z21>`?#wZvT={<(psuADlBM%u*@3hO(WNoj zk)<)Y>S^N1ezRSXq-X~aB+rgQh(wdck1X>AEBLbLclN9vV0Lm3m>}4EqrMf$g&8oR zo_>?7G7_O)bVC%1W{1^xPe=bi$)#AXXrOQ=yH;>rL%#w(RIwi!14;~IS?Mgn_9XV; z1xuz0{5;OSjYD%!qhvvf5=lr98jCBEiz2(M*3a-7V>N3h1bQL5q6XXlXd0riG-Phr zJ1bXa7ap8wL$eE-(p(MbiY5BDr51&DsFO{3MTvrD8k35gBmg#9G_5dQKbnV(ttQZd z(+$H%_3&;mcwCLBk_FQuccV&v@kCq81v$%7bt7;R4X_bL;3N{bPY>`TU6;n+(6mGb zU`DrkXt1?a#125kPLM=Dt{!1kycq0NksFEQQyel67aQnLbOxe1nzA1OZ>Ww8#0rO{ z0p2O}La+tmQ6R)Sa8TL98Hit>fh_i)QKF~PD_|3ugqZ8VCbFF@R@4@fj;3;4 z=44`jQ`4}9B`&ZWsQspQ%%^5%QdVRx%jgw{LI;9TmI$M8{jBlAum{%PEbxPI!bemJDP*wUbbE-C1)7$`{)0aw!irHcH*h~T{Vx?rCr9bxm>sRD2}t!U6xq#=b6 zxCe9qOG9%Y_Hk5xO{!qqQ-uIhDx}k(7>@&`5~R1_g+YR((hwXf1SD3=R)C`I)HPW*~60(3}5{jh7K#eI4862^M zQrrLnL~LaEz?qCKiC?V&KuVU~Vpz5zi#kai>4o7(rk3nHiY(Y3DbBM+_h%$U-e|SIp$*8% zR4B{Sp#;+i%1e0x>PC6A&7p#KJGa%HKJtLE6eYSQ5BI5rSenX-#v`ZDz|8t*va6Y5 zF{Qt9Gljq`R?Nfe(BSVy9}9xjZan&@#52%HgBY4;*e#@*YRAl=sI$zV6b2dV6AUnL zA{&|5aK<5u?2X3P8s-O!My^yx8X~x&yN!otNLmztqxQ(U8@iWW1;q zG&37$O*GA-9mP(%C)A=%e{!KZk_+QO`h<5XfEU6fQ&5xAdvG4cV+hKOcU+jOo%C@W zlv@k-f7ps_GnGx#CrB}YmA?f78wHLcM5#t5re9xPJa>>YE8OSz;#7Rv26Wo zBYS9XVAPkgsCluP{-~3BE{syf$~08%Ac{psQ1U}Veug|a>~69z!BDJ1$C0VED3Cx_ z<{}z-2TE#jBd#)mVPuyGPi#b=P;5dol0^*wMJfmq0ce4fx;R1$hF+ymftriJ(JNGM z5@E{>Oj}7JEF|0}Nhd3RwyyCNp4tKO4x88g_VGs_I$hLO#dq!G2`6^yU1IWYyc zWDh|xinWS2-L5aPQ>|5PF5+dzQp{Hg9NDZdEpV)hn}w99rJ>EMHTBVBivhNSM>m#> z7EQSe?2Yn#M)EN=`-q~XFjQQSkFmz%KIGQ)-o>(aQN=)6ba>EB#D{d3OKF;iC~YnZ z9ZLh|q??_{4HJC3QIHAQDzQZeLMfLm;M&fb>7ScWv1hM;rE>RSOty?7TS$)#yx#b& z+vB$f+no+E`#v-m2m6AzDO;#kGVo+ZBJ%!p3Nw=}N7_urfwshr#RYQUwLIdO z#C=nK8bu7;vx{k8UM!a-$&GrAEB~#;Oe;E2_$x%t}vlOg?Ee zAKy+ap2}K|N>mo$NC49UTBynb9F=em(aO|5!ooqWH>}$Buqr{9z`ym&cJl_^@iaFb ziUdTm*P~VvP3-nBzBXB~F_hDiuf!!EShF#w_y~K#YAyRO#oGNH9~WbuY)C@ww$FCZ za?#9EGegHPr0BZH^FQNpjlh+|)ag|TUPLPziC+?5FX;MuMwDY(CbCst4605YUTIr3 zsyfYZFBvVzvMT4W0o(kV3!Opm73#?@Axd1y7pJL&ei_FqR0gIr??mqQtg1|X6g6{P zO}omg^xO2*=ZI_LXOZ|;iJuCKQGfH7&1AO~^=KA!j3Hb(WaPap4~;l-_sJ~h6off- zIq{8X1sWMgPf-c7AykS-&j(3J4@1ekBoE-kYk#~=EStbFeJ<*fK!QRFNW#1~_s>-h z2Sf{hu8E2>5>||hfEw`tt$8!qBdl-nHPvzZHIwR5@d1;`4)OWg*Zco99p%$)u5VsF{P=AD$I}~f{V<*H zLTnG+_s$TiIzp%cy%GP`FPnR^{ThEVi)+3Z5e(U~m@(QsI2uAAsk|#qWmkF02UlAw zK6yJ)Z!tGeO*TyC)L0rG?q*XABB^E120NlJo5;Qyhs8wAMFCBqSjU*yP&vAMdR}?F zit~9LtQ8niZGLNGz#7$fES`D^f0OC2(MEnM2i3F?iz>EtEKA)LmySe>)}U(I9%8rs zBI;JpHD80T6qeEPg=N+w3w&Wov`uZuzSF7@Nmp=nW!jUgfTP#|IH>;yhC_?Zf;kT= z&w%)@>X(x-8Nh2b_fDR=Vplxlxc!(rA}4gsN|u{1kXZzp`T~ZT)~ctu(`Ix9sf5_p zww;n?UyyHPkC(o%s#v;Hgz#N<4b6Xp$I#5RZdF-2j`@2=IRBvH#$meTYKCZX@=Ph> z!#&s8!%;f2^@uFXz{7JLs0r~RLflmG#_Uy>N;cCJVsyIs z(!7kkK~X9R)TJk7>9)IwHEU~FS7s-3wW!4m{&f4wo_}0H&$qW0 zw?0$1P|mh9_A*U1+savT<^9oE7ur&QZyxS3kE@7P@Ufbq?2LRA+lZOd;u#$7FNn^# zUR~zejxtvoFvNk^1rYcZVBilZmr0}3!FrF5^{b~YC*!1TE?;HgB{u}Ejb6a6ip;Sr z?CW5&=eSh*E4ewEx~{MVvS6C;aWI^g0O9fgZ4Ys_jfteX6dXl_TqRXK7f}71uA&%6 z7+Di$4T>&UUG=ZMqItO{V)$^XG;3&@u=ZUN59HH(42%|o!nMjWa!&y@4Aoy1AT-}L z{W5)So$ElSEiHZs#}>?3eRGTr*-_}IlZ-`^Gf0}9#O*7QrC47h7pYRN1|DN79u5Sf z1E?f6#Z*u(RYg$aJfIC zm=-j6t=~k^kFJOLmN{MRD&s=sO%Xr%Eh3Ir8YPnJ!hd+&XiEWPshcJAR2H}mmA3@C zS}!FLjpO>J)J97%u@4RrFYw=-l}WK6&Eosa#hAGTy2 zNcO`*MM}prJHIp+yJ3U<%3ME+bWHThc2a3 z>CfOb%VG-!D8`bWMx71ym1%@86W4qt=!1G1pnVxACO%K6Vt4feubY8_71Z#oHzS2^ zhT~#^!{wW$H%y@jSIxQsrEMW#p1FNoz;jpt*O}0dj5Y(Q;fM+vma%jHWVkRzRGr=(J0C0dxmMI7Y4Uy={dYg~-6icq~&kFMiOCi_XN~dOvRVqz=-^R(63EeA%;^;A=iSAF1QGK- z4QXc(UflXm86tb1vZ|PBkK&re=F7RlV;~pnOYM+}f2p=wpva_it%bIEJWRqWS>VHo zBA59J%+EMf$bpukuO}LFo)CEf83HuRzl-@H#2I1R3cTywG|z^1_)o zXQS_q{W&{SM%7$vmndVRg$%rhybUx7&2U`{Cb_9jMznR4T*Jw&xX4Q5Q76!1P`a-`>6T^h2sTkL z*<+AOWBPr80dfHNUZm>P&;T^*I*AITfF_+*M2xIwB|3~QQL>)Zp}&Qn6S%dDea&|# zIZ=5opoXHurEXPea78Qa4B`VE<${5@If0p8$vRw?|2X5=GQ-B*0;YkMhF*`ypcO`x zh1fdUl8VnE>{v@G#rF0VNM)I5#7$_PJL&ndW3!Ya`{F~tP`7)=kmJevKUN_1cP}D_eFvYM6ztRl7 zaz+bWbp1&I9phs%0&hfH#(RkL&T-@q$M*{gL7pV6$%=oaF%X-}RV?J3hep#CgVkmG zHeQJ4RdfLYrF3M)IfencQid${xkjr(GFu3|0(ZabrlHf2vg_2g(P*(VD`9#zO;Ox{ z1NzKtDA~|3TaL3H75NFw7)sw#2Pa6=ZgH0_V;bxT4pl}ptBx>L$~jz$bn%VP{M1P_ zS{Jgv?}J6KGEoR+)?TO-+aPNX90k-Kq!C~PgP{5jni|NJ1Ne>stWWfbPrC&L*8DOK zU3f)uh>nx6OUiuiu;i9kT1_$%4CW&K^i(=CUbJTURGOUJI(GV`9T9Rv>iA4)w38HS z!*jvg+x{9K<;KHeb#S0SY4fo$_EtL+g#ho}pqpq54r`}}^6vwnbM5pY; zG?FoXS?MvZl9~R`7+xt>PTA z*wp1f8C^L?F1+J%5h(?E;jx~r$Zi~cZdZj=+a6LqIEw{dr?mr%POvzQXzv>|*fOfW zpafAaNza3?0q@F$3*QenM#!VE5-8PEDXiO1tNjWAfe33kLnJq$!y>+kWS!Bgi_9 zj3N4yZKAosm4IznqiJt$1+8G8LZ*O4kCOackpf3XV)=O_pTR8AvKS8cGQ_=B#_b|| zEY#@YAkYfVZc9|%Rib0rQi0whQlpyaQX9RlB9?9`C6u; zwuq8k{4`kfQ>?tB@mLUcxH z{FRc9Y0eLVmQNbgnrK}NxJhItzwIa_o|#~G=B)7bejhK2;vjk|k^Mq~%Qowo*M1Sh zvII?2eNgz>G&X;K0`8LUkk;3RQE}!NVHv0xaZB09kMn zdq$%@>KtlJ)(J29K*fv(4 zjsW33VVWjLHFrc*fM^s_6&%E zT;>l6KmbN;$%cIghJC>r*@Eb#WsQ)*0xPmvjBW%db^GzhXo#|3wW?0QCwolv)06xl?bY;vt@!Uo@f|?Gw+#vx#;fx^SbE$ z@VRw#uv?*puW4+-#10|`6GByH_$i4Nvnp_*RZUERb#gh8+$#~tJmKYjDlSpjT*eeh zVFiFMEH0#@-3Z7$mf2%*jEX799#dnDL8S$+a}fl~{Yv(jcSu)TpEPAOG>9xI4o8Zj z*T+crw;i#FXKhEVDKS0>MytCj@~Gi zFSo$lIQJJ@4!Ltl5hqy^x5qPUv;zrPTLkhG8mavny z40c^Ls+SHdth@T6*;kRp)c|(S21JlGsd*d~Yk?X;hfr(<%QCD&l4H=c2R}GstEHl| zu_ODUQnM$w?^o8~b+B9*M$T8oN!X4&Lo(w3&}is+2qsv~+!FToin zDZn8oo{Y;y+K+@ zP1*ToLKi(j_wc)(X0ChqU4xiW%Z{fvWQI3_yA*q}L72cqTQIC4b{CV3wI-e?1n)=< zbFU{z9mBY!CZk3YyfsA-JXdjEAWtdlNV9;p!H{(3vy*)tOKut{ce;py0V1{qspNLX z03+k9p&@WvQFsi3or^n8XUoo9Ly9t8N{TTQCoCheAQMUsB}{>yA#ttPNuaeI4<}h# z*p`iEd_tFs91f*b3F9tbn`rzfZdVvx((QpXemtNAo%D~!b)6kS`GUPULL)_oB>wOv zv^7=bG0p37x~juwg%&KT4x7B2mdihaHV&V<<1mRB= zI#7{N;K(gBvUQj2EYF1mPW0E8EqbX|wG=C2itdOh@T4!Qj|Bz-8Bl*{urVxA9WA5Q zg_eXy9(txK78A1NO#tR~hNboyhe11RpwbZ|O$j#IDV9j=h|pL?gUUPd1P~e$*HSfR zF|uG(d~?+IOHhbD)^B}b$A_YBq}xD2vWHGwan(ge^n$zzq>ojlga(JT=HRLn7AT|;rd2tC9b zKl zkc99M*-Rt%(BNd_S61~!PFc084XEb(Uh6Zc(EALL^scCDdw1w`3cED_N+6psNc6%C zqf8w$5KTHLbi`Ld$5+!W8~P-i!9?vf&sxxS4C|jYkNx#$?TGcuH!sTW2rVTuTF_P# zajmd6eKEsyCYqi}MKXzvvc0qXWbdn?j~eKlp#aSFL)z0=;#FYgQf83 zh?sVuv9vT%fn5VHyhbOZjdv@#Y|EcDmYE*FJ`fg45P*zcI1c#*dkgF$P1{m<_HnRu zL4*~8T??9DsT_Y_#I{5pUX10QY0S;aj8PPmgT}!HZ|l_$Y6we9d+UC2Ri5at{pvKZ zL;yWYR1)2&|L&^bUVt{eypJlgn| z?Hi{wH{Y8{NP?p#hs=e8u3Q$Jjl+NhP=Xv5^;$`RUph%E&AG)Xh}oz}idd*>hB_fc1H*{Ij5FZS=12d%8tto) z^wj@Y(o}67Yk$m$ZE08gV^ftH?-LnWYW{m}gexDiHoPvEUWkI^cCh^0uB%hZmVvdy zBhf5CCxxcl;I4KWKt9txlPJqnDW3QMZbgjJ9We^Eq7WeBjcS}Y)Lsi5023gYTCW|} zS`tQ#=Ms*>WPi#qF-tC(Qygi-h$i+~X}&Qu$Wj{7Og(dL=I4i13@d1$$Hui(ud1t* z2U%O0EolFIa@ew-DDpfyC>by}%+NTK4Ku|-!vQ)dj8oRQZocI@;Oaeez?Y*NhCtpl z!DcfIJVHqUN+J_kD2EY=v8o>}Jll>B|9Rd|)`qS^qZ17kW?T`yZAbK$1`MNMd)b3w zIf7wXO7ublB_cCM?fgosE4Kh(qvfQ30z2iWm%&@J6BoK_D^2q=G&jxnp$sclW|<;bj@=Jls42rE=rA}4-*cxyU}VV$~|I*ce(T{ux-ArCG+M&Bh4m8D^Q zFIKM}wgY-OsX9DpUmLtF_jk$CZd+y>G%=XUt`^kwW)V zJ3K93tD1Z%0RQ+r=j_9sWoqC%NpE4u*EQD4fUtdrR2kwSp(B`wmR)R@}@E!X8A%Y4^RL-vj~vyVOVXp$9H0dsZ9 zchcL)^#o3=`!j}PDr2eRwB=wqoCwNN%Vul?bq0x8r4=cE-2)_~0_^sz8g721hFi9J z7^)B_SSzwPF5NT59Srw+iaP-I?6iOo{mS56vd*Dt7$D6GSLq!3RY0IGuYzbN_9p=*8O>y5 z{W)%4>VhJ}Mw!Z<68#EV6i58}RAZ)ap_O%lpG4(*DZJejNU?@E#)=WmXA6*1fP3>8vm&7B zj(~<5j(}2gIKVDJ3u)~6Ty$7W^HB_+TDQw{0C3Y=D#;ntt6)jPGZJdDDPX0@spEMI zJx7;P(B8Y{vM3#-~_xu2jI0pOIj1nN`C&* z39!!3Pbv4I^y614_bfp#A^C^E76k#`xFu5(Rd1;onukD6)&j6M>!6mE{6mm$vkV8u zVEuO47#&kH(9k`Etqm_j+YK3OzS11%Dr)!}4QqBQMbq}}C~K4x-8yPk=DZzsxAbPP zr97!P_)7XV$`)POY?>R;mDMb)#p}wd!YG_=L;6e!=_3I~3R9oHC7Y!{o?7KNHPIRV z%Z$BB__q=(u0sTweae86F*<@5S_k^BIVZ3TVEWK2Gk_(fT{=f<(3HuomKpJEW4LOx zft1KTO19j`^j$1V?029)5i?1+rtkiwUZc`w)q7&C8 zM!-#XSJcYV3_m?;TJfl%n~R+Fi`q!XJi0vtfq#i}1wJG$y0C>8!O7;?I@1<{L%ICN zNBn%*hupx*v!?U}nj9Ra`ErH>AdWsjEBe?ohhtO!p+PyJz06;;u}Ade5R;ZP@j~>LiX5Cf|UQ!(`1Sc_^um;P@Wc)>9^Q93HX9i_+KCJIbqy5>LyR9 zfcEQ^`8x=3%i(!)Hs4Z%%8F-Y(I-dr4LqM>g`Y=NTh>No7f|SQDN9ug5z-JfEu4D8 z(-gWB1x*m%bS127(9V1$FV10cAM7?nluNJr-RcTmo>*nfSYYJ)pme364XY}I7`N(u zEv}NWl0)5~QLT>SDNd#P+}`M2KvVZ z*s&MSGqrb|(&bkW;bUMrrKIEkzNE9+8}@WZ*aO|)0?3{N`|MIX&`AbV%R}W=wX^F& zpILHCxSvuLU=iK2eN=&mEn1{bb%C3r+7PNkuga}z64}RRF|3m^zY}f71h>?OF)71z zg?4LF^TsYRc*2`ZUW4*5IPcqS+1Vm%>nk=Hd0Wx^h?zIJE@Jm-`PV#V6?p-96$jlX z1ZL3Q(KjjRCe;G>q6C%FR`p~9)X-L$o-Etbq7Z9c_fPkAPgDyXOM(W$O!gRLMXBld zoJ-0k?9&hB!r;9(HrtNaEHw&UgHh6=z;y(pxI~>(ET}T0l(p+n>mZgYGmb-iWNXEF z3d)p3q+PvZ6jqToo~YuiRk`PD(>;D{f!38K5vl_W9UV%}9|c33W$bgJhjQ*kVwpn7 z&6Cl5HiUMY82EAwmT=eDmGD?aYH}6>rz)i5?bI=yeUm=OW3c{Nh45!ae{1y#=zZ~8^wjzq?X+1GMy8MRVY~zYim9j4yCc+ zTE|}08C{a$@*da?BD1D#34Hf%WO^8WY!#Atfwqg4DjCs(Z5B7_q!Ge+In@fll<|;Gio~xvs2TVsJ=Yzuu-yF^_ z8zIihp*@!cwE5cC`~NkCwdp?BH?JOke767N>D9S@_{j(%w%axMgCQ7crV_a^RN(@| z;K7bNC@>$!nWN6Gr~O(rRwM)qD+uv~jEZ4-I%pX}NckYm_5Zxc@tJdY6*>(3Nu5xj zN*y_M#YDuPN>l0f2hasxi{?*}K4~_+Y*H^FcUF?_*ya}W5)}f$(;xY=e`!rj=jksT z|M$LBRGzNVOpTX4cv{AA?7tQVLw~ZUIM|7wx?hf;J7o^R>$ILgq2u8jvUA&H{SJY zhMR0xg=P{;D9HXbLE@pn$oBAXMO>DVoo+jaSZY_nTD~+bl{R>-t<%dnwc3;t*N29C zE*Sa<1yswO{x)rx%eHbf8%3{{qn72cx%}@dm8XQc#GWo^dG~nFD{4}4Hl2{ugi3~6 zSIJHx&!#7Y)t<}P$|DyRjW&u7dD>?oFSI1PIv=2F^beOaV3t0Gt3fwh;5><&+wLeU z2jIeGUYe=AYQ{2ezYDER^(=8 zi?a6j{1~;(@2~iZP!o7fG5Ao68m!-K%QZG%(Wb^H3U)J_{w}OWj#Y|V`RJN+(88>* zYHKjLLWxKsB%a>7Di6A@Nzuz^tuot;K4&6g_cD04P zA=`Xdb?Y?e(=?WC=*NO15B@?l2l8BBE%M%*YJ~LH62i5f>mPzvRhpS>?@^MrtdU4u zuLMreSY!LFbD#sO7HrDHN#JndI{oTBJju(fYSk7RMDin&0AHjq zFe}=u9kN==l?lZS&oQAoF?We=VeK$DEnI`)c@Z4jFB6~%s@nXu#DvjmgILn8&GQRm zVVQpZI)t3JadnnE*Q*1_XOZg`1_97VDUb-hh<3Is%O&q-S%krgF6TISluN=AgiLNA zB_@BP3I1X;KA%r5SzA8LRIOl0P9f2^@09@BOtgHGJk%YsE+|PK(6Xool^oC~?i=qR zMQ{UB1ly4!Sc&4|sAyT#peL7%-k?o%;n6ESboW^^O==srsq8_U)kj?0@-77U6bN@K z>@tL9d1}Dib^2@$W{cZm-k};SJ7jTa+w=Mm<5(#x96y%Fyba`$+Y{r}7BweWKR+$_ zp)IF|4I1;)Sx&aj8h75Ptk}xks4a=ntN?Ax$@0ZaKQNh=iF&yv|;(JUy@mAeFB1jXxp_5% zzp|uDSv*CgY@4r=#X`zaZ4i-4Sg*)?lFUTXovUa4`N)y;d7;zPBXg+cz0J2Rw=)-} zcDrE4-Of(iHoM&_5xVW_WY3>|ul;#W!IyC1^Y2I&f0E1qXWIV1vo(&_95gkxnB|rrM{}7H~(eB|*{wGHumG z6dQ@aC(&H#T(=)ze4LVjxxrSjI29<2@cI3 z1lZHj+O4FWYPFo@>V9cOWTj=WmsWU|+$WnBDTuZifvsRD!Cm+nG`WJdZO@SFC93d# zGJs^>f+G|6#4d~J2KX_oR6F$9&3p6{$hsQtecnvNZH>{qm|mxZwiIJe%3Pj1)DTLc z<&s5b)l_DbDg@tD?c^&|+CJ1_Dad7z)8ElXTDtMncJ1HDs#L>vq#9CTfPs^s>yUE_ zbsTi)@T&pcgrsx^OgN^`9apwamg5tuDB;kWY{;U;HyooiWitZyZVH8w6OAjx-e*vx zphmr}T&|Ft6V$8dL7B!9YmjbeDqvauW9+ZKEBw(on0Ip039&Vl-@D1`Xx_KD%Pbg zayic0RSp++fGKma+n6KiUPM4;{>-%(aXM<*L~A{nz+I@Po0O~};_5s23vs+dp6XQ~ zeW>TT@4Az$NA0S&7?VaN7Ja&tWecYPDkr)cAPd=!A)geO4t9;_antQp{^2u>T{n&O zG~WIs11zQf_MG>Qr}xfwls1F!{qxDq^(p75fEB3FeDj{9Y;k@^+t4(U#pIzf93|V6 zD%IFRuOFlG~C_*75ECv`BOp|x^ga_EJ$ zAbZ$cp;t!m%4uVvOyBvGUI|w%XP%_i^!Q@`=88LNBP1m(*KGl_tDo7Jy`HkEvyYRu z@+!Qb)|(VSh-g>e!JOV91GXV@02JIT|4Bl4U+Lqrt7T&k7os77D}Y$>4Bzk~bty2G zLo@u)bWd4Ato&Iv_sKo0ROxWO)Li8gaH$N0vt#f6Go2e#hJg+D?#K|AJVNPxPbpSR z>P4`<<>|f7flhWE$!VE8YV~58y|4 z^w+Bm`8M)Fwb`5ctvtl=;=h#eQ%0Eb1%I z%^B`q3a6kE?32GpZ<}j z3ay4kW7UEWVlz;luu~sF?~;v_e^@t9Hdo%2g$rPxJ~O9CrtWzrlz2dgaZsAzc(ef9 zS0|nlh!Tfoa4q5GnyYeDZQ?x5|e(=D5R zt6wC3Yu@XF2(y8%_gq{*y_`>;t(b{^idb3>jNHdnbz`?5VPzL6olcILi9X)Myys$F z%FJ(mdHZExwwlvMB&O8ujgzAk%tv`U)T zp0rq47F-QY+|OOGq#hPP2QcUUHjXnWpexPP(-i(5BOVK%36E6;a%> zV2|&2PZf-oQ#dE6uX^$Wc)C~%P`XTj1W=_6)&S7LdCXDRIu*e2pdgG_13UW4NDk=P z=LoVt1jwRZSjGQSw(c;_q_;Iz`w_E(sz(cn9XJH)kIt+?a#@29oS7L38W=2V5bLsr zE*S?HN7s$rA2{~fQXzZ>Ln5{IJ}8w@&I1cPIl`OsvKjN@M@d)3FVmAPHqaksu?bn2 z^+8J)1~_QcV$yhpPgOpOqNA?yO(}_{=<}r(fglNnn)x0`{=?e#&Vi&ph3cSq0WuYg z2D1tkY*^_FyyOY`YO_Hnjtho!h~IB935i*m_Vhd<2K7<#Vhb4=N@Jdm?5;f*SelM& z*>)?8fdfP#P%VT$OX+W^5h7_ijr(R$LXrKrVpQDcCn&6aKlkzSJ$)W`-o8afGE|1M z1>WZomQhp(+GIsyooVE=EDp&6JppWiq$H?TX=#REAh{1{Y`>Xn%65z;E6$~we{(9K zJ5mV)>a`mbdjK_DwE%}{wN)69Vmz8lSgZWHT*54ZqGU~!_PKb!xohnX5(cch8Bm||}-&L7J=GjsJnu+-e z=x807aJ@m__TUE~)d01&)6roU1_M$Oq6%PQBBHRUmh=_u zYK4O?ih(n&69c{dlyJ5isbU^|Udi_*i=v)zXwMj3Rn#9PPRMF&)|z4RVXR$Dy;;j- zB@R-1tz{*wHAwn{N!g#8lv$ObHbMwy654_CtDtbav{TN3Y9)X-*IsE*58fy*RyH`6 zL`h4zTL7&>l~A2qXZwm9!LJr494!{x3HmCKpTm3og3NJkQIzT4J+k+ zvw|ZSZGAKM5l)L@EiDR&EF1Va^ib?aVqP?AAn>tIMB>uHR#uxLI37VEh)__OydgqjQKBx zlL&jpsSTS}`g&MyF-%~oVu7Ou<`z27EksKXut~$xEk({POt0%vtPBf7oxC*}RF48| zE%68uX}L(s;Wl-UiMA{6FpWN;IM4u2smS&eLSmyU0KXz#nUdYgS8zlU;yEF~Zkk3U z7@x5cxv}^TTI7L372tIW8zE6vfmBcL`o~97Q3)-$cBa+o<;)yF*+x6vcT0~;tJrha zLQ`Wlbj@ll7xA_wJW@OU0jD=gHg3S$^dr1Xz}9Z{^JxQf|6(;0)69JJfFA?%89 ztAF8eD+MUKA6kq2rAWSiF(VfVDnPh_2kn zkE0)`ZB@T-z+HF@p*(Ccj(gzJ)ZJ+!NtDJVmxgk0RFg4l+QuqN{YmD6tQ`i1C4ZUq z3JGj&(pO&xKr@zSLkf0 z`n;PZ&*yEWOV(O)fZH&Wvc$CbiHgIRm2f;d`C*J)v1AA}%lb*$ioehbzMpP`D zoT{ER?RTCs6ko9DE-IUC`5uA<%Dh9GxgyaiQ!C#qsNq0ZBVL3n+~U*?z4GVBOIK)A zr@7AbPY7~~3O3uWrrHV=tOJZ)-S%~J+Fbf+z4+8IL{o&u|y@86a}DQi*y*&0ql*m#pwpOHwSaH$$O zIN>jMYEG;^36*|#hHPkeXIZ)PV;i*Z4sO0fCEr?ffI3g>=_t_IeNy4}$Bx~0U6)Dy z(g~S{{o&{I9!s-kuUYg;k0xZhOS==YW9?`x)A2W!E3BNAS*8|BNehJih??o|2!KgE ztgswm%0;a670#wmlHKiwp4=|2XR~^};tqOFwtSWZ2~z&D^L(9t zMU^F6mTHlT{e61pd6X#AXq6aav~$WrYti10^Jg=xWonKM(GV;R>^8}mqbvJHeMy&{2 z=P!wnfZRa>F$0a5vs7oZc%d*nJqOTcT=QXyeW& zU1B>L9F1b}=>U}yv8^;YY-tmiUOK)EYc83>jrcM}kv^WqtC(&~J#jI`F)w`~+aMTS zRAVtr{XNs2`tzS6r?{5?XjEL)fT|;Q)*S<{^k=v-pfgnVBCeRBayegL2=0d+Cpua? z0pke|q!2sedZvji3}wj{xhw1@uEw4_6Ej&=d3(UbR3F1dSINmI#J$)zleJ805*asD zPUGDQQ(Vg0M7m(<%^y2%v(9P7|8A_9QVG7fp9aQDc@g}to=T>MLFmhbp07tZ>eg^w~M9*n^HWae5#29o$+Cet(zSQ&CZ^Mv*dm|&1Ifa z?9cE{g!zaw{1G=G1Kf@4Oa2UBv}smMsR?vSO{|$xQ(4(=sBi6Y8m=4XlLT>ERtQQU z*9z=&N^d}`B~XJng88Qc?U=L{-PoSWG)37SS8m!G%39u(VXG&kxc0KYibI1UqBg1_ za7I@|LwPS$MmF_8MdV=c69aWSLVtLfs7uEWu9TB?FO!*j*Vr%Af}Qmq$Lv*>_eE60 z^L24x z#qs@)4@MK09ZMJl4hqlc$9<@n1*Rj+`YfSFP z*Z^Wipfs@wsvC#NTHHQXCz1k3H#?}Uu>sSI24!nz1OVgSE1D=%Qd1OdtJsp{H&IjL zo080K<9$!$7lOLo?eyfho77f+k@d#Z@jl1@!o(1wX~QgfOKH#EnNh; zJzjwNeqMnwUwZ4igMMBtN)b3Ym_55VaKan)9&Lf5OkzcVy|D7^&Rj7nr2{%A%`*F~ zg_emGs+|wqICGTi#+fBI&}3EDAhO^!V?}Qql#-S)>9;JFv^YwSULAL*K-cy@jB+`$ z^i7;^-xPsP4Gd^wEGWYbu71x_YUxy8m7pyO8+VOAvJS~OTi;-Zj;$OzE&$C_D%045 znb=sgBbHH*2}@Wa1c7)C*`F+w*|?QtL23a`I%|hk{U8Oz1dnn>Fo3`c4`TIFY8BTj z$|b>Kx&5UM-1zHa1doVim=Sooi0rW>SCXXf);S`~StC%5HvUWrt0sUBTUodGbvAVc zaw!|>*wx{QAKPL*(K1#oS~%L=!n`ED>rX`7Q{;Ay649+iy{dRL_xabt-(2?&P2zA*LrFs9}Nb@@&M;HaB&fpecQ1W zgq4pliC#)*c<#hofiS3^q``t>XU?KngpopnS86aDkj5EsO=}^0#68Rq`V)s-WhXdQ z9pO<5l1Nj%0t0ON5PZK7iV{#Ipli#poX?=@DZEh-tzXkAeBKxFu(DAD`j9pgmR)u& zFoD>V0vxvx#^n1{Rs1=H&_uIG@pcuC5>{Ijw`Br`m85ZIbti*?D`eaP)}X~^hFG1K zs61iTBKD2%YHWMgoKlrU#pQYNCI_8*HSs3xSOwY0b~p+=E=_e>WF;qBoW3rdXc4E> zv926M5%fY;o6G=#3$)1r>a$5y#|xxotx)=Je_O^*Sh;R%B= z^ztH1C5nx*e71bi9_*m|IJX6cu}e|8Dfq{*MeW6l8NhmELQBkIneGVA2aU+g;dOI& zQZ3Y9Eiv~>#adf%dG>ILGNr{nqazR+=$g!gX6PaPI{g|OT#D0R)^ZvY!C_Q2ZX!x7 zz)1bpfa>ZB6k!9YlL8b=GtzWNdz(e?Xt|A53f2;ClRa_%IT^%54}r}W3_x9HBYl9T zP%XWc^CqiJ`Fp;876@j66#p|LR9Baw)v&n<7)BjKi0UCP#w}3Itgqy4*ObW*-s<*@-Zq z35ygitYb4Sx0oTUNA^<#av88Z%%y!&^NXzJVOZgbz-oT!6)OipV!nZA1(kA*M&UtS zo;zY`4Qd3RF^%$`crnNT%j#~?a@2L2USM=AyGak)(qw(xO-wM1o~VmMi3yIS6n?1J zHwc)#K(fEJqmhWXlG$j80jg4&;9*?SmnRrlObk8%A{=madAtedJg7%jhg)vtWERA* zA>SAJnZ7pY)*185yhK$Cz94)K4jDRaF*1O23N(E4;4=ml9UAtvmFomTmoJ6s93<39 z5v*35qxd2rkPMu@($Tky)ktx5#*j12s68&V8yrK5s)G(i&XDFg(_#c>kjNFpF3><~ zt~KzX)7@4rEM?(CH_UKEO18q*uJuN3?SIQU#TehJ3DDF6Py(~rvxP^f=Mp@_QZu4B z#hb;|{RW>U4_;ZgC;3$BSV&r1)fh>B6=mV+5cAv!8aK}Qbbe`I@?1kbgOirQw!6Wg z#QF|{>Pa|^!-xXbg0O(n1~rE>(rJ`cV8F!41%q-LFZ81IN-pE`O#d z5nIYJ2GAN!)>4qTeA=N#@|f|ushtRJ)iQ?+!8lHnj59}(r4~!1iW~dBQVg>?#n1$0 zQIBz-l~JP^2AB(g>qQJp(NGSu^O9dNE8&u+lvM2GZ9#Z2^a>zhp;Wg}22%-RAL@-k z!O0g^SV_7dtQMOmk$mu@9TfKS!%9m+j}_!Iq$NPbI#p&Jh35GMTY@4pH88#} z;WCWH@?%ovq`_x=2E`ryzYvvC6I4cBGqeyOR}cdvqQGbmQc2X*UV?dril_y>j=DP& zFzM?cjjC!g^2nGsb)?TqMkeJ%J(w=)U1U0Rsh2c^6$YTiKri!z^d$-;u^Ax9lX**# zEL}_|P?1>z?gHsL(Y7Onx>;V*`=j zR`3u=D3LOXq8?1BH!3SZTpzL-4b=RoDX0Yr&1^6geQB=B5&~-U8K4_5&-YN!y8)5h zo-vYJSg`<}8uS6RFOUwMbUI@?yw1G{-BBJr(QvjunT{Ftl9dBHJw z56x=cus1Sv28cyaP>KlYDab>0`&`5H$&R!X`%HM3N;PxA!9v}15X3?E(QMvJo>PE0 zyLsa17S@vn@Vl!rTloE8`zfeHeRLA0Bk#jIbU8uaC#=U3giUpx%Wfii-E2buIuRnU zJ`jOT754o^#~ew}VB`K|Th6nsk!v23a>xNagfcI3P|gv-VqZGc&Ow4k=R4;o_)3YI zr|&N9?#U9xO^e&>CbRDkCL2HQ^PgyR^G>n=}g1&|gq2kg_W8EHJllwW|Iz7Yel{ zSL;eYRWm?89Q5mMqO+UmX6G#APP%X>(_*jz?R~SW%GqIq`dy?QF_WR)78T3xAUO(2 zdOD1H)sG3(X&&(03i5t>-79jE;Y>F)OoroDzxXE~PMmx=o%w(HCTObh*5)*kGnA{= zs9XQMxHq3f`sRDfCQ3-5t-?yb>`!>ZGQrhaaL?^tbVT@U)j?<~2caJp@=hGcIUj(JHg$4&(CEN(o7w#9k^5@4(x5@lM zQ`)zzV5qdutlH-~OX4W#c)$iRtO~ImkiyCACc1?^iQU9yH*qCW%(_D>|GG@kL}Fcv zXli#c?K3u*N+=7ygc_V*mw0}dmgI&J99gr8_7xDvSO_DM z#E_kYvBf2E3jrp1;7|xB3ZnrWd+w+5q|F(s?Lj4-QXsKN zfmx!Pz6ek^c6ud7UpZ^9G|060ZDK9fl%p=WL^9BVB#n*i*#)0*wHo|2 zWYv^3&5;1^1R2&`)D1n>O2wKu%jqmI8S<8!_>+$zv)5(B7Zr|o$omB&5>0jbTWC)e z&S!!=%XIo03U$Z|jlZRa0{oqHV6G4h3*$gv3T&+~hxDj&s(;kal^2ik0rs!N^+ z*3-MV!eXahoDbRC{WzWmG>tnas;Ex$y-3*lCHL0j5KZRZR-EtNYWcYpmFEt-_s@&> z?X-B%4x=UL?R(3Eb~pu>}B1xnC+NHm6PQN2b0z zOI%#C1}!&aQPTix{De9MY|{ZOJMebV}ntRCmYw9d_(uJO+dXEXKISr2) zt^P^;1oiFR?f2I&uO9!!wBR@2+V~%>-&eh>u=u8f3dlJwYk6E{i~~&n_qo%d;2i`%geWKuU>xe_08t=zwX}M?7r2H zHgB#j@88``*I(akZr;85=IU-<=Gm)f&#%6}eEsfn_b4AFe?I+8(+7FE`R2#z|9o>b zJ^6I^Uw!$-^cVNP{`|MU|6ud#_HOg=-PPuqmCrWGUr#miw^tAMn`ct|@1OqX^KfyY z{@3=|`~b(r{!w0heY2g9z(+lTw>|Nmmp^VlzkImdObfky_hNeLAFdz1+kE!N&;Gdi{^NiB;!l76?H~W} zn{D}C(>HSaYC7@mrx&$+#rzw7@!98J-oL*3_Uh*4<8Srp$#=iGLVmdU%Obyj@C)C4 z*@tg;_bK=PVfyG-FRpigGkwX)*FOK@i+Hg z^*-EAmmdE8)6aFk`F46Eckjl$*#4WHzp7~yZhp7>y$);57w#^4bNSclqeK6A`t~k1pT54F-h~-CyWjlk`qlih-cQf>!SRz{PVd*l!*rYJ z-%LmR^oia~hw#Q6rV{q&{&_8 zn=YAuo;TCSp1+FMKhB$Nzx@7j{&4yIkGl`}(?5Up``>MU64QSLu}v@g{pIupJzO5H z`tnO5zWg$~>W_Dq(_3)7=o2gQrIIi9@nUD9{o?cN;{S0qf0F%o_IPFdL}XHzPCs`6+1-Q~3FUS8cD zZ~dA0?9=SBKmTR%Qw0Cn7hmL8fxp;)y?y=i>Sl_BhwDE7{7YZ>y#IQA`}*qPPglq5 zgc5P(&-S}%lP$g&tqwn0>99XweXpiqI)6*^Mbj7d)AUcLD0=tTEfpmWfB4wNLLpAX;Wbm2eV zzkBgwy8t4IWNiPdPPN_k@9)ds-M|NG(i_xI(OdSCX)%>JV3AN_cO&-~L7{Wt|` zaE#`IQ{-TV6=pnRF5$m_M4k?dJb{@n-`&js$qYd~-$78n`r*5)o6S^;Tt8fWv`3?k zPjxxN6uX6=@SE$GAMBB@i{oWqeRqBTbgLPOxt)=%8C$!$I(fMH8@;-}pRbs1b^U5H zeIKu1U(I;v<;~_I+Rd*nPhh9po2%z9ufM&1m@&6KmifUBvbcP)!#QrQAD8~&cDni1 zo44Qnba%A}5w|mn_;wG49v6K467HwlynK1R`;w+l^7YsMw7I?8>|TYpuV-9k`WGL3 zbO2edAHRT^*7{G+AFf|dKTYyHaq+L_+wTA6$wzsWpw9Epym@_f^X2U<-_!PO;0w%OVbsZUWb{gc~ou714V zJey&<&BvR!(?^_ME-@?LoxD_ck1*2n^D@n0$P!Num2`Nw~313lZ^Uw?aZ{p$LPd!c@qUfInLw|6_( z?Q;LP(=l?u{dx3!|3=^5y#8_jSzphG%WfGdHq$nIG4F=D%cR(U&}kq`UM&o{TPPhSl(L!Hur*So0Bv$V*+f5b<(PkAkW;iucD{F%S-v+Yx! z{`Jk=gqx1FtG`}+_weTR++Y9$b^Yz`Or2hi!|U+%KNME_%hiw5uki9d zTjrzPFS`7jfBx#Le|i4JfBWyh{nz=T+v&%+d3gTSk8iIQe?9N|U(cW9KW|=qH=UhV z5C7%*{`PqYHa?e63x4(6uYUK%Z}x4if4RMXc>XVUw{sYp5B5Jj{^%e7dVkgaFaG@5 zpMLu2u*{>hJw2wdi zznM|JkN;Wy=Cd7IKRxoz=H=zX<@E2=^zZ)T%{TXN|MUL;|M+tA-QCrz=`r}%>iDBh zkH}v7@vkp`^YJ%#zu7;*pLdO;ZUF~r>pDJs}G+pzFocf^y}Hld_FcO zBXicA`ZJ3(&Pe#;dfaNYo8Il@S-pJ0PSt`+)q>rs1$$Ks_Nx{gR4hnaRSVLp1zFXC zRV9a3l^j}Ca%fe_p;aY^R+SuDRdQHW$zfF`hgFpvR#kFXw&YmO#Bw}ODjB@FX07vU z+c7=EGCWVStR2vJ#$0nV%e&TSY#H}adD9*RPgzAw&v)fI-|{!*IL}5?i1W4b!JA## zkkJ%kW2V^;4bQYEmeCk5Y~42<+lxOXQRAEZp{y#_&ZA&e9tCUnQLy$N1#ACNunryr zYtE=#6)Sxdtn5*+K8kM9N6{_%D7r--MYrgq=oWnx-J*}8TkKJEi#>{Nu}9G@_9(i= z_NH5&MYoP+{6s+ndbOGF4 zmN&Wbjr+V9qMJeD$g$4@D%W*e3l)en3U=Kl^d9WvF6DEW z%`3hm_c_v+XUubrWKkTuRYTy|y)JAIbzys`3){6W6L(j+1QbF)u3PMU*JerJ+Oc^O zxOQ%)5SOyYIx2gtqq1`y3z@`XNOr81K$2A*;qGNht{6p8-*W{(22~1zvf@}-4%T(= zR(Et~KEKwT8MMa83rkA#g51f4*FN~T%UmD#C3$xG{_4}mGu1wQfAQuV{w0K*e!94N zLq5Oxbn#k!HZnZjHRsq7>NE20%jc_$&sXsN-IsS?PTs!#@|J*Gv2YEK3?E3?W$LpO zOgF~Eo#T$Y4K#Ivc#>t_UtRs$xcIOC{_tl6a%|tQ{l?Y9+(?whik@IiUJ-q&xt`&_ zHa(}&?<<1_jSyaaxcYc;M!p)}!ty;bc3kqtnmDfMPiI855OHkcPYr;*IvWn1;pqwd zAa4y@pUyP*hJ0I?zEKwwu#~`8j#8li_U`M4U$4lwukU~(xyOKk(-ZL(8~7w>;nTNY z&i{wEINbwN95DrS@Z(R724BBxG!VT;rmisto|?^*SVDlQ_s+~XJ<-lihi(#X&A|iE zuM68ZX9k(O4v(D3;T zagbM5UPfTxKz;h~>6bv$ikBi&!<Y~IjNNRE-SQZF*%*7}G4``D z_RC`&WMdqZ#wZq?wqguFm%*5=Gqk+U&}^Nd<#mQ;>kKWgGc;RgXnCEX**ZhZ>kQ4- z8CqUvXtvJK@;XDab%vJL8Jev#w7kx+Y@K1{b%tf@3@fiQEL&$-d7WX|I>XBA49nIT zmaQ}M6ZAHcgFW{w*Bou%A+Dk6Q=%KLubFn>XOdgZT7Zl7;(5PETd&4V^3k*n?S0UTLt9EbB7BgZmW<&78f!A@vz=q)g zn+NeMgaQu)q5#cmlFyFMc^NwQ*ia^TknaC2G}i||9=v^f6108L0$6jvZfQYddaodY zwZeASTm!0oYmN}%gVbLbdq7;_V<~6=h%ipQ#qf-_fNl6UEDuhgxA5X}O|t&y+tj(U ziRpd0v@E&GE$AWG&$3&aY4b277KEhcAQ-@aMsN=5Uy%I9cnNCIBv;4}Di8pe`VIWHBOTFZjy>`s0-~vghKGU`&NYmIQ65bY1`Y;~7q`kS zbi#Wm_IC&_G>cR6KpLRZ_)~*GQ)p|Qc$Q{k2Txpc6u^y)xvBdE1ZsqlqaFeS&QlPS zsNLph0RGf~KVS#o1N!b@3xquo@yFY*@SO73)7Pi3|BA1LClV4X)<4)xLQwbL$5aBJ zNC3+$&tye21# z0449|$mnCw;|ne}r!f9#BmTn`k7v&>FT4}L<%`#&GaSUEN7^C>(j!w6kKnLKPrc;F zfmHNF5Il()7(0$1a}s@zUJsqogUo5X<>*>pc)l|O#e@2>W~&uAX$;W|+FO8IP09jb z{2BB5FM?N^t!@CXG-)n4ZN-&EudWh!(9${0x=}W-VLL6Q%NVbChZbn4$AxFjPES$V z1C)xUk_+BxDjoQr0^ps>;E@Q2l6%5So{8ln{ySJd5&>8-$2vBnq2tay&BX|vY%m1d z0+6%_p-C>m2ju~jaJmCD>6~0%06H&Vrq0RA0U#N3o0MMG4~m7h3~K`Mgzn7diy%CS z9OeM&xFOJnaT_|?K~NB|301)-!FuFoHxuECT_R?QqRFx~2|f8WPDt=tuv;e(i{wWI zQJ4yF5V^Twb8J+VHWlhBGrDpwfA`(j;%4`^I0UrgsBXF2~!$9<)DG_t{mweLPUI2hrqBgXo;_fx;FRV6*>en)8;`o{~ZL}rM2d8?m)cl z9f^1jbO<7(AjIMwC?t`ihu4xig$tJJVLcJ6Vj_wZaVyAP1P+bkVLbQEFrdLbhoc`m zmgU@GtSQ_|9t^s{3N_N;jW40#tE43YIN?Zq!%ZA~_}|UJiguJ_c~%loIhQII`x;QT z6IIcgdgRbE#KgedHEvA@EUVNvBg5BB3#BHf3&;hZA@i_!xp)RMFzYJz6xBa{{h^}zjLq2)>Z|I|n3(nwpca?MFeI+vxfi@2Ae=zL;PD@-TF01T>rYjk zf_2f2pO+Naf^dS<3J4+={vZl;=+>V}G~hgMyz8XD!@3&wDC!2)F0peSyU~V6Xd&^E zye3P)=&R2n@6}WBbP21IcAY5bGt^-~tCLRax4M9g#yk$l-}y`RnXTa_hL;3}57Hpt z=2OFk=Q=#&Hk$Zr;l0e)r>#0WI~Rk~s-8lK0ig@aGUuLSLJ$#-%DYfy`Bd9L^{a+E zk3b#vL$#exwS$d%W8B508*di|__Vvw?c~!{fbPd&a@=y}JCRfJ$#sF;8{c;|u}0t- zy6?EV(Cp^Z>|xXT#=K#Kva}1qUOvG-N{|DN$N#Z51Gfmgn5`^o6$zr2>|RqUm`Ho+Z)EiMES?(gGHTqRII-)!aF8 zO$btVAy>fEP;P4=2!!wq7tp|vLq+G#o#F1(t_7?ORb=D33$X&WhN7|gi(x{wz~Fd0 z)vbV;q1?{sQw>{`T=wcX29Hc0ih}d71MvbThxW6Gw{{|5!1hoyKEJr8ai`fg4ao{! z_Al8kv2S5kph-3%JkP1RNn~VoK(QcC(9&u_7K3 zOOR!IpjyB_F|<$rsrlYw;cC0kDqx%#8mEs78+_s$GBg|eFcz$UWh&<91$g9Xl~in& zbBEKIGiPMDxN#y<#zNRF1kb=eH7C=OD5p6nd2rVNeskQE5FdvxhEL2?-hrGF2t$YP z0(x|z`wQ17Op0ZY0&OUSXLDI`h1h;mZT#0Rb`rzO|dr6Qo36>VOtw77`m7bxw`jqe)LutqAc^#J?$9RTqXrSwnwf^YL zlPi$N;Ra+;!RYCAa+_(L+~JHLxe1q3#wLVSK`qyIo#G0i*z4&UVt1vyRI`24asGn% z2J_)&SY(^ASAD^(v={dy=PBM|%9o|Y=I3yyvyrk~=>5qC=?e5lQnn}f4k0cS4 z(@z<1elLYHM~`R)J)${!L@VeK&Cw%TL62yT9?=SVM050rR?s7wqerxY9?={$-+_HhtB>nr_Qx95*D`oNHRP^I!=HT2 zyqxJjm=7tkko>+<(j?O*q@C~n9lDHRMwj&TEiaxAX!jKv#~C`#sBui?1TMyLEAiM! zer>SiV>|h%Bp-Xy<8w^Uw>`njdIA@0i?N*IMzlr5H*3m#b|2x1DkoIgk_ScNgpf0( z*?ocAW4!sR)0u`}XLzoE0a7u;Ej;)a{P#j^ae75*2pyFQL5>Ig1MzSYp*RsJ<1{1W ztkZ0jA=r+=Hz)?V3VGg>T)N$6y9`~{9OTob1?cvh{c?0W`>Ai-Q^m-#e6p<~WR)Uh z8=daKp6CtARWnO|S+4x%C|0QUELH7kS4K2Q#$BQbzSv$L3fs+gNw=k)Z#LV7G(suo zE%ml5Mz4^%(~|C)vYlY3kkZpqX1bH%4<7dm*-qO3XOBePas>Ms1Cdf_AnNR`hMmsi zIz@fitn_6o(wDuR{veTVQ+|;3dC;lo6m?5=khEg1SlK#e7_UJwO)E=rXZze~cK?|a%ehL5oc8mg^eqWN`pm7lK93$TGvwhN z=F}XUHov9Mz`;2$StgNjc)IO?axx^a}DMQZ@oOULRmcRSIr`ApB6e zIVRprv#js{TXUe0Lk$xt8jX=TG5N_QF_kI~2WdKH9@T6!KzN7wF5|;>bHn8x=giPN zKD~OVjqr&2Ek7W3zz@XZvGBQtC$Zr5Q9V4C6&|~zt%C-?&Eas3^g#N6bb?RN_=iKs z@iD>2d~QJV;f-6CVNd+27aT+r2Y=R%G(p=WE==&9IdPr4CgJX7c*bqxt^1)tx9_Fx)GNJ~w z!^3wmReo`YZQ#9j0k2I0BjsDi8sPv*K(bsjxB??gml~g743A~S!kjPP;%p?DH=>4! zj>@%~Ed{$ic~CF9ydrt1FleGU&nLcwBFaE{Ma1nC_(UE#D{mcp zjA>JC6PmW1XX?!(LBcf0SkNlpBU$l4rH0st!Rbu9tMRs%<2FWUJ+W zELS@Gw_37h3%=rADXUDl?jIlSdkBaS6q7cB&mwX`!f$+Tc5Bzrbahol`#~xHSzrAP z-+{}nst3F^I|$m6K_Tv!VO3R}_yyOI7wBSvwp@TU2k8YUFVM#V{bYeo76nRQB%xV4 zbW1=h0d%9&%w3o5I&l@4$8} zdrDGRdx<{m(WnnwFOq;t0 z6Jw`SO(0pi{bacU*)7)TgF;|T*(+Ev-dMkwK(axqKw=u*<{)9LXgABGunh^$5DSFu zWLcnY)ZuN($~HRfHN$qSw9@SNrUg81NBY3W1Qr5zHUJ|wy9v^CgLJ#HP%2e~vfnP$ z@%>&guzoSHVj?~$Hl7a(TFF6YyHIu$!@1JgGMrPXmqWvsl*fKA0o%)x-PrcAlu^IG z#qzQgyh-CbP!e*!2D{CocQ(A5?Xc^zsXMwepI--?#Jr4sZNs56H1yF}*Z5{Fu|qD3 z$;9KxoUQ&!e_N_{eomjng^$U30q!}+e}YLPuO3cTjFP*WYU7$bHB?_Ko_cTOp}6Vg zb4Ov)2Vn7C-GB&O@H?n4i;oqH(i zlej0=Sl{fI5*@LIi}y=fnt7;S8>j;PAB~E~^%FcercnVL0*XFg9iSQcEgx65KxP58d$fiZigV%0qnKR)v z@Wnt8H5a?&MmFcJh5-(ftP3Jb~=v554 zuUpb3nW%PM`UXqO&|d@B1LQ)4zO_}_73p!=_0ezNx24CFir$+p6_Kt`hx%B~YrNL%E!-0*josRq&Tc+A1+CWC1~V5#beia%%yyWHnp{n2{;`MAV0S7JW>%BTe})GSZ?|L^RmYuT=)pfJ-k+ zmvzzjljl#=RN<$%A)+9goZDE`>_%FT_GJ5L+H(^m9c6|7&)+VmJ1t$J9cKB`+Y*Qvh=Z5!8lgSPr#+w9Nc&)$Eu%v)6)(eXkTmdB89 z)aRb(U9vLa*Q?=U+bzc$4XtD*u4E(JaShz@RowB`&9TFAt?@qvMjUIk+QV+-xW}1} z8GLj0Uw>*h>wXrBe-@lGT-NNj%zs4Vy{_$%%<%3IN(|+#5>YSaHU*GLl!WR z#!lT!a84f=QP?WfF_F$pCGvAA0bNQ%HZYOOgG!1&_-Hbdc9y>cOi5OzJ&8+Nr8|pL zU6enGJI}>iXqEF+Aw@_bN?0^MrjueyJ@=Z|1aRrE%4oSyD+kzX`xT~#eFzk%c!*3t zg>G%wo=<)@WnDHuT6I@8Kdrl4{4OZ6Wg1t{L}PrTV+O9Cfw2gZw?|`FHnS)`i*@vgwceY5ncro0~tg&+VGCKhp7M z_P>!X-I}Fbz5BJz+HZHxj(5VdsJdEP7aCcQG`2t2GtKn_&Gi#?^`1>Nt(<#uG0*sO z0pDE0m$=*;G0$nt!+(hUAMsScx0di_Ciga;`R(Soob33OqPO!&lKQq7BJO6c=o2Xc zHE8K8&?^`maUO=GeQ*9NFhBeSE4tEaOlKE#*q-*TOt6dvvbxR!byBS4pr?l{$62S6 zreFqVu*yb=dgrL2W3#OAyY)0@OCM*-j}t3z-7V*UJvu4+lLcsTIce~S1+X!_ZA=g< ziJ5UX-vjJYSCuB>KxMm+J(2Q$WvbyK6((>l8^I z;%i4#XWD>r=Zu3JRJ${Ta&XXjMXafxw{FWz87a&{-2F+Cp!7Ui{@H?3ts-yaItqj! zfKf!gEINr*-x_XheMTL7DtaLRMc5u$ecvF-pTP=9sqd)7z#*~5Z?x1uY5_muy#X^- zG2~v!8bykw1_D80V8gW~PvY5DMYJ}{A3ytRFSd^N`sG>*?08J7q0!A%fRV2 zVzGANOw%5cc9W%xU)lByiu@yP9t!HZ4*2R7=ZN!+K$qf` z37CU&Cs%6zn##WxmwR<4c9Lz)(M#N2?8fUCu>U>2;sK_@Y{G)`*+ydba2^pJT@{?K zrw0=4?WHigq{Z>>zFTrCHM^(=S$lYD>HpgzRvD9AW$2V*-%;5fsVF?5#yz8~R@N9( z)dK#G(ias=1ysfGYaxItlZB>MOVKm_6!-*l={<)KdhNL zQ$_XTtB%g>>0u_F={87B{i`(duO5U(?H6S0#%x*>MQ>0M`D#bm^3-{29HpTa=&o@{ zrU9C9s<71dq|B)1m32ueR*Ms7xJO0tXJcEUajKWpw}uO72A7aAjpW=*SvTq)eMbKl ze{a-H`;0RGV<(kycXXI4kpMN52Gne^A2+-z8OV{?=BiH7o*A+SEm$%az*5DxsnV~h zve#TIV7?Aag*0D2r!`oHexDsDBw{eco*j6uYl_=1J|PbO!f$xocB(OC$bsj0jSYRf z`}=mXe)ePUU2U3vx4Ot2ov+LNTl@X=HKw`?+sol{c%MQ&r|;LBsK@KQ+_m2417245 z$Lry=DZTE;hhRDgTlq*#W4Q;dij3%XqW;^-_IA*$Q%BH|vJm$m<94^t_j7Og_x&~B z2RKmBeIcJ7+}1YzhoJez3Fgy5DQnkb-in?#`oM0FeJU3LX5TYkhtK`l->0P*fwZAD zlE{YR`E&-Tq=GOUh6 z3eYzf`t7fq=jXNdX8K%_yGQ|Gx~TyzoSpjLfj{@``TG45nt;n3PE6(t$NOQ-u!9Z^ z2Nt?DZ$s<@WQ_$!%Erum_HK8im*F-|SCvP!5v&Buzg=}Su76}1L2*z#*s#xKmUASc z;9A@pIJCC$j+t=L)76@+iByol<9bttbO(g9=>e#Np=QbBb|2Bj9! z(jiJ2igDA4?ZI%uhJ}yedNpk%?28<3x#xPSXDLPBcAJ8Fa#xAn^OOZ0idJ~I(owgS zWPqfDd*k1`7!I$-;mpyY+!sCF0AZ>__sru}WUd7Z!Pkr8uN$iZ_aM1G4?$x+BFFdf z(i}LyAZ6a{ZRgM)$QYde&NP*bcRZ4aJl%qz%z5;p5;oCr-k#|Cw`UBJUo#${f0~~m z8|YhB%atuYV-E>Vg2uSTE>EPq6-)=nJ%INQ7)V_}(FBPo4PkTC%BM`*ntLWPLhTT{{WIc` zMffjE@YvU~ zz7Uh5PbGIRdKyiy;vaPXs5Co&PZCe#ptcxQ=oQJ?H zW;RgXd`&-fGg+TdyfzNOvb9f6e!Mo(4F|wY3kw^ewqJ`soUn+JJ>(WKwNB$ zQp8KWL7C=y&H`9_h(#&gHaie65@)fCt=4u`Xlktx}DC@o9@k9Q@`NiZ*ed zhk4h`6F|FRSfM^ZPhU%@n%Z!pG_Rot+O%*Ap9Nblxlc53aHGs8wvRU~@z5|T zrw6F2tPwWY?dJtmMjta6@}w4xCq2*{uRygRL$Ja@$$jZweY`wG9Y7aoK42;OTKt24 zN$2nr0Wcsx!xu#MF=mWwy`49%DQakL+no_f#lkX!U+^DG!TcP3JCE9U8v~@S5W{ca zp&+(HmO*~l8S{@5zg)_eN9Bm(EgbR}GN>x#7E2$*$P_Gu1s2WdeH08kU4Y!*`%V0S zHz|N0zBs<EZG`R`ppbE`G)65Zyk{F7CMY*4S-)SUBVKBPZTusL(P_2U}YBD?)kQB7p*C(S74+wiby=r#KkP z;-4;L(&q!yGdZ<`30a)zF(k!{ye?+wqj-T22`nvBJ2hxl*5V-J;1ngv3gJ%$TreSs zVNopreh8K*--gV5NbB|qH8Ut-BN40sjY7G6xJ2uzGTVJfJD&F}l^u-?ry%V>1MmG#@O znq%LP9zjWI8eTttBo231np&kcgI4n;OG7nj^|(G8(6#X%13SZpX-7+0J>4+(ah zu-7w7C>Eq_VEMm8u+Db^=yP}43us~3bHzLQ&2YLto|S6q_;WZXC4nr86iU~kQsU{~IyC3{16=nKS%&J>LDu%)W%TLai<*y{pc>Vu|a2m~g z?q_7oVIs#TCz$T}t^TeJQ1NknSJ8N6`J2_VfFD)$k*@y*AYWdJUWq@{E&Do+wRWqH z9pzd=&aiL6>Ql1>-yOXSpO3hc&X@Tr^+oCWY~q%@T?x%<{#Cd|D?ES)9W2b9?p##X5W} zCV!SzJf-$~860R5$iaU(NjX&g1zn+sBndyljNfg`-6WZ>zw7P(+zLzd9&Od%9_#o9 zRaBgNp=%xwVE@m9Oi||$@kEJ?9Lv74Q3+E7;)m5j^^B!rQdEVKlSM}?nmq;pVj`WS_XWvx zOO~9aW;{_mK(31-)%;dfWbns)pAV2VR!4mBKaAn?SNrHI2;`jw_X*h?Nwq~14sKB<&2@~f^%wK zL-6^V`F937---xKAX1q2wcy2+J*>0$usm_?lu!~w@RF!QarPEes-DYq1)dO4czq&( zzNgZCaKsf)8t>DdO0@()6m%Rvr?Ov^F7NdKcW;jq@RAfk6@(Q~K4BM3ufb3VF%0u) zJ+F=MvGS>~5NVaAT;$9BJ@Y8Czb3VY%2f^?OQi(Z6;NbtRUKx+_$<_V_0jwMsl>@x zc<6hH+f^iMW%{`sji&l6`TBaoA~BO{Ou<2@Yqr7InrRibSaS}BHUZYwbBYRH>hk?3 zUjmEo;7*i*nI53N_i2M6J3bk6v7=oIgj^n#i*rKBd-> zvuXW9+p~tL@z#S(gd5z7@@tfIFQ+OW_L9k>C^@zizzPt2UnBrMK;;wAo2i)jOUq^2 zA0BHiIk%M6cq+yilqWnY-+3<$Dg!!!#5=zmSEzkCWjg-oshD=Dc#4Wl|8yxD@wzAl zD$;}f-*XJO-R^F$ZwJZ)o<;VN$XB@#$BG&G?CKD5s^j($ePI21#VzBZ>q%RlOdkqX z{bb}MeDEF90^AE*FA1bqQU*G=t-&moqf3U1a19-LnD4sGU5hQl^5N>8+=G?FkXZ)p zcM6Xs>pn2(gSaE)<^Xjk`cIebhN-8e&LMbwmqu-wjF3GQlJ2dDPT^dEL`zI}B94U} zDoVJH8y$pQi!aO=Xz4PiN@C2a;e%fN>xm($ch;1M9~q*Y@1*NV_DFzHI??^7*srVf|ITz9_egO#6qp2AiYZM_gD z@*#%M%_<}{l7*472b*(CKoWR`%nU}gK~mrbgt0*vJs&soMq=6^_7~{L#&gV9dFf;E zVbbin0to7cPwJ#>4T^g_di+4`N?JMO=|=;KKL zPOyT;Ppo-pVVvlDgaXcL{ln6O``J&nyL-7@CsC62aC2VoTXXHvC`4)K~_j2STg?2iTeCDi#x_lPz8XfWO~K7Y>|4mjKI3sy0-Oo%kP zx8C@LKk)gpILcd)XWi%EFvuJ3@T_0=IKP?Qn7@n3DQV$1$?# zI$HU3_Zv}vlkWjgSZ zc3s|Z#jJF}Y*v$b^aFkRh#@6bxsNoylPEwe{l_tTki-{ zI8NNnh^zRU@U>2H4rQJ+G4uKhNM(pQU&`$*d3d{Rz`ac74hDBR^gD0_Qu~A4efK=) z#hE`sFKSOs^}|9+uY2&pa}iRP6}e?cUHSlp&5)+fc|LbT))Iajf{VC<2tIrPNycL+ zc*zJRCJbGU-UP2JbGP*iRelgg$n^&O!-GaJBMT{B(6mr7mb~g-+>>KmJ_9n*Ee-MV zFUP{Vvf06o77JM`l-pLqkb7nBY>_UfWBNHM)FY=*u}zpmHQK6Nb)M}PGBT;j#W!YVXb};81dNXqOxll7I50WL@z3_T1 z&t$8UM_%fuc&Vs^X@S9CDi$mYA`^YA7z@tgYx(e{yH(_X66L`^v+{aL8fr;%i5g|V z;%8tpCqqL)B&uPW&pZ^`_!QH|kAocPU06Wk_VW$$*zpR%|0_1Q=S0ysG-AU`#R8AIXgr&_18C{ly<~OPp4@L|J z;|fEGExDq##G5I7;$Bx-xTr)_%P9>_^r%BiXv;X#7p3F!hfG?{PGnFlJVnV;N(@^s`i#><|d#Cr`iY`_?0G&(d_U_>81??gsd(xos z!r*VUusj~KjjywFgp$F1S>c%b*HUVy*1(`^qHMvkROuF(ZZutS?u7KsY3FN|Y9lMA zx^sV{kx3%Uh}StDt=uqtg)7+-QkztS0YV=$P(Ql?)BBtD&IKq${0ebOMr0}F;_*yb z9;3fXeRr+anOn*QqdttVbB}^9082y#9Yw`3Z(H~PG5Wbv@*|TsG*jZnK?3a1i;@i9 z1x#75+ZEtQ zyq;L$IQkW81OK{t?*(oSoF~qXPtB%L5vq~C!_gm-B=+-}glab!0$!(!Z%yh|a3130 zr5>}p{pJVf#j8 z&I_F6#q2&HHZLr%0|wfz7mOP<5T+-O>jnr7jqwMN({UUcLAR$DNVe8jLk*ZPCSs-m zM-zR9#t$zQZMsYY_{@f23j-w$E}T0@@?fkjmYPs5H36rZLWW@eQjTTm;z28Oti6(AERXHb#9zhclV zZ4)9sd0cKyJT`f)pj}cw@e)P4(t1IGSo|y*iKJ*2w++-lq=GX}u$iJImXbSlD4u4%Q=B{`}5^vG9AcV5>Ql6?PF>EzyfMQ;*PsM^A(`*uOHo3w~wwkaEJrilBn(z+HciI!lhnZwyc-%Flu`BD*c4qQWx>oJC~D!2m>ZPb-RXb94a zxgcUi4jAzb-f^V7ZRmuLf+RK5{*wH-vw4)L!n=CZU?p~WMZ+vxsqsEfAM4wyymLL# z9!o)kLF_D+6GdMRQP6L|MFAxfB>ZG~E$93OU7yLOt*< zwfXbMpSr9rT0Z}&bv>Et<)3QqPSdUaDO(?wPTil{`m%6W^kLyK|Ed2E?CM{Z{|&qP z!Cg!D(q42)Bo#S70Bi7%(DW3Y8_xUeTtBV2IW+s!)8E0t=-bw#W;kVbx3W$Y^j#hf z4mq}%`MH^z-^f07X?yeRwhB&bQgIUt5%(U9_tHg1W=JOo+}V|Eo%dJ!9e{;wx1HXZ z8)oSs*hQ1P(>5s;W5yAb%E!-!OkO3QD0FUGRejU!@_=Tsvp&@wq1X3fTOUQPqohyV zJENY1`a^Oo6D}E%Tvx^^I!j|R@L6kW8!1p=!J%M_30gS`ccYaxbEMVje0QHE;r1A2 z&@%qhe+dWnfBCaZ*^DK$qupPG)A`Vdk{5(I`RwgOWp)A)7uUi9*BZdhV@NZ@#N05n zpGsswH8T(RDT%i|(j79P@gM-gD#fWL7rz{ydLiT8n&f6!vZ;pRl*lYYCEW0!yyG*_ z#kY$ZdAkvRhq9?c+VCvym9h;(c7^$$gIulYf$SFsckwgh3{&11q<`N$$+b-C9f`$e zQtG7>6P~`KJ3Fd&u{Ya^iL55UUeaOPjFsuZuV0-REVi}%zL66 zsS)44*ppJDYV-0mMhYGml-lar?fiIl$x8_mRmi@2(pt)@z)T-DZ-_MKja0j3%m6Ij zpS{YhB9|mg^4}i!d(6GkY_bNb;U!UQ`JCD*HCV0c-7Q^(m<}GWm)-<&Wi5!>5uWBM zpGo_36NL<1_Cs-!&#G+B^7e&|b2R&~=?J3-cVU|mv*2L43d0TVxe**jaDcsQnkz85 z5<;UJaaa0A%3x^$nbcsCkYJkv*-8gt1nP@&85)u$!bDzYG$)15VQ>u*c^3=Z_F#zd zar@198tzu!g2z^{$egTxsRkqYGzYNB1gJ|#R=$$PTxyWnK^GIe9INww<(T*!orzj( z{|SiPmd#Vi4y0fy2w6LSI6^Tz}Smls4=ZnP;+eRIfQZrj>r5IGy`JLA~w&LP6<+FU#1|f7!^P_tE=n z7eD8DvaC-Tkoq{Bt2SSDC-g-1GxAmV{#OVDvmMhwP0zVZfxU7u3$Q6zCv@^c_2KEy zwgYqH2*HQL_=NV0TX4_~ZK9Pv+(-69v9k5Z6%p*-*pp}juSZDAyA-IVuc3f*7r_i2TTxd{kI<_t*xIGAL;**&%wGJgJu=pY0JaMP?#efJ#j}?i# zD-NCR(=3h3HF^Mmx5KOcD|X_zzq?zjI_4!uDSPRAi1O}u8S^#tBFT6mRk#ICUm-Zi z@I~)eaTkZ@A-MBx?o;Mdk8RTz8Q)D*v-)>Jmc>x%ko+i5knxK$)1%%1?#lLWY8G&s zR&;FJ5})Rt`$nEB*<}^F!jN!Xnc`j|IN%!hBsxXgv6gLZ>Me0K%!6_*~oX)7AhVgp=hEE@gyeyp3ES(}6MR$>avEG!=Sk<5ywfAQ6k`Q%r856lv z^pz_AZ+=8Jh5^o|iw{H!g%pxA;^uNL0!<55W&3tRpn$*-s zBCv))2EELiMEP^f+;@cU4X_F1hx~k&?w?AiJr!xogQIZS=rvYve^Y^7a`#Z8EpUKP zT=l0@?GdUjBDD5i!p6~o0FQFEBOgk zmpRDmJKiwUB!Xa1i*z3qepQ5;XpOs6E!4Mc%e%yphy1f@+o=RVF_ikdz_$~~47?pq zva@Oft^~kh`CRe>mEVgEJF!i>6*&Nt6W_E@1vfa6k zdkrY!sa=&*B{YW@R-Uv{CAfsG%iPx`SAN{}`k1Fi>veW`di|k_J}wZGCEGZ~m?|Wu zBmxED)}S3Gq(E?0G!KuX3+hN(l}I81FLeEK+wp?i%MEtXG_V3$Ld~{RcSx(Qr-G7< z{u|~+kF{;#DJ8(E?dTT1OOHIkYIm5=qJR#JOc$=eAU9WUm>00xd>u~iFUywjTh?{% zizLSU))zs}bu8vkxQiWv_!?6!8{QIbY|Y|BR8a1RHgU2(1~*gk!45nR4{|g<$ZNXM z$=a*Kw&R4%%Mnc%JL=NHX2tnuFuYzYv=5i=?aB&W((>gh^KKJmn0x-<4{mH{JeB&i zX%51>2_=APc+81kn2ImfJLH+)lHUExIDtqvS8qoXb`x4wE|n6W-um6|JS6t*;QBo&u1HO`hd=`2+EYk1e(dVTjxdHrFa0!wKyReiTr zx9!p?l*|0`egUo>7A$)d9Q!0#h6#8^1t9sk@4Q8ixl@lh+fD=5jw6;GC$3!^Ec@mY z`}lqDk1?qJf2BtMZ$MMt4`|}LLHzGQ)8nWi9NF;hW%kAa`-bSY*G(Xu4@R#yu{b>O ziUUYyKg1KZz>MdQFaoj7t6p^i-TdC`RAXr!Q8HZ`+FWpax3QS0xEWgS5O;rXI*sxu zHGl0~2)!i7xT&)&9Z!5s7rz+4Pgl1{e6w!RZY_52>;*7>C2JAH#kQ+F+ex2-a7*hy@fGS?M)Ep@G?HeuQTzco?LVV?f72snm(Su!uPdAx};wq~#^l8qJt#b7Mmt1g$%Z?2+3<7|Rl}s1 zM5AUAR%FmRjA4LFpQSs&?a9^u5J;U9jw$N5Pyh0)E(kR zB26&mP-HPZk-J!=X=J9l^d5K`gJRJTY?F?4I#XIYt6CaI5p~hSjIXl%8$*)i?`4}R zPneulnR0IV5=(KBWs~A^$*KQQ?O)4Q8B{IHumK2%Qk_Ys7k3+~|y% z2*{fah6WKC%Ub;#W1V3X@O%j|uUI9OSSeM0^puQLA(hd{g1g9B84;CAEuljBSXJp* zp@rbGeEC>qI<=+R#d%^KJvCisU9$d&rY~iE1+kC445k2J04MN#?xu|db6Lm)p~XRE zbGd?mNh;Linb7i9(##0el1g%=Gj0(JuJ;~AZ6j~P<7VZgbfu))_Fr*Tt%6+1Vx1(G zvy4V2XF2+))tzS`C8cJ4k`vKhs+r2ZIa%>tS{5kd$*IU~Np$D?qCYO|tKH^Bx6 za5Igml;bsid`6!q6qsF8rzniJ)!{q$hulR;Ok*d*iN)s)?DkqVFl%z6OWkwz?k)AG z&R-hsTk4<8CAyskQJn@>rmO|V7p-ifLbjo4iplk`>x1zv%XGbEJ5A@vrHG(VtQobm z_R#1RBwcOSKbau#v1^Ujy*$&dpAZM33a86A{TOxitj!^d%Wo#FepUbR3I~zW(p(#^ zWB$$^wK`gHE?eQvwo{7I&H)QGgQztF>elCy+IpzPkFemh)u{(gwbXh&wNzqg6tQId z=!^Y78+|%qcj%htYSi^CCmdH*uWMTYhbrZ?oK;AR&B&yU$|vj|c32BWz~J?gxSI<@6H!iyJAZA(TucUl?BZ^Xr5EezydXaFOnK5D%_jWdz%8kp zuwn$S0b79{`JF)&2dBRMgn4Fr)3ns2lNc~5@A0euf&*=xG-y0AcnQF*&!%mYo9*Mv zlna74#N8o{i0Q;+>tyKqgV2Ipgrc~x=4nbe3Q05!-PUFvL=P&Vi~nJQA9E}n3v$ea zty1j6!M_fvWjlk~J&a^v)Y(HcTBosH5xJq0@gg-n?a{c$udHRyWasOT8ZBHHA{@RG z@C&VREpkFTA&Qqvyv_MF6aen`MZBF$9kTXWWj6jIJ1}`gT(qtk#=;6GOQR+;1uN-E zAEq5-Zc1~d!zg%T|KbfKnlP4YeDQWhm2KR=h`%a0M$~0SuFlC^5#35e-}UrQ;hF}< zh^M#%^&pj&PliuUBM))R32D|F9hI=Rr0jN6;52c1V8GG=RPuBsIDed7`Ydq#ys3si z|Mj88H;R02VV#h|AB+Wu4HrE z0V=&ADAlmInv5O4m=A&EFt#ek3^;7mf(wO;AuNk)gneiLz>7w&O_FkMSm!{-Jhg?@ z%+8V1O?+*Fr&fhcO;1WRbSQ~2>C7jTA>y3~#RU_O(r5S>SE?bkDM_)k5{=KC5=TlS zP^g*vfmoIR6_setC;44%v2YQjp* z2P+cYtXk8&q#6@jrYRKb0QNL%qXL>c3K)fMaaTCAhG1e#AD}gFqb$lGQaE!uZxe#5 zsj!)Cx&VSj=ddY3=b%9DDw56&Zj{JWZC+fbOi7kSc48E9lCdXcStwaiP6&v`O0}e- z+eSTS%QkNpw-&`!v643drAs=~9oXwog$tOKekUxs|s?nmj7 zMQkad*HTqeG}aMcR7EaOMx0`x5s6nN@lumPWDU#PHB!_hmW4iV6bOG%$PlqukZDyZ zfFC>al_>_WT2REJjxRM(D}!w%qU1bQ7EBwgs)dPH6>7?r?W4N*vm~4qo)<$E51Az{ z57L0bqAQ5@#yK$&W#=hFP1|BVN(91@4Wf*K zNsF+miKZR&`2el-thKK_WcX8tL2GzyQ)x@0eqsPo!KSu{VS|Agh$&XRbd8n>dXmAi zhRtGNHJ8Y@zgDrWno@VyE`!5*V{lTFiBS94Yz-I`nRA@~rE?}xsXT?_Sk)eRIiQTf zuYdz7Qa;T$svr;QTif)#08G1)2C3%jk({X=9uNmrVc>C5H1;>yr`aO2YkdJftB z()Ju7wxW2vHwmv>9Fq$xaI#w}4m`f1^hiUSWA41vYg058tht};5|qpSUjMzb#HZ$p zS&o|jPQrnYINg40S>%gPy-wVjr=7%qJtEnO>Q(mJg2Bfc#v z0*~67J&)VZ=w+M=%z0RFO^lr->!+|@2m)s|tv&mXMHU+!PP5i74 z&=?zp$Ut1nS}W?%_faO86pfn7Q2I!b0sRgL0c^csMTRQJN>@T>$FZNMDu4)|1O2Rn zz0x9F?9}PlTqaah+w{r{&ZPV=bJtzVoxYT=5_@+qhL}Bv*J>>uhQ2Dz#N9Gc+BCc9wJdWW3;ojtSz&jTbG{m{+X{q;fMC3>Hzg)d4xWd%>)Z^rvK z*v0z+2;}_@A`5)>#mvjw{`KSY-qiF1qEDl*5*{AH4%d|_w(FKk{m5?j;Z54k)JqRp zZdD)Vj1!t6vQdKL)<>I>+sP776A{T09EiBKtv^NtVSKYJ9p>bT_;7`wtZX z(>DhA!E`cwzU>hnR_(F=6q0pLrt(QgNWFwaRxfVQ+5?js1Wx;<*6b(0bp0T8#8_Qo~&yaP9M-=bo zsX0}&azdARbZLujrDi9G8T^9%--e3l{($-)=A+&HXC|baqx zW%~F*tNov*iSGJsKVq>f>b$7b5I6=(%eAjnI{hyQDLlW>K)-OaJERR_(m74t_ z^4d<`GWl+c*N(e^`u3;Ed8i7Z@0k7Nd!+hDYW5v59{9 z%jBDpI=lI|BXbu=9icj0hej>kMP1m{O0&KUuwEoE zW5)gjw`{jb+ovS{q$zQ5MXlV*FpfyO0zHx>#CAY>jx{&bSI!?B?tip6A}b#AdBVr9 ztfKvnH>VuD5kw3)$8e7(dX;ESul8mC`0aw@Aa@H^Jnz?!KhAZVr==B8SBn}$>Frxt zptDEq!`qeVHY=9eeXbJtE?N=>&mb@(??KJlH4Y&tZ6m3UV{iXKQnrGFVf9S%k^wq< z7pW$Y0g5!BX)ik%=|i*L<3rVF_PX@e7l3&b&}UAIiYIh={SlNCQI3!gDQs902A+1$ zGF<|KgZj#*3qs2)g8=zrcSU|edLlQYu5CA81X8abW)#+6%+uA2>(cOd@Q4fXwIJRX zxP)!O`Eg%qGCfpU)=XWl|JB^MjyP-Z3@#nzA2t@Jm|RV1dw6@qZCA&4An|j+-sU`w zDkte_4R|E6{fuMOX~i72-hAPswFDA zMFw=kL~KJEknKElU9rd7X~3F#r-^IN8O4qZ*S;O9Wz(i5Y%5YL*gQzfk6uEoiv-G3 zg7c14VMl5%jy?=XFFx8+0_7#%@jxoCC#8T`4+*r31ot(b;)e8eBt`gtSPFhDTz8}@ zJ5p_U23LK4b+^D3qt}Schyc~_EMi_RT_G^D^IoR(+Y+KO5#c}D0 zI*+in8hleIaHk43UI`0h#5%4q_Ft;7;`&hoTl{K} z4x&+4tJA0Y-P;yF2$R)jPz^$0KFM-VRI5M+2_ZnX~SFtmfa5(>6P4tnd7Z@4QuyxpU7bmR%hjdnH(gDtLzT zPi_xbw)iKv2UxuRliQ1_|8UN~e6F-({eQ;s{?EW_!%t!locF&6t0!!&%;x8^1$K(@ z$zol%A*MTflG5>Cr@X6dx4hstjICf2CtQ#N$3P%gSPGgWCn%ON^q;459-Onnf5_jI z--~m-WRXb};!QfKWL_Siub6Lti@(yLZ|x*nOQBx#N& zi)xGXn-q4HA7Pqz^{Y2{W&80`Bosxk_jVKzpA85RIOuTOx{jZhC=HQq= zWKs4TB3a(B;K-c&I`{B#FX4R|>Zk5BNQQ=8(ik#k(}6vliABPo`ZRwsE1~Q4d0U{X zQksv2m(jCx4F<^7O1#vJE{v8CQS^JZ1IdIlNx8g?sr9{@5dB}rU0>KxB-{!86F;A*N0o% z8=MI6M`t1ctiqg-r|r?Zxvt%bD-Jx0k>8EVo+CtznX2@RZGOU-VkhWhAs3tX3i=ye?~IA>f5iRiRTv5dH)aB9v-!}gvXSSuQ<7870* z4rsp)wA>PXIgOWGRJDYTgsS@zYCcmzP%ML@sM&Sap&p`i#$cyIdn(x2q^41`buvyz z7qVyHx7%Sv>pyd>9GC0AudFg4FtO$i2zhQ)8*PZR& zCT*JfMjKPWB=uOEN!%R!{2GeSI@JCR2}I%*tnLJG=Flv$b*h>f+<)pI5 zCM!){n=pr<2Kpmw3S^IKtiTNx%Z?WV7%X!p43Dz}f0)FFO=@zdN_JymGlNUm`n?xK z2UIQKf_rHK{+##7m#-w1_P+Hwig=x@5$q#V&ClCAX`SN^tgV(ofrd@C5E3bMZF#G( zIP1sy2^y~mZu`b$syU5cO!x+8QIrQ$jgA79)Z>6m>J(2fwvtvgFI8q?Y`SHh(dDcE zQqlromDX=(a#6xwZ7Lm00tkPcqEoV1Y$<}9d(Sv0rVnA_w8oUpO>Pn!M%JHDy?dhs+qah1%= zxFCb>cr%c7QTuQ@*Wk4H`H*|48JLDG^!wZk2SCnhsg=XVJxs-5=z*7MY10;B(%vrf ztdet-E}cC25nO`>;=I9%0@_(;WuRcOPQbC>tkDi(rc#p@HxMCzaZjK`V2u|42))*u zVccY?Xu#%D$XRKdn5`WPwZ37pWtqEYa`W|08~a4)XgUihQ#G72!9Sy~X^C0l&ys3` z;GW^Q)w&W{bz)UlaTZo0XVun%QoD#8?3y=f(|UU<*V8hyPCs+nn`Prk_2}C3@*AvK z>aR_wjH0YMG#+DIcdAO^ruLL9|L}eG(K?M~rTo_7f<|l#Q`IJkKGFQCC3{1i8BLs~ zfU6^_;PtNVcmR?^gGi7QF*J>lIr~A)8te^bg0hjuz+kDegxe?Z^Ev(0``AZau!i>Fj0fWEi~>;kPzGn?(VLQdxCr88r}FTn+SEDq?z)Hu>^k@OWBC#9Y=>M^a!uf#*J5K+1b4dZ?*sCF z8*q9ANP{OqAv-^?mLE#oe^Od|c{b8>qHr#XNThN6Q?Cq){ zaPBNCfBq2i8Lk*xFBh%Otsb={49Gj5dP$53Iq6aJSyiZNp7QP2Jmf{xQC!9iRUSZMs`$dmS zwQoNz%mU~Hi7xZ;WW$QjE9SL|e%)(!*{VK&>I}OC;zu@xe4?Kyi-A9sJv;Rx$fy*` zL6r5K48Dp@ib!2Nc9CgQ8$E!oYs%UP1{-$iY`WlZg6{9BF8PcJ zS$9)v(S%`kr^hWT&Z*!(K?{zn7ulpfU)@};>{vN0 zya=|-92uibKnhKlymb@cPZmur4%0v#arD%lkKQSN6M_Ik;zr!pnGkdzdzHx>&(5i} z2m3=Pc-8o(HK5niHt1NDPJoTbH@1p;s5C-xX1{r?jNW=2NHDYGxP6_BH*`)QW=A;Y z&u$_UyyHt~&P%QQoqL_{!~4Ih5AKe5W1;0iIVK6*fYdlCeUzT{hq4FJirZF+n#Clv zj_?JJjcfI8b}%Lnl2`&yaBu_?etcy>D7F3?!dfv5Vu_u4cprMWP6yf)(fIKA8@-43 z7cg;v^f&%Tl2gg!{z8#hf7i#kK~n!d<_4lbXlP>!ZZ!PiHMn;e|KS!5|K|MYA(rv; zLFh1jrR{PzHvoT{5|9D^uVfhL=u%D$QoF#|y}DCuFE~URmCAj%%-wPHXEMN^>|)p* z1^CeQ3oGDv6MchsEef#3NC^uO-YS0a;F5Qv_DwDvv!vp}{XY=K-kkq#u(~HhV_n5E zIhA9ScZ512gsF31!60#}3|6WCZ$SY|G}u?Oz*ghRY}XXEK>6G|2pV&#qCX_>a0XCD zI-Qg`V*&Yc@SP9ppT*6hpO)BI%xo}Ld?$;XopUhBkUtN|p?HVF*)DhW)WojJM-e5# zKEtntrX5ToPHf@P*!+$?>f|d4X!=dff>vj@AVpnWd}6@wuV*Oi16?A%eC)h9wFGd( zmDna$!YXrG*6*3#ulmO%lH!@V{t=}MN=3?`-QiuwZ>xFkM!RW&xM<$3nn~M^9yot_8&TF2Y^yqY-eHnrPDN6FxL() zmiK}(Afa&N?0XP?5h6a0C{r(~EXtS9E{wk<{R$Vk5y7-+6Lg9~QVQek-$_T$Mz@$z zZe*@|KX3oS_!}|b&aqggo_$!BZjC%<;Tm8}?$_nt1j3Ehk+!IbrTI5M?yWn<_`#Jo zDNB)+x-8h*_*1$7RsbR@^8hDDZT!z_+4XrI3rj_trKi>;vzjg>8fgV_oCj3YN5T!k zST;)~g6wuYYG)Tj=CPii)FtHhv9zm#PFxYX8WTyi7tksiu?~>P3Y*g$Azw`Mis9h5 zVv{^aVm?Ju@80sj#+q3z7qaG*Nrpl@8dD#TtHDu>ZL)3wK^mdBN{8DyXY9pobZTt? zbjhIddqjqTVUY|Tgo})045h~Z`GBR?=4ZDI5VWe9idKh5Vl9NMWe)`ZE+YgBDKUTj zXiKnLr)!`QRyp{yg^8dxuQirM>PPwXqGnKDMgRVTC`XG2y}KbD*$x-jETy)81;JHH z=7YYu-w&5=GQ$~fEM>3iAYHZnQsR|uqPh1LjErOu)v!5vq@!H@%5)HEAu!x^F=sDN zURh!G?|gaz$r6Mk`r??o#dO`a=HE0ozJklOW+Yinha^KdL^L*V^LLUb_g z>+Qd#L-0w`1Brf^94#G^A=fR7Xs~x&(LH*U61JEycRI|6_0Ao(Q~S&iq*nq=r}1G2 zqoy??K}&Xu@H%0iFqM+A3Ge50+~KpOr{x?p+x_N(iL%S8N(8^MXrkQ(HJ_C*bV@ZC zxE0H?cG2#h@mh4@a#qVY2pZ!8uUUp_CZdk*@Yx!P1`vPLTzrr z2aRiUzRvnUHt&v!kfj(A@E1$`p<4ITRtvR&(z)x7{9131(m9~+P(|S1nEa@&K2_G? zoZnIXo571al12Cj0IgEI<};#w83hik;SGy(bMlGLYfpj`_W`)!B(6D~aLRgm8|h-* z1#u#Qb6?2EoGSu}FzC8XP=8#Djk05x-d8Vui2B;d_cr%kC!vsv4#|Wuv>BW?G`W-v z!Uye`I!nu5Ol3ULqnF03HM0aa<1qPg3#5cvtMSDs75}&&pBSz;m59DOx`?D4w4NcM z^@NTjw`dqZ35J$&r(Z8O3K75Xm-Y0DUob2ZEVWwk=hP<;x5@&BZ&T5D-;_NsTtEyl zaA0o~nu|R97|GMGGVpK1IoYeH`zoUVC8|!((uN0nY(Pl8S&jJh{NT8QGVo=8slAhp zB-sT)lMY2+8!N>9?qFx<(EH%;-Q}TGor%0<{XG-DUfjtFNzfqtUTA(oT~rRiMlK0H z`570-2yh?)xock=LjsD{;|&JmhgZwQ(RR~@!isN%**Xrdw5g#SQL}Fg-nkb_9ht3t zccl>)MvzOvv(MDpLil>FdJ`fEHx``ePB8< z#aF^5HaWl3SMFT)+KQm>s0=vBs54U@z{@XO7^Da-eNvV>Uj4nrnfiv%#v|z`>UoE+ z@k%>~4KN{LU1JEAv~LWJz};W01EaDrN+_O~zZ0e&RA{!=06p4|g?l-W< zJO~~*)f!f}JpCk}qd%Db)Bi=F&Sj$1-v?s1{}0eitc7g@zh!HA%%5d5N_#}_GuicV zgd3fG&RbmY7t25FloMSe4$7?3LF5P|N>^XD#DQ_cLb+A@Vb`;JtE!OSz5{ZbhT<8SM3z^-8QAwny1>8(;8xcDX%Vajo}=15fkTpZ6%j0~VSJAb^`Ox-hjlLR@! z;KgHpy%=(=_sIsvz#$Z8rLi?8AT03ZLe8~7x&sT|jABz*($9rKSNLV2P~uBN=&YX= zb%+V~3;pS=i1Q6%1bZDNhd^FwJzrnoRndwPdpMp~hqJ^pxerIHz?6H1bLlck9nvC~ z@y&tTySvx>#nc{|ZZS3L)jf80Ks)kixG%!RCgF=-2#YhtO$ieAZ=Epz!21WD)sy2x z92;kS8({gwyY|%(F`6We_BOA;Up0}Wq)`Mj zobAaAiMET81>?l3X>uB^t03LwA)Ga*&v-Zz_8S`r6zOMz;2~#)vL)|JwkxdyzSAo8 zvK#en$f;kXBN8V%EXmkDmV#c-kYgqZ|6$`&m_)S^+|)Zi88KZ$dE+!OwE_XQj{7y zf9rgZ(6}NtiwZ2j(A{U8yDXu4YTS-Jb1yDp3>m{?j7X&kJDc{c(qSa}mMscD2=E~e z3Fe|)XS8vc-UDk%2;1fp{kK@}V{#R;Zi2c@^wa=$T7S9j+co`gcN;X#`@(aN@TzwH zXp>NTTUpelDjJ-B#OhqSB-I9@!y029AazXpc856kpmUxLG;1eY6vtf+X6h%6aZ$#d zU<-fbd9MPujoLhq%r1Ek*)2e3xp5-xI;V%Bo^1D_UTzzQ&n0C*u%Vt*EuSsyONbk- zFsQse*lTkV-8NbSWOWB$Y=u#CKiGn2c${^vll7c7E6twD> z*++Es4SnSOvBG!Z_W&i}-`lb3KOSjWeHYXMh#e;$a4m~XE%fGzL~<)%-ePL2Sc4Sj zT8+fZ2<>Rl@JMsoFGvT?{!wBjZR15y8l1=PmBxvpzhVq}LhI$03J#_V|DD;33W7g~ z=D4*MC7n{G;SI5z$6|WD4_ghSvl)7@#S@0{k6ZzP(qI7{x`1KDf)z zeNR77a*M%3omPq1hx#Vp6M9~mQy5e11dgS;;Syq?APyxT*J~>ZOE7}d+WgNhz2v*a zyDIt1lc5sg#~p;sN+-WKQ{sR~@1zX}tb@smH!Uv|2*G+;Nk z_TSTwH%mt=7(%zp(;$vEO0(UZEQ>D+XG=Y625@dc;HHP7w1+HJE`J7Bkm#VOqV|AD z0)uQhGG{vPe{>3&9>+|bpj@&{Sr`;rFYpdgPv#|_SYnHIqa_LBt8eme8p1~;z_7oa z?bcWQ1}MB7>-xX!Kxp_0vcRUjQv%<4yCkrG3_1)BXu29y#lV|-f@u1GZsL$5eIrwn zNPX4c5|#AUHn-w!H{)EmY8oSZjk4XUOYN#VnyfTo4-!^tPQiZ3ztzzrx~B;i2HNph z9P8k;ZT5#e$&*qS5$+Xl{_^sDg;*g*RcD;}R|DJ%$UtPyPD#d4asLZsHajOG7$ffr zVG6ih|x4TbZkJyUyyN$W+4dfbednW{xXyp7O%g+&tqY>?8 z{oGuDg~-m5ovhkxxj&1k4#N5nG-SHzsPb=kC?<+tRaFQy74sy2k>(SU;dMEt1X`@Z zFzD=npE!oolmxj^%Q+n}(BPKz7L`**c8r^h*)XZurc#PRwJpBT4J{^SP~6C_{o4?x zYTf}b?d;{4veBv)>?X07;=ENzQd2L}Ote5oQDNdaH5GT<6TrUP1*t&W9V@&z30{W> zdpyU<*iR-Y+`?29mY|cAt_`76Q8wF|T~!Rz`(8YOziUgGV=?h_*gUZNuJz;pRgqHp zuOf9;^bCBy{T18hu$en(v6!07z%s}Wr$$TUi&dSy?agHTS>r)LM%`UpySy0as!%gn zs=0KbTJ_P@9C-zz-2xym!h$)Zu~OhlGil7K`R5fE>J{-slcCzSAxB7|)l*EJMt5Gf zvKrE=;m%0^>Nm^{l{Bmd42*`VS|C%W+40MyjN8rr*L1?4kZ7aZWnl?-f0>S*Q6~bO z%WNyHy<Sb35x;X?PCOyn*^Ff6&0StU-`5f$0*(stM9p z!bUQKx2uv$hWc@z#x7Xvlp@j>9PdCy2+L!lb*JYCv@Hx{+^_$-f#WsCO2BcGqilj{ z#bYu)@t&nl1kv_vVApNJ?rdTy5(MaD7ZI{-uU>4o9yv`}Tzi6&j*7AGTDp5bKvYlK z)Rr@=D26$_=("AzTr`{^SLG=zQH@`59d5QXkW)m1d#qb>J}ESqJFb?(b@GCKb)KvAd(~0`gqj^sB2en9>}L`41u%a2~EQEg(d!$L3Ik~ z#`YZ2)bPFLj4r5XmHoFu;Sk9v-KT4-(rLTuTe(|>3fX@9a|xU>L^x-l5bxHTRvHzy z1tA|y%P$^Ot_-2cr$bo<`dt@weMM|tl@%$h|0Eq)|5P4%M+c@?Y_L(2bsliju`-#G zS^vQpdzqP^=7xv%;xt9rcIU94T?o4&+R0!x`v!pT%sun+3>BR zNNi=)>u*Jqctl>?W(MM1rP!1Dn-45SU*Z^e8&N`a)u}#J5Wd5r^!k&o1S@ui1J`+V zPl}A<-NX8kKBnN%@%WQ&au_Zati1p}CSgY96bJxwuF>HInTQz3jHRk*ce4Xq5NuxtK{?H|O?5Amp%`OEUzxq4- zh25u)*yh5h<0gZmv-C{$j}KFzsrND~2R(P`ftjRe{yyQ}ia7BXik<u;$05|5*oJa+Jp38VGpl}dH;mA(Y6ra{s#rV$?^aI literal 0 HcmV?d00001 diff --git a/tests/snapshots/biobj-1.json.gz b/tests/snapshots/biobj-1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..ecb93f4c4d9ceabbcc5264409e7d10c786c35e58 GIT binary patch literal 33509 zcmcG#V|1ilxGfsn9UC1R9ox3ePRBMX9oy{Kw#|xdn;qN9&G+s5W1lhZIs2Y__o?xW zRcp;>&iSnMquyFo?|O+MU|>MLUSOZb7Iwy#^vn#FPIk5+XPq`~coOjpPd>h4jT<+$ z8lMpM>neV$oPzfPTqlYQL%oaH?3FV}m$H~7%F zem$sZsSbUcoC;xIX~FMm#n!)#emP;8%5`&c7~y64=pc#~eMlW9OjtHS^O6M;s?-k6 zpE3XNlIXbmlg!|#t@>8u8-jRJl;;w^8Cu5M846K&(b%e^06gIazf)IMmS=gfXOoC{vNd!XsJ@ve_t=66esva{h2A0Wsb z{=Xw`nBYHxI*vz~)U81DLNa1K4zrbv-cdq)KRR-l04I*8j?~Rm>|!{4J?>j88Kt9y z{C;%DFo8!L&kdT}{2!_PXi|F#AQhV1_gz-M&?yj7e(=yI1mgd`LHH8_)h=sJ=#<}= z_5X=MZ0MBWmvz_^!q*c1PYeRku5l7Wr*cHSp~!bxsZg_pzG_Jim@AU<|2L+z2h69x z3^A{98op|Yc|+0fvi|z274d}dU!=hJ`_o4bW1oy;Q5xP=q7CdmQY|Gmo@y7OX|Ax# z^JmqwKx__Rr`5CkHb=ir>v({*ox{>mg=?#tDQT$szu_o1dNLK_SFAvrjG7t#8;YZ5 z{}CmLV~W%%xylS8#k#@rtq7TpI1~pgvZH3HiDNa?DMl&`{YARLGOY+HjyMblEZ(DL zPl;n!uqmJ(eP)T!Dxqz3|9`vC`6}~rdChrJ&rvZTb(asV{>OAy>}0m>=O(_31?&F9D5`74-ee!0Pz*~#EDI$ z@O*>Jtjwq>`N`nD`cLocj;B}>RWmaB#c>%zVt+Qx9ybgyXD53tf|*x#z~4D2Mqa%-Dx6QiTa&;;`X%2Bb%T)1pqw* z+o=M@oIIg@y`sGNIl{+n zH9u9w?>-jEZMJ4j`9ga}N!22+nuSe$agcl`Y+vlu=@!-plZ0KX9rwnp9eGB>b!fg< zwG6ewVft(6j^2mUEW+p(;%TYXE&BSRkT4;WstS+}N(|hgQuC7*#+2-mmEEDbc#CH8 z=Y_@mnw4m{MzwhfW#G?~A(|PIxEmp3&;-Sk==kI^8)%snY?~C+H7C|HC$3_Rtzt!4 z!jW9U2|Yz9I7Jw6ftq!J+T{j6;Rbie{Oz3i`(L`Dt~ryQIa3vDY85N?5{~Q=PUI;< z(J8{93)Gwo)E;-h33q@)X0LN*Z_A`%+vI+%|dKGI{KrIpdHy^Cj5h&YyD; zANnT_4?S>n;6!+CC5_2zxbe&rgg)PFT=`SSU;1bCbeUfZ0WLhLKeN5h==Ke~m&QI= z@DYo$%>%Y=*&=_q!g?V<+YYrhu$O)JcVG z-v`VA zkqAJz%tt{--U-%Yik`03XYI3UHo#&k3{1w9+gXZ@UYxf_+6z?enDugLe9|c2n{sj128hm+#Nr2o!T`C;c9s6)#gU zc0=9Kbvw>+I2Ilu*^!e)90rNKmt#A2W5aH)tT-9SM=35GHV!JlBw0HqK8(V?@$ zn1H!ma5sYZ0XVIBJ8lO~BttkQJD>_Ukrf57=nhUeX+N#MR=dUxNzwW|{5hSCp7*Ym zCWo7arQpDS*I)jpJBbIDS}o_zg(Wq4R!4VigW#@C&kUDZ6mf?D&Dt7vRj_^5Ud!HMbiO)!5QlW}Klx8akIaNpV

k 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

AvIVq< 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 literal 0 HcmV?d00001 diff --git a/tests/snapshots/boxed-1.json.gz b/tests/snapshots/boxed-1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..2341e44a4a96aebd3311cf2643eb6bacd7967cf3 GIT binary patch literal 13437 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 literal 0 HcmV?d00001 diff --git a/tests/snapshots/largescale-1.json.gz b/tests/snapshots/largescale-1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..c33707564450bae04697685f9f53b22cbe929a40 GIT binary patch literal 13097 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

    9=L0t$N1eV6e+zfQQT+ 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 literal 0 HcmV?d00001 diff --git a/tests/snapshots/mixint-1.json.gz b/tests/snapshots/mixint-1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1892fa0d5e630fcc4850c6ce7cac8b6ff439502a GIT binary patch literal 13736 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 X^NRuk9{la*Z#Vxx>4{+^mhA%orDar! literal 0 HcmV?d00001 From 978db894a6c1a0660ca806bba339bee601648556 Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 10:40:31 +0200 Subject: [PATCH 07/10] [tests] document the test suite How to run the tests, their layout, the isolation of global state, how to update and review snapshots, and the bugs and quirks found while writing the tests. --- tests/README.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 tests/README.md diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..cac8673 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,102 @@ +# Tests for `cocopp` + +A pytest test suite meant as a safety net for restructuring the package. +It complements (and does not yet replace) the legacy runner +`python -m cocopp.test`. + +## Running + +```sh +uv venv && uv pip install -e . pytest # or: pip install -e . pytest +pytest tests # everything except network tests, ~10 minutes +pytest tests -m "not slow" # unit, module and smoke tests, ~1 minute +pytest tests --run-network # also tests using the online data archives +pytest tests -n auto # in parallel, with pytest-xdist installed +``` + +## Layout + +| folder | content | +|---|---| +| `unit/` | functions and classes in isolation: statistics and formatting helpers, reading and aligning data, `DataSet`/`DataSetList`, target values, reference algorithms, settings, archives (local only), file utilities, the package interface | +| `integration/` | the plotting and table modules called directly (`test_modules.py`) and the full pipeline `cocopp.main` on many scenarios (`test_pipeline.py`) | +| `regression/` | golden-file snapshots of the complete text output of the pipeline | +| `smoke/` | the command line entry points, run in subprocesses | +| `network/` | the official online archives and the scenarios of the legacy `cocopp/test.py`, including its doctests | + +Helpers: + +- `synthdata.py` writes **deterministic synthetic COCO data** (bbob in the + current and the pre-2016 format, bbob-noisy, bbob-constrained with + g-evaluations, bbob-largescale, bbob-mixint, bbob-boxed, bbob-biobj) and a + tiny hand-crafted data set whose runtimes and ERTs can be computed by hand. + The real archives are several MB per algorithm, hence the synthetic data + make the tests offline and fast. +- `pipeline.py` runs `cocopp.main` in-process and defines the `SCENARIOS` + shared by the integration and regression tests (each runs once per session). +- `snapshots.py` takes, stores, compares and extracts output snapshots. + +## Isolation and randomness + +`cocopp` keeps much state in module globals and writes files next to its +sources. `conftest.py` therefore, for every test, + +- seeds numpy's global random number generator (`conftest.SEED`), +- restores the globals of all `cocopp` modules, the testbed class settings, + matplotlib `rcParams`, numpy's random state and the working directory, +- runs the test in a fresh temporary working directory, + +and for the whole session redirects package-relative files +(`toolsdivers.path_in_package`) into a temporary copy of the package. The +session fails if any test created files in the package or the repository +root. Subprocess tests import that same copy via `PYTHONPATH`. + +## Snapshots + +The regression tests compare the normalized output (time stamps, temporary +folders and the version are replaced by placeholders) with +`snapshots/.json.gz`. After an *intended* change of the output: + +```sh +pytest tests/regression --update-snapshots +python tests/snapshots.py diff old.json.gz tests/snapshots/bbob-1.json.gz +python tests/snapshots.py extract tests/snapshots/bbob-1.json.gz /tmp/bbob-1 # for any diff tool +``` + +where `old.json.gz` can be obtained with, e.g., +`jj file show -r @- tests/snapshots/bbob-1.json.gz > old.json.gz`. + +## Known bugs and quirks + +Expected values are often *characterization* values that pin the current +behavior, quirks included (marked as such in comments). Known bugs are +documented as strict `xfail` tests, which start to fail once a bug is fixed +(then remove the `xfail` marker): + +- `toolsstats.sliding_window_data`: division by zero with the default + `number_of_stats=0` +- `pproc.DataSetList.det_best_data` and `run_length_distributions` + (single algorithm) fail under Python 3 +- `sanitycheck.main` uses a non-existing `rungeneric.shortoptlist` +- `cococommands.DataWithFewSuccesses.print` and `__len__` use a + non-existing `result` attribute +- `config.config_line_styles` fails for algorithm names without a float +- the unused modules `ppsingle`, `ppfigparam` and `compall.ppperfprof` fail +- the version label lists inconsistent hv-hashes in `PYTHONHASHSEED` + dependent order + +The output is otherwise not fully reproducible for a given numpy seed +either, because it depends on `PYTHONHASHSEED`: + +- the bootstrapped dispersion values in the comparison tables (`pptables`): + `rungenericmany` iterates over function groups in `set` order before, + consuming a data dependent number of random numbers. The pipeline runs + in the tests therefore reseed numpy's RNG when entering the table modules + (`pipeline.reseeded_entry_points`). +- the order of (nearly) tied algorithms in the `pprldmany` legends and of + inconsistent hv-hashes; the snapshots normalize both by sorting. + +Other observations: generated html pages contain a few broken links (e.g. +to `sorttable.js`, recorded in the snapshots), absolute data paths end up +in LaTeX legends, and importing `cocopp.firstsession` downloads data into +the current folder. From 497c8f060a9291ce5dc9e00e9838fb9e0b643503 Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 12:59:50 +0200 Subject: [PATCH 08/10] [pyproject] drop support for Python 3.9 Python 3.9 reached its end of life in October 2025 and current versions of numpy and matplotlib do not support it anymore. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ce5033e..3cdd0d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ authors = [ ] description = "Benchmarking framework for all types of black-box optimization algorithms, postprocessing." readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = {text = "BSD-3-Clause"} classifiers = [ "Development Status :: 4 - Beta", From 0e556ff29748a63c6be0a8ca8f67892fcd5d9efc Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Thu, 15 Oct 2026 12:47:26 +0200 Subject: [PATCH 09/10] [tests] replace the legacy test runner cocopp.test with pytest in CI Remove src/cocopp/test.py. All its scenarios are covered by the pytest suite in tests/network (opt-in with --run-network) and, offline, on synthetic data in tests/integration and tests/regression. The doctests, formerly run with python -m cocopp.test doctest, are run by tests/run_doctests.py, which tests/network calls on a throw-away copy of the package. CI now tests Python 3.10 to 3.14, builds the wheel with each version and tests the installed wheel, lints the tests too, runs the offline suite with pytest tests, and runs the network tests that correspond to the former default CI tests (archives, doctests, subtests 1 and 2, constrained data, pickle regression). setup-python is updated to v5 to provide the new Python versions. As cocopp is pure python, the release workflow now publishes the single py3-none-any wheel next to the sdist. This commit is meant to stay on top of the test stack. --- .github/workflows/python-package.yml | 27 +- .github/workflows/tag_release.yml | 5 +- src/cocopp/test.py | 557 -------------------------- tests/README.md | 8 +- tests/integration/test_pipeline.py | 2 +- tests/network/test_archives_online.py | 23 +- tests/run_doctests.py | 49 +++ tests/smoke/test_cli.py | 2 +- tests/unit/test_package.py | 2 +- 9 files changed, 91 insertions(+), 584 deletions(-) delete mode 100644 src/cocopp/test.py create mode 100644 tests/run_doctests.py diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index acceb3d..0f70a4e 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -20,14 +20,23 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install ruff + python -m pip install build --group dev # ruff, pytest - name: Lint with ruff - run: ruff check src - - name: Install package - run: pip install . - - name: Run doctests - run: python -m cocopp.test doctest # takes about 5 seconds + run: ruff check src tests + - name: Build wheel + # cocopp is pure python: the wheel is the same py3-none-any wheel for all versions + run: python -m build --wheel -o dist . + - name: Install package from wheel + run: pip install dist/*.whl - name: Run tests - run: python -m cocopp.test # takes about 5 minutes - # - name: Run all tests - # run: python -m cocopp.test all # takes about 30 minutes + run: pytest tests # offline, including the full pipeline and snapshot tests, takes about 10 minutes + - name: Run tests using the online data archives + # the doctests, the archives, and the former default scenarios of python -m cocopp.test + run: pytest tests/network --run-network -k "not legacy" + # - name: Run all tests using the online data archives + # run: pytest tests/network --run-network # takes about 30 minutes + - name: Upload wheel + uses: actions/upload-artifact@v4 + with: + name: wheel-python-${{ matrix.python-version }} + path: dist/*.whl diff --git a/.github/workflows/tag_release.yml b/.github/workflows/tag_release.yml index b25cfad..762ad6d 100644 --- a/.github/workflows/tag_release.yml +++ b/.github/workflows/tag_release.yml @@ -27,8 +27,9 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install build - - name: Build source Python package - run: python -m build -s -o dist . + - name: Build source and wheel Python packages + # cocopp is pure python, hence a single py3-none-any wheel serves all Python versions + run: python -m build -o dist . - name: Publish release distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - name: Release diff --git a/src/cocopp/test.py b/src/cocopp/test.py deleted file mode 100644 index 4157370..0000000 --- a/src/cocopp/test.py +++ /dev/null @@ -1,557 +0,0 @@ -#!/usr/bin/env python -"""Tests the `cocopp` module. - -On a shell:: - - python -m cocopp.test doctest - -runs the doctests (which is quick) and:: - - python -m cocopp.test all - -runs also the most extensive tests. -""" - -from __future__ import absolute_import, division, print_function, unicode_literals -import os -import sys -import time -import inspect -import fnmatch -import urllib # noqa: F401 -import tempfile -import shutil -import subprocess -import doctest -import numpy as np - -from cocopp import archiving -from cocopp import official_archives -from cocopp import pproc as _pproc -from cocopp import genericsettings as settings - -import matplotlib # just to make sure the following is actually done first - -if sys.version_info[0] >= 3: - from urllib.request import urlretrieve -else: - from urllib import urlretrieve - -test_bibtex = False - - -class InfolderGoneWithTheWind: - """``with InfolderGoneWithTheWind(): ...`` executes the block in a - - temporary folder under the current folder. The temporary folder is - deleted on exiting the block. - - CAVEAT: copy-pasted to toolsdivers - - >>> import os - >>> dir_ = os.getcwd() # for the record - >>> len_ = len(os.listdir('.')) - >>> with InfolderGoneWithTheWind(): # doctest: +SKIP - ... # do some work in a folder here, e.g. write files - ... len(dir_) > len(os.getcwd()) and os.getcwd() in dir_ - True - >>> # magically we are back in the original folder - >>> assert dir_ == os.getcwd() - >>> assert len(os.listdir('.')) == len_ - - """ - - def __init__(self, prefix="_"): - """no folder needs to be given""" - self.prefix = prefix - - def __enter__(self): - self.root_dir = os.getcwd() - # self.target_dir = tempfile.mkdtemp(prefix=self.prefix, dir='.') - self.target_dir = tempfile.mkdtemp(prefix=self.prefix) - self._target_dir = self.target_dir - os.chdir(self.target_dir) - - def __exit__(self, *args): - os.chdir(self.root_dir) - if self.target_dir == self._target_dir: - shutil.rmtree(self.target_dir) - else: - raise ValueError("inconsistent temporary folder name %s vs %s" % (self._target_dir, self.target_dir)) - - -def depreciated_data_archive_get(substrs): - """CAVEAT: this won't work anymore as the get_first method changed to - get_one - - return first matching data paths for each element of `substrs` - concatenated in a string. - - Specifically:: - - return ' ' + ' '.join(cocopp._data_archive.get_first(substrs)) - - Implemented via `subprocess` to prevent the need to import `cocopp` in - this file here (prevent the script vs module absolute import issue). - """ - res = subprocess.check_output( - [ - "python", - "-c", - """ -from __future__ import division, print_function -from cocopp import data_archive -res = data_archive.get_first(""" - + repr(substrs) - + """) # get first match for each substr -print('_split_here_ ' + ' '.join(res), end='') # communication by print instead of return res -""", - ] - ) - # res is of type bytes - # res contains any output printed during execution (e.g. user infos) - # yet we should be able to split away and return the last print - return str(res).split("_split_here_")[-1] - - -def data_archive_get(substrs): - if str(substrs) == substrs: - return substrs - return " " + " ".join(substrs) - - -def join_path(a, *p): - joined_path = os.path.join(a, *p) - return joined_path - - -def copy_latex_templates(): - current_folder = os.path.dirname(os.path.realpath(__file__)) - template_folder = os.path.abspath(join_path(current_folder, "..", "latex-templates")) - shutil.copy(join_path(template_folder, "templateBBOBarticle.tex"), current_folder) - shutil.copy(join_path(template_folder, "templateBBOBcmp.tex"), current_folder) - shutil.copy(join_path(template_folder, "templateBBOBmany.tex"), current_folder) - shutil.copy(join_path(template_folder, "templateBIOBJarticle.tex"), current_folder) - shutil.copy(join_path(template_folder, "templateBIOBJmultiple.tex"), current_folder) - shutil.copy(join_path(template_folder, "templateNOISYarticle.tex"), current_folder) - - # Copy auxiliary files to the current working folder - cwd = os.getcwd() - shutil.copy(join_path(template_folder, "acmart.cls"), cwd) - shutil.copy(join_path(template_folder, "ACM-Reference-Format.bst"), cwd) - shutil.copy(join_path(template_folder, "comment.sty"), cwd) - shutil.copy(join_path(template_folder, "acmcopyright.sty"), cwd) - shutil.copy(join_path(template_folder, "bbob.bib"), cwd) - - -def run_latex_template(filename, all_tests): - file_path = os.path.abspath(join_path(os.path.dirname(__file__), filename)) - arguments = ["pdflatex", file_path] - DEVNULL = open(os.devnull, "wb") - result = subprocess.call(arguments, stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL) - assert not result, "Test failed: error while generating pdf from %s." % filename - - if all_tests and test_bibtex: - file_path = os.path.splitext(file_path)[0] - arguments = ["bibtex", file_path] - DEVNULL = open(os.devnull, "wb") - output_file = open("bibtex.log", "w") - result = subprocess.call(arguments, stdin=DEVNULL, stdout=output_file, stderr=DEVNULL) - assert not result, "Test failed: error while running bibtex on %s resuling in %s" % (os.path.splitext(filename)[0], str(result)) - - -def retrieve_algorithm(data_path, folder_name, algorithm_name, file_name=None): - """depreciated (replaced by cocopp._data_archive COCODataArchive instance - replaced by `cocopp.archiving.get` or `cocopp.archiving.official_archives` or `cocopp.archives.all`)""" - algorithm_file = join_path(data_path, file_name if file_name else algorithm_name) - if not os.path.exists(algorithm_file): - data_url = "https://numbbo.github.io/data-archive/data-archive/%s/%s" % (folder_name, algorithm_name) - urlretrieve(data_url, algorithm_file) - - -def depreciated_prepare_data(run_all_tests): - print("preparing algorithm data") - - data_path = os.path.abspath(join_path(os.path.dirname(__file__), "data")) - - if not os.path.exists(data_path): - os.makedirs(data_path) - - # Retrieving the algorithms - # retrieve_algorithm(data_path, '2010', 'IPOP-ACTCMA-ES_ros_noiseless.tar.gz') - # [outcommented and replaced by BIPOP until 2010 data is in new format] - retrieve_algorithm(data_path, "bbob/2009", "BFGS_ros_noiseless.tgz") - retrieve_algorithm(data_path, "bbob-biobj/2016", "RS-4.tgz") - - if run_all_tests: - retrieve_algorithm(data_path, "bbob/2009", "BIPOP-CMA-ES_hansen_noiseless.tgz") - retrieve_algorithm(data_path, "bbob/2009", "MCS_huyer_noiseless.tgz") - retrieve_algorithm(data_path, "bbob/2009", "NEWUOA_ros_noiseless.tgz") - retrieve_algorithm(data_path, "bbob/2009", "RANDOMSEARCH_auger_noiseless.tgz") - retrieve_algorithm(data_path, "bbob/2013", "SMAC-BBOB_hutter_noiseless.tgz") - retrieve_algorithm(data_path, "bbob/2013", "lmm-CMA-ES_auger_noiseless.tgz") - retrieve_algorithm(data_path, "bbob/2009", "DE-PSO_garcia-nieto_noiseless.tgz") - retrieve_algorithm(data_path, "bbob/2009", "VNS_garcia-martinez_noiseless.tgz") - retrieve_algorithm(data_path, "bbob-biobj/2016", "RS-4.tgz") - retrieve_algorithm(data_path, "bbob-biobj/2016", "RS-100.tgz") - # diff. location and name due to Jenkins settings with too long paths - retrieve_algorithm(data_path, "test", "N-II.tgz") - retrieve_algorithm(data_path, "test", "RS-4.zip") - retrieve_algorithm(data_path, "bbob-noisy/2009", "BFGS_ros_noisy.tgz") - retrieve_algorithm(data_path, "bbob-noisy/2009", "MCS_huyer_noisy.tgz") - - return data_path - - -def process_doctest_output(stream=None): - """ """ - import fileinput - - s1 = "" - s2 = "" - s3 = "" - state = 0 - for line in fileinput.input(stream): # takes argv as file or stdin - if 1 < 3: - s3 += line - if state < -1 and line.startswith("***"): - print(s3) - if line.startswith("***"): - s3 = "" - - if state == -1: # found a failed example line - s1 += "\n\n*** Failed Example:" + line - s2 += "\n\n\n" # line - # state = 0 # wait for 'Expected:' line - - if line.startswith("Expected:"): - state = 1 - continue - elif line.startswith("Got:"): - state = 2 - continue - elif line.startswith("***"): # marks end of failed example - state = 0 - elif line.startswith("Failed example:"): - state = -1 - elif line.startswith("Exception raised"): - state = -2 - - # in effect more else: - if state == 1: - s1 += line + "" - if state == 2: - s2 += line + "" - - -def delete_files(all_files=False): - assert all_files - if all_files: - cwd = os.getcwd() - os.remove(join_path(cwd, "acmart.cls")) - os.remove(join_path(cwd, "ACM-Reference-Format.bst")) - os.remove(join_path(cwd, "comment.sty")) - os.remove(join_path(cwd, "acmcopyright.sty")) - os.remove(join_path(cwd, "bbob.bib")) - - -def tmp_test_constrained(python, command): - url = "https://numbbo.github.io/cocopp-playground/bbob-constrained/testdata/bbob-constrained" - archive = archiving.get(url) - t0 = time.time() - data_path = archive.get("fmincon_consbbob") - print(python + command + data_path) - with InfolderGoneWithTheWind(): - result = os.system(python + command + data_path) - print("** subtest 19 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on one bbob-constrained algorithm." - - -def main(arguments): - """these tests are executed when ``python cocopp`` is called. - - with ``wine`` as second argument ``C:\\Python26\\python.exe`` - instead of ``python`` is called - - """ - - run_all_tests = "all" in arguments - recent_data_path = os.path.abspath(join_path(os.path.dirname(__file__), "../../code-experiments/build/python/exdata")) - recent_data_path = False - - python = "python -m " # how to call python - for argument in arguments: - if "python3" in argument: - python = "python3 -m " # how to call python - break - if len(sys.argv) > 1 and sys.argv[1] == "wine": - python = "C:\\Python26\\python.exe " # works for wine - - # old_data_path = ' ' + prepare_data(run_all_tests) - - command = " cocopp --no-svg --no-interactive " - - # copy_latex_templates() - # print('LaTeX templates copied.') - - print("*** testing module cocopp {0} ***".format("(all ~ 30 minutes)" if run_all_tests else "(~ 5 minutes)")) - t0 = time.time() - data_path = data_archive_get("BFGS_ros_noiseless") - print( - python - + command # '--conv ' + - + data_path - ) - with InfolderGoneWithTheWind(): - result = os.system( - python - + command # '--conv ' + - + data_path - ) - print("** subtest 1 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on one algorithm with option --conv." - # run_latex_template("templateBBOBarticle.tex", run_all_tests) - - t0 = time.time() - data_path = data_archive_get("RANDOMSEARCH-4_Auger_bbob-biobj.tgz") - print(python + command + data_path) - with InfolderGoneWithTheWind(): - result = os.system(python + command + data_path) - print("** subtest 2 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on one bi-objective algorithm." - # run_latex_template("templateBIOBJarticle.tex", run_all_tests) - - tmp_test_constrained(python, command) - - if run_all_tests: - data_paths = data_archive_get( - [ - "BIPOP-CMA-ES_hansen_noiseless", - "MCS_huyer_noiseless", - "2009/NEWUOA_ros_noiseless.tgz", - "RANDOMSEARCH_auger_noiseless.tgz", - "BFGS_ros_noiseless.tgz", - ] - ) - t0 = time.time() - print(time.asctime()) - - with InfolderGoneWithTheWind(): - result = os.system(python + command + data_paths) - print("** subtest 3 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on many algorithms." - # run_latex_template("templateBBOBmany.tex", run_all_tests) - - t0 = time.time() - with InfolderGoneWithTheWind(): - result = os.system(python + command + data_archive_get(["SMAC-BBOB_hutter_noiseless.tgz", "lmm-CMA-ES_auger_noiseless.tgz"])) - print("** subtest 4 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on two algorithms." - # run_latex_template("templateBBOBcmp.tex", run_all_tests) - - t0 = time.time() - with InfolderGoneWithTheWind(): - result = os.system( - python + command + " --include-single " + data_archive_get(["DE-PSO_garcia-nieto_noiseless.tgz", "VNS_garcia-martinez_noiseless.tgz"]) - ) - print("** subtest 5 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on two algorithms with option --include-single." - # run_latex_template("templateBBOBcmp.tex", run_all_tests) - - t0 = time.time() - with InfolderGoneWithTheWind(): - result = os.system(python + command + " --expensive " + data_archive_get("VNS_garcia-martinez_noiseless.tgz")) - print("** subtest 6 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on one algorithm with option --expensive." - # run_latex_template("templateBBOBarticle.tex", run_all_tests) - - t0 = time.time() - with InfolderGoneWithTheWind(): - result = os.system(python + command + data_archive_get(["RANDOMSEARCH-4_Auger_bbob-biobj.tgz", "RANDOMSEARCH-100_Auger_bbob-biobj.tgz"])) - print("** subtest 7 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on two bbob-biobj algorithms." - # run_latex_template("templateBIOBJmultiple.tex", run_all_tests) - - t0 = time.time() - # Previous note: we use the original GA-MULTIOBJ-NSGA-II.tgz data set - # but with a shorter file name from the biobj-test folder - # to avoid problems with too long path names on the windows - # Jenkins slave - with InfolderGoneWithTheWind(): - result = os.system( - python - + command - + data_archive_get( - ["NSGA-II-MATLAB_Auger_bbob-biobj.tgz", "RANDOMSEARCH-4_Auger_bbob-biobj.tgz", "RANDOMSEARCH-100_Auger_bbob-biobj.tgz"] - ) - ) - print("** subtest 8 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on three bbob-biobj algorithms." - # run_latex_template("templateBIOBJmultiple.tex", run_all_tests) - - # testing data from bbob-noisy suite: - t0 = time.time() - with InfolderGoneWithTheWind(): - result = os.system(python + command + data_archive_get(["MCS_huyer_noisy.tgz", "BFGS_ros_noisy.tgz"])) - print("** subtest 9 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on two bbob-noisy algorithms." - # run_latex_template("templateNOISYarticle.tex", run_all_tests) - - # testing data from recent runs: - if recent_data_path: # requires local data to have been generated - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system(python + command + join_path(recent_data_path, "RS-bb")) - print("** subtest 10 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on newly generated random search data on `bbob`." - - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system(python + command + join_path(recent_data_path, "RS-bi")) - print("** subtest 11 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on newly generated random search data on `bbob-biobj`." - - # with InfolderGoneWithTheWind(): - # t0 = time.time() - # result = os.system(python + command + - # join_path(recent_data_path, 'RS-co')) - # print('** subtest 12 finished in ', time.time() - t0, ' seconds') - # assert result == 0, 'Test failed: rungeneric on newly generated random search data on `bbob-constrained`.' - # delete_files(all_files=True) - - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system(python + command + data_archive_get("test/RS-4.zip")) - print("** subtest 13 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on RS-4.zip." - - if recent_data_path: # requires local data to have been generated - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system(python + command + join_path(recent_data_path, "RS-la")) - print("** subtest 14 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on newly generated random search data on `bbob-largescale`." - - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system(python + command + join_path(recent_data_path, "RS-mi")) - print("** subtest 15 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on newly generated random search data on `bbob-mixint`." - - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system(python + command + join_path(recent_data_path, "RS-bi-mi")) - print("** subtest 16 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on newly generated random search data on `bbob-biobj-mixint`." - - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system(python + command + "bbob/2009/BFGS! bbob-largescale/2019/LBFGS!") - print("** subtest 17 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on data from `bbob` and `bbob-largescale` suite." - - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system(python + command + "NSGA-II! 2019/IBEA!") - print("** subtest 18 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: rungeneric on data from `bbob-biobj` and `bbob-biobj-ext` suite." - - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system("""python -c "import cocopp; cocopp.main('slsqp!'); cocopp.main('abc*')" """) - print("** subtest 19 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: running postprocessing twice within same python session" - - with InfolderGoneWithTheWind(): - t0 = time.time() - result = os.system("""python -c "import cocopp; cocopp.main('constrained/2022/RandomSearch-5')" """) - print("** subtest 20 finished in ", time.time() - t0, " seconds") - assert result == 0, "Test failed: running postprocessing on bbob-constrained data" - - do_doctest() # to be backwards compatible we run everything in main - do_pickle_regression_test() # messes up other tests, maybe by calling config? - - -def do_doctest(): - print("\n*** launching doctest (it might be necessary to close a few pop up windows to finish) ***") - t0 = time.time() - - failure_count = 0 - test_count = 0 - if 1 < 3: - # doctest.testmod(report=True, verbose=True) # this is quite cool! - # go through the py files in the cocopp folder - current_path = os.getcwd() - new_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) - os.chdir(new_path) - for root, dirnames, filenames in os.walk(os.path.dirname(os.path.realpath(__file__))): - for filename in fnmatch.filter(filenames, "*.py"): - current_failure_count, current_test_count = doctest.testfile(os.path.join(root, filename), report=True, module_relative=False) - failure_count += current_failure_count - test_count += current_test_count - if current_failure_count: - print('doctest file "%s" failed' % os.path.join(root, filename)) - else: - print("doctest {0}/{1} tests succeeded".format(test_count - failure_count, test_count), end="\r") - os.chdir(current_path) - print() - else: - stdout = sys.stdout - fn = "_cocopp_doctest_.txt" - try: - with open(fn, "w") as f: - sys.stdout = f - doctest.testmod(report=True) - finally: - sys.stdout = stdout - process_doctest_output(fn) - print("** doctest finished in ", time.time() - t0, " seconds") - # print(' more info in file _cocopp_doctest_.txt)') - print("*** done testing module cocopp ***") - - if failure_count > 0: - raise ValueError("%d of %d tests failed" % (failure_count, test_count)) - - -def do_pickle_regression_test(name='strained/2022/al3-cma'): - """read in a pickled data set and compare to data read from the original data. - - Assumes that the pickled data already exist at the usually expected place - side by side the original data. - """ - saved = settings.pretend_initial_solution_was_evaluated - settings.pretend_initial_solution_was_evaluated = False - name = official_archives.all.get_extended(name)[0] - dsl1 = _pproc.get_DataSetList(name) # also called from cocopp.load2 - if not _pproc._pickle_file_read: - print("{} pickled data could not be read in, hence" - " the pickle_regression_test can not be done" - .format(name)) - return - ignore_val, _pproc._ignore_pickle_files = _pproc._ignore_pickle_files, True - dsl2 = _pproc.get_DataSetList(name) - _pproc._ignore_pickle_files = ignore_val - assert not _pproc._pickle_file_read, (name, _pproc._pickle_file_read) - - dsl2._is_same_list_assert(dsl1) - - settings.pretend_initial_solution_was_evaluated = saved - - print("*** readin_regression_test done ***") - -""" - import cocopp - print(dir(cocopp)) - for s in dir(cocopp): - if(inspect.ismodule(eval("cocopp."+s)) and s[:2]!="__"): - print("cocopp."+s) - doctest.testmod(eval("cocopp."+s),verbose=False) - print(cocopp.__all__) -""" - -if __name__ == "__main__": - matplotlib.use("Agg") # To avoid window popup and use without X forwarding - if "doctest" in sys.argv or "doctests" in sys.argv: - do_doctest() - else: - main(sys.argv[1:]) diff --git a/tests/README.md b/tests/README.md index cac8673..053e9d6 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,8 +1,7 @@ # Tests for `cocopp` A pytest test suite meant as a safety net for restructuring the package. -It complements (and does not yet replace) the legacy runner -`python -m cocopp.test`. +It replaces the former test runner `python -m cocopp.test`. ## Running @@ -11,6 +10,8 @@ uv venv && uv pip install -e . pytest # or: pip install -e . pytest pytest tests # everything except network tests, ~10 minutes pytest tests -m "not slow" # unit, module and smoke tests, ~1 minute pytest tests --run-network # also tests using the online data archives +pytest tests/network --run-network -k "not legacy" # as in CI: archives, doctests, former default tests +python tests/run_doctests.py # only the doctests (writes into the package folder!) pytest tests -n auto # in parallel, with pytest-xdist installed ``` @@ -22,7 +23,7 @@ pytest tests -n auto # in parallel, with pytest-xdist inst | `integration/` | the plotting and table modules called directly (`test_modules.py`) and the full pipeline `cocopp.main` on many scenarios (`test_pipeline.py`) | | `regression/` | golden-file snapshots of the complete text output of the pipeline | | `smoke/` | the command line entry points, run in subprocesses | -| `network/` | the official online archives and the scenarios of the legacy `cocopp/test.py`, including its doctests | +| `network/` | the official online archives, the doctests, and the scenarios of the former `cocopp/test.py` | Helpers: @@ -35,6 +36,7 @@ Helpers: - `pipeline.py` runs `cocopp.main` in-process and defines the `SCENARIOS` shared by the integration and regression tests (each runs once per session). - `snapshots.py` takes, stores, compares and extracts output snapshots. +- `run_doctests.py` runs the doctests of all package modules. ## Isolation and randomness diff --git a/tests/integration/test_pipeline.py b/tests/integration/test_pipeline.py index dddf275..ce15d33 100644 --- a/tests/integration/test_pipeline.py +++ b/tests/integration/test_pipeline.py @@ -262,7 +262,7 @@ def test_output_is_deterministic(bbob_small, tmp_path): def test_main_twice_in_same_session(bbob_small, bbob_old_format, tmp_path): - """state must not leak between two calls in the same session (test.py subtest 19)""" + """state must not leak between two calls in the same session (subtest 19 of the former cocopp/test.py)""" import contextlib import io import warnings diff --git a/tests/network/test_archives_online.py b/tests/network/test_archives_online.py index 745bb13..964c0d8 100644 --- a/tests/network/test_archives_online.py +++ b/tests/network/test_archives_online.py @@ -1,15 +1,16 @@ """Tests that need the official online COCO data archives. These are skipped unless pytest is called with ``--run-network``. They -port the scenarios of the legacy test runner ``python -m cocopp.test`` -(which is still used in CI) to pytest. Downloaded data are cached in +contain the scenarios of the former test runner ``python -m cocopp.test`` +(``cocopp/test.py``), which they replace. Downloaded data are cached in the user's cocopp cache folder (as with any use of `cocopp.archives`). -The full post-processing runs of the legacy "all" tests are additionally -marked ``slow``. +The full post-processing runs of the former "all" tests (named +``test_legacy_*``) are additionally marked ``slow`` and are not run in CI. """ import os +import re import subprocess import sys @@ -103,14 +104,14 @@ def test_postprocess_constrained_archive_by_url(tmp_path): ids=lambda a: a[:40], ) def test_legacy_all_scenarios(args, tmp_path): - """the "all" scenarios (subtests 3-20) of the legacy ``python -m cocopp.test all``""" + """the "all" scenarios (subtests 3-20) of the former ``python -m cocopp.test all``""" run = _run_official(args, tmp_path) assert "ALL done" in run.stdout @pytest.mark.slow def test_legacy_main_twice_in_one_process(package_copy, tmp_path): - """legacy subtest 19: post-process archived data twice in the same python session""" + """former subtest 19: post-process archived data twice in the same python session""" script = "import cocopp; cocopp.main('--no-interactive slsqp!'); cocopp.main('--no-interactive abc*')" env = dict(os.environ, PYTHONPATH=str(package_copy.parent), MPLBACKEND="Agg") res = subprocess.run([sys.executable, "-c", script], cwd=str(tmp_path), env=env, capture_output=True, text=True, timeout=3600) @@ -119,7 +120,7 @@ def test_legacy_main_twice_in_one_process(package_copy, tmp_path): def test_pickle_regression(quiet, monkeypatch): - """legacy ``do_pickle_regression_test``: data read from a pickle file equal data read from the archive""" + """former ``do_pickle_regression_test``: data read from a pickle file equal data read from the archive""" monkeypatch.setattr(genericsettings, "pretend_initial_solution_was_evaluated", False) name = cocopp.archives.all.get_extended("strained/2022/al3-cma")[0] dsl1 = pproc.get_DataSetList(name) @@ -135,13 +136,15 @@ def test_pickle_regression(quiet, monkeypatch): dsl2._is_same_list_assert(dsl1) -@pytest.mark.slow def test_package_doctests(package_copy): - """run the doctests of all modules like ``python -m cocopp.test doctest`` (the CI does this) + """run the doctests of all modules with ``tests/run_doctests.py`` The doctests run in the throw-away package copy because they change into the package folder and may write files there. """ + runner = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "run_doctests.py") env = dict(os.environ, PYTHONPATH=str(package_copy.parent), MPLBACKEND="Agg") - res = subprocess.run([sys.executable, "-m", "cocopp.test", "doctest"], cwd=str(package_copy.parent), env=env, capture_output=True, text=True, timeout=1800) + res = subprocess.run([sys.executable, runner], cwd=str(package_copy.parent), env=env, capture_output=True, text=True, timeout=1800) assert res.returncode == 0, res.stdout[-5000:] + res.stderr[-5000:] + summary = re.search(r"doctests: (\d+) of (\d+) examples failed", res.stdout) + assert summary and summary.group(1) == "0" and int(summary.group(2)) > 100, res.stdout[-2000:] diff --git a/tests/run_doctests.py b/tests/run_doctests.py new file mode 100644 index 0000000..fe1f545 --- /dev/null +++ b/tests/run_doctests.py @@ -0,0 +1,49 @@ +"""Run the doctests of all modules of the `cocopp` package found on the path. + +Usage:: + + python tests/run_doctests.py + +Replaces ``python -m cocopp.test doctest`` of the removed legacy test +runner. As before, each ``.py`` file of the package is run with +`doctest.testfile` from within the package folder, because some doctests +rely on relative paths. Many doctests need the online data archives and +some write files into the package folder, hence the test suite runs this +script in a subprocess on a throw-away copy of the package (see +``tests/network/test_archives_online.py``). + +Exit with status 1 if any doctest fails. +""" + +import doctest +import os +import sys + +import matplotlib + +matplotlib.use("Agg") + + +def main(): + import cocopp + + package_dir = os.path.dirname(os.path.abspath(cocopp.__file__)) + os.chdir(package_dir) + failures, tests, failed_files = 0, 0, [] + for root, _dirs, files in sorted(os.walk(package_dir)): + for name in sorted(files): + if not name.endswith(".py"): + continue + path = os.path.join(root, name) + f, t = doctest.testfile(path, module_relative=False, report=True) + failures, tests = failures + f, tests + t + if f: + failed_files.append("%s (%d of %d failed)" % (os.path.relpath(path, package_dir), f, t)) + print("doctests: %d of %d examples failed" % (failures, tests)) + for name in failed_files: + print(" failed: " + name) + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/smoke/test_cli.py b/tests/smoke/test_cli.py index 416928e..9496dc4 100644 --- a/tests/smoke/test_cli.py +++ b/tests/smoke/test_cli.py @@ -91,7 +91,7 @@ def test_cli_postprocess_two(run_python, bbob_small, bbob_small_b, tmp_path): @pytest.mark.slow def test_python_api_main_twice(run_python, bbob_small, bbob_old_format): - """corresponds to subtest 19 of the legacy ``cocopp/test.py``""" + """corresponds to subtest 19 of the former ``cocopp/test.py``""" script = "import cocopp; cocopp.main(%r); cocopp.main(%r)" % ("--no-interactive -o a " + bbob_small, "--no-interactive -o b " + bbob_old_format) res = run_python("-c", script) assert res.returncode == 0, res.stderr[-3000:] diff --git a/tests/unit/test_package.py b/tests/unit/test_package.py index ec8f221..9fb53de 100644 --- a/tests/unit/test_package.py +++ b/tests/unit/test_package.py @@ -21,7 +21,7 @@ def test_module_list_is_complete(): # if this fails, a module was added or removed: update the tests accordingly - assert len(ALL_MODULES) == 47 + assert len(ALL_MODULES) == 46 for name in ["cocopp.pproc", "cocopp.compall.pprldmany", "cocopp.comp2.ppscatter", "cocopp.rungeneric"]: assert name in ALL_MODULES From 69f4d95edbab2b2dd54d24e827e78dc04889da3d Mon Sep 17 00:00:00 2001 From: Olaf Mersmann Date: Tue, 20 Oct 2026 19:14:09 +0200 Subject: [PATCH 10/10] [pyproject] add pytest to the dev dependency group and use it in CI --- pyproject.toml | 1 + tests/README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3cdd0d1..eddefae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,5 +71,6 @@ ignore = ['E401', 'E402', [dependency-groups] dev = [ + "pytest>=9.1.1", "ruff>=0.14.3", ] diff --git a/tests/README.md b/tests/README.md index 053e9d6..3013fb6 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,7 +6,7 @@ It replaces the former test runner `python -m cocopp.test`. ## Running ```sh -uv venv && uv pip install -e . pytest # or: pip install -e . pytest +uv sync # or: pip install -e . --group dev (pip >= 25.1) pytest tests # everything except network tests, ~10 minutes pytest tests -m "not slow" # unit, module and smoke tests, ~1 minute pytest tests --run-network # also tests using the online data archives