From 2fb4fc1d440d99e3bc92f595fc19566d3804d903 Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 14:49:37 +0200 Subject: [PATCH 01/10] rule detector settings and config --- container/config/detector_rule_config.yaml | 7 +++++++ container/config/detector_rule_settings.yaml | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 container/config/detector_rule_config.yaml create mode 100644 container/config/detector_rule_settings.yaml diff --git a/container/config/detector_rule_config.yaml b/container/config/detector_rule_config.yaml new file mode 100644 index 0000000..b212ed9 --- /dev/null +++ b/container/config/detector_rule_config.yaml @@ -0,0 +1,7 @@ +detectors: + RuleDetector: + method_type: rule_detector + auto_config: false + rules: + - rule: "R003 - CheckForExceptions" + - rule: "R004 - ErrorLevelFound" diff --git a/container/config/detector_rule_settings.yaml b/container/config/detector_rule_settings.yaml new file mode 100644 index 0000000..c149456 --- /dev/null +++ b/container/config/detector_rule_settings.yaml @@ -0,0 +1,17 @@ +component_name: RuleDetector01 +component_type: RuleDetector # this is the component class name, the service will automatically search for the matching component config schema +log_level: "DEBUG" +log_dir: "/logs" + +# Manager Interface (Command Channel) +http_host: 0.0.0.0 + +# Engine Interface (Data Channel) +engine_addr: "ipc:///run/detector-rule.ipc" +engine_autostart: true + +# Output Destinations (where processed data is sent) +out_addr: + - "ipc:///run/output-rule.ipc" + +out_dial_timeout: 1000 From 4e1157661df664a5f47e2f26a95c6f43400cef6a Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 14:50:03 +0200 Subject: [PATCH 02/10] add rule detector to fluent out --- container/fluentout/fluent.conf | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/container/fluentout/fluent.conf b/container/fluentout/fluent.conf index 81867ed..20c857b 100644 --- a/container/fluentout/fluent.conf +++ b/container/fluentout/fluent.conf @@ -26,6 +26,32 @@ +# Second source: the rule-based detector (detector-rule) dials its own +# ipc socket, since each is a 1:1 pynng.Pair0 connection and can't share +# the socket above with the NewValueDetector. + + @type nng_in + @id input_nng_rule + + @type protobuf + class_file /fluentd/etc/schemas_pb.rb + class_name DetectorSchema + protobuf_version protobuf3 + + uri ipc:///run/output-rule.ipc + tag nngrule.* + + + + @type file + path /fluentd/log/output-rule.%Y%m%d + compress text + append true + + flush_mode immediate + + + # # @type stdout # From 02409617941964ffe226ecf08510ec91c1b5cfee Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 14:51:35 +0200 Subject: [PATCH 03/10] add rule detector to docker compose --- docker-compose.yml | 213 ++++++++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 100 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9ac07a5..d2ad97a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,111 +1,124 @@ # version: "3" services: - fluentin: - #image: dm-fluentd:latest - build: - context: . - dockerfile: container/Dockerfile_fluentd - volumes: - - '$PWD/container/fluentin:/fluentd/etc' - - '/var/log/nginx:/fluentd/log' - - '$PWD/container/run:/run' - depends_on: - - parser - parser: - # image: detectmate:dev-0.1.6 - build: - context: . - args: - LIBRARY_EXTRAS: ${LIBRARY_EXTRAS:-full} - volumes: - - '$PWD/container/config:/config' - - '$PWD/container/logs:/logs' - - '$PWD/container/run:/run' - command: uv run detectmate --settings /config/parser_settings.yaml --config /config/parser_config.yaml - ports: - - "8001:8000" - depends_on: - - detector + fluentin: + #image: dm-fluentd:latest + build: + context: . + dockerfile: container/Dockerfile_fluentd + volumes: + - '$PWD/container/fluentin:/fluentd/etc' + - '/var/log/nginx:/fluentd/log' + - '$PWD/container/run:/run' + depends_on: + - parser + parser: + build: + context: . + args: + LIBRARY_EXTRAS: ${LIBRARY_EXTRAS:-full} + volumes: + - '$PWD/container/config:/config' + - '$PWD/container/logs:/logs' + - '$PWD/container/run:/run' + command: uv run detectmate --settings /config/parser_settings.yaml --config /config/parser_config.yaml + ports: + - "8001:8000" + depends_on: + - detector + - detector-rule - detector: - # image: detectmate:dev-0.1.6 - build: - context: . - args: - LIBRARY_EXTRAS: ${LIBRARY_EXTRAS:-full} - volumes: - - '$PWD/container/config:/config' - - '$PWD/container/logs:/logs' - - '$PWD/container/run:/run' - - '$PWD/container/state:/state' - command: uv run detectmate --settings /config/detector_settings.yaml --config /config/detector_config.yaml - ports: - - "8002:8000" - depends_on: - - fluentout + detector: + build: + context: . + args: + LIBRARY_EXTRAS: ${LIBRARY_EXTRAS:-full} + volumes: + - '$PWD/container/config:/config' + - '$PWD/container/logs:/logs' + - '$PWD/container/run:/run' + - '$PWD/container/state:/state' + command: uv run detectmate --settings /config/detector_settings.yaml --config /config/detector_config.yaml + ports: + - "8002:8000" + depends_on: + - fluentout - fluentout: - # image: dm-fluentd:latest - build: - context: . - dockerfile: container/Dockerfile_fluentd - volumes: - - '$PWD/container/fluentout:/fluentd/etc' - - '$PWD/container/fluentlogs:/fluentd/log' - - '$PWD/container/run:/run' + detector-rule: + build: + context: . + args: + LIBRARY_EXTRAS: ${LIBRARY_EXTRAS:-full} + volumes: + - '$PWD/container/config:/config' + - '$PWD/container/logs:/logs' + - '$PWD/container/run:/run' + command: uv run detectmate --settings /config/detector_rule_settings.yaml --config /config/detector_rule_config.yaml + ports: + - "8003:8000" + depends_on: + - fluentout - prometheus: - image: prom/prometheus:latest - container_name: prometheus - restart: unless-stopped - volumes: - - ./container/prometheus.yml:/etc/prometheus/prometheus.yml - - prometheus_data:/prometheus - command: - - '--config.file=/etc/prometheus/prometheus.yml' - - '--storage.tsdb.path=/prometheus' - - '--web.console.libraries=/etc/prometheus/console_libraries' - - '--web.console.templates=/etc/prometheus/consoles' - - '--web.enable-lifecycle' - expose: - - 9090 + fluentout: + # image: dm-fluentd:latest + build: + context: . + dockerfile: container/Dockerfile_fluentd + volumes: + - '$PWD/container/fluentout:/fluentd/etc' + - '$PWD/container/fluentlogs:/fluentd/log' + - '$PWD/container/run:/run' - grafana: - image: grafana/grafana:latest - container_name: grafana - ports: - - "3000:3000" - environment: - - GF_SECURITY_ADMIN_PASSWORD=admin - depends_on: - - prometheus - volumes: - - ./container/grafana/prometheus.yml:/etc/grafana/provisioning/datasources/prometheus.yml - - ./container/grafana/provisioning/dashboards/dashboards.yml:/etc/grafana/provisioning/dashboards/dashboards.yml - - ./container/grafana/dashboards:/var/lib/grafana/dashboards - - grafana_data:/var/lib/grafana + prometheus: + image: prom/prometheus:latest + container_name: prometheus + restart: unless-stopped + volumes: + - ./container/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--web.enable-lifecycle' + expose: + - 9090 - # kafka: - # image: apache/kafka-native - # ports: - # - "9092:9092" - # environment: - # KAFKA_LISTENERS: CONTROLLER://localhost:9091,HOST://0.0.0.0:9092,DOCKER://0.0.0.0:9093 - # KAFKA_ADVERTISED_LISTENERS: DOCKER://kafka:9093,HOST://kafka:9092 - # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,DOCKER:PLAINTEXT,HOST:PLAINTEXT - # - # # Settings required for KRaft mode - # KAFKA_NODE_ID: 1 - # KAFKA_PROCESS_ROLES: broker,controller - # KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER - # KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9091 - # - # # Listener to use for broker-to-broker communication - # KAFKA_INTER_BROKER_LISTENER_NAME: DOCKER - # - # # Required for a single node cluster - # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + grafana: + image: grafana/grafana:latest + container_name: grafana + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + depends_on: + - prometheus + volumes: + - ./container/grafana/prometheus.yml:/etc/grafana/provisioning/datasources/prometheus.yml + - ./container/grafana/provisioning/dashboards/dashboards.yml:/etc/grafana/provisioning/dashboards/dashboards.yml + - ./container/grafana/dashboards:/var/lib/grafana/dashboards + - grafana_data:/var/lib/grafana + # kafka: + # image: apache/kafka-native + # ports: + # - "9092:9092" + # environment: + # KAFKA_LISTENERS: CONTROLLER://localhost:9091,HOST://0.0.0.0:9092,DOCKER://0.0.0.0:9093 + # KAFKA_ADVERTISED_LISTENERS: DOCKER://kafka:9093,HOST://kafka:9092 + # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,DOCKER:PLAINTEXT,HOST:PLAINTEXT + # + # # Settings required for KRaft mode + # KAFKA_NODE_ID: 1 + # KAFKA_PROCESS_ROLES: broker,controller + # KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER + # KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9091 + # + # # Listener to use for broker-to-broker communication + # KAFKA_INTER_BROKER_LISTENER_NAME: DOCKER + # + # # Required for a single node cluster + # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 volumes: prometheus_data: From 9f63b096540d5eb93b3481b6f68a3cb0f1bd4b9c Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 14:58:45 +0200 Subject: [PATCH 04/10] update gettings started docs --- docs/docker-compose.md | 59 ++++++++++---- docs/getting_started.md | 171 ++++++++++++++++++++++++++++++---------- 2 files changed, 172 insertions(+), 58 deletions(-) diff --git a/docs/docker-compose.md b/docs/docker-compose.md index 1765253..57a1c82 100644 --- a/docs/docker-compose.md +++ b/docs/docker-compose.md @@ -4,7 +4,7 @@ DetectMate Service ships a comprehensive Docker Compose configuration that inclu ![Illustration of a logpipeline](images/Detectmate.drawio.png "logpipeline") -Log file ingestion is handled by Fluentd. It supports reading from various systems and can convert the data to a specific format before sending it to any other target. Using the default setup, fluentd reads lines from a file and sends them to the DetectMate parser. The parser processes and forwards this data to the detector. If the detector finds an anomaly, it will send it to another Fluentd process that can communicate with various targets, such as Elasticsearch, Kafka, or a log file. +Log file ingestion is handled by Fluentd. It supports reading from various systems and can convert the data to a specific format before sending it to any other target. Using the default setup, fluentd reads lines from a file and sends them to the DetectMate parser. The parser processes the data and forwards it to **both** detector services shipped by default, `detector` and `detector-rule`. If either detector finds an anomaly, it sends it to the `fluentout` process, which can communicate with various targets, such as Elasticsearch, Kafka, or a log file. The following sections will explain the [docker-compose.yml](https://github.com/ait-detectmate/DetectMateService/blob/main/docker-compose.yml) service by service. @@ -42,13 +42,13 @@ Docker compose builds the `detectmate` image using the [Dockerfile](https://gith ### Volumes -All DetectMate container(`parser` and `detector`) need the following volumes mounted: +All DetectMate containers (`parser`, `detector` and `detector-rule`) need the following volumes mounted: - `container/config:/config` provides the DetectMate configuration. Each DetectMate Service needs one settings-file for the general service configuration and one config file for the specific DetectMate component. For parser these files are `parser_settings.yaml` for service settings and `parser_config.yaml` that holds the component-specific parameters. - `container/logs:/logs` exposes all log files that are generated by DetectMate services. Mostly needed for debugging. -- `container/run:/run` is mounted to hold the socket files for inter process communication. `fluentin` and `parser` communicate via the file `ipc:///run/parser.engine.ipc`. `parser` and `detector` communicate via the file `ipc:///run/detector.ipc`. +- `container/run:/run` is mounted to hold the socket files for inter process communication. `fluentin` and `parser` communicate via the file `ipc:///run/parser.engine.ipc`. `parser` fans the parsed data out to both detectors: `parser` and `detector` communicate via the file `ipc:///run/detector.ipc`, and `parser` and `detector-rule` communicate via `ipc:///run/detector-rule.ipc` (see `out_addr` in `parser_settings.yaml`). ### Ports @@ -56,20 +56,20 @@ The parser service exposes port **8001** (internally the service runs on port 80 ### Depends -This services depends on the `detector` service, because `detector` creates the socket file to establish communication. +This service depends on the `detector` and `detector-rule` services, because they create the socket files to establish communication. ## Detector -The detector service listens for parsed logs received from the `parser` service on a [Nano Message socket](https://nng.nanomsg.org/). It expects the data in the [ParserSchema format used by DetectMate](https://ait-detectmate.github.io/DetectMateLibrary/latest/schemas/) and sends anomalies using the [DetectorSchema](https://ait-detectmate.github.io/DetectMateLibrary/latest/schemas/) to another Nano Message socket provided by the `fluentout` service. +The detector service listens for parsed logs received from the `parser` service on a [Nano Message socket](https://nng.nanomsg.org/). It expects the data in the [ParserSchema format used by DetectMate](https://ait-detectmate.github.io/DetectMateLibrary/latest/schemas/) and sends anomalies using the [DetectorSchema](https://ait-detectmate.github.io/DetectMateLibrary/latest/schemas/) to another Nano Message socket provided by the `fluentout` service. This is the `NewValueDetector`, which needs training data before it can flag anomalies (see the [Getting Started](getting_started.md) tutorial). ### Building the image -Docker compose builds the `detectmate` image using the [Dockerfile](https://github.com/ait-detectmate/DetectMateService/blob/main/Dockerfile) in the root of the DetectMate repository. This image is the same for `parser` and `detector`. +Docker compose builds the `detectmate` image using the [Dockerfile](https://github.com/ait-detectmate/DetectMateService/blob/main/Dockerfile) in the root of the DetectMate repository. This image is the same for `parser`, `detector` and `detector-rule`. ### Volumes -All DetectMate container(`parser` and `detector`) need the following volumes mounted: +All DetectMate containers (`parser`, `detector` and `detector-rule`) need the following volumes mounted: - `container/config:/config` provides the DetectMate configuration. Each DetectMate Service needs one **settings** file for the general service configuration and one **config** file for the DetectMate component-specific parameters. For `detector` these files are `detector_settings.yaml` and `detector_config.yaml` respectively. @@ -77,20 +77,50 @@ All DetectMate container(`parser` and `detector`) need the following volumes mou - `container/run:/run` is mounted to hold the socket files for inter process communication. `parser` and `detector` communicate via the file `ipc:///run/detector.ipc` and `detector` and `fluentout` use the socket `ipc:///run/output.ipc`. +- `container/state:/state` persists the values `NewValueDetector` has learned during training, so it survives container restarts. + ### Ports The detector service exposes port **8002** (internally the service runs on port 8000). The management API server runs on this port. This service exposes metrics for the `prometheus` service and allows to start, stop and reconfigure the service. ### Depends -This services depends on the `fluentout` service, because `fluentout` creates the socket file to establish communication. +This service depends on the `fluentout` service, because `fluentout` creates the socket file to establish communication. + +## Detector (Rule-Based) + +The `detector-rule` service is a second detector that listens for the same parsed logs as `detector`, on its own socket. It runs the `RuleDetector`, which evaluates a fixed list of simple rules (e.g. "the parser found no matching template", "the log text contains a keyword like 'error'") against every log line instead of learning from training data. See the [Getting Started](getting_started.md) tutorial for a walkthrough that triggers one of its rules. + +### Building the image + +Docker compose builds the `detectmate` image using the same [Dockerfile](https://github.com/ait-detectmate/DetectMateService/blob/main/Dockerfile) used for `parser` and `detector`. + +### Volumes + +- `container/config:/config` provides `detector_rule_settings.yaml` and `detector_rule_config.yaml`. + +- `container/logs:/logs` exposes all log files that are generated by DetectMate services. Mostly needed for debugging. + +- `container/run:/run` is mounted to hold the socket files for inter process communication. `parser` and `detector-rule` communicate via `ipc:///run/detector-rule.ipc`, and `detector-rule` and `fluentout` use `ipc:///run/output-rule.ipc`. + +Unlike `detector`, `detector-rule` does not mount `container/state`, since the rule-based detector doesn't persist any learned state. + +### Ports + +The detector-rule service exposes port **8003** (internally the service runs on port 8000). + +### Depends + +This service depends on the `fluentout` service, because `fluentout` creates the socket file to establish communication. ## Fluentout -The `fluentout`service receives data from the `detector`, parses it from the proper detectmate protobuf format, and then writes the alerts to the logfiles `output.%Y%m%d`. +The `fluentout`service receives data from both `detector` and `detector-rule`, parses it from the proper detectmate protobuf format, and then writes the alerts to the logfiles `output.%Y%m%d` and `output-rule.%Y%m%d` respectively. While writing to a log file is just an example in this setup, you can refer to the [Fluentd documentation](https://docs.fluentd.org/) for guidance on writing alerts to other targets. +Since each detector's output socket is a 1:1 [Nano Message](https://nng.nanomsg.org/) `Pair0` connection, `fluentout` needs one ``/`` pair per detector rather than a single shared one - but both live in the same Fluentd process and container, so no second `fluentout` container is needed. + ### Building the image Docker Compose builds the `fluentout` image using the configuration in the file [container/Dockerfile_fluentd](https://github.com/ait-detectmate/DetectMateService/blob/main/container/Dockerfile_fluentd). This Dockerfile uses the official Fluentd image for the base and adds the necessary Fluentd plugins. @@ -99,22 +129,21 @@ Docker Compose builds the `fluentout` image using the configuration in the file `fluentout` mounts the following volumes using bind-mount. -- `container/fluentin:/fluentd/etc` provides the fluentd configuration. The file [container/fluentin/fluent.conf](https://github.com/ait-detectmate/DetectMateService/blob/main/container/fluentout/fluent.conf) can be used to reconfigure fluentd to write to other targets. - -- `container/fluentlogs:/fluentd/log` exposes the directory containing written log files. By default, `fluentout` writes all alerts to files named `output.%Y%m%d`. +- `container/fluentin:/fluentd/etc` provides the fluentd configuration. The file [container/fluentin/fluent.conf](https://github.com/ait-detectmate/DetectMateService/blob/main/container/fluentout/fluent.conf) contains one ``/`` pair for `detector` (`ipc:///run/output.ipc` → `output.%Y%m%d`) and one for `detector-rule` (`ipc:///run/output-rule.ipc` → `output-rule.%Y%m%d`), and can be used to reconfigure fluentd to write to other targets. -- `container/run:/run` is mounted to hold the socket files for inter process communication. `fluentout` and `detector` are talking in this configuration using [Nano Message Next Generation](https://nng.nanomsg.org/) via unix domain sockets for maximum performance. +- `container/fluentlogs:/fluentd/log` exposes the directory containing written log files. By default, `fluentout` writes alerts from `detector` to `output.%Y%m%d` and alerts from `detector-rule` to `output-rule.%Y%m%d`. +- `container/run:/run` is mounted to hold the socket files for inter process communication. `fluentout` talks to `detector` via `ipc:///run/output.ipc` and to `detector-rule` via `ipc:///run/output-rule.ipc`, both using [Nano Message Next Generation](https://nng.nanomsg.org/) via unix domain sockets for maximum performance. ## Prometheus -[Prometheus](https://prometheus.io/) is the monitoring server that collects performance metrics from the management api that runs on the `parser` and `detector` on **port 8000**. +[Prometheus](https://prometheus.io/) is the monitoring server that collects performance metrics from the management api that runs on the `parser`, `detector` and `detector-rule` on **port 8000**. ### Volumes `prometheus` mounts the following volumes: -- `container/prometheus.yml:/etc/prometheus/prometheus.yml` is the single prometheus configuration file, that tells prometheus to collect the metrics data from `parser`and `detector` at **port 8000***. +- `container/prometheus.yml:/etc/prometheus/prometheus.yml` is the single prometheus configuration file, that tells prometheus to collect the metrics data from `parser`, `detector` and `detector-rule` at **port 8000***. - `prometheus_data:/prometheus` holds the prometheus database. diff --git a/docs/getting_started.md b/docs/getting_started.md index 9215edc..e88b4bb 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -185,13 +185,14 @@ Let's start the default pipeline, just to test it: ``` alice@ubuntu2404:~/DetectMateService$ sudo docker compose up -d -[+] up 6/6 - ✔ Container detectmateservice-fluentout-1 Started - ✔ Container prometheus Started s - ✔ Container grafana Started s - ✔ Container detectmateservice-detector-1 Started s - ✔ Container detectmateservice-parser-1 Started - ✔ Container detectmateservice-fluentin-1 Started +[+] up 7/7 + ✔ Container detectmateservice-fluentout-1 Started + ✔ Container prometheus Started s + ✔ Container grafana Started s + ✔ Container detectmateservice-detector-1 Started s + ✔ Container detectmateservice-detector-rule-1 Started s + ✔ Container detectmateservice-parser-1 Started + ✔ Container detectmateservice-fluentin-1 Started alice@ubuntu2404:~/DetectMateService$ ``` @@ -199,29 +200,33 @@ To check the status of the containers, we can use `docker compose ps`: ``` alice@ubuntu2404:~/DetectMateService$ sudo docker compose ps -NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS -detectmateservice-detector-1 detectmateservice-detector "uv run detectmate -…" detector 4 minutes ago Up 3 minutes 0.0.0.0:8002->8000/tcp, [::]:8002->8000/tcp -detectmateservice-fluentin-1 detectmateservice-fluentin "tini -- /bin/entryp…" fluentin 4 minutes ago Up 3 minutes 5140/tcp, 24224/tcp -detectmateservice-fluentout-1 detectmateservice-fluentout "tini -- /bin/entryp…" fluentout 4 minutes ago Up 3 minutes 5140/tcp, 24224/tcp -detectmateservice-parser-1 detectmateservice-parser "uv run detectmate -…" parser 4 minutes ago Up 3 minutes 0.0.0.0:8001->8000/tcp, [::]:8001->8000/tcp -grafana grafana/grafana:latest "/run.sh" grafana 4 minutes ago Up 3 minutes 0.0.0.0:3000->3000/tcp, [::]:3000->3000/tcp -prometheus prom/prometheus:latest "/bin/prometheus --c…" prometheus 4 minutes ago Up 3 minutes 9090/tcp +NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS +detectmateservice-detector-1 detectmateservice-detector "uv run detectmate -…" detector 4 minutes ago Up 3 minutes 0.0.0.0:8002->8000/tcp, [::]:8002->8000/tcp +detectmateservice-detector-rule-1 detectmateservice-detector-rule "uv run detectmate -…" detector-rule 4 minutes ago Up 3 minutes 0.0.0.0:8003->8000/tcp, [::]:8003->8000/tcp +detectmateservice-fluentin-1 detectmateservice-fluentin "tini -- /bin/entryp…" fluentin 4 minutes ago Up 3 minutes 5140/tcp, 24224/tcp +detectmateservice-fluentout-1 detectmateservice-fluentout "tini -- /bin/entryp…" fluentout 4 minutes ago Up 3 minutes 5140/tcp, 24224/tcp +detectmateservice-parser-1 detectmateservice-parser "uv run detectmate -…" parser 4 minutes ago Up 3 minutes 0.0.0.0:8001->8000/tcp, [::]:8001->8000/tcp +grafana grafana/grafana:latest "/run.sh" grafana 4 minutes ago Up 3 minutes 0.0.0.0:3000->3000/tcp, [::]:3000->3000/tcp +prometheus prom/prometheus:latest "/bin/prometheus --c…" prometheus 4 minutes ago Up 3 minutes 9090/tcp ``` +Notice that the pipeline has **two** detector services running side by side: `detector` (running `NewValueDetector`) and `detector-rule` (running `RuleDetector`). The `parser` sends every parsed log line to both, and `fluentout` writes each detector's alerts to its own output file (`output.%Y%m%d` and `output-rule.%Y%m%d`). + For now, we will shutdown all containers: ``` alice@ubuntu2404:~/DetectMateService$ sudo docker compose down -v [+] down 9/9 - ✔ Container grafana Removed - ✔ Container detectmateservice-fluentin-1 Removed - ✔ Container prometheus Removed - ✔ Container detectmateservice-parser-1 Removed - ✔ Container detectmateservice-detector-1 Removed - ✔ Container detectmateservice-fluentout-1 Removed - ✔ Volume detectmateservice_grafana_data Removed - ✔ Network detectmateservice_default Removed + ✔ Container grafana Removed + ✔ Container detectmateservice-fluentin-1 Removed + ✔ Container prometheus Removed + ✔ Container detectmateservice-parser-1 Removed + ✔ Container detectmateservice-detector-1 Removed + ✔ Container detectmateservice-detector-rule-1 Removed + ✔ Container detectmateservice-fluentout-1 Removed + ✔ Volume detectmateservice_grafana_data Removed + ✔ Network detectmateservice_default Removed ✔ Volume detectmateservice_prometheus_data Removed ``` @@ -252,7 +257,6 @@ services: - parser parser: - # image: detectmate:dev-0.1.6 build: . volumes: - '$PWD/container/config:/config' @@ -265,7 +269,6 @@ services: - detector detector: - # image: detectmate:dev-0.1.6 build: . volumes: - '$PWD/container/config:/config' @@ -278,6 +281,18 @@ services: depends_on: - fluentout + detector-rule: + build: . + volumes: + - '$PWD/container/config:/config' + - '$PWD/container/logs:/logs' + - '$PWD/container/run:/run' + command: uv run detectmate --settings /config/detector_rule_settings.yaml --config /config/detector_rule_config.yaml + ports: + - "8003:8000" + depends_on: + - fluentout + fluentout: # image: dm-fluentd:latest build: @@ -382,7 +397,7 @@ generate anomalies. ## DetectMate Config -The log pipeline uses two DetectMate services, parser and detector. The parser splits the log line into meaningful tokens, which the detector then uses to identify anomalies. We need to configure the parser and detector. Since the detector needs to know which tokens it receives from the parser so it can look for anomalies, the two configurations are closely related. +The log pipeline uses three DetectMate services: `parser`, `detector` and `detector-rule`. The parser splits the log line into meaningful tokens, which the two detectors then use to identify anomalies, each with a different strategy. We need to configure the parser and both detectors. Since the detectors need to know which tokens they receive from the parser so they can look for anomalies, all three configurations are closely related. ### Parser @@ -412,10 +427,16 @@ parsers: remove_spaces: false remove_punctuation: false lowercase: false - path_templates: /config/templates.txt # empty file because there are no templates necessary for apache access logs + path_templates: /config/templates.txt +``` + +`container/config/templates.txt` contains a single wildcard template that matches any well-formed access-log line: + +``` +<*> - - [<*>] "<*> <*> <*>" <*> <*> "<*>" "<*>" ``` -We don't need to modify that configuration, since it is compatible with the nginx access.log format and we can now continue with the configuration of the detector. +This means every normal Nginx request parses successfully. We will rely on this later: a log line that does **not** match this template is exactly what the rule-based detector's `TemplateNotFound` rule reacts to. We don't need to modify the parser configuration otherwise, since it is already compatible with the nginx access.log format. We can now continue with the configuration of the detectors. ### Detector @@ -435,30 +456,48 @@ detectors: Here, the `URL` token from the parsed data is monitored (`- pos: URL`), and the first two log lines are used for training (`data_use_training: 2`). Any subsequent log lines will be evaluated for anomalies and compared against the values seen during training on the first two log lines. +### Rule-Based Detector + +Next to the `NewValueDetector`, the pipeline also runs a second detector, `detector-rule`, using the `rule_detector` method. Unlike `NewValueDetector`, it needs no training: it evaluates a fixed list of simple rules against every log line, such as "no template was found by the parser" or "the log text contains a keyword like 'error' or 'exception'". Its configuration lives in `container/config/detector_rule_config.yaml`: + +``` +detectors: + RuleDetector: + method_type: rule_detector + auto_config: false + rules: + - rule: "R003 - CheckForExceptions" + - rule: "R004 - ErrorLevelFound" +``` + +With this config only `R003` and `R004` are enabled, so the rule-based detector stays quiet while we work through the rest of this tutorial (plain Nginx access logs don't contain exception/error keywords, nor a `Level` field). At the very end of this tutorial, we will enable the `R001 - TemplateNotFound` rule and deliberately send a log line that cannot be parsed, to see the rule-based detector raise its own alert. + Now let's start the pipeline using `sudo docker compose up -d` and send two valid log lines with two different status values: ``` alice@ubuntu2404:~/DetectMateService$ sudo docker compose up -d [+] up 7/7 - ✔ Network detectmateservice_default Created - ✔ Container prometheus Started - ✔ Container detectmateservice-fluentout-1 Started - ✔ Container detectmateservice-detector-1 Started - ✔ Container grafana Started - ✔ Container detectmateservice-parser-1 Started - ✔ Container detectmateservice-fluentin-1 Started + ✔ Network detectmateservice_default Created + ✔ Container prometheus Started + ✔ Container detectmateservice-fluentout-1 Started + ✔ Container detectmateservice-detector-1 Started + ✔ Container detectmateservice-detector-rule-1 Started + ✔ Container grafana Started + ✔ Container detectmateservice-parser-1 Started + ✔ Container detectmateservice-fluentin-1 Started alice@ubuntu2404:~/DetectMateService$ sudo docker compose ps -NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS -detectmateservice-detector-1 detectmateservice-detector "uv run detectmate -…" detector 7 seconds ago Up 5 seconds 0.0.0.0:8002->8000/tcp, [::]:8002->8000/tcp -detectmateservice-fluentin-1 detectmateservice-fluentin "tini -- /bin/entryp…" fluentin 7 seconds ago Up 4 seconds 5140/tcp, 24224/tcp -detectmateservice-fluentout-1 detectmateservice-fluentout "tini -- /bin/entryp…" fluentout 8 seconds ago Up 6 seconds 5140/tcp, 24224/tcp -detectmateservice-parser-1 detectmateservice-parser "uv run detectmate -…" parser 7 seconds ago Up 5 seconds 0.0.0.0:8001->8000/tcp, [::]:8001->8000/tcp -grafana grafana/grafana:latest "/run.sh" grafana 7 seconds ago Up 5 seconds 0.0.0.0:3000->3000/tcp, [::]:3000->3000/tcp -prometheus prom/prometheus:latest "/bin/prometheus --c…" prometheus 8 seconds ago Up 6 seconds 9090/tcp +NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS +detectmateservice-detector-1 detectmateservice-detector "uv run detectmate -…" detector 7 seconds ago Up 5 seconds 0.0.0.0:8002->8000/tcp, [::]:8002->8000/tcp +detectmateservice-detector-rule-1 detectmateservice-detector-rule "uv run detectmate -…" detector-rule 7 seconds ago Up 5 seconds 0.0.0.0:8003->8000/tcp, [::]:8003->8000/tcp +detectmateservice-fluentin-1 detectmateservice-fluentin "tini -- /bin/entryp…" fluentin 7 seconds ago Up 4 seconds 5140/tcp, 24224/tcp +detectmateservice-fluentout-1 detectmateservice-fluentout "tini -- /bin/entryp…" fluentout 8 seconds ago Up 6 seconds 5140/tcp, 24224/tcp +detectmateservice-parser-1 detectmateservice-parser "uv run detectmate -…" parser 7 seconds ago Up 5 seconds 0.0.0.0:8001->8000/tcp, [::]:8001->8000/tcp +grafana grafana/grafana:latest "/run.sh" grafana 7 seconds ago Up 5 seconds 0.0.0.0:3000->3000/tcp, [::]:3000->3000/tcp +prometheus prom/prometheus:latest "/bin/prometheus --c…" prometheus 8 seconds ago Up 6 seconds 9090/tcp alice@ubuntu2404:~/DetectMateService$ ``` -**Wait a couple of minutes until parser and detector containers are up and running.** You can check by executing `sudo docker compose logs parser` or `sudo docker compose logs detector`. +**Wait a couple of minutes until parser and detector containers are up and running.** You can check by executing `sudo docker compose logs parser`, `sudo docker compose logs detector` or `sudo docker compose logs detector-rule`. When the containers are ready, the output of the component will show `Uvicorn running on` or any HTTP-requests for the `/metrics` endpoint: ``` @@ -514,7 +553,53 @@ alice@ubuntu2404:~/DetectMateService$ Great! We detected our first anomaly. +If you check `container/fluentlogs/output-rule.%Y%m%d` at this point, you'll find it empty (or missing entirely). The rule-based detector has stayed quiet the whole time, since none of the requests we sent contain an exception/error keyword. We'll change that in the next section. + + + +## Triggering the Rule-Based Detector + +So far, `detector-rule` has been running quietly next to `detector`, since none of our requests matched any of its enabled rules. To see it raise an alert, we'll enable the `R001 - TemplateNotFound` rule and then generate a log line that the parser cannot match against the template in `container/config/templates.txt`. + +Edit `container/config/detector_rule_config.yaml` and add the rule: + +``` +detectors: + RuleDetector: + method_type: rule_detector + auto_config: false + rules: + - rule: "R001 - TemplateNotFound" + - rule: "R003 - CheckForExceptions" + - rule: "R004 - ErrorLevelFound" +``` + +Restart just the `detector-rule` service to pick up the change: + +``` +alice@ubuntu2404:~/DetectMateService$ sudo docker compose restart detector-rule +[+] Restarting 1/1 + ✔ Container detectmateservice-detector-rule-1 Started +``` + +Now append a contrived log line to `/var/log/nginx/access.log` that doesn't match the Nginx access log template at all, for example a line without the expected quotes and brackets: + +``` +alice@ubuntu2404:~/DetectMateService$ echo 'this line does not match the configured nginx log format at all' | sudo tee -a /var/log/nginx/access.log +``` + +Since this line can't be matched against `<*> - - [<*>] "<*> <*> <*>" <*> <*> "<*>" "<*>"`, the parser assigns it `EventID: -1`, which is exactly what the `TemplateNotFound` rule checks for. Have a look at `container/fluentlogs/output-rule.%Y%m%d`: + +``` +alice@ubuntu2404:~/DetectMateService$ sudo cat container/fluentlogs/output-rule.20260713.log +2026-07-13T12:03:55+00:00 nng.* {"__version__":"1.0.0","detectorID":"RuleDetector","detectorType":"rule_detector","alertID":"10","detectionTimestamp":1783944235,"logIDs":["5269c4a0-304c-431d-809b-ae34a15aa68b"],"score":1.0,"extractedTimestamps":[0],"description":"","receivedTimestamp":1783944235,"alertsObtain":{"R001 - TemplateNotFound":"No template found by parser"}} +``` + +The rule-based detector caught it: `"R001 - TemplateNotFound":"No template found by parser"`. Unlike `NewValueDetector`, it needed no training data at all — it was ready to alert from the very first log line. + +## Grafana UI + The Grafana UI is accessible at http://localhost:3000 (default login credentials in this demo are admin/admin) and the raw Prometheus metrics can be explored under "Drilldown" → "Metrics". Under "Dashboards" is a basic Dashboard with graphs for Throughput, Latency, Processing rate and Engine state. -This was a very basic example, but it shows how to easily deploy a full log data anomaly pipeline, including two DetectMate services, using a parser for the Nginx access log format, and how this is then used to flag an anomaly. +This was a very basic example, but it shows how to easily deploy a full log data anomaly pipeline, including two different DetectMate detectors, using a parser for the Nginx access log format, and how this is then used to flag anomalies. \ No newline at end of file From 74f70622e8ca6200f811ed625b526704ecb95180 Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 15:01:36 +0200 Subject: [PATCH 05/10] add prometheus target --- container/prometheus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/prometheus.yml b/container/prometheus.yml index e70525b..b6744af 100644 --- a/container/prometheus.yml +++ b/container/prometheus.yml @@ -4,4 +4,4 @@ global: scrape_configs: - job_name: 'prometheus' static_configs: - - targets: ['parser:8000', 'detector:8000'] + - targets: ['parser:8000', 'detector:8000', 'detector-rule:8000'] From e28f2ee84289ee40d002875f1b4e805a7c7a26ac Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 15:10:26 +0200 Subject: [PATCH 06/10] update pipeline figure --- docs/images/Detectmate.drawio.png | Bin 379776 -> 406308 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/Detectmate.drawio.png b/docs/images/Detectmate.drawio.png index 25180aafa908ff213ab73a560c98caea95c0ba80..021e52473b1c60c1e286b2a11031746216953032 100644 GIT binary patch delta 60294 zcmbrlbyQUC`adi~3rI<~fPmD{Ej>s}cXvp44J9buLkK7-A<`iY0sA4mS0q<&Xe=z|IE#^`@nnm4&6tueaI2Fg9{TA6N_vJU$#3jA|8x7w?VecK7~6 zSPUmT6&xoFiUv;y#|I0T0e&}ipCeueDIZ1xhW~jRe;g`iye=}s-!6nlp^VQ$eTeao zYhX&Gh`v8>i7!Dx{*zeXb>6?dT8&Eef4qtZ%R__5zvEHluXj*>i-7~S4+2;gafL&N zQMtlBgz=Iiy53!g2gml4E!YAEd~pyAy#|I3*KhpS0kCCzI2KsHJskc&DCYS~F0h?h z5Z-@W3v+dZ`z61ox9k4A_PfoH@)D2|gCq;{=BbOP&G!(hIEK8IBe9-WmA6 z1ZTL1@zQ8vu-j$OU!wUNk1#n`I6@FFJB-y8j_iL>i3i7X$0qLGg%YUBuC``kM$TrY zFbsD%LKvAd95xKm9ggN-DM@gLBL;DTVeg#bi2rdq2aJmHmu=v|C|5!MDS8-@=l>yt zcpN1B|0(m4A0W#AOXhem=nM$umzf}}fb3x+v!IAOo&7XQN;r<6MhQ!I0K^8v{2vG7aDab1(;w!?3yVUBH-e2XffoL}@AA*HDSw^#^K3wp|2NSmt03)P zMArZ|%S!Y&Tg8Njr-s!~BVr)2aPa}&42yLJ99|kN9M%XA|I2o2M&RMq;r{kcAtL-^ zSOOy8IsS-%{7@hL_6raj9}Ec@p8U=xV1Ii)8X0&VxZ`hzDU1S-0^3J{r})ojy;0%W zVJpml47a}#Xc^Qs*#h0nG4Jw2|oRA5(CI0 zMBrm*;eyRE!xQ}_3eMli^HUT*O%Jd-r$0qOdFNEfKMfAV$@wR~{^2&iy+*G&7s3%;oSZ9zaQX6p=F2sJUyNYfezL(4?=`J$ACA5 zDRRP-{?q~xLmd8<)SnRdhhzOgst|~iFFq830)~YF&j(zN^8cT7Ok57W|1Y$A!b&-R zThLF?3RUBQuTkSc@Lc$to@I#9>D{9N{vYMpMH8y0Z{&TqyBv% z@4r1w|NBCAo;$L^fBoV32Mg@H|H1-*`ZzE}YDBF6pn&)fCb<5Io9w(?urg}IKb^)E zWaaa4MAl5k1Jty5P<2w zM_5-NZro)!cd=X)_QV|y3)a8_k9!w9M1Ru<@cU0D`FMW)+uh34#qy7w>Gv~#&v1a~ z3&ctwTlzByqbQiUs~g$6n*FaB{1E10|2Gm~KpZBy1LZq;{7W4Eou0w|LK7g6J-tIw z8Vr}8xb%xWjyn(M`EL{vf@kO9g*mQ)$YJT|@XELx|Dg&_Fc&Of4O9+T;ZGp}_cY#t z5;q{V|8XKNC)Yo1;_i%Sba+D?uAdI_7qkFT0S*H`6&7=c71C0OT2SA7&GviuuK<&pG%5 zZR)(~Fmw4swAteJPOmTW%Cvy{&exQb+3806Z|QZ;t~9s7{#aNbF%c9LI4VknKmQRR zf$cz<&98I}jhFbZU*0{*1QCg_mVJwt{x83sdx-G)y<3=hRAZbI>NDXau#r%i37TB1q=Q4Tv(Sy%lTwG^JrZcw~ zdXzJ0x!vOw4O4j~ASFa0ycIYx9VcJIR1#1DIiufML5O-1|NT3%Nz?sJlAvS+0ui}X zfY_-6gU&yXF(HL2TJ|jmVKD7h7_xCF&OloR!4};}A^Cgk%TtegKM~kp-Bf>orf50m!5T1d4&OwIY}uEt9zdZos@I+nu-GM}n~L zX6f3TK#4v+Bya0d(JC*eWX=mT;o8^pu~ne^6f9y3oY*-2HP}#z6$+5|hl6_47>cC;Y~WH#Km18I;fNE)Km7JHq=g)RD*S^j4$IwprDyhrIV+mGpiQT#qB-4e7MQA&Y(p)?h9`s4s*c_ zYN@Dg1HW^#Ti4A|ygKJqg|JtzP~<<3#XoB!^d~tBp!Nu4Jabw!3;^Fb2R;pDAs`Jf z32;?ki1NeVYQe>!Z-KgYKIxA4%!K74qD|koEvCo)5Hq$mT%cl~#%?C#$&H{AO~es0 zoahqhy+L!WnAD6P#Rz6`21$4 zAK}1pzNBKyXy2@^l|4BZBfJ&Ew4W`hok>=cy`lxWywazTWWm&2(L%ra!^Z9*g;5#( zmA-|^t$C8LzBJKHJM)6PI{sQut9M0xrPUCN-)g%1g+YVIK#?qr#e9tQ{w?*rraAPP zxtm#{{X4g~hmiA1LFS(Dpn|q*LG^Fl2GHR8mu2l0enn4S? zl~Xz*&Ctjgv``4FsJ+v2Yge!CoHa(IO- z?u|pyLNBHLnYE+KS!>R1e}xatW&fM;P9vZwOsyKb^qRcxhxXx0QKDseRtP(D{rSse zHX)P>z@2oobN5E@vnSz*rNizZ6%dG<{Jo(Xw$uphB~@MMpe)m-mY^1RgapFjo#@>& zh0dg<858Ah&#U=y){!1kWw1y7V1KBTE&&n3lS6uGlFI(Ab;S{|!mHT`B8~>IlQnr{ z7A+M*3)$7c15|0)tJ-gsdFlY^nGUFQdZ34LBe@V+n6HQYG4j zK3^oT9;HkaHvhD?##$2Vl`Z65SE>?env2&CWNbF(y^{ec1F3HKoJ&^erk zrUC7{*2pJ@4=)U6>edCTAuyCZc6i4PBn?*7MirgeonbQiLy?I>{RtBN6+- ziKIh4tWJb3hf&~1QHVWnd6MCEwf5|IcE2t*G&D@QZH_Wmc~-to%E1y5cq$ls$L(JT-UvT1q6g>K}d9gdlJ8B()bx@1w%16oXTcZuiG zESm~CO%C)`D}t@8UeCOp8|mPp9VN?&1@ilmg)P zln8MmB5}Wh@=Fnb#(t$aSw7$>2Or=e0Y4HMjGxR&(@=K5g#HQT=uW)enz3K2$8^u4cYpsrP|Wm?R=PAW}H@(ba4s9spaDfzlNry&uz`!pz0EO;@yhKSwvzR8D#*M!yd>AW}o z_)zi*=4hDeb({pw7yr5d3f@4v+CWy`Aa^lhrG=le0B(NhE_J?w2+`g@Vk&y>`FmnMG{TFs#bT$NIb829P~BiTdS~N#MtAn08!$>&EHb; z01X7F>CbG~K>?Iqo%lA#ec-2AXttHA5fsd0L?Z$d|O*S(BXzN6ziJm#%=N~Y~&Dj%fyT>Y*#RzC0omf;t&+b z;SnTomQD8cowmy#8AT~=04^O=!GT9CJWjXHNolb&a(ysY_A%aOPa-3y{fuDzLMNWp zK$fJ-&6Ovf*~QU%p&)~D&Rc)~dw3yzwT_Fb9(i&p4utuiwM%$ax~1>n)_)xVOg6zK z2CaTRT0TOObjAyiAwMLiLCJ{uRxp()(TOp?8#P|51-=@d~;<6ddDKqI(uCgAn+?{Dut+t(@r{*aUW74gS z@jHVv@C##9pI*%nhAQ}89KAy$U}?a194paNGMcKfr(wO>h}WY&>W7vZA&ayF;JS$SXVnp&2x~%z9sLyVWriPo%z2Ny z5}vgci{AqRjU{GfxT{m*ZEy;7xd$9;aEGcY0|;=q?!>TLGlSnZr= z*geT?)ULZTdg;3Z*~s$g{D}&gPd`9nps_1A*O$Xtl4u7fCwWfGJp&m+-dQfBLS6}l zss&#TiC7f5*O>y`Tv$hkO;Kfii^w%iNL|DaP-xJaj%@%^26C`xD3 z{W*CF2?Pu>{sfsMCTSou&{!VXJWlxTRQAMCnwqlUrN0WcKOBtPEKk2Gyjn30$|7l= zUs&I$hX0Ex*`tF-b5)bVvNZ3>nL63ZQ}tb0sc7adbvl1QByaV;KUHV_RU6#_LyH)K zOyUzs3=(dd2=vg(ZH}~3Q2d3+sQM~}QDUy?ub9x?_s^QA* z?o3t{9hjKds@hLgM-Jvli_^*^v@p=3;WNEuP|170zTta$l8cJ__&Z?2xT-9<&$tyN zL<*lE#6j+2N@D^xQ_F8$24@3{3ra9a6VFk{i=eOf}?7-CLX1`cNHX|Vgj|3tSI zoU4$Ys+=b$&1F4I+0f;)Hz&F=`hmoCXF4V1fc_wyfHlFf$*3LaV1NIeexnz*3zJTD z1Qv8Ur><%}Ajn3kwTx?ovhnQ|gEkhHZ;` zzk3C8im9B^@)S~(GuVpH>S7=gDC4wn3<*E5pNAV;VISTh$A|jx{2z(^DQLj}Ggvjz z*7+LXtO#q3u>@!K)slBcJ^Mw@(DDjukR?GS=45wN7_B5woBV7Q#L$4idGGG2RIv4+ z3*$c+;*3mU(#pzvLH$(|eIIOGY(c1d~RFG_k)b0m2BUjprW?!a6w-e>3)<?lL0Gqr~ze1B?5 zR|&gLRFI+h1?pmyMhK+`JQ3sWv%WCpTp3BN3UgWw4UIOCITG+AhU9Ii2THrZScu0s zkIiUAxoLMGRK;a`lB-JC^MJtq^pgteLuySii+ij4GL0hGl3VIrbfJ zi})Z+|7Z+pO0NpU+b%zop;QJ4WILHeg(hlMoxrA1O)fCJ--yp z+#nwMU}<+(k+8LM7`mhWL3^N3t!P+g{(I0)^2T2coRgsI{lBmWxh1yCWi4vjvZ8@R`P=JIH z{GPcq?>inH4n+5ff8%7H26S1OW`##ZMml zSza(NfK+R3-f#1VT7=l0bRWSR=t0D2F+$KAU0(&XPWA=SC#vt?J`KksB`wD15}!fo zvOI*^H(%$eBTxu=abl42#ylq{TmX;5gFq;aRy9!(ZX&R*BEw`Y6K__aa}d_Bfj zvP*qkv5m$j4NmEv(RI139?(EDd|4x)OY>J3vXlr;gWxnoYY@7mkf4WeHr%#XS>1xg zjdH+!R@TLa;P;d0f$Pq$fqoLQ`cJi!#pw|o5bt+C_7q&L6j|QxOUs_`tBz4h_qTg; zHCTTf{3a`6y_17*Gw-En{CZP5i6HYfn3j7iUNORms534om$_FqsgbEb^R6(aMA%Ag z0laFN>Mc~SCg!063QdN8-ny)Jg;I(K23tPoxWRZAskj)f?7i)mZbL7ng3TNcKN~6- ztED78Kzkh=5`vRw{4(@q!Mb{0t{hU?S6#$Pg2}$G*6ETzkk^Ou4K}y8eW&{Rn3EYJ z2NbchB-p;MyBHNH=X#~GnREtyJwQtmWJ+?NpT$sz`ux33Y3$X5mhH)uY13Yw-Vu%1 z3y--f1ZlhWv}aW)8IsVCpZ2ePppVO_pZ}(jm&%Y21jNYxt&I&Y$JKA?bXt&#gfd+_ zg?z1@dZ*c{b4iL$H!YcHPIKnaaYOTt^AL7`NO`FXFe?E;G|!`C#(RnqhEO?!Mz6$` z{&cAzWQ;6|mHzxR9y=`J3Egwvc=8ebX}hHN&g`Rn!@a$|HIPVRXFB=RVW-=hYrae& z@0F-c6^W1JPP8H7&!5Ni3g2GE--J^5y$2#NtSloV19tYV8Yy#`z9e7$%)M)N@xE_kUhw1(3CSk-JFHj3mZsL$+zl`79Myt4{b+93w+z!mzIrd zDek>4?HriesV1q?pV%Ek)xP{#J{4WSY3W&EqxWfEhfF!GY*K{wRi~;X&aOO2a8l9p zU`Yw>m`S^mzTR-2a_4AzdPYde#l(piFL6K19J1{zfRC4CiH-huz zpZf?>ogdCoeR8-FbX^;Njepw^%MntcvfFgz0(YQ5| zgeNb6D!t{%JZwkM+9M~m>0*PC-|woH#eSwiWxTf2#n$;=3oAOpC-c1(p`<~k&IhYh z9#X{Aym!58=>KS5+wj&Yd=QlePIDJHbzt#{p2Uw$#$)Ho^7gEYnr<9eK(8kDWeLSDSqCp9%*>eqvG5?$}UYuAd2 z10D(u+U|0k@QNfCf=(`6f7i5TI{l&9qWOey?BmM|E`M|Mo+musRuu90w;0QZgbiHb4JFm;0_p?M3hwz}MQqlagb^AXMKgQ*n&Q23k@|<>_DdkL7C4W$yk}9#Lx%!+Tns*| zzMNX2>N}Tdp7r5^A)8K~1+kOuseH%>Y=W+{X;_|>DdBn(^tLJW!{YX~{BlpCO6IAU zptCGc^1NloA@dj}^HW*iYArGHMi4l3~Uz zb|JrP;oP&)_&E59s~HDUwLhE+MR1UjK%of}0TLSZzErN>M$f~Q*D|cH;}ra-{JHI> z*U}$JhJDmZD|74(m3G_I6epAwPf8~BlSMj2UFs89-b4LRw>8yr0DUTJjjx!5si|Ey zZ5klnPkum(ob=F~W~_h~F70jFR7>1g0V-{&TmmkgLe6;g3!Q2q?~{iszLMNI8>N+| z=fN07ezt~@3)egi1}#QKn$_i3-CY)*(C|~%uWX0{C6i@JxeD$((^`#DCSYZ?xgreo zIYr@XRiYvv#r(cB9_SFzRDKWCeJ=Y8l6R#+5HhARkVmYy1|bI@>nLOhO4~}{kk}6A zNaLD;IpA~m0k{4PdBa}apDC=USMQc9m%@HmFgcQaFEu&?`-fQ%BDq>lRWp-4^+`7W z_C^{!s`IPDdYr5HEJp+4Di%lBbv(@Z0Ru+B;Q;lijpT78fp+RX%9J4Nstn9Ll<%J~ z4D<4}tNCzOf^~PPJ6`dyp6V6FLF|>cq|C}lL zag2b!qujj&M)b)TX#R2dlBwtAX^I6XlG|fwai$u?G+a^&%d45 z0)OobHBptBI3XrRGTimeRaGZ7>KoO@Xi5 z)ef-CTUQ)<|-JBzCiJ673N5{3ct$>_Ye5 z>G{~rWx-Uvz?H9=is{1V!7RG9A~w+LW3Frfo{|Ug@QJ5v6)nTH_Dq5+39-94ozKdo zJiao}49B8+czQ-AgN*_`o2BNyh}i17xo}ok^heo?(V0!Q*p)zzpi1jQq#*?{v-{*F z`1Xc0hb%o+<_3@PgV2onRg;RDp6O(qYRdZ%?0(qTI^?f<0;=!%DmP{oxLN6aY8Uay zz&v|}N|ur&K(w{BHG2F@ZCl_N_DT8R?0Le-)nn=VV(E7^?E0TE1igyO!Qs< z%aX?7*}>(xW{$E}CX9(^&45#$Bz}_rqgRj!%b*v0^k&d2Kljxh%8WN-5TD?qz>*v9 z#4G6ab%A_+wQ9MlwDrU%+PfY`6oo*Z-&<9$nYr%Wt=*=4gR!fy{8Uoo)F3u;Lab-K ze)@%wmRo-35I^mPWc&l%{g}Bqgmrdisn=#EAc}^=Zvyk>^eK@#RN5!km#3#I0~`7S zf$8VZONMf)n-B-57WhBt)t|7H^eEsnWkFZFz0(A^OTgLfh0BIPVMv(da=>6M7YNmm z>znquj#MUF+gQ=jVl8pO!MoPV^X$)r>7At+D%|>}~t}=@uIYwCCsZTtns$#goC+VqKh^Uts8{Qu;!airQ{#q{j1X%1ogW}FC*Q(H5^-raT5#HZ)l5CS zX7bv<;sXLm#1XcacD>sNZA$3U-n;gt%L7_G2IYi3>QFIb3etyip%2Jacg6NmkDDH5 z(3|iRe50md(g&d~57-9nVWa|;r_|x$+X7 zGhdh#Y;rbD9@zJ5MlJRmJ9kl&7*xEAmb@<3PXfA()!&t?Hzy8S~w0~)3LuPr8MssKlmdLms z9ss|NeB<~;tMFm^B@I+y?hZ`OEECf^1qjY+oETy znRV7DM{E!lPupIGTOWDiN1Ia!IRz__3M&Xy+iM8Nc%N?3MyTg6$2t)XV%vTuL0?|A z9k-vYj#Nk&gckT2mR+p%EHkdv6LDB=CNaC*O!G}Pxu&rn9eepro6yH>ZO4g7NJvP^ z!B_US@j1R@H~k_4GD-RZ8+v}hA(16>e;UK+iy0ZRd0M3a51*)p}k zp`mynNjjKZ2l_E4dPsq6kt20OInvqc_vmgyy`e&Wy+vw8d5{veV`9c&6H^i+QraFP zJckI*MWIcf^MeA12$t2aDe-$xk6z=_t>tI>Ju0SoYCR0hB}&j(aq zPhaO*1;;u!H5YXB15KUW9vC(9jEB-dT!X0a6CedT!aR+SD`twlUwa!L8h>JDRPVJ) z4dpH;C|~t`&}G9(YWr!ClPyx2gpt1Qx|z9Hxcps2KTRv^=?|Kg3PWX`AMVVWwDr%K zxs#96TC*Tr=;NV)GrtrqH|tlZSnbPQ=dk#GL^P|s!lbn}J>krm3>9g6vV=AOw7qV21Hx0 zE! zgT$zQ$^NcfEBJ@1n`Jn^IhBYn(+qU>9 z(!GF9CL-4AQI>X2k9ttkk#rw}dPeV)3snPMBoy0($tlFyq%$IbSP)E;(<<&I#|9K`b@zG3kapwS z-;;lV$G&QzBp?ZG)H>zH*Qt}!y)AijzH57SQ5Mh`cS?4$^CK;+eZf`t<}AW`js9C2 zb8$V^Zr=is-Q!2#D;WdPN%DyaoebcAe&U+L?>kj=Bd#tk&y^PQdW z!OOn1A_{RuR>iwP$O^O0_h9Mx_Zpcq2|<~phbl5*H_`SBf=|fAt}l;h<%b5&q;VPK zQ!6Vy1|AL8)qdZoY974rCnM;ygyPlK*$`fYw){0Q{ORs3pQo4S0IWSk`87^QcSlF! zTj)UGOP8ZBN{Os9t=DGIWmcE?Oo`~53!4kGK5OmnKtVS4l2xVCwdGioOX`+%hSRll zhD*j==cHFth}6UkZz@hcj%k++jd_j_#WH_Xkss=u3`4vfjWlULjQMb&IC`F1<^;^D z8dRG7uKBL8@~BSB*ez2}*owLecc>a_J}q9U>{ShAaOjic#>371M6oL0lo{F)3wKRMTp(RaW$1$JKa{JMLh^@wT{vCVx9G-~-EOtCix$*H4_ zH>a}&vNd)`>KW6)bo?40Z)EKWm}T&usM7%Hzgl(PE>Cs>gE9^y<@wz}a|G3=jc>=$ zv&|{h?18Lb(r5nSlX#AR*8+6lCJ6%2>qpP?719m(<{v}xi}<<2qDG1|>=V>D62HdD zy%h~Y>OV`>YD03f;U*liOdSnLWHH+}l1`Ade#9cKveV8yXR`rE^1Xii%T5h2^=W{3 zr4gMk9+zia0kaYvpdYUCWabNwD9{Y12N?%RzAH`b0^nj6WrTBgYocP{+xC>zew!o^ zGL5vWp)L&DM0tRUh77RJ7dwXftxz=fmNP5zH7Z2xHbr zVmJRLr&D7`RXh_JjueR4bChE-r9oZ%WM^|zHsr`BUc;;MSz7Azk}Jdml@9lrcgM}Q zA&3d8y6N8}*U_P7!9e4+smZUH^^IoLAQcG}B^03;^7h^;XOI~?8owE{2WAb3gt140 zafMckj}PwV2eHiy1fchzZm>Ny=_EdXvqwuBI~|sDs8juzF4-l zN#z#LADX{h51nWsSC9y~};qyAawP-&~dV9HQ_!QPa8d}gz7Un*a` zR9ru!_*gRXQLgl+{N3Qq*ZX8(S8#A^X9ZF>;}w3gGb7#=O`(xJPQqjRwz>Ze72$SH zV|fln*qP>#RnAb|^D)Kx*nVdUZRlA$=)Q^gCzk?cm%|nL$x6rU!60CrVD?iY#x@pC zQ7ZN;T+k@3zod1EbCS2FkCt62x_PT5`ffrHn?rpS#4nd}(R30IBBiww6~z({_1gnO$z~D@Pekr`hU}j>pmE zkIyvKFNnMKP&70B74mUOzV?g{q*Q7q`M}f2b(QQXNM87z*cR$EldtCHUto~Dm+Vb# zoQ?20*^#Gu_wHz*E8S}A%n{1zw}8MU@3S0+_gNT$R2aar+Bim%@8m#*n6IzL^}~C6 zgKNV(DbsH>eK1E}T->9wUgNaia-=6QAzCVnOJ0hfdGLLw7)5OhXJquTms&|p_``Rt z;-T*umIZ2dpYOU7FzUbAJ7--ZI_NO$iQ{6NS0OGbv5Q5F{Q!(KMxl@HnM}OoxJ=4L zl4Zg7aa+URod-vjGJ9K>tD3h`tP{?(8*!3!a-ts|l7b^U#<>=P9y3vtOOzHHx3&q7 zctppF$+eG^K~ZF}Wc8Bj?>2KgP;2Yz=81i*)+0FO1v<0SvYFDO*J_yIxJyr+1)!}+ zZd5xOYKBBzwEXQ0P)inEg9^eAXt?VixY3vUlAo`O2^3Xoopp-}ap)K_bb-Urbg}Gih?WP4lL5Uz4=a zaXo(!-iEUb%r^*eTf75C_$aB-_yfRsY^l00qvvN<`(*G5C%tA&az24}jeJZ3(k8Y- zqvjjz`#_;k4{#;jK3s%Tj-*e+M;=)FAKP^bZ?H;P0#dnbo*K1VH0L@m3l@KxboV(< zdFsnQ?~dPwiH|bt4-CZ_ja+xm1{bbM^y)2ve#72l?GoL(gGu-IxF5bK;5uclBJ|R8 zm-d2JjeRiCm$Uf4$|16_=2f?smUJ)UuYQIiLh4u`o|ZW(uk59ri67cq>_%B4dLB1xq^G(Neejc#;}yv3#N^T_=4 ze7@}oDP%_=aVEC`$Q&g*X1q3;t%s_aU9U>x*~`NpGRrIjSz%6ZG8?>Ffwmga5g0n{ zSfaWH+5T5=uHQbq6(UYUhzCtjL()@u7sSauY#Y*pF7!g}jmyQ0XF zZ!E_U6WdVX9+fj7l(iOz&MrW>Aa4xHsf1ohg}QG;rwbq{H7|ZZf{-6%#l&D8+Z~TB zb`_jvxgUDY60lmeybVzOBuBK6c4b3T8;@-^-WGj0PK|Sd#1?E~@AJU#6&3jv)@O0T zt8p9t^qwxPDDWHHy9MJygdS(R&fR_^RZq&$R$$4MI;oNg2|4HjjzZ8>y35#sVO-^P z$J_<9eDz+hgwGf4(m$F7_`GWYEpyyS1`f&+D4IbnJ3s1s%S9uRA zM(#GAl4s#WF=~{^SqniOV}Uc%+!t7GMBX9d_D|DU9`vOwbb6PydLKw7(jt*LluUlRF@8LK+%l za-@2`p{b|yOebrCwe}(I#UxWE^Eq;2E#xRP(@3}^ObpQNJXslImBBWLnOjyRWpvCtu2Zoxyc(1LH8A*_$C5YOCk!#E6e%ReDPOs3G0?RT-f(zX7Qn(7`<_&Qz z^|wG<;+8DAyQ}$w8`ri#iM8*=dVUZ63i5-EL@;3sILSy5HXpjntW!qta4E7+pUWJ_ds8%2SwOzix3!F$P#_+2q(w~ znpgufjou@++)Fi%ix6TqV+3G-Dc$fHCh^19(UFSpv(FD#hY}c65{y28NPSd*X$+mM zA*;DnD<=M4LCJ}3?lO}mIL_T|(ZOKm^B9soVtJaFv}mw@Bi6G~?&L-h?kC9n1yD%n z%4wGP-qr-iVBOk#&3zne(@!FBJJikuXZwp?PsHWA6*kk8?&C8(16sQ(yF!Q2P5g8~ z;)L)f3D{rleDZ}OX0B*#_hk55qO+$&*1)+t5sPCv-9pr=^dLF_N zh*7(O2(81rJEVKmvNHIeLzNj>k7gmViE zPLa^vr_u5xnc>^2#_e>#x-)XoG#z)N+EA-QUo189e5)WNo{f0=gAPyI+CI>p9l0DO z9AGgZ$JKdNZqcs*j9+qr?WmaOXiVSh<1qyBOQ1uQ2ej?6FUT0x4avlzSwK~-9QuIF zvNwtOXhg%Hz`eFiLAED>p#V^p%khR(i;U2EV7EWd(%QG@X8d)5vD351$C zH7F&~)B0wDCRys3ijwTQQda>;~(|z z*++&w+{%U0CKDEF||;C z*Q~m{sMBy1usAd)2$PI%eL#r?{=jcgJV6)+qrRL;F`)|aY= z2O2766PlvzOP#NS@#9r>$07E&zB2Di&CPQnS!j#lUaRxF{uI^>?4J?y*u6=8QUcO{ z0d{^c1FQoY<|FL7n{W#FdI_$oh%OZe!Ckc&W0H))kAG)rK{~0IGXeDqGjTgY1)tQ! zQK%ak$)+u_rtq@_EKLey0i(~c9H57MHJ&4r81~6!6LLtvlH-U=H;5UIj}u|5sHnI( zIyzcMyL;7zfP^$NuaHeR)?J@Y*68Mx&hLfBZ+JfO_He~}u}1euWqRNKo3{>cXv#^W z)3OY)fEyE+{mdJ1TZEz!P^v*y~u3>voE<<_p zBlY8VwM6m6coyJJ*yzS`=Mc&~XOj(gU=4I6%luh%L+M+h1z-gtZQPJXj$pR>MOx}j zq~zDV44%R8D=B}Bt!!JvXj6u6BsbKiZ&G^`)J*EGS95C?H#(Ql&xI*6=H0-a^d`lf zbI1+>S}tto;j7xyoUcJs+2(hBZ}?8k8V>6XZkZzJHIo{#!fFgpE?7v37Y$~hdq6i= zI%aiXpSq+W&i(D;Wk`szW5W%1ak{wtW#`&aGMkCz^~LdL2r(I-Q}6+>C70*(QL6&d z$n&F6jS=YGKohkE(RX#*OFO*>08f4g_*ZDf5^x zfL==iAzb=+?6Qvsg~4bLDX3E1)&(+95bh3EgegUC%%PUn*9#cA z5DWWRng)jhpHhuBFwmk>qOZ@|oejQi$F%%hCRMp}A?tnOM5U{R zA(!)UOZvsBmcqV0*$f}o&F3^n&Z0BVC9qSSbYtQw}&Gyie!rfWZ!4FvB8YRi|-5^UXyOd&UUrjLZ7qOGAJ+JG#3? z+EAQ%XFbA#&IEh8eFYdxI`Mp`z@%m^dU)D9%%CsHAyG$aMaF!{!LKCfTE?KuYewq) z8O=6hEKKStYg-YQ$kYG`&(=csIO(Cf7s#(4Z#+khSGyGf2AaCw{1p~J+WaL6$7i?s z%H%NHC&W2KsEsnjWQz)g13y?4-RJ^bK`jTlkHM2$4@^iYOrm#ncN;dP&{)sD3-^ny zoh?#oyR0K(2p%L?&zxk72xf5Gn7m>apfx-lD@b>WH|`KIoT`42JN`_kHy`R}!L}N< zUo+0T(+TOZFCH`Pg3L;g^9dR{fnL)ie3kHz)NUBppOV|aDGvE~J#qCs&ma)DNv8Dh z!6_yS)-Pee;ajN` z;b(^kAq1n&K=7BM(oz&3luoE$iSQxG2$=T;1K17YylVyc28FMk+z6|k?F1{B2(68> z)97R<<3u}d_$tBH#nAAr9?{rUknH6y;`6y83Mq7j_L!zc0-2#(PX&W?4+%{8_A-aY zMyD#>x;H8ndiwd_CLTs<*l_0zm1FM<6A-{SbT5*aR$LbK^z;rZ92=moFg8EpzCAN- z^g78K_2VId|uH1K8QF>91?}x$_bjyM(s1nqxaJl1rJ2%qqIwCyR)JE0} z9uX$=d=rUGUInS`ei{2JO0I?K0y)~qVN%&}i})bAFZ%F5lo!#qwVuV^tn@;$3i#@f z1nv_0iNT6ka;=JxXdd}^INor>Z|*6X??VZ=kA9ja5ljKxw1xp+w>SB#L=Yu&JyAmlP zlqsA!d_`1{Dn6^Ql3<+DbNK>Yh+9QpB+)`KF2{HeEA+I6+ig~v;zo1fbGg_mR3t(oeLWv75$c#0?i?&?EQ zzc7rA`3A*86K{zfcj=dwQAg8Q&lkawvA|rmhg{#-LP z>x~Y5-=*7G4sj;Ogxc3Pk7jN}Q81N1Htxte_g{V&24FAc1$!9%b&%dZq~Zs+J|Ag{ZXm}3eijt9Ernk!ufjcKHWT@oA9Mx)qrqXNfB(_(o?hd zy46GR7()hlxbyNIlb{uq#%yUO)P@Vdbo&6PBD~)*$ap>^$zIGi4(t8&l^x^9&=5yV z??V-Q9CI^}C30nGVuienTap(v^WqVKwUEK0is$T03NbaKAb#pwAybp~I;36;rrC0; z#$2Urhz6fRmL9gfyBDL{)pZco4h5+<>6egW=Sk-4Ac;e_z2HG%hdm@tsr`=k+{lsx zMl2xpW`=;iReG$3wKu=uWb9Q4B zW7y%dc52PK{Q(B&w$#{CptOVyZs~@rwal{hXt6ZVNTkBaBi}9Rodr9P=wgd zR#-$I{6DtdGAzold;1j;K@sVY5k^EL1f)AeO1cpcq@+_xf#H@$De3M8rBOPiyStGt z>6)2+@jUPU-TU2pfAEvX0W-`s_qEo!&hxi=+JD^{1gVvon3js!kz6Hw9|6C6rNs_V zl8rUnz$)V9{KRtO*T>hq;i1_?`U~)*6QQqo>kF<>3S2-N@omywyFSn0^(8iq+r(vp z-T!C>(>+L^FkaoPpRv?x6k$dVbMGDeQO*0sC-~zkg~bzUnc>?T0W46YzQKl-#cI-zs}7}i^|Wxig^(QO~biddnBjbcNvkNkNEq99DNE9(D(aiI*O@^Gq0g*L}I@# zLPAOZu4+#KR-VDBBbV)kVU6Pz|3~bWRUseif z%rjeF1U*fi%wRLSR$~@HsF&T`s#CYE9_KPSgbg-DcWkbDl^iz@o}j{f zo|n%f>UeVnA3uKVVexOdZEbY3u7*h40mR|#4_ld?_s`v~gR*Hv*?fi`tW-0@df*2q z{Qh|B`mRuo#Yhg@uiXY#0iGN=vxBr6e${7r&|*8%=QdW^h|d@01m{J8F&&C2QHqLX zzU0f5REjHX44MrHzOiZY3I{LlcL4R}oj2&Fy<&n$T=Ta`%t$#FLShLJ2)(lu(aH1N zpo($MP~5N=_!1S|r@DM!=Q>TOjLhaUD#=dVK?Z)rj+Z6vQP{IBlCWm36@tX5%Y zIuq4u;f*+1=%2!3??T^aGS4%P9`EDl+CFc562E5hnwTF_B?;2Yz32Cx(@gP1@5dA9;%-}|=9n84DT`#xcnk3mx zmUwx1c;taL=nj>Z0{M-@uA42uGuU^%CpJtlZ5H$Q9IhKE3IKS^>Ga z>sq!0hV6!HhaflfK^d67F!$$b%~+!|=rR6R4*gBcYMzWr)5L{*jLpgRL}Lnx@M#1Y z(x=^y?iX~Rsg7n{1FAgc7z(7bb^2R5Bio(QSRPugsm3hU7E+TFlm|?q7 zU;v@Y6wBa5YIL;2kcEttuEpTB&ARIOIqyu0g-~*H zgi3)bRn3rX2V(yg5=p}@6!oo8_l>dblFIqMw%PrdtSZ{x5*kvEK=o)dwr{}nd+J!g+cbwj;O>H2aTsjV|R?Ns&+p) zg>AccR9?;PvvNvOI1+QBV_}woFbB>f6Ju$BpF!5K1R?^r;CM1SRF_wr(BxGw78J(QnHn}&`u(^m6Uebhi1ka=iP+qRH4riY;!aAKW-aOa)D={=jA57+wn#? zM_Nec1CR(!!^XdLF!f}`p&eD#ZSr{Fv8QJmG#HGR@cgdc*K-)6Y16%oUGE;P^IhSG zb#Qm^09s(uC|LCgc=1pdKY&qy&xg0lA-$t4u$%@%9G&KTN+(TY zFZ=Yk{CTWXVTI~Nn*N%sGZOADR3Y`ppVqi<^V9}T-TnOY2|KkprNVU?6|&|pLsy_v zu_d3?azQ0mA$bs_j8=n-wj-Et{vUF|JBgR&KTLy;N`fz zM|}UpU}P#fpc;?<;_1X~WY_Lr2e+4v5N`;&igJuGHWp&w){tU8;QF{M(bC;{?USLY z0K&u#2~1YXhCl0R&~WHF+;*XX%Iud`)cySt)X{(_N%$8+BwzIOf=q3LqH(KVTbIGA z(Ux=DH`05w4M6r$8)Dak?Y{<6mMoQVrJc`0Pfw8TTuruA1p9rx>Q5#n8yOJ7Fv*yP zv(5^yb$Vtd(R3h1VSA!D7kD5TMWtHOF~1pp(QmlEG#Y0sC}ra7j-r)*C=o;yHROYK zR*s<&_s?@FGR#`2`Y8UB$X2#GBPzZ*lI0x_Ijb4AvBvoon@DJfC>;X>0|eUZ2vkU! zD@WPdCi%X>_CpFWON#|SaK9TTlGd5vED+jFbNY2V@lx@n`5+-*$M{tThWeLpMr zBm8Z)j>*yYP!$tasTl1Irl_!m%?|>nN<6SJj!M{B&T?;P*pU%!Q zdWTph7von8xyu&~P={{^tsgr-f0-~^ixJHlp+<+hOlZTr#bv+vs>MC)y0y6FWjW0u zaoAd$*=Rwh>HMPf(E^0d2FM&gw%0_lmmn!L0-`HFsG-|W^mIQ`ohQxd=dzdj{eM!F zv%s}SEFicM=d?8{^Qf3286^slz7Zy_4yhHNme(F?afP_VUbW1vZW*Y_4 z*~R5;P`sgIykPMiao7uQ5GU4UG4S`$wuxF9(lL1=oq8F9!vPo!CWVo2|2B@5rL}z-2~QqxZ*R|hnc0!(PvS>(1V0kTe)#yw9_aTP z$e-Pg+>~j;yKW*BY9{q)j<4**;THe05->=86HaSW4b7&3^(0@2u*aED_7KJVWTwAB zIVPSpU#IOog5{D!@%tEx=ofNI$M~+P&thLh3!X4Vf1Q<4;!Q~$6s`IcrjGuyAJ2Y* z|IJiUhPVn+-r7Z9ViP}?e2l^c$i)(wRjVS(^dTwIki;(!iSm%8q zX8+222iB(QE8ats=3mQ$ub=Ht^mUleAI_=QnA=kFn2>gw3pkRCWnYc@l|s9 z%QUv(*R5S+&_b-PtFI0@$(wv(J@c-pu>3b()+#obw0EQ!J-LR$y_dd3&SIo3E*g}L z;S(O>;o*h+b_j-R1+zXC>1H8%=@t7}T=022c*%EJ;3N>*Z{>4?)jyV(|m`poqzyfaV#hzDyzuHu>YDo~JB^Aj6$t`cE=- zU6C1iI%8)^*1j>6sgXX`re`3<$M_TH|?X26P%J~B}+?hbkC!d4}9)N75 zn`wGZVfTB^c+CGz+R&$FyZbrTg9_IEXz~_oi@&{73=eZXzcq6}pNV$QA$AX4%(|_u ztyu3CSh^!L#!TYW<0I#!%FP!(PU&XKdPP*rSs}3K37Pd>M!+K(SzrX zX)D|C^G9d+EiIi@5cIu02s`9C;*I2?SqM`fupNv|;5S?!%AGXb4|=`)EOucHiK*s! zrlR;q9J>3LM}tcth>g1lvm+=USY_aUq8N`f&+_8>k>okKdNcSV#f87rmVO@%pK->3 zbAn#{v(YL{(@uO!Fj?*7)Sbee%=E3c7KWJ2U}Dj`3J?f|tkeo+@O2v8taMd~9Buy% z<3;i7>un7|PZd%tb$8Qm8M^F=c)$HqbBEcJxaCR>PTKW`arP-%;#jw_1V!WLrFk61LT<)SRl*qRM_3T6h~_WmEe z=~LJY>{)9ddu$((i&bLM3HyhlT0OBZ-;R{HK5HfT@!-*qxZusI1B38bP?^*-!bJ>M%pPWYzwI-8upF9vfT<|!;n=*Q)M>_g| zf~xI*w1R&;WQ~ec_+ODR6#MQg&97g}bwY4kXjbe|3shV_Sxm=OkJ%yGd-fees9T2% z+*x!Jn7neM`o0V_4;&Y!zErGM}}M?bHAwLgaNbCgmQua)2U(L1gzFB5$q32`zLyul!=!Y6sCMu&UsH6D(U}lf{3^B^xXUTC~k9znBZTIdpyQ}Obs%M>b zlLyNNTak*i{4V;c&R+PzKi6 z4{bFC{8qtA$!{boYL$=y8|_0WdaH>d1yEp>URDsDo)5L>hNc~Xui_RJ!-h8s%z%Qb zGION}ZsZrOo%{P2OH{>SBA0>SGQzBU%6=oXQ92Mly^jQ8!%p^S16TV z{Jvfm+`%E`(3dL>c_gNgE-w(x|4H|9wd6iv7vI}1Zp;Do*1NxcD2&6lAJ1)hPYB?5 zxge43Kokab8mHiE^sRkDD5b3`!C4^Q8b~cddx!@;3zlz8 z7tf_lPJ9u~cu9u0qZ&{rguWVTtHf>nEfe`X>P8r#^>vAw9}I80B}CP})x~*!k4tL$ zULNn?k z=;1$5w6#YWGAsJSb>&YKAOrSfni_>7HM3h*n*z&rG(CmUeJkBPRm9$n>lDi|s|N_7 z`@9_f4n)uHB>6SjyXP~Y_wd$Lwxris@h`FC>obJOaqj&^H9I_Q)VjoGWzBMMN-t}B zdWD@rjXrsK-XO+iUAiX$RP>wZglc1}3=6Mmk>RoXT5r51u<%1nH~yRhCW2rSAt7n> ztqbe3WBD)4(Fm=YfNZ4Uviqkqyvel%U1(%iyjVcZ2xV>H2W9oSFH5aS^0%12rU&e(}g>oGHT#u=dp#y3>2A9xI3(oNSPk#eY zLvAqih?B_EkuBlz#2cp%-_Cz)xfYlYW%%Z{v3anw)PWKuO2EnTCRT=%X%C9_Tp;5U z2Ai&iE?D`+d8htP=b zNP4AftROO<)Hvyblj)SU=My%c>J_0(!E3Jj*|k<%NW_;&k_-CCdo>2PYd^>d+Eq3D{V)V_IkwA7}R!s!o5mGJOm`ee#k06$P;e$rG3xmTFX%h3AKtF6g>b%E(6?j7 z{JPbQ^6OM`ss2eE<$eB@y_yX4krXd8?JKZV_4hXGiBUkN89kdJ zAVZ*++k)}}ZuvWG4nx_rFJHcFP3PR-T^4X!yUXS>$7$9d9RJ7@%yLDn@6Mf69ev$g zoa;@KQ^&ywc-qv+Pg~V=?bG!R#_#!EU{+Oo;UC`Ta_AtUum|~t;%M~%ytur#h2PZE zC8$4rEPan&TwVVpuTPPug_O!n+tO2;U4mrn+IO4 z>cuj`pA2Elb||6IEE8kWj;OwL1f<&ai7wy#YX;M$bSgrk_SepGkwA)bxCUAqRV)y* zWv8DvpeSM#;(789Xvc8(kkya1 zqi(kEAhauUxA{s28ia2~& zUU%^*q1p9s56w!W6O}<+C^Dw=G13R5bw_&A*q#z!VNacVxu0FOZ6LQKS;j>d-D3(^ zJ%3*`WbSPr7#I9XxBDV|iG6--)asAMqo!$)P5Of82s`9@#`&0DDpf4NXP+tqN_!+G z^6{Gar|vI)&3`8+Z%ZSmbCf}?9D$rB^3pc%#_5nf#Kkhd zpdUHo<|Hg?&ElXD8@S0UsT$GwRA4Hw@xO7RKxUwka>>=up7Od!ov+9pX4-c)TP z9b0v{$ztyduOKYMpHhou!T_=+N83<B z-0xTt7IN5d{y+sD_lZ1zd$fDjDN~hJZzHpqB931-so;PT=geJUX@nB0uAxP!-v|Zv)NEr-;J~=tjF+Vn~f~bwQ;Z5hr z;}NCIODunp+n0p?6(4uwbGvbonDTJmKa!vn&RnQ&&EF$a@fj3md;)q} z8#aYXSwJSsm+0~?{Z<%0Nu)KOX?)ujbp=pBJ~HmwhSz zY;t7f-tF5Osm3y!I}E!A=$SdK3&cq{OkBDzSwmWYbaF`?*vV3!b=LZIo0aVTf?4rk-aWF< zA<~;lQZR>k-(GL5eVLyP2fqG4t^$g#5kc^X1NdjY6uFKfNJKwJ{n#q3`}kwDP!zk< zuyh_CKbY|-J{r(E$1HTdF}iU90#D^3&wM3^d~^HlXXl2y^Ywz@Ut-BHujXApuMdNy z3-3p`T1F=h4(u!Q9*NBSU0KN#Y4k&Y3ejR9MYzIxisNztjgr3cH3Rp4WaPE^^qdo= zzWZ}%RY*WHZtP3+96psmmVlB_S%LavAy?%-=cYg&DL(t;r8{Il0RE0`+RNo1zyW1x z26Zg+0%#Y{lHtRmj;k|vElFPl$X4RG+3T(7d{e-0*_CvCwr;nVG_iOXY$g*i!o`9Q z8;4Dmq(nTEjL^d2Bfw}_lh&%OO3Ia#>V$?w&;JUY;g6%l-=eGgkeJlXLAj9f6NeAz zwDK*uYXY9+#o^yf@HN+KuXpaZrlkt6Ns;PxKu1x(+63uCT9piMWLYpkJE{ zZTbrL&g{b;Ps(o{?=x{141T4h-#87K+2YXpZN#GSLp5Br1W&Ud%e1#wZrr9Vwse-r6DX&sbsgy!=5Q<2D5AhSrdMjB~vk+q! zUYg#2Lg>y^0aif;y)~+ga&ow`t3AU%J^FE;L;JCz(edn!Vil)6=o9LA_3s*y)$D{) zoU?B}J6l%iqE;-cUY^^@mam^=dnlD0nBUR+hy{^~uMeaufx%;TjzS_)FxmB@QJc~% zpF&Sa9x!$Io|OT@l>B+=pYA2CH#R_Ga9r&qth5K!lgDcyh-Cn0Kq1^w%}LN)sJ2#- zrH|cVt=F;{icd>>vaF)BHJVSngs77YBFo3x6**tT)VTAOn#Kz|Xwh+F&#ZYkjegRGs0x#)gkO~S&|DTKO&heR#Wxi&3?Ki(=S8W(_R_Qd$b)RYl_+F zhyazu8XJqHGhB;yCdD-BsnWnRO3bs#nMJ!n#cj@YvN0I> zJTyr{qu?zMP4-3RMsM-3M2MSksTvdxPUvgt0bPW625ig?&%)kat;=X`XNoH<^+|&W zse4P_dnV6aO47~2{x-+MFcZi1<(Ry-w&7ek@kZ8U+4az$Ijyx&mH|)R@aJ3=i}7nA zzX!OncwCo?Cz)1)jAo&|Op1S9WSQ#UjeAPF^A5bl&B!M%V}EYo!kg}JhJ-eHPS!W( zy4>@}N9!y8b>iKBc%Lpr?&m*$s_FyGC)clXPCjM!<_mA`{>ZoZ)I;QGqiTGa@t#KZ z@1|6Oe_Rp+b?(R}4zAyUIVmZF@)M?CITqc@^CgCuh9AaiiCKvWb}snP zeBtkWThqq*go8iJ|MOmq)N4eZ!j@#Ja^f}3Hk{c?Yqv)!uh~9%soa&{FI3NusG(*X zgHPz*z^2yUA2j_1hel;WP{2;u?KllkVDsK$56TFFtO>X6Y#e0IlHuS40zWMb_oX-w z_Z&Y->#`lQbGfDDX#adPS0CViigGU^?i%8xdg|~ymYr(}3thrG6n$CWoXNKWq z>Z*b3{LJBaFAAzxH=7oj_BhVjf^Tqz?0ZR24#AKX5x_ks$E7L*%dxJn!{Mj+y6Cf3Kws-$c$f42xM51f_ zAJybw(SNK_)|e;F`peQ8<>lU-y9D|g@fYv&MGJXurY{TIuNnKGu>0SJj#j4IiWOQ# z&K8+ZVB<<{h~Lw-6&@=bI^_=ByZrU^mLE$&e9of1#m=JD>!iZy=+pgs57R;JF^>yc zU`VcGpUQisi4C(#0|q0xIU?0}f$;4Y<6 z*NP~6wA>7&T5!3 za9lA0u0YgFeG%^EK=U_Y_MpRgEOpQG`@hyc|JUkqITl-8yqdSUS>fG~KVv-uw##wG z@=RpM1C!F3?a>ELK^j;hxfvD-m%T7krWYsF1_mhfC=NjQO!lJKUbm22wJ__xtU%zVkpfEyRX}93nQuq0&z`kQf zkc&$_V8M)V&-v5(^Zdu>cCA4nk-rDcCKcyuXwRy|HSU(KzGY2`_OLXV8}Q-j^@(W1#H zCXH{Cd1~GhxCaefIR}QvMqmIoT52qP;^+p9V0KZ^%(>SCPyx1mk+eEc6cK=Kh8Ig1 zctp#|BH|)9nBWJ^vcV^Lw*(h%Dtt5QYR0~{**_8L6GF_)I}eURlisDO+ej=N`BWIn zygl;w&j85o+F`&Rn&-8h9sH?V%|Ut4wh@ z@GpJ_l?KU&5;F$su>B+BRoMPWb7#BxU8(e&?57rUfOH|wNONfm#MdvWwxk>&WQp+E zw^OJd)GvEBW@5ne!gbV*Rn*>I;RlDJXi7-X86Se`^!IXowXO;^qu}w$gg*HfcI5aw zyLh?c4R)_v)aFZ-_o=~isjFW8jS;WPxxiy8fOCt%Jr)USdn^hjEJ+WDn^I)IumGerV>nt{flrS4od2}0hB$*Q5Y{*rXlP4@XRO@qTJ ztjOuzw%BX>!M?UQmCGW+B~=9RpvJ_`AnLP1PeJ zFrJ236D9AHhJ4Y@m3jpXbU11aA>J8vKz6PG^3%yf?y!^stWUrRBBfOcOYK`GRB73$ zpuxxq2AehjrP)D?D;Kz3x40fyGPeW_6qgvj0v5ZsOYEB}^pCAu{Z9yE?kD6lFMj=f z{87@txlQY|M2thA$hCdF3L(V5zaP>HP3ni@CEC_0MYIZzf8?`*TLK+B*%uJM zF2xpI)iNOSBU3H|lnGsrT~zPHikqf>QrqwzNSZnMzx(f-P0E3kD_k+QrkoxC*Vf2~ z&C{hf0O3@kri=6SXI5YE*2@~+90+~R(|l;6)cZ@$+KDci4kt@K{u2auVKDhHO=K^b zJB8EfWXThi!9aF_Wq+x{OSI+|wVNxrE{pap0Aqq~oXm)!R8ererW6|)T0 z&;F(wY;X+d#;m^mZ8E(B*o@LZnvm_l+UPcV8(KVpA&~qqHTi1r8y2j6qWWKroMx|1 znJnv_q@U2i5dHp$KeX#W%$!`S?Pzf)pfL$tuYyAS`L9SYq&>_83J#V(Qr-sdQ$cN} zBEwn>-S3Y@#{SOG-+R>A!9lQxbzSAPbRHe5zDia<|!TiJ!LoD2d`!YIQT*yj}g3!DT(tQC4t_`vkmD zVbj6saOtLjl8;oC-#E7Y#gd=1&RN%G;o{)DiTX!KL80{k_d{E@e*cvJE07qu<}(X| zY7>qEhyFQDwoOc&ap=#~8$-baN>6fw=%r?JFKVdY0}*cKV@E{!e^Axiganz>Za?Vg zZvg3FeG12IJW4LU4%&&z>S<%`h}B7guD1K03m|sKEA;xD4o0SM&iB4!)4!B3YH{D- z;_50_hPAMw1GlUB>1uC0G4eO_I4h67w8{D9Ov?qUQK=g$i5{Bu^fcfmrlut<{?9^% zoICbvzbr7GIsEr`65Q@eeg*Wbj)OOx!P`eNKVQH`vv#u-eS;qfDrF&%&GUzvWFu;g}wdAD1jx9|G z>p$RSPh0v$2JeL4fCY}q!L$k&Heyv}Z|6}K(n_?WHDMDI2H2T1b>I$JPy15bLs$p> zAxf=X+uCB9u=j{cI<8ZHhvPImy7@m^!DIs18TZQ;qf7t4D}<6lZ^1}k06YD`=Fbz& zA0C1{5tdr)qFvo$_rT=c7)L+#e9cjiQ*-GZ9G(ibTq|4Tm-YkD#$`!5FyZm>Jx8;J zCXS9B8xOxQeBG3A=UMRwS#SFsNGa!?q#t6i97UDx?0wbDS) zK*U;pVg>tU$3;R?ZyWD~;LO$JtKyc4%cfs8Gk448c^|{3arx~9<$^shVPg7dmwLaCIG`D)!tPh3liwhrG_c8FeiLvaN)ZR zpsrni+K=%)5KSd4b~*R!76bSYlN3c=_}`Zi_&T{GZKwUtZ(Lnnt=D_i)m3gt@#4fK zJ}p5iHrOFgB-!8?yu7~&L?KbpBSsPhQ`)B83^Nl9X2tzgfyxEBCd~;(*m%d(xod2f zym4usackG}%(0tHP`{uopw-^QP;=yB*?Ra;O#0QI{J@NX9QxyG1W?$4{i$KPzQC~e zzt_cl$%_K>SG6Uef_%Fl)Ms4usV!$=c*$D+%+U#QlBD&pEUKG~d;iBKB!9kKVeEz& zsFq}^dVKzvCphGAd#IJz4e*(LiOS!500`XAx8hx0k8t8F)dewe^Nl&4eCIIfmIg|n z++A)NPmM+Ap(@SaT1oSOiq{zXjxVG;gqY=QVJ1rvoR^3j~-^<`B{vG}7>>0G-kTgj}{3yCUg>Q zfFnaRvvvgFAry7@JYY=!Yox*B0%~2S^#CSlq@}er?BGPBM;>dqylAPn<}s{!hNv*o ziA@us-g;0!Y(2ra9f1-vg0*{Z?)+`AYG(Ro37*JOl`JrwLmojd@Pm*w;7F32zGc22 zDPaJVw7YyhNf>*(!kRfoKEv73hxD1+ZF}<(jBV|&56JlFiU9AFgyU{8l4r}sEfSp}w zJr&`BK-{33N7KYG@8jZTg6JC2@h^b?6%rCMKm72cQPTH6VUZA~m{OH`n;D+j;+v?5 z$~rl-)+@Cfc@=163`9%hfT1xBQ1hSX>8f;FZx|ZaU>;Tfkv#Wq)3wEW*Wx!7m-6AE zpHO#*lB=OgnVt4Jd~NU^)u#n4tSe%7z_^4{F^1C;mL#Yk4h|j_%$Epe_h>}kkFgb_ zstwKJg!Ds={BoOm-r8w&m<{M>d(g36DNrTY9Ice$LF)U+`6zZJ8z!|%%o9q!cp z^B}vl!!gHf=)#n%t^p?Wq5A5o{wUy!pC62D@OL%lJfBc5Qj1j4ewiVxmur16zLpSX zS{k*LiB!aCe8M+Ti{WS7h{qOjdfwfBT)&8Ta)Ne1ZAP`;n1r_| zRL2`|6&qc#!eiOv5801K**ughTd{I*H8 zI0`6B4(|M6UnVlw01=BZKe9fZtUVil>xts`b_c_Y)IJt--3u9^ky{r(#POM8DqVw@ zuUdHXw^rosbc^|DpD$%;1aFB;qcWs^isU}Dp&V?4kczdueNeVgb-CKl=fWE;+1mx@ z5YoZV^TH|M@SV}oe&x(4)QL9VXbRiBoC-Of`DUliLscWu$D!I$_R;k=2?Yh8A(#lf zq-uQLm3bGyBLLi_RyT1$`%nGTCli}52%O8xbGHFQWNc3;;yO8dsN`exKLBl62JebGRM-n-mVQQGyN^&rr)6n}!xyX|ZTu1SwJW_`+KKQ%=A zXj;E7y2g=9=BcbMcUMTI+cxR!WRg1R{ODjl94B(D9UBuxgV1q%VWyeDRzO~90jUJ| zhb1d`qA*6Y&$I-b4qAC^a@%{=ZuyOP}EBU=WIx}-6SY)9>W>sYfxRX%7VMwr1H|GC4 zZ}Ok{q=D&oZYwre-1{rAH>v6W*qa2_CXHo8-(WjN>hU_ZK0JWQi=#JlQ-NJry88=H zD2L;QviE=XCNY=U8FK>o7*({BiJDVgU267G~yT&5rU^G6FhTyHc02=3wq>6f1e=JRFe|+ z&<~69e9idj3TOhu?ev%*NGr9&q7?(m-qu>>Ds7V5?#?@s@CI~v1TI>F_01!-diC3! zc8;dwXZ|&div-hW9Ws^^1@o}=>B=8+5H&LKW|T^c4Y_1~`}=L^TN|-@-MioLyiJ_9 zXcT~GpuRj;Ld*EsEg<4dd$q-CdHT~uU08BPApMNg7(3Nf8i)3qiI!?14hzBn1tMfD%c%gY%#Vd7^Z zirBQ*P~>~#4T$*e;6ebDZZ)w#yn|JEZ>et@FKfT?BI4nmmW1R&znqUh1S@>cAMSYD z`cLIHh`)9MKVpibNro2#;e#r?JP`E~0Riw#z6(#Xk3%?YwAG54?4_qPzrFOp{ea{2 z!T2xdi%<53v=(usM{4_GE6Jwvb46OEPyHZY-y7$HI^pzhyW)92z zg(qjbJjtN3RREP3dsP(jE^MZ$Xs?#1-7tD5j9N&l5;p!+NhxgNuqR%SKDtQRA0==> zj(2VOr1~grtz`_CLGD*C(>ype7QkWWcrXih{I?@1aJv4{?f=);1kpC$&3E{iE`Eht z{&b*A`w=%u7u+5rVZ}~N+Pv4)Xvb*IppZSQmAv?lE zcCee}#YVlrn8o^3$1VSk>Aco#UW4Zm z^;=EpN1U#WD#trh91x?=Q3&zl#kDor*2ayD;1wcC+`;-QCB7*0Isf|6zpZCof;N_~ zlSJlbhW8Uu@6a1QJSWkmKc2#D2q5%0*70gSK7!kmwO*rwo!fRUjE$RWXxwgI+P`8p zJ-weQaqwSc_(JKQi^)5*Lvx}Q`CI>SUFCXw!kn<#6{N1R-Oe6e0Btr7 zI50CspyV+Cx-h9T;z~G&i!x}8vPO({nsn@`2qWe3ec<^V_ z?uhVf52UfsM4luq)B75}>Z%&$2JefFf34Ep=B(O9&q!Ga`O~*D2ShUT-Z@K7aodr< zkf-ZOKndaFGoK~R|2$lBCkGxo|GoJJX@6+UQX})nRC$3o#SR1ULrRF-?u}m%nSU(x ztoy6je*KKon#dEf~p_%S<5>I{SPfh1e5yeO>*@^fT>a zj5q5oO7x2pn~qyu+}9K*R2*A^!^2T;z4J#W?3SG#B+&u$mPQu6KZ_%)-T^nV6hU(# zeC~6fyjNlWH~fv57!3pr4D=N9{vYCUP{-P<>UYJpuyVhv(uGXC|iD&u|Om29omDClv1@IuJwQkCo_=ZhJN=p{~G3NIrcfo0$x z7q4OoUi98nIlW$;s|w<_)&Fa?+i!~2#KX@fJJCLZS)5u3Su%$zS`YkUJwOM44^;Qhwh#TBv&8pmHEkcb*YE2* z^64v+i{xQ3+;Wu1ay{H&%pUm8nxnRwyw@nEf=XU=H@hS3ei(JSKMSv1)QWzp(X}w3 zuRs+L5tR=a{cf`td46#bkUg4Vikt0VKk$5<8`D#r!;CQ-6E`FI>Pl#?`Xn_Z@2QS`CT+;^b&ydhGl!XMaqxsm;jf19jshpa zJe_O~JKqk{?~KL*+#x#X!#j^wQ)vsAWi$Gd`4A~k=wr0>MX5}DOHhsyCCK{^%PC}GhEi2W~NZz5nE zyG+Y$u-A?NVqG_LgTc`Ck-ZhOoc2dD68ZgG~*HxHp~^NBS*5|TduYL|6t08jhYeXwC~ZIKy}+w(nzlB*D2 z*-1f0>GPpGg3(PqGBoV^qc0D#COpPQH^6v@;O&Y4g-Z!Sx*&m%eda;%%KO1 z|ClNIGzpoj#K6Y9$vkJg2l)8E3^XNtE}b)8HYUhu8SG3Q#dZf$aYKgGefo8wBKuSC z|L$&X7Cq2(BIvjql*n>rf?MywtzgJ_HBl6XP6>0#s)ayQZ6-S!4E?LtKW67n35>V!W$>6`po6Efy77@f7nZD0-G79IMBSQyB;gl>+BCN z8KVb7-krIA5KeMChEgS9zfofU@sD*33||CIs7Evoyr&t}r+Q|TIW6aaCl4fHMGk&8 zHnx)n#Fg7a7>~6cXleEj-be@LSY%l|O^D&Uo05{U1boh}<6u#nIIuNP_bFxIin^cv z;MO+}huC%qW1j#a{rREJXvNzJ`v>msjfyTilPW0?6ormWd(p-b&LW~?6K^D#tBGPM z-2402>8sU3Ea<-ZVHkxwJimW@ zct5=BU5m98&bjZI*|TTQo;`bC*MQ@2lTA^<-eEojt*kBsmazv|LZKQpen5p3&< zo)1CC@ zIhjT2yn68Zgq>XWIVnxJMY0zrv#o!o&hF51Ia=8DP|P5@s+0_w=X{Y(m_-}n>a)I?rgADvB+HdPY`n= zr)|k+ZZN16;+Ne5V|OiYcR$cGL%9;4Gbyq#3{mRO@%pIYMOEnwgEVO5ab+M+i6m(v9p(LIHaS)Q&-^AS1wAtuO@j2C-{saPQ%;@H{?$}Cv~F!WG|hlS`?my;`L&as6eC0P}`de60uL2 zlwXc6$(WPb{ay+2OQ(i=Ph~x26KcGCGEwpP+Otb9ogq4_!xGzE6p__iuep-YvBRF} zpHhm}RR#khh)CRX!T`0WH!{7pkAnlA8=ZSJJbW+gRm;0d5^>Zq>Owe}NmbQre_Q!S zlrg7t&;WEYi}j%I2gR~1N0QEQDirs^YT5ifDn4<64r}B}Xq@hb^KFe###qE{NIk64 z)ESg>ehg89`MRG)N4^ySHoC2@)N;Jqx{oL90ml=;-W|Vo)~H=dQYl#?K&HNRBa>b( zN*zET8umP3u-h#;K4H@V0N^t{@3r>8ol=YdihIM$FCF5~If6f1hd`p&b%xW9e!xyn zdrbmZ20KcqvM$tnhFiB3pzXCVQRg|> zMyv%|IAHa^ioHsq{!q4?DJzYq3`L!??uxsn-252&G=gio!%>h>MvB7@wogwTtEapwdWm^ z>~(mQpMTCc9+1Bf>U`&C$0D_8)%67_k($NXz+DU!-)a{d3a-vHiJNVg zcj_hlFh9o|at1ZokGq)}7(Uz9R@=TY(~K*r9C)n78U`-V0wlKlS=7DAuJ29^$Mj1dxy__ETqaFHDI*pVhDv*r}i6L zRWaDaEx<3Ye6xwj`>=4LtDk-}kuAL8$9N0>@r=d1JN4%BM;voVE1?zKV%yo?x^7|J z!V_RgxD3TG4ruj{Sy6xa#jy&99icr>=i1nU21y271d9gflu{$BEAraVnmc=Kx21Q!Cj3o?K{F}HJ4|#=rh3^ z+0q9OB6KJX@?U=uw;J-j_JTrw{z*jEi(lWzKg0kAXu!a#TYZpJw=m~8wwy7g3q2d5 z!|c?nB(6%dse)z4=OOOp?hBW0WTE$P&OHHj@I1+Ki54MZ^v|IrU>bYQXyCDc}uN0Ov(s`DEMAl(IBdW2l&;BT+ zZ^v=`*9=5IDjf0j^Wnug^p|^Hhkn50puW)|pEey-cYg=5{){xPz?)Q$r2xI?dr;R| z(B~8hu5CNW=+MdGR{-B)z;|*Ff-2-)j<(DS3~S0yf?fj1Bsg6)hK}`iXfe2U)&zyK zze<`eG=DBlRoB*PUYs2h7sZwMzctL2G^|P|&V;1ru#jm@DN*Q?d+XM|oS#vs^%^iq zp?9Nc)pKun41Y4QYF}DhO?5EPLEZ=ed)m)b8!YVo^s~KQv%-dtmfc^4-KXKq+F7Gg zU+;iRt~O%w4!&k)XV~Vs{_LZQn*_o+r1@2AMPJwfVk3uw5W*j>W zNJ0XM!z4;~DAd%7J_`+-v>#w$=Q+-Q8LF~3NdlaAJoRmDZJ1e00MQ~FWcjIsOK+=h zZ@2?^#9X(00L(&m4i)D^s3PQD74SXP<(kfRu)R(rNIl@_%s__x94Ds!H0za zT3jAP_Op6Zg$u#e#vS8+fL@oiN4}!~QX|YNYQdGtP>u?PGu5V}lPKoqlS6=88~D@6 zmVDS3^7Vc3drEMh1u$HkC`8&l>57vPMcUfl*3cTwK>ur;EY=0AMEY9zJ@NL+fdYT4&P_EpeitLLvn{EKI+r_Mm*- zLjs$*B%^Bqnu~vdZb|X9ne6e|+1Zd4-yApnzIrp4qSxVXBlnZN{IKNPDhrA5(z38T zQo>@7BM{WT_a*C0&RatLP2{i5`B0|ZK#3{X!iQl@*b1sgYJ-RRr3lSHw}oEAx87H@ z6KEU$FL{Oxo@|jo_7zL@Ik|nmjSVm9MA$nC*gb@4d=4b);5_y()frg)z((Awg7m#U zoX3v?FbL4sp;Om)KlcOZ-65|fKwWDd2zUe#&Vj_<{)MjiUmz~WD%O~b)t4m~o2t+z z+zmi;{Rbu*JX|c<+vJ0rk@!E~U1-C{&I3$*Fbh`**eli&kn}qUJUGm2WwiEEwcz#* zg!bV4mq|Ap0rWh$i6b_vxakOxulFCO!8w;VO#s^G;Qrd1`unV+a}O1(&zeeqcYYhQ zSm@pI5BXxN>|)*juEi_MVH?Z$ks3{nwu5cf@v=h6>3Zid#~sPmcFvEvFJ(O58TCVA z1*yi@Iltz8ag3EMBn$993%L9nEC{N9S z>YMgBp){}vj9D+sh#hlA2N)TrA-QlRoxrq`G2r(TifX(s!cKmzO@NNhkBib1M2KuE z#t06#ezpY!_R#=q3DGGJe95!|>8(r=ifumP&Z{J5&JGb=CUq|(FV1$=z9!!T*E2j3 zs$|US68UWflYqSULDmGB+nNbppf&bGypo%3=*E6xd0Va55xw#i%@zh%%rHYhS&6dJmhSfv{hF# zZ4v-^9C4Vw8a>_+`F88&cFK=s$ZdP-u~thw<~If%)UyEqm06+m!r7jDI0OlmlXsj5n?xr6V0$lL!g z?NTPXz06jRk`d+t;0E7`nt#15n{|_rdsslS(JAKbc4HUI2d+ zBu|ASk+$C)bdZf~Mw&TT{(Wou^0Z5kR<77?JMd?4ewXVEL>6SaQ&8z_gK7uI7{M%_ z#qOQO{XI*qYdyzXfQz|p^oZOo$wNELkv5CfIEyoV?VudtjgJW;yJ_1k<1(}pBZT9-AcZs|6BOHlntc3Qw2prz zeKHvG7IytrwU*q+j&-!LaClPL(L^5`z>Rco3sr6lrn`mqWQ%`smAfmCpPALrqsK^D zj?-{+If4B+j9)Ld+^TK3HZxM^DIMk`B)f0;x3KT3wZA^Tsn?B2pZg<(t9cC(gd0;A$~@p z-y*Q=oWFC6km}VhTht(x?9zg^aY07H2A*C7ozZX14%^lSL`jnKhNkV{1wC@BJd z|EwfIhxi-2@~l6;*gN3evZ1;G?2s3LkGvJ48lP^0(!uS({psmx_Q&w!QrIQ*g;2St+V0SEZ?GJPU>o~xM5U42x}t z7&e{5!w)v#wQe9mg_eATRf7LYtg|m*pYvYU-cH2A1}^&(7{XinEQu8K6u7o4zM1@~-7$optC3V2lw~28!qQx)XKwG{0fOyv$ zNHRnb&asY1Db%09VSqTh)VF~gUmHbuer=wHf3a~x!5|#h@Z@k)4Gud5=o1FOJMwTm zRldKXB3}VcYF3J%Q-Q@eV!i`>cHs$}#}kG&>~QszXYkdFmw8B5mX<&)N#h6uiATQ< zppD5_v{KjjPH;Ft4vzZwVr)|O5UE<1&9n&$dnz%{JUjr$S2A}~HXo2pP`vNpDv!vY z=gNG5l7*X-G*6H)3|1ik1_{+CL36UMK*Ps-zN(X3~PgB|{J*MN$OD*OAd6_9)w z;F1lrtYEqw?u=*(4`uRrGBB7 zCZgZ|I`Z%4feP#zF2f`EVe;xvV-d~GzNo_}rh1E$H00cz0C7|Mt*N@je_^gT0{go) z5Qnt1%~t{0BEU{1dqrM>>sa^Vmn2tpEWQqD-y7~Mp};<85a!fss)b(I1W;^R zS^VT-x4VW)70ZoF`3uA%!=&KG0d5=2;kLnXUTOHRw}pXBK^(Jw^;`2#%n*%@EfwbL z1So$^Ux3ddraa!&Zv=P2SS*(4GXDJ#3@J)MLXiXh@={i0h`XRD%z)==gF?P)lK|x! z3Vf1i1PKVTgaj)xhX9x6)n{)pt&&Iysi^E87iIR|f*Dta^F|4phT(n3lvf51n*+^r z{$RBba3#1m@)CEs)1;kfPyl+bC!nx3LT4@8CD{k(L_)*KU^rPIL=fWZ1H(jqN(`3E z#AinFzg@tSXzuP_n*!`ex87y<>VC!s3^a2OFWczgAs4`t@LugUutuu4aao4{j@?uh zC7N2?xZ!cnO>w7H5+emBfVOo_F`0iuaAe`?L0VHrlsFYb@MKhYR0kc`Sf;x1yl znMH!7)z;LgQ1CnoiO`u;u-KNu0~jz(X-J(Ie?Gm<4yo?M-n+NbTi(~`@Tro;z;oyXZXljfQD8WV^xBOQ+*q>KjV5A)G z?iTXE^Y0*WP;D&k)-h3wy9e$p5fK}Z#B(mNEC`<<_&b_=!FKxW3GN(od+c+tJJEpm zXb}qZ(T|{wg0wwhuz(1;ipSNsN8I_6+| zqg3<4cfGl~%@SJK z!}S$Ec3SaC5x_ZtK~EHzfPEOe5q9nB9iVGa^99BbZ6=-JhaUE8E|H=Q`ljmC^9}#V zQvnneIL8r&q5Shia|=du^`{a#CF?(fGlSc}QWYjNgwy?}JNuaOQCEZ65&|7HS*De{ z`Zb0N#XTx2Fx?eEuz>GErn<0fF2qNLz}ii)zF{Q(rzynhCZIsZ1aEGRg*Ve##ANwT zGZa<&?>oU|AVP=>u@e9EEsHp22OIe* z+&Os&WiwL$*Y?2Io0DM-pIhMV&Ehh*|I^-;3_)M@f_)Zk!q2?qXnCUGj<0wSK@C#1qJAlkCP$i zFFJ`rz@Gyi=LbDhup3|)l!GWCpfV-wZ1?sI!y5ECN7HY7r0`*_3v>Q%!K7n~y?#NN zYdLuxwx>_I1Zz39>>kQ=I52ZGaXx1~vd?4S#IoQ8()2OfSwcxdf>(CZSDK*ui}j~c zZ8Pp_s&&DZ=P8R6q;1f7?Y+}`O>s4Vz;iJCJ!dygf-HMk4gN7q_MOXi;|(R--zH+d zvAaq2ho*!~uv@7}kImnF#M`;|R5dA62|k0fvoM@gUi6Y6miELe|G$pGC-!H7ArE=| zC16|>vp@MbWS0L;GicEwL7AOiZ~BS#Hx#LQ#?9qYLrduX&498)+(Z6gSoxHhuh|dZ zLq|b>LcjbH$M!bCA!H9iTGKw*H|JEz=&Z}02`6ENafI68$$=H)Vavl34+h&Sfwalr z<`6mi)y}+SW!qu8twr7V8x?hedV)TCkAAD(*0i(Bbgub$Cr@-c$swWKNg^EwP1 zW_f7&2Lu9_wz)o{L@CNSp*za~63=O43@;wXD|yI^BARjP;KL$=vSmtA=*<@uOp#$G(IJOjq(_k;2QWZWF{b=@Zi^kRTfQ2w&&=4Y zXv*;B3$FjI@7Vhn~*5K?bB;eUi?lQVPlX`UNX4a zKb+q&y>2%xBdCnraoPvS%6#>FKA=DzNu{Q+Q_Rin#$D}cive!sU!2_+O6(3?o!`rz zAcvQ=Nm{T7%!(h-oH-SSURJexd^gil+RPc>h=-bULs}q4Fh=JV4lgQxjM=FT#xc{g%0QHy;=W z++=s9`wYSNq`x1L!k-On4ue;G1^C1h$bYRXQ1xRr8-6wR?<*|ewykVJNs;a9C6dj? z81jOM_aVLnjQXEV0lKmk3{OxP?y|-??w-h-83m zbWpjCMvC(97d9~EF|R%chKPJP_SU1ntt0Gsj0mQD5#n=F_{(Ls!=8g7z|Y@*ZL=vW zawuFU>ETB=LIahrh+|{v9hAiy0X+=GfFVCU%*EL|XGHBBv?=$v=1&nDF4&d_fY3qB zCU-%WdWhVWtcdQL(&u*dW`?SplSe$UYh({qV6$ev|4p3!7~X^yQH&xsvJx=?lr=FJ zAk*|uAK*fT|3c%9LezF2z6HF)WorM=VP1W8EXZfUMj03di<@)*bA!MDp{8amKEkXC ztQbaY#7u$5$_Sc02J{@*62225Mg|`@gfS+J>~v%Jz*vnX{eScVF1U}x4w6|D>o_tQ z|I5b$2uG{t=X=|K=i#5PnT`TK`_tNzV@j)n!oSCtzuf_@S%eMr#MXJa_~Q^FU}dTX zTW}dmdnomfr%FQzTj&rW>}LoiD8)nWlKBrsL^S9D9E!?@s+d=5@X|(>tbf`@$k9O) zz6`9}lqv&#J`%;agKZA+_dgTv1m+o-eqTVOFI`wSsW!d3Th zP|P81VOe1XmcP;&V?$;^^ad{ZTzEmH*1aa}f1-JJ31e#i_}>Iv;-LMWK5gE=e*j&l zv4$~JUX4Kj2YgJKlb`+Wsyi__n5G%!kMub5Q5+!NLeC%mL%?v7=XZ_QVO7>lPX&B_dh60l>fJLICveyTVTr+WSOx7eBNV*c>jk2 zB8Ig78hm^(!K2AZI~xMa{sw>Il@Ggig1R};pL*B;pH%o9_1=^YgKrMb81kI|b`xQL zn{R=I3MY=EMJW0$5QLc2m{Q!TDBGSQx#A3YxpD z165)B#*M0Q{{y}*DB+6+k3uGl0p1&VRoDe6L7TGWC42-D8HQ`@vS_#HaZtotY5rgQ z1GVx4luP)nr8OboRG+_fhT-2F&4uy*>mTU!&+{!*Z)cY{-I$iGR>T77?caM}SA-W4eU z!h~B?&d&=IhU75xl95P9KfX-E{HjKWFu5skCGDNq(X>ZA{u-kbMM(#RW1QhB3Uub6 zNXmNG1UvjT;xm2tsQ#XZ;o1ORzr;j?pZl$Eh?`dtZYUjK@P10NDcnHx6Hi%!RPXuh zm-tLXTgr?y+W|hr@NiNng-HP-`dWI;m#_S!ijPZ)sw>4uBt>xvmwosnD{_}I2J{Bp zRt+!RW<5bPB}pTMtbZ;nL@4-4$al6Uyz4Mrmc;u!Wq6lgE(mm0=aL^?9xRhv;XbcM z?!&zh8eGP9!~sWu)t4VMlErUS&f+o~ZKBm5W=-OFDRd0|2yN}9cK%%3wK@|uW~2F- zc9s{GQkMw^;c^NoZ6HQL%0z?BeuH-8qY(+I;>kOt!PAC!A%N7rbu^d`Vra0A?2vw;dS!**CN6Ba*skb?y2;Fb&V|la|cdvCrP=ZQz+-Xf7ejIXmiITumkeXUJ z^vC2F6FPiFOOTXeUO#9eshB5oud%w-?Yd(m=#RqBp3|IK$h^|*jwMnZ>IYp+eLd|V z37I!~%53_bXvbh*w82os*Z3Q?4DuRTxvy80e58333afBD#)?;(Iu}JRp;)v<1a-x+ z+)nxtCfcT+>d(;&4M#3_%6hLG$I+Y0-s~r2Y7eSyujUin|GBzt*4%S#$&EktR}SuC9- zn>`vY#{P=#%tUjnfr8}ug#a4mxatK3lud^1N^-i)!m51d3t#&oevo75Z<9_Jd19=G z`_I+~{85?LrwG^HcfCUT6Dz=a$dh)~Xrts!|1cJ^zpN$M+l67(IYhMTOI{p_9_;Ce z&A-CW{qRliYCB*^@Zl`OWK6}ykWWBNWCugDPL>7f)Pm+3J@eUigFO8kxi_dTAF^Yx znV8Rhd0Dc{jiOpie0*u}yeBRAm4*uUnMIp$>oQBAv6vLH$7ue*(;1V;>SI5ie#L;I zX#=%jfkpzVEe77%%`#bCkc|u0nHdbtUQZ&U>cV$LNtzaMOqIXtuYfwz4O9ZgibUSQ%XQHGK%a-}#1%2Y+CaY`+N+^fUE*V@VL!X>-~7z?3El==l0 zxMAb+^$Bf)7H(U`F$3x8z46I|hndnaO!ovFP8Hp^=NYi-p!g8E4=OC_Pd|w)#4U0R zPEB=XS;-8#<+zHz&56x%aufZwg0X2FEMBeR-E@cH5*M4Lbvblkd|_kQvNP!&)+*0q zrjpNGpv9$0Y*}g6h#&mH;V014$Q*we$Y9i}7YNobz*HME_^x&|3ha+<_bb>K>daOP>vKVl^jXvekYa7TY z=a!>}+eQjHMR$JFxa99)gIG?Zgi!#t=p&Gp8l<_pc@X3A15ZQ4Pp9wQ*3&0hQSE;& zo8jF6M=s)63$Syfg!yQX?7&7Cs)n1!-{D?3-90WoO5l1(Nqs-nMRrirO-|-}y(V!@ zZN3FrM0P@^azi#spf=b#~ZYtC=|25$i7wiiaI1ab4zZN#r4MrhBf6d6ol3>p}osSIIemzKD~amGJj7A z5ToxX+mG9VMPQM(-Fb||fQ9YW5Lb4~Iw>c|-J{`9tcHv;%ly~iW10y0Le%N%CZnJi zB1w@fvs9Zyi>XmlJBjZC&b~LfO8QPFXm!4BVy4v^AdU+b{fu_6FURO816H=8Z#+}? zI$x1$UyQ-&+=;_5t-dx=>o{QDMD@|+0A$lpv&!iHXPMT+{m?sV1Ap!U1~A6zNBE>-RMq`Gol&jwLQ)+;%|d4hj%}i{p!_b^!}f>YV?742q>F#Y z234ujM~7903ZxKi-C3Ap!4fO?Z;d~0wfwCjM$ch!n~v-gw-FuPAygde5j}F&Opo`N zFxpPDf5Lj<)RNEr*gAuG`?Np|je}b1XH{=g>_m|y>hr?sIO^;pt(CV%_9<05Na87R z!%anK20Ms*SB4lLPTy_D+P~XvE(3x4E)m-L5#xPlm!5=V!}3NMku&AudY7IDF@`)J zH8w6z>KY+CG^Mzw35L=vnvRB)HHihv#ezF=+u~YhzL0HpDRh~$AcAY?w9CLTmCk$b z9GX*H(D`(`rv3r1j&jS|?fA<#LL{}P=yXlNm+vh_PjW|D_WGY=y*U#@yVyhf<$q@K zeSX{y_KW9HcIn4D6YQ#``{ix|mMT_pS7%;D2=+qftK8q4V{;sxOPKZ-l~5zHlB5A!F5fq%zaFaXuvV-e=Y*pHJh^CRtd+|Lf<%zl#^32NcpC& zocN(;c;zm>cp=T`RA3jQhp}JF2b2i@THaq{c}q^&GJ&g_ITj%H+CB~%A$bMNKusxJLXI|`2T{xI# zTF{)Og!n(t(Dxn0vn-C$FK9IN6}o!Xluu(^qLWm3zj&p}@r?9&mzJMxCBYPcVLg<+ zE-QlA>1n~W$%~f{Zn8vNsXK!(IDY7~#j;%|oRw>VD6<2i&gOntlaJ&K896yKj-D}Q z$`-`RI_;`szo~iD8P*0<0Q;@x=Wgf;rs6zCY}>`SiS=;CAVn2X6Yqw9j+R6HS{jq%er5mGlr1dDsE-X(TVK^n@rsQc~7lA?7pY>t=OE{ zv+!h}6ojP{pnwNxirU{dBD%~*?68NH&}x^G8?qeDLQDUOE5zvTGQ?n1`|bAkmpyOO zH!4SQ`Fq$NEQHP8ei|A<{&I~+M#MuN(~E%M?SxoW%UZBze^OdDzPF+~m2=ds{3sgp zrUW6Qh(Nq1N4?bCS1esw+nwa|UMX9;O z3rr6X2ZlF+KE3SFL+Q{z7Q^?EBF)~8iL_Y;W!`S}|M3LJ2{epkmjeag6-eHkEE!1tx_^WuIm;ofTc^kG!k=EKh~ zJcb%{px4*vhx;kLEh#8=yi0f|?5aR2?dqp)qvxL>QDhch(18PZEYZV`k|i158=4yg z@I$$nT(Rt!3e~luvz*Ie!i)nURsaq-b($07E5Jp&L*hhpnMcv zEUVu?zKhG|?90WleC5S)AbI$D+(#U<3jNJCd~)*$LZ^3}^9HPxgOYoF#0g@LixjY* z(HBBYxbLz_QMd_WXlPA2;qkZR@a&bV#2Mc)j1>!Ev(2CGR2fmO$0>W5XCl`JfAyI5 zOR81(eU~=Fg0>^*s?S0dZJ9z_FMp`oROl7$_283bZW;+j$ZWdA?}^=;X0B9vY6N-@VopchlD~oU| z1u42z@b~HrZA&)3+SuukRWbY+cxKJ66-U^jjftBBj&CZF=kE11P_Rj!a^idPX!12o z`k{c=jXy#vbWb}i#12;l*4O(;&eNCDlBNoAX|wUC$71&`@U!GXxAitXA|v*-s_i4T zL9!hNG6a05Ok#&#i&Q5a1DiiltP#4|PH;kMYauDT5CTt=jJVY4o~hON#k~PuKO01A|iyzRB+T-z%7X zSb$yAa4%^O*@f9k<~o)j*Q?e$=Za=emLQF-ApUH8H-#^kt@i5+Z4!PODx9$=ZK{ym zPu+V?)f*`vE}jV=#DPm2l&)7kHwxe1&EnMf15YSPqfgjBHB}?VUBceO^s4RjyWMek ztkQuO9Q$*gg^rLFp{63bC?O zQ4;tI=D!)LB4VBwP~Y@h355qg)-EP<3ksKWS{~b8yP@r1N^pbDR9k|OyT+%PSew_l zJ=EoaCqdTjYGY5E-%{LGL!Vg;N$A6f=3zv%nJ%GN*Q7I1ctSfPUPyhH8vZd8WLKNK z`rF36py0x0xY((8`cm}#WeM8MMf$Ed9l<4xGy3PsOb~Jh5fSuLnuGP|h-h|m5cO@9 zSU}GA3!faV$8^u8E`t(>v5ueWzO!0(xvu?zVbVQ6{NT*zj4z$$7gHk5{6yWrUa!8D z(7q#Y%_1_x0p(nP2O~$bk^(C87TevhSf2Q4Dopf&2Z1n7%-?pp*usjKvG>r|0ytZ7ArWFkx4T^@UM4Rw^l#w>lx`C!h^b%`Q*6YK; z_?cFdRa*}q(f3;*5uM`P|H`OKH(!u1e?w%#{Z(?t1hvI1B%II4Q+&)gSW-{iMHcmG z!#G6Z(CL}?Q<%%8kLmP%?SpO02}c8aXG{JvoyT$Ivz1gn&82SgD~A-z3OQ91jN((y zKMO8{GfUE>oW)y=*wnXLoQop|H)@>R+n${YR9Dvaj3k|t%Ul-oCD_Qrdv25LBklZ* z_<;xLHyzYJ3(K@a%E+V8;Jdc)bhyP`CO@yn(WeFJPq3g>7wh`<42SM}ou?fp$ZKXn zedu?JFg>3M`8lg-K0gY3u#ohbH8~yUUbovpO=Dp|`-oe zv0JoQG=2o75wDG)+EvW+$8DJ*V&7IFvICcm7 zQS0b_Le{ip_hnLxVd|k znlFOnT-YW-5|t2i%6A?pD|N8+4Y=gMk3)onWQW~WQY8e7xc@#qQ*xomFiiAt#>WeP zCd%t>ZgmAT!n+Qw7q7nWu)b8%ovFM#&%b_t8Sl9AqXy&pLRJ_)kx(kK$zxK}xy$t9 z{%AIdlIPi&!K27B!mLH}Yo6CVDVgY!#CAa+k`kmW5Pjf*&0U&I@_Zi-ypoL0jtg!!Yxytx9*nN9IA6FNd5ApR_02~^OpC9q zRP$5byTj5lVC_o4KwxISwB2qG+RXYK*GG+6?V>~7lec1uBeF(|mok7}`Ie|QZNo2( z)-PH?RpwoXgzK-jgnAu~q1(t`?6v$R!JQ!L>Tr@AeN*bhbEQDzuj); zCmKf7i=OEFPs?au>z8;juU;ByrgIN-hkp@4mN z?C!LE>(l*RB9D=~pQxf6*PqU%>Aq4?Z$nIPH4TjhC0Tc#0%AGt5ryop%(Oou7D@?v z6@Ecx+AxqeON?_EcICbp_YFXo@ zY%GQXnwHIv`t&hNdQ^Ox6vp39JdGb9%ju#ysy!#GC#Cx_k`OqihmQiws7ebT@ zs6lNmnuS;qbG*X+H;oTSTzg7<6gDZg8N70P0-Gj93^sY*eC9n;^SV7(i)$vJHqbg$~gd>QA&kvy4tqy}6r}9hp)SJqK)O+ny^!xTKKf*y}A9p?UcrZ+bdYo;$uJ z-G)cK(|E86Ic+&vFw>QC5*(ot^#n0_QHigIL=gLuB4+*BPPbC#3o9nx56w5cVujp^ z>Tfg2XwN*IKvD1trdgb0qf_njzY{IsUIit-4O|E>mY5q~Zh;Ze-;9qikCqU5rrJ*B z6{!cKfzhe6fFUVv=cyw$Jqg%XBCa%i7Bb`Cu<}HW%F+YP!5Cf#*dUW~sKEQPbuu4u z^-uF$x{cqKFs!6;?>9dUQ5}YIuQYU? zmJ5KFR)<^|J#I%)kf(Iy&9zjij%yzXAYNZY#g>ahdTt49GcL|JVmA z6iBPLzfh>O|4!+Lg(XP>%YY04Vl_YzcyPOw=O^)?-T1*ZRR$(TW`$Rl>oo`(UI z;n4m!lgpFQqaJ$@Snmd4QgR^sJ0;g06o{>if6Psu{-FQS;k(Mc4CwOW4ga&GZjY6W zU@ohEn!VLw?D*R%lHT58R&eGCk&~6IH-LLM@Fly|csV1L+X5leEU3B(n>qjO^6llX z$a~YxF|<!Z$5f27*m6 z5$0GZ4cBO&oo&dUA3I~b*ub4vW3GEOf@-w2k=S{@cDK`QoA8#a8|m{UaUg{>ncsf7 zv8SuVoo`Puo*TdMZH;`|JyNNAo0bqWcTZxX$tZfy@zOW@f~-YJwGv|7KIC9!Z3W=E zW|wwR$%ddFH1^G#l^w4U3pL>|%dlUd?q*I6Iw2-y*Yg3azZmJzh2RnZXae=37PHO1 zZ$P~b02}uUTOZ3?cI)iwYB8{H2m~cN_%1)3$v`zpKz+T)l;>`b>#Q$&sps+YM^Bz4 zV!Rm9B0@|kWHkk={~~%$oYGJL*GC@=Y2|m^Fg+eI@w4&N7@@f&`Z@O@28Og@>f0=f zJYNjIs_ntuNur^5N-blidCL$e^NCi>C#^*|b9B^P1LWKS1Ho9ss`|9}sQ;g_FF1 zb)u`QYvo7XJ$}2Xm9L7I+LrgsKVv+Z2GHDxfGte?%qKgW0kBTByX8l6lLL(Ru}?{FquZu$?UpQGf@Z$c6$!%fnw zmZNn2J?)Hx;H&1z`ufFHI-;A|0q;Ze#e4;Lv z|0iCyvCu&1)A(1m9CN3WU`Y4whv~bRLo1Yc`eJt)_Tn0Z4thE}nQJNsHfk`{5Ffbx_{`!fOJ>>?y2i_pGKlIqEwn#VEH^Ryy?-HRhVDG* zmweL_XLe*V&XSgAZe%^OweD0WqLKY?K_pBq-deScCBJ|Z; z9UGg1T`|BKSEua#fXwsaZB-PgL>UjY##(o9rmJK#asicSZ|*+9gI9i!zI1amnb29j z+Vmr%foDH&;M7z71vUhCLYy&pN05}hkRRGs0z2QFFB-cSA0BHf^ZaYSqx=o;>zW@R z^yEI%x_NLgaa+;TO&hakCK5_VyCX`x)xP;``t>>P5AV~BYM{MN9wK8zjkB)2_%PDDOD35zrJ z^lqNWh=q<=i^xno3H!uOKVV~x0GbMs-EVg}ZwmOw1-3b@6;;gv!V7eh_I95~03fnM zOHCBb{jJiZoQ-a6FEC8aqn@}_BIH?e(J$v`_uKHr4JSUR_EA7W2V^Bg`H>UBHp<%)4&e)WeeZ zH{ckXPNO_dbD4H~_XF%PTrR&24V{M{nD*brGGmkH28=vU^xjf#0O`%GA8C2^=(-o_ zfBb^^0&0A?HmG!`eY(NJv4hg3Us(aM&%TQ#j>)rIfRK_J!8BH4=1KBlGB<1T`F0t? zS_%4>w8@#j!VQQoGc9ns7_l?V5S`v2Ru2aw*_M<@*4i$QO9}=Li-1)a)$w3B1>qco zVMvphvV?sp%z!`Og8?C)135X`y1@yySH5w9iYb7b6EAxCUMR2Gs>kz?J3anKnFadu z=g(DFjM|*JDio6!ssTT2)YR0J;)XW)46H)%`pjD4IDAF$1Ak%6SUl1gF%TL_6aXO6 zac}0s$B}DjXn|v;KMmMTE-+vIp0eJ}T<(T^^xn(0x!k$StW!NI_4*UGQ}G7@Is%Sl zEyn=utL^w9J&gvqoga?5@)QJ628c|%$Y~fEZ-9bM$(JuFQk+NLf;|WAV0|3b-CyuP zop~Wsg^c>Gt`M4kOWe-jvB;g`HjC<~ORrTcEIi)tx;BE%tkkPNE!5c+%6|6ibNw7o zS|TSKWZalC`oUnjn7`!MhRp&ef*S*d^1GqWvtbxOY$K=WshN0gqk$rCl21FE(51)W zYGG-hbBzAX`EU!#P&T4_&6q@6s-(}8#6omXHZc- z^=t$%`L%=kKk;f2)i_IZyj#rxurD0c)C<&^a($-jkN?Iq0^lTt8!x@yMKdvwXX6 zT^pbD8S8%GF7853Kd8^F*xi33 zUVlAx9{{L@0zkaPkdwCH-Bi6h%$-#f$oU6n9;4LjxuvC@l~qp5c#j_Qm!+BF16HNT zWct2YKUG}`IF#)g zHX3`%GD`7>>=em1VvsB$l4&6&Oj*(la{jXA8`N(nSqfupe^H7=c7A0Vr_9)88%suw zJvH{wdB3T1uIpTvYs`Feeed$T^FHr=KQs69@P)M>Q&i*u=R3g>H3;E{Dvtxm2c)|~ z%B8BX6fC;O-cT51c`Sdsp#Zj)^BT~S<@BdZsERp9zfD@6La<(Zq0NF^qY`y5BbSNd z9!rAye&l;gOFFlublO0b?g@&<56?!>QmsqxjtHcl-@F(DKMOXiC6Io0J$Kgb-_tm0 zm~1TG9&P|7(`ZVI>tXiN*t7M#YHI=)PX`@wnjFGqY&jmplrb9rmbws>wedGXcJ<}1bu(6tQ9a}ew&Y5|0JPl-P35X zd52R|SncJg8z5iKj?Me37Tbl%#&OXHLinL=cJ6VxEs$y21E@`%fEty~;C&B%Aeam? z@DQ0mv5N-a@au;qtQ^sa}w#_~O42?!uAM6*!UmF~Qa<3^5L zGRM+b6m7qMwn2Ky=lY@QLG@KyPfq=))XXuOwYIN3FgRh9D?z6T@kZGj1r`#?^I!?; z^w?}6#(;`m{On!BjV)uSI=|Va3shb38$Rar>WoNxlg9U&Z_UFqgDdJ~4Us^Am{XLu zHyDVj7f9LrfP|L&9>u{WIIEGw-;hC#IK? zPy;luyLp~@fVkr2RbaH=c78A4T~ zma7CEu~-t{(q!ss>U%3Jz+NDF+p6oWJwV1ZvlFtdqIb>P4jBTt!SF=f)5i~(B!9-r ziZnkhBJ;+X8UM6HSVy>ymTHtu*Rt@7b5U<-DtBvDQ*v_VkveSPN8(x!gzgO6RgY^; z%2R&SQBaX^;1=QNB8h$Y5OiqrsV(WDVYvzF3j;lFX~E@IZrmIj*TT+_AgdS!4f)u5Pw5qG!^ zY_e==T2MFYP$Cbbac>gR1JmpnvXSDII4$u-&@u13UH}=J27QwAUSA4 zV0EYde2{u*KxPtW(H)ZbDaQcbaJq6lk%KG?NT!v5JWD&eZv;wT*7u1U10 zRp^S<@mk-04F?tN4#}1i)<>x|da5?liOWC488v`U+*ztAnbdWsXIJ}2(AVa4#qnXr zGHa@pt|Rin^$c`S4w+0XIA+H%&(rTG)DR;Er|2V|QsB0=AkxBN1;K5Ie&$Ej1(3!V8p=Ef8&j;AzT?^C;7vI@ZHwCi#@ zLEkY*NS{Y9x&0+$x1T7hqMIb7k6hc-v0Hn-8Y3znBsiKi3etg@A3c8||A9=~bsxod zY5+GD%W%(FAm56iw;^b^J3USH_gPVHM|AZNq{Vpx3ha$}ooUaXUZq8?k-fHL|8=-} zFUn!b0sE*+L+;Jjf*@rDX#q8=yu6RHa?#5H0fOKaUUQr!I7&>rGP1LULQboyIeXGX zKhah-v_00q4xYNJH_SZzPFiMGyQ|4Mb?5B8mD+Dm6=CE9k2WsUWV>ceV8P=PBmYDw zN`hy^w+4MH=>@Jebuhs%uK1;g?^*1;Z4xsgochgR{t?!X_s8`&AIUXv6r11HcW{ zsFgKsJY&abs|*2={hM*zC3*EroS@ACqXCR2-77zMYq4c_eaK^I>KyBfpp1N8jA!r7 zmG|EQ3DRtfy_Y8=$yyhKr|Xn&67&%nq?4FF5Jd8Rj@0?Op#XquZe`y?7#ZrA!MHWn zEG^=_+C^^OrxWTGL5KWKK%#c#w)y>!ZM`b-!yg4M43#ICWm>snkv&p5t{<3fF(dCr zZ9+;0cNd0NH17K7`ypoRc^l;wVWh4Ac$4!gd?c=GTir|>JG(zSqW21U#W@;B(ew=L zlcJe!q0;@pLq%#_t|M-pfwR0NS=isNfL7kI>Rn!6cRs4=!n{1uHSl?5- z^{-s9OkTsRGMG?g>OS0&KWEO;$e7_qv4jFr3iGlehi()3rBBQ5x~cGt=_abn3F60i za-T<8SS@9{nlj891f(Cjbf@m0t#pCG}-lAA8^6D`){p80jV>=Oa+K(!|iq0>TkmhZN7Nw zsqot|fG>=Q=F3`pj)}vhb4KJo{w@;mNHd3{;t@9d-ht1=(6C<-iQrX|Ju}iQl()c% zfG-FX^MQ*_%3$WdeY7lD;N;m>_RMtx$D|Ms@|lF2svxyQ4n|AA{y^N~|M{q_>yAU` zr0%N4m*Wyj%N8#IOD;>(|VF&8gRO!RrJdx3M{a)HizxjOqE^HY=%i5KDVdOQH* zJ*AP&dl)ur`3X}*2IS%OLZ^F55g5qZMJkcAI7y-a%uvm|e$NV?M>@=~)7!+PqPpRc z&yYYvIr8U$kdpggXAK-0V7fwd4&^?mC~GJha}JE9yzb`yYIwL5eguzGT*|%fBZXN+ zy`qAUV4WJOm8tfgh@%iNP}_Lt?{DNTi%5b4(h;V<e5B~;X=~>OJg4}()t|9_ zY(2dk-CX}n4|9mSXc)xU!gcLD*kBk;E=tV*oOG_q( zQ1U0CL2&tzD8%-Y(ZzlyyB3>Hb`!x!MHWsfB8qq>Kt_jH)})|Aq=Lw9A)X45Q6h9i z$y^cC-Xv!cEg&*>#6y1)`q-Rv!iYgJGH!%61(^>5E#i7?{lJu=4AX`ViR|Ns1K?9e%OM3py5 z_}?Cr`PU%9Z}#;LiKwYB;x=5aG%vE&LVi! zDOmrEdD9nIlpim!_wpJP%%q~Ch*EEocL<#C|2U+R-(QP6-RnQ|`#($V?kMTaKMOh> zB?UHAfQT*j$ck_!Jn5P~x|8RuV5{cnT-<^`Jnb{YR61KP6?OHX@SZv?v^iR!;B z?zbp_nK6({fcOCi$48d%p9=BYBmjlDj~M$&s*MObPbQ3bItKjPaH^iCJ}_3}?0=!6 zx>W{(rmJ=^{|J4A#KCEg@Kzq=9po38y2-3Uwk_uc%kP2pN8DXH4PE|>?&?5DlJWn^ODc=)P*ZTd#Q!3-zs*!`zU zgfxU!(d5>~f#2RQ*0E0ksB-c<E0ZY92XQYt|kyMC^ITMrF+z*emyr&R(4d00p z-b)@7s1Uf#`g1Fy#qZeg;O39omrA~!H+_EnP)_)%rtlW==Knser%%U6H-=z#ES+Qb z!>_INaX6-kyk?Om$$b3g*_@8mEl_acDAYe^uPj$URBL;kYFV}}%7mJKGQ4>&>-O-P z5xn`la*jvHI9(AsrgxB5n zj$Vy~z;EqwEnVUAX9wN-+jzj(t8%0*#jKy#PdXIhrzqZWCsvxq;o3#$T2<%4gWHo+ z_~5+w4JR9o81eoZc~N{-7he-njrrXzYyYp2h~^5#a(HGsIqUer^<%pzE`TYOMX54ZRc9>f-}`0GA8C~tOvNbH~t8G?Jzo)0qA)u5sdc_j#RFVK>; z5GX2dMz53ERI2Y5R3a*X)u24=Di{<49YBN!@Imj+)T)!-e)x|U{2?h$>B&y@)_tGp zGwaz0q(Yg^+tO#~7uI60Q+rH&v9lROjbHz!&9A)hJb_4Qursm$#%Nct#XzK={(L6G zLWC0iO~-f?0kW#a*AEhV^&eArAZ;mHY}ucz&lqYEbL3NbV|I z6OERlbjZrkyFWcr7;~=9Gx0p`U{ST=u)z28j4cU@aI$Ks5XD6IMwhPSN#F2ocK5aw zw(IAh@PIdV~E{APu=Ytl)cMq$PQzQ&d&JNYX zWj$xw8COR%k&^5>nF@2mVp-=1s9?2$*%^PS&4I)WeU;8!egL5!>A{u?uvn{z$) zgZD*#HARGZPJg%TPM1@Tzqgnr!*JzlxBOE{y!;zS<E!M@cI%XOUMacA7ar||D)6XmQC(-NEZTaJr;&?Dhy&*x!TF~Q{ z!ZjYqxm-=;mwq&OZ8i=5t2;yf^$m$K_9a_(oXS(lt&Wf7g9Eo#N5*Nd@CMw<)>?mq zWojOHdDH#4S?57ezy%AsU3!|aPa$jD{mr4Ra7Z56EyZwEQ+lrSrVF{-kxebOv+R!) zSa)65+)MpW6*c|bYokPA`^&DQHLr^6kC_UMvFGuS!6Gwe(AD6@At8mgtkSR1IG-gZ zknQ)EWR#b0S~o|^wW|8FNP25$jeb)8tV7z5&YDViOCHI-O6*X(Xf}9R&$H^$lyXoP zTs#!Bpz6Q1&#BLv^4yiKnd8e*e&E4p&0>>)3*Kr4;2N zf1D2D%9ZlqgNJZ*OCaOYToy4G)Vo;XV?R$d0h z^Vnt=ThvEez>h>BU_LvO0kf5%QZh6+_p%|FuManE50WSM~Qi0lDt8JS03KGc&)=dy|Cs%EXL>{%g{aiHpxZ+fDam zDM6FA+Y^OLeU=C}nLm6}hQr1_`QvNmIo0AHs{6gat6}VTYY^^( zB&z-0#VS_whU$eA`IlZJBXt^yIiSqPT2GW4&b2`5{WlaxKJOP2zQH>yRU(8w)RbNm zkA%n$;u5Ppt>}8MGtV|h2ioDC1_~O8`2140aT(LP0J`Jjk2jT^*%LyE+lzExwEp#o(|Mwev*mRI!yPmW@iOD%jveQRN`I7Sv8 z(w}cYJzDE)fZCtwfF~=b$w-Si4?X?z>H;N%t4m+SBuABQb7^GI(C=rQB9bX0Nm4XE zw=PGgYy0X2**dySXXm%=+%8`#$L_wv-Lne5GQNDiQ5Ziq*H8e`Jz5<$Ta>CNq0w}8 z-W0Pw)^B#vr4L2Z3kT0`vm6HQUA@Ys`8FUGlCB%27}*KZ{Dbtb;l)H zu1|eqc;M&$bdOiRAOZA(TQ_@E?{?_sK_`m~F=~7I>(9n8{OhF1Y^*~MsdT_*x6JID zOKAD?Yacqi^1zL^X|r#Zdqdx%<14J5SGkU2)VI->?hX=nzLGK#6gZ*x)^DTO={+>Q(_FE_lAceH>fdG@vZ) zZ59Y=9fpi#w;y+8HG=SC#D)SZjD;i4dFiLoYxf|Rr7ENF5Vw@~kA(17)%`tok8An; zRwr;tnFBv-H68jtmd>)B{5%WhYmh8;x?dJ&;b8jX6TBjOW40^HvN7~En=CHC@o2M8 ztF9thNQTCWB1)@Zdu3{3@ZRd%MBL;EL zJTfGB--@s~e|OiBJw+<;vM4qRLi}!Anks#TTlaR3AQi5lDqfb=s|R8Nf~;SvD3FMw zNrmG5OjD$ud+)8=!o1%l5btOt-N%!=jXnsIgsIYp+KB1qhKjU4f2EzfOnn{(HHBZy zv!67SG`_lZLrfw|11laVm?n%b8~(nJUFbHFdXzo>`B~n!t?1&xfm;Q1>;|7P_Dj{$ zNWMnfSLL?-h`TzOH}wk)Q>_aeF)VetH6j)kvUu;^P8GW`Iwm_e$_$EBcM4ZF*li$0^#{{fng!Reci`FFca(5{Q6pv;Re7>|u zIheEOh%pNvjQL{QM>E2o8a-vtYE9LOQZ37UzT|ACq)P3I$+@UCItBkW04Mg-aDL=} zOCt$0TlirBf)Qo5Gw|w2$?Md_VTlmMSyZb0RncBZU@hf_3YafqN+6~F=^NTRPmn&@ zI?tX{bZ3B-rrJN4u#|4OEQk$NcyOqG;JyT}=e_SWb5ZdvOGMdp8wF!&e1)e3Ncx9Q z96|%vaI&%*^&Ca8^oAFGzuVGx&$!}s^84!ZC1lffU8!bBgh`R91f}Om)j^eW=WVIT z5q_?OaE|J$CPFWRYwGN~+otco86j)3bzJ%5Bw=1>9-&huVGr)~mrpL5euxdY ztUBNTUg}_N=hDEoK1Kn_gfBH+TYS^UH?%QaZswv_gSXp_Aa~&g^e|#S9u-Ed-SQ%i zdym3%rt_7=#ta36184Y!>o@ZY`y6Mz34ziKR(L!Y&ShQK4M?EKm7`4)f7(j zlT}KU-dP!M34^nRysq_KAy8-$uXPLu>f~-;73PwL5gF`>!$Ph_ozv%~Yil}+?v4L* zF$}-#yHYJ14k;LrWFI^6SuptV&P*RafkVVKzx#j@$y4%{)Esn&P-k@(W#kCwM=*4qU}yVg7WL8}dv z8PP!ttaQ0(pXs`$YB18k ztkl!?^jJJN-oJ2!)o~hT`QcONw{`>O1YIcLP-^&Pt48W`9>LPsowjVfZ76z>Ed`0R_UZ(JNM7YZhd*kZCCN1!n8#8iwb7nYH=uUR=HGnL-5 zt{!O1DvRKYaP>oaA-JGDK^PG-g|~aOT1Nj&#IZN;QkzXh=b!+wzvjH4ZZ6=^TYzBo zCiD7G(PYcpd|}kI4XOVOaHd`_uMqH`hIXevlbkh6bqu3sgDa{nol}(vtafVVMZ;xoxqdm=3m)#)UYeIFw$bAE&}{Jt{f zpjLPNRHA!;L=O_dUE9LKykRi`V)-3Km=EYfZo#> z$q$K)ATJaUr4tP$jAYq{`a2Vam7)yYhDziZ=hq1Zh4g&J=^m5Kt#RD6Hpbb91x|xS zAK~8f@0r%(=f}lee`xo$Hr;N8cvjmWjxYo-)!y(o^jSx5x7DMV#fv2M8e?Kk!vi`} z{Xz8DU7fA~mBh5p)`OzGLh(vP4h?M8@1vv2bxh%#Ra1;bU`DFYhCQ~@IC8y&UH(I9 zD#(Q?{KMTMMU1U+eM^iCg6q|@1~`qc7n|Zp%_=x**P#Lo zbUX%MRZixCf#{*!48xGmU~I~ zk7z8`6y5upi4S8pz$}kS=}MNOe9I=Sn9GmsQB5{h2fHkGG|u%Zm{g(<;EITj^%RcP z93Qc|1d3GVOOmh3()1(6 z_-W{m686p%$wJ0C7ihMLYl>M#nYfdH(7N!_65R(Lhyon!Vg`%Q=Udy@WK778O5oIr z(PiF?&PzN>gk}eX+O6iQr#^2Y7%y+K1!oi7H(P0f^lC2?d>5bUyAZk|wZAS~JxL65 zU^>?~k$J=4Az=NE*3X|=nwRJi&zdAM!FBjHyJR3Ggq$^sEK%>GpFG%`P0PP^+#4?= z>NIe1v;r=z7|(AOZrc`5!EHPMY3Ym3D9GiD+Mj$YE9lsJ+qT_zaVSHvLbf;6SJH((k z16PW`_E|V`w0ovv4-eJ&g1>xyv)cBYg6A#6rbM}&b);CK>xP)ib%zd!L4W7BV`FvL zf_{>JYBUugsnX`#dv;u9WnjD*6Cb#PlA7tI2D2E(@n@yZ?P1POj}ko{1~Mg#q5krw zDez;L{@}0gJa@ZA$g|YWm+oZD&nG|Vfz&D}@(c8vBSQyo8m**=I_ZmYj`)1Lc-Nmb z9POGFtiua#1hSdp5I#lY?U%d92P-}K1_FztwFXC~sflIF#slhYspRW7Ow*-TY`&VH z{L=8-d^8*P7K&X4GvveG$&IWE`z#KL;NkoRQG0aSx!c0C2hWb$+rL|8r*=Tg^NB*= zAqUSXHzdG(S-nRUqHPgaFT19XAwXKfbhOonYRDNtm40KIyY)B%?Xmg1%c}l0}c0!YZ)v5tEal zXH~|`Jn_86OZ;Q%jFV~DOBQy}Ec^v5%{);LlZmMus14gD|=wg=u)aU}KiZkr4lNYvC$@teeQ*|u@K%4_yiA?whjbv};4 z*-m8RgQWzOkr?Ckrs4uL{Ura4sTfx*tY3G0e`^6B=b7lrf7Vi?{_3usfIOSCt=Nz%n|jX!asBmwx!Jv zcXxxQ&x=MKI4$L5MsF`2rL(_ykw6R}h&T`DEe^h_&j=YUuFzNdoA|*+(B&n5_=z;fIDJK2SKforOadzT}Q2N9O?R#?BPQ>`qgfyGxft(EWXR)}Cp@S0-*iRf&yNG?1Y^3vx0AkZOZe-&rFZ+m zRc!s@tJX?Uiq9TV=w#VxIFHomMTv}eo8R+v7$2GyFgyca-UsLBr}0?7-qr=11~%Ny zk0Nh1UMNhidh+@0i++)nH(ZWutE;2Z{-H`Mg}A(aY6)CScJor$;n``xl?V368!Rz+2HoUMC}{OW4Oa~ z_?zo}ZX*x)+@Ni44j-I(d_xS+* z>N!mTcR%{5G$VD%-cJ8Aad-PZWCFhk7O!6%ZqKRl3AN zcBwUg{2iwg{qDg*7BojS4;`hy(>oy4cFn2!_O2Mm5>2Qv6OcO%NE^Tnq=OuYU#8*} zq92Gz`^R!6L91HZ=T%vH(0w0_E6xB~z{F5!^l##Yp$N%Y+M0le1hrNUu80;*1%@mNAg8dYuHoO@XN9#tqker7WE~T zbHc&7{pfNg5Qi)vPunryzjja*MHu-#M9iBM>CumJ^00orOocb@C&$uX@zH0EvXD89 zT$(q!5@uKikgrfozAIyT%$?t=e|fTEqy%I{Mpa}FYetW{vo#CzFY#g{C3Db~?^zq6 z5w$?1PP0-Qpt9#9>ANtj3YQcO*l}~)KE*ty_1WzmI@1m=9PiDZ>qQ-1*u4B<7S3j#kcN^~d?#%%Cd~H`n$_fE>o2pG9&8oqh_CZV@UCj{ta-LcP zNJaKbRNL~vNzi-;p-(;^O>zSm*Nz?vaF1)wx=Nd<M~3O56w1i4`1@ktwy0MmtPF?{O*jY^wICmc;{AwUEHfQ#|LFbR`x>D5piz0UR9}u z*Ppu4(lA2~3~7f5Yi#eFD7t?1aTvSC2g@smDgc{u^knv|Lc_HF6DPR!MG z#7M*CW|s%h=86p+;ap?BK!@Ul=zn$C!ePCXGUkTK$|><7H|?pJJubQ{`U6yW|46Ff zY_f#s(ra|3v>;hk+6=)@#bhgEJ{s{r$r~D^U})t)e!6z8U7O!4e`TxxU>eu9Lsl2# z!;h@pa^^%zT4QKiRN=o&Z$1#*=};2C*F(%XXlDPihQ*HCaqri0GJRE?+CoyDDQM&Ls$s0y+mpa-i3bFPW6QwB*ZurL4qPNc4Sl%Khn07NXI+=dllb1|5}B; zI(8%G303sfbhJ-g3dK5L&SH~49V^iBVqX0O26#i@clo|>Nzpr`_a0N9c4z!n^9Qo| zv^#j6T~qkZ=uRRO$2O|1g{?%-YMo^2BheXedb_3m!6G@ zsYY6Beh2UeE;6L5_0L^a*&qv@MZE{fViD3iSp{#uB;zDbjwDn&AP%*RY3&b z>G;j8+7UDQc}u-{+w3@YXHJ`m)^>7q(e$7rB!cTO$T;t3)+O7|4|683-;|V zFr3Ea4?;GM9v0tvr1eLw{-dvN5azvAZ!CJ|MW4r7z`lIns3a`qffoTNCc%(sTrm7V z>?)_~HSGpUwxFjM8A0i0wLT6#nN5L>Kz)bn@L;9hu*Q2nurKt(1W@QX4fNKQs>U;+ zcnpJ>GeZq*0rg(MsEk|`+OK`WZ(MQLXJxGL_-L+&<7*_NaJyub1EP+_vr2QTa?q&! z*YZJ;G%9I@GC+?8^hXJL;Si}J4&Rw*TS|U*yU?u9}Spqi*PXUqw?VX*k$fn zn`^Cb>N8 zJ3C7IoAWW;;5#=YJjn$taHmDBxNjlHhZ0Pp(3d({iWvYHkpQv{|IUFr1oF6{Dz`MI z>0_V(_fEpgZtLK1ZzEp>NT?t0ERT9D|B$fhNTLH`8NIFBYVr5dU(_G2UpPEG>{o_i zAn4VfVQf}FSb5)X^(K6MrXvE_>Peu|2hH9eF1L3W`y^56IvNgu^OzU_m$A>@mjgWDChL@W4*-ja9AF_OGg z=o5X0DHkZFn1cDRAMWaq!VlNNm>5}wtjGhl7tt3>8Jzd(BU#lhr2SJrR!Vd7$ngcl~T6DTHq;EtzR1M)ny&ie7uL6+BSC-kea zIvln{z?OPWtA6lpj%1u0sVW9dO8G2mqxQqtLOy?eL)!y{k)rRVZCbC0Mwx~bRha=W zn4o&zSP{4`y}jnQAuHTan_UX1R08Zc+Oc)C+5t1lLc&Gd;RL&MiTBwhLmiVN4Jqp% z3HQIhQ-ZEM8mD6PgXp`#9g=eT;H&DQN@tKK%G$Xz$xxvR048eAQA22>)ERA2O*U<> z-2p)RiYOE!3S#j~+H$Ku3N6O%t0w*eED*ib%_8OVGE&B(*5?xFLtsaeXr6Wk=|HhX z@iCv`5it1-%@bB(yU6bXS>w8>NYghBW4Pd25FU_^K_4ERt^6zs&MclOA9ndW08)r+ z`PLE#i&s89JgFzQ=s}x52~fWuj~cR5Fu1dWH#v{9Lj4!BcZ82=Nih6-nf?Ime$ zdD8ZM0gj!Dg4oW%%^Rt}A_}wA0*!y^gQcC;E=R%>YK%>Uw|Lscl)|skj1u(a`iz?v z=Mh@jTee`9R7Z~ih?G;o76Y+Ublvjd+`kH zXY!B@PSXS`uSEl(Hc7|C0lt&txvaN0XAJm-T0x%3+bUN@Z{0&}0~QaJ_5w;a_}7(( zaMRu&)n1gP)-C$r23yrCm+XLznU0|n%NI$@r^Rhipq^|)={~YNZiTU}FjdU8wJ{mc zjF78JB=YECCN}5$ptld2BCZqFMn9vBwQ7OdsAc`l#@3(tlK2aPB7IgbQ;~bWkSmmy zO2q&)=P*!^IJO#~QPb@W4Mm%;9l-V5ldDZ@5p@@1Z529Rh`N}Q>DAwx7yV>5OjDIa z++X6#4DKttn;<2h5PWF4N_x1qLsgM?LJ8t)j0G1&#c*ERAP^>I@h;Z}$#$z*z$N!c z4ZBJM_SH5E;-0}Iy&J1eD|Tv9-B}9`{Z587!(KM$%Tr4^W`0dPv(7WGy%&XzcJOxj&5ZT(mCly6ypG0H0>c|7aHGvxM4HECgSOw&9OGoR0qdSstEHGo>GZoMB z$pj0{2|1klMr)fromU7XwDLX_^c&2)`u70%i4;Hum41DFQwl!`aUQAEh>}SIYH>@f z{GkQ|+VD1P(4OZe5q|-od}uSre~Vjw;J-f6ku3gB$6lz-D$lmCvIEjKSDr9yTJ4Fx z)Mc~xvIC-K#%EFq=|ZgF4lbXfYbWtBoA`W#1y_VM=3-)7tT(2rE7fy+`rz5vYogYy z3d(p{4e(>V;}TN3->*lVC>Xghg3Wfze;C4R95`h7l9HSEr9C#!fz9fILW0&kph2Uy zOi{4(rvVsr<>XkV6q*hE7-Rw+Q@EGzs= zjbwC{l(Ql?M9Dnl(;8$8p6HEwTp@Js4Pu0Us>1#o_5BRr^Q4_@zwT^z}a$h z)8YPl$W1lwsT+P4wU7GHj82#>&6DV~o0oM_n&I|ZY1Yw|XKO&zO$eXJ@DUVgMWh|qxfDP6tqDeOB03A4(}Dfo<%6oJe0_0bL% znq0R^3UCKIB?mABO&Rxovy%Z83hi#;jz`)J9Bj|>tNB!Z5PkEk__J+&acXzMK0z%# zQF3dKIrG^W!1oEV?4>X>oTsO|Ok&Z8HWy0pAHVcFEOl#zNLuuH`AK5K(K5$fMu#ii z-=5x=2|m0Qc=8}}-ivY=PG@4F;_MG*d2l6Ua5-JB_RWeYelJ4wQSnN>96r2xkqPIy z#5T9vcupN)OGdLwMVM5r{LGbOxgrarGJQ~a-edAB$V?dLfZ0k}vENjCl0e-d$eIwK z4x2!azn3YPAKy+OP5>(yywkbbOMYVZ1k7TL>;4WO5Bm(9Ov~`@`GcX2oqd3nBZA+@ z|H8%L0Mz*9sK=@DR;u%N>sEd)E%Rke{(S-#T47$mw~H+5qs$3s?N@~H*tP+=d`&)` zvJ|1(P`8niOY(u#kGt5VJ!ZZ#{;2c1Znp0&m(iYP)rZq~y>ynEo)G*EgbJ9isV{XN z#a(f6HiFmRmJvwcoS0n(;qf?Pq@snM!WIvw$Nah;mLjWQF5M1Dswz>2twm9LG_)eUjgk%CX~aZ-AwB_*y9N zCTTyn29_)6<|nh<0PBg)1O9R9;}PbW;1!wxEBO5H$i*(1$d|nRq^-G*{R^brhjHrL ziEDFatvams&XNvRpDqq#c>Xx#*bnVrQZe7x3!#iQoZ!HIy=PG!z@_l8FIVNf5!j_R zQV_2e?rFSHpT!hD2`| zeHZY)TilaqvjdhueVuDdO@8Ny+LsO`*kKw+=11sIk`)41$HdJ>XeRlNc)_Sh$${oE z0Rfxq77~iN##pGL+G7L%u>!E!nFM9{b%6I2j578)EHWy$dq{T5yelv&x0>tDAl@Uc z^81G(_)PS37W#7E78!=c=YexIiF5ZHBR&WK*_;5g8*U|35FaEvaUfSh9m=s%3PgKC zZQ@wjhT=H%d$|F~2tNl@ z+OVgs(({*VSe?0{1QAcrudh*Ay{XbU9rQSV-ml7AIOzkS?7=HPaQ@7D=cga1{S3Y$ zZf;sv#Vy}!WS4F1=9VIgpXJ|`Lxs8-%8*Eu+bh%-$^Pug;z~H#xY>T-!iVZK4TrTg zPyXVBU*kR!m8{r1PZo$(gijNY-Na<3Jj2SniqR*WFVP`eL{ZRGL8xAY;$$DGM0eqe zUy7s`74P-v-VxE-KL2vG&(s~8aP+|1=Qy8FNORsFel=epVS1Jm=xNKstjlgYYbB^Y zDNx7QsK7JD`X5Frxjy3$6WG-O65fFZ46U1^UE!c(3WTgcpwkDp3#-#sviQ~ zH=yM6nDN}mhnsSwal8hX0XY0+d^XnS(%eVfFuC>@{$UF zFB!+fQ zst-AWNcYZAT%gYrF75dE8^G9{IpBwnQ{Q?iY26WX5c(SxN^-%RNO&?SU9KOwCb?^W z5CKZN<=FeueE<@{E(4!=a5j=D9rMv^;WDlje4bK(<)KN(eS>_HOSU{8bm|Xq9n{K5 zu@C&(MX1tvIs;e|fEu>}LSbjXdvWBWVt_>pWZrOIK;_?_rj$s}n=^*XZNm1Q5O8Z3 zQYtJ<=`AJzfCX}Ke#Ft*s{MlY1R=7~8d&2yc6m~D*x~M442RO?53<4gn~Jxn%I}@R zTU+w}j_sWrZkuJp?*c+=V{`S$*csd-5$4(5=+_ zj#jx=mRfdg3DCMIp8-gl^47P$>q5an0(#R+QadrXz!IXmX1IXB3I}5rQOvRUufY<3 zwx9&DYCt!htew5_An0me^A^M(ZY#MvP8pry-f(iUhL_(>5U(h6&m}S{fXW+B1|~Qb zqc0ym?X68TbnloK!oTMdX=MF)tcKu6Pkvf7)8e!Jc);1+gOCF7$l<_<1gNa6?1P|P zZ8}b{dF*YK=g%oFSgnlJCCI{Ypvz^=A^?X1;DF~!?kgxRea&lqJz5EO8PNv#o&br3 zoX@!8bLb^zh>=Cm8pR#0)JTBg^Gc$I!B_%xe!eq8GFWLm?*KjK7ix#$EOyXUd}G^h z6uJ76inf33A^`HpwM4V50es|iAkC9Q!K{*k>dDxP8`ld5 z0i9~hffr}Gql?#E+6aJ^zMy;?Q*0<-u^bEUfGEa$Q&{YzyAkYU*(c-NIj~ zlnVK5b9Rq~O~-}$_pEyzDBf8V!arj(t&|DHavQlLDaKG*Unm5JnA325A3xq z8FM0(j|2Y!aO(;5tRJ0wQi6Wc`e|U7WSBo)T7Rq=cA3R}wv**G)wvJ~G5jfcnq%~t zhF27mXo>SM=mGrLG)lki-0mrLR~eg#I0ZM0$Gm~~wgFxn3~y`NdGQ)*7x{oAOF^hv zePjsmz>Q4k&Llpv_ql%Jy$c0socHln97da37;zXG$!7m`!u*BDN~d&WirCeNS-xgs zj*}p);TpElw@!6S=VT!O5WKZZylx=~D`eT}XVVcw2UgT*XsD;Q9Oz99a07>Cmq((< zDmLzltsKI+6mp?@Jm-W4D%)7W!MkaFudi`iJsc~B#y7UB1eZ7r-`QjpVf0;*R_M|4 zLXXzlB31^A$Meebt{>qTCCcm{663zTg)t}|N24Mw>U`9eZtyQu z_MtRgmD3;bex=pG@@fE{xlZ7jZ))A6FFy4nEL`w9aNQFBP=qjG$l5DyNIDpi$B5hl z0H9w#i*cL);LjF-=Ub?l=QdiCgZgy5HE3}^aJzsBPYZB!)S6zmadNbSo)4ATssSLX zlE&}{v^jvTQG0m{>0u!3-#zp!w&DT2n!|SG2qG|9(hHD$feQf{xT(GE5w{p|H@LZo z3*p*tgi|M8;Q7}ZfEAm}>#JiE%o!Vi4{V|~01&&v_n9&@>7zOg0thxb0jg9U?&kYX zpiI7nqNWtJYJjt*odm$0>6pGsMH2#_K<_Er4q#1@0%WJ)Ds|v2I>q3(@v$qXhz7Im z?4MEwdKd){SV=o+w1Bfx(R}TlKF^Uq6XuMNM(y{nfnQ$}(tS8~-a`L#x)51u$n@Dr zHZfM}7=lktfZu)oG_(~08Pt{je4$1>h&(gCy3@0{xxUt(pRebM?+_E;@Y{_AGLIm4rj`8Sd< zeVb>45H%yv`aLII+2dVn*w#UxH)A6E!ywNjdDYkqM-RDa_YKxg3k{LE!1b7w+qE7KlbTYyF z75kZYfD0zANGWkbrAq{$A=W@33!VAOl@^qV@@#9T+RIe(HUKULA^9$C#uR=eLa^1WnrrXN(EY{ ze)gf##FF}#gJr2FKxR>w(F^4N@z?Jo6P8-uvSumP5uyGXjXY7^E}yAe&2Cn4G*xp_ zYrt+U4!Zr2n`O=NIPCWa$TF8O>1J(`u2hk!s?rURFR0%YM0N#BstXE=sh!(za3 zRTTe>qsD7i(^e5Er(}zPXlZYqouh z?xY0b=jowbo=Gq`cUc@?N&hv9*WLsU>V$H_PjxNv+*#bcG6M>>=GLcb^TaYVm}tI^~= zCHikP3S}9I>QNtPzE0Q~cG8bwoe;+MJa#pJCx`L@yO@9KtFOBz5!2H~C+@tI#2(jm zu<&{B96{XD(DrrNK~jH;Tl<;l?Xz2bkU;lqg+NVUZ(Zs4z|+9MxIR@slriebH+Yg` zqPa=>LlU51qldKM%L*-%@^cry2ez@T`MAi$?{CY8iG0|$Nhndg4q&5R+52Yd&H}#u z=&cpUW#^3AmE3>>P3Z;B>5+d{VuWQcxQgOAP({tXFgQmNOYKZuzy?#AjpmDQFY33| zf1Rx)KmqVG!lQ}*^)~#LaNwo%x}}ZjO?^KH-CSJ3!{Fdt&2f)kN02nTA2Hugk9>O} z-cKvUoax#>WAtAHOZ(1vIuBP+>MX52DYoFSMp5@H7o`d>ll2|sNNOj7Il9L8n<%q%!@bcg#p0Yb} zYG8m7@x1+F;q%k6a(YxPf)~Nx zX>IT3f$w%_8wo+Q`FQ}2f~MJRKKY0o&7~GVMP-G{J+-_3KP>HkwKCxH!T(=N`(LdL z{{L#%{xyj8y9gvjb{7`HMZ=Y`Nz$ISTKEGH$ zHw(i&jZ{Tn?r_KD#v&jDLjt0{q zz#CQm!1FRzDNUvB-w z6dAEIn*Nfq$n_1=-bgKzRGWCh84Ya1!;X8=z$^);SJPT!I7$K9r*LCK&6xa?{yd#` zl(SRcV!x?!JVY9U&-OcZe*`WYqD86h5AgiC&d_wg59qC^o#Gy#SlT{1EN~IL^<)cR|wu%zxW%i?9j06arM``q8g$sUt%#o`pf#pB=-& zzzqWHN77QAoq7^zdw?hI*w*f;A&NhsW>H#s2EGXgGT2LL^sUb<$p}9iMNVAq!WO^! zx0{t19~cF0t<-4LPR_x{UWKk3R&P^pvVktm3Yg1b;#9OtopmlQkI(}X5UF4lKA(lY zoLm+e`dz#?-=J8G<66O_G`WozC{bb+peMxY0r(~wJM|D5%1V8DdyJlB)>vLzs-i4O+8xPCE31Huf*sh##M$;mA9oW)wHXFMMBEM3);@J8Dp1k>f808i7x~zGczLc_j28aQlW=h*=cP!IqN<6v(FArMZe0++?g(M1{v$)pwHtcO`x__;l^4;w3V2M0Vw25&X|Ae%iIhsFr`5mq{^{aZ^N;S9SUIEP!@W0m831sT>3zDU zzbrQNYNH!)9Y#IQZ(KAqt2{@$D}&bi0~(%s-|FhXF^)bsbW5@GlY|HUgz1i>tW@Co zMS2(0m%#n1bfcvihha45Iy|Y%5qt!cYJoe|H3G!pjO)GGYA@nhOolkK29xS(IhI?W z%w+&k{$jUBLY;HG8H}jm9F7h-*yaPxqMwCG8>gEWrh{bzFDJarYU#8Zn8LMMzzf)2 ze|(C-&_!e@A&PZsJ;Xbn!lNl7#BchSDeRVNF~{|{mzYlG@_(J9+Q3)c5X}#MmT@$K zhPP;$k;-ZF)9W++Kw^1=tEGwF9Y4VH_?vfl%5U17@YNsz=h=6^=8>;=dO3EE&Up0LdsN9} z$&-__LJuC?udkImF)Rkrp2b0w@&3xb{^Otg6f)gUf=!4%&svmv zk%ntH%_JeC9~qU1C%tqCqwM@)#MFL+JcAO$3_bDJu0h%YveifVZE{|iV{hx_9Hr!^ z;HX}xFPVm(%>`7&#d8^71wj<4xEsoJ+}&Z2y9TI)>tTx_QOxdDecwkan9EmsA~m~I%pv(-8Qq#p z0-laD*?m5bbU}5#I^7$l8jvbYpu zXMEAaK3)cdUn^Hv(jU#elO~pDW?so;!@RoKtg7~a-Lz5EfcJ!=KLzrmDMoT>9MW#D z&C=0NjkHZ{C8@u+P;@XAQKnhW_Yk=?%)MjXt$(rLa)i$CZ2b;>l%_40`BJMo$=QUQ z!;|m)j+4*9&i_4#!_k!mh;vD4I?Od0mpM;W72QKMwBC?-Tc=7tf(+ON|8!(?vs5B( zoCB4HJSfBKJ9J{~knU^r9CSqgL)WQg#5DJLo>4%x`I4;CfrndY4B~Eg*eG|qT(Y=i z${EKEg>}_0O@_WBC0eG*!#R#`ERH(Yu8f?>6^@tTVUkKDlzdXS9KYmwOG!sk=vO06 z${rCetE7a`gyqvfd^f<=Gmkd zI7bF4n%tQ!nB1|&X~Jfv9AU3PpPH>-I_(GF%w#=tpooEgTEj*tjc8vYyow9kb!P$M_&8?KG#-9YomC zE4s(dOvkz8$CcFCWrc3$jagtT}F zmiv;Qfm0$OqeO1&ES3HUeozhQj2^k`6uAsS?X0=nj5~!E{y1q!!W}B2**5(^dYGt^{Sm$@VE-`GnJWQ>k+gu;8bLQa^=ZnVN zz4`6U;o@|Rznf)dHO*GFWV?K^KI!e0r%N}rs3Fo447o+tp=q-{;?fZ&fu2ZAgLAk! z>f~hZLr@EC(kJKr2rS9sSLCIw^sE5!tNEuL>O-KsZmw+MX( z37PioKDP^tzHQgaBtBQ;_x{bK)32T7o%5*^0))wm17!jQ135|k3TH?e3O|5V_v6TIrVf8jd95!_?o4$jvNqmB z*^+Y0wGy~W@#fe;KQP^guELMkzTBMmDzDWm{Fc6O9Fu^c_ndiSt=8=6y>pa(tI7i1 zmyLL=ue1~QqrOt+IL39;6w2x~AuL(P6e)Q;TW-YnWKW71wTk0iEigKZ@sjk%b@{O? z`j05PcoHwfC>Y#I=q~vD@L*V^<}Zu|wE$D-v(*%p5q~!s&-Y)ye)S5buKPYEARbAg z{B13>)RTaszN`?-;#y~9${i@or-S>C8SHe+R*6`$bG93)Xl!p)RTUNKbO(qC-RS@k z0ouUeN;dL!KXL*Q$TGj9Up@P0nyjp9pYuo5xfXOTIBD7Npe!Cw@gH8cKZT4a)%sp$ zvc;2MUObeVa>`HJDE^}4(Oi6@oyZ{WsY{!>vp!;|EKnVxRWeskviY^E?R1w>R=nSD z3SHy3Sq@5cz&<{RQQU{_L~dKt`ctP(Im3Mr#O8X7`TxNfxYk#9_+9fGLdgXxXUE6i zI?d#Wqdu(NZ4=If+ztk=*^E{l?afWCTvyJw>e2FA6_TC0=%*7p(~O^jNKO=e^6hq^ zp|FZ0j!v}#X{EYex>J7}AIe@}U59zVJ!iLT<%Rnq>c`j>fdhf#a+kMP?b3F7A6DW{ z=1|3rd|p^;a>yN(taj3&Bpyy|A1M<>9e!s#3~$@G>kbf!Ad6J%s|Sofa+yu0$Mbfz zZT<9X2@u-1UieMQI@}7NeR*FShCaJA@KswE~$Gw;{hnvO8tETFfa}CBr zN+Q=Qw;~2s@XW#cSJcFM2{;}IRClQ{KyL{m?wb^)Ta8QRc+`cq1byUWkZ1nF%&YAKQWFxsF|}KQaiDHBRAb5 z4*AOLr?~p%PXmk2R!dwc&djN^tWfEW7Q$~@o4ic@*;xA7!*c$5J~X6hs(f0Nj85V< zeQ&Yw5S>@@o>japI_(*%yYe+UWz0E`oAgE>O$oma6ld8$f~XG|Fc8*EMyDS}wUpQ| zi-g^i#u$>8%unx}UTaNgqnz#BpT}=kOzYivh(~>M7Ko zkyao?EfMq;9Q^spUid!2(Jch0JN}&Kf6{(i>22jD5RuhQiC4agQg#%+JS$&0QfAY} zb>S+6;ZaGgdl%WvacIYNfpMIHF=d^6#Zj@7GGl z4Cz;vx;@5D$R+`)r{+?U${y3QT1b)%6&{oztKeKO)J0LOG0FkD%6d+(uQ|)9yGgncwJdO=Wk# z<8yd3RX6}k{6Nv=;VDyEPOr{Y69=92au@;O^Ud_w6vEkk=f}D z7RKkiz4;)FEkp*1;nJMgPn5G-tr=B0Ol%B@pOVoK>qPD4vP&@z#%4_qIONH?F{+;PYAEGFRX-Tx-{iCrI zXtA#;CJ;s3{oS-cr--R5YrGglY!^WML&sToYDEfEFEF0tuYZVVB5C%qiFi`h`lZo5 z!f8b^{T$jAXS$AOYy#>fX7#6kFy@x#<*mBM=q8`q_VUJGB$kyhR1L71C^1=0{$MWDgqv6i+v^j>;!l(JDeRnT=~Ye3xn!(=#4DTy)Y*7)`7P#~g` z1jp-XUGM$l)cNHW$-2l5^OD8pE={c1pA1gq#g$yt=ukaPM_%U8C5@80I4a*ez^EY zk6b|{p##5t84U8dg4sj{&7_^QdJMb1iAMR&py4r+N3_Px5fiWL((LwOa!uU=KTi_@uTD+k>43)UZ2i*OzNfdL*DbAX`tB?0?f&tX_ms)TvEvtRP9W92es}@V<>_5i%2b33m?o68>QfK zGvkk_lVA(RG3oHUGSS*wCa7IDZU&34PpmhSD!IzD`>4NE1E7Q{Rj2e}PtE$Wv+Va0 zGs~(13EI_;(oI726wY5;kVA2Pxy~O1W5UgnzeDC+y&nV+n~2pdXaShpQ_69FPf-Xn z*yl0(IQvl{Cll%2k)r`;NzZ;I>V7pb`7p`Xh=a8?cSYBgp)>t@Nh?||0%z26{EpJ~ zLjY7BBDgM?*041n#)V^gjAx!qrEkb-U&Q<<#k6Pg$CbMl^A!VPZJ-JTt_uip0fi~_ zAa>sdPH8A?Fuh6K%mAMUaR?oN$Uoi?$fLgiPIBbn1oVOHGOw~mpGDD&T$TyD+ySyz z$hl4K!}uj2-P|M3zt74T$ccw#TOuqcs%9(6mHS6ub-o>y^5#CRSHFF4$@jEF`R0bX zf?r~pWN6}ZL_RADXd~C1?@w5ic92!x`q$RaIoXg^Qhzao0(@PkoTIAPC#pl^!Zn-v z+Xw0ZyS6Eb1`4}n)j__D){B`&Uuux*ApyXKP$UbHMxZ`G7QoV|!KLLDy>;s#HF^;g z=hOt82m^4F?4ei4M7}DJKCg9}l-UA~bDje{y01M6w*VLtu5u;wgU#ssBghbfSN$xw z6cABDi|Z-AUci$ZVk(J@$#E--#q&4N(r3=7n9M{048sgWaZY#6Ch6L_VJ*e4)RW8B z)t|XKDSlCjcLJ5uhs&A!7lB6B1r6fIhvWUZAJQtFr>O2qNOyAsXh!QACYb;^Fh+PQ^ zjS>IS6df8SKgsn&{XN?t|Mgpe;|jm8OG*bUN|(V9izGoCeLdh@`hz<^b3PwxAYB~N z&C%=oU}RB>J+mc9GCF_xwp!+(MR|vk>Kk5SkW>z0Pkv-Li~c!20B8?g=s+`=IJgoe zc1Q%!*m`bHffUOg-yJm7J)6EfG&%EUK2%z!ketE8x5APUu#;1$1k?A~Nc$vygTmt0-})nvJ&4Ay`Qp&iy1GAPUb5J9_9Pd6DZVT|^wTtSfV z&bRMnYjAI7{&>8o(I|35d6?crR}|utpC;2267ZeCT(eu>qFULR~tzJKDioNjDt~J-6#O(w;b+Vptyx?qop1? zJ1`J~=zZ819ju-kY-V*0f_sLYEMOjZVV^XRfbIt9S-%oNWi<&`N@NzdYlRU9fbzV_ zEG5}e09Khlst=}tzI?wXGyth<0Vfv(cb7p|Iv|6w$;zFFrvI`g!+uW;^I336 zUoZs7rL&0|kbmprM>K(ja4qG|4~oDd(GIXkRD4Z#-Kx~l z%x${tx|;;Q*T*WM>B!|gDhpF;A`@uSQP8?t`8GqPk^ckq^9tOL-cxY4!`j*+3x19c zw552bzu8xFU{HY6Gz}9xQiNYz1{JLQNdorR;g(zn{b(<2PkvU*HK8@|42S!<{QwM> z!rY7*{4C$XL;R)iiOsZM5o!ckB9nT81!{cwPOOsG^!)-HIUD>JOSu;ZfugXJVY!w(=E0}1ZOfhn}@>>o2wetT%pK4A*M zFWyN4+^=oWr^WU|1^OvDmpkSeEW9)MrQ8zR4>1^RA`w3A19&RZ6u=zvEH2v@<@`@Y z1^O?@%D+NXp#Orb{3}ESV#58~vhuGG73jYpEB^{nf&MjWU;c+yFHV03^c5U76`b-$ua#>7OIf{*WdA^3=RVm-H0~V?Az-uflao`vQ zndZy~JI#`{^a7FH`H%Rp?`=1fd(jMKrx^L4h8R0{ng$T74W!A*6nC434sJkY(Q}eyXj!fCus2$ z-tthu4ZKKdmzY9lnPDz=rxy;|*zmlChB2^nX}X z$^p76kPTh~E+SZe;FS}={+$YohkxFtxDyYa&k)qdnkK-Bnit@+VQGcNU*FHi7yt<4 zKU{r+4;ph0y?%t93zqp90zr+n7<0?af6zq)zCVXmzWo{byqNKPT*O z(gm--|A))%KhOpJU(zH0-3f#KFX@s0K-k};>(BiECkpbvOW5C}3zBF0&lTifgrQF- z>$jd1ET2Rzru*;G)Z0bhundjWl5UIAcqrdl7vFbHD)ifpq~Uj5T<8`_%2L2!TVxVUZn%;j-*g;a;($lVo=Jebc56_H;xhoCwiRq_axmWe8w&tK+vL&pwnnA=&P3LXlOY@+#esdF7`z{w&GnDbn8o5y=PEtTx&VG*7KD>A ziKq1tCLSiHg20Ws@mf%cc*6D%43|O(zl+Yw_lww`6D~7p>8>thZo$uO5xzEe$6Tpx z3=t|3yIy-fceQOB1qmzVlln@`dGvPh6Oqry4kS_ZH~}D}R3T8!k@9{&JTen13 z(Jh}HSuxSG3)hi+8ww?RIv+%gLy>7_#o z6iT{l6xGE(_B*fOG_8RLrHnp|yvsvB**q0bsmV#I+uMHdM+()Ff!J)}V@IcfThdp$ z2Kk56r3&4(=xueObIjXyE1mWAzZG*l9U8GXqt>rcFo6*JBfMn!LT9lv=|H*B+W2mh zU{P!|D=mI;hT+PyKa5cJ_!;kB1Yl&J#BXSQZa{EuHTlMaW{4hN$VmJsWAHOBLF@S%QLQeDS79Lwdb`}k>)B?FklC19`Zwc2{X7QQ z#K{vM1<@a0-!mD{R$a5)tvBnZVAtDh`o?)w9bl%vz;;sCtY}c(w4nMptl9xW92JCan}!1$rPcIJg5u(zJPsXP1U}dTpK~xCwr=l!*5Po#o2kzON`!U6N&b6wSV3 zfuTOnV&B>*te0zQe;Z$X521r@HuIWTNf9B{FOZqlcDqHy`YOz-+kjPu1d96bc!IT?djEPXoWc{ ze?JsL-k>BSc5;?ztwZeN+vc8+3oD8brxaNlf88`}nshp3n81_Igmv5tz|-@t>=CeL zWcr#z!_On>Arn;nIFIYXLRqSm6A9|z*7C#8@ei!^?m4A6G=xBPmbFH;?hFYHGTgFC z$0CQS9+k{0 z>�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 From abafc5d00bea88acef9a223e0c7f2c4e65d4208e Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 15:17:03 +0200 Subject: [PATCH 07/10] add rule detector to parser settings --- container/config/parser_settings.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/container/config/parser_settings.yaml b/container/config/parser_settings.yaml index fb6a52f..3a0dd84 100644 --- a/container/config/parser_settings.yaml +++ b/container/config/parser_settings.yaml @@ -13,5 +13,6 @@ engine_autostart: true # Output Destinations (where processed data is sent) out_addr: - "ipc:///run/detector.ipc" + - "ipc:///run/detector-rule.ipc" out_dial_timeout: 1000 From a2e4cf406a9f78f27f69edbf5cdd2d64f3ec9506 Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 15:33:04 +0200 Subject: [PATCH 08/10] rule clarification --- docs/getting_started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index e88b4bb..3feff02 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -561,7 +561,7 @@ If you check `container/fluentlogs/output-rule.%Y%m%d` at this point, you'll fin So far, `detector-rule` has been running quietly next to `detector`, since none of our requests matched any of its enabled rules. To see it raise an alert, we'll enable the `R001 - TemplateNotFound` rule and then generate a log line that the parser cannot match against the template in `container/config/templates.txt`. -Edit `container/config/detector_rule_config.yaml` and add the rule: +Edit `container/config/detector_rule_config.yaml` and add the rule "R001 - TemplateNotFound": ``` detectors: @@ -582,7 +582,7 @@ alice@ubuntu2404:~/DetectMateService$ sudo docker compose restart detector-rule ✔ Container detectmateservice-detector-rule-1 Started ``` -Now append a contrived log line to `/var/log/nginx/access.log` that doesn't match the Nginx access log template at all, for example a line without the expected quotes and brackets: +Now append a contrived log line to `/var/log/nginx/access.log` that doesn't match the Nginx access log template at all, for example a line without the expected quotes and brackets: ``` alice@ubuntu2404:~/DetectMateService$ echo 'this line does not match the configured nginx log format at all' | sudo tee -a /var/log/nginx/access.log From 54cc544fdd9ea36a0a4f32424832aa3c971c537f Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 15:35:04 +0200 Subject: [PATCH 09/10] default rule clarification --- docs/getting_started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 3feff02..88732a1 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -469,8 +469,8 @@ detectors: - rule: "R003 - CheckForExceptions" - rule: "R004 - ErrorLevelFound" ``` - -With this config only `R003` and `R004` are enabled, so the rule-based detector stays quiet while we work through the rest of this tutorial (plain Nginx access logs don't contain exception/error keywords, nor a `Level` field). At the very end of this tutorial, we will enable the `R001 - TemplateNotFound` rule and deliberately send a log line that cannot be parsed, to see the rule-based detector raise its own alert. +By default (when nothing is specified in the rule: block), R001, R003 and R004 are enabled. +With the config above only `R003` and `R004` are enabled, so the rule-based detector stays quiet while we work through the rest of this tutorial (plain Nginx access logs don't contain exception/error keywords, nor a `Level` field). At the very end of this tutorial, we will enable the `R001 - TemplateNotFound` rule and deliberately send a log line that cannot be parsed, to see the rule-based detector raise its own alert. Now let's start the pipeline using `sudo docker compose up -d` and send two valid log lines with two different status values: From 5ba98117b367e2656adb3e2d01074971172c7bb6 Mon Sep 17 00:00:00 2001 From: thorinaboenke Date: Mon, 13 Jul 2026 15:39:20 +0200 Subject: [PATCH 10/10] add comments to rules detector config --- container/config/detector_rule_config.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/container/config/detector_rule_config.yaml b/container/config/detector_rule_config.yaml index b212ed9..148b715 100644 --- a/container/config/detector_rule_config.yaml +++ b/container/config/detector_rule_config.yaml @@ -2,6 +2,8 @@ detectors: RuleDetector: method_type: rule_detector auto_config: false + # Rules run independently against every log line (no training needed). rules: - - rule: "R003 - CheckForExceptions" - - rule: "R004 - ErrorLevelFound" + # - rule: "R001 - TemplateNotFound" # alerts if the parser couldn't match the line to any template in templates.txt + - rule: "R003 - CheckForExceptions" # alerts if the log text contains "exception", "fail", "error" or "raise" + - rule: "R004 - ErrorLevelFound" # alerts if a parsed "Level" field equals "error" (not produced by this Nginx setup)