From 3201fdd792e797426e9d9a823a16c0d4a30be580 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 26 Mar 2024 10:31:22 +0100 Subject: [PATCH] Faster polygonal line extraction commit 432422a27615824bc5dd53086d79e0848fdbcb94 Author: Robin Champenois <4519091+Evarin@users.noreply.github.com> Date: Fri Mar 1 14:25:19 2024 +0100 Better legacy polygon arrow behavior commit aeaa6890cd3952ccb7008f021bbaee6fbfd62d93 Author: Robin Champenois <4519091+Evarin@users.noreply.github.com> Date: Tue Feb 27 19:20:41 2024 +0100 Tests for arrow dataset and new polygons commit be0083ca02832766b0fe25caba0d8171868e3ec6 Author: Robin Champenois <4519091+Evarin@users.noreply.github.com> Date: Tue Feb 27 17:29:25 2024 +0100 Handle extract_polygons toggle for Arrow Datasets commit d830fb7a3418aa85340289fd9fac05e9d5635bdc Author: Robin Champenois <4519091+Evarin@users.noreply.github.com> Date: Mon Feb 26 16:57:33 2024 +0100 Improve legacy polygon tests commit b99b7882ee3f38c16d9fb41edbf6af343d527368 Author: Robin Champenois <4519091+Evarin@users.noreply.github.com> Date: Mon Feb 26 16:01:31 2024 +0100 Full new polygon extraction tests commit d3398797faa5216e61682bfd62474bb720c325bf Author: Robin Champenois <4519091+Evarin@users.noreply.github.com> Date: Mon Feb 26 13:31:40 2024 +0100 [WIP] test the application of new polygons commit 16be09173b7eb2e5c2c00b03254b6e246e7e158c Author: Benjamin Kiessling Date: Tue Mar 26 10:28:31 2024 +0100 [WIP] legacy polygon flag system commit 197569e0ce28df80613b9b666231e49c902dcb96 Author: Benjamin Kiessling Date: Tue Mar 26 10:27:57 2024 +0100 Fix tests commit 29c7266e802370c356f64b22aa9817e8df721937 Author: Robin Champenois <4519091+Evarin@users.noreply.github.com> Date: Mon Dec 4 13:43:20 2023 +0100 Faster and cleaner extract_polygons and _rotate --- kraken/contrib/extract_lines.py | 7 +- kraken/ketos/dataset.py | 7 +- kraken/ketos/pretrain.py | 11 +- kraken/ketos/recognition.py | 52 +- kraken/ketos/ro.py | 2 +- kraken/ketos/segmentation.py | 4 +- kraken/kraken.py | 12 +- kraken/lib/arrow_dataset.py | 12 +- kraken/lib/dataset/recognition.py | 15 +- kraken/lib/pretrain/model.py | 32 +- kraken/lib/segmentation.py | 413 +++++++++++----- kraken/lib/train.py | 24 +- kraken/lib/vgsl.py | 14 +- kraken/rpred.py | 33 +- tests/resources/170025120000003,0074-lite.xml | 89 ++++ tests/resources/overfit_newpoly.mlmodel | Bin 0 -> 183424 bytes tests/test_newpolygons.py | 449 ++++++++++++++++++ 17 files changed, 1027 insertions(+), 149 deletions(-) create mode 100644 tests/resources/170025120000003,0074-lite.xml create mode 100644 tests/resources/overfit_newpoly.mlmodel create mode 100644 tests/test_newpolygons.py diff --git a/kraken/contrib/extract_lines.py b/kraken/contrib/extract_lines.py index 95233263c..78f779af9 100755 --- a/kraken/contrib/extract_lines.py +++ b/kraken/contrib/extract_lines.py @@ -9,8 +9,9 @@ 'link to source images.') @click.option('-i', '--model', default=None, show_default=True, type=click.Path(exists=True), help='Baseline detection model to use. Overrides format type and expects image files as input.') +@click.option('--legacy-polygons', is_flag=True, help='Use the legacy polygon extractor.') @click.argument('files', nargs=-1) -def cli(format_type, model, files): +def cli(format_type, model, legacy_polygons, files): """ A small script extracting rectified line polygons as defined in either ALTO or PageXML files or run a model to do the same. @@ -37,7 +38,7 @@ def cli(format_type, model, files): data = xml.XMLPage(doc, format_type) if len(data.lines) > 0: bounds = data.to_container() - for idx, (im, box) in enumerate(segmentation.extract_polygons(Image.open(bounds.imagename), bounds)): + for idx, (im, box) in enumerate(segmentation.extract_polygons(Image.open(bounds.imagename), bounds, legacy=legacy_polygons)): click.echo('.', nl=False) im.save('{}.{}.jpg'.format(splitext(bounds.imagename)[0], idx)) with open('{}.{}.gt.txt'.format(splitext(bounds.imagename)[0], idx), 'w') as fp: @@ -61,7 +62,7 @@ def cli(format_type, model, files): click.echo(f'Processing {doc} ', nl=False) full_im = Image.open(doc) bounds = blla.segment(full_im, model=net) - for idx, (im, box) in enumerate(segmentation.extract_polygons(full_im, bounds)): + for idx, (im, box) in enumerate(segmentation.extract_polygons(full_im, bounds, legacy=legacy_polygons)): click.echo('.', nl=False) im.save('{}.{}.jpg'.format(splitext(doc)[0], idx)) diff --git a/kraken/ketos/dataset.py b/kraken/ketos/dataset.py index a4df23400..06154d78a 100644 --- a/kraken/ketos/dataset.py +++ b/kraken/ketos/dataset.py @@ -55,9 +55,11 @@ help='Minimum number of records per RecordBatch written to the ' 'output file. Larger batches require more transient memory ' 'but slightly improve reading performance.') +@click.option('--legacy-polygons', show_default=True, default=False, is_flag=True, + help='Use the old polygon extractor.') @click.argument('ground_truth', nargs=-1, type=click.Path(exists=True, dir_okay=False)) def compile(ctx, output, workers, format_type, files, random_split, force_type, - save_splits, skip_empty_lines, recordbatch_size, ground_truth): + save_splits, skip_empty_lines, recordbatch_size, ground_truth, legacy_polygons): """ Precompiles a binary dataset from a collection of XML files. """ @@ -91,6 +93,7 @@ def compile(ctx, output, workers, format_type, files, random_split, force_type, force_type, recordbatch_size, skip_empty_lines, - lambda advance, total: progress.update(extract_task, total=total, advance=advance)) + lambda advance, total: progress.update(extract_task, total=total, advance=advance), + legacy_polygons=legacy_polygons) message(f'Output file written to {output}') diff --git a/kraken/ketos/pretrain.py b/kraken/ketos/pretrain.py index a8c404dac..5d6055849 100644 --- a/kraken/ketos/pretrain.py +++ b/kraken/ketos/pretrain.py @@ -133,7 +133,7 @@ @click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data. Overrides the `-p` parameter') -@click.option('--workers', show_default=True, default=1, type=click.IntRange(1), help='Number of worker processes.') +@click.option('--workers', show_default=True, default=1, type=click.IntRange(0), help='Number of worker processes.') @click.option('--threads', show_default=True, default=1, type=click.IntRange(1), help='Maximum size of OpenMP/BLAS thread pool.') @click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, help='When loading an existing model, retrieve hyperparameters from the model') @@ -179,6 +179,7 @@ default=RECOGNITION_PRETRAIN_HYPER_PARAMS['logit_temp'], help='Multiplicative factor for the logits used in contrastive loss.') @click.argument('ground_truth', nargs=-1, callback=_expand_gt, type=click.Path(exists=False, dir_okay=False)) +@click.option('--legacy-polygons', show_default=True, default=False, is_flag=True, help='Use the legacy polygon extractor.') def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, min_epochs, lag, min_delta, device, precision, optimizer, lrate, momentum, weight_decay, warmup, schedule, gamma, step_size, sched_patience, @@ -186,7 +187,7 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, evaluation_files, workers, threads, load_hyper_parameters, repolygonize, force_binarization, format_type, augment, mask_probability, mask_width, num_negatives, logit_temp, - ground_truth): + ground_truth, legacy_polygons): """ Trains a model from image-text pairs. """ @@ -258,7 +259,8 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, output=output, spec=spec, model=load, - load_hyper_parameters=load_hyper_parameters) + load_hyper_parameters=load_hyper_parameters, + legacy_polygons=legacy_polygons) data_module = PretrainDataModule(batch_size=hyper_params.pop('batch_size'), pad=hyper_params.pop('pad'), @@ -273,7 +275,8 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, channels=model.channels, repolygonize=repolygonize, force_binarization=force_binarization, - format_type=format_type) + format_type=format_type, + legacy_polygons=legacy_polygons,) model.len_train_set = len(data_module.train_dataloader()) diff --git a/kraken/ketos/recognition.py b/kraken/ketos/recognition.py index 849408162..559d86d3b 100644 --- a/kraken/ketos/recognition.py +++ b/kraken/ketos/recognition.py @@ -21,6 +21,8 @@ import logging import pathlib from typing import List +from functools import partial +import warnings import click from threadpoolctl import threadpool_limits @@ -157,7 +159,7 @@ @click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data. Overrides the `-p` parameter') -@click.option('--workers', show_default=True, default=1, type=click.IntRange(1), help='Number of worker processes.') +@click.option('--workers', show_default=True, default=1, type=click.IntRange(0), help='Number of worker processes.') @click.option('--threads', show_default=True, default=1, type=click.IntRange(1), help='Maximum size of OpenMP/BLAS thread pool.') @click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, help='When loading an existing model, retrieve hyperparameters from the model') @@ -190,6 +192,7 @@ @click.option('--log-dir', show_default=True, type=click.Path(exists=True, dir_okay=True, writable=True), help='Path to directory where the logger will store the logs. If not set, a directory will be created in the current working directory.') @click.argument('ground_truth', nargs=-1, callback=_expand_gt, type=click.Path(exists=False, dir_okay=False)) +@click.option('--legacy-polygons', show_default=True, default=False, is_flag=True, help='Use the legacy polygon extractor.') def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, min_epochs, lag, min_delta, device, precision, optimizer, lrate, momentum, weight_decay, warmup, freeze_backbone, schedule, gamma, step_size, @@ -197,7 +200,7 @@ def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, normalize_whitespace, codec, resize, reorder, base_dir, training_files, evaluation_files, workers, threads, load_hyper_parameters, repolygonize, force_binarization, format_type, augment, - pl_logger, log_dir, ground_truth): + pl_logger, log_dir, ground_truth, legacy_polygons): """ Trains a model from image-text pairs. """ @@ -300,7 +303,19 @@ def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, force_binarization=force_binarization, format_type=format_type, codec=codec, - resize=resize) + resize=resize, + legacy_polygons=legacy_polygons) + + # Force upgrade to new polygon extractor if model was not trained with it + if model.nn and model.nn.use_legacy_polygons: + if not legacy_polygons and not model.legacy_polygons: + # upgrade to new polygon extractor + logger.warning('The model will be flagged to use new polygon extractor.') + model.nn.use_legacy_polygons = False + if not model.nn and legacy_polygons != model.legacy_polygons: + logger.warning(f'Dataset was compiled with legacy polygon extractor: {model.legacy_polygons}, ' + f'the new model will be flagged to use {"legacy" if model.legacy_polygons else "new"} method.') + legacy_polygons = model.legacy_polygons trainer = KrakenTrainer(accelerator=accelerator, devices=device, @@ -349,7 +364,7 @@ def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, @click.option('--pad', show_default=True, type=click.INT, default=16, help='Left and right ' 'padding around lines') @click.option('--workers', show_default=True, default=1, - type=click.IntRange(1), + type=click.IntRange(0), help='Number of worker processes when running on CPU.') @click.option('--threads', show_default=True, default=1, type=click.IntRange(1), @@ -387,9 +402,10 @@ def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, @click.option('--fixed-splits/--ignore-fixed-split', show_default=True, default=False, help='Whether to honor fixed splits in binary datasets.') @click.argument('test_set', nargs=-1, callback=_expand_gt, type=click.Path(exists=False, dir_okay=False)) +@click.option('--no-legacy-polygons', show_default=True, default=False, is_flag=True, help='Force disable the legacy polygon extractor.') def test(ctx, batch_size, model, evaluation_files, device, pad, workers, threads, reorder, base_dir, normalization, normalize_whitespace, - repolygonize, force_binarization, format_type, fixed_splits, test_set): + repolygonize, force_binarization, format_type, fixed_splits, test_set, no_legacy_polygons): """ Evaluate on a test set. """ @@ -410,11 +426,28 @@ def test(ctx, batch_size, model, evaluation_files, device, pad, workers, logger.info('Building test set from {} line images'.format(len(test_set) + len(evaluation_files))) + legacy_polygons = None + incoherent_legacy_polygons = False + nn = {} for p in model: message('Loading model {}\t'.format(p), nl=False) nn[p] = models.load_any(p, device) message('\u2713', fg='green') + model_legacy_polygons = nn[p].nn.use_legacy_polygons + if legacy_polygons is None: + legacy_polygons = model_legacy_polygons + elif legacy_polygons != model_legacy_polygons: + incoherent_legacy_polygons = True + + if incoherent_legacy_polygons and not no_legacy_polygons: + logger.warning('Models use different polygon extractors. Legacy polygon extractor will be used ; use --no-legacy-polygons to force disable it.') + legacy_polygons = True + elif no_legacy_polygons: + legacy_polygons = False + + if legacy_polygons: + warnings.warn('Using legacy polygon extractor, as the model was not trained with the new method. Please retrain your model to get performance improvements.') pin_ds_mem = False if device != 'cpu': @@ -440,7 +473,7 @@ def test(ctx, batch_size, model, evaluation_files, device, pad, workers, message('Repolygonizing data') test_set = [{'page': XMLPage(file, filetype=format_type).to_container()} for file in test_set] valid_norm = False - DatasetClass = PolygonGTDataset + DatasetClass = partial(PolygonGTDataset, legacy_polygons=legacy_polygons) elif format_type == 'binary': DatasetClass = ArrowIPCRecognitionDataset if repolygonize: @@ -485,6 +518,13 @@ def test(ctx, batch_size, model, evaluation_files, device, pad, workers, ds.add(**line) except ValueError as e: logger.info(e) + + if hasattr(ds, 'legacy_polygon_status'): + if ds.legacy_polygons_status != legacy_polygons: + warnings.warn( + f'Binary dataset was compiled with legacy polygon extractor: {ds.legacy_polygon_status}, ' + f'while expecting data extracted with {"legacy" if legacy_polygons else "new"} method. Results may be inaccurate.') + # don't encode validation set as the alphabets may not match causing encoding failures ds.no_encode() ds_loader = DataLoader(ds, diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index 9ff26d57e..33191d596 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -123,7 +123,7 @@ @click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data. Overrides the `-p` parameter') -@click.option('--workers', show_default=True, default=1, type=click.IntRange(1), help='Number of worker proesses.') +@click.option('--workers', show_default=True, default=1, type=click.IntRange(0), help='Number of worker proesses.') @click.option('--threads', show_default=True, default=1, type=click.IntRange(1), help='Maximum size of OpenMP/BLAS thread pool.') @click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, help='When loading an existing model, retrieve hyper-parameters from the model') diff --git a/kraken/ketos/segmentation.py b/kraken/ketos/segmentation.py index 4d6cdfaeb..f1391e358 100644 --- a/kraken/ketos/segmentation.py +++ b/kraken/ketos/segmentation.py @@ -159,7 +159,7 @@ def _validate_merging(ctx, param, value): @click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data. Overrides the `-p` parameter') -@click.option('--workers', show_default=True, default=1, type=click.IntRange(1), help='Number of worker proesses.') +@click.option('--workers', show_default=True, default=1, type=click.IntRange(0), help='Number of worker proesses.') @click.option('--threads', show_default=True, default=1, type=click.IntRange(1), help='Maximum size of OpenMP/BLAS thread pool.') @click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, help='When loading an existing model, retrieve hyper-parameters from the model') @@ -382,7 +382,7 @@ def segtrain(ctx, output, spec, line_width, pad, load, freq, quit, epochs, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data.') @click.option('-d', '--device', show_default=True, default='cpu', help='Select device to use (cpu, cuda:0, cuda:1, ...)') -@click.option('--workers', default=1, show_default=True, type=click.IntRange(1), +@click.option('--workers', default=1, show_default=True, type=click.IntRange(0), help='Number of worker processes for data loading.') @click.option('--threads', default=1, show_default=True, type=click.IntRange(1), help='Size of thread pools for intra-op parallelization') diff --git a/kraken/kraken.py b/kraken/kraken.py index 9c33dbf29..61b653880 100644 --- a/kraken/kraken.py +++ b/kraken/kraken.py @@ -227,10 +227,12 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, if bounds.script_detection: it = rpred.mm_rpred(model, im, bounds, pad, bidi_reordering=bidi_reordering, - tags_ignore=tags_ignore) + tags_ignore=tags_ignore, + no_legacy_polygons=ctx.meta['no_legacy_polygons']) else: it = rpred.rpred(model['default'], im, bounds, pad, - bidi_reordering=bidi_reordering) + bidi_reordering=bidi_reordering, + no_legacy_polygons=ctx.meta['no_legacy_polygons']) preds = [] @@ -302,8 +304,10 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, help='On compatible devices, uses autocast for `segment` which lower the memory usage.') @click.option('--threads', default=1, show_default=True, type=click.IntRange(1), help='Size of thread pools for intra-op parallelization') +@click.option('--no-legacy-polygons', 'no_legacy_polygons', is_flag=True, default=False, + help="Force disable legacy polygon extraction") def cli(input, batch_input, suffix, verbose, format_type, pdf_format, - serializer, template, device, raise_on_error, autocast, threads): + serializer, template, device, raise_on_error, autocast, threads, no_legacy_polygons): """ Base command for recognition functionality. @@ -334,6 +338,8 @@ def cli(input, batch_input, suffix, verbose, format_type, pdf_format, ctx.meta['steps'] = [] ctx.meta["autocast"] = autocast ctx.meta['threads'] = threads + ctx.meta['no_legacy_polygons'] = no_legacy_polygons + log.set_logger(logger, level=30 - min(10 * verbose, 20)) diff --git a/kraken/lib/arrow_dataset.py b/kraken/lib/arrow_dataset.py index c9159a916..d3b4a9288 100755 --- a/kraken/lib/arrow_dataset.py +++ b/kraken/lib/arrow_dataset.py @@ -44,7 +44,7 @@ logger = logging.getLogger(__name__) -def _extract_line(xml_record, skip_empty_lines: bool = True): +def _extract_line(xml_record, skip_empty_lines: bool = True, legacy_polygons: bool = False): lines = [] try: im = Image.open(xml_record.imagename) @@ -62,7 +62,7 @@ def _extract_line(xml_record, skip_empty_lines: bool = True): script_detection=False, line_orders=[]) try: - line_im, line = next(extract_polygons(im, seg)) + line_im, line = next(extract_polygons(im, seg, legacy=legacy_polygons)) except KrakenInputException: logger.warning(f'Invalid line {idx} in {im.filename}') continue @@ -113,7 +113,8 @@ def build_binary_dataset(files: Optional[List[Union[str, 'PathLike', Dict]]] = N force_type: Optional[str] = None, recordbatch_size: int = 100, skip_empty_lines: bool = True, - callback: Callable[[int, int], None] = lambda chunk, lines: None) -> None: + callback: Callable[[int, int], None] = lambda chunk, lines: None, + legacy_polygons: bool = False) -> None: """ Parses XML files and dumps the baseline-style line images and text into a binary dataset. @@ -141,10 +142,11 @@ def build_binary_dataset(files: Optional[List[Union[str, 'PathLike', Dict]]] = N skip_empty_lines: Do not compile empty text lines into the dataset. callback: Function called every time a new recordbatch is flushed into the Arrow IPC file. + legacy_polygons: Use legacy polygon extraction code. """ logger.info('Parsing XML files') - extract_fn = partial(_extract_line, skip_empty_lines=skip_empty_lines) + extract_fn = partial(_extract_line, skip_empty_lines=skip_empty_lines, legacy_polygons=legacy_polygons) parse_fn = None if format_type in ['xml', 'alto', 'page']: parse_fn = XMLPage @@ -216,6 +218,7 @@ def build_binary_dataset(files: Optional[List[Union[str, 'PathLike', Dict]]] = N 'image_type': 'raw', 'splits': ['train', 'eval', 'test'], 'im_mode': '1', + 'legacy_polygons': legacy_polygons, 'counts': Counter({'all': 0, 'train': 0, 'validation': 0, @@ -309,6 +312,7 @@ def _make_record_batch(line_cache): f"image_type: {metadata['lines']['image_type']}\n" f"splits: {metadata['lines']['splits']}\n" f"im_mode: {metadata['lines']['im_mode']}\n" + f"legacy_polygons: {metadata['lines']['legacy_polygons']}\n" f"lines: {metadata['lines']['counts']}\n") with pa.memory_map(tmp_file, 'rb') as source: diff --git a/kraken/lib/dataset/recognition.py b/kraken/lib/dataset/recognition.py index a80f6cd89..fde975cc5 100644 --- a/kraken/lib/dataset/recognition.py +++ b/kraken/lib/dataset/recognition.py @@ -121,6 +121,7 @@ def __init__(self, self.arrow_table = None self.codec = None self.skip_empty_lines = skip_empty_lines + self.legacy_polygons_status = None self.seg_type = None # built text transformations @@ -174,6 +175,12 @@ def add(self, file: Union[str, 'PathLike']) -> None: if self.seg_type == 'bbox' and metadata['image_type'] == 'raw': self.transforms.valid_norm = True + legacy_polygons = metadata.get('legacy_polygons', True) + if self.legacy_polygons_status is None: + self.legacy_polygons_status = legacy_polygons + elif self.legacy_polygons_status != legacy_polygons: + self.legacy_polygons_status = "mixed" + self.alphabet.update(metadata['alphabet']) num_lines = metadata['counts'][self._split_filter] if self._split_filter else metadata['counts']['all'] if self._split_filter: @@ -284,7 +291,8 @@ def __init__(self, skip_empty_lines: bool = True, reorder: Union[bool, Literal['L', 'R']] = True, im_transforms: Callable[[Any], torch.Tensor] = transforms.Compose([]), - augmentation: bool = False) -> None: + augmentation: bool = False, + legacy_polygons: bool=False) -> None: """ Creates a dataset for a polygonal (baseline) transcription model. @@ -307,6 +315,7 @@ def __init__(self, self.aug = None self.skip_empty_lines = skip_empty_lines self.failed_samples = set() + self.legacy_polygons = legacy_polygons self.seg_type = 'baselines' # built text transformations @@ -424,8 +433,8 @@ def __getitem__(self, index: int) -> Tuple[torch.Tensor, torch.Tensor]: boundary=item[0][2])], script_detection=True, regions={}, - line_orders=[]) - )) + line_orders=[]), + legacy=self.legacy_polygons)) im = self.transforms(im) if im.shape[0] == 3: im_mode = 'RGB' diff --git a/kraken/lib/pretrain/model.py b/kraken/lib/pretrain/model.py index 4685e1851..68626cf49 100644 --- a/kraken/lib/pretrain/model.py +++ b/kraken/lib/pretrain/model.py @@ -32,6 +32,7 @@ import math import re from itertools import chain +from functools import partial from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union import numpy as np @@ -87,7 +88,8 @@ def __init__(self, force_binarization: bool = False, format_type: str = 'path', pad: int = 16, - augment: bool = default_specs.RECOGNITION_PRETRAIN_HYPER_PARAMS['augment']): + augment: bool = default_specs.RECOGNITION_PRETRAIN_HYPER_PARAMS['augment'], + legacy_polygons: bool = False): """ A LightningDataModule encapsulating text-less training data for unsupervised recognition model pretraining. @@ -106,6 +108,8 @@ def __init__(self, super().__init__() self.save_hyperparameters() + self.legacy_polygons = legacy_polygons + DatasetClass = GroundTruthDataset valid_norm = True if format_type in ['xml', 'page', 'alto']: @@ -117,7 +121,7 @@ def __init__(self, if binary_dataset_split: logger.warning('Internal binary dataset splits are enabled but using non-binary dataset files. Will be ignored.') binary_dataset_split = False - DatasetClass = PolygonGTDataset + DatasetClass = partial(PolygonGTDataset, legacy_polygons=legacy_polygons) valid_norm = False elif format_type == 'binary': DatasetClass = ArrowIPCRecognitionDataset @@ -147,7 +151,7 @@ def __init__(self, # format_type is None. Determine training type from length of training data entry elif not format_type: if training_data[0].type == 'baselines': - DatasetClass = PolygonGTDataset + DatasetClass = partial(PolygonGTDataset, legacy_polygons=legacy_polygons) valid_norm = False else: if force_binarization: @@ -205,6 +209,19 @@ def __init__(self, 'set. (Will disable alphabet mismatch detection.)') self.train_set, self.val_set = random_split(train_set, (train_len, val_len)) + if format_type == 'binary': + legacy_train_status = train_set.legacy_polygons_status + if val_set and val_set.legacy_polygons_status != legacy_train_status: + logger.warning( + f'Train and validation set have different legacy polygon status: {legacy_train_status} and {val_set.legacy_polygons_status}.' + 'Train set status prevails.') + if legacy_train_status == "mixed": + logger.warning('Mixed legacy polygon status in training dataset. Consider recompilation.') + legacy_train_status = False + if legacy_polygons != legacy_train_status: + logger.warning(f'Setting dataset legacy polygon status to {legacy_train_status} based on training set.') + self.legacy_polygons = legacy_train_status + if len(self.train_set) == 0 or len(self.val_set) == 0: raise ValueError('No valid training data was provided to the train ' 'command. Please add valid XML, line, or binary data.') @@ -255,7 +272,8 @@ def __init__(self, spec: str = default_specs.RECOGNITION_SPEC, model: Optional[Union['PathLike', str]] = None, load_hyper_parameters: bool = False, - len_train_set: int = -1): + len_train_set: int = -1, + legacy_polygons: bool = False): """ A LightningModule encapsulating the unsupervised pretraining setup for a text recognition model. @@ -273,10 +291,15 @@ def __init__(self, """ super().__init__() hyper_params_ = default_specs.RECOGNITION_PRETRAIN_HYPER_PARAMS + self.legacy_polygons = legacy_polygons + if model: logger.info(f'Loading existing model from {model} ') self.nn = vgsl.TorchVGSLModel.load_model(model) + # apply legacy polygon parameter + self.nn.use_legacy_polygons = legacy_polygons + if self.nn.model_type not in [None, 'recognition']: raise ValueError(f'Model {model} is of type {self.nn.model_type} while `recognition` is expected.') @@ -430,6 +453,7 @@ def setup(self, stage: Optional[str] = None): else: logger.info(f'Creating new model {self.spec}') self.nn = vgsl.TorchVGSLModel(self.spec) + self.nn.use_legacy_polygons = self.legacy_polygons # initialize weights self.nn.init_weights() diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 8c979da53..9fad17a60 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -18,15 +18,15 @@ import logging from collections import defaultdict from typing import (TYPE_CHECKING, Dict, List, Literal, Optional, Sequence, - Tuple, Union) + Tuple, Union, TypeVar, Any, Generator) import numpy as np import shapely.geometry as geom import torch import torch.nn.functional as F -from PIL import Image +from PIL import Image, ImageDraw from scipy.ndimage import (binary_erosion, distance_transform_cdt, - gaussian_filter, maximum_filter) + gaussian_filter, maximum_filter, affine_transform) from scipy.signal import convolve2d from scipy.spatial.distance import pdist, squareform from shapely.ops import nearest_points, unary_union @@ -38,15 +38,18 @@ subdivide_polygon) from skimage.morphology import skeletonize from skimage.transform import (AffineTransform, PiecewiseAffineTransform, - SimilarityTransform, warp) + warp) from kraken.lib import default_specs from kraken.lib.exceptions import KrakenInputException if TYPE_CHECKING: - from kraken.containers import Segmentation + from kraken.containers import Segmentation, BBoxLine, BaselineLine from kraken.lib.vgsl import TorchVGSLModel + +_T_pil_or_np = TypeVar('_T_pil_or_np', Image.Image, np.ndarray) + logger = logging.getLogger('kraken') __all__ = ['reading_order', @@ -356,8 +359,6 @@ def vectorize_regions(im: np.ndarray, threshold: float = 0.5): labelled = label(bin) boundaries = [] for x in regionprops(labelled): - if x.area < 32: - continue boundary = boundary_tracing(x) if len(boundary) > 2: boundaries.append(geom.Polygon(boundary)) @@ -371,19 +372,32 @@ def vectorize_regions(im: np.ndarray, threshold: float = 0.5): return [np.array(x.coords, dtype=np.uint)[:, [1, 0]].tolist() for x in boundaries] -def _rotate(image, angle, center, scale, cval=0): +def _rotate(image: _T_pil_or_np, + angle: float, + center: Any, + scale: float, + cval: int = 0, + order: int = 0) -> Tuple[AffineTransform, _T_pil_or_np]: """ - Rotate function taken mostly from scikit image. Main difference is that - this one allows dimensional scaling and records the final translation - to ensure no image content is lost. This is needed to rotate the seam - back into the original image. + Rotate an image at an angle with optional scaling + Args: + image (PIL.Image.Image or (H, W, C) np.ndarray): Input image + angle (float): Angle in radians + center (tuple): unused + scale (float): x-Axis scaling factor + cval (int): Padding value + order (int): Interpolation order + Returns: + A tuple containing the transformation matrix and the rotated image. + Note: this function is much faster applied on PIL images than on numpy ndarrays. """ - rows, cols = image.shape[0], image.shape[1] - tform1 = SimilarityTransform(translation=center) - tform2 = SimilarityTransform(rotation=angle) - tform3 = SimilarityTransform(translation=-center) - tform4 = AffineTransform(scale=(1/scale, 1)) - tform = tform4 + tform3 + tform2 + tform1 + if isinstance(image, Image.Image): + rows, cols = image.height, image.width + else: + rows, cols = image.shape[:2] + assert len(image.shape) == 3 or len(image.shape) == 2, 'Image must be 2D or 3D' + + tform = AffineTransform(rotation=angle, scale=(1/scale, 1)) corners = np.array([ [0, 0], [0, rows - 1], @@ -397,13 +411,25 @@ def _rotate(image, angle, center, scale, cval=0): maxr = corners[:, 1].max() out_rows = maxr - minr + 1 out_cols = maxc - minc + 1 - output_shape = np.around((out_rows, out_cols)) + output_shape = tuple(int(o) for o in np.around((out_rows, out_cols))) # fit output image in new shape - translation = (minc, minr) - tform5 = SimilarityTransform(translation=translation) - tform = tform5 + tform - tform.params[2] = (0, 0, 1) - return tform, warp(image, tform, output_shape=output_shape, order=0, cval=cval, clip=False, preserve_range=True) + translation = tform([[minc, minr]]) + tform = AffineTransform(rotation=angle, scale=(1/scale, 1), translation=[f for f in translation.flatten()]) + + if isinstance(image, Image.Image): + # PIL is much faster than scipy + pdata = tform.params.flatten().tolist()[:6] + resample = {0: Image.NEAREST, 1: Image.BILINEAR, 2: Image.BICUBIC, 3: Image.BICUBIC}.get(order, Image.NEAREST) + return tform, image.transform(output_shape[::-1], Image.AFFINE, data=pdata, resample=resample, fillcolor=cval) + + # params for scipy + # swap X and Y axis for scipy + pdata = tform.params.copy()[[1, 0, 2], :][:, [1, 0, 2]] + # we copy the translation vector + offset = pdata[:2, 2].copy() + # scipy expects a 3x3 *linear* matrix (to include channel axis), we don't want the channel axis to be modified + pdata[:2, 2] = 0 + return tform, affine_transform(image, pdata, offset=(*offset, 0), output_shape=(*output_shape, *image.shape[2:]), cval=cval, order=order) def line_regions(line, regions): @@ -458,7 +484,6 @@ def _calc_seam(baseline, polygon, angle, im_feats, bias=150): level. """ MASK_VAL = 99999 - r, c = draw.polygon(polygon[:, 1], polygon[:, 0]) c_min, c_max = int(polygon[:, 0].min()), int(polygon[:, 0].max()) r_min, r_max = int(polygon[:, 1].min()), int(polygon[:, 1].max()) patch = im_feats[r_min:r_max+2, c_min:c_max+2].copy() @@ -472,8 +497,7 @@ def _calc_seam(baseline, polygon, angle, im_feats, bias=150): mask[line_locs] = 0 dist_bias = distance_transform_cdt(mask) # absolute mask - mask = np.ones_like(patch, dtype=bool) - mask[r-r_min, c-c_min] = False + mask = np.array(make_polygonal_mask(polygon-(r_min, c_min)), patch.shape[1::-1]) > 128 # dilate mask to compensate for aliasing during rotation mask = binary_erosion(mask, border_value=True, iterations=2) # combine weights with features @@ -1025,7 +1049,99 @@ def compute_polygon_section(baseline: Sequence[Tuple[int, int]], return tuple(o) -def extract_polygons(im: Image.Image, bounds: 'Segmentation') -> Image.Image: +def _bevelled_warping_envelope(baseline: np.ndarray, + output_bl_start: Tuple[float, float], + output_shape: Tuple[int, int]) -> Tuple[List[Tuple[int, int]], List[Tuple[int, int]]]: + """ + Calculates the source and target envelope for a piecewise affine transform + """ + def _as_int_tuple(x): + return tuple(int(i) for i in x) + + envelope_dy = [-output_bl_start[1], output_shape[0] - output_bl_start[1]] + diff_bl = np.diff(baseline, axis=0) + diff_bl_normed = diff_bl / np.linalg.norm(diff_bl, axis=1)[:, None] + l_bl = len(baseline) + cum_lens = np.cumsum([0] + np.linalg.norm(diff_bl, axis=1).tolist()) + + bl_seg_normals = np.array([-diff_bl_normed[:, 1], diff_bl_normed[:, 0]]).T + ini_point = baseline[0] - diff_bl_normed[0] * output_bl_start[0] + source_envelope = [ + _as_int_tuple(ini_point + envelope_dy[0]*bl_seg_normals[0]), + _as_int_tuple(ini_point + envelope_dy[1]*bl_seg_normals[0]), + ] + target_envelope = [ + (0, 0), + (0, output_shape[0]) + ] + MAX_BEVEL_WIDTH = output_shape[0] / 3 + BEVEL_STEP_WIDTH = MAX_BEVEL_WIDTH / 2 + + for k in range(l_bl-2): + pt = baseline[k+1] + seg_prev = baseline[k] - pt + seg_next = baseline[k+2] - pt + bevel_prev = seg_prev / max(2., np.linalg.norm(seg_prev) / MAX_BEVEL_WIDTH) + bevel_next = seg_next / max(2., np.linalg.norm(seg_next) / MAX_BEVEL_WIDTH) + bevel_nsteps = max(1, np.round((np.linalg.norm(bevel_prev) + np.linalg.norm(bevel_next)) / BEVEL_STEP_WIDTH)) + l_prev = np.linalg.norm(bevel_prev) + l_next = np.linalg.norm(bevel_next) + for i in range(int(bevel_nsteps)+1): + # bezier interp + t = i / bevel_nsteps + tpt = pt + (1-t)**2 * bevel_prev + t**2 * bevel_next + tx = output_bl_start[0] + cum_lens[k+1] - (1-t)**2 * l_prev + t**2 * l_next + tnormal = (1-t) * bl_seg_normals[k] + t * bl_seg_normals[k+1] + tnormal /= np.linalg.norm(tnormal) + source_points = [_as_int_tuple(tpt + envelope_dy[0]*tnormal), _as_int_tuple(tpt + envelope_dy[1]*tnormal)] + target_points = [(int(tx), 0), (int(tx), output_shape[0])] + # avoid duplicate points leading to singularities + if source_points[0] == source_envelope[-2] or source_points[1] == source_envelope[-1] or target_points[0] == target_envelope[-2]: + continue + source_envelope += source_points + target_envelope += target_points + + end_point = baseline[-1] + diff_bl_normed[-1]*(output_shape[1]-cum_lens[-1]-output_bl_start[0]) + source_envelope += [ + end_point + envelope_dy[0]*bl_seg_normals[-1], + end_point + envelope_dy[1]*bl_seg_normals[-1], + ] + target_envelope += [ + (output_shape[1], 0), + (output_shape[1], output_shape[0]) + ] + return source_envelope, target_envelope + + +def make_polygonal_mask(polygon: np.ndarray, shape: Tuple[int, int]) -> Image.Image: + """ + Creates a mask from a polygon. + + Args: + polygon: A polygon as a list of points. + shape: The shape of the mask to create. + + Returns: + A PIL.Image.Image instance containing the mask. + """ + mask = Image.new('L', shape, 0) + ImageDraw.Draw(mask).polygon([tuple(p) for p in polygon.astype(int).tolist()], fill=255, width=2) + return mask + + +def apply_polygonal_mask(img: Image.Image, polygon: np.ndarray, cval: int = 0) -> Image.Image: + """ + Extract the polygonal mask of an image. + """ + mask = make_polygonal_mask(polygon, img.size) + out = Image.new(img.mode, (img.width, img.height), cval) + out.paste(img, mask=mask) + return out + + +def extract_polygons(im: Image.Image, + bounds: "Segmentation", + legacy: bool = False) -> Generator[Tuple[Image.Image, Union["BBoxLine", "BaselineLine"],], None, None]: """ Yields the subimages of image im defined in the list of bounding polygons with baselines preserving order. @@ -1034,9 +1150,10 @@ def extract_polygons(im: Image.Image, bounds: 'Segmentation') -> Image.Image: im: Input image bounds: A Segmentation class containing a bounding box or baseline segmentation. + legacy: Use the old, slow, and deprecated path Yields: - The extracted subimage + The extracted subimage, and the corresponding bounding box or baseline """ if bounds.type == 'baselines': # select proper interpolation scheme depending on shape @@ -1045,7 +1162,6 @@ def extract_polygons(im: Image.Image, bounds: 'Segmentation') -> Image.Image: im = im.convert('L') else: order = 1 - im = np.array(im) for line in bounds.lines: if line.boundary is None: @@ -1055,85 +1171,170 @@ def extract_polygons(im: Image.Image, bounds: 'Segmentation') -> Image.Image: c_min, c_max = int(pl[:, 0].min()), int(pl[:, 0].max()) r_min, r_max = int(pl[:, 1].min()), int(pl[:, 1].max()) - if (pl < 0).any() or (pl.max(axis=0)[::-1] >= im.shape[:2]).any(): + imshape = np.array([im.height, im.width]) + + if (pl < 0).any() or (pl.max(axis=0)[::-1] >= imshape).any(): raise KrakenInputException('Line polygon outside of image bounds') - if (baseline < 0).any() or (baseline.max(axis=0)[::-1] >= im.shape[:2]).any(): + if (baseline < 0).any() or (baseline.max(axis=0)[::-1] >= imshape).any(): raise KrakenInputException('Baseline outside of image bounds') - # fast path for straight baselines requiring only rotation - if len(baseline) == 2: - baseline = baseline.astype(float) - # calculate direction vector - lengths = np.linalg.norm(np.diff(baseline.T), axis=0) - p_dir = np.mean(np.diff(baseline.T) * lengths/lengths.sum(), axis=1) - p_dir = (p_dir.T / np.sqrt(np.sum(p_dir**2, axis=-1))) - angle = np.arctan2(p_dir[1], p_dir[0]) - patch = im[r_min:r_max+1, c_min:c_max+1].copy() - offset_polygon = pl - (c_min, r_min) - r, c = draw.polygon(offset_polygon[:, 1], offset_polygon[:, 0]) - mask = np.zeros(patch.shape[:2], dtype=bool) - mask[r, c] = True - patch[np.invert(mask)] = 0 - extrema = offset_polygon[(0, -1), :] - # scale line image to max 600 pixel width - tform, rotated_patch = _rotate(patch, angle, center=extrema[0], scale=1.0, cval=0) - i = Image.fromarray(rotated_patch.astype('uint8')) - # normal slow path with piecewise affine transformation - else: - if len(pl) > 50: - pl = approximate_polygon(pl, 2) - full_polygon = subdivide_polygon(pl, preserve_ends=True) - pl = geom.MultiPoint(full_polygon) - - bl = zip(baseline[:-1:], baseline[1::]) - bl = [geom.LineString(x) for x in bl] - cum_lens = np.cumsum([0] + [line.length for line in bl]) - # distance of intercept from start point and number of line segment - control_pts = [] - for point in pl.geoms: - npoint = np.array(point.coords)[0] - line_idx, dist, intercept = min(((idx, line.project(point), - np.array(line.interpolate(line.project(point)).coords)) for idx, line in enumerate(bl)), - key=lambda x: np.linalg.norm(npoint-x[2])) - # absolute distance from start of line - line_dist = cum_lens[line_idx] + dist - intercept = np.array(intercept) - # side of line the point is at - side = np.linalg.det(np.array([[baseline[line_idx+1][0]-baseline[line_idx][0], - npoint[0]-baseline[line_idx][0]], - [baseline[line_idx+1][1]-baseline[line_idx][1], - npoint[1]-baseline[line_idx][1]]])) - side = np.sign(side) - # signed perpendicular distance from the rectified distance - per_dist = side * np.linalg.norm(npoint-intercept) - control_pts.append((line_dist, per_dist)) - # calculate baseline destination points - bl_dst_pts = baseline[0] + np.dstack((cum_lens, np.zeros_like(cum_lens)))[0] - # calculate bounding polygon destination points - pol_dst_pts = np.array([baseline[0] + (line_dist, per_dist) for line_dist, per_dist in control_pts]) - # extract bounding box patch - c_dst_min, c_dst_max = int(pol_dst_pts[:, 0].min()), int(pol_dst_pts[:, 0].max()) - r_dst_min, r_dst_max = int(pol_dst_pts[:, 1].min()), int(pol_dst_pts[:, 1].max()) - output_shape = np.around((r_dst_max - r_dst_min + 1, c_dst_max - c_dst_min + 1)) - patch = im[r_min:r_max+1, c_min:c_max+1].copy() - # offset src points by patch shape - offset_polygon = full_polygon - (c_min, r_min) - offset_baseline = baseline - (c_min, r_min) - # offset dst point by dst polygon shape - offset_bl_dst_pts = bl_dst_pts - (c_dst_min, r_dst_min) - offset_pol_dst_pts = pol_dst_pts - (c_dst_min, r_dst_min) - # mask out points outside bounding polygon - mask = np.zeros(patch.shape[:2], dtype=bool) - r, c = draw.polygon(offset_polygon[:, 1], offset_polygon[:, 0]) - mask[r, c] = True - patch[np.invert(mask)] = 0 - # estimate piecewise transform - src_points = np.concatenate((offset_baseline, offset_polygon)) - dst_points = np.concatenate((offset_bl_dst_pts, offset_pol_dst_pts)) - tform = PiecewiseAffineTransform() - tform.estimate(src_points, dst_points) - o = warp(patch, tform.inverse, output_shape=output_shape, preserve_range=True, order=order) - i = Image.fromarray(o.astype('uint8')) + if legacy: + im = np.array(im) + # Old, slow, and deprecated path + # fast path for straight baselines requiring only rotation + if len(baseline) == 2: + baseline = baseline.astype(float) + # calculate direction vector + lengths = np.linalg.norm(np.diff(baseline.T), axis=0) + p_dir = np.mean(np.diff(baseline.T) * lengths/lengths.sum(), axis=1) + p_dir = (p_dir.T / np.sqrt(np.sum(p_dir**2, axis=-1))) + angle = np.arctan2(p_dir[1], p_dir[0]) + patch = im[r_min:r_max+1, c_min:c_max+1].copy() + offset_polygon = pl - (c_min, r_min) + r, c = draw.polygon(offset_polygon[:, 1], offset_polygon[:, 0]) + mask = np.zeros(patch.shape[:2], dtype=bool) + mask[r, c] = True + patch[np.invert(mask)] = 0 + extrema = offset_polygon[(0, -1), :] + # scale line image to max 600 pixel width + tform, rotated_patch = _rotate(patch, angle, center=extrema[0], scale=1.0, cval=0) + i = Image.fromarray(rotated_patch.astype('uint8')) + # normal slow path with piecewise affine transformation + else: + if len(pl) > 50: + pl = approximate_polygon(pl, 2) + full_polygon = subdivide_polygon(pl, preserve_ends=True) + pl = geom.MultiPoint(full_polygon) + + bl = zip(baseline[:-1:], baseline[1::]) + bl = [geom.LineString(x) for x in bl] + cum_lens = np.cumsum([0] + [line.length for line in bl]) + # distance of intercept from start point and number of line segment + control_pts = [] + for point in pl.geoms: + npoint = np.array(point.coords)[0] + line_idx, dist, intercept = min(((idx, line.project(point), + np.array(line.interpolate(line.project(point)).coords)) for idx, line in enumerate(bl)), + key=lambda x: np.linalg.norm(npoint-x[2])) + # absolute distance from start of line + line_dist = cum_lens[line_idx] + dist + intercept = np.array(intercept) + # side of line the point is at + side = np.linalg.det(np.array([[baseline[line_idx+1][0]-baseline[line_idx][0], + npoint[0]-baseline[line_idx][0]], + [baseline[line_idx+1][1]-baseline[line_idx][1], + npoint[1]-baseline[line_idx][1]]])) + side = np.sign(side) + # signed perpendicular distance from the rectified distance + per_dist = side * np.linalg.norm(npoint-intercept) + control_pts.append((line_dist, per_dist)) + # calculate baseline destination points + bl_dst_pts = baseline[0] + np.dstack((cum_lens, np.zeros_like(cum_lens)))[0] + # calculate bounding polygon destination points + pol_dst_pts = np.array([baseline[0] + (line_dist, per_dist) for line_dist, per_dist in control_pts]) + # extract bounding box patch + c_dst_min, c_dst_max = int(pol_dst_pts[:, 0].min()), int(pol_dst_pts[:, 0].max()) + r_dst_min, r_dst_max = int(pol_dst_pts[:, 1].min()), int(pol_dst_pts[:, 1].max()) + output_shape = np.around((r_dst_max - r_dst_min + 1, c_dst_max - c_dst_min + 1)) + patch = im[r_min:r_max+1, c_min:c_max+1].copy() + # offset src points by patch shape + offset_polygon = full_polygon - (c_min, r_min) + offset_baseline = baseline - (c_min, r_min) + # offset dst point by dst polygon shape + offset_bl_dst_pts = bl_dst_pts - (c_dst_min, r_dst_min) + offset_pol_dst_pts = pol_dst_pts - (c_dst_min, r_dst_min) + # mask out points outside bounding polygon + mask = np.zeros(patch.shape[:2], dtype=bool) + r, c = draw.polygon(offset_polygon[:, 1], offset_polygon[:, 0]) + mask[r, c] = True + patch[np.invert(mask)] = 0 + # estimate piecewise transform + src_points = np.concatenate((offset_baseline, offset_polygon)) + dst_points = np.concatenate((offset_bl_dst_pts, offset_pol_dst_pts)) + tform = PiecewiseAffineTransform() + tform.estimate(src_points, dst_points) + o = warp(patch, tform.inverse, output_shape=output_shape, preserve_range=True, order=order) + i = Image.fromarray(o.astype('uint8')) + + else: # if not legacy + # new, fast, and efficient path + # fast path for straight baselines requiring only rotation + if len(baseline) == 2: + baseline = baseline.astype(float) + # calculate direction vector + lengths = np.linalg.norm(np.diff(baseline.T), axis=0) + p_dir = np.mean(np.diff(baseline.T) * lengths/lengths.sum(), axis=1) + p_dir = (p_dir.T / np.sqrt(np.sum(p_dir**2, axis=-1))) + angle = np.arctan2(p_dir[1], p_dir[0]) + # crop out bounding box + patch = im.crop((c_min, r_min, c_max+1, r_max+1)) + offset_polygon = pl - (c_min, r_min) + patch = apply_polygonal_mask(patch, offset_polygon, cval=0) + extrema = offset_polygon[(0, -1), :] + tform, i = _rotate(patch, angle, center=extrema[0], scale=1.0, cval=0, order=order) + # normal slow path with piecewise affine transformation + else: + if len(pl) > 50: + pl = approximate_polygon(pl, 2) + full_polygon = subdivide_polygon(pl, preserve_ends=True) + + # baseline segment vectors + diff_bl = np.diff(baseline, axis=0) + diff_bl_norms = np.linalg.norm(diff_bl, axis=1) + diff_bl_normed = diff_bl / diff_bl_norms[:, None] + + l_poly = len(full_polygon) + cum_lens = np.cumsum([0] + np.linalg.norm(diff_bl, axis=1).tolist()) + + # calculate baseline destination points : + bl_dst_pts = baseline[0] + np.dstack((cum_lens, np.zeros_like(cum_lens)))[0] + + # calculate bounding polygon destination points : + # diff[k, p] = baseline[k] - polygon[p] + poly_bl_diff = full_polygon[None, :] - baseline[:-1, None] + # local x coordinates of polygon points on baseline segments + # x[k, p] = (baseline[k] - polygon[p]) . (baseline[k+1] - baseline[k]) / |baseline[k+1] - baseline[k]| + poly_bl_x = np.einsum('kpm,km->kp', poly_bl_diff, diff_bl_normed) + # distance to baseline segments + poly_bl_segdist = np.maximum(-poly_bl_x, poly_bl_x - diff_bl_norms[:, None]) + # closest baseline segment index + poly_closest_bl = np.argmin((poly_bl_segdist), axis=0) + poly_bl_x = poly_bl_x[poly_closest_bl, np.arange(l_poly)] + poly_bl_diff = poly_bl_diff[poly_closest_bl, np.arange(l_poly)] + # signed distance between polygon points and baseline segments (to get y coordinates) + poly_bl_y = np.cross(diff_bl_normed[poly_closest_bl], poly_bl_diff) + # final destination points + pol_dst_pts = np.array( + [cum_lens[poly_closest_bl] + poly_bl_x, poly_bl_y] + ).T + baseline[:1] + + # extract bounding box patch + c_dst_min, c_dst_max = int(pol_dst_pts[:, 0].min()), int(pol_dst_pts[:, 0].max()) + r_dst_min, r_dst_max = int(pol_dst_pts[:, 1].min()), int(pol_dst_pts[:, 1].max()) + output_shape = np.around((r_dst_max - r_dst_min + 1, c_dst_max - c_dst_min + 1)) + patch = im.crop((c_min, r_min, c_max+1, r_max+1)) + # offset src points by patch shape + offset_polygon = full_polygon - (c_min, r_min) + offset_baseline = baseline - (c_min, r_min) + # offset dst point by dst polygon shape + offset_bl_dst_pts = bl_dst_pts - (c_dst_min, r_dst_min) + # mask out points outside bounding polygon + patch = apply_polygonal_mask(patch, offset_polygon, cval=0) + + # estimate piecewise transform by beveling angles + source_envelope, target_envelope = _bevelled_warping_envelope(offset_baseline, offset_bl_dst_pts[0], output_shape) + # mesh for PIL, as (box, quad) tuples : box is (NW, SE) and quad is (NW, SW, SE, NE) + deform_mesh = [ + ( + (*target_envelope[i], *target_envelope[i+3]), + (*source_envelope[i], *source_envelope[i+1], *source_envelope[i+3], *source_envelope[i+2]) + ) + for i in range(0, len(source_envelope)-3, 2) + ] + # warp + resample = {0: Image.NEAREST, 1: Image.BILINEAR, 2: Image.BICUBIC, 3: Image.BICUBIC}.get(order, Image.NEAREST) + i = patch.transform((output_shape[1], output_shape[0]), Image.MESH, data=deform_mesh, resample=resample) + yield i.crop(i.getbbox()), line else: if bounds.text_direction.startswith('vertical'): diff --git a/kraken/lib/train.py b/kraken/lib/train.py index 1ba45eb00..da1f4ff79 100644 --- a/kraken/lib/train.py +++ b/kraken/lib/train.py @@ -20,6 +20,7 @@ import warnings from typing import (TYPE_CHECKING, Any, Callable, Dict, Literal, Optional, Sequence, Union) +from functools import partial import numpy as np import pytorch_lightning as pl @@ -216,7 +217,8 @@ def __init__(self, force_binarization: bool = False, format_type: Literal['path', 'alto', 'page', 'xml', 'binary'] = 'path', codec: Optional[Dict] = None, - resize: Literal['fail', 'both', 'new', 'add', 'union'] = 'fail'): + resize: Literal['fail', 'both', 'new', 'add', 'union'] = 'fail', + legacy_polygons: bool = False): """ A LightningModule encapsulating the training setup for a text recognition model. @@ -233,6 +235,7 @@ def __init__(self, **kwargs: Setup parameters, i.e. CLI parameters of the train() command. """ super().__init__() + self.legacy_polygons = legacy_polygons hyper_params_ = default_specs.RECOGNITION_HYPER_PARAMS.copy() if model: logger.info(f'Loading existing model from {model} ') @@ -284,7 +287,7 @@ def __init__(self, if binary_dataset_split: logger.warning('Internal binary dataset splits are enabled but using non-binary dataset files. Will be ignored.') binary_dataset_split = False - DatasetClass = PolygonGTDataset + DatasetClass = partial(PolygonGTDataset, legacy_polygons=legacy_polygons) valid_norm = False elif format_type == 'binary': DatasetClass = ArrowIPCRecognitionDataset @@ -314,7 +317,7 @@ def __init__(self, # format_type is None. Determine training type from container class types elif not format_type: if training_data[0].type == 'baselines': - DatasetClass = PolygonGTDataset + DatasetClass = partial(PolygonGTDataset, legacy_polygons=legacy_polygons) valid_norm = False else: if force_binarization: @@ -375,6 +378,7 @@ def __init__(self, logger.debug('Setting multiprocessing tensor sharing strategy to file_system') torch.multiprocessing.set_sharing_strategy('file_system') + val_set = None if evaluation_data: train_set = self._build_dataset(DatasetClass, training_data) self.train_set = Subset(train_set, range(len(train_set))) @@ -399,6 +403,19 @@ def __init__(self, raise ValueError('No valid training data was provided to the train ' 'command. Please add valid XML, line, or binary data.') + if format_type == 'binary': + legacy_train_status = train_set.legacy_polygons_status + if val_set and val_set.legacy_polygons_status != legacy_train_status: + logger.warning( + f'Train and validation set have different legacy polygon status: {legacy_train_status} and {val_set.legacy_polygons_status}.' + 'Train set status prevails.') + if legacy_train_status == "mixed": + logger.warning('Mixed legacy polygon status in training dataset. Consider recompilation.') + legacy_train_status = False + if legacy_polygons != legacy_train_status: + logger.warning(f'Setting dataset legacy polygon status to {legacy_train_status} based on training set.') + self.legacy_polygons = legacy_train_status + logger.info(f'Training set {len(self.train_set)} lines, validation set ' f'{len(self.val_set)} lines, alphabet {len(train_set.alphabet)} ' 'symbols') @@ -592,6 +609,7 @@ def setup(self, stage: Optional[str] = None): logger.info(f'Creating new model {self.spec} with {self.train_set.dataset.codec.max_label+1} outputs') self.spec = '[{} O1c{}]'.format(self.spec[1:-1], self.train_set.dataset.codec.max_label + 1) self.nn = vgsl.TorchVGSLModel(self.spec) + self.nn.use_legacy_polygons = self.legacy_polygons # initialize weights self.nn.init_weights() self.nn.add_codec(self.train_set.dataset.codec) diff --git a/kraken/lib/vgsl.py b/kraken/lib/vgsl.py index 9d1cfc6cc..93a7d0cb3 100644 --- a/kraken/lib/vgsl.py +++ b/kraken/lib/vgsl.py @@ -140,7 +140,8 @@ def __init__(self, spec: str) -> None: 'seg_type': None, 'one_channel_mode': None, 'model_type': None, - 'hyper_params': {}} + 'hyper_params': {}, + 'legacy_polygons': False} # enable new polygons by default on new models self._aux_layers = nn.ModuleDict() self.idx = -1 @@ -311,7 +312,8 @@ def _deserialize_layers(name, layer): 'seg_type': 'bbox', 'one_channel_mode': '1', 'model_type': None, - 'hyper_params': {}} + 'hyper_params': {}, + 'legacy_polygons': True} # disable new polygons by default on load if 'kraken_meta' in mlmodel.user_defined_metadata: nn.user_metadata.update(json.loads(mlmodel.user_defined_metadata['kraken_meta'])) @@ -363,6 +365,14 @@ def aux_layers(self, **kwargs): def aux_layers(self, val: Dict[str, torch.nn.Module]): self._aux_layers.update(val) + @property + def use_legacy_polygons(self): + return self.user_metadata.get('legacy_polygons', True) + + @use_legacy_polygons.setter + def use_legacy_polygons(self, val: bool): + self.user_metadata['legacy_polygons'] = val + def save_model(self, path: str): """ Serializes the model into path. diff --git a/kraken/rpred.py b/kraken/rpred.py index 96dff4fdd..d41a11f67 100644 --- a/kraken/rpred.py +++ b/kraken/rpred.py @@ -24,6 +24,7 @@ from functools import partial from typing import (TYPE_CHECKING, Dict, Generator, List, Optional, Sequence, Tuple, Union) +import warnings from kraken.containers import BaselineOCRRecord, BBoxOCRRecord, ocr_record from kraken.lib.dataset import ImageInputTransforms @@ -52,7 +53,8 @@ def __init__(self, bounds: 'Segmentation', pad: int = 16, bidi_reordering: Union[bool, str] = True, - tags_ignore: Optional[List[Tuple[str, str]]] = None) -> Generator[ocr_record, None, None]: + tags_ignore: Optional[List[Tuple[str, str]]] = None, + no_legacy_polygons: bool = False) -> Generator[ocr_record, None, None]: """ Multi-model version of kraken.rpred.rpred. @@ -159,6 +161,7 @@ def __init__(self, self.pad = pad self.bounds = bounds self.tags_ignore = tags_ignore + self.no_legacy_polygons = no_legacy_polygons def _recognize_box_line(self, line): xmin, ymin, xmax, ymax = line.bbox @@ -175,8 +178,10 @@ def _recognize_box_line(self, line): tag, net = self._resolve_tags_to_model(line.tags, self.nets) + use_legacy_polygons = self._choose_legacy_polygon_extractor(net) + seg = dataclasses.replace(self.bounds, lines=[line]) - box, coords = next(extract_polygons(self.im, seg)) + box, coords = next(extract_polygons(self.im, seg, legacy=use_legacy_polygons)) self.box = box # check if boxes are non-zero in any dimension @@ -242,14 +247,17 @@ def _recognize_baseline_line(self, line): seg = dataclasses.replace(self.bounds, lines=[line]) + tag, net = self._resolve_tags_to_model(line.tags, self.nets) + + use_legacy_polygons = self._choose_legacy_polygon_extractor(net) + try: - box, coords = next(extract_polygons(self.im, seg)) + box, coords = next(extract_polygons(self.im, seg, legacy=use_legacy_polygons)) except KrakenInputException as e: logger.warning(f'Extracting line failed: {e}') return BaselineOCRRecord('', [], [], line) self.box = box - tag, net = self._resolve_tags_to_model(line.tags, self.nets) # check if boxes are non-zero in any dimension if 0 in box.size: logger.warning(f'{line} with zero dimension. Emitting empty record.') @@ -299,13 +307,26 @@ def __len__(self): def _scale_val(self, val, min_val, max_val): return int(round(min(max(((val*self.net_scale)-self.pad)*self.in_scale, min_val), max_val-1))) + + def _choose_legacy_polygon_extractor(self, net) -> bool: + # grouping the checks here to display warnings only once + if net.nn.use_legacy_polygons: + if self.no_legacy_polygons: + warnings.warn('Enforcing use of the new polygon extractor for models trained with old version. Accuracy may be affected.') + return False + else: + warnings.warn('Using legacy polygon extractor, as the model was not trained with the new method. Please retrain your model to get speed improvement.') + return True + return False + def rpred(network: 'TorchSeqRecognizer', im: 'Image.Image', bounds: 'Segmentation', pad: int = 16, - bidi_reordering: Union[bool, str] = True) -> Generator[ocr_record, None, None]: + bidi_reordering: Union[bool, str] = True, + no_legacy_polygons: bool = False) -> Generator[ocr_record, None, None]: """ Uses a TorchSeqRecognizer and a segmentation to recognize text @@ -325,7 +346,7 @@ def rpred(network: 'TorchSeqRecognizer', An ocr_record containing the recognized text, absolute character positions, and confidence values for each character. """ - return mm_rpred(defaultdict(lambda: network), im, bounds, pad, bidi_reordering) + return mm_rpred(defaultdict(lambda: network), im, bounds, pad, bidi_reordering, no_legacy_polygons=no_legacy_polygons) def _resolve_tags_to_model(tags: Optional[Sequence[Dict[str, str]]], diff --git a/tests/resources/170025120000003,0074-lite.xml b/tests/resources/170025120000003,0074-lite.xml new file mode 100644 index 000000000..504794e0f --- /dev/null +++ b/tests/resources/170025120000003,0074-lite.xml @@ -0,0 +1,89 @@ + + + + TRP + 2016-06-16T16:57:15.027+02:00 + 2018-07-04T17:25:44.389+02:00 + + + + + + + + + + + + + + + + + $pag:39 + + + + $pag:39 + + + + + + + + + $-nor su hijo, De todos sus bienes, con los pactos + + + + + + + y salvedades alli expressadas; Y fue acetada; + + + + + + + y assi mismo el dho$.dicho $ofi:Patron $ant:Miguel $ant:Carreras, + + + + + + + y el $ofi:Rndo$.Reverendo $ant:Miguel $ant:Carreras $ofi:pbro$.presbítero residente en la + + + + $-nor su hijo, De todos sus bienes, con los pactos +y salvedades alli expressadas; Y fue acetada; +y assi mismo el dho$.dicho $ofi:Patron $ant:Miguel $ant:Carreras, +y el $ofi:Rndo$.Reverendo $ant:Miguel $ant:Carreras $ofi:pbro$.presbítero residente en la +Parq.$^l$.Parroquial Igla$.Iglesia de dha$.dicha villa de $top:Canet Padre é, hijo +hizieron donacion a la dha$.dicha $ant:Anna $ant:Maria su +hija y hermana resp.$^e$.respectivamente por todos sus drôs$.derechos de le:$- +$-gitima Paterna, Materna y otros de ducientas$.doscientas +libras de moneda Bar$.barcelonesa; arca y vestidos corres:$- +$-pondientes, con promesa de pagar en esta +forma, ésto es arcas, ropas y joyas el dia de las +Bodas; cien libras del dia de la fecha, á, medio +año y las restantes cien libras del dho$.dicho dia de la +fecha á tres años prox.$^s$.proximos venturos bajo obli:$- +$-gacion de todos sus bienes; cuya dha$.dicha donacion +fue echa con el pacto revercional acos:$- +$-tumbrado; Y fue azetada por la dha$.dicha $ant:Anna +$ant:Maria por quien fue echa la diffinición cor:$- +$-respondiente de dhos$.dichos sus dros$.derechos a favor del dho$.dicho +su Padre y hermano resp.$^e$.respectivamente y salvose el de +futura sucession: Y en su consequencia hizo +la correspondiente constitucion dotal al +dho$.dicho $ant:Joseph $ant:Vancells su venidero esposo; y este +acetandola prometió en su caso restituir +bajo obligacion de todos sus bienes. + + + + diff --git a/tests/resources/overfit_newpoly.mlmodel b/tests/resources/overfit_newpoly.mlmodel new file mode 100644 index 0000000000000000000000000000000000000000..cb7b40aa9547f82c25be94202ef6c7cc7dca555e GIT binary patch literal 183424 zcmb@tcRbeL|37YpXra(vDhhGguJb?|MkOgyDhZ*ij4~>FWo4wSWK%}=xXy!A(q0;h z(tb5HwCC@_>(%T1(dG7i{oKkQUFSR>*ZsOb&UxI=d7SYSzKu#>mVfD zS7Yg9>tQ9j*THw1m9@2}o0YZqG|ed%7K*Y{e5Y-*a=C%C>4QBu@UR8dw_(qOmlZ*cuhdf_zwES#dkAI9UP2zJx- z1PMD-)YREcsK!uXhx%{s_BZKuSCNw_evi=K;W);yA}37@9*wgk`v3BO<;GbN?3n8f zDbJo^V;e~Vsk83X8|e~|tT_gkp<;r(GtYYZ_$UwSr>gtfRcuyvIaw)jmqx--{5-l zg#R(=#Z%&}jKSk^R>t7*I4fiDc$}3ncs$O^7(5gbs=dM6GIwltJA zIEbUaMK78%XKnPi<-o1q@AaW^Rz_bYrJ}6JUJI}X0}eAJ;E76yr?!dVN0Mf;;#=vjYonEeOK zZ2&Fw4Z*4^8fuD4YRc@O>^~y?y%PUr(W@+#0kqJ&{8V8lvFgeSYAPJYx4t~*UyDAp z0kkkkv;nj*NVEa8Fi5lkw7}jvVUOX8%8DAQYU~{k4!zLNqOaWs&_X}k|A?kKfEEUc z##syhcLG#rH%I??>u=HPsVZkJ^tb)5Xq>e$STxRB=nD{4RR%i?R%GueaNyRLJN&Zf z^OUm|RQvv^tEwm|tE(%rL;SaP>u1r2##syOeLwaRK}}IXRhhkI$06MM8T4UsRzg2h z4#fWNkM&`3*1_PgIICb#SZbU#FgUCM6!3qW?`Eg7IfnF~DK;VB4gM}MF6@!HvV6GS}9A{PZ-Ck5zQe-Hy(-;MI_xIn?>2L9O z&vI5pf8+lQ$5|DFg&W`)deCs3RWWEd&Z-zR9A{Mw8jiCn1`Wqq6@!N3tcpRy{aIBg z|5;Ti|5;TGa3DR{wp01Dsu~teIrVttJI&R~hW&rqd^02nIba(LS z0q#kOwt6~vus=@QSAr*1Jd)Y%A@Dk*e%nWggLo5**bf8I`vldbnkXs2RnNY zHkP$j&zm=WE$D7-Z)@Y}*z3Qwi?h3jm9s|=nzM_Wla-@`k5#Yhzkd0jYFo?g_6{Dl z?ygqWJzw>3^R#7u$=b!q)zQ|&*5+Sldc3ppwCkb5w%cUo=-y-K?qTctt95#uw6k(@ z>T!@A0PK>Ri>u`(D{Bvz9tZ!ea%GdTb?*Jao^j3E#of}w(#gt;ZPYU{P1|ne=H%(x z^A&c1-5s_*ww4>Mtha1rcXm(hPIk4UtsVQtBTH8oM{hf}yX@*-PCK*s%ZCUJv1Uht zwP?W*-)U3WPb?IBqFTP^6Qk$TT2J=vcZHs!(yyXQPf__-QLU$_@~f!cQ&jy`?1>c% zwO_?eJw^3@ii}=ljbBB@o~Mcmzsii>axbMGA`GS8@-Jcx)!yn_eo6f2gV0DEHto z{-LH~)7walf2gV0^ipN~Lruk|x78T`G@FV|Z?iG};iuBakMS?RUe_4^^4r+^d;jw5 zbyewKe!cCk^e?}jXs}TFmtSv3DE;DR+tb+}tU=Tu1o0uM7zqD&#*@m5T?W-pnes4yR~I}wK#DTCPa z3`q3rLZ2Dg*uKwQQ(gy{$zyjy!P8EBEgg@>4#8CSX(rCo_J=ITsj%pFJ^l;~gO>Cw z#K!9dON-|z`C66-Bgg2$r0ROWH%WMj_aH2G;~|^CA2g4(;GT)sv9R)4-ImwExUQ># z=*%;KBvlJ?e$rtu^}bEERoUQB-6wRL^J_@PV7%Wj93E!b&?v$E_+(o%cHJ$Zt@SFv zechA_3=6skbEcg&2&- zt*lY)>oBYA4C>2dqW6kDD4Bkod2FFDthLVpd%G?0Q0fVd8<`HHqKdFx=P;G(Dq$%c z*#oP+ljyJ^orLQ%6V5jpqJm33i1~UG`7cqp$p0L3@ycd$Y(xT<1#zSPR9{RI$-?yl z3~b352OFOhLS_Cb@=$I+ByQLWMnacy+vqbWqP&fzu<|=yK9OH*{K^F|#K-IIXmci}kZ%tCC{?`DRKx`!T0j%d2Z2Mg>2>k@QUq1*l| zOgR;9c#ypdLYnj8!<;v8@|**#+}TL?FpMxlYc=Lr6`;Ch7KWdd2l1STxM{8*+$&5Y ztkW^zh%?~*yPNfE?doCddVf6iMGSb#*U}4{)9~w^b$H?`AKc*=Y8df37Q1h_pq`Bw zhCU8J9kCcP=F=fezwQZPXIqGr{S^%Rwj4f4MUj*zAILGuY}l-6kHwKbs3$0mn0}tf zx1U8PgT(p;uUe>G)N|xH6v~v?upL?_iQ$&LjU;2mD443orIjjS4Bwop!0qZZ`rug( z+}v;gT}T*CtEpr0Byz*dvRlk9lj*>UKgaIZKztUMgUhGuqL#;ASTs!vcw56^TlGvx zELly8rSnigA{%%**2CqduXLqZIvtVh3>M`LDB`uBbUm`dxv%Z(XSaIcalU4fskI4& zA0>h2Jr+)Qij+0817~SYh8_FvV|lYCSiXCVvlounFq*9o##7GV@apH7^_Lxa_E-WF zrLtI))1#TUh6Hg7`E#d)jn!)J4kAw{&V^352gJeUBUbKFL;0>t01gqLd_o+$_zs}R z%a643vJO;US z%)cQBQc0_*nXUx>VE91Z_?1lU3yFAacqxqI;{u_HLIOP_DLcy%{WXG)<7HRH3-Xw7 z34P=d;_Df+{~2X_3t5xz@|X;@b93=@?McM4xO-hWdSr)oNPVF8?o@B>XKRABmz!eN)?fR&>Q3#cQ~b5L^nJBcl>)Ri@^@&znZ~ZWuua=>WPo=0%|Pvd7+>v@ z&AYVa%lx$ercs#ih&8QU1PtEnVu>mWf%FL}I%L}eI{Ex2s^~Btx&B&7pC#QV^)qIZ z$3M;!F7JzEgYa}zks=s!{5;iLYfU2O4M)MGGSc|okM%^>n+)INOB6)1NYukskr_<2+GdKz(x(f?+-Qyi_lZQX><9^>no)k1a(GD8E}>h zKFTNLf0XZgi8jiabQM&s5aIzrxByPn_y2ajxd58Zv&bQ!Z>hF6;H^d}@z6d`Y zak!4YQeI$^d6(sH5eLa!YhjXl2~3{Ago}P^DBzfY!S6>x={ZT77#M}KD^`J7`Fx!C zstA2cO36N7I~4M@hJf#ms9E#{t~N2DTXPN0D*KCU99u>vi{_%qM{Ag^Kw(7XM%>fJ z+i*oM6pe1xVcxS6xGQErV%AubH2IUb{Z;^u%i(RftDr@9&50&S7Q4~%eF|(&4kt0< z!7%Zo7JQjD1SFH9aq|i;+tpx%N0k^Vn_?dX7N%M8StiLDC%r;hFAI%uy)fCIN&yvwwI0rP*#Cq&l_1U zMcmOz<_ug7S&tFnWvs2!Lh;n+Ver{FKr>waI97dpN=`c6fc<0~NS_&p>O61ZY4Iwu zc1sJqsk#KWg+*b+)+lh|t3bJIQ6jBX3G=PW(cnr7m<#zr>Wou3qWlFce!^GZ7_MFjtfZV0=jfZnsRNXKn<5eX$Fz?uuq@)=7gcUdjT}7uIh}r23&+^M#zT{jDy)3Z9z&mGfDhjt zy5&_keB8ip`oz!NhC8B)B=k@OJc?~#31%cC2;O8R?K7m5JDyZrG6gMr zDLkUPo|timVGxanhJ~ugD{Ky%#u?!A~DD@sv?< z0X3VZqA9Z)il(=q@G}Pp7dei@S5iDKlt-&av}$HwXs2NZC{|Cg2FCnQ7~|r}j1}Xn z-`p+;ljp5q3SE(a!qIcEfNMRd?mLW`VT}OC)mR~tvN%_`6Ei-4gR-g+*2cUg=qUe+ zKH%LA=U+Uf&*O73^h_q~N@ap-h%w9DV>YIr>wusuC-M2N0yKPILt4M-VTH*pe6b=0 zRb`o2VK5Qiwy@_t!;#PtTZum$xH0zCHMoAl7bf1Quk$x51uxG47}k^uEotlVhlBv? zFRdVxbuQqt)j4#F_+gS`Xb&a};xJ-i9n}oH0(Qsk>0^Z=$XhD|b(?Zwrs{W1$G~y8 zTxD_n-0BIMUAq#X+KCHJ*1x8+Kh2_2lM*3v?Q`6>ycrk)EyQ{63DW+Z#kzUc3+JTP zq0Q?G*4PdqAE1}ty0dJQi;JKrJ z;n=&kFyzxzIB#+TYsy3EqpNvDN^>XXH$^~1?S3d--iG_er&9SltMI7cA>t|-59{)0 zW5t*~xJ9xNy&D7Zt+5OG85BdUm>Z6KQbq1hH;3xZwJ>{k8FuUm0I%*a{Hn);fmcOv zs=5S}Y37k7(fcrBjTlU@Q6=M|8&Fd^7R&Uz30D^jy+?^4Uu+EBF5CFCxExXtdEtFSZq(t^1Mv}m zk#)9RwENsD7@20v(y1sR-}0B!ZiyA(8*>~&XHEcNcQerIS_!i&bs)rCjb2WTht-k8 zY4wT{fV?p<;&LaQ{yB$umF~ir%rzh-4Y*k126d5MPuldSqt*&5P&Fubeo3H&9;>0`m#`nR@S%tu)+^{*~0g|X87 zkTfxp$#P$TC#A-Nt@If>{6ho`S)c%0ObW5Rcmq_A@j~tm;h+;4go^uA>6)4xEKsti zH%qIq>FaJtl6AnO@-7mzbpb4IO9IvN2T846o@PMC8R8}#3l&$3QS$bBR^0buIHx27 zo@P!k{K|DKae0OO1{z=*xdiT4r=yYnVi0$7f^dI+^h(!)H*1UV^~ay2@w^->-!_se z8J57qF`8h$_!OGI*Tt)?t#t9b5pb3J9gLag2HT(x&P+&y@`oZo%cUT1=mDnb9Y1(D zcNm<>?Iw#?w_$$!YcSAgW^KFpR`bZyo$!?9iCsx+Q0;sos)XmGeQN`~>-LIn+}A-0 zRd|@B(-1xD#9?;wSJu1HF7Q+{o!AF$2jPW2sD0xe6~3BFBU)dP$=`w?_0qxJj?yWmpGF?YgN__aeM>!2)*sd?2>| zfe>+w!0xy-GSPSm_?WB0LH>{6Z4d#=ujZlkh20qPb`RK#2Vg5#Bers@pxkQ%^8G<3 z)08&?#Ai-|Qmq7xs6Ru~)>mNgdHdVu;*# zP?L&==bM9Jf_EIOSuCaXT|A9^chH80uG@5P0UvBq@quJ_LoEDo8r#%O=W0}Xd275+#7E4roiE$f?6^=%`twu5Gc9sB&$3wQdj2_us}5s zGakmmy_MEzuNIB_+DmZs&lxanbrme_x<|HF`7=v1QgHl?3OMpJ4iBB0g{NXeamD+1 zIJC8bvd)yjxnOzJKXo1zDi?x@eWa${GaZbT@Bzb(dT?~&VsdZ#F}glL5;rMEz)7A~ z+^;TAJGq7GMT1Aefibf1_2E%6*?KG-G_!$CyTr)DioKvMR7<+oN1>g%X|_xLV;F?39p(2+?#FU7P}}r^axRbsA^J6;no_2Z+m^ zhM!7vsqrHTSfDT&%USPP3i`#ccT*C|*$4ak4U282& zcz>Qb-$)R7#FTMZwj^F(Qj4NnSHbJXz0AN_xggyh3MeAPNA5elXPUq2#Q$kF;3*oTe>xJgIiZDpqD-4as52;K11@iK8 z0$$QAgVCu5*jQFdKV*Gioe)nU7Vo53o8$LFic%a1PxME_;bUOZ)DYIk<_IWzkcduA zRp9zD8eckRkwt3{W6P)nc&S%RL`3d^eGCB)x8r0x#FOYXr)WU-Y;;OUB8d~-(V;pB zl+G5QjBXhoI?{~$w5Gs2`!EO|?g!_r62M~rOFF}Zo#(c0CvzU0!cCSb#C?(;div;r z_2DE$rBckV5W{TyIw;=|PvA@i-t9Jm4G!CwcRw9~((*(sNSC8A>ifv%(`^17$y9bi zAn4nLV70rcW^Mj{*gZV~OavTJc~>p0bh-phHGZ%qw*Zdv*wKUg&1pwU4$R3hL$xun zAR@v8pY`vO`vUpoU6Lnuo=+t9!PU^U+zmRd8=+l45eoU|p<$pAV8UFu5xW4z_~SvN zkQc`sYJqbvMiQs2!(eEUfSoI+;)Uf6=zpgZ($0D!$v0pw+P4e~`9iU9_bQEpQ!=Rc zn%Tso@)(}aYyz_6F@_}jkv!4IWPzn0m|HJ|IWg1l*s*B%x+M(?R%wA`#|a3~&ICc_ z5ZW?sCb2nq91WA|;Erh`8m_Q`hl-ErNI5@5^V9WL9GBu6*+#Tqxu3bq=mrko#0Rg! z6JYJq4zjv9tDgV79>hEO5PP8{NXyej<43~ib?!V#yiK5oF-^L#Om*O=-%gtI)x zX5smUT2}M?bUby#)hi)L@IDP&nGHXUCRE17^ws_uX7}87`L!fjFLm5ip;CkTk!Rl z22f_Ns|1R;k?~;(Zapmx>ED7;^nx~cuQ~;F0p8HA%ObZzAAr`ET&PL1CnJL#+5257 zu)pdSea-Dn^p-I3MHvrKY8y#yVw-5VcsCTkjwGKZx3fN{ml2_(lVMtGE9$rx0RP#1 z=sI|VG)Q`)T)i{~?|(#K!$Dl!ZVFpP{XscX5NyYcK*^S+u*D-0hK{<+8qVWQG}PbG zwOj8{i?Vo}#+42uZmfj-qm;-h=7FU68*)4%lD&@G%}SIgq-(7Y!2?N4Fr8$Cax#0F zk(KA+j=e8krsxM(_@|=5)6cMdj2Ucuc^1woDuS5kZt%W(2?LfsU|Cqmpo<}U%-mN4 z%zOp%S$Q{({HcL&gQOvtO94YHOkno<9rXK_^Ry(c9piaKDV8pTr{$$MWAzR+(Uv5> zpYP!KOW}Cu@?GrS_6W>H_JGlJf85b2g3D#aP_a&rYRFcShnu;yK0XP94=(NG-Gn3< zEnkh#s)oaY_zU&R?yjV}Ckiz9AKV2;swd!Rn{2YVHVWiK=HvOp)s(&rMKhQHzWx(o zLEA_aS#QWvI>jOaYL8j1)4V|ZWEwH&o`QlhhiU4FR;KFRRk(uwgmFdJ(Rt4cGW@(c z?mbeE%^``fNWud~Z}LE^H~Fm5nc{Hpya_IrS&qClTVUa>VtA%C49Hsp2%DA)F?9+c z5bXrU)0cqC$rHF=;U?4QWC&;pCO~uacdA#;2Ri0)J4dRL4qG> zq#eOIZgi8VyoLsXq0c02_6#SF z!);MeI2uNnoq+uo0VsPy8axx@=_8kD=oq&Thc5QQ+{Jsr&awt~9LXbJp8<_7I7{#D zYsSo1EvzGlc_G>D4)HY3!ciT`D3Gjzf0&xR8^HTIUkb&mk z@FDZEo1t(B=ORzE-T1~hmNt+Uza3}<011m^9-sEl%qc(N5KUfAtx&_O`7#i*kc-8P zx{V#NNASduMR-$tGGgo=sCzM%_)X*jwI7Qhd^ML==DW+pG1mwdnQE{`n8~omYF~Ih zJ{K$Jlo8D<)hubD^APMPjk}!c$;0^sBpkY_5pOQuyiR~uT?^;mJAsOEOQHM5A+#A@ z4Y^-xDgBg=GF)%K_GT4$-76vCyLZvm-7(l#c0uR+MdaetIcVE)h0K3^h&gPd2?!T#h2wNK{iIq)OuHVEXCH51@LXL? z$hrZV?-wBN4Jo|rdy78%WRBx9lrh!kGA@j3!5W)%P&~T?QWM=VJK{()pP|sF)y~wo-x?$OPkvpIfl?VKG*< zX5e1#bclKs3w8~m817I5V~(FtuQsrIqLxNEt_azPHqXB?9Zm(4T^}pR_}UB@QlW%bwuYdAyDE$oxIpi}YQ?RjEA(bCGVU5_ii(|u8q7mU)+j8>-;B&@RiM$)gj1NSF~)QW z#@L-B!+9@irsr$JQ+B#Cmn(%Z3PR9q)+Vyhx|uotgE`2%NJGY9d9a1eAS9HCVphY@ z*es1XKf(pKmS|$FMi98Jxkql4NyBNL5@@s7gtHh!@W-T32s-nMwLww`R7a(=?)|Vr zwTR6S62FA1dS`&U@KL(MU>>e;;v?L#v5@Pxi)0uZl20Ep!O%AnM@XF_!Y-}MzuL;+ zl)@bpyAul;TVLR(VSC7^H&ykuTq5`*GKn_XSfQmaC9kt%32(v}6xL0~P-hdGy|WON z(x>3KGqP}cNjwY>tc5o5V2nQ@jOh}*_^#^*O_A~j?Wr%Bv%Aw-hOdOl+BI$-uqlIjkd>~ ztLMO^zxbJX)A?ux?@y{9S&qwiB*57CHCaPD z|KutrCJEC>M|Q4MH68txcHn`cyRct_8|p-^fwyQGwJG>S-hMj3Y(LXY93;2U<~djC zTeB&kp4~=2%sUS?x%=St=SFz?W-`-QPaED}Y^TizD>3_PE2O`5MN`*u5GudPEP7Re zowFjzo}hTpo*jzramz?_+9sOJa~NH|?ZVSx>~pW_vDhB{gjCO23>QWy!s`3!@LuBz zndjmP!Q1t5Tk$CRpk@_yD6&UyMk3tXB@5E*JR#b$8SfMwpwCwnLD7eWxNKCg2J=M@ zt>sb0h>2}fDf0=@eZs(vkB7jwTl_H3(T<9-2el*gsYD{KHc-YR&Mj0uq=qJ5 zyg}ZzpC&V67J&Z!R3dnC8`!$z)zySknli{D)Yafb!? zur3$puFJ=a35l>_?P5HfdV#Rku(kL)j>K-<05f(qQ=#u~Na{C9sM4&)_`M0}VpoK= zKAE6$EED#!=Xvk7sj%dn8$|JElL9+O+(!>FxY|s=~mVoP@WuS0#1|BFI3R)@msO#l}n$?>=pyQT6P+XOV1t^W_z_0Qkd#!#IKOWlx zZ+@iFlfx=tM<*ZEubqq{>02;1tr7(%j?`>h*A1^`w3Af!d2)JNJnZY93hHh`!`*@`+ z^n0SkoHx%OM=n$#L(JZj@xv?8^y6z3kC8=z;qH*{eNKJyNHd&dtd7j|XbArl2ErGl zA?TJDgmhZ4^X*P}wKIqePbt6=+mB$vR0%M2lt(Z3tMIfT8>Tnv6RB8Dy4HI?&3PBa z43yb{tj~urC$knyhn0}500Asj)P&6Dy_lpo3dEx}l6RVG6+zSbTz{Ri(m_p=oe=Is?1sKEgKB#i;JF z7!rd`;HgeNTw7a!rzC69PJJ)PU0;sjH@NYFhY@rvts$D9HSosJp_sEF7pFrdMs0e; zG`wgEPnHaUV_r3wU{Ht(H=Mw9LM$1zE(a?z?-L{T-X&{IKIV&FrwdQG;PRC5lr`=u z=sc=}Ws^RUU1R?O(W`uLGQtt3n5;riO(s}5m(wTT@+p^>1a6no1Tn2V_F5$xN;|57 zhwOv{$F1ulgO1Xmhl2Q7PZdJ-RB&%m3zqZ6GROLy#Nx!y1m!xRYx-k)`pgm9xpODV z&yU37+k0WT+&Wlg_=Zl`JP0m)pJ>=UMG(pr!YK;RNsabNc=m;$WjbdxC(^F%Y*5zVw@-wj%%}bgH*jcNMCM;l|v>#lK4g(U(pD6o`u8Q z(BWFa{BDpEtw>a&mcTR-XH2}43CHimz?jXGiP=zuB_-)~%WkfQK>w*YEJz*AbVXo! z;At`|!-WRP9K?wcSBX<;Dt#LsjXC9CsLX~CoanEIs&!R(s-vBTnyYZx`roF!_NX`xvDYUauWvW z-^P$s1q+HPIUE zh1t1n=zeCLk^^Wg_(Hch1>=i`aVRIAgf2j0x7!u3JR@!aw+RP3rWi|JPkN6hW$<$D)VIAtfa3S4D<IGDTcKN|vc5V-3;g$|lCZB6;OK03uwMyil%Ge# zlyu4C>PKNNcab?;e5}yf=duYEHoO@20{$wID3^)S|HuEg(C1Xv0>o z4a6yl2evI2(VQ@2KJbi@A|JEgz_*xVuw;rV`K)h}FnK1^qs6!^<#6FRn@VBf*Kg2$ah@Z{9b)Ki+58hl~k3SmJ!6!V#^J0J%OeMaNS z$|{^WA`YclbC`;^yqF_LmLMr|hwkD9utV=E(eF-!xsJ_b*u`N~Vn`Y0MMcBA)IkBubF}G#A(+rT7?I$P z!=5!T?KcFYl*T1o{v{kOTF+p{8A-SSmt`8WANp4B{}v}A5MLjAhV+- z;PLqVWY~ysxOOiYTW}-f2pj=#z5o=OG>2w?SO|RAl`*qpJgwoCq~FeNhTSjJq3~oZ zqaR?*f>N*(8pnJ;yaJzV3E=sVLbzg4f>GYV zG~O@|E|r$x)2W4!r#AtMyL6a!OOE2l{2lbnibc>=KZi`T2uIJYvUsjDiMY9kvF?9} zr0$cxk@fR3QTMS6E9`4Ii|l_7VsnS-_2oF@a|~`hnvAncZ{QEEJFMGF)9}&oy{POK z2(*TsC+JzB()2|*uj3k-?c0ugFI>@^y;huaZfHYPS}M~jt$@5(BG91ooR6x7-^C!^ z2Y4fX6mmUJCZgW&fc54j$r^VYM4rz>Gwx6jlNv=Xw^`%Pq}WcTQ(p)-R4*P0L%)r}NmK8kPuO~B^?!x0^@0e_ zSc-&)AD2E!fT`vaU`vn|%zeBIBF;s?6ylD|_ckbjBXRrgQ)r~d!t~k`=v7yPw~lpU z%&K}2S7?NC!7P;6RS51TPbohu1E2WW)3q)UIPI{hrknQ)rtowP_W9FC&Fd!w!Faeh zE);$U^F+TBG4U5Lj(zTEYnnmTex$(XWcD8SM+snzGX?!GpV@nylR;Coon^XWB90Ns zW{=$gsM^^Gjx2BN{(6|c%REDCqy*to_IuLg90Jwb3XpY&5+kjXG_S6J3W|0^XNR8y=V714Z_dlVi{=S5$i-W zU(LePrxU1>wjWkjFQhj+(m^FG0L>kyqusgX#6n&RV&}ZZ_J$zX=zajJj878%jqeEB z+(eH%wWA*5VZq^E~(0E49^RCD!O=<3dew;S`IsbyHhi2K3hdd_@u@JAT!oYsLN z9#QyU$kKYEI=r$v*okE*OVx zb82x50JO z{b>1f435!Nfvbl3QHDexr%ZdS+pKD=5mobju-HWx0PvaGxXj~^B1KcXzw0^=Z_MT519(zuS zgi!#RK6pnzMCQP?DXZar&=}BuR!XgukJAl@gK(r%Fic*<#64q0sI{9c72}e|C4p72 zWl2kUmzbE{3! z&o&z4FXhlyH$Lh)pW>@s8_|8N1=f>yr1Q`zlK#4e%B+k;y`o?^@WdCN&wR(c(ym37 zWt&0z)Q2#1Y5;c8$kacZl^ z^-qJb(pea5oomRq$Ctrk-U*BoNh4!(j>43rMyT_+4(3y5f-Fx7VH`Ds4~DvMrg9YK z@q8izYv-ZI)gWT7DvW+(=cB_q4U{^(3QSkd##eF2Vf4gwnEdn#oDVt;X6(4#^)neW zTlr8#pph1N2Z5$`6`e8;`b8|@(_BH8W}Slx3-{wz@nSRx zZiANX_2|#F5f+}BFL<=br{^(qN&2evTxILlKvyG$^i^cid3 z4Z$S#Uhk@qP*j~b8S1%GVb7dx_~yza+`zs=_;%zsvO6yUEmB`Hk3D%v<)$Zr(asEv zpQ8iQe+Ys5mI5%ma0B98PC@dA1C$rS;pe3&@KkZb8Bun`!9EGvPOic2H2oDAZtt-$l~Uc=$~Z`pFB?E@M}j|59T*Q+?))W;S`3e9IxWLsSFS+J6^Law-RH+ zszB$~I+Sm`%-RV*sHIXRO?q^lELjqTqBfgx$TJiKLHAx-@wxL_`dqfBQFzgg&^SmO_MTPjf@h?_F#ug|& zNnjDwVQr$okSmRoND${+TW5{bVe(rdTEKEgm<|M9{dBOnCz{MsJuUQv^kH`vIR<#Hn;Q%_6ucyYj?7M8U*!e%#eaL8^jQ*WbB!GR_MsUJ; zSVqH$NOKVczHz{FC)7}-`#!O}G@B$Bcd~A&*5Rp-og~_NFCFz)7`(SR&5TR-pdZs> zVDmzKVtDa3Y3f!6t`)&h@BRqXPcEd#C%S|9@C7gr7jVstg2mKekYUdm_cV*Ky73#VO38qw&;9X|#zv-}xDINR)WfIki$LSk7Obdm zqEVYKk>ygmVff2g)YjwylbUxy&fb-HZpIrr#DKeDt^FGCF+B_1`TKFks&TM3J_xv% z-5_37F7RT57#v&v2_97iLf*p+P`won<@USbsig_5oOT*lUeEzs?-?Ll6bPj`S#U~V zFBwYOXgSX%>3S1Z-+K+QMdDaldKQKro)5garkWz^p^(6y zAKW(kYwk#V!`dkC3+rc=lNY;Qf~)j4*umcas(HpfC+3i|gV|^$*lMx>kNY0RYUzh? zHmd@pxyBO5C5K>%>oLr`c&lDnL zCTzJ&DSsw(D=6dq*r`-$j2wBWe2s8hhv4bEiQux<2p-*gOTDcCiC-!6#k`By`K1$^ z%U@IRSKKgn)?$<~t7cj?EXN018#IT!iKYq5GhwD!J3ZzBy|vZdBl2VG-Z`|z%=>1U?2PJ zlLa8qJxvZAH^lXwYEYH?|GfGPtM=g)5dG@`7Ma;_YJE0#9Z-SjbVJ-0)PzPR=^&{T z3Pra)@V;3f&Fl{w%u(BXBjZBg=iffo>nSBKFqY zw8dQlN8Rgytj`DYVIIgIX+yF2LUMk)792bggInyXiSrL$^5ab#9EsG_6tTzzt(-Qp z_e31&`R@=c7JIucaUj!^SL6UH;w!;fc;fx{%2*e+R!sY{%p@2v%GQ}KZ> zliWBao<+FkEFyW250SJOW!S9i3V(i|gKg^+>2=KsG?+<8ZCyUNFh3dQzg!75JGR4C zlPT74X$fY685($pLv@fWjKTmptG^gHkCwm!?+}cV?Z+Niiu*pw!wH@u2r1Qr2c4?e z*<6q3g6gqyb`IgKHzL!ooM31q5GzV!L6O53R%xU{cWf8zG#JOD?nOLS;h5yfZwBF(d zLGKDQetCmjFkeGE@(J>)}=XOIAo>F`TrE&zv#=t>fu9t};ez zLqdVSJ`U$+j<6F{H^Qe6;z+;9u|2#x;E=r=loWTuu;m8W6_bm{;_gDE@@wKJ)s1g8 zj$@>YC#_p<31?fSV1RoPM^uYh&Q}J|Ta(FVKGcSk)YG^lJ{wkYO)~S{Nhn^;#$shk zPi$I@r8~UQNcA;%cK#)~NrLoTxh=4tm7|x~Hhk!kgv$4pG4p;9jK!D3ccpTe_UFUV z=SXcNn@FU9E;Tb>GtQMd zYOe;AyN!+URVe*p3?t_X!jjFSz!j2++XT9>FWmvmoa8}mtdstT>wx090ia&`5>(Q^ z(?Zh;Jnj~Pq^=KhUYJ5=Z_oGv<8JS=$ zWu9j-{JlFtw|qGcB^*0Y`)&$2tVsux&FgTVMKWv)nAgmG_6{{APjKmYEbOexWdD71 z9`rcRkmG#L@ct?R1@j;*nYWe}9I9k%Z?r>~=ms+JE&_C3hQQLbf_NlZf+hTJ3|$M- zAxk0!R^XCf&KJKT?nckHiU*^TloI)1~K^OiYFE=2bX7JK(iCEp#2&yJD5pV-1Nh}qc_mp zOa}H&GF=O;qfHA+t()#eb2e+YR?*${MI@-|H+#p_efl_!Ia8(&A`eF>^1Lg-mqO3U z`4CxnA23C~-|ELjk1KFetPsw8+6zZNFM>@A{lRsS7)mK0p;PgJn9r328KoJx>31y- zjdJpZl@oU^?Ab9nRjiGp5mvI~`wVWueM6)2(|YjlB6nOh*1JypiF<#WSL$ z)!G*q#u&rjwi?PgIt|W+DR4>bFs}V?K6?kFm6mQ_1vkyPN!929;2#;o6Fn|qd4-vG zZqK7ngyO*S&tobmbQ&MH2?GfZ!GotN@y9i9be0+>)?6Fl#i~e+>AD#rcRLf`gr25i znO`89tAWV=OT@L!O{`ab*(efwpZ#lb2st$!!anB7LPM1m(C%)G{y%xKACj1!t1UvW zFp6>OlJ;3E*tT>#S-7X9(VzP&abbB3WdiM@K63}!gaV3COtfHYE29K@OBY=A@ZE6by?uuFESv$xSqcIYY*#u zBhZ!6L%lQDr0=FZ%JhqYa=IB9+DVes0)0%2Ed>41M^Jc2hCT9f4jgjsBjf3@`PoI z<1<8D{u2Ppr0Y0)G#NDBDN*amK)B1Tfm@93u`f9`(6Y50T8oavL+JW{&>?D#cK?iM zQh^_;jpV{Q$PN05kFwRV(Jfn;1bVWod}VqmI&N z$F4zJvI0zSM1sbNm0;Q_3EstN&|C10s4_W=trBh6y|RPmoY}$r-ZA7Yc86&#el*lh zp}&M2(EQUw(v{#wU8Lr~G=Dj287!rQ>0fwlPsbyXu8?uN7NrhefrnSb>1%5p$|26j zKKElcyjpz&%2JQR{_Dd;cwIWVsa{7kzfZ7_9JZ!1#Wf%yIp; z7YdVj^;o+2tRj0|aU3juFh=h>=mAI0Hmb~I|1*Rn(ZwN=a6N6K%2IWhX3tOGUvJYm z{$2o^G7>;z-wW1cVGixDe~RxEu7OtYHe5I7H3&6s!}^gb;y&PnO7|AyMgM&EDItW! zhlg2S{o2H-EE#@DNz$ok-q5{_$7*k~9NU;)T;T{Md)PRuy#-d3 z1mm5H`!H&~9RV9rDDWvob?41A+y59!7pB815h+doC>8K*PDBSjZQTEGjLw)Zpau=T zFcN1^BI5$-a}Gt6dc(qsHa1%-{06LfB7t+K?U^o41?>JKgdNUSpm5PkbI#{vI#0wN zZRPZ7yRA2w_|`${K7OS^(M_z$(S@kd8>(6RrIS=ldV#p6F8IXuQV+FLO#VUzzl;=+ zz^s0{ZrMr5GZZJkd(7#RtYeVZ=tTGUv_q**Ck@!0PJ-&WfICPZ$@(rd{$s-`VtNaL zxn}g+=f^m&X#=i4&(GG9Nr81(hlkY)!IJAfJ@hb#>FBn>YGZTu5uXKE((B8<)FOh9 z+%(X~$R7N6gaNs$i$#x4qS$^;Xti7fqOU4Zo-YIL-$-D?_B7}zzk{t8uW@%5xH@w+IDnM|J9M(-k9B76)}&lC`^e?MWsCk8--$@JzjS*IdVRFxD2VaoW8 zeFiYT;vD=tH%h)bB_Y>sKjO_j7i;SIVd)X3yP1vfb!{8nUEn?QN*|x|J1Ww;yh>({SdJFz zh#C%nw}}_&=-*GI&Mg)1N>{*w#X(HBkcC&0tI(BSniar3Os`k((VSH6#6C+4kbDxR z88|Y*R_#uvL6@CTf9-X!U$7H***zlVoo#@Vd+^EuDKK3aj>geBtVr(1O<#DEsA$t} z=$%)JE3Y*|m`e~@@1Koe*^kST>TzRp4CeoqXB+8z;f*?=6<>4F*Z46jWU>#+1kb_M zkam=wzX~Mgb7-wK5de3`bKt;dk5?t%gV?zo+|+Z8Oh7JNS-2MdyHyRF8CF89R}{RI zG(=f{qz}ppSb`!+@MFdq-5(gh4CA+0$uGpVbq_#UVHL4#%z}sC5I+?Op>;?DL>%7< zwSB8tX}t@9_xlWP+w-`I4Wq>3eHM=A%i`P{@ub;7o9SmQ1F!4XnGDGU*?uA&CjUDJ z>$=V0MZp;uw{k+ydBxbdT?VsnzK7edQ*r#97k#_i22AhsAm>a6XoaLhZQ6Wvc-V|F z;d{}sG#762UBorF|6#^QezL=tSsznF@prckoqm;s`Nwmb5_)dnl~MxP1=%FTaXG}! z(}byyi&zW3rK3-)DTd`5(hn8xG%g|*bn*zhLd6E$(H#WFmdQ%)b1EWir85O6vm2#e}xsdsHl<2LO%nA$>dic1Ea zW=x5}*Bo3O(M#&5<6%{QIOHtfigC{mqwEhUxN}(yt)$JN)^jU7h?oPf58L41;5&@> zxsZgq?m^!UUzk3ahx}bzF|p$=oSA+{Vnn&oNWu)fOJX32qYA!xu)!<)A~YUxBerk+ z@Y#uC2z6mJ`|y=;FGhz_?I>K@tpR)mT=4O`DjX_Ezd{k9*-L zGUpm=`<_}j*`@{ar2e4X$!o0d*F$0InQ=aMBdrEDs^K(;#?;WuVdD74T!|G#F+q!MKPpOiYg>zZVSA;cb_oeqR~;=l*MC z?uZAOn2x3mIReCAtDPP8qzl!1I;cf>AE@pxf%D(vQTuWZhRfQIMsb_o0&Ed)X3oHY_@+6BhWB3sfff3c`)~}? zUF#*wg1vEeVF#|=X%FF>YjH_S5r#3$6K<L_7M|t%Q(I zSLp%Wmtfm62=m|9L0PE@_H_Z?FxrFT!)nAdmj!APfiQW&3UAf&z-WjvI86!Q+QQeg zy=oUkcSvb&#R`ZIIfs)$&%mjujQ!q*(Mddt@a9iHwg&qn(&(Ky_opXGU#*7R^Nf%@ zDZt9dwQ#i81vE3hQr18KlXO8jVF~=|DFI`veiE|i8LPR&4Ah+OfrWV*q&_ajldB%H zlKCH#q0U0q<+BM`y1*N6<`schWfdIq9mmsN3n7ZP8l;3|p+f&Aj#D#q|MHJqu=0a1 z$zzy#B9N$G-$t@IS5W0xKeX$jY&B-i<>zal;%_+7|A`3c_&tEb8kQ(E^MkCo)5hp( z2G}?E6?v1j6t-04(O(Oii1jT+$iHv{#E15ym)df&?ZX>-K9>Ws3wbcmVvOpP%VTiV zLHsw^4wfq2~1q5B{k{w^e!ugu1Sr7JZ9~^ zHB^nVX4>SLU@eq21TkJ^Im8w-dzi94w*Ez^trx@_g&Yu}TL(^zLv3-907rf^F4f-x zd!KWF5E}MKD&U$qBT&Qqz(CNc7eu+Ax*`3;dr8RBXI1}gK{Rvf4sgARASr7 z8bxh*FCho&#uUYfH$K!}2F6F-V8Fr%<;J=pIP@p}HjgG=8u!@t>#jphEH6G~e7Ra*{syoXRU5LWkQictrLJ z{BZKemnkpUy+$h7VYvvWPv_yezcv`&vYi-(mt&i2FzmWQX~Iu`(6-ctEvwu?e$_4H zNqK>LJR+bWeS&6jx3jWdM^o2}H$iF09ugJZ=axOme=@=!4xl-M88F(`z2ztNDbxE7Sx`D(6utPS5+j)3LBJgt_=&+L!Z zqUfrafrh8oqrCDwl<~ZRE){IZ?$gFuJ0q&eT8X|*U|mRx5g7Tt5j1~=or3K$-?8Yn@N+zLA=YGhm*H=vR)lG zWxwHU$1OvN@F_Tj?J)g~mBh0GXR6uMYhDc`e4LNz4$Z8S)}e6tiyqOwy`4T=dlFY| zO~bY&OX-=!J=jWLv&9ylfW5_8?5nm@B=P=9Tw-++l@c1r%wuD`{Zxhdw(~*Bu!h#% zEhL_;{G=!)k_xSm#U;Ftu|V?{)m7(YvUF{DFrt7Qi#bm!4h({QCBx$3KaOeJSh#b^ zb}IIw8Iw*k-s5e~f2 z1Xt5u5FFn|-2$zs;|fFtsYhg$NGaYP-irMl?`ehUGQ4E635=(Na21~u?%7%d^WB*B zy?;Hhj84NNMRhPf^@aBBc|i6R2Y}$kaQs^%2T{7SM2BM?KIV-gmkuw-LWToyt9l+D z@?vK3&!PBgZZocs?82!39?=1r2-2S?13I$Tu%mA!afung>n`O``tBtJH^kEKiPxd> znHv1|6>W+U=O6*)%US#fmw>%wC^_es4mv!(&}#65+$~**xvDkval=Ka0r0<|%)4*_uxDd{{Iy%^$<0LxaRoMT+`>Qmvb`7 zr=BQO8we)rvQELB$5VLdV=nx*`H$QyzX*c6THp%b0Zea6#L0m=Lxw(JKd&15vb8|$11B{6`$$xcGl=YAAF#~IL-WJ}a@1}M`61N< zmn=*`^GG4aECJNLegFukCi=@>!e1OiNO?-I(j)*zT>DY8_!v&yo{tGz3Q)x3Dly+I z23OPuz`0)sMI?sM`AZ-KuYL*Jm(EAQO>6P-(dX=#BQa!1hRH4ZePM6sI7hzme8dAY zPw0*hhOmRU;H_g6oVok(bMpyw*m{d@@i`06y}bzcl?{0LVi#Q{aR!!NItM3~m!gn_ zH#l0D;})|t-0Tzw>G~D8v+@OUdu8G2jf0r7;}@Fn`%?Xt7eOYafk^C%hUPoUXx`O> zq086eYCmUi&0LO2-+AG}L;@bW<&9|%xzM(B1eRBBM7_21knO}ggBz*%R=tA?AE-s! zCxK*}_gXB@JOdAoEQP$xQhYgX0P4MZU|S;zV%530nI{9SeyzlKwMejeY69hIr{HUR z6mY+8r|TqN(a$Q5_;&Xid>df~-4{Gy!@&sr`8FTg-bmx6OSwcRH3zuG+Cjle1B}NA z%w>P0^}5}RetU<2cRa)53BeafM-lseq2$Ov(t5p`(Ao)NlD!0j=O&TlUpm;|ImSLF zcLjDS>k(&W?{J#YhQ$p#fM>^A=rB11QnP~a^g<|X9reSPW?~y0TkTyQs$K zUVL$GmLycyQ#J?UnB)^!_fMDH%2EN}S`*Mt8m62zGI;vd2=4rEI~@O@0)GRd;p&Uq zWbAw{{?z$_SH=CwwX?@?O-&FiOPnR#GnZ&^q7R(i_K}%8_Q05e72V1Bzgo$$a4jT| z$`l}ZDpL+C&TEqFEzg0i&O`SbXMoeu2Xr|*1a{on5A9MO#7t;CowWYRPB_S{xeFv| zVg6y(u7H~ub*mPNpEpyN7G8G2KM5E-{)28i9ZN>%AAqh8QLsClS@UN$&}Fj`jb)OP zwDk!OE{uCa-Op;_%!Z2~;jsl;@4iKiGu@ccmH=A}=Rnyvb!b}dL;a8H(_o`oNRbo* z@jqU`ds-ONc>VB|KnBw7%zQRn2Ptz8LsI8Q%4_S7l_Z#~)Kg~F#+PE=L<8NqtsWO0 z@rQKYHPG6ekD5PUq3YXg*u8K+hGgYaQ{C^R^NI^`(TKxcw((4J+UeT z&-3b2WvdgKCCT%#Qlywjd?+RBda5DQ`Tz znFoR60&6H0+J!K$9oLmIdy`2jSkDiovb;rj+dZ6qJR5`)B86nuL<-}Ql_87VLC4

zkHYBMBS5m)!8jPD!hUxu0rXELWBJ=K%CUD3#(8*?i?Tb&m4RaPDeOYU1DUwN`8k|b z+ybS|v1Hs+29)0gu$*4g6Ti& zh0OwYBd{v?6o#7kvwnz`(wW4^RE>8GtJ|0i;nOM*{I3v)#+BJ}g-rjVERA>{^1#In zY;tqUEo$^=8CtFNhtGko5cyLK)dQasulq{)Ip;9)l}(ey%N1dTq5|-Hsle*baj=}V z4&3}3iPKMM4BDsw*B{iQ^WStd?GOT~1<}x%vl)*WieXRoIGK@jgQBiM)+XL`@<}HN z;_g|3_zfk@d)GxRo48T;St#3JSq0_Q*MU>a%(_2onsP-i$E$B!@#~)wh-j3;-~8I- zx=Ij=Kd1&J4*~RiF+;Yx2IKF_VBo3f#_7U`*lUr1y)8O;YKaD}W2s;TcQsfg^pU0d zVR$pdgNjbQ!@j{DBI#3!1yej2d$^H$eD0@9_IYWm4GG#1+Y|mPP3QQ065f8 z8oBc^!)uHLgYQgdSbF;qj&*?y zH|f3tx%}Z2`Ddku8MD{%@rw|=Y!D89!B22S;7Ujgf=yO@)g3sqyn1K&t#bI`BDkVGn8jq2l#7xnlv3N=&G61 zq?5}Kj=7%&jzlB+XW1&O`0|wOQ5%P5 z#&=;;^WHJWix!|-)2-OO_6r%DtI2e@DqwM@BW|=8Xf|+M2N|h%>7-mY{eJch`@{J? z_)(%5S1r##kBfZGr>A7l^{o*;eYTgF8(ze6FJ+jNYKI1)=QwnOnc$j4#mL{|q0HYPw0D;t^}OOboBvCbw@eZ; zmI=cvK{XWJbdB=z+H1Zw{!JgJE~aq`A8>YjA7<+-u=Rpek#lDi{I{xSxc_ghGO%M73?hTAYiSV!hd;op!)3}`F-dd(Jb6Vm6rPh*;0(pP8DO( zF$oA}-p2=r7$~bxA}Tw^u=S2ERl41RDX->&%z7rHG`5|L*$==erswK(MG9XTKc?<1 zBU-lKoFr~hLCM9YpmEtA?+>wHv9}NYXL1j(>Qb!jFT<~8*NOeRjZ|QkVF!eh0+vMZ9l^2`F2{DZKS$?jg;vIDQ|Nn~atPgE|NqybmI(h}<| zx+x$E=EQ%2lHo`YsJf0=z{>5nsi4hz zIDX*~{o8B=15Bng$?y?%UmHYv?_H$4WTvreVv1__%upX$J}9r$Cx4&KLSFL@R!!q6 zw6XDro6{?`s&k@%Z(lnsk{pGdYXP8mCt1O;N0P=>=;}8aOkPz0Kim62YOg>1E40v* zi3vxAiDuYhq|TOo_K_CbFN87Ucn~;I1wZ?};oi_7X+8Ut+@{QczxpI=@lRFw&S;op zj4$?GQ-+rLa^r~j2o(~QL1VLE@N?b^NjBZAi{-nqxbZ)ldwMHs7CNFwf&$Eu{Y-8z z-cJHG9%0he-MF*h7$~WIM6CstWKNqa-Z4y}MNgt2Y|STje0er3t)%#3_!Y>9GF^tA z7ew4eksKQjCI;W7U_!tTv(A=dQK>WN)-e3bqs^#yDhG=^T3NEg42vw~C|+dd`kFR| zMN$nQ{w4&7wj%N=@l$obL{|OoYV2b8M!Odppii?t{E2=J#Lyk(9o$gq_EBuGO@kLA zob*+-0uGGbKw~R+nA;!%AvY&*lc_N-`&UQ#uS%i*&La5y>l%FOjKFWp5785XS!{v& z2iR3G#@2rJ0}NtKaYbt$JL6nDa<4jor@JTNhJ81F*7hNv5(8nU$XqIBnlO;&tqfsOQvgP4_a3&QD3Etu;%!})prIEarY!t1??k& zw+}%H>kxhub_KS`PV{qj#j2xHWc^Vsx)476|LU1^ zF-ulIiM-P)fW}Y3O)nk@^JCz`>e!vHomZ%4~JICqtM|-m@>UUJ{HIj*;j6Go6)lFTP5HquLmJK zDR6b|ez>D+N?$2^5;%4R1|P*kw$UTj{A<2grmz^azcA;4#3GdPti$@1;dGx07lgLN zLP3Qg299XrZ&nr_i1C6;V_x7q-wb74ykV+doW_R>z;>Y{(8#k8eEt5?;}>}#eoTse zjX9iL?-Rr?AN^^UeIw?+e@g~8GCU=gBKS+@Hks#yp{>LyI1JhWEW3=Z;%d-QGs%Km zEEru>Mb5|u!R#gqm)wlmp?m{WePJH`uvl92?weK|xjq8szbfIro*jx7dSJY!5ZPqz z2zk#`G5n(z3d~;$`a#{G8q!8R-)^Q_^(xpU@tAn|F2M71`=NM!06q;A1^I`&=pNkz zaJ{Is>5pF;DVcmvj@FgX1g%jVzWR(MZZv}zuGW)V>c7ZMkJS)raD#Tq9)f}IJPtzLvLPS;}W z-E;W(P7jt8YT|6RG79^2;o{hQGW}Bv4`-VKPwz3*O-zT7fH2b7TZ$G*6(F2H7gLxV z*Wq|K`2J}p9&zx4WC3%w{D>&54`o;&e&^ADR~6`-IEgiuIoKX~gviEw<5R_AlCfrt zhOzY^|MEj}y;2>s#RtiP^&zM%%z~a9@u<0^iddZgMD@;<-~nfURNJb9(_g(|FXsR~ zFLV=T8yDliiUx8mI*}TM7Bd>#2*w+ifoGq9R%>a-3D_18KaX(D9HTsfxAm} zVZZfb7FXh2vUxlQ|Nj1uY$U6&a8wz~g*eglgb46e_CVPLlZm_6hZRhZaw4z}Ih)fl zli5#pOf^FmqX(}~_klY{KcQNr8an4n!_=c^^s(c8m~(wj6X*6mFy6WyTUJNWBA+$b zFfRtEQVRUNF$@N*JK#C77B+Fr5UuhMVxx19dd)~Od4mupKcWqDl~dpd6+y|lpHUz^ z7r3|jq3V9--D*B0=^+}he%TgKWzND8yPNUY_F0;nmBg@jn@HH@IO@0jEE$%&gPpAs zR4slkKJ{D+ZUW5ZBWZ~+eol}5t#6d%HcsJ&^$D!MB9XxLc0Q!0+`%2+`k=^HhUCsP z;m{Ka)DAmE+OtehE+v2*JkUs2rE$RM(Q#(pEQgol?ieEf2n|{aQDF0BII6rGR;hfU zd?!SaJ$wty1%@I2kR~p-7KSqihUpl8G1;V2OQ_>7#1&>o6xdEzBsz!K6IhuDQ zrQjd&R9u)>j9w0ihX*65#p^SupRk_Q_T@bEzGw%7s$-Dm9D@4ipHYvMhjHwIKb*xy z_*Um3CYzZumuOsNw?v76mgE>)<()AKRYqW5MlJ1|^uuE<%V4qVI2-Scl7zHQnE71- z!y8kOUCD#LC*nZ9Gz|4do|1bJy_j>Pg1p(s3oj>SVV}xMc*tsm)4`!IJh%m0qNPD; ztQ7?+_d`-fI^Nh74!jx?xH+?s#WLp9(z#&>+^xAd17#pI%?VD22{^ubj^%xx7{`hv zGw=FPD!CTCg>K*>tz_0${)=RhU z&e?IsVFh3-@;Tuv!ehX&S#vzy>#l`T4kmR)ryM zm0U&xCtu-lKW z7*7|fZh%l{I%@jy;u@i6_Xc9d}& z$tJaH5I9ecED{)j?3wE@`_-gX6@Xlv>Nx3@jXS)9ap8OBo{D-+;5{1* zzp78+%Ipd9`C<%}@hHbZ-zppn&p_6qHY`yXgXMp!$m_Xp@NG*3lP?V;%O!iLUu-WN zr-^i(VIt)D*g|N?Q_#Cx4LMd(IQ#D~!&Ffr5vN`e-`8yV>s%JzJ)921U$>y#ni9I_ znjl=Yv%zEkmP1d_M({tO2mj=%nX_a%4#}0{<10=W&f7|!TuFfR<{_5ObqhRN6Aw8= zlUVj8BDb&)Hb0$<&f1zdQ{RBk7d_L=Z%f41h})?8{VE7wFvSq2gVB$X6f=l1+j6n|XjJi%rbhC0+QznpqJgu z3Vt>MDafm-xONO+Z!B z^Y4)*ru@iJ<`0~4_d%&*6!1V9^=hibxd%Mq&v!>EZ^A8it+FlUcs4tcGlE>2?$Du%>fP7~?Bdd;<|64)shgrMc0Wpj=SjxM|V|FRA*%(>MLB##{bWo;Ace zB`X7Q%y_DNh7qA8y#osIE$$Gn7mI>72rqBG&J|EU_& z!b=9T@;bJ2wLBaQ-iuPDi%^d{l>O5^3jTcZfm1b^Wb~j9td0<1WtscKAw4@-(dt32 zEK?@W%*2{yYZ5RZ={DG1Q^2FU5@=a|73MrTk3SbzfXV00kYcR^bzje;LvH}Xkx0Y= zk?Xi=r6*?1bq3i5g|Ol4U2xpnhFQ<=P_>YFs=R89EQ;|)4zVFn-JXqChn(?j#U5NL zt3#Gcx5GCjY1ppz0Eaku!N#E)^SoYzNNo@O;*pAWWyPR&CkkV(I0E;)YfZ!D!N6gb z0ZS~caO!X?-Z484&X*UW-aBXZOv*K|yPA&Y6%^3(#4px1LqYhFvx9^!xdz_tYv^O` zL)iX8f#GKgWBCI~=-lcHWnO24ixh=wjZ;w+)V+)Y0O#rVzTYL_7F!o-F{ib>o zj@3=EQa0a$!E?I^C$qQqJat4Tz6)SiycBv~N`rsA20VN+f^l32ad@5x{>t#fg00)3 zGa-ufhQ|=^1}`lBYECzQ%F{fdB>+*^l0e&;$;ng(u-zs?uzu+%{TdZVybBwkSuhz? zhfm^HjuPP3%On47enRq;)ZlBzcd(n^!xD(!jrw;7NGrqKI6r$CuPSxG`hha~(Y_Op zmz2QxYd>sT+6Af?xiGWD4mR&$7+L>0&|g7?^nFCeRF%ntAkh_6^g$TZ9k2zP z;AFJm|3*T54nW4HHvBnf6iZ)dLDkU;w#k=R%<(%3ZY9m|vZ0&|Khwv;%T{oFhdrjO zoC}|H?Pv&Z6<+C$#1D(OKr#Ila#XH^=giET+w0H%dO-oKn)pG_>JS+^vPB+@=d?r7w(pe{Ny(iuG ztHFPfAQ&ZHM7C86D)TevdS;z8y14^VTO4TG1#^(z&r2k&chh1)hF!GO92V=BlA+DK z;Fj=;`dp%9ji@0WdWCdxX=2knx|DM17=W8vEz@g^q(^L!dISW5(9c!ash14>t4&Za zK^xtF#A5LxZH9~Lj!84zz)qLK%ehsg<7x<#%khKXL)|oi|2y7Tc>;p}*5drwB5Yu| zI)l$mq2tLq6lU^Tp8R+4<$eW_2n~g2ie9)_f`Z-92F&&r!!3sc(a?4mCdYQ+i!5lX#J>fu)-k8are^q)xdSn(GYUha!=O6kCbpke!%pi-Vs?Ka z`TDyOHukXKz}dHWyW0;R8(kvx7d_YlRT{MCRUf|ZdJLakvl#tl3|{a{qB4_rtt;nj z)?BlMmIX3e@uO|PRhxmsHkV=Qfj7*ttzjRDkHI!%{0Z3&>{l`Cn)+J}$o?!(lwPI) zyA|V5-QE&Dl-Xe3=I7|>$jr5ow@_N58RwKM&~%2gkZZOD*R4nd{h@3sSbG>!QxiyW zS3UZT#xmZBJ9s4~;UK3pE}H0v!?*Xbey({y??$u8siz$Al9PWdnVE(Hw~5dOUBM0gE#tG5f+i*w?=W&zV+}DxnWe z{pJ2J;F9Ff!+#xB2mB>ILSdK?di)uQqYqizf; zrvs6HAI0KhF6dT6aj#7((_K(Q`FoS(?Am$ksNDUiC@YH@hqIx{%a^rDl zVNAww6DB>MqeY_~;BWQ^6eS9*p3Gk4seeRdyIOD$v#0&I0TB56CmA&A!(BDea6!!* zW#k2L_G$zg_Gp9Orvg}&83F|vdRSte3qO3*F&e^wEZ@pr_D`AYrCY)LK|PCmnH)Y| zVn*+JIN|QtVs<5k+*-y^w@6_a({}dzFFNAU0;H;cn!^m0>DJu2?d@p?1)#cFq3WpjX(L} zTTuXcv1>QB=xIQltr5&Q)C?(lCqcPH1Y%A!qUWi_Xf}Qp7D~k9L3ba9ZG9Zn0(9W- zOb?X{&co`8dwBECJa(mJ9I%{X@Rv#gX8d|cv;v!H+fxp(d#;ZQgrAXy%o~~&Lnm;Y znJul~sL*&N5h5IJLA&OC1TTpw;OT0k|4ui;=!Ttaodx{3YSsak_q$NrS6rm;R35m6 z0xB5+c)ckGkFimr|1tq)xMelhZ{G-+=hKMjmI5L!;txLC=3v3PT_iNb8K3cdqnVF* z=feM>QTrM^av*^IR#XAhs=)eZqM&PC$uO?BqPCzJI?6twb1lTsbM!j< zv&ve^<t?KM6|&vSBt`7#>^>g?`zs=wx?S!+NJK+~?a3sSjU}mc(Hqw}cJT zhS4C&n*uj}4-vc|hA-7qX^l++{l4W5aeT_z%%>|3-Q6)5Yj+3BUjCqLA78eOWiF{o zjHa`@Mu9yq6r~)zarw|r>I!kFbSRz9ez*grmM8Hvw=csBVls)`F&J}=9}6WADmoG< z>x(yQZf6=!8C{39d>`rHN#-s~DTeVLcMFev^F)sNIpA3D1iJ&PNqadLYg#@ZmzG}# z@s55LzT`m5Ff$;L`@o<*5}Ri0VMW&s$~7d4@BPid_EQo}ovp;e&k?ZvMkHFRydqkb zoUDyZzqWbxcJLA3$BOzs4hCB&3HkU0M!yi&l$#p#XC*^S?hYtF$U)^w^XRnQ2FUDa zrL5b^#3;-ho)rya&Bs|3PWQrh+m}FUhYg4_XSEYYA_zeqGm~mdsd3~F3Mue zk(E&H|3*{bZ8E$u6@^k;LDudnf0&(VMq`KDbZkj9(Em|%9{yN=Z5+=igwWLTLunXI z4Zi1+QW420X%iYmLsC?-_a?IU%--DRqOvnfX^0l>rA0+O=lKhG-Q#P+gt@Iu;Q0YrAop{yckMCcw^WG;<30?Hi^a*5=|5!S67( zI+ZkxaA4k8Ms9lN;*QR3IQTac-t6tgwM`);N%9b8Jg!FVSBue^Suyl$Ug58gLRjmV z0U09iN%$z^L9a=tj>~T2WU+7zy}f{Zxl~Efrg5h%lo*jj<#^k%n39F zSC)TA(JVfUzr2^Jl42Q?wUzda2Sf1sP|Vk{fRNf{bo1;Avd||HD_?E{7e_B5urq?? zw7v1Y))1F$d>9u$WW5lbSd`9)ha+oPEvq07Ciyp@TXYco+GYT?Mr^(|YCzND@@SR0 zJmsDIj`yD|#&7*^k>6}0D1W~NWifFy{uB?E)aHZSzBoFvoWiYFjIUi8RC9ZO7XI5m z13B|rZ0T5rndL0oJ6Q$R?@^^EUnHZg*hx4Qlf!mydoYOgF#i2!9=Yw)aLUmTdPq8- zi~014Y`U3%?zED8i@$TxwHJ`R$WmVEiwf9YQ(RkTO==4m&(S^6kkM+V-EKm^E+8tdkx!1@8ZZ8 zPqO>KBI=jI_?o`I=}EO^XwuI7Ml-#*liybpWtAKv)?W*z_k+;b!4(@T&Z3&wFg+Ic zffqTtmx%3~iMo!ziT39}=3CcXW5N62PVm{Z20vA0k;(~)=yrW8x*m*y^BH4w zcAgDge)1l!*0jR)zhmj4y2WtbE)-fnZi5{)p=87~0S$h81DSUXV_oM$W%gwJ@F5+) z)b1n+7G8cRNLVG~)4k7$;Np0C-^>(ewd+#;oe#cr{iQit=|tPrDz>0`(9-UNZM`&ua40d^h!Xmtq~AUFy4K zLeb{yX>w;Yy5Vt8ocq8&O#vV$eUkr`N?QIuo!7K?C=IZcNr62@_e+LoC$4Y0$y5wk#|b)Iuz*jK#!L#R>$ok zb(be&=0&6<{Yi{v&1U4)dufoh1jO>EQp3?#(5-qH^WJol!cZByZoD46qf} z5;lJvJ&p6)tzgcJ-{kd)>G-lVh&S=cG2D0~1{%^lz$beuKB}+hJ(W--lZ#?OdR`1j z{$@VpPo8Xd=7tStwt;eA1ez4BpoNBi$*bGTA$)QfESr81_BBQT9lC+rB-7YD)E7k+ zLa{?4n8-={klX71aH8!Yxin`J%(~G+w@ox*t_X8X|6)Wk7~|ix;2zv_8l+CCu~5Hv z5^k~agQfZtYRB*yZ|r3~QTpW%ca;`__ucEj35ilWRV5s1JqOE9UdBcZFPLatKnGLh z*>8p?9WkrH&3@mINE_hOvtkWOFJPpbd}3tRPn-n3p6!4EE~YT;r^Ax-v8bI+osoM4jhYXr51c9&G@1 z75ADE6(>$AKLu>R)sb&Sh9K~L7pz)Pg#P^1++KeRh>H(}t{==-n(_l{)I4F0ni_l% zQiL^q(@4z6bXdpcN3p+F(N)vSA^N&K#)(;@y+0pDya|G?0eNs)HdW)()}JJLkp=kI zx}#it6HYGvPUrqGA(O`Rm|uD>tPx)UsEn%pA%YLtJvAtusF}z#KIKC%BnUk!DEL37SZi{;yDJY`n z#=LNiP#W_s#DL+0{F;jnfw0mL(3x(==+EHbv=SuhE6_BYb5nqZ6<^{<*N zAV!2I<-;7EOHg*C0~~Mkat#LzVb*Tu8feajWu=OEvSShLIhVn$dUpz=>N6lKXf?Gr zc7f;Yj9T=~hkSfWVemvI2wnRLyHAYc8!rLMS1SeA4kyr8pS-c?v^yHTnNM%s|4s{( z%y|c*yBG;F15aHxzy}NLP$E_pF*cJ5ZaGCTG*ks6Dm zM9Y7yDa?y3)sBb|!z0^@f?`+LQ_4 zv0)H5?e~M_e@$>;+I1Q)^^$xkXKdiFd>X#tLfEcej5;pU@PhW#+HO@(5}D6~;#Xd1 znKB4D8pHHiwP_6~8*$%8?$PD-x8U>A{rEgUn5XZWj$6~MAXqJ&cWi|piaF)N;(^Q9 zWRk`>jJ|aFd4F7T>>kcmv-ZXNVr&iI-N~fySI> ze5RcS80QD6Dm5T#ngM#}-AUHJld$Kj1DVWtp^b9I0-L$M$frMxY#cv=+SzOl;5M1JocA6MZJ3O0>lWiKFAr2+ z>krD8pU~I)GD-T}1gK+O+}eSDa1`|*)}>i6Zm9upfAYiY#xCM>qnaLJ%n7+VPmo6!uq@|M^*cofqW>tM_}7o3h?#R0oIY=2D9_TW`q;w%IbRZ~e;zA>qr z6-KsYO`=g7-$VWTX3i%$h<@L$1A@w@;c4Ou@?t_f9&9*HEz7I%g+e@rZR(?GGy%^l zF~?G49L_o*jGbk%aI})Zem6U8QkFo4F@BgQY6V}8$W=WWX@+ZlWuWK&oqJ{xh!sZ) zYHZiA41UKc&PMhYwp7Pq)m|YDF@u-%ciI&cINuFvOMXK7Lu33CAA+k~p0Zs}Ff4kW zjaK@U9zIwI?b}6R$2uqU&MAgdtZ#f|)h+m-Xbm5>J|OeY@I!BT7cP}tkD0xVSmVS4 z%l0(-cGd41{hSnBc=H@FRradByh|4HG>56|v(>PJy+>s=jqt8Dh6v5v35#3AL996# zZ-)e7f5LAv;lm$zwLJmW}b@bZ0V-Y#TOd)`4YTEZTNRqnn@-`CKT7fyO3SZ61L_YbD^obQxTtZH3u-fpDkh zCdtnI2%SN7oQy#*?r{6c^WAC$kBW8R!S`m+*H6K+3GpyjkB9z$SMZMQYQx!qjYMsA zK8BkQzDyvwKT;0`YB_bAU147bdgL=nCxJ;|4kU)45p_Uc$A~PBJoni@dHc!lTCw zz;%5BPIz_+M5GsiLJ`|Dcldz*%ye}2n+-Bcuc46B2Y58e4HhQXGG^Z^vUY7F2@F|; z3F?M)j@K?K8?OkD)ma|7&$DKhQU=xjwvW769|`k?Wx=?>2D7%z1F6}MvAQ<{Ctau} zJbyPX&$A4oX%qPGPK0H?DmA(D2H|v1Ia=)$fxiVI^uc$=p3m3DQ(;N)<<=Kk_^=2b zlx!tZQ}bCLIR~;m$9YHj4&eLH2yA+~4}_h)ar>=YNL0-Nr>#C9=ahtk%!8w z>u`Sa1bC{LfkOq?;DI#{&tD3L@eW_8N{bBb1bE*@K&xYGa1N zfAEX19}m^fraK-;LyG!Z7@xHerWicKli$kmV_pQ7jz(~L=h*+Las~3vBFJ}~$LD#+ zA@ffKnAl`u-?LQ4XSj}Hbsy+6)_3)ua)9`5cmz(3xw!vf6c&u-VPUlZc3A)_?akxq z@m1p`QcC+Cr$MAbAXhm1IP{p;)42X;>~1p~#wK-;aUa&qX>Bx(9dAv|mFL;YpgUL0A<`kl*ukZ$`5n5y%UNW2l%u;z+DbVCNt2#O#x z#(22$q%WF&JqRl-rSN4?HE(30I7Hs!$I5x7Afy%wBJ2Ec(pD+B$Fj?RF3bV3_CN6N znu5mPAKStFUm*2s`vAF(>oGtl6#FT=&tykoZNC62-roT6oEqF(^dCKY&z+RbJ%@{) z&&Pl`H&nIJ!#=iKY0h3sG)32g_>FYSM?j~2E z_C0Bq<0827l`@$omrd@MO`;xmQo#L;49+90i#~N9&*`7B4=BR+=q6ffbo}&6tWU6q8E5TLda)T7 zvhxaFY+$d2Y9iXk%!7#@;t-MV1)7IbaP_??=+M1E*Sd|8IAd#!F|$&awDNa-(5GpE=pFkORfaQAb7v=BvbCqO$M@2qyZtahrW6*m_|pk* z1K=Mhe~)>u zZc&?O%TdEE3xqYS@b&>My1KOy9j_Z=NK`e7#~uZH_M71u;{#yw`(i}FF_;RnwE$Y zjeEI-lu9)Hn+4svVjyPPLI&8m_^+KM*;BR%KAs7ndavtAU)(;t{b?;k7Ba8%^VhiL zk2pHLVBHj!%PVQqfbWgn#Bx9DleWtNQwq|LW~ID9rC)ewcRnsMevD=pm~Uc}Kgb^J z##xc&*ruAtcBA6Jq_23)Y&H0%ZooBackp_x7YuzjLu;qW@ZPHf-o|?XO-`UQ7F6KT zZ%NR9N|9z_E!c-8@MO!RLG#Z(I_+p2T=h?(xyKAaufPB+^j?C{4t134@Pe?B6pT4J zMz*i<;WowdgZYBp=y+#8RoD>;*>AEiQL2SFu)NE2(OkOjODI&>2a#EmkHeh*WYlf- zXX1wc>`-wql}^4Zg+4O}P@g@AWDJGDHufx5x_g1b>Xl?#Rs<^7EQjCM`BAL;GZoMd zg4{cN7#?;Qe%Rh5_ng6~pm$*+MpUoAF;wsqHP~j?{tvmt-{d_(3uyT8N!cE!LUer0Yv;K(yryv@N)eJxc$0 zORs;%Jx^Cbc5@a@75vTi2$w-QUXd7O=7Dov1Ie|0h8>YtLB@R}7Z0^XOHi?DnKJgKx~T*Mz#M_aJA_t0vX#AePP zc>QF6`wz;`N}|LQ*4LS_5o{Jp!j++DdLqaU^41!|E$=#fc(wyh<;PRq>w1vZ;ZJ5) zhd_TwEZsG#OA6hDYad8F<@D?HK(acL2>P+lPbR=p5Q@ymqmi#X8}^U?>#`&p)QYyG3`o z-f~Iua?gZX&$P$nTe2?^`FNFXdmfBNm%nmJy^8o!i!n0&w9wEvjI>?rV%^JFjQN-Y zSy?>X77<3jUH?^)tZ{qEV$citLgP;fdO!c#WuSI{sB*PLb)9v~*U}%}bx5YhDR~ULDlVIZx}a z+=bU_SMb~KBN#I2I$a{cN7m?ECt084Vf{+hxx62ULWM72{6iVMTo8uK?)eZE?iD!v z`bs=5UBI*}1F*I8IcX?*g&BJ@px#Utl-6G%!~Ho_psS5If2e@}l+JUu@1J6uT?($u z%0=;6l^A?5i8+$45e@S!RQ!`nF=Y*`2@fTKGbONO`z;aZgy89jD?nQS(b_MI zc1v2x^Ycq^&x2vQcjH!w6A{7Ay}L>F0%I=3po`AziNlLh$|PR8iv;bQ2;STGp>kX- zY~vpzYZoPvDaqU6mE$OxG3zS*{P`x?G>}Oi+gPA_*fy~BSOr$^&8U8mBO2wz;@W*X zIL>4j&dIQ)#sN9tWw`(q(rj>_z-^fH>n?3ynM=Y|Ct?Eq%X|AY1slrpSjN1YetW>d z1)FwU{!@)4&bUV-0%nn_Fa7l4qE)cD;{c`}%tar)N4V>gGVHMK!i7Km!9n2>EwOo8 z&Ar}7w-ROh^cK`2E`OqaI=iygC zsrtmfeB{E0aykWqL9XQ%)@y6SJ|O@(0pucctQ)_3gV|=a=edfu0Bn4C7u)bZ|eyBe)2hx2jK-Xs* z&qAUWR!@$k^(t$qP@NpT)WpGoof|RHq?(@ZFu=BvRq%5O#pk6(xOn#@jn3l9a6@7? ze$QKukvTt^PdADFiO&S2~UUcCL`Cta=5Micz+qPnIvNJzfLpg%)6eIy^u z&-dW#gM4uQW;dRH7lbF%*5l&73$W2L6Cw>0q4_To;h_*bA)k|w#b{x9)b=7Ekfr$ND#l3bG$ z*s@od4w?4g6n1vVyc3GyRiU8wWeS-b;D*~WFM!CR=Q!beGmdWaqUP_V!TKD5berQ) z1pBag%Lmfju0vexYv3`vr%Ieo<^8D5!9&qWG(I^IEH0ge9p3vPX?rp)VjkJC9y7YC zxD+3*uY;PSDyN)D2$~{vD0@(3#btnltfe!)n~U zDu;M)44`?Nm(tNCTJ-OOuP8RY2GTQaVP?1`cM?cW|9wNw&|0{XItUMt;f6xippG*Dc~UL$ude+G(NT95bdi-P@($Fx{ZTH{A*1HL`HkG{C%foGM=;lAfzOjcY@7i~xd zfm?P^F+~gN_J_ckzB>9^DHKOD_Yn;?r^{}Q!Mvrv;Wf__Euv%KQbPlo75hJ$u1q36O8Ao6+ObTbi$)>PoPxo2?XfhUiSMw9vbJfTO;11nzc$IO~^I=xtr z_n~$beUthdRu8ael1ena-NuniUz>4zL@4=DB#!0pSE;)U#-Y|jYq~}KKJg5FPp6F^ zgYw4}T*{mrG<<50)|ouqtse`g+Y-?t$%x$hkPI86H&ExXI5>L0nD@Xwn!XNK#i6KS z>e?2`?@h53~!n#rhDX-})#>g_My#b47EN5IBHw=4Yi=%8_@%q7Wyn2H< zGAvR_i>@f+wWs12kuh$wffGz$W(5~oF5vT1ugGD>-B_=92IF5{f$J@+u}Gz=`aj+G zG+v<`eta7v>0UW-e}_Na|2!M&EyG~n<{W5AT>=x%Yv4%xBW%ucC69lkV#+;n@QD9R zo{y)%k-ihS*e{GWX=IWSWhu~n_K&1$Z-EkNL{3o*tp~@r)ozz*_jY^G={&)ly1}?^ z$_S0pl)#_QKhSs9D|q7THsSn|WE^<;8UI_CfIsGDkq5bTop$F(!S(`hO?TiX ztZv3d6%$}!rX$*H+DvQr{0CFSYf$v@Zjdkz;K}EmfNdj%Xm|1e<`tG=TZtgMhvOIpPF}&~HPwml-VT9Ov4!x1&+K zG_LrOjqgn_L7&MP7;yTBNrjAOe8mCp(y6?mu>*v!pZR&toQ7@fLp;6lb5PafM{7-% zk#{b4NLz3e0UVYp+Y z&x_Lfg4KSjcs8>>L2Y&feie(Q5-G>A$;AU53XZ|np$ssyVtt~21^Ae+jcze*RUZh7 zgu3t&@!96dCmSYhV zoTQhL%eeurHf7ZOYa8v+Aq%Wl9_lXmVR#@4a{&!&6%^=r^^**^Y5ooqYpJ z^%lbI*#(fL#&Y0sex!gowd1`;i0FcgFm=l`wBJ)m7V_~yI6v~vO%j8m{s5>9x`t1S z1K}I%LY?d?gZ*#ZsQR1FB%scd*D4T*lDqe!c-#@V=y#r1fy#71?g2en?uLiQXY(f4 zg@De}gJ7POgr$1b#4bk--%5)zZ@@D+&ozPDCg#lgc>@%`yP)9SA_x>)OWw_!i`#FG zBgwj{{>IP)oI_nnlZ+?C9vXyg-f{TgT{i4+i^Q{jKsOkj!O0;v;bq^&noO_#IJnpd ziXLqS1J-|~IkoD&yi|w|iGo6zIOfrYFFujJbTWPf06ZOqF^8r<|gwy8AV%(>RU1cA?KVe~)^2(59@))&X0>?!_;eb&|0G=aeH!+qB!IKJ5xOkP<-)qHY2cwf zG$Mn2w!N?6+*KK{#{MVKvHD3g+r!DzyQ;85ca;0Gq8CJ_&ES1~wGeKcis7^q3vlka zOtc9bg2^w~E*CD~u6;yXl#1^5!Wu(LJgV!^F8vp z>#H)U&RK2pczFP3`xZ0T4m*bg#^B{jYx44K0&KFhCOrz}HCsET;W2?!=-pRE+xYl# z+GqxxW&8b&V<~9eURX1!L=ALKq~Ot%sWs33PQ+#nd35}&Nh5qhVQ;n}oQqH;D|S9Y z!3C4)i+~WSv1&PK{qkJhFKRnv4j7WnB6hGgdk0U&*%*c;6DYCtBn=1E8Lt@0)XXgM zFX%RDE6wNG%RZ%(ABE7x*7YFi#?DRCgYkTLJnPK(QGx3zyrB8{@QyCwO}!jMqWh-8 zr!jBp_Us~^{^=3vTla%LpS2I=47$nrl{a`3E_lL~>K!;L+snPZpae@2wqQ|W2V8J@ zMO*nDQ1Bqf+vm)>Z_2)S#!Lr>GcC}o!-gAhE5tkI%2{njFhjKhB%AI6e~K_j zI|Wlq#q%VPZw(h|Hb&;mcA({>UT~<+6R+9K1m5cfc=}TY4KC1zx?T@7IB0?rm%6bj zF^qXe+@b6oW9|=)ko5N3*yJDu326&Zw2ZLX19OX>&Bhh4yYQN+60Tf!7$=p@BQaKi zj6HG`JC=38l8ptBT;d38R>t8UkFD7D;{hJ*^P@larJ=}&N?Lc2IiPyeAbsZ;iNDirnJ6R4~v7qr_rSN=FQXP1eKT z#|XV|w$sG8anfnvNa4B+%J%N%4T^5WyUT+~AAF=|daH0iGao8OtGIbzu7TB>rMxfi zXQQ!M2n_t(1C=B1=#X(7@1zbtI(66L{Sia@-Ebpa#%Yq0Oy-xDQYEpq+d#_74R77= z2LDxOu&=EVOTVv&KZ9LlpT{03+cTZIjd!BlxIUhd)B=@&4P?i=EcmD#j8Shxq0uK3 zyuEfprj8S+yUU}XUlo~tIgEaC_)OI%Zi2@5|KT&^tGL(k0|^U=!Ks0Z@GCnb7Jato zDOAs)=Uc|;f#51CGSmazA4}+yt#uRbcT)QJu7#ohn;T)M1Fy3DU_RZHx z2eGPA9rPogjU!>DT0RyjhGQFZSgW5lB$HQ*kr|PTiC9M)v<>BgLiuMpMb{oVT}@oO z;}K5V914Tc!rafoS`Js7=43Jsg3zhooZ{0dxQ_WCJ*WH!Q!li@?!_$UxHbZmCLX}1 z>gTln*nW)KQ~4oUA1i|W-o<2kvpcyP_=Qd=+7HQArDPM^8^!lrLyP&t7^CkF z3t1*>J`eD&=@P0(7EyU6A9P7Lg7=;+C#yRH(b4Glh@w45sj1Eh_}{fo{LTt{qJWP6b7YWyS547?+8?}ZGwWyf4JXY>*2jg zIT_ehh-c>%!S3N4-kIHu5B}U2p8VblXDkY7?ytAJ458oj-Nnmrf8a1E){T<`QNMTr z%ROMkmT_$~^B{Vz7?!Yec}WIiB3$`Kj>yi!q0h?D=hTk{lhVoQ;`8vZ$`#jV9wMD= zcH-GoKs+=8u+YyJHBQCB>cuTw-?CFMW%M|7>ayqjo>)HeUf}9!n--f~PSv{S9PJTTTbhd$4T7Iy5<} zj$hMLp|CZdSj@akFP4a6xnMj_j!rrB<1FJOz?gPZ{6NR+FyzI>;G{r-+J4DZkTup!eFK8w*Z3>yqVgrHlF@iD)LgKkw4-k^|-`1@lLOJ&z=W@M4SXDoqbT_ zKT!``58nVsfq9TzluB1XC|dUj!cnCau*O^*w_eQ0eNunuV3j(~FBs(1Obg+KyD0|v zOrUa`a$rO73Ap=lK8czy5OCTE-jzGj4!Z`Vz=| zqKY0$lQb@`{0WNP(%9d6l@x4BfW=$(kk`V>@N6&^pD&L_$x^}EU9Ar2qm+P4E{yOd z3bldlS_ddRzY@P}I9zl2T{8KtIe=S^df-LLYSJ?=7iIoShGXu>n49VwY%;UK;f1$R z*P4u+Aa3Ont z-1{={$g~r1;NFDV{Oyl;8^lc^@OLw|{yG8=_S>T3G6@JRU^|8fO1z6nW87}&Vtb4l z@crP*Oxlfv5@?o+{}tL)h^PP9+^Le@dXyIZvbvwpBS zB762H*4AU-CQr3j`sKV|4ar#Lxf+8Vni!+s9e;P5u}<(tmT9;R&+#p3sgc7iudZVZ z%)sySQfr>ouO{Z_88gysIz-GLr9`R%c*Py)AZ*G-OHO2cz(g|XcpEul(Su{wF|@NL z9`+s>uCX5DuRUoUKqM6+SYNY*Y>&PHpX$$n$%l4)^?oU42zZj#Z&s+7ISHp7_zyir z$H3*@OZbn!6sPQ%!T2JvtQ)0`hh5&$y{$Ll__{6l+qsaQ8f@j>tk-~kF=MjUv<~sa z?AkH?RCHRu37^akg4P!Hoa&tjIbYT=W{?XlFgSkGwFng@T;P!9Gt9W#LPRtg zVCBL>IDEXEJPC0k#rS|8vX$cwqh@dM zFn19ZpLr4t%dg@CU2QC|_r_tCN2`B1f<5NxoLRCHS?23b#K!&95?uXZf$2d^+iMLo z;);mowhSyY{RgoMV|3HU5`1gbgo3(~TvOy3bkAbDaX)!DnZsCVGB5DmzIasXUO-$2 z_;KEXe!TqcHk|yxx^^b*a45hKyKkMvUj}E@u8Mwsxsclu zKxJl2_O~(+Ao!VtDCN@Kf%#CoGagR7 z*o-&5FJp)HIB_{+0%?2VK&C|t)TZB5iwbLn@75WRt*{gpOMB6mA2gtSPbWF@X%5l( z+60ZJt3fy;pC<0gLmkUpBH9`P-Id?b&Nz;&=U;%Y?{^d3f-l77_(RZiizk;A{J{IA z2BiO~ab%bXMwFnjRXFUE9RM{8FU;XzkFWEh&|m`( z2Iv2!!DBA4mE_R~>qT(%+XEP0Z^_N#>qHUHHL&OGR=8Pif-==lIG^K-P)+743Kleg z)BAk%6RCvvn}YFSmm8=3SPpE1f^mY*eX5rqOSot;6n)_W_E#=bpGTX~B=aArjTwMl zm@wQU2f=gF7iuORNMqRkDs5IKRL#F%^I+^L{G8x}I?=K?##nhe*n>%L*_`@-Da=`w z1ox^_kl$LIlKDgEGM_zTOXBHP0cCpka6Vjg^oO!4F`W5vFNi$j!vx#sD7QZimP`wP zUqh}~>Rb<~8bIfyT7!n58i>!12Gg-@URR@gLH?IT^&<-ibea0hQ{T97*{h&qC5|>0yK&gy0tj~>#UDRAsQtBQ z)EjeyRdpQp&kBI2F}bjbi$<|oo)Et`7A_XQ;E_mMD3{5_yu$@3h-r9Dld;H7?0~nG z2f%Hn4H#V#;S9pkaYX?Kf%knt=Sn#iEzZL%{SrKdSfbFA2Ju2YbE#gPQd1I5&pFzpFiPJkbxHGnQ1x9nkoc4{<7e7jxBsr z&qux4VW@IV~17?N5wjG0@de;+j|FXF`%SkJ*xDOu=nUb^~Ib3ku7<3m+;Eif9 z7Owp>a$L+D)QAm;#e5=Sh7$P0X#iFnI))bfb8y|Q%V;-A78SZEOitO1T-F}2{rCj2 ziutMn&0vD$5<0cMmufKvkpL8up(Cj<$x00Kj;MjPWHI?-?~bmMtLfbEdh}kJ1l>4I7OLCn~Xec6jdsJw#WjxL+VPCry8mNBC14KWa1-`a@Q1|;iS^7O5 zdTvzUko;Tf{;rw0p38=F1&*w5ERT`@9573y71WRQV|&GEZrA(iC?&g@CoTUSVw0G+ zbjLJe{^K@&a*ZQ8%vBbpoyaZzTTe$m-=bUItV5j(Yw*oldyHP#ii5vhAz9!O`cFMe z4he0dlO`O%e|O_Rd|5NgAv@uz#jmvg}lHWuxdb(OI)K(Ot%Gq zLrX6xJmf=*;T)7*&_|RcV|i;e$7rwWJCeH;aq6@J+I3@u+{rZJeZI_(fAk)cPRDwZ z^sSaVbtf8xZTz^=r<*Z5C=8tc6%h-?rQj=U$UY-+Fz|N|R^+LJ$_^Dw6_AE8xJrjK zj;>vtd>yQIRD+|U1nRnM(bb=A20Mm!vvdyRh0$;K`7uHqAw3=$c= z7lq4h!C>${phG1#@O5#W1D?p&pAWtrJJI5kDHN|b41Xut;IdEpkifE(B4_ua()q*S z?HmENR+ezmAQd}>W?{slJJe8cfEH`%q5G+FZuRMf$nwJbs zvL2Wy_>smb7GiU;1aWmy!ob9KII>EacJ*DuUw@y1zrGwbEK0y?`W6?M9{!v)2CaN?ggR9kTP^M^ij-Q6RZg>Lxsn-pemzltLr%0$*}B1Go6 z!nCIWpe4T+DwjNhSn+;(Od}jCWJAH!-x*{08OvB90F0fJc#86;!K7`37XPh;k8Zhi zQ)m$Glq#klUzMQuL08fq8wiTw-{I<%#b7W{NvnjPkPkx@=yUNZeR*R7#x*a(eTU6p zAWVrI{UL+8#V^Ttt1GPg#KD8(LPYmM99kB8!5$Mu;3g;5JoKVuX~!Z?xv7M{*>wtC zhOVHrO%y0PRMT1|b6T+EHoluujE^?d^PEyHppRP+`SUgjl-|^%R+I&;VXuXGz$9o| z;(%*cXw%#SR9!_l8188-#n7!)_&fJ0T`I>o0jE~rdMPbZ^5h6?ufL8WSEAwZoztLr z`zqbI#Fx&Bn+^`TC1CK)9ik>=6Ehtla$vOx&cS79?vqF|nr>sxzcuLlQ6H6>Gr5hM z+iAwq?YLlF03P$og5pmTkvBU9dS`{Akxc{q=_$m#hga2iy?h7XZM^7SwqLA%kptgS z7~4pq4E_zfayf6mk)V~A;ditav`C*M+k3a+f#OQA6Pi!odsNf!6CT3Z^DEdqvIte! z8TOCV6}S-0BX86AG+y5d;_YM3kzEhtXpc-1L@E5@EqACujBEylsYUR5Nh%zO&87A4 zTd7g2C6yaQoMG{p6EF+MOa6@CwWJYxl;U9C{ZN==qk`=lLh+8%Iy76l0$O{ec%p_0 z@O6F?yz9-UryNdl()T;a7NK$&Ufzh$uDQaB{hc`D&LN!dFHUmB%Aip`m1p462%p{z zlZcKmIF}v{;um@FT4WN;v)c}u_tJrW4hF~Zi_EXLk#q%YBJJYVARF5V4Y{+?u;>Rp z(sUM#76^lDv=Oh^ED6L*){)bSTX4ZXdHCAs1{(y~;zUs&SKZgaFojb3>$3wsTrSOa zxGz!eLnw?!Fjk7IBknt`3!lEFLPt|6mMrnY_lvCYinJ)^gUI^^I{d}(W2eaG%>rc3vj~hgss{H{pXkSe5h8cx6p@XY zMQ)j~@AZjD{4#qZoS!?Dw5t|i=`Ru9A)6zpWF3m>!-sHga~BqHtZTD04f)G9pp;cT zj=FvzOIdHi_t$e8x={63_gXA%|4zz92wl!=#C0mS$Qt<|xFdHL-46Od#f%J) z`fG%?hg%3=c@CV89EKtJ^Wb%~A7eH>=EQ>u znck(rM?<0Tupugn27^oGLR59Ti7SI%5;N0b@>o@u?7T`r{Ye{?ZBWJeFFeU0&xe*L zF~6~2KK#1#mD_2_c4^{gqFgaX}j?2iyy>rb1KehKLK;%Uy+5k zVsL1Y7hGKQhO<3l3@3X+VIs@Y%MMZcCNUC!%OY^*M?tw<980Wcsc$ckgHK08K;#kY z=IYtNQO(QMMtn>0tNjv4uny$u){N97JLiz3wRgdp%^yBAO{*2RXd`EDSCO+yO)x`d z8<(;6GL$5);GOxCirXAyz@(~)s7nRm@@Faltb6!$4G$K&PsbR)RBp#S0*Cb3d;HN4 zb)Sq8;bWELa>EJou(gwTRL{qCr;=eM?-i*tpN{`F2*V?DS6XW?$JjOfWHpKs+pJ+C zubD|jRu{n0zwMwED@r#0j^h&gGQhjAiMbqitDOjbNNY|tkvl?p*wC&?PJZ5rL)M32 zUG!_b8|nq0f+^VDtfsS5i|LHl4KS`7hrO0_A%Arm{cOJG$rNB~^Gj!7S+oaCq1@DDd4xQGmgr}r0 z;r7yas?ll&_1e40qnD0c)7=(a5pxmvGE7MS{y!1|6jVwHxXPO)#3jrIuC~&zc&tR_Z2D1N}|$`rbxZ# zj!;S@5m})UB}K?cw0Eg!Pwh=Zs^{EIG(<}wQAXJ#Gy8Xb|H3<-=bZcge6DMc$$SuA zb(YBf*h}rZ4bdb(k$zuM#ap{(FQ^IXfM;$yq&5grgM?N*^fU||w;VDEtx6?^hiY)b zM@h2wwIs$3hk)a3VO+?vgJ*JQ>Z{p1qUOjQ^bGUHqNRb@btn>X$z(`=qKOu<@2I1? zB0QPn2Fni@VYEjY)&2VblNM~_@%`O|Ws|03_dp3_vTuOOkZhQGstQXPE5k4`m(=_f z!#7uRdCOj9z@4LlsOj~NB&n`OOW~6+*+&ScYMg_F9f8oDavIx)V|df~JL%7Dz1Y>G z0yWJS;g7VpL0nxhv|jZ>==BD#fDmZD{F0LlU5tfe;-I(q77PZJfwnRSA0GXo_crZ? zKhCajF)|3VJL@3m-#Yv=Jb*X;9<9x9+y~RyIk&w(0QbF2!&Z?tvdc37LZ2t_&b+lp zwJ*M8G_@3WcGlyZQza1aPaX#AzSA?EyWq*@3f|9F3w&ab3X_E&(gHOf*wk5zyHa+6 zuB;1PC>jcj*W9I()vuy5o6U=U35JzqI^HSD1LHJTvVOJ{Rn1um1*Q{Foc$kH+7_WX5UA!DAz)s9bReMs+vfmn&aLFCGQ^woy=! zoegI!cA}i2BG<}(>n^*Mh$-JuP`+ye-9OWa+;bsFjvAvq7hLf2^fU0!{4Uk=eFh$j zjqv%u={S7;FzR^7W0;vBnZ;OG(*&lW$WtFw(8+^ERWX=xaTAFYy8zyjr5JPe8VYZ- z!5{mUq1dS|n7+drr}_EPnhIGckJ`cO{?!N)JI>Klw_nq&wc+5H9|FctSK|DlZ1^;i z2$yQ>v40+-)5kEfvc?XwzMscgYC+(5A^@|`og!z5G3{^CMRCai{Ff91BfKnkx?;R(7Ym#3DN>l7fm)s8=mC7vgrJ{$V(`_cmGVnmZ%2<^#7Vg2j$pLZ-yTl0(M+V5cr>)WNpMv^P% zDsc9XBr6jrl4NdQ-zdnoi=)L)Cm`4fLR;rGC`Mb@km&=yZ^dx7 zQz;n^xLf3^4o!q|TER@g0DU2X74~dA>7P`}!i?YLJU3b|=!9!l$rmC>^ISNrMBf z>VU!-IJapwI3-r$;d(>Vtmq^EPF8`%q=`84F$o29?4aO91IaA93Aby2*QVJnOwyx5hyyLNTx;!(m#C% z8S_5~504~NFA`+g6SFih@y^5Hs4`+aKbxdU`QXqTHq*6c?9=|6RL^-I92pWMA4Scv z>dkZNpZ$SLy<`UwZP!qgcM{J?ck*4 z9XJQxHYMO#;VEp9WLyL@b-Jc60Cug+hS%2__u*;s4R_-q-A$OYlKjerJh}#2i!>OI#{teXFQ#?_X0Ytu2svST8%&nogsd!Y^z5y} z^#5ldOE%!YCG+9mUB-O58-ZhEt59b2JR}w6(cYqR(!Kl|)z7&CBWncc$Yd4JKQTZ< zhr{r*yg9n58xk{-LXz-?bzHwy*4m|~Vf;2fy!yirOWG@7TAu+-ed|incUCYzRTiw* zO~8ilAGp@bvXF6Y1L~F^hbqp;2Rt{IQaSK`kjE<>LK{xdM{0!Pia_u1ASoMhk}b{AWvg2 zReaVMdvGNa5{tz2LRWI znt|zzKJ^9yVE$eg*rmdgC)87{hHgg+M-N_$b&C^2htS{u0nULX1!#ogP z)(`LOf}zazAlS%6!R=d@VE2T##M4m^L#y=ZvUxoC_S7Fr#~AOeNW5;|&T8G16+G0hLq4Y_=y%apy>( zP&x^6$|I9^e}a~hPhj^i2lVWjzwo>SCObud=u`*%J+uSXH5Nes<91j(oWSyR>>Yn- zAs&|aSu2+lL_@C+kT=?_8(^J<7B&A!%AY2h(6k3`j+qm?MU!xJ5S-k#4GtMd5wcPq4c>K-8#>X< z{VEN!)Wkq!vIKm;v>VSV z`v|p&axysPqs+b9U4@A$@1aB3vSz1s8-%+&Cdzwk=~+8(>|1!2>U>)U+pH$S=a2;6 z8<|XUD0-Nr+&B(5!tHQ+sVO;qsE*$ARRgCI4@f%Ujls^6V7F`pC*&i_1-lSWULtz4 z?%B`!EYuRr*1G}b2sbX0Vx3WR-29xrW_#IRdzO*Vv)!1GLP+?gBG#8Y z@na+(=t=#@^A^Y=R#yZtc6Ty{R=c1>^D^w6*Nev|h2aPJ8caw|h1VW^@U-&{s@E^Z zNij!=Y1>`+cyujBs8mDVnuVmS&<3>!XF~T?YZ#vGit@uU@J-JjQio3CRHy-Wu^7e! zT#q@sed*28=Xm=9W5rdDlbd0LQ|p?I#TRD5^Z<$qdoJVj*nAZE5=wQE?KxMSCf^Un z@)A|+VR4lvuDYB7;%EQC7u3b3LlqEo@;~&Na1>40dk=amAVMGs-aniIMq-jUVA)7q zoQ&{Tjw$NhX77p1vth|oH`+504zG?EQb`YU2uM@GIXaf4(&iAxjD&++vIQO-_)LDS zD21AL0l46gA8Ks%r9rMIaFw_Rw|A2d`C%8&`70fOg@+9BL_A~NESL`!{mHd;)jsg= z4&Zd|H*K~YgyJU`@%^QlBr&KGs;54t7IJaetNod{%J!p%OASO1l#n?EQfM2biM#hW zqN!vf`SAJ$dAM?b6dWzaZG(Tw4@Q-ei9Ctk=Ptr7rARbx&_E|YD@z<%LdGO>9h zZ<+LS2=keST|6QEnib>RDFYLbyPMBk1nfSqI2q;Rf75TrMS<+B$ElK%X!TPbPkz_} zc{yhw?y4$R!JUDF1sh;P(X`s0#5K6l!vwMlVu3o$L-D#$`drfny#_egTPzN*LyS@Q zr54=0stENTW`I`V2yVL(f|{%SiEY(k(5qpd>Z2#I*h>qK?P`T@9(<4@n}SJiUH*`va5#HmehlGLp>>OW?2iQq)Q{(#hiUy4W5y0iPT-BOb|F zgjX1jU*Ao{>;KuqJnd1kZDtASOKgDqPm6i(UBij+?ney|ZIs(YuAKlc?)Dm3HbK%^%T2d`Gi{&S$WB5BEI%IhYy7u*R^L7Hb zq`INv_ay8%D-VCFt7)xaJ1n33ksPn|#BG+|5Im1S2%3X?-*VVi_kzAJDJSX0WiYeV zg4?q}AG7t_FtM);{Fqb1JmCU3u6KkI(-@MlW(FpT-6ac`n!q-d2Aulw4$OX04!`(b z(Zx|`nF~deHtJ2L9c}s`TrEoY&N|VU+(*PTDj4`ZGs)|yKsfFbOt1cqh5xoDfmFCL zJ*C0s>)}~6U33S;x47Xki=Xt=i~%ekOGnAHR&Wwl1;^FR#)YG~$rgCR&M*?qI%2gJ;PZ#xaa{boH`!?Hg+9?3zm z!gaX4bQKsMFQ(I2-fiN2RdD{ShuKl{Y4n~8Slm7l=l3n+!dO1Lhu(s{s?`SjMlQ09 zvmyBwe-~{tgXqo5mH0R25GY^G!sMWfv?zH3xEQef5Btc#gMl*4fv|>Y)fc#z*nDMIR@zrIImKS3-P> zE;*)Y46zDV>FVK5($-o4_I}}Lvv)a&DazvRi#{N+>=zgLj?E78J9$oX2XVE<4xGN- z7jK3bVf@b?5^*2{?Ae~+KV>UA%~BrY9IDA_?d!bgm=_SdWC2y%&dv{EFUjT2tf%3; z8j}9Z=Z${2Q9G|}GJXl#3Hq#asUe$&4@(x|sAwKGH66zq7a7PY&BTeXg5c!GS@?eF zD=j#88=crK`9Yy4KHl<>{CqeWukT4lzl(x^E_pa6!`zuBKe5M39vUy3@Y1A~V??|a zKE!CW`00-y*`EK)bx90xIKatCm(lGj|AUY5`aE)^09FXU;w%?HuEFe|Z~KjmRo#11f^=Xj2pqg1LNrvg0RlXIc3eElQ8N zHIQU`Uv{rLjI-mD5D(Zwce*t_Gv<~KCjV1N~m)S*B89hvLK+FnbJcRYYAjUn*Kt=Olh<+LgMbcX^UNsv}v>c)5 z%BEoJ<3xD2o?zpgbUGL2<4=LRG|=-d7{4DOybNpJvzJ?m-Torj5blSfv%GjK(^SB< zEDf7R{W0;U5_Q_|hccbF;G=#d3N4L6g&7ygZn?eW+04Ua-b@uT#tX$roog&p)ktRL z6o6;o2DG`T1~ZiOxP42T5SBbZNAXK=Lm-#Cb2pNH5}yx8xBn&Gt2p$%{0v8Vf9Ryw zzj%H3{&MC-73i5*>`F_AIpeE9`H~BnxY-}Bv#!nt#lgQVF;>~_22}~4_rDsU}D(_WU>Cvh58zh zSM{OqCaAzwsWK|8or^7JGw{&-1#q~$8g!RC@yIS|`0{oiZ<2NroRQF_9=lXwhd?m- z)tL_FRy(-}o&+6Q*uv{z9$quwH@pisE5T!DFr)?~W1Z|hn&5i`mq+j$N+b!_MOanS zppG`=Y-AZf^ng4kD0dAm{aZ?x)-vydfGE_9 z4}i_CL@@SMq`&tsg!=+2(Br0u|2j|Oj5T#+@hLwtapQFSAu$!w&#B@!y_xt!^wJ9N5M!$LnMDSe!T=SKT;89qk>U>xT*y!WfuGFCvx0<#6BG z8G|;Ppr^6A-N$z}zk~X`W;`t+k1RDW;Ba zvu_K|h@6CqLpRawV+~4aUdMf224wG^J?Ju{7p~Z)<4UhOxU#5EdUr^9S7ZAiTSjgy*d2>N$eX6HjPtP3*$haxXJer`gYnL-*b z&r^c;h3^CJ*O^VQrE(4MSy+R4^EF(X-HkO97z@ey4LU!yK*!5AI78hKHj4K?`Ni^z3^4t@MK?oph)Bx1WUr9b+^>u#42*WA7rdm00AkjvP9`k6ESl zD4Mky+iEX>-^YyzKc4CoF^2c-WTDz%c=9> zS8^v*U;RZz{$p8z=~A`p4k%-e>~re&hwUHc7%;BdC47H37IYY2TI}|F+Ea89q>iuV zE=H~e@3R#Uc-9@3KT#y0atCf(1c0M!K9tr!h0hQM%jGrEZ0>C`!CDPEJ?yaDw-mpv z-3B?!jzRI?CTRL=hD8UGnIj_>qprROw>!;fKO+FmOJcEc`Ai6Z$!4O-e<|@)0@tc) zdO~zQhV7QcStAkn@wqb<dyjTWc%c z-f+PCzuyye#sRAOpbYPA_rv#T^04Kz5adn&%7uMVq?e*&K>P7Ql#;w&OUz^8VbKem zB>tSpyfa4GXTIopYd+qv+mFF4A69!KpHthv8eTMS$5)Qm;l!Q@+&S!pL(vnUd~z@2 z3*~~O%2JH5lma#3R>~)Cg;9^3Ai>myXr16NwxyYPk7i>0vld)?tO=bG5v2NF5}7wu z^xKY3+|Oo;f{Ti9pe&lUuaJa?Y>of!`*Jw>XNY7?(Suf>3%m)(ve@%w5jyRxff{xP z`dc*>f>sIRjXr+EeTmFn9Xi3#J#sw?EGh))1?|*6|0LGzjbUBOD3l7EqrdQa64(3H z49CM|Az|1GYcuYUfs@8KZ^0QlZuON2C7j@W@)#xK-Z+r>~>}5S=|0oDZbcXoePudpA_{o!Z*jx?Mi zrVM_}MW~uC0YS-^*vxw#JHPXYlHwKGu_CY9nokMu{tZW=iafkq#Lo4f2grQZk8=L1 z3K1QdXlyeBgvKT5y}^30I?1w?7(ku~^62FfHyRjSVI1p?B=b@k7SA}0KZNAbP$(J6cYZpRadqTo^n?6Q0X(oR7`*w{ zlS3^psp*Ftc(tvAE*ndQ(4RqcsssligQ295)!{Lz3@zGU|E> zg=#`j>8vMI>Ndf>?pZ`-vJTHKu#VUm`@tzy5ynG3jHw!T;h=*btQ`NzyCoWiw-;2w z%JX@2T8uh8)xLnjK}zsUSqD}w42Qc9dbxdxi|M)D-OD?W-xHe z4~@k~@$v2uw6Qyl?$#N+18h!GWLp3>^E%v=wu2HCqi$irXd@&8 zoyGY$_vtt}E3g$~FOTAx(sj6hu>&~#8sq(zSqBpw?a(I1i}X1x!sH=7Sp6*n#bz#p zse((W^y$+uDti^C|K%q^=6iUh`tIPe=o;y4U(K_OS%5cxHGtz3G`PKUDeT&Hj<=93OV<)8-1Ej>&QDEh+9AuU>GaT0b;d#i6S`KLif zbwAy*`YZiL@~PY_PdZ1en0)-Xix)U(gT96n>#YB@k8L~&FKrEQziK&bXMFZ;-3zJM^LBiAObeY~h}Fs4AI6k>x9Ncu7l?UP z84Y-u4w=r$T!cIGR9^JP__fR-F_aFG+4pKinET3cO*n9EOQF_V06)+OnD1Rm4d;l| z6^_+njKg0H`ecNQ{wszr&6V85Q=e(mcXRx9yAqZP--FuLsko-%3nb*$fRkMngk+n7 z&ypiJt{DPXtkgKOk%zo=^Lw~EIvo|t?XljTvK(+AoF5y;+8gs2b1(>>KHEbyeU{^3 zwg5_OIFF_SC5(3*&iHPpNS1y(Zr$|;?*^;XMS9M{TMMno9;0(O_&1;G3dDj^x*4oL zs|wewZu5S6C_~TdN;n-{0_S2g@PT*}h#Ajrlqnp_;n>;AX9k`j!)EOt>(3*M(q>_fvB3>@!*vowpP6jv_3$F;HJQQ_ZS-UP{5-_J#b`YJFHK5`hc|P>xQn*$@nvKK zjuxDNc$eSALE4un=2@Wfz)#*Q-CjIq9}CB3&gZ_isvDescaNw(VlJxXo>c#d6MBi& z;*934tS1%?tEWZdH@gBHICDmSnTTMWybj`u-)~5!(v+TIdFsl{>dYeOPD`e#n+cQ9^Y$g3L;}mG`$pZEZ!uxSmWO@EI%>8qPTvQe3 z6xS9Z_st1zec-{NJsOayS;1x_t>F7K0$-|gu(qcZ1}mlT?!F+9eer~V_!c6XJqtY3 zufaXfRygm=R~IY8@`>*(@o>^1*gUWS^}icK^X*{#dW5;ZH!=_OfIohTp2t{^m28*Q zi>D_ZgL@K>=uRyI))~A=;`H2sa|yp ziOSa8sbP0sd42#flvxk;O8g{c>=-#QqE9x{0yvSmlGk}ph(3}l#+@tGP`;#*7a4s4AKe(k{T*hI zUOElu9KHqLU! z>3w(RAy^Cj-iCOP&j)^Pc|0z|=NUR3QLm3-o3_?asFU~aFE3yJ&S7Mjw^!SzHQ+>tE+O|5d)AKgPw z?RW+G<|;4}+mBbDf5q4K-|5A#;#gu`0qtw1(9~5KJjV$a$-3GZG~~q+kYydWk8g5_ zh-wa=@i|GJp7bP@`*Z1}2SraY$`nd z^n_fjeabT*cB3_AS=j#h6R&k9A9ZteB%_~CbH86l(&kWW%#u)|`m`F2>4R-*qzvlGYAvbMXb)ln65Q;2R7U$$)aZ24WIw0OPtl(C2pp zd9m;`ng%*hp>MC~=-h0c%i*n%?Vbz?wrm!>dNn}{QP}hMFl>7D2=*o>(b}R)Smt61 z_D+|ey2B6su60u9v7TDK_^sseWoy`_GDfRrd!f~RKPr0qH9Gbxz+R$G?lh0`+H#Wc z*2!?NIdTCWb!6Zqqb4~2qZ$3Y_2}bNeGI!(N_~G`2UxVQcJUo6+L5Y6rC04l3(0?Q zz3u`&cJqgrQE&J)m*3D?j|UPmjw~P2TN{i{L>jvxHs~fu=3XGyTnlZV^#Y5wdgI-m zL!4cwDGsgm0>kGS>^nBWGn;Mos||l5?rXv5+l-TH~kP5RU#PM$se06^g3-kIQ`;#nV>lM;JsS@~v<$^9xIfqHSL0*f{D|+tbQ7X17 z77xUQk)!MO<8E1fx^=?}X#RQyrWpMrZ$3{nRMp;uyDky%bj}6Yr&;huxsdGaVmbIe zI}m=X!5MuYAT!P|C*bNOQ2KiY%&LsVKB|EI*;zz=qcEoGM1fqbAD9e(<`m-v^?keV z*Y32QQWskl&AX|P2=0;xA#KqWx}Q&w48L27KTdkUjuxZ}Pk+G&%_HzK?kX;bI#0Qt zOq}q>15WK+4LtSJkYAw9+wfhWZh@;8T4pgOOT-wSJKm0|LUG)wKZBTGOo{w63zR-2 z10S|bgZa}Z*9pBkkMDndqgw^`z<(qLfMx)`B^&P0nwe(M z`t>~3*&0YJmo0~_-h=lxt5FZ>6zXJUf=#M&Fu3zAH!4~JVuhoS z!aO4qLQA3HX%aYRw866A64DVTV))L?1r>h|(j2Qs47giJJHNM*2MZ%{`|5OByIFv% z%ua?`&XWzp+`2ehvCX)XpLwVbc*3&H>F}oVK6RHn20PU$>ohWs|K*uzWM6>JJG*%r zT~So9Wj(x@u0}OfhluX<LdZHy@g4;S5(;vqJ;paL(@MN4gvo;%?>^n}f?jPlz z+_=DHv5s0xh&J!=^Ho%Zb+TgC^N<3xXK`on&DHR!l`SRU~0-`VtXY5Gv*$r>rci} zHHTpC-Kl;QGiK+nVl6gXoQy*)^B^W)21V{TV_C{IdL!46D}4B!Y;{`(*-K-X_s$R< z=6Pew_ZM7YR3gz(xr{=R28?SqLZ%NzgWUuju-tEfr(`#STJ#-qX6k+LITwnXj?c!! zRlkV{{w5jX9ytA`J1#!uNG61?gq_byfT~S`ZE@*T{}$`nWebpD-x0dkiFs;&-^2_f z1uEa1MYWG8!hY?8)F_CMsOxN|ZL|`?V@EOf>;Q;H>CiV{2)4Q(qgTqsYR~6eC;%kj_89d907$-UH= zjA_?a!8MkV_w(?K0GA1+u~fTzmYXDs%C zjt9FT>0-apmM~cK{VRQyl7;!(*J4HY0RF1=)_*S(g|C<@Q9r>SS8qw>aVJVawl$5a ziMZffwJSt5Asd%|RUpc7f4RS=_C&O!6c&{{hk=x>wU6FKpvKQIjF@ z5924nV>HLW*J&dG+QxcgYupDv=?V;h_O>)>o1~+f3LA$I!bo20ayfn2NkIb`S zY!fw7{?>tgHqlfaIqE_JVrR?mj z%s5D6+dy|pIyo~^L`PSj!~2`{@y6&iY+P4{_Wrv_WaJ4__t6fne{^7X;V^W$zZ;B( z4|7R=8DJQw0sC79@c8l+=B%B~MM~U)f+y-YDYP3Zl}k{4L>+d{j)%~ln>qe1g0Rcz z7rhZ^3{mEDfakH1cFz#Njmsy|@XXU7UZ@7$6BV$|H6PPMilIDK1q=2brW5(*Lw z_+ZI@U?3U`CH3oo&J2a7YCg=rzXP?hHE3@_70PKYq};Xr*!N8n>k2FAW);4=JwXdV zqGGuAsM8uU3!{kZ*>t#aXcWad5}@g#7XCN;4efZ%!^}Iq_*yFgx2f8L@9B0l-q?@J z&mM;dQL<9|5z)%sg}pE4p-S03NR0kVmy1_XciRY3?{)$C z{srKJk863F`@-?uHCuSHLkd?gp3bt}WmNZ%6U)hkVw07G;mw*_x>Ya}O?s^H=%b@> zS>_AGRa2;*7lTvQ=D?VjEbQxkLlbpIsK)gjc)x*IJqJ!rwyew->4 z3R85(;nEOAMLTv^&8|bamviuXL?C^*rjWVLvhhj8F&ME8L;s)0VDCj&mi^;H`FSPa zovHy_f4rcRq$z0jg<;C|Z$#?i5nLBgOiBbP;`cEwBAIa-7Xc0xI-|T=4j7~i%MT}ghRFcFriZ!zVna5YIVjM-Dn9Ok^F`= z%c4-D%bWYL-~^nqjKxZ8JBa%;4Wd@%!>6uUwJ-NrqiKROW3XQ(jhp__ad}tV*{4Tj zMb;7L$F1l$X8|mk^@gZxYeL_Ho3tlE5({#&;o9XAmf4Tu^>#4k(38b@S}>0>M{LkF z@CR)n-*~P^VraWQV;uKM)2ibKVA`5V5G@!AcIVb}Upp5(vYQ)`Z5?P!(1oG+!VE@NRl+l`q?^X_gDepCSY+Y`-|WHwf2;OJmuD7u0Kd5_M+z z<9$MTIIDF!nS9HQ+xUD2W{=({6W7edD`C0l-N$-TubW# zTZdI^Yf!nmpIYjg|YDS$2N$KhMGk= zp|pGV3>c2TNoKh&2FGV3^usw__*6E+sr4J7u?gch4vjv_0bxUXD76m*bd|?TsY9sBh&Btyqa%NjQ0UK@>(LCqhF0J))8Ogevo$CF_os z!aR>n#BGx?v}#>NzVpmG_Tmk67;l5lrbrAo>jDXbK(H)og9lrZL1Rc5j~rwS)X!z) zcKvVaW4eO|3f@1@?;>@UGQ>$ zIx4mXg5U%<%)j0P_rA8l`R!dWx-|!XC^o_-X~vTl@nadVOgz-T6-NB0fT{l)bQG)Q zNxwptFAw9T24vvXxdTw3`I;Q(&cgEbi6Ef#0uS285dYE3=#VdsgBPMv+dh-bEbT=T z?{%E~?K+;j-T>!SmVv7(8o7ri*YW9%R6HS@fcvV0ar@b`bd6X%V_2`HW>b{NulgSR zxlxcPu1(?fZ@U7u*6ygxmkQ~f$%NLw?*ROQvKSR$^K{^X#yDvj;L#`#X0-1PG0se4onuq-do=Ab#o~f z{MQ9H&lbW#m#;K2v;b7n%5j-!CF~VBhZ2ek&^c6%4KC$8vshj55fuTC?_JpNNDTAe zY=WKTQ^DAEJ!nkWibp0bg=OEGh)A;-W~vT@(5WmE<$MxXy%k5_n0l0UE5iQw%oRT; z8+C5qrq2Y-L0iY3ylmD3i@X8cwp^CVB^ZFIP8bSRr9gyLDm5I|1kWX{#4)p+Tc`I7 z6jwNrgJ0T6qx4PqAmj_4-<{B9jtd+RS0n2OT=9yK2)#RH1Gr0cLsEAN*3Uc!0yeQA zKF^%C>E%$3BI~uj*I#F827dVdqwo7pVk(7Mgo&1n4Xum_vbd^B6B@`Qn-SKYc0aVI8 zqc1e53Ci`##NqNgGH26#-1H)db4hE!g=U*EZs~cv`!EHzD*dD11!7SC18G(%^XRRr<)alJPLhX?4UATxD?pThw3R*QiGF`d}%y_N6%VS~kGx^e8A%N~6b( zNu{LnktKSvOM4cRh>Y$$l>iqq|?DfDu+9-J9VgKuFs;j}jM zvvYdjHlrO*LPIhO>p?y%LLKh(Ud0DN}pLH}7}a2b2V)v2wamc{q6&&3XY z*T>+D*j&u3`AMNK2)E`f;!ZxC3i^ky;_uh%;FbSInBKVtjnme^sly&*y+=0A*6rX< zI53}O{v!6jT!%f(0Y3UWn)W5n!@zw(xapW1UUj^JUyt2EKg)1*Tzi`3qb`E%k5-KR z?vJwnCDACOWkmH)4_PTZ0fp1fV%x1a*kD=#uhU|9TE3~c>*{N+Cg1`(xZNk#GnO-U z-c73N#vJ0S_`&?md5k=*2`N1RXwvZ$vZh*6g_%4|-Ta*hFmB^2(R9+kwF@8R&K$eYc7 zh`VAT0}^i8=zc{T<93|}ud!@AxiTEI_?DoNZ6){mN-u2LE()8*>|q7tKGmEmB4Z*q zN$Z>*5LO6h&Ykm6)RsUd-z9K~z61GMp9-6aJOp70c1xnGh%^P(M z_fR{>Qer5#AM6iBuwCYKxR6#z4qx7KmZjL_$)&J(neD8%bABxcZ!aU^8 z?7|l+jVyy>P9igYkTqK}@x#IG&~4}gOM(o+z;xWzv3j?mq5j| z5%S*JoOlRUVTYFuY%r@L(`5oN3Q7u&$XNqgyszdLZoi#CiJucMw1Md+x@ z1v<6o0?g_EPMiI-0iUJ7*RsWowHiy^Q6C&{jgz)%k-+z|5l=N)z6x91b1c4R@L>P+w#m#plS;l*uA^PpJ*odTuAd zyX$bIJrRVW^T^}RuQ7MkFJj@`Nh_x|l2u(V$=v8R40Rj;#jzM{Y3_ovRV%2{Ke^f{ zR|R~f^#r#WJt4bM1_Lq*Ahf=e$~;Me99{%Ac5dTNe{Z9Pi)?WxW5orvT47SZA+*P> zC(lk_B#Nd*ocdTAF7>$&!@KHGrZ@vdSA8T$x0k||I@Xn}mB)C$+*KW3stIn@TF}t&EPL&S>_HL{&baB+<6E;r)2^NKfs1wfjUDr`zi6< z1)?mIB<6jPY;&tcuc9HkLM#x9jWY0AtReUmW`Yw1-pKE@aPZVyvb^jXDrt9;)gxE& z&=)J>i-yHRBbG%1;~>H z`9JBK$>(upUj<63CUD0C$1$>mF^1+=Qmc)5=yLH6M1Egu5c=&PI*YsEe`}Izx5Q_G zi_kTQx?2FTAA*2PT8cV((%7VP9jnFuGd#VCgH=9Px6=_|snrGh z+C|{}q;=#`fCsMl>r7w$?&U%fk0bx*jkMgm7s5oWFnNM3YTP--D>FU-p1ZzMF!{v^ z=AVIMIszS_;Ga`BVG2TMW4!WRnwoIkB^*9QF=uhk8rbpf27FYX0*Xu6tS;YUv*V(4~hkbW`?I#TkeYeX(QPP+vaqe;LG`ary? zG*@@13l`rQBlU4h3H;Q6jHgcIrdlVdYzPAjhx-^+p+G-9Uu@t&{ODd@9_;=lTIZ6f z4`&>@@cpD#NIxS58~&Jq*ym34oq7TF_w5f%!D=xv`^S4=6@)A{Kybo)Q zK#&wJ$HBNX7;{PhyPJ+ArdEhCEyGA&O$kKrVqWchA5@K44`Lk#FeocXTyIQ4J8@;2 z^{N?LCi!5wNExau-;bNFPQy0wcDv7@^No0TC&IY=d3;dw zpA2~I+>hd3mr?di0gks6V_WxogPzBsFk{Akd~x?4nYigH`6846Yv;P4&6N2F%n{JE z_5z5pd$78$H-28F2m#aAp_$Mj9G{ztH^a2h^-CQl%s-C+DXR~5$=grS_^IN{r7 zM_;qE|KP@UvSH;W?E7?*YD6~Cz7y+ED^B@Z;W1m>KXWcEd!h*13T4E9P7=9wEfN>l zDB_B@?TFd3@z#k6b=<_wfWdfOLj^>R1z$*-jTNnYHI2hLEa%E5|?tJs`jiCKHr zfYiKD5FOVc+g5~7ca2r__%yaJ9CJrsmuaX|Cqt{JzK0UVQ>eOhhV1;gC;rx#V!nS`6b~w~v^HCqTa()sy@16jMwuW=E{ps+(pwjT zab1ipoM&g5%3e9z@H7+;b8~7}fjtH==B$`5+xtv80@iGj#9KKV``<}o=7@WZ$2%W* zZE%Yue9OoG?kD5EB-ZPast2dvKn`-tpvCkCoQ%57bJsN`ew)9-_P3#UPND$X+gl+* zJrnZWqsVr-Cvbk*JZKqDAZD8{V%_BycE^Z=Z@Ld@+bciptq@lg26TQ^vaA|+}r) zXE*F@&vharO2f)%4=RyLLz=()eLwHdKc0u@v**MLvpOzM0 zro8^_ysjY+11-04$=!UE`yGkX#S+My8)C%0B_B)$vZxtz7xnz3G4rt(j<^p zOeYTY4m7n;lzXmK7r71Jv3W`}wx4@PF4VIc(NuPi_BV;dj$c6D6P7He=L@QKa-Pgj4vm1D)&+S>MXjzz>}L^SptQ94bb7;&$P@iCC#F( zG@XALcu1zuy`GAsD(V70?z{-LKkXo4t2w!s=fm(vvfT4dckY<0GvwV&!%uNvsXUA# zZ{j1I^r|GODT|;$4dJd{D%?DL1Pk0HaE*{JWBDNu{q?%Qs%bt5z3zbLUtHn%!#!zch*Mmq)5v&H9G^G~N+E)%s<4tAc1#6_#cQA|vJi$s2cg0_g6$@j z1NTGSApg7s%{^5h+8!ZhqYa#YuS!0jQv#=eB3PWq9?$M@wCoY8i<{Ylcg0RYM`=DP zuYOGqwCq6tNBpCAFv%g zN-QonNDj(^pAtzu!0!|9N&lJ(P>M>0AvspZU^|z#cW2S0yGvoGyCoz>2BY7+M&J*q zWIb4RxW=mhF1jG>%gL!-d)^JRYz@JH^`kBNBMgc>!H_h!0KbQ}qh#+DoK#+j`x=tL zHGr3z_=sw>TBefZL@s$ET?uOZJ8|}dOSEf29ZB6+gf0UH#Oq5Z$}T0h5-R5485oM|{3alMb|=w&I3KM&E`Zvk6UHjn;ON0zXjJ;m zRTO+HA$8bJE$*-o+g zYP@tx5))6Cf%))XT%9OcH-Bq92AS}}_^k$vT%`h!X3qujha8f?W(4A1^#f0H78chE zl7hTVc;E%4K^kvJl+9Cvry|o)iuEk#@Tx=1hD=z;d zoVR!J$gf6GxOo@-v(_IBj2K)vJq6FGKZZHhFY)1?twj2BJ&l+2r*GVmuD&yg10Q2? zkr!Zv{&7-t<~$DhRB>bTmmpU?0DEIAuxzLfSBbHlhioDEeLxlO+K0fZ<1sX3%YB%i z-3n)})MFj(q!%=i`b}p*@oX!K{~pFQGpq5ez#PbwX8Rioi&6dTP7t`ni>)rV&}>Z} zCin)Eo+e&mTgdvVl9w^ZJOe1+&&J}{*<|Kl3)HYKr{f*7z{5)suD8oVN%eZ1LtOEJ0!}$GO5{50!Ca1rvf?L&YQapbzJb)a~+FZ%~{Y(@KGp{nukAvW)L_*D_ zDo3(VVg$ys`Or1wG^89TfVHaDcp=mirpeXdZ!23^dm$LQKksMnQ$B3Yd6;-8Jb+IT zHe}znI_A!t2f$27VRm&HJbSqi*33FZc9jR9PK6atwSNd3g$|PI3pH?u%MW5K_YQM* z=fdqnDJZGTYGlV_shzhn*k8?KXP`b{l-z;E#Tm4>Z-PGc%BMw7pAw$9Q8dVVOdc$m z!4;P3g?Yya2v55~-Df7j$?Yp>_u+Uvd-kn{^sznA^0JnR)u?B?PLIIg(NtI_;En=a zC9qdn6*~Q5p(pYk8nc>Zb;NbL*kuPWtI|mG3^`CcU&`j+6X}S_GAufA5&Z2hLZE3U z*42u@j2+UDo5E_r&$3WrS{D&X3xlLtc~CoD60hrQB7(-6xTvv${8=+V_^%6LjOZN* z3Mzwd@@yx82}P@8ejsMP2b0fbgXq6QpzBZ%*{WB$2j4YNjRa$0p8BD!`CF$$ zu5Xv5Yg64JN9G>3eYlI$rw4*uxg!q6xxv*>B`{&qLEA$vq4ZRsjS)Iv|0NSR+Y+%P z>^A7^DkH7b4_6gWqdPrW&&EM@*um=nX0pDlzhEQsDI}7Q%tNBjdLmSw+-Gz76?BiH zAxzpnrhN(4@Ik5q%l`5@@4y6z8S*M>t1?m)h!VHl?+F}DHAQVbW-qxGnRpz+5?MlsG#o18u=^p9& z%l1ATXhPwp1axTL46%DsFOA{l^jbciZt|ge!%2|(0>DZ`4YjtuCqbqI=!|ifx;u1;Ng^;Wyhi@~s%&CwxY2HuEJ^zmwLa z13FmiGsd_1HI@1*@j*@`hO@Z=>dj`Lc)Rf%+j*iCCZiq(i{UVn&#V{=rc$R{BAJTA9TyTtKdh!NCtU(W1Tv`U<;d7vAJRIK6 zassJ#b12`M0q-Z1@U@FGbq*<{kL%Y`_1JK*|Hkq%W2b@RrC+rbz4KsO_&zRka~ld- z>_cfk(b_)keoQIJ0#A)hIuYs*l6RB9`(-L>S2Un)nFKU6-5?WdHbKYy1CX^Z8d_{i z@!!f$jOY-@mWU#}&(50K6Z1&k_173lqH*p#ODIm8hV`?uK>DW}Xb3Ce(Is5GmAaA$ zCoV%7J+@yy{tFFXIu|%gr;<_u8QNLuEAhvJX`rM&l2?zr@|Kn^p_hfT?dTxh8)eywrm*@?RC1zp;_l8k_*5 zpQ3d>%iXB!BmAG}_Hrv(qit^u(N1X(Dj4fi5 z|0&V*A3aY!T~k4OY?S3>_ToTV9bOVVNn(d;YwZ>pgOk!_-1;dJ_sS&WuJzVrU^Sby zFx`i{cj&_CxDqnvhv2} zeJbudIszFjowR=Z4Bibo4<)hDR7t}fD^lyZXJzzJ=a~=kWW~V#-VD%_sfXp<6u74o ziRxWf(CNXZTFb5^X3Ot)C{W%=SJ-T!8yjWG=nIy^IWr6@0&_uLK80qSegQManjv9- z4LFp|#|!a~>F)1`$R5+{@app>h#Q=adh1rf9+p>nj?H>x{A&S6hh%1T$x3`3UXOp( zVu)~?dhK1gFnnLicC~*{$7W|!B&P4Eer_%}nhUV(1vwnLIEc1gtr){*j5B;@SIwK- z1|@b4_{ZA`mlM{5k(h?AOT}Sx!U8zAxDGbTvy$AoQ;0)s)}<>S%p@Y<;czdm-x|)& zcD%7+`3{&IE5P%Qq+rD_dKy!5Nb%Q7mEGi@IJoQ#IrBo8BzNt_1nSvfBjd8d~)H8pKtf!WuhPtKQxV z26I+p?X?=PHC~UqQ$LZ+&xX(?`8VJ599wZg_)XBVx<>d&2i%R|1I^q~#!JZuUR1PFL1`hF`~EJxdzR0h$1yP7 zB3>uD;{<$K_owQ^<-6paq!ruyyMgUWYz5b$By{Z(K;KQN=(lT}s8lY8YbS30O(7yf^&>+Sial&u2Bk!c~6c>%asy3!C9q%frAk zu>}g0N-=ImJDu*f4^y2)84s5Nvg_|Xs@>3!%i{__dsqpr=WeCE3ub{qVJ9rLSLU|g zn+iPUMfx?_H7=y@O3@<<}QPk>c#kcaSMJ= zxI%kw_Tak}tr{ISnyF#*S@3XL0Gi4>A!U&`9*nM{56))bt^@s0xz-4>TiAP@q%RH> z$-+Yu4$!h8Y&mp+ejkz{OtK9gu~VQ0-gaoQx*r~u#DM+3mFV!(6m}#v(vSVFFx#e& zvG0k2P1anr)()qt`IpFf#svz}a`DUkcSN~+E+nwm=r+R)EX#=D3ZC-Ddp6r}=Yatj zJC_3MO`~b!KpPxP6es!}J1LKwB4*561ogv(@Kh%q&MUKP-BXFwchCd4LC@(~HxH2f zd>3aZTw?c`2nBH;$c00Z$tA6Lfq>u@O6 zC$id>_$aaux(d@U+CCJYs{64_M;-7yTgappvEUnqmk{#KCq=^}lzWD0IF)u84z2kDg6BjmJ=GS=l^=f=I3#~)M2xt)bP#M!qU zd7r4$FAqhb(1OdP?;e4vY%XEkI}G~w&jEq?=kRvrJUpzxa?H$X$lGU8tarrN03sH8nFTrqC!H2(2)rA2e%p(ArINe*WFH+II*ffpPc-*XO*&RtAJb=du2(Omp^ zkPp|LbH{1I16*G`5A2wj7{nc?wmI48s(Od6DKo)|5q8#<`j4H@Er%VDfnMAlC_E=0 zZw{6qBc}^xqlMgs5rN1*Jq(r4sbQqkXR7k64Y%>fLhVOB(8xRm%ir~acieKef2oDJ z{?d|3U6_sUj-Di|g;beMcJ(wh>;Q@_Euk4-uD}fs1tQUEiE6c*p-_1TvuxW#aC>$f z;0F(B8p(u&&Oxfo>&I}*cp$J#jLKRjQe~%`^yHj0D)A-}_B`QXjBl$%PPprno!X5B+GdokvtH+&fInY?L6c_)Sjjz7M z;nUY9IMA>TyBw0a&y5&T9aMx~>psw?prqX;y11ffa(5t>mvgjL5{xH58u z^wyi~nia3_;<8!JQ2t^EmPW0j3Y=WpA6n}g~pkcX-kpMY!Ib3UIFKYIbfBUTPu560sj~W5&unt z%-{L!9zDFM)+sh0(DfA!yPSb?GV7r))sl3!8bQfnDO^xXaOpx5`tFnhw)Q;5H|*by zV_X%?7_Xsn#_J!N!>SH1C`-muF|G#c8}GDM}FPF<3lF6 z+(-ZyoBPA(a$Tx15dw2fPl2!CbadbrSa^{T4!@p{fX`b&n9g#R zBDVUXn*JSRir&)8(NUCN0Kl86#FA@Ap}lqq&F6xaMse>98`g}?vtJH^6WnLxyYQ`s&?M>Lb(k7C7B>-I;)z^yGA z%-Jt?_$}fR-blGaZ-1-A*WRkQKsp(Ii-=O4T`JJb`;uynctM8HHfUVt1M4(5F>A!P zV_Ta!n?alnf(6B2R_wCt^c2z8+tLO`P`NRS?vRR;qt9(e$!4#0( z9ErD7L*X@juc3K;Ed;7&V($ZU>T!A_&g+Z=ovL|6(I$ZG*S>>%EJI4bkL6!HTnLfh z?a0dHE%>Nx5iG0?f}qt0Q2VSmR_}kf-{%cKVKO?0nz?cbNF23Cu}u*zY0&(CGE_v!G{gBOhhlwp9?N*`Z4i1Y7q z@yEMh7=Bhl%>Ud3%b(APaCQK($i4*Yi(|Q2RTY?jK#AmTZHMg|59mNpE(pFO^nLqu z@?%yBwK?tssp>_z>jTT$sS2P92N7?U!Kb#RQC9 z>q?#s6vNE4{dhk$3uN!`)``0e;<25+V7N399`8{_%U8>oKNIbsHkV~Cr|X~`Zzwcw zNaxOTjHD)mUx|5KHSozr((SFmxSYK|ANp>@%+XH8UoQK=g*O1VDmj2*l|1l>{-C<4 zcfmK|D2~O*pvXl@$S_*U=GU*Xd+WW( zy62TLRUA79oX3BN{`WV`6ZZx^6p`C-=Y z7f5$#*JxA zC4NjaC6MC}ho!^7Z0$F)#K0J(Hrye~TJhN6tb`g}La zU#rJj?Za#j+CTEHGm;(}*oF=h3$W`>1AO711H;#L5gvCfsEu2XVe+xq_ox6%GZqrl zCm$K>T{obX{hv2QRgTs^ih$6?JBW*JA3gD09;Mhlud;U;9{JWoPaK~|9t|49Ix}a` zO}IfVUmSpVt-s`Jh(9bGlV-xNHF1rcufwMjKDbc69ED<>AauVZ%&RQK2ulH|zv@Uf z@Z`gUk5C=sehLTPsKb_ZZKUE<9j;F{#<1ts@Z>u`XkUnj!OJ;Vy*UF-l`q1j1U`J| z?}$ycuK0juY0U9h3eQ#Y=>73m^r*)|ylVA_TyideY(aNCxl$MVy~8yY?fZrv)*YnD z)RXme9D$zNb;Q4OK0Xrk=M(ecJ9x1NbH6Hs%Tfy*J!iyq+S81Kw{Bun-z)qBE-1ym zz1Orta^m($@UG1z(^gwE#)dCpqn9$5@|RGrl(}?;*b|b(+sLY#>3I6sEJ#~EogA5= zi#w)Fr%UT|aP^U?HFMhPVUczywCqixZO3#WeEAq^*}TVHt}U>4u?j{#4T8jbA5nC; z9{Q(9!uu6$guP=C%xYbR)0bR^^+7ji-YYw>(~IElUC4)4V-+AK`-|>BzJb`c3DhcU zvLh{9C7M2`5bn*kWut{kFf+FUYu+yd)5;=Jao!Q17;ooxZa#o+f68&>gc-Ml^8m#b zi3w|)tD0L_o|X{#&jU+Ghci~$yT?g~E5`s1kPOhh={`(|r@e2V1 z(G$*&u1*e)&h}Zn|2>te3j;X2R{C&hX{fSom^`8~ixC&U6Ku z<-?iwEP~@H62Pg558!MM_T?O&638ikd6rYH>d&bS3+IHWgmUbw{W$g&0i5;`AI?*$ zK+catJ{-}q0M7UG{+ys$zMRzsejJ{zKu+#>Fz4{S5Y7{MUru7RFDGh)ALn+lKd1O- z0H^GiFGo2ngj1C5!%405<4D}|;kXWlaK>Z&IOco(IftVIIWlv7Imc6iImvBm(3@LXNQUqz4SlFV-Sr1)adZ%u_X(FKe%2?Zj0`&Gy&+-0*3x`Y zw%Fx;KOO#1L@(98rIJHaQT)mZ;La8R=l3UxyoDbziZFSV|0iCpao(9?Vh*nM=KsBD{`)nVM!p4`WOV;?5y|MYSCPd@8xM=qHjWN5 zCoRreIg3T{it+C=U-$nP-hUovE%r1JcD8b{ws5i<=bb7hEa&cXM;AjY?#HkocsChYV3dBhdgnn=vB9u%)S(hsPGOFGp2xwgAdb? zFNv@FLxHcm3opCH!Z-ghdLnlLs2_GG3(9J+-KdMs*-%Z6%gC@mBwK7@%~S>V)3`=2 zM{vDK30iwC$LT5aurK{R3K}@!({;D8X`3Ie92TYfLTwnQ+bbbTrjncG7f2=U{y~?i zck#l@e`ss=2L@STa_znv#O-3(X%Ir4w0mgZkuEHjJ%YOkMS(V2VU|Rb^d}zO6wRLOLQe(o0nm0*;Ke?HIM47TY*dITiEW}Lf-T)MdOe@ z_$qt^=G3NO#KlS`T6-t8a=i_q+rmJ~@IJ}=w?KM}GM2x+g&`hYgaq@YjBko?OVx~qO{=*y(X|bS&CI0d+?T4CJl#zL361d zL^Hcv`b-0?Z=yDC$+p|crx{9p&WtZDItN-1g@5*q$o22QW*ph-G+;gj<@ zJjqjz=SIxPfw68(dlFM?tw))kzC}d(Min#thL1)qguz>}A2{qLuleq~IXH<4qvM(y zu;0QD9gF;k+6fATlLq!bdD&8QH3%^bX90Qh@xzx*%anls@)NkR*c)PYH^MFpDZDW`6?P2O z)AbK^Xwl!tkTh~1518J-1r-3Q7rM}Fb_s+wbfEmXM|f9yCrNqkiPHvdf=;9$4D+gB z+R@FBL6UG+-9dQk{unerh*4vk5$<8#dicbI){ZWZAq{mY*wGk9)sJV;gm0brbNUxD z^JYDWjopG{N*(Z*T_nG~+yK2>{^IFr@i=339}M2P4Ia-C#+N38H*vxFEp8w;ItLXB zUP1A}P82-r46Zsow0gTe{1|PfYxeoWrj?>#xXzD^@|1v(#$1rIFYb>x&imUb=)fAA4~2+)Yqoz7C|$ zZNvHZIx*$!d&X@L+>N3K;^(w7k#H3 zr^yCk#*IpPpVUiH5QO|2qz zy^fu5YM(C^Z25vAYLBtrT9k!cSkpsn0rhP0D0FjvhG#3iKz1aQ7zh;-ZR1c})oIW8 zd<;XC)Eju?UOIj*&I1R{Tl9qN5z@RI>1}xlv@`O<{?Rg6eZzysDm2jB2YT7-{1Ri- z9tH)c&VX)u3rUkTg~zW%>#`glpqd zwk>wm6yRI1!|1NpxOONTGweF?xz`2E=aZs)Z=7oi#dv6&0-J%a~|1?3ccqOsgVM>c9BVqN&d=15|JK^gmGgy_` zOB7f^WqbY*ef+r!hljS}JrOVP-teDuJq?d69YF2DK;pHA6}cDX;I}C|;aK@}TK#?; z;`++L(p?Jf)oSCi4feRYd4TcW@Pb4>On~8sZ?R~)E_|LQhuSg%bzkT)5^uVaRxBRJ z=7$RaK1<>&y_DJ<^Ln&PAK@~)uF+pv;eh;nAlh^lT#8`!L}{Kc@jo-jCev)1q0CK7Tg8P~FfByqY%;IVQQ#L1nQgf6 ztA{l@{B@6JRcLG*>nC%*SfKOfn|Ov@*OW^q0i7a4Uj;Q&W8WR%AA6q!$QOd!%y{l`hy>mckX(4v}I?uvjHnYIaX#BP` zlYVHp0#)q&HK>^_sil^<7Gj6?Hi)=z=ZkeQHAMGJ>kLnba++p4tn1w z!+Ndr_+9WExwy|1TqaI{>lt}sBDo(*ulm5uL_?H*xfA?Es&FdL5)@o?8+@)R!9j^< zn6$eZc&o3W`MnJ&VJ2CZ#0tYP7e1m^y*Bgs^?GbDXQ=)B40O7dg;jsB0@Ci@LU zbn?~3N=hbvXDj1y|1O>= z4FY}hZk)Y~g;43_!M?UAdWXk>>`m^3Jly9MoG&y2E;i*7OOo<&ia9 zbT2~b_QPDC$x0B9|4a{=^J?yu9>f_2+elYYdX1r5Dc+3`fx=IH_-aZDSnQC+#ZOm& zOiVCd%+E#1igw(){S}GUUWo@sU5P9?_6qZd1XG=fRVTk7mm_J~RurCHc^fJ57l<y*i60?JI-L;VGar4I6KHuJipAePz~lN}Jbdvm zS8t~g++l_O7gH^XI=p8OaRBQu#<~xat)x^dBU@L(Aah=z8*dvK-QSI$=aR70e4* z(5{{{DF2uST8+-I&nkdPnPNwljIhI}<@dOmm;tj-y9$KAQ^yM~H_ zIQb?ohBiV`(D|zu4=&gM!)woi$$<&7N+FZRMYO>4jMHej+XZ)DzX$s}w`i;p(j;&D z|8ZSpZ`KNZX$PMREXe0=K5T$>kh|y)cr4DMqauq*>Od`cmk+@FAaQgxEM-B7K*Rio zvAOOpRaSMv8^Hm1g@vIMdp6ShVQ;Z?a|(%B@typbA%g8WAK}Lg4NypQhDXlVNOqb$ z@j^siWALQp5cWJ(hD{xvm!5)13?3PH`Wl*zmQg+|0^ac#dTgS(bB z?1*n5joW9@Zx30Jyy{hUeI$=l78?;=IZG(g%!F|*Idbt`3N~6rFoy<2H8(jWgJ{Sk z^~k;t^`|d^IBRO}T14>mZ&5J!|s<`QyKJ@KtA;zsv?D_f1NZCAMwoGv7v4Hz9 z!^NA%7bXMW!*cGFV_tCk$X0mOl>+_6nLsy&fp|kQiQOWJIq`=;GqaawO*f$r|0Tea zB|DinOYPyz+eQ#lSPm<8d7{DPFuF0J?x&P8$N)V=4AE}u2i5iFt1UABz04#gVSh+v9M-C zdK78QeM>$uTDYfP4aYV-fvoLUL8EpD6gPY)1G08_+OZe@-Il@a=2xkoPdMgS2hl&{ zCg_;z2gV=k;iG*KUgGlw?U;P{%$k73SdEHr+h&x`GJ_Y#G(kFb050#!#ve=9D>4N6|(Y38kw$OL+HXJ&; z0PUk$u%F~XjNSHzHA?tFPP-!bo|6FGi^u7NEH7E>bsYxX<)L}WbhZEw;FZA)NO?93 z-Ft3BXV4EiIYkPWK4}A?m&4?D_W`(utHZ6Vf$Ju!Po>l+E}M?)+G1M2H5~qJg`MB}Kzg4qR+4>*kt?~kIhRbZj;_S&!AkDx(9s7`6mNgOP$#toje*u$n>|6XiWzi;=5@XR>GmKRr( zJx*SzX4-(g0X$$ebA*c9$$|ItTw4C)FA>j8rq>cq!J{iRpvHB<4aL=DTYU=B3!mr| zp)|6Ny{{L`T_&5JEg}DE^^oq_O@*%nQEBlBdgbmd+&-%d&R_aYE*)G1!_tW;VqMNX zo%9-J%axAtq|w;`0; z{|HBej6Gm@)e5&5zoVyo5*SzMzf}3ebo7|k&;5}UjeK`Mu_h>28uV^82B}{_v2PS@ zZW~c%Q5Mu#9>kMhp20lRG*Dl#6HoCZki{~otcjl;2nu9_dzXObPDOq?*Q5ztrB>0t z2eBBd#|x`OPou1e5uW=vfzQ;cKyT^@9 zi00dC@j$*$VRB9U=NVpkGGwU9n*8iH=lUH!uqOmfKl7xY4XP~yTbMsm*?IG?J+?kNgj{Vd;F z>oo&VU?i-$GOrYiCDN$y>P`^J2*5Lcy76J%Z&-R)5Z;@9rAw-3V*tCaR=q1gCmtTc zHHv;z?bKPEd47O=kG+I17fa*J`BCKUZ(XdluK9Aw8E{85u*q@a6<-IDA5OA+ ztSXQ!vL&*0)$rXs1H*;|s957NSRnWj#yIV8Qeqlz>rKFlzz1;U^Dv0r4kTC3yvC4t zeHg6C(&%oHhe~NbcwyX6uYXd5&V6;P8PXH?%rzsNM@eM&`iFF3!Fl%Hq(J|j+==RX zKDeOzBUF97g2I^>$$mBzP)Niz{S}JoOrAVg*(MLa`ijx%?{BaZNJI5zhu)Hh^aKmr>;%s+LumO_L-W^*ahIMf2hthM zUEyH_!eXrH_IMVm=&OO+%$G4(e+szGOT;4SQdlD44L#gI6k4zgn%za{Zo_jd9P2RMEQz>zSOP;u%i*h> z73e4SkRq?$OuT3$xX%rTStDM!Z^?FQcDaLTuQtJ_bPsGloreprbKy~U1sDd;#=evD zF~TLAtl86q%Pk0_BK-;0B_~25?+b9>Izb<2oFhwY4#BXpJiK1}5Pmhb({IAXkS6qv zzOhWkE5*HV<@{yT-4%(YpRNYGz6RB zu?_xcc0sJJZHqNY+|tc0pKyT}Dt92%@O1Jg4u5DHp?Q$-bcW7sRrrPGbJ~sO2MmN8Z7kNlTnyo zL>ijd5}lS_ax_Xo^WriixVz*t^JO9*s~0CxUBe&5<2oOhEoaNGdRF50+$an_u?*Bq zdFr~4KgJn`GAxL|3g-VOfFZk&sK4?8s|UWgv+fadEb1q@(Hle8b_9{-%0Y1UnH6X} zPNP`?H=!i+DTJvh!Va0Y;K&AuEETRn=W{-=y5f(CS^vk-dB;=zg>l>{WR{{d6cwVZ z`W5bZE=pQRQ3(xANh(ok841~Ylo3((Cb{Ri86hj$DJm6(1{G1M-}%G8UiiA-^PK1N z`Mf`S@Wk9qd@67NHyw6h`xs@cGuw*pK5vP;_eoH>^9ipg=%Q!d4H_zHjXq=ba4H^w z{YzqbBrBFQ*ud_@;gudM7eR2ZH0Z15qPtxueAJAB)G~F*xUd_&1V#C8D<<%hGxG6I zVkNPai-xOTYiQ5OE8whjnj|=J;QQd9!RLwWy?fvqXbNYc1I{P8Z4HopbUG~&@*+A0 z>F_AFj2M~cp|zY9=EvpHTJtCHRrn!_zLo0}wJBdps z)odW;$459bsy9F_j|;X{P2jK~3*T&`FyB5Hp5|P@Qe9@H%5XQKvquG&eGWg6&{?H(|7 zR0H13&jn@W^`OiE2aYVHt(LnH5<4_uQ<@Mr$owaqJ*9qGv7QEE1gwv&Btn_CoTx`gIw21+zFc?Sk;yUSaCl>y&t)pipTF9E&4dAwa zHd$f68I9x&Fr+w{Je60}YNaIogWM>HZ9ivc>Dsx52B9G-g&!Q=@Vurwx_-bfVy$NExQZt{peSsQ@Il)Z8J7X$Dce}ek6 zUbBLec(QBGT3Da9pDvv$3zaj%Vd>=>xGM6N(4XJwg+?b{%}_3C+)|~}j^9OxCMoWY z9*SAE>^U(|n5^;`l{d6C{Jck%BX6H4SEhrrtI$XUZ2Q3-VQp|=gH~mF(j}m)(~j%nN%)`Cvg|<@xEGA;^%EXfszA ztYczv@T)B@-+m8MJ-zYBat5gE4Z*|9Cu4D)ITUDxVfxQROx!acl=m_a{COWNjx>kN zB_9ngT|5tNoG=jEo`;SrCU6?XtJs;53DvG+7(30I?h$5X2V?h$+-w2Gf=yqC@f)zJSiDX5T!q;5f34^jAC8^s&leILJHu!kcX zZ{e9>SMY6LjWUVC{C2xZC^_{CZOt*quiX)_-TWm^92g^M$Hs0ivMB}e`c(X26GR-3 z{KF}RlVNjb4IFuqNoGe&@C`OuQ)`hcz@J`@r!U9R#g~LIY1cB8{viPBrm3iDKU#TD za5Iq{8soTMUkt5#V|jV5{p31`^7Xct!tDAXddo2#Gg31dDEKaRu|nPTU*4g|h7UNX z;sgEXS7Gw>)g6{#3iPEWfydS|W;&k4Z+Lqht|l}823MFL8(9mD!B^0~_BO0vmdAj2{q%{k9Uhrn z4_o#G9jc=sk zgopE2K*GgVED^4NUOg+i?DR^s(0Pbe4{YIFx-1=(i{^PXQjl147b2|NFgVkW9Mv*J zm#33qkU^bR?AV2c?LLsQ^fUd8acB_y0yXRs$<_D2pl;wMKFwbOZPN#7(u!Nu`qC)= zdZ!Fq3O2%2wYOyRPo@EjWkP?n5B8oAit>!i>DOhl15g3oahfB>g(v$RjovBZ6%TXss&$E-_TfGj|ab} z;I-iuWTjjV3P@>kw@jIYbroYA?e_C%H?0YtepclyoZt>W)E1aw^s zCK_9BVO>KVjDBapuV0;LvB?|0U6q58`AsMxUJWrq9}NQN8V0t|g93RUn)@ytX1b5_ zs+VbE!xcx&lW4_`c#&m3{ub==<<0ocWl&C;iXjrTtPQa6EW9VO5HxYLY~HAG#V?bsRc z#iHgT&Lqn)&Feg<^5+qm-f*10!k%8Ub;d~PtytybN514`@*D~i>AD{ZoQ2n#I0wC% z2G_g~Je@y~tK!E%)SW`!A5)HiM;vK%UITK>C~W@i8mbkJaXRv|pj|S9m@;C=45lTn zm}Q1a*3}rkb2D$_my;aF8E??&=uV9ObA_&w`AkMP1!JUZIQo)V7&TPL`^Em>a1}-E ziCy^6tDIW@Dj=y5%V6o}TC@;M;2q$X)1}JO`4vNpY1s4+B(2$`Qlxe&|M=oqGA>*V zGnZLl&HO;xa_==BsZD_9HDz#9EuA#_Hc+txkrG4*t1_gJ$btR3-p7 z9BIT~VlH6t9qgHKZyJPW2M$4kM$zlHFquN}X(r9hXjIUb3*Lfa11 zk~W=w{7(JgSnE`x;F*l;vkYL7djs~EyyiWxI!tCLT7va7ZI~sQPO4w-!ij&|(cNh# zc@TFMM((GA^f5a!{`m^N&S=7?rbnRsY77>%bbXW`_61}H3)>X5R*;z+8i~2L3CxmUbNkUPnDQeTNLVl3y3YbM><^&uo^hz_v;==8 z8Sc2nA)G2!087kMz@ur5HcnIn)6eTM{$4RiMoZ$nlr_ATdyVjFK^sl{`r zxDY<|1i|Nn2hoh}&l891SXgEv*TI^P;~t{i@FfSKJ9#;X8b^^Dk0MMI--K(XnNvS6 z3bh86yv~Gp^wSB)ST8S}e&rzugtEDKK@pZ`U%;WU)7bQv4>4|mc$&`RPFRu(LHWw? zPA?3-GyQN8XyS+13<&9V!%B3;YnNwnwYEG)!>5HX7JUhSj}PbtBx z@@%D(OIM@o@hto*WeC3SOUTw6A7J7)FFby9Dm|;~LpIDh0acI7;roejoSMeU4P?1+ z(M$y&MVtq<;2lsDn~P5-A11$Ab`YTt3s7)rDqJxy#K-r))X1oeUye1!7-RMr-~|6_(Mco0N%Kg4aNWF^A6_A zVs=UgF4Rtf+^`^c2jbkC+I$dP+DIy{ALD$F)!ma)0fA6{8?VTs5g-p0UG{4;AAo%j4YrZ?x2V5KxrpX1N& zFG_g9Ig{rsBZ5nfWAv(JB1l3&4~TKZz+?VdXqGVHuBs0t?OzK(>P$3^vMr_Ww}it( z1|1yl`T>H043Z1G=(Iz8*m)!tcGZ}IpVb3&j+x6%c(4R#B-Fu8p)9g5#tqM%{|v@+ zCt+cDCkT8{=Nx6l*lkM#ApJ%j9(6Ehze61I@9gGjMm>S|lQp^YE7QTZhf)0o1$b|r z08@U}p;W&#Hz)B8S>+|c=Xh%3)#gkH4RoYAx#@~tf=6o;Fj$aEq6W!sdu|637Qy}ZDuc53r z1HLl2{PM-07&wWG`RlxKu+I%=6wTy5HnD@XTQ5Pp*)Tb=T>)*D`a)RO4sbXhibV@W zv0b_c5-J|Sd5<9EUf1NN9!#NDOiP;%zOSG zLuV?(2R1)0(ikU8rm%8>AW^>Unl_qt`W1%H*#io5Hc{J$&){&)Mi6B?dor{VY*b#r z!Lnea95oPJ><^lnyU}2uJPK9h(I(ju`0{!(KGE3*O}=HceTY4`1!DYB>1@>245DdM zRG_GBga|74a3)?@jhhb&;F)LfxcAr)f$^-Gy+1ZsJ0^X1#!x*n|K#bA)bSJL|7GU~qLV#FF_829-|wn?VY>E9kf!x=5= zKK~3zOgn;35r-go;4}3GM0f;mf;|s{|!#>A*q*m-A=SHP;rJ3;|*v0#U z^#&2ZqdTE8vKsiaih(oxJGzcJ!9}S#g!^PFPpRiTfyzeEG`)&{uIu5<&=qVSX^mH1 z)u8&lEzk9KB0l@?7tszC=T3XGj10MLgLv81%t`kMR2QxUCxtNjVc91TO1cJHLJs1l z&5Pl}kT;Pmmgd%%+8LaGqJ@(K)S)E!5LjGjCCewJ8=TQdqVpWK&>!VD;75QRjoRM| zS?jZD=Sp*?*LM=j;@zmoJXeb~=2XV!M1k^)Ky*+)g8Mg^W9MBPT=OfFh*1limxm28+e^Z)w_**U{ z9ug%N*nMn`v;f!p+X&ew^8kMMM52Rt1MY6B!+ZyMbc~jOw<^K#bL1(Qzc!;4%jT2+ z?wvrBd&%T@?kif5TEKeZ=lVY`oGI#5dca%%3MPqht#x-ONSX zM?Y>46h`4|$xO~gcX{sZvt}qN?2GHV#kiA>Ekqk11x|S8O5#}?01e;#VRzS>%6_MN zU^R0ZZ@<`C_I!Y-UN1r8bGrDTQ332!C`zq-PDy+qsGGQ;dS(NNZApgcM_b60g)hK= znj9+(eg_ZP6RwsLrIq=*v{X$14t3jN`dd%XSEF$1>ksIc%!8F_Jq(_zjm?J=G5&r# zBtRx8#~j2nrxw$n6gd_Hh4|`^BhlGjK{AyLF)T}x@{c>>qMI`z#(pEVSy)roy@r)m zU8jj7(?BOaONO!2wY1-AB6mSs2c$;wIfgbxaHx=Z8?$9ma3IA%^8Oke330=y{am!D z&Bvri0vIK=5^Hw$qALS9&AjoPXgOBE+V|dI*Zh-)-BSTmgNc}vj^J|$u-tbgHa)zH z{9OjX>++=jJqak^=8CRv`cU6Ilf)F|!P%OtOiOKGSMC}-g91)(DO+Kp_T?}#@Az;0q?>@@oqNH~wG&D&+XL_ZFX&b% zhW*Yr$?&{s)Z=X#b#PE5zqckp`J9hb(DpmzjA@{%RUXW1Wea4#CA_Q|Td8RIS2*Vx zk4+}WaM#@VSSGg$Bu+hsICge8){7I)tYO@BT#O$tk%X3g1#oiHODeM|29jr%LwaW~ zXlsaI*3J@q@<|jnjy3V_)ytv1M}w1a@|w2Gt|gC_Xg~h6asrgU=(hZZ(5h%d8;R#0+mfN+2`(EpW-9 zEDYCLfj>M2ap>wJI5qMO?OE}l)>j6TXuAWudoSa*3@6Z;*+%bcTmsI5*_gLhfs>z^ zz_I+20-|_R_qI3 zur#CvGu-vb@R~!oD)TbRpAdsp!t2T2qGT{Vb-!}ylYA_mu>vnm73N>vzXhZ+_d(8) z<*?Pz9NwMJ$2ubhniGzPh6nWqr7j+z{=p50rlmp3?Zqr1cp9p%`LeczDww922L;b0 zpv+Q;->=vTDXqhF#kobW{ed1<{`w1oX6K3B$r0is=vdj8upB}{UqFN98LU50iVeFL za&DMEBfFAM;GwYNz$r?>6^1*oU9b=Jr|pL=yPm?Ovk@TwE1y2x^N<=IjmN#~vm#^e>>?r#Xvq^E@JD(GB|XCoeT3c@%e*W zIQVQ8IJgVL&)N*k-7d(z>!uE_{$DsRQjb++ZJ&nP)+zX{Q~@5%c!}q7hS0;MoPC~; zQ0>u3{3RQ3+bwAu9%)E`<-ec9*WyPY^0WbHvl4bp?t;VB?VJtzmvMPT2wX|tN6Kb) zL(;f6W+}@;g2O+MRMjTcn<{zvq=zbfNGk z|0eD(EW{haLr|kPO4H;{z}^E+ux|M`Sjx&Uh8Nx=KQ;_Q6y)Jtw%@zucnRG{1Pu67 zLP+-GLY!EC0MlMs@SJKd!T0aWA)OO}ZI=tca$6wOng7NG6%H_zf}nKj9N6UiA)Bw1 zL*ndkh^uCh>~;Z|(7Xqt+JTz?mrd?B{A1-02l0^G6fi~xJ6^a0ly{3!z_3%IxbIbNuD4CIu$l7ZX_xV?D;9#qT);nKZOc&r%a zZchTWZ#!`;MHloUIryJqH~IQJh{E7=;`_&!$nQ7<>%~K{MDHA0zZ~X?W}3m#jn(wd zwp;_>w?}8I;(~$+b`s^+ey3tY#Bbx;X`9cL!7Z(GpNi_Cx1RHp9zO zFpzJ=x5wT99PqbUy|wH_|=u2)8o*X~a*52I;G#%kE4%1TMJhng(@$)JpF= z67=qy0_#MYP~pEjxG#1hSMNq3)Ot^X;D~oLy_$W$2g6W);vLD`tALwc&ZL5yd-c<| zF^y^cQ~X^0kjD9maW!0W*t>`oF`6F7a|`3K>}3%Z&eX?_zq7z}EF9JN8Ia@EPe!Vu zc@FMBV6Z$EAD>+VN_(mppvj(Qx|TrB3juEM!VQ$WCKi5u42Q0)akL!?gJH1Z+|^To zN$T!&KYavrqX6C;&}Z=Z-H^R!C*HVxk+Wzlll=MQ4@F0}!Sccu{Icc{m5wriu8$Xq zf@~bB$rM1*V_$gbpHHNOOY!+qQ%KBt$4cQC$bS15P$_=~D}Ne7w#7g2&h? z+u*;bg@#4jt8SO{yt!q)5Lo#9vLJHmxEiL>!Grv#VZ^n-tb z6S2!X7`6|uCq;3ls9l}}_cTt!a;G~yaT7!6%V_5v`z#DG+jqj#s5(&XbwQJ7*|;NZ zJ?Mf-m^eqFbq@my+ zDR>;FO;#%{fiUYw9LZB|SbHmkX%cF82D?_8MSlYm?IFn@QcGt?e&f^8?(V~%bk zIK(Z$O*^l^pi(;2S;t}7s{7dZcQTYPUxj1(1}NB2fI9wCAnVab>I+4{{Z%m>UsekP zJ@GihvbE%LIm^s713meD>T_}rT;D11Xc}qAykD+ zHgvYaE6Ztc*fkXQe2j+e7ZUM{L;|cbP+@R_>GbFU9bB&31Y%N8@S|@rhTPA_DzQ}b zd-0KmoX~kN$ z1JLr`0oeQ)YUPhY+lW8v7QMs%*E}-u-gCU(O>l5-EWP&ZAm}OtLC0VuoXatX9~*Nq zxlA6n^&3IEt~&~B{{u#aeo(?OfUIUSoczR>GjV4GSV*$sArpBxmS=)bC$DBc_EMbF zcNnrwu7P~3DH(tAmw9CdP{w_PX_{}b_53i_6thAs&j2#{_%RGpiASf@3VJ+k0(DZ1 zM%AETc&I1Gz@NhW_x)OQHv>Rv{T+kkwx870brwXNT1*<(ilF(HUASs(H9Sy}g~pp! z_{xa^{TP63-O5;=jBF8BHpg?$n#u6J#;ceY>^g841mH5OQaJp9ps{85sLvHAQ@cH3nvi-3;)E!meZ@kup1uwqi zmxbk!T>TwG_I7X%otNULU-pH>>-o6+u_vwg*MQ$_+R2Jjtde_H5dOIT4><$zSQK*> zjJGd>pbKN9K#iU8Z$HuGm{92T?*{Ft61cSHG6c-|2`>~3Kt$IR&w4gt?^7?Lo;L+v z)JSm@4XSC|+fY2+GaF+0-*F(B!FHKmBsAd-HQ=uRIYC$48843^IwkPKb2|TiXFPhH zo`o-&CgnLNimsDNf{0H$a9O7w`y6UAtwD<7o;^f2CWZ9G+v6aE-d+``fVSd5+^XV> z`;S*q2BxD@XD)ND|1&^wlT@r$|3$7_MsrqK&ZEC{Dmar=e}lZBFn8;R1o-^y72UEE zVYMjBQ>mOMVJgP7g~7uy`5c_RYC#UfbFpzsAw;N&fnb~gyyITQX-<;3>831+o^uk~ zi*MnNSy$kLJ)d{Zy9)Y#v2yc!k-X-4vG6-kfGez=5ALuKKdJkJ{WUH6`(Iq8=>jGpZrQQHX)Vj#c`J5yV!|N%oA~L&zNhi<+n()vs|P`R=r^s2s6t4NhoHDK5Y{jc zO`mF_dA$}mG@YQ!`cC26Um0X>-byHXpb7rN_wd~}by}G=ojw@~!H@6f!&G?&X`-uP z_r?Ekik&-tJK2HPCzNBy{vlA;@xTMdA0Rg3Is6L!PBvt=Q*Os^_#3behNnNppue#+ zeUmsV2KQnGF7Kfrn!#R0OQ6YL6I=7mpkhZPTw7EP`vzjr>*`AkoVWlh`^BM(`RQHW zhG3TTR$95Rf@mI&Am4c|)az?8>H5h^#xCigv(rA3baM?2&}xGn_A)sCfENB`p10AR zQm|pt3;oEBm#j$lGUU4GlVYb)qPuuLie)gESpHR*baEl2|ICC-lEZZ0-wux3ogN}y zbr{t{SA!V;CNvTqILkQTH%a=Es>k{vEk9x)ll z&HutoWd$%gq6NBmRna8S z6z$HBz}R91{tDex+$bT$<&O=(;DtJ(J@%YN4Sa+L_J-8z_9&UMFB|jcCV|JoIEag{ zgLUz&;KnBiHWYQ!jK8wHxwDnwbl6&Yp|6*&h*%5jMx3CyAq=!wK5N6b%?$o@nAdT( z4Z^LpV5G$d3soPJFO&44^^6}X_53B5dm53nAA++Mk>uFlG88dPBp&n_ZgYCebKh_c z*5)nYEG~MEn=~u2t3U}-t@gsA?cs2{!VA`X>L*(Bk$!1BkH?Y@lGUP@(D~hUY|uQ7 z3;Op{%Lhl%!bF^FGueY>r~N^D`Vm}MQVP5;3*o4xGjR@NrO*?f(l6}=;JxJ{d06uk z{(Jb4Uiq>b=exb;WM4cEC9$7jTYndR+47TA9B@Y)<4@4{#s_rs3&@jrF?7+njiDPu z@NRZ6DikdO<&z0C{^=Pqd9;8NlX95M>#GKFeK*h=o<$x%@`rHMH)QVNSLBye2_27$ z$NAf4K+5)OU}2aHNrOjFXfN{^c16=3egw}d{WK(ug~G8Dk+}I*HVhqHgQ3?RVf|qr z?EjdK<sP$!B0&FXJ;fOORsKW#F+s6}KOHj7O);Lpzs9I{M!d zl$GBJ>z7W&Agvk*-0=_1dd0XF_jBn#QxR^{rm0-funfH5u>fH9Tz>Pbt6=!C8D8&5 zfF-WXA3m>4U%mV+p0at(z=zMsxo_7XmHEXNE?$P7#w}!zeH=7Yjewr^DcFAGI4)bA z#mbqjv1^wiw>#=4^enxH|5{HItFevnitX^Cu1oRnsxg?_Ls=SLqs^01ZboSzNsO!5 z4EtI_!J%X>Y>NPlwfci6PfVkIxplmlEE`ZYQKky6`12$&cZ zE=Sx#kto?qQrPyGY}hy(Tb-LZf5H^_r}nc>pkL}M z4Af+SqkuYo{q2H!#j=oUFb!|daKnMmeY`feK=9`ZbH7eYq`#8Hx!1NOzy@c*N|oM= zxOh$vb*t878X_wg71{|&B}d`te?Lfn9v7>&FU6TNybPj}RxqybDkvCOhDQo@@boN6 zuGR62*#441RJl(fh(UzY7^E?xFAv3!UIL?tX-M@`QF-~}%Av+~p74$eYEf>^(+TZ^ zEfveLJiP|`FFfT58#FRN^E=|b_7o@SMKAb$jKM?a7V|DFUxmH5ImnrI8^5Foa%GO3 zgtMQAK`pfv^s^>&5C2SLK@kJ2cv#ChZl(bi<_WO5CWhWHOD6qlcA&FLkUOJX6(!#W zfOW7D%6`~Hg&nSNOgy^=z z)7np@sKf@A{+SMqR~Rc|kYyeB24RzhDRmJzP10f)lg;XXp~+JkB7cQoU&AE+JG%tt z%VWNzCQr_l<(+u#=o=gr%_B`ckqknYj(*jDFzP=qydi%HROJ1kz&VF5me7C%bzgL5 zda!_FHpVaBjzOG7pnCWr*>E)vuf8m%HvPBZ_t!yc#_nl(Iy>NYOb0!=Wg5!+pND{P zJ6NC^jcuj-plo^>su_jhUachB3VyiMLxRo!(XdxwB0oj(7hSMuDqh_X3BzB~LF8B_ z<87#Mi-jA|WP>SmIcxBX=cci;@QM6sdZw&c_czUPO@-0g18^v(51$^0B|WPjV%e}a z>AJXp&x^O&p#i8%-Aa8;lHItV%QZV9vU$rIhMZJq#@ zYfU5mN9u`HVKTZZ)`Lr3K2JHU9e=zI#}M0Al&t-U7i&_Wyki!w8hFe3w0kc|+t#Cu zbv&LpdJXrquR}98Ka&5sn*=hJ!kss#AngSYw=I|CIv6D3dj}EpaXDyUBbtT=KjP7M z@Wu{KD=C=iAB4=0|jR zh623iUm-K2s^tE zVtWM-!bc)m?j;pBi+?4Ho^PQQ@i*9Pa)#LVKf+rsHN?=j06h4!h{wEE{9gD9l}sc! zf380vxrU7-x#1F>x7QA$L;B(RyA0?JY{$OkgK%Dv2Y(!tK*LxKZclT-l>_5o6dVkD z%;Lz`->I1S_#G?LiNaGwCHQ@tA7pv8k~hy3VET(7oN`kcWcO+En~jU{Qr~x+HD47+ zPv0cwhCV&6`jM1&E(C~>-0G6+5#P_MvToa*GELCVj8TNI!pZTTNOyS%~%z=CD@9{}k zH`w%Z@!Q58o>HAHIlOx%X6e6xfG@FVQ#MFU0@vZ+eY3gCY`sXco)p(@(LLVV_wT?| z?-KM{xnkm8Ni+~z4ZLCb%E#7HeA6H|G%QPnt&!`2{#N4;wx0smBr8^ERnFNg9Eir> zOt6mmF8}P!f(tim=<3GHaCub{O=0`NYvr@hp+$`U(_%i_C@;c)6B~w+A5&LaxJoAy(KGeX@Yvq z6mZqaAP<{NzP3%0FNv1Q%C`LUi~+dSPWGnRCqz$n!LCc&JCkwY{{W;2)Df?(z$?GwK2p-CP(gOvHfa4!C_mHZhZa2@`sw$zulW zecrPPVvAI1%Hf5)C94Ga!)fX4o>)gbruo4_*9Ojie|2DEkrXR>WMJs#_wZVcIir=F_K2q@USS8vxbdy&b0$`3$2oA4ZN2;tk z(6N=krZ3ezMKcR1v{azmg__X6HxC}~2q%|rccAex74nM}M83-Ug*T#0Q7(Rf>OEnc zz4IZo(Dec?ytxKfSf7DzeR(|ml4VlQjFA)5v*Fs-wb*}^;`igN|tukZm4lS5Qf#oIt|uM2HZ)W)c-RuG=ciie|vFnnYNIi9eB{E>K#oGH`E z-Lh&3*&swKs;%HnpAPTWWnFxy^#~TnCev$u^WdUh1nil#8Fwa_5G&_$nk}`MvnGBL zC!zi^x!#;Wl$mb!ecoTHp+1XRHl<&c}pXEet<_4sWNx(a! zgpxK(@ZS0;Jd_eewV2+uabO`e_4J0RQaNbIiz9cB*5E|p^#Hp!LuCG0^klxmrJE3q z%W`nV87mTMe27@4G*T1!8oI|X5EF}+h#=jt{UFR`WTR!|e zzZ||R{v#ccYjNI@T5`Xv0$NH3>8~%U7YXgPPJn~wkmyR#lOG3TR;9$>3m@%h={4IjXv9It+kShH z61OOvBv=G3#X%tXbvqg|@ULgj6z;8_I_xdGPIkKn|EIKj1*qFRA}iH$c$WJn;Qf(x z#I17y?Gkyx zW=FSRz|900l)6jaecVP>mK{ddwZ%lGy9E;Veh15cwfO0}ol3@WCyPrL`n?yv+yJK8(PAGewf;vK@P-Xu{iCA?}s>B=}U2NiG-1f!f7D z5+ZsM){fpIU2hHOV%IP{wd(`XDS1cImc?MXj2C!^J^*3$1@NltCI~lZlYGgAV9Q&N zvdjTrugc+;4ev+EEsD@J!wM$;Fvs-;Ex}xp zT@Tt}q4i9tsLqD9w|vp;V-{w77>18rN&X(TqqrMAkz3jxipX-_5o(cG5#a^LG-5ed zzp*>I+)jvbure6^9S)+rS{ytxmn;AB60UD?gHK!`Oi# zfIVX`#eN9yeb4(``jlQN<>3XlpJb7oGH;R1c5Is^%r$(`!`c5tgytkJ<##kardv+E zg5x8ptT3#Y#6OYc*1R1eK9E5Jv~rtvGk3@`w3kG9V}BQW&1aGckNK6SOnjl4 zc@v!u`N5If6JWiSFXkU8gSW>7xWgK&upPB=WyD=rVY8n5pZG)we8hu`Vt>J;@i@NH zOM@E~T)Imq0!I>GaNNQh!TQ5IvRYRRg1WouuO9)h%DI)C+3A2qD_oJDo(xwSj*{JO zxAA9SG-L=R(C9a(a7@n(R?PseApaNq6G)>52M&QwqBJZyw*}qOZ{xq>QV`=N(YJwl zFu9ex5LqyQry-04xrvt3v<%4aB1>1uK9^|sBE?! z0*URM_~mANCmd5OJ12j&%- zDm%{6Ea>H(6^Q_0=Rq3!;UMwZ><9u`k?5B%i62Id;h%RTo<6n*uQ1M1rHTwJeql-~ z2J=16L5}neGzxD4*I5HZ@eRv-p9_L3!{@Pl|4Gs!EknNvhm&fn9C#vqg>3rC z?$9Y;XvE}a`0#Bl)t*m*lkgCQ)ZNhT<{Pq7#WD%trAJPTW6F zm%MSo{R{J;+h79M!09iT{Sjm_4iEIXN@+@I38_2ohmF*L4o(SV8M9K5ubN5T-8xe_ z+e{Xnd7sGUtZ)#Sw-BjKB|L2l28+xIaN+hlkmYWLk|rUp&7E+%I9rE4el8EL-D#M@ z-Vqr)8KY%jFPrB=X;X7LVV zW@q}rqC?JTlQBR_|1b?($(3xG`-`ak{ zIpZwUq?&kEpP>T?)QyM{k+AH%a}MWIFYI=pN5gEh-UP()=S z)EcUD28w&>{Fm({=*bb-R`Lk%257)&@|AP@U&lcu8j#=RI=iZPVNWk;uG*Y_pF%I5VB+rYl;gQh?usP~Cy{yqd?bUbT zPu>W;XDu#8BTjml%Z?!NK7c2FZzpV3$z(*+3uGt@FpZ%gb4wA_ng7XI#21 z3FL`UH#zZ59JcOz1>!qz8c1jVC8pO7a$@&-!ap%a3_9n6?cj&*et_lVo5(B=fj!s@)s^_5UKGnL%t8-UNg*Yu}Z z3k1EZ#^(=XLGqLaNOB@^_4fxXQ*Q_>Ipg%romt@YW*?+PoWSmjVwfA60KNahaYmFK zRJT{7?$HeRl5_$E3w%MdwGOVWWSm!DcO07igFbGo2C~3|m$oGayrySh@0vK+XZRd8 z1x28Ol`T}SAB5#f8YI^&p7wt>B!8@aX0xR9)BzLP7T#=QSVFl~ORq z#SjIxTj7C(FxP&56_z&6hq)IEaruNh(6Id!6g)f)o`#ed-L1hhGezJ>=OeH`UxItj zRME+1%c1Ui4&v@Cj^xZ@9Lp9&$2tU?jp^*?e~L4s?qQnK2$k4*4m~^8!nQ4DP)3xw z*{s~IOqTghSJu(}`*UCA#7amzH9Z z=ou{XY)7{j3!w2*BZ=yEcoM3y%*5 zK~s`^xNIie^binJ${w}^&{=KK5wac}PZo%224&xhMz zZi=(4J~p_$u7P@XZ#kToOSSGFg^@XRpa}$WyjX9!Kmr@J**#C-8xp+uCp|Yy7%#j} zhgrr;Ks8(nW{+uO_uTgo@_m8{t48Qp#;A&3|mB|8f>n zUa>j8n$0(>e}JA}06u^H1Jw4m!TSs;8h-E^Y?qz~|5?6fIYbOPta(8m?0f~Ec_%P? z%^dh?5eDts+TqzVd0tIKHU#|==BlkPhUx4%E%4tMt+I^3(AlQ6Av~1WK3huGg}-6n zRX$|p)H2>{0w7qB4HkBx@Ll-{xG4JI-7_O#YbTBuR%P&}DO#ed(PD7-{tresN)X#w zL*O<&9nW8y4l0`@c~<8vVD@!0!e`eGb36!LT~><4(%Iz4#adJfG{a&(Cf5r%3G?1b z;1g{{^yzFRu^pyV;P?bD{-XlaU%y4C&++Ariugc}haz0LVF9jZegcxu=qHFqn>0(cupA zDrg4x&6EJz{U8QQoO95iNC(t*#e#N<6^yPw2&ZawM9kE+J2{-+bgT_`4ycmB<5)9tb#_`E?vBhI(BO5spN|q$9 z>;;@!I+;7uEfY3Qwg#QG6(n!x>&MaS@_>s$id> z2sh)t2Kao*LjFyMG(R710e|`kCb8VcNYEABqP$`9kx3wVax@O{hF7X; z@kX*O*ylO&s&3E4g+JX%#i|*^>MWBZDVkwJdKZmJR>7%(zBnT%7<2w?f}S^`jeW2B zv03OOSSI+8WqK{RT)7?I%$mdvoFvr9#E-o2DT}GCm^?Qk^#QrL{}y~!`;TX1*$c}x z1-W6{*6_j)O@+FD=TYNy7M^TLLD8R6xQDV{QcFWIuIcJUFrF3&y^9yZyu5d`++GL* zcK32N9kIj6;`v;;RXMy@dHkR|o9gX zcmkgi*=Ny(Ch+C!!*dquT)wy%$cvQc1-MS)es$iC4o+g+Lm@fnF+Cm5INQ^$2UtE{ zG7CTc@q|~c*<^wD6z;@cgxsulSp2gD7ynlS!=4sUd~`L+YHUF}#uvT5+XhtLghSaa zADEa^N}bE4QQPSao~%tJhjj#a)t`f4YJUwA3C<+yIgvPUwizm=#mLVpQSJ;;FPt^4 z56$z;>DQkLD1ZMjF|O>SE<0KGHivOf4$dSQJBn~|ze%xzO-S^SSoV0&&j$4%+Lr1{TaJh2d4vD3{s>GbJ}M$uWYP z(ls#3$bjSQ_etyy#N{U@folCZGL$+ECw&^myzXpT-{cS9yl%p=J9kl``YpWLcNHUs zeev&)E*LY?g75CKoGp_ksO+1k(CpmGWSTWJAZ0baeXft5Es3D#oy+j$*!{oaThOk(KZycgJ;7fkO@`lh8*v=2=@rD5%& z3drGOHy1Pa;dvbJCl9?jV4olt~UCwh{ryc5Xd;Sk6c%J4F4_)LGAvvVDey?su*g* z(;1b(FCWcWrddG@ulnG^!a&Y7YY|9%afu^(KL&lwWwB%r>upEbfth!gC$q~CMl2NxVitQ-6$iGv6XwRu(o_m_4)~SK=1|37jl|e9s0F3fX7E zk;NdGU&6Z^7>Q53b#Oyv5Y8=XhkeV^!GZ0^PU>2bUybb$Z{!Crk9{M{)(D`>!QZ@3 zY*yqC9VbsO{D!lI%q1hlxVg`}A+4g7H?ch%5331t`C9Wpn#=YLlRVIVFo1>^e+Api z)42UnEwr$CfUf)Qh2z~S7%*)P)o^WrwbO+$I7ph_9twbP{mXdo%r~;4WfII7n#5IG zR|Xm>GO*%{5m+}=lRI|>xE8B7(U#5q4x?n8}*IG{#}X>L{vdLXe~&_*23W0Rq*u#VqfhY#xJ`8$9=jXR+7K5 zvr>R7SeA%QUwyFh-(HScf-zLID1(b=5bP6)((S_RXRu<52pL%J8`y05Uh4SMymXkK{C<JD<@0mFLlR-4bec=LuXd6vq3>wJ@+X55rDg#YGwCcz#i? z(DZpfTxczXm=nt6@`DNT@8%c`J^jR68B~oIE1AG6v5?B5+ZBJh<{-XY*(|)Xyovn_Xo@ zg*@W*>m2}<$VYH!Pb|JaRsg<2k&Iiv4F>zKW5;C1Pn=(k%BQ42)$a)KE%d@cCOr2% zdoH9|PL1r?4fdES&cCq)&HfyMe=pi`yH*&f+;Emo3EqzBOJm^9+!HY7dmjc9 zxbSz;CTM0EA2F6C&7254x#0?~R=4AvYLvjX>=cmuvjU@96Y$T~bX5ARKp##p&SX>q zeYCV3ewB8SwuDmhIwy!KP7{EdL1?HwvMZXpxd=-RvRrUPi+P(i;ohtw z{NP+gbzdTBb2~&H_#Y+5d0SCIQIz+$=s|pfT%;h4dj3s^FNx!ZTov-T!Cgyo~ zYq2Aq-#UbcS`M+CUjTi}LP7J?Et0#p8VmUTaoi>)Gj7Nl2)BBSes$4!Y#QSqecpz> z`P1Ro2HLjC3!R84V8W5@z=crz&rPA+*)&x_U3Jy-7Ft)^5k z$P=Z0?Ri?`+IIBqm>LEpp2daYOCY+T8WLo_qP5X)-f*)!KB9emL*ziLK#n*{I+M;$U+!=shUyaapLX0PEV#r)n{;;U+dqeEO zAyT3j0x#?0(QzOG3Y)}Xc10SEZu>yiC`ywJg&#O_n;xUYiCyr-Hi<+F*Ki`2+MxcY zzr64E0{D9u>jai8AZJr8F)yH;mtFpfMmXxhv5PzD=C(9?n(-hEd6c?*$|p2}b>IC% zu_b9$ectkHT4M5s7Moe)Rhb0R!!pmwmG@}n>j0D*>?iu89k6PTB`9vmf?q~*|0yFe7W6mn5##b4gRg`a6J<1xhO3}fqy!x&{8fL+r&;JW>4 zb{~*J-~Jwlu0J*8df)`1lB>vJ+lLUla1FXY&w&ZsFQ6lFn@pFeYPe9x_B&fyrlOfl zcUtLk_Lna}<@kl5$?j1t(}w6ip$D)>=?IiL#$Zi>ANEWxAg}MYl2zV!NtDZO@ZaJC zvT~!uQ+tF48(c@(Hb()U4$EVuzl7|<}3Lz885!*hcqnZz`!k-dawuVJ$7Kqo-%N- zxe0GiSP+}3J8*}52(_4>2mJdL$ph~>cx^(M{JVb|e}-)4Y~U{hzOKdIax{@8PssFTucJ zTQaTE3BzpjL0?x64hUAls?tIFE_o{c%VL@Kr$f-SD;<2?ZqUPH&2R_QsBiv95K>p* zZutY`ZAlg`IQtH(>_xZ-N9!%$40>Vj}v%0J%tk+kU3dOiN+frk}b|5C*~>g zhTlnWFHadDuh-2-6*oToQL&2Cc>4tjavWvB`6pWOwu2_#DJNGxe&xl7SYsIL<8Kmc zCJ9TmQ2)+RFkb5ddmng#$e1oF$iC$ywHw0}r>k&%Q!WVKl*JV?HkWyls~=}z$^1GtTU)@Hm}hkB z$H}n32ehD~F@f9|8C(9YBH`%5ipI04$ch z0VUV{c`_;$z-M#@=4_wGO?2gN1mAkt;uQ&Armx|LV-b<=o(|o?7vR0qG!%L-fk7TC z@Yv`vjCN0g+_!@uWVi{hy2?@If9*(uMWF1}G>}r8rq#WIf?!uX#P9z}ekO@R`&d3& zS;oVz>M7iSo+i?s!0ytyg1l|-(!ioNi(Z|}ygZX8$fB@MwEUGNc{%L_y_o)iH^nFg za)SHtllTaB6n`sd%N)1_R|MGY&{6ksqgIKyfW|DH!k$us&txFAo@cAdT{;ji^?i0uG8N;#NI* z+^fz^v6Y51W7t&5W z?Ahy%gOe&CW|j=sIJg!rD(@$)=W8Khy$bzyNFJ^%T#k-!Z@{0mkBNUuU&HocF}!JO zgjw-CGCooTUkmkMj_fE{U)zcAA}R=H`~@`B*TQ()OWJOnkJh5D^ta#-VqKF!1uR@? z=cf~R_l+V~KUM(S$W6{A^$I9d2IT4ys;V&^NFY z-G475-#u1vMT?cm-NnNwkmUuoiY0I&SsevlKZXO0f%J568<>h1;3>l(D0CIz9^}bVxn*i#M>`3Z3?+!SjRy6kX*>3=A%Tzk(!JU>u=1W+ly7 zo&ruXGx2tLIL0n-LRtR1q$G*C58v+Oq_Yl%-hbtE{-=|?5I^=@nz;g2UU(0cf!FZY zeidB0`5LWKT#LptL$OkJ6;EZ_8Q#3&NbEa07j+L_Lg!chbWN!nw8yKV^}i=1^_DdX z>28Nh4-G;1TPoPG?;43#E0TAq9-Wp5bBE;4;FcfrN_2qesZ!FmP?I{!?Sk#%7kF}~6G8u~9xuI9 zg5_$DfoQO=Nem>fX3yuzFAc~3-8QshO${h?vCrLrYg*qo2f^S(70D2JOP|MnCzoQp zAnn>un3Sf2va93Cn+37sLwLf6#&IR1M_jZu+J(WfojlhZrhw)5yGA~(s zHg}D-ECytH5T~1EpfH*R#tbFb|6q{l73?Rzm*U{1;(nS>PV$&J4U~#)v?`yZ!~4W3 zSjcMw`JY?ikg@}~wpB9+Z4bD3X3~}jLGHtg?Nm8L0k5pNOd}F}(CkA3+%#a)_Qwg3 zoDc^2I>$+6IzRf`%Tmd^g_tgA4`ZW$NZF?}6b_mTPm9h$#(EnJa-0h~9erRLTEX5k z8*=;GV=}VP09P8{V>cOxfS(fslO3#JSV)q$Cng@J^tR%>e=1P&$Psng&8XXpJLJEv zCh|<%4fO5zbT9BmVUOZ9tDe9wJQF*ii1*>|Z^l{xCp<0FTYLU>ekF-V<~Rje_4#u`ag4V>1#>wh?$Sob3KsvZB@u105X-W}NOsTvEkT5e-?+-|rr(kF-5WvpNGsji zm;st8eK69sgm{kZ0-xkGjJ>xDo>fKSIrj`AG@WI~T(^dR)(~Fb(FLHjxS#OFq@kf? zE|H8d!KVf5ApC(p5vvM@jXG?%GG`Wi(Ov}mcd@7S;t~k1NCnGvQ*lh97VrLufs<-? zvF%nQ83`BX-f=ZWRdsPPax4TFaCcyiX^B>M=nKZLmT%mnunwKS%FUo zH~2V$;L;sbBs&hqja{K{G(0R=mYP+Ar>iNUnyP4#B)kUJa+X?y^1LVN7a6EZIhCbNr ziuF~#B;(~nnq3nHieFc9v}T$Zq`8#CVYCghYT-ltz(_h8hkolJkyjX#V& zaEgC1Y&pk=4E?UbBNG%oYO2ZEd-sXW!(PtRX)3tJR2>A=^6_KYEu8BAidet*LDw05 zL|0J;r*+)m$umZce)4e?a!aLR5|*INIVikL?e&lw%HeGn&}m2ZqFv>@#EB{hmStzCSxbN+x%fD zO8V9|z-w%Qb5|F@xJ)3Vt3INzz#2K%l1Y1k37 zI}FF+?DWr2ZsUUmSuHR&T!s1TX46y{BAVNyK$fdYv*kX59AllR{o0QzFMm+=+oPPc zP(kj#!auyZ>^_fkBMD*?g`i)kgN_>}(0}_Uao2r5swHNS4*s%~IW}Wap0NGk>f>M_ znhFW`SXV988qZqP!KdM$(DF8eB=Ch`@})=A<7@@SpPvSc8ZMz6+j#}51wyVXbM+Ob zL0IzuxV^Q4-K8lY&UR~2>bXR|GnfcAXy7-;#c*kr0(OarHs*G!fY}i~tdl>+GklVP zf&S%m(G)Lu^)&{3wiN-tp)~Pm*Tdp9vG7Nz3+}zX$VuHD2u`agxb3JU(&92$&Uj$y z*Y0A}7H`ZRyaloAmlJQ92b>yNW4!;-1oel_c^(~g$UpA@PL{Amd8-*`C}CT+EoV`KZ$Sh^$f*(lT8YO_FL};Yt2fY%v<8Zg;ylZt?aw)@O+(t3%nD zr9DK01bwi`kgEUnLxFBpI2YYcE-pwR!1~fNLmoGHFAs)Oe^=tOTw9R5*@Vl}BSFJX z6@6b^LZx4E_^!|w)rJhv`&}}v>YI%DR^IfSjTC4crSu|W<*kSF%)xB{x0Jd_=KPId z$C)78n!k~~`#0i@uaY?aR0sk!){{&6`f%#0AGUfNAZt6iFzQAPD(#vIa#!MD(*73A zEw6z4w=Pg6#`juUbAinj@mLv~iBb_~;rvWL91-Ednc3Z_`}Hk(GFu1p?TSHRUo#}G z8YN*LZ8Y*53yF76H$-ia1A(YmSUY4x4mJ8UmKJX>x z;ko{6z7Xcnx)g+;X}yf*{&ZuZf!b9ty~b_5<}OVi^)(RkrmD7%}qWVu0o-q@mXJ^hp`?&2K z^~UM2bzLF(s#1)te}W*!l5t5M*kZyH7tEhX$A6PD@${{YEW58FN+xQI>s^H5A@A!4 zE)r0X)6j~1*ao<^oIXc?YJTqq8UCGs@gn=cO^pvG=6S-CX?l2m-)?BLh=T7@t6?T5 z3>VAhl715f{E)~vQNA5;eo-t4u3Uq6%U59J78%~#MFAksJEG|t0I)v4g~+kLHL


)pvY(PEHDO9YkF4LB6&h*haYB=B}A@1bla1n0`)X}4-v z>ZuL9zm3?mDF-+FGlXBUdGPM?ee%z<5(aKWLfxze+`G006156p%ZW!2q$xwkhXIcc zWy2%M7Z_8;kHOjYz~{Uazle-+4uA5-OTD-1q~?@Bgpem3S4H&Vc3lq%iFt3H> zg3o%BKkBSZ`<;mPI*NSoe#w48X7Xppq0c4Hr zCd{$Q;CZ_rh|VASJ4tX#5{lqM|(rJupXb!`_dYCHo0i@bSmY3uOH z20s+E`wCspml1)Ubo!{l7-z}m;=#(xT1qA6U~VHp1eec;o8KS8f{ATl5PctxO<#wu zH+^99XfWK%OvTeL%dstTAF*KByrUUH-uW`rV%@mEZ10qEQ3s;}f^h2Y8oEICExEO= z0*{`N#wDGpxZ(RGd?BBMRbM-Cd`lw8)?WnCVlmWfmd2{K61>u5#&&vFz*(gdqMNOu zJ?}ZE|NAep?wK%{ZAj*Lhr~9-`?Yc!{bb-tX9!i4smIltjyU;j75M4olM{9+c>daD zC`iZ#1^X(vDriG|yzZfG>>cR*>V-vT`e4P*UVP8{ONeeE_O0IxC46BhdSnF*S!bc- z6>$qdrrk;brkK=`}j4rSV!9uhk8c|EBn$Q{XL}{3I3o z!t;p5jc^=2l|VaU7m@;NXM8Mn4V{H$@daGvjED?y);DEx+}VAe_S;KTUi|>g&j64c z>c;)rk9nRqzmgZ3+HfpQ6~DM&rc({uf$-OZnWGD)HO4@7RWHsC4~LAKcj?x+VEU&n zj9#esgEPu{AjZ|EGh7ld!dwpIGnCOv&yp(Zgn@ZxI1n8j_|AVjE9V2b@fXOtxZAW)n7njy)NiZ`%W6K4^nad3MjV?gB-a^tX}Ji zf*Z%lvhhd=>)i-vq|N9F+h1gfRTB9SA4GPZ@4{Q|@-#BW3^;$H$=CIaA$IW$x_NBE zfxt5SE*B0RVJ1lA+Aw&ikMb98z>WYjc+;{zecuYuLo5#7NM3_CVlL3n*x^2G7jWHUAFOmYgSYh$NN;~2+z$&U5;O9U zFV74HmtN(~ce?}C)7lB&M^Ea%wg_x*QL>uxwJq4aMeg`*ob{P?UU$f1mqIGmvus3e znLZ4({^IE|{tK67#ZT07!91%9=Khz%yXrItE6c;s%iEvxclJYSvCN#~`Zt=~Zi&Ub zt-Zvuq6EB65>ViCCWaqQhu=<;wE21mC++?a>92`_&L3qElvh9-%dWs0-BIeQ7zA^k zR)WioqY$+AA7`nJoyLzNlNl#R8F+i5Fyg8TD*IP8Tq(2XxjmG?-pXLovi%;MsN_=L zJ4tX&V;yhVzLmIs)@{x{kLhB=!(s4q90GG>4DJ?ohpeB-{4aCiv55x?|4Tr> zS=nUCE=#mpnSpQmzHr%whX%kkC?hS?+c=;Dboc&hycWSwjYK8_~N#|3E5 z@lT+Lg;;ZW1N3fNhRwgasKB%!B~}D)I^v(dqYWcoc7q zV}fRIP3#3!%nD@uzspz~^p(A5cgaE-51774iv2zIz{huzU}u^GY7dhk-dzEte_c>eekJ0*=F@zxSzEQ5PQkn}-2iJz%wnb-T%AI?KSH z8q7V58haYy`-;Cr`t}FSZ>#08N6G?X8yx6;@44jQCS#sbb1{5sYl9W16fow@1?0cc6cZO(mri>QPnS(n!HbOOH6x8Hr;>(AL(1q17t>PV7Yqk*iHqL`)#<0Cq-9-Je zBQfpefAqI{J$%cm#>ARtICs4s|NQmG@BBB2)gwzh_1hjEJev$D1NkuL+GR5S$qt{% z=YapRhq!f_Dy;alg?6a2XS|FTplG}ew;%MyIa0^bSX=|&0FZEkUX=4Rv&XFC2>(#`(Spr}!l#1sCPlB$*TX5|t#$RS)yw~Hg zkXH!cJGltDMeowf7d24td<-NrU+2rm=3vwQmeC2KfzR?FkF@#iNmdRX+Ipxg zm`Qi(#)CprBQ}f%;6>#vbVrmIR%<5^sp?F89pz8{SOnmTh?g9G_AGNz{}QgBT@K$W zOi*K6DJr=8z|UUR$=R%cl3wh#E!YtULT|%0^?Qshk_P3{SKvuwFn0YbBO_iuRB(SB zie4Or^;&!wqWA@B$vV)>ZDZbYmP_T`$3?5Man1TT`e^&bmivL_M_cTlSE|)MI~Nye{RbrZA4%$0g9v9$c)#K@3T=PiFf7%L#T`4~xIg>( zK9!=k1rKj1#nCjDxvGu^L0ohU%vNo{M7b@fb~qN}&X}TQ&k_(9iYE^qL}84|Hn_j^ z20Zy$4DV(};OdQXq;-=X6k73P+^?0q!SAP0I?M`YB!v^NOk)f$KhEx(8t90VIU3Y! zqNqt9d=Ixq%P}K(wkZQE`WJ#)!wQ~MJ!8EDUjo~s5qKhNCMtEhu%2^2V+GrT>d9)_ zvvNAF`dCN`Y9fiq$V%S9Xe-t)*^K@b{rH%%cD2@jf_V<17_7b$R2Lq@(#HD~UZ~@; zn#m+|dn3lhtHAaFNenB|1Q~62OjT6@srQDM8{m)253t!bc|X-28N}rgS7^skCsO9G zgpWjcxG_%!T9l`u$kLOzIb9my#2zs0c!R&{UqhpID7{{?1|Oa>h57phVJkO;tE2Q>U_VA#75OX|f@SR{qiv@*~2*Ne=pW(1vCG5E^77oxqwIAO2txq!G-Qiu}a4M8euY5_WrJ7-xwlB_>t)U(Z))0^3a@N5V#4r9bv}buQ zY_;-(8>%X(@@^IUdY%V1`uFMJ4I!34%%toS1O=97@?Pv@Z1?39j>@ZP5du2(~(TRW}!F;fKdjg^v=xUaTlX#Wj+AgG# zB``{?w`fquyO;5pCWQe-dopD47@n3#fT_cB;QBd$y;U=Q^-|^31%xtZ)p7jy^gaZ& z*b)yKi>lTJB&5g&oD}20e3?IHDa7Fy-E7SIpob}NfFnD55scmJKy@Qo+-h8gcaDgm zRoqSH&(Y;L83gi9S}(@FzRT1}&jlrWx8o_NOt#0nO)hw4QN65g);+wAdtLA2#*Z)1 z>-lHWvS%+HRWHHUhws3@ErUE9Xou}HBVqXgGtd_N)9^lA6F!J^;pGHhvvF;CzY%n$_MU@PJaM8@pgA(~UPK z!B0!)m#{)cDkkh#!0F!}!quTAkQkZf1gkN6Hey5lC*n&%Zd(y#c#WnaoOC7DdS3tt6$xt~Uj6as8 zLbuFoGV(8veaFl2R)4*XyB&tn$07;S#@NnQVTii?n&9lw4+G8j{d8aBO>*V%Y1RXF z;7MIDgvg%_(5;+`Kd0_QU%6V+t?vw}p*x8Gia<;*sHLyX{9xu6*3IsI!Hcuz!hN?2 zOifc^{g!0#QksPW&Jj3=ZyRuBc4AR@1uDHOq0k~v{?ZH3%DRwc)3P*oKJ{$iV|$-P z7yXD=$SRy*PDRhJ;pBu`5SGiyW9QEm_+3AXzPcli?sNSi-Y^5QvI-$->22tI*@Rj0 zb!aoN8A8v@gBjB+kt?_zAD(_i^ElJaBsGOC5hK zCGLH4p1Te0 zCdGi3eGg1dcE-)UJ7LDYCCoX06VopZ(|eCSXiAAc_%<7($vP?csnre~#sS@EaS6n1 z?nAcSC0go|gQ?%7@qh&5Vn*B1_kG^HbNud@-joU3QhDTe(gjewG8c@pv*1z@`*)XB z(EV?9h+0JqahI5kX6vJHRZcP*%QD7UqCJRYR^gkiT+;L;3QSaQV#LB~*u^&oCt0Y2 z)!ar@U7v(6%PvEGCi4zy>q2&}3fLC)(72n7S8sO*-EJqqa>Hi?w%uf}Q3&z*7X}Ko z!sMuPDh__g#v*nPa`gRMlzn^<&6P4)4?~i?Wv~CDrpq8QsThV%Mo>Eu9cYdA#Ol5@ zcr=^c+6igWu(gsmhFHeSZ#Q-slQ+kY^gt4^%EWvRrie)`!? z6>dc`@Au~O5L^EMpA|LWammB*#4Zkx&H!len1vf`0vW4uBlFM;k&fF^RLtr*h_fA^ z(qA#`Dpug=&7KSP3+l)>0~4yse*=vcUI3GVBpA`m!e1Aj!GN?dNI!Xw^~ z^ux?#*BQK6#!z~-F@mSh1Je?bY#kR4`;dBF*cQIbo4Gy_7 z#Qy7=q+{dLLd-8Yh*ta(xUMV@*9&&zboCPE_P7I6o^B+Ye)qw9UT2xMjlOC&RXS+Oc4!R7{xrQKNm;|f;#evUWDIC0Pi^DIT z(jv14w*R_A2l&09f9f_8;y4TIci)ESm_9h-@`&vsjWEaSBHRuB%;^)jPu4C~qk6pE zgn!$5Z1YhBHP^{Rr{pyDkIcd0S0S+G=Mu&Z@g*%J3(fbm!_3NVWHK21AYhLg3wCmx z0z5G+JQZC8!$DgwpUfXj2A!kz5cA?0{@!~SINzke>{b(4eF*`vkMbaOd4%5P7l)U| z&A4vmV?1j_L7`CxPtDK4;R~@eB1Mk!d1s@}*e#D}!~%Qdvoi-CZ>vxzD_fY-$k zEIO{?E14>6$t)#28#m78&+gEC?FQ}9%fojY82guFMK0{SqN!4Gme=iHN}3(a8M}$y zO%0`D&UOhp?f5(psTTwBqCvXx+8MZ+5&?hpGT~5zEEX0p-(L10wO%}cle4SIk@yB& zSMi-zv_)XdKmgKJ=U`Nh$Lo#0h4E)osIlEA&Zew=$Qim#-nmmFVEK;UJYBm8d@!Mo!j{i*Xq6TItfI^#l*hP=l{k4pBOI350COKlu`Wv+ zPEGIwNrO;a+o*`UC#l1+ut<3IGaLn-4`71D1hvhL!p?s-@aU}oT-JMzN(&?KcY-`k z?Z_vUc`7)+>mfZ-xC>W2i9+|;^&ola9=@vF?E)eyE+tx(cy)@_sConuij( zc1758wj7%eR}qD%S@?6SI{)6*r|ruOMUS}<007oV1SIDRs-BDgjUJkjHx-3$o$Pj&1dzn zaWEWz`1|8~iASV7a1DM|=mE1CVOSc`iR!jN;4`Ly>#a-ib?a;*I{F&Lrsm_(&(1_E z?+{*Icn;)vG4Rvi%uEUDb^U4D5v+_WrPQ@oFN$3q;v14LJVWl#F{9 zp+m=C!r#6Cw?8|Fju%vL(#%tk9QB8|v%C6TUsr-PN}`P68Tip-NnSW%eL!aC;k}XPq~o(=rVj!UW#IKG>QI19Xf=oqsehTRNLP~ zqNDv$V8b!^^H%_k+D_r}jufq*PCXd)vWM=UmqatX5<$k!3VwRE0#{2D?Ur4D4bo9i z_i6#oJ#+^q?zG_8-9HV}E1zCm~AGIF{WCb4r52 zeqld|9SY+4ZCylHu9*y%ug)cwuXf;9_dV!j5dgK(eZ0mB)+s!Y#Q8`N*R#LbgP$ZJ z@NR`x?C2vZ>>LI2Ob$U-O(~sz%owNeQOx`xj{eE!_%tyOPJXz7@+BeA`tKB862D#V zzta~t3f`hJpXcJvfHpM!Z9^V@yNadesuRK|7usRDt+V`$AQDtd zo-l9A2rV;7LDIs^lbfU+_XCgztaXF?|cFd z2WvCTyAgzZzK?!$k5iNEASk-+gPDgqxyDXq@Idn->|Lvlhh2lIgY#$VZu*eCJ5mTK zGHak@=UdRQ-iq6GcXO*gK0pEA8{B4rRw#8m%57I{=e+B0!nGU?(iSWYXPN_XW>+{- zOW%uO<0ep{Z9>1zO@j|%$H8fe9?q+&fH>|#D!d|%Lfcoed2ut#!&gK68C!6i(7|+L zd9=L~2P$8Lz{P)po-K=`7UK0__MicHdwa<$Z&%zUQi)M5wygKAAEu04MLx+>(6eJ6 ztye3@tn^IMbN((QSbF2a8bs;*^-#4o8Dg3~pyH$1uwmnT2pBzr8S_(M3gf?LJ9(0e zA603`)M0eUJqu+E{Xy?^Biv*f)fT_YpkBbT!NS+5JoCMMY0E<68`;n&7{_(2N`%*i zvRsccVQ5Uv#g5t9-1n#KF;M3h=W_IX6pDF8t!E4pZ^taEyQ&3_x3uBH^?*C)98)^S zW>3zLGc77i1^s|xR6Dg7`A@O#<&t+qIR2KRyNVrHeeEM;t~8E)I1T$*4siaFG&Ix> zBk$Gg(O5d3Uf*aAm$*ToHZ}u3FTYA>A5}vY%T#E7>w~chiTKXM2zrlKlC+v@#H30K zD=rc0`{EXnuus5!pN?}zZ`Ff%j2NBwB@2S$_JfYUB#x&ZBC%&ZVFk7=EJ@H41Nau2aB1m7U$rEYszHNr%(jAs}0F| z?QE{9X$sU87=S>KB(`MOC7vpq$N4T(XRD z^75=WArGo>(ZwTpwdE|={f-kxa>}vh#8eX9D~EOL{|Rf@2Fv_Ikgk(}qEn5SAKna0 z-J03|;Q?U34V!ADsJsdi@wg%SZLc&@6Hg`WNzPbe+X5<5l2Ewh4lWLELIndybbjFt z@)oHWze2lgV9_Dm^F7`;jT|BH ztYSLm>T}c~mLFb)3c|DQGHgw(Ab~2^KuTJKZvIwEdGkcjps@_D``^R1m1gj;RU1G5 z80TtOk8u24UGeHcPjncK$Iu7fWycNfn#n)CMg^A*vNz5 zqNN`PK9jVpUR0GNrZLtmw@P4AO4GGtSoUMlsCSJZ%o$U-NG`g*~N4UiG0}o?iDDV zQ$op~JkS#A3oRcVvC?%H?q8ga$&Bs4>HA-f`c8Y8&z{}YI-lrM#VpjedqeN{>;}Ej zGLS1g4l5K2P^@4Rn5ER>r_bj2>1YpKCJ+qa7pt)*unq2CSPVut9zlb;9gf&m(r;J7 z%7Ti*G5o|AMymPN!ASxx8T_a61me0 z&hL3oGVAJ~uBUlpV=Vx0J zyxCNQzYTbyb*mZ(#%6NLoR0(FRe6{#o=9JYdEuvt<>b7w8tiBJ>_3-wL)n>R(t0Wy zPc!~-t$P~vzywH4tA{tk4rtHzhh;J^QMgSR<8=Oz`MX_V*r^3zU>QC6ok0Jp2+;cy zg^LZhUfmSKlE+wQyiQU~;w^GXn?j{Fg-0D^f((m?y zL#i{Z(e|MMD|Uc+e>D9PmC+Sm?}^3eAa&4-*gdN7g|DPst58* z<#YdB)PRC3yHWnTHx{bgquHJR;q@QYSoge$BUinXyU8gZ)AbzT=Auw|r~VKF?0oR- z{@a*CzH`IEwNYXdFRbXP#oT%~bZAgVqnm3{%H#?T_)CKJO+!qG55~`<14Q2-lavd2 zV(M)TQ23I8*72=u|F-~N{&E7#4lYd9@PV^iyT}b6F$i6>8P3bLf#>c|^wsLSVEH8# zRc#be@V5)`GhAB_1s|Mz_iSABpL0gv(L?_%UlT?_m6@1RpHD5|r(nzYkC zTzV{to+$FcCl2!T`GySqCYgc30YgZyI>P9eGu(|LO1SRF9q=GCFf-l?3nXlSG2@{k z;4Z8mqVTV~0g8DMD`H=96`I50j>zFv*sBHSj`9bc!xGwB@2vp$HOcf46=y%B1k)q%N-w}Y};HfRLyM(;V5(7(Tch(x5} zUgbM*!=3RWTiKkI>Q66?o*?dh(iqHqFexAAaRj?u=oF>*hc4rSZ6twk9SZl zoC>ck2akH^?auu}eAK;kEz2L@VY{hjpVen|bGc`Zu z36`bbDNo)VbWv%--P|y$(mY9J#!A8B$|(Jrd7g3jd@*%I3g7${#JA2Sutu0bbg3WA z9qtw9Z4kiP&!4zHzVhrZHRnK|5f z6XlrmSc7Ft0wL7VmRJweqON-dY#x=sm@WgHbJ`n^jV}lN6<6qoIbE>dQ4}w-9F5oK zN}O}x5H22!!%gezL0)SXq)&>W)2qWMwjdZgSSO_Q8h1$9yacZ`=D>=2VX`^^7oyru$&Jh>-bb20V{^%fieVrEtAIK5@x(MbC`p5d;PJr+ALcGgY z504jl!iHu`xGu98E)09Y*>y3{*v|TYBDEpD@;n}j`$PBCJ){CVCYi5h6cRO_;_CO0 zp|orvoc!+qTC|E|KyDQ3F8s)OqD;W4CK6bA0Y{~4;O(&;0Llvh z5{2o}`AHy=EsJ$WH{(3U@CfBZV=7+&sq(Hw{~~sOxLpDB9rCff>kvFm(^0^n z1l~L=$E6k*P~4wDkB}Ev%P<&^?z&GK`HIlN@H~-n(1vxUC&|xR3OYg2tb2PAmfLgy|>&6%L~fuT6zwV$eHOVZ%^?Qs0+F4*-h2E$f6b1JQwcj3J*^Oyz` zFVk@P><`;K6-zY z?Ic*oPNX2JbkAcmP7#1HV`Tnb3Qj6FX#6=Ye5Oy)_HhxY?5sxa(@>K6Qw_CE{82MG zA6M<+!s*-?z(7A}gdChD)5g928Wis1ok9T2Fm$Mw3LhV0NY>aNZGD z`dZ@+_xlig2J5|{v3#vy$MQQ|`COb=WC{n58c_?!O5%ORkQ*_Pi%;Plme*BsUxgk5 z3uj-vIAaJ6HcHNaWqfDLC^+Kr#00mV!+$tEiUk zX?#d+VcryHmYJT8yGv)H*g8*~>&uV1vkwu$=^yF06)Z~=6aY4sV_b)?XW$r3!Gb~? zGCd&(+dbo9+iNM%-gXfWt~!D%7mCCDM+Kzpc_SycG={wWa0zxCV0m$;k04sgeC>DA zG5-A)A{vqjpIOE~z;->%U_OX`)eg>=0WbV8Hw>zB+(G3^3OG1$X@TJbICn_|cb|xW ziz^GUt4{*=eC5R%_hV7_^K3Hm^#yL$x(hr1bmD<$!8lts0I&OYkPi#RpxWLG7MN9l z)jwIRUG{=pde={P?thF&`Ezig@>0k%3&yDaKDu@Vg|Qv`N&1X;fp*sQ{dIA)0CPbBu3k^;gnx7E7QaNhhL>2zIxF(re`8<4NWWml`j9=9gPNEpQne!?M$btgYITMfHL+8U}u+0gf~;&7WsK?~%)P9H zMCclFGWlWcG-stf%ebU@nFF3R@`1RNc+#414D0yRA#>ARh}-{`sBPIyo2Cn+ROw5c zZ~cX4i~OdSYhU1UjUBY8*xPE=37;mtWf4dBKmRjV=JqqxTDVrx;tQn<+DcfDiwYh*69CVY%jx?UM!1zRh4aPZNyp3g zL{Nfx9S<4fy_NY?rhu_%EDod2`6BM?9Xa^*y96w0;sDm_!W_3Hj9tNt65|4N^fd{Uc(xeTRn-tJq@vHdL&+R2mlS!#pLu9102X{rJV_zsP%9WeWh!U{K@HH z;ItQ=&qrg=y=sVPyMyi?5wv|!1k%*X;K%A~bVfEMjpLe(-yDeAjg25FaTD5ntKhfb zT{Qac1nQ>Z==9MJcZV?MY28`ay)p=2UVBZSXIy|gkJMqm*E;;K?Em-ab+EPB4K0Q0 z;P65f*&#fy;U! z>b5lRi7& zD<0A@y<)h{D+*UXD?p|BGT4^tLrP2+;DMI~pfKr3^pv;IB?}sfZHp>OZ1-!g=mV`}X{a$~K>0D!W*JE8c)*E^`cQ@RG-dUAEP3&N!Y$53n zjzZ4g{ixuURTn~YAL;?9C8j2R85AgXXMVPU#imXdr3SBLU z+?CUu;cQDGXQ1i?WNeRzgYr=@u~8L17-w@2tXRY)W>3&>!7MC^yNL%Q|Iz}US`Zzn zQ9R}xj1|YTs78+)Sd}a#X+dR>+0Et1YN$iQl6cO8L%tyPAs1d|Z@^dX4X~0gn2v9( zChvbpL38XWBKO`0RI@D6Ib8(w`YO=lyD~<~Eu|mwBJqlJG!g21LQb#@*p%dF__5I% zP<4{N|GXb(rYNyFOXadzU-l1oTB8^a^0wiid!`jjPKa?t4JtU?V=mw+ z8wev;nFBYWfNbt5z{k(BAm&ymh4&7zkX>nNnxr!Xvq>jcxCAj&3ExEl| z4ivt6?#bf(3Vd=03xnP=3OLec41<&H4sm=&KAJ0;F)6haWsg=-sV8aOCkVa?W`sR75plQ9?fzpBUq;&(g(}UkLnr5C%4L5=r=< z0Ad@znaKOe;q`GJ7+~6fNN7A%+Uno|&wNx}{fL~fRV8D?+HlWqA!w=xqj}FAlv(dW zIot$PGu#V%M!wLwTL;M;kRV3SzJFd&D1kFc|lo&I}+z_|W9oXKy-rd%<+ z3+A7?jzhBx8SmsP?aE#Q8EX3=$Ziv<`l17-Cd=s1f=%d^oyhb?=4EpZhLtabFsASv ze$14Ha}EgYBZau4-35f#9K=mwyU^+2NwPxQ3cEZt$yEDCurp;5`Yx-+oTFZ}lYS>{ zq4U7{D%%(T9Kd_mP2uxWM|wE0lgx)|kWY)nmQ@ZoQI~{2d{iJ%FB$o?w4rS13g&$j z!uvM17;41!6`tYLRQ?L`H%}px0zP#114*(V+Z>*Xtc7f|sl>gpmz>mHf`#Y4E9zHV zp?zOXhzj#yTlPDHI)4L37Cl2p31#9x)d`Yv-O%Pj94wT$0*(%d-5!J7pJ&q`IPrI> zf`}Q|9q@(K7R9i+%^OAslko-B=UNzb;h`uFZ20*QMzXKseU^(^{8t6{R3|H4-gTIB zc2hD8g|^WTO%gz!N#aou!da)+gU?1cqB%Ab2F%`qy?zRPcG(LBGxCsM+aGg(?13dQ z$x2BAeZB;Th1o9(6X#DYYWy4Hew z|C1)-2R187d=#f^75!21@hZG+R}A(a?!%cSN|1AX6Fx4BASH{-Q1G%Z=a9^Cj%TM! znW}mYkvHXn=5{T(`ele4y#5FnUN^-nbJ!lvoQH0d$blTTgI^<@0^_$kkZ1cmlpJn^ z85CvrmiLI-FF`w zA)EVtpp~w1Oom_132@1%n)P4Ev$Fmg!cQS zaF3=b=$0)(jivm!<82oxMKJcVkUM6k)WJVJf0W$D@~KW$n7z*revcob5wpDMmIc1( zawmW$Pn!q-3X;I#XawOmspL+q7sf*Z%~)Nqp9=NMfXL|zu>3C*od?D6rcEN0FB&2h zWf^49LkD-Tymb5T9(5am(+zOxo*;$UJrY(t6}iST!{FtfU}fS83$tr zn11wT&!0llVe7*=d2F0?zNvuwr|huXTL(vPl|X}c7epJTann3gVeX;X=(i>xzZr_- z_LBD~^YjkYP+dvO-!)*x)zh%U!w`Rel167yRq9%>1iIa$FpU2UewZIhbVuHiry*HT z<`P79XP<=SKV9&c{YT>9c#qch_>rdIX)w$)2NHXp;J=--;7-zAxSe?lW22Lm)N)qA zFXlHtSX2Y51M`tr{uwpl2gSQ+%Y}BvC0T|^_c!3$ zvPjT+^pM8QJ%&pspWqwEQ5v*uBNnG!g6b7%+&NzoIDRdSaAnh8;(E&igtxy$lUQ3c zkUoL)cND>*b;cN~b{m}kT!JgDi=kvw9JtSq27x$v@Shh&Hyky_g-;_O zO5JF$X&&s@9tA=(Lg0X)KO8(P10#>qk+C#6yoJf!OQUtL@sc<62~{J{7sh=1mWTrR z8=3Ex<^6WBc{$}66>L#}Tff(k{54xptB~TWv_iTmY(D5qJEF=@LmI1=kC!Lgl-e7s zuy?Qwm+xeE@PTmX;fugUU2`dqLOO7lsnA(VZgBdSeuSmdYrsc4kA$SUk?|+5h=P3( z>wBF_RlSC(cz_)17B=B3C?NnJ%isLrXy_p4KkpRoIpfPvbA|Qg@Np^UPc+Kf&wz!E?y&PC z!pBi9ygpRS(YAEPr^5VnTq+H$-8X~Cu>>6Q@nc!=Wk710prgVAZ5p&7^uIk|d?5;I zrM7_1oGBz(RhK3`vw_-UKWU-;5tx=OiF=DvVa@qFkes&?_VWHm*Uw+f&AwiX?uTP> zXXIV*tqR1=QxhPo!x8$=s4!1o0xoUogVyBD7)X2J)#3`8eMAKps!Ty0nM#axeF`&_ zI;f;o0-Cqaz&bHQ;9*(fohy8yV?#DBV!qEkU(|442|wiRQzkn)9>diGTR?JCFjn1+ zBtyRfaOhtkxA0j8DrPRGhvT+k8=FDT_MJeR?JRp5%0m)g?JQd(WCS-9^P!OC)ZE76 zq21~t^*bkt*1eBVs=t!VX8sjl!_(yJTT3XcQ!W$yxD@^C;y|`G8AtmWS7vJt;d}j( zBiNaUqS9$p*;WkR-Ymwmd#@4&c~>aq&qv8HE3|Q0gZys>AlNe({)>wMd*MxZ_<=E` z{pXCc=7rHmc5Y;B#zRc{b_sjWv;L#Hdf3>I0VQGINQyx?hO&9$yAsPLXvkr;o;v*0 z%cPD1vndsIgfQMma74CLX-ae&^(nB#r6cL^RzDH8FT6q@6-UD}iPyxdFp37vbjH5V zNAZ5iFe%GF4Q=iHlRwMwJI%ba=p!dO8F?4u-QHYA`BS2Ko)t zVSAx2Kwmixd&#n2cPdERfHnO1rwHQgy}M>4j@Ijhpy_ZM@O!O-#!SW+KUoN`4=l&> zi3j+z*bl`V*TJmeDI6u?o3K0R6r5DpO53^P+>_JuF^J`?_$9hPV#F6*1fK#|I}Y<2 zQz4mH5Cu(r646&j`i~^T`hB6e+C-l5_`Au3w=?_|4T6&y>!I4`9=Nz3#icp<^wmTy zPFY#Pjh4 z1h&EpF=JS_$Q5@#i-oNs4UiyLg3D!Y;Dr=vu$p29hMW7~GiRKfF<||_hczMRcsqTv zIRc+$--C|zYD#=YJ8)p)5g6KT!6{)+K&!D3ULLs)7k?F@VEIp_Z%?`~s^ld3-P=R% zCdK1I12YgkY6E5i5@<7TA4HEmV!e(zAh;|76uBGlX9x!_Emi;xi3K2Bgtvh9i*il5ELtFH6V!1Ng9Pxpp1&flroU2T#ms0bf7Ji^3e0oZi( z6(^zF5;ybSfvCzdbU0c9?sAWC@9xdC_>Vg5c+PaX1`a-akpjMpCE-@lGk8;x19i_l zVD3XFV(xu$mWc zS13cxNoh3v%62&-v#FZgFWO?11nX~afhxHjF!#|u)XW=#*|vKySZq7+@P@(^-FskT z=ZZ3)w_q;wVx{PMV%BIQRLiO3L+KVU4myIqfs|e>$fZHwDE&Jf;8k5SUXyu-7iL$H z5S=nCR_-S)`_sTF*OcTxZEVMw1nfY7syRXbmW%X7a}5>2RUq^(kTarp3wbQ& zq3)G4AVbRWlHNfm$#J9}J^i$q-Ce&#N73231K4>q4uUs-BbuwW!kfS<*l}VZ9L=r- z6_I*sVciO!&K-p>j}{YM;T@2f9f=a%<)|q*15M|NaO#w5pX{P|HNOiV_^3-$a!*%;Z#kJ;&kUyT@^Td7n;uLE*A(ES%iHx}swXaoaU% z6thbJ0ZLkEDG=kB7xf&cQv!!N780Wp-0}X!$0iXI7)@8gC;%}Xz> z)spXM@+(zvyCY*s4dtlDtNs?!~8rwq> z=!b)~pyud=CWY5=MRF31Uerca*L1Gjz9U3hB?&HF4o3C&3-S5fc~ENR4kB4$7^`{% zBxW6~}1LOgLTM4DyHcK__dNYf~+c>P3^Z zd-g)OJii}JT0Ro-#8l)yw!>RoaeUjAj7j+tc<7Zm{>$oQxu!3iy-#(Z!(t_9ipj!B z=W+7g_7Sb>?o%2bi{L6)x&e=33RWNSML9)3d=Pg49@e-9UWfYbcJ9y_bIh-0 zTJjvGC88j%%~wPr1GeL?kAS32ZN$Tj2l!$c6PX;qH{CDDW8w!UyCPV}cOe~Z(F0e# zN?a~C9a9rBK)~M++9mVw%qeHOIrJ$GWblK)p2hgYXOd%J=Z4ANVK`&TBUq5jh-t3N zQ9wKnMb<8ZQ|(?j-zfy&c?`g0KgCquV$9GM#+R<|Xh0atOQrZRkA*wR&kDr1Wv=k! zFuOav(IK5IoAB}BBM^G2hYgHd7F`lTb!E?k&$GuIgFI3zr)F$QJfpsJtt~OSbp%rI8;V)U9Is+7PDm)Iq1rZTOUGL?h;H z!>JQ#^wXkaRC+W5<5N$QH5WX&C+y3hUOE@@G6TU+Js3s}JLq$Xba>7()=ydI!=d0A z*e=kH;@ozu{lGL8lRumzc?l@K-;-0wGCa7?3)fee!rMbRxK)(tdy|UP`14{^2?(aw zm0jR?gd_YnFB6w9j)lqGI9OMqj(cZkDy*|pR4Uw72YhroK8f{(w8T1$GhB{8T0RhO zcF%nDlI7Oeyk``%l`PPAhu4hxv%|v)Q_W1Fu_Pb(B#glHoCJ2f-$->2ouQ9;6+vwO zGvvDw1fTz90Pmc?)F!b93L+B75#2SAY3_mdRSp6_^kQ3%7t>rlA<~`gkk;m4o!xn` zyxj>iPhG`tgTmA?y^~m_WGe!I)^+zK!WC z9}nS~WlV~l%c0V?8lZf!5#J0eVfO7A;QZVL)V_q#t`(A0hOx7*oe+jqhF$diUsIep zU`I2C7Ex!$#h7@@{FHj-^vd2yESz>9?;197&;3=Ug12`-$X*@jD*ixk<9Tpyslcgg zBw@06B?^k~1!_JBJY>wN{pVxln+wI>$-x`zJ8Sx>~N>9)w~hs=ZG~8(Y2+;BC=+})@sib?8O-t}K> zX?-Nly5S8v7c)rzM%J`ax(X&AiI}U@u}z@BU`H=wUvR{;3&z2BSdYZU}Ck`5rd0ciwXg%I!XW56lHK zL0ou}^850VsNjC8&vP2@?dqX|9jEcSQV#Y8=b+!|n^+cCOX^26aD~ez@EX_jen!N96O0d7$Z4t{Q4M>DexqN(nI{@sLMdH{pb!Zj60UMmXu(~@NQ*+|*7VGsp^_2r# zGMAy&hCI#|^>g62ER4>p4Fi5W0vyHyGHN;i>P!0QJI7#uQ#m$hRo0{^$7^e<%ho z{z~V@%|zH{d=e)$4|5K!vcNeCg)Do}#qhs7!RR@;IL16}4*`#S>>)PSb=bc3 zERbS1+}Hb!$cfLTt75&-CMycu-P$3^M-;UVIfKd|^K8>zd=N1QYb&Bi<^5rLc;FmW zIuTBmYaHQ>ZTFzZ<$T#)=N<7@O~GI9?a?pKnEt2>hq;$7!S9sk z)N-U8D;oVd04tBaCHJmI(d3L8*!TSah}{|?z16vB(qGCkcgiAjWJ1`Sa~DqR_r$gG zUSJkE3$J!8r6<^#`jDML#G|j`fXpW%WR*g%w|}IGVQ*+bq9aN!Da5TE#yFyz!oCx2 z;^NMWwSNzy->PA-U+4e|{4K0^u?QcWQ70a1uSr!;IX-$&OPyBiMvIM7WSX-M`0rVR zvX6Mt@NOj4S1TtE*GAxAjRzS^9fb3F*_?O6Zg53&Asnd+!Edo4AobZ1BYI~*X4M!` zc;TVozp5OzM|GhRe<(UQIpV^;7?b#SjD2_y$@fwEi+)W~ICjt051dV|)l(y@L?F1+4#opP_OgaC&FAZ7C(nMlmX#JB1ox}^hZ7=!Gi zemq9=bdXmM-+ephywW7? zBg;M4;l2&!pqjq`b1&Q_V)Gkt&nsV!UKrD4gl9vfI%6D2Glubv-57YRoy2U(z`U3M zY(Etay&ukFyW%mXo74d>+j%O7H9{13E`GHLh1$Ah+@<6;{J9@R&UapcvqcT`q3l!U zhi7_=)&Us)6pg16Zll~qArRP+h<|fjp>20Is+fsmgVS%9T9)+3}PkW!=inT6z`dc7=g{ znjgD+`k~ae_b{Sb4LYVzxi&L16HKaiP;o1y(d zI8JDGDK!Lg;T4;uCUwTqY|w!PO}z2tp*Hw9rH%V_Bn4}a`=d;UFOIYCM8>t7zDu~u zIK-Cl_D%xx*UZGJ7L2juKMjXg6cXp89^^k(0kYq|z<;AINROq!T;{z=u1>~ZecRFH zcN=w#22>91gPKdVR5pGdoCtYHxU<9I@vJboy}AZ&71+D1f zh5ff%(E5=%E-g#|j+8rOM=HVc9c-UB$vUOA8?b$~H&91aXkF5c1NIs?gC_z+uDOyG zJMPmvGnHvaVhjvOF2GHl8$dNB8Jv|~fI#p|rM)4%u-#P@)7OW=nJzt;usn<-f_2=r z#g8$C?t?U{iH~dZ$#drYGyb#?#y^{2Ne599TXYYbSsuymQZ2}&n1D#MA6|S@1Cu|W z!PAZ7#J)WnT_&f&OG!rzQd#)_tU8JCT!7YIZ=emDab@mL?ggh7Zo0k~kyv&D6XmU7 zSEU41yRa371k>S`YG0|w^o96RFcSsd?!mIX_i#Q>2xsJC4e@Unr(wg=_-AuDT!S>I zeewoxF)!L%!7TKfa)U8|vtj0GH(b6J;lB#Tt%$2*-6ealFxmntXFi1f_oL+Kzdm}3 zKLT67mQwLu(fGpqEjcxFloW`>;0*Z3Df*-jZp{SswL0PP_Q$ZiXM$d6Dk8qNrg%<} zhx2{*E(qM4Kq~}tamn*=&hxZo_Rrt=_didmpI~|WySbgp@#eUy#*A0j)bA= zJM>@pK_abnhwjO|3b0`xHjdOGPmUUtrZLv86+aHf@Pkq7KDJ}2M0r{Qu|>W#B&!2| zp67=j@$$$v3V3PrObFjz#gS}wgN7lJcATa{SJaNu$2SL! zFz}NiCI*Fb^WWuwQ$QA`+Qs3#;A_wzHi${5{J`kfX*Aw#NB%0hbJO>~#C{n~QuHMd ze(SzKu-FA1<*neGq5&T+R+4PhwNRW`1{a3TpqB7G5Ei#Y{mcKzLBq?8X`u~bUjyLu zW_xo8r4F}}hI$dQ}fl)F6$!xtn&GiMLW^(v7~*S+9W`Xc;~Ke+5l zbR0JS7$ALlYAmoe9il>hQVrh|uxqCbUbokQ$I7>e!HMxp}FkoYqkz*tO0FgL_uP_8)3Q={W_bX?|S2qh9F4ewQ{H`Y1Zy ziyt28LD(C6yvU=7ZU>t1R<9u#2wcH4Ki}Yn(Kp~I{s1@2%h5#PiNTJ359kzdcglZw@p+mqF6j?TT-BKw+(wWw>2D4T3Si%QJ=pc5maJ_E=5Fxx zN9#O&n9(f>W+wu0%Bqvd)O66WVLG`6A4GN@Lf@O7aAJKp?7f*mN(306e2ODJ-EW=EvdLMd4)k zlP%bHDHwn5iiXIAN$^SFBX?UtJvGu)K)J0tASJSa)17%3mYUCp$Mr0msriD8eCNT% z?=HeIzj@rQ@xNqy^b%Z?|A}i^rB9nn3FKa}KzHR$C}MgOKOGP!d^_iY+11(btY|(+ zI5+~o41-4=-3+p`BJtpybm))?1oQ37fZk6c^GO1+`nwVGRi5J59CP>}9|NJXPDHl+ z6B=5$65-@&$p6|7&Rp-sd=SOLeW#e0@gM2(3qkcSY+hwuVI9r7k0$eVTtjx^xyq6Tytxd4*JQ%Sw@793Y8ftIU$Fl5(&vc~o_S#t}ysaTA*Wr-m5 zvwLK=EzSYldtAu8V0!soA7!2T6E@YFC1w@kZ?LPEQ+ zKsOF|G&3FhuL!MVJJZu1GDP`xG4UD*!tTq*IDgEvF};KJsj->rin1lDXE64%*@vFD>KByNj`7gpP#@|PL73Gat3+M4h>Z6{-L6%w}2 z2a|^l&{8rBRxRF-`F26X>ZT}Wn3d3_KCJU5L6}&mIpU{0_P)Ebm5dD?=W2csCIXp< zaq&ufG{3TyY}BUc`$Z4tY!Symg(TuLD;iCIMp0YkB;c!lL|3!-Lw~OniX`ZOeMc!Y zZ2m&xKV`tVYd^_`Tfz8Nzy+qgJW5OcWBcK1Blg^I=U&~e4$HrVP?w#VU~Mf2FC%+N zPL~!2N%xY?xQ{#Iu`J9w?+>R1?I^w;M(ctKxDz~0>ByrPl32Z*-RrB+#rzUD&)P`D zG>l0{oeNAcx5dYj>(ORcJ$Nr?XMV;hd_0s0bxXuhPt+6i?}pLFkFI#)dk?w}@ZiN5 z1i6z+xZqwCF1fiM0yc(|5|1$W9N++#+u|@~q7<}?Bk}dg8{nE$R@TPnNwU5$y?9Fk zo^Q5CVr2`rOtmn(?*|7k^PxjJR+gowUqFXq1K@5C&vl7L=24ZK@8+6<2 zgOjIJFD>*~vn+;YUQhaF zh77L$&zCwkdx5j;3Jm?lc35+3Nb8ar%tJg*R_>aC-~ZLZ@dYcWi`y1-cPgb#-%?;O za1|zq3=#VbQM5Ybj~7cyuqGoD&UjBJPkr3MF|QnceA|kdk`^#`cOkAeY)3jT62y6| zVd2&@Y$tq=+jB;hv;S8NCvo>NG@sIdF=}O`>Af6W^^b&sLgv|PyNIjyhM}>;BiuLp zE;_ii&^t7n%By-q$Nc$_Qq}?+gG15%$T;WI%u3v9ww1mVNJFXTDtP}|DY~f?QpICo zjJ>v=aoD`^f=WMq^V1a_{8QkE{~ZVmmw~1-Rrofv4#wE$H`evF~{30-_lp#|3}ez_~rP$aoosCMv@{?LP%wV^qfnfNJUacB_e4Up@ppW-r9Rl z?fKm2YVRE)8QJ+#_6+%*-yh)h@_M?T`#I;jKA-oy3>9lFu}5|RcYIM0@8M$)yymRL zGo?q+=5r^BTM_~@1^6)iXC!$Urw50%6(DuN15my@4No0@PWx5`a}(JtR)_Iw4;=8t zQom^^@ID{UruIUvp9GHh)?)Ky5%62XIGq!@6iugc|; zoq{25Y6QBs^#hMFqVhJj;mnUO=!MNH$eq3oWv%&W?3#g7TLWQ8dox5Xh=o=0)gZU> z2;DPSfEPBWpq;KfGzo{|is0+0T6zMi_4`TBoI0?1m<9r&eWYs70md0>Bh_E_(UFE~ z9KKo%JADeVe#bR>ZHo@M@92W=z8}fqm}pWil!MX=ibOA9HI2-UBb}E@p*qe5ja>@y zcF1gE)cA|HY{NRJ+&!60X8FRo&n!@tb4As>W3b&hm>3HEhB+OI&`npN@Xp&r?uR+P zT-OG&qJB8wWDFw-vhdZ8?T&7Ql7Yo9;eEw!vQm5(3XJo_;YX*y#Fvk~r(Qt7%=MHP z@4h+u_GDbka{O-AV|>q_!YJqTh4)%sfQp$1Ldo$bbe@L|TAeP&>*e3*e_6+2={hI& z8o7<@wnY%0Q#MX=)g&qQcW`65ITv!^3a*IuB6XVD;5n2{mv1j3(xGDb;zJf5QO<|@ zEoUIVMH6Z@HG!Kef<>B;0x3Y41q>DFWmH+xhii2@YFwBz-Tld z)#3usWz-$h^NYd5f-#&HpTGwqU%0`X7zk2KL8B$rgthl+nMVc2oef6i!<)H`U$_d-e`zKasuWqN58ie4(n&)Rm@T1_pvt|vJh6=Gx#SfKh4bqTy;V&5&=R@4|x^b*M24+b-fM?PB;n#0BAg3n5 z_-&o2u_qI()wV+1`WQU>;}8Zf&Y|-j84+2bFckdthl*{sg1mq**j@XXTA%QQV@7Ad zpz9tq#Yw~4%YDpg;|g7i(;0__^mXQy?z$`JtYtAuQJxlhevR_rwTWp>7&-x9M-q;U{z-f40aWucg7Ml z9%Jt^T`RJ(LxFto|E2Ob$`^}!nfqfdh5M!vDlyNlV-jO%PM%l^Ux%)Pmvj_b*v`St zi#@P>Bpj{OThZ=X4w+_Gi79@;M02bLCq5VCyo_D3EWZQ|*fTddU?cQ(U&l$`Jn6yr zs~B@E9U5j@z`Z@CVBWU{ilRF!`_pdXkw{?@HT5|+@y=EHZTAD7(1UVNzLG=a#CSa6 z?^gJA*>y;w3wWFwqEBZUG$_XL^lO^&$C4DNYd3*Q$}MC=$RlWDU9uF?ud+F6F-+rf zLHpGQ&Syh2ZCgJDCylLyx;71x@>UyK&m05o?sL>-=L;GjtBH~y3P68rI4X@#MKcY+ zkW1aTpsyK+785Sx^B?B_?jef#TQIMm(v+)H;N6*vc;T!z?A;K7m-;fv-1HItcMCDj zyR{zWkIaR)2G5uy@)mFDSPyqI@eT9JJ)z z3#u9z2zj?=!h9EooDPZtd~*nlO!weD@hp(3S&aqD-09Bm7vasqLwJ61JRY>GpbyI} zamHycFbdb>md(_l5AznO$ghupl_`(trtsA~n;q^TbnYw+Kh?(h@}7hiY{jZG^Wj&; z7>!hq#=zt?_~?flY7g9|WB%FrH&Puhuq;vD-5hdeXEpK^0ZloUJB`*u$$0~K$tMI8 zH8Wvj{biE7FALT;d*Gn$ZTM=`h2%vaIrY*V%zcDdZY&oDh92O0M!%fz`pD_OticO7&1#!A~?R^i9`{_^LgnM%8vn}B49d{zUaWibSyAG$d z3b1eRHa_0UoNM9BhIP5Ud#sgvgb`_$>Mubhj6SSK$i$zJp~N8vQ}pWEw~t3lQlS zd6;NWM#VFH&}xY{W<6n^>tHV|G_HV^@ARv!v%_$*MLcNKyTX|{i@|&?N3IKt;I>!$VMKKyc-C>ShkZt$ zgPQ2{)ctt>)f8@;;6h?!YKlu%-o}+bEg`#53=-niQQpT36k_}^*K;2=(-MU_<-6$q zsn2n9dMVaCbjM$_f@tre2rzmS1bgqDhj;D=;gWtCC_T$ZzYq1SGdzGL6VLFPwu^(1 z>;t&{;1t<#XD%li#T+xNa~nD}6e?Ri(Kyinw@y~ZA9ISR`G&n9_sa=WTG+Y!$Klm(FilRz!n12UfX(hWaL zaPdJO(58)G{-F?F=ysv)?LGMERw#;0?8c0tfAsAdNw8yc%)Jx*(1g1~Vk9`cWyjt% zxoITp*IXE{y%HZycSQ?3f3O!Hfav!I#N><>oNZ%X+P(lN9#+N87uR6E&kh_v<_XuP z9>>rx?)Yz)7L+^1hJtE$aBjF!wq|JSk4<|R|LT9|L&q>^bc-OE*EWkr|`xJ9!1+H z>*-%R8`L;617eNpRsK$KCD}p`u<+t|s-YIcFA`me6XvhO&eLu5=CN4-d(!dpcpZp4 z%W~7kd^AKQmG@E;u!v=DR~zWVhD1HkH=b9yqqLc9XfDN3R|?B^*FjZBCe>NmiZy>? zdFhMJLBk75R(CnVfF(P>9jd^qPkqt(u{awdnWx}}Mgcs29FLEF z2VjQdQ8-;6N}fzM$C+!|=zO;f;V z#qLMl)O4;i`7js(pL%!UX-~$#d{P95qLuNv$bH^-yMC&cI$vdTkRSd_?B~?Xk3mnW z7pk}_^4}B%QCvR_t;}x0Xq6JTeLErZE~S9cpd}vG)Th_S%i(>mN}@Jrm@`}9h1FUw zFwpBF-FRIFFUfr22GsZCf)2(qvONe_%nI;_g#qsRAdD(mRd8QRjeh?UfWIa4S>Egh z$xla!OB#UiFOq0mD~_tKS(b0yN!s8xKn5$)$?Dv-Fl3X&n8^bb7Ndcrn(kia-Y*ouxv2+CAuqt|Kh@mWB@h%^=6^OL%`x(n-(E zaA@pr1`~_PSW`3=M0X3X`LD57?F*?+It*L%-05=BXxh5M5x!dPB9|hb z<9Ys6)DB(`3DT-mc4;UY=cM4h%Yk5h{VJ@kxrw(HR1vk;jhut7CXxE#4H{2N+57Su z%QKt=sWCrVy@!u)Wo$99Iv-@er$bT9J#ObNB{27r1-Q5uZ&O`Z+4l+qy<2cjp$3*0 zE<$Q4iBMw#lWtbSMcGdFxwBlWMk`r3vI4aZEr8tYRou`-#${je8mkIJK>8CqC-*PJ zMT#$R&#u{ItV#?OZ)M|9h8t)-u7&Em%#}LT4m-TY;o!R`+}@UlWPNKHyBAJ}1m|k} zz^5d{KKQDaY>$>Rt}`WOY2GBaclgY9D!?x)A4w|1`gR}qIVy{^n-Dv z(x!pt8wzl~$L+z&y&UU2^Kp(@BTqCe4qq?10tdD@KwiiKs=)2w_?wsTUTOQ`J$Xtb z_IW~R-#?(##%M9M*lIzxt#0Q|Z-_(X%qaK}9|k!C!&F;%0nP|q zimy$Zc!j?4khm-YdOoh<3x2FeueU|u^eUTY`Kz3CXjQ`P`~LWDsWAq5VW*JS^;)vdyec&X(<}mDzYHQ<-l*e*`U|Px_T7_D510$-fC$uY*#e)&k{f{M-}kV`o+tA5Dt5F-jG$I zH!&}0I~_Ci?_X49osh0ETtBxmsNxJ`s^-!X9Mp1tw^uj zaYB(vr{JBe3%*>i6kg&tvetAKZ}#L{5Ld4SfT>}6I|ibq9jbaSw%N|+6VD{g)l8B z4Bx!TM4d&cVEf?@>FAe-O?}(3vnLnocZuLQO?ec4)QUgK##0F*iJKbk;bMPxq=4!>|p8Q16dl)P8-g8e~nz$aIqRNPpDXFsaIRg=B6*Y^nM z9vF{~El)vtcP0$Ic|@N^wezG6bU{z*7K?GF^W>L!;olkY>AZ`6Z?6$g&f@iLfXqzt1>+u~>sGJAk@5DjBu>+=k zEkmdM;$)T7Labr${I&ZY;Pgp<$YCF36`4vLvEGK_F1@65{X}l|o;>m`UJp2_cPbhZ zRWRr4eeg2TrI+*sAZ6B0+_^E4aUR-Wv#KfiYK5uJu(3l3mK{!)1MEDBmP*Mk1NM%)t<#MOJKtB8t~p&pxc zor}8(IZa;BxA8ge+VbPDaL-3Ndwvh7wdca2@4LXwKa6g0SPA@}@u)j%A`KnU0mCW# zsGVFn@7>agm~*+Gl!EU@I$>xtYB{40H!#?d_Iv;$V%>Vu<; zSU&DwCGp5-E^X&D_|bI@UY#`~RYP4c_u^r^@7M;NZ#v2Huguf*hCcX>rWDxsZj@Cw9@g2%v^96zl&YwU+JmTQL<@36>dj&!qAs+ z?EYJYKC(4*{@(k{#+-&t&nsc_V-IqBtt&Pu)!^C`cZgUtAF5vQ(B{)X^qup)?o^V?|&;81KI&kDZS-&m- zwmfZut1L@%@xQ-h$NVmg_1Qq1>NmMH%@PK$f zo|zs=*9V0_-&Yk>T2+kGSl)f_v{2ak4xngNDQeshf)DHR(P^{{1J;?Kq)I4k8@0xY zP%F4AJIu51YJ>RbB+NB?M^4nZK}%%>G<2;83D;cw@L3FN7A^(Zv}a^`?_T;Z{1Er) zr~+ucx{lS;tZC<@r(7^&V8jI05nhHZv>Pl&fv0xt-6ag|v=r(FWZ;1(kkZ>bv2T1I z)r=0MOBVLQuRV*=OfsJGTa{tP17jf4F?9F1nY?1B^|*N@>rf`U(4)OgRCn_p^xWtO zMP{uq{iYY$*NQxMorR!%f-&EO*!*I863)LG0pIJQz_2EmmUMh2mmdO}n~VpGvmMf?7j_->6WtJr!Wv?_s;guKG4#9 z9WOdJ}UyT&NZH0fI)Ue>;Y zdp;G=W5Df}qbOeES*z|uc@G=E|kRo7wX5%<4z2e%A7G(U1m8~%`)>5QsB<14lGk;5Jb zNl4jP22Oe2BwKqiO}(7UdN+P}{7W$odqm?yGk1vnQ%mJnDq{Gfa#&T!*vjko;>gi^ zq<{H2RH`^hiq+5KtS41;^UF%i-*XFhu<+8}C4qGM&CfJNzKra+v<;!nU)YEf>()AMgVIG5wwZ|&dJloZE$#>NjWdAuafPhOD?y6ed#tCJAXV+12@IT-TY z8Fda-qQdA2Xj-umGRL&BNp1qI4cSdxf@4V@F9s`x|D*DgJE40^2oaLpi0ZmY%F}8B zVB@|r48If)ZzYeg-CP!aF*=0#Y+tf#6~~pnN%zUY5YdYUSzPY*L-T%p9NC;UHB_QN^srN0_gE{CywRE zU|bq=MX>kr%hY~)HKYf+JZhnIN;_s+$HLiyn|SY?GTfe)L6Q?p!1bRTT-TCd67(}4zs&K&UsIyN;$c5Ip|cr3^(}$qzS(pr z>M7McTTewquF)%gA9xEb?~}8OvoK);4^BP*Lt_Qv@veOhWV{ZB5}p>Cn>pYrO%0+g z!n|g-rMy!Q%ix*HesW~F9c0VYfs{ZV4F8!5CaX-rRzC#8PTwJpwQkCLX}1R-%U zOxNB-H%`mM`})H4OK39n%M7O?KO|wBL@t-d_JH9m<1+ET3E;W$JK;AoM*gY|WTZ42 z9@N{xe3skmSyYIsHtUJiv=E%Ae*`~VSpm!TviSnzqPw$B+!~)=TsyayW{4+1Yj6Yp zldXiNh(R3HD8RP5Ae7flgWR-GxU-}iW&bAfc8-rh!Ehbea5M~ho=0=TtJ+k;Gec1~ zA&>RqI?(-^75?!}0vl-wh_f#SdG!qd6Sv_^>qfl3;TauK5Qd2ATlm$cpSNuuV?f)V z#x2GhQQv7VY+cp{J3KN$(xjZGeoi5Atp*@4)<7RP`$5U&6>!|umX^iq6475NkkC`j zCA~ZZ(z?~aX&=Wuv6&e3DihXDGlI-w1#mGr0;})GVcb1FR&S3cZ%Tb&-9AcX{ZGK_ z^NXk>14S zco#f3h$ZuCZ9syiQPXLmB&g^nY-E#H^vyE3IO!FMH!M?$;HjZbV*r_+ zZiAm3p(p7t1oM<)Pu%b0DTw1ZBVZ z+|5cq-hAU;=(Nefp>GkCMO>kNb0iMWj;6IMW@ChE1bmTwOH2j3V2y4M{@NXnb=$nK zy*2}qd@^9BNFq-C(}{2UZ-7sZ6c|&$4wFYZQOn~ZSU8ws_ta?oKG1@(R>~Nwn}~G0673dZdyP_8BC)&(UJo5oX$Z{b z4G(qWSHaz2c+e2SH6l@KY6|=q9|WS+)o?VU1kNlGfpI%5;ks#6W% zY-jg*;XdA5?N1bz=2QDUe~80{Krq&BB^4}t9e&>i#YHS|MM*B|8Ro2VSxV;Dk*$1biJwf(mwGbxJzgsE6RPF@~45w}PHN35Yk8#Dqg)K&AOG zSUm&P!WZGRSsBo_fsa!^%tc_F?qy2Gc=g15?($7dtW#uc{p%&Tf-z0>96a$c%hMch zX@hB2eB{jwp)s>3@G3G^V{=v+T|4Ore0j<|vkzJsGh~ox?%xf^B6}b%@h=$-6UXVk z?VP#!b5so?WQHr7aj-1rig-2h_D>edyfnqs$$dbsI)U~zeGv8D%>6x*jZ#i?LBBZx zti@CCf=(rvo2TQn?sn=hJ{3F*9^z8JGElf)iHlFwk$&b0o-cP6Ui^JQgm4&nY^Pj# z;wRPFQcIR^9w6_xOb6AAY==A+i*`QiNX=eHnE9^^?@I9?K5ae>x8-A}Lm~9*mf^Zq z1rU6_l8j1Bhs>w`9F5%!g8WsWu;mC9aGrzSMuO<|-2+z_ALAbOuZJE;$MWY_KtSy! zu2R#*B>{Wzv|blC<7pBqh=t;PIp*70VnNoX2tnXjENP!)2zrg;q(OED?$H)UqbI>6 zKk6ctZK=VnMSb*P<7tSsb%nt#()g%L7R=NHpv!$R9dOOU^9x1rakvM#SO#)-0VQzS zuMn1MJConD7Gt+xIxsXA_B%{~E2^xYop%k-v6=i8o6T_a^e?J*lusI87QlrMY24k3 z*%-P_po%+0VfJ+!6s@d*o~fzuW8xI@)$}q|NnZwP>K%mtLltZ6OQ51r0tPpDp~A^S zIPy%BUW`iw)7ZmcD9IemiZ|)Ne)hds=0XgV)A07yC~WM%heMT-!~ivtQlWRS7q|aHIq_E42cK3nH^N+k`n&&@Fc&z+^EVD zAv`>M0l5(qZ0bHqC5oSuuBpxRV#iJ>@=HR;wMyXP`-$5Wbp&?(;lo!sUGi#72=`B4 zgf1(J=mNd~eGzdBUd-6dGT@qcuPhKEEKYHa`mE=8lzE}&_2X;RiEzgMHg6~27Srd4 zaK{saG0MA&e6d)8LA=xS4?AZWy`Baq=G>q=BVLpDYlTosa|*029S`=?JkZEpAIPjI zbf4db5;Z3Hw^fzCujSL>J7PHUK@Y>-s_DoQfAoLIy5xJ7q1)%@bYdRcGyVyo7rS!M zK*I%aE@KhNKH)sx-$c)kJ|L1X%sCx)1$VJHNOg5U4U+&`8+!)yXS;&xPzrpIyMehs zwLwLk51ZB7U~XbK>KA&WX?-$YcwGl;vi)GQ@N7IfbQzxO2aspQf9TXTrzo$=7h_6) z^E5hh=_@4z9NX^%8PikAxhY?$+2v^7343d@#j=)GO0(URW;pu|o(doSO`>xODzR~& zBnGMerHT`B>4Q1%NQL=Y=&QGcKz-IlVgb(SLd-dSArVd;4n#ARDu}lC<(&_BOVMN> z@^|Kg#E3hlZHs_2(xy;)zKP@qTk`VeN#OXNJPaD(qjSI@Nsq3fAvFfj$LnPVWD90JqdFy^!T2%T231s~j)P5bvT7yGzV zWU5LKK3?dJ8Nuox*nbqflPXYN>?nTLRK>Tgnsf{0GY{Hgv|IO&ddmmmk!hO<_hb@w zP1XkQX|GA~~(BC|%1BXN2IyNu91Mk*GfQ5V}eX0AH_?(l0fYD~!zU(SghWYcWr5x!6 zhYD~s3V>%Di|8)zXxM&i3f3h{^Zx7WB8#<`;hppr?*0oISa#zbtzJ9;PpTsExvm1B-prQvY`TeE1K}{1Y)JR$K19o78gz7N49?FA!KcDW zsNc~BYR4yWmZSS{-sgGT&v{>Y&So!&{-+>#d$onPG?>jN)TdUa_Bd2d7D~WC|GO}l zl8LVcgTUgm7ToQ0Am_^V;hDLt$I`D0IT9X_F#ZV34nE>~m!xsK_0w=C#ipE@R%#aXUxIl zqpdJ+TQ5A!I!Q(hWBAhw%i&&JB|aVaNtJr!VJtrdekUm5Vb`yGgEfrf&kDDHg^x0q z=OA_$Rl^m-I=bUY5?p<6g3Dz5VOjbzEbf-T_l)gyqxuR+&1UYj&;mNDZUTXhg>Yb* z6#Z{Gz|AGmsNXpYKONErEqWVv{Rl@d*WI}K;2>GJ>=Fz{T!lr2N_bQ(6+ccrfq!Q+ zrokHr+ErfN<2Zeq{`2FW+q9PCg2hDv^_dkEE z%`Zknc5ZvmdckDZyrmy$foDy!qRg1 zaa|0|^sbV$3trG(sR`>Plequ-q==z@9yuz>I#G-hK(5KqZ(FayJi)WDc&`k(#XOSV zR{P*h18Fo7jmL`g0?fO-8A{my^Qb=S%qlw&r|qhE_)RE;PgTWdS7yQYghDjbUBSBd z2`DrwkGiq8aD3Z{O4i&p__|ydoQ)a({&_oC%AJDgo_F!Y-b}KTE`;8J2y!E!5SK3) zCQ9as@TlSdKFGR9WY*bY6WbBA?23UO3y$IvhZ^``>~0z|m`hcrB}* z+E3`K^mx1tw;no&tCnhEOiDK$3ofBA+}4m7>vhyx>l*f_#&e^2K47RL0y83VAjsz# zO|w$M12ZzPKzuPSZa7RzBvQH0l7rMj#||9s0Vk1jguYc~T$BHnqsNh6^g38XMTAQ@ zg)BuZnedJx7X}-v+%VyE1PEFc!GpgyVaMVM++*a1aXHsORO1=GH?ddQ!p`@jEMsCL zbf4Ul9EYYiO|i-E2=hCBpwXwxP^=}MCaJw5wO4L&9>0y~to!GX?{<>tSzIF_Ura%= zpbBF13c$Q81SehT0AAQjvhu-1dT`(Z^Kvks!V`Jy{+NKq8Cfz!LmuFazzh^V(*5x^vt)WNF)mOj`dtDsf1xZ}Hv9Iw85H$ISTWX_5zdLelN z-oap^f7y%{Sjmtndy2>pQ5P82ISB?yyNS3=4t!q`i`wGB+^<@Ipztv6W??y}7W{|m zKFlV%|91v?=@66A{g5l93K=ea=r$<{G$tot`;BNSDYcMgrqZ!^Wh?eHF!y|d0y0kw zR$p?Vmmd|AVoeFqeb)g)Zih&~icI*s#0*{K*Ptdpl5rE}!Cen&$hNx+9>=7>M>dUo zzp;mxH?tY)PadU(bsO*t%j|gB{@_ARviImo8F=^T3w_vnh>m|c6TCbqUb{9IS3e7Z zV3jWXwp|8!{B(FWDHKZcG9dQ)5Ax~mCD`|=4ZC@TH1>=p)vB|*`3$MCpyPf3V@`XarqDSWH^l0IREr00(A?Dge7j}=Sz%5UOF+A%Kd*9v! zMW356)yxH#&oc-4pH_IHy9p{+%aF13B%b4|5HRfc$eBj=;mi-FbiYLv{^v4mO0<1k-bg|q{8?N+v8`}-#IG!4>W&Bs^NLo)-n zd_RdL=FF2l(Hm)U9klj_z_-n2_;@S<4$sw~D{{lIeE19(pIlF#WxS$&drDzsl^Y&m z+@y*h{@CvDk4_h)Agy0OYr7*sQcr>ZC(|BU4xOYPLR;}s=0lv&b_;%;p#hxa;{(X>K`C*$dl&)9ReLM#Y>pAi8$4?TX;RFstTs_MCn!MdvI}|HL=mJg*on}@S&^@ zHbz7csl@e+?dXPC<#WiHIiKj8uRgru%q6HLxdjhpC*o^+05jh z0{=Mc;QM(BVEb!6=s!w@snf6G)#+Y1sR0Ao3RrT~5zlx8@Q#=z zfvZ*oX}76@trkh3^sOKE53i!r6*B3`Ew?f0zyC59!20Utt=&D00PR z+)|JW+JpxiZm~{R2()^L;aEJ&F+D!QfbTAN{M>1@Ut|F1`GM3`W(h9(x)i5*-(c^u z$slUI0#12^K< z)4Me^!SyVdZt)?@S+3i_Et_W)JPV>AfCx!Ml7&`{7-8;AzsD&7eLJ0u*RO}AvPu}6 zG9UY%`k@HxLrq<;fP49ZIJR*-e2++jIlgiD?t(pviupi1Y@`o$wP3SeIMI%zPUj?Aogl|+;Y#f=MRth)Gtik=8Ayme9VV-cPF~1~YSlt>Nf54j zy_&J4bHU$iBIx)%!H=Cq=-Dz3_l=u^(s^chqqQEQEaJExr5ucvA#gcs5fq(rgf%S3 z=ewkzXvu8CNh@x`oc@UzMJ?ems>ABaByf}YLnerNU`vH0UcJWdB>SrHkIy3#wVb1i zmHS9th94%WYvalnbueqD6}PdMWs=9#a6qRI$5$8Nv!~%y`OO5_-g%cURXYloJ_Hh# z5Bni;dp!DH;gb`;jZu+AL9e4R3~JU;#j$j_CB$Nihg3oPunO*3lg&6`J=p%c1u{Oi zqW&@ix?oBXx?J!e5sjL%!z0^`l2u zU*p?k#Q<3kEMt~}&c!7#e_tvzN2|FOE#Bjs%x;u4sL~9TE-J z<9{oX@!+$c^xda!lAp`*LnE5t?VfpftTTmi$lRfJ&KULiP)<(XSwa`Zr^4DQ57^`} z4&!>>au$E5k=EYbFx#d91j7%~amQIsG`yCSYSRoS<%p`D^%LcyF9yfTDwF_3TeVX8SDI8p@1}!7Wn5=u5 zH@rv(RVr8G$bTpC*Y;3MwO}qnPjj5Mzy=S$4B{q;DWGiFLCm_Oj`NZ_!RlWUS|IswoUp0-{m&SQg#6{}ir0vQQefZb1Ls8;Pp~i-Pn-DR!RY<; z{Qapsm&N6$x+k^1ELIj!QRS@t;VL zGIa)%R#CXXcHbY2EAiBxDA=^U6K=UL#Nq4)Trc;JmQLA>+5Sy9$Z}0_3iM@rVb zKv7dQ?!SG_nD6fhyKC0a=`}mx*gR<>7vxH=l3*NU_Y2GSLDcGO0w^eJfWq7;oP4j0 z?7QHJ&(B5RA>;q3WM(Kx2)C7!u`Z~Z{>|P%3o2yb!0`a;Clo_37cQpJn^$7= zq9){f7lEB&9#3F@1nd`gV)q5cKClr2$)+86VC4oVpY@w6cZPxOReP={GXdW6ZAsJa z+bAwphlg~B$#cCpNNAmflXvxk^D&OD-1?SG6j0)(lxgC#h+Obu-R|XyQds$Q611C{ zbFo|dcu&KhqTbspcu>$2a-wH}a(NAR=RyHkDt_U-w2QbSXBmIp<2qbRUjj?*MDWD^ z?J%pZ3=*?zFeNAxHw1>1T+yi32FXOS$l`uRr5Tk^HQL;S< zRFa3d-=n>t6zBput2?2|pbEo>V4a09xNOoVYv%2MU$M_YRWk>7&$h=g0V6!8T!9v?9dM$j z0IOexqrBfc(tIljRcD=q&81fW9IA=f98*-?w4ZUzT}X){$2%sU4eQ*haq%k+th=@y z-aBW}jyn-(d`=0~?n~fvt!#L4cN4Zb#o?m*1DNvDjDG;5&`T>B=L=uQb4g#wyB#(- zTeckbVG5me>M**0iNw3f)1jZ8_ielQ^gyi)oH^G9YwB;puBUm#MNpe6Y!if=ONVJz z>>AQMbOcTX=EBK|FY$S%F8o?&g}e9pz?;nTY!8zFA8g*zH;jAnVCWEY*!4rtdV6d; zunG$2-@wA&Ovq?vGx`7GLH366@&`u-#X7mD;8hSud zy^pRi7>`Hx-J&mMG=pNNAg^7t99A#zhk!@#$lU;0lrUWeOL^B|VrLu-PYnR?wY%VZ zQWLjok`x)8wvP8`q#6J4|I!w1FOC5`>2s;u@Ok-S6f!&j@;++#)M+c%t{n-}pGkwK z*kusn9|E&K>^}IBvA>SagbOi@doarke}83NG2MBXIlUZ~CES1t$11#YaR%O4$sAlV zOL@zh9*}Z#Kdht$sPCAA&Pvg!b|x3%HU(nsfo?p}-hq>vJ8ioAzgO{jdl$KHWQ8-^B2lnA9QC57()WhO z&>naI&NKh))%r&6>SceJ{nH2M2$oPC;p$ojz-!SC#tbVa zpIpzPp`<@p@9l-1uN&xQ_(#g`7Q*oMF`_bI2E0zH#*Ss0a5Gp0_P^ET>bHC1U6*}m ze_$JVKE4DqlG@4N$&bNL$rV%_1nBsy0W|GG8}hHrLQ|VW49IlHkeY?ODF@?l_uFT5 z{#PX!>Pm#I(p~VWfsY0CPe~)252#k#5X-R+m^17ROJnD;U1|aD@CgCc$K4PxWg%XP z49ET_X(;@nnvR?627Pa8RHiBb96z~?YcUMMp)_~AR=Wa|&9|a;hA+t;GQjaO^l1DN zS0dlL9jB;<;M#(5U?NwG4a1)~CA)O^v9*SNw=W?l@+AN}s?bKn38dPD@Q`man*P)! zyPZ;rV4g1;+>T`K>=Jw;qKA)-&cT_%CY*On9JjN+^=WhFK(U$zB{gg5r^+@EICB@Y zbL@y#Q#D>|^5Gm_Ed;$4%tpt~?K^ff!f@gQ82nm;P1KB5R4u}gY6Il=G!nPDKgrog z!RTDj3QBS*xOnIa33hBloo5?Jj?@ImQT;*s)YNg$lVh-hZl`poB~-XSr)3e{Bv-=? zjTUEMqINx=-|zsJ+qpse!8BZSXD5{G+k_vE#G|XGEk=k|!>i`z+sJRMZON3%1lI02Nnum z=WUr2fUtd(d(Sh$@om`d zuaE?Iu4;o`rEb#leSlWw9m5l|pTL}^-4G`k28t7%xw8^-I9gEvdlSN7lXMHbSMMeN z#23JjkRpC$JGxXV&9ZZ=VEkNG?%w)FuD`q9QS0hqjEgjjB4 zj@XrxK_{~e-)0)4*dONImJTL&K0O7yE%&iYXPDkRmxrIXxznvvIzg?UeE~Xe(W!}jhzH|{BR5T_X zvpSGcH|(=3!v9rto&h<(Z5Sre5+W;#7AZR#&vRcwOQFypn1bGdrwH60o<)1bJEXZg3xp2X;2rZMoa;3O)^;V~GMlaN z_~LzV+|fYKO}Gt3wc$|IqzpoH1xV>lbN+h6DBg0LbtGxdS6E?e1Pa(ghOLHR%yl}f zsn^5)R>q`fy=14wUg#Q2xkF~zdzN1qDhygTt^y0->8^yH)EqA?nK-G%B@2XbRk*Wk-M6}qud z31>P~;O#RhG|XozH6KjHj>@@Uf1n41+dq)AMK?fs7vo3n_vFH_DRJu)m%{NNT@X2K z3ae7s@2BVuS$o)sSiJ9q{QV7lr<;`|#Z?J-*9PEad@CrbPX)Qv#wcFlPqe}q$B*S^ z4>%@Zcli$fw{F&>cq)d%>+0dWWfC1{e1_#cXXA)>FvhAh;hFF0;v4zNn7#lr#8_4QH*VCpHbeOG2J^Z=mbLPwTO1XE=QRkg zK5Am~;dJi(cQZ7=*Tk0m;YKc*LhrQO>;?CghN!(Gd{a;7(7a zJ%u$c_Eb7G9*OGwY5 z9H{gSNAV?>;p@6VQg^!%gMA3*#ZPCS&CQ^1pManLM$&Z$I28X$m_KC)c!;iq7qbVz zp7jFu)NuS7t8D1GRlsz}m6)Rxjyuv__85os~?Sk0{R@SK~Cy7 z46go2#$G*!BOjj-qr+Yx8a9s`VqInHTa)Q|HxqO4}6Cu=95H2~z;LUF{&@x&IHTZ#K;{hcq zC9Oq8ZySKFn+yz()5XorMv$P;M%-L?aMIuiR}!KjfMJdoK8i|4A+Lq$jV&BKgEWEDlVMy}w9mK=F{CLTjpu7-kF z_WXd{L>d{@NV5fhk!^JcNO;0!s(Zv7L^J*9K4USQ@^}SC6sjYJmlNT~UG)7V7sxm` z4$fUz1*cYhBJX`1@x;kn(2zsGvl_|IO|#I&kp~qL%`o`wGU&^BVA9D$P}W`z*QKX{ zm|-tiEb1c$UTzrfOnIg{uR+xFAdEj(&fhRE5w@@HgwL1%lDS1~v|?W=HFZ+OIT6`t z_m1UUgRCKC@+WeoMFI|3T;sQY>cT%;qF`4&;~cmvlNFnvz^H>3JgNH5pAAx^!RsIi z{Zj<1*c>c&*K!iIdkW6@#O@$Y5olt?Tx!p{Vb3FLNC?oN;fdMI3wQzE&}#JFY7D>P zB>?JJwq5fj?QaUjkPXQ+Ytkf~Q(Z~+j#hxG1MB2Yzk-QF_c6sdn%?N@hlWoJ@WtvZ z67!FvfA&1Wp}t1qo4ggGyytOtFDuZ{KN+E@7z2JG=P^eHmt2T~yzikHK~7_o+Idt- z&LhTRmC){KNb_B6u~5T{I=o^m?kUOm!6pTAEi2*AtsK%)X@T=5+^6eCO=+da9oTCS z4%OX1N$j{nl;jGqw&FJWN<4*+1HHV1Q%2wjJq14>VEp1==Z9NAU7nSl?e0)l39AD7l^@-%kj1DeK>?E5b$6|2NR=Pq0 z@qOBLY%y1U~Sp*t^8d4FRl=*Ukmlb)$4`lzY(W+4rjnH7DzrRk|4A@XB zffmun(8^*9Q5%wiRP^}F@dR>P~`73fx0hDD}!ZJLF?M>(9G-$@cIeWCVuB;MInkAivG z;GkZ{IY_^x&tL6;WRU}CwzUM;HOQb@w;tKPOdSKOt;x3FU@|6;xcJ8%&Z^-CY-qa( z5n8&CnVg3sx~-Vj`#$tJ2T?s@ zVR&;wFrK^-g!a?xK}&~aZd+^V3z^%naFGb~6#?x|Ml{(bjgRe+Bu_>#X}gN`slF)1 zGR~%X~ z4=;u15zC%Gyk+_uLHE#?s>|&waOrkwBCz=?%#VoV2j?&*)0{}yW8H^K+jDXK&sfw> z&Vt$6*3k5N623XzfL)jK;H%(ovg39r_(^BbopXZm?ZQjoVPXj}wbr1fBt!M=bLg$F z!f=2;n+y691Wzt(BQ2U@;BR{ZT$#J6U|I`5?o>Zr(kMe4P569~*&*=YM>>sIEdo}f zr)ksnr(}-M7)ef7qFzp3WZly^s^%VuI(LgPZ9)s|Wi#e)pI5-NiSISD=0sql_!LO$ zh@e+epq$Ou!LD1Y5z6fQJU?+D}QEG(d8wo7)cL^?AJ|=NB*?2pv6OCmQAxu03b7il> zsU@wLu679xzds`@m%JitQZk|Ud_BCIS_DnuC!t)S2j5dZ?t1o$?i;@o#I*}yzu*~Y zS4gK(F8AomqDCTT)5kS#kHN8mJp5i_1E;3-;D(e6D0_SvZ0vlmq9N=`jD>Px{a)h^`a&=ZXdWptWxnyjV~R z%b#k%Yx&jCR&Wt7$yDRBX_HW7brN3`GO((*8D*9T)AcWQKTOJxObh7)!1Kj8_NHSDjknUAV$S<~}rWdPG`ppNNEq@bv`*L7@rZ-t{ z4INj5vi-7nCzG8SLkmIpfIikb9foLGNn#y~@X5Iq z%)WTm!gME&#LOWjei~GJN<7@}>81A$3P45T1e_G^ z!*9zLgbmAv`92tglf)`uZAl<{T4fTydKX>?d*8ZP?uHw`4WUI}9epb21t&+Mfq!fb zEKMJTuLrbXQAz;kI6sdb*#8VpUCM)uRRJjRe3eR?a#>}yS_;@oK zQ(uPZK0;_!cAayzzmDW#3s{zS!Y-?F?zyA~Sfp&n>@AFQt0O?|zTbjBxu4)rs~f8C zv4K4r&*|F!RGL4ni?YErWS{ro*52`jQC>CpowtMyEy*}KX%+I`w4%p(rZE(iAm(#k zlSuce5PVdLUa4=uVzzIT=eL57ryklhTN9nPBA^p8g#X=ECwso`M^5Dtc=)kvwaDXMLB9ddo`c#_Y2Na?suuqU*FtgnoN ziHVya^UWXb)6MzlIR62bxCY^G*MD?gxi=nPZj1I?)_^zjDlYx5ibb{y;f!S?P7%0` z3X;*Nm@uEYByHipmBF+`zk?*-&H>G*rJ#9AiT)jb3h(b-00!G_K&r5gTC0vh)WJ+t zymf-;ejKB>voeWr_(|Lo_mHS(1Q3Oy68QIk2M_la@xQ&#!cAKr6W$aR46l(zqw;** zxAqlGV{doEH)5gaTsKsw1(Ss}1+dC9m~3l)4I5iLFuNuWM+5Uf@pv4pyLS!cZr;bL zDX-vKZ0n?MPQ_XmrEh!ztYRizdt+ zJ+Vr&hC*tC7ZDJdii^&>V{yfLlzwptKFwt=>4({<+ipTmi`c-CX_Zj0oiXjHV%w^Sb4xehcR}Kmk!pjC9Ixu(X`3tNIM~?Db&h*rda2&Bs#yA%?a}C@` zrjL&X)xXlbSNA7j-9ax5skWsjB-7!YeH$c|YQoD_in|mez-h4>xH7l-7PAac@5{nC z)gvGn$0?w*M;Ww+v&?(^r=I_&2>opnjP(R?4z@xv4pJo>YW*k=3yEL@I9BkVaL`93%I)+2a0-1CY6# zF>(d3(ddlFkdQMMQ;URPNlrLyPt(En((5p4;8w!gKkU#dlYzlT;eM0-I} zcLVGan~WhU!e}r~i%V2bg*%ImV4YDrSQUgqlcg|yvTYySHVo$$xGCU#&HJ?Bc{w&o zq~WO>+h~w#4lb0~4pIsYuwi;3d+#wo&k+-B5U&J{xa;8e@hFBgv-iKF%6M#12RwF4 z=RWZzNR3z-!e0XtE>S^V*yh2Jx@QN){qeB+Y7y|y3*om9 zXK{y;EuOMSpyTX=@Gkp)j@8n^i{T21(AH+2e6p3N*Cfryk zhf56FXzN-<7?)MSkJ(`cY1iV2(B+@hE9g6{h*t%xeNB)*YbQSakcH1JLs0uz80qd$ z!KtTzaI1_XP+-41dgP9f-P!l3(NjCV?|ftUH>n!7?hZ#Xk8xx!?SUg}XY!Lo+vtc* z2-%!gi^Kb(=zvEiF4J^_a`Bf`ymuL``jijScokN4GH;9aRC*_CHd$gG3M=06(X?v_ z*ZwiWaqLd5)z(jDukq)q9+|;N<0Nu7Wd=EHu?lRHP1*cS46>E|;pvY|^15#sHib*U zrRA~k)%+j%>mLYfs5kEG|4Mz=R)Ok&Iryel9(KR<2GO8<pO zunf1ESy|YVmWa#qtMU45w*N(GfLNd~bm^+Yih>RF=&XDUVDkm#$Qtg~a2V)kB;tid z(Qsjw8jkI5LN|SB@^GdzYW{MCRsCCFthXF`h3av?({wy*FAWOg9iTq92>EZ{(28{q zxI>loEv{8SgpNL~9<#vH;7ccqzao2vm*K<}@$mFP8qsLQoK-j4hH?=mwNw-1> zH|YVLEvK;e6Jlb`A*9n>VP5=AeBU*N-y#{nZ4ao%OH-p!`nN1PKPo2ozL(%7$Ms;+ z^O5ZAos8on-_gmp7D0+u0bJ0Z0aoJ|poFsz=IG{8yO&jPWAO%9u{wj_b3TVW+Fb)O zrhRxuyb}4@X*Bdn4_vui0COxFaQfW;$e$nos6g2x{^7w$P~a-jCbkFW>4?HWr~(|U zFbAFbI$qkOMiBntfgQ3}`F;`!#Pn+)ouSFPHp;oEJ&tjOOm9FUo598AdT`x;1IYGS z2XQoN6WMtx5%kV(WwR`Ah?8B*yq#I}?%paK*&IOvP83#sQCp2odAH$*&r-~r$8wA< zzA(jG4QGU@07S8z&)+MYz-CiY(EosFW~;#xt03UnHN*A88n`gd4tIVm;?hczaNkG^ z%v+ap;@f8N*O%#RB=wyz8h*d$?^#e57bS`2gi7^lM`l_X9t@E-WA_>yLc64dUl0VRGmzFJp=w#i|*Xai$fRHf6y z8{SqL&n}_9R)6Tz)tMMr5dd#rRKi(M=TdgB{o+TQby8&C?Q*S|!PdB6Ab6XvEtY=H`JM^B+wm1>$$(1w4Y`Y_sw>w{ZYF(&ruV7}=YIs^H=9ZE`dCt_ln1k} z8p71%wWw_}9@gX!b3#$mXpKh)CRv}w^MY5%=6}=SY5Yv=dSHaXU%bfDs~&jE{uRe# zxgOE@9y0E^DEioB!(>qj`0J=Q>_}<=n=y0dykgy);!s@nW&)npeE>Ip_S1Re2;-=T!nPvD1l_O^ zO*3`KwJ|v~e<^?k_r;+#&;$}ZhH1WX1zw1}j&AC+Ib7h!4g1wYyU1+VFzX6uvY`T{ zS?5`Aya>?~O@-Y{EaBd36);tA=Yrq6qZwUBCH{uusRoJ#XOxM$N-D%zF>mS0pY#)R z0mMf6!$qH3IPP{HywtzZm?}%aP+2s$GQo6hKeV{D6R^36G1X0B$J=zyA#D@dFb8Lu z4s#1d$Kc)Bx8ePwNWSEOZ0O8i0exN-P`s`Hq7{>|N;ZrvSd$OKreBEu#|En4n1b)$ zN<-G7#Teo&$vDlEL2l~`7-kL(B|~%I{a%DJzU<86tB*PwwUBH3mGizZ6X$Q>@NQxp zZA%HmKdWqM-AE>Za>MBu^5GUVy>|gSs*TUf zd*O899nkunN}G*BAw~H=^6f7l7su`Zb>{n+Ad`R_TwC${NENt^Y{x$N4$XsOV|sp9KQ zTD##o?%Yy}%a4wS@xS!ooQz{mY+(sPp`whak0=)FMvx1ACZYqLUFmb zG?BRxJc-ZgENXhv0&L7?a0e!r;|Ez~ymOz25ofpX7C2oYtE)oL@o+OpKfQxOMI4$H zZYCpZhDfu%FrF}Ngs-0yu`eIb^eno@zgtTtlkBj$FJmc|aLKP1>w zl3X)v=I`=}!C231`p#%C9v-3~oH+@e9MZ#p&_GUqnlS7ds>N*kc(77_O3sge1*bB) zQClY$|LW@CFEq65kQ`YE3x*Yz53*v>7X(8bC`-0=UZFgT8NqaAj8ps13@) zwQxgpUiyXf?m7pP^*EeXb)ATKZ^Wb?=Gm3?qW9*~j!% z;8qNnBMOs)dAxEq_wiU`iO!vJVEAh~rf<$BleJV(BkvCHi%b!m5jMc$Y3}ec>?Srm z9HsrgDdwE11gqdpAgo{j;g$Djy&c26v)tE;+itK@RvY&xT*s>;3t(G_7-%%8k)VRKkdy&jP7;23wvb-97C{f2sUrXObDFH5 z0WYQEsrIUW^ju{-=q5|h)hFVht?vl9?()FVB{y(dmS@!lNj12&`7&Q^Jv(RWZbJDB z5v(V>9=B_yl3U-?sM4;55E;UlKW^T*BvTL8&sCvaat}bM^F3`C{KDP+ag6ciglPY; zI!}V*pgcAoPYquJ$?8>q z=rV!BbgWSi4o)wE0miTt@$jJYi=@#wrx~?I3~_i_bCTC&sHr>itg<}T%TrUpF1egCM#)uvwlYG-@-`3y!p|uo${c^%p)>U-Bl>XRXK|KSSM)|d?+ekMa#$6Y-6a|ZuKUOZ{3 z%f_^_g_yfJn*6w0%hl`(m*yvOaQtStxRapk!!TI{<24REr z!Wk!$q7Z`L+6u98=0AQ!Q!DN<&BpbgBjDI|1AflhIP7*SCVyBzBkuqYe51Z@~M!GECxu!0b5!+m)+^mO*N* z5zB+MlA+OD6nEQ*=NH$4du1c{R*8>^^&447KNBwp{(s(B&vBHP+>{;mP;ki1dz72)USss(S9krpykN{DjDw z?=>*PE(o&*jG>U{4IXL67;3T~3&TU`r-&wu=`x@lhIb&I7C299WD3}~agRToe zok0ws&uuJHs{^ZJ4xr9>6alW*xZ>$mSbFRRszzDh$=R`d1~0?~PF`?$oGYGAIR}?_ zcXNUQm&nXsmbt3*AjYmOjAu7c*|Ls@l`Xx zlZ`dKLwMa~1}JGI5&PfoiLSVvEYc25_F|C-{PZ&fJuDxJ@_YDe8U zb(p#OJ_xyMQU23|SXNtv$q$3+$AWOYxM)63Q(=t0?m#d~VcCTOdzxsm8hR|FFm!q= za#`^5neHZ9@L$VnnY z;sdnkn*~@HEFn?jE}@ZA97K)h;KXZ|Ki+keQz)0DZ6_jmJ;}^t*340ZQU_GeZkSMfx`!G(JNeZ<_N^_cvu8g|U8g+KKx zQ9)#oY+q7D7J27zJ-JEnN_`ji&7%}#uLp2Xc3eg8*kZ6wS_M!NfF5cVP&(`f9V;tf z+`}T&4qcCXcGSb|5-0fZEg2)m72xpOmwe^EbeL1y$o*@3hPV5jL1;xi-dVXE4w-+W zjx6_Z&+-zso^qvY=hwsJ<+>2QE}C$i73eP&4$u4Lh^^i~E=OG*n?HA;>mO~05Rd%Ofgkr5!PzBVq-MM+{c8D_m$W_?dgd<2RWL{mYm!m^ zZWY=->BK-z7H!ttfcpyLP|&;&UD8KLxn?~LxkYgFO$_cmSjJe|Ef8`1B^45HBj-l@ zNyf=&#)y=`)B2|q4-+;`MXM>Rpeo=FdJc(^ z-rAvW5abR1;ox0P3`IJHEGUFw0R8COAGcM)iuNWrzLwUAWPLfn_+;|9~Cyo{?( zEX%wMyj@jc!k`E2wJm`)^PZB-8$NK{JqwTdJ%S@I`CuV^7$yprL3HguI>Wb)-HjLD>l z1P`S;l6~$sm=|6bbPG>lXIBf3wiYu_R~ejg_eE0{9C~xj70y2{(TsnxhZ-L%1gVNP zR9aJlD&0-cvv3O*W%;4!(|oviYbUOlzZqd`6k%_vaEz-2?}#|m-SCsVdMJ)2wVhP{ zaWKmMxDOXsM8lp9S3$*hHi#D7ghA^(?Umooj@_?9NOKL0%eRhZN?pkISLu*$df?-i&c1mu^nW+WwFWo zJpPR_#~aJ^Vd^c)-sA1~b1nqHj(wXz?X?Ttn_o(QdOs#xyfSd&>qfkFrj(qi3nWFR z?RelY%dKQH_wJx4cP`Tr>K<#uyealj-P{Zlbo{Z&Xes`Fa}uTEC&5R?G23{g9PhuI z0lQv^fk^LZe7%(i$uBN~hV>6_{f!dv7^?wa%cbC=-2f-9aQFfaGS?>KRGC&VuE6pt z;}w5M;Pp6|#P>w!uW7Ka_8a*rYKP@s^;E;HjvOj>!>!lCxacdhaIx`aIGNmoh5|Xn z=oRA%u=zv4mGS85mw|p>E!bD(3p~BW_&p|sIr)mv@Xke0o}0(m@>g&`yBOc)9D%(X zjX_!26Qv6&M0=ls=M5Fi7kPusXEVn%+mkqX8jr5HHIo}J8btJ0_TviYKxmoskN?h) zvGKpFz&3;Tx0$hdLaA%98>{19zN@22l$?9dr%m*@wQ=Vs%p1`k-KRtfsnEx0V7<&a%c zkeBBSZvrpE@Q5AXY|8}Np4f~-?tvIw(1qs~$-;{(u2i7GljK^Z&?F21sonC-sZ$SO zr_Jd%3pvi@p)-iybJuK7*Mct74!jf?L&f{FVf{>VmZJ~B)#Xj7lFl-|g_ZCqww7@V zns7hMVttxy%;pGnF!nDH@-mv}$!7_i|DkYl{8uRBkZ4YvQvhk0*op)w9J!&4FJI@le!!WXr42XNqz8fqp#!qx1I zU~1|Khgs*|yswD-30FnW)1DwKaS6AX9tX`FQRtdcOT|>~;d{?WOncygQ-x&k_~#d( zAj0NuMzUZV;Rt#x{~U9bgDW%7aM}N*!N{VYRllOI6RBt0s5s*bJbpZx_=#rIAXb~X zw_TEY>rFs|1s7rC;4xHNc%97K)C^Z&G7n|74IEXYP;>YJDu}V(we}btn_7x(FI_9_-SNr^41guy*koy6DR^8atUeMIy>s7HcQ+x(~p*u%ql9KDg3u zP9{7tWPe-org*{nGwl?dO0Rv5#nXm2q21{Oyk+mKf;ZN}XZ3low!Z+}9z;OVXSSY1 z)PR2U1w2(%%KOsk4iSrsAx^LwS1$C%Jxx5Q4m2k>AL)VZuQs@|mi3!-^igI#W80b} zz>At&p#OC#{>#;-8VlH+XJ#7f6#Sq@*3Bfk;Vww#$-rYhA9Sc}M`kp{9zA}QN6I_W z#jB_HDgyCJ)O=i?Xo-3)w~3a|aolA*ACeCm!ppmtQT)+Ol%M6#*IBrmhAC>|cH?MB zoN|PPz#~y;XaGIOZs6_o?O5-$1ss;JzvUYC|N94U==d&d{p3MrP4&ix%nSUT!jCX$ z!~*9?Fu&lSKdwHXfLDLSA~nopj^9Vrcg8a+^m_`Iwpx)C5xO|8QoBws7x6VWSx$ihG3AjtX?6rhQw@CiuGq?HMSnK=#^I#aau$IJ~ zxWhl#6pCwKgj1Sh&tEn+g9atW^HZc8VM6pHzVpKZjva@wdqF4=@k3DW=RnWJ`$1xS zF<#j24tn*Lbjz3zn)7<$!xJa)pWZ~@*B8?;(cCkMW?B>}QuUkM zrmtrWpa=8qjq)NOam!Dr&O%utycyWxo#A6kI}qrV76DzbUZe@oCsG`5QTwc@hE&?XhO3E`}|-KqQqz z@qAB6mGTNezd>n?TqBCB!=#~q-ZuEAS&JGPowz)2BDx0-pnsDDaL4?~-ZeA$YkJd2 zk%M9j@GE{fl)0=&zHB3l7!8 z-hOkeoP7jeJ@dg;QU~!=mmj8o*aG*hm!i|@G|-IQfM47k=|qzoVE*3_8Yd*`vDXSDzDoyrVn;H4dha3m;--r?oq{&V8+@n;_VIMo98 zwQl3zk4TIP5^?cn1O900FrBgf7D!L8XZ%7LcvLe;E&mCyo}&^?`aTt_TAcBzV+NjE zuSu54%JS1UrQlKp=BwP!`VbfO(f0f*(3Gl1C2up7d^{JD{1VZ$={7_P9)YPJe6VZC z4!5|ng~&S`mhDSLJL57~Wj+Y~^5;NVdMSoZ(}lWqee8R!#vThVQo4LAynB^`N-pa# zd6qAJjeJid{I7uJ$sUrb8bH<+s$k+O#vwM5!ua|)IzzIU7&gA=9F~2g>7U~9z)cN$ zGHVXn^>=WYCl?Tl1@j@AYeA7hH&ojojMVun>G0nUlRT^GgWs80v6pq0uO)DEcZuNd z&K8*4sf8c6oFV$2Y_a$5A$f|PWdFZISf8N*avvMG9nE*ptxyQA9?)dHo(kByb#m1d zIp!`oltAXS=Ymc8JebKII%U5L!kP4Jc%1Qszkjtj9eGZ$bk1XvZ=C=ZZpyenuoX17 z-KNXsN9btJAS|yhCr*1$V%aw@NOjnU66=x}KTI1J3fe-DoIZSYe?s@Yj=~$#tMR1@ zhpPXYiHJoYRL?FZ9-n>4N2@ma4@KC_?=l(xz8LSeQ0(Mq!rB*#WbDHmJXp2}-q$_i zsaqIwf~R+3xsMM{=`6(`QV%$zD`QvS> z+S!3!d#B*+fwNc~Xn-6|gncj4$v#b8)Ks;=$J>wbeP2Alrq$Mv$Ff<^y4bn8OdIw+ z3&tgh56HCs0u0O(;WGPd80nC0^tDSGSP56cM7I>c{%i0LGIW9Y+fT=0nXh3B88 zRmac4a~oa!@BeI6K?mr0Eet=}{P;uu;i&&Y0_kRH(0%9$qy7=(`4fMv-rWU@#Wg|w zk~!ZfaVIGEmcoEw05$2UVeeFKG&aHt=6mLl?V>3-PBVbcj&y|Z<=4Qjp$p^aW19Fo z5+*(s0QbBtXm%h8lPZs+qWD2rU!DTLH=M#&?=EUN^8$pF))3#)6wGCJ|Jl#xV*7(U za`%E77!}0B;KV1`wV?(VuIpv*MUA-YS^;)+R|9QWMLtio0{{DAG@$)5oUiyw)m18? z3eIw0d{}1e{Z4GV7ETf#WHL9~5IOz0hQ6A03FZvPkP^2rG>h6rzcZ%zN47@HGyD%v zM!Qjavn*s0I{xa6H3CA~2>>=r@J}kK;OC`E*5P1+DvOC(!(5K|e@lQ1Sstf(%!W_RU*D(6rL++l^ z6ew(W20hy%fbWQV8tzt_sv)>~iDK{PGCW$b61D7a;QXt5(51VTG2}Hs{bdaDB{$&& z^_4X9R|3k}uH&f)?k2526{*J@4;)+|OD&~WLPk(685DXJRW?)S?J`w9$%PR2%L*qH1Da1ru;(^IGVb-Z``t-OJ zd{%ITNl&WC`}+iDypx6THqp>HWeBuC%*NLKdH7^5pTBi$0h}`Ff_riVmL63=zgcxW z&3E3g?BaEJ_PPv~)}|t_kNIiRjzH<{c@R9Ej|rja#8)s4UOJVN>UdXd`Q(mQ!*jq_ zYA(I~+84el-seK6PK4dY`=LHDo%dzt4Z0}gDp^{=azSTb;UvfXBu?BL$8ODsfBu1Z zI>#LP3$w^rMh6-RpRSs*=K$Q^6^{Jng_xSC4X(HD!GpQ_AZIs#ul4mHTvnKRAFbir ziifhSpbuo7Eye8dH^+;Xj!*a_2fvS{kY_qFtZJ7_hsqUb_612?Q7enV?dkOBrMc*L zZ3h)Im&cpdbMZFwQ&{bOPw($FVJe6bnkA&qX`Jw)>ofL}*1Bl^>Yq=zsjpJFJ7uTI z#i|m%mee>YS2_>E*qvltgvF~~m=Jeky4*76i4|K|Vr`ixU8zr^42R+?Kl zv7^d+Mj$Qq3gQWGPNI%!%JjWb5hoiT%Da?(mp}cZDiM_t6*E4t_P`lCE5}oZoTm%@ R|BI8yZKWo#fBX_1_df%BnY#c0 literal 0 HcmV?d00001 diff --git a/tests/test_newpolygons.py b/tests/test_newpolygons.py new file mode 100644 index 000000000..ffa06d0fb --- /dev/null +++ b/tests/test_newpolygons.py @@ -0,0 +1,449 @@ +# -*- coding: utf-8 -*- + +from contextlib import contextmanager +import unittest +import tempfile +from unittest.mock import Mock, patch +from pathlib import Path +from traceback import print_exception +import warnings +from typing import Optional, List + +from PIL import Image + +from click.testing import CliRunner + +from kraken.containers import ( + BaselineLine, + BaselineOCRRecord, + BBoxLine, + BBoxOCRRecord, + Segmentation, +) +from kraken.lib import xml +from kraken.lib import segmentation +from kraken.lib.models import load_any +from kraken.rpred import mm_rpred, rpred +from kraken.kraken import cli as kraken_cli +from kraken.ketos import cli as ketos_cli +import re + +thisfile = Path(__file__).resolve().parent +resources = thisfile / "resources" + +def mock_extract_polygons(): + return Mock(side_effect=segmentation.extract_polygons) + +class TestNewPolygons(unittest.TestCase): + """ + Tests for the new polygon extraction method. + """ + + def setUp(self): + self.im = Image.open(resources / "bw.png") + self.old_model_path = str(resources / "overfit.mlmodel") + self.old_model = load_any(self.old_model_path) + self.new_model_path = str(resources / "overfit_newpoly.mlmodel") + self.new_model = load_any(self.new_model_path) + self.segmented_img = str(resources / "170025120000003,0074-lite.xml") + self.runner = CliRunner() + self.color_img = resources / "input.tif" + self.arrow_data = str(resources / "merge_tests/base.arrow") + self.simple_bl_seg = Segmentation( + type="baselines", + imagename=resources / "bw.png", + lines=[ + BaselineLine( + id="foo", + baseline=[[0, 10], [2543, 10]], + boundary=[[0, 0], [2543, 0], [2543, 155], [0, 155]], + ) + ], + text_direction="horizontal-lr", + script_detection=False, + ) + + ## RECIPES + + @patch("kraken.rpred.extract_polygons", new_callable=mock_extract_polygons) + def _test_rpred(self, extractor_mock: Mock, *, model, force_no_legacy: bool=False, expect_legacy: bool): + """ + Base recipe for testing rpred with a given model and polygon extraction method + """ + pred = rpred(model, self.im, self.simple_bl_seg, True, no_legacy_polygons=force_no_legacy) + _ = next(pred) + + extractor_mock.assert_called() + for cl in extractor_mock.mock_calls: + self.assertEqual(cl[2]["legacy"], expect_legacy) + + @patch("kraken.rpred.extract_polygons", new_callable=mock_extract_polygons) + def _test_krakencli(self, extractor_mock: Mock, *, args, force_no_legacy: bool=False, expect_legacy: bool,): + """ + Base recipe for testing kraken_cli with a given polygon extraction method + """ + if force_no_legacy: + args = ["--no-legacy-polygons"] + args + + result = self.runner.invoke(kraken_cli, args) + print("kraken", *args) + + if result.exception: + print_exception(result.exception) + + self.assertEqual(result.exit_code, 0) + extractor_mock.assert_called() + for cl in extractor_mock.mock_calls: + self.assertEqual(cl[2]["legacy"], expect_legacy) + + def _test_ketoscli(self, *, args, expect_legacy: bool, check_exit_code: Optional[int|List[int]]=0, patching_dir="kraken.lib.dataset.recognition"): + """ + Base recipe for testing ketos_cli with a given polygon extraction method + """ + with patch(patching_dir + ".extract_polygons", new_callable=mock_extract_polygons) as extractor_mock: + result = self.runner.invoke(ketos_cli, args) + + print("ketos", *args) + if result.exception: + print(result.output) + print_exception(result.exception) + + if check_exit_code is not None: + if isinstance(check_exit_code, int): + check_exit_code = [check_exit_code] + self.assertIn(result.exit_code, check_exit_code, "Command failed") + + extractor_mock.assert_called() + for cl in extractor_mock.mock_calls: + self.assertEqual(cl[2]["legacy"], expect_legacy) + + ## TESTS + + def test_rpred_from_old_model(self): + """ + Test rpred with old model, check that it uses legacy polygon extraction method + """ + self._test_rpred(model=self.old_model, force_no_legacy=False, expect_legacy=True) + + def test_rpred_from_old_model_force_new(self): + """ + Test rpred with old model, but disabling legacy polygons + """ + self._test_rpred(model=self.old_model, force_no_legacy=True, expect_legacy=False) + + def test_rpred_from_new_model(self): + """ + Test rpred with new model, check that it uses new polygon extraction method + """ + self._test_rpred(model=self.new_model, force_no_legacy=False, expect_legacy=False) + + + def test_krakencli_ocr_old_model(self): + """ + Test kraken_cli with old model, check that it uses legacy polygon extraction method + """ + with tempfile.NamedTemporaryFile() as fp: + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp.name, 'ocr', '-m', self.old_model_path], + force_no_legacy=False, + expect_legacy=True, + ) + + def test_krakencli_ocr_old_model_force_new(self): + """ + Test kraken_cli with old model, check that it uses legacy polygon extraction method + """ + with tempfile.NamedTemporaryFile() as fp: + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp.name, 'ocr', '-m', self.old_model_path], + force_no_legacy=True, + expect_legacy=False, + ) + + def test_krakencli_ocr_new_model(self): + """ + Test kraken_cli with new model, check that it uses new polygon extraction method + """ + with tempfile.NamedTemporaryFile() as fp: + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp.name, 'ocr', '-m', self.new_model_path], + force_no_legacy=False, + expect_legacy=False, + ) + + + + def test_ketoscli_test_old_model(self): + """ + Test `ketos test` with old model, check that it uses legacy polygon extraction method + """ + self._test_ketoscli( + args=['test', '-m', self.old_model_path, '-f', 'xml', '--workers', '0', self.segmented_img], + expect_legacy=True, + ) + + def test_ketoscli_test_old_model_force_new(self): + """ + Test `ketos test` with old model, check that it does not use legacy polygon extraction method + """ + self._test_ketoscli( + args=['test', '--no-legacy-polygons', '-m', self.old_model_path, '-f', 'xml', '--workers', '0', self.segmented_img], + expect_legacy=False, + ) + + def test_ketoscli_test_new_model(self): + """ + Test `ketos test` with new model, check that it uses new polygon extraction method + """ + self._test_ketoscli( + args=['test', '-m', self.new_model_path, '-f', 'xml', '--workers', '0', self.segmented_img], + expect_legacy=False, + ) + + + def test_ketoscli_train_new_model(self): + """ + Test `ketos train` with new model, check that it uses new polygon extraction method + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + fp = str(Path(tempdir) / "test.xml") + + self._test_ketoscli( + args=['train', '-f', 'xml', '-N', '1', '-q', 'fixed', '-o', mfp, '--workers', '0', self.segmented_img], + expect_legacy=False, + check_exit_code=[0, 1], # Model may not improve during training + ) + + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp, 'ocr', '-m', mfp + "_0.mlmodel"], + expect_legacy=False, + ) + + def test_ketoscli_train_new_model_force_legacy(self): + """ + Test `ketos train` training new model, check that it uses legacy polygon extraction method if forced + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + fp = str(Path(tempdir) / "test.xml") + + self._test_ketoscli( + args=['train', '--legacy-polygons', '-f', 'xml', '-N', '1', '-q', 'fixed', '-o', mfp, '--workers', '0', self.segmented_img], + expect_legacy=True, + check_exit_code=[0, 1], # Model may not improve during training + ) + + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp, 'ocr', '-m', mfp + "_0.mlmodel"], + expect_legacy=True, + ) + + def test_ketoscli_train_old_model(self): + """ + Test `ketos train` finetuning old model, check that it uses new polygon extraction method + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + fp = str(Path(tempdir) / "test.xml") + + self._test_ketoscli( + args=['train', '-f', 'xml', '-N', '1', '-q', 'fixed', '-i', self.old_model_path, '--resize', 'add', '-o', mfp, '--workers', '0', self.segmented_img], + expect_legacy=False, + check_exit_code=[0, 1], # Model may not improve during training + ) + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp, 'ocr', '-m', mfp + "_0.mlmodel"], + expect_legacy=False, + ) + + def test_ketoscli_train_old_model_force_legacy(self): + """ + Test `ketos train` finetuning old model, check that it uses legacy polygon extraction method if forced + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + fp = str(Path(tempdir) / "test.xml") + + self._test_ketoscli( + args=['train', '--legacy-polygons', '-f', 'xml', '-N', '1', '-q', 'fixed', '-i', self.old_model_path, '--resize', 'add', '-o', mfp, '--workers', '0', self.segmented_img], + expect_legacy=True, + check_exit_code=[0, 1], # Model may not improve during training + ) + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp, 'ocr', '-m', mfp + "_0.mlmodel"], + expect_legacy=True, + ) + + + @unittest.expectedFailure + def test_ketoscli_pretrain_new_model(self): + """ + Test `ketos pretrain` with new model, check that it uses new polygon extraction method + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + fp = str(Path(tempdir) / "test.xml") + + self._test_ketoscli( + args=['pretrain', '-f', 'xml', '-N', '1', '-q', 'fixed', '-o', mfp, '--workers', '0', self.segmented_img], + expect_legacy=False, + check_exit_code=[0, 1], # Model may not improve during training + ) + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp, 'ocr', '-m', mfp + "_0.mlmodel"], + expect_legacy=False, + ) + + @unittest.expectedFailure + def test_ketoscli_pretrain_new_model_force_legacy(self): + """ + Test `ketos pretrain` with new model, check that it uses legacy polygon extraction method if forced + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + fp = str(Path(tempdir) / "test.xml") + + self._test_ketoscli( + args=['pretrain', '--legacy-polygons', '-f', 'xml', '-N', '1', '-q', 'fixed', '-o', mfp, '--workers', '0', self.segmented_img], + expect_legacy=True, + check_exit_code=[0, 1], # Model may not improve during training + ) + + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp, 'ocr', '-m', str(mfp) + "_0.mlmodel"], + expect_legacy=True, + ) + + @unittest.expectedFailure + def test_ketoscli_pretrain_old_model(self): + """ + Test `ketos pretrain` with old model, check that it uses new polygon extraction method + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + fp = str(Path(tempdir) / "test.xml") + + self._test_ketoscli( + args=['pretrain', '-f', 'xml', '-N', '1', '-q', 'fixed', '-i', self.old_model_path, '--resize', 'add', '-o', mfp, '--workers', '0', self.segmented_img], + expect_legacy=False, + check_exit_code=[0, 1], # Model may not improve during training + ) + + self._test_krakencli( + args=['-f', 'xml', '-i', self.segmented_img, fp, 'ocr', '-m', mfp + "_0.mlmodel"], + expect_legacy=False, + ) + + + def _assertWarnsWhenTrainingArrow( + self, model: str, *dset: str, from_model: str|None=None, force_legacy: bool=False, + expect_warning_msgs: list[str]=[], expect_not_warning_msgs: list[str]=[]): + + args = ['-f', 'binary', '-N', '1', '-q', 'fixed', '-o', model, *dset] + if force_legacy: + args = ['--legacy-polygons'] + args + if from_model: + args = ['-i', from_model, '--resize', 'add'] + args + + print("ketos", 'train', *args) + run = self.runner.invoke(ketos_cli, ['train'] + args) + output = re.sub(r'\w+\.py:\d+\n', '', run.output) + output = re.sub(r'\s+', ' ', output) + for warning_msg in expect_warning_msgs: + self.assertIn(warning_msg, output, f"Expected warning '{warning_msg}' not found in output") + for warning_msg in expect_not_warning_msgs: + self.assertNotIn(warning_msg, output, f"Unexpected warning '{warning_msg}' found in output") + + def test_ketos_old_arrow_train_new(self): + """ + Test `ketos train`, on old arrow dataset, check that it raises a warning about polygon extraction method only if incoherent + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + mfp2 = str(Path(tempdir) / "model2") + + self._assertWarnsWhenTrainingArrow(mfp, self.arrow_data, force_legacy=False, expect_warning_msgs=["WARNING Setting dataset legacy polygon status to True based on training set", "the new model will be flagged to use legacy"]) + self._assertWarnsWhenTrainingArrow(mfp2, self.arrow_data, force_legacy=True, expect_not_warning_msgs=["WARNING Setting dataset legacy polygon status to True based on training set", "the new model will be flagged to use legacy"]) + + def test_ketos_new_arrow(self): + """ + Test `ketos compile`, check that it uses new polygon extraction method + """ + with tempfile.TemporaryDirectory() as tempdir: + dset = str(Path(tempdir) / "dataset.arrow") + mfp = str(Path(tempdir) / "model") + mfp2 = str(Path(tempdir) / "model2") + + self._test_ketoscli( + args=['compile', '-f', 'xml', '-o', dset, self.segmented_img], + expect_legacy=False, + patching_dir="kraken.lib.arrow_dataset", + ) + + self._assertWarnsWhenTrainingArrow(mfp, dset, force_legacy=False, expect_not_warning_msgs=["WARNING Setting dataset legacy polygon status to False based on training set", "the new model will be flagged to use legacy"]) + self._assertWarnsWhenTrainingArrow(mfp2, dset, force_legacy=True, expect_warning_msgs=["WARNING Setting dataset legacy polygon status to False based on training set", "the new model will be flagged to use new"]) + + + def test_ketos_new_arrow_force_legacy(self): + """ + Test `ketos compile`, check that it uses old polygon extraction method + """ + with tempfile.TemporaryDirectory() as tempdir: + dset = str(Path(tempdir) / "dataset.arrow") + mfp = str(Path(tempdir) / "model") + mfp2 = str(Path(tempdir) / "model2") + + self._test_ketoscli( + args=['compile', '--legacy-polygons', '-f', 'xml', '-o', dset, self.segmented_img], + expect_legacy=True, + patching_dir="kraken.lib.arrow_dataset", + ) + + self._assertWarnsWhenTrainingArrow(mfp, dset, force_legacy=False, expect_warning_msgs=["WARNING Setting dataset legacy polygon status to True based on training set", "the new model will be flagged to use legacy"]) + self._assertWarnsWhenTrainingArrow(mfp2, dset, force_legacy=True, expect_not_warning_msgs=["WARNING Setting dataset legacy polygon status to True based on training set", "the new model will be flagged to use legacy"]) + + def test_ketos_old_arrow_old_model(self): + """ + Test `ketos train`, on old arrow dataset, check that it raises a warning about polygon extraction method only if incoherent + """ + with tempfile.TemporaryDirectory() as tempdir: + mfp = str(Path(tempdir) / "model") + mfp2 = str(Path(tempdir) / "model2") + + self._assertWarnsWhenTrainingArrow(mfp, self.arrow_data, from_model=self.old_model_path, force_legacy=False, expect_warning_msgs=["WARNING Setting dataset legacy polygon status to True based on training set"], expect_not_warning_msgs=["model will be flagged to use new"]) + self._assertWarnsWhenTrainingArrow(mfp2, self.arrow_data, from_model=self.old_model_path, force_legacy=True, expect_not_warning_msgs=["WARNING Setting dataset legacy polygon status to True based on training set", "model will be flagged to use new"]) + + def test_ketos_new_arrow_old_model(self): + """ + Test `ketos train`, on new arrow dataset, check that it raises a warning about polygon extraction method only if incoherent + """ + with tempfile.TemporaryDirectory() as tempdir: + dset = str(Path(tempdir) / "dataset.arrow") + mfp = str(Path(tempdir) / "model") + mfp2 = str(Path(tempdir) / "model2") + + self._test_ketoscli( + args=['compile', '-f', 'xml', '-o', dset, self.segmented_img], + expect_legacy=False, + patching_dir="kraken.lib.arrow_dataset", + ) + + self._assertWarnsWhenTrainingArrow(mfp, dset, from_model=self.old_model_path, force_legacy=False, expect_not_warning_msgs=["WARNING Setting dataset legacy polygon status to False based on training set"], expect_warning_msgs=["model will be flagged to use new"]) + self._assertWarnsWhenTrainingArrow(mfp2, dset, from_model=self.old_model_path, force_legacy=True, expect_warning_msgs=["WARNING Setting dataset legacy polygon status to False based on training set"], expect_not_warning_msgs=["model will be flagged to use new"]) + + def test_ketos_mixed_arrow_train_new(self): + """ + Test `ketos train`, on mixed arrow dataset, check that it raises a warning about polygon extraction method only if incoherent + """ + with tempfile.TemporaryDirectory() as tempdir: + dset = str(Path(tempdir) / "dataset.arrow") + mfp = str(Path(tempdir) / "model") + + self._test_ketoscli( + args=['compile', '-f', 'xml', '-o', dset, self.segmented_img, self.arrow_data], + expect_legacy=False, + patching_dir="kraken.lib.arrow_dataset", + ) + + self._assertWarnsWhenTrainingArrow(mfp, dset, self.arrow_data, force_legacy=True, expect_warning_msgs=["WARNING Mixed legacy polygon", "WARNING Setting dataset legacy polygon status to False based on training set"], expect_not_warning_msgs=["model will be flagged to use legacy"]) \ No newline at end of file