From 251da8d873ed36213c194d184e0589c40c742391 Mon Sep 17 00:00:00 2001 From: Robson Oliveira dos Santos Date: Fri, 14 Jul 2023 15:34:03 +0930 Subject: [PATCH] Add NFC library --- libraries/nfc/examples/SendText/SendText.ino | 27 + libraries/nfc/examples/SendURL/SendURL.ino | 62 + libraries/nfc/examples/StartApp/StartApp.ino | 60 + libraries/nfc/keywords.txt | 24 + libraries/nfc/library.properties | 11 + libraries/nfc/src/NFC.cpp | 158 + libraries/nfc/src/NFC.h | 152 + .../nfc/src/cortex-m4/libnfc_t2t_lib_gcc.a | Bin 0 -> 3053302 bytes libraries/nfc/src/cortex-m4/nfc_t2t_lib.h | 271 ++ .../nfc_ble_oob_advdata_parser.h | 137 + .../le_oob_rec_parser/nfc_le_oob_rec_parser.c | 104 + .../connection_handover/ac_rec/nfc_ac_rec.c | 170 + .../ble_oob_advdata/nfc_ble_oob_advdata.h | 94 + .../ble_pair_lib/nfc_ble_pair_lib.h | 137 + .../ble_pair_msg/nfc_ble_pair_msg.h | 279 ++ .../connection_handover/hs_rec/nfc_hs_rec.c | 107 + .../src/ndef/generic/record/nfc_ndef_record.h | 311 ++ libraries/nfc/src/ndef/nfc_ac_rec.c | 164 + libraries/nfc/src/ndef/nfc_ac_rec.h | 199 + libraries/nfc/src/ndef/nfc_ac_rec_parser.c | 205 + libraries/nfc/src/ndef/nfc_ac_rec_parser.h | 88 + libraries/nfc/src/ndef/nfc_ble_oob_advdata.c | 402 ++ libraries/nfc/src/ndef/nfc_ble_oob_advdata.h | 94 + .../nfc/src/ndef/nfc_ble_oob_advdata_parser.c | 524 ++ .../nfc/src/ndef/nfc_ble_oob_advdata_parser.h | 137 + libraries/nfc/src/ndef/nfc_ble_pair_common.c | 53 + libraries/nfc/src/ndef/nfc_ble_pair_common.h | 106 + libraries/nfc/src/ndef/nfc_ble_pair_lib.c | 576 +++ libraries/nfc/src/ndef/nfc_ble_pair_lib.h | 137 + libraries/nfc/src/ndef/nfc_ble_pair_msg.c | 411 ++ libraries/nfc/src/ndef/nfc_ble_pair_msg.h | 279 ++ libraries/nfc/src/ndef/nfc_ep_oob_rec.c | 188 + libraries/nfc/src/ndef/nfc_ep_oob_rec.h | 135 + libraries/nfc/src/ndef/nfc_hs_rec.c | 107 + libraries/nfc/src/ndef/nfc_hs_rec.h | 165 + libraries/nfc/src/ndef/nfc_launchapp_msg.c | 97 + libraries/nfc/src/ndef/nfc_launchapp_msg.h | 100 + libraries/nfc/src/ndef/nfc_launchapp_rec.c | 107 + libraries/nfc/src/ndef/nfc_launchapp_rec.h | 205 + libraries/nfc/src/ndef/nfc_le_oob_rec.c | 106 + libraries/nfc/src/ndef/nfc_le_oob_rec.h | 123 + .../nfc/src/ndef/nfc_le_oob_rec_parser.c | 104 + .../nfc/src/ndef/nfc_le_oob_rec_parser.h | 85 + libraries/nfc/src/ndef/nfc_ndef_msg.c | 183 + libraries/nfc/src/ndef/nfc_ndef_msg.h | 197 + libraries/nfc/src/ndef/nfc_ndef_msg_parser.c | 94 + libraries/nfc/src/ndef/nfc_ndef_msg_parser.h | 123 + .../nfc/src/ndef/nfc_ndef_msg_parser_local.c | 163 + .../nfc/src/ndef/nfc_ndef_msg_parser_local.h | 166 + libraries/nfc/src/ndef/nfc_ndef_record.c | 191 + libraries/nfc/src/ndef/nfc_ndef_record.h | 311 ++ .../nfc/src/ndef/nfc_ndef_record_parser.c | 219 + .../nfc/src/ndef/nfc_ndef_record_parser.h | 100 + libraries/nfc/src/ndef/nfc_text_rec.c | 107 + libraries/nfc/src/ndef/nfc_text_rec.h | 161 + libraries/nfc/src/ndef/nfc_uri_msg.c | 72 + libraries/nfc/src/ndef/nfc_uri_msg.h | 95 + libraries/nfc/src/ndef/nfc_uri_rec.c | 87 + libraries/nfc/src/ndef/nfc_uri_rec.h | 189 + .../parser/record/nfc_ndef_record_parser.h | 100 + libraries/nfc/src/t2t_parser/nfc_t2t_parser.c | 679 +++ libraries/nfc/src/t2t_parser/nfc_t2t_parser.h | 195 + libraries/nfc/src/t2t_parser/nfc_tlv_block.h | 102 + libraries/nfc/src/util/app_error.c | 125 + libraries/nfc/src/util/app_error.h | 192 + .../nfc/src/util/app_error_handler_gcc.c | 101 + libraries/nfc/src/util/app_error_weak.c | 110 + libraries/nfc/src/util/app_error_weak.h | 87 + libraries/nfc/src/util/app_util.h | 1326 ++++++ libraries/nfc/src/util/app_util_bds.h | 449 ++ libraries/nfc/src/util/app_util_platform.c | 127 + libraries/nfc/src/util/app_util_platform.h | 278 ++ libraries/nfc/src/util/ble_advdata.c | 838 ++++ libraries/nfc/src/util/ble_advdata.h | 326 ++ libraries/nfc/src/util/ble_srv_common.c | 237 + libraries/nfc/src/util/ble_srv_common.h | 409 ++ libraries/nfc/src/util/nfc_platform.c | 124 + libraries/nfc/src/util/nfc_platform.h | 97 + libraries/nfc/src/util/nordic_common.h | 215 + libraries/nfc/src/util/nrf_assert.c | 54 + libraries/nfc/src/util/nrf_assert.h | 122 + libraries/nfc/src/util/nrf_atomic.c | 449 ++ libraries/nfc/src/util/nrf_atomic.h | 274 ++ libraries/nfc/src/util/nrf_atomic_internal.h | 343 ++ .../nfc/src/util/nrf_atomic_sanity_check.h | 153 + libraries/nfc/src/util/nrf_balloc.c | 399 ++ libraries/nfc/src/util/nrf_balloc.h | 351 ++ libraries/nfc/src/util/nrf_bitmask.h | 147 + libraries/nfc/src/util/nrf_drv_clock.c | 618 +++ libraries/nfc/src/util/nrf_drv_clock.h | 298 ++ libraries/nfc/src/util/nrf_drv_uart.c | 141 + libraries/nfc/src/util/nrf_drv_uart.h | 661 +++ libraries/nfc/src/util/nrf_fprintf.c | 85 + libraries/nfc/src/util/nrf_fprintf.h | 114 + libraries/nfc/src/util/nrf_fprintf_format.c | 810 ++++ libraries/nfc/src/util/nrf_fprintf_format.h | 86 + libraries/nfc/src/util/nrf_log.h | 296 ++ .../nfc/src/util/nrf_log_backend_flash.c | 740 +++ .../nfc/src/util/nrf_log_backend_flash.h | 125 + .../nfc/src/util/nrf_log_backend_interface.h | 271 ++ libraries/nfc/src/util/nrf_log_backend_rtt.c | 123 + libraries/nfc/src/util/nrf_log_backend_rtt.h | 80 + .../nfc/src/util/nrf_log_backend_serial.c | 116 + .../nfc/src/util/nrf_log_backend_serial.h | 77 + libraries/nfc/src/util/nrf_log_backend_uart.c | 116 + libraries/nfc/src/util/nrf_log_backend_uart.h | 75 + libraries/nfc/src/util/nrf_log_ctrl.h | 243 + .../nfc/src/util/nrf_log_ctrl_internal.h | 91 + .../nfc/src/util/nrf_log_default_backends.c | 76 + .../nfc/src/util/nrf_log_default_backends.h | 81 + libraries/nfc/src/util/nrf_log_frontend.c | 1583 ++++++ libraries/nfc/src/util/nrf_log_instance.h | 163 + libraries/nfc/src/util/nrf_log_internal.h | 516 ++ .../nfc/src/util/nrf_log_str_formatter.c | 258 + .../nfc/src/util/nrf_log_str_formatter.h | 86 + libraries/nfc/src/util/nrf_log_types.h | 93 + libraries/nfc/src/util/nrf_memobj.c | 258 + libraries/nfc/src/util/nrf_memobj.h | 203 + libraries/nfc/src/util/nrf_ringbuf.c | 237 + libraries/nfc/src/util/nrf_ringbuf.h | 206 + libraries/nfc/src/util/nrf_section.h | 191 + libraries/nfc/src/util/nrf_section_iter.c | 125 + libraries/nfc/src/util/nrf_section_iter.h | 206 + libraries/nfc/src/util/nrf_strerror.c | 161 + libraries/nfc/src/util/nrf_strerror.h | 89 + libraries/nfc/src/util/nrfx_clock.c | 403 ++ libraries/nfc/src/util/nrfx_clock.h | 206 + libraries/nfc/src/util/nrfx_glue.h | 338 ++ libraries/nfc/src/util/sdk_alloca.h | 85 + libraries/nfc/src/util/sdk_common.h | 77 + libraries/nfc/src/util/sdk_config.h | 4231 +++++++++++++++++ libraries/nfc/src/util/sdk_errors.h | 168 + libraries/nfc/src/util/sdk_macros.h | 224 + libraries/nfc/src/util/sdk_mapped_flags.c | 220 + libraries/nfc/src/util/sdk_mapped_flags.h | 199 + libraries/nfc/src/util/sdk_os.h | 76 + libraries/nfc/src/util/sdk_resources.h | 86 + 137 files changed, 33879 insertions(+) create mode 100644 libraries/nfc/examples/SendText/SendText.ino create mode 100644 libraries/nfc/examples/SendURL/SendURL.ino create mode 100644 libraries/nfc/examples/StartApp/StartApp.ino create mode 100644 libraries/nfc/keywords.txt create mode 100644 libraries/nfc/library.properties create mode 100644 libraries/nfc/src/NFC.cpp create mode 100644 libraries/nfc/src/NFC.h create mode 100644 libraries/nfc/src/cortex-m4/libnfc_t2t_lib_gcc.a create mode 100644 libraries/nfc/src/cortex-m4/nfc_t2t_lib.h create mode 100644 libraries/nfc/src/ndef/conn_hand_parser/ble_oob_advdata_parser/nfc_ble_oob_advdata_parser.h create mode 100644 libraries/nfc/src/ndef/conn_hand_parser/le_oob_rec_parser/nfc_le_oob_rec_parser.c create mode 100644 libraries/nfc/src/ndef/connection_handover/ac_rec/nfc_ac_rec.c create mode 100644 libraries/nfc/src/ndef/connection_handover/ble_oob_advdata/nfc_ble_oob_advdata.h create mode 100644 libraries/nfc/src/ndef/connection_handover/ble_pair_lib/nfc_ble_pair_lib.h create mode 100644 libraries/nfc/src/ndef/connection_handover/ble_pair_msg/nfc_ble_pair_msg.h create mode 100644 libraries/nfc/src/ndef/connection_handover/hs_rec/nfc_hs_rec.c create mode 100644 libraries/nfc/src/ndef/generic/record/nfc_ndef_record.h create mode 100644 libraries/nfc/src/ndef/nfc_ac_rec.c create mode 100644 libraries/nfc/src/ndef/nfc_ac_rec.h create mode 100644 libraries/nfc/src/ndef/nfc_ac_rec_parser.c create mode 100644 libraries/nfc/src/ndef/nfc_ac_rec_parser.h create mode 100644 libraries/nfc/src/ndef/nfc_ble_oob_advdata.c create mode 100644 libraries/nfc/src/ndef/nfc_ble_oob_advdata.h create mode 100644 libraries/nfc/src/ndef/nfc_ble_oob_advdata_parser.c create mode 100644 libraries/nfc/src/ndef/nfc_ble_oob_advdata_parser.h create mode 100644 libraries/nfc/src/ndef/nfc_ble_pair_common.c create mode 100644 libraries/nfc/src/ndef/nfc_ble_pair_common.h create mode 100644 libraries/nfc/src/ndef/nfc_ble_pair_lib.c create mode 100644 libraries/nfc/src/ndef/nfc_ble_pair_lib.h create mode 100644 libraries/nfc/src/ndef/nfc_ble_pair_msg.c create mode 100644 libraries/nfc/src/ndef/nfc_ble_pair_msg.h create mode 100644 libraries/nfc/src/ndef/nfc_ep_oob_rec.c create mode 100644 libraries/nfc/src/ndef/nfc_ep_oob_rec.h create mode 100644 libraries/nfc/src/ndef/nfc_hs_rec.c create mode 100644 libraries/nfc/src/ndef/nfc_hs_rec.h create mode 100644 libraries/nfc/src/ndef/nfc_launchapp_msg.c create mode 100644 libraries/nfc/src/ndef/nfc_launchapp_msg.h create mode 100644 libraries/nfc/src/ndef/nfc_launchapp_rec.c create mode 100644 libraries/nfc/src/ndef/nfc_launchapp_rec.h create mode 100644 libraries/nfc/src/ndef/nfc_le_oob_rec.c create mode 100644 libraries/nfc/src/ndef/nfc_le_oob_rec.h create mode 100644 libraries/nfc/src/ndef/nfc_le_oob_rec_parser.c create mode 100644 libraries/nfc/src/ndef/nfc_le_oob_rec_parser.h create mode 100644 libraries/nfc/src/ndef/nfc_ndef_msg.c create mode 100644 libraries/nfc/src/ndef/nfc_ndef_msg.h create mode 100644 libraries/nfc/src/ndef/nfc_ndef_msg_parser.c create mode 100644 libraries/nfc/src/ndef/nfc_ndef_msg_parser.h create mode 100644 libraries/nfc/src/ndef/nfc_ndef_msg_parser_local.c create mode 100644 libraries/nfc/src/ndef/nfc_ndef_msg_parser_local.h create mode 100644 libraries/nfc/src/ndef/nfc_ndef_record.c create mode 100644 libraries/nfc/src/ndef/nfc_ndef_record.h create mode 100644 libraries/nfc/src/ndef/nfc_ndef_record_parser.c create mode 100644 libraries/nfc/src/ndef/nfc_ndef_record_parser.h create mode 100644 libraries/nfc/src/ndef/nfc_text_rec.c create mode 100644 libraries/nfc/src/ndef/nfc_text_rec.h create mode 100644 libraries/nfc/src/ndef/nfc_uri_msg.c create mode 100644 libraries/nfc/src/ndef/nfc_uri_msg.h create mode 100644 libraries/nfc/src/ndef/nfc_uri_rec.c create mode 100644 libraries/nfc/src/ndef/nfc_uri_rec.h create mode 100644 libraries/nfc/src/ndef/parser/record/nfc_ndef_record_parser.h create mode 100644 libraries/nfc/src/t2t_parser/nfc_t2t_parser.c create mode 100644 libraries/nfc/src/t2t_parser/nfc_t2t_parser.h create mode 100644 libraries/nfc/src/t2t_parser/nfc_tlv_block.h create mode 100644 libraries/nfc/src/util/app_error.c create mode 100644 libraries/nfc/src/util/app_error.h create mode 100644 libraries/nfc/src/util/app_error_handler_gcc.c create mode 100644 libraries/nfc/src/util/app_error_weak.c create mode 100644 libraries/nfc/src/util/app_error_weak.h create mode 100644 libraries/nfc/src/util/app_util.h create mode 100644 libraries/nfc/src/util/app_util_bds.h create mode 100644 libraries/nfc/src/util/app_util_platform.c create mode 100644 libraries/nfc/src/util/app_util_platform.h create mode 100644 libraries/nfc/src/util/ble_advdata.c create mode 100644 libraries/nfc/src/util/ble_advdata.h create mode 100644 libraries/nfc/src/util/ble_srv_common.c create mode 100644 libraries/nfc/src/util/ble_srv_common.h create mode 100644 libraries/nfc/src/util/nfc_platform.c create mode 100644 libraries/nfc/src/util/nfc_platform.h create mode 100644 libraries/nfc/src/util/nordic_common.h create mode 100644 libraries/nfc/src/util/nrf_assert.c create mode 100644 libraries/nfc/src/util/nrf_assert.h create mode 100644 libraries/nfc/src/util/nrf_atomic.c create mode 100644 libraries/nfc/src/util/nrf_atomic.h create mode 100644 libraries/nfc/src/util/nrf_atomic_internal.h create mode 100644 libraries/nfc/src/util/nrf_atomic_sanity_check.h create mode 100644 libraries/nfc/src/util/nrf_balloc.c create mode 100644 libraries/nfc/src/util/nrf_balloc.h create mode 100644 libraries/nfc/src/util/nrf_bitmask.h create mode 100644 libraries/nfc/src/util/nrf_drv_clock.c create mode 100644 libraries/nfc/src/util/nrf_drv_clock.h create mode 100644 libraries/nfc/src/util/nrf_drv_uart.c create mode 100644 libraries/nfc/src/util/nrf_drv_uart.h create mode 100644 libraries/nfc/src/util/nrf_fprintf.c create mode 100644 libraries/nfc/src/util/nrf_fprintf.h create mode 100644 libraries/nfc/src/util/nrf_fprintf_format.c create mode 100644 libraries/nfc/src/util/nrf_fprintf_format.h create mode 100644 libraries/nfc/src/util/nrf_log.h create mode 100644 libraries/nfc/src/util/nrf_log_backend_flash.c create mode 100644 libraries/nfc/src/util/nrf_log_backend_flash.h create mode 100644 libraries/nfc/src/util/nrf_log_backend_interface.h create mode 100644 libraries/nfc/src/util/nrf_log_backend_rtt.c create mode 100644 libraries/nfc/src/util/nrf_log_backend_rtt.h create mode 100644 libraries/nfc/src/util/nrf_log_backend_serial.c create mode 100644 libraries/nfc/src/util/nrf_log_backend_serial.h create mode 100644 libraries/nfc/src/util/nrf_log_backend_uart.c create mode 100644 libraries/nfc/src/util/nrf_log_backend_uart.h create mode 100644 libraries/nfc/src/util/nrf_log_ctrl.h create mode 100644 libraries/nfc/src/util/nrf_log_ctrl_internal.h create mode 100644 libraries/nfc/src/util/nrf_log_default_backends.c create mode 100644 libraries/nfc/src/util/nrf_log_default_backends.h create mode 100644 libraries/nfc/src/util/nrf_log_frontend.c create mode 100644 libraries/nfc/src/util/nrf_log_instance.h create mode 100644 libraries/nfc/src/util/nrf_log_internal.h create mode 100644 libraries/nfc/src/util/nrf_log_str_formatter.c create mode 100644 libraries/nfc/src/util/nrf_log_str_formatter.h create mode 100644 libraries/nfc/src/util/nrf_log_types.h create mode 100644 libraries/nfc/src/util/nrf_memobj.c create mode 100644 libraries/nfc/src/util/nrf_memobj.h create mode 100644 libraries/nfc/src/util/nrf_ringbuf.c create mode 100644 libraries/nfc/src/util/nrf_ringbuf.h create mode 100644 libraries/nfc/src/util/nrf_section.h create mode 100644 libraries/nfc/src/util/nrf_section_iter.c create mode 100644 libraries/nfc/src/util/nrf_section_iter.h create mode 100644 libraries/nfc/src/util/nrf_strerror.c create mode 100644 libraries/nfc/src/util/nrf_strerror.h create mode 100644 libraries/nfc/src/util/nrfx_clock.c create mode 100644 libraries/nfc/src/util/nrfx_clock.h create mode 100644 libraries/nfc/src/util/nrfx_glue.h create mode 100644 libraries/nfc/src/util/sdk_alloca.h create mode 100644 libraries/nfc/src/util/sdk_common.h create mode 100644 libraries/nfc/src/util/sdk_config.h create mode 100644 libraries/nfc/src/util/sdk_errors.h create mode 100644 libraries/nfc/src/util/sdk_macros.h create mode 100644 libraries/nfc/src/util/sdk_mapped_flags.c create mode 100644 libraries/nfc/src/util/sdk_mapped_flags.h create mode 100644 libraries/nfc/src/util/sdk_os.h create mode 100644 libraries/nfc/src/util/sdk_resources.h diff --git a/libraries/nfc/examples/SendText/SendText.ino b/libraries/nfc/examples/SendText/SendText.ino new file mode 100644 index 000000000..dce3444dd --- /dev/null +++ b/libraries/nfc/examples/SendText/SendText.ino @@ -0,0 +1,27 @@ +/* + SendText.ino + + Written by Chiara Ruggeri (chiara@arduino.org) + + This example for the Arduino Primo board shows how to use + NFC library. + It sets a text message specifying the language code, then + starts the module, so that when a device with NFC is near + to the board the message "Hello World!" will be sent. + + This example code is in the public domain. + +*/ + +#include + +void setup() { + // set the NFC message as first parameter and the language code as second + NFC.setTXTmessage("Hello World!", "en"); + // start the NFC module + NFC.start(); +} + +void loop() { + +} diff --git a/libraries/nfc/examples/SendURL/SendURL.ino b/libraries/nfc/examples/SendURL/SendURL.ino new file mode 100644 index 000000000..9fd20c85f --- /dev/null +++ b/libraries/nfc/examples/SendURL/SendURL.ino @@ -0,0 +1,62 @@ +/* + SendURL.ino + + Written by Chiara Ruggeri (chiara@arduino.org) + + This example for the Arduino Primo board shows how to use + NFC library. + It sets an URI message and then starts the module, so that + when a device with NFC is near to the board the message + http://www.arduino.org will be sent. + A list of all possible URI message can be found here: + + NFC_URI_HTTP_WWW "http://www." + NFC_URI_HTTPS_WWW "https://www." + NFC_URI_HTTP "http:" + NFC_URI_HTTPS "https:" + NFC_URI_TEL "tel:" + NFC_URI_MAILTO "mailto:" + NFC_URI_FTP_ANONYMOUS "ftp://anonymous:anonymous@" + NFC_URI_FTP_FTP "ftp://ftp." + NFC_URI_FTPS "ftps://" + NFC_URI_SFTP, "sftp://" + NFC_URI_SMB "smb://" + NFC_URI_NFS "nfs://" + NFC_URI_FTP "ftp://" + NFC_URI_DAV "dav://" + NFC_URI_NEWS "news:" + NFC_URI_TELNET "telnet://" + NFC_URI_IMAP "imap:" + NFC_URI_RTSP "rtsp://" + NFC_URI_URN "urn:" + NFC_URI_POP "pop:" + NFC_URI_SIP "sip:" + NFC_URI_SIPS "sips:" + NFC_URI_TFTP "tftp:" + NFC_URI_BTSPP "btspp://" + NFC_URI_BTL2CAP "btl2cap://" + NFC_URI_BTGOEP "btgoep://" + NFC_URI_TCPOBEX "tcpobex://" + NFC_URI_IRDAOBEX "irdaobex://" + NFC_URI_FILE "file://" + NFC_URI_URN_EPC_ID "urn:epc:id:" + NFC_URI_URN_EPC_TAG "urn:epc:tag:" + NFC_URI_URN_EPC_PAT "urn:epc:pat:" + NFC_URI_URN_EPC_RAW "urn:epc:raw:" + NFC_URI_URN_EPC "urn:epc:" + NFC_URI_URN_NFC "urn:nfc:" + + This example code is in the public domain. + +*/ + +#include + +void setup() { + NFC.setURImessage("arduino.org", NFC_URI_HTTP_WWW); + NFC.start(); +} + + +void loop() { +} \ No newline at end of file diff --git a/libraries/nfc/examples/StartApp/StartApp.ino b/libraries/nfc/examples/StartApp/StartApp.ino new file mode 100644 index 000000000..0aabca21c --- /dev/null +++ b/libraries/nfc/examples/StartApp/StartApp.ino @@ -0,0 +1,60 @@ +/* + SendText.ino + + Written by Chiara Ruggeri (chiara@arduino.org) + + This example for the Arduino Primo board shows how to use + NFC library. + It sets a app message specifying the package name (for Android) + and the ID application (for Windows phone), then starts the + module, so that when a device with NFC is near to the board + it will try to open the application (if present) or will + look for the app in the store. Finally it register a callback + function that will be called any time an NFC field is detected + (it means that a device is near). + + This example code is in the public domain. + +*/ + +#include + +//specify the package name for windows and android phone and insert the EOL character at the end '\0' +static const char android_package_name[] = {'n', 'o', '.', 'n', 'o', 'r', 'd', 'i', 'c', 's', + 'e', 'm', 'i', '.', 'a', 'n', 'd', 'r', 'o', 'i', + 'd', '.', 'n', 'r', 'f', 't', 'o', 'o', 'l', 'b', + 'o', 'x', '\0'}; + +static const char windows_application_id[] = {'{', 'e', '1', '2', 'd', '2', 'd', 'a', '7', '-', + '4', '8', '8', '5', '-', '4', '0', '0', 'f', '-', + 'b', 'c', 'd', '4', '-', '6', 'c', 'b', 'd', '5', + 'b', '8', 'c', 'f', '6', '2', 'c', '}', '\0'}; + +void setup() { + Serial.begin(9600); + NFC.setAPPmessage(android_package_name, windows_application_id); + NFC.start(); + NFC.registerCallback(myFunction); +} + + +void loop() { +} + +void myFunction(void *context, nfc_t2t_event_t event, const uint8_t *data, size_t dataLength){ + (void)context; + + switch (event) + { + case NFC_T2T_EVENT_FIELD_ON: + Serial.println("******NFC_T2T_EVENT_FIELD_ON******"); + break; + + case NFC_T2T_EVENT_FIELD_OFF: + Serial.println("------NFC_T2T_EVENT_FIELD_OFF------"); + break; + + default: + break; + } +} diff --git a/libraries/nfc/keywords.txt b/libraries/nfc/keywords.txt new file mode 100644 index 000000000..c90fe1d68 --- /dev/null +++ b/libraries/nfc/keywords.txt @@ -0,0 +1,24 @@ +####################################### +# Syntax Coloring Map NFC +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +NFC KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +start KEYWORD2 +stop KEYWORD2 +setTXTmessage KEYWORD2 +setURImessage KEYWORD2 +setAPPmessage KEYWORD2 +setOobPairingKey KEYWORD2 +registerCallback KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/nfc/library.properties b/libraries/nfc/library.properties new file mode 100644 index 000000000..be39397a0 --- /dev/null +++ b/libraries/nfc/library.properties @@ -0,0 +1,11 @@ +name=NFC +version=1.0 +author=Arduino +maintainer=Arduino +sentence=Allows Arduino Primo boards to use the NFC tag onboard +paragraph= +category=Communication +url=http://www.arduino.org/learning/reference/NFC +includes=NFC.h +architectures=nrf52 +precompiled=true \ No newline at end of file diff --git a/libraries/nfc/src/NFC.cpp b/libraries/nfc/src/NFC.cpp new file mode 100644 index 000000000..d46c5ed1c --- /dev/null +++ b/libraries/nfc/src/NFC.cpp @@ -0,0 +1,158 @@ +/* + NFC class for nRF52. + Written by Chiara Ruggeri (chiara@arduino.org) + + Copyright (c) 2016 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Enjoy! +*/ + + +#include "NFC.h" +#include +void nfc_callback(void *context, nfc_t2t_event_t event, const uint8_t *data, size_t dataLength) +{ + NFC.onService(context,event,data,dataLength); + switch (event) { + case NFC_T2T_EVENT_FIELD_ON: + pinMode(LED_RED, OUTPUT); + digitalWrite(LED_RED, LOW); + break; + case NFC_T2T_EVENT_FIELD_OFF: + pinMode(LED_RED, OUTPUT); + digitalWrite(LED_RED, HIGH); + break; + default: + break; + } +} + + +uint8_t ndef_msg_buf[256]; + +void NFCClass::setTXTmessage(const char TXTMessage[], const char language[]){ + + nfc_t2t_setup(nfc_callback, NULL); + uint32_t len = sizeof(ndef_msg_buf); + uint8_t sizeM=strlen(TXTMessage); + uint8_t sizeL=strlen(language); + + NFC_NDEF_MSG_DEF(welcome_msg, 1); + NFC_NDEF_TEXT_RECORD_DESC_DEF(en_text_rec, + UTF_8, + (uint8_t *)language, + sizeL, + (uint8_t *)TXTMessage, + sizeM); + nfc_ndef_msg_record_add( &NFC_NDEF_MSG(welcome_msg), + &NFC_NDEF_TEXT_RECORD_DESC(en_text_rec)); + + nfc_ndef_msg_encode(&NFC_NDEF_MSG(welcome_msg), + ndef_msg_buf, + &len); + + nfc_t2t_payload_set((uint8_t *) ndef_msg_buf, len); + //Serial.println("Send"); +} + +void NFCClass::setURImessage( const char URL[], nfc_uri_id_t type){ + + uint8_t size=strlen(URL); + memset(ndef_msg_buf, 0, 256); + + uint32_t len = sizeof(ndef_msg_buf); + nfc_t2t_setup(nfc_callback, NULL); + nfc_uri_msg_encode( type, + (uint8_t *) URL, + size, + ndef_msg_buf, + &len); + + nfc_t2t_payload_set((uint8_t*)ndef_msg_buf, len); + +} + +void NFCClass::setAPPmessage(const char android_app[], const char windows_app[]){ + + uint8_t sizeA=strlen(android_app); + uint8_t sizeW=strlen(windows_app); + uint32_t len = sizeof(ndef_msg_buf); + + nfc_t2t_setup(nfc_callback, NULL); + nfc_launchapp_msg_encode((uint8_t *)android_app, + sizeA, + (uint8_t *)windows_app, + sizeW, + ndef_msg_buf, + &len); + + nfc_t2t_payload_set((uint8_t *) ndef_msg_buf, len); +} + +// void NFCClass::setOobPairingKey(){ + +// uint8_t key[16]; +// uint8_t random_values_length, rand_values, generated; +// static ble_advdata_tk_value_t oob_auth_key; +// uint32_t len=sizeof(ndef_msg_buf); + +// //try to generate the key randomly +// sd_rand_application_pool_capacity_get(&random_values_length); +// //we need only 16 bytes +// if(random_values_length>16) +// random_values_length=16; +// //wait until values are generated +// do +// sd_rand_application_bytes_available_get(&generated); +// while(generatedd)l-&+lC`0d{3yMJ7sXSz;Ry;XI(`}8?=s+(dBJ-h}CX>6$C_Rqg+ zwd&TYQLR>uI(6LKs%omgAKl!lRjW}q*P@?+VVLp^BNqA3pLDo5^Z)l#_XIQlr=M&8 zSf*rlkN;Ock1=E1CyXCD!hPV-aUT6gc)Abu8q54C^c?kX*#NJh{$2RFvU%VVS`LXrGGq~!81?359sJqG>{GFRs1G3g(2g!l9s zh12=FThEabMtF>;vl}d*OWkNB7^-T#M0O|APOAC>QHNef}Tc|0T;sPyP=-|0T=wJ@7w# z|A#CWJ#Iq(fA!?QWCKQyarc}&V939~f4XrWGVWhWc=})Qpb;K}X`JO==YLrIQ2kpt zV1%cK*Z;{vQ!-4I|DCS>IeGnyt6B4hsGs4_hkXo&{GicZBP$Q6Ji3bE$Fr0>gmPtu zD!G&14`a7KXT@6ooE588ty;TwZ8x`Cb*k2_uEpwSnAx&JE0$$3Ip+ScOj+hHj$tOR zkw_ud)YN5}P-dg1Zvnm~euQm>U+cVf8_>3S?RKE;^V%IiJLa{22JMvB?hN`%Ub_ou z*SvN&(C&Hd9-uw*+Py$~=e2(Y{VlKE2lV&6c3)8UymmiOkGyt&&;fbvfuNpw?LnY} z^V&l|hvv11fez1Wj{qH+*B%8rI~uRRBJZeH6L)Gx2?4>~WeJs)&IUONCZFt5E3bWvV= zF=$X;dkN^$y!JBC<$3L3&=vT39pKv8hOjkk{l?JkW_aQn*!uA?{SQDAN3#DL%pO`l+Gp04`Mm~P%!lEn_`*q$-1 zg?9WK7rtN%%Zyy!1(si5G55E)FpNN*U<9_p1eP&I?&g?#EYIZEgnu14R`=FZWcVD6 zAfZA7C^%L!&t@5p6H8U)8NS$iff4G_800v8y~%)ntj{w-M;a-dAXfVWk_CG?M(9NM zlN0p2U%*%rgK|U={kknM7ep*#q$|p?48O7y%ZT}_0xhn_f&Xm|_>%Ob@S`dTj98Hd zvUq@ase{OfRcWw^2{`C3;h-1Ua@78r=NLvR0zYG=)00HTOWKu=K_@k|FwA)AVFQ+# zAgwGVG83g9Z8&C{jchu@ zGJ-r9_g|F173LV7Jh>vv=w;^+j*;Z;lX*s#kF6CLgM8tRz$o(ad$_=Ic0q~i~KEaQ+O3#l%$c5rWaBa4Tezr@nLX3UJ4D0SC`o|7vm@k*l`N7v=%n{I&b4~x65lN>Rm4eVho_sjQ+Tt)GrLblw)d2np+X3T2 z>9-HPe#%}0hEa^QFd&|3eG!zX63{%?7)hzS0h$*YW3+YssSCru)EJBHCt_Y{jNR6? z4Nf$j?E}yGmFT^&$MA8o(7e}}hPKYF5cC5(5pkN@S`hP5JB2p3W-Y*c(wGj)yRUGa#V!V{voc(d zpr2WcOr~G%Sop&30yAED8znG&w&vPD_hQlsXctpqM7m67x;|rn2w6twfwo8jO|#&K zhG%rsNJ9cm6aGUZFuLWWA%S*l;{MWDz3@2lU)gH7TlQ6{9K*NJA4fn(=X?UAFGhFY zDe#ZZh>TPmk1~@__<&>n$;DxH;wH<;6$b&r_dJb9)r*?e>63WI;K|aRFCfSf4Mc`1 zC^Y35d5eXbbyR_ow~~}GH^8AiTH5LIOd&}Q8v)iey*@DEv~n=6Juiujyv-G2T&Q#L zU?RzcA94f1cRfaD_+G<#Msd=lSor88JlIN=`{d=}7Lie^QIjd+KAcgilS9FEgaM;X zYfG+Q(WTtB(e=RgA970(_sNV>BMp{zoNyqhc{UCo@zc7BKt(t3Oi88AXdMFGz$C8J z%k6GVd-SD#ZlrF@;k^O1CD(npKuSYu8(kZZMY|ETEw0CYg>7SM+g$Dc!7~vrQI%y+`bw#?WxJQ)-DOJ9jNW?R%Qui%b%&w4C{6? z0E%CT5m?6j5LYY?AninoGDQP1jV@-D-@lPR?UMDSCgXZ@3(VXps^Izui?#=^r!%1r zDE;Zg{c&-~mDvUb{qlU~6AFMSR`_c-o{^LZO~AMooriOqNKMA|nhobRN%IU3GzbcN z%;K0K%Kp*F`--@aVUzVBjjqW-xB`c$Z7$;8 ze-NV_E{GTw*V4%Alp}=C?^=8V@F(fSU0pq*(2w}rGOnfT<3Pwy>o6Wlx?Z6BbGAI% zWnD*<0_I!`v?~^I$HqXpKt8LYjh-b_&+4L$u3aSm4PE(I*j{c2d{fun$X}EzzoPw9 z5%&?Wy-v0*To(ty=Pkl_aP|BR_|z}xPiNPTZ6UuKi1Y5|x(ZK)@(>Fw)62E#Az&WU z)6mEDt_A&kLbd~$;zS5uLOak&l{kyDDM4~v%@C+`q$+uFE60=-lztqkmExGpmHwQD zQU)IbGnk7)F9qc$9`rm$QZ8kp3%MGjasv5LUaoTO>m zD5>+nOy=TX(?J=Hgh`pE!8$8p?crd$b~@dZ*DukX_1sv%dKqa9D`6as+cvDRB2ph^ z6GtPn18dxT1s&SLT?FtpXMC^|4tH=9apw0qvLZD5DG;*}Z zh|A@N{opaP#?g2!%C^LSD)sUyG9je|8wtIlJ+`H^Hlt*B1gbPkofeePXfS11y3-QE z(0S(*$Y-g@V39E^P1slDpy_*D&8AwLQJ&4hHEO0YI%TCd)XiDSN{x(}$X6KT^-#U2 zcyr$rAiC0IF`J`FpHWs>?tn8#71aKzYOK-)8 zW|%;(Fc^R3Tp^6Yg<6~qO7bE&Sftr(Ql9h#vsh!cE8cOi3DTH-O1@gqEYX-FN_h&p zRAWvlqi!P3GR<2OW2R9;A+-xqAeX188n4ggEB)Z}G3py-CHD(>#qbn9*Jy1k1zd3nV~&~W#|c4mUDDN$|PP#@7Hk@D>rJ`4~zUv ze*YFodIge{rDstac@XSGJD)%gWLbJVevHyvW4y?)U1)<-0l!K-_G)G{=#TAM*$%d`##AYWCjDVLg|n(Zn$@c1%FD-$ zLG(I5qGSJo=tJQuH+mzAJ`X=FI<6PfmUM-R!>AcW55=!aBlx1;X)&776R$O^bHOB8ivn+Lt?Of$u-pNr9*y=Ss@&Y31W>_b~R4*y% z7?wKESV*)cojkl5f@VQ#!b5hR2kIPKvS#nxt}&ViT+n<1MXI$E^8jmQYbD@9vkF_g zPA}wU(K@;cJYC5;vu+^yjn>h=k*T}7I%d_OnvyO-z2~3657%y3-G|tSah)|C#md!n z(RB688nLf!$=B~wRbaUKe1ji-%k6sr;Q!mdhCePq?gjeS1wfxmnQaPhE@bYW&&pcz zt!$nTeJRN(N@GaTdnd!2>njbEwl>_HJUIrqSc@TvuCzw<0^3+ofFUFcxl|TruDCw3 z*1#lZu(65=wMc5kl5Wv*1Dr&H77SCZCIEQW z{vPZfp=#jK$QxtStD1#@|I1IU%FJJ#bVI<}m2qk5K2x9FvuaK+&AVE?6ihCR z^nhwS4m5#au7Riv5t?oEjF&h5xb?)iw3z(oGX6-|`X89fS|h8(uw zdbG7ailNN{OlKnWv=fhGsx5Len2v(%%nWn-{^;DgOAQKyLU^ zKda*R_rL$O|6l&U{9z&^7g*rwnzB4+6pdriS>9~oWwU6=K!Joe3ZicK6U?ZRi|CwQ z0u5J-W+D9j$eXOWEN>S7E*DM5GEh|GUDQ0ag?45LF;tgJgIU_gL6E}Q#qzuj{u^<* zSb@LHI>d6rYM_$HUtyhM`J`y9-?>VD1)EQ-ka!FDYI-sMacsPVAF5&b^Dx6{=LZEp zg0?->n8m7sz&i@%u})Xf=#7iOTMCPXKGW$IE+R_9DnMc=nuk%`L@p;D6%(OYuH;`w zzC>}cT&MFes$}@Pk}sLZie<*GK!u`gi9DAL6;Wxs94<{o1m0}G<&j0Z7xvqH{*t0D z+es%}K>C|2em(mS&v||tmC_n~V?*w#qpVa~w3)KV{ta73(7k7;Mt~82;sO!BQ`x_{ z6nJa7JU6!TqT6(;ewC>34}iU59>7sa_%1sBR)fd!Vl~oMdPNf7JA^lVq~n$sBq{rmqTH-Yl92-m|G5xJ?iV2c>aQeO zdySAStI_d^nY1f(Bp-~7QDjCPB;+n9Nqm0N zA(P$dINnNfA&`($o)fb5Lu!vNLGq*w!hi9g=p9f*6PY4QsC}ym9gB@54;vN|(t#S4 z$PC&*whhur@~8`GgE|v(DKc0Tf2&h_E!G2(d81wVn!Y4wF_cB7^CXh*ctWMmbJoeyT_9V5`OIiI_8g|=y&iFnd?&t+2Ap?3(-!8+3|+7Pn*#( z4XH4^m7|C$)9Lt!2jORIp=0a@(l$Zy3D40GlH|i8C^9d`ljP?~6nCsI`7CphBnMIn zzfaT3$RtGO++nhEnMB91orF1x&AiAgIYPE>)yVb|_F^KlZaaByv5wjY4-md`MZ#pE z>WgvPmfGuw(QyunQX*691hu>NBW>w!WIGnGm_)|A8^sOQp4L^PDAudiB=4I|?X!u5 z%o;$)Jrn6z%a`zl7SWM$(6N(t2P@zu59XP(6l;nL9lyUPZD4*nS}&2L%5}nIU?YW` zQ0wQ5R@AP%iZH_>sO_#@`Rhli53353)g7!_xbG-W*>_ zvF{_~nSSKqI%?P=GXk3ej0fysa4%{T#S<4OsTU;QLSF|l&0KUiR=G?b3Z0;1 z*ih2mC`eW_@s1Do?i?ZOr;_~1Ji>&U$tn&nrf^=`yuH3DAp^RRBnbH>u98Z}g|QU3 zEmAGa9$)D=yavgygps!nl_^R~RLs%0L!|Z9#_jwF()te|?J;ehFQr|vzE=o&HJIc< z-^tJSeiX5_Hus)eOzq~{%zq@CwB2iw&r8}}ZoH3t3fkO~6-vm@Y2@dw=DF!mlH6WL z{-24248wa-%pTeqH9JJ|0p5i1m__o}o5=RXFgh--OY#s0wckD4H2n`c5$&KH>q zwdq)*5Jg!OM|~JIl8)ydk^Ia}I-~Y)sl7d%F!5DLTdWPqo4%)Z^bpd{yG9ZFqwa+3 z`Hql-_0*oL(s43ggP=cMQHf%PJSVFRe-wL}a@v@g_XpY5u0%d>O+k*ucw%3JXQ2Yg zuiYox6WX)zb|cx2*-P@N-|1NIq!#N9^&$2tX^r>4`0nj_DzgZ31!<)M~Ke=$KZW=s2Mh*^a?$XFNyPi(uq0A<4Y8 zB&mFwBsX?b#JAeKl#2I{$fkZFc?wcZWNu;PIZ2!0PjuAqz36Ds=7C2TnOc4+?E3<= z?w1cZ>MJ%u3@IlNZQ1{qJVdd3cxNb<74fNw_?cZ&2PB6TQA-u)aV$UFMQ%8@Zb1oIC%>{dn_tHkgVwVNC2FI__d~BHtf2&Y#t=%= z=d#l%6JN}3ae;mrn@L}1tY=+mAa7$ECL!QnwsBeXb3dzf_W-N)_aLivIGWXZ9K&i| zKEzUQb#W|pJ@<51Q}*j%=ipdi@fbuZw0svL6x&5}X{m~XAum(<803{|S4TIi&2Z2u z4w?#iz1ekvY4r63+D-ZwLA!;W)`GU7)Z?EsO+a=%@0xf2^If2)wD9W)cqx&F7nowX zV^7eoWvLEy6IXnyA-27W=SuAQv>d#6v%3d^nZ?F!2AR$NQWnAxRw@9pk{zwUbQK#< zIejR5i4v|bc6T3;O>B>n5bk6H9|F0H^{<3byV?D%A&h48KS!rx*ou=t4zWKE2ILew zViX|pY;g*dz%HZ}@d{g{G9Xu3IR-wjvC|4dn8v=MdF3fP>v!m$v8NV-d}N30f$kGq zjMnZfHoGpI`Ere{(D`u_-NE^Dn`jsWb55F6R&Y0VB2Wldv^XFUTtR=3&72S3fQwtW z5`#bvaMNgVJ;-%A3WI3w^kWDUIC>#3p5|`!g)o`(?gP8)+`efbcetB#0J+QM!`ndd zA*b68E{#+0{zH7p9YD!n{K(xlLFmoTTm)_|-`!~(ca+-)2v3uHqTg;B8ML&qu`Jzfd&dcBVhK{rgJ%)DsLdyW{(0M2% zo$^x#@w?W|zFIfkXI6!F1?z*Yzh+u;5G6_J+`Dlg8Qc|G^5zP44}feF?gYbpr}*1F za7V?hJwV<{L6p~jmhKmV!4ZQr55kj%z&&|qagS{ofX)SQ>lUK}3ppKS(TliJ^wclm z#*PPiDYx?xTrA^qXm*b0^fad=aQUahAd%CZgZUZmMI^X$+#( zW1%aprUSMZ9}a@yu!s5hVUxam!8iwF5?`?Q*O5onanJ{%M|#W%`Ytp$Q*v_ zUXZ!`FI_?W_)?Donzr8yc%?Z=Aita*)`k2X$|n}{=dlooL44JPKrZDk z(9*t)A6*~9V7@hNU{~;)7lN$hSIh)t6+fW?AUk*;TI6=}Q!~Kr=6n0WXB7WX2eOxM z{}KlK`1HQuuJTE=`Mk!v(~dTWA36>?Z=nF~9%l;!u+WJBy>?rOODi4OiF5%-XaC?N_ERcJJo8)@GudL&|n%e3u@69%@0m*c{k$%t@5_}m@3Q$kiCNW9R% z3&KR9pBKEG5mcJ3&k18_K~EBTP5^g77)qBsSx9UHk|Kmr>EW_){WTz0g>{P%;hK=% z0Nf2B@(jpL;R#Kjsls>@bhm{iM?vli8GB%UPw@JNKo5i~bnXv@9<|qm+M0KTxci5?xnEk33RW7X6qo#5CUnkcr7%eeRihsBpKwbkVRMUz0kfN!hR5b ziU;{5ban$WOURlJ@3u^A%5~fcS|rty%NL6DZY-^TlM!Dgs33OdtcrfH{CH79%K?3lbY$1-De(b{>#r z;uhM?1dH1iL$^Yl-W6n(xPj8l)#4e-a@UIffiPGncB8yxgZPpb(~V*qd}SkU5<~F~ zmKZM9!6$y=W^o~NSH&?MK(2{p^Fwz- zJTL;zZi)^XyP2YXG>~t_#+#vgFRsjl@PoLe86cm;vK9!l#L_&-7jYfUOWEQPnu)%P zSCS#j5mPCno2_%13*w_&c?ZsXb;s<0`01*AgO_=_tBoO?uM4LYIY3t^hpx4*FFioV zbzNvvb3*r+7PWZY8hqU%Cg}Vqb57K~?FHQ#U5BS|eNK1iJGdm>vF`A4LFYw7I9XRu z59B4CL?lJ$+ZxC#x^P#JtGWvP0J*MnZ2<0uZbmEE-O}xA24t$PCoQ*kbor^!aaVT* zA18_rbT4ASJ=A^RpnIZ=jesysH}56LbDeQ5$P3-}Qt*8M^uDK)%u4`;N|M z>Vggf@=iDGILLdQhZT_Z`ku34ut8r8?`FhM{eu$Fh3OB|a0}PZ_Qx4T=>XQgBRoCF@552K#uFn^?|`jeQip`Pw6kM z1TsN?gl6H>`co2wXY{r?5T4aH4FZ>>KY0!2=k>vpL6Y_Mk02NI)&!6ged1}@UDo$` z1n!!?5yiW%=awVTE&UXlW>WPxD*$p=pK=kpdwNGL2p{Urv`9VDr)~#H)9=ehyr=r+ zmk{rTeq}F^mwKKOzzqGbR3v__kEZlFQ~!t-{I~k1-QeuKzHCE4KIn&0mF<&$e`g4@ z^uAPm{i3gw4K7=sj89C&Z~6oqB7E2P>J6l~beR^~SyI{x=zOFpbf@P?JC?z~R|;tf z&QDrP+3a4aY7KDvq*13p4oc~?8b(V8XfPa>7SdMhh-9e?U95x!5A(IOke=J)Qh6$_ zosh~t0e4F3`x?&TrC@TFD3zmtiVGm9YF(IrmbOz7<%^WR1;|(FCrSanNd+ls&XKmzQtmC+3I^vR`{sjg zj$Ate@%-d46IK#``IRpq3*=X00SS=H)55nVf0@VQZLO~r;#xt0X(g#6$L zxRdf@nquSS=T9I^kOS+1OO*SRhqE)XE2XeWawIK8=jDJ7Aj$GdO2jY9Ro$RVkzED? zd0FoJ804C~nI44evMChYE%_-qOO?ZZhwzRZ)f{$rWql!B$oq1alOPY|cE>;-%dO7> z`9wZ=06w3}otFdoOfGl`+zWZgR+zt(>rr3Q|S>oAy}9nML9Hs~ky1;cv1)!W_AFJUDN|W-pL6hWJK+ ztTi~!fUGwhISt_kLt|PELk-OczRPg%4{%Wi!w^9B7#>l)eTM6!f!uExegcqaL$M%`7(?+=fE+Pw zrqbb2gG?LjI715MOUDecx4@k+JU;;BNyCaB@DgvxJPMLv*l`I4XACuH!aa+Af+QJ= zP|@YQVZH&wE!pseUPxRtl%@PE#ZcrsxXXr_bzpbZ@Uk3`*9^y}LwLimg2v!YLprSi zsfO|%5Z*R$4JT+(G6lC1{cIRR-3Bfxq&KR_1xiGAncol(LBs1}H<*Ko%-z%R#qD zDN9!=NGa19WQkISCXZ#xMJgaJR~pc=v_eUscp=K!4LGA!N@3a~tX2XI5Uy3+djhgf zsYCUY4NBBOkd4YfG6+*#X(8IAT+~4rp$y}3u{J9M-+|kvDFb_h>``1h1G!JB8Ux6FWgcC~gUX8M;G&gvOAz6ZQh`d4hm~h-5a_5fI|rSQ zRmK}&cTDMG0XeQ5-U`S`;V5t5YQ_u%Ec z@}xbuWF_Y~kQbHLQ~^m*rkn+LS@9SGa#cB43dn1Ur5VhhDV`F9ZxpX8Am5Y*sfhPo zsa6|Yj&eC25N~6(`7oGeyh-!mY~$EM;CzfXc7x0@R$7G!zQ#fHaQPXJ)Pjn!<8Q6qz5w~o`a28q-rA804GWXwsZ#`a@mHV!1mQfj*K!!lSKXqZTc8#Y zVGy9U9tAE?^`_Tz3)QKs;d+sJVg!(j)#z@p3sRe91Gz-K^ATjJ>T(5;Wom(P;Fha% zPQg4_olV<}73zTN5QeBbYk*s+mYN9UDs{6Jkk#r>v^86!UZs?It=ifJkagQ}5G@flcZ++9`*tJJ4-0LM=%ZqkSs&0C*t`2JxcR%L9RoQCm?x=aBk-19XSgWpqJ~sCTFgeN=6MIsBcVI44y7sMgxYB|yqr`Emxl0^y7>}>@#+!>3=-77D!9|?8z&%%>Zgz3&Zu?U zL3dV7qIKt-`i2aW)Iu~I&#U+Qfm~1zCcs&;`f3KqMRg5rHZQ45_Mw9*>U3JtF00=f zz{?divJnifsveYzUQ>U+4dHdwuM@Z%swXNx;!U-61|YXo&z;~>Rj|4=R25RgY|^O7)ltRADa`ia_v-aMwMeYNaS z)i(k6O#M(5$mi~!!S+IMr4#irn)sZTc6cMEMHp+ABJRSvQ-Ha(Ps24%ANqw&@ST!13 z5tzpPDFtZqYALi$E9OJH+4yFVx0uxrn3iQ}2j6O3EPB^Esv0D1u22#6XTO0sc4|a3 zW9Rea^Op{EZM%#*4nN(kZ=mg=D+YW|w-V6y%Ao>m?{Eh4J_gF=e*c^RdEXf{!MoR` z?L)t^f8aQ%1QquNZ#3dK^89-sM;&a2_Gsr^cph`B3yu?O(LR5Y7wu#JnAaA^8E=ci z&m8s??J?)Fzpey_+MPEH1K1w4#R+Ct)3{o}mY)rL2&<#L#!5C3MLTf~o469Xwd~n2 zaO>FbR5V=AR-z0olzm5YW*9qz>ZhC7o0%Zt?6S)sTUhBdyliFDX&v3hE}9GTNY-Z% z40f`sdx6`2s5J1w{Z%5FH zr)(B|DD#4S_W`<>Y{(pNuh^Eq1Cq{Gi2?V9wT%bKWXsVG7ysBsbN4&8{xCp3vgx$f z|HN94AW#-NeKo>4!UI= zeIt#xMHjllAeiH~fLp=o>7-Y2J1;`GnyWyYy)|4^CD^Uy%Ftcez|~v}vXKj{1!O2U zA_kB!u45;V2(G*UoM!bvM z$Lr8t;!65~OX1YLaCVt1LA#loTzv()TU_fE=tL^_c?DeG=1Nf=;2xK@2D*rm+wK}GWhZV10bBo&sYF5pMMgKKnwW0p)d&G`$*sx z@z)E2EaorWhR+~Afs%tIe8C|g%lWd~p$q22(_yfJAKn?l5dJ&OFRS@pl|k0*vKsNEab|J!Mz5iDFU%}aNUQY|f3H}sSl;ioqu7D))S9Q>x=2zYXGLdii z0Ngpg@=oZI_|`Okp6A!sfZYXtOh=GQd`23ODf}Nae_rNi<_EdLpZx^gb>96F%y01V z-O$~ed<1+Z+C&*A-u1EBL4UQ@+&p0GR;+Q3n6!<0J773?X0^2Jv zDD&PToTl_RQW!{^{Ov-8JwWadmQixBQwR=+FiO}#73@7i*nEWDE7&Q6-Y2x`4RTPJ zH4wsRL81{7Bcx9ScSxAV!}U=ilM;|vL8e?ZP6#gva!i~;vR3cpht zb4|E)96qlL(R7t=2sLQKlPZMM$hs{&F9>o+SV488yTYlj;2sFK!$2MiW9UJ6Buu=B zPCOPq(CYhCSWyeeXF`@4y63|7=|H{^wl)PMT{uw~T!xTMOUP@X6;<}%2){l9c_%#l z0)zKLhhO03gK+vNdiGH$xBWp#A>bB>J=a1PAok3HL7=#Q1h|D_>pJL5 zkeHt~$V)_57wC`yrb4$&>`RmQ3USRo=t9Ja6+u>tkLZ%G65XgWyjGk{8PYoOE7c&^ zi^ZHE8^lKqLBhmdREXJxZ*YMO7r#+L5Fz?L2H7fhssb@KBl;+lh;?IX+9xXP11MaZcX%Tcs#5u|E za#ZY51|(MOy9&bN;)4KiC&blj5$L2is22=Qi65!Fd0H&EAG$=*mGYM};@bp}v*L-H z(47~@oC0@2oHiH+$@pvu$gV_B5$3*R`it%ptlm z@er=ml^+7KO6L**^VK>(+Iz0koo)rldR^6yFxa5GL%XAmy4BMWc9ZVRHE`j&Xc`O= zx`MYM+^n-vv0Hx((Ob>lVx8Ke81-d-Qk{SpJ+QQiDd2xE0Vv}nZX%9%h;=q6Qy@T6{D z0FbA2#i{fXue*K(x$)X0J>Ak3raeNc4*#``L8MzX5`pgM%Jl_zUpK`I z+ymW*wutah7pTDQk#01-%}Udiz5?#4?r~v|XS%PnC_dNKqFvi7oxB}k({*k15N7CJ zcL(=cmyde=R`)R;+&kTz$3VWrnWGOhg3Q(Lr#+XiKAVz!KmFw#2!t| z0`x1pgA3GqY=f7@`tdXu1nDbIguxPh3T+&g>fNeA7_9F{AHb~8_oG!bL?25}>Pmgm z6Oc9f(8tpIE&E_(8KPK{=Nc(!}`OuK#u5NRR@XF2ed#3kLjoG zgWYlcq=68g(8m`9IjNscTdxFt9F4)#`uR8EB~c%{7s508w1*I$)mvzByQptXtNkVY zkqvN`qCfr-rEI`f@`*w<^d@eFyrU;+DQsQJiL~e#mSH@9LZ8 z1G%UFjjBuc^<`;q@jxF$#gvEoj*CE^=?`Pt5TEPsOhqSN=zpVK-AjG-^+0ColW1%5 zR$pKgkni-$ERgqlkM|IM(eKfrFWLHnsQZau^YK)XXlj9-jMQ4~Fl_LB=Vx^I#5g|_cP!`-VsWz4Sk4uBsgF7X)q=Y

f>r4*VHZcB@nLw85IMZ4C!(vnRe_av3_>FuDHe7#{M%M-TBspe7 zm?inp68TvwmI=GB(zr3O`z8g%A;NdbN_lyXRPY8MbLIX|!THL!E&}2wx2+C{zdW6u zqlI!dzVyWFueY>JES3{#xCP1AmV+#l6G{TPT<%ZKg5}nyKvu}7s4y2I2l#?pCl90T z+^? zQ*h5^S8tFP@&cN#)8+LK(cKKWd?4EzL7&|EWMN0cZARP^6BB=KFCWJBG5Tx1 z4LM%{i816H06AnB@*KjWhJo|YiC9AprABdv;&h9T8K#$n>*I#^!H9RtQ1Kar@rK(p z+!72mM*?};u#V~)iG~|hfjno39||tX(2ois=M9}2fm|?*ISlh;!n#{uH-wZz^c#j$7YOef-tPc+&+v$9?)MGXx5D6op%fL{pBi>gN7!eEqHYjAH&lBH z^1={DiRVkh!@1DCGKAHKFx^nC62fK}DnAGJ+7J;7$QwgTnx#G(ejftvlcCH$a9M^` zgMj>OI87787sC}Qy=NP$6$SZfm`S^(*-92I!aj=EX>fCtj$|-bSw@MjuhNkU41S8| zc^LRB+d|OmdCK?6fXr8_HwI*Za-tiALCT7T5H3*~Hvn0#oGuPsuriY}|20aB4iK(Y zBB>U%P8oO=KG!SRt`LSQlWElsQ;y6-^i7KKCvf3PTgts7lqK{sVY^c24um@tZ+d&Q zQ!x~UmtD$q5yJfnPx;vaB`XT?4l1qAz%E*;QxPOa*+_ZFA*EX;3{ELV%9G=jzyk1+ zpcIP&IjwY7Fs&sjO}0XJPPtqX&XN=%8@lt#Od2m2l$Iqxl9iz}3tv?RQYqq^vXypc z*OfVw5%z{M%>~>|w3FJ}Yh9(U&iZ{U<=aDPL$veOC_m zM!X!Q6$hcWvCTUWA7cZ`g60@^wt@Lv;}&}Ge2vGR!^=ElgW@olZ@j$;@fH}TH-Ip} zc!Tz-i;TVLky&iKO_@%RvG@}}mKfD6=$0Ex%z)2eqfGn$6-MJj*o7E3(gVBNSc+=Y zYmA-;AY5xa{4<2>jD3HF-A3aFBYcJ$zi=R7#>TIJ++w-qbX%PW>k~l`nYj22jL0hiS~#PZ(LOy!USWB z65vi72hkZN8jVyjJ7=t#0lOsQ{)r&xjoa#hTri%bOy`R65fz!Q8iRj^vunm3H^5yt zHam~7H;mOwL6~a%mI2{yW71o2cZ|^u!QD0L>2BULUT*Pkd{7n7Fz|P+k`zwU=OjBs;3NT%*2@+`f^;hT? zn#6+evdA=#N^FZw^E)HLQd8>=fGjg@pharAsTeI%!KUl4z^yQyqRDub$%pcS)uuvJ ztyp8~NN*|Eng-LyQR_@^l3=jWban=Wp{7-o3x}CDyhoQdnbf<85N>)!rH?J9J+v{} zYRZ2P!fmFbejt&i&Xl`tH(ebEvdffCIni!Y)xB^XW$H>(>>kt0`S7{d)SXIm2TYT0 z0&>t)jAn*tQ^{ix#+Wi_#XDrW@C~}7rXExph&8E{XT_O%4g`11bSe#yw1!F1dVyJS<$a0oA&b{+z`WMUqGq?p16fm}8H zwiu9WrhIM)blp^|E4Uk`gA2gjH065+a@$mf=CeDde(~V$no3d@bk8)A%F*{tF~7jz zk*U%_aF0!?1JL;=ru0CNG}ELzAWu!@XtH`?8d?o@FHQGsFnDEZPfK~aY1(XX8K%zcZa|4DP+@`a*>LU|K*af0juX4&i50%4oR$VtSYfVYaCSebn*Q zw2U&j9MjuGK)lV)9tbA*Ms<)@6kEEW7MoqDXc%O6Ujwqte60+S%gx7*Ko@Mzrp@vSb33Xh zhnSaGAY5%;_8YvcF&8@lveqoqy$ZX0VfI}NyWQql>i~%|>x)9S#~gP9uJ@W% z50HIkqX=ZIxpWG2ac1>9kn!e6ze1Q`_M!b>lG%ec4(H7cmciL&b9H*+u9%CtgWNJd zq2xK$95)e=2j&_rVE52mn$nm@=F|}&kIhvX=w6tcYzFeBxfk8IOtW80khkV!nkwI! zXVN%(Z~itB!Y}5GZxCji+m1toujU5y`P?`2@bz%^-MpTh<(R|1g7bFSJsV_}i<=9` zY?r}Q6ZdynPKm@kmkHxQ=DS2tU1Nbu0ZR4)T&nj233Pce1;T|cw}&C#GM8PUKrVNY z>1|T5OBNLvR=DJ&&3T9m!veC>rTr3c>s|U>2if2NEt3ayga{WRFYWW;ly>@uy`s#^wGMkT{ncMPPo+rHKIs2`=?E zgPeBRNXzGWmqiC)cflpLH+)`kIY)WVRhRr^km@os48q$kf3WcK(4}$)$Rn5NM$1cPU0+Waha1_5cQREx-1Fxv%9k zZGHkQOA#xkvAowplzvV6elYqk70r%hHzM_E{Q=aJ}CWM&mQu;++Z-W7*ss21hLK9wXjS z%MMy5j#*sko!fCs%2L>!wg{9}CR#esHvXI?mEI~OS+<=3NwzeniTa{NqHhQ;TSkA! ziCwYmd=2iF<>pxEQY|Md!u4Ito4MfbS<26a?x7{&6OfNAYiY2jS?+L%_tY|?HXxan zfxDo4Yl%FAc<(GrzCifi@`x_-2g~xt5N25}Q=0bK@__1!Uo4GVz)Q9zk;d6q%TelJ zj-@RvDBjk4`CvE88be#C+18xhAU@WYlwq`)xXAk11oOq#cm=v3Yw;1#Ewc`J3$olggwn!bYg#mPE3DBwK|-u6 z55V0FO7BiszjT4YN$XL{Urt$@ z(hf7;8bYt160IF*!aZa44FY%8noSkfbJkNdq>`+gXqiv8Dl~~-w4O=_xnzBP2WOOG zojV7}%hm_|;q#i+LOI`cYa`kN-LM{c3+|@1Zyk_Z)}@rA+_8?J3HPqGIIT|itef(~ z%YEyMdk{Xb#;%6lW9uYZ%AZ*AUKeA@nntVrQ|qMpKt8kXsRr`W`XT|4SJs(UkaX)= z>RE<$PEnlEYisFVu=`-$>;>UR>si_?f3h}mhc3(df%2?u>zGJDzFPNCCF2{uZv**m zbsGv@jx{v}NN+W828fS(d@7K0)ci8M_^ENjAoN#Xj0Tyf#?gvCU)@Qcg)UH^(e`+` zI{ZCIuo@K!$O_dK4A&uQJt|tPR##K%zD5nB_i=00?exLLI&}*b`!}j;ei($RV`(=L zrZ!s#;U=}f4n*Iko^J$$NcEKn;dV7_3WPh<%E=J!RLh)#mngLz?d|udS+u>{tH%4l z%RcomUC8~aK&9Mhb>lM#W7Pi3ahiwJs16VwR*$@e@QC`JCe9OTU#c^lR69^Ya7tBa zyA!X*%it2!MI54^Q5R5ReO4`a5yEq-n?FdB+N~7K&#Td8p}VM_s}FKX9Ze5OidvBd z?Pc{5JpotLZigVerB<#9XQ^tyJw&*z-lYupjv7aADjum-QlWdS-q{B3iTbt}%+u6a zZVt0VfsS%w-*lj>`=Vl221>YFOieN-<|rSy|pkygAcwH;MCzN_4NILlER z(|GZ=jit$ImTf4#-1D)jv1aND~f;38~N z1GwI7b5iMei>;RfUbfn7TS2zjl4ysy+jgrybWygwx4`YOdC^<-y|(U@B<`~XO$B$z z){2(M!?r8*KJ|!Ao(6K%)-4&rleT*z>`vLH(wL986`-d$!S<#Hgh{se^AMi5m8Ubh zV2jBFNw!sOf^)xUQ~JWoHQTDbFt~0zN-sU{*fur*ch?qj5w0KGylA<8V%z@*kS}d5 z3xm9}ZHb01-Bu$G$PAn9FzhmIDKxFUwXHr5F3aXkF9ko_Ts|V+7u&*@AlbInzr*~y zO`#k*$JVA7$XxrD(-8XF1E?D6XLq49^0&v$fNp`k3Qh0nm5H7WIkI9%E4R+!7p3NYPuz#m1 zZ?}CVrP5LM40>(6$3D6&$X>fZm6`+gjj!PBpuN*wAfxTuXvY_052jT5i2V~S$w%#r zb6_58uTO(0&d%I`!3lfep)fyb7wq6p*-OyKinrgOuObrdPevp98T(mJ=+4@EQrYgD z{e>RFOZHAC*rnK;)7*O5&IUnv#a{OjAXn}0sa$)*9z?U$O?zw{oZYf>WRPmVG84kv z_7QEt-LuP-PTsd4qn+#ajd z$m}&}7Jg&5)&|M6A9w@ETYCeV%iq~u-@(~?`$5X3KG}y-!kT3d_XqN`z06XOFLpP2 zE0k^DLyz`%`x_UK9QzvDv3ol*!eKDW@iX1V*$zWFAm=(FCxQ4n3f_ckKS%HJApVYm z6M&rONTju6k>m3(Ad4LxD8UMHthB<}62}Y5g+m;LDYIJXxHKMQm4l&Mwc0U}c0?N; zR@%6SI?6a840B8?0^ugd$|o=ncN9)Upa{nj+Td(` z>~@@{ML){%TRG_VIJ(gCve%Im0Np-Ez0ZiS-%-L7AJj={8?9d<-6fU_fxqzmAVI^OODiFJh0CzNrH-C;l;b41XK&Et-V<3LV3#GW9D zjtVu{XPb4LMMc3(Ko&V=i9$Fuw}$Z$-el=!t{4rM`a z93hm6zjNpVA$;!$?Fr-uM|nDxkB--$0Qv0LmXg4^JneI7o;oJsUFZgQrU0tt6!(_M*ht}OyDTb*ayg4^ayrBXzs z^D5$Pu}B9qnviHb7~ln`<%B9f!ptF_7FY~I2#oJGRAp666BCG;S&rFJBI~B zcf{#?0i8JNbo~h3F=yiqFhA~e(i48dxsS?{C!NjcsXFC!t_Je7v&D1>6P-J#U~|UV zi9T&R>zqw<);Z^C8lM-O9cg?fJEyIM@S^kaB?vD$`ajvDc_^NaJ97Mn7EPon4 zuRH%gj;=f|%Bl-zds${_%)SD%@53-F!=ScUmRg!+U**Ds0b>Gih}GSpa`lK$JE& zJKdFcx( z4Y&=^aXsX=6pyCJr#_Hiyp5aku?+`Rvc=ql!I%@f)QFYZ*#9 zDFYc@b1=dNh8I;fp^O#lKnY{$-i2)=C=rawQuswO>}_C1G3w}}Y+*Es z0b3ci(-=L5(YhVB?Ti|FYc-akqP;GT;rs^>&j@@K${mbQI@otI?y>+$j7uwE+r_A) zFFBcUXc1r!quU2DQyJWPNP8J8wFsTYh^MS;A7d)5r2~vxfv_E9n3YgwFlOCGuuR6c ze*+FNUZjF5n=$uuz!8S&Gf;9ExgSBwW&BB*@li$>HN*26%QN9nz?eXL`EkZeZ-P?D zh%AIu#3-agxR}uw2I&;zQVp1=84IsKDq#%M=2*&@lZPnfjCcN9Zj2fU94Z+<&;qMs zyiZxsSw_Q)@H@wFRl#LE-|$9eO_j~5(nuD!{!ZJ z17n=-P_8n@DTi)iY?uz|7UKZbN4FXMQjE~dC@}+C8SANw+Q!&@4z_lNDjHG;!%c#i z-Hf-V!0#?2*aGNbeCP(a$5`|gnEi|ndimo%!&C`)!03Ab81+>^VGBb3LHZjj8B3K0T;fsJs=Bu4xMlnyFg!>kztO=BD%n9!SVwgYE zJhwAbYB5kOGx!FW2~3$9!4jEu|ATS|^I3ODJDF!Gi%n*#X))|(zD|wg6z1=fA?;yK zrJ6aNnRgB29boR+2RO*gr0g(*X`oe*$;{Pape$yW3Q{)n%NHOWVb*y-%3+?QRi4Y- zm;}gUew2#P`OLjEmjdQk4uTzLw$q7M$XrcbxRXrpVNi;h5f(_NnER>Qdzx9-1}I@( zQ-WE>47~wcIWwvWv#ww+_yD#_rZoY|D&|WApwuvL&|)~pyiX^>d8Wbu$_3`rl(^I~ z4}TB8dS=rTfJ@BhDKEIp45EI>73Lhe*=k@~D9vtUo}~WEb>_WekZv%C4}o%%8CnTt z6Vq!c{F<407T8*t8QB=&4%2LevX%MoL_iz!#a<{onMS%r>|z$k;n&TyyawqmbJYm^ zdYET^1N1R}rk|sqS@IDm_n8+j!|wreBCYuW<^*cg4KXj%>HCQ3wFlBLvuhKi5oYH_ zNTW>6O*pJ&{XkD+AZt7x${^O;`G8>7$ZN2zW2MtKzk!vs2b56OCzMNtv6iHPvXM3V zG$yf$_5HK3MX>&O0~3s7T`$F_jbimp0dothm6TgqpZG(HVJ)H;HMX-7z2F|p!n2I6 zi#3P7ILobM>(8Wc@_$NvtyJt?gn-sDYBqV$fRJ!^)y&cq;2_I^6cMrqKbN z#tOH=wvSayx#9sY}Sqs!92p^^#XEO{XEPw zmsR5r+fmkvJ5c7c9?|+NV0ljl9B1XzQCG<7p(MD7b;ljSido))a5%+!eG!zWSzfe} zm9Uz3!dA-4qR4r`GLFWV7OrD|wF2%N*u`H#3S|#G1~ZJE91qyYW=FswoE=CV zmk9Pd3*i^Z9;RG4iv3(MDA8=DKis#mJ!Sx6*b6UWp4-{}RgmJ@PfUiCz;;o0E0JAD zXVnh&7h`~3>>AoCli8os)4!YjEf)?c?AEVf+si(#fHIA3HDSDc?B)V6_p>`=0SDP_ zRZwQIyKaM-$>!1lc8J|e^=&pgX%3V}*#BD(DTn=bAEM;4nY5MUvD>MV%4bhEAW8vS zNKejjHk)ouPOu+;ftV-Rv6X;gcDE9Kr`X3|1C+95voOLLw&O2A8N1~)DCKNj9fDP{ zGs7{^S#~%TRMqSkzl2o7eq$~u7uf4NpsZ!rp2a{H*f8WhY&R zLkD{n)!LoxfrD`9V!v1mhi-Np6`}XoFHnQFmmQ^q)W>e2W==ml@_SGQ*!w4Af)Cl3 z{)FEk+h-D#Lu@0RRU_gH*XS{OM-HL{aH^|&`2mp~cLv4=y7;Jia$$QI6PR4Qym zXp9ienN16N8z-5%R0*6!dTtXrKU42%2WRd|%yTE_+;j|^%z26S*4>;pQXr*pN`;{8 z;T&EHNaMWuF5LHVEOcD&=Ugwx2K>OpdZ0%0nC_wfhcpzM;J52xoTz zq#TZ8Bbd1yD^)f}Ijhx(lFvCj3@G62*23>N=W}WZ7jjBxfpU^FHUucg7fXAYGn6`Xb>9L{nc(W6$)F;Vqd!||qO)j7@=`(dl) zoDxF1$oYK%D0Q5#Y5T3`>;NHc_T@0z4b9_JC?{c1X zht$ilQW8%<|%GB;HK+iq^}0XU>`#{*&8%k82hKaHD~ ziDCC~KR5?CzMfW+LX=ph{WhexMMAV9B%cCpd8~) zquYTzZpl(e`P}S43|qh*)B%dPk$=K=f;+1jwv*g7eE1b}cTo{q!i}d>w3K_5UbZ~L zt)q*FGVWn|A-0lh%YanHeMnpES#H1&fNHKQ2{F%e=S{){FL3{oBT6lI%_t^+k^A5e zC@*olXhmM;&iOAr=T6v-U=7?ER{=M<|InpN6L&wIN4L1`o8fSqdn6Ju?{F1%1Z(B$ z=|pJb-k{IW&fV$*sf&9!AIxsM5{4SI`5T}upQuyP+@eC7fjoI2JgBLl!tkNm!Zt! zO%{Qf%}b{Z{s?b;14hW8^e-fwi6 zp5$$r3x{IfzK=mU#jBnU%4y!nQP|G#7N>z(#v7%6YdLQo{RM*x-cLLzD|xw;7FP2r zsUWW5J$w^U&hc1#U^~ydLVMi>-W5vI>UfjC1Erq#I-TK{c-nNpWnL;RjVrwG6@Y8J zgxBHM$m=hH?KT$O6F69F{ zc+00@@|`?IIbwG4QV6H@`0dE0@dDCbo8sV+q3kcws8lYUuAEg2ykbhwkltKKZ4EP1}d+#F3 zdj1WndN%MMg&}k(zk^cgFuvOaIBew4que{3Uqn&_Kl*d{Me<(^g%rh4pb~Kl|5ys7 zZTu@AK#Jk#iy&?1>uAx&@=bI|#qn9w5han|NjDrj_}{OFZ72UtN^O()KT@f%i(h>c zQVRbRW%qmdN6Vm0<)76e=3f3kDR58YKS@27bpCfUF^L0wKJ8!!`F5(#GWhRP9hb=; z_zSiy{tK~i$mV}cCE^kO%sNnV_%BkDk;_lK0c9S4W(cHwe%Av?1^ka_>pIR?OW)4lr?-m>W!Y`&-H=ddA>3alnZ>;4A^S<$rjA|3V$y>Y7KnpCcsVpKa>wP@e6r? zRz8RNZf*SI)aAd+w^Q!j!#_k{!2|vo>I)C>zoyrN9`ajh)`R>7Uqc$^-)e%x2%pyq z$|yhOGHhe~dJ({1u=GO=8z6Xx>f5z~bxcqK1wYYIuugEg3APZy`T#^(FUX=)Y=a=c z1JXvpV>D31X?|5icnH0+bzslXNq(Q!tV8x+KBeb71ZgEZ7QVir}Rfz#hRSYS5+%K3WWE zuVB?FQ1%PjJuy(aAjt;X0YN$SN)8H+&>B1>$fYm&u%PWCq%47;7*e+2E!s_U1yXW2 zDp(qVD8~d-D?rH;bRUN8xZsl>%&buG{VG6_;AzS#PY9l(WAKz<30-@f7A$%S4kdyB z${w;zUOg9OJq<~ujEA>2X z3%;NSp;;iF4Q8ugkoK20!TAO_vfbg4t071g}>jA;SEO+>=69#kvLWFOc0HMNGI+??SXWl@RjY6Fc%1y!q zIwm57Ki9w(Df}uO5GCA5{hlqtnSo$#6D}M8Ge-D?9ph~mvYTLw6-Lu#Z~`vXVM`QV zoCj%#Fd`Pgb_#E(_bHW4tRu8y=l!8ib8hP&EpZ*1_Sr@W&ScH-ul%7Iae>|0djT z3nx;I*DTBlhOI@Ia{pj<0@lJeF-k)#;Pbt3HS@dN(AY8QUceqE1j88$@BAS$q@wSR0=p>02eMci~7ybJe{9;8mdJi{FR7gj1 zyy)sHpzIL!ZG&>B=(E?MOcK4A3x{2zgLG%JR}?gem}#PqsDj=nI(QSGcE70MB~a2u zeC2%1Fnj;QCW6P6iD|LjiS~wkZy`5(KFU08m0w(OO)sfxGfSL z1EpCMYeDE%QSLG*+e9}`1KLFm6uLt+L^m9rqHrpx?uuTb0<}lPrrPkHXh|zV_lh=e z1hY?cbvtYiMBh*uKOmZV2;)5z3D1BzD6&yzJS6g>uV+N0p`U+LH2qg7$3%~*q4-$T zLVM)6=mzCWYsJsU0RqKEhrtXIe@f{{u=u-Ga9AgPF&nlGVoeF8Q1L&}fH3i413RCe!aZMn@H0T6`14tqQjvJb05~BoScDNyiW}BJDi$xM1zjSZOy5hXICnMR zjQFLC2v#OuMtecI_~dOUtHkPEV4f8}^#Wp6i#er$8gV!68t24Wd%>&~hmS#dQQWx( zP$xc70%g7UlaC->5<96|-yklcV&tlLwjOXz?7IfaMzP%i%5`y0K9o)3NJ`IdiGO(? z!`>Fp567_0V%rmd7V(J&*xJM$w2`%o9n^&F5W8o>)+ts|Z@WwUFBLdF;&oJ~-V?w5 z0)Cucaa;kUK5-dU-u+_V=O7J;SKLRihvG9LI1Gvl=pTd(iKo;f$|JFg{^jMkc=CI2 z@ONjw0f#{MZ|GYJa(`9qVeZbxBZ~MVL#eEP3%tbjwhyXU`fKj1!tdR7PBkI#U^A@`YKP#$()xfYZx_o(BLvfby=@sj6$YAY!D z?qAVkane1i05OZ*OWwkC%iQbQpe%P^8;@WW?ssEht8^csZbg;5VlSk#?oU(FR_i`z zDd3{}J0HN-;Ql|#Mz6ZFhT-1i{#_ZQTkgB*Ui-GY@lP)a^7yMA4qH5ieu4W|4;}*$?Gf4nWxPkH5R?QD6J5dV^w>;K zN0P_QccD!6=%6muUXO?_1l#X1%?w+*NA*hBGCU4Wft2Y{OT|x?$AdMXWP5P-z#-40 z{1PDFV;~8(LJuz;q#}>AE>Ma+(&$k;`1UcF z=RMy01k4K_^LBu8*<(GmDXw^U_=9=PBa2=MZ}d2K9m<;?|D(^)W-Mvp3P>!HqYPo!!O3OhRV4_Pdi;8 z?C@+Swmy3^W06laJgsy$57UI{xJc} zbDowRh;PQy92f%gvS;;wEd|fi|_uT#km;;^@--qb6My?}M^c@;tqZ6D^tW8*JMo zGiidck^)*iagu+ekm4m{R4^t;PLE;Oosx+LxF<>6N}${&F+2xlvSf1xq&<>^vw&2| z)c*nYN`9J+C~1=8^aP|!4u6JV2PB7{h3%kZ4m~Rwk|P{Qhb3QiL&}nTvj+~@lB{Mp z9Fa`Ff+-!9Oxz0Tn51_Fq&$h1iP7^VKJ;@GN`8<)Dw0IJ4BH9Gx8ZOu@F;7cg zrn93&a{C}cmrCBD66%cP58BczBxj{yR!Zh9fUQareGkmDlJoRDoRj==6t?q{nf-`z zK_a$-QY-nFa+`VygNlPok`ihST$W7w8xB__Ve|uBlV~V)Xq0TBjQYCd97VYyxk257 zTaqV#K$P2(>yggdRyJ zE!umMKel1?UdfxEfO21Ql@`haiM0dF0g0R*!-o=`AEZYT33Wh*B^y@4VMG!^-}R^@ zlD4jK$t?Pq{?cDgB4&W}Z7)#PNkDb&)w2oAB>P9ZJ@1rk8}-v%v5P9eV_ZJ zf|p_2FTHpQGfS5iY9Sqv7Ss2cCC#P0HCwvNiI_*E@zi$Bk;YMOlPm2Vg)L8d{Q;Eu zQYj@^1=4>_fa6l`FqDPTy!ja6r1U3B$cv@!)XO_1ee@+Lr=@Q`2W5$Lb_JMa(s~h; z<5BBe8PFj8iTW>%QWNDi*QLJS!0(3iz&a>zN~6S3Hc5-A&S{p; znupO_q@DEJ-jO!dK-nrCiABsdX>uEwozfGxK2GwGbypflKSz&r;UYkv zR6q@sekqp+aIV}BvdQT(Lrqi&EO6|u1 z<5Ev*r})casl5>(`?wd%wKCS17&cJWLSMl;*>jZBhRC+ffO5TTGgT8CWSMv15GqUN z!nR4apZ1r{vWm-?OStUc&q0Zh75ZV=NZBOne{PleP*xHx+e;bsHktf5q!?LfKWy7& z)s;}j%iJ$PnIOyZfHF~bcmhV)AbrG zWosA0wokV99$>#LYzrV=CJhH1khORK4$2n10KY@B=jiZ0ESpAWPL}KwBOqIrUJp1T z^JzlNqq4v0cITMPuLYs=WO@et@?{>OfC5>(5$;7Y?^rmTkfp{zc~bT-)knoL#WW~S z$>J!FER}7U3OFN6oB>;z><@}kF4I252o#`Sy zpu8dD(AjuPmPm)jZ5i7Izh>FWY$#h~0sFzcBg;F7DD5&+A}AfQ8Pvt;l+B>k(!dHtyeaM7Ezz?yq zV}w-s`CkEhPC519z&3Hh3@pgbx6lCte$ zc?VUBr{u;z;c#01*;+8q$k|j5l*w0p1}K;Jj)7Sr_oeTwQodmVMz5BSE(6rac@yAw zPF`k)bY8xnHqHz3UjqPj^3a=bua`4upZ`MekeyCN@p0&q=!gL+nt^6?Nz z*X38PVuTy=#cv|mP5D7OWp2wm*>G=`n_q&mMXsV3^6tpzQ;WM*{_l88WkU|t=)Yx6Gm^TDzgJNSkV51_MnjM=IdoCgLX2lbkkir$ZTKGjMxKwg)QA~&h zY*oynKR6k!2pNUnHbpumi7|@iY%t>#D`>lqSG=SEBq-!_piESxP`6;p14a#S%+-`O!mLe0dI1Esw z2(f{3R&gT@Qng~^$Cy%$;?*!P&nbea>Zw(5=%006R7{~#y-x9BCn)ub6K})el0yCm zm<@`9KY(&o;mL(`O;IO?)TrRoAEv#oIQlF`Z&Czohw_%9<8#1m#RAI9n-w9n;kGFL z{1;N2;^!>b+7%Vl```5u92oRQXQ~Q`K1Gt{mPwmw5BUVD9OlB7I*+Mm7j7zIi$=} zf^t~-T?r^hluM4mmZOw5!D7~K%0f^0 z-B7A&gTJY~@)>MR$~ZNcx0Ju$hI@;$jwX0V`RZ;kTa}-tz}BWbOdqpTDa?nhOKG|X zsayFuy~}u4xt8*RUZrv-q&}r^6)646Z%KJynL`P|L*?x&unj7E>F?nVDUbdGh*dCs377M@W!r(q7CZSmj0^Y`y9^3v3%y z>2w-~s+#Bp<1p3LTZp+?#nyrnu3ESVF(XubVn(Xkg23FWT2398XjQj2q;0BrIuT-2 zQ(RESsYFKj#jE;da7a+SUkzKL%Fzd9l1lI%V3(?qRzb1~EhMZgRS8vDsj6Ey@%i?u z>~|rhsiI~>+NWCc0i*+}AL%uigQ`pQ7$HNoc^4p4^^_Z;WU0Q`2V1tPy#NkJR5!0e z%2B;phtUgE0mV=rSA7%(C{*cPfFjj)IiOgz=P2AysrprrPODDS16!ips7VG*eq9|LZy7=J@~N40=1 zid$8Tjfm2wn%NGiU3KASMCno$I04kLCCd;cSlt~7Sf^e`Mbic~kIwK=wV7@z!qixT*q+o1N`}JK*Qv6JP%qz!VI$QQ z)KiU8pUB5}(dr8-jId3;{#__z)WuZ(Z&#b>@QzoPo`5YuT{8+uRR0!?U^~=L)5E+= zJ(pHUvf4oRGP~6sv`eL^*&cA;t3G)iwlwvZM!-JxOxj}is~`I!%0ac)XOJ?~V>OU6 z)!#n_>5$sC3{kSx|8zn+qCWKvY&q(%5R8|r_G<*>sb5*u6~M&$s6i_sX=>F?U;ZOn$)K2fM)gFV~EnCew}(3chrs`gl<*;NR#hSM}7q8 zRR6US$}V-uFeu&XV{F8{r?%4u*sIQ`)8c;8ZhxAZgw+b+*{__E* zG^Eb>3h-Dx{1^Pj)%W@!`D@-Og%qHX1q0ST^cPVwQCvy0ZtSq-ZnQne=F z4wyBXYD&J&X<}$csnuMkL-C@9O|Qq+X(rJK^_nlDAzjkQ{##%gDW&dLHB145UDKGU zMsL*Y8Hf9I&9OfKO_~$mVT4%H7)eYW|yXsD%u{++YY$j)5sfO>($I;LD{Eyb1{_tniyKN1Dca`fBR5#>;@=< znhOaCHl$e@4C#?(p$g1V&0IR_#xxJ9m;P8&OB>+0=G`dx`D?Gyp%|#G<3kywT|?gaj#Y%1cx*&^DPA1r(I4zz<%v@4IBHlA8#N3|Ru#5|@gr=6id8@n8~ zhDTZN*6_&uUAlBUr6f(4&1``$8!w7qpr=fLiUxAHjA}E2@D*o%W|KfXmwP z>#$wXzDIqV2JI`f?_SkL+`~ZEv{97d-qLP*5zO1#hh4xL)|X+W2@`bPxo)?TBx2JUH@7ASkQMms|HY1^sX>DMly6YsvZnevy1 z+BeG}4QfT*h&iM+QttgoJBI`JVeL|Ss>ZYrX-9di<{{pAcoE&TAB~NoV>Mp*QPn)bR}0y+ytI zC|wU7AzO4kNr<^sS4~N9wC;y(U~bdBM5k!1&X10zINfr3?x^PN76Lqu7 z;g_Ua#s=)t-K7*DS=ZkK+isna+IA_rK`MIp>SodlL20_5_95mzT|G63_UjJb2c+x1 zp}jmqH$?-7OkDyUpNDi@dYlgH=CcqpOJ{I_nWM|Sj9|ICce4RUb*pJ+$8;RJQOVQI zr%Sryy5UbC73yB2t+Gh>lMsFt;-VvPSpX1vp&LF(<*HR`*O5f?d=V!~p7aPjkSm z*S+5Zzbm?r;y`K8tq}pP>Wox@UDMsL!@W`0^EW6rb&VWIO}aj6yxh{I(*nD#vn|0u z&ALRYdRle4bgk2-8~h%j+jU1K0y=btHGocC>|)sN>aO@8SdWfD`R6^|CaP?Db*lZa z_32)r$Nqutr3-)oUFkVY;-QYm01WCjP(N!(_c_%aBf3X)*o^ARXxA9ibm@*xesLH=Z_$?)AlO!Y9-S63`mk=mcD-2xhgiLb5bkmM zSh^jE*Jq01utRTt7RsIakYC`Jq>uU#uuH$4nn%fcMFN<6^uLNArRx9AK(M{~>_R}A zzUCRgK0S+C3J3JJ-Ub}h$I|U%hJGIfAUKsX}zA*;OZ2j6>2z^8!YC_B$yZDaA?q%8X-06zYIXI>v}~sg5A)!=fL5n{?TG6Z|lD#saYTU8lXl0 zuNag&`j8jk*RGe-DcYgG|308oZ(0lJ((~!UqDOz8zQKF?W_}VuTL-h7Q;T! z2hD+UK;KOX!9%^n3~5krqK9Nyznl6YBl->W#Et6jPy#uoe}NJLf5Yl;U<)vWe1f^G zHB`DF1scptL0M<;r+qNQ;2DdU>kV6qLD^u4raEq;;e(x^Y%&Nb>)LFHrS?>~Vdg$a zQHH5HFt-?3N1)tlsHW~@w4wG3NZSpWrI2C`-~9?^oFSH;+jv9RGnmT`!$Ug2b{b~; zf|6u-t`#HfGF+zCc#0wU1Z;Z@U;GG2HC#CfzrBVNpMtXA!2AGGx?$E2@H=2wK~LO4 z!_KDxhYS;_syl2r`zuDzGR&ZJGu!YBz0#3ui2MV}qlQJ(5apQRz2gX-XP8U*{BeVT zGXFwD4RvFR3>WE*f)j=g>Oq_`TvWhz+Tc72slD)*JiA zLD^t@dN=$+joWzei!jdgMzBcZB3cYl#)Fh{Y%xC72WhLZek~|5#slS$wi`R9z!q!d zQ8^c9bTuMMys?&A#|g#>G(w{BApNL2j8#FP>@<4648J5}9{uJMFE3UW-45Ly#B$cPN9s=FGydVO|p=pxo#++XcT( zUPs0->}IbI_W{Das=tI^wATyAz})8LwjEN8*Q)m+ZTAX$j41J57AjB^yiUCbDbcI# zU%(EpFHC?Gua=(?Wsg_sYj8;QY7PhN_4=MV$Z1|@S@6s7vM+>`>9u_mLLc%P+YITj z*TRXgWqIwV_xz7}U8nniJg<3LQ1ZQ&QCp>01$ zuESR5mF5Fwxz_}GfU3OSp9VPVHAo%0TCXAcjV^jMP%%2*=<*7P2XuS=G78&Wuh*`@*5mc#e2j3<>xapR((hGD z&G-9W_kM@%f!CB)%yYnN&I33+^fG-2=BQUYWk_RQ9(0sH_WJBDl;d9eeubaE>4peW zpy^LvNI|AzCMdzC`uT8JXNsi!EX1^PFQibDZwp2UGrjQ;%#EfVs*5+7mQR9mv#B~6 z(pFREhhRpVBIzgIW_mLl?lGn+2BbLC$Q4NOrtqtf5=`uQh>~bZ{}xh`X%!uHyG$Yb zAtjsI&jWUwA_hT8H65d$YOkq@&VV%2>fpgHL*JAVx(}PVIA=5PN z3T#;>Eln`nG$#zw5mVVE*m6vAYJ?m!an1wsOjXIS<(uw*hbRT6brTV~$h64-IALm| z9PFg&UwTrDO~PfMl$k8gV@lXxoQfd&v(rDmD(Z=0IxAvK#4 z7QxnHvWLN;)6`BiUYDt?7Qwnrhp4@H*Ytqi!sszA+K++mnSP%R=rz6U4rQNdP8a<8 zO|f$L-8WsOtoeaSN9%gP6!a(DADZlx*A1FF>3A77#Z#|j#B}R_fKgKv7s19%R_Y)J zdOzet8RV^e5q_cG=OfEnYxd?{eN_ioCJ zW4(>EBICR#7D1Wly+Q-#4(~&BnZMKf!vIK0-t(rww%c2D4x^`d_fbP}kGJd>_@#PJ z;lXym`{_j34tjfx!It6uW+rTz-ap1c%JNQ7B1*P*5mlH+ygR6r&GEL-$$ZQ^{tO_` zyN%j?`Q9UmuoZYG(i@6J-dCGoJK-HO4C$nIfIle3-T@0Bm3aqz2wS=L02eVUyiaU~ zROvmHnil81bKU@4@QynNhg$DgTI?6SKcg!0l6QqS94>oLqz(6qckEs;8@vY^AT@fw zeE@LXdow*pH@wH0u-)|LQzCfVJE#XEG}O4C41f8YRaMS zdS6=!Wv_QUT_X2+XBptuqPpR#QcSsSGqu!h# zxUV%YrL8j1{EH_b$b66bHX-JuNr3g{v3pQ%FtZqN*k~@NCv}tgugwUy+1#E6Wu)1g z17?)D3Ju<8wwOmukP^-BCIfbu=hMH2*lGTSZanhhmnwX$RbM%-*!& z=9>GG;C|HnivFre5xo0?Up%u{>daN3+g z2SbTDiJrJJbL%EZ<>tg^0`Il(C#M=j?>?ym=aZSr^S8 z9D=RR{QFjnP;Y*het=76$A3S+nN6q6Rr4)s;$1Tb)noKV^U1fMylH;60MKNXCBprd z`EPQ#Z9YB(0c`3CeyUhkVeeaq(&Vtfoev;N; zpLqdgoc-n#si539$1cOL56rXgz&2=}`~qOe%>4@^JTfOe32E5OqV~<0IXxcIWAnBb zAdQ<>Q#J2ziKk2_(2`EMO_1eD>U9NM-gW@iSz>R&w!vb~f-Tf?mW%PiEV?F08!f+3 z1`%!<_W>osG9eidY3cn3lqgG_6A*2ws)TZz#X-ApjAbVqu-zi1US7OKLFGV#<*OV> ziI(gfNINXKVUTuNp8FZ|OtvuRLfUPqT>`%p%RG9t_gXCU8PY7-R0r&{_}mBVx72CC zJZKReg)PHUO0|8aWoi_pLzW+@1(j`anjjsqtfD_nl4FTh!!OrTMT;TN^63yD-|~R= z)&fgeDIAVln!FI@gync5lqW5h>p>~D%=`+{Da%4?eUw_<)qpdW8G#5^W?A+m{K_p6 zp`cV*_;g&KwXC6BquTOvE}+JuoR26MEVVztR%_Y&FQkhW6P4q27F8RfT()#|HLiyMdPyd8q-11^GB!6qxm!Je%d;Bn7koAQl zkb)JqxwLV9sLY%dL3Zr=IYqXywSg&2dKs&7mo`o{W z`UI7FyR7r5(nz-YQ~AH!+VmQjsn*3$fwI>+a|DoPjXwtFKI^4}pzOB_Xbm2;PNH8r z!)j8(A=CO4y;F0@+UIvjnOK5~#t2g!ej#?Mexp~ZbkE-MXYeW_( z$E}k`F+!m=k@mVG>sL4MiB4FzPX_ap^}QKzKW)AFUq8g!WrI{|{feH=Ggc?{kSeU- z&^J+OeTsUVRn}iGLwVMkE{C$(I@F9{co}y&f?co{Z~?W}eP6=;qBVgI_B!i^F~DW( zRcaetu?EhA)L`YfA<9+j$DvSOvnuIk`G$4XZ&2Q}?!5+OleL`sA-AkYsqcN;8n*^9 z?^xG84Q8t~k0#M(eQ7S3?bg6XNFCPaFG1?I)>C)+uC?2mc_n{oMuD%22n056O1bb{P z^a2Ff+5k-uJF2IHZ7)+}aJ}sY2;+w$pwjj;WzhZJesat;t>%bEh_7TfvBU~aP=o)3tzP1b<9-PS=3jab|Jd7#AE zrqjYqwEatG+zuOCiP3l3=F``cWNTgo+b-MVa7Zb(63YDd*aTERq}n#`hP2mqmnM;B zTTF>yx~lm9k)5%U@NrA_CPvqtEYUo#CDDjuu|Jk^kto~t)S*Z zne7x+rj<7PR7h2}za@aPw&(a5sM_|!OJLU6p5p;7*yhw?gj$=1Qt*qmf>QX^*;Z1O zRc~wPfZr9{rMd8HugC^|aaAsiDzsdu9hl@309T13GO9w65>k_;k$o*jBuRDEDm7J0SJi zDyeMgv-RGD^uV@i7}9`km~Js1+Mc89(Lq~wDwIRE3TiTs*zkfImYeNE`o+dOUt)_&X{ z%sBhkN(>Zl=QU#39riK0F4<{6{R?bK_Q|w)?6SAfxtVOAmH@v!_LnG4OSOMl0p?zN z5H<1A?AQK*a-V(DZ(tsHfzYYwe!Iyl6l36O@zFT-K=g~JhtKP~7ShwT<1*YR@^{Ej*TUV&e}Lp>eJ0*7xQq~ngN zDnOy5fxeKFj+IoQ6gv)_M6gqi88J|vcC;BW!Wl;&_0P&2S9Zd^+|jQGv%=Bh2RQ5K zt%tJO5%L#;)i`+X!r`1FwHr|Dh^3Wv(Gm6y-0K_{sAQ{mZ1#u46-WE$aBpz5QhImQ z(Gmd4HAgr#$8I>>cY%4+vD5?5}kV zI_6x0)a6K_ervbm&Mdg!b?jRYWv^o{HKh6+vhTs{cleHB*!zyZHp2GM(L`DLpkpO% zazl>3c+C2dV-2m=QAg|sIE*>&)98;K8tNX5J38sYeyz`8vIY9QoB?H!&v*0{1pBs)t54u3fM}nR zmqFR)(^U?K7@y%a7%$G}Jzq>B-sf{#m~o*q z@!aim`x`*2Pk9ZPdwq7%!I0)Nc@;+3=VPN2?|{!h3*ey7htx*P@Oh1n$V{JOtDwyC zv8)6$+viUUq$57FN)RQ-r-x48V?JLUfkU3p_d$^IeI6!ZpaP%ilmZm_RK5=737_%- z_?`5z&`wnBv;F}>m-wt$1ZJtv;u~O|@rh}Mvdm{2EzC-v-gGFde5P#&<*d(wWf-B_ z$21%6=Y0a`{fP@cv2Vbk)@Q;ifQvpQ$$(2fvToQe`yA~8Bd>`Gn4uAs^R8P}Vx1Dg^{OwVs$~ zkn;uu%wXp++Sb-P?KQA%aK^O&LY>J)u!T8SQUSi%S@RIeaOaxY@QZL}abSyd&iEF7 zTb;l5LW*|&k2;XsobS-?7UN9ugI}Dpo;I?0=fP1xg0uX;R+4ksI@oqQH-3qjDbB&a zVcX*@pfe!Vxri3ZKIf)4G0=YJ&-BVex-*Az{R7V8Zb+HV)qlY6kW)%I%3x#^2$jxbbW)yoUZBq91?SWT_|-ap z`|rIA=jzj-)H(g=2e|A!_zEajoPHcYgL4TjjjPT>bi7=1E-eDwaHdnmchecL08*24 z1(gxEod1&oZaeqghw_fIi9SlJ)9V>1+nj#%sI@zfQAer6xrmb4Zf8X#rhC^}`Zea# z7Mfh?PtBt6d{=RoniCf_rUoNr7;7}>6E`bbgr2W=Ad&YB}2o`TxxEPINzb{ zV$?Z{4;XW6J+8cfU zp_6%&uZ=$DX5ZgmfHK^7IHF3l_*MDN zJqp_e-v!jduk~I3B&3VJbLpa^&R4q#lc@J?uY+{SH-t*P%f2I&hFLjr1>^0$hJM;J((y+yYqd z3VaC;8(f#4!{-Zit@;}d5w4_7fJoPKfl$V{KK2D=yGud)M6Am#77*te+YVczD}$Pb zJ6!xX0BNp!KSH_BRU!lIcO94kX1Z&TN{kHGC_Q+YuD%nP%OTf&A1DvIo}_=daKy!; z12f0f@;9R7x*F-0^r&myv#{m6c95;WRmFtuxa;yK2wmthRfBTU)lYwipx9+KVW3kk z`!*;~yUOXeJ>&YCHl#Aw(=!0&t{GIoRk%z7#60UNq;ss=rC0~3aqXkUe$I8f0L*&V za#}B!Tq-(HF1tqP;{A%NaVns}_3*!cBj$Qy9Fw^2dWs&r8?FgA;dj$jLaBR`YZ`S} zn_XYfW7y(a!-n#XD}NgtT3tTVA+@=3+u+yfI`tALU9O;6fNqzAy5M(RTq@vtT&rkH z?{l3$fY09VN}Y_D_g#&NaDU)BVuo_S^*m*HBd&8@u#LLv>7Ryns($YyI5l-%thmeN2gNkY6|Tbb|ffng)k;ekYDX+Tiyxora-)ZB!YSY>S_P_Q7%At4pRZu%R*{?zfX}4cT2`DLk75{af{l0t?q0{|-4g=+Y-||_I4*IG4 zAZ7cttcAl7zn>`&$?)r84jg>6?CSa@oS|Oa@NoKDx_+^_nH7Tew|iO&iO@C z|MR?GDsAEy{1QhItk&<3_hGx}mrNg}&Tl=Ho%Mdd7C^e>_xWjz(CB9#1zh(F&VsGk z?+q%bTKpyl13LYdUVwX-pPcU8y8Sk}1Md16BjC{M#|{Ma`7QYYeuIATX4r=OGG&<3 zBfpV3P!9V|oelRfzh57M`PlDE%H4vd89oODPrJJg%yrYgp)EFKTG}2^LZ>}+0m7zL z6+;<0Z84>xQPU>Un=)Ib**=29)@dhiz&&o-9;)Btr@gxl(ynPjI)Rd>#rD7}T(_*M30Qa;@945v;(v zunFk0^+Z0RT(KUq1I(+|lP7{%Y&}u|QDWWS17@l9d-@)(S=Y6ISz*1I7L-cs_OVb_ zS%<#`<+}B^RCBy(EvK~UmbKy(Y&F&so#9Yx9p(<@ZEKUGP~Nd_qNS(7dJ)y+?pklN zgYv$08@+k-!1^cJ{4`mc=t0?RT}D%LtMwHcbx*Aa)5~nntUC^(y*BIim9Vv2uiFar z(z=DJcdx7~R1h83wo2GuTfe>vTc@?(d-%Pz&RK#e@2nqFo&CM_p>&`R)&s2}x~VSuT2s|1h&$vqY$05SGjIsyj*o>hj2rX`D4gr(3lYJ+ zN56A-kgHz`6vcHb!=*%X&6*K9hP!nk++(?G>CojccgrZCcy8tnL`mR&y%7$H+&CS==z>=2h(=-8TZQHptNzBvuM4Y zn=b@<&izsa^oncuCt`MReO;k^&21`!=;SV;?Z+GLCL^GC+;rO6yys3{4(120{!}Qt zxsPeh>ERxunm{ji`v(O3%r#sETOT(i45FXwyaB??W+p9)em1claM)ura|h5~n`D~S z{B5q%*bTOER)QH~Gu;FY9k7|b0L+6n>fx{*vPn&Z@~}-d?UIk$aB1t6Y*R1@$_$%L zuW`|tHjR{Z9k=<=izp{-Rz@TADVy!zgL2v?rl8x7L zpj?};SHqTPv#1@+e4F57a4)bay#sXFM#Bc>icQ{LD6iU#rS!beCgBE@Wj68}L@Bqq zLg#oDHfLzwt+csCvs;zTkqHpjZI%{6S#85#f}Yjc2ujdUy-hl;hPQ1Fj0WY7P1hEn zdp18&2H0qmO_TV2n`0l)!ULP=WuQE?aajlV78~si*q+!V=0mjFj4*_<%_e9*+}mx+ zXp8^aCXlwPoi-7(;P=L+jt=R%Y{pO;^WG*Z4Gte{zFGlWx6PwtVD{MD|7W_e8CMM3 zXPZIoaPYGAZh_6)wskI)zP6Qn(3jn|WA6j)vkjo~TcB+r4Vy6ANvW`f+fJu>KH8R7 z0TE+6DhrfDwu!X79JZa!fnTC+Q#6=IY)4R*o@|>H17(WsC8`!?+D@avcieW{N;sUf z^*aG^%GQdW;0Y0+{Xlo{yloFX#TRThkHZxe*lwU*&t=;`5}+)x{e?CorM5x? z*sj?QdI?*Z?E-qMrPB5m16!4C$V)J<+crl*S#3MW1k78u`~QHg#@4SG4b|H2q?NhO z_6IuMxntW%Be}tLNC?nf+xf1b+_OD69#J0HR%O7}WV_D`sM)rI*0G1S^Qa#A*!DCX zAUv@>K}&0^t%W|&Q(NsRgl@Mjw@0w&ww6s`zOZF7fnM5H?}qKQZRuMmJ8d6Q2LHx( z0V%s|hrL9U9@|lLUq0G)Q#I|AZM8dM_S#y11=Mf5qYF{I>KvdZI(Ke~l?$Tj6 z+_HOg1r9ZK!Bi)zw_9}t!EW2x)xvhy&fr@p@7bADKs>O^NrY&!TR>+^Ep~~KpggiW zNry_UcH5*-KDC?B4$*GsJs&MRw|jUM=#`z-EQk)fRo5Wi*e#5N=(3al2*0;>qPuAC zo!t{9l-+jc=s3N{PB0(nqur*}XyKFHxq)Ez*$ur4Tfg0}RLt?RpEnqO-u8!R;`FuO za38ka_Bp)}e)fS$aM)uXx)inm`)XR|_t{UGjF^G;&uK*uvJadY%eZ`GTuID zHkirwN9f&y6#MZ9fsWY+T!%=tx22~e&3?{DbScyRwh+qW_QQgKPT0?(JUPogcss;7 z`>}`No^7A$2wRT*a4Hj?xAz?abk%;SC;STSYkmYOvJa;rRcycNBv6HYo*ueXX@5Bf zeW|h^cMrA&VKKl?__`G?Gs7t$e##G7h;mxJh*Ozy37Q}Ae z=WfK@%QH*>)1P;u1MUI5Cd$(H@dDdH3FK8$g)W#^LfJ(K56>OGP2Qj-poH-T&}$;$ zynfn69^mP8Xy_pC?LT`U-X&@_niu&!++%ool<*|*UTy|6k@qPN$|F2xGu)GS8^)ud zqr9+>5XX2Ej382ZF0}1T<2hDBr1S0_La+?pAU}u`yg%qk&Ekbr0-fajLhr?%;(bno z@-%PN44`woTpG05ye+hp=kRXpBg%Q+(NAdo0`IgtL@ux9EgbTAWfrjI^MVV(#GeYF z%Isy{5A>)N@|-3@6!B!na46=jpwza6S0VwWlsA|%)hgZq`qKf|d01UA3V5bUbm;~! zb{m8h8_E>blEg(P`2>o{p+Ujl5Gi2;Ic{hL-YX z-qyW94|%4v9<}hc{*GWzcnjRYY~_7JwS=d<5!=9g#+yl(`J8u`_QfxF_Z4t>$?Kub z`754VF3?+EDQ&Xf@pKCz-t!zN0r|kI{1TLIo>wxIpLoqwo$Td_=w^N9Js?US&;Bw{ zKkvbhuPvO=E5(W??y+b5&Sv_I2`0x%mg!vzw&$7qWQ9=Kr#F| z^!&y0uhaG;k*}cL%Mt$a?QlrqkD^EPC|^n&qhx+eDLRqLUsMm2#5haXMHGw1o4*AeUj ze;cih7x|Wy66f(FeSq@$tIorrfZq}Uhs*rQ7ht=>*V5ro5q~F5_r?5)w9_czuUZ01 zDSssGG_LWTa}o18pG70Nnm?AR0CoJK$xznw6+@wHqFej?2ctCpv7jDD0PjEgJC{WNrIeU=c=^KRJFX$Bmg$ksG5MhEKI_?S=nEnkE zA>gzj^g+R#gbVm3qHIDB}Tyc4Yosq${?V_g3=K{@q%$Qha?CtQ?Mk#Zp!YD z3YeD=$$}LLu%!rYPKQI9K=~6yy1f8p;-&|EJUi>I6GBLs>8IrdI+Q1lOsyc30rF3SGJ=>O zCZQb-lxE>*oWZak3hU|5FSiJ_pP_sryi7y5Rd|kaqNl=!&4}_$*f$8MO;|^re<55! zS>8)wJk8gygm$!<>=2%$YT9dj{SaNkpyZ$ zt+0I(=C>nsuW&Fu_Me4Gp%8t-zCPHzMah%F+$G|C2jL?MT?OGQlBPrK7A@Ed<~~u; zScDD~B~L_@AW`Ugi2b6mPa%RuwvHfC&_mICnl)NPo|M5q62+Or)+!q33FT9fvkXz5iTea@THJPtFVLpKM;u2-eZJz+%OG}(|D1qsKry_cgcoCI-Ld5sSq1jNecLGqD_<8{-;o{j# zAR@#g-Xi8fu}%$2l(;+;lxXp<(-1mFY;+7LR{Smt%){cY>tM!<^Fv`v5D%usCQ*En z&Yg~k$53XRDsKA~lr%AK2vEBCXR0$~h_?=g@|1WmC7!3nA=ITa;vD)m&x+?zR&r4+ zcn;_K!Hnfm0-bj0=0`V_YoxCjGv=Fu{;?0z9T@~wI0u_o&scusw zHktxuvG~qtC@aMMw}2|eH=_}=N-Xn4L)XO%PQg|!4ju+{L%i@HTDU2$J`Bn&@fS3$ z)rh}%gJ8AdFCGKciT|Kvs9yXNRlXX$U##;0<$-uTjn*cydn1Cih+oq(_((kV zD*PUcGwG{(B7QIvp`VG5(v;UG9#{p`E^g`sdM@ssg_tkI(X>{*6nCsZuvcR1Mu-ma z`sYx-5l=XRC|zPRYlyevy_6xn6YKNf*DX%00i{QLgl2}1;*Vot`y?J+i`IL^-IN!6 z7B8oJ)+biELiCIO%7%lNL-<1oZ-eBjyTx%B1)3Oq02x=9r!d4COh1sJS4^8%N_7L=3q*h&T$7B9p9gDXfs0S zEQdBvC{H?YsC;wEVekexoOXD)59o|TaTw59hqOQ8e$L^L1_zHj)QL#M4m&^ zJt*@XeqRAp;4p$l&t-=STIvcN^68DiB8PUW@|QbI`2oxdhg(z{xapvq0CdaY51R7s zIK)^2H8|8gLqm@oCeo3?V+ZSUFrPTsP{P{k;9~~y)Zq->x|a?tn#*4~u;^5;!@;8$ z?ynu1Ea2Da5PBWrgTpvF-RXA7xCLg9g9S}9A06({zTZpIKxZM|68GO=+a*~{8-Tr% z3o$_cl1ns02S{SC0fk5wj)E;z@+BQngh}qvml`F}CZO49$rrTSjh9@Zg&{%mnCglt zl4P1Ak4YK}AW|hO!$3)sSRVi-U9$HZgwBw(TtKi)34b+g$0g;I;hvC$(hxo?xyFIt zImv1~OdN?>-kbz8d&eF7!2IC2^&UcZJ0?=hK1chf=zPDU zYc0@jr=L0@{G1*Kz#-6S`9nAaIoW0-O1KlZ7%0N2WDh8jP9JC$J>b;46UsQJSa-xc z!+=}v3u zq0Dd+(~-d`C;O{Fr=0>`!8$HeUd<*r|zT+7hR!bJ0+# z(>*FyU32n22TGaK3aWxvI%U(PR5|$;fpXm`mJ)($CjlRRH=O*p!B*oG8iQc9PUEOv zQ0Fw}J5cJKPOb&zw$qJMu-$h`phJ)cP9?*jY;rmi2+`~`jC%dhDUTk!$4=oHX!ePd z{2@@Q(+XEmo;oe1TFEn~S+omz?qp#O+Y6_?DPX>I`kt1oS5AZ9p}h{L$88Aq&Z&1W zl<%FqQ-D4=otMMb?eyXiP><8q;}9R6T*{&Ja=t*bskd_g3!!&8k6H*nALk?Su=zTN z&`sXsJa#+KUgvKq0r7YKL@%wQx^#meB6{nDavpM5=QPRdmywYiMRqcP^#M z>~ZG<&Tv2B>`Z0Cv(8>reLd%VB@E1L=e<)w$#G7(4s_A^>y2pdl5;#gKv$f3M zKQ)EzlXE{6Vf&nS&x2pTa~-{?;pMW6c3IvowNzO0b!oo{vD-x_hwyVr4+mwB%VA2| zf?WJ*5bbw)@iS}@E+6!OB3<6mOWE-*h3RM^!NraaIuc#Jq)GgUi}_fHB$xVPxTm>n zpxs%zizC(YGF;ZuI-Tj_nh4u*m!##0dB!F957^GS1kr+W(PhgsxLK- zdt9^V?@{k{4WgBKzpHNpVg|cP)QB1B+D4r};F`G_orrV&njZT@uJ)sWl3b6@fbFQO zVGWq+uA7F#mf_l259K*m(IP0bUDvMyGskrql?~3jzNVb^lIs&Ol)0|zDk$?@n}dNH*uLUAnl@=yU0p^(S?C&g9F!v0qn}|bb?tl#am{u2J}Apv(sPr7 z>(T4$t}pb^iE7s-S{rY;wvR`Y8rR=vAZD#=$2p)n*GZXlTV1OtacOXMK7c59U1!qK z{ykUmR}hV^77T1nu5WfCShK5mJc2!RebWJ3i|Z?D=!t7UA5g1n;b(}auFERHeCAq1 zU-5I-URqOMxS9=y@}=uG+Bm#&oy38%)72;#?Y(iGLtk8%>o2rEymehpFYESP?B`ba)_hS z1O*(DrKgP%B}F=f&Kr+O!voO5ap{dsP@a&U{uUxjx*`$oC#8I<&76{2_5z)iYKOw% zob+QKqGU_8Ht@@l*3tgT$Xw2tI&FOS5Puh4JB44`THYf$s;vz)3DwTAj zg+l2?%Cm~3L2KYpELDeqSt3=@sb!gT8Wp+9rSIa=LWT5ObJ!}S$7#o2C7rV!${SKG z?LKcx+vr)jCB5B;m^D(Drx3N$S2{57N_(x)!aZr=S-3Y!H{5}^FMU8KBM+phG#5OU zZlpW$MA|(YqE$L{AYwk1o}zjFnY53dl{RTkJ8T_N3l)^FrCT4PXYZxaSqS|>dR_qG zEpxjD+b-F-*|7P@q_lnXl{HL&a*xcj0b;N0tI-htvcsF;5Fq>FGa3q!?TP~ml{wRf zKTNirChBn6-xFYqkgcU!^Fi4T8Nb$qBQid>>M4T#midg z+7o2o(7jHS<%h%Xh|Gl2gJf9;y_1n5b6pDMF}BD z;BZ3bFdWJ(*J51HW%d%y(fL)P2rd?K%Yzd`5#j^8lP?pGc(r-7H%AQaXd`;Ft^L2%6ya4^I zl-bZ8uS&LWEo|3i%V{;NmVJ8)?zdz{!$7H#CGUo4+a*f*uei;i zJgdlU0cA7AZYtW?mbgs`0khOCB@>i#w^TaeuW;ME4XDyB`WJ{Qw`5u(Z@3Lyh+sF} zmQucS%S}p0k~MA*QbDPAJ45NnZ8yjD2zJNKnf3?`ZoyPUXmTqY1czp~azYQ?<{gKv z#qABv*H7I3rh(GxW=~O`x_x5_<})|FKfrwM*7^*}7jE5yp?v8!f%@{wZHopDoo?gl zbmxtmf(m?HZiPP~*jqQ%3{XC}O{RDKy4{|dz}DlYrA7av+nB|O^4U!=6MlVe;ddeW z-A2&J@{)%wz(xDWKgR?4$~V%`*e!o=26sRCj(^H2@{Q+U3z7@IMnn7M+o*^TEI&_0 zybyV(6rscA?`d6$kO$JqWTgDeP}mO0S0q7sP`akK%ho>6eVW&^!I2D3>%h{j;Ed^&CQ9?6*mh{y8rV?cQ#Px3`4 zTIB{qpnNLd`VgXB-bJ(bbNN?pu)UCfT#KH)ln3&md?lYpwb-}v4mxgrC!b6=yN1^PM`!>Siv)sY}$jiOa34Y%0FAqcPa{tp2o$ztzPl3(f zeVQ$l0q)=2gTp@eJ)VdX=&no!CCpvtiqPTii?+cQ;l5-IP^5bgtyNL(6X|3*+Woh7 zpm_JWUT{cozq=HaM0Z<%h$HSloB}1;J&>N;6!*(?)Sl_C+y->qT|jHc33omn!DP8l zE`d1dZnFnbvfVqf;GW~Yiq6&Y+>gvam-5{?vW0XGWbO(y6A6s zM=BC`!Y@j(`z0ERR$OXFuo#8uYM@v}rw+EmiZsf^;}w4_hDcB>qa`a*VW9=(s3L?0 zO0r@to!O))+Ne@`OpzG`O1fepJqQ_!C|Zv)73v@8qPWe1!&Su$>Pw;G zr#ny_x z7KQC3h)0Se^@#acaga6@PZiatA)YB3Wl**$HV%M8yTalel+P9E^c=lXuzp5+9f~i) zAYLo1<{(O^LhuN-H;O`9i{B~cQeyUAF`d4n4~ku_2-dB5lL^$L&@6eu)2m1|}z8)KX0cE#GD^(r*JZe25_ISLb?PHL~mTE-V z?-AJz_h64zm9T|)@TG_n?(vflwg`{!DGiPESUm@}10I_fBXqRK$h%O+c&Id>#CmYW zP{w)uxEC>xc*IhAm*kN;4kFFNc^GWz9XZ(Ebw^c3*}{x3YrV9csNrMb=AX&>d?g=C*HxY#N!~X zsg)jr3^1!anj%1{@fc6jZ>>j58CtmO@n}6L_dJXr0X_7XKs&G&4{jKk&pbF>*xEei z(?MK^$0Az5UVA*A2KV%U_193@NP~TY{i0P&Ql!x~g=cU3sB0WC&1^ay&gG#mbuFKqbl(GvHUI+#3y1u59=O zTZPhsR@h4A{z9NCGrU5Y@Dd*9|`B=G?QjRCeBI@TenwJD!% zL+EzpUvxVDT-kUW=!J540vhU2Ug-hlwQ>tp>pPVnN3~-nYe3!n@&Jgv zD*tcMp1&%YcHV)iWV+?}$CfvMxnH%r4(`FKSa&GHRL^Lc2v<1`0*X-S(`}7Z{ZfZ0 zv8t<->&K}=XyH4g^4S5(Vbw@Fr#!0KaTp?5^^F@GQdD12`6g4flOFBkswfToPN>>_ zAkM0sa=|>O+OP+dY}GHxpyaAXQ-vo_^)MHq^HqyagIT1qpxs%q>Kr{z6{^K_1XHQH zxESJ=>h@IFYE)hBK((sVjfh#N%De?;z3LN<^4qHP7=*r~noWiI2Gy-K5O-DEsDgD* zwdoPsYgAQM!0*0l!E&Gns;{acnpBI|z`a>DEE=|lsIA)lKu@8`T2Jce_-7m4f+JC7Os%^r?&k zVe41D`vFWZb?g$@yw%5=VDnX9pw|d@tB-C5@>8$LM$A3x&rWa%QU`B@!+!OWW+;Qz z)wQ66sMGS%Lb#e)0%e3+O!-Ts`j4|f2h`lD5YcM$H&Di?FBZc+Rvk{o;y5*b9BlFG z`9A?AsM{#nOH_9~fHczjomZE+~#l~am#ttY`)p{%h%TPDVq0ChG(SvYY zy@sB+6YB5J1D#TvQfcS3`Z6V7XVmP$@H?ySq;Zy`&K(NB^J@I7R+xU(yxTw*)g}vo z^3^DU<~jt7CakmZ*==q*|s{RG__bb*MK) zh1#%rwp}emy=0Vx4)+IqaRL2*hy%sf#X1+)2c3L+dtABq3ho|Zkn#-T5TS|f2)Xx?} zw5vbYg7RG5Yz6U3Ev21Chk6h#im%nf=sD_Cvm=4ts2^>Bc&B!zr{leP=QA|)L2a=f z4&Cb21qj`v&T4|PSKUcRcAwQnBOvNZ&@&&+MIM;Gnw-DkzFX5k zTYNu_%L*v>XsqoZ0yGnrgSk&*oB_>Ad)erb3E-CTX}=(3hi{ z<=IdsYr0rqrf5b*0;OrXX}V9>xIaL!42|Og*fKTy=fUr|#_uHjPHM(RfO1M>JPpdz z8c_+9XEdR-l%Lh~QV}ml)8YZ!c};Q@TEC!KeizD%8lfA+CCw39vhp?UwDT^|G*ZrR zSu=>v1FmQ+XhylJY5OPV)BK$ZN{OaGj9{gj@AJUCrdjg?C}o=YaS)Z7B1(9wG}|3u zyRKP%7tCr+AHBACLvz{)${LLp7s#&F3|#B{XjF1D&!9}JbyT}XbLw$`A8$8E!1O8E@kjfG(XdOmCrO5RQYPt zj9LWuc1<`Xbk8-%DerlqarOgxqe&kO(WS9>M3lFh6$fB@r-@64=+>BLK-r^-je+|| zjb|!ie$qsTLfNM|ItQWqHE$WXdwJf-ht1nlb?;dS0gA{EG72MOEo&PhZ-##(9n;amce? z1otG*EtGB@_1t*_ttWeq`U4Iro{>C=G*2}ZBGWxRAA@<)^M^F}o${14!FJkn{2-t+ zo=#Lt$o7=^0_AufQ$plz*9iVpMx_={4*do})hk-SjM;hnTlKd1s-l@k~*`z1DO7 z8#vT?rZ0u9-t(uE2zJ}k-U;ZA=b0!_8ay9If^yf>o2nf5JPqm9wnopog;3u2Ttq9- zBhT$r%6;s)y$7~-PopxR=bk6zh|=l#1$Fn0XWmhWZqMB5u=RL0(_cpH_l&s;rI)t3 z1zqyfj$#At(UvU(3ewurMsvS5-U3mAwVP=^3(*#x0}9uU9D*x~(9WZxMWj|e49Wvq z&Mk-ptz$g=61A&+5apQG`y!O7+A7)=oY4NB24$A^_t!w@w4-Scl&zg=g=TZKy|k%6 zukE4Byri{Q3BO$J^7&}ts&0`8UC-v$6xX@6pX zu4`vkBg!3Z!ALkXXeT7V;jVVjY@mDE@)Dp%ZD=x3i}rjj{2pnY>9++QYwub^Jkh3@ zgVL(ilfw2w+fJM1ms%fMq+V&iUIx*jwWi$WowoB6(0i@c9^!-6_B$}UwHdUb`lKB| zM=-tGR0im?R#FMnr*)#uq_?h>7MoqVEfm^E_j)IUudY}L+aBE}I$PSS+a!jczi!tV z^d&&Ig*KBxy2*5V_Uk?vgA%NB?1u=^?HLGLxXvdIC_)!N`>{x!?LTK=x|iQW8Lj)Y z2YxX+Rw0Azbu_4 zO%Nw_D{1$6N_S5W=(KJgRff*#1RMm**4?1xEJrt!I&oe%;yTbJT@jTDb9G-CB3Pbo z!cZvlbtnIT-xb{<3n;JZx~WcEsN>P@rAYT%50s_4OLRPQP1orRQKs7ygcizm>2$hN zrJGOHyX(5WRAZ^ug^vg2hVH`Oh*G1A`5mHGH;+=dI^DN)d+K#%#~>PXgJ{yZs}nTC z@1D-#Ec_aEF*l)X(Jixtc%=Jq2+GGgr>#IwbPp-7d!`#b3d%NJnHL9Z7tfK;ky-x)+ zg2B{PF{>S)9lx=K)uqP}KV=^;gQK`(il3@-ef;!1`W#Izi6Y-$M|1IcMIarEum0-{ z`0MW9Mx2eFR94ucw*j0#f8L3oJKZVu_hE9n0Q{IMv;p*IdcT2tAhVwG>@en=X9y6< ztfIPkEOSZ>hr>*w1P+PJO>$3V_%z36FxTmr<^;1P2vJTk2h~8?Olt#d7Z?^DW#=;W zry(vgSLmB7WH!>mS<0+>f?(y$MjGwajEfpEZ!tePBT5}Jn9}=olCgtC*_B?ILx(?pNe2WI*aw9w0ZqMr3Leg9l7OKAd|AIl>J zHh_Fxq3g*rm{fqIvx>rq23QH zpc1_)^pKV5h0yM&Qtz*kuvP2rPk=*>9_JWPz1~-JhSs2WiOQOddgjZ)Y|`6AC!3G- z6qCSg)nn7V-KH0M5W!yP4Nd{GLoc=iqDzmp6b|q8Ui1R>=yg-fUcHf2D(%;MTLT9l z{Z(Ti{PbVYJ@eNOq8G^m^`99)8La<%9+ctw0ngCT0sYKzaF5pCa2K{X{X>64#Osfu zchHmcgUmol(JxP?`=USd0HS2-KiCCjmVP;vQP1e#e*%%MAL@fB7xXO)VawIGFGULl z`g5pCQmF4o)wmM<(-%M~(;sRJhf4hktw7cKQQZ(V`i8V$t=C`W4%DE(l!kYs{-1Q% z+N8go>M4)(H5~M;Re!;Jh&KIZ`X_x~=x@CTW{3XG<8bfN&mIltd;P+p@axf^OWT)T z{qTu!=+{>yL-?>2-$LofE`J5&kN1Ay7s&qf1^j~9)8c@_*+QDL4zOz<;!>j712>_C zIQEuG*y7n=(@}2{+jSh>7xp%q(bCv~gfiK04M5346$9>P*lk;(%w|tKiS{nAKg@zM zm+em*hyr#f&D4c#l{J(l>`kGd)U#Kdhq8fv=P251 zWS>h#Cz{x|DFJ=Np3(|iE1T^C)W+T(j?gdIZXY2!@Y|YTcCi~eV0+I#H58(U-9b4= zFMA9F(a)}`!%Bgzk|U;CXEn$42%^+*HZ6dAJ?GIRC>uD_ zsFvHvDOnEG#M$r#{2p;8#zNW3S@#a2jkD({lrK1oqd@84Xs1Hi#d+wBVDC9urVu@x z^)xz9u~2rX}Z27Jo>{0;JG>m6uN@f;4p2IiMR2{#bYzVm=VHyxrx z8*nee7H5z{M`G~?LJ1s_3~odNr5F@FfkT>sG6c#@gTOaHSq7i!F+5{%gT{5X!FDS! zFBq(%xjfh4HRX&229xLrpwM6y)g?;|hQ)wcW^m^lP$~_Ue*mT0pk+U7H3qRXf7ToP zv=g=lgU1eNsL{aH2M$dJ?j1mn47C1Wwi?({=F(=+W(4L7gB?`*>M$q{hI^O6H(vt1 zH;Cc@^%(p?323juGTIUM8@LG&%+GM#a3Fufvvxp%hW%713N{>1>wLIjgfSXAU}#G@ zakQagFi@P~%`0dq-jGALJjpQ46U-FDlYLO889uj$$TWOzgl4l0A5)e2jN$78@XI#D zKZ=eUVwlW@L#|=tN+=5qn`o358qT|g7D@~!O@Jsf+~f#RX}FH6N7aTecR?rCp}{cz1l$`9N3VxyGJLQX4v!337a>{=e>_4r#4zdy*j^a2DRt~HWYeW|8IG9) z%6r4f^!WA|UZlNAuVLUjIP@FV=^%WJLUx1VXO#UB{q#3l_A8iyMh|+R3^uA64H0e> zU=P~?Bd2W;(MB`+(X%+CCzQs-8(qo-N;3MLj!9FD=3N0L%_wCqqGTF%Y9O+VCb~m; z#^^6CnAt|=-cVjJ%BTHDu924#Efg5Np?~3^(CGR=bg;ze0&NG&j6Tx}RB5zmHAJ;h zEfpbZj67$dp?ah7RJ3X^s{IY3(P+N`M3a%eHyV0m)V~|=&$&4R7VX#NNE<-L(?!I&AM!SgyxV!<4JT%cg=V%ZCEOdPb~%Wx^Wzx;N3Lts|Kn!o^l!v4aSSZfEtZI z&?DMp{3E^5_1HL=Dg{rC{l7w#SH@nHU33~peL}Fe#p1x;*sgrztO=~Q%@SLZKmg_%=prD?gKDin>Lq#(q-x@ z26}HA69)9rG;cnbpG~XibjZuhKn>wz)-W5&J!Z^UPy)=jbbUc)`5S>k%y!b&EyC<# zGnfa>WRDOm#;ku88aiYa(F$9lSv5_LN6p5Bz;?_mCjx%yW(!Y%a>8u2HT+JQMbXoL z*36aimmITV+D2V6Q@;Z<-)tf!8CT3EUV$hwE1{|Dn%VvDAu7xk(XxBpOyvc1(=3$g zeRXEG)4{xB*7g>*duC4bRXs5Cn+;`)**&TSJ~2C{2l31-ii&{G&Hf_PVKzmIC~wRp zbYlO`EP%EV-DVb*@ar{m*MZV+cI_Hk*k!(fD#E+XrI`@^=BvD53p77e1;1ePx4v)) zGyj+a+X3@C3lSyS{P9d&W}LY_r8x2Cc3*&!WIlsd?G*FjYoSatzr6$wndS*J+)kPs z9)azQxeeu6+2*1tP+l-Mpp9Ri`6Rl`%jT|hCkoArWoWj<{K^KjP;P#T9`q{nFR0>n z!~8xiXEo++FW`RLeD825@0wRpS^B>Di>DCH=Jy1M^4MJT1KgjQ-=b8d-Mo&zoR{YQ zN8sLRew~hi-wXm57hdmbFSK+?TqRIwh zzlG-=C_^n2v@Atfd|P#rrkrORSEsx6Lhg7TJy`ZA)_S=@<0lm?6ErxB&m;_-K|HCc2{hONaSITp%R3*J`{Z5GEr z!}h|WV=7RG#W%~~*JUwoBAR_~Q9c2-9*a+I5WN;->mj@>?PwqFV>w5QX8kNRe?jr3x@FS~^bvGtW}755cZj&YcNck!3PXzonMD zrSL1aOqdVKb;|&HHg8%6?}A^grQa4f+_ua=2g*Im9h87PuzVB;zlWC5IyCgy(%~M^ zGfU?>I6Sw!O@|Y&EJr&-bXvw&!2O-&CudN)ErnE||77`o8T|SzOX;!LWwnFu(r&BZ z<7i>8)sOU5?Xx;YZ|DSD9s2{eFslzVvqxH8X@!Wg@;(j`XH{#07UHcYzCz3+R>!I! zlCA0|!o1^WTm3;piPRWqQxZ}p58ux6_+ny(*O z{Y9go)oKhCsoSkgV}M>-4WpXZYpaY-_;p!b+X(c*D*7qVN2?>vXy~(5<8&y!21+Q~ z_8nM3Pvf3}Hniph3{0b|4H`H%0nE^Waj8&74D=6xI5^O~0o{!mxTg_*hX*Rxfs#1T zfemzY;2e6Yjt%T|0VQMLOxo(57&v7g{7wy&P||&NU^Pmmr~sfyX0!w3AAi^3^PH-a z@R<9v96x{BM|t49%~YSA|9~>DpKEQPSzyM3W}%%aKL6rCC*g};Q2w}h5{(_}vM11U zQ(N%a#*+3gwv)d{G`l^gp|^iVb0d#SwGmO)E7pH{V9+-fj7fVR!e?gm^^gnN)YWOdUXj5+eg@jtZN+XE3 zs{&SG|3Z9LE*hoxpXlOlI#Mdu8$t&L*Yu`gsQ%MxBU2v68o+5`>`%hb%G@%6^Lu8} z0<`v#xkgE?H%mej{T^1<)PI8w&$fhlgdWXaBYlRT17j1M@Ojd9dVFo8>GYBRaugy7 z_MG|m@`lf(n>=zSy=gJ33S0Vrn!m(6Oo3xABcV~4$Hb?Cl+PTd!}=I}zBwFTqSa0KDUol6Mp|$d zKb7XRr&6_2p0{E;jh&U(W5HS5mx&1L_z$38-!TfzO(Vmh`Mu8)pZ{*%0_Kj}7w~!K zm2!OcVd@e<4Pea1fVH3b^E)Vmnd~~CFs57qVk8r8gUAP%D<(h(nFkpV(abg#m@!Oh z8$=v4J`$9}Odz$D#l+fza+2|u;yO+-r|-jdn#ubT;tW$*0LnS0U>Hy~!wm){hZ!&t zsDm-0Jg}EZ?S=TvjG#f^$C%M}x}Vv}g3^oi#~mPV);h}gcd-T*0qth}avjV7mS6~i zC9v}Az&ytCr}27{HFP00#7dzRtA-Uh7FYX}^;IQo?^ziS;MdEVJr~N)tOwf=%vbO5 zeMAY+3#TGLh+Z>gexZ8&7N97-p+CVEttT&ldxBmYeWNLQ!)V32sJCDNl=t-B&@fYJeng4TKTN~;04jP= z9@KZ)Ozr8b2BD!O{ecY#ma1QL22qadmkkExr2Zs2_CKpHwg)9!-(wLd4f<~jV7ss1 zD~EWfUqvU`PxSjcL21*UMum)*`Y!un>(oz~0f%?`U+)9z)$jZTl+XG@sWjTBzby*3 ze*L@Kp!8xdD}-Mp`yg!w53qmt0wtAQK+EO{_UvS!Q|!wZ!92tM@mnx+*uz7>ynw%D z3tJvLme6H(04?ZO*;D?6UkQ8qNGPx2PZ~j#vsW}iTxT0>hr>;FJ zhxYxU%-&wT0h~nU1dbE_eU#bzSRZLg4rGPXmm9>2;=*A+Yt<%*V3sW%KZdZ59wv9z zK$=~`SjBXF70z1hi-satjwc`@S(_;fI>1sNY)$PD15u!=D3 zIQLj(I4S;bXApLxo)#+}T{UOaRVo~PpMXdIpS!^7DFz;Wkyga9QH|*8_Z?LFocJrH zfRh`l(d^XMlrBxLwghL!`#lfH{BqP4siY>b!s3`8&)TJ|T>0 z9qvpRGwL^R4l|jv|1-`0zwgz*eP=hbWb4qyr>y>$xUTC#0SULLe0zeKu?#N(=@iSfHp&?nc0+4oM+9WG~)v67G;?i zS>tFIdx`a>ADnVogQ-ZG$J&+whkVwM5VTXy%5{LUf;HR{sFGz!oAp@sHvypJuz#e7 z?jqZJ11Rn6uZJU8H~Y|b1nXhXsDb#%9!5osPxu{kpkDTyaq#=h&ZbqokKIax%8OG= z66d^qjD!_SxVjw(L8IoqlJ4z|)5rHU*Zw3?~H2K<4yh zFv1v4feylSwURGdn2(yU5)88K{8qpyF8(^Y9GZ%NV~% zpzF+eHc$vaIzZ`mx)vuse zN@y-t-MtJibq!_3niVwhFEJkj&9B=Z;q&r)NORa59S^~}X$?)jn>{-a;rDN5;`6rr z@%Ze`_IBnN1kR35`9Zz6U|R9`hWUZ&Up7`MW9=q71vS7XHIC=&2tuwi};Es%SkMHAR75j^1+$ zpT|6y0{&Qrsu<&fw9tQ_KwGf!qv+&kLL=>2GLMqiK@;b%hNgET6g- zn%@ovqYo=SZ}|6a8H}T=pSY72)M*8g`253e0iw?yPASWrwKNpwe@&%_g}s#ZIP5Kg z-tl=FergBM{(lLRfS=21v(eIT_GDdcbrYXAwa4M}HrtW->Br1cqw&4WM9S&?nKv|G z0+`z?5OE*lM$c{_Gv^1`f*3Y^!TTBGU1&3y=^GA#Me;tBp-d7zu3^mL5fI^wMFEr% z%w8J=i)1ceLX-o{8QQ)cWH!;>JBrDt1Da??c?l?niN1vvVi^(TiE&KmR-i*nLkof( zW(HBYF`oIFHjoL-r!hc@%s?xIKEj+o4TmHqFBnmdGNpd7B{NoZvr-u8YM^6`FEyLS zY+nLOI&+(H$PDH<7Uh4Ysu9iU(2wCqfIGG{8`SwHXuJIL(pexbNLfSo_q;1OznobI z>xOZ3#J26k&(Qlbx7Pm~X55)upnN}&Hell=^!!dZUkb{^TR!OLq?z=2atLKnQ$|z5 zGu2oGj>}6ge0CpB|6s;a1?_$|-=PZ1mPne=cP3?km0EYSi)_^V~0xaf}x!C{)|AQL;F;k~b`_KP3EnPK@{#(28*V)sS ztXs2w-P&bqH*cD@dWCMIcH@d=o2G5qykhl$|1E#>x^=6U{H9&8cG{9Pn^tU^wqos) z)mxS>8?a=aa{b12%QtG*jQDlM>SdcojQ?})j}w&3*KQd>E}KSZH?A46Y>jT&(xuCm zD(8%tG5v?RQ#Q<)vSs~J?dD}lSf{K-m!>S!>Q*TK4XRwDUGm$Cwf~obZ`!))e{s2M z)-BzFOQ3u8=d?9TSN)$}9PY7l`I067pNpI`b>`F`{+~Y-4e~(b(@wB7@%FhUPB{7vwpR9^RMeRu9>=o2Gl=ax&{|B^*0Q; z0ZZ0xT&7vFX4Zd@X6dqDwOdwi)@6`1OlEUH}9ferPT}K0ZBMTt4YEtU4~&Ha@+>1biuh;_&NT4x8o8 z=PR9&wj6*!cr+hxp001!w^sr&eaCLUdAb+a$ zT-FZ{_p&5|$31?P{axHW-srEoef|CN{_gtb6MhF=Zr1DdwLk!;b_uA(!DnEG0RG0t@BiE1{ndZ?{XhQhuYULYqu$&9@Ov=9F!JC1?!SMF zvCSUF{`Vf@i)rY8k}rL)U;6$3_`hXe+x*?{|I6><7r*6Sd@Wzxl`qa;{R4jeALQY8 zug?A&{)hDV|9eBG{m(M(n3=O-^VmU!{#Y<4(3YR(qp$)b2_Ynabn49BvP;Q{}%MIPG!bUFDHe(P{L zT3|JYXU$@G%KzT(?a20axRU0}?&HSk{Jfbj&amR;WYy=di^Ot<0KD0OZmA-QD`~Dejq|Iy+wpPz01a&FFT0@zC4s3q%&H zk-`isXwKd(4o}_}K&j2(4*ePQ_Zyy}!TIz(Ha1!4f}7>}!E!ZRy&pEq#hYe$dAWXs zOy%ok;_P&)M>f~_TbNj!8(%e@$@;gOllx!nHFG)6hrRYT2X6#e;e7(i(ea>s<@s;{l#3X+`mkJ`&F7PG zJWkzn@sw?Q!?wI(wm57Ab9$Sb;n!vb3G{a_dhzdr;d1g~gCKSQ25fN%frJz->vMQ` z^Y^Fe+?SIda6nl6c&k~ipX_WL(1A1xh8mqQo)$x}oTajwePuO= zhxlkUnnP?t>~d&uj$NFb3`e8$(`GVXPEXEeKoc;Z`OEG8$;sOqd~+xWMv?eV7NfU< znGyKHzrdr~69{=ZN5F5Zk#ocwceb|r_zzrRqdXKa&aL=VeQ0$GpQ}7*^&+y;e3Os;A8zZQP z5H_h?Qr=JWOqmiRw z<#jpCD--zC(a9N5mW)asJX)>J3J}(2@T-DAVRA+YSY8O;m^Ti;Y7CNwRl@%!(` zs|IpmhJQ+#o(<{C$ls5)$=_vb$~uq2-y6-l;mP3eWV}hG^ZTDKZojTyfOqm2ITC)} z`|H2%3Dv*o{jK`|&wH^k1d2sC%+6NW)7iDUPONQvA*s%`|qxTh7@E^gG#nW%Mj|;EA9O_&!Bt!sF4ot*O)j{B4SI3=bOR&n z^sL2y#070IBu*TK^YyA~KTW?UuYU6)g_8APucW zj(4-aFOcREoVHp%uRYEO<~kKt5j1F3x$66e#w{=2mG{v?IWHOcf4DQRnd0+x*zoqLou%gw@x2lk!lKJ9jCaO1KG9Z>EcH1wRbvDGM3Y&mh-K=CW zC(td%y&S61z}b1zf@)CabV!x2s{OFr%bTlY{dkXiu%P%SRO$&G~rPOb@0jyk~pR z0f^n*M);^d~f;k zG<%+RmDw*^)|iQ*B~TgY!ym(s_dl=g;gcg18O>@jJvy2I6Z&T4^m!VPJ*K!cCpZx> zwt@|wu0P*D{CawEckyZcSycAv;)lB$6Fwe_6%V@>bS#bqwZJG;SKy+-BqmC4bLW(M zoUp_JRkZf@1Updi>NS_Q_mAu5VSV`(4!57zoATj{UV~4b;8|Kf3E%CQwIRqb*ah$0 z61xfiqqR4WhQO|Vio@LyY&`^MV9-rxz0H@3zxK)S190kPuivsq`-+(`VoV7$-YWr z>f3m!9h;$qPiEPjBj>869% zWCD(}JRP3Esd+r1l{#H*etCL$(c5rFDHr;z*&1i)s=x}><9s@tjbJsx_gpNhGJ{_= zViO}&x1Xqwe-|3?pm=Tot?>y3QdQ4~KW4i65&F#>LCmLLGau&>{2|}~Sdg=U`M0{g zO#?OGPbmlh@TjE5DcoE^5<8wJC$;q8d>Iv2Spsg2lrIKnr)+IHQxA&x?^XKLX7HVf zlI4tGbAqsP)+q6T!|CA})cDnDv;Yo8^1%CeJbX84;IBJs#%JdTCzD=ZG+pNk3C30a z(8k2Xp(gE#hlui4MZsZ>P z$l-6g9}yCv`5yV^=y>O3dOC%EobRJ^8h7M(ImiUNxcv+{Pe*UzGy0(kKeq>`wB3VW?tiJRHsNWbsW0oq?7>z#9Wl)hU{s0W=~C zQ$1=B+sYkl$D%_U^RxGGGtbWsfsgW|1YhFouo0D#_Ea-iuEyARGx;#z{3hBy9E#uY zU(+CnK%W^5-X-MIalhZJCiC9t_>cYmpI+hDC>&&mh*IUEmCl0*(Tev?j!<2Mkc1U$ zCR3)oGdr+8502ou!0v3=?4`JZ*$58!g3N;a(hclqs)2ucax0aBplcTKBRn3!0r0}I zm4~GRNouaKw?J8@%iyb7K!O~$^dAWSTu ze0vf>4h>h7tmzE)C>%C$mP5JvOYigg^W*wSeCH1@zVB^)-+R3I*Y*AN=HKy?7cc&? zSA4Pg!(?$b8FTm_d;g}s^u=580ZsKswkC>ixa8?e&!%xz0*TRTaROfp4HUCng5A;@ z&9Fxg35SSJ5v-*_;Mt!99%+d#Z!e;s;s^Mtu%$V|PVkbu-(a3EKYzJ3&mgBwW{7GeG4#S}{0@zyb% zo5=jufdY0)EL8HQ*AZn^NAI(lh{)f0x zD8?`3klX_3<@BWH1eaX&EVqQiNN=5$wS`tB}`a@_BkpgS!FH&dOw3Q5@bp# z$J*muh0FQ?D{e(nTf_WP1N+n4=I)4;mu>x)7M@SzL*+DxEg)lN3gIa7^J-R1ZWG3F zaSL44T(^zs3f7YZ%;eigi{VOqD+H*p-y%uK^yuKxWn#=B*z@VT!v$2*`>)8iYQDfl zdLt=`;atpX_{_w(g%FXD9U>;tm%O2vl+u8~oi>M);p%*WWRf}9$8t18xB;KTYB`;( zrlXC=n@@M3&A;EOG7u;Wco*;wo3pGj^%dE_86uGi{4kg4j!UqXK0-coAdmEN+XZcM z4DFcY9xv2#z9iA$#{x--(I-$O?|?Iux>Jz-X6{dxjr_J-YImJ7aNk5AHxw|RMogv~ zs}Yu`Za~Maq;60PvS@+*AjHT7Jdscd&0*CVRw#iPj^^i}qy=&b=?}E8siS~=L&5;R z?Zoe!m2aCE-oEUdnmV3PlXd_EGeAXS~? zVpB;Xgeis85I)e#zN7YAI);@NFBq+3V|+-xUwHI5~wY(?Laa)lCaG%rk0+ zv*lD`uE=!ki94{3LtY?P6fLaI!ui+g*~1fr))Cc6$(Ho7Y2?fqqt$HFkhQy)TV-K) za)8iP^!%u1Dz{QE0d(xr56~J`LXzylC7=Oz(!i;wk>4@!Zak(-!;nKDBI{lPLh)xr z9TO-bNT8;J(>K$}WYTtvZJTQ4L0Ll_z^pjbDv>c67Vdx|nw^Wc6J+vz=)E>u^hlyW z2oMF;GZA6>Fq1t|%>go#R{8`sxw(bMcH9A*J8C!^s{RYS3cnd&h2}0vRnJW1!N?1O zZ&=QHI%~-tP>+V=5u@-SPKM~^vlQg?PZO0uo|cP;+6*ovONL5GuKj=i329UVCM6?{ z0fBK2`-Ns@hoNrz$`K(+)y_@*#R`8<17@L2ut!LkB|O}tw=E}I$0Y8yK_c7$p^bVn zT$%dT`{#dl$#8rCsvGVlrxUCc4uY5eEcGtv}qSfcTmf`K&G zcStUHgo^-NUI|8CKY96PcYL_f=^YkunUUobD4ySku_!V*71ie`W~s;M^kBJIoxLBa zPCC)3+$MhjSx}4f3cd&_Y-&D#e0+Gi|K;)#C9O6v-c+V`?Kz+MIwTovq!CnOsP z;bojsu3psjQ99KL4tj=~Mlv;~^W}mdeRl@zo1RQIrO*dCr0|Aq^7j|8-oK)l3em^r z>BZ*H_b4AjEmiO5dt|Hs9X`O+HgQNEmDnuhAvVX8;k;Q+|1jz8Y`xj@Y`}sRKIcjp ziE?eDf$;an;sAM|v%?+=9Hew)w8AFNH3K4qw)W`QRbzSy58(kA3e+Xde>coa-l^$K zYS`tVl4KKz(1V`*`|7~Nqr}%W^-bL-H`mBNq*axwT6#L8Z%Uqv1eom|oprg7 zjmJgh&P&;lD6w0rMjexgz7%*Vi~urqNz^9HLOQJ`*Dk+)`EvW~^5)wF+15`#JqqYx zG2h&M3c@d+kHpJ8X5mJbKrE#Oz=?*WI*)(5^v(D+1RTL6!XRjmTEE2?^1-Ec0F$M}s_GQ? zP-n7?r^||OSl?Zl#)sJH(7IC7ED<}r6u^rNxWSk3x>P=(;Vu)kk7q+D=(GE$#rkrM zB(>?_CD|dYH%1nHW=KIhs9Q)DLCblV3zXI{_sj>TnCQU7uohS~ z$mG6-bE>($`SZHxajy|qstt;Fo45)}Rz*?sk&VSf3|lOhs4Ocp-;=|d@d#<97>~H&!}$fFeSCZ90^k+nwz}KRc^nN9OyBLd@ix=Z-G3~)u~DymJ@W zpL&n$&o`I%w_pN~zsQ08vVKU$t+2k}naDSnlam$4+3s*^IL_6N^9jKD_|yHvQxBqd zV>LnW>|k5|kBQErOQ#HFhPlo8yD~c%*^D}eFRK(e~k z*ek?_azv?E6BD2qBr`EzMd>ui$N0uf+eQx90nTEQoVPrUI<>T4oj5hzG(lv`JT1Z9nq8Wlu1 za?gvjx#L4r3)3|RpH)sX&&^Z-O4d=Xr$tnuh0Lu*V-5sO$f(}|6p7j~ORYt6Gt$07 z(Gh2n6=g?s9Gout6sifVtCm#05r}>)S^4o-+e}`X{mguBN%UawC7%)sRwA@#SV6N9!%`a%y05ixt#sq(n(_%)eJENKv27`uC0Zt33g#Zt0hxOtoZ;+zF+#S4M8QmGbWE|c zI7Af)QXAkq4fRK_cDDM1H~Tw#(292U`>*@!mpi+MUON-a3H&}X5H7aVe?%G;9FF2h zoJ(~0ngMFbv&61+B<6^QpS6`96r>($M@pYsVowDtGHfgmm;H98;8F9x^c-;&lqNV~ zsl;a097|<~ALK_`X7w1=eKjbZ!;Uv9!>Ev+ItVSvZFLOS`0?4vxOq1~uAf#*yk}Hm z)$Co5{-0jK^zWzz+i_KWHEY(58xO9R+@680LAm^9$DP`cfI@(H z{3j~XwheVQ2_(|Kc$&2?2&Mi>3|49iUQW|NgJe-T0X3<>A6He&lKSz%(c8(7Bn~k@ z9Q&6i;NVdymUPZ=R3u;szrOeCh%;dYg>?DjYy^EJV`t?UkxZTzVixi?Jg{6Yvo+ha zTwrj=EWSiJO4NA4P|n^K?6`ypV$8$kTYSZYqp{>?rtwW#!Mf-i1?UUl?K!KyX{3SF ze)A*}7I{loZAUkbGUKOwTGaaLy$$>@m)JB%h@e~1&1!=5NU1Is*rPYg1TIOuw}M;H zDb0;#u13DxITWgPKsL_Ti&4@l-0Y!9u#R{5j+tu`q_;|UNp;j;<+q3kJ_oaNwk{$i z-$Rp9_y04Q_ij$y!w~7^g69$imB%Hj4F55HsaM`dl-`jW6n+8$>8M(;H+Y>2ii_e@=71vKE{|Z;CU%x>&G{UP$H?}~)i^~yAS#l} zKe48s?(sM0RdwGtW%0bMtVr?`Wl7$n&VHU7>C`EKa=P} z-in7}?-7!n#*SjtD@t51hIu}+f(}i2w^Xr$GX>RR!)c&s=uHy#&d2y@CoZwr5SULn zDPA#5UY+OzP|t;VwrJf3W+v+X{&P$gA^k`iRGbAkOW_0(UKry)o&owCIjvESV00W( z3k%E=L-7Q4kBso(7pYF(9e@cZD-C&^va7yZ!( z&%F*ABR&|OkHt@ew}xLXzFxgS!H~RsYisaY3a168$xkKhA!`L-yxFAZGZT5NU@ej% zKS5g}5a|d7m=d)c?53J~fuz;kClb#27b4mDTU*0?SL6$_=ExU{v++O*p5-zKz{f+i z} z@C?2drbX(d#C9qxSe%8D<`f)1g&+$=B8MPuk1T{ho4RGHx1%B6I|BO*W@NQ{UI*^# z+4^INxXU4}Eu0=BB?A9hn2SgnlwzDiX?$cu+-W!K0{v%3ezs27BnS;dK01$*Uu!_2 zGz_20(T5Lozq4oIuDV|a10zu{qE1!}5tPvS(X+zWakhL~U2n*{|4ZWTO~;Lc4M zq;uu!rp(bPp=n`P+Z2LohiuqEi61zgdV0Q^d;pk%DThNu=f*w+zZ^^1*D3oLv6mrZ zP=7B;Hs~5Bo-m&cjbajXdlHM`1A;_FQ7lfORcG6FP3ET)vbB*g)yytt4VnqB15+y! zy0a+}cRir0J{A<^o=BZKs zrk1?A`~tzkXQXU9Ml7sV%EkMOoehLs4>U za)1UNEqs^~-Z}KL{MJf87ss%~RT>x;_KME8q2ni2fuN|wnoAAoSuT)g1WDIw8Rx-D zK3XlcYFDfZO8_j#c;3mGDchIEF5=*iG1VA!qYTnw-Mku->C@3T*21g~5W<|3mjHy) z+J=n?mAU%F_|(3mzz2m-d=nxFr_+#%K({xS^LX|6)Zx6^j@^pP^suBK@e!*B@gSjp*m zQ!7G~Na_<|1dCbW9;YY;$3C6L2#75lk`$uDbNFkeMn_V%-qp%hx@HjsWc%hfXU{S| zJ%uJgRy5`2K=Wq)_4XDWTG0rBulF?$iA1U66;$SMN;QCDJkuDhOvMz&=%kGC@ecb< zO^D=jCQU;5qnSx>Bu-I-BIJQ6_hGM5(u%;|ikVs&!7=nFlVV~_RpquA1N@Jk6KUok zylBoUk!Ss;u&PQC9uO7vf_JG4F-QFB6drusl^}lHmA60PkDGjeDnf$?vEwL|WbG|n ztgyhA)l;9t<Q zvXBf*ON>CQ43F7-|NVia;BLNuAxY|+2QQ>?E3{pxRPeL*@Z%MJddG&W4@}0nor1iR zWr>=w?>yXO@!Xe;R+*EBZnT!R3z-_pNgfl)DesEaF=wZER9G(8Y6Ph$79Zf=uv6MQ zNn_Xv=1OSw=XVmq+j}LVMr#{jkH5#{xAcjaAEloq}h15%JWjQD5($TKvC*wS}p#SzzCez`~XoFGDuTaN7H z-zHp--ye>2w-o&TD5B;qI-_;_civPNqTKZcY>k@&E!FdA8cN+2pkVh)2zbpAOSD58 zj2R1DG0{@(XvAHdu_8V}h8Ag%YH$@MDZZo{UW)d-E^O?8=|-3sj*SOP#Yk1rWI||c zI}$EEohq%Ol_9JqR|-utV>k9<#O{XB1|58?>SBeWA}fZ*XlII(p|PIJ-1?lr$Kn5x zEF9@L8BnysZV8zbs48M?RHx>U0uy#wm4FlJi033n&TKzLxBlz%4e={ttuoJkr0L;J zRKlesC0S5Td5{wdNg7JBd>6QC1fgM#NZS7s{#kotALC89ojE8aD=ulUDK)n$qoO2js~vM_G=CEkph1W zuLNcdH(<_5Ys?NkV!9_qrmVeEp_RBQGmBtP7Fv z0`p(pz({GYjX&^d|gsL*gnh?IdoMoIT#h{UNz&txdq*Qt#>65BPZ-++{ z^siK7P<5ZWJ}aIi=7vr_;0NP$SOI#cpBG7QNFsCQsl%kGpI*82vh-nmjIx53VlMiI zc%aW2iA}$q4rCM7N}H+yRiwk|sx=*JPDL-`G~5P-_gOzV}adk&q}o0K<*e2*{+jnKHk7r%xPK{Ti)swmJjq9KuD z-ah#KdJhdlWcOSB?cp0PAmk_pDFp{_!Az@2Mc>$79!eC7kgR%`(@=G_HdLVZ1}9#o zVxm+`8degxnR*t)Na>*~2n%6Md{>*5F=*RJn>Cc?&H)X&3YsAzJG{E!147A5A03NR zh3glQF}?@lOymY6c1~Z#AXgwQ@kfhy{4Z?>$mbf%D#uOO60__Cb^Wz4`Rh8T@x@$B zp5 zqzvyDuC~e{OjG{}kcvt5dJb+j&No-_m;95NVj&t%EL2HZC!Ri725Dz&Ye$Lq#m4o; z?c*Btkfb?jeJ}qRM7@H`&t)utbIDIy8abXXET4}fT1(l0rKslzRh`18N2i6rsl#bu zTo$wfw@Qr3>&u~Ogf@??R7ZM)Uu=!~O?l2=@Sytt|BVBEOzHiF7t`#3txkZ<+$2&-!{3;ajSuk6 z_>EJ!!@_-of4gt+Z}~=e1a$MBJOJPM%htbj{3a_mC_F#0J=*&>dAvJ2^T-Eq{-uB- zrf;B=oF?`6Lhs~ObvhVCe~-6B#hVa9PW=J$#_D%J<+7?##yd$kKSp>Ie&&i>#X2IVgXD zOHh75P15I!??2y2iIV$RKJ4LBW$Ef{%ahsb>N9LDtf#6z>yU*e)o&GCzpqQjfY&75 zqy#-NW+jtgF44eZe-0Lk1)5s+ObT;8jc@TQh*?zSO#>a1_y8hOjsdFlbyic!Dnv0b zeXCD}D|oKbb&&90y%}u18SKB=-Q9YG_LSFiM|*idh5H;i@b%SEx&w*S$&ktc+=rx8d=a|=rJ$z*nf)V6vL;5Qam z5l5S6orv&?4G(YN>>j_b5YD4+fh$B|W1yXEkH5jcr+-?R9WyWWK0f!Wan9Ff66?y{ zxcilRF#SUO0TSMB`2*62dJRP+Le=GKzKJ;H-%;xfN+5NI0uX2J7o~?SP9y z%JsFl;lO?jb`EYr1TwJ_K}X$1m$*qYxH4lO+6_P(iL<$(doQRArSbaj$@&^En2{Jld}#K`J;;rGOOq?>Naa1#GU)N#3b zfd>SCy|Yw`t=SZPq1>J)nKo!}%BosedP>}FzyvohAcOD1H%q1+A$$ z3j9XX`LQs2YAq@*i>*!1y%lqH7{I(ru~1 ztIV@gmvkj2;k~8t*vJM##$cg=bl%~T1HE$^7wn`AjZ%{``6D_e>5i)oPWT-Jv|i4H zn5)aYrUUX22KDOU=F{|!0K&!9jhgzdkAp72{GlO6a3IymP%&z2?U>3HGnzyd=Jafd zfS!w31b?Z}WE){POHxFT;5b~5`*bN2JWlONwUn}~wTum1p zo^L2&orn=LL(0A#yrsEVNX~-5;Rz>gRw?`f-Wp_d*l)i7zqoNkvB}%lNn=MT?XtHR zhj)z+(c`LF9iN{b^olDj!Tq%2z+yBtoYx`xQ00*O&WpNb7oZpWk@Bpf0x-Ixk?hU{ zZP_s{e9$zz%=w%Bk)tQYd^Vo_K{PGs9lDpQ0|c?==BU$)S{B%DCEW&~X+!ss!D z?FZN47#GX^2vaH&YgQ*Jp4D<5*FkQoNEYg=C2y9xnBgn0ufi!GzHjtj+ik9=wr271 z&-HxCH4q%J_>9JoZ0~zJDN0VcuMOveY|fgSsCOzqfIwk7#)a_gliRN}09(WSQh1JP ziq%}2JlQvrmw_&K@P-0{BoXxmyn-B58(E2ctjiCd8dqVP09OsubCXl$%tlOH1T&MH zw<&pmH&TMExVMn|k`T#QTKD!=XoN6HrjyU5IXE0jcS{M-HIL}Yi9+sM_#KKcT^Qlr z6vfl>tA6S$DjU|c`~jh(X@2^h(|D#y2N#fJWqdh8dL|PLxc*jIRn;|%@3NGmw!dsU z`pc+h3K53Lt#C{u-x=J22C*SZ755TIv8Oz&us;IRuj9 z;7S0A9rN$Jq%bIySZc8h{aTE#Dy*2%RX-yXgje%A1<#;vXK9^ojr9^BJu9)4!!0_+ z>O7YVPE_1`SIiPH5!!Mj%Fm=2pU2LvgiXq^w-PMgPqC+?bNCEyg3GTr#YbC45 zbq*1tDXYAIsgVj@lGF!|AU@Z)?Er53EptXvKJ zc)2LEFQ1#4kU)cnAwN~TvmR*#y`R54Bh26v!5PqeR}4yF4b3$=-zS^Tgon7P3;Lu% zWx4XB`SGwh!NpanPek`d;1UZt9Ap<$_Hqau$3 z8=$sTcEyOC&YbOFjt7z)SDFOf^SGnH!eu|BW8~f7)8Lm2rzB$#9X@e2S4O3gfqcU6 z@~DAhX!!yxtLHKM1U=%9BN$1ajCQ%%^n$YLs|k@e(f+-YTwGsBq~P6~d-X_QKV}CIH}FxvWs)ftE21PU}7s6o@UA)JiWO)_l?@UQ|TTbiI95bc1HLe}FsX}$u*f5eaV#?|f(tLN35jHkYnAOqKkmsA&Hk{_vc9G=f$xS*&E^+}682a3Sm}jo zX5#hw>Qb>w<_MEEm7Q^isWC(Pt?2WyV+{av$}#^G_%f9ujWLMVmwL1$Ndz|u_(ys6`au)AXu1(|0#V{GJ1}P#S|^m z!(Nn0bxh(~T1+IQC?qCA8HAh+Q$|JKcGoZCPh4rh_)B3n#nxH*YPx1|InDWU0wf#5 zl7kI7!eyxFB4k!j*FI}62qx)D+!nPmwrd+Xc9$IGiw zVCQO1Qs+e2+6qmDSU!hq57I_d2J|n-XK-NAB|tI@*Zbj*=~z<|EEKKJ?U=+zgHtHV zk)3LaBQnDHiav$9J4aDxtSwp&`T88bz(KF>wTYbiY{CJ~e3cHs4ExpBgi0soSf$>Hoe{^m~&)}Vq^wG-_Y zFFn1``y$sD+jj!#N)$}?Z*(|FiUF|~nlo74`MlM{mO?J_3vgg~Fx#>o+{nxkc!hA6 z$gf{69)nY(n^hoFA+tsl4GPYt0)mAeAan)_a@gydiZcy>B~-l}LdEVW8dz#NO!{r~ z)B5tyb1YhFil|}5(9t_w|Ai!cxm!J8ttpp+S_%7A8!pxX@MXz2hluMHmxP6L4mgk- zUIM)si8pUz%f(yslaLQ%FQjvcIYcfc27t)Sup6cF>J*~7Hum5}^3lTv!LL3x$phqh zFrO}1K5#qARnf3+nRYb4c({4`b@K3V|6tHHvIa+J=hSQBF1!vWC`JKZoz8Ka%fVDy zgArvD$zXT&icP@Hxe=#j2VeaZ)qq# z8LXKlyvxsKOnY>B&P$237j)E&9;X-z2wrTTnK>OO7E1EWB?dY?KNB z8o;F&6)^e@F->Vw>Ia#qG3_^ZVCpS=sfaH|yQ96z^fD?DfnxHs63Wovk#UoWtH8BW zNeZopVuE(qF^f-Nmv}92LD^drIh08H*4T_%tM%EtYK2<4#5sPW&ry7+9Fpx@!eXAh zJ#K|0zS4PCpTk`^l=U9^1|^n@@$~Re;Dh=;*8ziUO-Qt>xNN-s`1RB2{oT#e{ezFl zDdmYhsw5*M&w2#9Cx`H|x^qrHqkH&0KvAyq?0 zHF&_FDAbUoF$wtb90Uf7DUkBZ9~!&e6y+DhmkyRs=BIKu@@rMYxN;ZVW>Swb#v}&$ zH%uMY&cR2D=$|MG&tGj3f%lP9NS8-f_foLd(B-B;I}%KUmGoK+c{P^X z=hP`g#zXt}giO%d!lzbk@_e7j^nR-OMqjDtPx>udNpkCrYWMHuj$alDJCjwVN@%K8 zpbr!^ks4i@b~?uM6b=~y5jZ4Ned3jx>uJ}fagq>qDcwl|10)G>uSFUnlU+UkRamU1 zhj4PpKMA9G&e#f>x9o;PG)6h(oc0{yx^PS0_~3+GSMnS_%)xxOxAr%hU#=cIl6Lnf znrl2QY7c>LEVTR-TFoDScuSdMDT&yb`dQ1NeYul5Luvf(g0lH=FM5UYOnYcQ}X@7y& z1i^zD5(lWy;hPPc#8PqPE(yodhe&d?>Mt;YB|2b3tu9caRP|fKX|sTgyTtsj$%95mm)4s|aVMv@T>#Q9M6^uLW2!_7a#J#$o9WJq)9Z~bg^0`0;wxB67eL?Gd zXllAFsT2Xv*I3=}PVusiNrZkp7dh8(bOpGScf_hEGr`OcT=g;Jn@!e4;fDx;+#~a% z3r!_P1?hqhd~lGJ2G7>9qq8D<*l-HBSMVzYukxj}$N4oT^G<#Uey>F`gwGxp>wo#m zc2%aTM-xaOs0S(*Q2w5dNqm{@SYV*GdIACe&V_0W?(V7b>JK+?A04vh=XGu}*pKYw z_3p^nNit^S+GwXJkQ8{cxBZqyWRQ@c0U3lha4Fktj1retv>VZ)4^J1jCJhAcE%uwE zwo}D~)<^O3oqU=s$et#PfE_4#-MHcgl-`mgXb}@zW(KUrlYwdZaphw?CAyBZZMPJx zq~CV7mU2a^>SDwv(-U&fWL@bmYbq$$L*xdkh`L|LN&H(-sB<3I>Tx1i+)7PYCOII? zz@&wsjRn4jA$l;v7EJp34AJt`rZZmwnPlv0wO9s-ipN8BmSUVMrA-yRcxNYw96a6p zyyhuU88YSdgW4=B`?+;>@R|N1Zl-B|aCZ)H{ZVsm9c^ov>j;~s3Ku+bA#dOKFom0V zXc=Rrdy=4XM3lc@LuK)AV72W9{0t68QxuuQ^WWqfDR{cxG#`*BezKL zI{C2|L(koH(nt4auE0zda|hBP*`YqD{P0K=7|S+?YRR{Zf)#QjyjB<^v_l^S3k0T= z-HP6~hIvRV6Q|)}HR6N)q{+yWtjl`NAi}d$aY?r=D8c$&22yDyETdDxa=7a6Z4MVl z{qOP508O_40+r+mRToc4W`PNf&NH}C=BVHMryi%h_(%C*33cJS$H$AO{$B5I(D>$W z&>-h;2H*Am*WP#k^zdB|X_)xx3;A=3zxZVIztItS+KV2agGv9``!}cHe#ozhkEDgq z399U5At*Z+sWPs`^MjC7T4j5-T~NBmM|W0NrwQKP>qvJpB{OJ{b11w-y zPa*x~wyNfAcJiaRZ_A$aBkO10YMc{KUlYD!e)?IsXF~~IS}wGLY}t@vA@9{}9@elq z9@qGZQk+LfpiRkgmUcrA>!*u{PwS^==S2={LKFQ&Tx00`2y(Tcn@&)ulZDZ9K8Ddi zsuh=Uz?Z#}EIrk`Nd$%KwsAUoE5Vs2{M;U#j;HUy!z9xes+Igtoa5cAFBR?QX7cSa zA^OQojTU|p8!TiK%#kMXiyRBPX7L>nbqdCTtmuF|PH&Za>TRO*7x;uLt3nn*exh1C z0p>)7!KWn)t-$q~FR?O#tMg~$V$k`QJ0|e~jC({FzDJ}!Y|T%I;M-NApjqA>VJK+V$`b+VoFJS{lj*-rUjzZW;D1&lDZ+Y-!1&*JNXE3)lH zP9J*qqFnB^x8IsR+@M%tIwN#!mmq2?aXHMbJ8d#_ zXP%vngx6Eb?4GBl<+9A^|1TMGj1m;HvI7qcE4u>RHYmQj)TYQ=<}u%`WPIL%ofC;E z)g%#B>RqNp$)+k3(yEig8vNb!*-SV`I~nB%@%*Y7m&Q{I8bOiNg(eq-;;=%aRpbNC zWLk2~8x~Fzd*v=VK8tbLAhJF#` zR>CbYK3p!Ee43^Q@?S0?bG^rhKx#kNm5;#H^c%fBh%x7iqa%Vl9GPqgH{ zIa8ODJj*gvi#dE@HTx{5Ci+EoGzuTP{SwbxL66oj-{E?!Ag0qyp{S&gP2v?OL&bDt z$TEzr0e#%o9(|?JP2q<4{CK>FPqX{G<8TQt8)SO8em z;)mmlyQ|yvLytURt0_P4G6pn(tj4rT|d%vS$1<9rv372tn=yZS3!NfmH;@+Vhk7Jmm?+)r@HQKVRQzg(3Yx70pBMg|JOM zBeWtNS-j`jsa#UhMTOcs%D`LbS9a^JOyJXDaKm?svShb*$m~q(NSLbA22AOolitd;upF~R27vto&k|zzNWf+Z7cm{p zK??Oph~}eEAGvKoEDK1`NHaVp#3`AW5?WPIorNla4^y&Y`t5Wd(D`Q4lByaNdZG%E zn*FUL^To#~rSw{Ipy6EjboceL`T3$@M-bBJb7^ZXgdmV&3iSdFR&#+$bkxiPc+OJ1 z!;6t%UP{ZK67^XsBGoXO{m^UYHJ5f=iw4a~Ovs<-U>A%S4w1A1p<<=_`_(czr;3wU0DXw)J3nj?10sn|-v};bTBG(Icw840Gfzw#3iP0i#h~&uGX^(R~ek zsz9RmiHId8gwf*S>jLG$2her>Yu_;>K`#*ZT#mlY!>QTn<9_~S5_ zDb0(ldl8DHR*aCq>xwM33EE_f+OZ{m){GrP3R;{`RZve)K@uX=YU)CAcJXxIpMud$ zK^7tVU6W8Nm-t|PRA5t%pdLMQBik^}TuTl{!P|Gy2jAyGStT-E| zBoU&Tf(^3E5L-MzoT;_Mnl;iNl1o|M4EapMTs`iX!kN3WTjLq><`8*Ev0$Vc>DO3D zB^XJ4L4=1X$#r&LR0T?e!z&(0mVbD<|8sMDfgWWmerUY6|{cPU?KgE{x#f_H zKd(2hKTAmN`|F#B$ETk!9`O4Ml&qxI$Vy4!K8<1>DXBt|DGtxhk*-v~R$%u9Z-R|m z2W7ln9)TO>z?9o!+jDH$M=+Z#$Ck;Ta4N%91Pfw<9H|8yto)`d7&bv^D0oc`Ssy7l zZs-)wNV@^d&2R6%}pYTth#M5i`Mi_`{7` zQ@T!mhL_aTS&5NY{P(rSe@Q56p!j?&aizRlA>np~_BvA5qvl;-OQzH-TF&7M*-0^PlMA7DToRL7!X=bemeUMBACSy=1FyEV^;%!m`+B);dkEwI&(2FF^_UyUb5l;gBiEIEb zzwsAfnd@(oUw&z~SOKG^E8M;nuLjd!?hN=6J+y%rXYOI z4xzLs=#-|sA0gzG2hCZi_Wl?VW$-@?z#{(~gR#4{MeWObepbHg&0F#0lRLmDKA% z;lyMN>0xE5MH_G{DU~88N*T@w%&dZp36At)lu*euY=A>GY~(UT46iXRH?C5}Ns)Rqm?}=Uz zP)a#DN_?l8V{3Iy965nNPUD* z9oZY68`g0SF~30FHIz%kG3`Bgs=6FfX`C>vgntguEyMCoa8ZQfMO1cUS$&R=U9VYW1_-QMph%R~?Eir1Y9iaR6Mc*O!F{Ao8beP4lij zPlvL!*yKKHQAU7>@>3AH&C3x{q^TDKfgXc?QVU3XXKHge{xv8p~B>s7adlyg+6aTh<80ovf)c7;-tim zjUagc0sS;4qcjon-*-`KaUPjyl>kFqL5tdcXvE?*m29_>ICnI1;CCBD6NU^Ua`#;D zb?4}!oYv*hm5jbJfv3cMWtwN!7TDP_#IqF?@zZ3&Vd zxnBziqz}9Nvx($95=4}b!>Qa=&O!a1=^zU3V^%@bjFMX9lvUk$^d87#@kes~7SJvb z#fQTn$q}+RhDUly4ILr*^C@bSaFnJQMQ$dXIAE|R4+qHI8X(h$rh1emyRD76Gb?E! z8zR1W*argKHq(;c?+8i(Q&cijiH1u2M#dU6R)&Hv8t5YA}oKhI)#e}C{5I#&Q$W~Ns5np z@&T<~;}3c17H&vGxIp~8uEO+CV+Dr7qVK0TU@uW-`j82!k}I}cz10vnR}7_5@;yT$ zSsaW2i&}#MPK^A-0z75}_EDBz)aUT|vf;yX#5hnU-$<91cOu22tH6B7U92T8n;uZF~KLG8RDO2Z~}at5gFT(E>GdD7(gA-UU5kMtp&ai8F% zhx@eo`0M63Q8&e_+Fab--An(Y&1{qj46Xxy;GWoHNhgUhd9Rq*SJ~6*NXE^ma zB|7A}2F}*ClHKv-;5GG$%$|yNK z^E(5QlSK|^@j;b?{LS?_d^KQ}8E)dT)IPt(>Ytg&2T={6G3--h%X`hDO(KM4WxDu! zFbYZ_)-T6!^dNq;C{$^8S8YT34O*3o&rVGTlknbW`aI_YKz$tbhFaKcEA^z0p|ctV zmD&Y@{!%!qIRGdd1*jrjI}x5l0uC&6|7~-P7_5|Y01%%IXl8Q=m&h|Rg{U;syJqnb z41k3m-WGR%t+JG;7VRl>ClvG+bxK)5Ffh{QGlMIdm-q#^R)aQ2n8+XEm!r25;pM-@ zt8oS7!>8uy*Dq+#eDP^h{`m9F-PQN<%gxod@56}vfq*of$)LbyH9R^7*Fs)jb3R4_ zkgP=`2MD}DZH(?Zfj4X_pZ4xdBH1vc46Pz`k|*(aY(pSx1@0(mMB%!iTI1RL_5^}l zBA(I_U;Z-&qCwZ-iEYi|NjlXByQ(Bcxld!Am~3zllfQgLfS9VV5=%Vx&>*$|eqy4k z2Gx^(loO{JZk;$)#0t!S(cqN}fxu=ADmuAI;lr#+o$3_6u+T7b-8JXQ!}HY{?(0Gv zG2pTuEW?Sco{=6*DQJwYip_M6Ufkl9r8X)F$Vjx)MC_<5Iu8>cs5id1uqoKsx!CBO z#@zuKlmiL6rs#HYUI7Ml5@hEsC#ff-+Th%-H`I03vyQU+6(Mm>!Mlb63;R*LT;PQS z#w@qTcuXFw4h%9sd>#>81vr&vad__OF+aT^b@j}M&B(vEv@KV7aB{-R1N_iqs{6O| z!tek*fW}_EE@2 zX?uXXX4Mu2Jtr~r9N_N85W<{fvi$XNbpuU$1gq)*EmF{jS(hG6tI9av`-~p2pV!_$ zC-k?S`OqYtA=lQTNED)po&@hsX`09DB;9425=lF#t9&<_Qn7`iDO2e(+mj_ON6tV| zju8xPK{bG&GGoG!I^3kFNWJw6A1~CPl?qEli4KRjH4?uDEDWWAQXf@JNoNixqMxf?mh0|Pn2rOKKUtr#~6OkrV`r?vY0?VbY zU;1i`LGE2Jz@ zAh|9`$kE=&Uq4DSi-3=!i=nO%_oziHJF1}h9hH7TQB47(DBhfsKBEgJpOG{;!7IH7 z(#v=F!qHI+=Z-ET!4Zu95y1x&i?dWJ1Y}ToLt5ASk20^KWh@TNHNsRiiK;B=-wi4% z_=bWd7zfoEJX>OJBpTR5xp&rC$x1xIgPo&J=QO?}LyxGqN|l(oBm9X(%8$cZWs~#5 zf>P>la6R>EZ%D%%ZAbZXHk`xo3gx0dx3l(E{{*E=|Rk zNWHce)_rJekLYjud5uh?>mvT4u9)LMvjIIFsf}U>+=!lexGGB_#3t%)20$L)2ahCw z%1;Gb$)VXA=BuRIcnY4I+7{xk=V(KTUX)nGoM1wmmjVW5 zUO{V^dkvq8f&ewSjLEi!`6byxI8yoIbk-7lbv>^YxQkI#p*H-H5~5|c zt~v=t_C>$N0#VN1OIPI9EbhyTPrgW8eu&K!WaBwe%uXwV3mudAp1NiiaMO^pK8fFF zEPD@cgx04mW;b8LphgE>qR0GvItKc^o63Cv#15neL`chlWst@r4f&E&FvZD{4>fDZ+bu|;1T*|Z8EKa58MGAKP*8wB z@;ygl#S|50@J_(l(|Nm6bPx+fkmbz+o8mz&3ya&;+ul;q%E9^cWF@v1uBTYZ9dL7m z+@-Ut^z8}_UVDG{qQ^gIalYsZ=H%$=YsOr}hgjgLP_T76iwP0Nm1{HjRY3;d(V4sA zX(78|n<)pl2`2Vvve|&lJ>Wm~bZZFI8s=lE9%A!S(qccjK@utK3A9p@h#Sf5T(}NBX7RUt16{ND zCc!?#yI_9iK>Lga+s!4eP`$alxJ94gPdE2>>X=;?jk?kbGyF*Qf|84eXT)L-rS$W# zB&jPe;F&b%m`=hMYcGjHV=0cZ9PNStd^wgSWl}7?R9LOM?Y;s6J@5f~6Ztyj^s_$6xID^XPcTT!O%qrx=^ZcAL~$i{c%$ z0|WUSA6NBledzPh(JiZpIm9ftmWhPcxKw1=5FIInUP#` zdkBi>@2BTz8=)1|2o12qq*f->CU%}4_>h9$PEWEiyghY#@h#X=yQC8am%N?yPkcTeP}nlM|D*mx$LJ59fDJf9_zOW=6s+&hwBay zIw73U1EquzePO?ufzg`9hdX7UWrKEb3U#MfIR%HbV{vpmsp}@+ZF24i_2>*PCkSed z`B%c#l3~gsVwBkV-|hLYGY#EO@W1m_sBBK16__C98!L3{9^W# zwLuRtAE@&d>pcq6UjAtx*C<@AuW^sn6%<)*Kmx+<#fXknXKrL|PN8d`oxFoOk@G{a zaomS2HyGmP@dcKQQ+I+=2Ky?1r(eFC#8*a5RY0x|j!L5@3L3VjJ=nta zO!oL~NF(Lw#Wi zm(Sx@ZxykoL+s1E?N&I(?J`K|*Xt(bXD!5|XT1$8jnJU0N{}J?0-p7D!FUja0R>7G zMKX#cS+u_eHDU{yVbuLvu0V#qx5|oI>Z~|J+K$v(p#XC#+~1+y9JQ7luUw{nGGrYm z0UOyiHj)!XeGcJNXM_3I>vQ;Z0Sw@<`TqL@T-LMs{zdO^aWl{VvH2BIbEKm`y?|mV zYS8Av3;ZwUCmK9`h>XlG#R2rBi1A6a5M&HWX>iZ5U0wsuJVgz4E2wGtHR^Kq?g$Bf zXdfF$4D^iF$2q?rb=FY&S>SQ>qbDz_Hb4P;^LnkjhKARZ%64DP75#gZ5o_p&%!BCIG}h1oCUlq+;T zh#uR(DBTEg#gl6?L=6W9^gjoQXZQhf;EVJEUuj#E=O9JX9k&n2ME{1s5Ok<8*-;sz z4_4bi??{^9MrxEMjD9Tc#9e=0<7%3_%U`vlF^ym}oEC-<4yA9hLG;jUoEsMn$E<|W z{5YN@-p!h%Vw7JYAQIz2sLHc=j;eb&zS%=;X3;13@(*uI-MP!HHSzhKem70_6OFsmp&V2*8G+|kiW3-h|9VP;{gui z6u6ObBno+V%7CX!ZtHlhl)Ps^Ynory7k_RZfBpRN{Vv? z0v69W+&MW16N!G@<9kC$MGitlCErdDU^`_f-)?)HiwHn64qApNTEqNOWX-aS7=Pc< z)lgHZ;6nvgiS}~R3vX3)YuST@Wpc1)m0Yzrdi3_1VqIArG7mKO&8CPHi;7FoH9dDo zSOLU}d2=dpikkBi>6{Pu8*f`Im_#&&ho@-Y7XuM?bb-lt%;H%nby^$)Za{Ic*bgEH zeHWtQfKx4zSB%hB`b~QMgM$!z1JD%{TA31MP<+XD5J)JKxAf68$xR4%{RjlDpw>suOLJN6jr6frb3hKa5J?(Bas9Cy$=Fz^}6)|3>- z_Z26!V8@k!E9*z=MplT+6++RE3#~!`>6L|O?QqCKM|r}B>`j%#3XOt+&`1mJCHtY73|^@n)P#a29ep78U-)pLJXXJ(<2zL#je(%Hxu*YB zX7J1%K5Hv|(?1EqW_Ww^=?;BzSgw2#|0r};kYA2tfh)>#N5`6QqWi!pmaW2hVq~}g z@?^Zu!1nky&?dHevYDCZVg)Z zP2zRsNd?|2@N8*9#UNjhH^}?=D{j!Z|BCyfWQ#B(9JePjBNQk+c``oa_IX6o6|J-T zXxNp)6~3?Mz>Z-qN}PmvQ;4 zU8-EdnHL<|=Tb25Oy)E@eOUFX?X>dEGf^I@7zNU9sq8mccO%N&ne(WSVK0BA9wWhb98oS6@rYbrwivotpL)FR3Z9#27tXbkA z!E~Dsd9HRg7XGYE;K3xZ2-C%DsvuW}Dh>Ea&Xi$21kTC0>K^R{%bo9w^>ulwQ@8 zgVn9kXgXE^o6#LOe@ZFg7&I9h%iXTdM@I zvlX0}o^eYtkP35DP{XH#Y(3~1m7?ZYs8NcKB7?`o2c;6vRQ3esmet}`(WP`cbWBP( zs(yh|iy*AS5GOnII6PaR4L|y~^9Kfu-}6g}?Bp)Z;<{^@k$VJ6Q`Z{cOQ&UZt>iwM z7+ZE)cT0zDJ1d=KAdn39F{y{dL)KR7;>0OD_)NiwU=B7{F-nl-w;9@soeW5#ry!%q zO~vwM#x1s#^Wy3hzO^}6qR%bbXQ#^vaPwB?O|d2*#aqvTX+EVa*^z%su7XtP93P%-%L)vE2NZ# zi8A;tK0rS^pIhx1EsU@=%8O&Lw}6ypz5kYO$?@uaB-{T!%bPy;iFXG!AR{lUPbHG7OS~ z2+LZk`+qsceIizGRW|7nRDe?irHxQ6#UrLp9jO{tr%pM6z=%eGyj+b_d~Ru-cyXei zkPDozEz*#DKl(*WtwCk0K~@yh;bO8wlrDr^gG@Tyk#ckS)C<3utw>`e53e-Kv&9$& z;Q+d7bhQZznJ1=5)&w!wAa@>PDq~2gE}%vV;Px^KAlK*7SPaHmh{lsOpOd6B*%cbQ zdWA-*CLAIQL?m6VCdjLmE?{`jT40%7dF&p$q7J!L_960OQ???tElmpcn))hsotD4Z z2jTCHyi3FK?c_c6P4s)WyHeHTi_`&0A28I*Qq{5vJOEM~5#N;)7o?Wf5tV$}Y-GKJ z4kDB|zrYm@)yv`ZK1bcsB|v&z6tA?FcwouR>11_$Hf~S_)ZgFR>o?otOH)isT4dh$ zpKLcc%eM2bAAu$F0Hl#lShdn;YdE3l_!R#1(Q0wRjHd9t;LJ1fmKb3%QJ<^A2AU3#l7+cjuQ+u)RE#CzrbkEkg${0qbE*j-8A#fFAw)mxF@ZrKcgdB*b%&! znZ+F#vT+IoxRdVR^|t&8GAqJEmsH6NjL#y~=(Kj@AerUKN1EdU1UWu+Od=2!l~bWM zuVLh}g){r#=I!JxPK zuolB^{i65B;o|fk)}KGFuh3KV}Le}dBY^UWjDT6=@N{+q4WfBKHy)1YGa z1lvimt;(j-6T-Y|*Du9fA?D^h#VSJS)Tz%B6gy);7pT#L1Xm~H$;t3X@j2yxB|d13 zkQ9{9$^UXkK#Wijhq~b$gv!N(3)A-T4jLlOP_FfP zKb$uMOcJvo9*{%>(iR+n$lg{%{a2>{PdRg{U{HoEQOU?`XY&F__` zR9q@t6@KOf}>og4}IWc5b3;@$Ze*DQNU zg4+rhy|VF)LxbDtlO4*Fb7!?NKJq;gNPxj34jq!YEu4j`J4b7@RVt4xCA1ak((l0SX`97Rp|A3h|dHo!H*CLUKSN>jotu=;rvW;cNSgM#jPXhn{r>{Y7TD(+M=8yp^s~V=MBuG za9I>gJEjL2=Y>Lx+j&QC%Q$rRJi@hUj(=$4J?5pe$lvyBo~Xj%Bs2IRf&enlG)K=2 zVVAgajMUgKRq~bxz_fenYUV1dp zCx_Y|eIFwUvEauffV+mP%(?WD_0

`N>Y}wPd@c(2;nCbctV%?gYhw))469>GJUG zL?dYf+)pDSS)5t47!AXgNC24KFYga65Lq}^LV9A3K=oLZg4`o%;Er*%jFd;-mKbrt zP=I+ZqN-Q36=zbJ!4o0U#y14Bwe@A^@(gqM^_+X|wi5%YVvR-@sxtD|JUf+dr>LapN^rIH@)qF z#Y`eJj-N@Fx1v?rvbdrP6g?YCmx2C9Ubj9&g8{J6=#2Bn?RXNl7yO?B20dvt*n>Z| zIPaBjtCn>pnxVT&+IEp??@*3m_`?MpFVyL1ikQVERl&%Y^WPZtNsZInnq3Zi8 zXYe$YbWG<3?P=A?s$F#wx0+P$Rt9!`4&Or@O_2qJ`U&km)6znE2S^U$JY|;}YMPYk zn#JeGR|{W{MnUQR>YBwzWS2q3yQ6Fk;>r?r_UUu;>-yqB{3YQ6^3cl(cWI91Khl>1 zZMtEWMqxR7#Ea%{DS|pCag9k8SVHqVIT3{b9xg7jr43|L^y{g1OXZ)|u9i9ckc*Hx zlR4Tt{jonlal~YvW$i_p(Pp11mh6}ACh^_sVVX`x%`W7>7~3b44o~$CKr^O0C{?b>*vq3n|M{Jauc|b(uW$|Gu7@(}!xg zQ=db-{dPB90ZtSZNi^dvy-kKSoC8jw94-bape&9a->ZaAplu=*H|(5LJqWe4Tjk~f zZ%vyorEF_BK{1&C^`gOSoL?*S_k$nj!%!`(r6>RpKS`Gz3*;sze^oR1R2DGjR)8~)mWCO`kI`IRhE7;K-f0A=W#JXZ&z$9eQjY>b^*n{ zmGG9daQZaE(irKa#;y`AP2=CDr&pyoH%N~gw52(Zy*WG`9zsqL3!^8*zmV3mG$Qg1 zFJcM~*-0*qhbt}kF;G<{c9=sjQRNkaOC=%pUCZ*XOi=iS$iDzMU8LCZz7>#$wc~hU zHz~Jz**!azE2D}z$({T`ig|>L~S;Ef2=x0N{#^3FCa){ z9jZ$7ZSy-Q!hFWH9Cyv5T;eFz>KdFrJzMH;{KXE{UGCp>Euf(n)cub3fP;VuTvL|9{-N4()M`5 zXZ1U{JVb+zaSoz%YYx9oog(()LyAI(2i!j`zTVy4+p)rUVf;(+Q0#VGd|9w$UAiAQC+fVZ_IcwcpfRwgM*X)lS> zgT?CXJ+kbi=&~pHM`jEMVgvU<<7kjr8f;fjWr6FZVbAs; z1nVY{t38G|JUU2+r ztR(cyt4-l|YddSTc<-3R2bjnZ6GV;JBt!Uj=$jJrKF8C;vcF|k^|2BoGYRFkk*aa> zKq3lZkD|gFjrC!LX1<7)B{cry9IcZs{>%MCdHV$daP>I~;wk#;govi--^eXT%1rwf zyIRcFCh|d%Ba~b*mvEia+-iXDYyuU24t5T1kR=y`!YN~+Med}psIX#~j}-#imY!fr zJ|W*7>^VrA*Lkin<#6<>r8=SfZ0JE3`4*`>7{G3X3y@Qcc1@@$0>3Y$GJV|aF9E{? z1>m?Dv*oza#u90q&o|VR40O2MqJon!)_tnitEiIU0w85(JZ&+v7e*|rAX4NIkos!F zS~ewR!yyPF0qe;W`JS_LxD)QLuOHV>D5YmLJAE6?)Azkw^nz^ezJ5j%A6uvd94yJl zEuE$@8{0mYVJz;7d|WZA73IUnrYYe>;C(~G``+eD+JN#GTRyz(q3^x0mUw8o%i_F< zZOPpBk~yP%qpOG}CJgFxxa?RlAX)1p>GXm&PDhmd;uxm-7T13EujJ8kdTvL zaeVGna0+OTx`W~r&>8_Z>RdJtsF%Zqh~ML1_Dj$YFMf#wJsC-|Qyv)Ax0an9&Q=y) zIx+Jxf-~yM^3-q{Xih8!uy9QTC%V47@n&pZZlrgH<<}fJPZbCyxT*3pug~EUL5wFS zaHl5HDxFS#gkNd(lHtn?eOrSH2=zor(*e2bxOG2Z`f$U<5&|Bx#UIo=fJgMutj{MF zTf!Ri)q+a_N=Ft3kgwr_v!G{$+iQ0`MOJlgH5W^VmI* z)5T+Ry}o$Dy{S?IYn4bzS12PvByvA&D`acScSE|RdO-;cM2%~Al*}VFTV6{pnhYG~ z1glQr+ms+f%Q+M5bmJ>HXJciT7T9zWnm_pDynop4Q)9e%`^~Pd|PA{1HDd zKHmJ(#l@G)NBn+$dw=otQa(YGYpyL zu$R}N&+^Mhl$Ibd9nZOzoMiv^-95e_HH$CNWar{h92qaK@!`j>H@8o?o9gBD<4;)H z%QaMmyq8`nPkCY%V_>bLnC^y96zgwZz2W~RZ}!HAWhEM1t-g|0KrbsQ+OEj>4%f!y3*O1Rk)x5<2I-Y@CgOkO$b8Uvb zj~T^qAmOejdyPmhe|L-ABS^oQDzX#!?fDxX5C&*BXs=f3+KSqa`TZ~Jhmkb>kWWq}#iql9Me2`c;@hfa$z7tm zWRCky$m#=Q)D^bJxj4~DN3%mIm9`%VfI){yx)H}v6NCe??g0)G+&J9;1LnKS!e{nn_Yt~#hk!x(X6&k0B_{g2 zN>9LZGr1mSxlY+!O#Z=T(cvhvC{%kIzis#@h6aj!gCjU$W%>}Ta;YA!_Y+`BZiA3_?BVdXjv?H(ezaEd)YkwO_#5^ z&~KBt&04t*cK3h)-_R>oFh&Y5NR4NxosjN*KL6LrTWZm?)rfAYRY})+Gxek>DU|l+ zLK_UFI*V}SV5HDwFjpQ?nZ-vCjgv{9-xm`GMH>f#XJ&+;OcDMNM;`Y_E>w70?Dpdn z9dTYj0S_~Ipcod%Kv9BAmQ!VO(?c$j#58G4>pzJ>3b^Wx61?->Zg^je!3XG3)T&@( z;?K<_&~V%0+5E=^x|*#vQI-cm`W40hqU>D$Iq%Axys*lNJQqhLpo0d(yS{NNDJ*Yw*+_~-$fA4m-l+h)6g60GIT2z+gHEW&h|eoaRgH7wa- zwCl4PYDK|V6=1#N@~CwbXJ<1sIh`Tt8wLmdNy5ypHU=I*I{BWQAx@C<-oPTsKNfrd zG;Ar0NRE2Xkp5Tkc>O!Ey_n{96xv*3L$2p-sX)MJb&+tmS8o1wjmi&BQylkweEjJO zMd5V)*IjgjIfMKs=P1=F#GH6J%q46N+ASADl4SajLna?vrfp{ z?1Y@2ZH!piafKQMB@#92^GVY}jFg-{qX?+$8WC)LOGoMyO}&iu|KsdUxZ^sKGg1DO zdhF9a;FzLOSh#qbgB=)RQx!l`W6!Ark)VXr5I|#5OZM}Be_wnNkr|OUFVyqe76DXd z-J2O18T%r)YM^a9Y|tNaaeQ&S_~rJ?CEp3-kYq3+N#`MyBHd>4kT`^qJ47rp#@m`E zZ3aLyNzXv^zp3v4s}CfKMYPhIj9s3)sV_#Vhp!}Ve!vw8OQOl%W1yTctyQm zK1FX{mY6~_9psi1^Mq;QZiCKC6*Yfuj4dpKd?MmTKYdzkY#w;-{A9X3ovM)>74qS4 zL5>-JlxNw)C|~6%e4^-cCB9eECqBfbnAv7z5+6X9?OS-9td+yEnkEay@zm4IGwQ}? z8h3_FwxYWVvY8`cAP$^?E`B&OJCQo^OeUb(US9i0nIQ5GpTm8y%ht|^okLh9B!TMS zaA$94J4WCOcBO*#_@2=hl~PWj`a(v~wv!xG%fi4hV~1l$BM6j@W(rt|B`|lnTLRzI zxB_!akD}O{$r6KCD01oz&$0y*=Lb^ymP?V`w+pdN#i752e5gQE zTTW{>25pJaNDN!np2ohfEo>G)qW(jXz6ACVzy^_Rq)>w}qOOOJFp%e{R^%5%%^Fm0 zni)I=aURXjx3=J*9I4%nvM8i7gH_}=^yly$4l7w4C|oOM4u<(9%}Ez3AF|QKt-3F% zkf@ls1xm*y7@=>+>Z@z;PU9So{Mk|Pol5lmIQKA#k7p{Ra6Md#(0#%2-!8z&EIvWl z@1RBk?J4lFrT8He{z{O-e1;--`3$;KdT>CjN3@Wd;FlX4&n~~NE~c+PeEBk2qH!TP z#zy1FOm#AozLt3|%A83t{g5xc#46{=jJojZn8m_t?HM9YwKJ&B3(iB&`q{K(~f<(jYk^ ze|29pGMDdh;eGb~{>9CgYZz{M=La7@+dy+?RV_6^L>eXV^6DxTW|^A>9kC_QVF+bT zuoCQxS;C9O@y^G?12p(MK;e;EFs_~@O1noXT@_P{Ff_*OpH)<-$BjS;zD^tP2y((G z2Ltv@t!F@Aq#XWCE4Gj8FtXMrup&h{_pn)wox;*@X?A3=+F#Gx`kY|&E5T7 znB4Z%$SBQ4yzYxg5F-+0b>2$Ko5y+^%G%NO=M<3jX&$QVCz_|*7DK_oG0T(+eyPww zuLV&yiBlQ$Me~z6MR#e7qm0esVx=i~$oj@jL3E0T%d5L)cKP`dHf7LBszZVigYDtc zqeg?FtQH_Jue+t+NQ98!^XfU2gR>H(`g&C^XDr1-lW!y97sCJ!jIF*c!5z9pvD`0y zy#9;m|K_DGdyr(?1JvHl#*-kzL`2B^W@HlKR^`ngZ3b#FD3V(m$>JI#^BhInlq^xH z1O~|Ycc~(YSDeN7HA#U$3w=aKu{0UU!2=_p^!&ACrp2Rn} z+#>K1#jSM|4#)X5p;WOxZL=_iX_l8qkh)^D;T0_zl;}zgFAbGju{H)=Qt^btGMLYE zFw6yqQ-+M{xvTk`{W<))Tn8HU1y0J{#eR{$KQf6AkPYo_oF78>g|QgSoT#hPYWjS( zag2Zm(k!rY#C-)rwIqCr+TK9h5mMH+wU7AURQRq)JS7eY3#tuSa~9b+ZZ^4QHL%z) z2f>ZW4YDC7+7F}#Xsr26vXcNf@!jpC{@~9spzz_D)~FfWl(H+WtfKf2Fr(rc{-k6uSx9)DaI!LC&&`0rTdD(;N_p` z&UdSCSGTuKJ!GMV(zMDm`LgLd7_ ziR#zQBm|xD;1e{}Q!{`tugb5WDg2-xA&x;vy+Tvkrf8~$!qns^ep_Yi908&CpG%e} z3yLwuor_Y-i|`7O2V&cEX&;UK2z|1m9rA7QJSvcCm_~s{vIj*u+yD>Z9F3pky4925 z-;Jjo9l(i(RxUp5bSz5u>l<)EnbkcWGB6ejM56kOfkEq+B{+3lT#kw?BQkHt|T zkRcvJvFvS~9z2~DRVUyOJUpGi&Gi$7)ziWS6-+p5xF`fdz6N=Z0vni#LzC|R7frgm zExMIS=3H1}2QM5kztY_%jxFAcuHsBionM5#Vyr@y@ z&9VA^1fUU@Ed)Sv_e5O7zq8+Fk9Pev-e+qA4SI#M@fWaxT^w4LLY|n&MfTOY7$d-V z92k(HZ=snSirc^!dA?o3M?D8*Y?@+}2Tc_(b8A2>%2}AfuT~x@SPpTd0bhH0#q&2{ zKpJ3H4mug|D^cl^4C1-`z5f*sIKtx3Lt)~h_sL!2OXDmK}^io`hfNU zK*hg{Eni?)aL5+ryU{VODLpT<677=e8xaWL0%hZY56TEuoAd`CvoAuuh~k^DM`c0f zX!5=4OyGj?dDW=>R5K;?ZR(-r=mXLWB^6*^i~8Gib^41SAQ2~p*_22prA-lUT=3*b z!l)9+vD`Y>;`ie$?ykn>!5$)UB7p<6cJ=L(y@nR`X7I79f)4wIZeBcoS$n&Tq#bGG zIbGePj3Sch9LYIwZ=4{PV6-hbAY zgu=b;4b5AvWt8(?Bizb;FcpDi{jl;Sut8g?}TO zpqxhP!CFF1P`F>dB-wK#H+F>rPu!p%$66sA#g-tFMT%(RI!rx+o)=VlTsxE|UaTyZ zzD-|#TR~HiDX8_gm1o7^$D3e+V8y(F&#G=Z+*tW8>2M(%dIWJFzANiE3!0)JX45IMuh^gux1Wf~j+FO2g4FO?huC%;RK>F#$Ut8cI zZy*O0ilC?=vUoJ;$rLK+HZFFK`T##Xdv6SYRneh5U3+cun`-=4x?9GjQ4nq}#Ig8x zK3|DLi>7Ia2e3dc*wJEl2aa}xqaJJ@e1J(-Yyj5W^0hX%lrK6RmURm=%?MDqz6_Hy z$R`cK7X+u2Rhz3Vud6*9(c%|hGOI5_Hk{FNG?ALPe2pPn2kopV1$w@Xe2ks&3v|;d zs(sX$SFzd*&FA&-d#JbEpk5)+f(31IJK3K62R$lzm zcc2Zt~~V|Es6IoIF3k`*p8)0~hj7lmE^(IOn%hDfg5d z;K&$xxVydi38?n<^6v8H^X1b0@4vicdN+P~F}eH=*$Ov!?gQ$}-U2TC)W`Ap#RD?J ze|&tngqw2-fZ&k){wJA}=<3W#Pa1?%d!$I*;2dgd|WA5I)(b+7MNDK#f=f z;NAvk``*RX&D1Jw75TPUKy}6D8kX$MoZvfsqq}szYp>G+AsWZEja0V9t|mH)3YLxg zT6Za5E*GT$;@A*J(gcdWC{KZpb0Ez{^eWf41i;b)MtVIojc}*AJ%-Rr$Z}~@9f~g0 zLG!Fl7A#)$ozwXNtVXyRU>T?1i6h@ zVsp(lK|PIQ>22UY>g$4UwdqM+h>9F=l}>_HSB(yGFZ`&4;7I_32+Ju4-8@XiB)4MJ zscNDyqd2+zgZ9^`rOEY!xJT=Hkz+Haw)N_Z3A01VKum;_ZreuaW-VMFSQ2rQHiwF%tLH zAUqAXCkEOMOnIm=IQy`(JraPS-qhBMJ`Q3pX(%zyAq_hGicQ_A%^QK1Wr%alzGlE?<#(1F=FC2jQD=IFF?d5b&=sA=X)##}#U=Si*S zd^pZWtMMhY3^T5wO`pVc0ZH4+Qi{wSOmp(5m^7dO)<}G704cslM8j~C7C$jMiR-XT z3JBnbbJV*t!>oK2W(#IHRb>F0`03)qHVTv=Ntq#ey|7|fwxn=wLz4&w^MG{2PRbaU z)iUEku5DApaefWyRa?0F^ zsz$)=#LVs|#T3xsj927cjVxHD5tpu9)^fT$2*QStS%a`62oceeU)T&18gmF^$9o8d zSo-zi`o*ODx!~XG04_H~BLy>VyK~BYQ~o!dRTFrtS_KxqExZ}ZWQ}D71+=Ttgz5|X zD{n`ZNq)7Mc%7V-y8D$IQVWXYsAzI38$fR-f;tM=62RPIJdck$f*?vb)cnvZ+IDPh zrLM7AdIAzRt9~sI#PW+A9VxM--ek8IhM;Rn+Jb4|G{TgiO)0%5@edH6?fgx4@6F&6 z8VWaB>VU8)UY-tel`%dx!ahhM`4D5;fWTPkXN0l5HPUG*(LfBM65D-=p%~ITU|E%W zRw*=HCt@GZ%O9D3}~koi)<~8B|;Zi zgH~t=AFIE?0Kb?@t3h(-#QJdz%KG&|y_4XGQUFj_=2C2fhY-$#hg?*P=|Zjxii`se{1?CD6aH$FO*v#fL`koxmt4#WH>j&A=pq3J z8BsbDh$PMW-Zn(p5tu{<;b+ZLBaW9Td~WfN!vKVmN5H_A&-G^TtE{Dmd>D<;n6Agb zZ%^3DKo$VG`pwIz#c&!-i&s__tB2N1ir7#H@4%tyP!rw@!pnnD>sb(hp>cSf+U6zv zdbK(X)ZO8@!gS#K>piy!p;%ZpAE#ST1mM>r_Gy=b!?@mv@`5+b^A*Qxruz|MPkLCG zXV{tlba``$DzQtKzkR!V`S9}h%lnr%x8g=IV>^_Z+gs>1GQJc(78k?);xl@W!kUa! z&c!!qI_h`ev*4;`+`ZEo43WRa0vKyqEnQOY5#to4lc<))qQ zNp}4bW}l=SGHBtzrc)>?S78(t%r%phWLYwsd$=ss^f!PD9yOmaT-+f;7&>HxY{0si z??yAoYaw`7h{93urNK+4u=Qs}Xk-$FPOyfKgqq||6$=~FYg>>8N7 zc|K<{u5~FDbV5qO6E$T=-h?xb8SKn#w%k&?mnSnR@^EE`C91&LOmwBP2?0d@=`x_eJub$Cp2?E?a{nv8vVUZNMBDstiV@ zE{icASOsbi1>%HrJE_G5jL}v@tIKbD)$=oOBr8_pKXZBg7olM$Dqj_Y5pS~cC}NBv zy&I!f-J55Cvy8bTkhLZMJu2!dt*ELlr>$K}Alp-b7y2mglkVM;dMBP#55`fwI_Ug5 zYHEK$tspjVkEw+WTgwRDq&$Ks_NFAQ0~D62%deVmF)KA(Yn)=Cuv6o4 z?#@;6+EfK``e}@%HCqgZ2@)Plp|G=0dMqqGTX`|RTBM#V?tL!v` zIGS&wy3*1GVo;Xk-@k+Jba!_d@g)EHuamddrnO+X6sAO{z@|tC{h3G-fu|qv6uKfF z*f(FlejSNWK(e>va1?)C_pQUv78}=BKiyp1OXLHiHh#Lf{DMECw6oT73)ZhB>S7P8se}PxeCyXPJ$d(J6 zcX9vu>OUv{{`~m_512g1FARL~;cs`Bzh2!w-aogpW`$6Ql2D+dOeNVPN?#B(vk_Vc zgDxzHq?zKY2y{+HjLH$ApcERa##V@#20=P%?v8RaQ#(b?GZ%V0FFyx(z$>iXfNKx} z%mF9fg9=Xiwkn(r6L)3JQ5%%VwPtNf+6(ARVj3h2B4FmzsbUb~!GFVli^UV!%UHV$ zQRL|Z&Z>6d%2qjRfp!)s;{GrQ@;STuMcNk>nU>2n?NRJ2gcCz0MiLv3&OaeU<>17g z(YrCjpox-ZsBE`y$mvWX=rfTz>k}!;5vwboSUkcT7EYH}Q1CEG5_t*_(qt?+cL4ZYoM;a7KjS^2Y3@K~kpHSE zD&;_`>({QbuF;0L>Y*=KlA9BGPzk^yZzKei5pr_+OBy{JNrCMC;CQh4UAS9X(<4Hb z=#IF!yL$ND*HzxXsGk%7^(7$V@sp%o8zONS*5v$y% zl7U|i1v@J(26@6lM<_9p>#+RS(fQfVC(F^>fKsy^Yj^hR9cpdEWw<-MBc1d=KtYKa zn}TSwf>PBm@%e!!+@pj8;%|D8yN5I)r$KbXT8T2O-NYspm+Obm>~Y2q;U1(PCEwuw z9Nq@jR6CG_&&A~R8kvwHSc$k2FMOmgjBeT9l)3}@Z^p0)gJM84+XUzl6vrChCI@ZB zGa3N&4R!1Fmi1m!hbf5Yu8sN_2!b?XpQr|>MTEtP4rE$~mvd)!urr^8n#QtLN6BWf z@lqKbA^RLsiIPobmmkpIKR?<;@nJ^Kih(I>rHE5Xp@n=^m78F~s2AvuxJ;c1gTdJh zWqWr|x0KzBn%W~+^AnKp+g)3&;h$ukYGJjCko=q2Ll!PHQ8zLy%pgKxk&t3US;iyr z3DkL%^D;9C#9$yJ>cS{-k3`)07%xFfIZI$H1X-RWoOUSG4Pap5II1-bx66K=Xh(g5 zsHfBvKTd%cEMnaxi^}L3MPT+@cgmc?&CQ<0kKfFcshcSfhP$X6yGbdd@LVcX4d3t0 zAh@_{wYf&YU5LoaRMN&JZyf)7N~CSKIu0=?Uwo_G*RteZ=x|X?wtm<`-C@w!xF=Q- zI6i#OB`$kdrV=`UDT`4;fo8{!#$>bT3bNR|`iar8B_vLc4%i8IVnY?4*pV6GG#tcn zz*M1_JG8ZrtYu4|nXYO6j7nqWc)gz(Kx768Rk0Z^CGv+LigN>VCwFEL!hc|1_NS8Kgv=STy%+U2u9spXyKZ8wU=RbU5n8n8?W$0>kOy9jDInp5g_i3j z(qt9@@jgSoKZn2|5qAp?&BdSCu!ANN!4M7XDIv3@)5Z7FATZbIvSMx>SZKH)ZK2^M zfy%%?F!nhA%#@yscEce4IM`+@0{%8VySw^UY-_Ufiunqren

EJDaPWw&jhFAT@| zHLbc1mu#CMsEbg~yw%d+aVp#R>H6`qTb|M4s5n)VGh!b{RbJn{X_6%vG^$?_Im(wR zSDXRs-yXfJu~!I*rY~5(%KuO=#J^nup--m5DuZt75F8QxCdz>Q^Y2UZv~Fo`A>W|M zA%ROloamRGSZvNgzbxjtR$H4YA5r zK}&UD=S?O&I6#aSH(t50UYX$09uX)mbjUc^Np|{?nUW~a(UM$doMyOsW*+2iH>`S>;Uqfa1MSaCfPYng`jQV@8H`|25F@49OphwdF@pLx$v$ z{3x+uZkYFG`{?WSAoB5vx@>ePg~FLq`<9lzp-tW87xqVFdh98LM@JOdU|ScEFX6%e zk+xxYs(BFL{iHZ%tMQ@?$%R=O_6Cfg38BHNGn7OJ;h3zvRwAL29>k;J`QcbeeJJ~m zBu@^)CnGbj0VWnesn*>#F0aMOE!a^G5b$J526h2!(b9{_cyl3X*}5b#p($`nFmucL zjPD0ks*5v3ZnYmr+?8`UIBsqJy%|K4#p31?(dU1~MH1U=V#O+jH^)(EZDh1u3yFuk zNTLZ09aLHdIW%t}Y>YFJccqe34RVNWHtkfi`>a1zH?W5cmX9+;w!p&&OkM0-N^;@3 z^y(j*9SMJceg*e=^kE*#(*S5>te6dF%enIxV^#h`6lo!|JW zX{VhTe7E^E;vrO5N22N==~@?Va6f@8wCbMXmpLluH-WyIKtwZvntMJ9eN&6i`2o@> zW}ByC^g)#yvAWw~WTzTO~It9}l(@B&A{~{1T+4 z`r)$<7s06KCKRDRaFVg;EMqh@z`xLrl^REC|DyOt^li9;#CxbAd>PM$Fn1>L%uDo_ z-eORk^V7j)_l7#sqY`TNF^b-gmmc)H1P3WKq5J2VF$&&v~8RNTuMTsA2X{*5}hzEu7lNQ;TC4-uR z_Vd-3FPBm&C{zTJROMfcb-vsU>yVk7Ha2HA)T%m0%Y9s*6-7PDgszDRSX0P&K26 zC|ZLle13O_43~rjBZpC$=RjTp5n(-$te)%*SF5DHI{Q#PB#>t%DV4MH@1S#kz@3z` ztNs(KwYy0jS6cL)ZtU$41JfV2^Nxa^rlmg!3i4F^bAY7YRrK4H)H1hjL3Q)c23545 zp3LV-Q@b-r!Fy+OCVe1Ub(4pg;%m)^NEGj`tbqAU!%4XD16(tAP#a5CvM`n3^9Hg)ml3fUzQPrXDbc z_3kE2YBLwbTr*Qb_Iy90fM~g-|mzFq3vqC1P)79s2#o{5=wm z_V-p0dp9Y6{oBg_d4&%O)iI1ifMhE4FUmQ~pry+O$Aex+8k;szQrVAo6?dyOH*uU` zw-c8xL(1hU3h-|e6~ zy6Bmw&7>@P{W=<8I8~Jrf}Kl1i`x+jI_2d)IhCuwi&CJO~rd?v~_FboSJC8$(5sl_T7$|7aU{OSr<)T^tf0mECJ!)FELQ2{;EN>XpL#~JM= z4h}NrGzb*BeQd*Nx6Y}4z9N+|__%FuzqB!ZQw=i$g5pZAfo>{iD3)j%mvj{?T*-Go zoWOTU5nqXBI<$YcFTY-(MvfHy6CFbH>;oQy#yJu<2-X}kY#E`N0;*mg%@MC9BJ?>L z?t$zt-M!GVo_EV}I!yBiLg)GFPfVHNl2tC-^uqYsIsD;1E4n#T18y}8y z=aHhhKsBt_M58S-R1v%(F*K;|fjd*w(Ez=|4))vESHp9d!zDbSfVZdjP_q710N;R}3g z6G9?@k1lF1L8Bl7qUbgADzQ3;&gBp!z+X>NuV&jBN+VZdT|*DtQsO^1Hg;(FT$|$m z))fpwi0uqek3yIw|JCe@$=Im+y^0#?%W$JKj}MwD>?Ozwetv!Z{2A_wcn+oc;(Ms8 z^DT89YSMK%<1L7*+&quSc)z+D@c{8SE;fF={S`)8z9J%+)BiS*q(4VbiUusTDOb(q zX?;ehnTi5N-u!Q)VMW7u8;)dvIt(%0-Z|X(K`dqB$o&Ic8-9U1SkTvx{C!0Cl@K!t zID3XZ(A(&_1$rhO=vgO=6jbY}!h{KUimOe2s}Oe~K(0$H2ZGYMI3t8_TGSt7+B$)4 zQkpAZY&Gi33C~a-H0BeDw~9vozn`p{AyhqNxTrPiFjVNtR3dt z<=Jg^2p-+6Km&LHsGXZ#v@3gPFl#W(M;DqUMH7x$YDxYn5pP7`HX9Fyq^*tFcF66M z$dW+RROVQvK6%&}e#K+Edybm964<@HaklZ{XyXjZCzdloj5l9cP7937%Z+LNpiu@> zJp!Z7r#U*Uo@N!@>u>L;<{_HCLnlS`5EbhWv1fL$x3`0?*15oR_wZ>tf0Hfeb$7Lm zTV8o@1{WQ`D=WzsrmT5olg9TrsZZHq4{~i~%#?pbQdu~D$?Uh2H8+g?*nGVAUnFnNxa6q>W& zSKrp#dy*yJR+?@{^_G~!10+93jdl-Sy+Z5x>AQ$@?Lx>NXmQfCVyKx2p7zZ%hL z96C0TpLn=xm_{ru*UNqn=}5EzW8v@>5ldAsX*vI6+z zTW}`H8Iq~qMD)dyRm7XFHkC0#% zT9au3C#qFvIygIH0sd|H0>_Fz@a7HTt;a7Zrs}DpLk3KQgG!Kw7sXO4{{G%PKE#0m zA^}ylfqW1zL>q&R6yKRFX${Q4xCg7dN;|_X`Wq+6g$?8ncZS(O3lPw zy%2LGiBevRuwTawy+(tW)4?!-l;}`(O*t|MCmo1ypcxxS%SC((TeVl)ZZpLjou-Gi z*gHPoIy`ufj$Y>nhiAZrV;EzltCe_w#jX$E+WdSIs>cz+I>aZ5vO%vzf?A&LeD`jB zdpDRqX_E=8LL3qDKXpdPWmT3?=5Y~DSS8aO2V8x25KdW%S|F_zTMN@EWH9e`I9}ec zsFzMkU=~Je@+4Uf|4|b(1879E=2-+G!BP{o+OimeFE4H$zad&0f%+OP4Sy_tE9AGb zXzoLW-69akXz_5s12^P35`OB>XpO(b*EG)kw$Y1QvmZ`jFiVlGt!>ewpGda4U>w2% z_GH}>zocys)SLsG4houakO1t7BrryI%BDlroNa3|^4f?W2vEwWUVtfAIs%ixpx8Z3 zrR3r~kzLB=RWFKoF196K;sZL>n*M^D?!Y#}k#pIe;6xVXwW1-PQc@t~*y2|xN}E5N zIlSnXu2)5E58_bh=Vs`I%Corb9>^_RCqwUviF^=uX7?79V2%SSr8dlFBe+zgK6##4 zKL}vGKIBuJAh4^A#iO&qp{r&Jewk>6xTvWml#-h5+-Dm zmnelzq1Gufm*L`pjwt_=E6?R%8+P+h$R`B&`dSSWavrF_iz>YlyJd(j`RXyg1BMrk zS(v_h^X|>-cdOIYwYRTVR;TN4F8>~t9(PfBM5v+Z!>AWOx1Q0>@pb3$oi@AVUy$S<$tENU>fgxYSMzE|`>mFj0q6F9~amnUkoTn68n zAHv4C{F`X%gPspth+TrsP%s3c;L2ZF%cus68d}x>5ztM@4~QUaK0>%&lTVCdG5N6c zdKByy(FaY|VvUi^;=wM9kFA8T!@9-d3k~5KjuU=bctrx(<>Z7u3^R2K&66pW=EZ=$ zk_IKmSJs_(=O`k#Zj8C;7+01QoN8(a@x@V|EB1yuZn}c|mk;LBdutm6am1zDov28r zhF-DVs@PO9fq!S&v#b@}TLg1@%haLq?-jX!|#qYQcM%Y^bzRa({SMJQ%Z;dH~MHp9EN?>o1wvv*^ z*xynBsM~j}aRzN}1>I7s1dLpTfuaU#8r+8;5dws+Z%HO1Nqjqb{`1ezb(N@hwV2HY zdOof0DlCel!z&qwvF@7>xkU3oJ?&DPZ2)gxp&L>e{-is!ksC3MG6(9+kLkqC)P z;(E=RVIOhDdWi00Lo`F>pN@Vme~g9R)p0)2@VQV|hB0NlOzA%`4{7cXPeFBkxKMe) z=g4v`cbwx|EgPEO#g{L4q4r6!1OS!VN>08<4sngXH6g2mYBtRvzYrc_DyV7-F`%Y! zIdo?baQDtvheXEuzM2Z_MQU7HruZr%!!DCi-Pcrxxl#sF72pSRfF1sDoL>v{M4o4z zVOndOV4Lea(oj=srW)fl1+}yUEyfD80!@NTF+@SC7i5cIDwf$2ZD=Bkb}obArwo{s z*~=6%LryJ>5R~}n*MG|R0F{JsX|U{S(Pr?a=Y>}r$^&&;y`I9ES*B}+mZ&TagI>OjkA*@IH?cO<9E-hP9jV|ZFlJE*6aTyG}(hhZx zUUR0rq?{axS|iM-L4%a$OM_PHzd1bWNVFMTdr!{y#mf8 ze6^g590UV;KE?5QT!}9aD~G){e>IPBdhfdCrJLooG-&{P*nx{ZdZIE3lT}NbD^~#Q z@crcqRr3Okffd7C-jBDp*OSF2{?)5)nPP1#Ind>ety7>|b!`CYkj#Pm;}&v_?8Z|k zOEiv0t}?KpHG^D>#udO0w(E660(6nyDWhGLxB7N>B-te96!yA&<2r5EBoW>WJIE|6 zBvdxhOiFm>jgHOYP8}71qb>U&;AtFAWqQV4+@rTm~ z$Kvd;wGG6ht8@5uF+OXkng77Yg*O{}ka|eAFV;cEg>4{TXe8H%|;WRPr*?j7%aN>rIxo znDGxsnwLy7zN)Dus>1{mKERgXSsZ3UY<47m-J=h?Gb!Hu>(!S_M{Uy&2g6(+Ra{BXe^uM20Ao9{$u}BU4g7K{WmiIK4|AgNr{F zgpj82-3~5+73{E;Oa!59AdzAB@WN(r;De&|ig^gum!Z~MKa5(hGBOQleWIL3o~Dq4 zG*CE&itl0;Z_to?ECT8(lCK|1p&W`+l$RRa*hFo0pjUZI7NId@Sb$L@vSzjvy+B)(+G0-77r|YSb%VEN^GvP=lMqEF-2NHZc=p zDeZsr)8fnRqqH1+K*@g;=hDBPMcUxP&YM;kpuz%?X>qo(w+|&}2e!xa?V@%&l^B~w zC>PU)U`DKLlGoZNW)=e%7wha@hiKTvL0ZWgIntml%AZ=W#t5rWm^M?Ce{02i1E|%z zCxdL^4rzkXs34YmC+)ZerZW#FR_311WMU~eKrchTV#&LeDU6VyOTR5ky%IAi`9sJS3B{jN$Df6( zL%M&48Up3ZsszVJlypNB`D~^kboBGjL4P3y|6A%X!(?WkC*EZ0q?{&jJt17D60uSn)XAa&>ce>s1hQL6H|W6|x;$FNRO2#1M4S~c z@=8!bLzW3up>fRGGRh>aV2*Fu42GbOt1L$cPRG}2{Flf6R7=LntCf}IwYAsFYwuRp zmk|auz5H(aTEc(e$@soF^WLh&Z}&TCJ@K^;oVTq#qI)ad^SDA!L(w!oo`|Bs6EJey z3=!^ftSG4wB5U+7391>Az-0)sC2-F<#Mm@Ge}q|N#2A^x2h2ZgU0h!;4kWNN<2eXB z&qMHFsGlfftKSnyPsN!4O8R**aF>QNclPQC<$|Fab|i{0X8X zzgT$i{)Pl>>`ME!9}suU`0sjBt$kYn(0I->gsk)0)y>tzlGJaIlq8gGySPKhy7q44 z(BE;a-~1`&$AMvgzPS4`#{-JItsGM#6kw$3RWz)hM$3w@SWBq`1SU%>l-|>c;y|Tj zlu~aO@5AfXy05FB26Md_pMa`GUb8!18AqAG<0y3gLPI1PDlkoVJV+2{7}=yr8zU3Tj?|gc8U{ zAgGX*p#1$JovBWvN(PcTsD1j+<+qzxjtYJK8iYr794U4Y@i=6JN-A&jKW>W5MW_#} zw6RRJ1XGCD)G28i)nilSkRYuZ#4<6xJbhSfNg~R1{{7?{*9^rz}}@Pql2izYUDe)n<1_A^8P^taUQJb@P+RnvO0jLf0!|5!%XxcPcU<} z;W+U?7|g}>Wp!AQ=c#@#6s!adyQCuHz?o`mvBm52w`OSOhDL<%4^9~>vzY(o>f2;6 zCUFuRdM2has{Cx|m|VALyo&;91|b3HS=H4>-?*4pt|gijUyt5bF0@bPFf69ZLvmts-U zs*m~M(EOAQ(2$_{1SteS68feRq%M)IIu(+z1TXMkffCt>uswVfdJAAEMwb?$*ytjy zql?I%}ZS)s5K|gJJ04(-f88 zOgUa@l9vj$(z26_;s`#UgNVCvIR<`$yx@^Z zT(2PC2G|L5tq?teZ6jIs0F`O^1iKJpbNB$Vv)FmJ;bYV=Y4tywqM{1EV`M2x6GMsB z`J6I2e#^{*=ktxTCFDm=U*eyY7n7y`nmi&`2BwV%s1u-JFY!ah(d(J>Sl?h{4lC62 z$NP(i$)A^BPX0W_e=E-?|2TR6FL%!+F$G_IA%7m=FFqKx4|GJH^cBk;^#RwzVcJcwe+dqbj z=M0ZX-#3W&BauN9nr7hQ7a_r(ORNFtwXe`383yBAo-Eu{3SxpYIb{o>2wy>QCpfHE zUiK{;(C4)Mp+(j;(}3i@df^kT2DO-N%@4Mh?q5twKk=W@Ar0+zZixC@XLv3Y&(fI~ zwNxBtDkNy|H`GQ=#1!TZD$y%(4e*p3Ab8-blIIK?w)Z763lZ*JYaOychajR*3^8@s z?jObfp%K;&OcWdWpQ?o;a}{$T#7i|#l;GySb$`ZFL>X5eTbT_1HWwrVSAbv(<&5$h z>~U|UI}yQIj@Qms!u1;>V}^x`o10rij$SVP_F`f%DAd!7m^nh~#sWK$6Yxp8F%s$R zt!;Sy7xrUFo(DHW=<^KxwYeFszhv`#hH^)FHT^kUOrnP^PiqF#7i3$wh|qNYMju_? zB1oym6C+p*QP4Y+)nLbv`&Mfo=PxQ`XP-1%$bVMkkW-~hBMa0^D-?|rC=NvQcB87~ zY$j4VGKmlE;MADWX+!So#jQ^=(hWlg^% zT@(wp(8c;3l&M9Fhx1d$45Fp0Sovvd^Ha4G%snMIXaY8zoP3-bts0aD5ja6{xCFKC z6mHyZsMsv}1=%9NUFDcAw{8pN_sfg35&__pB#iKdLSX0n($nmGzDMa-EZ?#nc=w+P z$WVqx^H}jqRo(>k%+@FpQM8gKqFFLB)-{MDuHR)u9b&R?QIpUVS~wV*Z(ZKFMaf~# zcOM*&vGp>{4N6D>i!_~{fEI~%wT+0B$$w3*P+F!~n!o$kZ2`jM?cO-~3Q4Orba=`~uBDr|G5|E&USVKVonPul$OIcTkEa>j*i0nZDI2azCrDA>XvIS=>uH zgMz59HMKb&fFmk~Y!SQ&N;M@zQqypB%)l?StHgv$Ne-8-P6==WCU;(x|DHX~dze9Q`O(X;qFr+FFVN?uswdJz-V~!-`00?$pwE z%dgfCXZkP70D@Rn1|~zoD$$za=s9Go)lx8ot!tIG+FwN zS;PEuLwd4&H<=0{)Px;;qCGsO%PVWo23ZLE2YdT{2lyb+Cg`g~n$(7(L9kL+VCU1; z&IeX2${X)Y;I@OF;$%frTjZFR)PvnhiNrHVW@=isjJ6ypNfEob^-oOXQWzhi#^sexnV6W_@k-Sy3C{(A*E2~?3&vM3vDN2>sLA^xhn zHKUHSn&T+sfF!{Usme0+o3sm460Zq^)ygYCr;VG7oH8;)R#`z+df6S`p55+W8OP;L zTqKY2UlFZ~%q;aE5?Vch!7GNrw1D|*D#H!d^^H@ogzq_BYP*OkvLc$uTB{DLTA(}R zW5qZ;E8Hmd=iBRv$%pzkbIByk51!5s>~Y1+U7{=9KxVP31?7^&Ejhmj)lX&Zcjb1 zLV1T&F>e+T@0D8!-VDN;NhY37T0TX(K(~QvxvkJ}^VZ_K<1B8Ctkdv-i7aP#7wDQ; z8il7sZ4uRAyw<2iM7JSVdqv$@#-7A~gzB9ivywYBpGbOdmCK)E&hOCv6XF646l*&h z#+;q3&{$*yM@!`;wq4Z}t+N#2P}CG;FDYqL3tthmEzKZ9GW)}AiXx&YY*juibS2Zx z)?H^=JFc<_DOd7It<*mzN{yC@@KG+qAtQW2T<%@#8|Z!=5BTl9dL`8iAA?oYm{lcs zsbyXUO%()i6B+l&z#(}M!pBb$ph8_&|3#eEi6$38GTl2ybkSR>YB%9u3-o4VWp8|7?? z2L_+2h4%Qzcr>tmnL0-g&q7MX;8KFDY7c__r?_(AjDsv2F;q@|y}d@A511}dWAgr? z##xfSB#NjT%o$(s$QZ@33@}`Pv&SEYX%QBa_JTuh|RD=CQ*gl z(N-JA`{Cc7!z`OHzNFI!watpdjVHyY|%Dbm(VP0yl3xhr90RSAH)D-@!GZSPj2%hX4M&{M4PriOOcKubsqgr{cm zB-)r(+a=n9-``)}J=`zuf4;c8{Ic}w)hh|*may$b@xkgGo5C?fS$x=#&D!n7!{z7O$J!_cwkLVd*er!M$=Z;o zt-Qv`(s-4xIt*J48zy;yJX5omHR(gas?PM7{RY~gejiXOnG?a_oLYyHzkz}Pd=8e~ z4Z9@6FsO0x0tuz+wbmt@mp~nKasP_SPG2OWfP@O=mdcrwsDm&>0P%*JVbkkrNLFdS zrxfbSGH}BfX`knS$?klDZDjk0;|(d61tZ$Y78c_GmK`G2uVo#BVJ>U@8Vz{QlT-OH z5kRWS17is2P{V{A^H0xV`ZH>3_?2!3$NA93;@jihC2r`&!^7RxkB?{$uvo%vluvmT z`8}vGa&UM&jGHx^(8o-@iD7`Tc@K438qt67#&EP46+oS$ss0)8!Dwik83KnZPdw4q8)VjFpH4>@{@6 z6y`;6nn#aL;|qwz=JIUb~9??+0#mOmf60Sn};{kj%IMxPi8+UgXzaL)Rd*lO7m|=bMZNz3ECPRK^ z=xQpAi>x%9F0{h$Z0GEJ1|{u>)18T^M}~0p2&*0%P>>(^6d}Q-(!HlGn}wUmZRb*r z-pR_UMBMY}@@)vl*gWltLsh}MccvUEM_P#}9_9w0#o~VV;BZF`d*%30h3otP4x~Wh zP4=6kbG4doXZ2S;k0dd!!a4Pt<+-YbN${Wz5Xdm-&EWPc2Nl?8xMbx)f#%LX9&Do| zi2azphPxqlw;T*#JtT3=7mkXkEslr0I^Lv!BJ#JUw{{h&jg8Gs@QvTtk8@>>!eqLn zMd(?pl5-cFWmpbPwVNkR@`G6fCW%H7WWiXIl&o)jeAihJdDbQoOv}IAuG0Ipq948opurEjjzS_xo z3Oq_CSt59w%qY7lH%L)jOy9g5G_D2;qj$DqkKR-=EM-X`WX>-50L|~8|9*dV1!V?> z;}%=zNOy#*Q-+arKa{aokyu@|a*v!CokwuEup`?Pfvd4lMSOI|fsEhgmehM{mo%Tm zV3=>Nuz)?b(U{@drFy_B3J`}8fiA+<;z%r5^cY;ee*Gm8SzxW#Bg;%C95|61?wg3) z(9)!yDg!o_mb66Ei;+$oaGr&|3=1mo;550e2RN|MyfkWbJbr}JbmfuW3__2X)6FQd zdlavl>^rJS)Zy@iknCRL#_|%mUwKcgb^xEP#Ckv@IKIX2Gc?p7Gw2)P2SQS!f>l%* zhMPIYL4C1RF=mfyj!|5^UsSFdKg<`_b1a73< z((*eKc$($r%VTR3nsRRqh=Jz2!O}BvTYBZvL1ramv+7NRPL*l`s1=~CfX+)hHkA*H z1iW|;fNFU?W3zNUT@%-9eMbCdkv}=q;p|fT&nsAHYEP=!HB_<(!-GA!b@8n`z{nJM zPjiIS#_SRS;xw#Dky`MnUBp^}`b#zn9%~m2m{H=`N}vFTsqe~d$PUcRXL4#Ed{rWk zsG#GvO58|TG&jz)C8i8Y%h3qOvM19+z3h+_)jXb`{jK*VUKc2@2Pa4@Jw?TZkCWG+ z%b*bpD`8~)(y2_UD@fK21I1iIY85!D1oiJn8>lK}2x11)(bdh~g;cSX2nh+@QMpt| zEKFD6j^_jZ* zA>-MbS@=_uaLd^yM$nY@U|Zo{*$;wC>Rxy+z`D>m8|FmDy*8+ z-r0FZIA;g~5i*1*6!wn`mICy6L=|1dXpeyo+0urw5hp9{7&oBE8fvn1D|i$;OFNBc zE2Co>5UWTspe^R}_2tBC$JgbVp;M{D>G1}Wr- z&se4c{!gKf)17mxG^X)&XyRFZF+gb8ZBmNMl=4eOmSQOB)a`VHkxfE`Y7$KdYHCZ{ zJBJ%TNd83r3okLANmmM%mxm4E*Z z3*WW6wl60C`md9fHTSm;GkzsrKbBo&ygn(u9{p_RgEA2mW`Y^>s|1RDlQ)3@zrg#x zy+ED!uh$np-Q&IJ)4lU3mpW1r7#JY0lT&GafchIrw<@ zm$%`~;pt>0y0%@*SdUQOwk5lq!=9 z_h#_j;f`p?NxdWdn`vlLWPGwT-xqe=}mZ*3WB zLNsMaHXy`wG1c1-1AD%~!)dyC_pX&LO%2lYS*Zn0#v^rBszb#)B&;hgmm>b&`7|U& zOxJ0lP>ut3K2)q*R3f^J%*@*o=IQ;XFcL~ElmbIel%c|Bgq64SX6YMDbdv*xVE;A= z?fo;&ZJXP$FAMph11HGK^8674<{chtg@87P0>Jy*tZX?DK$m68i5w&EwfQ|%5PLf3 z^KM7^6>sv}xLuJW*MK}_%zlVd@&yeLW-gE{=VK+=GACT)}XPeUbGQw>*p_oF1L z<6nH!q)%>X#C5q*U6)gJT^601_sVtZyw?64uC9|lR~zUh8e9Ce$om&Bbi7l}TQOwv#-Lj{t_ChKVhaf~ zlQh~Q3uRIOTG2uWQM_5J>sIEAphQk%ji&0Sp4T2lh9vR(DBW=TSNoi=#y?V8Xay2Y zOmFxQkSvHULh+9!dn!w0>I2!Bq0B#m03|kI^5%_4UPy3;8Yn9Q@o&pw8}tSsdTY8+ zAd%4dIiOC}!UTe)y7<`5V!_LyaeoMy4I65t9S~nWSsT-QMi^>hu&;B#e-~x?FxRs2 z^rpmLFTY+qUYB?@q!8|iHg|9)rI8s#BNsp!b!jUsZP2(#>Qc>#RGmdZR~rP@FuW}D zl$?RsM;b=rcb+?};E#KC5oC}iIWh@Labu@Nt5IsUdhEJTQI1C?;7pfBr|~_Yu$2gF zxt09&&IEoji6IK}3~HG`Vr3^%%QkxuQB49>5A^aUVHlPJI!?;~M*>0HJ(P9G4Xf+O zn;4nIjrb)ug9gG)>qQsdTXYv88bFIh4Z?{B=z_)T*5f)dNrBikI<2c>6~9jP!xvAvZ8n*+TQ=jH`pr9VL8s z{QfW!%5z;{*Zc^p=6o6PbP#JrCS`j@5*MsTgg^5j)W2kTy~7yLKEwix6BFJ`@xsK= zd|9|~4*{wAiW7InjnEMC@rXwacq|t|BnVeAhIXIy@WT3oJt1z#akCC97pNq3%tFFN zh_{*eLqY|pDa)@F%^I*QX1dJ+9$1immXv;Yd*p zba5)hCF~Nq3AZJt{W@ud6#UhQum5g7-v91`PVs!f z&vP<^_y!iShYJ9iTvcUQf8L_0j8?}TsdOZrYW$w_qVSa_ynlr3o+(T_s>p$rP-=tm z@rlw=s=%`}Xx7GfUi$J<^cgxsNatK6k-;7&^&B$F7 z4<>hxUtSJHk6@?3GnKJAbb6~lb z!9D|($&;m;P$fbH>`$oMYKU+8kT25)iTADl6XYj{r$)ZDR!?4;L?)^DE~3^7a_E$Z zB2LNIS_aCM70WI{76Q=92?Xg)rADnF!S(JWO`B2sDsHV-qIUcXCAHNwsbfIr@36&AZ$otxu2L(5lIcex`>#`dRbN@8uYX^maLQ`m(Y^l&kh>n>6Bnczx0b?e> zRtG4)VzVJM8ZPDbG@x!43VoBmpCCNWkJqj;4l3HWNn{cKx1eQk=jQX>@82G-ZY4&L1~JzW zR-e=nRw-gjkq;@=A*9$s8QG_r|9F`_rpnP)B4iVB5K@Nl(q z;$i_eOKQe)Cdtp2_o2fKq(N9|tKBKLWKxuo9Tso#@yY3IH*~-1UO}5$*Zs*3sn{#ogss}WpsEe|4_)_`0 z8K&il}ofT*M7PBh?d- zg6c_C{|r#9_^7|+HCeJT`~)x4Us45PkXLa7TPo9hXJ`8fSMc51+gFSG%S$9ViJOno z5Hic3l7_0NDiGJ4SKb>fpwu2^D})Tkd0b-BJcO53trtp_?W#3HoGx>>%LAye5hu~pM9F(%DvJ*3Ym25dJ|{|pAaw;9Ho?rj^x|*{nrB> z%by?%2Zv4>CLAl8AVe_*GRYY&b382mvvG5Aeftw5r-0p*V?>_haneyP&8|R3x-_Ue zlkjjVA$u49Slyja%_8o(B_T^4-6?$R7BX?r|3~a*%m3#Ue#@2URXfQK1I|_7Nl{<2 z>Bgb_cF@2q@1wOI4Fw_CZ&0SX2tvXCUF)|GuCBYtS^x0x^5RQv>&J^7z@-(AD>Q|R ztFS8;DJfki_Z^nL@nc4b6ixj^_>PG+9_2!-OhbPPw>q4kTYYB1CU_GoqpPKxWk|Xa zL>zX51)}@%)`hf@0)p=B!AdWwsd+|FZ3L5G8$mmoJY`TI684K+ATp>Ja#@Osk?~|~ z7V!?!1p;Pzjc*bMsOG)#OhLG=Yy;2ayR%JHAJ^@dI|c?1!RP(~c%Vc?sn9?v3uMT( zFg0c%IpyRKHHzW*khEOes3%CVTwpWBb~#7=c(Dzl7l+)e5QeKrRuNCZh0F)3P$Cr@ z8?>oLEyO?s18zRNe7#!5#;LcpmLL@a+Hu=8R9;YSN!{j&4~g31N8Gkl53&s~rGm|gbQw&=99f_NQBAgTTeyG9_4@veue?pc zjFi7f+0g^!DPHVPG5*np$qj}TLqr%yend=uNZ>2#=UOmxMj$vb2;+l$ASa4*7TR`- zRBCD@Tu*QhA&yafL&Y5_8!>legNqG6Q5u>Nk;rahrZj-sna^9M-<_gRff)2zR2*rh zG^iY8V<;o^+Y5wwn@4o={QCKLaDYHSwGW9Q+t@m#AJF>h6r`q~NuF43R$66ZF+oRm zKS4v2+|59h)gq^xVg%<`NFSm?kbQU!V9R~;7G_*G#Z09S>W$k(J0Nq$X|5T$nkXkV ziL-vQ=(Td7(dyy*HvMPMERXFp9tlaSbkIfdD$E!{Z09WU>?URqkYCQ;p@N#_D%X1)*n-Y689Jq< z!f~~IiL!vTqi$p&#qm@k+h`~5`f)aY|Ayj({I-LaH!Z3xW1*<4$Q)_(|sC zt1aUGE{X(4c^^^BnI_au%!;Q}n%ihmv}-$t@Ny>65i-pJwF`JH77PXreTZO9eT@(h z?@|>x6RIV#)kY~uEPn&p;)k(`fRHedO-mf*o48s<88dZBc~M%%&aQsByaUeg<{^<7 z(e3z8z9m9Q2~3cOf_NeL14XIw`Hf7%Bgp^fCumjn0bQ(-sXUp!d-qOJrJ_?SdNkNZ zx9y-#0<50Nm=ko*e!;(0eY2x5SO?sn(Nvjkx(r%I`!uiP}svg({fOXQ^A~#P#CYQoP6&=3R2aBaE^}qT?D8V!4~W5+O`+%SNf*_uHO<{W;u*i${9T2c|NW zMcW`Bw~7hL(HUZT|G0l}wu9Du@%OsV;@f~gyC*R&DcD(VT%l~9c$1H^J4N^4*;Im! zG1a~Sq6(u1Ixzw9wQL=-<1Rp^r z*a%|w5K?zhxA`l|urGaOTCe=b8NH~6{bKSzC(G!M?Ny#X|HtDMipSnvqQ=4f)vuRJ z_APl9sDvXgsnEXqLZSRowod>kx;2_`j)7E%s&BzmlTOyK%3XsJ%1-W8@Ee&#I5G9$ z49%uW(yqrnh7r!hBE+pA`g{7o#Kf3j7jP(x$8_I1QKgwQ9*oc4}~S*7M*j%I|{3S*lIMG zOQMo~lS&qKOST^|xi-*f@=6>b{BWD$dMbtTsYII@awEa0xe{2;+8=rY_AK;Pwqu`& z(588`@t#-tU-QzJG{?YSA@>Nd9?kf!<6OIN$Kr-FJG6_VOrWo8S@A+d;d4)YLeM}} zk9{ddLU0H?BQ<`c11z$MPM}n3FO=dji1O4#=I9-*A1a0s@bW5)t<3i*-vOJ#op)i1 zct`KMe z^Q&REBQefc)_nFhM}V}a)CO1##|bu*nFgwSyX`&nsy^7+TCA_5Hb!!Y6a@ByS=u;H zFwwy&ZeD>U&w}bL(he>@{{?m62L?v{9O0SSsa4*B%ho_vF-#9_OKP|0wR7WU>7cFw zU5IkvQIiEI(b;R60 z6xw??HjD3qhh@GID$4#X#bo~p3{gTUm!99>U&PX}y^^t37M3MqL;2WTB$j|siWhAJ zFv?n#lXzA;^l~^s0SId^R3F@Tp+XIs(g!o!IX#)3AukJ5-+%MlqI%fqG`>M_og%E@ zUln1Uoa-Y-Ch-A!7sGWCj+v>JPBEd!JfsWO+n25}?Y2NGQ4AQZ(ou@h4V@lqC1n%_ z74GU?&_s9?&h}Od8cpRrNU&bxFrs*A%xdtmkf-#5KzTT2hpKS$Sro4NdiJVHgpuL7 z=|YEk4vs-YWauO&c)FTNlNsiuKTrmEIWJP!5+5^isCg0(RrtGmw54b>I`_!GhdaBe z^MHP8W0zka%oZlbA8)rc`^_4KE*9UHeo?4cBBpTgVu&i}Q3K~RVtxtI8a8XUsGC|^ z#<Q<0OFrMa zVG-M{Gpqn%P~zfII5_L?O^C-MG|UZj+!r!1ZXWM@T8Vy6t%O=6?;g?g(;569AgS2=|*oR5II4J!!V8*DmS&T}MW2Nr9&UJW7sT$bO3u2#z0-)nzP_04A5lQGGilh4voKou>tkQy@!ZQH6m*IgQ;3UfEsGS5rYIA5;neYinrmB9s$2@J^LIk` z6qy3>7#{8ar!mGFmW&ikTE;yzSyHf5R}@)=H^?oas?LuZRn+?EGL*oI&!NOYDP_Wq z$XBh>uKFP)TG6&RWc6n<%Kc^Di*3WRK$@auW9`VqC@Yk4ND9KIBgVcqogto7G1hqw ziqkOaadzpof>X8T2yU7|H=<`YP$Ceh1{0Ay3dcketY{l&XD5ssFOWkOxXv&ER2l8& z`J4%2=1pn8Jl~gxHxp|M2x?-pUmn#fMCAXpeOUV6i5-a zf%_RA7tt-~_OHc!CO9U3hzvRB zR6a_O`G;~v+M>2xpGy4jl;nryF+Qp*SVQoe&p_pxIwkyiwQDp{FqGhrkc#iT!n0x zcLbAGL^tOjbvThWzgVj+H;>NUXE0Z4=snS+}QYQx%-0NMU)%#6hndUtN*-2C8%tmhHVH+G1TpvS+#B{ zH>#)hijkc&%+??xTLBu z;l=8~%MyJJNBuatQEQavmvk=fFatdZP_htO*du#R2}|CAfl=kiIo+A8uu8=Up3}0@ zT`g2N6wsGe0B(060u9OE6y&c+BID#wr|~@cryt{D!j^HrmMzY*!PWM3TB0!==i5hw zUEM+#y*Z>se#AQmp3EF<{XvWfcS{ID;RYrbRDwY9JZtrSi`-GJF|}NLWkRsL$71wP zp4gB#^i4|2)-CL_giDne=&+~a*v&P>xC%8wl~}=KcW?%-B00hk2`nvRpS|DM2d_40!<3hk zwhxi+p%ZnHP^xs-bp;*x^ctw?eH?K%k+v4Hw_r=8^ z?u`L&c<$Y$1kzqUEdFS&!lhm-)t;9aB@?fH5sTZBAw%+~HL(lM1=DYm{z~cWHa@Q{ zqmjMn9*zLy57{ilDRrPoYykYeh)|RW0n}i+M6)KP>QOK&p?+vx<7gK*T!gAJTs_4n z$D50jW27Osa(40Lg&Dj}O1>@P;K|D9-I@9fPwF{3KzO@IK>VcuS|wBkH>@ifK%VF7 z&4yQp@-_+=JW)bW!|gn9w_s$+?QN)!WHpGjTNr`=#OoxWKZ4b1JBVtg$|IAwV$}|c z%4(M)S0Z#0;ncG*VBURRkJL@y+z=ttT0Rpqdz*s- z*MLy*x0b~?LIwF8{;c_A`!u&9QbHm=X4}}YL`?{=H9V-&U6DiGj-r8>aV5nfGKgm% zn?;btMW{Db*3no%dJ3J}JM+OmHIwf{RUi1~kp8^0bvU1dVv2Iqbpcrbl*mk23$~U% zznFZU{OL~<$e@k_!a|~K6+dtKIQ_xm%xTvbpkT?6okmzX_Ull!lV;r@#yR%BU3EqC z)2iW+jlx1yIT3;~3oBsld~ea_vsv|#pWMwZ|KsuUzNUdiU^IEpG!>$XABE4w{=we9 z-(ev=MtC}tc#7_az)$fW5eMXI4w7tOe&EWrsBCs@X%KS8pWY0xLLzDuPm($|lCG z*~wfwYo44IW~I_Vcavt!M3o4*x){+`6FTc}sy(A-`z6`}e!fJTKyt~&*PHvU1AXXB zZSksAJ%{6j+}1z-`_0$S^51t_9r5>nM?b<}FCH%Cv&F*-#Fncc|NY|bm!Cd=X5||< zhnEHys=Q5t1HLxD7--(rz%Vo}xM&=^1v&UAws!V5wN_txMU*ojkYV@W(-a+s3ZVVk4JYuK9eo70K@-Kzkq|FD z2fsafaKApauzr>C8>S^nKjN*mR)7|PU6!70ptlh>U4DGm_E_jqSctoV=^4&XOypjJ zhOCF_$%ygNRm*;eVrbq|TB3(3e1qePDsONo=Wz6_o<$-aK@7b$!Js& z>2rFp)*-k%*$ubnRB5x;bg|W#1_eu{j%c$8YZa6doEKv)ze~HG^UV3j6XBUMuvUw+ zm@*t)@BW3exfFEyd~uz#u{y(eCaQ!3+Peq4CvbU4zr8}Ol%+SL{D$0mTnAGi~NDUy{e@A(htcT;;o!9OUCpa#SGRLOG6Zg2RN_s-*oOt?GxuscIRv!8G89u~hM z8|(J&p6CIgDdbO6GKr;uA}JK!P(m6#MsD1ZLTpP>zOJ`3xC3SZS2UDNiq)@vh`WQH zguCDmQri!IZOo2#4jCaLs3feg>R&AX2mT?^FuCHoGf@ADitC^V@7228*xkfF`1$(r z%Vp$q)c^zqG#G%Cxge~8>FNHH$4Lnqd?UmaaTC*gLtO?8mS(A!f z5;7f+1saWH@|}5$xW8jAA2J|gk2qL$F0LPtM8Ej)@vE!ovrnL*5YPRm;WDJj+Ih^! z{3o#vD}sHt6eQ>GQi{BlUx&6%?gf%FV<#oRQXw*k>K5cVI4&U)b|Qfp*Tlhm1G`hW zXo|J^{%xuC!8ent$gLeQ?sGhu9!p0^exFqLUT)Ro@6iROjg9bz*Ol*+4CI&bK}4Ez z+Bj1$zLvMwo59_g^w0eX@DO6qu!KQ0`(5OB}2he(jPxdJiId07Wo@ied-?f(*EKzAj>x z)qbqr54-6@qPG$X%OWV@^To~A>#M(@Mga?!y!?rH3oA_^B~(tV zW%BzEN>;TB5U2`b`Vo5!a&dMH??q5$ggVwly-Y}W?eYYou*QTb`#4+dPl|0ofLkfat0a) zT2v<{q*z0&-bQWN)6ra7hqFdUr50(kIQ8Q0G2RO$D*M#)+WUVWD^y^^#eUm1#9a4 zsuMjZ9mhN(A_%b^BjfeH8ztxcP?A~*V(Xvk&mmY52ED&A1GhHIfC`W)=rZ0R7u4Pj z@|fZ*ziAMuGH)a@VhfJxaFO3sOn=HRwNd?2(=}4xC>hH0AZGHERHqRe3{0P!i1tEQ zymm9>IDC4+(`gMVT7dOt5L_ak8#*7JoSe=XzC(jwbCy*gl(nhtKf^*K*CHdAyTL7} z@f(@^k|>XV(NDWuJ2Z@Y{m^&}MF^}u6XTF^mRmR9x_f~DgcJz30;NFl*n-g?OCLMz zvh`e-@~IsoiON!Smx#2_*S<4>a4vG5+=U_yN)UCD<`xGhhnu^I+Hb7AC`||FF`N!h zR!v8ktb&{5;|NVIjyC?+$!q~;KZkZZc?<44+nP%Q48{sB-M^UJKioZ{fXrg?Gs-Pg zR|64RZ=tvv@+Rf_6}i;q5zOLFi717_?@m#$bvO(1aC$i@BBc!oq=J~5 zNB>UKsRBR4+wt*MOc;ycw1o5GZvzh>A_R%N< zDC-FD8AKcPD$|Al6iJ}r5TBa^t$Lcrx@3IT8V?ARif< zIh#R#q2|?`&-Y?pjVu)@qXX?B2RhX~7n9W$vMtxbg~3`)5Z(MJ z+hGhbFu7qnYV~<*;VOwOxEzSEhaUY^ z!>k1!4Lk)!eg5(k(rWa+-7E}=G0B)w3iqWVG!o;L`v%IO&8~val zr|I~~lfL+Je+!ZF=F9aZ8ykqJ#~hTy)US2(Q1&G^GxrVPe_x!5YjhW`2f~*U=wfYEwqUFyDh8TO^+rX3d5STq(pjr^0|Nn9J zCdzdkNtPf#<&Da$4D8I*U?WIPTeTU8g}@M7Vj-!jE_()%pu|oRWPxC&R6RZ4KIa^F zkMM{a7gA4F2>@TjyEhi!S6Us(HKPHxIB4=dYs_yqsYWT7qot#EO(H^Em};{|xO;mf z56#rU*e!)51x>5{{$yBR5+D?6^Q3cI6fJsbe_gEB!a&{ACv~jqOuZ;Wb0`~6Cqk9U zk~Ijm{-tZu6%=*VvA*L>_6U{=^~N>6PLezgC_Wh5}BY<5fImkiH}kl_8pXNBpINl&u73qGNj$o zIg#$v(C#r-zWOqH(ZsDGzmhW)eU&9857z96#!O_E_- z)LBwGBin{G0ycz`JC(7J!7GkB14Rf+kg!@L&yxc7f9UaZW|B5C!zb!{@|O#*D8rph zVK4+qLFk116?Qz~_2G1Pu3$NNwYT#2BibtbnF0yxp_du?b9*XO|6n4Shg8{|fI}Zz zujh31sL$+Zt3zBI9`A0fgNY9q!`e)!g)b||TJzg|)CsLU)ymYaY7!(bSgjUZH@Hbw zl;{sjn{rai@0;88Puma#Bq?>@2G^}G&Nmn^^0Y)+IuV(flz_e1rh7NMgT9DLIk8-I z7U^e^B{eK^*vS{AiU4{iuucGET;T*7n|HoRk1DKNjWY&X0#!(}yuxo23gE4tS~fG9 zq1`hCu88s@W3BmY_TeIWmK7<{s>)^RUs{&&8@Espp4iW{T~TZ%MzxyE`D>ZKmMD<+ zKe%Fv7N%p;jUV>4ozJ(oxpRskm-X6wezkAT#vjl2)HS#Ie~E@cinALPnnL8Sxx0T* z^oMay2y}|Q;uw(Ip%bz)!sS}R_S8o*mzEMp+b%ol4l=q<3U-_kf+KIst6H`b#@>0)v{;^e`}mPn85WPs%K}etj?=-pSlI6abxzV8ZLWX; zmsA6h(yE4eW2p305HeGRApU8*TjBb!P!oV{b&0j)4l4^H2jcu7?rIEwldS8L$>c{Y zX}YNpJ5$}<$)X}>N2l=2qE8Y!Shkn0tDTvt<4FN8IJpRcY9BJ5W2Y-4bCE^dGTIw6 zoHi1ytB2bKelZ*n6va@3}dN5-61b&u~N?e9r9S#;G3T>m5$l_5_!BS!j;$|-I zKK%SM@*biyGm|+^4$&A)KVowptJ4k!%%BkN1F6sHM2s#)9CCpf(`HD-4-m=)hsn<6 zrKfSB5l|(T*ite(?VawU=k^-P?*J$n1-gdj3(JEu{P0!#qNn3pxWz&s%zGcLXBZK& znT>cxwqyII2om6oOu7qB&q@0uNdQ%Lpl-iBJ53LH;AXp4@}@H&SWik${Ki8ZM&$N{ zxds1UKkNl(mqV=Y!}i%(xD?fSO}TssQRQu&Avg(!qm%99y~RpN(M8zG=R1U!y#Kts z|MTL@l1(a>x7VL;5Vm1h1oUF1#xrPh`>w!hHHv{43;H?YWGrKDBoSytXVSQ_eMjb+ z8K)FM1kwhCxF{peG(TB46!H@9m|+#&BPPT8Qj^%?7i1S$$N1)oV6TzQOHJu*iiOdUl?ka)|yIj}-81 zR=6Lmb@D^nJH7{@)ZC_1zQ|as%7H2l8vhOZL`O{a&Q}+E=jhu!8aw@GL+55nm54T6R5cA`T#0DyhxMpLw7IwK{z8QmrJWXV& zlyIwpu?aa1V}s0wOj`hfOK*8e<-*Oz7D&mbLo%E+*-*`aE2P)34L93~y`nYTgu?9B z6Ho8s#j7(2L}OhTz;d?DTFddF=~JP#dBJgpX}tI-;;k;Ex~=C0%vf1Om5#Z)hdu@P z&yPNMPBSJ5O=>L<#XK%?VJ5E)r|8RsnNIy8n`ys3Gf59{DV?=iYrt@0!6;$G@4PSA z4y=aTYzn0VM*EP4bn1veJ6km|uAQ&3i&?>K!lO?5SQPe9rOUk-tQFgI(Y4$CO~(`J zcdn@xtCh`{*H)6rz{k0~3>X2)*g&^H-cQ88?I4&-y!_l5vv}(A>I7y448Ul2OdlGo zZkcHy_%>UBPxfA;)dcv`hMNaUcjE$1ISRyfQ08d&t$YzD+aBC_jsX_KzK|$?TEf28 zrmYsb{B{wrp;l*Am~f^#RK$wiVE<;@JGo@(Ugg5K*t*=f|u_I3Zpa-8+n8&1x$IUq<= zw4s_?8mX$l^|%(vKyAS;yAw(Y(D=jb>4=onb~sIUPR~$Y1kTfo-)`@JS$LHK7Y#4Z zFHc`8?%aCbt&Gy;5O8?^`EN@Y)bHo`WQ{A_uBd;9qwH4j#7EaooXf4j9# z_e(qtd6F+KB+wOTssIP;b&CZeErJ9dm-P~Zv4i2-sDm^Ruwsj76WLKIi)p4g1%^z} z7B;+FG$G9mqby84)4P0_1A1uSDS8elK0+u3*?0?Yg5Z9N;B81hg$&Qcq0uz0L>I@v#nU6jFX9)^ z#H0uhjIYH~>R(;i6c!X!xEJhk(~9B9W+?Zcw_ebpj!y(5w~kL?xs&187)m=5{dT*P zhlNbs`=G>~{22v%a!k(2&V#!$r`u>h8tJ?gf9P%x8it(!01U!Ri)aPh7O1YYfub;+ z@75<=muNZo%e&2IGewr_x%*C?A2H;{Y;u)B>EABU$QzWq0%K8qG=ESNE(=DpCb9=2 zJ)l*^-A~VV86fucr+sFRIqOqS9gUn(TEPsSC_Ct7tIecDMh{k&1bR7BAkF(B`L3iv z6sw!53+;X66cCa)7Bepq|Eh6x{*ndjU>sN7x`HAhRB@ML;w_*~h66-RR3U?kjKKDb zfa2*CeK|!MM99VKTO2W#M9r-;F`jMzsP5`@H!~b-KFbZn&kxHVzPrtk76>@KZx_7! z=Jp-T!(Jg7F2eU8enwMDUQs!JLtH95?UrXIY11J>IWQp%bSl|WimIBIUwvtl$6NI_ zdG##PVd=?!SS@VCyCuH$;%zsm#X{owwEXnpwjy1XR$%C9r0#T^iY<8sa(?g<(u*CA zTW#lOjZTLS^qLD**eFjRiRlCwOLrUafNcn0f4`~v6@f-_Ga6sKf{sq#RgnssF)P!HGbN7fvNZ&Wwoo=VORMee zxV^n;I^LYWFCC(M6Ow6yJnS>CaEAEd3yRJ&{jeT+cyWg2HpB8NEdW&y&cXJ{?$O@) z3V~+_NFH={a=fJ`~p7?aHeUQVMNV*b}ef% z^GXr2gN79uTS_abJedUl!=@Y~8~RgBOBen8pNqi@JkiqxQ8e2vMspRrDsiz|&5sa!m>(zE# z;+k}qMhHaFYWd$ zlbvdJf}0^r!rWIl+tZmL`=cY>G07JJ+OvZcP1HjF?eXGQ7SR2zgq(l^fBuFTtmT)x zo3|gne)xQ=>we~Bvl0+UGF9RbbrMI!jWzXkA+UFqb%O z3nFeaZJj|%;-wZ7l!Ad;8UsJrMW}xf*JL*4PUTl0w~I@ZB*bl03r;xve2I!vwUo`e zXwXq?E`T-3r{;G9RnBqOI3#idy!tB=3+gkmN#8PP; zc)^*P-{|4(jfyZdsUy%BBiAFwNW0FM#>xs8jwghrT`VFoZ z24;b&EPy44Hc+J)oHp!xL4z7^JgEbivmb+CPe zK;Tjj3Wk(Rl#hUDjbK$eKI?HgE12jYd5=X^K_%z*Wanm$t~&?iCD0_T8$u@l7bau{ zw)3nIi_?C>>UPd%B-)#NUz=BLdg1;V1@Y;;f>^}5=62wOG5ClwBvEQEmm$#%IOO5O z=pYP&Ey~cg2spOL+FPXc%!m^mXD0EthMnTxP>q-xd3Ay8@8jppae<;0rNO8~HzfGD zm`WvF4NOCP*dMSU#WdZ$Nbez3KvcA^wN=bx9<;lgqwh&m6UmZ{rMB`V<*aCWbF=h_ zN@Nk+qO(L+H14B4?65*;f|a!q4YhFB!ewm5=JZJ8dQp0yE%xH-=(3CClbtC@u!0gM zKoC?>bfPKCB!=Y`^x;z3ezAu@L~ygSopV-u^==IFzQn1O^nnsE#6weEI7u7Sh84m~ zk1Sq~LSy!O2StjNS1nx74osWtCBtS&57mi)DKd%NfA!#;X>Vb_CL}f9wW9Z!{1gZ2 zG#Bx0DLxR#HPZvy%H6hQKMPf#L@nwiHB$;atg&GX+7l2`jScs z?3>2vPfU>yPPMZK@|Tj-eX2g>6A%X{Y8POlAQLLB)RO%196g>~MX)ZKPCkr}-Aotf zCO!whYO*LN=`_VQRy3|37PiV4fMK>$6kpMbuB}-=F&%K6Kt?Umavc)2yMfa z9|*(2YpltKT?8q$UDR0gd9nqwnoMk~drvI1CrV~Vj$qsS@I70LePMveSN~J)qshT4 z$^ksSD~KbLpKhd`IMP#V*FX_@|G^QNnuWGFSHzg)aLp zW5!TN;Q_!7@;srTl_?TlS>6&&#kvP&deODUtx;?II)g%fY>YVZRVNaY$d&21;9qbJ zix=BI`7d3vj`0CudyNJFCIz1E7FgwvfbWFbtY~OJxiU6 z^_V_8O*^eQ`>LkGU8b-y9xBkjb2`PPJ5-wVY!(!XloGzlH24R94<)GKOf5-wQdPsI z#6|n+ocE09Au)&*0@RT$7*=oEcVMU0)pNxClk!0*5+!kmFqIsF#H3;m^@N3|BRBLq z=(C_kB4>sI9vsa32@t}Z)WM_MQe{fz1C+Yyq8T6Gvg6SVov2X~#NN8Je~DUI%e5z@ z(29qZNrY!7I?o$V+?oLsvPX~s9iD;wz)y?fgZ$7c6GQxahH(2@-`T-tJw?&d9mEHd6|!g;0_$8y(GqF5e4q9rC&Ri$*v7CpTF7{i=y9nY7m7#?jYbUgO40f-Sc41v z^daJ2dX~O>`hp&t%Fs<(+1^FH+DQM4-+2AzyXT+8Pt2@e3-=q{E$U9Tpc5X)ycIzf zjnHBnZZ(0?k+d^(Dp>>z@d?qEt#TbCMS|j7(_SCcQ)_l8q&e5Bm>PIIwg^Y7gxD8)ta}X76%nMMg*WJN<7oU2B#DJ;P;Q1WK7X%@68!d5~5_P^v7*L zi0tmS(kL`RxnwC5G)-q*w*kysc96R8k!X`*I3o zsP`+ClqE8n?wJ4Xj1Sgn+Cw9gg+3(MNotloE)DD4#`1h0p)xnikDdZvh4(>~<+K$j zQi?C>ZNhzb#Jmah=R408bj zzkDs?WQk6%T0O-F%JX>vGOlnUmhl)cMIw(?k&~ z**1({BHk5QXm>e0yEp@thR$}r_kSzEvq6@aQJB3YKdf$4Zcmg13pP)d2^8e^Xpar4 z^wsGJOQ+~GU{%P}x|otz-5hNBD0xb#4XyPq-F#J85-F`8>V`~cf-t}qF>cTb|8yUm z*>}1LBPv|p>PL(PZ6B|mmYR>%EcR;q95o*q2xz|@?prhG{B(zYARsAU?Wzh*(YGtE zVZwbvLw)eba<+@M-~b-C?!L{P;|qw$FAyXCBw3r zLpBZR4jmqn+8aJS%7PPV5JqT(DWr2u_b`LA>t3jS8YmC=eKyK5ciFtY^**j#v=kQfU7!WGC-JUL-D^Q6k{*K zmbuU~_!fCYLw=D#x!s7lvL+E+)f|CIAZ#J5I9}xfOh`K3+kOe5waHBW$IbQMAffj6 z)5dDLSgkBmH4ZofaC zqpveE2>?qsv;P{R)^wVol%+A9BY<%e+z?jz2hx}j&HnxJ)3=XEVlpl-0fsS#v+AU& zxC>7F9=fi@tMD7OaA_y||AW0#?jHeJJM&8WNwu!1SBZO^66VVVl~Q~Nt)R5vsO{0V zQT#STMe+`px8%~F{aF5odb;e{AZZw6u&S2exb;X$? zuaN(fV5(Cm8Gs^&thlpDfjQ8`y&Xx*fTrEU7odqx3z?Cq!#v`2sXXuG){7{AnN2*5 zhoS~lmGkWv$J=ey!{-xlGh)9`}*N4khsmA#Dx1_?ryH%Ex%sBzq$VnS(P@kp$rCU zPrX7>)`qwSfw3{u=+f6RRKpL3r;0tDUoH2MN$Pl;kFOYBCdTGkr}M8@hk(12Z3+lp zOcB`D_{?PA?~7IpPs?ZOrlZT%q9eLqtqpk5V;V?OD%ls2CHZ(PCVXR^_zy+sa*s>S zKF<{2Zvm1meDZ0=F(&JtYm7C~UN< zBO+5pV;xm}+#o9~EOuw!Jj~NLA6JT`h43Y=p2x5570ED>;j7ISC>pKEWVpMs-IVGM zjp=Tku{!)U-An(r!>Ektcn??&S%Fchq$>zQi*jExs@DQx)?L(IE!bb_e4sQpMA5Tx zR!la$;SQe+F^P5|qU`J?;^mt7%jvj)*E9rI(Oo})OH{2$xWkoVW1i3!;XoUYz2BkT0$rN4D;OVw>JAm)IJDgxdwv_Z4_HnHvD%R&oyK+7TMnTc#ggv{EEoIFmv6suyaW5#2a9()-=l-$cu|(hf}p(!OSErf#@fwmjQ(T zIy2%!Y;hM6tY4C&C4Z)5PZkG@sGC+p4Mk*a^`W)oSz1ihzoEucm0!L@QwA9LtaeOq zBWJu+bnKFrrsc~fpxtD{U`w_OJV8VaUN<_WI&7LlVU=!0%b2fItl=iv-qO-q@9mnu zo8KLPxYbKHGm~_3i8c|K6x!4R!DxoQ>cj#7*b6v$L{sw$0@~Jkv1VqHEfccd_T?#R zrO-rxLXKeCmnhr|R`cd;g}-46c)a+_0tFsr60x&ac>vGGO%bSvBg z8M-p1w6?l(a)4#}tD2T~-z}jzarzrk1v(XL~1Ez$IR`wg!Ksx9S;8(31lE z95%;M5dhCRuCGaPlQbB~<*0G`x{nDOau&%D6Lsprsd{Cl5KySQq|1)r>>PwEB?zwR z{jpghTCVF80#a$D7a zZjw&kWQ2_DWjY_Biz z|2jcY{|PQw8EJPbT4VKUcj2l=+Nf3y2}7!hIjAWcnKhvs)M4CUc+RCNKP z6##TUCFQp_p!B0!oyr=ZJlgUc8M3||2dHL9ll24(9*4YCWug{xA2ca)kD8zdlE#OG zf?KgVh`w#Vm1-V$>v=jkt*44}4H9zgZIsH0H5l@(I^^40zsEUbv`Iz6tf9m{>4 zoZ>!rpd@lZ?4JlEfBW$UHBIjB7&pGM^7qdl-aSSM#K(X6Z_D=|uYdl!_~(CF{J)4R zL@UDc|A)U$_aDROHN;q%SK#_=Ohi)})1KmVT!#oF*X4E?xwbY38r~Z0v4w|+_A^xk zZJM4bk+WQB2nen85zCQ?(1#awgL-HNq2R<%dm%3YsUxMcvG(N-1_X}ermnzIKWqbj zyw6U@?<(aNQ7#1%yJV5t^uv-OD}I{doAD}E5Vk0t6%%)g9;(J| z&|LMKh-v~S#JH-#)W7DOb?{CG{YLO-lP6^sc=U@beTO!n5Szr4#sp0X0z7`A-oAYh zstI7kC_5x_h1H3@6zB#E%I1R^07OmvQmU;LKyg|iT=@|J{LZDIuO@>JJZ(2O@0QEu z->;V+*~1j1g&qa+YX;np^|8(0DTyG;++4vgJO!8vXJlX!l>1f zp>|An(Ru9{dGH)QCrEc_o`Pb$4YuBP;r8^V8?Ok6*=U60ZZ+Gu#`$yey|IfRd8>NWr!3{80OSV~W zGg&JpS`GC9ND!B`2%{(Yd&Mpf2Tl)zQV&aq&tCZj6516VvtJl5%l;^I$<*_+Q?`d$ zULHUZJ2*YsUA|gJ9IwR}(uNO}D7u?cJ4(^o9C6<$dIApZCmU-==aD2*)H$n}-QrAd zJhC~u){a=f4Nx_U!f<$@#tYh1lFR@&r}rZ&NPW2UEr>%{yqx75>xdLm7~T5!>j<%U zy7BDEGyGa#U1Rjz>a+cg^O?>pCFF zuu^1(L<@}t#oO&2W8XpmQjVQy*=sMu3^=eHVv~4RH`tgp;qY|-`95OzQXr-d#mwlr zxCNhIpvIO$qj?KCGn0%WIEH}1If6;XeTxFvi$ASO@F`r52Q4#xD@Tcyl~5_vCg?XIa97P!@FSm>3A*=gF&^2O2VbL6ThPH(aN`QzuGu0Jk6d{Q3Vm;1X% z2yuGV$VgN0pAhy|k~Lo49zkn61jR-4KYof}nSfKxD%@U3!`%^>+|Z@nJX2t$o+3PV ziyFj{_LjRr9Y+#G46e;udeD7!cA7n;*vh!{Vk^_;%+m?}lp<41d(*Io^xb~~k%&x} z+Q-&~E#@{bZ`*4i`lro@J>h^3ohe7R{)gR(Jcy{Jfbg|x8_f1t2NQHE+4HjwVVBA$ zWfZ!Mz{gt#{_tcyZw%Kx9h#e^M-)<@bqn3YdaC)cyN&j|_)h}@D-(PqtDM831XLbm z=g&;ii-UGjN`62Hv8Yok>A!n;!9op(2+n+d^#byI`_Bj*HPX?Kjwd%dQV|N^ChD-P ztH*e-k6dS&pgc^}_<_lz?4z@`e9elJz~}Bki8C7OTm2sA363aKbjxE;zuGY!U-6YD zB5&c}u56Urz=U0|4P38JVy~$-5H`}i7P_C|p(R|+tADSaKrqi|;aw1Qm-cM^z1FS2 zSE7b!$@Jo6X+IH8b#S?u8~pI|FZXtEl$Rl!Ha1&s#TDkzWNZ6fmmkK0COjVH+J=m% zj!WyYDx=_YmnTI*C<-J!6wQPN{B?Z?lTt>39c5Twvd~ENM(v{{88gWN!<15`zBtx4 z&6AOLQ=AM)OR%!d4K~Eh!D7(fzo4<$;CpJ*k80sr4mDZ|CuTWfLmo-R>eHzH3#V;% zFRCld#ozR@U2~`90JaRNMh9R+DW=lx@a?w8WHiUXlwuORH5xOj>aNCLO9-xC??GJd zi`QqZSD*N`_Pu{VdD{E+`tALzYZ&Y0ZlMUBIg*frWSNCcqIk;HP{Ahi5;`|T@C9Cz2Z?>ri!gbGmG zWb^Dvs}vIhYGOK)zM;}VvVbQQamMlJk6WA9OBU@+VgtqB>_|2rC%T8eD>R9)4zg6t zN^UjKQGjHXVV!x}B+BC=3EF;cJljOQG3hiWqAri0U!J~3uN+3}K9EcD>8Jb8|9Fd} zD0HA1<>s_9OLsjMZ5f`L7mBkKo2a6e*bRgDY_>gUjAr| zzfG=-2iRCGsYTNdTO(M9>F!nodg9ByPCFgfUXfF>dx#JQn6Ow*9-;NwhO8WnOG%E{0{uR1(m= zJUn9sEIZK>Kiq}nF?y;4%<>|COKZn+fxT4vh;R$3K2Sd`a}zdJ%WQhGA=qhMA3a3! zjkY6d^Ik*USLMJUY;civDC?Io9Tsvfks5JMEb(M?E8~O`u5r}04n35Z?c21*bFVBZ+p=zqto%Y z6ui&vm&nfGKcYaW$NlDybae{)<%I}td7uU;gEBni0xmVzK<~8;ka~0vEJ6#Z4S-BP zQzy&tfyC8u$Ho)dGntNSYh{JLJX!}a;6jKll&&~g@WY08zC$KQht&Db+SWP9xri^7 zpF<{A(z$o>id)taTo4)z`{8~=W9e&H6rs|ruw2?t-#&c2|8NT#FMS@KHpB?cbaZNv z(W7s)6}kGO$QCPX1=w07Y>lUyz5OFgjD~_2QJAx( z>zQl^x0_IHBUn7@$Wzrb{=sL3w08YOADaVL2TR;`E^bDGl&h-@-FluNn=5v-y;l4p{n#00SuRP@4|{SSY! z)0zjT>ivm{L$nMFPRBFyO@tQwG+2WKqV%F(AK6$xM#!B~I@Yc5o$O@<#Bpp7Y!;0` zI(qw-pOXl74gK#5i3_mxHksF{AE4~6+Se$>wJG5}_7n8PZ*EpPOs4>_eo&w+CRqe; zNoT2YI@*bfWK31ukg{>FlP;BkZzNzpB-S%LP>XM2Bak>!9f#eztHh>X>gqwRx~tFJ zYG)fEl)Fev{ERp_RGoAbi^Y-vFd9gwE`b4&M9?u0_kUcj{Qdgl+ZF|9d@r zGORbZ7Ys$XgOPgLz3i4p^eC>bmlnIH6(PY&PBoRqy$aQgDg?sOURQlRxKc$#U3Rxs z#3?nM+R)YQ!zUy~7Q>WxW4<`OV#Ym+irep-+b5s4-OSWMM=r&pp=#i0<0UhJD<*ej z(RBwC*FXS6gUEitpMH{Iqm5fBp#mQLnqL(>)rL@Al<^_w`vFWU_+Z$)b)xB15xEi=9Y%8QP*1#vV!duy%fKHG7MM=U;??eU_eC#a*KA zjAsya_EDn=|IaM+>hLxpWLi^32qyw4hPju-32=i=cq3;o$>HQ4D>;;4uTp~*0#b9&Kn8(P0g6? zySwY(jusm)L0u<@9~q&{JS4IR8&tQ6M20p%hC)6r-l%R|2V>67@M!|l(YUq6$I%RZmABj* zVL%tVMU7o2>rU_(t;gpCb=6h7uGTJNZkCoJ7oBCY8%=U%G9<`$qP-B{Bh83QFAkO zV_Vz%%f`m;(IXr+OpZ(BaUkAzj|Hwe|HpfT5i+`!1(K0(T}-`khw?>%qAAFv+#n&& zEiuze6vlu#E=wS(hco+*!c;IjPn$CNtH|T9lAOZ0-^gHsHwaBm>iUD94C}f1AmheK z-r>5=1Imi0$29%>;RRLcXs!6CCm~=9er1kK9UD9emm;wK!&W_Gm zeL(hs<_Amrd3F2s3p&-k|3J>sR&CJ1nN6e(iKdMWHV}wwaHjx#QQc|H`eeG+mW)-GfYL=%u0L!m8H-DQgKGf|i znFkHUEo*+#Rf1>j2Xl`2OTaJwwcI^Ht-o9#7FM-kO?WR8iwes@P{^|SIE8jhBo*Sp zD8d`Nm@Nzw(Kv7aLqRASJP=_1vXo;TKU3$ zjxW1~O!8G>bu{;BOg1utT^S0+wWSNgDOy^g7!lVs?jR-7lc#iFHd4bRs`kb8@pXp2 z3fL=7Sz1zq44P&X#|ZF3e3`atasD08f9@4g+Lz8u(t8u8h%b9?bL(8-+f;iz zl94}T`>@of!j529uhyN1s?vc~hw)UFqm(4w%N1?iCxm6f+tZ%Soc*{X38VJEJV^M} zboNwDS$p1S&AYzY91M`c|%WLcPZVl%~QQ~o&G+i!w9%Ru?#=;r;s)uk$?WHEpa za{(mam1LsSU2TRPDle6O^YEQNjSfiu8|5~gv)Fg#ErFqWqPgo86Ux;=5h+N4A}1n4 zxo0p*>lN{x={5Bf&88%&t?oVFcQLzY);0T~2fE=Dy)1)cC}B@~?Qmw29?;23xcQd0 zO~>`MinoN#?JI>1z``l5DhjM?I@C%le4#-vl_18 z=rkFgoK(oWu1Xe7vRvtd0`F*+Ua@_CzKt$FFfy)O-=Qq}%GVG7$!1~t$N#zTf9aq9 zw)*c#@Fx21BeRT%8^8VdrINY~&2TM-XP~~P%()l4h)dl?IxhYPMOaLE03r~IJJ^oP zBqyRdq*v*u<0BaP0Xo!|Z{e;sMbK~K;5C%&mVisw+kv;6rOlM8-24MCaFL?~u@IuF zxvtwnGb=)4yE&|by7tVB87lY7s!5$39>;8vCDU58wXQ12YnyZh{hNws)^)gg z7kML_!GfdZ8mCRf=-3b6|L}YN+uK^(+=R&_5nxL&vY+ia3nLlRVKEEPNZ9TRM}e@O za>gS{@T}*NGm~^8W8)`^qSK_CzZiy34EZ{Z2NdM-Y;n+v%+Q3pSyYI7@|cctU1sY} z0!0@!U#x1dq;UUvZGrwa^k{n*a>Myw4-j+w5w(|GVk;j!Se9oKYAW@rZ?|nB7ueI9 zB7$(>sElCxu!a)_+6gC_5>>HF+JVKMqM{GNyBt~c6}L|eNp=~tYYV1E%r~aSK)Y&( z@0-o@$h>KK<%W|kH51`>-GfQ$gI(&NL;P9TUfv~5x_+4Ln_*Yh+1gubeE0LKbL3oc zg>E81)TtF3KSy%%{^1E~DZaU6Ibz6QI;iT#L^|<+zcu4JFnz5^Y$(~&cr*-{9!^hc zU-L)4p%;fHV#>nAa@G#RN8FLVs&K<=EoEDA#?;a%cWeWVsxocwfNQH=YxfS_MSRJ{ z$Idfd+%h75p)5+xBs-6giTJIn(`eidAs?3OWW%8tW`GJM+9NRAWsYX(2&su2B5>w8 zVt`TM3=Ki)DdkH6(S?63@fYn5;7nL>cZ}^3fKF6^=91fZfSj0H9?!F_Osrk`%p@&d z>{3}>mz(fd)}vbfvP1!y+s`vSKSOKnjgEy5PlSP1glo)mnIeOafXXTw4!>)-Y6NSi z7~;|soXL|STIx876o=*daqSZV?F}VyvAIQ|nyNxjB=1V3XiKa|Kf`xvZc-zH%ba{yt1tmv>?aq+qz)tQXfJHK3w z)F4kjWGsL${uutccuI!!+dYi92!}VCb=f3YssH zK8bBjm@f`yzh*>>e*DMvZ(m_PLjRH*7%=S*NFrV5pKd17HnKd6yx=2v>6jzXf@o}p zNC5KfCWB;KETUc{SxwBt6Zs(;5RTu(4buzI3#_q{IzEL-&_}BT4^Mm{_8F0aT%>Vp zpZk1siT|I%9Nz|Ov=^*cPZcYaYGpcng+TM+60|N-o0_PvgYwFzNGbV*-aDYg!|B+0 zlJ;Ba63SBU9qk^SLQE=qH6=txWwK#gqCIm{?hGuKG2@vm1aSMtpEQHXT19{u@Kk0q zBZ>KH&wo0}4UFoCwIw?f0g_^UPtVSvFtprsJV#&m(`Trq$$j-Y^zRY5Y>y>BeCkuJ z?Hn%9BIx7I@(bJsAHJf8O{~$$#RC(y&_5_1y6|7^SiA~THb)nQZA0E|aS3*Wz4KRl zyOR!Kt|xf~y_mNCDE`_ge{Gh(p7fFN2&Q!npo2R^|9}4cae06D?PmG@`s3Fd#IoO% zNJX<{7kW_@lg%91|ak(SEg8@-m&zv-F74kX~sE zN@DCjVj`my@o+<&pI%&8K6VG7Mg^b4mB#-Hg)`%YNe=B4t)MFTh!8#beD{e-%h+>` zEFjoXMQSsygYP*9h60sM2YJ(B8b-GUywPnI^PVMf68vCMla2GX$O#t0Cs_U-QBkUa z323ez2$8j@)BV@m=aVu41eJ-LLrMP}+_Z~|>_mj(iPZ53^<8#Ux3(knm{PMgi6I3f zJ4a&J4HA2x=os;o4MfJLO(aLi3Y+e#Ghd;uqu30#rv7@}vlzU7wAo&!Egv4EZf_xk z`~oU3)u0W7iXs8>jEe-A+t~FKYmkqh)c`};X*_Xbi#I;g^4`G&K{U+}JdOqbI0Ip+ zDMuo*rNVUkxY=oXsEzh^pbi5w)W2C!TY&bhIT2)wFuP{bJQVY;Wj+iF%MxH!Gimf?N>*p+q94l1|6< zH64zw0X3RXSEPMuKdP?;@grSqef9+?wGHoMgYP&^HGeh?w$M|;PGn2G_ScngBzh-_wc>k6Y&q39}b+5rl%^P*h)JgvM`hR@Bqc1{` z0EaJ~1R^VFAX=1%xp-J-CXM#l<^x4sht5p`3W|^ZBgf(NPD%q$+&D8mnky18i+i=l z6IWQr^)<1F)`kIBR{PcQ9Q%5Xd@E|-lj4VH%qKx2>hO$xt*=lMyXD#;X1jvECbn3^ z8`;#9y?x^M&H=5kXeJ?qu5e8-6X3UWs!%*$?3u3jY?;^wD2GMlTpllvkfHnhxBD9e ze%M(#-cxAGWA$dLTsZU~{W4$UMvtfZ|BP6yV6R$ykR8->( zHNc!KQ*?uAkDlwDgFV_hk1fbKDt?9zV3levzJ2-f@we04n>|*;`sEBso^P+!baqa2 zSd->Z#Cw7d$)KAl%&nKD1@Q#IR;ebBh6~x+Ft$?O;XLaHHC8bI)NiqZxGgtQsZdo2d zZfS;F?#!#M>Geg7gNvp0{dB9#$tt(GAeaIcL^122_G!9tvn_ zg4(nTEBY76#zTbp)Ad;^8J_&fB}%9Kae=ByH=kXS$t5(kaTqWCT4t?qbzBITFFmL& zDw(wim?ss;z{PivA;N%TnU(~atmeU~S{P5Ejm>S>)DQ)nUuh)CQ_c$x6TF!MEx(5Z zn?)7lH#RaGd0NEzD$B3+VS3fJM}|{9T4vSbW($c64LvG^q%i7gdPam`!~7FSXd5zjTfpGuqt|tlh(HN=>V?|-so``m}(73*W%RiruYVB#E~1!WxRZWKYB)5QCk~)KTr6KBrGKw0%`YK02n3!7 zb)p6Ryq+8*#oH)Usf6gcIbkS(wi3W_igr6hS0zg3g3x%s7o6p$O(E4aiL%G4odCJ= zr4Rx>K13G{GdB&7@90a#vPf4Thv+7?48~d|H9hae{VCg_Hxy#yo};D!tZa0kpnu!^ zMtkMqp_v9D;Dx3$MM~oy>@_+M-7p_eaL{&7i(To{+JD=mr)Z^vv{O{!lhkCXh2TCNkg(YO{WB=+ld9k#o<3augmGj6&T&$~zT zTa`#pv@6-zXkPRF!^fN3&(^xede-2BAo5OTBD8~r^h}}}-O21^?YKaqpFT4K+C!K5 zTW3NpX!xJC4+3IXP=w~Rp!8eL!fj#e7701x>Kd639C!u;!Xk0cHeTQvj0(uKIXk{Y z)aTD%A20aR`dUoV-(G^*x^^fEf~>ep2-b+mKRiZhbK_J$L;wcdI1FsLhze6U!4P0e zTe($mz|>@J_20k82c|R>`4-8clf0sO%mOTMk@hmD>wu)bu}RLa7Y zXe3;OM#H3vm?T`Ud}N9#H3xCmdpoF#h;As4WX-6p(CS}n>)sSiG&0|K7|EaYx_&}M zgSif*iU3EbEcVz*rF(Ku%7Yg>wNg&;3CRnPQ}wcP1a<#?K!Uzr^#|2)u7Qre9Cr+b z)qPP{nJo0csO|G({0DQ&)8a2usgHaZ;Vz0AAZ<2g(M5XUXUvBtx=0cO2=G!R3)Q^% z?d#=-w|`rrljGrYl&(BEhAIFIMF1Lz8|?yYD2-GR;_xv_4c+}2HD_;reZ2T*{)3Id zKzIL5pZ&XGY(2RyY9wqZkc46C*}en<9h%k+^Kk!NU}YosFX6HVOML(F!{2UJ-hX0- z+n?WmxchpKRp4*tyk4faD>i8He<-S6HALA zHjZUbO~4E3L73vAX3NO}8Hh-1b6XLiU=pFK5EIwmMH3Zvez>zu(-Mlg>JMnA`!Kx{ z#!Ous9qX1KtBf(3^~X9rbr%A?91{5%MagwV!?emoo}C@H+Z@`j%-nVP3-bKU;Pv$n zSiCmO;#FlHu6dew(g9Z-GtZI+vP{mVL5gb@q^Lud4Hs{#B?Hv#+{IB%DL2KXl$$D0 z0d`D2UP!^^`H?ExfSI*4J7`6oAdqZ1CRBD^uQfZ)AkX)+f7?7##0))FhXVVWtD^Q$ zX;4K!LxKTE6C)nMf>$_Ov8vgyYK2*`uaPwRa@Ol+aBn`VAermiL$VkG9DPyy#*SuF z+bM22=bV@4&8iv!(9sJB7Md)1j$xrKnkBPeC(7vXrL;SzW2}-9tHkTuJn0KPfoz5Z1XgmuYy>78vHTkiOMn&D($vZX=kQeFgDZse0wS2lu=-~0 z?CHVF`0c?F+8Ghe0!ph2Vd1_<>bHSXz^7-(kwS{!oBI;xkmTn}7Ie~&mXh#(`g#OZ zQ}ZPhT}qWo&3+7m2!m+R_q0CtYA~p`{DKO&vWMFcaW~uv7ZlqXq-{rZqLA8B0^>Gw z+?VG!Z*P!8ju;=&f(G-lP2N=2zSUfh(Q~u(`UZ!JUQ?;j2nWlk%o4i5(7m9_>KwFg zC*!WZnf1{!$Abm>1_f0B7FH8vmUa45PZK;9A2Jk0R<}g4NTpp6zlfHo5 zJkCO=ZxWyl1Pg=?It7hp=v+&%K5HvWwMFn;w{0$@JlriU6Xmr`A~Ci})Nt#$Y@eMm z;0l)J!&4RFJUV^dgq;Kr1{>@6TqlgyfZG;{61(Oi;DYFDJ~oR?cAoBioEAckcq@gR zscosGC-0mcD#FgTizWd|eZ;4X>h@ne2*}4xhPGPMv}|oUu4@WT&wYV&!5^T5H3_mf zrnuIVQwZJJH}yNLVyguuZr!RA*a2|c_9{}0F^cvIWyZyTZgen7D{ip4NTWVy{_736 z#FL-&=H}7&F-hk*elhG2b7C-t9LRcIqknB&kpPC3VD-X8`*U)m8}GAer5TQ@ehKnv z%ggS*QB+nNIa5O(xTeYmbrGU`oh>oUTrFDV^9&=x4@Ki}0b^#8&Y(4@sK{9RhUQiZ zaYrRRt~5WnkO&P8J?Wj8NO*QckBkxF4srkxy9I1pPqmfMQC`){*#gUCf+)7P&frmZ zMiK|957%SF*;45}etyo7AkzJZyPn*^;uu{|1`8Z}`wSmr6Gj0n$6C!G(3LyUp!M~u zYVr^M&UqLr1V+l&L3l%h>lnAA|XY-(pzCC#xEzS#(g%jZ}xVGxPO zi%u>K63iR4&g|>H!tG<-Aa-W7I>-g+AKrIa-B#!8x<|gOkXfzKC~7nnivR28fNz`^n+Nl zT>j(jFF!qcjjN@7a|~+xNK>m!brej>1yvE1A?yPE8_{w3 z^3(ONQOd!7FYg~SmT`mSw02IhY`q(I0R=d6MZ4@h1N zGy^-kI6Q(2bh%*ZkJZi1t<}vRkg>3_wy}2em$j`?pWxa06ofvSbG%ZHV77WRdPnMH zSb)Nuz&-0Jv|;@Vi(ZnN-mKLQIrtW?PibBWckK$IzpCcs^3O(h!5~~PY2i3MhfJgj zFBuJ-_O{=w7~sR&6FD~c!9}#C=HI!voyNSnold0&ggkACWEG5zPFUVmN0Bp}1&`aB z^x}qkce2X@5qpRQlY?t*eFN^^7KVF`Y7A91lh@hojuM>g7S}fI8aoP>!{Xze+!UF1 zCVbUIf=sCPu$v(rG$OZl4p?YlNfLKK#E*NAw2+&$W(J?H_wPP`3;6~_0fKce z&c;#Nm|ka8Es|gGkzdlq&Mhr7h=IVdL#{`ShS$%p&@qffXMf0ksN6Aua25dDpPz+$Xd-L}0w=efd;&WPV=HsuDm{TU1;s1rWJh{Qy~u zZ9fUGhinDvJD|uudf^LV;yEeCFEQ#D4B|3vxb9W|HfhJjd?oGfq9fbRB}gShhF-CB zf-Xd>4_AU}-L72hpdu8j-ddGRtJPMng9lcwqfHxY?Xk<}Kb~zPIIpEe-MPcst!1pq zIJd-iurRaoGo9T2Iy$_(gpdqT;&2;`K4P7PI$X}xS@)8HSfZJxN6-b;E@cRY;mOi^ z1jd41+Lc)%YuG0gj#3nIqPvuN6N~@yc)`DQgb-Ai^T%>R*`iJn$uFk}Na}2xFQp2c z+{Rd7?I>7G5|R%0x7o3vnMry;IYx*AFA!!nFvLj^@SU^~f$dpGp7I)dP8^)!2d^Ez zzDBMMBLiz$k%kv1N?v7}q*bOPATU%DCEwu6ww5^;g*i@G)3=2k4~Xj9zWahZd%d;GBBE!wN0IV?w7VGen8gY{mvGA}mEVH1|ikitYuERT;HC;lnNT!&l zyo8_yb+{`AS5x!xaD*QiyQ`GdC`MElYw>VGSYn#iNh=l_4gB>AJN+^JrT6GH%ECek zpvwbgVAX*_SNZlN`n4nz-qA}Gkb8xQEQa_ZlMiM6R@aWuo#=$|dpj$L>Vdtq{;QU~ zp$rkdA7Ab6E>wV3k{ay6lbraiK*TgrffzJ=PEydbj*^^XaGSJGFeg{`v`f|NG~U*Y_A& z`EK#u;{VeBeTPY(?|%C(`;K+VO091%xTeX~8M<4Maf8sX`hq33)s8Y4hLQ@~e0Q8} zoDz%B@{=qoTY0~NumTBad98;(&^1PX$Q8{}>QXHXCQE0#-T8UibXCnMZd9K|wSwxr z02ZWs{_FY8$It%=of!(|?#)l%enzz9F>4&{$cTlRcpjl2PPhX4cmPETN@eXTM>EQ$ zG}XK5rJtY-YNRsz3Fxx=Ya~crJxZz$7qfGDdD?x|02s))fA|JU>sgVLB6e~*!B&-{ zehbCiR2~1uffCfxhEzC|nOe%$GFd0yM9LCFW#>iMP4t3ix?-y0gjR*kr#m3Y2Fp8= zjuresIG{eU>bOPHwHSj*nd9^lonrBdwKq6BO`CB=MfIai*siLJ#r;?_U-`u4#Yab9 z?Nk}5)9|Ua<<+ihQ6jr&&5ZEn%dO4DcOO4~=h6Qzq7s4;#=GftzT0(MeCBwjmYz{Q zt`qLf4&`BiKsDM0dM<3X4d@DVz}p(vGJ0eko@hE^B$;}Q=_>sA+**Ek5LO!kRlWK3 z%jdg$M377ZG5?N2LqC1^_~HIH1ZIBs&U(mx_ZU%^X2={Ql5arJF0yhd>0&58&iNQj z(is!|d)a_`M|)Lsv-F6LSR$M_mwyDl%6o8uy1^=TL)dc46fYLRLgYaSqp+aFPK}N83=+; zA&XQ?(UnnvKr(6)C;VT=`U~NKTlRFn2X>>1Wlm&4x3@JjNz3C+j(vUbmvkjtBJLW$ zS;2=}VBB-GVkgmG`~K+V%7kQIlHOfjBLd)!qtOamE!Jb&lGmOQ7P3WSehoG*t(2)R99`O$D z-PNnxp)tAu+2tGPzE@|H{5gHUIv)Be0h3|DA7c=su^+NE@sg5fojSGei=nt+p-%0c zt&8@di{EbVe*r_LJ+PR|s;vyntwoFR9IYyjBILc+&gS4k;~&CO`B49)d=L=?oMKd> z&d|SyW|xP;2;y4ZGd2WnkZJXwY`6K~RP8poCTowD<4 zwYzKLeeooM2~TpPRM2P_X^d(f#r{sIAUyv`d*Y<<8i-^T2bRx|UaAy+Ha1q59=GB3 zco|01-qY+f-I1^t)vvw4ME%REhMeELM|S`1+Z%WYWuOjhbN&YMS@noaE(%MUgtEcL z^#nrv`YI?qXcVgZeZ7Cy^rD{;uK$N*~hq^N+tz`zn-BE z%3lRZD9LFRJTEtv`2Py2)YzY&qJ9XN2*Ez&38Aq^GHSLJ^-O}Y!kXIq=tp%8&<&2K z)p)R>-a$YMG}2fR$$k9c_W8H>7yqesB4QckrdCyRYW3f7vL0rze|56MRR@*lishW^ z&MF9#Un|H@z1A4hZskO8H#8mBmI(NWNPq`dkdHqz zfDzh0b4k(+Yt2bBCQJp=>j|V|rYq9xCf8H09|AIwZT8}1<#v)Oh5|M}-`>2#2s9Ej zn?p*$_T}mEAxa|>^;80!9Y7cBc2n@~sf-Srow(D>I-3~zaMJ+mQI0nFG#o-YoDs`5 z{MWD%8$WYU*odveZ=t=CpBQJXctJskDWn;V@+yMsqRdK%hz&9ngPN%rqbF!2!~!bc zRiaGNL;VmZTl4c!HxhvZU~kD9hztpj8_?EjMjbry=2Gp&iC_uxx4ImWg~A8I|0{@q zbr$*9BzKU+REyPX)b-45W|Hv1ARx6kW)!L4s%#EK+@i0#{bg*`9?jA_7^h(6nUOTQ zzYPmoXfTXfZ)rwmU!$vZ?fzl!7$)h(L3DZOIWvmUAa1Nv)#%;lFNXEVXXfeIk{D)s z2(7Y-kjq?o4bUxjBcS~w+ z{F+yrg{r}nY1xJ(XK*w0JD41+vl+=A@uFh_pg$&gbodECrP)uRBFv80>a489x+>5}SR%Og1 zf3;R=J`IiM#}#))N_+MR3APCyqBww{CYJ^n-jw8E{?p`OP6IVYn;c0EB+O|ZM|@(s z7~Kk14?jjLnT+SyaZxz*U@4d!nro5Z=+vJ2212OChIJV7xFpL5p(4y96(;gYgCbNv zmfw6{nK6`Ba8V~~6r)&_1Q9eIMm!FGY8ir`EO`jd&9v1X!px+43R1v_*|;Sbla_Y~ z4G!I_&#FfX%z|~{Ua;BFi|Y@!xHv^T@~wJs#XpvZ*V_A#NN}wvyVh~TKdPNjJd{V6#krRXTI}8YAU8QYgIcL*h$S6A97h)}(~m zWOpULH>RDMR8lwF$Rf&1WwEV^GzdV7PmO38U2Hls+e5=33R+T+D(O#L>CQpK(zakf zJ55*CQst0%dhcgF6GCyA0^9eD=jbIxY}U80*xq*+G)iY~*5V>}ino-6l0N)oc3Qs| zm+upa5a<=W^em{ByiU+hbjjHRuYImX%NvS*Gr-`^M zhRBcSXq)*RAwPRU)5@BtvX)qlG5sk{hTBULA@6(H1{@07+lz08^r+4osldzb6vbr! zcLESeN5^*bXT2G{b@ZB9AM0|%fr+CQ_q|zvH>Bk=9PNc)M9u7;P`Fhjvl_v{7{AQ z>A3EEXyY8@B31)v?9l2yiYRkIY!(tjE+q6ZuwcNlalYHWeRp+xck>_LZV&(-x#IQa z_&5_PT8JIZ&?YJg?`_$6L{l>shCOCuBHl=XD*VP}sCF^KDSCxYy4w3uC9@GEnTK&% z@-Xn1x=HE39ZdifK5?Y3_rg2_L8EaPUz|~TwA`Q7J(uzW{ zVgKq=u*X{7MIumvk{WR&((Ttn2a^GMcVCZCY;3tkb1PeXG`35=%w3@!*d_etB%HVM zOwjtQlq)jvwl6>6+Jsi-qN7f{N{&;((e2I%W?EZ9cGP!8^Gs)pFddVtEZoWWXyR5e z8q}R5y~#aorPR~qg;$KZ5o=`0=wW;ZAeoz-0dmJawfnk2AdKXw)04e7IAj4gR!=4- zqd+$<7;%^8@1-`ut9gJkb0A+s8MXrDqNvBG7bpxy@GBDDhR7NpGP{;&iI* zs49}HVslZ1JbwRmAB2g;$VF|tXF)Tupw|wX@>!PTven*(G&f7fMxBs2a*1Z9(hBV# zSuylaOoxOpZH7azx#iBq<$~HcU40wti$_Nn&*9f!xn`gYe*80V0~^Bs*yvJGcO(+o z&oThVD#AL*CYWS(risj3KeS?gAjP#%5LnIr3opS{Y+se2nf*+6EloTfJn^#>wd@XF z^{NUDR%1nE%65}{-EujiruuZ@us6l0A|J&PX86gDJzAOkYV+am-D{zxpqSa^0k^h* zd$x*wj0N}!Rq16s33YxV9JaFrB$c1=P^ymAy}1>VYBXHSoaScfO|uFlU_o+dQtsQe z*d3bbCtQ$}#lWn4aifT9WjuC?%!_DxPtX58ihj`RxO;;`lQf{`GuX^^^Kr-XW>n9f zwb^Rfw(5b4!3B-f6>6I-~U;UON5KUsCk&`#M8lFk{8cexkx|i z@@F!IG{NQO%(#k!07pp(&SW$sU>lOW6E>~W(=%lMAsaFDfD!aPFj+@Z1ryzsB0p@M zo;a0E9z)JR5N&fey0g+aq6aYDGRAiQ{gATGKdNa`o4X2N`f0Uc9`{!#EZ)D7^L?DU~J7+A|#iEs{ z$i2hB?VyLLr|4jkZl{Vv$W9Fk?hjusPxfB3&g2=3As_9%Lai#q)jooc4J{XuJNo=+ z&(iOa)%nxMn;P{Zv`A)(Y+N24WN($%jlxL5=Kw+5&^cRjvH2KZF@bRV&E=Z~aQ1uf zW>f&Os1S-JdrHA#&WsaqGn+-ot&Y8&mrsWxBs{<&E+&(9`qXSxZdu78s|{=wzx7tp z(i&_GU8xR>osLy|cfN|J7Ru9}{Y2$T1ddF{?f0$tXP(jBo!uQ!9{(kj6=m(7;3TpN z{VroUR}lzz2y10lJ<9;z3RayfjEw6Kdc(To8FoD4qtqxyWy}FjV|Az&6=@SFDKL%| zbF*~u0f0OrngBMk6)Ofgl8dde&X!Ry`zhg=R3NH6tRQ*n^BtmD-hW1^2M^{k!qrBp zCNZ%F9we}1VZuT5%kfQeLy+Z`{mEyuJ7L6 zyo1bTX(vo1UtZtde!joHzd;Y0c0yBT;15I`fzOJ{LBQdPQHF%(tVFjFSf<2x3CvEO z=BfKb&HikYL48|sZX#F2G_8p_jQeN1)adsA)#!*zGwn#(Kqmu_a~%eg^Z?|-UhFPk zp+PdK>`*6vI4)Y)ZPnBCK(Pv}{p9q7>JNl!G^kHqj%sX~NN!tfe;?gEbwd$q^#)9V zwdDNYc!S#ytMSO11(&?zo(6pS3DGS7cmtaxYWv|yw(?~@6z<`zNz`iKI>83${v?&Ab}DuTm-hbYIT{8aWaV=2`~)F2ulD9T zR@Q?e_!cqHVZGFRvjDL|)2lSu*FGCg(S48h_e0G*a1%0+-*qt$piH*2z?Y9Z*o2YJzD)`KAvd6r(3K)9t#-cfhF z%3d~}o26ICc(Q}45E2s09~g6+E}_Hdp9wlN!9jBEkSMT*@`=YwdmhH{UQsV-&TLgn z2^BfUc^}TMmSZ=Y0&F;r_w}#3bfHXjuK;DP@8AA1V}Gs<#j5}T7PSLx+F1Fr_UA9_ zfBv%Zco82h`Pl*=F7WACGO{S-)!yq-E6aIT;th`vGya;k)>x3XYG2I984 z3v}031(ABHs95=W_XY)O&=9e_KtkXFvXtq4X4@vzQkZGlvWDu`40J74DSK`AST zqDZmqrj*rjMnvRj^|Fx77+_gHZhXi3s>JPda3)cNcey=1%E}Rhsn}yKBEkX97Itba z2XA5OdR3*0d;+%kQQ%5Fr920#d^|~_L&Fa*ZFzckUR^?x=!41eA(VEvk_lht!TMvU zKGk9UrS}gcI0S*ahUpTVeQ#}f=^gQC4Ghd4cZKA8*if;pQ#U_deh1 zVJNR*3E~zlhcusDIPcr7P;L% zi6Z!zrqqhzqZzusYV9~=y+mYzLEfqwef6rCwIff`_p?!rfzK$Kz@J;22q6?f$XK7n zf)=Ug@(!E>kCbG?{o)ssn^9?YZyzt-GL$>0Xf;T;?<^7);o4ha zTE3el3f_qN_ZWXZb}(_1pHXF}Xx(#!{zKDS z?>c^Hrcn~eGAgv{MnHVXygzLhz$k5{7mK?=J*SAuJi=1KXrgI|raB_1_dDNbBqEDj z6INOorJ-!7OJD$cfOJC`x#n(bOM3T3ErtQalf|RE0SD9yx-ag+4d)z-L(!#j&7vBs`Gcx`=9xx?Or!%_+J+49~MVVli1mtUyP!HAF^T zTs=Rb@dI5fAmqS1@V%Vm5Byl$`^(na=Em;PTsZ+demaLF2#llPepp}IVuAr+d+#|K zCs?e(5H9-6LepVOk=#z;pHr6+f9>MKO!_f0Xs6OZLVNW8W5s-VbR9Fi#;8b;P3@S(;j%5sYS%z^pFHWv@ zxG*SP%f&|4NX@X5K$k#m$&4Z+q{9&cQ1LNIb_B5*VKlfSrvP7aebf#>flE1bX}SF6 z`YWvBGVZEK*b0gslP>q4qaX!G29~|e#B#<5SWRD}^DCnwlVN#*4Z2uR!RkuHdS{x( zemV*VO`&8inxa5(1#vT-KV$=Ct-gUg2TAGbq$4n5XEQTJZ_+gu=SKL~#gIj}q+7MV z26uSR?#BYQy*=|x_tZZu?*VYTIu1QF(3?>M*#^Yl8eqo-__pbkGm>A=LH|~N0fu>i zJSYrEAs;C_L}Q*FQuAcDzy5Z+^YL$oraC>L>I9TfJWvh@w4y>*T7e;}M`%lGKeq4g zu75lF@b%saNw>|cRD&@i8N#EFULWqFN*EAz-JV(yF$@;nQ3pMDYTuSF&9EIdTQrpG z9x*uM&c})Xj>$SY{x_OpaQR>gl1rKowEn4tCytP035=~bu}+5d`b4exy&c!DJr_ba zEQRY7IWqjuw-vtNW=s!Mv=;<7@;vVXn%=={upo(|-YGic{5fQFw9z}Gki&QAQ^;fJ z_YOCG`P{TW6ZDX<5n^6@*jm(Nj#g*v%vG`ysb~FiPtRg^P8WV>vK}}Bj-I}Z=jiL> za+>XS#&h)bfNsBXP>*VThHB;_@gTIZi>kL$FK#vo&go7MC}DED=^a%O!}~#FBw*sa zYxqPaAjHxrN>$lTn&)idW$+Nb1YCqEgL;9LvBG!v&!VGh{VMEM=1eR@ zI+TW^6;W*?g%*wQK8=SApa8yLR_~Hkx+sG0iq2!mV-iDk(FB)|(dN>m^>iQ?7iwd+ z9b7=iLTey}pKga?k-jr&h_1(*4IdFqI$_fhx(5U?Z2@s+l2$o(;H)P>!wf-{P98yn zQXeCxia*hZm^ zhLr)+3#24Qrk?cC_K*6m1%L#a6E*Q=rDort*@n+4Ji~_8is#)KI*l- z&-r<}R)$VG#qnR=K2_4ljnHdbRMnmXYnpD~Yfp!D@ld1%?uq`~s(4im_>oMK2|2+m z#UNKr9?;c9;Nfa6&K<_3@pM=S<-s!v?1=RwZd>sBfCeJ2G<;ic8+d4@PI+9ovpZ>9 zH>G#axXyq6{pN0C9SvSeN>S@v$U&#+gkAJr|D9rTIle5>sYF8pJ|*v{c#Y)&z;ZNa zjHrP>8QY36q6}yEg)4i*ECSNKe$y>V%;ym7yaY}%hoBr^)Rkx*-I+=mNY%qMQ%+|( zF$k@;6GB2z1wk-aHC)IoO3q9Yq)=3da-IIIBubVc&6RCgVw?n+ko5poef=K3Q8J1z zj%#YqPF$t9*%61o7(aF1Oz{I%&o!BN)?sH{Gz#EbjyyzDk2r@Osj}ZXV5{Qg&J?0}z=A z0-HK)G6$#`12e|wTbPBveP#_FolQoJLg}G8wO3Q!awv3v^XuE2FU$M8>n~s6qbpnX z4MPq*ezkwT%DVX7Ozl1nRG58Wjfucdgw>_@3kMlGm%VYrtt&PHfpp#0b;K@1IRUb~ z<9It{0F8D$xSG?WD+O7jAN_J| z{ihGmw*}hTML2|dg5M45i8`Xhn!uI%D$l|N*R5J)C~Y)42$!MOzk$XSVyJkV+xFtn z%5}DYQ&Ep}H6_=#M?X6)9h6Iqs@5#^v@T)@#8X_AnVu>vOck#o^X%QAaZM1B@K;*X zhjte|J4Yp_=Pb2ammwW%kOOjYk1BGgL?v9)mBDV!qk!_?8s_a{KxhCE%k%xKr$u0N zQZksJ)3F<{cv;v?2emfqr{QKpJ!A`GT-LvKKmQ|+qxgeSrv3B;!TY_oJeZ)hG}<_0 zl=sjtnlV}x z-kzyu@e%W>#Yc>#&q6r(#RbYa3AWp#NobHK`3>Nb(Z^fS;hU>JP)siYB*l}0PhR#c z)q@PIq(>aFxO^iz2B9J4?vADz(rQZVeMur~w>vgJY)*5DLVdHLN7y#|m9RAhQc2!i zB2N5{@dW&Z(&J6@kLVBbkZ&&ArK>}1+W#k7t3vD9M>DluILz}&xtU`;!diwsX11@Nl^-7X@$;2K;#K#@#|wVnTVF7{)X$~I)4!fQ7r(mE~vd9m8m!`mEUJ|&f5r#rWDOOj zX(nh_+OIR)nxjb(AJ>6stPb*jcY5IF`D+E3t*th7qfr+QB0^i}H?H5A6v#;{pf16{ z95g^hS^^Q@t>b!su?X>*9_mx|l>tOutg2yPr5?Ty>qPm639ezS zAumvttIHiQs;q*i&@i+Er&_d(-y7@4yN9pVH>i-VJ~z)c*Av$l@9V=PrsLXTLrhB~ z0Dz-+y9YW|?voYoCCWG@v8p}U%zD~+4=rIHqfPG!lm-oH%T`pRZoNnbB36&;XrCRr zgKC+qKONWd#1$3h#7oP{xh#87VmjG~PCVwc@P_tQtuG&M$es_#JUc!5kr1liu?A@! zc_A?&erqu^5<%>QOaMmvxmkKd4S_zsy4-uSygIQWrQ3&SsgpyXFLqzj@<=jh7Flh| z+VHI+-klGEezcb3PrY$^L44wd9oe8vj$agQx+&YUUSIuiXdi^_oA$vL*3A6mM3`I| z>C8S+yD!2oB%ZcP^Xa4!W?DRM8DG!xZLxKXWIYu8MvkiFAbyXPVZF35+CM-JU`?&< z6V%BQRyswSNVDCb-5ztFisdmSgtUY%Gs0pSSe+O^G7ExTmdWMkWLC1#dVsdma=#X` zY?gYN)QYGdjwE&1B$}Wr>eWSiV5AtBX#hQT95Rm9Y|5U zj}1g#hNT2Ysw`!hN_1J`L8zetzpIw z9jtW>W%2=8vZ<3CF?=srXby6Gs2UaM>xTl4R<9_HsN9G@U?ZiDOx&AzZ8G<>t?vdiWBk2k+wqyHoNAX;pmxK!?qT0G=}g`!vTu{oz(;8HaC zV%cw7TN{R2BSDx6rt5isdyjsrUkgchK@35yRebU-EiVU zCecylSbg8_dZ-rFaKhp9!;lcV@#~#GNYZNMcU3({gx}HO%RL0fGqVcuB?)>J zt$CQ);oi|Mz!5c?5uX(z$<`~dU$fsI1pxjIwW_j32g6ZF7E z!S(a=?H|ET4w<<>Pjj5&8-Hm+#H~CvuwHN?N>d`EJu5|V(IG?2faJx!CdG3kHIx(? zT~ICb804ILqj5~bioqzffJ4RBQ{pI{Bqbf)-lKvrdW!bdlo8lFK17iG$%|5f!)emY zBs`@J%*$<{K$%%#3jcY1YyTHZTX&11;lQd*XMJetW)L?gT}gbD*+gQc(wEjYK%o(p z7`R5!zJx;h38cUpo)282NUkpG-=u*%$bdhDe&qr;a=Z>N(Vh~X zp5-_6rCh#+jPv2`^+zMa^WQV>(?%9E0wqayk3(DoEEH9V7kh8|mO z15rvsAZXYwMx(~2lz>6Z*y-eA=aybfSn~j{*gm4MQZWQo=NnAW%Lq0mOOtYV)|DG+E6n1@SIxro6b_qgHThY z*wR(B)z0F;xpt7a#%@#;R20Top6)Z^8VVeK6#W!}&-Kq=p;EHhi1Irwenh_Hb)S+j+Pibb79?$_Hywdm>%8wwukWAsF zKw%m;F{DunNrF*pN`l9gm(2d<%p2q^aDoPuYIAI%4iao;&v}>OH`#KD$+lC<>K|-h zK!{bk2^0C2%li+X&=2{3iRPRi&En6NVy5* zPiN@MqOatd$a56;)NcY=qNWky0()mi_=T(GfJ-}?eDeVoB>jEp$OtM>ZMHo!+E*Zm zL&}73s7AAcSkL^%_KaKOEeoBlZL!8_Ds-wMr{G;EumzLjA|)p1%3a!QS2aWCYa~eC zTgZ9HUhtB~BQY&=5Gl+SpSKRAam5Upd5xci5rYA&_uRbiR7O5_v|0{Zdk(JR zD2dUNvNk#%CLz$V;2VJTjy$;;s#jT$q-S^CiAgRA^Jx&gi9y?KRW&fC{6( zgf{>RXh~fKprY)MuxBJ`u$USUpEkb{h|eY{=-x#SlkI_(TfZwRi7 zN{G!MN}~mg(F_CCq`wGT8HbK*%anKkI+Nm5<2ibTa0Ne@51%Urc|E$-UmHR+oT%rB zOSMtt_}p1svD_0oT*%FpQVLJEO-jz9vq?{|e)NMa^f5GKS+dC4kET3uJEj#k+y$xw zvZw$&n-%Xe884*23_ltU_uO}hm=qK_L_rj@EF#r+0JT{IO-?pHJzY@|0Fu8*$D^PV zR9X1&pEr=-5SU_wf<@f*U%iF#hSBblQ%1Y7p+^_LeZBmE;*2Vpu|tlvf3*F=U^wLd z@f4l2zRDyV%Gvj=%RE?Qp7J_fvdqp^H7w23vS<~MfKYJ0{o;6gd3pw2@9Te zpRQNFgT-X=<=fX^mY=`e!vy;EJEZm7*un<+`6p1r#mayG?)2+_|L*a35x3F1@M^Jb zr?exCdg6{W+au_=7v(Y|_{v{a->jW&tRb$l{?(M@;!*vPh|X@Fj=L3!y_-!7U`0g1 zW#?ds)n$=UtbBu zHLIj-zQH!S1G2y%O{~YnaQ91xYE7o9hSeU7=+B%$VjhUpp%p=R=Q|HT0pd@wbxaoZn3AGftu8^#}ePjV3^x+*MlI&mq zHzJXqz-27w_r~&hiRr7FXByMO1Q^A@)5pg5qxCD`)8smi(BcUd#)*Fg#^a)mIFD`P zPo74-7!-^FaV-YJvDwT?r6{E#P)(Jpf5pu>Kc1hcU8z8^#i?EFAkd!?YYOf{btbyd zMxe3j22S(DWRG)^fsy7--ZJ_&h8)cZS5b&E+JzUGG|SfSIgXxsJKw}fp+Sx7UT&kx zG~(PK;=yuxc)|Sm#n=10Z*T7xUvEBrc>5Vr)bi^;*jM@=HzN^yLz!GM(f^`Lf(^whV{#fEM4fR?-Vsk6JbMdVQq{?GSvyP692(C*p^5p07oQ zNTre^TPxgT4h+Cb8aQIn+_t=E+KLh>Ol#XWTLV&AbdWMb1>;5RaCD;5We5X{8I_O3 zJ?xzSh&=W7t>Za*b$eZrtzR#yACCo1vwdXizPLn6pKPz5ZN+06C0rTZ>E)$9lA${; z3ph5pR||6{FDN@Yh5Ey*q1!^!xxmpf)A zX%z$OSUGExDAlEk5JpE4GX+TMRGzUV7Q-oeM}B{ptDf|CG&4yLpcaXRBAi1-uJC7W zbjJ}dab>7dE`)vHP<`9rSbm6XrnMY!m)B8TBsCy!M(o`*;V^$Wc4|W^-+cHQ z5fYaGlS5Q^z|L6Pb-bc^AG*2Fi^ZN;LM~b^O=28_u*|CR}jGH7pAR z?5ISGqsi=-St-H0TtqUObkB-5cL;3y-44j@9IZDrF-p(yuQgp!)#y{c}^UILHX_7rhHI5QyDT0ty_g zcfklX{a5f|EEyC{-;Z`~mNv9pA|&-3p~%)Ps{G9`r(yXKXPZ_DBAi%rll>JiyTb-k zqhLJhS&#~Td;|*$qoT(sA!!sk%Rm~fpC$!Nl2?uaw+!rxPH-@{yuTUJl0}CK{rAAZ zDz|h9UDJW_WuT%o6fqC1xXjK~j9ace#$1#`c#H8M~mNA!sW(=QHCCp(~BI5>_!Kp}SLA%;*VeT-x9^ zd<$e6hcC`g5mrkGf-=|<9=IK{MA2TZ`w9hv9sWEpu^ePUrpq^=VOM9FZcc=XY&AAW zs{ZHaXr8fltYR{YR{=x8=r4%h4(%OzdS%a1qLUwcZn^lPWto|z!>xFgDO;tXx|J5ag0+~@XfQz!ZbWk$PhuH<%vPI1OVUR5hRVZdg&3mM0m+LfMJM5{36wot?Ww*I?gPA_WWx9 zfrM0MG$9M*r*H2cs%`=SqD#@t7gmUPA4j9ZDejd`=C71eaJtkudLj&u-m*;Xx&mJf zr;E41FB5Sp@71z&1zj>$RzRyBub_GhYquyOe(}!>6q^JGf4ulV{srwa8c%8_6MYXL z$tSFy4F?+Qti>;r0z&+onWf`*;fjh4i^)%tqw9x~m_wRjS_!R*EqrBBCT8d@94O%n z_Kd0Gllcs*gW$h6_tzi&47<}uC51|?OyH9ViHCv9Nj!f03_8uf>H{WSqnteW==om{ zP$=i)&D}!(2Jy}xTGOwnPulJ=)n=1w{P_Q4@7;qW&8|AJoM}B)fDn?%qlR;vX~_> z0R)RNL9iHOATTBfOb~!ECJ0u6WrEOR`FDQj+~;?*M*D~V^MLx)m*4%q?>^2w_qlF}s~At3rgDx6UHp;2tYMa*-G^gLCoRzi!8Ioa?)u3~0OZ6-E!O^vYSPEczX_=%*NSNkfh! z+JbOld=Wt^7k%JTMP$=VhA1&?WqDvDoL*j)2U-_*9vDT-e11kFF)O05EWAoo{2X6g zoSz&$yqKbX#6o-<*Uw)C*^+&3st@Oj8p{^o8T4EioKU-YeCEYu+AFzKyCSE!$;j@$P9hc>p$F-56h~@nOo(YJD;JCNwzCYw`UE(p_zSN^UH;2`Z7%OeTX^SN0;)B)u zd`!6{%}XyA#3x0wJe2I55+feA3}P@e3s`Dx*S3K(&zM6w4DVp66KOc<(JMC zq#P)?lx3*G9E&NQX1v$+C-;_N+Dgx{avRQ?n@TqoPPr<3A>n%|Df=EPRdqm$9km~>}taGE_$`4L#f(xkO?($X2BJBcLiafpXMBSK|D zu=t#0jS3UeNyrS@aj(*%k~*bAQg(`di|{YpfJn5tbpW{QTP-MhXdT?n=)l{Eya*(IcK8|p&whF?S0*OX z)}-?}uN7YrZ(XkiD5W`aGLw8tj(~vu)HjWGwl;Te&Z~+8((iOs8Ghb|VOor8BEhsq z=&D7E#{F{4g>52=v7u_<-U{+%vuq&$lId3RCTFiwXs-@TKQ4PZ!e9wO5TA9=;OJ|V zU{oq!ZXezm?TydDTuvd}kpwuj;S>>V*cmOXrCbJj!hBE;?o6L*_KvUL6V7m*0}J~~ zUZeg!S&!z^?RbX#=o~LA3aoSj#Gbqbig5jfiz$pnL{-??itndUNp0>6OiLuAbsk&N zRf!1Tc(r5>nB*a<8-YcNBYGr2+$#C`nFkY9^$7_Qf^0%-LW~VMrR(eRoO~18E1Uc@ z_2B#wWmYmjX)+?y`&CiG9Td`t290#XRc^E2Ly&{D?Gbo;RNz9&2}ed8kwFO8*>i@v zz8ltus5pts(j!f%jNobHquWy&JXFs&b4AW>BnQ~2>REl1O3kVhLV~heb+q!0p0EMREa%A$uTp zY^g@e{sxw)Rn=S4UjV8Rjme65LHUm3D>Fh_mZkFe`RH9)8rAG|e#-^oAU+KoKK+MB zqkAYD>@*DzF2>0BQ-@BWHo6XB;5OWaLCQvSz9#jP_c(KGVD3Wj6?VU*WlX6{5vA0? z#~#w_d43Ojxgatcoy}$srWa?k1&PXhVs@hC@Be_gdgI`7*L~{6 zc%DVLB~a*ltR-KiOpc^9fVAM;W|U**x|)F0YD8>}rc82$PK_LD_G8%3>a7~47cOCN ztgA0vV`!CJF{E^isFfPd<5^%^MP)lM%Fl)vdvr~ zOfW7(By~f=$nck&aTTBSlnHmEza*nybR;@HHr-~02Ap9mH(X%Fqc}dOLArq0PT7l| zUM<-}sva$(uVVc78q($>!bL*C^-p}v*iOv#$_7d#NIh>2Nj-RS4|z?%tvaS)4+Ww3 z^vENX@>z*=nVO6JrG9Uz-E4OTy;i%~?N1+XHwNpdeHQOR2zyfH)=% zzVcx-v3ZuouxG9e4KYLi@eAlA59~_MhnvbD#C$u^SBGVfRAN$hTC^62<`K>{E|c^5 zi7JGx@!AN@?P)TfiyQ@8NEQHYG}%!s6XR43m_9AtSe?5{6F>14Mu+6B68k@&zM&~! z7Hvz)dD3Sev|Yxvu1_$Ls6-vEh1=%BqoKuRa^Cm3dz3#dw)Gv&xH5kxQ5Gk=aJXaZ ziY0b`Jv=={5i&!Nz*h=+RgGhOs2699+G1T|yoS_ByK5OIK`KDN_C`_0N%+sPou?d@ z%S>akJBv%5Mz7oJbO)VgtJCXtrq|l74he`mSB(6})!&3-tu$qo!B04+>|V3ea^d4n z;o=|^FP(-dURJ4QPsuHg7teQFJZH55r(Z-!b6eD4+MQidhWy6|(poCeG6JVrx?)P^ z1o)+jem}xsnKJMMqAy9-v?zaL8U@qikN88=JB2WRta!{sCdyScQEJF90#3Y{CJzVZ zJ=N znycRZG^L8Otv_G7)jAn|zw_2SbCTbq+zkl>FYYQKZ(t&yS}|0uAs)=hlJ4M2LxJp(CZ*FxbL|x4>5+A;W?8!WO1BowdZO z;Bz3##*&H`kR48eN!1@z2@i?{YASW&EG7x_D2hp-g$j1@eSX_ZYMSVJ)#~aHzsIiNf=0oNU!bkYlc|gZbvz`5O5)&u2)$*$ABFo+_O3DTpjgIr;E2;t$BO zl38(WkVY9Ml;3L%o&FErwGIeGz-7$we9F+J)lvCpT!i#Y_v1ndA;UnI6=T)!nz1)4=-!UZpx}gvyrme(HzJGh>1=o-7}I8E%qnV_c_&S+L`PevE-%f zhTsmz13}3G2sm8oD8pIe%fh-Gj#6V?HCCDd28Nb(np$oeqYrMF=t7#S2pPEogaEKK zfiW3QKffUWDBy?NA+mmAsD^4iVjObpah^Nre9UXb8F3$w1s08q=jiOTuHFxisb(+r z?G8tkbXkw87~LcYPqOMm$MxWa)0Ml=Y@TM=UTVQ_j*u@~>UwN#9vmR04T#OA1{Aj1 z9jhtGx#V2!uHRnX4;;Q8{(8z+8u9JXk-!bRzp{&Zon9U^zCjDn;$m}^A)x%LIfRgP z7b&eG((0`%ZN&x>)DjubS+sH+c9Dp!qz~sd^qqwW^l}?M9+3l%a6>8FR?Yz!FN888 zNV$mkDm%qirx)gcNPbr5%Q@9;?!eTPqFtc@yOAQjVn~>0&D+0supyQaQ%+^|JQGzi z@zYe15TT-sbw#EicdnY#h#j57{2(1xCOdiNC#agDxfTVg9VK|J3+$tN1BywkY^(|4 z`wZPo8=T#kt)9C{)SQO;IGF>R+(ptG*{bXXG3&^YD(5aV^-1A|?+X-oJnM zg@fnL*2HW1=?9W_5!F>hp;c_ZKb^IR`EmT54%Q_N$+lOi#|h_=VHlP=xNs|+1qLC~NU(N58H{p{iSr;eu`#nZ@rffQc%RgOvX zT5)=kBq&f!Lho^Dc5_nbs2N`p89e9~z>1_QSIJC=sj9Gt$t}K1fAJxqUY%&Xd?YDw z$OkSmgo^A4Q+d}UmaRmm$plTIGLtX~WGPp`F|6+MavxDsXYje>3FM_zrhXe1+=?Cg z$~T1xu6_b;NkM}b;gQ!n)tt#7V&sq!nol0g1M+8;Tw$C>s!!&L2Q7< zPW9I9K|G`28B*h_F1ItJC`zDeJyc5Ir<3U0Vhl(|;zW53j;j%Cajghg;fO>imvnrg zNr5Oj1*BFTWQ;7m!sb7g+o;q9=iPzQqDUP*Tb3qukQ$m=Ap}=xISt z(f1M&?edmV4E?GL`-!=8!IQ|ei=CuqVWU+R=rykuzMc3XxN_1Sx?o3*A5noMB7DA` zMF{hK>1LX<``li`Z9cSux~Ir5USHWj(XvJSrCgMk)?}6zG;e!6Sx#}b1TWnL&9dcI z{&)V?$`COAgyXcDjhvO`H;mH)5+*r}<`a6f%%T`d(o-(dmfLXZ=#qfyrz;DStMv(r zkJPtvyEKeaL!N#d?hW|^|tk+FFvPaq#%l%e}fS3q@Q5{r~NhHxb*bY^Q* zDoby>HO&f5n_x)(zVX6?i*DoIEu)+!^g3mF?zLFC%{k<9rMoQk<8Vlg^EelNlUsZn zxsH&YBm8x<)zw5D7CI+>5|P_fTwB5uI-&2uZVjRuMY22lrVKAl8X6A}IxM99ML8BIzO>E{RNrccQNB)iyE>WIJ6B}4IV?GLQRV^k6iX2$B;a^R zPs`m=HA#Y-u0MK^ zOh)J9=SJs&CaW7=LTIW@{zanx`B`eO0H0D53&qrot&UR3N_RSqiai)kXqtj|Rq8N^FGHhgC!IS#1^>a)rbj~=8Ptuahzxow zvbS>MST?bAQ1W4CZ06uJzj;DBaKCUHymf+_OtC$0#n z(dzQZ(j~WB8pA0|?n0fzM(|<3Is`2{%-fW$oV9qKho#S)C65PlUP{(djc{niz81MZ zx_p7FNZ)g5Q+-+S*g+XaH{~%IbH{eLtuEE(Tt#yzh-tz_#Ls(fS0ukGM;$GlNnM&C zd^DO!1oz2tl*RBI(Ht1a}>TqA;UCS%F}&<&0?riC4)uMtIY?Cc56Zca9$CHFkwH zG+MT?42hg5OrwkuUQrK;uv$qi6Mnoz&n$w6=9n>M0q^ycGLf%zYDL_J0 zE@{SZIT~eT73%v0W7nlXoC|@*kTlPAAY+QSAlpPu2b;a?C$3mG>3bv?;ZG^nxCsL@ zS6B~o(G+ILg)NyN7eW&Dw@2%1@Qg)9sny{g($KK?A!aF$O0L}IkjsaK>KRIi$~&bp zAW{E|R0~RDM^)nYy6qWm*P>N&Q9nY%XnFt#*N2uWq^zp(pjhGb!Pb%17c(Dv=^8Rw zKs5`0I#JL%XFBl!CP|T!&?%k7TrVF!sk|YB$MduGLo*kd#Z%Lhz#g)v?&hL$- zS{oQ6_w;D=MBN@n>`{0c43j?t#m8fWvb}4Nu`ojjwwvRx;MOhilVzgZB$p-RzJ>7< zM^>rzi|Y&c^WAkR-3r$22w%=*s|rCb<04p9&n3l}<49+8pA=7bj%6TKvkq}n!+?_$ z-kBD7UzM-0_pfZ+fJzmWF}R-cg`HMK zRkC0`naLg8PrN5^*)&q0NnbqpBk5~*HnTS>9+(f$@aE>8D8fg>XHM>01K|tYGLjZ2 zZZ^)y7j){)dieW&&G#qFwwnhi-%>nQ)qXjSJ_w4H#V8oZMLy%X0G2U!(;-HY>;{W} zP)qBZjXN`tGKi%XiUY8mZ0zseu7h4cUvEK4s;+0jW%p4#$e?PCZN9X;vBO*=$@~Ke z8Llen=QH;cCI-@W>;zr_-9QM9a*6^*MtROj4}BV7qwK9}uTB|ht4Nmiq1c3$gA+M+Mjpwl!TPU=IV^-A+&Iv(X5;l>s`PRUmEE}2;7Rf2GMP5hnzV8^Y|dHMkbi7P_o}# zG8DhRQY}^}++6C0Ptz2Yf${pu_CYUEPA^>>%8t>r zOO7N(I(pG~ZJ|t>*A{{VW~0=D4URl38_hheX?l;7Nv?;??Y5*8T*ZvKeI~rIYAZBa z3#u15T|lj8XpPh)Cl@d5-n8kZ>U-@yc5{C}BuS`dJ&aESnGbV1O(#UB5vslw<-Rpy zT4hB2+j>fV5!P}Hh-q-_Y!?q0@m8$JQ{hL(#OmmwThiGkt}sLZ;pxM*Te~|fkVW(~ zO-A)D=h96t38cga1UZ6P6hPGD-0@xv;*RFbiDPT7eUZ7cqlkM_vWcb>1$kZKkn8-n zR`Tlt4zE{Yef2Q#$-ey`GL5=pWSpB)=|GYV$t)nhzRogIxWp>6C{#isKhy4|+$l7- z+dg5Iw?w8XDr7=7cXFKtH%O5(Rr1(U8Tst^?!zgjd#v2s6bMmgW;ZV4m{*IZSQj%P3k|4gU+sX#?fY}V=$TxP1n_W~^ zL^3r2`+XocgGQiYnRD6j@@DLUWaS0ZBPQqG`xo(7(bn#FzEKY0X;_(Obg+qPGg-I2 z=26Ba2TOe1#%u2x(%jHHrxAOsXR)91z2zr;j!N~LL>>=%_@)Tp86y}kQVfV3TV#p~ zUMA#B_(dGMB<(V)eAMJ6_jcEDBGP~Zc42FEbgKn%AxaiVe@?F;^JlCGW5Glk{xVe3 z4IkwZD7TI#Nd0^FBxmM;+42LIk{*^tU2NWjF9td$n*iE*oxXd_)D89nFwFO4-el@0Ac}BymKhg3$&Xz0*@13d3xF z$kgkMn>jO%7jc4Y`Vy$$Ik%ego_viH9~lV_YUJXBb2^!QJd$0#7bRa!!6c&qNw`cp z^%#L5z4(6L9Ib7y?IRV)%9H8^9H*0zd1DPL&lAYH3a^YTNvOY##ET9v%P)%h&TnuR zn&#gpVU~Eakd0LLSYBl_cU;O)t!&kGwg51t>>zPaEg41W z#oc1#^b7NZ3ORG&e4y{wM_7R~axb)B+01F;vQ$-vQp91kQfpGDB5>+E=d%&qZ*KBL z&?tkT0wIGy704a)gH)OrQki9(_-OR(*7-+bsBYTKPlG!UKu)yYgZ9JNOqqi@j^as7hv}WvR{*i&eJT&|l92lo=Y|Yc zonG;I+~j#rUkyAzI#|B3f%9-}zYP;R)C2K=5q&`nFU9LKs?|IoZ(1L9^9bS$2Gspp*kgv^iP}@ zmQt5bciSdglM_I_rP_fkwAQ;^W!)<7Qm54Me5}JA?jmI-;-hLbdV2f-Wt*tRSb+If z>_yQ}C@CO6nkk0I5#NMBZec%(BV_70*ZMKP4-Gbrd(zK+?cI1^Y9SLJC(T+(rr+XVdZO5Q@Q*K{T! zeI^s>xfga_-LP`$u-s<;6jpXmJkf!Z$_1f$t@xoZ1F{j^EM+TEdc#t#mmdYUPKk#Y z)e$kB28)4N1jDH)DW#GkPBD(mX}6#S*WD4HgW#bxcWbo!mmnOXvIGEng5FKtE zP&nf%ai8h1d{kqu1shN5WL`rm{1jud z=?~2~ifrOzk|TUH9V~Yax7A`~u|&#XRc`Zr5*RpK0cBs=5ULsdX}wC7f%BX5ZL(Py zth~D~sbJI+rQ8jvqF^62VzES@E`nnrc_eRCH4a={m3(tsCzezZD9|r5>roO^{3(uK zPaeR}*G;=qbY9k$qZ2U(LMgPiYb#rbY+kL$Ybrdi2`1@?{GfF=x$**dB{FS`f67QS zxZ+jd#;)j5@dquVU^}2#y@Z0%eft32Y?J{B)Rxz=R!KS$a*cQ_4~M=+R2qlnu|<(h zx|-bQ1VT!5#d3oCZsE=Q*MXIEPLWAMcx3R-zmD@n5sJ)5gbheA;#O4qkltelY`EfP zPDGqWua-g-2PhVS++?@5mxni9 zPo<*}p1dJQdW;R2tI-M#I2`WmXsx_eC0z%%{fzX?)1mp>e0M&1W`B_=8&!5teY=3a zp>SGVyIMa!tA9|fz;(%21do!7I&vSQT&QbP|B`xW|E~H8jQv>y)YYWL@^EEkZ8*G& z7l!X`Ebpu$O>y?f)pylDg#UZ)Y0;l8AjKs6?dL>I1KCy#+f`6^=(T=<6&2Fv&>M@U z)nIf|jhWykbb9pYB!E2;2qF3Z*&_I%sPYp}^T6;c%1L*tp7nFId~tT~WU_E@wGKbb zhm#A;$AMQ8LJ)_M8N=5vC5&!?U?lgs!g}O#s$?ESUN7T~P?c78Z*F}W`*eD|&TY+( zhz%A;j|AAjP6GSv=IOB{J9sE%MX7yKPiV)jy0{_@3eP5u)9!gF2_IB$!DwCOw)TK- znm&@as;h(<-%pfP&Q0ekb)a6~UIq)#a9V~e`do-2t-;!MdiwD0-Fc?feD}wPpBJ<> zj@6aza2ocCS=MT9KPRad;s5b8PIgI)585em6#vBp)cK?Mk>jT{APj>58xQjq2`&Wd z#vMuhH8@0>mE_#^5;E9U*+_A(opg*_Hf!p@LH!-*r0_TXNGP~#ufVBT{r$O^vXn1U zS~y*F3J0JS^72HxB@C=dB4$-Z1T(plxCo50P zJ4pSI;Vuv2Th+7qmUhz79C=)Huu0~fpi2>wVm=gU|MSlkx9~XJ;Uk@=kH6 z6gMncDb-436hw(_SV?ip@|4(lA7(>_1MxPpb4slPxWJHjJD-%$L9JcPhk7iKkx(nLzLukr%wOVyvq9RGPRRQwJwgf@4VWmFx|f4Ov&D zY{w4_@IKguy=~cZQAXNdv^ns?^0ezqA<10_&gSo-W+ZFDN3k6u3%Qmq3y73%mcTg2 z`Y*Hz=ZMpH5?#p^%q6IP)5P-@QB`+8XF9~?C?s0eWGrhqzed|@2OGPqLe4e%%6Z+Z zi&)cm@W5Y=mJjKOjNMU2*0^JCLAwWJb+HMjKi{bcYJJZrY*Ef2B353w z2am*i_s16t`16UAd-v}?@$B^c1T}OJ>GI$nEEbO~;tqbRKURN>+^;|Og#3vg>yLdI z{?O)LYL&i%1Io23$zlb?iDq6XP6u!KCE{L5?LI+Zb9NdLqxIn6St(A&>1k~`$wJ`9WfM6nch-EMP%bPg_(>%kK!OS z6zqa2v5!x(Ao=jc`_+WZr51i-h#_8og@eWs6%*BEIBX+t*K?kvNb2%|BPL7i}l;W$H( z0}(CQB~p9YT*Cf(=($lQ8hqG_#uWs+6mRW)uBSJ%L4ep4{a6abeX# zMI7n*B|3G1v3LF;D|;Kuql5GD2~!B_;1e1abMS6eV>%X{H+Gv{rH{7w%?#B!0Hc)5 zUlCIYG)QTDdXFP;h%bl(3KwTtl3Nbz1#g5aNvulTXh4K2dmqOo$FGsEh>v$zl5o_? zC(<-BY-Ps_^7%{YE|)x@8EY2hNz6mVLZ#HzQ($evQAav)ptz=K$xY1K#+=-Rz2!O| zj49U2rb_o*PEPZ?(Rb&&TELNlGD}%a5py(Q-egYa88*v!){OF1ln^btPZMYEb9%Y! zl1FDt4N>|6ZaGvIy_0Ms3rpmViehznEn3Q}H8IiEAMX&u9bPIzc2$GSN^Bz_)4>RKB7;^JiT+=pcAH!m9MwZBALzLI!)R1ZeiA z#E6XQ11ITfteG#Ga3EC+va_1I5}Oh1x|9Kd#l;W|c9}vM5ZveJlWpo*+IDbiNVGZ* zgQEM898agygeHMzuL^+_VqH+e2ocY$J7B%(Y-o-Na$mBF97(WT{1YuZ|d0 zTgGcY=^!JqnoDR-b3u~Jkh+r~b*!u;CEl4ne=)q6x>(~m1QDMG0+HF)zO&M96R)mB zh=m`E_}t5>MG2qLh=cV84|58=A?(48@yRJ_w41Y>)u~DJC<~x&jL~VV8?s!}-Qk`fkA(GeX>%2yyg+mSQO2Ma$wxeR zugz^lwoXN2Pd)e|)bP9k4d`kxVb;OIbhd z_=~oqK7m~MXm$UVBwwZd$Ev5)8S}5Vg>dd|pw%WStB{jdGvMRY1~y>J@w_?ZUZH1@ ze+`#XS*zHRvkfID$i%t0Y5wdbigvc`q>ie^& zLYz_^V&s0GesFFx{pdPvl=aw&O}T#@L^3`E-^Za4(LI#Xo}MRtbw~)GI++kP-N#t! z2GcjCgcB@~l?@_Onv&Bmj?!I-AEj0t1Uag;J4o5g{QcoC_Cj6oK&s2G%k4a5$uURN zswC-=C?=ZHKs40O;Flf6e1W$c!Mu_#NaZ%ika70(BTM%doZ5B|hV>`vi;Ii%DQd5K z4>QlxAv=5^JH;%OaH+a`qza#@XyLFCZ7X3?aR6`EAFuZ!i92v1A#si}AraYwU^{e9 zQRz5zq~+`+Z5$VxoJ6ve&=s!W-R>#{ae1SI&{^G(D>|{g&T>>Qd=AoTNBi6Tm;GG@^2w4@IUWnG|8KW3f{f#0%8AYNE+=J<>PNX~g$C zWRRCb-%vrm>U};I*4ugogIL@zKIW+i1lURy-)ObLJvfm+?RdEjJ0N4ql6fU+4A2K5 zx}|_`_C3p~;EI~h^SzC22pUGo@DSDV7J;WXtH{E|6n{`COtA zDjVhU#^O;YX!+%H11^Ng$$UdVFzyyRUkZ~(a&f97l+mt>(@#2#IgL09iM-#wwGM0W z`o()k_s&p(!5&uM9p6drf1e{E{lJ_?{J=z#Fk|W$?u&uqL3GLF0d{bi&w=ohN;-Hx zs-zCfgeL5!I&MfVXM1g%zPA?7CbNp5R_DtE#EX3ld8pht>_%Q#%_a3{4(HAG;lUbk zeHR;%bG*L1xs@I~!_}LjL1Fa-`ohww%ec9YI}v0JRFq&vJ^S9h&h9}Xfyl8W-Mi}qr4(1$ny4JUrZw%+0uTAi%WN|zxDyjcU zE%-50w6Izx7SS{<51u|dzexT2VIKAn4H#!dLRNvqtz%(VZSCn#tzF$i9H{i4QVua_TugoNk@J5&2W;Z zEU&U4Nr^Yp*5HH^Z6imIGeUml{sbW==Y?CngupF-LNjWG((FG{a z{Mx#A=aRph`EXy678-1L$X10ajgmx-C{(pNolJ#YkB8vcAk$`ODqc!xg_LH{9EFF0 z9b~ApZSVT0_t-bm!HV@ZTb{BCvKxqhY0HM&41U-8!$QpRlxF%HQ<~k{M)nmH&R*MF zM)s!gmpk1}zo49Cv)y-QYlqcD*8uikbl9+n!SV`E-9y0x{-BKr z1dQ8|E9^cf&9&BLdNvgraMs|ww2cFA8>NJiI%;DMH zW930e*w^7|sE1Tl_zk_~R`Faw?g%G>+4>>015!0W0yS<)5CAB>uw|$)1@CwyD!6fo z*%8lSZWPh2q~4ewGc(;v+KO{f!W@*X&h}cgc4;<^bfV`rDi(>=yVo7*B=MkI|x<1=C+Sp$V*%c?`;?4Sws{vU>J8?CYGbD>m`V8CfPF z7l*wrQ|-h(EQm&nS68FbgfEtd%ep*xc2r+7w<%wiZnxFl+ZCF_J> zM~ecp1%7fyn-GVfDjrs**j~LlZF7wkwOS>UwTN0G;VB_OB5NXJP(S1v zwaV@JD8 zfxB8pnH8e|95)$BV{7O)vb8%DPlz3;VN-^(Kq?I>%`97JpeqC=1T%r+odo|^;8 zJ*5w{hd9C^lo7nyn!wVxXR_teeji%Ba@!% zN=oHyOT8f+RzOH$uhke?76L=hWHTX!rHOHp%re?Za;YL*d@#rW%~PEpQrZAlcQHM0 z*}vgV8n$3wPODF5J4bcIp1;!6(UM;&ou|3Y{A{i49YUwGDVFSQpL@D4`5+v=ndV}#eZ51#~g2Uk5A7!s`oM@N|s&tAt~Z4P0i0V}g6B~no0XLNxI7%HZkC&$V% zlv`rpt17m#796h&l8{7vGAhNaY_CRp8V%+N&5FQ5c7xWNhGf9BGnX`EpE%2MY(QL& z>tAVtQ;~r-)_v+w_SD{4z00>|RtFr@ zdfMYA7y(e}tKv;P9PPs4RQ|``iQ%#ZC<7AzsRtJC*szsA6kJ)#rk15N0R{s0<{gBF z>=CC-`iYiBUMOdLws71ya7L6!AF*mif7+6mS>}?49CP$3VMSjo2UXODk5hny4gmk} zzMF{7(S5epQRjK(Do$t-q9kSQgW5*sC7ich2|8FnS-JtesHjvB-tc@MW#%Gf4>V2A z1cFBC`Rr4!%B!90MvC9XCL9-n`nKVdDFwPVksTrl0hRXw<01V9dLXe|!6z^(5r!+C#o0am_; zcc)Sf#k}+F7b%GA8eOfw{q6PA?^2_ti3&9^4UOmriE>O^QjT&Dn|vDqp@&1n5yTvm zAR;&>S-g1fF|mA_gx7vzza!NIF|#&4nicgHha9wdd%H*n!&0hePdz31703aRKB|&O^rJ{HwO_79 zSZTwr>MT?51w~-CCeg7EV^fF#=8W*PhdGidv&E#Og%}*)VXWPyP1ys;TSN0(0|ToL zY{tp*R?)33N-P0$BNR?4?{G=sgKUU-sfwTosg|nX>1b>>KS~FH?1hnWVg*WNHD$38Ct=}vGsf~HP&r*vbU%*hVfyKG^pPy@l2cnQf)w4M(@m{~qkHfZ^)yAMM}(kWgyf>| z>oEeirUp_M#v$A&BY)CUlUCKd4JSbbVHC|Z0)fA-OY6pnRkKL)Q|{zF__))a-QGm} z4?PNDn205QTx!}o1RW(#v$2~qwxO9uF}@Lk-%JzwOi`T;>4@tOMkhLC!x_O>_~POD>4VY5b0?E2-klZR zOK#)cQDz{SC$l~@A{Ni>0Ul3@qo|^3;;zTJ$+=)0eoo}ex*Hvj>I@r>M_#9H3tlU! zoMybo95bLSXMD%oTVQ6$zFv(T$r>pCW31bZ}N5tXv({*E>)4SUzuGbYuQja7f=XK= zQ~B;GL{PGqewZU2qlg&k5|&zU{@lh8)Qv3=6wCI78$|JfY&wWpxvMccCUm6|?f)W; z*$Ly%ebOo6&2i#ZQ%*%{!0nZrQqgMUV7s*qZ!+=4L-9EM&vV|+rfVk37Kt#h7h*3Q zkC4O1Lz}g}VP3HfyUY+;XsJ_7b-%I+g=gcdS7dXgOH#R=tcN31F`aT;&c^c4aXOL{ zTvT@CZ^5#`fsYas3bE2ipM>1tG5JG|HH69DcV^aeE8OUrKG+k>1v0sJ?xJ^`u`{I@k4h!kR5JNo$i_CKj zDHGQNm|h^CZSGQYwY|BDF_@P7_R0pN0|}<@n1o%)55tsadut%-o$ZaUGL2Vskdx@b z8kcrT*a+XJQ0fb#s;f0JQhIr}44bMz&;^b10jYnmdbDCLF3-lGwzi%1-4O(S{_pVg z!TssvWOhPL@ADyOR&^FxMvYtr&6S=b#&B#D=egFDG`Dw$OgF>iQH2$+HsZRIVsiS@ zDCKO6xH*(*az1l#o#LjR-&5o^$g0~3O?6*`j~)2J3_A-A*v}ZLcg?5lX#M0I)lXH6 z?g>Jm22gNwrkXSwCDtF%;oYrUz_d%p3B6aWA@`q1XbzmNA}zY&46w!7Kw$z+{u2jt zH=8n*x$jSUk7LIqs(J!yi{t6|SY=9`MyKz{_5`zBu?iRXJn#0a?`D91_%+tKafcOh z>8D8vk!opq6{_nyckbXskoA=Gf$w2(jFd6IxHo$G?BO~7(oOHYo*)d912En-Ve6IQ zkRwG8mRKF23c0QIm93k|Hnp<5_auZ!&$nS8h$HcwMw~!5r;5GccH$M|4lo}M&DTMs zp|U2{GRWLW-l7a+?3}#A;u$DEjaEBwwUV=P{>FgnG20lnG&C3zYooQ2>)Pmie0tm& zc-*nE*d#V-mb7QYs-mt&;U(&?S3Z+4Au{Cn2yn-d-pUhdMQN67_I3G$Cu7DfX~3EH zk^{S|cCw1_5FrJ29B03h6hdY{@{!31Tw0bXYR66_F5oLquCN?UMt90>__*U*r3)f+ z8ER@ps4&C1={>%~_r&w-j@W))Os90fCv!sP3%u9iDs0nh(I0S}Y&N^##DpttG*^4V zmkL+v4@en+qZdJJ;Tg!IPc~HuO1;=adWGq!qpwVx)@sxrw@nTB}QN|Gy83}@9$AgCBh)1@{q zRbS0Inr^zlImlb8t%>vGmRGM!4RZaLOTrb2XUDA>vSw!Fb^f}cNK-~bN241%hbvo~ zHzf}T{A{luZf+qcSylj?KeCP_ISsK8E;3N3IPQ>Jt|hnGmD2%gcwgWknlI5R5I^ok zbHBY~H>lt_m7dyRMpIsF-n^%+INzjIVPU}m#j=ZMU}VL-&=fLMnG$7gGkzi-r&~9Z zbrWw~972Bd#%{ab2^mdY{M8_~27e8noVqE<;Ye4rLP42Guwpe*Dl%_+DuiYnhBwpg z?pz=3?tp2BMFjr@Bc1E599=^9^H7QEeZH8hh<{2ckB08lamv@r8}ogRi$XnkVYP5V zVnTee7*irqxn zW0gW8t|Zr%Mtaz!6$ePaJo0xQM=eyl;WU04#tE?nyA(-FCB{}xBa%MlmJaSXg;4m* zwmQ^>!qd)gXP6<;7nTUTTPxYxingY}nP7vaG?2%PDQK8cNB9Nv&j`oIyjJWLEqa_5 znKy~J6Q|;o(m_hvvgKdf8*U=19@`$Zw)Svz;?7N5Lgt<|?nh-!?Yc`_1MW0^ZxlDq zR18CKXQ(I}iveDbFIXc!zuB85xYs5|ow;emOG$HV#ETknT68V7 zx*6Boadi|H;GBXu=z>qX@?2w@aqD`a|fs#o*0$f zuv-3&yag2^SY{kQ>CL2K$t5b+cnuxa=ZNi&D|ZO?BbSc&dG8-lEjoN4F+*?)x(>r^ z1ActE)uWY$`>C#Dd{R3kvQx<-99vYKQoF_NAd4>4CwdzWg6jPQFF`dbUV!d%dM|2z zgZb)##E$X@Dg#$#_iMEgx+CdeicQ!-cit&)7Fo1g4Kr!4 zG1Y7wT_e+3FfJFHD9Sq(B=P!=X(59=EM!TPck$ZMnOE}r@i-H?8?OSTq*Q>zEHBa{ zWqNQEWqLs7)J&#jf@B{#4uLj7nGBT>)fa2D7kSK6cRm8+Cn7wYM|_$No6w+A7H%PL z#L*%r?DH2j&+HEl?M2Q;n{SbG6#s*+ z4!dIu34$a#bUGWu0gpoJphJ6^C?+B#!ZwQdqvGOuN})Vdbkw`VYA`UMjFq_{nJjZj zGtQ6f<<+%Y2u$09sHka&Qgkh~&_~7AY%GD*=b?c~bzi^^<@FG$XA6wZA#+@|wIz}- zz0a3($E(hMbGR!lE?4Zwjr_nIk!qERR7~z1xrF@*k3+T3#ncU{4tH3hJrrn8NlhmsFhst`4h`L;spz%I^jUpZ5BeMg!GXh#c@)iut%hYB)n|45TJ+Mw7$8% zi^WFO#pMItPckTsNtaek`Iqu5M1X254vMqoJzOM2{$guqX4E$#HoiEUl@Rf`-pRpKn3xl zUbug8F0nlVu6-%arso#nmL(1hEH1;L<&lRcG$ET&($q#==&_jAt`D_^{Gwl_XMxp)C|Pw=E7 zZTSV(`(B|Ev?-8%vqnN2)0jBDVm9LJh6*sM*YW#lsj#11Xsr-(^Y;P&l6_h6)=TC2 zZB7IuKUdebmY)=d+4Ra&CWlln`b3yuPyMJ>)u@z;TUfeOY0E%Zg2y_z=@=zSyDZmj zx;emRhD|q{Aav5Z5om%LO$)*FgWg!o?3@{^M!IgST8e&`G~|#DhFtHg6}4#8Jrfi=EMhZG(JTL^r3+&NZ$&9PcV3(Sm4GoqJkv}f+O7{ za8{$?ZCKcvms+7A?xomJ>@3w2XiA(IJ$3i&Xnc2s(rpjVrz6nuG< zS$ab-th7c!V8j@(Gd=~jrqrskRXz84sZK9$#aD2Ip?}akCAxi2ENJam7xD|$AN~pE z4z;S#F$Sr8p^*jiHfnZ`P9V3@*T^42vdUhopIU!~NUZE0BJBl^{k`~4!?eD4sh{7J z10pW!i4#RSW}a@5#9C&MBU6!Qq4|2n7*?$Zzf|&&3BSf3GNPP;fS00Q^c9(#5LSmz z0f)K4J%c4JESfsqeWe!cYfOSX-i;G=_5y-dEhy~+~m8mEGip(y7Lf0Qn_YL+OY z?7drP#Gd5Rv?Re20bj<6lx+NfC#Jf1qSTGgQnyw049Ip_JW|ijs&|T203x2N1CeRa zD4o&HobouFd!9T&hCyw4$=Su2vheF=$sRo}DhB)PY3K-94K;Ga8?s zo(V6#;BV{ivxlb>QI8`r1Kv1H6Gg!QasD(*ep&X_c&{+cB6~pz5B%3- z#WXO{&uE+aYIri%$d&4W9?LYQ99xDoj`T@GtWsvxES_o3@gs8}i1zVl1sSNXi;GPm zzwsO?v%^CnUR}Riy$v#Bxp7o^0hVVS$IanF=3=mQau23tBzG7%dk_9Iln|n!@xoK(^=KCvY5k${+J#Smo#Lr5Y=)jTYrZ15(_hA zwO?2e08x8(cl^|Y`rAPoP(*lN5@j@yD8mTF`4k?k52g^sClAj_M(inh7eZk*3Jp6u zl$vC}Y=;+avp4w~L^T9V?j1qihtv<*U!{I!m(x=}*Hmyr=S}#%X!{B+DCM2tIbk|m z$=(D;uVBZW*GlIY@egqw%4RQUS7hZ3mnx=dF(7+HP#!bgt0-5qwj!Ri-Xh*yyVhG> zpOerGq$6OEqk+-^@l{FOeq_d?FG~=h9QzoJl_BGl=Dy}Cq(F5%nM|cT0LfFbq)bDK zW+{#qn{ej52ezzEoPQ@i961uNl7O>iMaLIY2+t&V0&5+;%#ND&_p+7|TA^U4sp$*B zw~BdL6F$5mrJ)bj_UbDe?`w87`531=!(eaVxBjc4MWhOlQBmR=;G&WBSRNuI{s3qD zeW=tVFQYIt`lE!%%h_=iSsCSyw)tTFL-pxhq+|ME9rYfj=NF^X^Vw)JzI*p*Jb7ke zL5kqQyXfL7Tyqd=IvPDu=Wm12Mg5)laUDe`K)&CF*L*1D^zb=BDhXzbmy+9Pe@((V zKpsJk-Cxb2RQ_HdUo4YBcGJkU#6hI{LS^)jXC?V-O_IGDHQ~G#(KvNBE4IUDCFyF4 z8<(}@@)BE>9jz#h(|c?lqpsupdd6x|#!>?B&m~RIAktJeCo581FMgZjq?V$(uBtn9 zq`}xDtZ+KU>V0-KWxy<9j_69W^G%3UBx+#IKO1yL0}}mc|3NHfn}VDlx9%^4~BQa6bSci zDdw}eT8CU&^o#8TnjC+%z!NiMo3dQr6FMOsXwx1*b2GgAK`bE% zaxtNQQ#u+>KdJwf&RnSlr_HR;$=@mVe~X8H)m ze<&u}FOZWpjw!NF1=WO|7O^%%ZM4zT{2_W82a@yhFbwmfh^9Q3N5x3KvIeKty8G*W z&G#qhLKY&(h`7!13!A0XHVS}7Z3EDA3Ibe5Azxk)@cq#PIq7$B zZ3fvlx5BvFj#{?;ZF;ftxRiLE<(*~81}`UyXV5DihGgZTs7;4gJi%O34pkSCv`?zP zI&qs{Q*R%y6PA-H=!A3JEe{z!n0{(XV~*}q1Qn(SK^;R@R}*OzqaGYm;byEkVvL~? z*g{v`AMC3hwdF%fz4}X^?`S4MnI=7odEDDt)+-mE(DMlp$ zbk~tapFO9MgsYGQ`C{%OM@z20ELiv!9k~4r>$_ZZlQ>)rH=!s#pFVXWb!i`PI;$t9 zGG^&$qs=Yru1z@B0;w0$kdS(kT93dCIFlsGTq!ca@nU!i0}Wx?iFvKq4^wQMoSs}n zS`Df*Kvob zNzWw|e`x347H%Pg{D{75FbXVpFzn#0cygY%T0gZ!IP~o1hKNi0E=q4_pT=rRN0RE- zSoAIR3^wBsZ2GENT$UmTq7%`g3-Xu4PgTCpZAR>j$PDfB=m+OHqRY%9yt}toO>t1# zl}sSbyQITBwtC_PJG3AaHKk?c_zya)FvqUjkT42^e?%Hof+6^HgS zN(juGx&m-43E3u9oicn9McKDeFbG}}?yo@ibTWmqNiC?1mK1p;s)``xCz{ccjSN06 zVg={7Xr>e~#(50y?(Pk#qIP0#E^(>fn@c+E+*^FHtcIBd{8=>=n{3qs zr_@CRe=u!hi5u|}2_!y^nGc&RaPgGXF-$?Vm^g%lC@AAP+>m0f_&3Zu+(W@IUPo!$ z1lS~3a1yCG#MMh{OVotG1GMT*u_HS82#=OP)@q^{(8w(zPIjuN~A zHSb)Ep|#|eClBN2XRcsqpBGnP4FwAQ;dPA^4~Jk%CYo+Vkx9l-xVgWE)0ci3!DYj9 z-?9dt`;Od0m5wuRo%ty&a$S;B`wms?IgAhjs|dd& z=4L1awiKJN%i`sOyF8s9GG(r^e+roL$D_$r+KJPk|v$ab`3+*f`uqN^3D`-`?H7DSoMQgvpMIw()j# zD6h9_quWU8GZGcf($Yw)C=cazRfVmP!P04I6uB4id0yU+bs~{Yqvx3rWP!K!X(Rc2 zYtU+UdcF3bi}Izt>ErFeIu1^>L3oGcP({&N(MLVW5wD=?4a(w)Q%L{)i}*{AF3A$Y z$QV1eOcr*bLP7G-6F0-p=1M;tonxN9eORP&eUePPprmZ2lssp2&^02q@!L2gNFbR)DAEDMh~oH0ctfNJ z=+8*$z8$1RWj+t&v{BLp8$PrEft8N-2|6jP30WJQ1i7AG7%!)qQ_UVp4QEN0j#2vF zDd@Ovzk)1o|wT;-GEh3;P#;sn%HBH z-JG}Ky&O#XDh>_YFwU8r{R*l_LAG4?=+=&hYAe>2so-Dr;cX70;&vR9ZIDaSvP3Po zGV>2xSb>U%EmQmi4S^DhQkVf0cZ#|NTYh6{h|gW+ps$ugg-d964eiTkEw1~N*l!7D>OGfF{(G1s>yx$lI| z%B}-bBG)n3O{2i zb$1;hBS!>Z9Ef&oKno}w+K#8^=ZWWm!`s$UzSf}VD=Wjz)dd)z9$cJ1oLoS3e;Us4 zekN=jW&1-|b=>FddNLu;c`}iklp{`)gFu-(bqo$n>}YLkb5$zhf+~qKKDsAy@aUfU z#*IcH&QRZB*>F;EC)4Jv9KicEZ2{bJg2>9A7K1PoS8m-|+22L&lI-aafGfRlWZ%qu zn&c$boVbCeZ;94jNT(C|V$v$S2kEnXM$4$h#u+lxb5d=j?Av86xrDMf;ZxXMA_%1K zC^q54%3J%6g2}$Uyq}jVrF;4^9I!m?0>h?MJ-T%XK2>N|`9O^M+7X!q@X*k*S@}ds ztfJ}(Nyvd9=(Rh0U}qMtwHxq_;BF0~A=r0x2u{z;!c?5ZA*3*EBg(r1V`s03@?EM} z14VJ}0+otQxXsxh^I*yXVqA?Shp2plkw(SQ1t_rA3~C2l6djBQe-GToXjI;2j8QmZ)Nc zO(R4K=ouGL2JY&orE{QE4DF36T-gcSIm=NoNBjtf4%EYnA0;}g{ut4L0;;*cj~u&= zMn?W(da2i_C-F7{~4xI zfb}G?+Kj86V5{d`qd=CJ+jAU1xLrBmGk-l&INK-1=1#HkMQpNps-09M%mJT0CjL$4J8F-h1w=!sRi z%{jKEVA|*g8H(4q6D6X?LMIGk5h2S7{=TJea-b+Vbh~;pzGG0}rQgDR(so zi>L;rJ8-s}QWCCII1%o+H3mn<4r-B`LV{5d@^50`&o`_bpHO3;5CsO95i&y|LVu+m z89-%~M0YKYYkWNg;O$tYm&x2sR zgxh`k3UP}|4ysKgVcbHXgpzB%x~WN8${`|Pn^GjaWa(&~Q$A0RR7LnSuw03^-nZP) zV>s$KrxwUxSYl**_P+6V&rtD%|A?ET8=lb~V<5Ghm-4*I%t~s~h?!Q(RBgbC$Ho-x z>GE(zPF#eOtY4CpaQpxQ?!D>g=-~{>Eq$`h5F!SSm`E9&-t4R@Cvs~z931bSJasxf zUU)w9b`&GA-5ZT?7k1*c{Z@T#|9!3ZOH`5hMyDGDJ~T*`UG?ydlyMZ7498R4hz)fJ z3cyZ)$;kEr84AaZ8Z#5S)y8L)R|+>`x z=R+(>*gsFEWME$BksDrfTDSsdkn4w%uMdNe0#TQIu$h~V=HQT;*NSuHC8fkWK-?4M zgqAL7Jg@YkVEWDEh?Fit2FUJmE+B6fJ=6bgd0Ndw|cGJ&3ZhAm$nk0;P^&U&k;R;&2TyV-$wR1ufc zd+duhd%?r`V3!GnWj#<$EUuZP@|+u;Qusg(;}A4?pNNe+NOgzo#K=+vuV-CjL9;jt z(2rbAwxxk1j7yKys564-Dz3&%q2!47ybDYSEBj9(m^Q^R;^AcRmDeKnN4DOP0};7mjlA z7!>pi9+H|0RZ%ddJQXe`!_aKeQOGor_bRtJ!(k^1uB6%8(&sO08D8ZWf5s}_kr2JP z`+r_jzAV}8p4mXoZ$Ic5Z#?%9?x*x3yg_Y;#fLrXp%i0Y|71*>yM|(ii?_n=eEJNj zd7gzg*BOFxZT5_u&uhiOMzlp%!DXR@lUNk8N72>rL=*Cea&3PVPk3RLnJofoxI9^< zJKxv^Z$=d?<&w!SDFTAiTwPsTmkgC&t6z}A+({vCqMpq!nfi#Nh@|8=YohF2u!XLi zJ(MjQ>tUTAQ{0tyot z?h75YG=Y4a8Zy~b>R~cm8R}n;i(-9siE}JH(*rsw`ID_{c8+2OV=C?L+Vp^*E38#8 zG^iEgYORKJ;9pcmYQhW3tedcGplGpf&|9uwzmHY0$&&D6|^^4vAoUHv9<9d03 zf5y}C(Mj!*Mf~|v$e9<@=dW#dYA<``%ePBxH81(>M_%{H>#w}wkvBf_Igh;Q zko8vFZTRm> zt&z2Fu)WNQ{?~AS5MHUZvi7a){jIEhCu`ry-rvdE_peIrP82`-AXG zZNU5C$3g8)cJUIxM{qy>?@DbczQlaLE4!A}5C2`MeQCJBUsq~hmR(=ZuK%m-`n$91 z)$DpbyWYsIzdXC%%C2{^>%HuHKf6B2u5V@6ce3mEX4n59yZ(pS^;cxqN7?mpc0J3k zpU$qoGP}N;U7u#x_p|Hs?D`_Rem1*)KD+*^?D`*N*Z)a&{hwyn|2Vt;y6pNt%dWpM zyZ+C!>;EFV{+8_e+p_C_l3o9o+4VoouKyV*M*QC+wcGJ!?a^BPgLxqe0M|4FVtt+x1R{DDuejg#xosC^{4er4@flk3l{ zeImJjRjqp^e&1)+PLu0b*Z!O2`ZcxxExCSe?K6eY`2Rk;_U`2Rb+r#9*MGnE1IhL4 zYrmOXzoB;JrT%63sws9UcKzz?`Zd}0YqRUm&aP8#<4W!CXZK&9UB4l_eq(km z+*bJSia)LOw=1>J4L9-EmD=ZJ*PowVe?fL#&#vE`T|bsxza_hVYj*v%?E3B5^*gfb zh3p#KbM)Vp+B<9C1QZdhl$Cl-ZL#+Kb}cji8c^SVliY9Beml9}seR0@WhP%!>(yQg z6qEOTF|WZNdClVeyWh~5WwYOf>-g;cS2Op_i-&lL@UT3kf*lbV7vtGB+X$=}n zjq#vA?aijgOM}KyciihuyKE+v)$X1gL5qZR?q{cqPcL>B2mS8RQh#aO>GZ*^cRQ2g zez$SlSn9UM$L-m1j}KZn_5(c8ZZ8hH{qA@=n{~#mZgVg=K5C6;jY;FEKbTC9j(SJ> ztz)h3IT9tOJlO0mwmSW3cXA9-VmcYOJDt|#Xx8mCI?cvl(phR-Ct>1!@bJDo(O5*1 z@m3!L8FU7nNnw`W}>b7*%a?Z$EI$ew_AFCMZdTAkUrJ)X6XCe41M*Xy;8j=J5Y z_86kbbkJ$CbN+!=d-1s2Zgl$N-T>nnbesMD43pMrwT`>3{-BHOF*3oEvwQcEJpX(& zMmpj1G3#XDd!6>;(b1rf2+yO*q}Ok@`Vey`tx2of8?+}!?HN)#$>YeVt*vLB-fXhe z#9Z|!vtvvFwBsGfRnsnhY%L93>uw9}d(Fk8@o{IVcXZS_KE^V&TFu$Cb=(_Ij|T1T z6f=rOdY#U=Ia@lKv>-(-<)3L`-r7rz85%UEjowmc>8RQ6q8#tw zsMp63d+ljDlosZ4aoXvQo89B_(s=uM7~a5d@sse&pAkqI-YXd#G$ zNuEtw$CF04F_v!idPht6&R}WUne>ht=t0|0TXS@ACi%-be|T`x9#5y^r9rPT9XDG? zOOSUvvp$vx3(=Uh6&u)obUKDbfAX}b=JlgJ%-zw_(h+)jJU+(M&jw4Aqh|lOJL`8^ z$5Z!Fs6QlaIv#?g+`{0FI=#Vo)@n_f-OkapIi2CDqow0!1GRRD4cvujKUkb$h~9$*wM$0Bf)7* z<7C!s^txD=K6;N0Yg^2Jp}n*?Znc|20vjD{dtliS=vWK1X*w88yN#KmCLTbxM;SwB zv5hCjt!8i1n6#GKN9|)^eQP>cY7Ls5LDObc5d73R9Pw|<-@vJ%K`(5)vbFqa^;B7~s_Q3wm6f_Z2GwCmNx`SzFtay#~M^Jgm zvMnt(`p4KIOOrNEssXUO(>dzT`bWpjrOp5(+!KQlLPgHb1zF{I9MAgA!SuM>m^LSk zez!TpC|buD+ff$;O?!3xj6Oqz?JmlR)deCovC7S*-n89qcX8g$j%V0=)0u9G)3fvA zlgS7t3!)|QvHoHY>phztH;=+${^X)||NP`xoUKnyM|Y>sPVbJ8fwhK$Gv_d*ick0m z_s^s#DsQN+>`}0>!hZ=Ajyt{<4*yXwSNy$Rs|g4FD4=k{0)?Y}6l|$*mXCt{6b|uG zu%%B3tk-HygI{WJ&ES_C6nR?O{R4w121S!0zkkr+KQ{PbgWqZJpBem@27lV%uNeGu zgMVf4;|4!r@DVVJ@|~9(e5JwH8hoR{FEIEvgO3|*8tfbV5`!xSHx2F@JT&+|gJXkF z8N6rkfx#CHezn1`H~38kMPDHO|4xJ7Yw(8*{)oXJH~3Qqf7akH8vGT5zi#li4gQ|N zKQ#EK2LHm~#|-|B!M`*35{Q=4uQwWeyTP`>FEhAp@O=hP3_fS@>kWQ~!GC4&=M296 zihk!^1~&{I8ay)iPYwQ%!GCS==MDZlgTHI=PYk{RBJiUa$J-3H42oVue*d=y|3`!W ztHD1t_jobf{Az>WY4C>(ivC2N|5<~-V(_;OUV(fl_g`i39R}M5-)-=w!P^EW z2EWVTM-7TRD)0M6ga4Dk-#7Sw82q@wM})WrM$E@DB~X45}G<&ub0V4PG<& zMFwveJTN#mcxv#22EWnZM-2X;!5=sHa|VCa;QwOqj}88NgMA!K(yvv6A2j$42LEe= ze`)Y<4Za4dBzf))20!26TMfR`V8dX~;FlZRGkDwJL?F)3r|tS&Ak;};W7pp#@J+Sa zx7qLCYrlWge*f3@`@a#0^Yj<&_kU-<|Bk``T_F1X?*-y8{-j;M0;({1|7Qxs?{5-_ zc5k!a7wz{YgF6PFH29SQ(f+chX$W9cxLcfgI{Iv!ve7m-)!&)4gL#*KW6YJ z41yfi9tAT+J`Ceze=)wg{Eopa2*g}A1me8|f#|RN7mpi{@ET}N$oJrV95-NHen+FX z8svNN{wL%bknhKLI&uxj@8El1EZ2ZvCJ^6UHF!lJ_&$F3&9$27?PQ(t|Abc!whVre zz{l)8Z>iPR^}Lwchzbivg>a#==i+vmuoD_zYzE(wHo<5ATjy7cjG*i-@mL@`yT`@*J|YJuGeb6 zE7$n`pBQv}++WM}O0D*{0#%8- zgP?!U#L!xQdD{En&PeM|x2vO#>B*V_h%2A?!IGU)K; z8N2>~LE=vx^J>>$Yw*Jczt!MJ41SNnA29e)gWSLMTJ0z8`ezLOg27)l=r~TrvE8g~3-De4W8J8GN(BcNk3YzHWFA_-?zuX7HxL z_ZYlw@cjlS2A$kPYe`)Zq4gRgcPa1sb6@A~Q8+^6F*BksigKsf-)!-Kz6g%{z z=#P`@mhJk6!EJ*>gHIY98Jrq?#^47GK4{+hwxH0b2ZKd|dRHu&cT|H|OU4SvGlBQMeUc)7t>8how6HyZo`gHB$3 z+^(Aj`v$+n;EKUbgS!R~4ZhFd*x*wJ?-_hx@CAckZSd<2ev`p(H~5_fzt`Xo8T=80 zKW^}+4F0UaUo`kD27le)ZyWqQgMVny$=`or*B>+ZHwORC;1wKHj{?tLVenN3UuW=5 z2H$M(9R{x%6n_MHe#zjw4XzoyY4AM;ZyS8S!HK~WgJ%XG8vLNauQB)y2EWuxs#L2A2)qFt}}SXi)q%WF1BZrv{%f_yL2@8Fc!KueEEZ z$M{yecKVF(vFjf&_)&vDX7DEsieHF){}&AYvcX?7_?rfQ*We!*{9}WEZt$-Re%#_^}9vXa~!Lh-o4Bj*7 z^fNEmwbRply3_5+-7ut2#;JXYi z8@yp~+u+dPlLkiyrv{%f_yL1XkM<$E{#t_{Hu$XuKVtBE4E}(@j~e_jgFk8TXAJ&= z!CyA`YX*PQ;O`pz1A~8T@XrnYmBEi2{Di?rOb_>Rga0pke;Fk89kz?Q1PkuLg1bWq z?i$=BxVyVsaCdiif_re6011!~0)$|}-6c6dUtPOuRlQYfpL0I!s=Yp4b^m9E8Sa^$ zo__lI%|vEQ#$#fpWO{zXoXpRnEX4|}#@hV&Z9VY$;PaO2 zDYp74Kpy-0FQ#%EHdVeobk{QCRfeLw%} z_p$%!-}(Rc3UBg0gY&Yt@)w~3J%wXb#%6F%mO@U?tjx_q49>^O%fUT}I&x#SVsI|j zL+;O^9K*>B&chbUD;V6T*edU4a1M4-KFf>zo3|O9f4!7HFtmA1M8;rn?v+GN%}mVB zd<@RJO3Rg4gZ0^r!8unKxi<%L1jjQt-Ku*qd{D!$0oL_w>mt$}*rMBFN!MRljxjXxD2uCwGuL|z1EO31} z*K!MkbE<>#3I57Ic$LBV)I<3N-!s(5JGM_fMPP6)6<1EoRLsb149=s9$fa13!F`!} za&QjSM()gB{DH$6oIg#K=WsDsaRY;Mr{KQL5!X-gJTEagZ~Am@=~E9+_?rJQ?3aO; z2j@(&3aQo4kjI`7_TkI1joe-{E7v;=c^efg*(u^c9ownUrZ5oc{#(qVl_5 zjAdAv!MRTZxjEahEBi1w?-?mi;B?O8QU>Qdo8+C`&tp8p;C$zbe3SS2jBgp7>x7FC z=qD;;Ga*wjIM2x{=Vl?6V0i}TICbR4Y{ic3!Tub|F`UdfcEqKKch3a2bfq+#f;3xyez^}{9pG0 z>$%>PZP=N;7~BsWE|23>2KNLP%d5D7+qsuVc#5C*20z|UU6QZ!E}!rBfB#=mmDOI;5bg< zEH2-*3LWjKT9!o8{m+ zso9LeB5-gJ2$mvRj^F}R1fUp~e&{GC@A z+`qdoKjT||VICKb!M(fKazdtHdS+#C->#5cg5_C_br{^UYbAGN@LXMgc_@SXb(7^; zT*wt%$KYPwZut;T@+>bhxKDRme#Dpjz|iJ#!9BVday%ws@El(zIk-QUPcF*Rti&1& z?#(rm+p-ILb0CBJa^vM`oXaI#Ðg4tXDs@-%;Aa6j&be2-80hM#`rP{@zB=ipvk z6!|MAU~;Boa33z0T#&(Yhvnp|4DP`-l3TI^yR#pI`){M=Nu0?AT+ZO$+ZK5j5ApwbhfGvJE@47lZq3!{u?D z$~j!j;2zrsc{}&=2!rP_gZpcjT))n{e8SfZ?yZG2my66;{F=!a+*ivY=VSpEXITdK z)N08M*@Erajluo2;Catct_RP3&X9xWKZAQ|!E>OSy*zjx^niSv!F{v~@?X5g2Yk-p z9$HBAxbTd|I84Og{#ge3TjpV5mSk}6th!v6O&C1K+DY!o0UXA$oWj{$#Fbpn;JMd5 z@?rkWbNrLnc!!VqivKc*V^qdwLZ)DPW@T;`VhNUKHP&Hcwqi&2 zV1Ewf7*6IaF60WX<5uqGA)e$}UgY1r%}0F64-9QC7m>kp%kktSOwCNp&U`G&;Q8fB zat+pJGqz z{{E1!@*h6r3kJ_$hl(2bIVl38GcFS|6*Dp$^Rfs_u_C``JvL<|1n%1 z$Elpd#azV=+|Io`!c#oYOT5mze8SiKk71()dWy_g{F=#_mRXpS1z4PAS%tOOkS*Ar z-Po6dIf@fGgY&tJKXNmF;sGA#FTB9Nc#9ACobUMQ4EU#?x5G0U<1i6ZG6TP59u{Uv zR$z73WfQh$C-&q34&zu(;cPDAO0MTN?%`qn%yayc*La7I`HKHC%*Vy|Pd!CqOvYzY zreS90V15>38CGUZHehqMV^{X!Adch&PUk!>qJ}%6D z>L(ndGBy)31=BMtbF&ajuso}=4jZ!-JF*A+b127fGG}oiS8yG-ayJk0B+v39|K@Ez z;!A#D=#Ni?ed;M9V=x|*Ff}tVJM*z9OS2Mdus)lyExWKc2XX|*a~kJz30HF?cW@t% z@-%6RF_>vzO`s+YX5gCK=n1rdBiP@QtMOm7aScCQ1jBVM4y*ZF0IG)oumrJ;s8@Yq~ zc$BC48!z()@9`<$@Y650`h5S-DEx{En4IbO4Rf&|zhgO8Wo78JCHfiW!-Wd0B*|Sdrhe9-FcaJF^#m z;Bb!PRLIQEY7m5!dh&| z7HrRM?90I%#fhB3`CP^yxtTxl0FUz*Uf^H6#Rq)Ocl`9b-ag;|GaBPC5mPb)zhxd4 zW=U3Hb=GAQwq_^x{FB#shmZM+|1!+S`?gO#MPf|G zXHuqNX69gi7GoJ!W=%F=bGBnw_TeCo6RF z_>vzOI$5Bnh>XE_Ov2R6#O%z+qAblytik$h#EXS&>%|>j=4(!f;9Kz9@#F<>c4v$=>Xxt`m&hllwy&+$)Q;~hTcEB?zcsRKPlVob(oQl?>M=3ssn zV;NRvO*UY2wqsZJ;UJFW1WxBXF6A0-;!f`8F`nV?yuzEj&u4tgFVY113CF06&4f(B z^vufKEW{El&uXm0#%#ro?7{vV$}ybGSzO2!T*s~4%|krNv%JW^d7F>;k{=j4ZJ?)! zjKO$J!qm*f?99iaEX_)+!TM~*w(P>*9LNzI&uN^?C0xyo+`)Z3%G3OfmwAKt_>^xL zB3+=LFByejF#(e^9lv2N7UXv<$EvK&Mr_Fr?9P51!qJ?>nOwl-T+1!o#e+P-U-<{G z@*h6r3%+Nl^nsotFgoKhF;g)kvoSA=uoNrud)8x9wqa-X;tw3oah%FIT+CJ6!0p`2 zBRs|Pyu|Cg%O`xz{}?typr^=;#jlx+X_t6GdQ2i z_#-#-Cm!H&{=y6Vi?{fI&-soaGY0wz&uEOpL`=yH{FZrGm?c?()mfKK*qWW#lLI)6 zV>yMhxri&dp4+&Ghxs$l@lRgk9X{qO{>w0#0zE}yOvYzYreS90V15>38CGUZHehqM zV^{X!Adch&PUk!>qG6(tz$Eb|WgiOKo%*xy>#1bse zYOKS?Y{ic3!Tub|F`UdJg0FkmvA*VatHVEC{Ob@Ugizn<5Rxjr#~WOu*z!$B%#i?ep8^VnKe#a;(bQY{ZuA!0znFAso#~oXG`T&b8daT|CGW z{FQ(3D*xd_zTkU?${P6pMqqTtWn!jcMrLDP7GWt?o*cko9Lp)3%|%?v_1wliJj|bYj(_qR@9;5S@n43?9{B%8Vob(oQl?>M z=3ssnV;NRvO*UY2wqsZJ;UJFW1WxBXF6A0-;!f`8F`nV?yuzEj&u4tgFLDI>3CF06 z&4f(B^vufKEW{El&uXm0#%#ro?7{vV$}ybGSzO2!T*s~4%|krNv%JW^d7F>;k{=j4 zXP~EujKO$J!qm*f?99iaEX_)+!TM~*w(P>*9LNzI&uN^?C0xyo+`)Z3%G3OfmwAKt z_>^z>=?_$WzW-+we#Hb#&UE~Sxmb|ju^g+iHXE@eJFq+ZaR^6q5@&J&mvb$*a2F5q z1b^ipyvl$0kT3Y2p>hX$ioocM%fw8@jLgQoEW%Q($nRN?P1%N>*^57LILC1+=WsDs zaRaw=FOTpP&+`(m^Ddw8HUDGSJb|7fGZw#QGNxq~=41gDXIWNZEjDBewr4l?FplLE&gLSnaDHxKb7&+;Pw=50RWOMYPJ0)d_)G6v%@ z2~#r@vojxyvNS8P2J5pK+p-ILb09}>Jg0FkmvA*VatHVEC{Ob@Ugizn<5Rw2h=PHB zzGM`B#RN>wbo_?7Sdibb9ILW68?hxjusi#42uE`gXL13Tb1k=U7Z36Tf8`&%%76Hf zFZiCJ3I%$K!03$2#7xDE%*MPd!cwfr?^%yc*@m6ji$8EU$8jp>a4}bL1GjT8kMI=F z^AfM~E}!r<|6|y~fu15W7QbdPrezl9WC0duSyo{!He?I7XE*lcV2H1^9HTNe6EX$UGb?km5KFK;tFaCn zvlTnC2m5m<$8a)daUoZ59k+5f5Ah_=@*@A{Z9d{leqiY0fu15V2IDaaQ!^8@Garkx zG%K+N>$4f#vI~22AV+XKr*STqa5Xn_2lw$PPxCik<_+HCQ@-J+KOh}4$;bIWqwp&x zU~;D8H_XL?{Ep>Vm9^Q3E!lzH*^fgwnv*z_3%HzXxrMuUkSF*n|KL^r!-ssq_Y74c z&{G6PXIv&`DrRIh=4BCdTh!z?95*Lfx|hDQ#pr=xr!UOoqKtNr+A*1c%66o zgs=G@!#_-3vlDxA0Eclbr*JkGaV6Jt z8~5-qf95&<$!omB$9%;8p&^hkU{J3{^hRQv^n5Tqb5J zW@I+zWf7KQMSjnEY|1w5%wGI~!#R#qIfsk6iW|6{dwGPXc%GMdopYa5filCD(Hs_wX=(<~jb! zYrMn9e8qnmrc$7%NQ}w&Ov*IO%pA_ ze$RSr$~NrGUi^W>IgV2~hl{z28@Qc&d4#8Uo|kx?clm^``5(hp5A+n7vG_HUF)gz& zCkwDR%d!eWOu*z!$8VU61^FGzu_|k`5nHkYyR#pMa5N`z zCKqry*K!MY@gPs|SN_4P{D%+ug6|osPN1g@(Ew_KZdOv=qWN|@oOeyT4rHR7GQCfWfj(9 zL$+Xhc4J=-<|t0&49@2={>aVzi3fO`zwiS8;w?VlbG~E9dVzkzGaBPC5mPb)zhxd4 zW=U3Hb=GAQwq_^x{FB#shmZM+|1wPdKu?hvlku69 zX_%Qgn4iU1hLu^94cMIR*p+=ah$A_H(>aezxrUp#llys$XZSm>@Fwr`8Q=1Y27!LU zF)Cv-AyY6tvobdeu>{Mr8tbqzTd^a1us?@#3@39I7jgyHaVvN85KrV5Te1VYvmb|WG$(N;7jQY(atn9y zAW!gD{=uvKhY$IJ?-{Cbpr;6o&bUm>RLsb1%*!Gy#fto%_1Khc*qOcf1BY`Qr*aM# za}_smJNNPkPw_l2@jCDF319O+hHVn)DKca6YbIk_W?@biU~!gZ71m-ywqSd9V_y#D zC{E-I&gU}z$j$tT2Y8&n@B;thEk59LzGKLyfqud>8sjh#Q!)d;WgZr0NmgKW)@2j6 zW+(RK01o3=PT_1W;!3XPHtykJ{>*dylh=5MkNJxKGEB2TPmvgt@tKrqn3*}4pT$^) zm06Pw*qrUym3=sfBRPT7Igd-ZhMTyP`+1CK_&cxgChzkZ-|~y*fqud##9fu_Jr1KZkM*Cvz4Tas}6MD|hn{Px34;@^9YeBfjJZhHeq)DI#Mq z9+NONGch~!u_#Nk5^JzNo3Smsur~*C1jln4=W+>Gb0c?fACK}hf8%A|;5|O&8-{2Z z=;upD;a5z+Cvheha5>j<3wQA#Pw-d%!K?g- z5BY-c8LCyFrwEMBxJ=Ac%*bra%OWhriu|7S*pzM9nZ5V}hjSdKat;@B6*q7@_woo& z@jNf_I`8rcU-LhPZ5`+-GGp;;CSzJ=VNMocah7Ej)?!1pV0(6BUk>IdPUH;E=Q94t z&HRZ6c$~lR0{`MIKHzh{W5_mve!?>v<1i6ZG6TP59u{UvR$z73WfQh$C-&q34&zu( z;cPDAO0MTN?%`qn%yayc*La7I`HKHCOxr+Dkr2OBFg>#}Hw&=@%d;Bm zurXV)BYUtvhjI)ja~2nJ1=n#ack>WW@+>d%Z{Fr3zT^jnZXf6=B4aQflQ1vYp_0>u`Ro>HwSVA$8#FzatT*+BX@8gkMcBs<7M99JwD|dhUgIJ=SxQ6S4_a< zOvi7Siv{@|%dskJvk_ae1G}>yhj26}aV8gVIoEOvckv)k@K^r9tNe!#`GW5ms$-z1 z2#n6SOw3fw$ZX8ZA}qy<{GRpLlx^6Vz4!x%a~!8~4i|G3H*h=m@(54yJTLJ&@A3&> z^FM~|6zC~3WASSyV_IfmP8MKsmSq*zVneoIdv;@A4(2FM`rw!Z9jiGa*wjJ+m@53$X;tvl{ELFWOu*z!$8VU61^FGz zu_|k`5nHkYyR#pMa5N`zCKqry*K!MY@gPs|SN_4P{D%+ug6|osN1&$&jLx`B%v8+C zY|P6dEX9iap7q$2ZP=N;_ydP?9H(*)7jqRia69+%2v6}mFY!9>@(Ew_KZflY=qWN| z@oOeyT4rHR7GQCfWfj(9L$+Xhc4J=-<|t0&49@2={>aVzi3fO`zwiS8;w?VlbG~E9 zUV(nXGaBPC5mPb)zhxd4W=U3Hb=GAQwq_^x{FB#s zhmZM+|1wPPKu?hvlku69X_%Qgn4iU1hLu^94cMIR*p+=ah$A_H(>aezxrUp#llys$ zXZSm>@Fwr`8Q=1YK7oG1F)Cv-AyY6tvobdeu>{Mr8tbqzTd^a1us?@#3@39I7jgyH zaVvN85KrV5Te1VY zvmb|WG$(N;7jQY(atn9yAW!gD{=uvKhY$IJ?-{Cppr;6o&bUm>RLsb1%*!Gy#fto% z_1Khc*qOcf1BY`Qr*aM#a}_smJNNPkPw_l2@jCDF319O+h8+;-DKca6YbIk_W?@bi zU~!gZ71m-ywqSd9V_y#DC{E-I&gU}z$j$tT2Y8&n@B;thEk59LzGKKA0{w(%G{#{f zrep?w%RDU1lB~e$tji{B%}(se0UXA$oWj{$#FbpnZQR4d{F&$YC$I4iAM+LeWtf41 zo+2?O<1;DKFf(&7KZ~&pE3+mWusPeYEBkN|M{)wEa~_v+4L5No_wyLf@ONI}P2T4- zzU3E#0{w(zRK{jPreJzzWo{N?36^Iy)?s6|Vn_C1e-7msPUb8wIC^!FznlHw-Z((9f5Q!mpTs$(fGdFc%B*JC8I@zUF@nJ1o#sWX9sxOvbd#!kjF?;w;N5ti^_G z!S?LNz8uU^oX8oR&t?3PoB0zD@Hl_r1^&fbe8A^?$B@GV{e)*U#$h6+WCniAJS@zT ztibB5%O-5iPVC769LBMn!r5HJm0Ztl+{45CndkT?ukj8a^A-PPm=S@VA~7c8Gbz(B zGjlLMi?IwVvnCs`Ioq)-`*09PassDw9+z?rH*qKT^BB+YcV6L5-sdyE>3H zdwj|_3^6*;&zFqCub6<{>58-z~_9&kmCaVgl9CyVIrnv27b#tEXggkx03WBerA*c4t2h;b>0cOfKMZuH_c);z6F^ul$2o`41oR z1>ZB&Zw9LYsEWqL{%POqJhHSz1?8d$v%u$@k8Jy2${E?ga z6A$n>f8hoG#an#9=X}SIQv>~kXEerPBBo>pe#<;8%#y6Y>a5EqY|T#W$pIY3v7Ex$ zT*Q@J&u!ep!~B`&_$RON4j=Os|7DnIfu15UCgU?H(=ao0Fh7g23@fuH8?ZUsu`By< z5Jz$Xr*j^cat$|eC-?Ih&+vC%;Z5G>Grr{)(*ym4V^qdwLZ)DPW@T;`VhNUKHP&Hc zwqi&2V1Ewf7*6IaF60WX<5uqGA)e$}UgY1r%}0F64-7pc&{IUlU_2&aYGz_~=3`No zW+m2OeKuoTc42Q0#-@@urqt{2M*^rPURdf<|=OBcJAd7p5l34;&tBT6Taqu z3_Cl}Q)I^C*G$H=%)*>3z~U^+Dy+qZY{B;I#=acPQJlyboX=(ak(>Dw5AZmD;RXK1 zTYSLhe8-S;0{w(%G{#{frep?w%RDU1lB~e$tji{B%}(se0UXA$oWj{$#FbpnZQR4d z{F&$YC$I4iAM+LeWth2vo+2?O<1;DKFf(&7KZ~&pE3+mWusPeYEBkN|M{)wEa~_v+ z4L5No_wyLf@ONI}P2T4-zU3G50{w(zRK{jPreJzzWo{N?36^Iy)?s6|Vn_C1e-7ms zPUb8wIC^!FznlHw>{L(9f5Q!mpTs$(fGdFc%B*JC8I@zUF@nyC~38WX9sx zOvbd#!kjF?;w;N5ti^_G!S?LNz8uU^oX8oR&t?3PoB0zD@Hl_r1^&fbe8A^?$B>Hy z{e)*U#$h6+WCniAJS@zTtibB5%O-5iPVC769LBMn!r5HJm0Ztl+{45CndkT?ukj8a z^A-PPm?eRpA~7c8Gbz(BGjlLMi?IwVvnCs`Ioq)-`*09PassDw9+z?rH*qKT^BB+Y zcV6L5-sdyE{e)vw#%4mMV0va{ZWdw*mS;8AVPm#pNA_TU4&@k5<}5Dc3a;Z; z?&cw$>3Hdwj|_46!`W&zFqCub6<{>58-z~_9&kgEdygl9CyVIrnv z27b#tEX*9LNzI&uN^?C0xyo+`)Z3 z%G3OfmwAKt_>^xLVr`(GFByejF#(e^9lv2N7UXv<$EvK&Mr_Fr?9P51!qJ?>nOwl- zT+1!o#e+P-U-<{G@*h6r3%+Nlb%CBDFgoKhF;g)kvoSA=uoNrud)8x9wqa-X;tw3o zah%FIT+CJ6!0p`2BRs|Pyu|Cg%O`xz{}^_Cpr^=;#jlx+X_t6GdQ2i_#-#-Cm!H&{=y6Vi?{fI&-soaHw5|#&uEOpL`=yH{FZrGm?c?( z)mfKK*qWW#lLI)6V>yMhxri&dp4+&Ghxs$l@lRgk9X{qO{>v~M13g7zOvYzYreS90 zV15>38CGUZHehqMV^{X!Adch&PUk!>qHU;_#$Eb|W zgiOKo%*xy>#1bseYOKS?Y{ic3!Tub|F`Udd4u=( zly4YfOQ4@G8HHal0h2QwzhN#GMoXR;|%vIdL z?cB>FJjL_8#Ou7vCw$HS7 zIG@Y-BRBIW9^i5Q!VCP1xA=h1`HmsC2l@%mXpF-|Ovw!VmU&p1C0T*hS(i=Nnw{8_ z12~LhIfb*ih%33C+qj2^`7_V)PhR64KISX_%P>0vJw;+n#%EHdVP@uFeimaHR%T5$ zU~{%(SN7o`j^qSR=R7Xu8gAlF?&mR{;qSb{o4n6we9JF(2KoudsEo~oOu_Wb%G@l( z5-iVZti#4^#g6R3{v66NoXlBV$Q4}2t=!E+Jjt`X$iI1;kNA=w82YC`PZ1e|@tB0E znTgq%k40IUl~{xI*^F)3g}phDBRHPZIG0Pfnj5);`*@V6`5Q0u2Ji7H-!R0kKtEqH z3cq3kCTBW+!(1%L?^upiS(}a6k{#Hc{Wyf9If*m5fXlg-Teyn{d4j+44_@Uze8?Ak z&rrJqJw;%2#${rrVn$|TUKU{~R^<1r$EIw<&g{h>IGp1+m23V5`*JWxaUy4MK9})FZst!s zz~lUd7x))%@d2On9YgL7^b?-Z7>9|Nk{S3d^RO^WvI48KE}O75JFzDRa2UsO3TJZ> zS8_eKaSspkXP)Dqyv93x%vb!EVfF=jio}?V&!kMl%*?_3EXFdd%$jV#=4{8V?88AE z$qAg!d0fgh+{B&S&tp8p-+6^Md7sbtmS5}-^b?Lz8Jh{2g6Wx+xmk!MSf15bhmF~a z9od8ZIh12KnX|Z%E4YqZxtoW0l4p65fAcmU@g+Yn^npN65gCK=n1rdBiP@QtMOm7a zScCQ1jBVM4y*ZF0IG)oumrJ;s8@Yq~c$BC48!z()@9`<$FvP(?KVLEmzhVL=XF7hv zTr9}%SdLX$n~m6#9oU`yIE14)i8Hx?%ej_YxQhpQg1_<)UgbZ0$QOLiP=^9NMPPKs zWn!jcMrLDP7GWt?S z0iW|7LmmnA6Q0o+hl!Yy8Tc*purN!q0;{two3J%Iu_p&`7{_u7XLAu(ay_?k4-fNa zp5vdq#yfn>SNxY@js|*)#F&iFq)fxi%)$ID#xktTnry)4Y{#zb!$BO$37pP(T*@`v z#GTyFV?4v(d4)H5pU?P~UmOec6OK_Cn+chM>6w+eS%@WAp4C`~joFGF*@OK#lw&xV zv$&8exQ<)7n}>LkXL*r-^EMyxB|k9q@jy=z8H4edgsGW{*_n?;S(=qtgZ0^rZP|sr zIglebp3^v&OSqaFxr6(7l&AR{FY^ZP@hRUh#EC#ZUor~6Vge>-I)1}kEXeOzj#XKk zjo6YM*q!}2grhl$Gr54vxt3eFiwAjvzw!@WtS@{|*j(6F*jd<9__=VPaJX=saH?>&aFK9@aJ_Jw@LS;_;c?+v z;YHy!;jh9+!aszX=IbA)Fi4nQm{pilm`_+lSXx+7SWQ@0*iiU^u$Ay*VOL=vVS;e5 zaFlSOaE5T6aH(*$aFcMSaG&sq@RabJ@Urlx@SgCg@P$yf`1&b8=oMxXh6y8u1%<_h z<%BW9n!lRju%c7&Jiva zt`u$%ZWrzmekVL3{6Tn0cwKl`_*nR-&|>%XlS>#Z%plAr%q7e(EGjG`tR$>1j1#^q z{7~3h*g@D$_^Gg;aENfUaFTGQaK3PvaE)-YaF=ku@Tl;#@VxMf@Rsnt@R{(X(B|;< zQz~H^VTdrhFt;#DSVCA{SVdS%SYP;_u(`0Uu(PnI@N?ln;c($N;Z)&l;UeJ*;d}pU!XROKVOC*IVLo9IVQFDSVKrf0VME~u!dAkM zgxZZ{e51 zLBf&33Bu{ZxxyvFRl<$J9m2iB!@`roAB8^)ZwP-AJ`w&UvFMLnfT-a9FS=dwfxp1IxxNw|ss&KY& zk#L1@y>OfGTj3$$ap76vMd3B!ufj*dKZIJ4uYa7vAYpo8R$)$IK4B4IX<F^kU4?yw3Btj`QNoGB8NzwOrNY(1O~ReReZnKcQ^Iq?%fg$&d%~x}7eXBm z-gw^@AoL0|3B!bu!h*u$!g9hGVNGGYu(7b2u#K>ju!pd(aDZ@_aIA2OFj2Tr__c7I zaI0{)@SreB_`UFg@T%~R@S*UzQ1$rw$01BDOef4D%puGxEG#S~j22cE))6)kHW9WI zwikXP>@ECKI7m2BI6*jFI9IqtxJtNDxI?&CcvyH+_@nS=;SJ$$!Y9JNgjTPwpWH%^ zFrzS3h}&V`a{?i{*@*wQJ@-wio++jO->$z|E|wEUf1x`K;k)y!YaaA!urDZgw2I*g`I^xh3517ff64s94Abv{{Od+ zxKBUTjMx?D?B7TE<7u|D7+^8RrpBwhftH}>`q~jFugFV zFsCq|u!yj94iSzP{%`-D z_fMm2jgFe->}d_j`qhg(rnS3V#;f5dJ27BK%8e zmHEsq^awKwLxmB-0>Wa#vck&38p3+QM#84TkAxkC-G!eC`wNE(Uq6no$6p`MS*2Y$ zh53X-Y?b&l4^ct`?@$o|Mx6Th}qe zq&;JWQ-q1ag~G3e>x5f{yM+gZNy6`i7lc=ZcZ3gx&xNXt8;3BpFr6@qFo*D;_VYj0 z3zvGB>+*sU|4--9|Ea%g`G2+E=JnG&kIeb;zok7L$LxAzZIo_%(yJ(GXjfBFjP)zhjCBd(kxYlZv2I1YB;)WCBl^bm3;y1bcq0;ijq4Wl+cFOS zN`Y>DI(Bc~0_@(^$F`=_y>;*IN`Y3*yEj(~wCL7NDbTfbhXPD2(7a2Rraij1>(I1S zyKc=}bZFhQUDr?El-;`1+j3iWZP&eB%jO-Lc5U6JUFS|sTX*W-y6cq+Ed@Fvz3FRyeMPrw z-J(Yu=}9uPGgyx9kd+>_eYQhjyJ> zn?<0@l>#k0ckGB7`|EtvwRuMDswR7aY8Qw()Q7aOjga zj+p=iUW02PSS7r!7majNkK;cjJ^C>9>bymdW6;#&x+*1q9HXWl@4HgcV-J{m_1>b# zbI{a_e~aD_KRur3DV00HPmkwUN_w;X^cuWHZ@Hfy*O@8#+vKOm`_Pp1_W0?s&r{Ol zTFUGf&Sxp(JKpirYxNerW`26D-=f#SPw%6* z==Ju~w_y)k}z?cSotXL)A7@IE>ve~bO}K7NbdIzPP*Z_(T3r`Pc< zdWZe=c>PbQerNskI=@Bl7e77DaVh!ZGf1;vK6#7YpMHA0Pff|6Lwn_~+gtR~_~~_j zi(aUo9@j%=xgn5ICH{$|vGH%Q{WmijhD7RFB=dDW=5dQaa3K6Uy(s9sUTzT5%#HQq zmJh*+5RJgC)jM{>Kd+Ys5U9uUcn$OSR~8jdgEVt9{b9NFH-8=c{IO1^KaMefe{t~l zruy+*@vmQZKYx5qW%}bYMt^^NX8fl5jeE^sUq63E{rqit&0lx;%ZxO0GuwL#3I64d zfnHft2{iQ{LiRsyhoNWYQQee-;f(&wje5&do3U4Ud^f_sUk0Kbi%|fNTPXzkV|l#S z_xH!&T639$2l-}R!PoR=rB#$~k#BCMUS0_P<>vdquIz#m%VoRGa`_I1X@g{?c{)X@ zMnN_+kH3}p`?~{wC80xqw2SunEwizi0&0U(470 z{SJT8=xcK`{qdQdfBojeUk2#WUnPWp@h3h?WmFXYU7)%Bi$BrZ*37OXrSkcUd5gcP z;fj*obOxs6Z$u}%algd+#lFSgR`_f95B#lzzh=;*KOVo7+8dk$_jSlOx0LFart>R* zHK3f5zZUTKo9PTp$zON)<64LHtMwLt^>Qi-uczjglD`pM?8*=*(Vw}#=XsPoUgWrK zg+IQpV{WFHJYGaE%_le>km--(HKq1W!^F<{%-mA)R~CQ7ZJrNf-aqM<~89A7EbFEU*a`}-4r+#h+%=dT&mQ#u~)?W&TK&q!oTsl7S6Ih1F(Hn4sz z5K{6tI+LpOH=Thg`O7@ip)5u|{k1|!$zL#D{=*cVyrtxC5Bz0-7X6v)`#1Gp1pKjo z%`GK==i#q6l<2Q5LQ3`f3jSEGxuxXqonf!)$N4BFe>35)nCT2m$zKQfD~Wv8uLD9# z{!YLj*Ei;tlE3-z7Y!x)GuQWT>OWiNe|9{6@oR4v_)MvOnc;6T^35%!`uz!i=6LA} z<&^xbg}?g$;C!2pmpX1hkL~UL7Js%-Re54o4osK1SqHE?;8B^?`q7=6kor7EQh}X z$YlLEzoq1_d-y*)zTL;aI=%zn;%^}QZ7?ebrc}Ry@Rx*q)^89(O7$y^2S5L0K7D7x ztNCikTl~erAK%9|H&cAQ|2n{5Tj{@HZ}Ha$e_WN_bOxs6@6<#`^7>`OTl`Ijzfu3d z-=FZe33}{5>ZjCyxpS$?Y~-6;O7&Ye>DBQV1Lc(b9fUvrU4Xfn;_Ll4a5CmUWU_wa z5K{72Fj7^jn$EzK{N=+-Y>kjle-jW=^4Amoc>K*RC4U3q&zzqpLD}@jzi}{a=;HVGV&FUbq^UQlg71D1 zNycXCO%XjaJwr6sb!G%)x!fi|&%b_6P`_qKXZ>a%nDtu#JzQ3jT?~Og-j|yj%jHID zhUDArfnI`3F~-(hB+*|G0?UNI3qh? z^$le{JL1}eGM_E7lPUALAlC$x`Am=3bIN=k$7?a=UI@I7QtpGmYar#%5P1Ef%)hVY zwTJST2nizh1N)0S030atS0LwF>JLWXc|v(80_Ql&!x1sR5IiLEci>@>kAO!-J_aUlL>@=5TN$fvEZ` ztNKl?ftA#x?KkcXw&ecf?8O`Ey~>#zXyLuBc^@U|c^@2~@T_g|`w zc>5?N@3rHZVo%*v<1Ar|tE%Axk@nMd+9>z(^wY7A?gyJ@c;I?3s-F^;Dpm`3gx0;M z2B?Jso?9NQu2kukqFL_0ETY`Y-Y;p*dzr1bb^E>JgP&DX)MEB+?>`c`dLRmnRyNy#%iDY$o3?wQB6ywi`f4pWYgd!{C|Y*+Wz z5WAi|u~POf){}GF1(lv$Df+?lgh91~vPZ@$VK1M)c-gXDVx@$8cM~pzEV!RWO?rqh%y2_QdkvmsID`QLzLvO=v zr>$;G?U=$z)K%_XOt=CD{%|g2ZKcSaMPjEyHxtUt7DKPpt{sy(DZKsGN@-^I`1ZH9P4_LS_uaSa zJX+BY1|(e1poUFmZ$8%_2i?*GlzRgbLT~8`d)O6oVvQPc=DtmL+*j3@ooW$#&ft45 z)z~CU?$kf{Sw0E6WeaGP6ne|%XV)ofs?D&-<+!h@w8{&s06#06QC{ui&0~G#bjcb> z{ZPk!i=pp@esEHFjGAlwvubg+=gPgbw-hyGXU&;_lAbfG%?1`_hb1bmO8(zohVBB|-;q8^Y6&__-c?IL6!lR5U zRcO5kOK%)KwY+{`)xUfcxU$UksI?{6duwsbsTis1aYty$?UuZMBq@1+Kkklg)=bUm z${tXCUNh_c97@hEEXP%I&$9wieY2+y4~rf6tZ?YSXGNlH)wbU#8I9bX_Rua>Sw6Ry zFMkdq!&bXiGcN#n%+I~r+t&TJ6lLe?$GyI6c=q19lF4<^P?vk#x>U8~eU92ZOH%Vz zI_?ZFWb~moW_s`NE|zd5GA`*w?magx>cD43qYkX8{KL^KF4qRfz-J|thie8tD;}Dr zP@0IBp>-ei?NRqpLW_aViYa?;>Yoh6Y_R91J^saNCCtGyJLlieY~47sJxQmH^IEar z=Xo8cM&Z29dHcGSclGgr@XgoRo~YPjvEdn%P#pI-9621l_6loVC(8zhr_jSSo`_x7 z)m*F{YN-^7^@Ke(juZQ3_2a%@Rz{l(yrsdOt`H3RkjUY8KWU zZRvtG6+?TnKV6;fRci`kp1MD|QpH=k%6ZC@bYx9q%u+Vx{_zC1rnuRfJ!s8gqc!DY z%f;r3Q9=j3)|#MjTP*93GSI?yijJ6GabXynr^r14vX&8 zEV|X=)NsXCRZX*6G?$`5aB4Y2e5*(7)fbagC1B@mt8$^9McHMQJ`rfDZNx)ev3%Sd zOVPY#kgBPU_=T#XX?l%;7Qh~6vniGVqaoLRpZQjOB0lP33CEibT3s&3G^_n|2qF%> z)La{2Kaf*btX3U=ki@*_cy-270l&RlGDJaJAA_AGcB)meD;r}YZC zKJ92zoJ-@7jHZukipVmWSbEstawVS|Svu{pD6Vwu(=_?;<8uaALH-2oO)v5 z>r>d4r<}(7Z~){AlrwlcHHU39<;>pqKSa$cQVvzpor%>Ihw7?vMMG)Tiy@~bMa5gb z7rfS@oRI!b0NPcDbyn2$Utwl&)nmkp2Q2A8zI9_XB%6}1KbDSTRM!WEEbw7;*@qMr z?`S7#+_XPLtG6%C0ax=9NOyQy0apt)dCp@xHcM!X`A*^T$?C5yyeot zesd+r0p256#=Evq4ous$#Yc*3D{Bzs-P#E0+o+$xdnp9^-?GJ-y$ydtJ@z_n=tS>( zcBJnw0y&5G5f99vcOgf5Bg;YOyH6nJOWU+Z3&nMmKBFL`X2)2wl8{lgB>FGwEfxmb zll+EKw09M5A6%yfL#~{*X$RPTPum#p%gyllBlT-}6V{>|&+%P|IB$;DNWZWJ?S995 zWH0J-c^T3hdENMgnClmw22H#jKZAUgwyl&51l2PdJ2oogQyn{|qc{2GE}dDKvg6}! zkyckTjoBg7@d-Cq3(e39X)qg+HrfT$(i*ZKQ-eetAydX-y!mnhLat+8a#Z+=xn-Tao3orp7))DYS%R z$WN0==D{gN9a?d0z0!g82S_mHL5>{b{Ly^z$z~i=YB7%2Y-(3DGs`9Xlpf#;SI;6p z#5iAb8nO~HbUS(`m&#(SA!SRzBtqqpcAyxx+i9iGEFa{yDS@sEYDPO8ROaZ9He@B_ z2_6QyDjE@6$o)a++DdA5cneU1h}ZHAMRb^=uR} zi__syji*A}kZhS@Gt7u|3CYpj<0?F+<1wzY@P8HTKcSaYGhGqq93=&b~C zf}v>m$f5+XQ#sBhp6#CN3*$5l=yLHOOvPIjskzZ42XYFLp}!{ufq74KGs!tE2FiBXAX0ELUpSlz>c?kZl@d z9Z!Qy*=P`!UxUHeU$Hn+EDbtAHD|Y1s;R;bQMEx4lpp>%j;b0y^j$2o!spN6`f6-` z41dKk@0>+RRdkl0S&83h_N*1n=HmN)SXpJlXBJbHJB@V97dA~v70b_GIjwH&!*XJm z(U@gfT*k88ZAI;``xKM1VrdNo zW}-)FZeZ3mG=BO5qa=LNTsF7e81~PD*65w^-(RXQj_qbs^zc$WY>F*>*%~a*!cX9} zSVwp{be=Q3_j!yX#WE%gjV<8n-h>Z&Hx*J30KjgyIa}8##z#^2>crSWjTa|EQ1Y&aeh`iLy(*)KR7#GyxKgSae?^Lp6A>*slLH4PilzDz zf~F(2V1QUS6+Ii|9Fu=h6}2#veOfuU;JT`|&H;`7Rf}TDE2zmwNy+(QA*D8!)=OMCD~z7q^UFFO^2*| zTRN<&RYRIl_h(3pWTN4ae`^hgtou2`;R`(VR13Z4&>4s)gIn96y9;R?gH2$SeAsrv0$z@82gSLx>qfY+?+2y{KF_LZ}R(2-SB%C`uh8 ze>`K%FUFyRiCMXa6t#<5l*1;H5u@8NI~ZH8>d<8a`1des9_=DaY=|@sai!P+ucE|5 zh-PkHEt1TA3W)_M`~;yjg&Poz(fAO;MWkTM8QB>&-sB#6gfw*qKX2sg5wFoBr7@rR zdcS7VcHFDp z`5noB@10Qp-eJV(9eym7TP}W0heJA=Y9U%$)IOp+(%M2Hl8Lz!5L<;+%{#HfJtQCY+QTC~?KYK#xLVsDbNYdy}2M(3dpG7FJKigAaf8xhdZaG)r22!;o z_b2_SGx+(Y;_X^7gIB9xpzAwNEE?zS5=hIR8V?qbPt}%)m*Dwv-#8WOaqMFV1M$3$ z@l?v01)rW(AFAeT1S^zNELh5UsIr{V^lO}t4^@`KuSNRK$A|m}k613E9M=2KHc4mzzvljb@B~tCK!kA zBwMHCItFjqAvB}nU6o&HD#kOMX+xOyp=!(xZp_sM3nNkH2CaJzObmZNw@bt!-yR}! z2V~A2J{8pD^Yy6m7FFd3no5P>bJB>gzNL&S$`+ODno_)aBAruLDPBDxvzwICw=r|6 z%T%>wW_(y$ovf-HaixqwKiQ{JX6x_hgD;GT$`iiS`;Sq7UqadL9eOHif*OPZeWyx4 zvC54BsJ1uySY>CFDvYxR2}TtR5A6-jQr9{g>AvcCaoesoL3Og>`{ZgvReeBHqxKCA zQ}lcWs8m+f3>cceoXURfs_ZMiWW6b9WI(d(NhE`hRj-_z%+Gt@qLoVA6Pb~~dDHDMKplU`$<@8m`u<<37 zGHiTu;V)*xm(}pAl>4$1j>Ap$B@D{L+3o(QQ~B;4yC~`%RC< z9%g6Rr)n--VJaGRJ!@DP4f>H;O1WJXpf*Guovbp1D&@QPZlS2dcXGB?Q#g8hX)X!O$iiUqfsbV!Oj(z${^zXS)nRbS4W?VoF+jfTS zCJzo?JGQUl_DOJKC{?`kC2S2*i9R$CCBDm!Qh71*b2ce#wADV}9Xx6rsqoJqLu62VMoE+35;;Rml@OVj4&!{m_@%dtc?=pi67!)~5n?OiFP=8bhKrxcCabbvqj&Wi5 zi~=tVYbfx-@RQG+eal~^r_g+lx;(iYCK%=L+k_~G=khKHn-J9R)N$-Yer~N1vLjw1 zliwCB#`F3pG?J)QLyahe_y%h5FITTZh-6Oga)_nDtQuP#g3)g$kbHwiCDo|(W?PwH z)S)nRp~w^LJOl@ZpIT0J8V3@CG8Wm9Ow64LvCfE9<#?lNNvfV743`o4kkp@=Ov_yY zv1NwaYCQQKLAMcgFGLhZ^a}MD&s`3&05mUmH3(l(s1M-~g%2T&M3r*4gRmJwRh}V% zub!&$gaMYAzG2|(trX7DL&+YK znXB-g$aUd*Z(eCQQsUKUMXAvb9%jSALKx@nkH|EHnyd-m@Zbeaon_SG8zdRExJZGu zh(VQ%T6_$lU^S_QS%Y|ue}17J*Z`fKAbZbhQJ!MRc_e!dANK z2~|&sUt`oLHiG_Y(cK8-;|x>O9aS=lyQ6<|_tbP3fB{)>%Ioel$IHL@TLA^5rAMZi z{<6U^g4HtwRXjp zp(hSTF<$3>gpNalaW*w#CE~+)l;%Nr03nh&xwj%#2uIy@^$8|Q~0X9QG<2oPxq$vymHR{}KU{M%Sht(;Hr`HSt18{4t<_z?@ErB6{ z4blgzLFuPfLJw#;6t}xPQWeFLPO$`*c9(H;KCwzVR*-rwg(Ya8ZK$!N~=Ub!Ob`yTELJ7siYiuz=@t-b-P)pib(2Iel-YE)#_fR28W^G zg7Dz+`ssRK>6S!Z8n0>zeB}w>WVSebZDDRYBUd-eN$+QiyPXVa)rgF~vhcqySgTCQ z`Z{Y6BKoK?z2QU8Mn^FU^DN0l8_HRwmPRC-G|z~HzOuu^Di%uT*{y1MH#^L%U3eti zXGS?WUiGYo5ty@pS|OvaU{*I+_1K~_Os-@&$(2Pdbw2vMN&^JpneHZlE(M6Ph z9!?FTRJbVoYLs9N;D!TMqA0U*viVmQH#I1Z$4ZJT;qyMRQby{QEP&(6%!rJk#uzE} zs&ZJo7m%7V7jN3UQm*dxsh34X#5q=hlJ7R1m6u#&xK?=8-B{(kQZ-#wl#&hQN>r}y zEt)Yi&Ya5pq*jbH!^o~uK>95C2*vUM;6mWncB4Jj{1b2)Gy9f9qlT~JDAx4rXtcDJ zf0X&PlY5P^It3J-i=6_EzJtE8<-%T5N+(Lx04ID+iQ+f;6|@9$kwjdUjX#>!l;Vk) zI~7c2NH3X4W;NteiDY&|E}cmFe3wb~Z9`g7BfnH)aj=@mfUz;(S6);i$ui$mHpY@s zHksLwOC^SaW?SgHw2>Z_Nczeto?H(kiEh!qrTw3@+bo;D%?+zET%F-o87mFeE+rJlc+dWo;R)Jt{&Ar=8YecZDo($ZHO#;Cp~IS)eeMho<=sSeT9g_x`b zA)TT4hK})hh@*!$_QWfHai%{GS1H)c<(X(^vt!wMrjb}F8D|JbP*y`O?spcJG|s|E zBfXT-?oo#P>TG9GtRDTbPgo{p_6QG!f7vw<=RsgvGmv(w5>GQ80VZz&-)2Vh1O!!S zK=_yZpSbXq-0+{5TqKbtcQQ(@Pjp4FDBaXV3Saor315v$7=7WZQ4zmJ6-ho0Q2n=u zJr+7FR?Y{C%MpAh0i_KHI6H`>Jd^P$A*7_^%7a& zCZ8T*7FaKl(ivX{A!XBnf7$gCS@tcT9>Hq-eKJ9U5^bcq&Rdep!#Rf-jjtu=K}g=B zA>n-TtDdR*PtP)+hq%{zl#TRt^(%jINHez5*heFv2s77zqz5329l$j5KRL@-jdh0Y ztFtT`@i!l%B;>rg^VwVMWB+wc~CW_ zaPny8Nq~n!Mp=VEmVvIJ`)!g87@NN;O%@{HY$j;M+Wr$WUkPzI5UqP`4DJnFC@Jjodlk~g2rdj2l! z8S5pgzczAxy7B(H@qW7T$xb2gmkJb2;4VvQM2Xc!hFNQ0Rn3Yc!>qM01Io#p&t*M- zm-Q0q($~npUCMmA@&3B;e!73Fs;{MSL>dHN!IbhLphiR?1k7Ys5#vCg9wmB%9?>c} z148na3ljR0pd-B1N|;XSB~qfLUw^Y*>=h0~GRTmN@Zqbm@Z$0{1LOimddcKdfbqfz zICZc$UTGqY&9Cd(%f1FsN{4_35PSgouU+r6tVfuZ^^BD*i$kr6Ita9^mq@9JFN2to zoIz=eFN4^H;Ip7~2m!8$rJ9*RX*U8gi1W!AlrH%)2(#Mtk~;!!WUR&eS3BOQcBD~O zJYiNl-mlv6{?(38WVKiMEQnnQtaf}NrC$)RdLYbd$0t%+Vbm_3FsmJ(_^R6R{?(3u zL$&MsSG&GZ?aW45^$D}u^^IzC0AW-StSoC+->CL1Uk0%&IfK$E1XvJe-_=j9L?BZC zH_Q6(Jsj+b0!CN;H;!>0qfi7+5+qN2-e&^F#_`Y)f%#+;LoQ+r&*d1GOgBfQza90u zid}bb1_I8&eK0vBW=JtMe^r`HLcml)(26De6Ej~44c@L=#8~C_H%h2a3_&oiqZB6i z(g|P5CHzW$)h9d^<|43We%DXDq$zJWZK(dn>A+w8 z)v12;Kl4uY|G@hVsI$WOq<~qx-`53BAeS2K1;E%C--2g7Q2Paj_>(@08ZHQ&A^eQ* zCg(N~Q^S{^?Tr*|Sx9E1kar2ZF^%ggf5%8y_j#V8Q>W zW%PxXnJz6#ub|{(VAft!qK%sK7Z+7&NYp~G;T_mMO6Ek$`~?MB1pZ_{Ig7Hdy$y+_ zep!@#?QKZ#c=7m~ZD*NgyD6I$qz>E5A1_cK!T+vpQF?xvNTIYZ9nCj3HX-z1Tu1r2 zel_H%+bjiLcCY{NQ62y}Iviu97HU5N? z>rLAyI6=%RqxSx4zUn0p{ARG?uYagK(Zdky6Fm(vxgAtwlK)XP({K)&MGS-rU(ts@ zVzME|6TWes?2n2}vMA1%$w$Ua^mF*?JWn12W|DtZ%rrks{=~=et|^6*3oz4A>WhC# z^t0WKnz?-QAQ5PY^@tz@3w{Akq=3JkM@#Dw(FiuAJ6>i=q#WbRB4QC>q9szO<4Y&H zAixB_c4kVX+})Q&^hAJ(C6U4~Upg@h0VWojT!PHgACvn%;E^xOr~u_>zAS4%uYa=X8Wb0 zW*_*?BW%?f1phJgBjSE7eS)YsNP|)e7zH+{0eonP4L=2>8vjw*EQij~$*&yOf91G7 z9m}zK!vSFxedY~9q3Eku!-f!j^=iO6jX}Ut?p3`SvQK=6Hu+?2=4zjuNiqHk2D?(4dbk3p$5TXz`!uaG@*G_3kG-BzX@9&$R;>FE+i3s(NdrKwGB9;>u(QmUbaNPEvKBa6-+j=b806 z3M!Yyr)#yGRI}>2gJxS{cS_Bs=UE$vx0ZaFmdR?Uo;ULcs9_KqQo-bhJFn;UQBcZF)IJf?NVygo_K z)}s(!)e5ug0a5S4CI}rGoWpAI5d#2Qx;btpRL7RSl2Q=fPuA$pEqf&kzxris^{K-Kc2PZ_fNK3a^Qvi zh}2K;(pF?K@mAKQySukXL8+o})KJ^hPCZQvtdlJnitDz={n*(V9exE_64k#s6+h)l z_=+ws-ER3!Evb7y`y3w$u-sEiS#6KhfN(_g%EykN{JN$6Tv{z&w;mdI35AB2`CEG0 zvbv?jMjT83R9`_0)N;N=eUUnnO4#E&bPNXqjhWR z@%)4VzNiqWYXNi6$JSK3b-+f9xiqPL_Q9t5O0}Xnp2UH*l26bbF*rqa=b)lYW1cG?UD>e&33#E?iv96Clo#REHC?&$GgHo9q0ZyxO!+>oA#Y~- z{Q$gDr+hjIulXtgSd;1s-+R-QW#3Q^!%Tp5{3W!mJaJI7IR1=ISL#)voG=&_QAX4- z${N9xF27N~w~Hyaj#6&?8TH@8RZLe7JZAb1ybh!*O={EU;nnm|xI1mDY^7#j{AIkZ zeASTYRjO0J$O~$AYRido)Rh6hunuLmQQozQ`c#Tt6`e0qJD;x24 zlCES7rq#`n%&p~N%KK|*rH!Nf+abzN;wjHtL7jq?nbI^B<-a26ZQWN)so0GvHoUS9 zO}rGUEA5UlrA;(*r{G0$UFnMJm#&P(m)LcsRB7t0zsmjH_1xzz$6VFU+=dzH=lCjm z$Qa6ft{cp~pNl2NWMj%67paYB+mGp~^HFmrZuoWO zRD0$|8Y45ZF+Kl=7ms!2*HPTp$1JBS%|543=MJNkFv`X80xyp5N~UbXe^S+zKR=_l zDJN(Z{vPGCiQIn>Mg670^jxYrbAK$%lsQ;S;)w63|ASvKz28jgHyudN1u**|O=rrD zs?^!tlj&*t(rV;c?z{G5Zu?W*&ne2>)_4_NSMm&FN+x6cY&%BH*n3RxbAUdZ7o)tp z5A_EbxvjRd^as~zbs0}5P`WX?=Ht~~UFlzjx!XUY?Lqu6Ta4tQ)R~OWWa!H9%alD+ zsCgDEWL+7viE=Gt4tXCRXwa3PD$~QdY1Ci#9@{bB@5B=+ye0^4zqnvm-nA7pKuSUvK(vf)8ov%6{A(>PnAX+$TO;yE=F(JjXpntm!Qyg zV@^&?OFvP@-0~wn&fK!YXxjvT;)Ww~ zi{&m&%Y8SzWv(mNN-#ZNDD`i)WJREJpQ~DWnHJ{GQ6FMc@Otx z7^{Y7%Wle}3)8=)3v*BPq5i66O#eNe=`ZkxJLZk&%sqaKrF_50D9e~pGgqgaqcyEg zeodVnFIk5^#*uomlpa{XPy}|5y8jAh*| zNO|QRYK}j|+$S@bvNw!!YU8*p*~FT-jQMa*Gs+!C({`G1E|kea+g_cSa^?whAFpM~ zVcc!&O7}llpAR}Pcfw~ziIPu&NnZMPWb!i?%nKahqVu7AXRyWd!=<~>--eq%gePfefYhfwoUV|w@? zt5ITRmUyZlZ{I5(WKEtjyTybnl-67mRUPuql0j$F@+^1o- zyvG!^5K}_GXUdWDtixVoUJ6QN`jdB=z7VTnUAcXOp8FUx{5N=FigiL$?uQ%mz$J`K zV|{50eFp8|DqJ0AQ*CHZ&aS~IDXJPC)#=eC(;z)=-zmt=PF6y0>uH=Xx>VSRlmxY3 zP9%*{%l5`!^mFuEPv=Ft?MhDU?O#uTAIH&k(09)KM#>9&{4G)=&wU2%-_?3}Y@xqY z$L>c@eWm@v3*$)bNm^)3)T;B+J6&t_3$RQJ=R#t&cK#iB-l^p(4II+$9Z`MdrVg?p zAycIts7_eaWvCWBUmLE=g|9~G^|PHtddH<1pff^k7muWc>Q;Q+N#CX(ZG+fRb$Aqx z%p>(0redQZu81_q4%(F!2Px0}G04nY9~UrNK7KjJmcL?8e21d|KVW7n*cPvh+M@P8 zL!pJnMP+SmJ5^TQ*WA=q;3(~?z1U}*aUCUve0L5d zgjL|oob&y;NRL>Gzvb8Szdr~SEVLjUN-Pq!73rm(?M8aJ8FINPJ98UvNRY9nSxyVW^x| zuZ6(%8MWFRXkJqjIZItve{G4p8|nc(@z9^BU8=$0sd{%2G@q$CIm!lUS$KmtSnKj7 zVnejdT*pk(s&k5(taW^hf~IIiazbUH){)niMOyn4sQhB>Bv-K;wECQ?H)=H-!fulm z$d1~loehHdel7Yoa7???0(RePb@~H8X>%^Y?t)fhFY+#HHv(XAP3!nOVz)I1p5y9I zw5%UOCBbr=_dZ`)e&Ef|2+On~C}E`KVsBuIrE4n~Otr*MM&5i&_DiVz0!uh=L04Ob zmV(L}%O||C`o=PV&D?FtgPS2^AVsuVf$YepJa_YaQ33m*YLyVE4pTefk)J+Xt;lQc z2z3A#C?nMu&5$@s&C1)i(dvZ#NF1YX#scI|u+^%lz z3%ea^I$q>=suzmDe3!Z;8*2EC`gLJw?p6 zVYx40pMA+hsD@wYjeXGx6Xd+kdkjD1>ILR~jMRe7x4|Z=HucJWimR33quhX*NFA<5 zad9_79m~0Yq?(SW&?t3iabUDsXD%>C?bHcv8LO_}FQ$27XQNd$Y9_%!%?4*-U+dNr zw59GuF7`fn+7-DUF4+OOX=dJ-Ojg72u*E1fN7`#}`Kz|~C~#NXT?*M>S^xSISZYl* z8zw9CgnY=`uD=+A#0QRa2ax!?V}C~^ZgZ9V4vF8o>Z0X_zl&-Y9VL&@YK5cFQQD6` zquHai?|I)jP8+xu+T*oOPvK&Mwvrdpz1q2_z&XX$nVvD22DrGYb+gDYV7(lQXM4?V%UoUEcyKQ>=e=LE=)tv7h3o?#7b1eICV-FVMmPqco> z>*gG5>jacH*P0_csx;qvdjT{TSZ8u2u*lkT1M(JI&y)g|T6giPwaj{V34E@wuHssD zrS;Ms#MW2~%tzi@>jk=AZ*?|?mkrkbm7uc8YUd?*vvvO{#I{;{S3%x3E55sgsm5BL zcNIIWE$hO|H`ampHi^F5x+XVD-(#(J9N24(ehGsE*5ij^aL~GqL*qMZXRf^tTTf~* zIBMOVgkq0b2iJwO6V?jPV0Y45(2Ce;>%5J?8SA8qFgRF&_6|zFW}U~&$qj4wg-E<QMpOf?ooL?vF z7e@lqbpDm9K3yM@8knhPvBO}NUXqKx*?JJ47R}Kg-2mq6O&`Ja0=?ug#1`qlavix? z|E(i5m+J1pC}Ejij)U)O-E|k5EA%3*k+@3#j!U=IdbZOj;eehk17ZjDdwF1fSg(ce z!sK+xdb$B{eO7PN9nOBxEgZY| z^?qD;JkXPPfqSF}@}l`zU&6;pPxXzT!r+LY#ZZ&>!G%ije%jd3SYqI2-}c0a5mCbXdzU-v8kPrx7&7j zA7XoLZL%P5pY0KEat_#z^+w)7TZ5t~;XB)}1>yRzEwBl^9JT$%r$EPS!T9Kge%y9) z77|a`8gY4b%2qZGIBi>f5-Q)@4t;{yS=;PdQ2Ei;aWV|f*=E&5-cPnaxx~0&+g2Be zKidwJL?tfU9>pVe)i#^Yd#>5Wltk>Nt?XsQZrQ$#gqL4!+un!fU0bCFFu!N3HU~%L zzO9Z1&4>7AAMnU_?=$2j+P~u+*=+mdZ(uOj-kpoWdG-!`q_DvL(Mq&$p}qPU#1`9k z@WQaf-X51%eVP4UVVEzsJ6&P zppEv;V^Nn)_AR#%+hQ-xysh>^vth8^p4c5;cG$1(h21WDmu^t`#$Nvhe12=cdIg$$ z?4cK+xzGMRXW{+!ZyO=;pnW&j<%jI|?jm;Be$R^75qtD>csXW2nhUWcd%g#Vov^QL z4!e`~@4QGnW8cE^zPC5#!THgC+ya$z_I>M7-UWM=`oKl|&Qi#`Z2x>2Vpr_@E5iJm zeP&G*blu*Ux6HTfmQ{$|wvXb&(YyBdwnFnadn?|X+_!)J6tM^P)40XfAKBOQ((~B9 zoAcRI`=MP(d}fcs=U(;a_KXS8{KLK+A6(S`vZt+z3cj!hG(+hLj`_UM_H#t$Mcx2M z_C&-6IyR(+!63((zR(=(n8{m(wT{Z|5L@Rc%ZK9|9Yc6E+~jaIf#z060`H2qIpR7& zWrt%VuQEFwZFt^($JK0z?R6}nvjdKqe7b(n@mEDu_d7>?c{n@l z=oE{*V~$#dkeK8cy$l7NbbP}z=al0jN5L6<_ZNxZJDzTa<`0gll~CS~j#T|o&-0D} zTafpYBRy+<(eZ%&a>;R`3^Xr0s-6d~INs%%bJeky%iC*?e3?**n~uctDB+f4e+4-E z)e-*?iFX~BcwxBbm{$$B?^wJQDi0kUc+Gj_$iEt~Cyq7ak@wUwa|C?;?kL1xPo6sp z*8u)>{K1ptFUKve9$z}D^HQGRoVEk80nQrjU@*|Rs5HtO>^#Di>=0++QBWD~OrXyZ z&ZRGaQO+I9p*h;QK0WfrI;Zyl#yRie%hUP<=Pj;SCOY#ZB5|^_0v?3vQ=C~pL&K&y zfA0&s>CVi&Ow4lTu8+J#=Wiq6bB^<2O_)(J=FAa@yd>vm*^zg`neGR~PC5(VTMzmfXSy`- z^1U;ER>XdEMsV45&e{KSl=qYK=@gh>aE{_5*GtX^ynp@Kx!wW%;_Sv}g;$-$_rT|M zXJAde>$n!h_&e+@6so%6cF{7>ifGD!T(xd8W3`b(#yDq;z)yEoC48Lsg0 zP?_l}Qvk6<*OBVTo9#;ULUXRGK7Rhy=ee@;P%Ut^;)$@(HF^rlTkP5ug2W}R9G}5_ znQIRZ>T*{<(k$TUUs|Y@mGd#T|GHk_qsggpt8@EB@0vzx_;sH z{gCVBcfes+7Cs+4;`;Dw*d252b3!x8<>C75gv&Jmv6HTy9Z}F}*J+NPGp;WlqwQy1 z;a?;6gUiaNEazOa`Hc0vt3*}cg6nWQIJ@ZTz6hmXc1_{U#}(H*_!7N-)pcbURIa(I zb3t&!wRJI6Zn~=TM~>UB5xghA?k-gW z7~u}VcWw1i?nxDqH`?8Zy)@Q+gwJEfxq~@*OmMH|v)+mB`Mf`x?C#4mXNtSVHMD4& z`|C7_O?RgagTYMquq!Z_<$jM3t7f~K?gi$!Z?b~(+$(vln(yXs)%rqrF8ry5zQ{er zj=ZJrwR7NQnS0qin1Aga#Y4Qp{myA%wL9x-V2!(FL)fizAF2nt_3m!$$c^scVTf&V zM{k9fE$&HwzIzFkmXLn}Y(Pd2$U7C|4eKLjoFw07C=T=7+(sfW`TcH$31w-cyeV*o7xj z`pAHcA32rgO= zxEZWmg!ZlAgh{~d;9VcV)1BaK*@0h!U*ai(em8i3CFK1U+@vNdaxeH;d*FWXdkaz6 zgWw(0q4_Yl`XD%a6x<8{Sw?>xyo?4_zZzzaa5t@NoWCklrg;jsgu!%AYTla7@C@RTd8X&d1gOmNRN)VQiJqo0NSy7- zv<d&CQ-E9HU!2vz8%mtEVeFWSeKy52(_1&$R2v+u`x@I=9o4zBOXI zJf8i?`^IyHi}~H2bAQ6ix1MX9HTQTTc<;E^Q#t_#`#imJBevgjVg^(WcveTi{Gey= zX5=06JmYoeJ5LuH9QLf`&B_tak?Bx5>e-eDD#tuCIh7`P=J00oxF?8*|Agm#UeZo_ zlx0vkE4d>?$v2AJyiQ(>9-lW8ZNdf5Y#Tp$X*91Jb~A7uQ8-_B_H~ z?lqZD+&|CB8UKrNT;vQ?C-ENhD|N|S#D=To=EHD=dYHF3lhjW+t|qGweuDlKbpY=* zrm9JJ#HG(rqj|BLsSde`*erF}e7H|k)8k9g`dsz>_n|pYT|ENk^VQkopt(Su+67pw z_I-%FCF)-UkjO8s~(%vY;F6@|e%wR#b_Ua#ii zGrSFIiCakAs0Ps47B$rox;RSTt)8XBSXJghe5NpVE|n(L>dY7l?gJEq>^#o&Z`i)-qW zYOc!A{Qo$*?zkqeE?lx$2@nF=g9OMWm#uBJt*hHwt*x!CZLOW^r>!8!Ql@}7 zKm}15%0`AFs0c1VK><-f5l|U2l_h-7>0i(9?EAjC@4e@od(k?ZW-;up+Z51}+F+Al zivc#;6wu7QX){DU!6uv6N)fi%=4Z;VTWp@TLbKIoyAjMgHoE;VxM$Nx2P5q^In7Y% zu#tNJI&Dt;051=1KAa2aviZ6d&~0t{l*3(@uR)+%TgGcfjT}t=)q*RjrUc zw(0&E@WkfD`v^2*Gi1eBMs2pmBl?7mnFn}kv%UpSG-*@(Gt8%K#%L~XWr&VJ=En$0 z1?A6(re+KNAA;|o8N@Iy0W+A9MW>=6j2E%~p{LzL`C%9%p0cTMMlp4YA{cl707Now zc0m@+IDHnf7{-D$7{oHtb3pB4%%X?h!?^!EH1{(6sMy=b2;L1Uo-vJnl>?0Q>41Zb zte>En$mp+tT@r=_ILt8o0?i|gyuE;<4ENJ;ox=Fo0bbG>^ZKE3jG>f(d7R;N6kakI zeK8nyHlsEN204r?A<)ca4EDfv9^(xvTnZQs)K)8GY@x%kB8EG46N(vMc*9u*JO&Vx!BgTn*!9ODZ)T$eN2spWs3!Q2Ct3dSI{H!m`rDJ8$eD0PRyWyUvh z$gVK*!@;a!e1;2}=mjSW5&artjT;PV8FH#-8W|^OF}lfE*#x^L#!J){xW)K{`T#AA zL^14I8F@>fa)*($8_c_m@biFn#!fnT?_fC4me|QKZ-KM>jLd(a*~Mtfgshu!m$J7W zhWH$)UWW92X!bK!QIlqX!T%eYgN&8YfFVZjIxwFwoIio<5k~PQP@{}v)Ttk12;PMG zQ${46dQ39TZ3c6SvAqzQKFrlm!SrKVX#w+Rmc4*@+n7;VfB@!LF_^(jBlXilm|JLl z-p<@d$ILsJmA@lwIP;Z2Km@b67d|7ILzEmuF>{9jG0ap0AeLG5GR$`|x7`92$MpCO zuJHvqBJE-_N!Za?1v!l$R z7eS>kJE#y$W$ya}D#w`KlxQDkCg7j1$o@7RDgWYLn#{h5O(-koAurWI-%dFGd#3I#&-WgcXhdhld@?lGCmh z#>#mF&2ZKWd0v=SdC;t8 z-J!#>8rCimB3xxjq5#)eHiKYZXZ=cP%njBGZv?7m9jb$u2G%?(c$!$@G_!88YJLXX zW|e0`rJ0rX56thd!kM7%vNCA6ZLH7lU=a6MHMIM7vX&%5cAqum4QCHns~ceckoEaH zQ0Za)xein>%hd?#5lia?=wtPb0|r@4Gn@^vHs3~sVOA|0nvYqFF2c(wD}B z3ddQ4R0L13&io7J6wCJ~X!@|HQ~u)1&ix9qt?X@-u5M#5q7GUB`+d5{6vz%62Q!GB zL2bkB>_BP|?_k%^6NR#mr-BM&b7o^Kk!;2$XhyNqHX`0m_FWyQXm-{Y(A>q=3BinG zuh;_SZuTmA<$KsH>I}!TS(G6quzl!e>3(+ZJir0=r9S{k>>uaB{1E#YT3eIZEtC)( zW^bf+e+v8eLda6tU(ni`#$NaXRMOc$4M3K`K79|eOtu@Xty%1-B2d|Ex1$J@hg}ZL zeD=**V4h&pe}%^`V9)*wR55!tRg@>$B{QLUiv7m_;H-pwgA%$jb||e&XV~Tn1Uicw zpinu-wqJuGRj`xk`6}5TQ2ug(-S{;OF0xnB7k-5;Sq`d-ZK3?7nmtDKNez35a)w%V zCEYftV;8o-^>y}}EWizRR05cd?AjjzH`!5L@Y2NYNP+AY+wQ+aiTy0K^jq0kofyj< z_UZ^kxXUh}bJ_ds`Ls$sz;Od2JY@ed3cD`0!2p9^_JkVLBlb-NRQlK{RQdO_QyxJ! z#CE2FZJ4ci4%B0|k2f@*um!1jqA@m;y4&OIB?}N?g1wB==cnv!I=u1W{PGG0;meu# zAq=*1ej0(fA7_d>paC3F8)Si;*}0GfaZ+A}vtZ7}|59nrVoGg8Ij)z%4C9nsgJwAA z?I(aJPOmErc5)_6prSd`9KL z9kNrL&|_ehaF|D6ewy>sSAa8|q%BZ6%h_8EILFx+if1V2Jf^D^m7L37P`SXdTmp5G zv$PH>mpHO|P*t3L)H|u>+@Y{FoZJkAy~_EA>h(H~^;-nG&PfRc+~9;a1L`^d((-hZ zqoo?5iE|*3B7y11de7S#-eG%aPv2ARcjSY19LpQ!5}FGvAKgF@)&KcXs_W3b{^|dwAv9-T#EDx~F_Mc`9 zH^BBhCHaB2Gu@#XVtY#m*>>CF5jfjn+xaD^P}?7GKo(*9;5|^0wv#omi?WTR!eOVa zM=>;GZ770f%g}w}6>!EB_XD8MXt#7+I!mHGNfCw!hLzLbh#W8e|2w7e9rwLfaZ!*cI7c zjf7pX?VM7;N!xc%0Z!ZQr=_dZR{R{G%yy(7aK<)@?nIrn^`+A5yzOe5gB7-acEL-f z?Sc7leZf}zJ!BVcooREcv7OZem8-Tpo&j95ZKAt0wYEb(c=kHme%jybZL^FROM~sw z_rPqleU|Q1+_XLZ5gxP2HvKSU&9;}UfEL^H4uDqMU#YgZWBWceQ|{WX%?Iw{hw-9g4_Q0oraa#s8FeYq& zmVtU|JA)DzAG=j_wawS=cUtnc+6_~k>Sy=b8-M`2L)Z;)R5g$C$RN9RN~MGCPSBBW zxZQe6(<1CXY5_CWZe9VXU3PQgLB-q6q+@~vyMM33=OMdy9zd3Cm-r5-47-(77iZep z+d(DEE|FG^Y&%&LeCF6Kp9C}C&gEGQ>4cq#Dz^eVr))r>-AlAM7THD60rx4pkmG<7 zyIC9I^R!*r8=y+<<~)FAncYw&sB?D9&Olafm$LzO=j|q_@T;(MqOZ8p&X;z>D!WF1 zP}O$-qa&aiyZk`FRl6fQ5&fFo)Co}6?Zzl&ykRG%bh+Mc9qp72c1e#QYqS$*!u*!q zN2HQPz)I$4Wd80E>WcKeUQyv^<#2F&l-z4;dG+U;;L3*WolSTLwgJI*;! zJ$5fsjn`|JPq!o<+2zs_+h@1N2k_X=g-Y2ccAk9r9I+dxEppT@jZQo!?0(AtJhf}l zKxNV{hVt?$yPJPOWt;uGKYM}Sb_ufgrtc`sz9tDO;r2DOO+?r`e+p)# zJ!d~4+P;dqU@`V}vfO4E#>pQ_B_fzAK6d54rZVI7HSI)+FMS+=a9X93Yf$8EAtTOvHgx@FemKw z`(g0Z9_b!_Tl;Cx0jBITV-RSo!-gyH>F2O64@`fDVOrLl70gWB(Klp65|9A>@)p9dX=>A?~mmPsH>cDS4m&BG4=XyN6E!wV|F zQHRlw!Ax;TZULk_w5-FZk2x&Ngu!u#Z%Y9g4w-cKG1I|o5Gwf&tWv-UhxrkJ0*B|U zFer2=q!YO!hYx>&&r=Rrlunj7Y^MW?(+(N**_S#v(`|<`hYyGGVCNi^5>Vw11MTo~ z-eLb3>?$1Qe*l9@hrhmu?2^L@Dx@zvET%@t6^9op0aXrblrX4v*htNs>ki+}gX*nge8a9M;qP>~!ee54-ygSLsOffx}X& ziyu1dr^K_%!7mlEZingNpn4o$K7p{k4nHk|$|Hvx95DMF475r;b_h#?vnLJ@C<_{K zm^lQ^QHMWiff#e>F@QPlAfOs{!l8kxrLB&dcF6o3x46KIzhmbhsBMn#P@)^)_yIK- z0v(Ty!63+Sl$trgjvXAlkPt`iF{o^JoIyK!q~n$f*hM-1M&}JNj=^DT0%zvNb zx&|=g9h(k7Gr`gKWytnBMkRrn=om@6c9LWFC5+{e10A z$1z9K`=E|HvefXB;rK%cH1iz)q5Lf0@!&L2Cmesa!mhy4gKC*V$Db(=DRN9_!|t4; zCtdU`cXWRi)OklCogP&TgRbX$Syg~Nr#uqj^0$cU2(ij^QFo$k{a99j_=Sa zeBJRz7-Tmbf1~QG-f?q3A~ZNQehsS8v6?2|El1IHXx?_b{020e9lxSuhZe`K>3~*8 zzc9q>a1_(j=yZI23M&1Ms{#;q!0`+%R)dZevj9Vm8fq*(aa=SmwQENmbEz~L zb6mO`)KkYkno^UF#TyZC%5m-zFp(lsAKH)GLkBnh+*@^Uy^Xt^KD+>K?kuPTbG=@M zK?wJCHDuelzy6LuJGgeupu)Kp`eY)w-g}`L$sPCs%qT8rH_T(WY5wpT%T2m~2)np0 z)1VT^9i?-Hz1)AON4<}0q)k7b+g}2e1a43VWCyv`)Z|a(UKazBxUy#ehqxTNvvh=8 zumIFiZq6t)Q@HCo;4_t5O3C^$u0Rer&h@2=FN2$`g)EcXPwkW(t|1tZ%Uv`d@$$Ga zRFmX$?Yu!1a>t7yE8@EU4D({{&;LW9lid6Lke%lGH9%I%{b&K=m2tynf_a9!JPGFI zTrE|*=eexEK~->bbWo|}Zu}n1OWa%~G%s_5;$VJ-TStc%Ra`A)I(6KI^s8Lw?hk~@ z4Q|v!Q1#p^s}Z(=+eqt66ZaD)3~q4?-+=4e+?8{n(#-vX>{_^2RzP-_TSa+Y8~5Z> zsNCa@Q=71z`^gfRcW}FCt$V;7dl%G0?#xUKsf#`KIKH=ybx-!MevG(p}CWHp5A;k?^QZGj^UN%!DlQloEHCGyaHN`_wZ(v zK(?2+mbw-Dct`ewis!9!29>}YxdnrRyiGU3Oysd0;W~-;CjBkeA>M!y%w*n;O@O1k ztyCDL@Ln~8O68Sbfa^4#h;p}dUf(l-4Bi`_P|4&~O^53&-ZENZvw3k*h>*kcr`B9P zZ!dK{#%22>t-VJ6U&*3vb5$_e6D5rQkegu^8Tqw^v&0F3Gs+6~js;n~JD(c2m z@NlStnZN-#70L>e`zdr`Eo;Uw23>tV5RiGMq9?#=3Z}DQ&pmLjMbpqAQ+d*kX3(w^-n611j z>hiboMyPFYk2mimRN8rE4*?y#^tT}Eg&IEIm_tr&37~_5RI$)glh>q(g zcwbN^H^sZx02LpnH>q6pbxNT!cdOI$^`QKm9A zgbi^TPy#}oW(ojdPO-EmhdaIgF3cmG0$#)*BAvqT0HU3~ejO??PMLJV5$iNSm;QD+ zJwyHEI4AWc_}uIC%r^+M&*|b-K)lo6-+`In^wmSy?RUCDYjUE~u??V-oU~+j$f=5s z-IJYW{RGX!PFLu9ON!ImM**o$N0|te<}@n;LrQmAOAE^}CmD4QGM$_#56N;W{0*{f zr@eo}b&iv!7b>|YiPJ={(`ZgTpFlI}ZBhw1$2uG3jMu5WV+Oat6=l5azxhfXHyCwDnH(+}70 zl=41cz^R9p%0Z|1X`T%^?fM7IF()+*V%$kqg$NT)V?O|%I=QriIq9^WoJ~1xq2oLs z=M{8H1UuVPe>cQ=2K6=0qjDZ?+$$@%wh;q#F5X&oTdIqNlOra8wAfy#2eI}DX<=UH^PSK#~(ZM%ieOFR(hv~vO_ zx~0z3BM|Sr^B1#WSK*vS+vgSM$(dkQId7m3|2$Ps$MJp>rBlV13TB&%v(W`6(U5J#pTr0CU7SgqpCE&U4=cHRaqn z1cPlZ84++6;4&`|5q7u~(VxVIy6jScxzi<%zUgR}3QGOp7C87w- zM3?0Q@RID()duRY%X`#IPjPu=BkWRLo__-Bm`ijK?2fzG(%D3=i%5n*c`nmweipc> z9bs4K(!LRJ(nUm7)+v`u)GsJ?Y2OLEGMBCWpekI1lvP%`ta$*tOD@tOFfY4I(?X@% zWv~%cjY}WZ#kDRq^rhChc)kkeO_%x9>uPez%*T+LUCQW6Y>P|dd1&5s5p$v0=5odd zVLMy|^o@18{1O6{ewQ~-;=u-77Cwf`pv$FiARBU7N!91D%gE*;Xi#1R4BjuOLz(6r@snjIDgm+<`Mk%FA*VoQhc1H8 zTt3Sk%shUu7Ml5d!EcbA;D1QwMf_??o{RaL=}`V8|CBALQ~VBF$V&LDe*={9pP}^b z4ByxS>MVc8BnEhnUrQBcIbXK|1FYoVcnvBS`2Kr9UF3H>3%JDpj;7RQejIJ{)%+}4 z#B2C1cA&2Et7-GT#+Pk}W-WhVF{m5-DBAey`4_`LHSk~dgk~e(x(Ljh{Bp`sZu3ta z0oBY`(eBj3AABDwt^9Ilz#V@480_xx57SoO&cD(Ms)O(UEbKb@M=nFV3KLrH4etZsI zLR{mzA=~b{h&GKF*M0QC#=6E+e|MMbh7v@GbKOHti@mPDQtH0Xb*Dcd-qp1l<_WHg z{($*G*CFb8B)YbF!!F6S>Ij&JTn{-xCDqkJA4!@kQcTPc*ReVb>6mNm1enKNN9V#W z%Qc|}n%S<(X@DHpc2{`Gb$v)LGS9V-%AEpNSumJ|uGZNwFLIsw0nB1oKT5Dpx=yF2 z;ThL|sn2lMwSW?WbFL4mb5ib_HyhM>SEQ|&A+C9pSYLDvkwNp4tNtQnmt7mEEW6@5 z>kY_iT%Y|IaMg8oBAC})ODN^9b+x<;gF094M+kJ&wSmsVn_Nq%X>rRn;BCNd*Y!&g zugz7`1KB;-21iisu1zn&S%+(kCz#!?FP{dp$Ms_qn7yu_)1vywbs1IH!>(`cgY2=Z zJ7wulT(90k*b&#?*MT|dx`F2Wl1WA;59ukb*fM&AbFG}bR3$osT>mz~)I~W`l%vcR)DS``BaHI+je+kGGEO`#H zEI~c>ezOI+Z7|3Y^wE8dT!G0KRFQy7TV$~y{|=~=f+S*|62vS9^Q_=JUClTrXng{z zT+p)+Vb2RT9|!ZYVDS%deMOMx4rf(@qhA231!C&`)(FdJUNB4r$8AB; zC!m@Ijvv9%rU_`9|6V%wiItt(8z;qO7H`P-R8EZ9?Sr@HPj6abX!DE6y&BZ#aOnx z&HDy}-{I!XhDwy%eY$+N)9r<=U`D$g%Ye@qw;Qzk#<`88fZFYrO3i@%Zbxr}I^ec+ zE8w8pQTiwo-F^>%U9#K8OfV0-ZKfqJ)6GifaanGXdkCBDmX`_0aqC_LFZpiybQE^N zjY*ej3fy|A;wyCX{S^i$-P|dCJ>}N#1}Jg6=L|UQ7E4E3XWUxL0B7C8slq(x) zTSGf!9d2K4N4!qAi}wNd-BeFt{=lts0jP&=`+tCDpIgm;XQpl!>AZNrt%3S=gKllK z1P!@0Q7-kw%_|SiM%=!qa&^?rdmrq^+}eX68+Uu04CbVpLI;>~8)5)_+)LPy`MM|5 z&$!jSF&fl1_oO~Rfcs-g)&t$I+W>;xyQ|?k*gcVk6z(2L=N1v}zS*#gbPsbypeXm{ zZ-I()ACW?~+x<{1V2^t)-N@SO-t`*H54t~IgTW`duQNlI1}Z7(_Qc1p-{-LwBi zggp1{iCk{mF|57fD7)?_K0`U{TB7MYuxv`1FpK)O^587 zJKF=YTK6$IBGkFZj3VAmche0}P43QKklk{R$Om=X{lEkaI^D}Dm%8u%FC8X6a6e7k z?nC#WGWhIq-|i3dUiV2#i66OVQx??c{yt^mgYNY?@G|87BoeY=cXI`pkKNno??gu3 zFMbGW%zYKT`EmD@Cs3Ji-%Xj^lzSvKr+tLW=o|ADK8l8Ht1v{5v1}7YazF(LJ84%7 z6pmAo9VC1}&%Rwa=nT6Z!X|okp~6qTf_a!Q;~@+pg=y6JiW07*pJS(RY6T!#n4bsD zT|)mRn8yjjX*1m|jHCNfdxXnr?#2th{}T}sg#S`^a=$QbJ*Wf1r6NF*@J0F>4+($0 z3`iCVsDE}?xGouCQ-l{5f=U(MZGX|Sv6CSC8<{9B1)RsIe zqpdO4p_o#?W?>19 zx<$y!fl8}zCv8$~Leo-2zb9Pv6R39KC+|b0L-^+(fKK60ccA%DIESuMPrZ1@!hgTigApfV)9L9=dHIFsJQh;W$%K1YSCy1^V1 zmMsH}3vapuCWL>|zBwgaM~BZoqGeS3`ii38gKVp)oOVh-kt_s(f<>dRKr=-2{avVR z7fqu>FyJ)Ux z#RI71iGKVORKDn80W?pD>ZuuCBpPx7RV*6+6Vyo&VT43EBbg59_5B;H%*Ot(eflv4WhL_L)Iv|^8y0h6ot@--YS~D z9Mm0AE7eDLMK98F(I$HN44^~wxeZi0MYmdDeqZz%)te7QEp#f>Em~=WL64|>F`!rE zXazhH+5ZB~0Z{~Pii4srr$KW_wB8=FVbQW-_#6@G1YnMe^pt9ji7w56=D4VF5X?!@ z=oDZ|l->zVAMrE~$b7|xX950VUKNlcAy^H+EwAz}|&QMQXeqshKQ zoIVq>Q1NC73?jsT`~{zp;_Y++H%k1x7pR@$yVO#M71O^*&)y}DqjAQGn@+;ZZt*k! zKy#mXE_yWVc(KPWyt4$c`4v$6#VbAol_>s}CPR|=&(~mnNPLL4-DGhu6_aUVDO~|d z7xSp#2?Wt&llHR1Qd#Y&VuVA zv4+M{EcTlLXD7we=zBjUo*>ty;@Ol1m5HA{4CWcJG71J~#j9veoD-k>7f>NSxe}U{ z;u9877sR(dhss6qun5dc;=yIGs}h&eWT+OG+Ty`##J|jf%2ja=B`(**U!{Y2U7Y?L z;D-1(T}Y@G+fzTJL0m#7TaDsuO0#c?7ZiheTYO^?sAlmtS5Pft{|^wTRqW*lyEgH( zv!L#Y7t(=!yO>3HjXK0%XaJq!Z&-kb;@|cIy2Qs~VBRfWYyj0GPK$>@uXqj<&@Zly z#aIT!i)b4h6#xDJ!yOVEH-I@TcB4k!h**jIl|3pJc7Ylbue1d6HSN@^}}GR zB%HoRKS^LKRQx4u%BZ(VdIXRKNXqEV2TPb5sDwzeD3{tU`L`FEJ0$mm!3>o&QtcEW zS^G6)k&?RxP*IWzH`wiz+#+VQA5bibUI{oUY1$5hQ<9HPL$gHktPak~B%e|ReMaKo1Lj%DLKfnklZ2*1RxU}Q zb-q$^f!6s8lGKw>xhQ$>9eBAUkx;|!vLudvv1&;e~W zvgHlPIwhHofcp|F6`@@cCpr)BmfZJ1gdWNBKf_C}FIpeac(ZL~hpD!R|$C!OsI zroXhAZk%nCHqJ)80IBgjAXwUW5V8>Ia!RSTOQq8QJEW=tI1887P{$=gdhrCt5-H_T ztsN!(fcE|vX-*T&W2K>c0K23`pFkx}dU!g9v{xGbKfpezl)78-(gfP85~NX9m>-m` zr=>DcDiuSPBwf4~=7*#|P+#hZ^i9gWk4oEK!Z=f;3qA&wD*c9T-yD-ZqP*_7bSV`^ z8PX+DfK2Hos@QX+@6eKvD|MvhB2T*PZ@A8vdQ);)DE-V0P$d20B$&lgE`2#CrR8*L za$36Pd-yDsX5R#qNo(lZ=^5!~RDhRDzoyg9^V0Pv0Tt5FTBuY?D{Vksl0MsmK$oSP zsGzzc^*sfuO1k4Ss9cr)LRTfONte^%Wv%pGno@OA&TXjFOE=NIga&E+Rt&dM`qBj8 zrnI0NK5t8_sSacI?`HQ9lRkWEmlWvA>9Dixw-NhctS zkv&TjI#y;!U8;Su?zv#b%Z$`}N|3#>0pr{+bFG0vqO6xb-z3@ZFXAO1lCA#(&XQ#+ z+CGoU=29}0BHMHYnyIqh`G7RpBpo0emtCZvBSYr$Gnkn&nLh$$$u3Y)m@E5v0?a(w zf@-+VmwiK(%?X(weE~(X&HE6jST?j5%#$(~1E^E7w-3QtsqESx7+IOjCkM9H442n@2lEKIX&wwBjNcgfxNLS>KKG6Uv&(OGZC?j5)l?;e()r(0e}LEKWKY1RS8S-hAEM&{SqqHeU{tGpMa^*E6fIRtpN(2k!Ya3x# zDEDaqRV4qW9AS&)(cc3~!)?gU%ipAf zhYI zC-QgcbYMh&k$$65`LY9`#^kl1BEnPoXY_oNa^JJioRU|33-D2VNi)k&;T8s;{tB5A zzsfen!bk8KposK?ELib~mdX%Ct`B@}S13wB?NEI03o2Z3n(BZE#m}{1Mk@AxjtEf- z=|;$66dAO7$0{BL!{;uA)f3D(McNrqdliya#M`HsN6BTpLLLMvL9y=$WCs;THo#e; zVig@}B`KzFgqK5#e-465Rdf|$NNI|1D8WisY@kB(n1WFbyW@&icET=8apMd4%vStL zUw@9Go)-9A#UGJ~kf)ef393Nx{1(Ux6+itCnnj8%N?3~(zZzhEQgMKekxncAj)i8a zLXZMxnId2VRL&?IH^J_#V$Ce5oL9uZijh?)ia&hS)vqO>B0eGM| zLN(<>g}ebiyA-hm-HL&)Vcw(o&;Y7W@hR;A{feDYfC0tOB{&;Yyhh8#km4)4S^Pv% zIR%3eh2bs0s6u=KDr1W3N<XWSI(sapl!;dpMV*p z{Eix*!AeKkCPI{}UI4XS`N5Bn?NH9A+&f%3L{xDr*^lC}lhqj60PsYEZGt zuf71aOUVs|W}NctUchdp{#lH3kMe(C!ArdIP1?K@lyj&u-LHIuQriPcGnEPlm5g`5 zJfyUzggjY!+Xc+S%Ih=W^N8}_YM38Y-laK_rrdoU@zRwc-+(%%G>t>$xbju%TxKZM zbgeyGxsM9J9OVKk5p$L2+o6)DJm3j~d?o$oaO^_mV!HHGr2NMlP^@&NrR${f{xP^d zrHn5Clq%~!1(Yc#>HOi0(kBNhXO-JG1I{VEZo#fX+4tXVP~~j8cX&ZLN~g{jl>ti; z?~-z@ADC6jDY`>Ytu#@uq(*7m4wb9QWwhyEQz}9+qz0wuQ7{{oGhT&Ci?WRJ!B(Y( zy1gCBAt{`7DqAxFJ<3~@d-p1v=@kqq|FFPrSUI@?c8`_zgVRD|_amC$FnRmGnUFMg^cv=s!XM(B44RQ)~%2vXHFf*GvZ z76EF9>MHGcp{gHfNeENTqD?AXHJv&qQ7XYZfSszUM=*$1y-zD`jOv;T!p5lt62NZN zqHhpykE&r1ntN66*Fq&hg=zLr?knW`vy$yusZ^t)%P7WRP3Q7ILGd{qhc zbWW)53E`zc#c6~}q3X_U$WE&GpJGU-RIgt}yb@JT3|`i0RUB=DXH;uD0B2PtOcFF5vqG}p7eJ`oLUkIv7Rc8WJtF}LaN{wnQ{Ww=uZ!dsK zohpwC)axpjkHNg5+R%nU)T^Fl0&c2K(+}RH`l=Dby``#)1Kd_EqHnrYwe@wt9o5RW z0e4lG=tF2znS!9wp&Fp|rBju@9%1jRYP#V1f$F`Fpwg`>-3pZ+)%N>f_Ns!L0FP8c z7GOZN;TNb3s;1E$o*|Vi4#OQ*%`Jj#L{&oz%cx35vu;dfNBQo!%7!k2O{!%7WkD(q z^=W-P5;&0gdQ5u)1Ah;t4XAA%YD)G3JjSEk>i15f+1X-lVeOgVUJeElBZd={rjG zn6U_u<`F>sp5q>$(~_6rG4MPf(<9IUkmXVECRB<&M(9s}PI|1~2F+6*bE#@A@%Z2= zm}MT%1VQDDN6fdN&U!4N7jn+S_#IR(dd#QUbIGHHcJ#|03I*Vb$C}rmS>rL$i^sg` z@h(+8*E}8-gIVk0AprA+$CvJadXGg^r#5($ya`#O2mfujzU6VW5(c+DR?uaWW{-Qc z1hsfvxCWKG9xI2yZ1Y%E0l4SUD~3V4$G057eGl#}zypsWC44^g$om?0T^?h$VD@@U z`>zA(@w6LMp9jYT==X?v7BJ-Teg+<8*u(!Q9_6t|#3<~ZcwBt}5ym_^LjdC*l|^7q zczl_PK|J*c7QxO(eSwZuebpSgZ@N|eCuM+s>XY_h2B_E39v7(og1)#QHHQ+qV0H8V z06WxdH+TtE%a6l#m^z8_`Ed11w5&y`L!JkeLUvKz{2~HfQeVx5>&xnH`oXWL*HSN`O8qYF?^o5yE}*Wd)9e7X z>dsD>*Qveeto*wAGn&Z_>aJ;kMs?s@fSYP7m1Rxpf9Txemb$?i5n9w^)RAvhm(hg2 zqdt8ca91st0@~D4YTkCJPtn;@r#f>kH1Dg|lj{d+Iejt@)j9@z_NafM0<~A|?g-{1 z^-nY4x=&q5E&P79ArW4N)H-Ltu)1_E-pgaPw-K68)IOnLj;O76@G`DGLw6Y`)RIkL zK2iorP3Q>KagKnvl()(lu|;NkxWc2Q?ouHCfkSkfjk(R+6o8pe!Lr)A|f#`I?Oq z7@W|&OIOzmG}m$2=n)Dwqq!LZ}`+(D$qkCacstG*?s!Y>QllGjZ zklstVW|1%8yvA=nphEKzRcDnN{bNv>9$hX*kaT?rGXyhh3*ePYc<74TGAn4>XO#kUi9dQg6FUQ$VLJ zy_)PsID4d#r(-OA8aDOh`ZY1%!(c%3`wqy4HC}Y;^;mP$3kFX#X~&^5qS>QH^id5z z1=N&g`HwL0@qF(p4E#L#bm8b&-5h7c6mOV24{Ob`=151*K;+EbD!rUTAJfMpF0LB$+Mj< z#~<=kX`zzp>GdZJ(mdaz*_!UTka||fJjbXo%Jf_#gF%+(Ju2t2J@ub}%JF>d5vW4X zi3X??dCKXtDD~W-#8}Ea!@k9XRd~*&y|~izH4&U$@a!2uyo;VLWl*`~IYyhtWltOz ztpU_}t~di`qh~VRQn=~qw-@HEo-Ko*?s(QHLEZJdvIUxLo?W!~cX&3?9^UDB zK!FJNJ+=R0EDtzbPCiXPiNXG2Ry%|f@9FL{SLegdD_1N zFT*adp|M1mRQ6-SwWu-9AxM!myJ z!h^F=ukHt6hIzIBfl-HhZKBj6%4?Y3#7?iO|G_-k>uoV0#_PC0n0vkEQANAYEA0$? z?)S=h5zY>HEyZyJ`>>a&3e*v=a5<<{uMDcy(!5S&fI9B=&v&3QynNdL*v zT=9yZ29+AG19TvI)$0-cA#Az)X*4zG`QK(o{9&+TA7^s1m#tjlZTSr~MC&7KD6@p_#$#a^$7 zXJGKiE0|7q`n*0rf_VL239|rCyh3Q}8Sx4Tf#$f^?E`=buf=@Gro680gv`fV_dA$= z-V=1&z~6h14Z?2o{)SSO0PpqGGz|96*Mk}2?cWT|Xzu|nqQ`h=%m&1I7xf|fF7L0Y zoQwCqL#bGTw}rBV{oYY@zH-2OO9wO$dPj}G=Mir^nnXvvU54T8xc5S5sAPD5P51fo zyrU>f&-Z?f3{H5*(RGLd?<=%1lz6XS3WL+$`!52@y_3wc1#kim>~&>XU#2+INU~P@C`@MwX~ON9O^_ z+S8+e!&)wD{~GL0Xx*Ga6=-wML#0s5PXJYMPWun#S>@WNOgKBQWzmv;L3_#w&5PRPNXRZ}og<)mS?fdJL$&q>bt`JL z6&lE{YOnkXxTf`|+~&HrcNJXU(AJb9UcL4b<*f}`Z~6h6w0ph-+|o)ZqrR>EryS6% z{erp&ceKapNcpZdhK@_xw4cyH$35-G)R^zoroIB|zV@}hA$y?RNjcv`Z7Ow9d$hm& z1*%tjmL}~Z?d=jo@6)cMLy$o&XE~w|Y3Iy>`LOl^rS6Zl7Fx?kwRxLBjcFB8Fc{aq zNbAysw(oP;O=%|%L*}EC*TAQ*&g(;{Y}G~m1KBp+W!feJbaLt}1nTzE0auXj5WUas zy4Dsjcj&HDx)rM1M}fk0kKcipNS*8{WKp`MFF>|aS3_HCw62`?zg@aNDH)2>1yS2| zx9<6Jz#iRC^giQto8N~lLFbtWYQHX>vgQLiF1^nrT|y;fhjj1kfX`&zZ}galb#Ku7 zOwloRLWXOY)X7NGwNh6lUH1yT&upDY0A`ME(=qtW)dl)OmZuwWge+gTm~z@eozs7P zOx<6UU={1WTmU$!%btl*pVG}eh6ts)AWF!~bj9Ofp3!ZhX5(31jxCtybYIh`D|96< zfmx~BK&kBo-Sf0mUex_7fcYg|(>lbf(w({km1>M%|NZ@Oevjl5(5dx<*b$y4||Zx)7m9C#IjHS9hWg z(62Lf00wlG#~>TjEv9UANY^BVmtkFM9lVU_-lzRxR2Q=Ynq#_k)O#A&eWwF8p?h8h zn9{ALc8ZVwFKTc2>X&>D=2rcgpAgnhe}P^>fIfRZ>;m;;^T7<#-#!Qk*4J)>EJXjq zOvpm@1+>0|=~qxMJzU?l3Mvu$u1q+K)GwX|Dq3Im55^gzUuT0M#p>-@(A=dj;lM6V zUqOxXz4}58nEUjTIvB+3;{(7<(DU8kWxw7<49!ISbHz|e(nmc5IHVt00%o#4c{L0U z>t|z8cAqvYrf zbY7LK7gO4FLT^i_I|X_-I+rQbx6+wZk^bY$fMWfd2OulaA2-9`w7#ba%u@Y7rdM!TpF(YsEBekGfGR!y3uVkf z{g5lX)aZF%z^+z*lll{N`XkiPxUR3H-u(@|9ko8{^^v}yZtAz>LZwL`n~fpe()<5_ zu($PVzJ{z>zl!$8JNjR1pmJCL&r;a6>FuZ%y{BKC56yP{*kZW8uYZf0P7m}ST!ieQ zemc#bF8$YKV0P=@rLEwR{`u!X_37iZaNVy@rNheseXImt2K6IHA$zRPzYKVyH&SLb zqJNL_f>HfmJzz|~BM3g9>QB;gIH`X+4=Pi7u^RDw4BcuB-q*0j4V1rO-Daq4GxTO6 zLV%&4>c2q4&OaavGPKbv*lwua4cK9D6vB0=;jP_J2{XiQL40AM>|&V58F<$acDKQCIbfe*=U%A98~lm^35K8?*zGq|tb*nN1JePJWcY=$yhDbK zZv&DI-zyQ}u;DayagG=c2w|6M*y;`{&9L(~7^E8pdf@t);cwa>jvKzG6(!3MMtgX+ zVIQr+Ifju zuQD{!;HwP*l=IaX%m#$LYS^0$W}SiaFT7kgWQ>A&!?0Kis5cA@V-O97j75MZ!x`#X z-7*}%2kN%L{1xIg8~o@ms#*+ww9DKz{I&pgZH6~^VBRy#HNv3X@G-TxI}G_hgZaSF z?G4R`2Jsfix(uaX1G){uNHBX06;EN_XAsd=*Kb()GwcQozGHwv!>{*XK4kc4FJw;) zC6tGZ7`~!!bJXx@00uv1Fwx{2H=L%jY|^lkQuist`n9n0F={`9YhUB@FM-->+(-HD zHltz}Gy{yr&p`zm?dfV%knz>`;3e32k+R<%#%a{-2sNI01+p;X?P5^j#@FZsHNsdG z59Ur|E%nx-jp}4DV~pQ^0P|Sm2qlTTjOFve++&<7!(;9>HoOVgXIyp*2JyxK%9j$1 zi}JxdXjCnNL88%0t74K-m;>e^W7;qv+4y7-vZKaDRF$U~18A2?HCCR%SkjDHln|sF zN9c?J|K=WbW;2ahD!9%vuBX&J+qmmKyyO_)rXnrh7~BuL6GqQK$O?=zA467X{Ps&Q zi;RbN08SZS;euIWT;&Rb)5fvwV3rzdscl|nRJ{P^IU|pL+j3*TZcyiqWgkMN!WcCl zgQzs}?qd*_j8{H_%4K7;J(yRF+6#axV+r-^s*S6u>bYhVu7gUgu`vx}sWVpm0_Jt2 zshe)=8|VDh6xBG8mU=}3s_!CV+@H{CrC*E>v4D5DNFc}w9s!o;A{fk;!I z5U!(4>ze^PO^%fI#+nw;+Pcg12_2ornP#kmv)!h4%AWR_KBZDG-ZWwdW`gNQ2B`g} zsI!QWX!4-uLz3zC0H{Nz^aen(>0in-Kq>uZ2Brfrl0oHflYh0pUQ>tjHL=^@=mP^mIyb-`J+DT7jnYoi1)rc`RzZZ+qu1^AhR^I+g_emfgu*=Dw*2MaR) zL+zAc^KSabszS_v(}~e`Gxs|%!_1%4@)T|^r)oFC98H>$<}E4k5^YYWA|}RMx)4;X zxrKIwUFPShUAxD;j}El=nqSOnyY9LzhZvY15jlSVnbGK-YkN7jrpgKU|wrxI6||| z{ADtz>*h!Fv^UJvA7c=W=4@*F+%zlR1l43NT?g}9=BxBsw3yFQp4@7F^BGWg%!euS zxNH7oKbY<2&?Z2K`7EuGo#t1l*L>g1$p&Bsd#D*orA$onju9jtgf2mSt2l1zWVVhlf}u=`-AJd4qo29hUi&424^M zpvoq~(lUt%k(RBy08y6BHh>sQFCDAKTD+1#?XobJ1L7>}+hDNQGSvmyK8uYNgNV1h zKMB_fmOfg@4qBGd3`w+HNCcH+K|zG&$?_Yey+1t^GBdEEyextl4Id7fo87d?GVWFEPwvj-nS$!hpf=zFbG+ZrH%Hn zVv9ZPKqoEzmq4Ai%v}I4rIx1Wz$~*Ae+uf1MNiSoEw9sGh@Q82ECp3z37|Tl((*h_ zjZ2n2CGdIKvi1b1D;9e?-mbFzN!8g^%Q0sdT(kUIis-c#6%T>xEWY=_thaQT01cME z)i7_gT&4{ErlpD2iQAS>KLs>fB-Ea2vHVV5u~tjs3V3O=@XjO9Ji_VDGZTaarz$42yG?qTg$CO<5TWU)|4Oo4JZSxb67ocA zIh8F*)-8A8?2z>!{iw;-c=`~IT9cxol7fG@6XvPbP&!UXv+5{py7lh_K!(-vR}44P zI-9!ES=Orwu*o4md%eU^PeCdRBvn{9s>)5++R%l&Co%|x}-_L+LWqpnk zx)STH9>8g94sGnE*1cN+WmYM*OwL)SsOwN}-A(WMyftbrm=)Hi>%gqEc2UKC$-08( z%Vle86sRlK=1@4Rvi4E(Rc(Fi0qm|>|D;W^*4nrp%sQ(bMYwMLoBm|>hIQ$G6U@44 z8C>7Ao>>BCP1a|qM!#ikSq;tG*6mMW*J@Rdf_cZ9audwER*?aL+N@=?VcxU;9R}#M z4p0Sl->Rju<$<-D>f(piyE9?lWj!u{d9U?)1cvm;`Ua&Aeb#P&IP14&Q4KU;-FO$8 z!`3<{$R1m7Q&aSbbsjB9Bi8?Oblq`PmR~$AD=jN68>VH`%JMEQcV8}C#+~eYxm@n0 z91SbA%u#8DER~@wm7xeO_EMP&E)Y?0uoOfYvQd;NBIEZxzrW7sjPE(m``-6?o^#Ig zpdXgQ+0*6n{~(-LzJ#{A$>qOMFMVqHXLKety<8awxDR7!7KDBbK0S&*Ba_mf0LILF z-~t&lW#D!&W;X#I#8~$P!eEASH3kvFI6!4mD8sM>UUoAYeg+rLNDW842*&g$Adw6W zt*ucEclvtvGQ7k%5jK8TC&0x%;XU}8|&H$Ii zXxR^v&FJv}$zjBH!15F$B?TtAjB(mG^B7yGRhG{vrQEH6;ZG~WX@>6w=*}>r&tfda zjLmbwl`t+)x9}`u^#98mk@-aiEahsCz=me7~i{t zyUKWt?s!%*)~6yu6=Q^s{ckXCQVMXBk(mT%)r{jN=xP`%=^{!kW7}&Ww-^?-FuBcm ziTXATj4x>2ZDh=%eCZCOkTTp>#+*$sX=B8b<$Z>r6S#K9%w(8!Fdj{VbTe+#xmpk7 zrA`2P85gOx?_)TG!`TDIUOKjV#Av7b_A#TCit_=+;X!bN3@h64h8W9e-i|W(x4=DN zG|)Cb#wexS`zd2FZ9U_RR1Jia3}?E*FvTdKbD(L)w;KTPVaDb{x1Cu{iHkqeigMZj z<^?an1DW4_4Q>Zh_XD_{%=hg82xj8HnaAhGe1nR@Q07b5L3T5vUxCj(%q`R+jbPT( z781$4vAB#SxeheEb}Kv_>5yFP`Bv-b1NmD2bpFS013l|37Fq3Y;bt-cs{eEf8*oWXU znU=H)XE8a0AlXbUO{W~@O%0r#U|y%|)VWL!T?NTwcAo>uXU?s}APSiAbfQwobjyO} z8Rqz3;EI`ZXsIk=e)0!!XPKjP&Gj5pwG+CF%$g6ND`k%V3t<^^%>o3v#QaeXuAFJd z1>g#E=mJcxGSj>b5r0D0^DCzHPn!Y*dsCb;fqCcO&M!wmHU>17J*0e{G>xeSwj=DHQYJz_dgW%HOBwG_eu zWtTCp9>W*hjSK3e}m}XivCYi7CU^&GU)x*m) z^93DT`?6j>2opb6?XMvItcSGB1hB%^f(v9_po(D^OHcqVh&7jryI|J04d6mpYjVJa zvVQP^Nf=8r;nMe6O;&MvFs8ND4Vq?5tb)eNj`{hij}q5? zLO8;Doz{s_*78rmJz;I3NixpzqkhOU7VAF%CRp#RK-fu^h!O%HcG?lZx3hCPAoOM5 z+yc&zJ#-7YKz1W#Ry)`m55wn9_HA1Hcd-|Gf(v24@jd{d?17^oyV>gV5bj}f=}anu z%`1X1lASXSXHo1Y)ScYR=EQ=FW^bmy(~n_G_rWrjJ#`i&j=e#LK=JGhIlLTVU(y1Q zz%E<^lF06-lZvBk^>yfuu|J^P<~Vyz0h}eVuT?^q%FbQ~FKKK`n&Ihe4sEm<>|kmX zWV37DhGh=B`6S2*b}8+fC)u;Utr(<2fB;w;!;?avJ0vDEMuELL4?cf5&GdO*z0CNc!ixuH!ZHR zXV7wSjlGLz_;t2<2}X8<&G{ByZn8tEtyRnZXE$_p>~=~4>e+L)gS*9!qwlkk?M`$0 z4*PSOn@wziBV6BQ&n|;y3;Q>k%&qKxT7}!#vp0dN;My`pHX2n$o`$??GRf+b@4De$p+6b!fyHhV#PjB z_1IIkH60(0vx_NNc*ZU{1>qFiZ#!_)><{T_eK=0^MB6#tn?ZJP&V2^5lk*k}fL)vm zl*|TkWc2lfaXOD6Y&fTkK86U6AEg|ToHv}oMR7*vA^LvKk`ZvxoWk!BD28)~+MuzV zC0~Mz;}lN8*#XW1ig1tnH2va$|F5uEQYeqoQIa}TXJcF~FRbDbNjY)((LTa`torKE#Q82PVTD-q(mX!l`%;+!$xaJeWM?+-n3k z&N-70@{CiS3ojEKr)|LbS}194^|N^F4V=Hl+mFEoSlsc)Sb{B7(GZ4MFohtY7K;-w z_}vzPQjk3s|CGSXUW>={OYgJj9R;`F;=di>qAj-gA#9w5dpzPDuy8Jh&x00&bXO(b zV%A}hqZU2WFga%NmOTK+Eu3mVk}TT!AjuZ)EZ{ONc1MBBviP+ZuCp!HRD;X0u=p6d z6Bb-1NP)$ce=xE_i@*F}Qe=VB7kiFH8vRjHiN(Kk+3T!DF)b`*79G@6y=1ZWIE3XE zFB<^AY*9cH=&FUg6Lggp#wu{t7AxsDsaG-5Fj1;D6-Haz+I66lR&PJ^0OV>^^|!8aUaofUNCnvU4{zb3g!YA%KiQ`kloxH85n02myrhC zUhWO5o%V6xp#zfrTs;F^Ece!CaB*BFJ;MQRRw;ZQAZNLmly#ls zUbqgxdG78rFe&FA-GFgk<_1mzSHU$M0J*}QNjp~+w~<;3*SNTKJTwgwVHrZ_PLY;h*?>e@#?orK-*N?24AGW9u0iw| z%ML0WVl724f*iE$&P9ZH%Mcd;4q5(v3?#ww2us zEU#GpNL||-mP6Y>Zd%TyPovs$6MgJ8mL{qq>n$yV0k~y3Lc8{D%bbT0HdwwVgR>^f zujYc>wG5(pbkA}t?GMeC-%%>vX6d~F5$;?5LmOYarIvQX4og4Etokhfq-5`br8xn* zhnDLjAbe~YLWjr$mV(!z8?^j-4um6?-yHxqY8j`%ke*m>d;q|>r3a<9&n$gBAe^w= zpA1W1ULUQ4emn*(3I4pdzDB$qytk-zwUg(+1Y{TQ*#Y1}csFRKhVs_!gR|Yd_h}Ii z=T$kvB!cJdf_Ra<4}S%BkT;t$fp{J#877B#b7_Z7;H94e?g%fz2N90)97X{+#`8&q z>*Ksneue84UJdmCQ+W~JK$pgAUICKM>otR9@je@aE}Qp}FI?yF_S2?#f)~FK!d%{0 z)Vj*!{YZOUKCeFn5ej&BY5tw&&7~&S8J_ehEQ@(DG`UK64b;m!&s#;!stdeIIwHKt z`i-#a=<~{cTge^Qn z8{nz0k4|%VSV=Vo= z8T1Vf@CJM!9ON~lJexJdOQ2(cVczrt=$`PRKZNBN&$k}YpYpc83vQg}LaqNv-WO%y zrg(v3SWfdaRL%SFpHimd$DiX4p+Dc9dR+nh0c(&zex3td@8Z{tK^MedMc223`MPfa z2;tjN2C;{4CU_YC(hOLJ^MmJNND+L=73lWz2N)3U;|pjN-p~J>KB;KFC2ePM{Do8w z9N@QKK%j$s0SAD1{;EiDiTt=ojPo%6TMuwY_}dNwca&dAJ;x;eZ>7*B^ING7Na1_@ z30x|_kqyfX{+%}HGWjc40+7YuT?8(h|DXf9ll+C$cRR&T$cC3({@NJ8^Z2p!F%j~h> z`4?sacbUKM7YMKNeL_Gg`7V@eRPi(3fyp(#-v-3H$^SP4x@!KlBj9TI-%~kW%b)iP z$SwYRmC)VhHwA%f;QuifTq8gEQ;fQW?^+CJt^6(%bZva)dFbx*8%Ghgo$pQ?bQeFw z6M?$N7bKuc>~iJ+A(tDhCTY75H?f?;pKFA9GCe?LUv;tsA%FhO(k zl3>PpJZ*(w6TOKmf|UlquL`zG;H*-x#Tu4Xf_gtVyCL8ng~?5UEA6n=0@q&wuMt>0 z2d-9dW;w_$fd!Qjw*_Z@2d+WTNImLC!A|-H?+CPXvV2dVpbliS;D;|jS_F~Q4`~%7 z{0FX0F#0-3hag}rEIS3~Xb@e3m^Bzux4?KAUU~%aAH(E<;2w3C9}1q&0qGYMtOt1{ z_~bgmJ{BCff{_gg@-iVD7Ff4{8xhRThHg~gc@e@V0wv9yaltlv_Gf~%jSx-NBV;K4$KEGoVB2m{`QvoPVdAMp&~LdS&=MhN%61=!z1hb~(9 zU^6UZgf-B;nY5mL&87zK$sxBO&MUK(B~m=M}=-%p*tr0p5DuG zVdsY+Ny5dHOC<|e(s^T=Fyc5&(uMQhh3gFAbR$Tnux&O>vV_i_FgYQ#xB!!rLhW+6 zJ|*nj3zJ--Jx#nkVN(-yg~BD2SQiN&YXCScJVmeIjF7(_x?-VYHgp$-4=CR)6>4Y$ zD-*hI0=XnS^es%vg+EhedPS%cfV(Q3qO_t?Soj;fR0+4th2=HjBqf(Oh36JSSS{?5 zB2bMm)B(6!A^x9J?3=09u4^ zD8aP~7bM}wX%oIh@$L&Z{|+ym!nn^M>=F+D1=20_O+xe@q0gHD^a@AqVEIrO{wcil z3++FF@R4wD7=(|7zfjpSApFc8x?y2Fz2p($r*sH8D*S|wN1q4}RRb`FuK?UL;h)dL z^@K3&0pOFu-zd$V5+;2HZd&L>OP8=fg5s^icw$hlsu&1{W&&dKPqHBGY}4aM4fn5k-i6F2f{JbdgAu=;@mXyI=GJ z&A4b0cNMr8(OWxU87p%B7s5Evmw#g{@uFOJ01k<6`~_Wt$k7cx6Gf|O?>{Ws&$UyZ&rbt0`T$ZSuYOZWi_U90u z6n+0C$SF~X19Z8f2UK$BiDIcH$rojR1YwcL;V?{2i?U83>={ubHL{9DVZ{hkBATMw z{=CQ|3%Cm+RXJQ=6j{^(UMjjj0n0K`KP@bmMFo23Dnu{s1i2#m&JKX9q9|&vRElEg z64iClMyl~{h!$@JcT?151uxa2CR+$=MENxP>qU2c5bu^~`~OpSk@*6gHHfy2L)R!; zN*%hpA`9x<+!NK%>}VEsPD9ut8V!U=t7slgr*@J0dFVPs&qN@dqOb2k*d;2UilJL{ z??YJjiEL;?c_7+L+x$b3BjprVk3^fU;K2q(50}GaNVNYh$grrBe!mfsGld-$ zwb9D(ROG6LZd`OB0f1+syWhgugy`WSIGYru)4cT&zf4nfyV#%pz|>cquoT2k9Q7}T z)m3%ObGXgL;3&@6XPl|zCm%Y z14x88_z#f1;z{~*uzli)Kj3q}c#=A$(PHOG=wigvlt&&AzfKA3L9sF&gO3-x{DF9f z#9tI6LV~#VZCD-`FQ#=qNt{8wk__>GpMYeFi=qHHC0Lq(vM|o!M6L<_?f{u@5a| z9pcOM!*z46yAP8=@i$rs zhr~;JKt{#8Y+(6BJVJ}yn0O5Xqkbx$u?)fq@yeNaqDk>}iasSiNcGsXIHnvCd?kOA z&QJ1!8~}gGEP4e2lBWUivQxqxf$LooDQ#{+k{?JHENOlRmb)d%w7~C?RA#^=Od_Vz zIb6b`FEUCZ$pN=l;`bU%_DL?S1Yp0!bQbYqCE1=BM4aRneV7L%GFq1oO6EBLkRai1 zfMud&&pLQHENKpa$q~u6QIO-38PqRGl7!O6kSuAVJS0U@Wq{9gNdx_nbB1J?*78ir z$zydZ}dFLy$7bt;evuB$@RixN=D?eVA7y=kg)ED)Cyy~8gRo!nHud};NgC*BA4~q*4W9#&o%B;Zk*H~38I#<84gg>2ya8DH zNjc$&=P$jy2D$)gDlKa}rGC$#+a*;|y%{8}{Rk$((pl7#+b!L>6v92ylV>3elg6wC zE?gS*J8)6bb#yqiSNb(|AoodUZUNaZ?W)2!W2G*%g~UlG=7AiLj#2t^P)a` z*a6{DsVkjS9Fr!|9&lW$p${cR`dl8oq)Mx)C!QufL%DvsG>$I$Wl2Ac11?+IKsiH> zG=viQ6Ve6qV3I4fr*$b$8hZv@zBIfUqb`uHn*euOnidS*8L5u8|O7p%0};I{5>152Slv0sNtK z(>aW@Upl1#_elCNt!Iy=fqYmFN#F1SZdiJZ(wGscf%2D8>Eja^{1fRU?GNM9OLW%r zOd3Yn#e{UeCCH@Ik80*A=~DU|9Dl1FRJ8_JMW`VRwA#o6e1}yOm83hZQmHl!wrcN! zvkcHBFp05R z&@lVJ6W1xTXRYyHq2whE$t!4WGZ^*@hV1(m{Q zlGP3>laj5r*Mg*2mA?<4saAikfR{9@&%Ghcw7Tp8VV2cqE5Ng@9v%QL$Lc<9gD0$_ zo1rVPT1_=0tBpHAYOFq^gN|COuviSD&T5trVe75jsMNb<)l6yVZL6S{VcB5y z)(n_5T9r~+-C{LwE`+UC-@FZYs-MM^RjPu!Q_H$ z?tBO@%6RnKUXnEh0#GhXqZ<9Ptmbb#ScUB6~2`r z$*Smksh62)L%Ah;Rs?ceb~+ZMLDofYut~P!Em+=_eMJ-Ro-Fq@JW8|7x(vb=*>)d{ z<-V+%reV8`N$W(1toIN`-6@MVgX@wl-3c$fGEZ7t`(*QJb$cLNw+hZ4$}%WJ>X-dZ zOZtH9yCJw9l+9lWpF^^>ba*)|8(s_Hh^*~*SU!{Opmx}VY^)I6q|DC;+?4ExH()s} zd&L(xU+b4nA)cT03zX>kTlY~lm7p;nr_ZW))$*aVMNbTKmu%8D;$n-ICpJeTNF8XzPS9kQnQ;(=ds( zexA<2;;e@$hfcJfuZHWx)^|Svcf>k>E^tS!jr0+vS~na3mu7wYeE`y}yN`g&upXw{ zJ}0dWy8t+4-S#`UTrGVRS6XLKYp2S3!#5CKv;Kk>&g<51 zUWAt$)*Gq$aMSv=?_pVO{lPEL)mZ1zqtsf@c?dwA^(IQ5>#Z-DVA*8-E1e?VwZ8i% z0Qar8mVmTdcTR%zTHmKytJa}jiC zZ1SmJ_sr(S*|40jiJ&WnlQuoh&`sIA^#OF#Hu?Xr8|B~dVd5wMd@lm|%ZI(-EI?jP zJ6@ptyI)|rORkFmJV^fQ7~sM3x+0i_$bZTP7b?%7)Gtil`4oU~dBsr(BjlUaz(vZr zmmrLi2OfjTe)+sNAdHqjqx&H-avSQu#L5@ZW)mm(cn&09K9~Y>NWO%A+XVT?bRe22 zf9XHK56i2mU35%dM%}IB^62j%Op=G4hA>&)vIoKxxh3@y(&baM1ZBwgzlK1W@*Qq4 z$&!!J#LJdP9D*=U{xW@M`SKl`U|AsdD#Ta{<$GulMe<5ExMKOvCRmopFRp;kv+~E^ zf;%VQODBxy<=^}UQY!B=VSr`wJ+!P{l3%rkWx4$3hu|*Dw=Mv8Ro-0ypOx}Ps*9`S zuh2p9HMvXz-F3MuC2iI6p8w~A@+fOKtCb(LN1!_SoQ)v$^06pbHpqoq=o;lU-@)}A zx%m==P4dr^K<>(KeuW4v@A7A6%bY)&t9adCR}>`A9zc|9ntxcoWVBvZ4IsygSrqTh>4%_fo zVY$=R(G0T7_9A_|du&hcgJqa4!wZ4-*}k3*%l)<~A3=A}HkB5uc-vg6l8@THNu}2@ z+t?pqnPxk_1iEzFLb{5TVcVgBFw?e;ve6vdAN~e+!gl6zL@%)A?*vz9%l#adMYh3o zJM6UW%w6El*xoIJuGn^REi6lHkB9+3Yx@PAVV<)Uz7Lc0wo*#rF4*cQJ-=wXgcjTi z+q!mmxnldrH4OKvZ9c8Lm9`pM-KuQ6b6|PR*3us2x~(-Gpx475rjRq z3&Q~KwT+kw;bYsGbjUMcTSNVjLED`(;d99LEC<}EZSNxhp4jG$f*Z39ISI?Bw#}4M zP1p|8_Bm;r=sr?pWXZ{Z+5q9s?LKkUQ zPpyF%yLGfK#oAr7g~>s?k7#>|w@c^4OQPL|3$Q$F_bO%S$L!v3hVZ!Es>9&Y?IP&o z%dp#i5rAyFd2hf=j@=tHi%;40PXd=~_lVwLfn6xg;zBz~1WeA_*(`?coSo4E!b^6p zRE(6{<^K-dWxEA4psTPePKB3ByKWA2Rd#cB!}6NlDM~!A+l6m}WwqU6%6Dt*{-qMQ z)@~n_S9Nyzbh>lfZpkg^8tmF>QEjx_NT()u?CxfRyJu$}gs#~x=5_dNvGaQYq}9%z zTC(kSA5%5aVdwrU0G)Q;may!yo1&7Y*KS2V!uHt({{zbhc79*N*+aVpq0l|H`)MzP z19mGZgCDf335IaUZt(|zKefwv9k_A3@RI;Mv-_BmlL@;|4}zPvi$(!B%f~*2zWMF; z2WEr#+S`8sFMjr(&!F?SFQJMd$UcUeu)+4Lp9dFizqbYhjIdw74wljO9~K~9jJ=TZ z>qGW}i?B?vf71uT?cJ$4mSwM|zIV2LF-@R6ds})v`SyX7(-zxL`QpJ!?2Q_bQhOzBt!4JF&j7h< zuU`W|rTus9a8_;qW4jpNLhdMB5F%EO6 z`v+XO!}22lL^urg!6ec_y9mO44%^NHx8Gqf1SHxasRn=;hgVynJKzvRh2KGko5ul& zclgi&CWjo}ItOyh;ZYcb#~ptD3J;d#uzC$hvcr2cJ5n5`l_2R3&)kDW1Zh#_20r*bEiBwqbQhX5x*FlN})D;U>640Q9KQ9pJFb(iT#Qj?Qk8fSVwOnMsX(#!dS(9N_Y|!O%EVUR1E78=&<6g zlYk#lJhB4dsA7=PiX=r26NvO?iX}9;Y82v^;JQ|^m-dx9g@UTOdWFFcff^JQYr!=tdN0A`j>4)KfF?yW zJzujzL|b`_V*3?vt%?vzW7-tIKf)k76a(MFvQzO2HSxL>NwYz^6=HhKKE=XyAP*GJ zp9J8cVo3^I_bXf|%NthcsK_2sh-teXRn$=e@<`J_df!8rjY4CCKP;nS(A$I zKZ41WBE%CBrWHRQ1O3c@Bd4J zaz4%Q5M?8+-l0nIn{XDUw1@x~t}OI{vk2u(SKuO*Tsk_9Qck$T*?#35$^@d7!&KSC zC~pmbi&f62`4^`Qq4Fb9d5@}>!%9o4H;*VgM!_9bPS82?F=d((fMjL;$B2-kEXx2O zRe6M#i!|l7cOXnx_E57bOBqD7KU-OP0whNnT>;z)<;ttzPAV@qg3D9p(`}J_rL+UC z3zTn7z*(X43hj+WN^eSuiAfP`4=@_t|`ZkL3dplLtTJ6<-!{f)+_5Nqi$0AwnBJUIiy2`F6D22 z5OynnIRld(B~t*dSK0d~xQ9yZzZiADQYM4&k+P}^glMwFj& zK}MB#>9pmk@|S$*#+9EsLHJBL;|&CwP=?Z%Jgpp_gID0Ay7V8&c2(Uc0QjneC&2}% zdUJsbROx9V>`-|f0=HB3suA&mRqJNMBt-RIFStY_l7s_y&-a!eJl7(S1yM*RUuQ7xu(gjCgAl<=gfBI#31S2a5Uo}~(-o`1H= zoh)-yU6jb5P%-IxNv^7LF9PMM-dF%(zRI6Er3I>2E+WEdReuElXH?nLxGq-J(av0= zlF`+~^D09f02fr}KZfq2%H0;0rK+Yt_$*g_EQav1D#!_z6{;1l0Cz=INeOwCN@4@< zn(F=xM7XYsTmf=J^|w1LYgDQ+nAEBcJp-vzjZ-(KUX@18oCehzcU1ZG zKATi$e}J%Ab>#$nwy1U=0aq z2dQ&I0SH$AN#~m(YUy?a3RSnu;d-|^pSI+1wfL*& zqSgO&Ll~p(r`<4Co%t!8#i{Ra#UKu-%c)6uNPWc?x&(FT2rLuT>|Y=}tbUpHfFtT8 z6NJarK6Vf$snvXN$?9BM>r&J{TY*betErmEP{;K_n5mAV3M@Revag z?t*$o7jzfZ%c&Tx>TjL`cT4>ebpdXx?IN-6D&1x=f^DXL_DR8aov?IW^skOcsQiu8~eZrmUyimE zCK~($_2aW357n)-UiYi-4uX56&c2R7kJXQlf()s52Ld;&9&d)15w$&C4IWi{(AM)r zoz;a1+ZkI-$`{1Xl0t9d&J#7`q_1M%0~S_&@# znv!>58LaVRHOPu5)C0WL*jNXA%FH4{`*rfDX&gUi&kQ{kSa`R5i)vNa!4#+jqZ zZH4fJ<}uwt&ed$7)Gtr7dmpf>z`C zsx%v32f3zkxC1XYHAiUnS8FWjFPv&L%epa^T1_E!^6NC;z6jxMO%`3kY0ym2>~GYp zXM^0)6wimxCe77;cxl#L83Sq2y!QZOY1Q!Q`bC>2>Q|WD*A#d|*Qts40m3d#*CoKa zH6Kw6xJT1-4qUIsg3bdTYSi@E^lM(Eqv1!Ii*&`}v4%<2%Yfzy9qkWmPAmZ#(WF!H zGphOM8|a>B{4c=SnC1^EWuIw|J3}|2`Hhn2NzL_EkSUFr&bg*FsSCmRYU`+|@Y7zm z0Ozlrl>ic;owWyp2-GG~^1MrXhW6qh?a-I-8LZ9O1VD(^hKiU_?F*Nn3)2pNiLl{X zm**gi&@PCAFjA{{3_z4tDhIb;dyNvaXzkyRz{O~t6>uG^ZFmuYIBlXGe8y{w--Ygw z_M0N;60{Dq*(7T32ZJ2eCefymtbO+|2AHD#DjvdAtsC_j(zKH_VRN(}P~v$)>rO*D zseMSl<|%ChovRgTmrld-wD$XYkTcp7@h~aY7X5+fC0Z6$L}#^*zX5PgdovQ;d2Isa z1sAjnbuhW8P5KJLQtkYF2+OqhHiNsWeT@K>+9gNevr4P>L7;2e%ku%auGP`m?hWl5 z)RDibU6BHlYV9^!)@ro7tpKmp^8CToX?vE#q+Tnb&R3H*fR@p_+FWWY-_x#l0@th^ ze+68d_D?EC?rV>F0oShGO+QtK){|=OZtV}Wmj*8DNJK5cv=;19HWsJ-}5 zd;0>y_G`bL3+|D2{UVqQYP-5XhP0l4fedS{cLOk@O{X1UOe>fH;ZyB>S{cT*EIx$K zw03mlGoekSykJthnO?z^HnasM(^^L==X@MJcR;t@@fam8{*L~Szy&z|M)NPwao(F4 z#16;btl@g6VJQ}1HG z<0Co^jCM4?11`?-P#L%bj($8iJM5Ud8eWb#E}e})M;*`7s(;M!dFlurcf30aU6SK1 zM~phdu`m>XOh^74xXyBX`Vc1Bj`g$ya{bZlP^T&d%xW6)i3>`?-K)iGHG z-A%{k*5Ilg)wJZ@b}U~CuEDYBSHSN(7SNGFyW?egXC00QUWBgGv5e}tF2^K-~i z)|>d2+jvT;Y&O<4Lkh5n4N z+npAjhZkQbM=AhzIu+7o#9dBleux+3be03&2`pz2BlbwEwgGq|h_FAx4@v)E}D z^;ph1y|xyB^G4cZKJKO(Mb{vleMeo6+)9L-Y7;cwS0~J)=PDN=D_Bp-W4Zs5@CiS5oIz6F=X}^=c1muxZz-H(M zotAAw*dZqg9r+A9T{;EJ5hwFP=ti9m($h{jy-L}|q|>_;Xv%4w0A$)pR0b0t=VO#P z`Z=$mhMT{0bw6|g&K7TBEP>AZ9z(dpIfc&Ef}GFU!ZO(T=T+cBoW)F#Q0Fh{iFP}` zHVhK(%vgsfig2!=8X?j-`#QKNXFbiTz0N;<0F!8EKJCmg&M9929_xJVCy+SjOZ1P{ z9&moQ0pzgr70OhPILFb|ucOXi(u+Li971(fn)9{>kaXuGMKH;5PFoHx)A>9V2RY6= z@jK2s;jGXBP~bd5OJ1S#I?COOoLwjdIPIJ@1j`cVssfO+&Ua~tEptAW3v$U>ONm*z z^Yu@lyX?GvHsDvCyHLlZ;MLAwZ--@#^QWC4wa%Fd0Mt3ZOM6be^K=%tTh1Hk zkl?m+=q8vnIJ?XMX>?|7g|NkWb0jQVovTX0bvZZEjgW5Vwjh}FJIDSD?vZm#J4{BL zlc-@c>g;h1mQS2lJ%sBqXE*XW<^1(8FqwA#U4W4VxHx-)1iD<#0U*TX7dmncb$Nr< ztVox6uYrql+0+iQ*QK9!(S0sO=i&2!i_>@TdC=t~7w~wOGaev^Tz)Qv@UTlj1GpnD zUu1$i>M|M&a?E8s4Y*{N1A%az;&Of*gGhDJJqOomE>(17kmGW94!9F8F5xgauG8swxl0l?t1i2IqXn+Q<=u-2ROwPgk5c8L zr?l{z%S+$GKkyQE~}`o_{7EGTa0DQrI`ucQW!J@Y zP<+jG-U-03yC$&Uy3X}i8+fUA{qSvYO|Aiy65n-wAOpGYx}PekcGsu0r1!dZQ%9oD zbyx$}1Fp<_AcL;o+rZhFYZ6taPhImr0ypmZcrJ9$Tp84aopN3DJK)owvt(tmyd);#0#*hxV zMbedk1h@DN5GJ~rn-Jl!8><)Oh}*$$0Z(!JfU2`pw-+*DlIAu+yG6Pij|*Lfo7*|~ zJn6PF2D(#jZ`MFp~Qx#3NJg|D`{um?f%^saC_WS>HfrC z_w_XReeRogV-N@2^)&4dy6;*8?ufgdR_LSde}4d6s{3afK+@c8JrN<--E9ltdG5vYf2f^@l`q}G48dlUV{eeP-0jd|eS{R6m%?p|Mk^trZe+8>799oNF-iFBO&q3(^e)Ko_jbrW4AYr;m0ept*)>4%fp;Y_t87>vR}9VPehN_eR~6zF}lsvM>(K- z?>`6+>dNVz#p_;K0q&5_Is;sSF61D%BRUJ}m>t!HT?OEnF2{&L9M`$foKDg`zYq~J zbce4&n5i?Y2g%a)bON5O8=#Zy99{o4;7;k-bc~d%>-56_^K|EKfaL4kE<#wKi=y@O zv@Vst`7^p6n!d%lo>}lxqAQ{V>#QzvE?i&GWz*%4i#lU2gr&Og+QF6SRuuqnN%vhU zxC-6UL69rDWDcTV)xAqMXe)KCzrtCSZq*zJZ|L5jbD5jE5}GU3I$LVk)abT3g4F6d z55lrh*H6a~cXZ#i0N$i?IgaReb=T-*D1NkhtDpZnAZ7j z-5HuIJvx;ud_K~7?T7HOPWcl|Ms=Id!1WVdzB9;n{rP!-`|2yHxbxGOQ~Btxf8Y(k z4*ehWc6aJiJ;3eKr_oL3ApN#O5QgdJ9tRh$Uq=OhguWmLBvOBou2@9rBmLoXzy1&% zLPqPqF~N0=o~45@7AD~0^nH|u#_ONa5!xaBD^!^#=%eXfC+f#;0e4v6Jq_-d-a8E@ z$Mu>+5GLs#P)U=l52LIkMW6KuT)IA--b998_7b>EJ(CBLr7w60lC9r(7?vmX3#;Mf zl>YNJ_{`NCDNE1Ok0d~suYZ9`nqvL@Y;Yy|0LrD#>W{t-_&L25ZD8m17Jq>&)wk03 zQl@7IL3m03lsXIL`XNe!FYBvmy}qhH;)`)s>W8T@QKkPn47zLjy%WC6O>0)_)%ZZbY9!MfRBfAA69e`pt>(GOqWcrR15ucRRdH=>MU{ zgs;cI9uPl|D%!yOJ>FXn65uhH-b=7Y9s}?YkAqYxhI(l40UqhmzY3#{@`$4fdauV_ z$~faZbX%c2;L+0q++mM{lw2P1@S}u0#l!UvkW`P*lhCDkZ2l5ly2o?%0AzS9wuQ5k z9$!l#JmqnfO8gR!kfYF@^@tmXmvbJql=7eVxH*JCr5;~WHdE&D;4c6!c@#W^Ww}S` zZJ1o~NTU<}s~&Oj;3_>PJVB~F*3%Yw!=r*qjGG?4FGE=Ev3D(mH6B;!zD>QyJxWJz zd7QrjXSY3$QH{{x@t6mbW{=(lz_oZRqiZ*<9%WQ9w0W%i4}<9N_=cJRogN(W(&aI4 z9xS^(-p+$%pGV_+AP+n~dgysU=F z6OS2m*Kf?@B`TAida!8IANQ!CpK;Q|yau=_k4;OVoAxMp4xEqSS8A^K8G2LUp zK8*kapNgMA!?ORkQw;X4(1jYx>E_CA!;H6}+hY)Z1uo2BMJI)khUKL&i86fr33Pi6 zs{`S4pJB~42=^PF`vF6WHLN)a%Q!<0t%C;)eWzh^(BMXch&L?z8_p69g|C7;Y`8}o z?GeK~8p~0`;j`e58NM8cvt+|Ri(#2!xJSQHs$ubmh?iz?-VH9@V7v@nmLZ-#{cJ-I zW$8JFrb3VthO7@jP8!Vrz$DLbfbM|g8~iOGEHLzO04OxLybocKp`Xry&KZWOhCOdE zQ*nO5AUume7YzZ!0F)X`&EU!n_pZV6vO!7TV1;4TE6`mrRIdQJYA}2V_%%aADB#x( z%fj#sHw+ruXm1)eR6tj47`+KxoncQcNWEd+BIs@z8arWm+fdO6lLkZJo8X!ZrBp%R zHMng7xn~G@3P7{LoxZ^qgXNnb_YGU9XlgfXr29P`hA&buh)%=68VI`#pHj!O*I=Ti zVV~j3M(7?GsyZ;ThlZj$IO{iDr*vf45c~wrMhrjE3mG+hM2XoG!yVe=#tkq33TMv@ zb<`}LFoe;~s!79EYOeTreiIDbcF%}*5MR%;dc^bd{3r&xU7l;VK^Wxugs$rZdkz-B zONi$zYV<^S#{CZ0k)GwVp^NhThL)1Oo)xrLMSC8g-!{hcvvuGOd8P*fkl=a6A0~;O zUuJ_G_N=1e9`oEo^Y*xBd=k8*d%iaeF2l1h6}U{#xvSwN%kzWRL9#vf?}nE=&(E3w z&-cuG6{Og+JP^7P&#$NkD)k(xhp^1^M|$N~J>7Bob+E>28(N-24s^8AykvuV%i*?{{PgF1loGnSbF@HdL-)LEoK>nItDG+tRnFWFd_2C~Bjvlpvy4Y(EfhXxFZ>ZKV>Yh11{J2AQN1kae&r|d}EUoT#<2X z26U&5t=9oKV|4a{u-LebQt%SvZJILYjVIm(;DYgAx^QvP_>|5ON{!)41S&JK@&LbV z%qRz`Fb+~D_=<7-Ymlo(RTprT#+T`o=DM-$4>-GFY^Ot)o5qjN!KB(4o34(yRn0c ziZ0_?CP=q&4^6xtEK*o%|w5vTe&Nu**3FBQVUMG!>^DxdSWB3QqO&jk~mgnO& zMN6!omzjFM{$8)`g)qQNO^a%vm+VKtcX%aI`55Zu^$Wc0_ELTc_#Ur6nxG5wI=u&6 zq*vrEc!}~Fj|Oh9*D^75`@Hs1_7vk)J{u;nUe7;(Nu1YB>bD;7>S%}0!(Q+9!19RK z*)Ks-yrjXeWXlX-~X%F9DhKnxqcMl$Ymw08D$Wpl!m(6rBjp&$O66Fn`mgSLv0T#PJ}3CanP` zA*N-|10HHxM^C%oQ4yg30_b zERUNOP=7wjrR&oRZ* z)^o~qlq#@Xla8w7JkwAu!seS2D2FaE{m~2EX_IX#264u8DhhyN(}uU;y2PaHh48G& zOmpRe=_D2P7ft`tJ&01%%6YIXGwGH=cgZxn7^K4V+&2(jG0ms*m8&M-Qy`V5H1s1rq?J3t2Z5`9_}sE+ib*ZG_h&8cT7W0;F?V5P#Dj;YdT6d z3htTGRsz>*s&a;|&9u}6;eC_#5lFjf9;F-|rcbGR&}~Yj%B{!bOHH+2(=$39?=u}M z0{6gF+5yW)rWgJL?y>0<4Sv9sN>#z2>9vC(L#F5Glx@^xBZuV^Q*a>2m}x~Z;7?8W z>G*ft6ik`FkJ*<}qwVH{w1WAX|BV6XXMW0qF3{XCjj`-7uXr0|r#WU9eC{&;{t7HZ z%%9OK4>dQ_XS&<`JoP8`m~}Z2MwnIS5HHePkqBXw*`99N?=^SKgfPymqrTJuvoCFY z2hA3ZAo1oG=~Des^Kxo#9y8nD2RUxOZbZCv^J*0$WSArV11{72Lo&!IGeZQET=No| z#d+p(>K&djucS??*nIv4kP`C-y4F`}7B2;$%-ly``Bn4pl^8^&x%W?yYV&yq=xWUN zQQ&ILAJUcEI2=9ea6dB?m`g#k91=Q@JB zYko+n*gdn01%%D!k2e9{Vt!1YVyiiQD{yUQ)(PnDo3EaPWxM%c3%Cw*G&Ozu%(B_A zd|4JY5B>;CA8(J_ zz-{;bgc>jY-gZa91$Zx+2`mC~s%VU-o*>q#o`*Z(k}HW4u4!g0QjP>92u{^A4nD@d5Ao^p9s8@)pziZG!hB z2LKYi*9;)gVecJL5FYd1KyA?D-i2>KnB=``3Aki$!OyTv^Zsiie5QNPdkm7{J@`3< zncjm=7*dY+Kh)Ye;T=ZF*GX@yui@;JcN%?X`QCCzcq#B6+5%GOeUHB6BJXvyMizT( z3Lq@;=Kc+5XT4vfCH9>6?NN}6-jlTHmwGRzEU3);`_&L$@;*b0YK3>__lS4J`_LO8 zSG`}R9Hr9xJ$j;Q@1GOkv&K7$HpN=+rG%^VX3?Hn@4Y=4CJo-3X?yt}Z|?yfRnhj1 z=j_=%n_jT@1w|7|$fiIjmXr;Nq_LYoP~c=Y1)?Fr6vRhGkluSo5kgg(pcDZ?DFT9k zC`C{d5Tz&ziXaI8-#w@7IVT&R_r1RF`+G^|%*;J~=I%55P0yjkksa@Oocz{K^xX0f z62I;F%pfFw-_v#u!4Eyt523wNJ^O!+ywg256Wu)1^U29*@28$Agv)>Kx!aA{xt`65 zpFiKTjtt>;@-Bwp{?^fD4}_AFcm$bWiXSd8FS&v2q#w|ma|03ZX!Pid|PiyN{5 zIYf+_huBbYB8m9J#5%G94;PC`n>|Y0^DPobi(?{D%NTLTCDbxj+`bKojktvOI*1KAhX3MNiv-y_8@!l zeDUfJ2C_J zuUCoDVTipiw%mYVwb<)264!|RN22u)#DtS5uucsA8F}l)SN=jR8^q2Rk@%6A5C@Qr zqKin@$Kt$HByJHOAA{Id@iP)lJ`w*&LuH?eM;Xs2K2FlaPVvGqRQ8#e-T}cbF^W$3 zZgD$Z-`y*^iQ(BNuKpOszYx<&7}+mcN1&ktVyhY?9un_K2FO>UFbToe;#WkS4vX7K zHFrdOoRI%F;xro3aq&O`+B+f6oQU9C@mJ!uzZ37QMBWeLUq7PwDY5HRG<#aS^)`y1 z5zi6_|D$*pk+Yx0{FR8E6AzMp_q_P^QWU=+S_+YPQEa~pvCCp~27)W%FXgD^53zkC zcz2zDX9P%SGedk%**(~Po6{76AA~?rkz&!9_fju z7Y^l6U`AnE>h;XR)n6fRPGJrOZxud9M8B>upV+Mhg_j;coeK*CNX}VQcxf(zrG@WK zN0;6$48IG(s={J&CVjtfhy!)5E<7<4wX7+e`#g%TE$nv(^41j|q^+^Ju;&_pd|cRR zF>2XT_!EhiTMM`LKzrK?PiF#Td*K@sQD9HulYb%hdEsSJuyJa<~a91g{Wn``#j;Z3GU*15KMM2YlR7!;_gFAi>dDLmjE)&-MkI*-f{m)Qu#dh zdm~ZHVt3|4NL=D>LtR?oZvG(>SGq5dQQ|{)w=Kw9>;8EfnqBAaLqh#}caPyn+~~eW zPCuL614*0qse5k)Er2`oH-Kz+U*Hhi;l4-<{<(YW7>r@BJC~TReeR~iP}vvmew`5< zb+;ksk7Mpb#D9L{e&|KSj=Sp)BX+`FeF3pk?$wW=z0>Y1QHY&!KeQj^esq_F0pzUv zEfSV5x<`?){EPb(AdOO(6nzU@KcU+znFNWA90cMQtiaPOXs zs&Bd%1Cv?)aZezA;+DGw36q07$B5Xx=_xNq)q_2^LJ&6e z^gKfQWt3+y36o(sh-b?yPxLS z+#QKCJ=^`z`YccEB|y&h%>59Fb3E_NMYD4~`~E}=?|6Rx3c);2CSlC^o;u=_mv}1s zA#tha{s1Jd^sFTWvC1=qq|f&~YwtsBwdX&i8T!!U7>yRzdioJLTj%+hgu?Zn-ZucU z(Njrk!cCqb{-|uT=Y$=JAA3fu1LP;3?!-EL>SAmk@mAiO)ivUwaa0 zdJcO&JcQtwXFMUDZ#?EJD0kd*_HWd3!qct-iKjew(pEg}X>}TjXFS8LNc_=rllZ!y zJr%T<&Ux}Zh@JO*{3P-&cz&jr4u1FiNS2Yyo}EqvS3Lf(Gy9oQee-FvQ6_OC8A03M zPd2Tmnyf}j(vNM?ztpEbK!-ZsaTn5`4Wq7RT_pp4Zt-mVbzLLJo-RDm#OGE&ioZP; z-jBb957T}x>P2NsOKH27z4R_p%D=o5byhz098#+OegiGmPM-vr{zY~8`^Hzq`VZz3 z9z*lPxP3nXb|klu)aPTk?zD1~xKuKYOy%N8Z$6t_ZA^Vl<(iw@r zbIVTw@(<3IgWxajPa+pLxpCC9+uRcB?qJigS|koLH5U+!H0>e9=@`@GDTs|XJu(N7 zlT9NZM6=UO-|a_irs;)P1anLeP66ax(=HMV>P+DW0J6yB7>w4Jn!Zj&@nxncqID}x zaeEQ0F^!`$y4G~Wg8~~&yT&1Llj#f%Y>R2=C?sw(op=bf>@>BUiP&ya%ZZ5XHT{S9 zpZ%t3v{??BekWe+u<2#e9Un90JO{`Vrkp&~a?+I12f=AmQC}1|YdSX_({s*r@_B$< zG(ABU-%F-X(h<92I!k7ntETTu5L`F?LDcl7sohk7+%|1bM%9D)NAE&x7$4dd!ARao ze*0tiW1%QMp5I5PZ!&*|_UttN5FyE#d=Fxq=kQ{Hn7ER`S)0P+$!|;0F{~%Lfu2+`t$1LuH%zbtIH*;ls-?qHX*`!_m*3d^#Pn z-F(y<#P;%giTm2mhn_`&L;Q?Kk$9Nj{02ZED)I$1VP^Y0e|@+`kS z6b+r@TU|wE7x~3a0CI_+T7%#UKbRDmSNWrZ5M1YY#Db~^TVftY;xJ2(E{Kh^Z2S(z$5`%cj&kGii)pB3vSkY4tZ9~`(FkT*UOW%T zITnsK-(1V+XcVZk+@KA;$g=1n&5I?D_U-4MT%kLu2RtW2{SVqriCU zEB*kPY#sFu3QV&eUW~k%)?K@hH^*B004kenU2+^jowaTV+FNAZ7mET*t;>j$SZ3|^ z3}P#-0ujnJ)*cfOthENchK4p+j}tE6WW7N=;}+|ZpHSyEtEoToc3L04j=bI0O0s3{ zwKk4Ku-}^PkA@Ce*X;r1Ve9OzNIYf@r`AtcA3TWyC#{FZql2fd%}hua9!Ko9|M@pj zZg4>9Er1LQxa2`FGN9u;gehnE{jUM7cQuMa0I; z4H&o{{j3W({|6Em1uTCI1(pVMu0-Cl0Ou?OD+A7u5VR)XEUDVo25kNmi5mjqo}zgP zSV1!BmVhHm(6em;Z_YroI|ClS6S3U^Ib)HxH{h332=)goia_3>fDTz`=y1UCUWgqF zC^?C|69IpdP4Z;GTsnBC1Iq70@v{MoY2nTV+$5isivi^!C~zsD%f|?=1S}%U?bU#o zWR$xe@E_V>Hv^vRiM-nZt!j`rBJiR0C_X;$VcNoz0t?$CZ${unLcg;EFAYTO?Z9rN zW1AN^Z7^aB0`q&Jz~Vq>TNHRV@JHGxs{${~M{G^t8sh8M2KFM9w;?c_*paP)!%3sD zEl?;z>$?IMk_G7Vz~_kF`XVs34v7Z?+kB7M;lSP3P~ceL)8wCbBJjfp(B8?wh2+t7 zCeTfqf}a9kd<(&^fhELU{2q9)2o3!a=zkt{{uS7c1kS$$(@8J+PvG68YZ(}HoRHk$ zpdp6M#Pej0+k~!qdc{A|9QX67)U~km*5>J5g>{Q0W_py%n^9tmN+m z&A$r;>Vm@dqGyYO8Z`mP(xAR%=~xzYehKnc2Av|+(CVN$%&rr(jubxYgUUN0*cepx z4GMf5^cao(lc3BH)VV$AS7K^D3)(<5U{6ryXcXTUbdKoXfuO~&B6cWf1WBQXgHlO5 zb}Z;rEef0ndVLULCxedPiM&%mg;egxpj&j~<$REt7?)py&Jln8TTq4tc~^pdA${Mq zpia|J^^KtNuh89FK?$FuvO&RT$D!QN;9kp+HzGJc0Kw?szi%NJ7reU#f=R*uA${M} z;5SIRn-LsJO7YpjKNDJ;8yxo(s-7SG(rmQ1FnG;c6j%~`?>xko1wU{Jv6aE6+ap*V zY`+4?4}%+%cf^L^&7YvoO~E&sqLwYe|0E*zY4G>7^>+s65tF|=csNPtdxQO_qxk;d z;2#kj3JxKM_QS#HV-Oq*{;LVfod`C+hv56*FGwA8I`~KsI(RlXlXmO5U<;ADUxM## zj=bN3e{6<2uLS=}*yd{Rv%ewn@8GTPBJrPKzg9>b7}Cp**x-=wh&37>63`zYqe7<8 z_{N5`+K1SLkZ2O!ri4rlM}g@f88Jwl6|%S*4ZRhzei+Kl3mFuJU_r=+LjYMEa_TZb z-VL#C1jvezecjRd_e0i`GU$U4_XSkFE<_xNyp18FdLZ_3NdF#)eG+o$0D|oya|m_q z3Ry%l{^udbkE60LLV90E>|jVA()WEGk`fQdqag>nBk_1hV_K^3LXHuhI2CecIATAB zTungmbI8?6NW2iTn_`zjKHY%=mqRLS2>uM2`zeBJA@`g>Wj8~3()8R8sq>(PH$w;C z!k~wSCM6*@GW34JZev0N<4|CH=%~>sFe!9L4)Ugju3nDBnW0-VG0ZukA>`OKH?%2D zPhDtRqS}i>FP{O(($EOvlb40QPUL!3=uXn*t_l5!_^h>|gA$OqA+%aRY;&lUhPgGg z8O_AD(9YxxuroA&GFsRZy5M=l_JyXC#_d4pCL(8tLeKjl??`C7D&&0=`r1xZ{cY%< z@rZpNTJ{kj&xHDu8uh2pkBC({AG(?r=a@1O`DPb20`OgRoy$`|c zFzY5%_I6m8IFy?gmNFQzg<&N$$xFi8e})de7ZyXRniXNIDQ|UHd27Tz3_IkHV0~B{ zItUxXK2Aa2matblq0UdkrjJKoc7%OYirB8OgyzWG8#ZbxV*A71_yTo)8FnfO?R_2g z@)M}#SlEcIsO3c1w7$qY8Mb={AWwzGEJWhjutYNTo(sF5D!Ul=XDniu!d72K@hf3% zMO1w??1R@(?s{0sN(48m8oDMY4)9}Piev%=4Z$oV?Zts|CVU+ zs_@@P@cSUV4@tu7!k0`z;z!{#-UsC7@G-3sd=kE<4f3{!e@4skS@^f)roJcq&<^B% z5#E8$=E3l%NKE=Fy!IEgek6R-4*)qHUYmlv@4_p{bnrvCbt4)&6P`%UlRt+aYmC^1 z@F>E`zlKj4jo|n2nOUgw&+viW0CFw7oD}po!WVrE$Xnq(=vcfN@iXn#p%KfEp@k6< zeza7hBQ6Ic7#}h2OXN+8_@^I&sS)S1(3cqz!C?sIM2vkJEzFHDSE0`N5kHgiVPV9U zHvqXb;>A}HEQ=UVQu4}(dkPS&j__ZJa%&?l(AL-x@%C;cZi-mk4Uk(R%x1K=E#jvY z0NEMQiNv|x5uX<$Z*Rosq|`YOaZfjZ9E$ku9+W#Ak#P{gv55Yikoawc={V~AK4Lks zRHq}t`y=mc#KW1W<$T2R-y!jrh(OYL{T9KK8^)E0?o$x_718+yApedyl7?pgiI|ar z#DR?(6EPjq=qoyn!yCO%nz~VqJT%&|jke!L;>1Rd50E&e(LWOrOmCDx9`~~v{Y;F@ z+l}_^2FScdSrw>rK_fezs>O{Oe~-NP8h!H|YFW|fn@K45exo1gjD67Pl?4`AN%qk( z6m8wC47qIvr=ilsUS|N(_5w+B?J7t=+x{csygGFJ0N+mrI|2XHHWI;}{xlg?Khu;r zj-+q4;XAqYOMpz7O?s!)ZNxk{s#c=5w5Bom+p!UcI@4=Q@SVB6JuFO2P1jxo++c3h z0Tdd}o&OEFqq)o5(V=l1_qd-7l}YSPtH((mjOlGcTXF4gqg;F=!d$KQiq!vJq}pmX z_gSBYx-G`v+M7QjrQg$8X!n(Dk~aU7 zPBw+t7G~i4_0PuP?|@~CQ1*>Rn^4<8u7*zWAnvYclo-Ng5qTNP-QI!NaIUBmni|2~ zO9H@1t{owkQQQa3kvE#VaTvwNa>Z199M`iPdE>dZbaEzebu@s9ToTz2r*NN>Dq<@4 z4sE{a+*M+?W^jKkMS+=|gSONh&OZPJ-r|~(pU690U(yKA<5EZ{T)>%0U%QZ-M_6hJ zw=fF9Qm&AMie=nUn(^gaQ{qNfa_`c{T*b{lj^gigrTY+D!&S!u@&m5iiON3Y-X|7( z9rrxBC9UVuXqh)~!J|=pBUer=&L%F4P|#-XBI&WWaJ35%Y~{MYf;vCp`jh0goolxl zi95Khq+j}syZ05uc5!zUA#XSL7YWjPxt+xb_Hn%nQRe|}+hYh0a`QW&z#(quD8#k$8$*as>rWbD^XJ`jJ~l zSpR44d*ZgwajuTYyT~Qd7=GcNAPwCm?qCJV{mx~4jbZ-5ot=mRe{!|N2>!)w&PH&Z zJ4`-Re{*4n5&XlY-i-pcxSamT8)&+*46!#&pDsk+P*Wdr0vTrNv=p(CreRkBGRkBn z)4>>1;83(M*7PlztH+t{O+jpe>1|R{PBgV4jl?9=&gJORRMW-Q$eU)mgM2z>n7(@) z!EDns=-Z*ST^NkKw@m?{jqF%A=|Jk4xRwtBsP*lY_)h=s623d#yAgfpGML1oqP?>* z{_?%=;P1=bSK;qq?v5P@CUE^0p~O;d$OZtf<1WBb#%i_fSdE0${%>6Y)Ea*%gRnpR z{{(N(vd!BG?FVm3M0fAJ_W@LKR{_~M9^7#e@DB~&j=~Q=M*`#{ZHfK0O(mSrSh$A2 zkrM}_RMgIJz%<)V=d1bT2awVt1XWvFt{;V(?FX75CBDPAsI$#q=aJHG!e!L+2 z>Y18ZD3#KKOtxuTSB5+b!a+j?spHFkzPr>3%RdF5Iz~i#STS*A>0)j@hI-{9>hj- zf4+lY47cqCfK1>DXwfHg0Z@9gNwRJrhOYUTXjGF_fs-Po&Pqi?%~q1Vvehv%dfE>b zpsNWx2-&u2L6T?tYc#;8EF`66pW>08dxSKd?l3}|MOz4_WOE!+dcXV|{+4&54O9K< zPJF-mL|gnF#=TRFc^SdIOQz_N+>JQIMsfQVqOQ@LqdfvxM1~@7EVu4$6duRjOUQdX zcW@CZpTJEmL@<%7Bemir?$Ja9lez0pA#nV!9=cLHL97!`FBQa9#=6P6}`vhCL{4fE@cG@tmUGwA=t}JK95?y;fAb5S5I-x zUqIEDx!)F}zzuE_aSdZk3&_{zT~h%GCm)!4TtI-gp)5#5B5UwZ%w!N zqRvyMpUB31(ew)`?yi`+e}v#~(_f^48P4A>LNJBT?+nO!{ErWzvK9PCpCeeuKivq4 z8~9)cVjKC>q&M2kFaHgd?c)Oq(fUz7oEZDleAfa5zw%wbMcys`FnQ08G6xb9H{bl) zER?G=fAchISzs>nL!GP51;YWd-OTx;FFVXlyCJsI++rjkKQm8x2avnW)kG@~ns;pg z$d~3T-y`q1x$D=6oiHDzQ}&bj+b)RxVgBSG@&;N0+9Ec{^1wj!Y=q^tKhe-g%eNg- z^)$;U(h*FzRFXaEZA&+@v(2@9OtfU4CA&Q;n{RplZWOPxI7o_JUru-h z%M&CvEw=O_Lb${-b~R#4EfI4Nd)KlDcofgn_WK6~mRSZ6QeSR)m{86N%aAvbxYBZ- z93xg)W)P?RzU6m9U#l(M_Mq7{mcHqz>;ubdq`CRf5*~@XwRlGi#n)Ln5vE^n37`YK z0YW%pA6eq|qrgVXFv7)~EPv3p*=%|00%9Lq?tdMSTPzQH5Nx#+jzKM-SjvftZ@1LF zjp94-(;P_Li61&bEuUG2+K{))GMrS3yDbBkAlPHsbP$Q3TdKz**lT&wiE{faZ;@#9 zg{A8=2=-exzKMPwu!J-P$U)2a%Simva<>(Uhb+UkBKDP~AFb`zmTqsMp~Du(9t1}$ z31^UZ)FNz0?3m>)QrmxHX-8(d3 zZ@%#TH%P1#df1V;K#ViE%+zG9IL0|j;qBgP_ckFeK<#LtD{zoPhFVW9)%_6aN2BKSi1S^(sJ z;XZ%l9S}?hk$6y;OZKEM1+gkY2LSSeP=kVXTPQKSDm?ms`S$XHopNP*{m(2U@%NW6*=FeMu|u zrgd%|g2C2pbnu2)TMkEop;mDPf??LsGYE!TONi)=unwgg(Ic(-UjTBH^>4D9jJ9r@ zkE+L5i(4Uatkp)W!Z>T&j);x7o>`4xg7sJ09Fn>n1`$ldVByqncvnNYhtm z9Uvlafpt15F&0_3?L&dZ)@M2aa*6eI6Ix$t9We}$?^>4!A@4owJ)M!a%z9TH5|>*~ zeu{D{tS{vuSZV!=HuNg%^7F`h-}*US|6gtGb{L6ktds9Y>;vm?veSHMy+s;|wN}$G zlv`(wnvY<;^&06>H(2+c0?0?!KKCQoXnpn(l-p!IJQ#Iuww67HavxjMXQ99r>;9Q2 zu+^F;0`e2<0kU&{YQ0X#ahtW|N#t#}J`#%JJFKM-p}0z@Q9YST@AEKkutH_DmswWEZ{rbcMfEmf{{zYa={j;Ybxlvcb&v)Ii z0+rqSiv^8DzE9?`*eA2mnbyrn1=cp>e&lvo5QFch_ufQ$@^zBp9B&eePp>7}sMA35 zwdsDjM&{*k3;YWiXSp50D14QJF`Pt(DBA0T zOqU1=4>$cfNUrP=$E(P;&5K2H;}XK`O+WkuDa|8D6XhtJjK3KVKqj|5TluWal0VXu z^aYKJi8M9&=R1kf5$18P&bRmpzjB!$`uPdRO_yIlFp>XxJ32p&Z;_546q&>4=A!HK z_<7yrKDTlgp|KeMv#8LXOG?l9WsT5en;-5$dg97zbh=%{LVUM>dMhgGFsvOKeX@uS z*)tmQ)q0c(E5_ZxT}8Ap+O<2_U{ii?X*-|Dc66Tt`w>e#lK)h+r)@kffP)+>R$v;1U=96Y9Ll9dC)? zAI@0@^ddB_6{FFO8!OsG%`4R#CIcvb)1i*zMSA^oj@(8`B*}9 zXZTps*8aplLj(JrPbvb)W&W}EQ2ZtzMq0Z`=3BiGOfgrG)^3`4`#lJzo15;Z_RJ9^ ztkjuZ#4s!|4?Bs5mYcIkm9olQU`FtP`PD-JS!)hVLwm=}O-Wtzt@%+BTz@d%pO3^J z%{wQdy>sS1@1XcE=8Gh~{cdjD26=y)FYg27O|w5a8~kH_dI*Z&GVhs(yxV3W5akA1 zrd~q1sg`p=2&P#g$o#Ox(r7Bmt+4!TMrH3?I&DGmHI{&QfUL9ho`K>UEFm)ix!H1t zVp}cogmgc(Sk|N5PD|=zNZf@dq7dw{xPC?Oh2_&DC~(jcO}fS}EsyP#kdM&h^8}dgHhiTe~^l&*$;2xyTySQ(d+2X??ELoD@o>woiG4@?v6CU8;h%znZo&Q*un-sa-A{tG&=d~dqz zWnhJ$O#24{Q~fvOu%7Gw3_7uuOWA_DcW?tqUfsjp@r^vPh)a{ugS$d(=v{gf4|-ZW z{u~OtaGLB-tGVv?AZfknk#`Z>VCu0Dv5!o)L4eq3+VL6c+GILlLu|9D*E0b5*px!HjSx0AMQo%ndp;mX2{)cYxzWNIawr-joS2T-SfP~kcH@K# z0e~DY{6?()1mV;Eh)ooZ4hQ5UA*?k(CJV!@$eSVv4pcT(@c#pO(}cCpB5}HK?Ia4! z7Jh6D$U0%r>xjK4e7qW6+9(XX6ObPZ+%2@XUHGs9U1)SKLqC8%I9cXbAGMsQb2!7_#mAW>ryH?0-&rgI5|f97zFNa$a| zEi6Z{nrlKWuH^=h^!gF^*+j&)a6P&pZ#(zO-zdJDJ5h)3f5F|Ch}a?SdN;(5a$Bb$ zIL3Xj0tLR~PTY&wN$%Z`Q0@%Zp)VRb2PK`{Ytts;K0;o62(WkU?kZEf&UM67Iy=+! zyUOxVd7gu?ZkH2u%DX0~<8MLUEDWSu(te~rcY|2N=YOLsn%#NANBF7w*8{UU1+6H_`JZa;v+(h`%*FG2Q*18IJE)hm-HfAa21LM22%G??=v9 zF7Or_oyrxH5`6}@mlR#Ixi+7op0~Mo9zvz>aDNcTK9Ac(Dwp}(ua$@`;2xcf$`*1x zNo%o)TlXr072Lhw0c4!ziHV54XX)Pr!G25fHz;rbj}aqw&~o%QH1wtAVGoKQwp46J z?1<$W5s#ymg%@RQN2q}FZMl21fZTg-q~iPDyT}lCUvsir1f_>NkOkwB z)cWtOG9qc*>B+c@rqY4X-_2`OJz+?1d&+{g zlXkn1o;>?Wd_UWx0zK$d*9|F|R|sKdUEq2Wolxed7IJdS@ZhU!C?D(noTT@}*%uen&nMo+yk3VC3a$ zd`HbC-^ON-jY4J3@2Bq;H%Ss{d6iUk(K9vz(sd`{?{3epMsD%OnaHg<@*w`!HllO; zM$;(NGl+YdDCBhR+8+oOa<4W*;s!4LY1F%mOK64I0d5W~y^lB>;hOE-=cGpaf;&b$!%>cg2=&zo z(rsMioZu%|OV_y03COs~{d^t4KV0N57|JcK&w7B}=A6gTuYsn1Bjei_@Z<=!M zLf&B0@3ClXh^h8@1Vc^NpGV#>)4_+3H{8^Nq{0!Vb8S$3l&Mb##73KzKY+Y3rUTgT ztZD0B5^KWEzo3GMFVG)Lqh!|L%Dr#*tm6heyHg{DYTS*^JNpW#W zarZX1*cf|a%a>wX*7hxS*OYWeZp(_wijtNkZckbFvWlYe+TxP#ecVO8%PLCz{-0D{ z{Yv%!X~pGbp6`Zd$%Aj8V4uq3T1+q1(!W)o;ujkL)6|bnR=KOnP^Pw~tlSG<6I&x9 z@BefTe6F0g72T+{oo@i7tfHm_yQrqDvVx6~z=|p>O3QjRP(WK}tKzD%ekE1a4S*W! zZdG2=%U$$J1JJsPt1F8d0MpF0+`5Rhp}vbKBvw>b7yD7siqaxcp7Q7-92!5dxTMrw zTV5m9ywbO%I=VL|0V**0*;_mAhP1b8KQS>avl5?JPSIUaT_$}>ODTP;TLUXtR#H+@N~?)eQG{bay_6Q#T}2xW z>#FW;Voj3r2|%S60K^2S{R@*u!MZEHT;iq!M1z$!XjMsZ3H3#p zCU;F`pEBAPNT5B9omp91RmAqEQc68wY+K961FfoaiFp8FMv9*w{}Io%;ven6d!qS4 zub1){Oa=Um*PrEoHrWu`D15#fKM^S_xSE$AY1gDhlUB`R8Y75#qzS)gKr}yuuiMqN^S1}sGI{Vd;{f$H*MXlqAo^jxJet49#+TXXZ+vN4Cf}t4-^|8$ zvBmOH(R^1M{yrS{bj!wk=TbhQpjqQb8$ZH_c6f@F~F}nGqnS9!_{L`iF zA5Ce?Z+yeXpYSW-Px{&T=bvq5Z=S`MML*o6MNB4t&$Ca(wP^V$|8fU8=_yLGKZ2A3 zIVFW}WYed_^mvfJ+s4luFrg_wnSYl51!J1bNAoiVl=4B*O&*SE#eX`W1OF`-&0oU6 zj`&6M&7O7eKMg40TQBRz_vpYk@4)BV_*T&m*`c)tn6QnAJL6l@-Ij8fi_csrpha*^0CL8 zHRWHnnM|7W!Dgh#*H52p=J=xnIDQFMV+Gp%4r@36wdmre{KuwJ{v{h99?jR<_`v8V z@vl|;Cm-e`Y^@x8NHpI+nm;%oy7}Ys4rrp;0WJ6yrf7aO`nvCR+ar9Kjm^<~q@Bi; z^(#PCCab1%b?C${bYeX^@isbf5uKRAIL!%YHTuVU=EnMC;0QPM)R}G%O2%V zziMmIl%HvSmj4OmW|^b;A70fj;3?F35_t>ErA_%GRA(LP>?Kv|N=X)7=_xL!nCr++ zb#+Nf$rE!ct8Mm}e80Th3^CQwB_mbLuF3<#F3G6qTU#S$RljJ9jOlMr%g?k$`E|_A z$PrUBP(C9&HbKoqUWX1gLnbPY^2_U*;Sw{l^BmbJnNG2*Gb7I-rj}K^J>@0Es5d_| z%Fl&{Q*yG?GCGPm&QyofP;0$xR&Q2PiZe&dO3D@6MuM}hs%+tI5gi>JWn+;@ca+Tw zk!X`aY@*mDNlbEfbctdMjK-OiO2bJ?X8+`orpfrgI&KwNe<;b(MPp6|J$eW`=8hSG2%EPvc$jivd_GbI#re=w` zE=Q)AlG%x%vK?5dl*)>VlA;>##HEV4G*QW3}rq$t)r0cK}qNE;$of{y{9VOOxGGP0@vabjlW%W^Z)fF0Qwb)2zC zQf{t@9^^R1bWybVWyETBUS2_No-;o;RW}pbMTnCZ!H#W>vDzYI+tOqy?a@@Gq{kYY z%ujM+L#z8cufUa;lbh>ERo7V>v!RwaxrI1tA={ZI(w4GCMk=3T9=PacQ8qQ6oaAyu z`LQY1Ofcpai=Z4(X}|i0VT+gBHOss1E=Ol95IUb4Bf3gzJ$?W1fHu8z~+}$3$qj>(rTf__?Q?w{sAfY8K)k|H%6^hs?;Zl zQg4Bnl$Vp0ks_u!@>0^pf*faLUlFlsGY`wg&V;-Z<)6Bb=h1HT~A~4Z|wW z%g6%Wcctez^IS^DVuAY{Nls;@<%YD|!Fy!e7zWW06l+Yk-C`8nY;Vo#(3Xr*Py);H zOMw)XTH>kgC8jbGnj)qrW#&mO1GCZ2XaGbvfiy#el#LUwOt3fKTMW2dNp35qm-Xr` z&qhj$=15DZPe12A09dUlnK{_o3~y1zDS3GyrZT0MhvEZ*6{U>|?18ySb2?nsYigbdGLelxMg~HcB{jzATgNzt?x}+kChe%i{Xoz&W79HIBbk`0 z9EZqAw%ut-Si0@eM{R^3qHK?A5>Ow|Z(53&7n>($W+aPAnb`Ucj3+I_k(nyyWc%gj zbj8L5IwNY9@1n0{ni#v7Q(CGcNgZR*Cm=(8C#3|2FhM6|E{7o(WwvFULaT3pU=xs; zOXrj(o45fbC@HmziCu~n4obxx>~MC?0v*l(;>|XaA;yf!r_AX{N(GsA5hG}%YEYKs zl~mUxRTP6XB$w6rxssAnQ@|@YoUWv-+)Rh|Qyz#sj(>w>18U&(;T(eK`aFFs%TMg% z?*DRewe}P`vcQOC0l#B2v0l_ag<25SymHFe5o{xqxHd%MG-sEU>LPJTPMIHVpl&Y| zt6myDJ6EE@3K$q0Mj@RAsS;U~w?v9Fg-E5dK~2S#VTrR9?UBjOq?Arba1`X>D^D&I71ct^{wn;jruw+ZNUDdi$4O~<*!K;u-XuyZVo#(!!aAm5tc|VI&u6xp|Zm2>5TqXJt& zK@#2)E0HVL$XT+8r_fJ{+o+$Y9T~@_hS6OH;KB;}kSOW{$SY3f53#7cvbqF% z*`ivQ2>O*oQeqSkZ&gVR^v5Nzd^HH=N4q690f;h&BscuW2x6~}mUQz)`R*WUFC#akU!=f44c8On(gyNQIXJqw&6FQdyQUa3RRg2j2 zWX4jW!J>pQYN}ol-9<0emQ^)`Q|dveyb(01I+glu>=Q)lw$nkP zqbn;Z6KdOZ2a~Te@*?}zR7KhRGy*s4=)VfwDumQ4AWum#pOuuI0(loIdPAptK(L}R zy(aNl8or#M#8td4qYp#9%G4^ktWLJkjq(dLNf~+Fz(?i6JEP_mMJKE(=}CM64D{u( zZDoO^MPD}@Y-CwRue@(z`yUE4tO`YxA!C51oy7m0I3xAOhg3s`0(sh?l1gvO4z+h4 zSzI!6$ha{nH5D9;%SfoOb5vej1z|JFcA{6s6R)L6UW-j~zAG&We35Fi$!CXAQ9sif zWn@cAEV9=~gqNx|LU>nE#YWHp07K{r1KESpGSYH@lJbC;f#WgTkR;O z=Hw@5I&5|lHZP0AQ9rGS0r|uP?Id<>Bt8|MjB3@p-+NfQ^Bx{M4O0EdiCPoq0!M)hK7lhsj%yn>b$Zd z7|J5$FQ&n_L4~ixnNAqCE~ZFP=RGAoJ~JaL1CoROKsN}c+b=ZASRMaDDpq+HW~Fq3 zey+2qgmJN1sTp1DcFb;nduxg7BiiAEMb=KY#6x*IEHPn`%@zctBrGywG93-aBoxRK*JI&xCT)_E~ zsp;S6J%-JDqIcfOTEHeZR%0)SNdf>^aV8C^TQWb_Fq`$V*+eR)%9L!>TmTfKFiA|( zQO*qw6Z^)e_Svi1U_z2X@1Qb6aS{&nwKh_g1{F|qz%CH1v~*cgtLEmUVbVkd#>w`g zS1=gTF>5U`IWcW#nW{!195`Pxpl+K3HNpG~OoQPm z2vR$piWR|A%=pDsux1A&UmBhg=v-Sz-pwE!zyEiTifFk)WxF~h?(&@bTSJVNrJc6 zGCg32C%v01Ph@|xE7ReCDAOD!=jcpr!1YdtVnq8_V_S6%>_ZBqVs2|-)g{fBcDB_O zXIZjmg0ld6VF-xySEavXWRZxwbl|l>Y@HyM6C;|D4bdOUGnhiafWB($Q_`opq=qab zRnXqXw6Il|{imd|G_n~>iHd4(GZYk`b2xJxsVuv_?RCE-7~?XWok$-i2f98fy{Zq! zdPOsllIP5X-iyhKvV15L?)te>As-B+J@Nk^2GZW<|2hMysGqUDF+8{!Fv(C$w8>n~ zbQP|0x2pW=4CN=*CKuLGSavjDQ0ch(^evZ_#0<=lodj+SsE`GEuEGZZn`I(JlJOAi zb8<#1EJrrxiy$Qu>mp|7XW5$D>m z&BV=WrCf2O#tkP*a)1Dako_MBS7_WHC@OhBFp8@(BFuaxO(LTVUh$i-ALVvaj*Ps; zd_l0`Gvo!52umgikuw8~ZF)>P7$rtGNf>2nG2mh{IiT}~BvFj=^L3QtRn#s!fhe{_ zP&~3Kx?-eIzeuXZ(E#P`YN{g-nklwRG@ooP7(eF4c(I@^v_!b2AR{=Et6aH}Q8xGn zi_j-l!H-(3?i-0^&Cbcl&dkVmfT+4_-~#QbttkQajl_T}D$6R$ad!bDRh*_|zKRjT z3>VvM29(V3fa3TCMkyz=3$$qlAO{rTTB=HMg&<0^reTGF-CHwuIn%JTzG2vmF@Zzc z4$@!mJb5=15fz20*SAjF&!E~O$w13F$jS`Zhe<(4fAjihKm!HQfGr1_k;Nj$3o-vB z{gV5FV4c^jq-5&x3U<0I2CUtJUJ5VkiI^&0VF48?ZKVG7^I63ru|XOGjAphaY8><5 zWEeddtCS{|Ej-RD^L@jt>Ze}JW1i21JKW{il#(N~Uq&8glMKI9x}!5GkMwM8KIxnr zyxU}HL{a%OkFGQjqO2}yPH^~@wQxfe`$Dr`M!xsTu9C^?zW}O3kUB5nb=bP>|48(N#qx~W+%+JH=v%<7D#Dz&>tW9!pz&<21s)zij@dxRV~;jymhfK=ZE zcE}u^fidicl~(u|Su5gjiS(6V#1+c+WWv2pERcj$870YU4n?33_aV)(i7_&<8V*}x zul18qFIyTu5LQZVK8}bJzOzi}D;JX#CG2dIStuzhCm)nV4ip%9t2F?{T3~~NxCVZh zDT878V}x8DuH=y61i2&ph82eEo3fE*%J*NvSv!mi=$Id`5l8JLuaf&3yiwU@l(NG8g_|N~)?2njD;HxD3*XWw@WlVL%bFG+-4)C}OANb|lZ6LD)=6>71Vd z%cb!=z$X)~zi>}swmEWzFi1(UzGWem3zY>TXh0G`S{aK6^R<|i?aCl?JG^LZq}Z&# z^jh-ggB^lNQ1;3w&G!T2C_?d>1u;s@gQv2{)CkEA(QX?6F+@`1NLDgfue^@FOywro zOf>?v(FK~}1jQe{wO;C!HiP7649-DX7RD@UH6`Gbjk=I}+0ywS0}s?aF7PrU%&L-8 zKq{Q5Qd5~)M_F?wv&eomvGKC&xyD&X%l*n;>I!t64@Y%|_L)Pf3u{6%!a{YA$yA1@ z3)pv{e=)=1aMaf{#>uRccJZ``U6DYs3jdRH%UrwR%3^Sx(9^t3Xu=C@Z4CP|(SZL| zUVuo1mmOfoh?!RPD+!vY9fPdsOUh1?xMA#HpV1m;+0;p*W-7r@GZ9}wn)R3NN z`hsASt+EA-q^Qp5_W$|bgF%n&kXYRo0U|SQFPSJv#v4^0@{$TTlgmPqVV}ypvm{hW z!W;CxP}?QBVERbuR8KD}%Wg&>HeeA>sE+J z8_pnkyHXkNOiBs9J*^}fn3e2u=H+xvk@$CLJIP`l$pUOMTxyMm>V_^{i+w!RRW+3_ z7gb~PLBr#9h*YLKQN!D)3rLwVTCcx`DijSsamxUho;~x;;S;|kApxdHV_dYVP4N2F z=;xy$C}~E>(w>8@pONW^q?@+zC})#F-%(J7)Z%6~=0~iFitJa38^XBaZ0lEvt1Qis z0MPo!k$fOYE;Q~SG2H=!wd8Ud)3&vi5t7YoT7OBxUa^Mb#^Qd?WVncDr`d4(i7uR_ zy~^FR28aT z^XWxqS{_Gna7r+8#ti_mJ;AV{*E9x{qPc;g`mkyJ^If%cN!?Y}-+`+dHNC58rlG{Z zs{#rWqB!!)Zv7>tt(iZ+ioV!k0P4lEYus54v=< zD#)6uQaqd>ijnRX9=OYB*F#liL7izhMDpybvMKI_x^Q`mcw?r#C1nw_yhCy3kZiAv zz-g;j_C%af9o7I48NH<4WgA$vd+_9qt#Pt_wJB(Y+$oVi3O{E%fGga2C>JgaK zwhu5?iA+nFWGso*Ug`qQ8CB^fSJC;jF>*~nlVA~vq!GqQ&BmPq@cK2*lA;p$pep)g zP~f~Y;7XBnALIrS?Xpl}n)EA}z$oISePDV6{Ue zRF06$R7lr3MO-5(hei?iC|)dqQqj8+MN)LjnWW}s-mf_8Ug^5DR7OJTc5}L1xW{U| zWmE6ohS%lk|INjX*#D)gldqc_Uh4>>){@IRjm@j8fGZL?xIp1W_8I2ff^XTeMU?Mu zC*u}3ie*617j5xzv39TDrOX4+eE|JBsw{v8f-NNonRJoU>!gnRo2pIF3A32p_w8@p z9@j{=dagqC{m^Yy**GV$6tWW(yxsG%B>HP_o$r^F4W)B_HdN0zBh1uh0|u)tsnlV3WU zZnhe}PZZ=rL(^WG1SO@kT8w^5ZFu>}j!EcWN4zIy7vo_sC;3dl?=<0%@Cpeq<(Cgi6=ojOYRktqOBqt9?g>`}E-QdQXo(9&4sgt3e)t|C|gOsL| z+$Hr)tGaQRN`nnlmBN7n*gOIKNR}Yn!jhD0(1Fqzh)EtamGz=WV~3^x6WG~3p=FATz}BvP{; zGo66m50l6f%VSfbY;|e?x(TC^$t&NuV9ii{8pxuG9(uPG6(^-fYaQn;^^2*ar7P;b zU*s_Kp`IpHvm&&8;e{qEWi^XJZaE^*`@dX?XVC0080L$d!t~qF2LMVFdsF>NALZ4y zNPM>_gC|J~yh2u1On!Cl>ONq`n?xfAX|^V|M=9Udqy>E<(bnXdXYeJ#>!_{5Q6?Jw zABgU#3?`S#b><+Fh>hLNE`@G;h$WXU|OUn2FT2kL=@Hw`PK90Qqf8eO#O zWlP6P&j71rK{BZ*cLcobag?NLB1}oMtA;9v>Sc!-?5LN+k8Oax*F5eN8{^}6(VY#Z z@Q#JXoAA5DH2{0agNLeFk0%YImsHW}#ShZgN_>i!aba=QVj!)%tSM)Ta%PB-JdhK6 zL_=vKGrYcEQOE%%1y|c&-VEM1rf`J7=deHO9Lk-Lbb>SvZ?D*@N*Z{*n%l_uvi)rY zVN0%HCW%6UKQrtj&6Qa84oYfr$4-uJj1rS@Ii)UnkwLlN*3yjo=Z?jEdrt{%o;Tcb*() zC#af~8vw>hfe%KKs}tywOps-y{V!mwDV@_C@i&IKH_TpZjpjrY>DACgv4*HRob`(9 zTSKsxI*iL|Q#nlEFVbkUSr>h59Pp-k8w#sfql57&UFh;z)g%DOClic7;x~+#AqIt=?Ma1+QKsiT5%+zErK=Y?&8^Uz69hNiUd4%(4W)CQ{-O z*bEU`P!x5B)1=Z&8Buj|b;P3;URSCm1!K}R-2 zeNd8-SL0qcWV7|59{9FWz}jqZDN$53xCNV(p`2*2Oytv{&tp|wKu>d|0qGfj?*FaeorQt^+iL2SNO^+ihn=0bL% zHS8>6pE7Zuo5E;(ki9t}7Kq&|&4T_ByC+GsOceoDV1K$t=9Os~p=|>k%^qxHD8l!V zOpusD?b0je5h+VX#K}qdsigfutICJFwzw^>6;k6dF|lpvT?v{~J-erSN=3%aGG+W1 zKy}AR*hTfW_5H|z4PK@Rc0lEt6diL z46rQ8ER~TChZ_8o&5dlcE1WY9v{Am$WvLt5HwpwE(JLQ7UsQ`Y#18rDpjX4C?W?kd zyb3VrdO8;1fh!mg*s~pu&iT;Z;#t5x52ffE2`ATUd zHz`4b*|YNV90gb;?6b5ar~^{9&KRy#x@DThUcY00N6Ze_oE*IH>?rB?w095Sgo`Hu zNhT?PDNS=dm*xs$M!n1b^%br9)llw6z+X?yc4v$DeTfoo_=PWg~Io!-Y}AFQoeGe#Rp#2t)3vSNYowkTwc!(uspju%rRi=u2r^js)eD1DrIyXv7Q6Gzzv#k()OUu+X;2m^Z} zRB{LLqE;4Hf1aeIPc1o>k@>i$vak2~QJ9-^vhjRMJ!cZ-PN5e-Jz`qwcxY(p{|K3-YP8Nk^ z&n_8fdb3pvpUg_>Rv+p}ysbmef7^{uUu#G#^4K(xq_0O7dZx2}J}X8WP|+&UMx}RE zw6nvMMJ@+>6c%Vh3e(^tDenoDFclOeC4pO)&f~v}e(G$p4Wo5zqO+Eo(-qI0%5TR@ z5}u?`R@71Sv<~4^7vAmEsHM~+sc^h(Pcjrj&SYu%ct?OP(2=iS7hiYE8iHc;#HfU< z>SxbOvf8~?T1EcM#bFY2Ys<^=?stVOplUBAc@Y6)C=F*>gU}^qq9I?$p2~N~WbRZ| zn=x3yKY{ zU79Fe*lxgN1M!V%N$TB=^pa6oB9Y#Yl%_g0Np>GGzMCht;&o>LCn7s{;MGKl(E0?C zW=Cr;#ibUy2BmQZ^+n{{Dh~^g=#BA%qG1YX_-cd(ufA2{(p;_b!DTQb89O30>0YM^ zZ8ua5LmNarMS4`F@#0Z`tU~>8l8>y+Y(Z0xY9hO~NG1pL$gWXz4&*+M#3ceCrZ@{G%ds%#TqX^5wA!k%Db%EktnwL*2ayhisYDf^;`Ax^QMrLVN~jP zU7DoXXTB%w)B{~IHc^;q5Ova*-NH|=KE9|$5=^?&Pw;cuBJrgz1<n_As%w`* zMJU8k>Il2C6=+k@Zb;C|slKo&wxy)zl-UoRUPILvNYk$Eg0in!j_gke-yK<$U_EAi zk;G*fc{HvQq<~~bN+x$oMpj6kC}dQXwK0g&hT$Pweec!C^+s| zR2jtk8!!^BW*U#2GTPia9#(w=n&r>QJN%L%%#IPwD^19H-!B)z91GtIF_IhgR8wOiQzWvWOI=v#S zY;_-Ko6WG-x@Iu?F?K4KJrPe9GWL&X9$RnPI?TgS+P3yK`Amt4z7m`qcsI1t661g9 zRVc5VreQ*8?iIfhR#v*a1S~B5&I(Z>8W#)IuBZwCY(HF}ing~&h;JJomk^T}8ylP0 zCZPlmj;FyT&-+X<(-zP>#hPc~@pwr|NwN4cewq-McJ!h}Lsm&P8pagq_yih;_rX5H z+B%$eo5T6My$2H-)h#OFKf1c6UuG5AP^DfVw$5x)G1Y{tNChghXR%&m1Tv`{WyWX^ zC8KN73j+|$)eo9;mPH;J`gaJB<#o;T5*Xvm_@F`Z{3ys-njGLN!@Ni;jrtAC)1f>X z)exA^3DN5mlsza+UvVM;vQ)G&~W(c#lTQy!_kFsWQa zhgz29Ne_C|>zvL!Z@f;*tjSn(!(M6-jEz)t4rzuI`AEMV8vtSz_!g9y_X|Wh$;Vpz zi()bl7%O`5wqZwulIWYSpU+xm$*N8k?qWVlX|q`&4MQRANyUP1*qF-HD+fs;kCD;? z$xwf=-?!~6={`gd3$GWyPEn}e4!Wx!cEx$=e{}cX?#qu!axphx6Cam*Rdm7*frL+p zchUEbXf`Yk8M*0rcNM=E$JFL55tz86GbcY+{tdZEm-NeWnlxyX@$+&ZI;n7`(L4sE zOrw1IwALjh%R9q{9igtB_Qr*T3DhA=%(;FX(h|!LpOD2$dg5KWmnO@=zG2u7k$Gjh zAw@P(`F$)e2klJ+ZNcw&S3q$gJvM2$EhOiG;Pj_^pFXWLFksQH^@&Va7TZ^fC%5;d zclk!V(e6)-Ed8nyriJ~!+JF1?B^?|aJ=PmW1u~zoi?1 zU(ZSgTV=D3{N-ApKgO;5UB`QSB7Ag7jL5R9(zOrIz z#^v9JU@Rcx57`f3$)Qmra=j5c%2cnUJ3Dd#E8a-R^pObGm2ZM zLX&0G&*V|6N#oU^t*A|Fr6zk^Qd^%jL5pr6Gd?#ZlY?99jlABUU(JPFAl<;G*=}3! zSiwB(gAjUW4f$-h(=ll6dp5+=pjh5chYhix!yMfo`?q6`v(_wF6l1Clbc>5*Ld<_ra5dtN93IuXt7j338`_~)^N%2@$(&teqfm-KYp9UFR8zH?;%|LL70`~S^%ju@qC;Fl=$tNw40Y)%+} z%5*a7mzjenqJ3231Bz9l96^R1+xqYm^b$*Y-de}4va-5-R;5vv(OtSU5E?l7Wanek zJMkk|opE)mkGn;mGP=p7Nv1^G|3}-KFg227>7w~l^s=qnU?#H$NkEdft>>8#VzFdV zNsvp=8w(>rWbujwE=yI`^zHZC-*>)q-2J$RhpKujs{|45itunh`--bd=`0E+3&5`n zu~5CRo~jV5L+0UB%T_QlzbUN6pYv zRnA(ao6f{BlV@|wb+AvRCd05%y&bH*9c;bb*jRhBg-+6o*Kfo`C2nRK?vSPx4R^Am z)*sAE#t=W4?Xg5A7Iqc-&L#Jbza&ncf;O})GXdskM)Fo9KY&+uM&FonCV<$?-%UO! z90#2%B(0_4bhaiM1zp_C&SX8a&lh%#>FCqp3EG@;tLf51%|xUG zTZMN^sb?~Ml>-G^4IQr@q=NGB)@qQ^PpJ%;(lvh7_qGRj63*)MH`0s@AwX%PvQJiq3go|gz4G$w>`9|hW@a3rhi)gAA>oy zwX6GRMCy&{g1T}y?mqGzj6I5GEza8gqSqe%D>Hq_zI-RMu5WSM0uncLyDFX z#XgE?JXF~+sZdcZSe86-HboyOsaz7Ij8el)AVwULB6oXZf?XH77QTz~qIyT#78QfH zHP=)ejreQiE&ncABB*ST8lfeJHhnn+)MoH)c)v_-%dEdiOWA=61=*Kj{)_|gG(in| zTDKmNKNYpD0xy-o*ld>Dyrh{4WX?Vc!NHlkoLPI_A;_G?4t7lBTUQlgT4`Vw=1Hkd zx#Dv0#zk0YkKx#PJpH!)e1#B94a!daq|&GSG&&nladD1+P?IwLk>8?YlD=Kl2x!I; z&q0Fk6+=e$UeKnt3oY>U;rh$LEjMH93^qvWr>+QvF2HPt!Jxk_3DFQ9YHRJ7$`#Ww zMB(JrD2_(fFr~t8mB=4|q&Mz+uNeJaWGk3evwO|W^=3EG#*<;LAho|H>QAs@o`oy{= zFt1TTuM5@?uLQrUfDt1^%AzA<=^a=sGN(hUkx3o<*aJ2rb9FX$|3jF&b0OohQKxX*N!2OoUkJ8 zFq&53&}WRcJPi$1G~6z;UuJ*g;7RtJjc0!lObdL6RHclbpr~PBYN?_81bQ6uPw|O! z$egU%#$4)V)&h$+g1}_|7XofCek@&UtY6v`)kR9+x*ge9{GQBn*ym4njo)2;5hEZ$?xrx9a_<1{k{PYg zIg@^GFqO`&c|t4JpZl8BCLeVZwmfLLnav#@Fu1?q0KI)ePPz2qD4bwn1cD#DHL*6^ zFgQQ+&Yr$k=dkASl*$doTJuI`xr;T_R{3o%)=-QcZxNG_LPMK!2P0n&r=OUWY*f%Q zmb9YDrxA{mHXuisWkcpj=*ebKHv4O3Qd!q5KFflsT9rl-T^J0F6p2f^FBQyi18XWJ zLdn4?eON~>$45kBJ18eFw}`Yj0Kl2GNg|Im&i1Xd$?9v~sdr!pU_Rg0Hia)}MrpE^ z@9&1t==HM~uKj%JAIXNeH+rRki(`g%zhN??huk<78?ZYhES_Ae-J^% zhgzR7*?=5?r6NuEyDXj}2eT^vU`RzE{Dj zf^66gThR>Ha$NCDBH>T5zd3>Ln#I*b5uU&vYJQcDqpn%J1vz_M1q!a?s_EXf)=`C2 zq}w6(5LgvK5@8kL_p`84A&UH4&W%ZkB}u5y;Yu8TQVdC)iGP#TQ=ry$lGjTVNwEw>e8XKm zW+df$AzTl2gBhxp6T5ca1COFl8dQgYverPs7qYlm_j0PYr}R<6eHWc0mZYR2G>Zj<6=CIJ03!=i#ZwtN}Kprtdy#awIuM<4nLw-NC(U`MG$kXnqDF1 zvs}rEB}_Kz?OOx*q~RA*4TdX2+)Ymta8_u&5D4;a&YqVlcKP`e0 zDQx`YClWrO0HMo*W;@emFUdS9wl`6x5efA^PA_z>7AL(>WVvTww*SKn5v*It)enKA&@YP zWY7m>jb8ltH}nX#Z!}lWw-=HDyW)j(?Gg$LJlb;7jDe5dj3Fat$3WuUEgxMkdZdG0 zPcfm{XsG2MNSBEmemZzXK8dUtrh+U52E1ZEE8}06%_Jm|^Qpf!_@o#d=UYvQGk>tL za*NXDk58}uMU}?a|F)jEwMye2T?EsYHw_9i6rs?y8GT*^B|t=*D>UCo_6ajyRRk6) z?!dxbxc{)DN#N%!uPu?>}BMGsYQzCggy&LwjWY=LHlzS zQATjdeg;FLd_Ocm!2%$&*Q5qH4k=O1S#?P zFqvS_!|`!>Qlx=nR10#oLP~4M6^`?(M~H%Mz>`t^{dB$}kxQ=_qK0gqlVfHr%C*(r zzx7yGxc48u6*o>ghEhrZ-+IuK52GYtI`SbwuhFn%W(mOsHAe=wu_%4sK_u2)%Iav} zyqSYrv@Dphc10Ufb9bVmUJ|Qvl$%X!?DRmqLSy}99NxN;;X`Jqt7{KJVT%JN-U1BwIA)A#FTk7CUu9&-xE zJi5LGznuS1;QCmY6v(Jd;7(*rH42LBF8rd3%W2$IXA$rWc1Dk(PKZ|_E(r0-7)gtC z6q{kCI6-gg>&J66ybZ3{1|3rW3{%+W=j$67_nM2F|8cqMKw&BJz_TDvuR)`!dzzXQ z@lmBXS=qclL$m7R)T1{Nv|pLbtt8z}#4~-Wkx^O-G!Z~*#^(ZNG03}vh)EO+D9Z?~ zE-m^qxnuK)?An=awo*}}PvUTXHhC{1U$KtL1g=HQ)*>eghccs0X@A6W>H3BYxpO!{ zyKXVdKD)sAC_>7{ON1`G+cC3}8;~lKq)$Z^n{_LwnNxT(RD#e`G_?8?@O)E+k;tB4 z@&JgHrD}3|frPdEXj;SE5DD*$*jk$^H(Vx96ngy+_f zJV8cG)rK2SUJObZe}?r2cPI_P#T-6TFeLHTXg&$w*9CV>(i?br4k4K>ArZABxROsI1bGXXD$ONO&lK|I(3`GmRTlXzP%K3;sWg&NB*nv>gJm2H`96rGGY1zHHO)fHlljH3M zN!kD@<0wcxws>yPI~{;j?0ZM?<5E&O|ff1CKXfq(1xhj|;Aw}E*Z zn7593>zKEWdFzzFshydmZdF>i=@L(Cgu-VpPKm^Z|{A?6J* zZ-99N%o||d0P_ZzH^96B<_$1!fO&n)>tkLY^ZJ;}a3X|w&y{rO`s$#v64#4KeRqGChA9F)g0lL?fK2JajM!C2v% zC_p>F7ct+naugLkLQP!_gG-;rKP)bOoMO=;;8BK+ZsYgp3W>augZI`!0#caCNX?q3(dd?Y zEUvsrAw;JVaU>^E)hVO|S{4Z973ZhXZVZ`9EyofU%yVLVZBRyGOh=;RDJ&5TL!6#J zTtEFXd3d;cuw6H*2cp+X5wT!=VB{h4uEe4_MLVnQ1F6PoMSaOY;q#=COS!4zfI4bC zFg+-$Z-lLSHr zqN-pkqitBjfYMqKBZ94Wkpjmn$FGWppvMia)tvN7=uaisLGNpC66mdozV%e`1c;in z(3!H^Z2(1Jl`#S57#uqnF)6*;7%0<21(1xo@GC@|uy|N)8~gCi=fK-Fi}0&QMewI7 z<7D^4Xa?QJuM3>4x?jvETGmN1h!x-rNs_&EKz-mY7kkt>wUOb?BC+!D9g^Y=+#10E> zK>?jSAOI%X0wQf3yI1HArY0OAm#?8=1WhyD`bL*RKMuw{>7#sokr@F4O7(#%#Y!qD zojg!V^fQ8qC%kj(>{h5zuA6pAWVp(IB7DVX$w@^Qpf{C;*OpBY5w9vi$>6U@P~>o> z=$5fR3$`X7&JAKpPC(IOEKBr|GaMG%3=U`Do5#*&cXN{o%m79dRP2g%4q|)9EZ!lq zHxeaXb*#~^N!etTQjCy1Hu?jXDAVz#M0aDFfLM?59uq>CacMiP&q;3{Ns5XD{`L-_ zy`QRy)fEvPp4Z@k?uKg4tWq3|l;EMGBUS3!*UQ3gJVR(EeidHr5W9q8oWN}sAQsD0 z|E2)3YL)8M7fjEhC4B^kfCHv8>R82av59i}DSC#>91y>hs-TtgE=)bzG4=5J=?VSB z)el@dA}FB+MM#3AKm_gsZ+|fbLeBjA#(IGx8;w94IFj+Fz?ev)RqRM0KGX9zez>Fg$c;5KlARhG)y^~F7-Xg8abXyjRw1OSrA4Y-02B*lfxXu-xeU5;M_{_xf`x2`y69V4I$ZI2saTr4#xal(^i zR++fXt4a&I1B3<$5aPQQLEuaz@I+Pj;`Cq_p(^qt0Xxq*8OMF_CWoB2h+pEmko69; z1S{^sTl@1Rr2cAp%qAs=8oi6cO`s_fIt3FOM6hMCQ|nvbN#e(9NVZSr?(`{_XidsY z!|4;x#!1mu^f7^vQ3L5WCmE%%QB8JHX>doX);T>#{PS;e_k123{i?SIKvHuJ`dXvC z?)V)p$V#BR+c#HH3pGVq%RM)tEXo&0v%PsU+7$!lk}o6w`~2eJF4L}j%(cK{O9u&9 zZi5P~{wSlLYv_C;{y;-Slp9xng-XUlPd9}9C?W_Uv{1;L{wJ=7ajNGc1LQ|qpQESA z2q>0gciEAijU}UQNEU`rHb)l)Rnw7OyNfy<%0ywPEB%e75?7!P6$JA_W4~RGCP0<> zPYh*kQBsU3RsC@W=yWmqe`MF>6xW^6NDzx|duUEh zKf;}p7xA%9(-WK(Mi7BW3Uh?E6mSawEYaU}4v#o&cUk)`&Lzgwz+l+K zMooq(;MeBR5Jc(RdYyKc~3ITXR~B~ zIeC~Z{`d0&{V+oG1RoK_9z3Cy{9>;pFR2!x^p5;Hmxdx4+EgvXpI+deI<&M0+h8jc zDO-E9F*Z|9(onaDjRiB21{2oD+riz6KqqHx7R5^jX|wjYKgtN5Kylcm|+WDzJrCMiJZFo%V=SRE>S#ZZR;wzxAuDF?}XjG#iR zr8c0Dgvs=2eQhos&sB2fX^Uq8j5W8m*HyYQcaY*jaMnUnRF}d|l3C0T+&PbH^)L}E zJBlt?CJ!{Dlc*bouVIL`F(>>B(q|c<1?GSLd~kX?n};nGW115A zBqea1O9k>DNmPi&T%H`lP4;yC(}KrDNx^iDkE&vx+V8c??XNUM@@<;t`|lh0M^43_ zq=%j`t~}cUA1sN(sX%9xz_ntYTEj#cR)mpM8mN3zQ62|dN#c|{S^Je7 z=EA+y2$6a$Dc$nN6(~k4!v;l>=A+a8<_c8E{ww?#AddAf5WSv|r}zZSfxAN#Mj7t? zsmE!nf0PSz)Y55sj?Iz{B81@M^^83_!obE`5C1*U69M1++_SgXCb8-KCA+l)U#**;wP z>HOx^=d0%8+iDLN!xj0A3EPNhUD453;SvH>>{5s9hm^Vb3^vQ0_f%!SFqLnmM@hq~ zruEqca560wjNO#{F(mJZTL;B~5V^R0wyUO)s8V1&`6-QmMT4N2V@0;Hm}+kq)TcGl zrZ%*yRTG?8WvTWMoYiJoYPj_|e63CnJJ>Q7n?=T$H7yH-_O^z2+$NFMPFjav0I*T< z6jcydzp3eM83iOjl`BUHRPIX?VE z*6Ws`Bu$N-ts;F9%rpczrhA=)u3rhFTR!@1h|9C;a1RT(vmY1D!vZxl9w9`mNP3Ty zHhfq-oj-h8Jh6p2{;JN4d`?zQR_AL9=SPr>cHJ0@s@_@HkFGtY)U~ok8GcLQL9!_b zXpF#c#lDWl?!1Cbc#rjBT`p7b6?uA40ly+dY&ndbhF?(>K;EJHc`X zq1#V_Mdp=~!pYzY?Wd-TFUbDr+lM*^V{bT2Q=ZCUzPeK+*dhBpvh7dCVq_xm*f{Hm z&M4`AbQcQki!mJD{ER~lbTKOgYuhSz=Tx=PTWMl6bV*;mXDUY$3&tUesQTyPxk_=V zG9iI!@c)5@`_Zj-R!a*yK zLpHiK#5@j+vmgxnx zmuV$G+~;&WeOO3^q=@xKge5;z9+W0tC6`o6-(z z#~Z6=+E-1%s!rjTe^o*mvVp{!3mj7#YBA7|c92Aq_+OUqP|V>2tHHWC_92WGDHQu7 zgfq`S9ZSRr;%H}+M#^f;KfsAKfR`?_>$JUttzkagRk(xO6`58C??n+WNgRq)U^)^p zXbacKp!H@H4e-%2=7xCx2pGFh$9K1Lw0P!?hG(fI=iv;xU5EhVQ`EC2v;?#+)-&so zxU4nh^$x4@#yX!=qHftlOG?kK*HO^7c10Ahu%s+zAWyktPrVbZg+&swg0tRRvNSY@I-wFQ`6;t0IlZkAt0lGXRQ6 z-*)j|%ir$te8 zXr7hbgTh`lAObN~E+T?;c0l)E-n>k2BXWu2REk*=wncqIB@DQVBL9K6YzH~q7tWyu znm(P4_Krp?KS8;lKAeB~dXA6j5qy1phE#dT5g=}BF^dNjX@U$!IoO+?BhxaU1@dK} zi)(111w&M)@b+peJQ~g4nPE^&h5YlkSrG$V${tE+SN>+@C?5$psaBXcf@>eqdulJB z+&~3`da~R;^^wWl?b2{)ehQn)m)qxy=BIO35z)A0iO28ixZ=EDvbDyE0f-@>6u4=D zERMT1$wg9BDk_BOiPKqaxe}pUr@js78j&_9*aY>QVn2CSSV1)-w%8$8Y5>J~{Ii6v1cKb6F1YkN< zs1YZ4m_MRAg%6OCz}G|ZU)>%#zsYPU%}s~@9c|1!>~8;R9M+r1s-R-8>Tl%+tG~;Y zQ~GfFZ~iv#O+5&%1vTXvRVMHa{n>^1ldYBnUsq014McX@-y-OxIz-vf$dYdfke{s+ z)cqtbFYu2}Nd_{Dj&XW)BFiy`ifj4Ps#EyogkfIY4=a#6f(k%qOg5K*dmJburi(#7 zBHKscnRljnmRf<3s<@rrZQY91zfthFxUjqOQB2;GFIs~##Dc!&**$3o?d{_jVd&C0151kCsqawd`$0Xhw1j))o@(z(lcW(+d zycjgAJnbk>(dOU5VjuNy1B#IJ*K+rU?U&^+gN4hKlEGGHGRC#Ql8z|a2|B@u=(Z-d z^E!otk}71uLr9=-qQ9!hl?<3Xy$?r{q@IZ~Q2AkjI|cY?7Q(wYVF7%8M#(XmGH z+P5qF50O_C=lRXZ87i_~f~``REkKKKO(nrChZ$kT**HOyI~u&u`NJhrhBFc77K6t6 zBa$Nvo71&3mY8o$0hMp1MIQ7ghe$(;1tZW;zs3|^$Yssx0D2$vwa{#eRXAp&4^sbY zi_{?2)HU4&fL*+uKz5M|Fa9ns-XTTm;py(j=H?t#i3^Fi*NT>m4b5^4N^+z|OvY(= zCqP}4)B3;8@M3ps&mn}_WqDCO?a3HH4Z4J+_ayFC*UTUv6gw0vCuyCQrNx!}6uubS z2RxpiUtZ+x12lyvp>&JmY*=Vwv6vK)C4pGGV$ANSxV8biE$TJI_Xn!Io9}`2<>ykL z!?mli(>j(ha6w8|y%Xhcf_TgsEcCp)U0>{|g!cXvO2#QF2JS)!R|Ou`F62M8y_a+N9!|)r2@)pMiU3e81C%am>b&G7 zvcv;_6wni8XF0MZ4P(=}rb%3E0zaX3@Zfa-Uw>$-cXIN(0$F~^C>@bctF3Jt=Id4MF*p6dm15ti$GsU4Ga z*Cb;{sXsgtsE7g$9s>X|l5#s@<{4^KlwHA@qYfx$S}_obG>7aaJq?tI#y_qXH<#w` zF~F5}gMXf&^d6alL?EoE8e<0SxRsTEF2i)?SlARPXL4ftp&glRm*f*a0LonHwW5o< zFDzD5!d|9@B?M*x9F;1Q2uS`*>u>gr6gO5DiwWFFHADCy<^ zk-xh!!XrS72?f?BJ!N&u?t}2uIe-VWAcJ|wx1)>5P-_!K(yxS;zN$i$ z1w>aii2>4mVww6*F4(B{%5Ip@udHoJj_AfFvP9|6a%-0*fMuaLQF=U=9>cKJt)y}z z(+nsQjrR6YJ#qrBa<&s=5&{A4n#OhV?n!(^pp|#;>*y~XINgob=eS?6RT+u8dT_1>ae1wb9(d+c25u_ae9PYL9wGMT|qJ1ycR($ z(dUrOQ$fq?G%SiDn3u02s0$}b&AFW>nl9E9QPt#-tVhvSd<}`oh=$-LhX7`Z2hf)C zZEPRKsiYYPaaxOK;MN;*@RQ(v$*dnSVP5-A}Mr*kc6VjmzFPxB2UH|@q9vX z%6Eaw6w-NKA)pxL^D>1C2dpL~geyhf?J+*^A2&5v9xk;r z2Q|WZK7k8yoeZFWS%=!2yW1}kj;;3iP#~ci#9$U@Pv&G3r4j?~Zx~LE)`DPNjdM=m z0VX{`PG7@KJZvb55XS&CpLQ$AaKc^F_@+_vmq$dO46Jx@%hm5&+jwkEF)Ki;P%Nt3 zIkb+&Fa!uk^v1KrwXgo6Y2blM9X1e+kXSODuPCtkd=Do-y;zbt0i+9EPjVud`x1_? zF0=qw)0`aje!9Csg&$4lCsFVAY~pbOC`>_BY; z)3AanSWz&|c3EK4IFGO5xZOrMu^B9VfHU;rQxJ@rPpkoy<3ZOfK0_oOJCgA<<`8L# z2~sfOmG#ui&C;UlLN6rl;(l=%=mH=<6m(`x&hAr~z(w$=u|HzL%)D1Q$hEvyW-+3q z#viT>AS$U9cxFgeKz03W*Olqk&Q*t`wIW#d*?5dPRjjgOpLJl2u+RgkdW`CCEX_Rm zcyPM1+KboCda0MKsD_?HvPiZtP;JnM2k$I+eEaO+@DvnqHaSFN++JZXlTftGM@Vj9 zG)W>9V388nLwQJI=Bv!&NmZdiL?ikivu!z6%5uxNeIlp zU#PMU@4y+-;SsO|7c15G(`2V3VtG4V`Hav2CtBQ!2_8c8Kx7Xh(JuvCb++@#>?2N$ zL2zO$_jL?vk=tVasY;Le=2xe1(+y|#QC+doAHO_4ZLEELZ}^hoL-hPWNbIzW#?D@B z$HvG7$;y^E<=2ka$~Bg)Vna5X0}aYFhT6o8`tb)Fa48~XJYL*CUXS5i2T_nHQ=FnW zmSiy}{pyQ%2A#0lwa-#1iq|wvun9%KXbfr+I0b9<-eNolF0p-hMdl*3HHgJIJ3xRZ zj@#t1*c8k|xtCDgW^dFsJ6$wEr7R24dU;VNh{;PUWJh|4a}eYlwj$+6b$zpn*hR5VKoT)en>hMCTR zXS$e!XQWSFD6hVM5*gH#R?CN<@4$HsNGjENLW%O@C2=jqEN(>b+({L28E1-hzKPW- z{4(VP6Nl#Ns0D|$-7#uiKT6OO+yPT))l(W(Xc)E#3*W9z;UWU%G*+S9PC13RMJ6B7 z;~@T$hpwRuHaubCRY-2IwOw_C8D@ycl3d*GoYO`29hQjH`RO~2DdCDuUJl`^aWHOJ z;qMX?Orky>G6Ll)bJFT_cz=1}-7~oFP+N$0f%g*h9}PKXhjnB4)tl* z-VV0jZfvZ**+Pq^#p^e_h7Ww?Eg$pz_V!N7HLhf%7U`J`Ggc>!>!Gp} z@g&hZZ}$}D617Dobsci9;QP$GjS&=_}yuQ7BB_FOYzr6}0^2cgVD;xux zn$zI98Nzdes5WjoZR%uG3H?2zaSE8c5OUYdX~u8&Jv6~>dJ3W`0dL54=Xi?0PTDX) zTF1!dxTWGKkZw@&&!~WU^@FjwH4Ar${t}v4he2@jVl7@W2)j`w5q)G6m+zmUkyB7s zWQfO#inuJaUz}?x!ZC?puqL(dDFr4=i}*+pRhl*H%ndGoXEo4a|DO>EotT8mzV%o< zxut*# zY1VKsMgJEuWK(vQYzl<7Y8pzEY>j9M9alhgQhuat$rzEdtrornn+4woaI7+hP_E?O zDwZ7Hn%0yIRE@Tq`b=$yhScH);A?qY5;!DZh1kx~JlVAPzU&#|k!vI{59SE;J3n%= zZrD{2{m{&I&pbKlg1L2`6$H$QBo!c zul6|aC~ArUg%*57$XDCI-ZhK2NZCw?<(i9{$!DcyLMTj{UVe_QhakaB5$2c2)9Z^L z8#p7-iVd+S#3+dfN+Oka489F_x$=-rX)Wq8;2naq# zON}>IHO=D{z3q`dC{1j~9;osab;?-Y+SV$8cDqy2xDV%3Ay(qJwyh|~xDD-4iUIhx zX;s7(2{`t-iEH2C@=P^*NkE6sZg+Hcc#4k!E?u15xR}C+mhk265efAL9d@=EkEoj| z3M))lk_6dYf4zT3tl|CBLvBlo!j7wGUz2#!u*Ta(k_%}t3s&hOhB?M`&Eo6&=O_19 zY3nz7b_}^@uF16w`NCzS+3wsuqmH5-GPg+6@IYQHAx}Q_*9Pk%Dp2NukwP;{>6KXf zh)#jK)7SltnTygDA%d-vsAs`Jhc@%gU?rC_UiFSwSBh)L3{G6lg1E$YBczjk6_$GS zJ7#gS#I>Ua=jA;j+jA0VB_R`zoy_Qh_#0)7fTO%QZJ<6HWywwKj?0jyeI^z8Oyno_l%zgv3mZFfn7y#m(JgS3xW{ z_99w(C5*(1P+LjOMEKwZOXRx@FNwqo#VOBjCFs-2;gZZY&9gL~gWmD<^l<(8`Dp>0 zKY~2vYSSRXGmt*Wg?LQyVidrv+^ki4okVrY6hR><$vK2QgDZd}E)wWOoy&C4>J)B! ztXs$1OS`08PRU7wf}J97`3`EY3%x2)@+oa4p$}l`qJhkSVCqGT;+9od6gS_HFX$xa zmREwvcp{nA_?N!Yl`fIfy{=i@nJV2AKdHr3D~Ily#aoClv4akChy;Y!dt~vpd?yuq z)CUPUQ~4Fbj@jzUM1QC{hb3fl@|e6`()1QLrYW%-zTXq zP36TP-Oy3hwzhcwEcKWILWvmL z)x~ZzlPmlRqJ#%Zmp{ryt!PL$>4!!EC$U%HZ!{hwM%6Ylpo&6`j7G&2{(IN`GuOe#Cx=K#63=OVf?c!t zRw9`0p|Io3e(o%;&Yy3d_Rg=7yu0>IYhVd&h{myB(|!TvFbx)`2p0k3W6!42QD)x6 z{vUk^tB)uh|8>!Pe!eQabfI%V{x%LC@uV;(0(|a5%OYv<5_OAD9#xm03SK3DeruSI zmU>8H|3N8H`(@uUb-O*zuL;#LL5Kv%75utQ!NKYHy&)|giji%xRtBt7jz+c}a=T=e62;gnP!})}hPD!EsAEs0+fa1o7zUmqp_b0E?wodz^D39Os#@Am4eN zgfiSkrQ~84*nh7*i|^(6F&`uq+u9+jp8CZY(9+x0F^SI!B9AUPh`s@O4Seg9_73uDbV$>Nc%kTtlA96*G+Dl3WU0KQ+G}FU zd`g`^%mS%{oP>^71tF|qZ81eD9HdSd?%LTiwy@|&kR3s)1``&G8?|&+zAim-E}T0= zazl8*F(A}viZ~Kbv7PM`1m*Ade1RJEuh`E_Q|xfX;EA=+h8WRf%vWepPPwTvf+Q5p zGx!EP8HDEy8KZ-`MNW8=?O;k?p`R!3BLYjj@6V~<-G{}y#^Tt;Hq%!ECxq1wg*M~+ z?BGwWPttBc6vd#b%|&r8>d>XY%EN_49bOio3x6HlJ`LWl!*CL!hs5el+93{4Ze61M zX+tEvIr&1#7Si_yJ^?!cZ}5FOWPK1~6Cf_+3v zIK5AIPzw~rYLU+COLdwrCa0%dI^N3$aUUo?zPUhDPEd&^5NuBy1KOrx5B#%_qC3ft z{IL%7(-b~j5`_0ci@`uULQs;Ib_BkV@<-F+p=|*3mibC*aL7Y)K$zyE(DoO3bN)4= zmV?8~m)bRpt94i;6h$l$1*GfOarQ!OdV!=;b653UNrF|>Go?HY^C^@@u@{oFX1{RV zsG3`X(KT5`LS-WBnb0Y=xrm?gx9pn5=O_DlR@5nX&xf4nBb1xvw61&z!3e{JU{uJ@ ziMEY-QBE_x@bP{rgR)MplrjR@k9P{o)vpe5Dd?zuy zaG6cUhtTnNv~Wr+iLw6y_e?LRnHJI)7S>hv*y87-GZYb041*I%9H>hsb{+^QpN56n zfs_j1(fPNa{B4lYsz*!1^;7AFigS6%M6N~*yFyqxWf}!4_6*_6N6-(-q~pWr|9QCW!`1{(SbIo+B;{wCe?_z*#ADfJ|Q3P_zqkv-t6EJeuw z1e)`XoL;pi>$?rfXFz10E0P|BC^LaOM?urTMF%e*>O5^VPtE1x3Jp*%A#o!o^5A&y zVE0oaJ*!rGzr)YG%sJTZ$stw}MHds2rchHGX<~i|eG*k(-yl9v%dtHEfSNxY1zU7X zd+S_1-@*uUcUzQgfkp)%Kb0Oy=w#cnA*x=5w-cCvc>#)gqPjqG7kCt+gYvVe&uPO| z7GPF`<7DPDfidSUsgi2`{H|FF35a|Gu=tvS1Ic|tbu_BF!vUzUs7X5z?V8@kFcZCk z1PR42f2Lo)o5V*(v6?KAYDT5vA9)l@&Tp@w&8OXd7%LwYtU2@uY@-k%p#dxejqW<$aFW(H8aAdjkipc+7s9Z@Lj2wZ zIVTO1tU}Bf%|GoN!8ooZ3xw&Twsu8JdcT7@0d#?7Iy5M*zJ9SiqTNZhm1%cOymE~p z__qja^_Dop(pT-@CBOkDX&?elFyX1JBiWmuSpy+ zq8_z|2o8-PNBI?kH3KBp$^?4~y49(0EMy^BIn23ym_#I;^ai%Bg+F67g_#8EbGT2j z_`KqaQNT~#Y`4;wJ<-@COX-)EBwf6_KJ^cSt=?KtOk*-#@SMgsA8JjW!=8>KKm+J??-G@t|KuG7H?<~4U`D6!L}YUiMTf? zOqQjaIAVa-8iANqXc^jWkXE#6o%t54!kSG|9#8kRddvyP}aE;PwHxonl=)T}Gl@TK7ijk{)kJ8R-)P+_|@WHTf7HrS{Aa zxPCAnAsS!I?XUv{Mz5sh>E@^T_5Yxx4mdB&&vL8WHteHw+Yw&*xwXgLd&e?rrNbm_ zmQlcU<GwElsEA8sRg7D+y>dIcc!MD+P3CXnL2A%Gs9{Ay0(D4+-8s{J|dux;9 zUi{;~*8cmu$uT{6qk|bj+}|UD>)iyEUJ$e#4<#N8f>zqoxadq7i1zvo4PCAAObi+M zNDlK1Kf%Uw`l1OYKVLV4Jip|_0knv{Gc?0Vc_c0*@8medaa`8|C->>nP! z^PE)QQe%_$RT9#o3xxXM?J@V3?opTA!50}&lM>tz6(ONUD4-C(>;yx8aoN7QzNOX8 zP2)v?zjmjOJyW#pytXgN>FaMy7<|>^Exi9iLKO8eZ=mf+L?U{#&zsS%lnv;w6t!Gd zG-(lvkKO83uii}sBuc`CM*v)=-RmC+27iHwy#v_HcGh~~6R3kP7$?Xf1>+7NP#0uf z&V^P4up}+EDHLSVQ381@D@Y}`)$iu`PFYd?Qz*0`l5GT8vqDy85MYJ+*OICT4=PHI zZmz%F-d$+{h4c7FA;Y&s>A*8&&EtIyK$p>&0;e&O^nxlsJn)J|His z`uP|12)lcRK~!E5N)rv9(Z85DC(#u0yRfgx&Mvo}hbHcX&Qjdr6?KzgEX*lYYuL5` zkS+AdSLci+I?bLmm(UCo9c1a2Ml6cv{vQ728P58kJ;_pb#sIsTU98#?*INk{ndlOn z39WTVDJs=R)1&yekC(86MOsykG@gtF^>k=dmd3MaU=aXn7c>ypH)~_>oSaG8t#s%9 zOsn*4aen#Y?&|7s@f0N+xrbVk`4(EUn;|)-)`Nf@oPG)iXb$Ge1Ri0sFGIZu@?-vb zJjYasE(eTakoOeABr+#Vud(&S+EDGn?tD`jrC!?fbP@JQR4S!ya)lqUalO|)y()q( zqNZXYb2*yen9UbhpTl)V_9a_tDtFuU>rXctFJf*rbA;8N6me1CCMryjl(HOef5QMS zs8(4c7ttmp)#@{&knNbjt=0@mTgbmN>`2Ql>5gBEpT+(0M9DX+>kybonEVZ+yd)zf zg#>n5;y4d(|de~XRVM;+MWUgBv z)9AUdZHA~&-BP0gjzJK!vE1P7%#tL-`u^*g-W&Dmq*VJ=3fhIJTHj$Z7UNe=ESn5< z#glExhEstWB^^LX;4FrCkpXTIWmWQs)-ZvHtVeV-mK@WpR18@CWrpNSU9H2(WO{Ne zbyk&d1$(4~>=)w+Hr3KnHNAx)PIh1+`Uli3`-K7HM?4r2cgDvcUV^)pvJ?Zva=S`t zJg{Cb$7pI)D>ok9@%Ffk^nh^eAr!q5vFbD!frz#llGLqMJ&s$8>@XMLC%L)}0q~Hv2>*HBPd@rww6fgW;WbYKwp0*zW{2g9w zlbyyY54*pV)hh&}I#!|{0>=@RGGHK-R%pXom94lu*9~`j023VhQu&5lSF*4*?maWee-rr=>T*&6fnv0Eea4Z`|BUMgYa!;69*7BpQPT^4oiYqt*9QHO(ke}f0 zIGIWI<(Met^h*$Z!k-Sr3l%ilu^~ErOM@C<;d=Hv|3ba9B9h#z9nZa+$ zL5M2TmgPyJOb7Lav&)n4uBK$~NoBaJGE5v+KRMBmXH8#MzA4y;oh*V4E1PSln)wcz znOV|iSvrj>G>Sn<{2N;dScqtJ^_3I2A2Iw-R--ySu3Ka?9-Jr!c8#I$`C=p0kncxJ zeOc8(WfR+65&GdUT_-~-goLJnhZc$N7f-$L!El4hUgn_7a3a>xc9^iFDM&~v^EUJ3 z_!LO2mxBTAvqA2j#Hn67nDPBRcpR)2|84{J{%yReZ@XzarGFXE|%vP$lFMgS(Bm-Op6fp zTl1%qTEV|xxilsRcl7waf9Ic#t{aVVML@HPS106Lh7V|FQNk5NU#%{_c|JK&@;^^J8UU+Z!RM0-Z_Z44bP`>A*vP(8njNd zuSLe>hB!3}1I7PCaL}vLHs>RagTuZYbi1-r!8|Wga#>L(3 z?c(AoR%=Zz+5Y;kHm{DIsXSW|S8Pj<5N%BlnI(oj&^Dx_H0!L{C8Tyr95B~8>zE~4uk%DGsX{He1(?nS56WxXEZjY;$d;64Fukj2m|>f zLY*2XA|8!r7AQo=QRU(8`rIEj_cmMAJcf6E7( zuG~M|Js}aRr@y000cM;+I8B}iJi@3Sf=ZH_7-6+|S`LgdEAEFbsS-&W??qJBY3*um z$6q2`F+<0M7MYn%L^zcR$h37XHq%+T~+ zzWHwWUB5S2>u;@X4hFrIhlNC%FIId1HJTm$$Kvbf#U%=Uzkc)j^TYY=#Sf5Xf4Y`* zh2CJZ|90)of4?No!WK#vLi{frmV-jY`@6i9rF#I0iWZfgl6bZ(gGC6Ek+Va}t zJf=h?1afA`W$*Ad4wa&bc8IN2nN$MRTr@DpOe!VGkkE?B&g5|PNi3}SUy0!uF8=B_ z99R*`m7#?i2svEjgwYrAX(ibOt4p_dYE)H-y8Cv+KHJ5&A_~gl47Y$b#jLU6;V0HOfLvA_T*F zCUSQsa!4=C5cQH9#dKvP(kWC`-1)P`*oIA?(2OPTgT(Fd~g?CC$JIJ zf1!j@yM+%S85hPZa9ne_N6eS6Md>((0-8XH0+qL)5nm#$j+R*cH_hy?PZj^WX(lOw zOuc6XEVY;4a!nNWqsd2=FiLhvEypZAOVmC_bDB-=1LCH+SHzYo{Ri7cbh!~@Ez=&P z0x%t5Y7R-#Iu$vIFJv8~p8Wi7)X_dG!gWyfM>`VB_|aro6v>rOV{@GyxG`N7ppUQVeakLPrC~50rmD@)0*1JMDS4>uvjZ?5^ zW>QU5dZ;@&Ao6x^B|_X31vG>(25OK9S9n1Y&P)?Wk^j!Aq8@iXs8 z3Fr4vWua#9o6g7V$M^+LPhuQHE1THcS(e9Vvjre`gqQ=G079^M5{jTLat-4xP^uL# zMb;2cA-93jQ&KcI)Mk*m|>=%U`T4~aKI)%RswXVcz%j1F-S47I(09Igf zbjBKKht$3;S;i$Z>z>CAw)ZvaAw^=vppj!4Uu?EL6*SKKw$)zQz8l=h(aq|c&U){6Iu(um_;y!458L(c5)YR5H6L@f-0vUZmOibUZpjMHUts*#2}nnXmT#z1K%F zsE-F?ao95ay)uEzuR?3dfyK>;;)_V5xE0g{IzqbHIhgOB9I9J>fYyJ)cEmhPV?qF2 z#5En?&F^+CcA+pWg<$I(@arLOdf0xsfmOuS(r_GQNHep6brnPvFz-S;w9{^J+?5&J zEuu?&LaO8d>T(gz5Zx*fm9)cc*5s7 zh7f#%7W5{IDt)#%w9|bs!+kl`_mY^{H)|>0*eYl>N{O1@aA0i}ARHr(OacV{Jei$L zc2FhaVYT;{9)BbA^YQ5tB|HDrgUv;1SDL2~&J9Ixj5gp7Iswm@n)|Lcf*B&pRC2FG=Q2yhKja2JG8 z#L{Tt;0r6LLtTUOhCoVlh?6{hIKNnIKR+V=L>_}2AMb1hr+H&Rw*`3%z5sa-T~H+& zQAESIVJQgEYsB(G6x8i5buU0UgQP*}u*riUm=4eVXBc#EuQYwYoAYY+ptD2h*C zn_m{^5BL=2`%s?p2)U8dPg0sP#U2)BF%O&3U{y*|9h11mBoEJ_Qym_P)C)4iMLNYw zMdECVem&)Xs~D%1Ey~Y0Nv_PrJ|rROStz&Ubdt}A;`EcHCrTY z1f`kVN5a#b*GK*o3EXNfY$M!4B+G!l(%dIT6^PI^i!-<-e6m^aN@&f;I6c{lwrh%n zym2IGNjAe*dn*((oS9v>F7&4>wode-q70cymnnX$?n!8kbKTXWP!mNhl3*|qNAlX4(1y&_QuftqB$`7=8gANeb<#q zd}#Dt%i=@Tr(=|=H1i8yeRI9{^5*7cs!=A;Bw7`2VDnVwog)i;awPVM^vU+X$VK-& zE+#0I3a+IAJdDceLhEeBX(ts?3$J9kdIJ$3h^T2A|29pp%DVZ2G=oEmn)1Y(-J{Vi zm=obMnzZ}_b4OH&^jz+Tf@)BAb&J#z?V6$3Rt&PluA*Nzg2Z`SSc1SIkimL5n#iK&@`6D!f}zRzp?*u{&V` z{3*fEVu%k6ZkS@lS`dCag244Qi15rJ6yim0L{q4g8_JKmJvQ;Z z3K_9DFk-!e$L{WF_I!JLefz}-6#Z5AKYuNJp#b>I6Pj>WlIhWe_g~Q2_aAQZ!-ziL zqeRx)-^|zQ;?Vyoy@~o8|7hgSY8hi~e72BAmd61G8%rQmu-t{1QsO{VOk3d;6hPn# z3m8D!nW~eWqP1)Q*$^&Vj1uC4Tq0BjK4r8v%tP0d(x^iGtL>LanhdN+Vj;8A_tosoI9t?R(L)UY(GQ{oEX&d> zR-3_{QspHyu?W90G_*EuYkY9rgR}wOEt0W-luVPn%bxB97K8Zc;H+^HHpGj%66kM{ zl9bDEk*-;MSviIPSJKl<0yJ|HS(~8{CCyvevLhCDU3ooRdt;taoIInd%82d&7BZ1VvNRHJcY*}Q*`(PSBkotS zvLMfdx)v!g69e!oKi}M3-u;ZOM#t!QKWM6l%)}I(?q~C??n70a&-xs` zWi9&)VUq0-^d%E{qex9kikc&1=QOt(;0nuohTm{OUL&iM@?${0z+&>Y9fD;;DeS5^ z7F1l8qx$l8L35Jlx~FYC@0F> z$wpa%xMWEbte&nf77>Fsi2^s$evw;g+G1^-_ct(=L@yXmqv)ET*Im_@QxS8VJhEjD z8UvS12^=#kvf|R$2ZY)fE#I#_UsO$t+jJ|U1>=QSkn#WpgB=h9W|_6d6=+&<4Y+Kg zEjAkFQFLYMt-NM|Ci#nHw~(w3_0ZeB75$21AiO23h-chlo*5q@q*bJy(I+;4osB-E zL^|Q#W6>T_gmaGu0kPUshKm58eRkVE;I3JGV~Ytx!F9%8%Ud`@9RAc`F$k5gfHuS> z&~~lAPAsa0Nfg~20-_O08PS*}x`i;dQ2qg79yy{{CUEWTh$i7k;;Q2gAwbIqSIY?C zA9;M0bAe11*jeJH@{K6awag&lIE8nYhUI*Wi;WD5E1Z0fr9}U)5op`W(BzC0T@Qwn zz)BG|ftr)E@6qg6K7kJEpWcWI+z4jKd^2-dD$L;$5p#fCw>6)MTbx9~Gb*L4FIwgN zJY8uG>P9Ftl%h17sa|EebGk^A3m6joRL8SFtGxxc=%KE%kBhLZK_(3bBMn52%rzrl zLucY3T}VQC1L0!bBcV{!mO7&p3 z^QY&Bg;d*sSS!gW#;e2*iw#0>alX>}oCpF)F_K6^NpCEjd!!M5*j|ve%-@e#g$)HT z!|8;^hiiHz5(6WS%a~nt3ZJIn7E5xx$#vG_v)=ga+ulE#1J!%|_2T~dPZxI&Pm6D_ zzpmr&ryribe#YI0*O%yl_WH}P z_xc3?5Sj6(3oPvQRp_(v`Vq;TNV~#)t|TAD|9yLhFG#6@*Qh3V{wTJp*H^gs`T6?h z2@xKzuO5HE(q1oapTAnsc=H`a`OD-ao>&~q;gN#C+wb1;zscLpo!v6?8-{e>IG~$^ z08$DAVw7|fJ1R}Q)hV__q^dQeIY9Z-@hR4Mc6@+1Y;FGre1hKEj#&WXpC^O(&M~mP z0Q}kvdmhtDU?W5eQ+tdsKgtd$hjQ}axRD;>G>xi6JAr7yPq;xCVAOmn%@6C(^zP@y z!&s^tN{xOMJ`yicD`}#i{M*onq(;~&qJ=(;W~l&x3WJJKtx0^F8sTj9ZOwEu5tLB( zqVM#xR-Wo${anllTt7#0Wdc#=NjC-ftjcJ`(nV?V<`qWV=-wLAzjS6tUwD0V~AH4~ReE zC^D~Udm6uOXr<1*iXQkTDu=L+o-+3DAL<@5Z1*D_+<)3B8fd&tAxSECP3~RdNBx$N*W`{+6iP$sa5T>PMq8$ z~F?W@)!nmiJrXg{qGn zyrxx6IeWanek{wmOPV%Iw(@6I)xCsU=I-Rrs!rjH)1r$XQd-8BmHD#^e9&`m$>PPi z%JCCa$sH6BfH`p<83;0Zd^rbzua|5^qQH{?H<;BTG@ z6K8$-(T+_}m9n}C9LhrZp^lWHM0$j={jwPlV8MWmp>U-;89upF|DJ_ z0T>dt_o{uHB9-d^|Kxn7=Q)K%Wcfk;>p%n!5h%UQNhk@7QaKdWHezVz1Ufue?{N$g zKi(Hf1_esP#{_RtYL`buQ&Uz6NAV`crdZwPJTxWB5+}##aUjJpp#$Sbk|g|Uqc8^= zk-w9(4JiShBR0FpvWpp)Kr}X`0Plpa5%VQorEPcpjVM}7b8i%?ed2{&JB?C$gN@q7 z#-MCF8r~rF(vC5>tyrKs8v1w-MF3yn(jUFh2pqk?xAV_2?wCJvT34MyutNAH%G)D* z_5w=o3rbs%0_KzHcm|VI{A9Pwtty?fd1u-ANc$j%YwK^n@D5-LFiSU?LVyH7sWq96 z2gApo77v^2-+C)n>KL)bP`lMiLfdq2*u|$@qjC?4ky)j2S`?4o@6HfGfV$qWi9am9 zTuWQF$AGgM+w57rIBOG~H*2b#Ztf~qZmF7MY_u}Wx~>e_bp>^LQ6CEa%jW#%1}yme z2?m7E&rge{S%HKa1{>rrrDd3TNR31l6c^S3JC*hbS70e{M_`)Sug_5&$gvh$_Q zbI%aYnB_Hdf`JW8$Q!08T8rBy38k)2C`#r~Bo=nfwscaDH{eL$Lf!H~-VuTAA3rvu z?SoL~sMlZX_lv4TU^5Wx*MbnEZB(ensnZFa#z01qht6N&WrClQvTiS=V3I{@U?LcEEOF`NZ~U@2_%uvysp+ga|B@bq|~Jmtq$&U0pYk!-WDT zxd4;EeWqd4sA~|7b3&WUIMpOE8h=u_;s=FAn{e3mkUEf z8p#SeKhMxxMsoVEFU(%Ti`Yot+?j49WEe$Q4$J{7nqavImp8SE&?QBr#TyN*>qmtr{GP zR>0#E2$#Yl$*52l)92sGYiId)N$ZCXCT5(Ek@&8>mBf)OB>CJ2W}Vd5k%hQBw$7~f zeeK@bd`7vqs5;0Zpgpt#waEba{yx068v78>P;D21bXr(~Q2SyAPeI)D^Rw|7p&K2= z)KF6qtC%aV@TrjNvO5p!mZ zWCot7GphF!3!SvVSYKU0l_ee@zq3JK0*@^y@U|b%LY4hgz)UH3M z`EmHEk<@6*ZQqATlrqi@rR$Ya6k!$VWAtnQ1fg{TC$QSw{p^kEE=DSd+(brJSCINL z&3xybkYePr88u+N1xBE#^eBIW2OObFfb`DVQs_pC{^a{JxGO)oPX^yfK$iF(A!fD7 zJdbE5iZN3#EMNoz72Ot2)D8V3_fcB`E6X5x$uvGD=#jDcA=(lFy<3-Gj3A||2}JW9 z#r#1$_4G1lbk3Gh(qcD+f}r|ZEGN@6$CD3-2k3Nv0I^x* ze}X*O`@efSw~vXK_MiWWfH0|V(feO%;HS{}JaHoguX+8v)ZpLE(U3ZuVW5-RY#rb2 z|B|{?Fjpb?GgN|qn@jNO+sJdI{uzxBkyrIMnIl!7sJL1r=R(7VcBG@v?w>P^%SQ-; z!cNARxxKr;3(vuxdQ%|w#d48CraH8?6fGpF@gZHy2#NuGeGZ>3bfgt9ME4xp)n#G( zdbDB&JUEu01TuAi)6Y$C&1BCUV58|q=0qbx8Qk7Ai;G3p!ufoO(E*5atf#uLHk&<-Qf3+m`uq&~yxhB zY%Oye1cM>bW<|uZ1~nT{vSY>Stih!xpCpHU)sG804kT^kG-hpV4s`>#@4`{5HjC!- z&5vS7v;eHQTT&?I;22UW$Qi0~*mgN-(t}(sesxUZ2{{jm1S+;FVLHOzY8=xjN%OTl z$w6pR5`e?ME=Jkvlu`tlMyBmZW74!#ugG* zm?%#L(^C>BGOo0TRI%e3SBkM<)MS9Ym5QSqUc8}{>$d?WuTNwR(GOA z4obig7*qx>&N}g7DW!}A(V7%jR$Pv4Ea1LCFey7!H_T|QH@1ppL$Z|Ycz|)Zr7}3! zKwVPnMvmQXkS4MAu|Gxs@ptiqYmq@Kg8@KGKo?3CWulPSk}GL4MZd1$t8f8H9U($$ zAT6bUlQV3kT6+?RuC@-l5dJP-72-PY-lsAPESA#c$kW0pXA8gSyXuZ>lHGN7C^Xsf>M0F| zq8t34Mis_0&5V#Gk48O5V?NYZ_VO9`0U}d4yxIIynf=3lm=p0q+NnKElV|?YPRA*R1dgu=-*k3nG4Ygwu z@14U+N@goI)vEa4KRsMtUtPiM{6jC4mXS1mQT6e2_HFiTgik|S$D{H7L?i%yfY`DN z+e$92Et$v#*41m1W=Yx^04lkB=#IKSLD@>U-_Lep;zuwvcTLkR4hbJImZ!9IyVw|u z_Mv8CDSDAkL2N6wu0&>unrN|JNA2X0Gfz{ZR8x+nS+G1MgiNdkGx*!sWA+U3P5-t-?1s2AC*2L4-R5LVbSC5X92 zHXyU8ErTfbn`fmMBs4*-Yn}#A2-JCczE8rpoUCymDRPj`HL#kmMQDbQ7>0e$`7;aC zh(y8YYkAE2LM$|__1^6QV1j=~@0tKfL>7Cgup2tYcgpT3DD3Ztzm?=e&>qb{?HoY~ zR&Nj66>r7ZWLd22VrGT%TL_(~txq~(KkwuyoL5EG2%tH2sZ8M8;Cd2u18nEKAnbuc zL>}ozs;R^hEWJse`osQLK|`WX7Sb8DHl@xUD1*Gukryn~+#uiZ1G*y5S8iGA5~0a3 zhR4tU-hp4Boa*xk0H4 zf`o`6BsT-!PY-rKA-LuJr)WWWG{2PR$(g&u}XB`~O6zQnmq+0)pLCk37&pHy&(ETEs;V zH?bWmYa!Oy+?lSpg-l+dI7CUEDgjH+!GwN{UK>prDB@s8ae3WfCkNwzdpZa9fPB~= zYoy1BfQfHx#{8`93StnjB8XS{z3g435{SY~dU^i+f|Ofw^~WF@DAnpd^7WMsiQ=h7 z8?o)71I-LX*plB(T=!T{Bvv6cVT}?(^yXyTSoobD;@aPo)F=fZ$(>U(qWoR`zD3+O z+YetuS0ILeH6@slrW91o0+7?+MCqC90y2-4i*t$@LnFl0YnI!_u!EnL4LSU3gWk2} zz}oU`D~Vudb1BDM95k&{k=Vd}Bq>b=#7F`2sAk|6?6MXKA7!X$^p>8Z=T8?uB(Gu! zS4G~P9GX;R$dEJA<8_FdMfu5a92l}Qi$*#T=CUYZuER8Mm=#=dr+Kah=D9Mr)&1Z_ zw7Uy<1X(OWp4<_-Wk@Cvv=bg6HLwEmC)a!eNQcdGU*=O3JL(THqUu^{D=y_5Gt}n* z)-j1tW@92&2~&|5nD_M}mrHXOAyk|#qa!Ie6Mm)lQ9otd8!8Y$Q$PiUC3vWKXQ~ML}^WXu%4Wzh9EQ z!R8^Fx>^@`Y*lSy>t(}Mqw_=#pZ;atB;J5~2$Yqh7TwH8mc;ra`21e4a1|2mS zp+8(q70Url-Y++O<`B{zPrw2EvZQb)8jQC-%M?29p>6FU8C(om^pVa9oB#bCK9P=uv?+OA z;i~!hMfNYlWCL=gz(#>jid!i@Vr@fxEy(Bsy~=og6vz?3QS$sCi(|;vK?4uK`)mg( zl@s^j{Q0tvmI5g~M4&_bBYFy4uhj8LwW%wRBV_Ur z-SnDm{EYGlrGuOmHO=(h{^|A#oU#2^_%V)^O)l z`BVRr2jIr(ufb{$_g&mPF63#|yS?h2KR)z+Km42Sif0_)`TDMS0vCerIvo}xDG7RO z(MA+Vkj(k?02&Lpxmr9dZZ8%qkH36<#cDqIu-ZdAq{YK6?)&u1{o)S$!mD8%*IoV| zj92(u3IU0S`gbxD9u#`vL+G_eU>i+Bh63vaO5|=nfkX*734e@beWgEwvIq|1tCLCC zv&v2CfK>xPDsp4b43UQn<4B`?pq8n!+C^HXs%a*NyYzK+;#5RV&wU+Yg>i-6D7?eP znhWpL4ni3D2RJ%%%H0~~l9S3K2xw&}NWC2S#5MNrZ?9EQ#MU^y^O(iFK-gzJp0;>@a3@cs%h$zrH z(=?8%u$!{(P-yK~`j~}Ze0-2N(#K%NsEE?}+j3n_McWKS()@MpalQ_CfD^(}8g)*Tq-2I< zQ6J3Sao15$qHJ#!jhxl?Rvrd}F$~^^9LhT;ah+<1lX`duNO(jmu1{pF>Sa@h1?o=0 zLBvWwFm>X55c5_*uzy0}vJh*!m*FOH8G?v0AC-ve(Kzg}!Nyx}+ae1#6k}mcF=Mry zNwh;#pODjUTLB3MRhHbsoTXFwGLrn@s-L)z7&fh&>SPugTn z&)E-bL)$T+}qrwlca^FbN&R0L3-#jl? zdo&E5Kiz#rZuB#x>#GIq$IavO=P<9Q6Co36d*9|6xganxU;LQM`XcK8eEx8YrgxZe z8D>k_j@l8moR`EivpD4Xo-^sn=|LE^j+r@%cTOW9MF0>Si~!J*w#YP>*<&-QAIwlg zvWIGS@-G_9Z91e*XJ;8?2{~B()F)wi+o3*_CI>u7#c4=lR;9@?YdHXgZ#wJSVwr*m~n{&_X@;LmDdzXP|z`p>XHvY_x|MpXtf8 zCjgxcJ)w$9l6xw$4XAPsSM@m&sB%Fq0#qxX0)lJGr|??{IWB)-F~}z&`UB}@2th%` zlAG0D{IlWTE`LNeNCR5qQn1ff*)0ia@P6UGFlVJOa!@q`{32^6?xEZpnP2&h#WWiz zM$AD^+>e@-r#MkNgxJ6(Xe4wvU|Ry1na`Ura&1c7?4v1Ph+3CVr5L3M*`B34DYifL zV}ZKG4{`(~tCAj(k*B>iP}wSLyCdM zZU$Fb5VIs57Y{8o%i(WJ1Q~R+Jmevm0$Ym6`4|`Jk2Q#^sYR37N=-nK7zyE~ArU9Z zE$%8cs$gjrXcGcMAfftQeVzgGfPgHxny5UjM`B&i@vCDJUy1f<0Rc(qM5elwIDxTb zEG9RA3)e!BhCU_z!VWjrN6q%xZc)zHw+g%2Tt9C?=~~P{P*%XPj8uaCzv&NnDnsavYA3-F z)rr9vm|C?nJ9v-2077VZJh_e$-akK``Wzlk2JBivIJ?w9xQmvOMDk>>u_C~k^K`-5 zC*>QO>CRNp87**tvBUNLK*Ls8;%C%H!8xv;;HP9If|saB`O+&Bxb8sEZ6R9)BM1Ob z(g!`VxT}1-HHG(8<0AkSrY_W5#D|i%)@JalY)ua}SR+gN8$KsO)E?1gbAS@8339%? zh(wCFJ@{p!U^RQqLr-J}Ew^geH90%51D=E037rfbYEl*^D zd;DHBhht&6Ib6$_kZkPtUciM4RXvn?I-2ikNhf^hsP!Zxj4oBtIeG^uC3cnh`L+cw zn!$Tq*_&cv3Y@6`iWG&Yfp0#8dyycv ze#1A08{O05^|zypfvjidlRuW!zrOq|F~Lck@!EPg*k&)mPMcHZ&c;hjHXRU5I_j5MQ+e zVL!797X^mF|L$e>FiNTiWotdRX1b8Q(%D>>I49pTULg<)IiETQ^Wl!35=99nsoPXf zBf%GE5Zv*S9g&m9%rs}JQ!6?{4ziwM2bWOM+#~D_*;bbpYJZWv z6d6Ly?l5>|Y~#e%Arw%`ABE4}@WC-NeaLB#*ozPflz1ILuf8Y0AF$So##e=~Cbd(z zz$RV@p-mCwoJho#_bJoDs4yO%A=W5&5cW6TX#fei?i_JSB22NKi^#6z4`PXCKR$MR znxI}LJ2mL{A{Dpj*c|f$LJ4vLp~_%}oKvJoo%|+>D^MDwGFZxGrNSa546t#M9ZJ1r zzJrLCogGL8eQX<-pB_>#Y$I!g=jfe;{_vl{N*?Fenng5TrcZ3HTXE>p0!3FhkA~Q! zm?mhN@-+`odn7Gi6+4Qa!F*S6!EOqNVuS^Wtfqj}xe@T9 z#=O&T{(XoAW&t8#sp$C}DkKSi@))75emVhK8e%QKH>I=F&PnT3k!6@s&9qbXD!Mt_)( zwMJtr+H_##olu@EkG^Yr*exZD5CIE>{(N(H@gt%c@_7IhS;Gpv>0qRkafxXb)RLn| zYnX>3Yw|j^L^Kx9Rz4F6yNPwJgk?lpeVj_r6#(~lzx#%NO`~3oQbtzdvYE@sEjAC~ zM6$FOPv~A36C@fm!n^npy$eF_?gfg_e?}7+ToOX2udf_g>|NX*-5q9pwc1}Doqa?| z)4_>(SQ0KvVzZ@m6_vjKRViI>0YIqUrsZfxyYLG&{T1mR^bQwAV}U1QMU%@AmiMZ+ zv`(N$>nP8g{--@QWxI-YLUjvaWfDOLp&#fV(j1+j*7@l^%trl{>! zW;BaQ&N)TK;ou`{ z_JqUj9-e$q#GzQNBuC0e#VE5->P~h*s))({h;0g{ZP#an)ld2~Gv0>@H!wccO@si$^$Ojc1Q;K^fGs0Nfb{`U7}0D|aln=&Zw`MOW3yrB zm_T@xe@R!7y?_n1SbPjWzz|p64#$v!7a7S`QP2YHMBrmUtL)f{2Cuhato8Dep;+4K zLAdDuJAb%-`UUr*4WLu)hCQMt6PjKl)?N9>RdE+e0e1UrhOW~2nGiHWLV`q%r_x^t zP6TZSLZO1o#SJ>$y%L%pBb8%sFS!bYCBc{EX!-ER9>3AkTb%M5;=cb-OjE;G^5el5 zlZ4Hr0H{zH@Itqp1*M%RsZx?@KEmngwm&*UNq$|#?g&~h65lBXEGgiu#hTY3HIbph zm;o7&br*(%*vlM!oTU!YOgt(X>7egA-~r&TWmTyW{1%Tk-8uKv7VO^eeSn9gZqvO) z1=7Wk*Jn;oaBF%ybG*nSg2Egk2#bzd(bdg$@^6K?7)dGdGoGX{)NE&4%G2o29O(QW z1X*^|>|h{4kdabktDL>nTq-soGnPehc_SfS?J;SX@}R!85hU9Jr>WZ&4R|mI+5|-u zP}3OYy9pG7jR?fFZz=vMfuHSeARqx{Unlck!C3<^7w_cJ(I^n5qBebr$)iR#a7g=S zN86~rE(N2gEanD}_29}h5sHsWQZAIpgnGZmn$k?@8mBXKE7+ZmRf-b}NR8CZPeA2cpe;#z5it)AVtN$3Vw1y>gqLW3FB;7dW^oeO4Zz8| zYEx!UG~cavK6Kk0p!$#wYxYBcB@-~pY{Awrw~AMT<8_K~m}ZJ@RnO?E% zsBr6Uv0t2UQVNA!80pU%8`Nf`Rj4DvD|(6f$UCLJqMpii?+%puoVqTZ%5@m2i9$~; z-yy2D<4fB<{&j}b9>eth^&3W>mu7IjdQ#&V%gi0AAWPEk!*k+Vd1!DjpViv;H< zm$rNv7=uWH0%w-Ik=dLznk<~X91IQ{^e`r_Rq1o-Jm{FjJsa<%@`7dl_187^&&iI2 zMr)QWFQ~$S8k;Dgi#R3#7|zOKTq-m8xL|UzF&M1kr|mcFRk8b@7OJ%GV4AvBjfo+#O>vm7bN z-FZDi6D%!<=p3Ewm7wDc6D_^tmhN@;>E`yHNngI8_SGta9-dbwxZ64Ip=(?+4i7^a zpy#0^W1nq9nGm5aT%TN2HJ|yL{hiZ2Xg)CIh|qsHzePjLvUms2+{#>j6h%COe^t(` z`B&F#!PxT_<&o%_gy#(8GNYu zn{BiYLdghVgy^9zZPUEa`eqroeTw|J`P#|@Hi8Iao(lX^H_CqFOvAHe`X};%fERxS zbH2!nURwGB7xUr*xeGg%*I*4i zuBk1&0cmp2%kpJiuDR``3JrQ3ef+R}^Z7HnGgJ{ARo1IDNxUyg#!O=s8-cd1ce02% zJg8Gqf~A^X1_2ZBBC|}oWhD_lHj{Vq?UKD}Lu28~vO#`<=Tj^^Y*v52#GYB{mM#osF%dbb zmzz(OEfI7eDeA-t%1srL9eX;La^BmxF@yKEpiJC^9H%LO`Jf)H%6U*f90-K|=@a}S zMMNvezvdm%Yk;Q9a0^e+7|}gn8d3moTQ#+XzFJtokQJK}IFoup74A~Qbu4E;*O@%?%^}2!4Kui76txU>Jl=Wuh3Qb_Cx+<6fx zK7VQ_gI+Q7Ru<`xLL#CQtVR-|eky~?kBZ+4u$Ad^#5kBQ}HmC5*aj!D}7D0k0GXcBU`-_jaNLV@ErazLkIshYp(SXbp zBPI#1qI5{A-;%je_%sm{bw9+jvQ;E20S&?eEa>X3@&2QmYrsUQA?X!0kP3&!np!d% zRUs|WM=b^UilTfK$7dz3VG)RoWZhgY3z%y@ZCMUatKpUe)0)Bc_TBa~BXQ^KC(Fjg zubUrv1}}`)&r~dmEKXaU<28SIaJ+?BJoIE#6CW)3-_(4b{TW5m7GE##yD8nVTof4U z{B(rONOTp(r4&qt4xRWMP6{afpDr&h7E+omlwgyx3_o4oUB17(0(3*Ljymf*slY8ebTdXoL` za!XljkPFI2@*(Em!A0;I$5X0T`%gTr9aGxl(gylu{BlCydhv(z7IP>B8@`xU{K2K5 zFjqpGuTFPZSk4G^e);>b-!ABxiaHy*Zf%-SZebgt6TSIg4o1d*z5uH8YtTd5;|I^Y)62H2rtupLLC?T4UFj z`{WD)CbAhsY(gIQ7HabQ>0bp?2@HkSGRO7gz)J+{9f2G5cGa%j2qIE9UD99;d{{Dq z_8h&{{0s}CsShY};yy4Bd7%NJm^f+8&?nbi>ZUvr)zPrR1AVAY^ zQ75o`m)e*srt+0a8O2?$$lbDhylR?gAbhJ@AR_9Gj(mVvLqs4)O<*_3-Xj|WSmOWy zu+Ef5>h_mr^9vdo3fn&p{O-*yV@2F^AUz<2FAq>F(?# zl96(`l7T~Q&h>4Or35PCHxYd=MM%LP*0a+?EH5msZ)3WvZyVA0Mti zfFTR?gSEAY$zJ)2#Pi_`Lb9{>O_nOb9Wqu^bv30XL@KOvY+7$$rS1eSl9YIt^t=Q6 z*UE}YU0Nt`U#--NrzF&cILaa%;aEUE_?f8aRpmaaD1u_=ULE!O@4sIjQ2Z8G92ApV zHBvhT0O0=$0DUwDJ)9wfLwX~jd6x*rB_Ib`^!m081G>S!=|yH(`lU`=I0Q=)R?giO zhl1?k+u8p7_5N%hMLbb}GfL_dTU>d}4v(|ADbys)_n#{amNb|G3v$|v$OG!&`ldH0 z@fPNy%w+=dZ&kqnr1j!O5nN}$)0|Z+NP~7UYkrmPIBz5!x>hBoxLK@>FZXk+45g4a zGV)xNBL61I#bsUh`TPTM^BM6%9Y|FlJlu&CElIdn4*^N$>;CRBc;6}Do_0okwY;mk zjq2lzOWxcNu6M0|OH&I>;su;bnn(uhE?Z)eRD3};lH1pw*#&?&RB9?J$9TWO?cG-% zpzXH|ZG+;IdE5cPVn54O&FpV1nVqNcNwZ=-@22pS?DNs7X_-CZBuo8xGLz5XSBYV! z%Eht))4E1mcbs1<4H)%(WHaW!wl~^uwQL?uq;nz?`9fq+x%Qo5zJ1{JQMW-s&jeC6 zh({`GARQa4WJkUBO9@)Qr`@d=mOpgAqDZD>YTYuunPH(; znU;kDh}TYCWWnk_ql3f`fcqe&ARe3r5W52RLLn^Z-O2S4)(B6n@^H_Yj&aTQ93`;+ zKt$~~e#9?C{<{1mIU!}2@wW1JxzDW`y4CO|YLIcGVByXV*K3Z|wr1DVuNpavUqOem zvS(fpEGhUnknI;YKRYByH!Yb;sA+My4<}psDm|0 z)F>+4lFXZx5DO3x(UoCm^@MC#7eC%loyX8G9TdY~kKv9UB$tytB3c*ZLzSU6ZUg(z z@pys)5KtY5_`h|19}xz&m?&^0BAfpz0&UPYswp7J`{|Ei#;BILhX(-~GAcfNcXjpM z6C41EmrIZBS8yxLwl!d070lR%PS2O;TAK!<0d`zK;_UPB`L< z)Ua>Q;YJ&8VIbmLjXvQns*u>X{qHJL0W@6R5(+2v4JfLY1t4h2wj)U2@CCjB;2uaL zguA2DZt4gXY*1?qOiJp(2`p2YpB5*VPQdxMyyC8kh=mtRN1=p04iq4{hB_rgvVoJ7 znwODc=XWS|sAQsHTO6hg1quahCx>vXzTTc69dAoyRZpuiz(=NW5!9{1122S_OQw`Z zzIPfgLi0;7`3L*RON zq9f>i#nU>&ymVqmAcNy0Rt1!Q5`vPXCV?>0q_G0H>9)tyosfwu`hDP68iaGB@c!qmJt(Av8R=wLEK1 z;r+?H4IP8NOgBH93M#A(`s<_3?oEQjfGa%hE)RTo6fRqjMb0j7oQ`;oHIy{nGfBx> z>M|4Ew=Ei;yj4<~d1HiCWos}3pKb8x6mD{H5zpA4zWvivHpJ0dy>#q!AXz+O9k74@X>{zr z==ib^bwy-*+Y~eEo5%MCNB~7jFBT|U;;RP60K>E*mY|l1)#Z6iT2^|FNVC-L-xVL( zeDFLe{&|0RF07i39trEoLi9RJ;j^iib~` zOea=cj`1GRNDd9pOS^*vapbYHoZ7HqN!HN-&*69l4Pv(bP2(rP-K~l&t z`s8z(A>Vb#N(n)@3R5@htjiBV{g)!1#s$fdfAdN#4QaeMoB-;QBpb_Z0fTp1(KFQR z&P&5wq=KL2*q|A%)I~aKAb8LCa3!gM;Z^P0_O|6#^-j~xn!h|c+dkNTg`S&d`v<3> zlOuSIrJ9q~O;B0DR2>kz^N%uDL=I>*k5`8rQ+_6t35`|wq>2S1=Ta-$#O}&R?~e1T z)gDO+=Oj5zQKTH=KkE1<21_hyQHp)Tgg`7=R(lwT`+0GG{qO}nRFQ?EIROZPdk+U#Ltq8jiNwK-4Wx1Xz zMk2&(q@I`JB_65eB@IjysKQ|y76an-pZ~o`4PdX(XNm$*2ngi2J$-kAiUvhJi`tcI zph2?ZCIxlL01kYt5nL4f5+UsS>VkqF3UxU-NZM)Ptl<4`bXkE_Jw@*}DL$~)6Ym3o zlmwsXi037lC?rxVM%GPh=EG_3?|k-JfD|cgC844n$%A^mokTS_mQ#q_1?pItHu{LL zHB)J%xNIqF5>n#vD-6f2cc;EyG`ccp&}g?raHIfp`QClANVu&fH)vaVl}9G>LQ!t_ zm2pHO#mVgguJy(KwRE68zuNlcesRYpI7vNr4^SoxUs3hK@N<%q(54yaEZY?*NuZD9 zP$0Bp{-(bqrMdLv5x-UzG#{d<0ODsuP#FFvoQ-+sM8GU*Jt|gNM=iR=)y4AQ?(*N! zN_T0=#s*=^KQ2oHp5?GYHCJ&^=KzUpVQ%b4g!|$KJ zc)sysb-22=xiMNDu0LP=3B`|hQCLZYhxrB2w8m!`=4B6qXDkuK-CQGrQ#@ zlN4fJAgBU$oUS|0`^uimP7dUk@8!3grEzq8I^8~br7{p*Yo9Q8=m?LvGEg#MQJMp| zU{}}_pwm`r8`wa&RE5HjLb8mtAu~lxhgkHk5nRj zQTu?6h?s5{rmBL(dM2qn3p|7@&{FMPcbw~ox`B6#d(f#kE}tGwP{e5(dkpyaKqm}r zMrN_442~e#lqM;po8F~-V<#J-nLf#?V|?1eaLnGAfa8z)ZF0%vaWktN7_dK+wxK&< ztUa;#=0qht+H#D$sT!H7gMXL0czRZJjuUEXJ*E!ze=pfZJzw^5LBco&5t_l){?5+i zD4-D2XE{Gtc1ShKwzNn}L7w#3e&i^3^F`qzOV4VnMqF9WmmB28gtFjK&BP;wB9N_6 z$|GkGHW)X_(sE>exG>cL!iE&qh{kf@1JbWzn+(3wif!b_wb?nfikNL}2YX%HS4W+2 z>BG&#^?lR@p9b&s3e0Gx`QlHZ;gBbUCu+&B1q{K7D5gV`q#a z_xMj}RMmI7n^m_L*?J_om*v8@W^nae8p2SuxAHl9&9msZ%CAFeMPX?5+3-=-98K}C zT?hjZG=JE`T6Cc~g-;t<5saL|trPZE*3v_!=j{F^kU;R)Ua#B%j`C+?A-Sa^GJbtwPD+rqinBqM9{~PN;CSPdlukJ%`sv<-#A_do&z9@8P~{ z8qsVqUL9)t*SNQJ1^j+0u(B9Twf;d5nR%z|BR;Vbf}>ecg(inlorGnfz#`#JDVMrC z&MVrPNKBr1xstY}uXNH&;Q2c~cnz`1T~J?5&NYWm!}$cqwFw4{{qu{9+fZF8l-dI3 zr#|HU@a0&BWsGSwjUrDxa=oMR3 zqC)^W&VHCDU$S0Y^vNnfYHngZHSo@r&*{&UhWVr&0C)AR?l`{|IGbG0$}rYPIi5mk zWhOPi?X=rrPd(fvBLYnczF$G__Cj4t?nsno7Ltw2l>Gb&lbhrbGJD~Q7^eY4Hndh;OyouG_U-#q|a~o0xv8;2=;r^{wy$%nY4X*eRX-g$T>2@ zGDl_@b7bU^nVVYVr_xbkE4 zU{Y&j-{5tIM-55p3EaoH4FVA_kHAV{S4m^c-6d&_^i&R!WY)lopo{VV6B%hQ_X8RJ z%l2d+H7WLAFZsNu?rrdS%gcXFj#s(6S2Pe0hQfZFi})zFM2NMF-Ic*ZS-J{_A*>*- zu9ByZv znOb(;p3}=V7_L;6N#(VqA!)tqMUc;entkhz;k5AL?-o{7_Y=nT#@iQ`x z59&n8Idv!_{r&Ly`r3=N)%BIl(P*^!e0}k3b#vF`LY-+AQ>ximSkZI>1uRdY?OCba z6l9HMmQqy$d=E>2Iz)Z5_ zFb+)_g<2xDl74l1{jhb=B<_<-<=QdRp2I6k1fj1uAMh@x$A{A26Gi!^lLM)a*DB4W zA2Li&-IAp<;Wo?(?Ze5l;rh;w*2u>(2Q2RF5SNo3N-Q~VW3{9mgJal4X|>)G&WP$_ z!DS~zZ*DH2NeV+-XiyzTwg&c~j|>=c19E+Uvv*pWNMFNahzv9Pw9_f#BbkWhL?k@AN#rld=xF zm1~ov7}EEMmPb;lgYDfS+dORSQ&Nz`6Eg-zBqbraGH3vwVhZo=J42xo0aTyQzs?Zt zkT(Ykp?r0G;GO~|gu;o|O;X4oMhvB?CU!c@0}>8#IZaoo!3dz?Rn*P*Ns9X_N)z3u z3Th1S2tP4#;GG*40y9`rKuOj#`7X+!&|=NqyW_m3qcLXhjZ{Dxx1;nRgZH)fu-?b zU5!!9DINaUJ+r)JA47dJ`BTfA8S9n6gyIG=FV?^T#hk~7wz43vmRmF;!ouS;9G;xMLkwemJYJXo5VCZLqwKbO%*%E3 z6G~=K0Ce;OY>m%$rcw$t|8o9t@nSTDXBmv{>k2wyx`$c(8qjszts!bpSX>#g)eX^h zly=r473r6Q#m9S7Rpbf`K8XI@a4Qx>kQ{Mne3Nz|QbGy{=5Z@~n$+;^QxtwlU)B&v zyhKVgM*d1Q&5x$aqbzfihwSGMpMuRsiIJih)wSZLz&RL;TD77o@Tf_+0Th9Cqlc3G zgKxzlu~SRRYfMn$0}enlaZqP&=Lm(ayV4`cOo+6O&S+F!14_T+h%%w5-#+uE6x!-Xr;a>YTk|GE8A&2( zt142HcR^_@ML}FzXKx0xFL8qtlSSN z^1G?DY+SUvXI3XlD}MWQ@Qui3ULr<&^KDk4 zr~hvKj(a3)c^Y~qaUJLL>&4%f zKTkIfTE#>!BsxyEC9{%KXa7n%vv>pxqUfE(y95zgreH)$)N(K&!|rmq>X4@JsvR5O zE5%1|gfm*0SshPAIhNKVF9&0x<>OF@ap6_JhBnVHCIx11EVlWsG?smbKs9q*@=n!p z6j^}E15~b%^_0E5zPw+OVmgAIL}nBEP%rMaD=mlq$T3artr=a+C5E>1;r#Yuh6^MU zNP&h-!e7q{N7h1+1LHMVO-p0Q5M@$YMvK=1GM^6@6}Th1;#A3rE~s{xqa2 zC*!i*jx2T41a3zXlE+6|nsp7=De9^2kI!N?}6c z3~T=YST?e1kvY23sY5iaOJ$>cz`E|$&Sduu>~vlhU}CWVs!&N%b=B-tLl22siDAp0J!Zx`nuKApqOvlQXJ#pev2X%#`JZ?>wEiewRFu(&j; zyQ;J!a7%Pp=(>6Hhskd!UWX=<(}JOEdVC3KDTPbQfT|dI>!AsuX9VfYnR6M_0+#k83Gr(4+21a|4D@bcOu$MUDq(2JNeD=u+$*lR zTb~%RbqW%WT;uovl~1H>N4btc%uThru?Q@#CXL~E6W6g{WE8w@n%b{YJ9y?j9=xOm zH&uKC!$%q{vqO$}ONnhs?ow1jxn6w8v|*=H3iUvBXt3$<1bM)Y9zLl6*LS$;vA!&Ok$u|PJkGhMHgsU%gyJbdRG_b9Xd2wT?L@I_zY*B^O z8*AYaug1w}@G#jje}@%Qn=ixF-)6YeVpu+$rSGC{zX~_$Gok@}R(P>r^9Hh2I$3)TyuxXb7tFX4tGo{9 z;}i2BY14}oE)malpcgPh5pRTc*e8P94lCAg1=AZS0Eg!pQ5?>(l3uj7e zbpW_$5-*S$QpvW;N9dcyOMn|eK`pou$f{F*uq05XUBcNX!KeZOBho#QvD%A6aErVz z@okc(U1R5aCh>WbNg=QX6x@M1L0U(Jj#Tl3s=?0IEr_`3`Ov1$?cKx!9Nc;j|F?i zGdO3zd$>ElAN*nE>EI7T{5Se;@K1y9{`vMhz$@g7%kt+w{^EsE%tuG$TFV1o1(W{A z;6JS&fvCrD|Abz2`Q@ULDt;k7z{XRn!E&jS^#;$__;8f0x3t@CJWFC~dEMp|UOjmg zg(Dolp6(vJ31^FHBFIv^c1y+wwi_H}HpMYNy73MklrJRVM0MGDQa(xFEIy3f&hEv1 zcebGND^#{kH0ul{Wv3)Qpw|)h7d;@@&F$I#&eENLeQzH=+=C)Mp_8np@llmr`h0Wo zfPnl$j}*wL`T%wPuZ0(*6be*cI23kIww1}Eo|wx(6{=;IA94WvBMouut{#vFiT;HO zT%nZfNXxJ4fHwm;me|lbw>s3->8Q7vLnCOjA^hxN4BN)CI0ntQZWKf5)+%+QMG5M4 z1OAfU&t!-=Sssh_j}K1{P<9)7W%>>7+iFcQ;sC!5mJ(9$3Lz@C=ia6`U{RHVWGB$O9 zrtuXa>pD;0YWk-C%%mcF+z3>20;N9-Q{DZ1S#|bCZF-m$Qfh5KG&S^#8Ix)AN!~8Z z;Hv4mNN}w#!<3(ut8GyYHO-f__KK8C8oq$$4aJ)=MKjTf32sSGM2cpaGxFpJrajik4xu9i!-JA$OWn_~Ld}#mm$#G%EK6vvQO` zWIk?u4Oi-zo=W|dIohr#zG6@;z-r`U{6Ftc2^8dyh@O}C`y9DJ&^3wbwy9bw{VqEhgiBHDQaUvHc(aLZgK5?Lf;79TZ%K3OWhgfYYLW! z6$np`ac0Dt+yNA9@E?Oq)K5$CglB)=*+cK%!5;_dqd7mB13fa?I(w=3(q(+FydYGQ z!GHez>GDdN(Fwp{|4^vQO82I)HUfMJlgZOnEK+jUW@!}H$z^gHrAM=MWROfVN4_lY zU}--RDEQ%QA3W)3EGccnkg%BJQ={kqf8t<>DgJ(OFzm!S^xI?W6oMupr6quJ2<%`~ zi7wedaviLcrmexfg-TPud|CYCGPhSp4da#dDj?L_*t23Y6UA?6#f!L;PVpErre9!I zJSS1dY#(_}nm7Nv!o@}%OC$tSC=%n@A)SbszQE#umKIk+l{l4P|c2r%eS zcd}lkChRz@q9sPD7@!YZ!yr(DM~Ckv1fuQ(YCbSNq>Y_~|7fuE ztm-uXY%mo5thtb5R2F%ABJ6Nwv{qmT931hy1C0yDGrbW-3Eqk@Q<<8{+wI9~7XB+= zu`z)!7(CvxHyPC}8@i4Xt9uk*VkVp)-^Ck%@d1Vz|`jd5pd` z4cKark+LPYtTjhkt*i>riiT#vCzH?%4&)XA<>4T?QOlb2H~aD#S|n$3=D#L zgk)rN8%=vr|M>p$rv-6@R8JnR?=FA5UR>Zvq5~oF;tr*2=nkgO2kWab)MkAMxvqp` zFcZuiRpEfaZqH;&)X(x~zq;=0j?G zqjLFDQ{#i1)0@3Z6Z~@i`oOh(OSefP95kMwWF?4RUl83R^lgYK%K~?kUWqhpYQ#HEi?`cw#@EU)?SKNA}d-EpXKS*H(!IpsN!Np050`rn$i?5p?13Kr7RHgbXe@ivvNxKX<>gQ`V+ z4p5rvA#j3W@1(Tsv)+S?9vt4S_3vKdTo30cJcy?510nWG%$x(rnX4M?Dtx#fhs3_BIS6@PwY>FOv zx>&NHRgitH(AL0*f%af=MM^vkrbG$z^pAOXd#h&oC?CUDh8i=HhMR8vkodyIs~TPo zCD$BR0|Dd=5xyC?8Ik{V5>-T&JyYc@K2~uQhtJJm2?wzMWF>svAkKL*J~=V9SzTa& z3Lk5pu~+KZlyWcw{w1-7rQUB8MR}m7-vd_BMpe}UxNlb8ir^?I>q%<@@ZRqB!7DjB z@J*vtLke?s#HJ&JEB?(z$3GGllnN1Z`~9tNj{7tEC-PaT>3|8K9`<}RU-~k9`UOGo zFT=q<{KMc2PB>c=mmu2wuqh7gLEusqZ>kX06lkI8;F%&>uXRh=G)I3t*Dek=JKdw> ztaSR#38lS%d3f@UA>hWB95It4yhbobYk``2#E($QHT@wC<0nT?Qow0_etG+E2p(Tw zTrF+~Y=#U{0z5nioLWzddaU7r5Z;mW1}R^SYJHyug+#))|rI9Ap+1HTufOcOg5L%c`R*hNx6Z*%%!0Dha}vzC@$nV zsxvB_RB&&vj!+%b_!LP@qe4Y_3M8~L`03^f)zuJefrWAxn&X&!b+AlbF|rR-!&j|c zL9lYmmF2!CPZ5T>stvI%Vb3hK%df#aDb2(9)_&ktJ4z~59&&H*?&v?yuJ67qK3t;q zy1;(EX81rY8DagG?^6m+MI8fQBk{p8+`YE{G;!zMP9L~G&^a>%k_rkigiE<+5^6ze zkfzJ4aXZ{r;}$1DQvPEjKAv9QmiB|n)U%W7>B}*01@(r|w>M{9b|J<3@+?y$3F#lM z9xjr_9uIiUV9PPymam{sDbfWg;^o`Z04*b|$n-R%s;Q_=Bb9&#Gz)PQ5H(F9aj80m z-V)POZNF|6%AT(py^Qw2%~ds3dgFo3}&a$zi81U zd^(&;o&ld>r!E0!2&_RuAUo6j*OU3q$_!1>8An2qv6KzEv0CtV7yU0%G{@oqkr9PS zTneoYl2Ae!FSNGuVngsk+}|9A6U#Wql7B}};BO5aw>^P{Axr%_elm|cB_RS<;bN0` z$wqyTo*pcB?hMI_)%DF%T4G?Yt5l3OK({P$VQHbL$#p(_dJetvd{~(_h&faelTtxW z)v4E~!C~2PXpCaGB9IL%UGu>pV1(G>mIv8`p&}klO-_m^fFa4(mf${x;YE9td{2Ed z-BKgey`<3R{9^GDWl%39L6PzWwMi5GL1YDJ3O>jGbJ56H&CBGK-J~!`$zG|nr^f(W zV(+_hPNqz8iG4$gI;ZueshjMu97YkUL7MQjoSD-*4uPSB5*~$spx>Q77upN%`qVxHehuZ~gPk51U zFW*0)f!llu$4EYfk_Zo{+*P0|KvNF{;)B1Ws{L#SBn2*ps~CIb9kT5m!S2nMqmabW zs8D~}hiQ8(h*NM#{^ABZbqn4HW{likQciPP6!dB-@Stf=)A7;H+NNbZg*rc8o!)t4VX|A#>gL=87{>mLqc;e zOsO6Q7(_9Pmk4&A9DV^4m)~wo;1>zGfolx3B6b-d6?gaIO|X+eg)CcDfR5sg8gww$_?9 zb3_#lxJp&w4%owzip zhGo;#^8PhULph6!Ne+};A*c&gM7;fUtIr)!mx!E8eoz_xwtN_h_zteW*lBMKXiIJc zB}`(ofYc-=hO~qxf7jX-Z`%YaE29c z1+a)GMRm{N6XbS@N;f|g7b>fOEjBhT3MLEPZ=+d7TO3(n3_w^;kditBi}OGJ&*0_% zhPE93p3KmA(1z7m3MuT{P*LKk@FOEjx4j3SPc6;Zx&d&-Ib^GrFkS9xe+bJgEK;{L z7%4)Vm9};S!nnLQZGS2?fR{G4rq&E@3rSL(dwscj@eIq+i$-Qu_>m_w#sAAkcmxp%^XBLVzfaM{kcxjh9Mw_fdK9WInA=GiwwOH?yIJut%R~1xR&XWG4ojbo zmxj5Zi4S2X*iEr^YBeVCi?YzBE2Y}ceo#!wR)zc{^zq7KSM2ca1bL_k8Jrzi=n&Q{ zl&me3shq$Oi2R~G==8=jCm)o_O?RmsN|2XeoOwSC6taVp>)JlP)#WP^0!a# zV@B!I!PVW>;18><%+ioe8golW%lON(PVqRrIBZnrnbNfuD$@yU05+_HG;VK;S8_-F8p@5hP7j52Di+Ewv27l z(D7a^jig({4`-nG-FHj($P9V<(>cu_`F$A<4oQFha*sw}%L2@UEs-KqKiynj4E}L> zNxopAr5rRZGs}`DmW>k=dRFwQF@a>4SQ(jP0thEYWG6dFM-MxAia2x1C3nVJWi7oI z;|~8+6&18IGvJR}a6u~}xSlO{cLl7J)-`|kS(zs_Gg=TH>0YNN-;>ea{a9eh`{!sQ-x5hZo-U9WM?bH0?||SJ2sgy3ZV z1)(66C`hO#4%5vcr?CV?lzfO;-YB<^CgbSDDel3flDaz`tP4)}@hP-cv?rHKk5F8y zxisb?h!sf^2cAkGa6-^Vclpa#IL!3Ja+`0LzCm9mZ4d>|oui!sZq zzz2m@+0`Ox1`}!@**ZB!DtBm7xBh#0dHoWYU14FGLZL>5h$nbAp@E!L2c|ntipq*1 zIYz`ORciPfc{_{u`|lF&7Y$gkL{j*4|73>s*gtwDW{oL_q8T@4;Y*5b{*&hEj^G8d zw>;px!qr^AB}rGf*URbg*~v`RZn<{#Sx&nLg#Ml2xA`-Zsc}F_veo09S)!idq&HEd zx^7hxtA0mCDs=RS;;@ z!VpLa=~N@L6-WHwcsf9;xuu6yg|wB!!xYy=anXW`>f-4+ot|xP8kAfH71GkZO=$aJo=!3@uDcvI+LT+)csZ)oCk`e}olRs#gF3#!%(b7HW#U0@Iwl zm8M#!rmrnSuZ_&Bi^4?(`QMa)+l`|NN0d}*Y#9D!Zw)(!H3VQywT!k&O3|J}dOkq$%l_OxMY0*JyR% zv@I_Q!=Ot1<(kvJX0C^1pq78(CT5<{YBAnoxenPU0}dd;V`kq-$p!$I`9DUSD-$(4 zpv)=WFk>-?uUDPI8aAeJ@aI1d zMr-cE=@w&=JjYm4o_UURHR#q(C$ClHp-w71<{uX~AD6z!ouDo(Ba-rE8Ev&bUY-AV zhx?+9D7rN+5B}Rf4_06|NWbjU$;n`Q?=PEw{T{#N(hJ-sRl%jreZ^6@nb^gE57}GU zL3om@4}uN_o%{0Hkr!k6#&wflskWz)fGkM)ckp2YgePT`9wAh^x_ufqF&>qqW!E^o zo=Lm_8PfFZ1QZN)4NcgUZ6`H8&>BYjS2$C4DAHQ7Q02>f-y>c~iR%B4`^~VqM`w?Qp zm+$B2SM$5Y{lgb^Y1S@2DXPzs`aHz(jhuMU&%E5Vo=IuzDh>owS_}UgYva2^IdGC0 z6(p0Q3n$we=kj9qY1LaZcyBvLaImIX+q5zG39$m18yQW`zMC%rujYW9v= z3lhHK-l)2RfX~@Uk|0J8?Kyn5wr*P@^_9P3^^nH50y8PP!Jv-9ifeR-8LmH(C@M@C zi9y+qreNz%(^d0wixwpA#N66~*)Z#o7F;mvPzrmBAdIgcrogJwB zs`U+qu4)==>Kit*d@Ug}Z?%k(P?sxJp)`UOY*tC>a+lUD-l25#{Z~{BT?MPHRrp?Y z75Zvd!xTTCOLZY!*j$?hA8pemKrzAQsxiS#6_g+Vl34(p@FP}|DGfub;_yvgppbV~h#&K0tJ zRI1BQ|kr{pFin=f0a+rGl>t$z+_^eqV_O#Xz>==ePnR40+BS3Qt0RkE) zNf?5#MazmDqj@UGohu}MAzPrvsnLg0RJGCP$_R!`QF~IC7sC+)Q^1pOIZPz?P73Sc zbtQl)s`jlklADFR<0=(#pn&}go7HLph#~;wAnH^N`}=tjrVEWbR{{6b189ft#?@7qA`INFBCl8mvTE~0y&-S zTJDY`D=9CTD__9XFYej5;!m=|7yXP7Yt4nymW1g!MA^vCQNBdaB)*7~gc(%;b7vMI zS_0pJnhes%g{f!UUfg6?g*Iv|Mb9K%#AeWMU2Wd@#dM}PR5(eaxj560m`4a?0|rr>=?)V*^Zbv&;q zw-)Wnf63#wwy?uhDK6n3!WohhdxOO7o;wK3>Ew^te_Z?fD_9X<|{O3~pDSDJSVA#H)dY zLhVxilb4Hlo1QQ-HA|8HWa+Ru)9=*ir%V<`e@@Y zV4AlwaLl-P1KXlxVtk-5%J+JNPr19^QGumRnrVgd7)O}KOen)qDaZ9{l}>}im$DXD zV`_?s-KzIv{wCuu)hBHke|7N8-kYzDlMMow^DUY!z(oW3ot~(YQMuE12=>mUQMhNp z84}xxmP2F>F0jIQ!3|y9Zq{xE6xSTHnwIMn&pk+GKTn>m4i8>21T@^R(1^r+sezWe&DP`PE?)9@@1lx^)j*ZELIcMR&vI(zI^hr#UL#tS z)}1Insikuw`jw6;Osdg*!);*S4aCf~RkJOxyR22NrC+^p;tMh>xz%d;WqX$5PI45b zZx*)>DqdE@lwoI`SN1j0wxSrLtvZ9KdbqlJ_+nnp<}R6vMT-nZz}^q@6yj^SU^?h% zwD|yvUN)vu@VKa2HBxY6cD4|JlXKiSTlqj+V4WApmSTvt6lx()ZZey8v`oBcjXWzN zNK^=zdt3~oYBPC-l!WR;fk}0)USTYz^bNYo%0u|bMBP;>&NYkeC#w+blR|fmjzpVT z@yv0U&zvgzq{8VCNaMp{>fz)9Ql1;%21=`O*}SC~*XvsJMJc#b#2m65GMaN@xI#pH z=Tp+QyU*l$;y%M(S1Sk3)zPqd0M<%Ny`OZj8zfcrm8mP&_;AV^Atx?Z;E{=3iuru& z)yXOF>{ky9X|gL>7zypZ^xgZveYqce_YXRpE5l_XsfjO?(x6l-;wbjC7=WZgyI5lUkf;pYsndd;d+ch|ipY%ttew*34aA-M~Z{9D*e*4)#vRB zTc|w|f}BZYL$_gCPzp5A_O>@phlbT+W-EI@19db@xU(}eD;w@AQHk3y>2ycOC(~Va zVJpN!cbxkwZ3Xe4KwHG$5`HyGbUQ)AUH#K}HR^1V7Za1d1cnUUj5@Tn`}pMAlO4*s zl-azf6a!a_g*>DwPc=;U8>NFxNXgD6b?P-d7wD?f-aLp1vo&sSH&h&ow z@>UYlDf~&USr3AHG)qt?Wn*jRY{&w-JI=Lh^JjQH`}i}4>;=ijTDXF0nS?(%x08he zx0C*1-Z{|`BO9{!Hpy)Z;73=$pm081mtl*8*%yDwaxDVDq0}r zQ^KFr08zywnFS4XDJ!Y6&TjxKL+Ly!n=D7*k7k?D(NwzQ+ywE~9w45r4p@~(X3kYZ zx@R%m%=9cn`#x3ZMDRWQJ`mx*7r((?jS^oz13q}9=KiW7K9k<5hs?kWo5EZQ&&tr7 zw1l^NB&_a1_|mh&^#@-C1FX>%#SoX7BHfy;?ZGAgL<_f^z8!$qe6N2b3F61@d0?8n zWY#3W%=2-LlFj`m8y$1iuOMq9x=IXRb1Z3eP-&G8$;as@B|Y<>@%8!D&5wjMg40tU z6ki1?o`4R-*yLxE`8H>Afl__WVS(dAsxzdHK%)Yb&snLg6LRdrN~GSjGUWLCQXy$h z;UjD#%W|Tq*cH?ah4&LDGzZbt8Z0l?n7~W5c4Tu@^L)PZEh^?m+eIGNTSfXBf!NcSUAfvi}568i_Uv*_so!YK{qMmR#aXd+Vh6y1xI8)F&a zn&gxO9y3$3EY%O{h>QBq$^}FjOO$c9!!1?1;ae)JbO+l{PG7S9N@D7pMT&wZ2gF#b z1?tIk{PJ+@g=rK421dR;5%=0)esX*Msk&%%0o+WOOJA zNnXQ8Jl#T-D3!+PxI@Wel$*U{PA1ShM0rx**knh6nPlBVWp~Oz+rll4aRt;T1@G(e z0UE-}wt}9~O6Nc|g+o@vc6IZD=BC4);&S$7h*PZsm)yxXLj-7XobK) zNg*Y}s?Ay#%hr#OycfjHN6r~TLfA{{S1c3^(Z8LJtm0i~oJ&w>XKA;Q=-|&9nU=fh z^KhBSn!nGRH@_+)s)vFa=kg!lfoz3!h+se2liP#)r+Le=g1T8?qNy%Z9-(pMV@j5< zqDj4`fsUJ`VcuGbG45YqEWSRWorOHJm5G0iDlALN@X&~fSJ zS~~M~09rPNT76%a0n@U`{ouQ+@3gq1Xa|LcpoSTB10UmUc(~+~Xp0FtU)D>FOi6ke zZ=XP;!Kv3Cujw!_;3@adUQR8-G#_kqn$(p&>eTv9=9t;P2)qXKn08+oKd!bRox>Rn z0Xe69ZqmQ<6^#UMjjN*nl(}Mq=FI*4&E(*a?T@}Pe{VB$Uz)GFp2n3G5=M_MTwtQn zAfTv167I|K|DGHm5w{4{BADdaN?aK1VOzt}&{aK$_eCDkyBYQa>p?A8%sHFLUgc7p zs9Ho&VcVE{HCkt#ofY%)ik2^o7P9Y^gt7TwUN`5#vK|okhDBEN7)h_(%91fPCh&bo z16F*@e&jo5*&y0zEm{0|REdSQQ41Ze3%`kw2(QX;ZSu-+WB0L=<-rE=sP##rlZtbg zf;A2ChpOTnoIA|)oDd!-IiLEQErH2a>T`K8|Hg%wkEKxG`JFKY`2#hKziMp1HrTPr zC9$@wdQ9S#%bbGWr4N`;dYP*%WutZf0Xzc7*g*oi2g6oWl{HOu2LK?AIB`p8A%R3) zk=;UdCMhP%K^jNoqTEZ_Fr|WO$Kb#kvs?kP%b1g{%i~H-cbE&~21op+d~7nP#AY&&2?9=CNUfp-C@HU&m4T3E^l|H#FQ|TP&a3`F#U=rrv@a@s+L{>F{KYjxIcZ`R zZ${;CzPP=;zIk#dUFhKt`Gji2h1yzZl5+CWq1EteN#>0S+zqn0^i8WtT(QBXO5se5 z*_X{~`iKb5P60`f6PDTlh3I8Ti6zHE>OQ{h2_;L~nlV7Kq^C7HSkI22ww(&lNT_H1 zjQ2_euV-H(5~M92iF_M565dET%2t^Du{681>40rs^@XST{N?A*Re5&=vg3DS8#Lp! zEW);@4@BpX5}dhir2suWy{DI-7q_4|J~L`kAjt;*$!mzNwhMSw+B2|Oz^$NUl^fnO ziJK0!Xm*U{dyOSV(TlquTqeJfPr2!AsR zaYkB$?jThZQISB`8CRF|bV*a}OyJ7Y>V@DMONuX~si}FjeU^8TxufeX;5K}AMx>K+@?Ocx&(Xk>b^1D@H6cEKDgDXhV!F(*ZWxPdWr=rspNRPtd47hxP-Cl$hh39L{><}-kgTXf(!R!>-)g& z6p^9!9KHt0OQr7C+3ei_f4jd5e}<;N;`>&lFS-F(^Sv+Ux96YD5&r*FrtIl}m#91O z*?rc3n=f3SrYEKG!}- zBQj30@tkp%x9OS08^yC?ZcFR-ppDHAq_LS4{>BqX1+DVR?K!+~+p&RCWwL(pjJZ!k zhg1M12Je6>MqOZM5{rBUa!fuWXnX1S$IB-6kjI}$mDSyoof)*)>EX@ALf^)67CM8) z`j|#%N|J)oK3?5?_}fSHcv||%Vj1$ItdoHRgyq4%3|0pJ1kZ-do}YjCw}(q~9JpP4 zc(}a-dtS0{$tbwDTXK{Rdy4UZdxAG?FX_5IQ+#({QXMjSPWpuuwuU#i5?-o*ciMm0 zew$CNXA)P6iA1O9R-NR6gT%uKXF>~*Sg3X{I6TonXi$O-%2|?H7MwQBOYu{8l?r_V z0tyGK@@Vn2-rQoPJxpEd##VYCidG^ZHAS>;8mmG95u#Z~z_OKkuj!&4B|u~XG|aUC zogIoYg!Fsfo@aFijAzS-6NNf}hQ&;(lvUP+#=@Amgty9}9I!(L<)KJb zNu&BIHLmh2nd`mbGpTX`ogjR3t6}a+HnXKk$*OgUEQMYaV{B#8*916UEJ#%sGyk}L>7>(VJ(8Q zq=|4)4!6;+@W;hWtk7g4$Phh{kH@*2 z*-lz~N^PR*hKUxyzA5qP)W{*H!fJQE*Iw}u3)~E zZ`K{>Yf*Iv)1=>&3O=Mg9&0w;EFpdMF4c&+?l`Y+`hUmmRoYJ)``nkl;p?co)%NbB z)VtJX!ktkz9DNfbLfyHp+9-uS&?^W!F!TU4MoGfAhhV38s;w zNIbl*Mk?8os!`qYXh%fl{_Caj(=ApZ8?&p*68`w_z%0tY_??iNtoS10 zqTZ`$;5H1+&I)CA37PH^ehRmm93}2OXVmCA$b9j)ATI^K4L5*^QTFmEQqROCz0z3H z-o~Yk2_)~#FU%Y)4_#-NU&;X;ae0U~;NPkvuL#xa1b#1ac?ak`e_j)qvk(Dm&IhCK zM+k5{U)@~a#E;R+aBcByb!BrGp{wK{6~zR;X8v2^a_El!DSgCo0- zs+e%`9UrBrwJqJFQ@M=smXyChErRRC^QEh)0YT`m5)m}oa;>c~Bkz&n1mIXHe196p zEMeL}kFvd2#opu@)9dQ`h`S88uu$u-Q5{$r6$zmkDZS=z`tn8Hu3h zKPU*QS=a~QzWiHzI^X|%c~^OWC4LHgoD}Ymp2+we|KfLi!e0ybaFEZkHGRchd#B|+ zfEp+~0qRpI48s$YQ4AdiUU!@qtjJiNY&NX2%t`+3rL0eP-z?q>LD|_jv{8Vd_>X~< zpqG3TNKUXe5x5Y_G^1ljc2T{|0`zFG z-0lQjKm=o8txEAL_(&>RXV?}cmeA7;SrK?a|ILq_I+lB<@eXoeQbaP@x1S-D)|auG1*eU{cF!DKy)@ zWVqwp<7!@69uNsqCoFuJR-mNd zYSa4bJt#rwCBhfuf+uG$hY*N)yLA4PeL_$v`F{?iTgbog-+w}v$381<#UTFu1)NZ4 zK+h8)0VQ#qs*xgRp`!C1x2>kK#k0yLk`e>z8@;k)}MEd?A8 zP6fQ=Tg5GNsH9u>FB}}ZLh=cUe$+&`nrh0hL(Wu`=`k^HK0kVmuF`Bt0)uJ>v5H#b zKzN*;h=MemgGn5r9}M<4DU?}#@1*OJ@h3AtPFdU%INVX@IMrtshzA1iFVEtK9cV+j z=SEY~9##23CNlnAtV*T4a!qM4>0EN=(FT$d?H~T`zX?r z>V5IcBJHegfCh&I6m6I7ivLp?<<&}-i`Rzkg#o+}Wua{uQ!v06yNp1%rG47tU9401 zxIeN=k)Ei&AlAj{>9Ih!^_od* z=nV6+S?B_;z=Y!ytQp84Z9;)~sV4Cf8aDDOCzc>wqy?*WnU3b{0Lf0;SpM?v{QXse z?upr~I!LgKxp^ZlSzPHL3Cp$XnZ!p;Y(%IvIRd%{TL|w-Hgk&asnT(v{Q*dp)Jyq@ zd_oKDIj9~?CKm9*pN^`JPwBegCP?qv)`P?vc}A(yCA3WRL~bqM(*1eRH+J)La>}VB zuRZfFwS1IImH-H{WYf1ud48nodNGGz$AY{ykLS+xwKzuPFLv1iWFWv}8l%cj?TBC- zdXESV_@266&_toyuhn(ExU|_%QeoI=SAdhuPscC!5Qdvfr5RdUuY&(Hrt!^NqIj*} z7nYKdvj$zha5C*>d-5dSEAop`$l@escl!*`5|pt+yIlLlF6{cA+EP7pdHuGs5d3VB zoLySw;ewI+!CkF2hj@)Ji4Ty#E@XR>UQ#Yjcbs3dqfkQa%QH4J>>(Prp{UZqtJ(3H zH9UdqC8=ULw zmc6LSn6Y#R6SqMdfJ*`=|_q9Ql%*2FhbBD#Ul4t$*!TN^4}wERmL_OU^sYr&RKgCVgsL`I z+=l<@2>&fzeY$&k`{{0Z&>chjfRh7G7|4vIGs`_?%{@>~-#*1DJhHJ<1@mtJm9a7S z6<9k^+oD>XcnMHH4f>3TnTc(rF~%_Hl_QwxX}+(J}IMkQ4hFJUGYR z!m5B*t{CFI;y6x1zSXq==#J|NYnw^>mQ9wzA#py>phrSf)PC0Nd`30E$-(&DTr#^Q zR4$&V=-0 zU`l*fO6-JR?c1g1(m$P7D=k9zG;@d4tjL>+7fWSs)XS9AUwQtd{<=UjmojUb9HQ9; z0nY2r{1qcoV%nY#zNxH9gud~jYrl=tx8W(Ik3fp61t}t&Vc>4%GKp@<#4KytN z>Wsq74Z%m*h-Bqqv$v=3(8H|U@%9{URLV3am3a-Q%w!B7dw=^&10zdtETV_E3rU7p zpz)SqX&;%ZuaZ|oT{=s|ibbxHth2f$W|#SDbCpD+Ax2Rc38@Fm@)LQ;Eo6V+dAgdf zuPgz?VJy4iXHY0n%8-;qSYkySo&ydX5;191s>~Av4Dby=J_`;8t$r4_7I)`yab8&J zbUCP&(MWNrlGdJvGd|+@Xls6a1fWE1$feM8m_c$-nF6LPU>o5B@?Tn9WdXXx&6#=) z&rUs*>?cvK`AhpM?YX>;C>u2B)*h{wrjTCXn^59^3Sb=^2VP3OioT|r9F>d|K_np` zNs{!RcmhcQi!1G-20XqzUOkie67${HTJ;GgH59YS_;CLi($2Yp^Y5fSnCqxJo|Z%x zKImW16~^pTY?p+CC6{C4vFT~Ivo(fj{<=2A|Bcp2t@IykAEWORGOU@y-Fibf(5iLR zSD;*$4PvOcyHG*k@kx^74?w;C9f`BJ zi18s57LY2;@)_vbYk3mfsbAjfv6;Ld${!+Bjj|$>?St7Ml&Y4U$`25A19k7g4Ys}X zVR`Uj@DKkmfcEV~AxtXjit+QR_o@T|;c{9x#KUf~jbee%6$G6kPzdbDOa+-2nsIu0GA086{ z(E9V?@h}^HN^q3BFUQmbbI`&}MblFMWpGzhA4nAA?z$oMKTQnZ-3BGh zphRmYfa>bZ3a_T{^2xSP0yM)S(w{Gf@e&jZi=*XpL)%Z4)~S4#Ui0ngF?<+Im@kDV zFz-+^9=;fd*uH^0yuL3uZhCi%+gW;YUoivq;7k{QLN2a9EJh|==O3@{n%I#pu%ITU z+8NOu=Ynqk)4$?v$HyP1W-KRB$cW_#v?!O27H0%Ach>q}-VR(tEQL(ShdsN_2DPEIBVp6_Z z|yJ{x?`sFDG0?3&lcYK?KX0)rT!y4XSlSKT3F3*(D)yi4yR;g=1GnA*mvg z#D(hVOiH35v$@RrYz*VF%TJ_g*Ig3p+Szk|_ZUGu>4%-TlA0*pZrNJ4PWnDgUQ4bc zp=0yo-CcS%aU9Ijkb@EHa^X)c%#~suaw3ZtChO_hnQS*UcP9;(mR~|Az~GmI8NZv9 zt;BtD(+=^lo5!WLRMO)YbGWjiDdrPbcx;sFQX$6KJG4>ET}GiUONEBcuwGSCE=vRm z@Dwl4;)l%tJ$$_j<8Aio=JtO66H-HOZtqA)(4|5ODixNPb`n{k$c6;gg=mrdH-Wd8 z+^}yJA6FS7Ax$|6XJDU#1LqHMm{6r`7YUnaZzSb}#?wPI9u!=Z2oMp4*+i2mB0mHJ zRyMeC0qQ@|;07$>71b4ch2$I*C+3H%hl@p&b3pyLlC~1vLMmK<@ZqWUpLj{*Qw4ub z*+3j8kJ9fa4s2yJa?-Y4MpiTN`n=?ETVUeIQ9JdH$A4^UaWv zpM3gGj@{r-=pmRZeF)D+1lClJtQe3_lsDsrXpH3*(gl(XnDX^nGx&C9H6)4y+NP5E zy5Rf5QA!Z)G)pHf@74(=D_cnC2V=C0iyBMcRAFl78zQ6hS5qUUJIhK|@ z6=#`4NZJI0<5c9_fq9-B&z1*&jGr8iXRnqA|EK=4Trz$cBC7hbe4<}o&ORF1+XSDe zeUZuRtY&csC4n;u06ICC(pf;NG|FveEu!`Nr|+CVjF-1&@bWp1RKfhq)10TtaW&qM zngy8zLh6M`HT4dUHZqV~-YYZoF2AZ^LXvWlPqdx#deL0e$Z4STz*(N42qPqiwwB8l zj68B~DN)|OZx-(_N`|c};eEcSX2W!b`6Xe%`m&miDcRY0q4KsplhOq$)kL_fx@lW# zflZdMe#JF_F=)FcMTL?uWl*8C37+bLo_B1OMZ}LGwQ!DqLMguov|CYK9532VI!Tx^ zTPS(kzM1;UDcC3p>qqmYqo?F8SbV@Q=A)$yNRbIo2S>{hv!hw+5|jX778zP(>e)@6 ze|rT*5i9t0W%z=H+M+i@s06fR+&SqRbC)s^f`;0?AI`5oUS0kTHE>v{?CD2o3-Z%B zd{d7UEknUF`|2DoVc#6YuclHs)RA-fhM+6u4^@lB-cVU8>KD}bq2?A4JhG-|t+1?; zWn?akD979~!jSO@r5UgYX%;V4Bj)z|J5`~yJRtZO5|)rc0S0w?gt!${W!N4($0E^k zVzM0-V_l62q#4|U_0eVDgh5V^#$o{ASL^$FI^P>4UzM;R+=a35?5-+BAO_1*+8O4J z`6(V4muvT`N@JXEo{sVMs|kAX%)VaGG5H`04)Bo%t7}-9hsU@E5Po zrKG4Kbp?1Bqab+D=p=e9^f=C;M}!}R z+r=9O4=dgZctp++r_Mf-F1G+4MMjT=RISe|!nGl;EnXdIlTn?S)(oy$lLJ9y&89Kb zCYF|2AP2|CCo@9P=-aKfJuB{Vv^DxuE?WzEF;b4CaSqfok#ZsU7nPQqRoKJ0S7MI~ zm{j^skRZwJp0tg#IVPSo&+sa}_CI_YIza)HJK?ncr% z<=7<6RPsa$&2qkfe6Y0(pNn#_bR)Ej1?B)@r=$Zm-`K-SQxH(O63sOym1&D%l9%W&(4&r|m3jGf zIcuFMx{3nz@N;OcDY$J~8F!-Ebr`9xIE~2mfi?$f$K3^c6=f8L8%xN+A3nuDqu*9Y z;Q@sdP!H%2D^CZ17~;Rt|8psYe%e6U`-(QQc1l=VRSfiZ!OEfA)^9>ZcTHnalHtz5 zd4Qi|@JF~;?@`w2^yW(t%Wx4?aFF1p6v{VRsD*6bEE1DSgq`i3ZE5|YKPp0Gy_s?b zzp7kEj1&b+hX{mfO;Klx)AYuWWKzC@555PCYRTK89vAz>Nf0p^-nw6JB}Qi@=Ub)LG@Kv(P+ zGMgsEut(#?`buMbDLSLcomENDJTbG0szH7sCq16cUdE(Hc?9J4cTAxYpfeXF;?6#t zSKi-cbwvA8nY-%WR^>@wO18hb^i=9V$rYDWU*x7it_lg~sxMO7$}qf2;uPEr|&vz(X{-6%I!w7D=6;H!Zvd;sM3W> zx;q*@KnY8G=yXJKo+!Ua&4^rPXKPpZBmXUrPiGk4L|H!M#7d=_(=jUFvmK0PmFJ+W z#CRp1`{Cx{`kv2;YLr+8!RP7-YHe31K8i zu9uE^tJ&L{`A8BN=7~ytBu?RS(uAubp_(pKZbtTm3d7PFx*zELGI>?Bkb~ZwRfFOB zua;pPuJx2))F^@F)4|6Hv*Bd^dS_>_!~)62u>hak10F}7H}f$4Ap2sCa?65|^NYKi zIqIWcTrD&O>CHYkJ$op3vwhbsR9|?El4@(=jVK#kwbda$ib^zPEfPYmgj1-m4V4An z+oJE8tJ-Gq!zB>l`u66M3*TnvdP>oXc&{TwW!xDE4@1yM${d7`=T~thy{%oZw%(mi zW`p5+mItX$VXp30K7R5_G-mQiBE7o7{=B1%sXm(dS^f`N^bH01w<{Nmdks((jX%w_MJQPGJ?`->1kMXw9d_X$dXfC`U z)oQKCtO`!xHvHg8XmGQQa=^lK#M2?yfD@^yG*DqeB4fex6T2?F6cOg+yg-<-IAmkH zUoE+|Hx}(}cmGJDVgetbbV@Ye#Tc;|=Drof9I2o*mu`Uo2`@Q0lto25&FSQaVp;p< zaCvRn#5z!KLS9Gy9dJ9lC-cSE4~s9z{W<^g1+G@rYM|ALxQ90ee)XeU)+wTI(3d(A zg)3Rm%t|g0CpqoEs5S7ONqmtbAdRK2_Nml)C^1%9F66t$OC{NmhDE67C|=1SN`)G^ zUD5&eoGE3~+JDmsodk4ZD)=Ru_xx)Bd&ErBY2Ri{Z2j+APD&Grv> zR!1W38{Uu}P>~6*==+p!*Z3-Ne`P0I`Ba_!5H42sb(_DDDkd!kI2u_8ApKlik3Nqf z>FFG+z74UOZydMGN#yyrxT7#B(0cOX*Ju%=d}=UgQ>`$pSjZd+1OBOE6i;uqZ@&D} ztbAK36IlhT>jC`MS^%jW4SHBfMH$Hu7oIl4IV=OohRV$g=sSnAj`Js9vdZ)Y4#ql> zma?lggKJu59ZKc(>R@X$Y!?x^&1lv2s^@$xY9ju)LZyP%Dxatx<19-K>!8)U9k*Emf{MHcw9_7**-+dI3mwTg3p9DT>W)j->b5NNxdEe-S~1QF=3e0 zDHblyMAXTY=5z38!ksyKI#_)=SbI8He>&KBI(Ytcu=#ZG0z)#4@gXJ*F=L1+L(Cas z(h#$Tm^Q?`5$28LI+!=Yybe*~-~mX)=dQmx-$9}i^iZJ8 zNZPN8Tv}5|Gg+^Ztuoa{F=t$9sx2+HC=ii+xMYTF)k3jwQwT7woX`fDSQU=>(Zki1 zNUy;YjpYd;1#A{}Uy}n;w`|j!+H?4FiP+_lG8!>FRWmk#Du{`JMYijbGK^PGI5H%A zdFF7WIt7~t!U9;!dG-y}okbN|BM?{ZV~fub+kJ41a6bSyUYRQ?{M_1}nOvT72YM06_GHidN!o}lXi38*AL&7>EOg{aIa^9xbob7$}* z*r%aK6aaLmx0gTui0r5fGX^9R>}Vgobor0JoPqYrL8lp%n}4LdLs${z&7QuHXhzdG zMO5wWLiK))xX-C&e-6FksE7o-QtzUdyJ@k?bO8Xou;eU2w$zYt5m7I z1;{Q#5dITau?=K&9WE~XfBvvGI!uYvZdK!x6AwpIz%eTrv2xii6jPA8(y(PGgC#%- z?-utrAC~~Cy1AWSUVprq-=o>xTv}_*ug^a(00z=yg!6?M>HGwy@~#2W_%uY;w2h>Q zV`_V9R)hZ7Vl-h~WokkOOI^j3S`56slZf#|szUvfxkavQ$|eggr@BOEm|rUL-)w_4 z^yK(^|7p}>Ms<4*FDd6_N6ra)F%v?h58~XtGs-WKC@BBElG-XN)R^lHJ`eZm_s-*0 z#DT*nb8n75W=M+?~Z6GQC0_s4xEdJ<1h6>OjF)U>IoRzAIk^ zY?vik4w0xFcOO#4lu$lkFq!^k<*&VnvA254^tMf+?9?6$o+-IG0V8~NDi0)?kiDFO zfaB|nv+LWOzl0m&4OuFDD^J0c_=Hmm?0@t2ERhVOO2itpp8R{~-OCFyW zF8PW|Vae|z*Q%byTk%XK;6hYN8!b>Y1;z@Vz)u$SBUvM>`5jeaXKDK8!Jk?T6unEb zqAHlo=fA4)$}IG^Xh){e`}O?pUK(&hA3ndozrB3_aK8ZP27pN)VM%Yxv5QD{L9<(U zD+4s5VI8D}XabgUp#)BIr$uRNkJUVIWek(JJz39yAEPX&KsBTC82cRKVe|Rr-K}M1 z(ck1rF0xtsNA-iqmee!&6yZJPn`OV1QY)+?h@m#$s;R_k&$Vmw4PPLJgNOgRva&WA zTPPTBq3teOZjd^_)>svfu{E_~WLU~+X>JsUo_zJ@(srtbH(QW&z2>mDP4nOCln* zM7r?Bas|5M+zsVrRmihsQ_P-@&z5p^p|}v7lC$mP0jU~PPZtT@PcPoFnk7V&;BdfL zQor#U+|)DXXGd^EVF1qLLkR-I8yrO^(DJ$932MYo-k=v2%%oLg5)#?we~HE;z)4>+ z&EW#6GK_tkqK^)E_PFtwd@PfYO(HhaJCxc`zA3U7ffQH zc5nC1;w40xS4}jh)DY%?tYjHe6L$<1549#)E_8xoH)w@6U?5kAXIOVEj0BSO177P- zaJ{Uo3{j_!#vtMxQ+!iu$!dAu>Tj+!rCZ+N5CHBYf-r?IBCZC$J*wHK)6?TuOfb>b z$xE$7=ezsc`e*MC=iNk7TrdmUc5YuAAI8_m`)#ugkPe3 z<7EGUU01E4(GnUM4W12#Y`6LzIkOH+M32E;T{Y0`1pVbWcLld_IKjqSpy&y z1v5-o2l)d#M7K}cyLz-__Yu&OS|(EU<#QslW!koF7kFp#Sdu zuH^BWZZf~Szqq_EP6cC&u;Prf+Vj4@y#IWD_qRY9qm5ss3{*R4QwI6pcsfN1Zwaa_ zoeSy~c`$dE|Gq%D!2bO21N+-PfLj&Px-PpRJ#|u!7V8pDUrEVX#H3h#Hn9@d_hg-@ zQI_FoeFo+Vy>u{nib4%jFMiM_dox^IpQjm)F~-^utL+b86=X&(_t0-4e} zBUz8Nbhe$bfbgKh)^HS<`itue1ZAv|2Ji|;?o+Ubh+}Gq!!fkhpBCA=7P_EigdS|K;TjSfg&Sng z&>H96o4T09Ii|4Q3tXZvmg!_b^u&x@kbF*JMWhvu0x`i=ZmZ)YlBZ_}4}o|u38`-u zpSF%{)E?IEIKNgsI>O>baX~f5;E{ZdHN-#mK;G}L9_A$(nM$X^V?QPB<*4Z1_-N-~ zGF=kVkhUnxgNvKNe+&R?oNFLdap<@6_pn@+@-(zd{?EbBpAZ|xi72|XDa+D8_+D|I z)M?S-P{}S)FA+q$2od=Y?_=jpgK;eG#eYswI}6ps6&>Y(7HLrLvZvg-1dEANxLSgy zSP_#xwq%fUMJzoA-~b;gZoR9=@PU_$s@m_-1g6RENs&;KX)2&e^x-R3;UIXvId83t zM%gNX#P<93OyV1MFhd<}sv^SLDC!LJvJ$G*3KHY#_TK&}G>kK(<;jDKw zg0S_qHAe+$?I-TXWPmfl!&$DKdF`4<5diE22q4=iPT_NDXio6WsJ~)~wl!c)&kfIr zJYNB{j{-oKwHU0>iC5PUHI0BdKo8L3({WLS=7_L)o)3teY5y#EvXNu z3}FYUqNM~bP&&d|^CbXKcbu2>ZL$ZpkVd1J79850id6AbI1F{8qdoE#hS)Bk8M3-r zU$H_-LO*)uIGp$xXg`%*RyWlA41;V5C~zRH{7}?(o+-(qHA`-v@e}*B_|OCYD3v4*>zNd6G4aHNqPWh%MyV$!v;6}wF!byFIy`yyeD78An;VHhN8%o7)o%ub(m~Ro zNaf~`tTZG^Am6>Z_~zN(REq;S*Ylsef%0}S^itAr{m$iqVCZT&W;^Ujv&xwqJEt9C zO(KfZBa2{RPJVD`XAuA4_-H%(@(>h@l^29{OlPxwytNBv(`hQE?vuC|u!!y#U+)vL z3*7kT0Z_;Jm)pgM%R6}dxW1b?gl-EDwI*wr$|BQSXd0uG-EmXMA#aKMQ!F*N`9Ckd^Er1*UQ{9&v& z4wpx$T0EA*B7y~^T(#On`#Yz568ITC(SSh}@MQPk*(pjtW398_%A?u#00hbtdk~j| z9|YW;?4t*N@=jJ97#d(6i_*YNU7iir!?K%?$V(E;A+Je>Sn>>)7=h{L$GKzT;E=+5`j0#{PLB%IE+ERT=RUb<7CL`{er z^XjFRfFG>n)~trPoVwQJB%*T*qpa0h!;*TZIYE=QO~X1br=sneeRqcWK>Rf8P*{U} zAYMaVM~BFbrJrQ8(KJfRL=yM{%#jq4+=MIozy0%IWqI%~1D(8yiJ121KVuAD>dnDF z<$<3R(#gVQqBlV}qID*mad4JptbM^bOWN<{t^m5g(>!@C5!KUuq($|7PY3zlp>+ak zAk7h~xhQEYsW9vP=o=k+GFLQflHKwO2;4%1WW#*-SYQVrXe1&eb)Df|ix6vK1*xvB z0 zC;A3uf8ZO$!&{KGM|XYBvk>1s>8?pXeR6(<48Uz*_uiwTOtG_w+wm6a^`N7GPsfH; zZY*(z79UGs5P80~Cn12`Acx!?t#_d@*P{znN=6p?x+{LOeDsr1h#?lj93O5;Iw1^8 zFM*j7!g7Cdc@T7E@336>!{j>O;1Kg^xRS|_eOSA5{5NxwRBVU~FUpwz%iEhc#g$~| z;`&p{JrNG%QMajL5&FHDc>x6op4g@ew2a@x4I~w6-XM@!y4Cjc``drt`OZ0+C-de- z-S&*fZUL29_vUi;HP7MqsFae%%1P!XNF_`U&Eg|6SEyQ}=BOGGj*4hXYKA2G8Z&RN zE7u#w`J!;6y4X3Iqg8Rdw5d7I@VhG6hLds9ouY&wxvn+wfhTSAX(KR6G?R{UGHsG^aYm1556lDpw|%q|`Yrtv@U5 zP);n?dQr-JNzPRT(=E(3FaLaS(36RpvcA@%oG*S_T>tull+U#wB~@ymWKlnr7T=yN z_R*<&AS6AS(*CH!JeJ?l?d6M!^dCT-h5`3bwOZJay*)zgM=OekuxUe z4-=;rc$Ex{BDhp;rxf2yM@3+2;AoDISSPp%)#yNk^>_9wJ!8EZ&y0D23{X{qDr3HK>J3+`)&Hq;(Qw~w z&t7*Rc4D~ewxyus2!m?I>yH^Zr62(}G`X+(NCZE>CakVk z8aJYs)A$}C&YV>sUdlJ4d1|#mC)PqJmPiot2zX|BA&T3WJ8kERqep^)a^)Th@d*Fz%dICAm|w7=aeu9+#v(Ogl3{RAevXkXgpHh2a1xL1H{47P~jv zdyGOp)6RoDdeNO`)lCftQ{1&{Dg6lqtom#ulx$e8N5S<%7JYei#$qv`xCo~CKhF@X zAa;P&)BEE`r zu%uR}8W@OXACy8wZk^Q2Q~2ua8*@N)V|NMhIh0Bw({U;7<_2brO@>rr-I8Nn7Nso*!Rsp!SpaMrzAqwjA zN)@Yhf#nR|VdT6nlp@x77_d=PxO}t|TrvRCRN+!!M-*)!Bbx(K3RQ?sR&}|bFzW#H z9CDqPimNKcX#-A|AYO`9-#AU6zGOXxN|)sH<;TEXF~Y4g6`E|^`*akI)Id0 zlz>>)f!W*@zuI_d3r#ID#Yd%B!oOF$m$5F%UfFFshN_l-G@?o3!o|!y1CY7*MeM_2L3l0$r|9 zp!GG98DKO*G^4GBau=LM4Rs`cn%xV$H}~t7kfb;%GJ-0#6c_wgG!I(+YSw0rEWFX! z+BP+YV$KU);qi!+OcuB_pU(9+=0)x1VeN2DkEGZbPXlJ8$XYTjHje`=&(LP#6xaJo8*aGytZ|MqY~~N26p?Hq zUphG2f%e9Eidn3K^F>-YnlTjjPhK5;5wiQL;UWl`6iWHHxVRJtx=TC~ebpTwvF%7% z_?hB~)T*TeRqn_C(3}ujUtgm&+9o)t|FoSntkccuhF;*oLSLs#AD)grjDGjKkw|2W z8c`mq>R&!fwH;aKo}^EjIPPG@dqZ29!#Qa6OQi0IdWVx+f|L2XKiqyd!Y|Exb!(ZH z%y1>d#rUv^xf@pyAchM@E}GUH#$3 z>hk*9`s&)o>UgrczP7qRFVd=|Njl?DCD9`WjWlRXU5oXd0j!{$E}a?T#t3B;sMy(j1%;!iV)66h{IAe1 z>idzZhPfS;sYonUz@FLTbz~Np&Jh3#f~3=GHDAOHIZZ&bA;(l@90QXQx*DQ-Y*|kt zrQjS)<%o;M9DZG5{cuwf&S5{AsMVjQFHiY2ED`d}#0i-Nd8sV|shyJ|oCNf`nZ0Ln zP&3F#CjJ0>h=zs9H*&S}N^1QXqIP%x#6@rwv)mhN6}1G2lTl9XeCh!$ZV$WGgquVS zK;Oae%lj%*YIFwAi#k67s!@gl+y=fRxJtrWS$+tbqGZt_ThFApC8-B-{h(nh-6MmQ zaOL&I@MZuaqXPvHqB*Ud0a4H3(iOr}-bJs{2DzRF(Y;3bk^^CUmPUlC7b?llcMc)& zh{4T%LcBGxA|DyN5~)sdI2+-P6o<{6GCkQt494yG<(0HAQ*&8UK~$$usUJEr3+sMf zT1E49=a?unHCChpaR%Attj9yB5Xyoo(P{$EGbDO^!tilqgv*+sJq7M$Z$iQ$ZZGt~#iYtl!Hr+H>B z$YfFcveFD6i`A0@5_7XsATd(qYTWs^SCcjMY?u-v03bI2=azv0Nd=*s&TFx|(29R_ z47E#QSks1;j5a0^wDroRo`EFHm#AEhaeBd^l5OZyc%lPEFz~*fQ#jI3EOO ze0aD*()Z@akBHy8TimrBHU#rzx`R+6rk`Pipw#I`%0$d^R7QCUL1R~tbMpWXr0cG4 z*qWj{zdpbEXWcGp2^ejNH#0FJxY6^w$Y%LF1ul63NO$D2ve59a$)}EwUHX^*l||(`xpr#&d89Di6Eu z=`a|d4lC;u%8!aNL_W0!4ing7>zbtsJRInmXeMN18DtzXCATB4zi<}Ys zGhimBR8j|_8>uwjRj=qltU^_3tws67Bz@j)*tOOmW(V2)13Ac74bWBlEw* zokKS9kuY}LxWux0Y0d%Fb%eZ(D)=||E6N=G+xRgM_P$Gb5}$y;N387pl#fAibfHZ z48MVoq^qceTubCNxaQ= zU-A+V+H?4I=ZJzcWjoj=SdcHlzoKA4~E)vxjLV{3`CxR@Lw2D=7YfgN_Q(5gxp0?l8w$4BB2 zJ$#e8DVNWb3hpKvT}x`7fDaK)t}?eyDG#e^l=!CE93w&CDdt$NXvQi@FtT7zAkN3{ z#&7uu{5!P zVFb07kpXpz_f1{QdX;9&J$t@`B2`!fv$^J8~ z%t^UuJI$F~S_jOXPlNvimIR2kh0VwwX?+?S6Zj;@GMB+>R?#2l*Qgy+7WPmKR9k;j z2NgCMDCMf2Ui#=%zA_U+7%E)OFlDHstJ6Jq#5rG4dx~#H=t-=Cm@q+T&5$-0ZM{m~ z-yBhD(r1TTm$O7ERopHCM)-2Hr$edWZDi9x{;dfIgR^-qN$AfKe<-K!L1pbU)aS@T zEd;`(VWGE!pIzi7u3_b27YoH{pXI+%zODRWwCBkQqNGmrm#U1-I|2unJBk&?WSMm> z4LM&R+&6p0A`hzU^_Z2LszWKy-&o4vBn3x`IP-UN=upVW>+)|qNODDe20SIZ)eODX z3+mcDSa-uvmU0q=qk`YTFGL?r-EDKy;r|I|doRa3N2Bxy z3$Y(P5n)tv!C+a2vo`;>jY20V!q2-T`6u@#S!(4cipTTi=Kj&l{!QdV&1Lnl-c^o! z!0KG9)`c_oBM}ri;ABOB+E$%$%JP$WCTxKUgk*ApN=hcHElc$%SCd?nWb4_|N7a56zB-9cE= z5|kACjBa!UK;WaaU*#~Xfpj?KlC9#hL3bjs;x_xWl2E3jAA?>VSxM6c54=9JR6LVv zOXY#LW@I=7)wAW*S?ULt>|`;;>t3(fgNJ%Lpf6Jo0>cS7+ouhaMU@>9pwz(QF&iB8 zs8f^~EO(m(WgFEsG>y6|s$sRbq7GG8`&iJQEEhJ`YBAM*WCGXId}WO&1C#gwz)sf2 z)I?k!Lt}z}(Tw8EcR+|0K8Cq5aQ~MQ6cws6>9tB_w)~+R>=<8{P4k+7(d9coMW zdiMkdc0_U>ALhp9>AsGRabr^$17tzcJsfo9gV1)A#*%~^WR~Atete3`$0#z;U0j`i zx*PrDA4dNV5zlDEJ^R1%<9HVlf^AbE&1i7i7p6cWDBSh}u|kcTqDL{#LxklM`RitH zU0-b|@MD+AVCjnAaaom6KRl1d4z(2l)?V$g`Az%VK%yKge`tt>2(IMva9 zel|A9MMjWx>E?MB+*M8_DZiJRVHGWBOg`$xr82sG`CL2A?NySeL7>28i2ulNnLjK; z^{l64B}PySPbZyA99PqljwaHQgmE>J&syj4!t6F0ohNvBe01<`v^27at|j?M1TYl# zW~BH(gaH-OWW>NYILxFNUkXYzwl0_C#xMb5@y2J-wGVLOCQ6-T$qca?6!qUjTdTQ> z9M-N+>4Hj@$u1zpM8tx?pF%fO2axPfq#>N41@^n}+mHhqUU=s@5cZ4QSLzsBXtSRB zZmz&Up~7VtBQQ>3{Mv^+i)G98fcy%Ne#&xs5BHZ>-G|%D`%CoI>24RFq|^_5FN@nH zNh5HkEksTE6cho5&yrat-~dC796OM6<8h`=<|FS-aZ#%z&x6yP76g?8Tmxh}uESB& z<%&r(W5SudSbXfd?x*wa>Jsi-5K?iUF*;CsUNm{(>Nh%|gz+a?Pen}PKB`Q(qYi>1 zjPXdJ+c`i1*R%Q3!(AtY;OU4o;lmB;v7?dm$Hmq8FWtjk8jgjYc(5M)QK6)$7j!M{ zY{S4?o@hD7W31fS5@xgow=DJKK58rrYWPDP=7y&6`IQA`_V)DHzi+B*YeraFUG}VJ zMxl{8w=1C%CvhlXNrT~CL};_0h~xz! zr3or{sL~D61(oT4zU)>?kzQ)4L+4wGoItG6ksYY`=Z#0PtkDt!ut>aV11FFX8tOl9 z7Yo)w;UWrf&mJK4dPMr2TpJv!!xbSKa1lxn6HQt&r_8%9Bj#O4J8&FR zzb@R$_+aK4hDr6;wj|}v*Vag#LwKpk5!`24IgAWSjzRM4A}NC?<$6X{v2GpGBi8nw z%^Ws=LYx{~tXrN3A_f^_JNaRPFsJ7$8*3Z*F;i4x34k_9{s0yIZ@6`_+uf3?rHb5er7i^V7I4MI1>b91Tq3GJM5-buSN&wu@d3H&Ys06*ccu!uLcGC9D#poKHz)T{c7VcAp z3{wg(k5A_F+89$)(xF*=gitC?Ob-emMqn+W=EPhTDc9GXdeolq@J=)-(3c|#P%lAF z#lhYrzAex`SRu>rm1DX%Nb%eIb18wek2vwIvzNU;FYkn5ejF)1`*o9_up*R%@<(zJ zssPA!4^#nAaa+1gj=MKfq#DGa!u7!Im3=S&09*o>Cmx$gye59-7Obko8*20A3wYmr}i|laP;s#G05>)cH%QSG@XLMR{BR*l;Hs84Rr$+9h|QU2+2{ z-8?``-A?~S1oqzPXlU^A(~tLhaH0-~`6@aSWGe=@DXk^>y-W#e%f>J=q-$&P!aPn@ zPz#fT)t(+D{U<5yqAL~?3Sd|9r&n*#;XMuWOBxa_FbykK867Qu$-q$CvsHEI#oZ(y z8uG4GK4|*xV9hD-pU{|GOf^BNN4ap2tL_w1%gziyk3c9mxemaOk(#=@Q^?**OX1~D z=daQtrF?c&pU&Q$K+6PW)qG00nIZS0CqUd<(nywT;kCH%OS3A-FUoY8T=GLo@pjonX>Y1^eg*Q@ewh>CA??( zwr2r!FLSiA_i-TQ}&2$Z6x#CQ^oc0eoWkBKY`MpL0G89FZU) z+!!nd^))qF&aY0+10V@5Us&E{IAn$$LsOL4QalX80_ENM0XmkecjBR)G?jv51BEgw z1k#nBK|i(Tz#wCd3WB&HnZUP|ywSZ%_b*rv{p0r&@+G2;*lk z+ukl!8jI}c#g0l4VaY^j2cXKr!o_mQgb9+*VFalht#bX!sAis~S-vJ~(*t$*;{CdG zvNm8i^X%h>B8_A5Pjh7j1qV5}`7+8GgT(e*o>s6^PbShO^+CvhQVps75aj~JdStf} zQ(o<#NO@2_Q3mVEBdQo_$SX{l^T$fV6sL^uUFm-9+^tFQ(6HQ8oOrXXHUbfb|}z+?D`ouVU7 zf+fKuRW8_%R1}8R}$IU6N8C*u)0*bnB0EYYDS#d19KzfJs)ob8&0andIi-*;JUWcwcAJ z@%jvWhYGwXwfoZUMsoD$uVu@;Xj?#Im@B|LplwLIsGY+z`E4ex4+_k(*gdX`Avs0IR@-u@!9g2mK?;w2Du2uR>lTO{BrjV|yf6 zsgInSClPNiF3umWkVj0O!PN;Dp}``o3}r)ZKQmbv^=oL5ydLp42A7xJ3V(OVJ^wwf;YkF+0=RuE*uYHOgD_RC~F)*o1hNSM(c z$&x{GlhofMR3^nj>=#?B(V9o6a>>EX3{lqr%c-;DUc5ZFOFehiKq)S~$ki+VsU^*r zl{e9vzQg<+S0nZcl>kcJZsr_w2}Ve|~#E+(L#3!X?n1BUp7d*9x7i zi%>LbX?w0}ia0bYoUoAvXwo7YyBqLx?(*a4pB@WulfH4Tm+*8HGR)$kHUfeNNLnRQh@3a>c6V@)=q$KBMPQ)DD8cm? z8RB#kG347wSieC)E&LaT)xIyGIT9$y3!=awu_ykhCv(IP!R!5Uc6@dcI{qlluw{C| z^sOnETFIkai*k1CXDC)y%TcM-RhLFxlOIH93))J0IiWBueaN%s%Q9PAcD_Mklb0hi zlsc3*&RaGARaw}LBgvGAu&iFwzsii^6=+LZHZX~BTP|-9D!CFs9rZU^YoQwI?WD>z zHNWi*^UWndzz|TuTR!euR*WNjRB6W=ul*fVu<`2ON(HOM=xW=v<|&xO;_jP|6nE!X zmc+{9OBBxZqaiIAq_~90e83P5G8H2{SC?5k&o%mCetd=qtqygq_2=Sx?enHtSSxUk zHNH5MtzKokd1MgTfIR><9{W&7)O4TU)7n)AxuvKd%SDKBBu_BV1_**c$yH@s8lhtE#oKJfIYt{z=%sALK7SE zhkTrZ)P<#Bt0K~BBZ?$Y+j~IIPr?$i?jWRVz&nqSVib$S|4R{<9Q&eTV!!qr=}m~o zf`U-~m3JA07I{DO7h8lakzdbnd^%X^qx^cgzg!OlOUR+m@@f!1(5Gy8OL<1xbfZL` zASBJ+LKo&g=3I@gx-e*t*eCF*io1B!Zi<;tMsbk)mUG^+Z1Hj(5r`AMz`8L!%RwFS zSrt(d6{MY_f$raxL}A2(rd$=F0y*Ia&R+!{VMcKNs?t9$rOq4PyuCgD{qac(J=B9o9xLAC!cxaH$!Wv6q69RX6^^a`GjEUWY81qXG3_oPqeavZ@0SU4P zGLZO~Ku^sP%Q3=R&jD6l0kRkV)LEv@3fg05Kh?qU@yVPq7~W=~M-(dwz~s|jo%#Xq zUv8}(Vajc6s2gb@RT9*@3Ak11;tCQfgDYq^BT4Ei zjt*n8HGm+Hx{%odb(I;UozV`Nj>fDZA!E=LMtMZGJ`F@SR};8bB?!)=;b_38;7OYP zN^Jn11}U$V7_%Z!gdQhW9C@;}+d(1OlC)eVW2H>`Mgldl&bh)^mQzI~Ow{ZARwREhAQqO)+ zv`x0;gyh`lpkn9X5GHsizQJm(JZBbN-UVUU>-68V>$@*#QGIbKtuPTNV*g7i z{%Sx2#|q0}?BBnBTzn`(|7af!(S2<$fOhOS|4}@C=KwlnIU2!p!Omu`Ka3P0!Y~~h zrd=r9>Y8hHk8t2agaIdQa+X5clzvb>p)u|F2zVm3%%1&p&PK$nYX+C#1IpCWRPuBQ z1*;%XsKw;zlFTbzgh^yV)P4zEJg=6Gal8&syjU4&>;XX+r0pj?p*E@9*z))V4Q1pX zO`->5fWJ8&yF+~*CjUuJAq(mfGek*)5=kdZF!z(HBUYm(iPGi|SNlo@k9v_3#s~5Y z%coIq8tEKo-NJS2OnXI^Z;_n+^ zGnxE0i^Zb>HfVsn_{dbAGU`G|g39e$-duttf>#4Ii zLdwGa7}!w$Fvev%FDh55z9v%#(<+C$3}Qy%JGU8!%nn8OW4$s)@aTm*Bk|6Ii}4sM)|-(PMO&HE-Y!bZuiXnv|kBGA5ipc(lGB zqWKx$^Ke}jYJ;7#sLTu>A8Rv*4|9N@lUZ71Bpo$l6_o;wP8Ud;?-&sAEBHW+1vN!4 z$Mk5NZ;9xsd`m)Kv&-cO-5KGhM0f(o>%I#45X{J~5-;mnx`Vu;GOf!i`N1Y7pfbVU zh8wh^XqP{95ks^$_Ym-GYn<9Tn<0T!F4eleAz*#B`SNfRJKNsb-9JJQ;M-Ga{on!d z*)cv?hDykY7{pfP!RnSs`!-T8c6@y+tyE>`gr)#V%}KQeG%8Qp6xN?KlR>O1Rq6RC z19*P_;YYDi0w1G6gspDAnHMj!P`jd?Yogfjc;0F$2mhLHq?1z!(=bf?w~5EXRvUBq z90i6fvW&EV&H1i4`eaiWe?+via`d7`ilF@Gmwt^1J2W zGY>Dfl@?e|;?5{7Fpc7WMwsxG7g! zSZ||d1U3qG#K>O!%yIOd?y{Vd8;-9HWhEh>v|BhZO)Hbb+<6Zj)6s4Ln;RwO!AdPN?x(|ASs;J8qY%4~%dhPzxY z?lSGvwkZU*6lRL8CYEj-FYeM=x(=x->XhYd3su-=36d`xU31 zd9`rk`bG587hBu+97f-nGvTW3oxn-Yu)ydPi*jaqPa+7!W90Y0+<`m*D_#jw2!+4; z`TUnVSkus*Px9UM7f5JTbIocdVgRFRi#iks@G7%9Ug1IY%?MUNqA=4(FtsMR!gE}E z*7BZQ)LN0f|`&+5o4#{q`Jw-A;c6%hk|#C|ML)}9i4X55Hs+RMb2Pxb zT6ACFMY+6_LXp0^>V9q zK{*J`MJ-H;O+>8-t}!61LV%a0i5H@VeK$eV zAQmoKrl>)yOab=+SSG4oN00mntiPAO38c z@?rkv{Ndxu*t?7_YhIA3fh1H}pDpsw3h#O{#vC@Q= z5m0uVqNIWWwA^w6PdQu_(i-`UMv_F#uzc%Gr1XO)9yX4=Y}C^yWijEb93h%Uo|e2d z%XQ?`&@4XU45;5pOJ=5=F;l+!Nb&?|vT^^@?0630Q_7%J3`hCG7JRMppHs!c%2iI` z3bHu};pRs-x1Xi@3YDFT*MnL|aE{w@PXOAX@#bh_8Fns`d*UDIz_chB0J^kh3xcVumG z=*srX6ulDAMR&9AUz3_Cy?Azea*`J?=bsh=uggb>#pUDFUCir99y*EizE9>Hu7@{C zp@#pIxwxDWlI99BRw7fa#;6G(-hEB$8r5v#Lli0d=?8`DMG2-stt&*)H=`49AP|{h zJeT8Oj>XAxu4;=mbs`|^u?6`UoXGA`EqxW{L|FF2M^AzBSh@vjq<>mmfB1#6OoBos zHzL86^QE;5DpR~fOBXts5GK!Jevi}^|MjKCQzo3rDQee1)haV{egh!Tl%Daua)@q^ z5r{ZJVR1dY6=Fb!RYb-pXft+|m|+A-aKKX0jRDn zRyJ|4o{2?%y#Dm*=pX(tT7EkEpCg?;Ugx|&{!s=;zsJ9$|1S^y1Z64}BTyrX>}kHN zAC{o?$V4trDW#UjL$yxvjA$S-ln+^5f1F<<4yrh}eT>pzDO+N4`XE2Gh}T0U=8i_11Tzjl!;W&kWi{VeX0c*GZ$m+$wS$N4pK ztJ1Edy@CZ943XM%^y@KLHEG^!$u(AZshqau;Q^axU5U zr>~@!r~jPp$sSP9h_oaFzknIBqa>Khi>;!>39t903tS9DOJl^4+IN6*4qLyVOZ12<8dfx_v4x*Hq$+?gPN{Qqf*PMj?B#xih)j~- zet?KT=$5z%7;D7-e5iR5UM@jpk}W@Z_D0Jb$w=q?_vKa4QGyMM6Lb<&vaKpc4dZF7$9Guqts z+30ujsI318SmF%EW`Xi(lAooZHG<&s|@1Oq{|7`1v{$a1NBk^T#+xJD0xO`BEW z>Hz^hK6ssCSLl!fGLznI8$mD%;)WZs1sCH5c-#76$IZoY#35pQ+9MjMItc8klpiEu zLG4%}&slwXgs^%w$LsgGse}1e2R42#mx6!(QG+88Dk9!861^m}B7r%HtfUDX&Zd)? zvBw&7K{dkpqi{n%5KF)lT6=8~-~xFiS-%I6!lT3ytqJV-D<=m-x9RiR3MNEGq)H#@ zmpLjyFK$fsinY62IEHejC83LxlHQ1GtE5sA21va{1|{{Fo>}(Mso3ySGR#uPF4xp{ zGnmOWE;w1^X^mBPzHnlxqwGJik-=E#WK$0wwmNV$n^%cN$pxu~RoQGpF*gaocCQyp z?me*Jk|7UFO2=i0u4Ntd-?i}sIy%{2Fj(7Tb7Eu|gC^(C{l--X`1}Lc2A^6oGH<^u^lW#^X3`Q*2 z$|XrfG2cG$zmxc<f*_@fOB|M@ zTRgsktPJD7T-}^Y`PZM39C9DSl!X!-7plGtLI3asOdSEBTk})tk0P!z^h<7%Kklv8 zQ68JgWAaN=Nf(5+ythPqWRJREF~C*}EoK>oYUdLqnh<7j9WGJkpyX%L~?-vO6l!_N`CCF@OMzZVLZ9R_>B4R<&+m$NdJBOo?F(YAzbi@9Bv38zKSB=NdPE=- z>ruVssGow&qCg`qQ4_LT445Os(Igu%bBuy$RtAx5!1MtR!^z<(f}TI!p}8FX4GL;Z z*?k3GZtAIs-qxr{x~}XWqNR!Qz87v#LjOMrg_?UkM1_9Jzo~cgGI(m}Y#nm-UAEB~=orvE;5V zJcFe5m={wB04P@9gaq_qiiU#^1bP28ck6>xlpk$5=00=J#VD^* z>e*QR7~qpc`)|%*1A3mkYAnVS55t{mUZttb`gU^_x*3*)sm+sm3RF;S1={5%GNqmW za(8<9;jbN9Qtoe|iuBPT^cQHw2y?8Cp$Z*xD-MKf7@IavwdkaLb@HP3UEo(?c0_T& zTkyJztINMGmM%U^*5|W}%iFtqL`8}ri@m`U4;q4`GtvF;sLF4A&HxRYvO?%LnQC*I>Ge}f-)7LO=JotqLjkF!Vsqx zif1-pPIy(3LpU_70m`feNaUvkc@sKIXz}Y5j94u+qJ$Cx9*HWL8Gw|Hc^GJ0DuykX z#`p_(*e-M?xp7qm<0_LYlvZKm>aBIA!h~9BB|amm*rsx5wx+NF4;4`wS8lUuJyT=b z>5nK(qUN@{Utw=sQF~h!c;syRIad{6`7z5`;-tZkGP3ovB)%0s3Fnfzh%n!sDbzx`AsOsLMi&G;b#DOC>9wUoXUfHyN0*{0%m&$He~C$X!9h zFnFOuZ^7_|I`O&~;%*R|i-7}Lg>H^ID#JO}e8m^Z<^ z3Fb{OZ-RLf%$s1|3g)d~-io{s^Hwl#1@l%gZw2#KFmDC(RxxiC^Hwo$RaS<1tC+Wn zd8?SWig~M;w}yFZn74*`YnZo&d25)rhIwn4w}yFZn7593>zKDbd3N-S!B9t5?AkkX ziz9u8#0jxDLAMK!lE~^VusQ8w zQM4>a2);CKk#))mu~2I_TYOlc5{IWm6=ZHDI8xMlQMZ7u$>-24zP=z>NfpjKtVQtB zXpT7nOtH2F%uYN9Qg&nhrtxdUj@I9BVaaHY*Pn-dm);NnqW{XL5kOOL1Z)dZsTGZ)(pSaW^JduA6Z6vv{~(X){Znu zXtkX&CgrDm%Reo-(PB!7C*z_Vq@hY&s+5x)xL;yskosg@aC8Y-i8xiPJD3rx_sKNf zmExHDv#V*TT*LHapYg7G0`OlldmNuiB8mNwvw^-;6un@R#Zsu~kMrJKV3d79Zh)N4veS;sU*sB4cxg zzg~cgKY6Wog0#MmNj%5)ixFBa9HaF`3>lEss&4&Szao?A?S+A%Xu7!mYc8c|o-2mE zujzoVA zOyYHv<~oJ|ly4~REAas<4jG@EhzIxC3>2xvcY$;x0jZ6s0*R>oz{80)qFnxp;sp~a zV1Q08(&km=5wB=F(ct?ic7|ib*-EU(;nqyT;)F%)RCO>BJM{xQfV2(F%;e(34J3%~1PLhO0 zMmjy8eLd{lWrU zA?hkvBvu@{ac1eZtre{nJg?irdIk2jyXh!-jVLQ4Uy&G3+TLAUoqxKU*a|nHtH4vM zKkWQUvu>y+>GOF0Q7nuJf`N(a&Gy|!9+769EcM=mj3EEM=^Meh%i1DbIU&#?79^ia zUmz%SQeq|HDS-=1h~j}S-AK4GS<=HO5>V(-mu(3kMI4SHjwBV2_>W*t*C9me{S$9R zC5B4>U6L2F8A+DlODw^+J%>jhj#$IZSIas zNby3AYCF)9K>VeE(dbF)+%AuVvh`~I(ruO$@!MF|E5|fAjdx>CGc;g2neQLK7vGJf z(9-hi>iY8P3#1yYj912s@5k%AE0eED)-ZXmS8vdTjXa)lFNKgnF)Trc--^ThlB6x~ zXlKY3ZQ7l58jTDmERJ)AJ-&szq(d)2oZiQCE1AYQu~`EV4%5~mGG2VxY;^}gwOK&iXmPd49n9!2B8WCoeN#@ z8xjx`b9q_%s!)HD?o*OHc(CplpQkXqO{I<~o#r%yp#K02wJ?A*vYL~%ww%OOR!ah^ z6}~GM$Uoj(FP=c4`*`zkpRNTE-pkMTcwD!?gRmDKQqlI4RKl-5t(Ue4o9nY~Ep1&_u5#3ixJa&)55v8G{& z;xo;oQm=auweoic*#A1bwr*<86g;Sa*f<+P2@Mo6D3d^JJ;WmiiHr4N)##D0hkr|s zp@r9>%n>^X;)x*w?-`u&H@BDfzZ9W(19L}dvUTTEKLKk;Y@>zo6s<*t`0c-zYRQC6 zUZe*pu>I=Ne2SWk2+jj@m2MYW`V^&$x(_!W7Xu1#nY3Fv3%ad$CrF5>NMr?z@L6#^ z8q?b0J6NIm>ZH7h{&ui`dJ5GFa_0Ufq(D#nNQ%kXN%ayD*OMA(77Ou+mmE&~O^|U8 zMI=8(yG{6;;C$cO>6S=N3qmS?&@r{Sdy0U?F|-hORY{eV7VNCLE0Ve*sYLP)Na0Hf zyF|2>X>P`;5dHxFo_RHHv)-N{0og1GZZ*&CuP)gd2L>+0}U!qy1f(pBM^ir zhT;~IQd@_h#Z`t$TqTz<6pG%?IhJ?l#DTW010LW~40uGHq;jV@$VxM;4J;zQwnt0W zGBah0@YDZ0diiI`sS-Dqca61y9c0h+J_jMo!5+dBlxLMoY);`JL49DTodh-^lu?T! zOTjR3uFa$QA&}KN8M_sMIG^)ydXij!oG&U27|G8RXT6Y!?b9+PlBuu@jDWR zT5eSCqBX0(gk($&fqiia4o!L3DOH)GHd|bb;gn|G4JLb3n3pv^0+*_d0L@^E$#q0H zrKdH@b|EsGW?61HysezZ9qTCMCX1V}rSD%OC+S+8zREcOYpsSST#f2N2m%9D#s4!G zGY=$6pjV>Q0Jl;idHe3yLl9m(dB6NH4qFTCcT*G!o3Z2-vVu{yc6p52+90CvQB0S< zN<3Wn%hCj}cj57fG~vImw}E#Qu`ir$5}=kN+TEfBud{}#a8uQkQf3L^xZPd#R-g$zKF>R!C{mOKhnHhZg+SdH=WHM z!eX<7D4U*nUJxCnm7)FET3p@yY&a#V_V(ia!zaYg97>71DXoPnYYP6r7b+*KzQ*8+ zEip$5`TluFDi|@R(Nh}FER-XOMblH%gDStOP>3h*?!QHJ4v8Cxxs}YX)cObhrZ|*fD#2RVyd2DuDwI9K=7zI!mttGEyzXuV|CjL3 z_kYw+v{-u*(KLXgC!mFwPzNv~n*zDT_6}q3;52T=v;k(dsg!7+fM{z38?Er@Cydf*$>Jw?`ka#{5Fl5HgrH3#EpH*d z!fB)qfj}<4e!025N03f0I{GJ+fO~&=1vL!O(%+$e%K5|9{dZ3x@T_w zHws4MjakAS;}~x4GBzv4pj_OPPpb4rGo+}ArIuYpvMvc%OTw`rg`A#lq!BUwq0(>H z9qqgkId2cNWXzkSk7kaV_$>b=*k@$P*CBgh^W$XgVJuRMZgx0h#4N zT(G~??Uw)&KJMe)8V{6Ura9ki!Xvlflej#xQqZKn0~L;^s@jyNJK(337qh#uY+L0A z;~$>Cc)tDuj+51m^~uV3?fK&OE6W=PLvemHbPymkw-5-0Cme-)$^LRVL4&lX4DY75 znTS3x$9v=8fJ6CHG*d)GQRpfJ|+1+)&VM>sf!K>R3o1=vC$>MPtkYS~Nntdwi0KI-sU1!@c@o&R!u z|0CGA7}=67G#f&}5=)BKo}&oENPCE1JDaCcI6^Jc!9E@hH>KfwW!$VO=y%}4V`=j; zfD}`+srdm!srQ=`gT_E5NqGkUZrUv#3j}cv#&~t*0zJb6xB#SX6~VHJrip+)mFE{6 z$~l~u0I6K<_zh?_tXI*`Y0OYaCas@zFybGnz&Al!l$eM+`y25fGLQ5=MeYf;FtH@o zb1y||_ayTB8lUb3Bh>}11~OX+@b9(|lr6G;GzaqR16^Q? z9T)~H3>ut9ARt{5G}f9r`8TbX1Oo;Nlw5yUz*o!*cRFG$8H^!oJ&_P52HT5&MB|$^ zn4KrfkjFrMyNmO?`;DS!T=KBHNNiEVF&d&WTG0Vl!2|6mNyyj6UAXMb00`z^Pf!8s zPXtxegVyrVrz;))Kcm7UViibP?dxZLU`fS-j=)hKJrp0{7Ay~4tUZQJ)Gh|G3nLdE z2^WW#*IN%4^MB*o5bKVrfXgE(4Y>RloTf#b?4BJ>#WCzWOIaFBan<=awR7o8<2=JP z2XL%ulz-PLrO_t=b_A!u+s?oO$u9`dE+Yo+g$SQCg|&XflIxK=RMs(ZI#7xxC7DkC zu}q0Uy#3SUJhy#Rj%q(a5-sG;W>Ak1;f*nL8HOi%v9UfSUpCo@Q?kUWhL9)aHuiKGtJ+8t97SzD1$W4Lr$*`$G zkgX?h#3d@RpJXjyl#w}q2m+Who>6=lp)3#sRCObjMFx@CWVtm>5AY}<#Bf7%firaZ zy=ltc&7w*A@*puMO815n8XQ`}dtNr=j-==$JA3F@Yjsd-UJjPgnxtzW*z7TX3#KH| z$=n63z#=p&nHStu`nVAy%;GyFv_@MYAkltz6Xs1}*}P znQyWHJ1~jsh{P+Vat6cd-V}U&d+TMhb;!UxKAZB=K<7fZ$|XpGB5hm)>;%D(^GSU_{yTZ*_hHVkrPBPrSQ&=GiB)Kd8)&`|GDLVW>@>O(fSJho8Z4O{#$-F zLc-(-iIXEFP>zsDIYL6^2#J*=Bv_7+XgNZ{9}(q|(~ z!z@g~99bIPf(Kyi*$AWmPhy8rJjN6mZM(j*v4&6Cl0HV~fh`DY0Dzd)MZ^IQOV>_4 zFwcs87G46YI-Ke!JTjLM*RG8LG!A-+8oe}J`SdMlD6O65L=*VPc8i=$VA{VCLs6$| z!#Mx0Wiwd8iYP*nH4)utqNc<-22oS~RK-fBf&55Jsr?$~$%su1;0$TSEn1}<-=3q# zC0<*V=C@&yE3So_h%s5wkdd}Q8IKo7hoh|bXn*!f|H&5`4;Z92`y+jl4$DS+x8qDM@6H1iAVi$e$60WWEo93 z%kqFGW}()NrgN)D@9lF-DmnRny!Og&SSpDlaeBOudeeGLDw+#*TpS5_kMkL z_v|yAUjIJAr+4=w_y3N%?jF%bn6b2UhnS;f_{1>ppSter=no^f$o>arZ14Q(48?4A z-^r9`qowK*QpOfvm9Kn{**6y#cZ+*G@$T8^^V6sQ<3HSGlCCvm=kQ)BRA#e;FprtG zXcOy2b+vXz(ho>w#e8UmgeVgLdal_85ixwKG)2kgT4dS!PRw7F+(hNL*3ZPX5_Y%~3~xfDdWueyt=v@`;% zmkpb9dBIJ_yK|fW?LOZKvkS=J9I!fJ^48#mxQrgj%tn#rYhdr z>Rl{JB*wcbOeNbh`c$hw`Apg(TVQ6|eCo1+qz13lhe#Veo8q$#FiMC$!z6fnp3zMo^*D_q4sqlxJpXd zX-X->Z>)^3QjqPu53^MF5a4RDKF=?5AKS8rE+K+K4r{Ue{PDi1#3EM`YOQdo$`9LV zbZM;EnZuZhF-8KDC}&D#DaZMt*jJwy861?~5+vm?Lc)bzp%@Sp8LB&df>NHS{sW(a zv?42FLFHv8$5x;jXn;)eQo-mpn@Xsv>|IxCF%8EdHpRzE+yr#A-2;g@Eal+77;N4a z1RRb#NWy#=scrE7gUs6TQbTZ;|LNQcuW1Vt>HE-($pTEvRw79W;BGXNht(tT>Ls+7 zqZT1twNRaqoGEM_H8fb~m&#Q50DEVx?{8&rA^<_;G(&(fikl`3AhkU_o+JBIz%FUE z39zz`&~H{(#a|c&D^r+cDc3}4$UG$$tenE zobH%@s((Zk)jm;e78-&YiopQUe&=)~b^&SowlaakhaOuG#in~H;gk!L)fO5+m&IiR zOhY$OTcqltweZbIm_O7CxuDWHTn$`k_a!`6`8FlqFD*-rr5t2PKsb3p%TxinS~hDx zCi2V{*2sOWCfN$TGIHBE29T`IQgP_EkdI9j}Z(lTZ0X#Y}V4f zhi`;-9OuqG;ZqPj&mxp$RwIX!jT6Jf%&!QhRd~JMocYS&D{w`U+g-C(KtyXerU^qI%L-x4>4J6D`w79M|mWssBb1e`@ z(Ru0o*{Dcbs-kb~n+RXDxEHTS`5a`Xm*6z`WhnILA8v1?^G!;DN`gA%a{+NNS@35* zNoVy!#-nvUz>XY&xdhAGYtP|1C#^Jx3vJXmFYud)i(ERwUwfXY5ctBT75pbJe<1gd z6HN4v95=bLaOJX>Nbi%bsL?;|xN=ivT3F&X!{g%dKi^zlf0Q76%3Cb3eIjKnrMNU&Z%j|mkRE8@ z7<$Z4`;)M=sB|E4-x-NLJcZMqNCSfdWKJDGK19e1id(#dc7C>Xu%m@fko^AsYAERv zp$gI5-a5$IyTcm|ksGEXs{KbMk`rNq>{xXRkFZmt#626-Y4(qHkC|QL^?jC*9({9*fVGPo6#20b0VSMeAiV*& zz%=$r~akf&=aqG zT3VAzq#n@#R#r*RvZfRsVQz8EDqk}&i7!D)JCb%^@6Y#zxHk%F*pM|go99MW3IF44{syC(?VfVhoLL&$MP#{Ut*=w9!bGM(P-EyDA$ zrTvl6=mzcTaIo}UCdQH_R;>|{qghqp&POk=K6FLrIds|MjPuH+?E=3U_~?1lYh%QBc5je8SwP{{=<(0 zz5$Y{gJYx`WNR&$<5coMmcERieVIJ_vhsA~A9eEC2p^8{XO zs$^uuY>YQ5>}v}Uo_25~)fm?w4;Ptp*Igk{O+c?XkMb+6eSuR|tR--7M#f0jv{DKV zcoV2eSxGJBNu_60ugTPIvi40%H(4EUQ)bKe9EsZ~!?7)$1f>xtyxIf(@5b8xI5R|b zZw;=GmA}l2^GuB}rAYsuwEsxSzyG@ZCz~BewP|*!wvz5bW_VyA(Vcc)29iRwVQ2^p z8*hPKGKwOwEr^NQTE0X%B*{)aL>~t~tG{o9mC5tpc6^UESRoaI@nN=!8n+11Te`cy zefV(C03GylT3mfZ0FO@U4WnNZ$Nzcs=mg}2KvxOar6ROPu#_k!q|B2Hm`@|u=7Hd9 z13%UV;@2>7zm9S!Y_6eBnEM(^(YZZ`M56W+PZE7NzMW++F?pB^bdMBxMt=DTf3~W zwLDJE%b>j$VmwUC?%cB>T~woy&}97I1n0|tLpaAN9G#rXDZoFQOg(;oF$)k)D=+_T z(cNoNOK&hFGjY)sLl7z~5WCFqO*~!7)~N#z=Z?c8aP6^VR^xrSDfZCXOB! zo$kI`6=bGXBp7vKL|vj=y=Sv=aIYw!7q3FF9(vMWEzopr@c~r^Zm#(-6tU8hG#b_o zr*W)_{BW(+PDWA}S>I?z8CBx4K~0ht#MleDkj2zb@C)mXysB!c-!AS@(xCfmPH!-s zs4_D4sD&25+^f!?)FP=zYFsW6zBPl8oh`4@^k)Yt>{4hgVXI0L?MeK8wBJk46LL7@ z-}P05#gn^4782$7lHO6IK%V84vfR3N=-fOsG1Z%umK{_LmotB1wY5rbLIkurDrp?d(bTr8j-bT_v?ZKU%U48g+wAhFmT zQ4Kx>7ieiHsdeybkV{H>xm--D)7dj}kf|AFPEYpCVN8p9)WUiKZ&oJd`4;_3LZXz0 z;a+lXg+c|(b%|oF8Tmw|r?W(3@+7uH$;nu0vOU3zuvnmq|5#kCvZdc>32tj|>1?oK z$~&@1AvWkPRO|HPm7S`cs@z0>oLea6yvYmae1Ah}7-Hk!KpG}^E8e;E0Gok%JPe7s zM=&1Qf9$2IVVhD!VaK3|ze`9z+Q0)%HxBpy&8V6Kl$;~1Wm*zU%|98OEN%`b2&JEJ zn8$=BWYw8dNG>@8|A@-CGAon#|Kt$o<(eL!%$t+`qKl805M?jUukIF0sJbJS$lgC( zUfrV{6aIuZe~BL>!gGMv^MzfKl`|7($iF zZa8;Mj8&r8B@_vkG%Mq6_1zWjtgM9BiTG7jCEWRfy(V|L23T5Nh=fbynpsVo$FD~G zimCK6Wu&xYaXeDCt_`?Xk$0Xch| zA`N-Ivxy5$zi_AIo^cg!Lnqo2;KSz5RLkNvSC_urZsgD#BOgH*wSg?4#Y=1U+c+ zaFwG}?hHz>iF*II#9vV6EH=*hw78dcG#xxx;O`WJ#|RyspKTq9*&FrDK$zg1{s98? zqyHQ4e7`q%`+jyg}lIE?~wIgE43kuAP zH6rGGY^gD+ZAPves^l@pPbE`CDTZ0>BHSxD5j&67A6~33udl7Iu5GN2C#&mgtBdbf zCaVWe5S1jpf~|ener5>q>Locr06*#&+X*)>8D-o0hhjIrjOO;!FfQ%o(b-fM29pP@ zkkKHhHvRzCf=^ni%dalnB@jyD-LF;y0N#~3y@na_g@1Xt zH}=t3b-67&9C58H)5xdAzgcTXa2+Yy-q@rlx5F_@_sm6AHt&A9o?iWRU-j}e(aW)G zM%BiEdo8c2RmCJFJTMX};{cV)ZrBfkO zC_z`ZmsUrenF(?F_{~wpIvW!RRnAIG0x!D)MAUKbAX=kW?`~WUI5%O=LW>YSnSjMN zP(r^1Q1+%M*bnC|^A%(fkF+MulN`k4gdhvn?zi4BUmu8}d~cdsL-DzE<>G?o&`ROq z!GE?b7m158K9IGBA#+M1_W?o>w@1+kE7zdWe93aan8VrDi6{ZEX+wOHckok4dLn>K zp*-2=YFJR25=o#gQI?0zMfK)zb?T@wwJin8&)~nH>Q+AI8?6e*xHeiWi`DDmWkPwt1Cw~d1@}$S1uG%?wnaq3xWyTmQYC(y@p7rp z2L&ric}qjF$wf@E$G3&&zxc0e_9?!T`2#Ktd>mz-lXU!#Nncg!p%Ph+@zAfNFc-O* zr%!(P;h((^5*m~K@S&x~HGooYhs4Wx`9dNIMqoJeVojqJWzdyw-3>Lq+QTMz5~WH;!Xw5l(fnT zT)(7oq_yc6c|6$=QjM0Yw%dn_xR(Hb z(h)#HCBw|NYRD%*$ud|Tr&PzQ(TzDnltBQ2BYMZQOAa)$=kv%q~;U=#5NU zFj?|M-s#CuqHCfEIMaM)$WG+ z-J%FuNuK!YPm5c$vK%d?yzWx>jcGICgiYd}P>ma2WelMY1UnE@hfm73EP<&BmoP|{ z`J~zAm79r1s^Mll&~Jn1TGI9VsypZjkE%CaqQr2zCMs!Zx@I~)(YI(_H*cMSMOKIe zE)@KNQ6Zo_!4rZLu)f4MIc28{VG_XrGPYzf@jv@k&HbO@x7UrymF5>r7* zSP6>NVl(hsyELN$2*T?tu;;BYT4Hm#OQ)I2PF2G^8<*gF?vXx>tktg@#sU}*D*FTT zuaGzkcNT1PXnX@TA2nFulz6z2(wsd@WFptlb;~Qr-hFhXT73Pm_|o0qo`3lQSHA<0 zB`Z8Ole_l=C3@~5I8HBts3~=!vOt3BI5B#*xpe@`e1_U2)z*2$CJ~l;yTxEbqCw5Z z683#%I>yC*VVNO`OAg$wj?ECf5Kx~>B`o`y@I#|<)TY&%$k!{V$8o-<-_;;S+K?~IPeP8NE(~9eaf|Sg&NW;rxq{C2UO_fIL#wx4jeJH!g$4@2 zk5rP3_8h*&Uo7Y#*Rvemw06p%G}3Fr54lf~le}SbmtTmip2FJlJsljp zs#&s$Kw1~hPhF0@EEweENvL~uG{q2C)6dy2x7#)mttyg5`r*2$4k=7EwdBHM0TY0! z`yjLBo4Yq?g@~Jg60?iW*xM+>68B7PcC@axg2sSU~# zYnMqP-S4aseXwDa8IN2APMxKjw-GT5|MK!4BCsj6R!;w1)S=uRt2KCvz5i_T(&kLP zJM|oi-$aQ)MmGLkMn_5DYjDcMl#!$isEpD&PL3ErvJXa1O$cU^{Gm9{QVkvOcw`Lk-)+o1K$U$ zKAoZ7_?&79)+OQhfv?EgV2|pmg}KxvF(w&M!uRAXQq7zaoTaeI-E8q0bt)k#_gbI1 zS}JCfEFQ`8unrW(igcKVlyg`hp~vMk_)yfTLkQ{N^pz+}o&Q{!9Kts`S;1+4UEb1w z2w9Kcq)Qid^h|%8H(e0Mgg{^ya;hy1%ABMS%%+7MTIy{4ysXIH;76F_4=rkHLj^^91#5j3)4B?x3?A(-g`Cdlun>+}z_&CPQOWZty z7zAOGi%a#SMmfU)Xjajx3C$d8+7m5Ov;<`}R~ZEt^$On898&6~Wf7VU;fQI?sW;3q zq-$C(iiQd(zm8ZPAz~nAXq>eJMG_%9MAl?MX-S4io|8aik(xvWk-#bIuY$gLkTt{* zzbV|q>5vIn<#wt;z0Ksi;F_}DSCUG(kR;yQg+RH-BP^(hegjfT)49ZlAjPL7%X?H- z*-u*Aa?f59Ste}3GgxS6!dz(=F5OCo_##M1{k~o$u>4_5SAYqn2p1IPKX1^RiEw|} z8-x9|dP}LBV@Vqu2>TNE?nr+5L4Ko4WKUco@HI{$6UtLp5C@K8X~3B$JySmK81O^G zYWMT)<^3XKgj|zefcShXJq#m~ckyOALt!@Q-2%O}%DjldctzhACI#vl=sUzbUP6HY zP^reGhQ9{x4NeDJY;n@95+c<$1`u0&-ZT+A8+=7KK>E}itOt1<`8Tjp^kp-7*NDH% zT#2%xoHa6Fj=IwyZx;&>mjmecsuI#KAnm%m`VfU7eGb8hM~EN>5m^XDw4jB6VWbU& zkQ;_58s=D14ZlSiB0BHnx#cX04Ddv%A6zZ2t@wdUt%bOHZd{pIPnUc6V{L_bNlli=#!@&0E!rvy z>BV!5f0gj!4U$GtzA5+*7S34-vjOd#=!pZg_Brq5~^TiZ9#1Vd1jPFB>9CT-G=Y=tfpT z^6wjn0j=t97l?s7lJd#;&zc!zVqVi<^2*8y&5{EgxQoT)4-$*{Mo}(#EhvCL{v6t0f=VczbbW+ zCweZ1N=v3;_M=~Ub;I!$>RzoKrA?aVQ}fD$wJHXM%ePg(kl3Mb1tIthJ~kW45N+Uz z1tP*FEwP)wo0DM9VL3-v(E0RQT>CI=1E#V4-IJ-^fDxbO1ip*{P!_eJv4ONzpd}7# z@mxv0NdL*jkdHdLDOK;qKsosqbOU!U9e8|uU-;WPfA}~auOaiF2D|b0a~N>K%U()j zm|UVd;7|V0a1EyqGSG`KaAxPLDofWZIHlC17#TbDtD0Z^Ngs9~y2q^DMr$3mk z#3CcdN^+8bxj)Xw6LE+zgVkb_F9YA@-)BtD=5Hw0ziV<^T+ z+oF|DHmWqEA0E`CMyfv7sqc?YZ7l~l`KX2w8FAdDyZzQp$kXkl#$kI>5mdA95Pt|b zyr&1_zJq%lEv-JPZMba{SS#xh1C&CDuZwvlk7>{0eiQD5w&%TcKR^ccczx+yvfc2{KZ;>zd3P6z5%?4;(ZVV|(#){gh^ z<`OO|Y6lyIKSIfFf1F>-QG{FQ-{{|2X+#-voZl&8g!@?68_w&qxVfNhNlaDd(eqZX zwX}6|88bA_k9U2s#-RqM@%3A153+N*gyJU8-ha5hfA${T_5j54fA~xw88Z$y=dYHQ zWc(?*^>ND6(bLf%|7d?mjt~qOvm=SXekpptoiT68R7&^iZgfSnBA$N;ad;69p^XgI z4KYHgBZ3Y+NkNUu#1P~68Y*0Q@9jA}lcb1eEj-zxO?6Z1qy3;5D@(j`wgFh3MP4X|GAQMvtXXk&(v+Dz3CRg_&v|2Z(;Q`%f1BRk z+&Otiv9a_XQ~?U zDk-Me3<97^z-Z0j`{mkPi=wD*&f~}ei@?GsYd>hdg|I}qR z|Ll(_C}49UpF>t!CT!qyg1+}}RKzmnH)AD;i52Jqn*r97#E1{?>-WyLM6W{i!5BC)eH zToi^#!?i@wp~L_xoTFHpnu$@*p#kp>9W0E6V=}zA!{&`9p{7~l*Xz?EN5)h}>0`zJ z@}(72F>QEF#rW1{b?C|bm%GzTRAy)ODmHQ2J=lDy;N;p&dkU|`D8U};dw2Y&yvW`# zzf|ryG+~WMQ#dSGm~_R>ifXM7&WpcE?k3EZzsa1~_Zb9JlkynhdMRO7MfhmaPuH{> zB;VFug>g~lyA#kz9Er#RXc#a(J~*C9mk(N!f1A2473kbi~NRYHFCCA^-5`6a`DR6|Abi4MtXC2=uqUL%^Xvkak2aTEbgn z0>4Oe)D)qbbM}9ArYGhe#8q5=`q4SK-wJ7M#Myvp-L^~iIb{{xAaxYL!2ZMOOW&^R)PeX z5-r3i*bYr{B(z9<6cn`@7@2|NY(5?>s!LcD(1#M#foMgk;U!-?~TeE2~$k zPK%}Qm*0+0R>lbK&cAu3(!`_Sr}JEm@~8t0$&rS6Jd{#-vF4^FfF)rLmmR~bmb)a) z1)pNa>T1Wk?nX?P3@r;$o8{s%&k<138C+Iv@!NTgo(!<(M_v@^MdAKx#f?E!Y9y@k z5dlX{wH?zvM>9!UKFfPn2LmTF{Q9zP8V;F5Mrb8hQJ>M??)2c5oE(yjrrm7%2pk{` zp5NBj>(|}Wq+hqwGc#ms22U>+tw8}&ahai_*VpG-wB}-fn?UkN)Targd0ed{qV?yx zH?}fUcnDSH1TGA6Umu>xT^*r2wS~XVr${)GKv>3% zqPn20rUe?SW|me@SyPrm{SV!Z5`YRle08td+UKEJxU`B^gKlF9XK{<5DC0yV!qgU6~O z(ERue5s0Y6sy{1irZJ+L!Y?D!s@N%996koQ?f5M`G{J@#yh6Ot6z%vIIV1D4N5^6-=1SBBy{hkdl2R&B+hN6+`qu zLP1IIOP^PkHIir~Od-ivbztt$EFPJgoMna6y%&AHVP?EW-kIB1YI$yk051~=5mh7& z*h+37M8YFR? zVqS)_Ag^#@>~%VOhpHguTazihn8Q;nugi<8orwGmv$0A$Wz{;+) zsFs=PFq-OEG+RPtUoOBX2BAqC!N?UFD;1h05J|@%y@Ic0pK?T~oKQG>GlMf8ZI(j` zUy|~So``54m!CGZjz&Ko@%JEN06dv>7{LNY8+HzoEHUohNc+hiaM?S>f1M&KWBsB0 z;gf*E;F&Lvz((cY3>0N_&^W~fsp?!tqy1-Fi~PnQpuW~FQEov?5QNGRJ{4ih4uDPg z|H4iy$mx0WW4L=rDDy~}62FDC4jJb%BFIAFSwl=6%-H1v_Gc^N?^*eaWk4j+(yV#4 zCesc~W%*Z=3Q9Gn8HiF(Azlk9OK(RKgv4%?M4>fOH@+7gv_%pb9pAsDSStK zznfpy>gybs#0Q9WjfEmCiM1;6YwY?iBb3A_BfqtDo|beh&-s=JZPX^+zw}i{zam8& ztq~VRxRj;vSVbw)M#IDokjiDM^s={}#PcgOxB+vt%JiD}J71P;F5Wt|axNJr z6I*iAl^ln4B}v_suZcw{iU&fz#+IAaHkHs@kN9X$6(j90py7L~FPjVQrz za`+b)XA*`LMoy0BDw;LIQGztudKbE7`?hFV+JuW&q@4Yivty)H2q?m^RB>mxF(Z2} z!JugQRf6Ip6Vs6=R62bN(tdUl`D0H=jy@=bxg74Zg~o*AL)O)!pBscsKu#OphyvAv zh1LwNE3cdKT}(&u!!u#KBSq}47Uy?0UF6iV1pcu;i$rYD4YwIv~5pTGhz%__K0X**I`OAr$78P|{mGFg%R@a}L%ZwB} zVs;9jL251`M4Pjh^KNrj+Cq+Vqo0uW{6yB0z%(XWSMO4IPcxIfygd1#S=@HKmSm1{ zw=D^nu|pO<+9KyWh7NZM_ zl$6~%+kK=~hGh04iSYfy#eheYVm_pqswPCdu*5Cus7D@7^8T($`eQ1i%S-j67krbW z*HRcE&d2<~6Xj%TSM2T(jdUw*DJ9haV&7A=*ZJ}k9l4lrg?Md#A1YV4&#Caf{nR*u4{2?_c#Z>^F15(aJV)3qfkwJb{k;XMP}+ zUC^5e{5Da!5&s5}j~~XGwY5Dtl(VJT=xI?F&en}h8>JJgE8DYJPBC2N(u@y~WBw)P z9m0jwIQ#IaB3c4N3Ee1U#}Z00%ww0vsI$fS$2k$?YVlDc4BLjGl?|8qmS1eInasx& zKM^ZMw$vWvkmM6j%nY&SQOzk_RNcgTJyF4a6Ys!Pg+KsC%?VKI13FDYfV8OW%S}Ll z9gszxG^Z$RLaH`cHqE(u-d;cOcQ4VGI5^B@wNFq*W>}zJo%|E-PNy zMD8PCa&9Xc{ea<9g@eTH7-@=!0M!^NkQ;NN)BNJ`zML>)68M5uxHK6&GuxSV z8&*QE^cu$+CNc|AIc@8f%35YD96w$qaL#eAfdFBVCy-apW+)l;?qr7z(*J+n-Ymwl zBs~wiJvHQzLvlooNO2W;Ii!YJ)7ibya3DGpiRZ zmJO1kvPlXv1<#oTi$6>3Cu3_sk8l@(x)^E26X zjWgjO;p(bN(mEpxf=D{^6}b={)6M)2(#`y?(Ao&PjG*U8(Mm#!X@~_A8_z2ECUcei|I40fD7X zg$3y)7=$c-LKBl_s$9GtmMVakvlof@Ph=3dVkI5IMvJFfx@BB1-;@kYLA=JB`i%_D zd?-&Ja&&|O8T^vtcZwbOsK+L<^CEE#lex0h$y_5V2Ft>#LhcD%a2{_X0t%kjcw-X^ zqChPDOI?(sM#s@5_^emq|HFRiZkZO2E%3cETca*Y*Kk=czQSU@phqZWbM(TPd`eJ! zJOTvl>TrMW@bR+Zl0aErNtOaB>PRA=KPtaU$x#uPP<>U*+Wduq>t*8X>rt3HS7d-< zk?-Tp6A0B(?wh}4!4(?`r6tUQC_C+BIutqP2B;-CODghaNKJ7F5@c;6sT*}G{C#l# ztU8|DfV-SSh9p+-fWrkN;BYWn3|ILLjD$(Q{olj+d+ot&;{}HN@eU^zkD9zjU0kyf z$+*ji+pbWUo#%B$1(q&=xOD6TNk|YAY?K66*s1-A!X5%&=^Ti@WrE(%R7NMkQfy{g zd$HS|qDm;1TNS%1LW+}!I@HC*2A5QK{G$3Q?1$@u*FSU?bx})g?8tNSO@F40VBoS_ zDA_D75;ZlsJ=Q~k%*ZJV>rmj=L^{#xP0*nL#8MbaaWJ3JC-+bDJw zN;DOG+&dkRsRDb&dXFrE!}j^7&Yo+O9MqkvIOHJwNZ z!5-3!&dC;&cm-E%j; zexrTx4Dlfd5AjXT{Y`h6>J*@;x+W{$B=hX4A%VKA5o!BmQ3zl2o-*|lR?6?5oeVRd5{?WJgsWSlZ}FV>)3j zdQhmr#!^%4(t!xNASrb?u!4|a2hTfan7=Fv<~eH&?_8yM__Nmq91>BV;1EHtD^64M z;u3)XdXlYt4!~K}~AL zAA|eK1Td>hY+x-%D#A^iQTw1 za)MNEl+KbC#jHp8nn$Ygu=uoDN?K;9YvxrBT4k4^6%8unIov!)dJD8wO^vE;_cf*# ze+74+K1IYnoF-c%T)cdBGw(n{lW8(hq4D+(`9~kxK#LGY6@I`SVE63)a4ISSNuaqJ z=J?lm#4;DqR||oq&z=L&rkEy6UXIOpeEJCIKSh$ka^zAtxj$~xYWAhH7dNhW&dBBE zAa-9*eU;@!0=t7$27<^+6E#^5A{~Vwk4m+m28@%z>P}4U^adQ5kSBuq~9ra342xJ&4`haD53t6rCq~--C5=K$(TcY zRvA3|A!kz&X~$;Aa%)x!pF}XFK*Ny8Pg&|>-!RaOk@SZ|YyOeLIX`s!iY@lPUR_BE zGH9L!wM14}5V9(QEgw=OVq`0wStF#T-`PLwcCfRW%v2eV-rBg=8uSOf{;1dP^alOj z{H<=MM{MEAlp_Sagit|1Zp-e0een?{gLbduGmm&>mj4T-Rr`4v{(xm7f{QXLs0fU=84SSUbgdSpd4G#XLRV5bWT)@?|p2 zR67QGa4J!%3jXn-_DIOs#_A*fWRrw|2T1`oZ{;#m5DTYWF)l%ekW;-kdGTTb zHPCxk_=RM}V%`=z4(;2WMg_yk#ttNw=%5h-+*UN|h3d9KGt&LShVo(+!tRrG^|wWR z4FUs)BXb~^f*_J?B@B`k`4S19IO2OU!64h?GFdJrf>iII@>v{Zkz5`{63O%IirZnj z-T_6Xow)3F%RMzggn5dfkc9mGS1&FYcj2itRoTwi)24F8xDIpQeL{|OFw1&zG}aES z?>MR%)XRPNxW3zQmmyF72`ZtL?E59RhawFsV^E4KKCI3xK<)F-oENjE&r_Hoe2f3* z(G&axw@tuN4VL%kmIZ)6HvlW&kAz_4Axv2#7sdkW38K7AZ5H*4pbO~}jzSWHB#HqZ zc2q!;Fd9(`SUMo2TQpT1zJ^V#Bg0}O+tA&UXzgYoWr&s||Bkk0M=b9LUor#$BE7^B zgw}}uA=cCqHOiphoP+)Uyu38%Y0fv@GT6dd7b z?<@PoO;%YepgW%??L_isqyxXNXV_b_-l=?I#V9cnKnl{M?YMm=50N!ETYt2R;L>dv zvd;l;94YZwMtWV9p>&MpJ{(=a0?v0+MIA}Blq`0h*(!!W9qAO7n!pW)!yTFqA3LE5 zzOaNCQUD#VM!|RlC13?MKdl-;CO<5D4A6^^=9@dbyMz5YZHiny$yP zUR)9X0XIHqMbd}s?eQ^4Ei$Gg_8}5Z8L|>i3x#46m!d_;Qh)o&)#mdL_O7xomU{4; zRdsYCHRSg9&d!jL4wz_91I#e+q)S;1dt^ehMg7s zgV+GQ<)yl?Q8+_KkIiv+tlHtJ@N&4=+hTqJ{%W5>n0=1ylm;gUi=o9-CH$Xr#pOO6 zK7pQ!7nB|j%DQy=HNUL#fj+*W4`W#4(rEGGO?Z@?JrqXsw_?{62RcUh4yR>Gp$~UvP*Nyv`q> z@EP6m#6zh=FPov;#1ka{g|gTGnJ>E4=*bLwN?)Fc9c!-LNfW0m6h2Zwrt9RGx%tU@ z1W`XaT-mks(tc>w&5S-SuBt)wGtUr~x7B+XaCk1sHD^EQ-6qDy@7vfdyj)!K0>W&G z-`lB~Yj?eJiQsm#%j%+XacrT`LCG3f>4lE z+2kk_f+=JDg*+fI2=%rLKbqm?x+?)S=*0U{UwaASU($(R@asfUo}~H~X307Dfyae} ztN%Jg?evsOI`P%9hVs_shnFtzi}-%Z=9R^mPAX73%p$cGjU6%Kn|PBxM)b1V-a_5Z z{rTeVgLnKX;0zQIGzxx>bc4xX<08WPm&S~dR)@Wdn&HTcWgJl z#F&cc&;cj|XGF9jD;<}r#@QuJe3#w{!O}s&>UHt7#YR+ehIILOuBrNb1F1@7R0e_J z(1z1WluoQ!DI_|-Jege0uHpVkQW1j+B$4WA!Rg(DuY42aJt{b%KLN9N21Z2x zuCH$}DO0bF|LbVcvyUZk5q?rKC!|k>*a@2f?TWBws+zw$vMUNQotn0FyS+V0L84!6 zLN_J3dnOl%2y_!;h$0w73^|4?WhhS6+TeFuRVDef+XBJUY5^i#7wA_bFM_-OEa`LQ9A2z2b)z;kC#S1=%;9El^IkAg9 zBx*Zx@t9?nDO(+xTktn_Hg+)xWd~i9TMUA$IqO)r19+UcBiYYSWNb?7FVHj^%gXG2 za(mZry?Ejf49{XTFXT?MwPvm%w<|qmsRN7$YS80cHcguNHsG&k)ua9MZ5P=fagH%v zQ~dR~)7J!fQnP|~yN5O)+y#aZeGqM)Y#a+)f_!oNMm}W|bnq_pGii(g)PmbWln7wZ zqA!ZaUSGPwM64&0epYe`rkk44I7|*x?F6C4Lf(E3=rIyg;Vm2vXom0oZioj8Y#EjXzJHFZJM~L9nt#W8rfJZ+cNn3D~Ttp0)(v zMMFpQ4eVG{ok)j4sZqDD`Lh$8avCOMHk`Hnu?vniSu>|4ya#d!lroGsqEa3XD48eVKXukCpk`ydN z|9aE>za?r%LoFI6P4Hc-fcR9+e%4CFXXdeG9DfA>xEv>dA@Se4k zg9RxfC1vI9hsV;g?bjw_XO{gh(>3|9cf62ZB6Ot_^+S_!Ih!oC-@G4Hckp75faOen zJl77pMJpf+FKh)>TDQ|6Z=q&O!(A{LO)aZth`ZN09UUM01dQYYeQJg!UbsaTmhO9C zjZ!ppZ&!j+vM+(?q4u_4KzlSj(-520qaWKMu8_3VN(NR-;_dT+Wf$=^kv{W{aH6Bu z45_NABt*EJnY@@*EeBfQJ%0q6oP(VRt1|!SgN0x&=I7}P=Q!bn?&-#Fd5Cw|5@uL?Y9jx^SNoC?5j!ai0~=Gr=>N6{8<)r)L)^w! z2t{^c9)3#k?L8R4x!LSglCN+!Rf*(YRH5RX4{;^A$`*-PJD{K%3yGPL7rAv*-SmjXzSp18*la!DecYL#F<@{m&k))+0YJ?v*C6JH zCj`(7T=miltS(MM+K@a4956st_+alQDZhz?Kr|#yk1&ms@9Q}T5-<=gEe@dV$wBha zl9P^lu29`BA)OxP^JGLgUZ`v+;Y$!{p+`g|s|BhQ&Yxd?Fke}`kSsHZYP-3;`*3oD z-;#SKi)>n4-@KS0w5bW+#y?$Et4;SXqTk}v;CW1z$<$?^fSWk#2XZIAE6^vy+{cCP zBB40>n4rM(#s5Wu!lAR@sV5|}OX?b|(KEihL0uCEPi@aMbG8AvR?8@ap=H8%% zEWX0)wuY+IVCkdWOGlHd?Ph1%h+TrMd=56|Ru!1On_a)U%iR=ktDNOV?VrsRY_C*| zgL=CwDcYH281%WAt|geCy)H==(cnB;5e10Co*-Pd-3iLXEgAl4D~el3i-mimf`ao! zI(@u%yuP)Cz8_q^bR7g!`kf@sPTYmO7Cl-L+Rga;o9%atIt{3MQcza=X*rPvIU6!3 zwf*qoGQmcL84<{MZXkHL%o-7>h@>Gl(mr98@%m=R&bdD+elPXFivb2O1nr(2J(UVx zuu`}fHZ@Y9)&~2 zYt?Bvb;qizI=+q6{!Uxs1Je%>4Hm!C9)AZFFIWlaG-9BjS8y;+Fz}Mf`_QUhizs_J z`Q-WaWQK&wSMS}u?^ISkag|LEXAxUu+k=k4oWnrgS|0!huE&Emzva~!!hj6R7;pd{#w2h0#4r#%&qY1?sLG#MWXu{*Js z1Uw>B38*B+3FSVD6%fBSpH(mKZf2K@MRn)o#a$%zKNWvDL|vmY3t%8nR9$Ko7-3B# z)QD8c47`;eEAdut+0GjI`IL=T7xZ+M3k^s_cye=j_sNlDt{3`Q!Fl`F zaD-sA-rR>D%~n$_729vS{wF6f+e$sXs-0da=BK&(66esXlkVxfdRr;w?)a(Ox6aOfd8 zr|?>9=0lQWaq?W4V#y;7Du!GwpklTZN01MBuDQhGk(_IrWKkXM<;fHW*mOmq(U3#| zP5#>`dCUQo?Eqxb$CS>6x(MNitha)LEC`EPN`f<4E|e?L?Lg8fs}wXbLK6I(ekbL~ zp_@JkS^I*)S(Cncydn!CsqtMoL%4 znyxWmQiOx*@caOlGZV-l#~CvJz0vii50@tuw_s+h zKV9s?hdmAySb#>Z#(RJ*Hw7a^6Qt){y$8bCk}*Z~OU^OOcIiEZ?h!o;Em|W!)iq5 z@5o0PE(*RpMNN9%g)bK2GpsZY91~-q#pP!bAKU%YbIBU){zw~`nI)`+<36+;Jl!P2 z%VlY!25>oYWh$#A@`{dRF808YPIrq_kXHrA5uIcgej}U&CC+UjE0T^wpb5i}YqoOv z%1DRpWI(k&&EXUPjX^X3qw1_9fGSowTvoDIre2A-S<$N@R15U`@5l;q&N0wc*O zSs*eQ!pV?Y5?U6ZlDa7@rX;zOK!(TFHd5apJ<{eoT86|@TG_(1MyNohv#wy350GTm z{b8ziFzL%XJcrU5Wg3bIx}L)(5&B8J(kUd!NeLCtC4+{|O2`z5-tGAcXu+GJ3aMA6 z4>5y-Jdqw!ZoAY2V-RzVvO7kS7Vd{W8xMU^RqwibB7v?Y1-O#Byh?oT5|{x~*;T;n zK{Qc^t7@PIwxY1d^F3(I6c1!f`{UoGyDI1CxbU40R=I(kFQEKA>aE|q__U^7lP zTbCkAQHM8+3Z86_N$50WhM8>xCTAM^?Pt!zc!j7w=BUhspA>N@rViNSJElZ)NKUvo z{;x>sb%{bBg(gvrc#Eb~R!Y|xsN^tlMD8wF1Z&Whv$+7jE8?sY} z=asL~jmnkH>9HyI;Z$`eTmnpD@vK6r$($)pUmCVwwU;`4rLq0Z`uo4_Vx^jrlYl3v zCh{2Rq{A+yojB{17xLsjTiFEPZERGLU6N!7iU1X#ZyX)%ugE{!P(C6W@O=AWb9UYQ zQ!0};UqSkZmhY~K5CbVcWji*1rI|hXwdN%hI6qhD`w(x8pN67y3z<1qS`WY0{4@B! zkKVszBxas`SqGW|r!bP+&k!pe{)Dpu-@Av_Awsc?`b6Ag%t^Z`Xc-m)SYLy8bsSDu zk}X;iDU*btQ8EB8%n($h6B+iDV^qf06L8sQ36gLwwd(msKP|F^KIpnDLD#`k)ieA{ z`*A9IfJ#Y%MsBSjHH+aCA~^qDhq3qiJw_OyBnkOa3hFej@*mR!4wbau1uk9|Gbjja zvTt$KfPYR;fE_%mPNu`t5#f1i*(-~$(IhmvczADDE)e&?I6sY3&985ETZBMwJO!mO zS$<=JQYiAynAv$O;!Ce!4YF5(X|;SvRlPsCg*uMxNFWAnPR~$+028AzMAO$}*Pd z=9FRvf0)#}Er!oTRgFCDF=jfCa-RqvQHz?e*@5rmxYR2%hrn{5X#r}=F%I+xv8s)KI#%9RA}+qDdkC{F_~u(pJz zK^H0#An)*JQq)flZPa5h2s|0Bk;dL50fiHFIGOw#p?k#D6X~ioEPk12ZW^xHNNwu;RFl4I7=n& zqRnepPj~muAgE_6Tk63_V-mHE^Hb(@U7lvm_Ta->r|Y(wi-+9p<%jbX^4hL^*W~Kz zT54vYUY={#!d+5^=<%=TZKSoK#x;6 z=i6*E_d)J@WFZGczLbuLteav%xXxwxFF6O&lNkuIPT4ZXYB}iumv-Yovvsy8O>j0{ zykWXgi;#=z)KHpQGv6l*eYy#Hvbif*+5B{)N_FDPyYp>|^C)ddvMn_<8>}l>n~Gtp z0ZP}|^Q&Y8L`|2iBKJ=Xxp8dbCFJ~vH z9!d;r8#kLJ40bW~R+zLN9$#|^uY}VYZSL?`p{J9dyL80c@Q;z!vH|#_>zhm$A?GpJX9EPq zkPEY~WhY?WT(og7+`wfff#Xg}lX^CM)}UnqVeWDotg!;-0jdo_EL_52fw`EYDuqkg zMK4!yBQ=Le+KDwXf&&FA++tOl^v@OM%|s4pHQWY@QCI6{M+bYG-v8(H3?_Dw$d6a# zFO-+?E9GfM)WF#;P>%!Ku1immv~fZTli!FkXiv2 z1_|A|R}JO+hz834hAK&vPpW(JH>GGe-5Aa{H@8nu%UY=Pk(x@1<0b`_`KbAADO!;u zb{h23kuSE#kP4+3kj~TX=)bq+=uT4+;z+zx=~AnW<#eJGu@CqN8JXyhJYD6JsiwNS z@?EfS!SVQ838yVvxB@T|UX%+lx@PC#wSIT~;&QrjCdGYjUQO?yP&{jXc>NrOexN(7 zDmcwRnHXd^*$7#Y95%UPSmxMH;8gyEyDTlDFDZk(Anq6yreZ-{==Sin-Z1S_sV4D; zSz0Hs$k8nA&FP3239Qo+SP82tJWTekW|HaWl~j?Z2}Bk>OqUk32vuceUTS*GP(WR` z59g)ue(06OgC(8~b44(aK4c6Q0efRdRMRWi;?;}k)!pg^N)92M0noSN z71R%<^&!F=ei<p+wD5Swo_F-`p!vFl9}#?z?iy*&|h7db}rWsldLI zmwj4X8cb8@lb0w3hV#juJ*l&2PvkWboqIf!q@RoGI6-;N zzbqY};VXgFqhw(3C!qt8wUu}7Bko^ZW@YdoK>qxtg$2D!Z5)PBkqqxyZ`Pf8z+2U>yZpMYJxAB_z2d@Clg0 zAwFihH>c;v$MiGblQOv5 z#Im90#^&Au%z9eduRTocwm}ff$sL(Qa9BqtvV{nE#tr5+IXi+~eLe7^PH1^;cb*rj zx%8!wz&8M%`Flz|_^2#vq%~THHgaC+xrkztRcWG6e1X@F6Ec;Nvi5L`P{RgU65$nn z03qKVJoW`N0IzAXU`=PXEvZ-!w$FBtwgjD1`a-2)>wVjH;#cI-tMzk6df*_G;nEJw zZ)m>;GGYEQK0klQcDP+EcH#2phZ2^~x2t#>xq^t5{Nx3kyg9`<&sRU3 z-&~@8AYv?UU%+_vr8TtRUh_-MpO^OLmsaH`ZZ^O44gApVfwan4!Hne!uY~#qZ39ZZ z$$l1>gH2(J$Tg-Wz^HBTCOe@sN+U=sC5j`ZmhKUW9(Oi?_!7OLVZ8z6MsZ!0OXf`pOMa0l`KCEm&FZVS%I;>2gYSY3TO$Mu%yIb0#QyiPXW5lAl_q@bwIN zrsOz1!yZ8E7}%!@Ap4Gi6gg)*SKpDt@GPyMU+5G1B2km^P`t6(BdaknK}QGVo$ z>A{trtx}J$FockxcLYuGNX1*vKzSmg!a$2CTl#oRX9*FmjPynlbx4Fwc#!ye|L7D- z!pmEi50P{<0F4~CVA#(?1%&BqMeprRa2`UN;l1DTIvQEhm=!d7HY5Itqy$)h>u}wh zVko1iV_q=~^c1K=WEo25Q#5!fT+Q0 z1_oDoa&dO4X7j}*ybnPty?D_ObIH~Y@=!t_jS;0G7PUt19=BE9&?LHvU!pq&R?E9+ zZ5|^L@!8Gfk|}s~Itd1|mF$z7d<50Ecf|rLemAM5a;Z#$RtOm zDXMXJG$=rYrX1J3ao}siH7n~&EI$+1O+LTO`dx;zwn=IWYIIhv1d2ZFyWSDN$)iX!5WxX4&SO8fBfrRZa~p04wADnkwMdE_cE47Z&17#A%Z++kaBlo3@jd{Pe{{IHe%4<3 zc=eNq&5tF+<|l!jaRZXV8L?d0^0S$%%kJyRi7Q?A_4NE-sjKJ8>g{aGaj@IwJ%L8~ zmMrOHz9RyMmmJSNcBBeaK)rf>EP-q%v;fc|>;co)RC$4gbMOMu$yJc*4DQ8M-PZtgQzEJ2F+GysU~wvPRgxCFsprfyA&H;eE* z$WcWJp3#Yu4anXp_8$q0@Mv;*)vk~bhdz})p^GhM+3xfox*!`?w*!o$0*=(VlVAOI z=andY?0JS-M6eyO>0~yR0cv?rm|HhlHrlq0m$U4nQ?QADW~hkD z&A>khaH_f|Thv#%fT!whWtG$k)(~Soi&}OduM_3LPQE88Vz{fhDoAAXNBN~7 z4juSw`%#&obb9OLiMWOd)9s6j*)UDPna8`;7R-v{U(yYTfDidwvSeph{vKz$%P zHN2GS3bmH4K#9lA4p%MhrXSs*ZMtdw9hLb94U+jGUyF@*GHsJv6dli;i+nN+Sr277 zEs*-T44p#4ixo)c8J$L)??~7ir7$9@6?$vJk{(nwPwyCwMwJn2G4b|5)fW@-tqYfqJUP|(Tr%_c&f1A>VBM@-jd#S022K%b}HZ!MjN&a|B9 zstZD)d9Ebk6}=t{p%Lo%BL?m|63T#NnnxDfQrJB8nyPdFJq5s@BM$ga#fCx=j!H8rAWMH8XZD zIHw^ZbUIDNG0Ow05ySfziGy`w6;EJ=v^rKF z)RfLrrc&y~d17-#fvAbM`~}iKG3^!*xFnvz%DUR;@dqwAEg0j!EY^CtE!|<8 z$Ipk&UtwDTGx<0gd>G`9Oj-aPzu1K^T-j@gC61>C){zSAMieLEAwuv)Rd>YCEM0^p zowx>)Oy%Io&Plu4x%;*1#WiXtxrf#BDhx6*zU8`)JSk}>eqbs^d9ZOSFU2r&Yc6HG zksSd0vkt8u(ANKT_3$ z^RsQ7=p&#T?&i+=9x5qX@_M@U80`nFutcY9dMrAYwdbKrPKJs~WPIJvjZa=qZUJNW zD7$liRw5kn%HYv+um&D{bNysXxw-_IzP3A8^a%vDQ^clzC}}!ZveZ7f0Uq$K+^|gpNx1{AHw->OoQF1yB6RUG$ADxQn_WkRdyEM2Z)TY=!f=dto z5tWBfmiTFxU?-!F64?&*)ut5g)bF{gQaTz8B0Yu$w2D4ltgFSey6fCk&o3`3WTu^6 zUoo33p#+++s=M;Cz524V`m*~_RFUyMig>q?r?7+nb-e|TtMfV)v1{ko4Ji zIy&7Dx9N!cl1}`DA^e*sFggGyKfPMY8`v&<98O95dyfuRu4r7nySur(cy%|2iFE~~ z{v@vgTHv4SftTI^jAjJrN6ivOUZs&L5yPhQp>N>ZFpYzf&M$xsP%#AYTGsXSuGvNn_aoPuYu z)RLd`$pO+soB#-qC1Y3o>q zE8kn3YA*v+zk?nSa!gxq=evU(E;}Dg+c4dv$DqK{Np_d0%d`QmqVhi}=>6lFg-3Ew zEvv8}o&3+bh|9B{=02=jy%)lNxdp#x_Ilh6U^?!hULws1WtsRK+@xC>A~8eOm# zP_{m7{%KI@+Jn%|Y`{BNCCR$7A&!w9F> z1SKfw8RLhZMY9iSwxM*9bRo+009b{0R2Vf%0qpXHI zAtJt{BMXpbB^-{*zB?+Yb857#7hhq#2j!f|ZozxdMLDbe!y_zrj+R!Ks|7mQMRP0~ z2pKDxsk9ibytoKH!8ugQ;foOy87d4%G@j3#O)Pfdj59th1}<9dfULQfc(Lq{4$Iq8 z>%uRPMTIz-Bp#+R6s|7yy@eQ7?#h>?=V$BaY0Y>@>fvSE6Y=d&zfo`Ji_Px9;)7ct z-&FR+&>M=xVCQ#^s-qp~eh~oyRg}aJG*{qE4_4@Uu>z^|`Ud$Y7T49?Y7^&5M7D@aqq!n88fjIs?jgvcV6G;@a_cQ zj;7+UuBE}S+f%i^JcfuXYoTgT5jyaO-Yzc*?nILfRJE{X|Rn_wlm<=5&6dC z)vPmuvKYh%V&|~GLqYsugW+fEsDS2h1s8xqtz%@O@0jGd0V7c>X%tm5Rz7wnZ^1fQ z2i+>WG!fyJ*`R=MLo?V)pj*b0KkUH)4S`NMB);cgSyCN?%o4KP-5yaCS_96Oxog3E;1TM_C&Nn z;(mg*^J6_dM~D0GAWxHXb6df25sD-JdQ{Yz`&zwM8X%~Efm=tc*$H?GXuDZlhzLl8 zp;W2lw*w*p2dZY)vLI$g3MfX>V<$98`eOsaa;XB7McFc8=WEXo>FI>CzSz_E-M_IV z4ZLvfTeZj}J$H(#;oXjm!tD;~OUI;fq*<)OAW7p=B)qg6MxkrCd3?SHU6vT_53sU8 z<1#==mEytAi6Xi6rPj<>1{1^SJ7BNinJ8~Ve#mHNl0^#qt##rvtk5QMavg5(BVVKc zvA?}@W(}61A^eb9?!g^fKEh(~$?4~UYa&olLdIXC)~UF7tFMA$6ZMMOT!$h}ET(lO zn)93SJve(GT_RaoW!aSP%+97c!{`vqX<08lM^QS1KupU#{-%Surz6M2eG+;Bnb?-1 z)J)tW$2S7Pvo$}_L1oeD=1xeqF4Fx--4f#E=csh0zrw(_l+ z9S3w<$v+yw8PEX;7y)(c;F2r<<9!}1IeE0o@z@1l<9LEM;W*>UWVf|UwqPICR&O7i zix&5gnRuMb6tK?D-ZRA79MhEO%r^J42e!8u42C#cNaxKiK zgyVOK5Yp`~_u=Cd-S*%h6qy&80ji0ntag%N=rFKc~A; z%u>P!mo-GaS64%1e`j<5F{~b&N5}7g-GR%69xz9RRU=jF#8Jo3LQm!GP54Sm=!3>b z_RMR1(v>WB;U@sxK!tcr!ut`rw!MCUfX<#_V~+kA%#b)+r>9|@7oogMuzTxtrf^UcbCVj_-`E z19Bh0=y3J>WBc`w~T5r@?A(~B&-A%x5kvYMy_cjhXfK! z5!0Qyrz2ma+yMA5cUq&CmDFj{1L$#MHRS(TDHk@9}Y(aAYaLYCC0%iiu)ski=%VIgh7^y zr7)mD%{J6Q$I?oe~wotF7{EHGolOU)Y zYU6!(5(IU=#hQ5a>?zKMv$we}xpNLdoaBeAnUt-nkX=OPI#4?~iQdiia#-XcoFK%N zqBm^zLSsJKgd$(fNAe3$u#B90x1Mz6mvP0VQ&8uf33P!{@<>uL=|B;ie#c@G&zaOP zm&Zn)Wz3ncdbIGb( z0}hns5wUb&&Xwq72M`u|Nx4A*6eqc*BT-4g*NgCdxCk;x!}`c9GN4KMrMK~EFihsa z=W6q$iQh=wzO6665-SvllZ2j07DiFt%JESBBrysxozd0EcE>sZ*{CT+lI4Ksk*uGA z@dD&Y{4K`nlpCBvWS4B6Q63?u(0?kH!?}0Y!#bTF9IhX1uS~BdFXrOMfxv|mlrf_< z7oMWfd~ZNEtr9f2wI(~#`#i7=UJA>mQ^7(0?$yoJZFTq2E6eCx! zY)#8BqW)CK;)sDW#+oVMCQhnc8*(`5iS*)kHe5sC)x;hNJg-Ij4I-fV=ytY|5)#$Z zw^m+Kwp&G7pjTKwCeEEOhg9_nEbd~C`j^$@YUcYjwd1nvIXsT`2zzdd5pzkPK`_w4 zgEgB*J^jhqu!v`?es?NVSD~oufKy3 zc0mPhsWe0c5LvI{=Mt1vos~&@b<%m*oOF~EaF6=jFe$B&cl!)l)@b2Yzt+qJDigBY zOuMWXUr}0|`~~Gcaf?#k4Lnr3!3EyUJ1*HbdxhgD4_;oV> zpn85xrY+s|yp@lXKq2veGOI2t7nNG+%a?ZJS3KX21I65;$liH~ET5+c!s#MEL`bb9 zir)0SX&YbW$*C-Wa56(H&87P+{6~7KjeLF|Ark_RSJy*TVi-Zu*~>O_u#n9?hLS2Q z&Ser!jzO-;9PRE(lBV%Ho|Lrn?PhE=ya1T2a(QhEzEn>;iC=R!_=`AypU(akb+0dP zejU{;u4d1X0Z8rDBFcS=boS)Vz?earh1(ndq)=4o;CIJ3p~MV4>C;iaqPPQ$u~fSi zynqC)IL6Dn3M`7^QQ@icUPY0stKNT>NO@^X zn|w*N)TB#55*v)t0psj)bOpr;Wsfjy2-<@CMFLG>snmmDU_WO~ zhLqY*L^L?5S&4Yc@;C$xOlUyOCFV7qIS3VpXxN7;>=YW~&Bx%5>dEzLXe+Ou_?<>u z-+n2@he=%S=Hng7Qg-0}b~^{~N)?}8)Kb>}a_8RE8ITyoD@e3FFgee3x0m(eVgs!} zg#{l-q;3m;-Sb0-IEsDvbO|2}{$S7n3`gHm(%*a00rNk=`Pfa|&D-DQRH@K&Lh`2I z_;itL&3wq8C(k8VCc#S?S(Os;p`x&gOxy5@rb49E>d*@+k?#Uu>jW#-3ncpXavS&# zQVFOeQ~twZr64A@@@!hcrBMh@umaZdL6UePZs9H=ovL&`d3#q$DP*eIWHys5vp%bp z>RX)Cy0FSoSSQ2=MTfVo0Pp7c=HOfvlP_!e_5v^2mb5c0SLIC!qPGoui4-dL?IoJI zmv+(o+QX$QU0EKLKr@U(13eu%wxZ+;8QpvR5r+qJ*;BZ=VD%w@_M8R1mWYgLHIKK! z3-k_l|D@BN8v;i{4xTn0sW9SUpt0mFrs_&18|8TLW^?ODUc_5bu(c96p|1W|ko(gF zsVjnbcv2+~{^9)U_T_wfxquBuFkkdsP{m@Frzy9DEniaSCrAyOLZdk6q{%KaQtqeh zWDHH#xS|2%%RhwdIhoIVh3eADwra z(n{l>rZ?0>Egax@dSO%J4!3EYPE5qh?8E7zAa27N<-#Wt907+^W4H@3citTY-XO_; z5@qARBHUOfTdlN>3xyR|?I5&eAA3kgd?g?<=bAH zC(9+7WwVpzh}|>R^=c zOQdT9aT*W{;y@lGBR#+?K5t;d@GCf0&meBn+qe&U8)4(Se~m-;z}H<_Fx}+wDHsJE488_SQi7e&PB9 zeU`QHC@o6jWc0{i_!I&s=S)C;jI2ZRD?=|3&j{l>jI;25g43fY1L^L&@~{~i+|b>9 zU@52qI<4lLYN@YaHj+_51_kQGs$i$drHG=E*+_TS_nj^x>L^9DIhPXcP(`@R?0kqL zC#IHnGGjo!|iUDOIO4w5Q5=AFiP1hqc`z+6l8kP zvl3x}n{o96)dtX88~0j+{-D<%_1c}@px>Ln1!KUDM3hK<1IJT(?r@m%O=X)U1lyBW zS67!v8L198T%j_oi~%2nlvZb`X}DeFDEMf%MS1Q-9t7J=u#Oy&JXX)&ogUTK_2w(d zwQp_gfXUK-2`StWyE=Z)kbgQO63h1uWiT^JP*sl(&o}q?9!pXy#8hmY@9iVahU@@j z9;EQ{^h`)Q>xa>Bwa$XV5+#)H(qvn4o6WI5MMg%09#u zO*ztK>z?-FdS@F8*3QaMfisi+4bof8^oX-45tmE5@e_8Qe&1Zs2h9wMEwSMe8J7SIZ46_6$Ygi|IC6_5*m!8tT-_{^< z$UK1~YNGt!0o)rRNO&j8^n@By4gtsa;slLrtr~9`oVCCHWZOoxn5O5b>q>?=90y3* zqQjN!J={&r2LNpo5CU?IOTLD%$qK}0&%8P>{&HhRu!fxZ<^4EnEuQO&ar7G{3xCnV z9a$&f0v}UmD4J7CF%_OLG|p2Fe2>FLqy@=k9u#I5I)xas4=BXSOK2$6ArVesV6$>lFdYh% ziDfaxlGepd{5}yAQP23eq!X7#_fjjza=+cYRKRyz;vBPOym6D@n2j3=n&=DI>Kn|it0XZ{g#wrS`*e3dO=|#&~MwfNtHUJX9;JdU?@gniRms1P> zd&&D)7y;w5DZxN9;@k|4iaJ$h(n8<@2E;LtA!v%dyF4RN>kSm*n(n+<+rW3vO+SoeZ=& zDzsK6t4;BL(sJ8KFSdbzqsM{}ksk*vgtHhS8I*%(Z~5(gi|UZFu~(BIB`gz?t<7)p zs8Li}2VgW;I#kwkzlbda80$_U_4ed&gQcjstNVkmZKnGpR_FmH@MJSV)BbL~psJHt zRPPeqmDXWR(5aBQC0!NBBfH@KrjV6_s%H?KcKMn9g_BXI<-ClxnNHtEy$Yxm{C$dD zIP;C8qcbTA2`0-iESgO39K~ke`c){pcbe$4MiOvQtfFXsJJ1x3Eq ziHm`>_#Xot8QU#}5&jamJ8kPtiYh$+Mj`?nwTg8?6z>IZSC38bDVT@&7U4_qKLlgq zeD5QNAgg69Ch*dxeqq$30a|Fcfh!;a16RepEufLD?I(ydKZbaY;$rw^kOlf#aOVGR>)%BKD{X>9A<%d6L}zz4+ZeIv$u1E zeMb@Q^)o$MGCxdDcUhcr?q!b%3-w-{A6batNhoP)vUJ5|(Ysf#@9NoKG&!!{$YIdP zrb})pA2m%byrgV3SNI8ec~w>xBd@Y-+-+*}+*?HuQkoe*!)wRO*s!aP_hV6}!Z6^U zij`SSc<_`@7j~)J{?;r#b=C$eW3?ZchnCodjxRu#J$D`jnRckTZ{I^;LY=e7g|A=| z0;+W0mIRX=K?UveG7Ltk6G6QiY5#Qd^aK$TlC4^D39r1oyOGSZ@&{q|SM!h75F`R~ z##;5!^!5gE6BoDD?cL;VE@8nm3y5{qg%*=mK;rK-61xx$NAQkF;jGw&Q!)hBbADE$ zkD6P-8%ZXD^b7ZN`ltU>drC>{;yU|c{4J}HN+nI1&()yM{_$Id7Aua&^yk}$OcBXz~< zl3oQI>%r@_PsF7TOk`TY?Y>P*d-7Gpi!qK0JiY~vC=yA2f)yf-^xol$c=M|y1m8w+ zyK5sSqQ6l+Oc6&>Ka=a){i*`H!FeKEa!fn;=4cu`O-aSnoDhK3D}&k7Gyaow(ea<@ zD&5nOi&R28u;HXHS<;Cwh-+p{@NJKgzq>Yg1l)e-Z18m0fu6(r>p()V-a4WIQD+^u zq>|2xUatUbIb^YdEfIm-Wfm<47=gE27lW>&r;_$eRM`kEH{#LZkyczLhTTJPcz>fQ zi1c#Wh{dlE2XS620Q30tky!LnUL`*TsmG7cV(b!Q&p!kPM;&OT6+DI>v1YF=fke*O zDrgu=9l+wdL#V$+XeiNXC*sgE?)24#0021`taef1Mq732>dP|=jy$B0gCQg;Rb@E~ zvE(q4!bxct_J&Hba(U7%afvVy!7AUr*Z&3@;`GE82TSdsDw9E!-+O+2fn4Y)oc!u$ zUV)A$A~k6PYLa`AnnF;G> zWMG+%a9F2)BtwL3&^}S)m);PK@EvnNF-72nPr<5}deyLP5U%dk;oDx3s>Sg^FPtur zS4_}=x+63hs($MVPk!u+;y$!DL z*#2~YI1Isg%eyK80XyRC0FpWw{H0`%$lQRV(RhjcXIR0|gRghu17%PtqfYPQnUz7~ zUF%IKdPL|iQs^{ak+q3A0q`l+Cr5+rsMXEs-tFZ~&Fzfng zb;{yE>7YQ!IyeA_y?JzkdM3!^j0}!QQ4`$xf|XEELza`R9CuRaq#H(MvO(*aKKVN> zfAzjKB40x5#w^I{Ns=>G^k}keNNr2Z7pR@G#A1`O{G!J^O0_ejVK*)aZ|bt>iqQ}; zfk~#lozOyE3Yd;(Vp#FD`c@>U?dsKwi#epL>b_Q{a8-Q+GN4HUT*mrG)x>>8PDCxi z5n(t?bO3~+5;}Po=v|$wfvb=_qUS8TNlSck>IS00(|i`G?+_n^z@W* zWX=TwsDP`bWwy$yCyjPzHx6HfZDgs(`P734+9m2-a~pkA_q2d!Te*=h(hE7yshE*L z0mmI_SLTgTB+1^_E%TW&cm^q2_V-|gf!7|&0cR_fs1<_iQ?Ey&Wj7>Xli)kyH9KO@ z=$G(fyqSrVpt_+P7TE$W(emic!|R>P`E#G6>RgMhUB0eP5F?dj`pc3{kBP3kyt6Az8DAssfoh&56g`Y8G%V8E0sY zJqSrE5xDATlKBbxjs30aNe7`%T8j#E@R^03yJUJt+$EQ6rdkAxn;GJO(%E_=0>Cpj zNeU1qy0>un+yNEZ6b`6chMc)9_tBNGu__?^QXUl69dNPPSqXY^+1R9G6o^W)YfEqX z&D1@)rz1y&K-(*^fG?0MZDoN%Styum7479W zbHqm6&Y{ir7I{*71qwxX6Dl7s&SPbVUwCpVSO+pG8|DB7(lla-jm(;UARvn zPDqHAO(kh5HD?kugmAg7Sqv<;3H(eT9d4laIf10(y{{*SEnEw$z*R6h3gbdluU6*d zK1^kx88j|7VO{az;mF|QEQ{)*mn4r=Vw!c)&q7Cox_8Jl2X!0r0YTc?f1>7`c4KoF ze$PHPabJ@YmkvlM&*GLvExIBm0@I7p$^9WLkUyc5&CT63%V7Yt3UgR=vXN~{h2@$S zHh`UMw|AxjxY*sMvkJI%B<@KwYPU6^HieeJY2tKlz|IfD$;yS`J_!YNHPHl@AMYY< z+rI2l+Jo4I0)rzjqESxIp6Zlh%EIK=wv>u+m}~h_Nvly5`{A=vnjwY1`+5=(cp{WL zubH{hHg>ni!?U^r$EZ?*R)Q4hsOAY{=Jsq&U=j~#17f%&`OXNX{g&ra$D_O#2N_vU zxE-e}P;d7Kz!c?k)x`_cT1Z*3TqD0?=v}dsek#~ZC7r6R#%Rr@D89&{2JDpb|3cKn zrUVsb^fDEM?7d7Q^H5s1c8i{n%@xhOW93*M&`065A>=L!g&w4Fu(Hir4p`~9qY$CE zOS6H%e~I`&yi!%5M~oo|Nv;hEW%b+vG1^&~yLTc^cYH~w}#YNc1H^G2tzMFnN2>40ab$4;H5)!zg_p62!lAl0J=0K(nSU#Xu!L$ zj*#tVdQ3+bCF#QQJkSD2&m!Fj@Y?Z?_dpBeKA+%%!*t!zw&TpMQRe_2YR4X zaVUPo&P~@FP9RDBs+gKNM<`x(v3_=T0-zE7lsJh*{imD>7>-=Juk3Nvj|4z>@>}k6 z8rW+!D7Kz?w>P@hJ=Wqt@iY8P`6bf%BndIGYO#sQFcZl>cz?jm8n(|4+%w#!Ju@J|D?bI`DESC@ zpqc9{T(yz`*(#Txq^96 z`8k_*Rz2F>f!M!+Wccgw=@ShAW<5H$;a{O9PWZvGs{S%~Yl~|_$Cpk>{j{wEHoJ9} zoQd*`dYU-5w1L`0PQ=Th0#)$TlAKy8;K|MuI$aCH$cBZXZ29S2o6gute{|Zn0H%CiW^XaR9Y8xjbaB%P!=!)CWmr91;1szxF^t4 zF|%{P^iHjJd3AYbbb3_g0fQq;$>ip}If{fcRwnKAW%lxJ-@)}`>*$feXpZ%AtXyEV z3`Z80VJ12*6gqUVlC4HP+j=_FdOPPBsit_}v_nQOC66h__-BmlX!pNe`~F7e`0B?S2s+%GgbfXW42`ig?m5RnM6+kPTBg&apjs)1Gp z3Yg?!WVWh`nVl16!XtWDXwkhFA+J*7-my_Eo+RmV_H{Qk3t#ROlX=|GL8`i(1*aRR zMfMJnVBP<9Ym}HEkiZ6x@L5ML6qi928IHp?qOOnC(QJ^1P{ppQe-?#apw~@gaPO?7 zFr}SWn}^_Z(-uw13NJW)367A*fR*exhhoIXo{eSp+eK&ZZtB8-ld&60fI{_Kr2f?N z_-z!&OA-srLBb0(A=Cb?Psju|A|_wpC&%e0WjIi^xyM75i`~n!>wRCDG^i1UBEgWW z{{{dw@-|s?#-&?5T=G0PG(3M?rP3rZU+CvXBZUd8N0ZB|cGYzb8W!yZZZUT?OgAf( zrceck-y9)h5+Xk`itcDa1_y((kLKDqwhvQo+aHjAZa=+eI$|9QJEK6`vOoer*w^?U zX9q}fFp=r;L6<3}tMNvqXiI@n>PEGyqv%HCFMsOPzO=L(&zzefE0wgZxZQQMf!k_$ z2lx>AAcl-fu^a1Gv|G#L=XsfSwPSP03Nl3L=_QJoeyA+nEPW+5gEFC%08aDd_63C_ z<#U9E4=&GlY3`Z~nqVL{1#_VLq9Y{o-a&Fz9&UR}(SvR}ydY!I7FC@J?cP2RdsqAw zk-bt>LFc>3d62E)J?LVXT3Slp1aQa{e+kv#Zi{$$9Z(Mf-uRwCzIEVYc6o9yuDifv>MAO}stSa1cM#tcsZlhw| z7V&uIQ&bN8E_A{eLNQ$7uQX?GeE0nPUh1&oKw7mRYYC_Y^v_I z;-(sruXnyHN2T?!9W!pqk1kN!=)|yjwu^zC0mdCaFqYz%I42S;E^ufLi%@^%J3s zde!EorWuoBQXomyF^ro#j=4_{7R`jEZiF%l!Ct70^Hl7T9>?^xr5*{~%5mJ>g-xbD zYirXN_1}^nJkWafC1fCl1zEkY6VABl0t@bo8Fk?Wj0!Cuq8#X!_2OhDyc{O>^KJKU zT)_%uByKCj zmUQCiD^PK-EgFfidr-)!5~oz+8+}=*A}>@b+jG;w|s(Lav9Y8RWzZT z3j&4D&E5f=Nw%Rp1zSSw#*;`>OU?KGJ}08rkENF@`pN5hizdvAYUDJGNGd-=3!l_k zNd?Y-M%7y9+b*sW6fMU0!!dIrS#y%Yji0SQ+J%C3`=krCBgn2t5We6DGI>;`euTYy zB&r@{7Vo`AtecWTm!?GpDPOinfT-YOQleYwzLH6Ej7*|U0@R4d%idvgI^$rTcf zR4~ZGN9+lz2A&GFd)VYpXvuj+Lnq`r*c+(i5K+%qIiUA(=0 z+}zy#=J4G${8Ao6@&+*u+wm7pV{`06)T4AOW2;U#V?AtyrC-gbcY%%c%OqO?-r!!O z(gsp-8A2#RCdOs0u4%H&djOX=i|(RgiUD9?_`e&r^hOt8Ft^ATgFEuHzbpFYT#IxsAk6LhxO_LWh}S>ERS2C?nMR za4bRY5_Aw0Dfv8>_2Mh29S$%F$MqvbCzau2^<{?9NHXY0~nAmek9YCzz#l~`su-eqVcfS3Sk_)tjo#Q{c@zJM?e zc%swy5Frgv&=@_js-@~dH0@Dyh1CFr4dFMTl9EiTW#wVsgIlzmgMnqb$iXo4s9zc{ zT9?8Ip{Lf<09Q6Tyl?<=xRGBp1w)W-oq%h7&Csk?EQnC|&OH&_e=sG7LJ2VGK>@u@4hr7ET7&f2ziP@WU(17BrJ4Yg=Wt|`0PYDRr7Rqe zz@|H#qanzu7K*}McfmS3!Vp`hQUas(3(up5- zbVfA2Az&Lvg$O_vu)xiw2VzMlzMME43E$3X=M zZtjkR+WAfon_vDi{At-Uxi8U>ZA3#t)9xY2VWv^avCD@sKQBhhRE-!dEl!Lk+C2h8 zFaPs!;{YdVH8Y2NT@J2j;G!{T&{rz10X-Lls;Ww=f zyFR^xdfjj;{taO@BDcq;7`!6PMV5>&lTL!2mirj^9FIEX6XJNO#N4@mfRN7SH#Z6O z_J;tOaWlXHdDz@M-P>A$x%T$%=GF9$3eM>VVI|x=%BD7B6eC0Oa7nCW?)PE%S55*h z58`Nz@V>wrSKG)jx-Qg8^B!S%lI94*Q@84>ifIS*9WK#jDv@Q1!OgR?qfBN4Y>6x- zu0fl}S|!ktsoao>G|5QwmA>E55E+H1AS?AnJ%>BFcL1St0`PG9Dzo_leqJ=S z$Xd7=Vto0QR;jnf$f~-yzJ6X|ALrF#@?0YHkiExOYHX{+MtC0QMg#|cY*hCoI}<ZFaC7>1VxAvTL=TM*5OARP#kzSeY0ue`vP^6xdiG|Lv3ZcY1!Y3yaZTn zMmZt{EBKqB8-d;r{f+x6qp05s5r&7ep)A^l3YSTWVI4$sh z?iGB4SB@w=bkrLmmPe>H>*@*=?grChnuse3kpdVhM5fzT>cRJBbA>le`e41Cj{$3= z$eS#r7?#}e&`ilFa^%hjKS~q~1S+c6z#(UE1?#{zE!-9rY4pI>ssf^}OJT&N5%Mt( zCU=kl0uL#!vj-;&Ct0`wPPat&B#dr3II82|mWoRPb)U79VKPQX@~@}tMl!PDxrIAcGK+rT)J-g@kQ4|?kVxZr+IbP8QKGT};b7L(%4T@8LRYXJ|B6qH301lcaCOCnku zz1BQ{5X7>1z%Gbq_s7x0!73oi+wOo;b7N!nNMQgI)Dr=B=Ww?UXFD6*j69h-mQQ!Q zN|j4(E*?1mjIl{qZaJi8ewTjv^&p9@F3HzWYvy+5&@J9ju@1F-JGdC=S*d#~BpOCA zH|CTPh^UtJF$T|^x2wBOhxpp=3V}P&w-CF^OzV~VEZavy3$iby-;68ZXL-Z@^9pR6 z$u^LVI%DrlKMq%Px{eJ%x+&P_^k1|&m{jZi&NhNDknCj>ZVpGA&34}eCn6|8_-8&s zuV*qDvV^F(yM7APceXPB_@(4phoi;A=F%2wQk}*gdQ=xZmL1hRCMIy(>sPb$tDE_+ zznUY$%W9;`@rK~v5=6?~4g2P@B4DUsn>jPBO@HCLsABOwVd z(EpHk%5jPQ#40@Fk605KZH5x8>ES5%;UX#N)7QJ&K@&I;daD3PIxvSEb@&;8pKdjtU!y`O{}In` zzd8=xIXObheyQjK-piGhFfx^jRP_!V5~eBme*F|>Vh>QI_M8#wOA@kAZlS?=F|RIO zEyyN0(@l(ZM;-$Lli`p!SXE`^o-^?Ieonvkm5(zAa0z^NZB>O<t`C8r`uq`Sl(<6&W+31ql=9I|< zPIY71Qssn4=w2Ho(3^B1^d(CiBo=s~$m4d2y|kiDMQv*>_{gsgq^z1ACT-(b0?5f? z8IX&zV?=*WfcKMB@=$B$YXudFp5PEkpb@f%jFbrfPz|`iBSHjd4U{DGVFBtnL;d^_ zw5-Cu8ZU{W6+|R`wq;_am{({9hTlAS2U$Qe05PZt+@n5$KzxmnC&VmYa{|_t(i-x zT%b=^6Azw%Mw%8lecOFK`C@j+WKRTllV8n7c5hd{EIHgkm5G8*VF@glcq(GrQ}htu zpjPSP!$F0j2x8e(WKNpqj}nfLV#Q&6|N7>x`ViiS*GR|VR?i{vWxY6AgC=Abd^&bG zH5wB+3@V5@E9i3|xl-zO5Tz*+MX`Fag|`JcJfoiXXLR=zkHZVbdx2+er`$LrC= zfro(pAD#DXVoJMeE~rr$Ze>s@NGQ*CwzmUKxcuYy)NN|s%+ki$%pT^sN5`C5xaa>+6BlrlCa7~7w+7-Q`g9j*yak^koe)zQ5k zUMBBV@!(Rk!9&!9vnP0_4w%~@SPJ0#C3bARhZi>K^0Epdt>OpQvryH<36iD+;XReT zZhE7&3hrEYKH-uW(L`hX8m+PI=Jy&}BJYA+ppeAiNe4bxLO&+=k|PsKfTXsT$j?N% zJtVnT6dzX(KU%m^Tr&i#)~cUUzFhm^KM1dxvVIind2jyh1a-m#Bl8D(1Uf zi>Lh^D8^G%`>5$7x47Qhl`A0FRVuzpMqc?}+&ZGOu<2fP5O+Pc3z2aMkB?KuDHK;7 zyG}Px_1BwX-`N@yFl1zzE4YLq@_FJ)zhUxnVO-(ZYp9oRb=wKHwg6kvz=n3twt;d& zUu~{DIy`Tl!C&CExj=f|^_v&XZDcQ;&7iwFzPXm9)1bJ`;ai9V>&85qP{;>JY^^=2Nk_?FQj<<$yU4Z+WzTdo=e|X8zxz3+u!r&Kl>LR{EMIY zSKs)|yPyC2|LyPpo!|T0zxz91XsrK0qtWya;O`9o+xWr{|Hfy3^TBVS@9gjY?(h7q zFW{bh$N%@k_+hWVi0g0oM*RIJ_}|tSe)u;(`&$qG+-H9Kjor`tPkrX#3yt6T-M{^N z_<(PG92(Pi{M}{m_>=d(qVXeU96kJA|HAkECgxGP{~P)JUG2~NKW^Uh4c>P{#=F)> zxG|YeE-o8ytl{VPKsA9D$d@sn@- z)Ehtj#?O4_OK+fW{-3?~*~b6-6-EOc5AzqJ2m)_*f=-_F{% zv-U?>`y+nuABSF#!Y}#pps|;IVjS3|GI;g|M5$oe0!e>{T+ zS^uM~{}KCtBlJ27zvRb*#@ku{x7j~J#oJl`Q?|#Or;VR?7titcjmBwg!mkI7v#kGF z<14Pm=h;8*q}+bcIM4c@H@@on%X4p_-TwEW@x)%@`DfYnJK6O&vuhwY^WTF;Wv*mA z|8sWzzh>8y?0TAA&$H|Ivg^z2`h)EHMRt9iUH^J^eVbjs%C0}ku0P4H|5SGUXR_-* zn_d44+4XN^*T0or|8{o$7qaVrDZBnxvg^N?UH@y@^cKsh^*Z)a&{hwyne%W;@|2Nt7-^#9kH@p75?E3e!>;Eph{(IT=Kgh2ChwS=)%&z~Z?D`)z9wm3) zXj~=NpK1Kt$@PQAKTNJa+xWs8_Qv-#9wpb`+xR58{#@g)C)eNC_}`Q3?{ECjXYBhv z-}riR{R52`$@LF5{#tVVLyiAFx&A`qvk&b1{!n9-T>o(6PbAlWxbg2M*I#V>qvZOJ zG=Ac<_I*Fnc$QrMXyY#@*MGF}_mb-$Ykcl|?DId~*i5c}qVZ93{gaLVIJy3*#{ZFA z|8(O=zt_I+k2SWF>z`?Sm|XvC<3C8Qn~nb^x&FDv_kGU3?@NuZCf7gT_!G(XFEsvY za{b2}zn@%xx$*xd*T2|!>-+5cR~l7v{jl*Dlk2ZE{$_IhX5;@#u2&mB`~CKPZ#9mR z>$S#jB-g*x_%D*{R^uNh*X_oN5aRG(r}1ub-EI7(jb!Ql>jTQ6JW!2 z0xY;rHW$~)=HNP6TwEuMgX?6nxK1XE>mSPIBa%`4??J|`J!pue6#skB_`~r6zaEe~ zkGCE){z$xFyOn07|Bq(vk&~B(2aO-g+N~5L?LU#VN6ufL|Ea9~r?cxnmR&XnrQt#2t*l+7zxdyS z#xKPS{Cbc`<8s~3+B?~GH@g-IHvadZ(T^AS^`J4xu7}yR2&(bF2aR#Oz^?~^d)M0! z8eeOC&49c>??!gLmtDV$YdO_3{O^m92S3Q#|9RIg@A_h6v+-|a?SI3yi=6t!#&+ZP zv-bbfwf_^gKWh9x?)vZYdavK#ot}%IjxR`EEoX3_(V?$6eIF@Yj?Z`P2Y zn>pM=-$x;X#z!xDYpu0*cRpDR`mJ7P)Ec)Yqv3q8n9s(e)8f)!@+cPF>j4Ki+*RwXAs){k(3($5P8?(0Eh>B zYolTRVmusAdc7fZNB!P(Hte@%t#Q9InROSl0Uvb9m~Qbzx4Sm#5Brn(V$qv)`t8wZ zcF~zETGQ6Wa5SA?TnsMsTL_AgDOPtVTpyDTD^8_ zH0_PMIx+FazkT&mo@lM1GFxYe$&Y%Y-n2EEbi0^Lr`7FEyRBL0!aae2EIj0%==2to z?qt!un6`(l!C=t2xajxC-3b(9^HHy@i{~Hcbk}D6ZmTz(3`UsGsNWtA7g*?Cr!&K< zjr#MMe*p85>5tdO zFrId?L?eu$*Si=lh8MH;xHm%15gijuK(%BJb^(XEA zY%-oqdi~z;qJJ@Ibw>j%Gp?pXoeU>mA$oNol}!b>WZ9;j*|gPfO=MVu!NnNg!LIbC zgNqhMpxYu(wyV4A%C8?DoOUPk`D8pAwC0m`=VA=)YHu;@PCM9$)}kAB2lk_@3H8%M4)#bR}a<00@sOHU(K_G2+(-s&1-;4)Zj z4}h(6A$SSkJjQdg;rwF0xaf9flWo zSl^5BtlfeES@7l6_08;Z8fQ3M8(g#jb~B*DS*JVc3??JM;jD+_ic{Wg>!&cm>ebz) zoY3BIZPA%uj29RE#TW-;G#*adfQ9jR)|yUx7l75clJ7l#C3_D%fURmTW*1!m_jIuU zs_9KTgFb)($Yea4>w!>=iw8z)3!LB{Q2VsI=m5R92XfXHi+T3~X)s4aMVpuN`FtVB zZ#a}3nX~b9I-N{`DQErBaL^w1fzolj7d;>pcj6okK<~8y`VJ=J!KgpQdiUp}E|B05 z7-xvDjb=KeYJxm_GKKcqY~BaD?2r2B0#x1|%&}0TL8moo4;CYLJ_Wm8YyaW21++Kq zwSmI=i|%4P?O(Lplj(FZ8g+&P#T)3axDtqQtv#N%`@?Rx+XJ@70h@KkzuSpnC_eAGBvUrHc{Pa4{cUEM~K52aZF-#Q-E`!PiHU)#>DGH{O+LL2GSV--OZpivqJK@xGbQmBtO%#+WP4_pCRWVu_~nakmTn zIK`SS7Tx|r7uD02TcZkLJCJJ30S9dv_lFWA*Tj{!5fZgd+18GS3t;&tt|y z<|*^65K^KjLMbXrAqqt(AyJ7+MH(~+MJaO;>g?)hu#&mU*4^Q>ig zt@pmh&;E?xz4x`RFhxPQ%zDW1@Wh2vs#>LcpYSQuyH|J>mFroyN~Lmr!a0@i70$Fr zrSPqhI7awU6mJv1nkto!emYbNFN<>FIV)GKa_=+UQQ)t-HN^bVisp{qZ<#4DAn6yD#xtNA)jetL8r8YWi>oqL0#7h~1Z z6|42CR5AR12&Wf5sVeoVP`+o+@PQoOl4YZJ)_;5ljqU1VB)mpT_YQBI=%w4Ma)l}t zEA~mCA-+2~{dp=~J#+_a5OFW%#V`*}ZJ2_&$~URIK2|5NbYLS2ASO@X)OpPOwVp z-hIMeLuFRz5k6bGhu^2+7}Y{eMjw;Cdj98={2z5uK78bb&%&xz%9RhF;C;%6pG)P+ zRSIvR@an2iwS4qJmH6lmU!wg7M{obCrNgIl_%&KCd>EDu@8|HIuM(Y6@9@L7YWM}z zGx6gs{PONPvV6F+O!#dcJ_M?i?ioJr!(%DiBYaqtE!VwIuWFThRVdr1TsX0E-NWy{ zM2-LFR?$6ax<}s>l}m?rQg|ta->khVR|*vt&azK<{f4(x@9=w}w@v@!E390(=p|UK zclbgs*Q0m%A=0OJ?w%gvF;Q&O%BO;6GKQQv3k(9DpT*&;); zK-p+{*gpD;KU%MCFj_C_(Mlbf8M;M==BH-CyuYW!EmwI@hW$qQbV^D%M7(||iubo& z{y>iYHjVZhwTMKPw7hI&*nia0k^awc9?GMZv-B4dedf`5)Qif)dB*EqOo@Arqw?tZ zsYA0whRD!-5Vb%>MlDH^QHxOO&P%sK+2W?h&43c!+WNmErk|9|vz{8Q#MByo0?sjnV5O+RtLH z~7Io!FNnIFXO=NiN|UzRjKdf@hg3dcB6@XXK46!bWV% zZXCfyT*>X+!vj3Zvy5Kw(Q(VNI@_`v2XZVQ;4Hq!y*$X@d6DVTCXRPK3$hHWvoYJV zCx>zZALf%>!ZqB&kN6FbF?yp#kAFUwa1A$e7mx5X|6zvk84~TM0&B53?_^(&4MT*@W%cgM+z(oB1KX;359as9z}h{>c;>o`+nKMN(2K z$+aVkrKB{FTSpcQ?Z)gE8NP?cMusoB3G!5VW@K1DYCn!#6B&-PDKd0Be8SHo!+s7$ zhW#9sPs!&a!}_U1yKc07#>mjkk)MSl!}b*;!}itWI&$O4uzs7!u>D<;;Y)OoJT@{q ze|efbJ2I^IqWrSFJ~GtPC-T9_P)|S0XDz?1JbmbLijI4IWa$1WESHH4$E_&eCfAeO zMuzn}Mux}N&+=gm!|LMgr&vB)el9X>_k!}(+-UiGmVc=HYvqTPA60&m7c5VmDRI1O zn2ot3!{aL&8M?zN%e5_UAh(v=%RM+eGHf?GGCaUMvMV4SV@6622 zVywv8Y{Cxg#ZjEer@4ab`7ZbHEB?eYjJj~6^UA;*c@xXCCY!JWdvPeoa}JkqH8*i5 zkMS&{fv<5rF&B%n0vod}yYg<1;sj3Ni`>EwxsL~Vkyl=yIG^j7hoxAR_1Kn!BExU3 zNs-}oGM$fe0hdIUPf1x78D1wFm2cxN?u`ul`!X`De~hOszifG`@YxqF&lnlj&(Ffj zE3sx|IIqT*H&=cKdssezBRPT7BE$Y?M26!%!Pj+cynZU9F;7uro6H87IFu0Wn|c{lk#5duY9;XE;6h?hb#GB zWTljp5BVwg@c@6|&-|6A`3EmERT#n&?Kd4WFe|e&FAFj>C&a%i!=IP2;o~YcJbkhG zS(qgmsw`e!mDO2~jo6B9*_quqG&1}y9m(;W#95rngI-Ei*7PvojZOjtrmGrCEv9 z7`H2hCA6k^MN3BRGclMTY0)et9ZqayI8jhV>T8i@2I= z`Br3DZ>zkWyOi&d_wpO%N9E%@%kxZ^E>T|@n2kA@kA+x)Rd^fgvI$$TJ@4e*9L&+Y zmy`J*=kX~n=8Ig()!fLpxt$+y5BKtLWN7m^$`d@p%S;uuV~6$9F#~U4+#Vm67m$mv z3U6f{HelRdAGT{N_herV=5UVZBt8}y+LPw+X+FzkjN9|Wdaujt`3`q*H}~*I{>0yS zigEjX*zZ5`wNZO{$XR$J^ROsO@RrEXu2qw9e?Z8MfVyV!&MBg6Y*kUWgzIFVEN zFc&iJ9|*@=DzD&ce1luLou6|*zvm(T$`kyP7kN$8t{;w@i8n+ROGzmdnKt~L$kL4a z7eaY;xfUC;DZ8*c`*9#g^IlHogM5s0_%xs8GOpz7T+es7gS)whU-LU2;W3`!pN#uI z!sCwWDyo-k%)xvt#8NEBs;thqeCR z5ntx3T*r;v#kjvE9On!9YaV9Y?-G`ukk9ZkQ$_t5VR<@+sSDzK19P$fi?9qU@D|o& zD@Nn|qy2YcSN3PzUlW#(l;eJzknfkLawg;co3MO={2X6m+@BMcZ<4q2eeU8ue#IYn zn7{KhFEH-^3H!e)>OToy!z{dk`5E^Mh4o6vasN=r)#MtC`-eh#Bl%9=#op}C5gfz& zct2-w7N6t-zR2Z_`;)@)*UFo@jXU`f_wyV6$e(zgmzXAe*2k|i=3v~<6pmY1F2=H~ z$m*=cR&2}8?8d$vz;T?&hxiB|=M#LE&+|3D!Oh&po&1R3@*t1#H=bqOe-$2os?3S{ zNy`k(%pAO~m-lfdXY(mO z!xy=n8~HYOFz$~G=k=+)k4G5yQ-HBD zc`b8BhVv;P7hy@|l^FMih1pHn%HPjL~Ka0OR!12^$q zzR%B@?5F)s{*k}(1pnY)yfW(74CkMo*YSGhWkD8aDaQS{Vfz|#JvL%nc4Sxfb@@%k{m-F%hrE+} zxR>Aa5Kr?DUS_J$M-{!!(lG$cpmb&r3`f^^b#MNaxlO0xPLk1xPLiVQh8Oz{mY@emRyfR zjQgX5*_7vCZsuoU7Gp_Pjm!{U|E$G&Y{X`4#kTCmUhKz#9LkX#%kiAX8JxwroX>?^ z78yP-R&q7hasxMUE4T9l?&cou<(E9bqdd-&Jj?UE#FVIiH=JMG{~e6`zk~60O2PO# zrC@xWQZT>u3$qwYGVT`->&5-z!CK1eu@RfG72C2SoEJvL%9wqjd$WM}q^%$<@lkV83=lOn_SVSF7=$WcWPU&JQBP=fiI9i45Q0d--K#XlppYA0or+>@bf;hWa_qlab;3 z{VdN%hR1t}(Ueiq<4eQ%x}k8s8KN8vPh))DTe`@~;e8ZYDJ3PE=_v9hhMyiOsjGzc zQ@K7{umihBR!vFi9T`?05P6GnUAKno0-{_!C1pxvjg*v!Bg1j#M23s(7DnC{t`mr? zm6GyGWbKrcHzMnV{?*8^{s)nDL%qr0^GIa9l$7{;H>`I-c}nzoA5Qw}$OfVRE3#o2 zj~Lk~T=y5*IE<5w4BJ(WY?6|4TV&JF-=(~DWV6t&F87RV9{RT;LuHIq9*@Tg`Mi8!I9xf9uwIuB_$p&754vVl!Nma%>$Uadl;7y*&{rU%GX8qOi6hwvRC-st~?$W z6<#!-M!8Q)%Kpf{DJefh_6z;L);rDk`=Nj0y4BQohkm;7LQWlCY}ZE)4DBP4;e1N4 zBCAIZ3fGH9hI(ohIXESyedLhvcq50Vr1Xgl+Ye+s{wd`6^E`N;^0Z!Gp*t*IKfHBg zvorZVDkK+Y8HQcO>xZ{SY;A^@W}L%ID>gj!vEeCUEato+`(P^l>7ND5AhgJ@Ek8PRjS0}xr!N?l{YdUVc;8&jOiX@0#N#f*_63wDKPO7b z6&R1d4C_VnfJX0+`fSS9?7(>ZWmvDL+@C`@isP94Jb6f-$$0!_*w0h)b9|92n7mGS zz5F)cW%9b=Pvm|4h6njGf8!Zm;6F_3bKn|Y$L!3*LX5{}hR0DxuEbkeoAEf!uwHYy z4ewz5ITe=omg8}nArF(sZ~~`rIv?XaF5qG=K4?dNlT#dy4DC_f^{<2^$@CI89Gydr#2Mfg9{(BEzvjxscGodG z^DrLw8P+QnIN$90c9kq`0_#^XlAdQZyF@C7dCDkkS8h{unH?cP!TK0o5;{EFZ62!G`% z{>jU{BK(0DzyFzu*E1IjuqdPXp`-2Maizg(ay+gyS5`}hqH@@M|Wc)V%Y&jtA(roB2*KiBX& zW@jE2VsVyXCC1}V!~Sc_4cVM+cn7<(HwSPS$8Z9pdAFnIaXRC1sUgpk7jQ9`@)gG8 zQp5TicIrH1n4JQJTP-_LLP@AFTbP=1c_IMuM9RMGg$;8o1Xtc=I2hUNL>B8*qvnQV8MD$iuxzZSMzAjj8rhMa6~Sgm|LlkJT=L(v>VkuT+vR&)nKLXI5 z|DIBQp8qg84_mUmDx3Pt#eyu(WIJ=RovNnt`fSEFypzfHsebZ*_55UaUU~TcbiE9= z%gUTAz+y~(E+sz?{=3I1Wc%X(H~aaYJ&*tDb1D3p5x=f7G8=QTAd~0y-~GO8W4q9V z9`C<9`*8?Ia{?dWBYd1saS@mDRj%VEzRM5!DZk|R{E5HuER*f8slBeQW@ct*UKU|V zR$y|z!P;^oCfi%v%U#%u0~lY|9zM5{?W~iOKg4kR!MJ|saUqlKt1ruIxPe>v9+T~= zd*yHVBaiVUlkKU0%V|?5&Lab}GAEPmsKw+mtjrpW#|4DPn`}RguU`*2?tck(vtD1u z{V$#de}bM9~1ZUhaC6E1h-i~*-jew=ZEsRKPLE{LDML zk22Zb`G!}xmjuzj+fGr#hpjIUb{%PYy%nQY%|BDZGTA0F24D)(WsU2~*7 zj`wpq3CS>-P>?)MJMUzguxvK=$-?+(l3>(ql^SpF^Je(g}6Y`;9M`~u_t z>#+Q)@Wm26ukm&2!DPE-Zsmm-U!NY9my@e9*9{9QiBOS~d{u|@Thp2>E|9CCd9d02jv zT#6N$Y=5jHH)bn#;9X3%I}Vgb@Lo>lG$z{{2?SdhgTUxyy{muye0sk}bp>(9gTHga-) z(qubgd>wjNKE(3ToWKW|Y(IQlev0w+=3)D#@~cd?8*Y-{<%j%~UozQV_>=q_^U&6k8?ptH z?RK5zp1hm!b>?CJW99fd^N{1~%!4zPC+DMGAV1ILT+Q{|%pKg#_>C^Jj(bw z^RPTQUu|?>!szolO=xJ0jITEjW|41Xe#Y0Ehvo70=D|wJMn<$9r5!L4j=o!+Zz(~ z@W0y|67}%E+Zz(~@c)QiVlL$?e4WXDvMurs?qaf?GTATlt@1-W#uGfpOT0pR zTzX!|9L&d?Sc(;SE97UbzTMvI1{ma{Oi^xh3QA<>7c;B`U~*h$JilgGzku>$EW^r7j>oJg zH|6cTgWZ@MhdEe|=hqC!881)a!%U99jIYxP>piFZCBDM=`kb&lIqq_s@|}#Y%L&W( z%il3M-ZH-aCM-XrJf2rG=!1Cw0oU54}N4Z(v!QK3v@%))#f64KaN0p!8AH2*o+T)Vr zCbP&lGCzy5G?U{c<9RZ}@#-pX!q)7_9 zKZkM*lQ$hWs|;`7y)(cFCVHIll1+`DgylbG*dlxW@F_ z<*s8qKW5l}KKUjl$1_%xZ)F`eW-BJgG2SKj=0J{MJU?bQUUK~6H06)+2|mLmOpaT8 zP2R|@e4igPIbQLAe2BmB6wfm`PVq|Zav7P8xmb|N@rh;SDy+%+Y{ulc#5?8g?8hM- z&E$B*2joZiIG^GoCdVPZDzD=vzRM4p9Dn$w{5|9OF~jron|zkZafhk3$6d|L%+9<_ zjyEhRS71C(X4rpixe=4&4BN|H*oy-=ocC}NA7b+US@Yz0e#~&Z7vz_@h8wtr$@^%1 zB=6-n{E^3alJUHl;W&TGX|>B`U{>a20TyE!R%Q*>V>~Zr*#GVF9qh)wOx~AklsukO z_%LU4KA+=De1&WI7PoOH>#_-3vm@hqFT?Tr z$jSSZ#q(Z<@^Q-Jc`rkrF3;kVe3tS3%ffmq<=6Qp-{A+0?`sy;|3dzj@qCw|{8u@i z?=s|gzRO^W_QQC-%aE^?LZ zpXA>d&vzNhFUs+Jmmyyr8swsWl+4V|yez_!tiW3s-!Cp~-$-uB_Uyu5jPD;8)*mk4 z!%2LIk8&R4`7gtEFUT)5zOP&;-ym;cd|$ax{*fHde;M*O@{c^mll+r^Gi|sKA*!Ev z9?W1?IVTG+c^|qma%I+FJvL=L?`7EE9dbAJY=gJHCJmYyS!}8?)_0}uj%y@pwuza`tIltyX z9_0!C!OKh&I>MrQx`tVJBl9zPpT2mW%dmbW<<(i2O&HH}8P<#ExeO-nraU54e!`~9s{{yN`eJnv;#?*sW0e!*{fn7{HgFEF}6c~n1FF`oA_ z-E7-X=F-b0+UA*h%ie{v66NjOW9w6u$rE8JxqXxtPls--obLn7>7S zo7)-Rk1#Bc=fw=h^I`^nu>5EK&U3uPD{e^CQ+i&<9E|75tQ5w*$)#A4x3Ui7`xl1w zTge@G7khIcNAO-wW_&-xu%E}|Cm7$?FqFshXa?hXG=r~MzL8rQ&z~8V$M-o5?o)n% zhxiLm@jU-wJbz}`K4Z8rAbMS8V=l(?W`^>3-ppWGC-4D2 z!pHd(i_Y_Qpg##q(l@?c;edgE^HKU@?|qW!7Ll#`9r@?c@0{gLf$J#=acP zQ5?@He3-L2pU?3nzQVP9i`%%9@%4PDVI0eeoXVM;%LRO%%ek8CxtTk-o1gP*9^_G;;J@Fe zGEJ^TJzc{rypj1?l%-jT)mfKK*qR;Lm3=sfBRP)ub2?}7Nj}RLxstE*O}@hq_zAz@ zw>->Wd72j(O}HApj;`Xhyq>vPh&QtwtMWEBU~{%*C-z`}4&@l$#|JrsbNDnDa~W6h z4Zh9o+{MrM6@TE*{GI1`iC5%F)Khw1#~jSZn^=k!c`NI%F0a1AK&!^C>RkQohP{+{AbJAwT7p{GLDYH=gB1Msp;_-~Y_a?99s|EXfMIg|*p; zE!m!3*oy-=ocC}NAL65&$Ax@>FLMnya0}n#N8HPA_#=<;B>&{!Oq)MZPZ^k%Iaz?k zSca8ZgZ0>yxAP8mV_y#DD30e8KFryi&*%6OU*TH5#ckZlJ>1Xlc!bA!hJP_tfkgeJ zVOZGdY(F_&k?$HP>@9cW^g9=hr;QqddVs zc$sMmChF-LX5o#@&!Q~NO03ShY{J&;$gb?eK^)0(yr0uKi%;@dzQ~n)op16Te!x%o z1;6EC{>sz5z?4FX`nihN@_OcGA>Pb#tjgQifX&&Ko!Eo@Ih12~A0Oll&f(Ks%w=4~ zH~2QUa~D73SNwrL^LL)(C0UVj^cPu;lrHG`FxHq@fEJ+TinK-+{69+jz@T$XZROW6-(4l zI%eVx%)`Pg!SbxeT5QM`Y{$;*$-6m>V>yviIg@j_fX{O|S93i#a|d_xbAHW(JjxUN zgO{16c%q)JVHV!V{4C1Sti*v-l*R<%?X&*ZC&j;RpPL zU+`NV=C3@>3rx8=Q9oDlT3*lGEX12xj#YUZ8?ZUsvJ-o-KZkM*@8g4R?B5-iVZti^_G!FKG-p1hmGIF=JRl`}b)3-~;jb2ZmL zKX{pG$|maR8fM{*%+I1M%}T7!x@^MM?8vU{!$BO$alD_?Ig3y7S-!}Xe4TId9e%)1 z_yxb^VgAa~yug%liTb&U*YbMiW+C3pa;(bR*nrL1mYvvx{W+9lcpo3+49?-xT+C%$ z#W(miw{sUi<5&EFKl68<<0W2EK2cBUc^z{wA8%qQR^+X$!^UjI4!n!KIglfGFDG*v zALA2zhD*4DuW=){@_l~HeLTQJ{Dr4@p8xR53W<8k$ZX8Tf-KImtiqbC&t`1HJK3H6 zIE14)fe-KzKF+7Oh)ek@*Krfy<%j%~U-EnY#NT+97n!v9zWtg=_g1w{a);a6iA}5gz9m{>4<4 z6ZMmhnRo;9urN!oJgc!58?pu4u`_$}ZVuyEPUKY1? zF5@b`!MC}cyZ9Nu;t%|pzw;a~@rr7RdP>jhn1lIv6HBooZ)F`eW-E5!UF^+)9Km}z znbY_fpWriG!WDdt8@ZM5^JDJg0UqKnJjL_;hgaT`sHcp~##}7O;w;N5tjYRp#x}f@ z-Pw;rIGPjq03YGwe2R;>l&^9fH}PG5$WQqtzvoZFLMnya0}n#N8HPA_#=<;B>&{!Oj|usPZ^k% zIaz?kSca8ZgZ0>yxAP8mV_y#DD30e8KFryi&*%6OU*TH5#ckZlJ>1Xlc!bA!hJP_t zjYR#VVOZGdY(F_&k?$HP>@9cW^g9=hr;Q zqddVsc$sNxChF-LX5o#@&!Q~NO03ShY{J&;$gb?eK^)0(yr0uKi%;@dzQ~n)op16T ze!x%o1;6EC{>sz5z?9n(^>Y=k<@LUVj^cPu;lrHG`FxHq@fEJ+TinK-+{69+jz@T$XZROW z)l1Y*I%eVx%)`Pg!SbxeT5QM`Y{$;*$-6m>V>yviIg@j_fX{O|S93i#a|d_xbAHW( zJjxUNgO{16exjbPVHV!V{4C1Sti*v-l*R<%?X&*ZC&j z;RpPLU+`NV=C3@>3ruN{sGqBNEw5*87UIn;$Ev)I4cMG**@->apF=r@_whl_;2b{9 z#azZ!e1mUuJ9qIje#Ia7Gk@nfUg8xE6ZMpy*D(k4@g|mHMc&FfY|K{dz`NL+137~C zax$m!F+Ra(xP&YC8aHw)-{;5N#{)dXUwDe=`46vbl&GhS%*I?S$l@%^Dy+%+Y{oXc zlik^mLpYif_y8Z_<9v#XxRkGQ9XIh^e#lSxCBNrS{EcUMk*OOe>gQ@^W_IRf5td{H z-on~!#FlK&F6_kt9L{?DXMsHY6f z%A72~Vl2bTtigJ0%G-GdyRk0^a}>vO3LoZd&gXM{iLY=i-{Lmz+7`36^Iy)?!1pU^{kZPu|U89LtHE%9)(Y1$>^%xti;_nLD_fpYv-T zkEMMeGzRoxK z4nN>0{DR-|Fn{G~USLY|MEzXFYk56$vk-4)IacLuY{2Gh%TDaU{v66NypIoZ2Iuf; zF6J_>;v0OM+qsLM@hkqopZPn_@e;3Sk*KHiypB1Tk2kRtEAm#>VPm#p2j0cr9LN#8 zmy$r*U@gFZn%x;%_|5i%i`rQ9oBRGqW=tJQ}{4vb3UKrOMHcE`4+cvC--nazvB@e=NbOR zRJSMUCml2K2IgU5mSA~SV=Xph3$|lt_T=3h#<85pshr8VT)^kKoU6H>o4JF#`8mJl zK_2A^{=v&k(yvWoY67_R6Gc!B$vItAE z0&ih$HeyS*XBYP301oFpoWzItDCcn@U*OAJ!wuZR_xKU_@*DohV?4<}`8U&cOw>~b zW@SzmU@?|qW!7LlHs$TSgWcGdgE@-hIfV~%Hs|v>zQk9!mTz$zcXAK+^E)2lah~B{ zOm#=1e$p`$Z(tr4W(k&OHP&K7wqQGUW>4PDVI0eeoXVM;%LRO%%ek8CxtTk-o1gP* z9^_G;;2*rqGaSz@>#yfm3*CV z@*RG_PxuAD3JP0ySmKT}&u0;J@&CJZsyez_! ztiW4Xn~m6#?b(IBIDo@>4=3>a5EqY|W1B%03*#ksQbSIi0ikB%kGrT*=q@ zCg0%){DfcdTOQ`GJk1MC>5-_Pt9UK1XKoha%`C^Nyp0XmoNd{OJ=mW^IfnP~LC)YD zKF!5k##MZSZ*x0$@iTtKANVtW=Q&>D6+IL6l%Cfy2lMeJmSRQT$~tV!R_wsL*qZ}6 zg7LcYM4xrQ6Kh41kr?&UZ9k;izFfAVjp?USgd z49v=$EWlzc!^*6|dTh$uc?Y|(F9&lJ$8!oF=4{UAb9{-fa4p~BHtyse?&o(r!s9%{ zznH3TqJGjb6K`N17G?>SXEoMhL$+W$c4klB&0!qNiJZ!roXZ7#p3AwK>$#abxSOBz zYaZlLp5Pz6%ryNH^>huh@J8lmQI=*UR%cx{VQY3|SN7o`j^sGr&*_}SC;2R2e$0J5z(f3nr+A+K@X7&+ddkRb%*BE%&a$k+nyk-e zY{NU*o&7k3qd9>O@DV=Fr?`kq`6}0O6W`^B{FGnvd;Y}Vc$OELdSIe{u4ZOtXI>Uz zNmk%3tj$Jj$@c8RUL3&TyoZzc5Fh0{F60Y*nQOR#TlgM7;$D8kA9;)?`6vHo+ChnW z%D}A5$pS3KGOWxRtjDIjop-Ps`*JWxaXhE+Vb11!KF62%3fJ;2ZsShw;eLL{BRtMC z{EMjuC+a61Gw}xIVPTeFc~)aBHe?I7V`ui{-5kcToXDx1$+=v>=eeA#xt^Q3gS+`T zzve+6?F5@b`!MC}cyZ9Nu;t%|pzw;a~@rq%IdP>jhn1lIv6HBooZ)F`eW-E5!UF^+) z9Km}znbY_fpWriG!WDdt8@ZM5^JDJg0UqKnJjL_;hgS|y)Kf-gV=fkCah7Ej)?|G) zV;kPd?(D}Q9L))QfRFHTKE*{`%2&CLoA@q2WoI$v^ow(~eBk zQwC;bP8MJ>mSJVqU_CbF?Yx8C*q4JjisLzj4|6u>^Etl6SGbmMaT|AX5BKvs9^rAG z;a^NODp5b_n29$q4-2yd%d;A5u_0Tq9Xqop@8&R$9OwQ#3KF{S`&Gp>O9o)^& z`85ynC{OSYUS^uniF&$*S$HG!vnWfm605T=o3J%IvMc*=5Jz$x@8@*R;*)%qFLEVc z=bLvPh&QtwtMWEBU~{%*C-z`}4&@l$#|Jrs zbNDnDa~W6h4Zh9o+{MrM6@TE*{GI1`iC2tG)Khw1#~jSZn^=k!c`NI%FHs)eM7H3&jVNKR& zGq&NK?9P51!qJ?-2lxmd=Tlt7rF@m^xQXxbLw?FH`8|K)Z#>J3Onq;ney(O_W@la& zVM$itEv(H(Y{~ZQ!d@J};k<{F_z)lEJTBx5e3@&wfm`? zGyIFG#wY409W(I;=3!x$V0l(!EjDBewqs}ZwJ^%@B@CrFZeAF^H-kc1*Y7WsGqBNEw5*87UIn;$Ev)I4cMG**@->apF=r@_whl_ z;2b{9#azZ!e1mUuJ9qIje#Ia7Gk@nfUg8xK6ZMpy*D(k4@g|mHMc&FfY|K{dz`NL+ z137~Cax$m!F+Ra(xP&YC8aHw)-{;5N#{)dXUwDe=`46w0l&GhS%*I?S$l@%^Dy+%+ zY{oXclik^mLpYif_y8Z_<9v#XxRkGQ9XIh^e#lSxCBNrS{EcUMk*Oyq>gQ@^W_IRf z5td{H-on~!#FlK&F6_kt9L{?GIJ zQBN6|l{r~}#aM=wS%dZ1l(+K^c4J=-<|vNm6h6$^oX_X@5?|q3zQt|a$vxc9?|6jA zd4_*6)s#g2q+=%Dz&tF>5-iVZti^_G!FKG-p1hmGIF=JRl`}b)3-~;jb2ZmLKX{pG9!S*FHO%sVuy+?=Qe54l_p4gQkQo?!@IeL}26q_T-7UDgW(dKZ z;KAM9-62@8z#s{ppn(tpf=xmQ-&)oGB7HcSd2`-#?sxBfzONptf4g?ATD5Ceb#+a3 z(~T)HJ!ZvlEP%zaELOqV*a%x-d+dgNaUc%IaX1BM;X+)2>v0?I#V_zAp2u(S7T(7n z@mKr}jZvQd@x_Fg6jNg;X2(2O7)xOVtd8}tDYnK=*b_g-!8i&h;O96Om*8sLggfy7 z9>X(u5wGEGe2Bl`OZ)?!qdol;fQc{#ro$|l3-eupM^AKKKa^!?8FS zXW{}}j_YtM?!m+OC7#2t@h0BG$M_6m@E!V$@$^#yOoAbp5wl?g7Q&KP9;;zpY=W(@ zBlf`lI0#4LXE+V#;9^{b8*vBj$D?=}FW^=D4!_5r@dds?$5>DQ_+ttbz5h8MeXB*bAd@2#&^yI34HVQe1cmwvRDOcV7uE%Y-7r(%hcpksOTX-LT#9#3@G$wfZ#}^Y~QcR7Zm>u(AVJw9eusYVmrq~)g zVNd)R2jeK5fS==BT!O1{6Yj(Vcnr_rMZAW$@ge?#FYym_PW1Fs04Bl|m=3dGF3gX` zunbnlTG$Xj!gkmd``{-y49DVRoQVr?Ij+O4xCam8mv|1p#+!H#ALBEO!FT90$ zFbRfWM$Cp0SO`mEd8~$Yu?e=qj@SeH;~*S~pW!r|gNtz$Zp0n9ACKZ`ynt8nJNzDh z#uxYo9g{u%O!X23AajRmm;mcy!82ODEc?10^|AAX7>a6C@M z*|-Q-;s)G~`|t>!!f3pL-{J%O37_NdXioL?j~@nNGE9S+FbC$vA{dDku?E)1X4nQh zV=s)tAvhW*;&hycOK}Zu#$9+2kK9Q93m4)FT#wsuFMfe1@jQNmx9~pxh`-`*XngMJA74y} zNij8sVs^}fg|QS?!0K2Jn__G1ggxi#xiCK#!!lSIYhgqD2-{&-?1P`+FdU1MaV9Rn<+u*F;vPJV zU*b9Z8gJq~e2mX92H&C23{O8Lz$6%g88I71U?D7t<*^#p#U|JaJ7N#)kArX|eumR< z4lc%3xDj{Yemsh&@d94O@9=y48DHQVbjB3qF5R$VNGm+&9N*F;e)uVl!0|X0XX7GVi5qY`?!zN^3ZwA~ev1$ACwz{-qdD8tKYkd9 z$uJFO!W@_vi(n*H#2Q#1n_(O5jJ+@lhu~tdmc=Sq8yjH@Y>(ZrFAl`vI1Z=aEL?~ya6N9rz4!&5 z#Pj$K-opF%BmRoNp)uFfKfag{lVWNN#q5{|3u7s)fYq@cHpSN1347wlI2cFa1pFN5 z;u2hqn{X!{z+-p@FXA=4jSuk`e2IUcbDpQ40x%J#z;u`eb76ighGnoa*20GP5w^pw z*attsVK^2i<4jzD%W)lU#XWc!zr=I+HQvN~_!ysI48B93`JR4CfJra}Gh#N3z(QCO z%VRaHi%qZ+=oZ- z6h`9}{1zYJPxu^vM{|*KV16ZXW9aWIa;3HUkA#U;2JH{nh^fXDC*Uc_s78z15?_!9p>=Tc8U1z;jf zf$1;{=ED3~49j3;tc4BnBW#CVu@8QN!*DE4#+kSPm*YCzihJ-deu?MsYrKi~@G(Ba z7<`95%RK#*0Fz({X2fh5frYRnmd9#X7n@)!?1(+EKMul?_!&;aIk*^C;YQqn`|&8A z#tV2Azr*kGXMBNg(6QXpKmHhm$uTWv#+;ZBi(+Z4gf+1NHpjNu1$*NF9ExLb63)Q+ zxD40g7Tk@8@C1H^m+=PP#UJn~zQ#Y%y~5K^@i7=vVFnDt+*lAxU^%Rcb+9qE#17aU z`{AcJ0>|T2oQ;ccC2qj&xDSuuDU8M|_$@xbpYS>Uj^;{F|M+1bCc`wC33FgxEP|0( z5o=(5Y=&*HGxowL9D<{9B2LG7xD?mmX557b@i?BvOL!gc;3Is3ukbCpR(bj<9wx?= zm>#oYI2OR-SQe{bZES=suswFezBmwv<2amxvv47sWQ3--nVI26a=B%FcsaT%_~Ew~#G;R*Z-FXIioi$CB~e2ssid%dTh;$twT z!VDOOxv?OYz;aj>>tJJSi5;*z_QOwc1dhk4I2#w?O5A|kaUUMRQy7g`@LPO=OonMN6Xw9YSOg=nBG$nA*bLiXXY7ShI0Q%IM4XQEa4D|A&A1B>;&D8S zm+(5?!AJN6U*TJHZS?e0JWPx!F+FC*a4dkuu`E`>+SmwNV0-L_eQ_WT$8k6XXW>Fz zf$MP_?!_2k}Nj#6=;4Qq5KjN?W8yY)3{o{)XF)60T zP|S{burQXw3RoTMVN-04ovcDF72;3QUJtFc;>>Vps+%V=ZimA7MM}ihb}C9EM|YGS0*WxE$BvR@{S!@k>01 zU*k=@hmY|Y#^5{j+3o441egRvFe7He2rPspu{>78y4VC;VMpwN{c#YE#LsXV&cVgF z3OC{o+>b}`G+w}~_#J+aKjRC0gN{9({_)2kOpa+WGv>s6SQJZRC9H`JusOEHF4!9f z;7}ZclW+#k$7Q${x8QC(geUMTyo@*SF8+W|@iqR5?!BIVijTpV3Nv6B=Ej0p0?T1l ztb>iQC3e8>*bhI&5jY;F;%r=mD{%vE$9;GNPhm7(!Ef;a{)Er*cQp5T`o|9gF&U=8 zOqc`nViAnQidX~dV>4`nov{~2;Sd~+6LC7u!=<U|}qU6|g$i!=~68J7G`!7zg7hoPeL>TwH>y zaTD&u19%M2;6=QKxA7tVf-mt8bRP8dQvfEy6qpXPU@pv$#jp%k##-1AKf-p{75m^P zI1I<)WSof$a5=8St+)pdh{GxF3(=X}o|}@jLt;f5sR11|5ez{o{{8m>kn$ zX3UBCuqc+sN>~#cU~_DXU9dL}z@az>C*cg7kIQf^Zo%Dn2v6Wwco}ctUHk!`;%od9 z-Cub6DLw{cD$Iakm>UaX2`qKiV?X>9N8oszinDPMuEY(v9rxi8JcZGC z1;51y_!BXr50~N^ z+>E>MARfoFcnPoL9ejjO@D;vA*HKSD#lysy64PT=495ak9Lr)Atc{JZ1-8d-*cS)l za2$tIa277a6}TR^;a>a#PvUv}25;eg{1Jb}-_SVb=^tNAh)FRuhGKTigN3maR>105 z51V3Z?1Vk>V;qd5Z~}ggb8!i-#!a{r58yF8gBS4{-o}Uc3%18+3f>=^uX#!sM71GhGRt0Gnf5?1H^<01m}5I0m6kp??=sxM`r}!9*sW1bEVQwslC9oV;#X8s+TVe<7j{Wdc z9D(C;D$d45xDq$ucHDe?#Mp zr+<7gAtuGt7>e034;IE!SOKeJJ#31tu@m;hk8v=L!U^~}&c!9T8aLriJb=gW3|_=* zcpD$$FZdGwK<8OcKLubSOo8bz3+BT7SPaWxWvqn_@gr=9U9k^-g2QkuPR5zI0GH!B z+=_e9ZSefSM3@}YU?^t8+*kmMVQH+0)v+!%#unHPyI?Q;7zg199EX!}CN99`xDMs* zWWynil-tUdhf!W;tm$+3HQvN~_!ysI48B7j(~~CwCczNQh}kd#3t>qtkJYd)Ho;cd z5qn^N9E2nBGn|HVa51jJjkp8%<54_~7w{^6hu`DR_yXUc!{O;4e+S2z#W6SuXW)EXhHG&P?#4rS0>8q`cmwa^5BL;c4Y(cm;SoHA(Rc;F#RvEk zKF8nDbb0#64+Ak7rol{@1M^}LjKqpq1M6cmY=fP#7e?U_9E}rkI?ltTxCS@lEv#tr;S+p?Z_yZ)#{yU!%VHI*jg7Dcw#RPR7YE{S9EVeI z7B0jUxE{CRUi<=2;(7cAZ{dCX5r4(s(D3o}k1r;~q?j5*F+1kL!dMC`V0EmAO|dn0 z!k+js4#rV90YAsNxCB?@Cftb!@ED%Ki+Bxh<3s!fU*aF=^!4;p04Bl|m=3dGF3gX` zunbnlTG$Xj!gkmd``{-y49DVRoQVr?Ij+O4xCam8mv|1p#+!H#ALBEO!FTB6=jo>e zm;^&GBWA-0EQBSoJXXWH*aTZ)N9=+9aS)Eg&u|*f!Ns@=H{uT5k4Nz|UcjsP9e$5L z;|qL)4u4Po_+tb#Fu^^Vfa#$7XU}J2F9k4t0!%uMpj>oAu z8yDe9+<@D0A0EL|7>!r(TYP{&;dA^Q&3K;v@xwq&hG{Sp=D@sI1S7E`*1-DM4BKF5 z?1fP{1V`gUoR0HwDXziIxC;;BaXgEc@H*bXNB9I^;ahaY_w-XdOpGZpJ!ZvlEP%za zELOqV*a%x-d+dgNaUc%IaX1BM;X+)2>v0?I#V_zAp2u(S7T(7n@mKr}jRcX(u5wGEGe2Bl` zOZ)?!2|fK3fQc{#ro$|l3-eupM^AKKKa^!?8FSXW{}}j_YtM?!m+O zC7#2t@h0BG$M_6m@E!UDdip5=CczNQh}kd#3t>qtkJYd)Ho;cd5qn^N9E2nBGn|HV za51jJjkp8%<54_~7w{^6hu`DR_yXUcBgoS~{uqSGF)e1soR|-bVri^|HL(FU$F|r7 zd*c8cieqpR&cOM&4AM9u1b&5=@dn<-AMh!@#y`<5H{SH`w)hx~sW1bEVQwsl zC9oV;#X8s+TVe<7j{Wdc9D(C;D$d45xDq$ucHDHpL2``Yc4mv5_m(tOKQm;voLxeT)A|DU=& zBWT|^oQyMY9xlOExB<7JeLq`1R%UJQaXf<;@Cx3-d-wxB!I$_3P43_NqJ4imnCVoQ z0mCpi7Q_-*4y$4vY>X|j19r!L_$iLS@i-M{<04#%8*n@B!y|YKqwxxUix2Q8e2%}P z$^BbD48&xZ1~Xv}%!@@Z5-Va2tdGsG4R*#}7==S{G)~0nI1iWN8r+P#@E{(?vv>)w z;~jj2Pw*AKMHlyR<6&Y zQ|FoDJkQEv6|9Z+dSW~8+4KMZ^PT7X_g_8Vy`rDrqKoHqJWPx!F+IlC@3GbYzx{gJ zmF?++pWrYYi}vfz|JU9>{qOFlg7jYrEQeLG4%+W?w`95lcE^7BDUQJLI2C8(B3y~~ z^?>|0SL^kEA0EL|7>)LIz_(04z@P9r{*LzRzaPhaASS~!m(;LaUxE~dAJnU;AY%~2k|(b#Y=b{@8BbRg0Ju`x_Etyhlw#IrpK%pjs>td zmc=Sq8yjH@Y>(ZrFAl`vI1Z=aEL?~ya6N9rz4!&5#Pj$K-opF%BmRoNp~35>FDAsK zm>NSdJLbW{SPCm(b*zU?u{CzWp7=2i#!)x{KgYSa1XtrG+=&P97@on4cnxpkL;M9_ z;veYb{o4Rcgefo`X2D#TAB$lbtcy>6 z9Da>A@g6?LXBdO;&__<-dY>c#CczNQh}kd#3t>qtkJYd)Ho;cd5qn^N9E2nBGn|HV za51jJjkp8%<54_~7w{^6hu`DR_yXUcgZFR!F$j}mTFi_&F&`Gi(pU*=VgqcBZLtgX z#sN4K$KWKKf%9=0uEj058xP?L{0cAQ4ZMp#;8T2!f1;cBZ{uSyros#uhPkmImcVjY z73*MQY>6GPJNCm*aRiRXsW=-K;Y!?q+fjDE^}P6Z{~oZfYyP`=k8wcP}9D|c^2F}N2 zxE8nIZajo1@GHEGH}Ee0fKTx?{)uj`yW(Roros#uhPkmImcVjY73*MQY>6GPJNCm* zaRiRXsW=-K;Y!?q+i@Qr!BZHGSMXbWfIs1L{2fiMxBM^=lVKXnggG!T7QslYh&AxT z^TvnMi8!ABSKSw|*Z1Lh)V^Q-@1DO3(e9G?ukzUK_*duOOytXfd9esaVnwWh^|2YY z!OqwVqi_h0#)&u`?ep4Frq|$R+=cSr+^yH6<9HS?;dQ)&kI=r){_oy*%F6RQ91CD^ zEQ?jJHa5Z**dDuKUmS?TaU6cQA3vP_@OVzk_GZSMm=B9$X{>}bu>m&6w%7%G;{Y6r zV{j7MuUGS#UWRLN3&yrRvDN>tdfwXii~il$>;GHt8~xwb&*T2Zar1xb@nN^yzVB;4 zKkW0~|MY$+^S`;DTC)BQ_^(hwJl_IBiEx&RhK`@C+yURS5yZr6wBxqsCUv9<5NTL0gDUdFb*dcJZ! zATtm8iE`1E$X^UJjBKjBI9i`u4mJ$ybHd3Cqeg(#r!e$f*59{s z@sdLOP4Zf=k1}oTU&-H9>t(v9D(m@_>7lBuZvxX(R9SB{)0b6Qe-eFN*Ziuq!}>c( zrXzG(+GD*>FVoiFN763q{=7_E@6YSO9NuR@x4x}fi+y{0>}Z$zwu|a(gtu+gx0Mmz zx=$Y?ymz~<;aVBqs%wq*ZOXRl+O>77HeI~xySH!CqHo^5E&8e0PLpT4bn z|6K#UBYOPZ7Pjrty`9xt;nEjfJ4wIx?x8)%WVv?jTlMSOw{qL}^|GeE?YgyU)3K8- zs6E`aUF&`wT6F5(zK3+aRc_U~b?l;U0Bd@rAam49$s=Z!4x5m~!?F2jS z<2d5%HQ%t+BOlw^!VtIO2|Z#SM-l zPS0m{zv$m|v90%}SKQDz;vRX$4T~f0g;(70IN}UBkJ{}U5l38nuegzM#OZn3&NnKK zIDH@1jvF0EoSx_HIQ^R`w*J$GH$RTJ!d`I;;)v7xt@e5s#t~P`D{fI7ar${dJKy3s;yQZ8 zEr}yeKWAX)TN+2)P_H<>=Mr20P4J3a9!H$sC$`tSB96G_UU4hqh||wG+WA(+5x3tf zZgm`S`uPYu-1SKPWd;`IKuz25a+ar$?sJazQHIa@=F#CZ?)WCp3! zI43hdjMI5KWR=lR#(xzTAu%7WSMQPAgB}MuHoWKD|Pl@=PM@_-uVhw@iW4tPV<$P(azUW%HH|(edi#lvj;n0GpX>-*HZFn zdo*8J8SQ*SrR<$=z2r+Gb@pKA(`zH|d>y^=m6v&TzQt1Z&UaYy={1Kv*!lEa;5`Ie0#9lr=MH% z&UaYyrH~+PUrib9d}Th!7an)s|p8pMKs=n)Lp8)Kl_xwUZI2 zldV{<{XEq;xi#>9u-B{aL&`tHj{8*NvcHe=tXC(e5oga@DRJ5KB*CCLbkO%NW!C#` zd?eeU|JO+SrGbq0b}alL-}y|2@u%1G+|IZ8gSfB43}c|&Z?FeDZoL$|*BjK#&sZ!s zopim8Wwh6;?~~fgXpTx{45OnK80>la`F`(wn+NzAjU-0%HI>oMckzRKn7W&(QOv=4h0QF8MP4gM3kvPYwlZ zd)vj4FSQ(WdfeGVZ0%d@)xHiA{Gav*{nA^K>GRPZV#{Yt|7&|YNpNiWj!M2Kb}})x zd@UuP{tc)5w@V!Pwl*}3&i^1^)C@mkti)-)ZgJ#GEeHK~cB3TDE`E5t7&B#mNFQmw z9&zM*BTq(s&(I$1;)nUdX3O(eW@^4(GRD?_9p%aRPmY(9^2HVQ@nYZ4k*A;W{`toK z9sdvWJ@v|G-_QBGeCs7&F?(ah*tR#tTzUS=eBECAe$LZ22NL`@esFjFI5j^1YON zTmH%ZkbE84ALHW4x3Z4;kIv80+a;gm(e3>#j(mSezH~C*9%9?x!aMwp^b(}`?E5)? zcl?FamHlfckyyKUTME|o0mok>$*1>-G@pGx=kM}G$_=b!5@Qds<%{0w|NeS%sszWj zKN?6r{Vaw(*u@Xq_ek>TJtA$NeLv^#+P7aWF!ei*>>;*%Q{)S5^gDbs-wYXJYhOS4 zAlyGW|8@Mr|NZ&OzMu1V?b|H*KC&Ap#@4>gk}pc;Yy0NN7+d=ieq>txEFsnqTfVR( zf9;QX5*b^*ERwGPxs=%QMM}QHGGE(g-_QBG?QJUgy#GFmEnk%6izMG7F}C(imVE!@ z_}lHZy-VWAx8fgeZ?xC;E{h}Ib;(!vADmzOj`|yqBu@KpMI8BZwKo65-{-OQ-&1*G z)&Bc;RUG+BNIrcXw1?R88DIKa@2zV4*2oy!{+J>8{>k~Or{qg7c{Ja;IPz_md}U?6 zJ;c_&DUz>}1ZlnvGRD@v*OD*2%(sWw@?G_6-zEu;Enjl^4_cG#Bob#AKRjQ>NWPOY zQ`@&Cj(jWH{1z=cTsqkc_eAODCN&R_5D7Z263H ze_daFA;GcbTP68Y+R4P&@~xJ9DP+F3@2HHix zJtJdm`JPI?^fKQbV#}94`mg@`N`hm{7bO3Y^q;(5MoK<=|DKN{UzUHEuaQ^23vuKt zF8M0h8!N`Py*(wL{rtETN50|zuze@J+V{0rzL(MfyA9f`*^=+Y-`sC{CPCKqvj)j< zTk_Qsb^l(G(cT}XTwiOx2pM&l(9bmXZI_$c3_4H8_#ebo%dpId^ zeZ4Aly*eb7IPdmFT=2KfYudgWGTQCSB5`sV(o>hwp?FR|qll+yJ=&>N34K3AN!;N; zxwj%U@@J-%FG$pNO1}3);o@>1B*FX1_iaxkRo-#Y(mwq^7xrMUH+SBA>AibvpRh! z_Z9n5?l0>75{-|NaR6ogY>J+zwEj~W^&W$k2g^8w@=$RY<>BH8$|J>5lt+tWDC_IS zILhNi{X0~1PLOdTWqmExzyGv;ii}e!>wE3`cbC>rmvIK=nWFx^qxG|8oI_dPTb@UG zzNmjAXv{(x7g1g;E}^_sTt<1hxPtOZaTVp&;u^|pMZJ#Hoa<%WKzXC6*KbvzkzhqAsOw2$(BQD5UV=AexF`mE){GJZk%hg#~k>+PJcD4!Gc{HyiRGG3s3QPlIE)?b!U&skdjMn*l~X!)v) z*JSLu!&Twk>p$Oxc8ttXEIRR3SB@OfK{>)MW1M zk52tQ&Lm%(j#c-htP&g*ks}~FIBIE*V0o;SBSmz!^qNOnawuWdG#!mxH=`3&Ue?jC ziYR6WDqFDIM$pN6M93Rf*$rAW0R)qN`@tnj(~X*FhZ z@|GE+!dsdfM#)r}J-(p<8S+&M?WuiWgcZ7wF)AQ?&FJu|DWX1#niKV1lyCN-kA33j zx{!K6lL4Q{_}rctlQ3xHV_#?8$YD>%niFH<8^FQ&DOjy&vJ{_Em3idV{OZM7p~zNjMAWlPdKSG#6tn{`dD3P#xN z3wfdfvNey+9@RZ+UDTr}-)zGl`}mf)FeAowdq#}=@WdD+{e}S(V;m8a9vhAk(jOBZ zI~-%A^_?S~879VD) z-^XYh+gysg>HGZa;2Ocw#()fVd)7qHdYHLF>*v01>8s3bo+mF~HgaHO%jfCZd3wq% z>v2UGGwh!7k2w3#)5`raIr{{yo0b6qo+M}Gw|sn8=N(FJ8qRU@i7{u22#I!2kp~}ra9)38O=rImW(bymDC zEs1EY#bf2A=~wk-!*^?#({Pvb-7ICl$0J>aPX;}U`nw*F--li}75}XYG6pzl<5>wB_##lgIc6<(C(n;eHOoKc{Y-b7{EC z@b9g~1O+cS4Ocr;+mO)Zzp}f82d?Qak*oE8dj&R@-yrZ`qqRnm_S;}7CDLH0k=W0F zne?|2JbJyvtk4%pBRGkC_q_jF{SU9Q(b27BVx2DHY~v#-H%T8E&eF%7MnE#{(j?!> zH<_de$gTf>G`Lcvj}eeZi$>BGU5tRdn$Q(|HbA!DD*FY8&303cAG}OnP(1ZPNn1o2 z0r~XeEs1RKVOdAe4tdBX6;@OA1L^Y94w(W9=@*g(7g%K)0Yx;s5$v~I+EP@@uHe<# zqyfdW>=*n_Iw+vHmg5HxKPxdMv>X(??-N!{g!An1q zmX+3WdLvol%5KBY45)I~A#0mnK;~4{q7i&izAPl5nwEzq4-b^>s-bN*%;e)9%Z%DO z;gY)-Nk#p-6Kg}#8Og57YvmL(pm~H`-+8+1BP|-iC*(^#0$L20qANH;o&o`_^jACl zg0%qwt#!l4O6O>*pt};JUvWatBx~e*Vf+I6b;ICbzZH>DlN{d}YogID-V{g-*AoGCbE zZrRM!`UMeT!R`_gb4GvcAZOAR{aPCV=QL-8l%-|owPl5+EKQ5n{KbP?hRU*+`b&Id z@E>xm6mWU8l*=Y<(N&hcqRW;G{(6t(ysq)pf@{j}j0D`sCGDvdyi9Jb1bnMsI8rZo z#+TBbyKAMsQSh6zlJmYk4VneN7$oHfx@=n`g`(jptq0wxl&{?Kp!L$5$7Gg|&Pvts zsly0!1+;YN?2x6wvd-2HD=H+^UnV*@Zb>U$A$>+kcF%-gNS^6(ym=<#heXL~DWH?J zOi;+yIuh2|amY{hR>&`(N~^nBb0R{{jgzqMj>|G9f5^*g64uk2Q#fR?eBdvjx8s4# zDIW4&A6cfKL!XF|A#WeZruMhO%7w_?FF78pIh8_o%f)Lzl;cNPrdr5B`G$~yfmT?p zkW_M4A>dPMbLxfs`i)F1ax9RrMyd7S4Or&T1GbWx`mCG{0+u`UkgR5=PTyEktakh% zfj1qgkH}A61Z;3Dl`X&RNF5$5k((TT{)QnRN%MF8Aw4B0@FR~LhL4v0U3x%TU9Mj> z;??EWBwg;5S^h5lEshk0WlQuJ@`}_dd*|rqz^rU~Z2c7@OE;4W>pZAOj`jR;c&2CC z({f53s>f?OvzKgUnqJ8rM*M(K^NP$5u}*QBt+Ej^{*df}ET%Tj6_TWgERxmKJ?$r} z(e3tkY0fl>bNCnu14^0tn;0P%<(qW^BCWC!k}a|HQE6+!C#2vQ>Dn@8Wyu!b2vYP* zMQCB#=zyUP{c(tpNIgpocjyV+2s!wJEHlE|QLd1_@^TR{(u(p6*|<`gGs>Z7&`cp` z`pLv-Yv)!9$v0kBI>s?jmai3(bg>+tV;%RTosB~7jF1H59Qst~7&2U+%Hyqxo*`vA z$TFW<6Hy`av;`9!MWi)DL(a^Vu!#=&c%CsfWOj8q3MX0XSsb!Xep@DBvbD_ekUOVk znJL!9`jBw>)suj!*2MOZ_VU$00n@CB{UKF#rJq|9$3oJ}mlOp|x3Zlzg7hfSRazaA z(lf1VwWfVN(~a%v5-B`CaqOF4Bzqc3qt|nPCdW)`pUQJ1mt$g(PlVn zS92KgqVy`k-?dmCowe$?jrhMu$f6m2O+CXWNUA?(5gIHHM(7p8ZJ43Y^2&8mXg>LJ zCb@`6AiqJAAXPQ51=23i1;(V)1%}EukD8%ZewNE5!`WM&Jqa=olDW?9s~kpxBHN_s z@7g1e87|2~O6#Pkt4mo-9}Y_fyBP^n#*^3m(5L#>Vd(A|a-kJE{DwR?LZ@$)28V7r z;xbZ)wz};$GKHRukYinzbB&YOgk^{7a%JUUHbX}&)`stx;gY2nWOT{W`!2hTpwJ2# zWJiQPmv0w}3{58eGBi`BG}3{tOh5f5o#dA(D4!6N>BU_+lp`}&uPRe=HIw_q*6iqLiX^<|+MOKTSleJfoM>YOT9diEv-hMmx;O&fir(M$et8DXK$ zA##;sH$5<)Z#5JSbK^T<&e0tN7B}5EinoG z0vqiXv$Sv9*9dH)M_FJFId%e@YG~jz`L?ydW}3`M96zxBRVgIx)Yb@WsI4%93+pyC zpCn~T+D<<07xRZlDnh+ z&O~s8e5+evXU_uFKa%8Kbha$;;-R#uYZ968(Lh;4k0!c6Vfoppz+M_;$i;GdS)zA& zoo@#A`$XqYxg#t3SUXnc=ab(?3XEze^$Ft#4&EdN_Ae+8Lp5dKfT6OAVVX5CQ%}h{ zToV|Hg8~OeOCfl11L@2`y1R|wUUI?=95G6&rD-px$;3#_=A(hmq)%kw&-7;|hQHw0Kq9%_iN#Dc05|ZPVHaoO)a4O2&_7NH0utNJ!Q<@-?$&w$;*W zZaH&FMPK==z|5}CLQh4IT%#E+B-A>M1RCy|x>)EsIecD z7rxx1HTtq!o_V=+hm%u_+hmTF)8EK?_@o4cFMGdq*0&O9Mg&T8JxhcQ9O7e`S*2B; z4=0=RUnP{M$&xV=mesVjUFKxh%F!AlgBfn>Pden#|Enrz2~VM+WXQQ%re-BJi~=${ zms!iDp#qBWJWs>KCXt^@MmDVdTCo)w-m$RG3^=`botGsTfKsxzQMr zmSt!Chh@X%KrlA5hJ3lqI%Y&Jv#uFoL{uPNe;7g%v#xOYgTc5>yoA1w&!Zcd%dBnY z@v1cP4*f}a*;UbY%t+`=mNpZklb0rBg=U0XR#&~NO|B9*$`p8ZhNDB_T2 z{w_1La%z3qV29s3J)rW-T7zVsF68MbIizz+7VW)Wo-B4J)p0&I>lj7K$XvJFb+J-4 zlm6OxOOC%bc7MglY;(RgxbSlMQAx9no^8Tp>k3D|mOa!)JFJCFdU~jxE~el0nDv5O zPMhuZMCO=U&)Q&VoAtJiIY56z!P;Q$gM2c<1~-eCDHYoGO+87JDVyMh zY+t0F*5u^!w5(z182D|uxn+C|9Rt58H>jVFp<__qa?_{!7&-=SEoau2O_wq7Y`N?5 zH>Qq(Ys;mSKYw*hl&)NA(_!cs__^Ghs-=`pFZSLCNQoNiIE|$%q%Pq}0mmOftinuxPwu z@hZ#qxv2M(Jzc=i8lNl{zWF^-?#jWXQWj4smqJv?Vx9{BKuM`f1SB=GNNx;MIa4)2tkui6SuODnC3bZnkAk^Zmcw6D%#!i6!|c|DhDrL=vYt#=T({Rv9aQHo5w=>MI93WQS)@W9A92W7bdJ zC|+Z$RXN;-+;5XPImZn*G=LRi`mo;U2Yyak>Db~@EKinvl(sRIhwOC#+!p*;_*VPu3+y)Jlla6;><>3FY z&9YlOnLXQF#BQOS!DS9TsM{>ZU45arS69kp39Z#Nmh|c@YYR%nliu^pi_EY6CVf-d z(=6?ZGWwnFW&cVhX93;H;wif)^t4A$3^K^yfO0XeKTv68w2Ji859f^5XprDOiKOE~nAgwa8wZk8V+@YmvP^Yo@(Md*)$TURP$#wAX6SwAX9R zwAXCUv{oz+|2x;MZHO{3OpZ{*@=YF|~aq&&SRh4({%luV8TiGb1 zE?Ik#md<%*D0a&K*p;4E>JL@EZ)F{?lyx+v-O4(Pw6t4B*=og3S?B%U(d(cFRzNL9 zn;=h`i^6qJtr8#`C$&m^t5{3XWKt{htwHl&kWp6Usqj1|wyJ6=x-Qdbs9crN(_LDM z@yw9N_k(A#+Db_o<>T{;y;$uL&P6s_sbg(0nc_!aa>yyPE^!dA3Mm=rpl~KLMw6QZ~ z)aKdi)fw7BcD*iV584*pLAp!q_USzB0lQxJkv(YLW*K+NsLR-c9y^(243jatjQYf} zw^NGN@ITQXE>AD(ON5*iO{X(~+m#@ploJNKT_4YuHzj?NxLxg6Nx?U%73Fd{eUrIe zsb@)$PjaV!3U|QMNV$&_-{JNt+C^XYQo57sP}%gYvjT z-e||C$?Nv(8!j~g`8>-yV`HrvXJ|opwr|2@^|`#OUCvDMCUe$b zU&+nautH=j9^j7mZ6{e#g1jzgLTPQFzdOkac|X#bqOjX%hP-|3a;Eh2T9I>+`5%&! zr$iCYs!P)C&L?J3cX0V3a=+F2)GX%md122e0gbEkjwovbue zzo`8EapO!{((TN@>;1IXJm(6tlxNbt%G~-|Qm!*2-L5zC1-VY2V7n2|%y^#0I;T2H zdoAs9`j>ILOuZW{o9qg5yZpLIA0!WSyJ}pN&h|~{9qe+t6T4jtSMT*`KFV{6lFu?@1T^^PfARxF&m^D z@>EVz?$q0>$=vig5=&R>SD;DYXiaiC2fB@8L0WDcpmSYvcgJmvuBhdo?q%P?>728(fA&6Hgg;ACTLAI`3(WL(OzB!+{U4oTHpCIUGFsc&Skf;eXC}u z-(Mr&?AORQ19fhZhFU*0sn$>VQzJv=w=Ud9klciI8$S-#Y$X$E{Gn1>o+5AfyN#GF z8X5mP&Cpoxe7KExd-Solyua)=O3cv6jjc4Y@k=fDX`uBD`)QsVPxbNq6|4rsZ^UVR*XU*qqr)%rB@ zY;+r4d^B^Ve7aOgd3!?oAx1O*l1L+)2kTPxC+Xbh!CKR7t1h)~x|ZXe(ejh}T5hvR zWAezK_->CyHy#+=u$;w_6_zmC(5{K05cIyo2aAw#omia~s3b>tnV@I(KE5u5np9t$7nq zm%8z-KGt_?{Nig`)*o_p8+(drOn&+G2JZ=1h7>mwlN|%PnhI`(m@!B+aakujRXfDQZFFv%JX-U6xGtsF4Q}J}FPJ4F`63^vkRF!T zY`1YSO6!+=s!LUo-%*icc$m(8C-*w#8COu}j+YOC$o7ucno7BJsmakAx%ZjYN1fE1 zXA5b0@j#7lY|Tw|NY{Q#-ehnaA#?Pxopp58lp9}e}$UNC}U2FGRW$PSL#-%kE%WH;Kb2VPSZ9tAs`5=uY2y{os<3ZLpTx<<`h)@+Q4JudSZ{xuh#`-Rb4yS8tdiTPD3?>R)FLhKlK0c3 zJ%@Cu`*IB_$G^44zzkZxzfJSpDXNiYvg%{%yZSgo-br;Eo0jR^ayK;cl3d2fk@iqq zRo+_adUcJQZk@OHp3}Jp^J(cHjA+2vHzcJ%B z4!72t?WJ@rFCS=3Z|k@%o>}MSTBviAPt>_p-e~4seY9NYhSr~!4^6m@qI9Sg$U*nzDzWC#ambd2D{PTM2+z|sc zzO}VQf%Uci(oKy?D{pN|tA5nA%#`|u+Cu_N5 zB`y2NN5A9@CO5le>&od;53JYgXzP_F%sMI_z15iShim-cLb|R-@*x#@T|TaP9?56x zq|FPo=4q&wueH)PRM@SLEv)llmlj&iCHHjY3~W6YPNvair}xsDC$Dtwt8H4dbhox4 z$6IaBC*5^!#2|ePxTiH=57WowdI4g5HDA}Y&N|!7vd#uqt@G6~`HYgB@#1UF#$&a7 z?K3T}w_Z)E-_mwIo344TS?B)()*h+g8$qY>#oLf=2#k zou9Wx=(=|K`$&DMCR+YQerHRL_F=kg2kW`;>Y>Jel1$gtx061$e4?#t^RccaZvl;8 zlu%mz$^(Aj>eV!jQ^GDVh zKAHTDEj`>q9~WEafp6rg;` zSzm^e$D}sw_&~|~o7ud$@Xj>lgE;O{4uAO&o_nI>ZBmJt;aI9Kz4IJR9ti6kuk=D< zi=$;-$$ZF>t*~&~aZrD8M4xqmf4Ze2RgAtyX2^0+LUZdk#JEf3-Xd$Sn^!&>>+V)e z&*l@%Yjvb*rTMy&)a^HO%csiR=giG{WzW1Y6X}~unlPYCGMOEeE1m45>^(l0nK=^6 z3z$z%>%(EWzLo>s5w0K6^~vowR}P(s$G^!+^W2u5>du?dAvyCkmk%4b^OtEZj|EzX z%kC+7u8nM8q1U%0b7aICd8{~FFYjtLYbFhAa$PTy+TD*YkKM-%l??p`>E-y~(%Gdv z%FMK17M@}*C@rzm%{%gmVD}>PU>liQYc`O38SZW74tdkdXNPGucc*E!cb923c(-Y_ zc#ml{d9SIh^*Ll}(>rDOy5zV#rX2QztSO|;4Ov5mv_aB@%<-2>ebz-~rOCPW zkCq1J*_2GynD6#psW0;UnADe85>M(&9gt6Wxyy~vSC-1loRX);S$$2cJ8l9Uo4iRr z$(ts|SLTJ@k&nH)v)!#JlMphbG3X#(tXSn(biF${maU!5Z^``x)+wG0(UKOQGV*yM+U1W*2Wc6+gRq z-}@qhKkw02Ksc}WXBh0~HSLEimUpfZgOB4y1%f)mJ2@39$-Egt-+`~Do!C(S7 zyGp^_%h^DiSRm(FDkfC2e8cte^2H;oR!|p!RdW`Gxb6;3#*?Dr^OH+(pQ! z1%bTt#ZU?5?MKETIK*rD0;7)N$-hBUh!!cM^i?pd`StjxO%@7O=Rf|&Ki?Wl7xzfx zwPQHIW!{*u4%|17WPvj-DFpxi-URGc|7lFSd>C9m&X!vk+HTG8B=EmI0+U3h_I1dznb5spc8mU| z670F?s|{dgNhVi-Stx14lmGV=jhsW28tmq_xgh8s?jOx~>;Ud>w50CiYEqyb#QnAp zE`quHXd}f!M+-X+RwWY1C;~3Rg|os;;HOmp2!>ZEu1Cs7T~|fkB_%?Bd8RfC*}63 zyp_1N$Xw@bo(8DlouKB!2yZPHGFN`-O)%Z~Ra6DL^X>k?tJ}p7iHC|O-vgUDGz0kKcLVnF=V}plAOBNog$41oxRAnx@b6LwDU?670xIGB z5E{~c{^kXs4)O z$>8VEe97bw?*Vn5pZXDGS$q`>W)A-|sw#5%UZ2BDKEF)}F9rP807Nh1&%6pK=8xil zS;mjO2!nF|k2EzZ_(2O{P|4rShwKvnR29Nr=IiK7uHt{w54&oiFYaAiK@?ZU;2*cT51Yk-rZAu_LCLFQMsnhrfW5sC)d;orvDb zH|~Y`eg3H9V7Bwa%&>dFA446ghx};gUJb2MqAHQ~fi@e?<+TG!lV^`3b+l+?82`FPz!Q?Ai{>oiTij z=pIbqMmY0izWEy9#Vj5V@L@PQ*!eO=d7ySPo^`PEXI9d3xtEzoS$80l{w-ud%)&Jo zL@?7#g=Z-92Cc7Q%y<5P156=hc?X$TDo749bHy--U}jO$cZB)f3qGTm{&qktllc^` zHw3F`m%S-S_!KI)1gT*#s1y86xlFyl_W+BvPdD_pJ$E;_Z7k*TG&Lr;TYj-)KZ8O{&5=5cT_l311eq^MH|C$ zVGZStCxpgHFh42$X#`ZFFpD|@r-fst!pj-qe9GLCg%Tf_rwSKR{hB5WjzXZb!UaJX zONKDJ1JpU;29li@-id@kws7)a@RB3ERt&p5;ku1b$rt|80iT7!y1URU60UB5W{EI| zR^d|N7vF+eE=<98#as|pQ=_F)n8OEkQTX>kc)2WG@-nC^Lg#K!Rl>ZLu&WmS!-82O zTtM+|2;(9U=$0^p2bDVEJ7*EELHPAzK%?;56v*xfG3{SsT7<$`FmDyk`4oZLgl+G@ zyhC^|3Dg7OBu7wPLWgWgsxl z!l_h95uI8MDpmA8HOJ42exThjL-fH?Xr33Xr>Zzh^bChLOpfR?+T(IXi|D(}7d5ni zSs;4U52{EMU;~jVNLhm^VbhxT((6ini0MyCr(f4dc8mT67+=dJ#*bZWQgO_tGS~J_VY0 zL;<$}Euta%a_)&XQF_}d+D6M^yU6){MCcF=yboucqDehqc8StyV|Xk|_!#g+RFn;s z9?|!-=k$u+NCEXsG$t6bK9QJOV$Vg>X25Ph^xh|cA<<*{NM49`dxpQ0QfwirySC%O4v0N>!0w>fK-)xw*!C4Uyd@EIjGQeP-q z9BKkpAbyjooI>##T4IaEpDOWuCE^T9yvxMT!U5&tcPNFe6n{?}(M54iEa0;Edwa;P zh;Q2(Cs9&J=WWjk3Ru4NZRp#@n#w&uE#KlJCe(Pf@+bR!yn)< z_ap)SP-&HX7$v0gHyG!~xZBm}nQc9b=r7r@Z=_Adf zR;HhH6TSJ}Qr$Yh9_iNiAq$XxPH!nts$LJ+Cl%1*5iG5tUT=u>b=sD~q;Jru!=)!$ z@k9rtsZO8{N^eN%Do1)v0=qovX9_^Rlt=BmLg_(z+9K&#DVQbF8MKU+ zN{`UaRW3bFS;+Z0_m|GrJB=T&H4k#Z=Nu9E(H3skjq`6K#mrBmqq zvqm}{nGt2s-~A4AclHMj%stqvloae@?Pmi#*}tiA?Zw8&!;3fj zcqy1Z>;~#L`LYYByXMCt@xtq48_J;R&vLFqwufEW1lIxVqI*!;%dYtYc7g0n2f#iS zH(Zg$vrT3g1hb#K4)YLp!`mqaT_VRm&WR3g~Hx4?{K|D6Tq5%w|7<|uY)FYKaOO*oh_?6a*HODx+@OMV<{hz1;G zE!2)nWrNX%VA5FWWZ0#%qbNl<%a$EMybRXX3(ZWnf#&ErHva@<=h;GfAzAGFTTsbn zTd0kb!}hl#LN04q1uBodR|;7^`#voh1?=KscqwGJ{tadkYopq+n7!o&gA#V~Xi%l> z&k;~5V`sbq^K!QNJY*MGB^4bN>{>FYWX*mszsPPq2$f50+v`xd%r2p&^a|@u#pYG^ z=K>6{iv5eWv}!i=6jZLU<1u5I>+Ik@sMN5}mLt#&R=yUpo9tCNWVLMfRCu|?PP_%X zI##$JUT(A2K``stX)(}jU~f>K*~l)YPqB$DiG@ltE2UE64x3GZTG)oaU~rdproH+e z`{OX6m93u#W*h6j3DkXd+!1KDv%&8II@mY9f$Raxe;LjmvL)^qU?;1k)<_rIyb`V- zv2W07`sL;NRz5aR&dR+)u#y zxSl%HpLx+*^LfuM*nY8@R_8CPHbQg3dq2R>;&4h%zKVDUI$vkL0_iv26Y=lge)A=s za!JTcNPoCVr(R2oX@OtXMfJ_{8GYdYoV@^=zf5(;Ki4l?2F`{pOYraiJFSH0-(I0M z(dLzj;Q!T69rqnKqwvpvA}Go6;M}4ba~J1wG$=pLSFteM%_*f4CzNAH^D2x}*#P}; z&L323?B`rA!>A8&MyQxQ%(>JKDuOfZ7~Ds4VrgrL=A8c?nlYTW>HI8~GX?)8049!8 z{|6wRv#|%VRn&{K*e>-J32#cAoReZctgAHB^abb6O9;OCBe79Uz~R{0^cQaH<55 z6>{eN34;<&6CFF2a_r|pR>sMqQ0oHPUq($@==W7`>>o{Zc5VnD{o-*u4jR7|U~xHWJZ?I2-H%FE~Dp zc%os>8|z>`!f~g$=*IO;gv^~=cLJ0Lx15?SySU*$LDQQ%a}k(6+$nS_>dW=U`iGu& zGUbPRxE+*D1#mY|%Cncdbu%E4oBRN>V6I~kWFg#_PQoCRdnyG~7`H77VGnQ@QgwKc zyM~IrL)=$N0EfBXeS$zoxbIB>L~-?Np&89xR0g{kZu?@uQEomx-!bm=2tYj7ng`d% zx$`72Kgm_odB-X44*GEtxyz2j%W3Y4FpN5x+nEMg3fI98nyK6=U2vVo-AaW^2Dgmb zYN*3L0P`F-=X02!=a$hTlf(UxdP=!mQwZ$xxF5-&lFyw+CnH7NEE8N8a|OxJEaAR) z04k;2$wiP|;PNRYui$QUf*8*PwzyHFwT@P_^98 zw}86EJx;eJ>bM)ID{!0ZLw$foZmSM<9A>cLnVn_qg+DVQ%GKqeQfg ztJn!=_qmf@q4|LOR}y3oxiQo^?c|D!Ky`7ynh(tN)q$rEIQ>BD=2`f0wruW5buvfe-nwKk zqj_T|;Td9h(?c3x2D@{-Pv~Wx=PjWblEs_m4ZB=k z2mT{EOdjvZ9*iuXr=*RdfY(7Ctqrc|2M_ zFY@HS!SyBH_%$%U%5(S<%qm{O9=NXN^{)Y3l9p+cB88@V+01cz1bC2NCEVZ~P`ut-R1B2-ME|nsUPq zUh8ydKH%*P0`-s=ObNgv-Zl=LJ?07Dg|jETnY*FV%}b&*@F`DERr)jDBubk4c*m*0 z>gO5he!w6vg5Jas&uJ>C7rgr^Fdyb!pgzY=e%H^Cx$)njQpKHLLp3;h3Spi;sAlD_^*e*Ii%UgqDW!?G*+{tCJZ(#zl1 zhzL*lLIE_N@yA?%m*@Pysd+WP|7j{*5AubrP#NOy_y^1pzQa0bx-t^VUv@GNmqF&n z=-&gii&;n=G*6}mf69-O1j!39y_r>GLHRNFsX@G(IY3Y3&wP*oY7fJ{hp_}Q0o$Ot zkD0I?@q(DCuYw9@ZhsHWFs4rpW;oORFPQt8Bzols7zgSMA7;+dHKho~OlxZ-bN?g2 z5$0FQ0ArZAc`%P<2C2mt$LysJ?@^{>JiHud9-MtFoBqShE8nc+XGwDq7J7At={@n}n3}*UYpw2TlsG`hbMo)%jHgj<+ zoaHbf)V;`O0%=_;V2q^*RLJ~>Yeq~Fv+-99sg&8I0aeDVz5_4i%ou7AUtm_z7k-iX zdO4^|3`_aTW#)i8;0m*XO5$oJ|8r2+n4l)OzK#PoKn*jM&L(S_<&>Y@Vs1WwmpUdq z0kYeS_`gJn$)#HZP0XP-jHQ`*cON3$VHVO2t@}(nty1kcZa{<%CVT*P512)-!Jvy- zWdrqyS!#gFVSroiP2m~~}BLyqsV17ifaxlBzdf;8GRas~EF0R@7k|3aluu=+BfNbo}do}pO4 zrPQ`e@G`yma)H}LP!|MquR*0k@M(;Ow?wJoOdo1>?g(H3_n*Mram9P;q=m5GaFP zi@@Z9A+-uN+ym7n_{9RaFG!$Gv0X5os@}3-+%E3<&0nz#J4jrS*DP@B?l0BZ8qyjM`N= zdH`W}3cpwY@DK*d0lS1>Qzyt%m~Mh=FQJTX-}(wizY3Y3aN7W!?H0bb1eCvU_&Q{J zh2puO0)Q0LWPd2APW;dqFppxSV&*$e&HD}z#*aSU&sy%Z_|bu zAuRBPN~AD|+C$O8A5TISBmCzwAXd1Yj$Pt}V}F9(X<-EAC`rN(>8m;;^rMr6WZ|8Y zkYxx3U&C3Z@CvmX&k5JlzH(k@DF9>%U*rICg(B zN`%6l@L4MS>jAu!2`^A(UoJeb8Y&lr6KQk1A`Iw)%2nYV4xmbSk6uBwaNkZm`!(S- z+TU*oe;)h zk7%VQWC5Zi`fm4%HZ_77Dl%t)3KKm#1nRITl#U4^L|<0HXROH74q2S2dk(16q86%) zlSE<>RL+QI(yEaxx<;MT6wz^^eQM8frKKitLf z*9tlt2@yAtvrzHkj{#xgaB9qji@)Q5iV#=0!aP#^uK;jFTtZ!rDDnM20MX)?!eJgS z&ZPD8xVVKbQY47$*8)z6%cwt>DBkfe;IugJ8#qf64|sw)BOdJs^HgynHKWtS0o0gF z7f+@I;;i`DXJBTCUq1|$9B~XaGjqjDXn!acPmhGGMC|=03`)gEsr6ANE~*8yTr5L- z7YA%p$|1WbKCuR}OX7B#yO+fq-@yQ{h!g7J<*NAl%Lr5_o=g?{ZSmLhp;<4sr<|cd z>`Px&v$&xj)E#ki7wlTZ^Qn_}S3H?&<~H$J1(^56Kl^}c7ysY@=n!wG^}17>^AxYH zOFWM9&qv~g)4_Z!enMS<9`PHw@YyRq91rGG@%l6bdM2JhKlqS1=?Dy7i2r3E8y07b z1B{3rsV(m&*>Dj)-6f7`V0uWZX<6GP85)M2x8(j3sQ5^3ECS^#$^R9aev*0iaJEMh zszQVSN!%(J?3Hv3K{HVDkZRE|iJG#DaEb3t$o5OtQV-&QczG?e;9ORTh`$4Oc_9=u$ayg_%sY9t|}K;4k&XtBB}Np}FVLE=Uq zNu%UA_1v2zVi{!3l5CovZ4&+w4C%gP%|_U@OBPaH+#%sIpdLs95+HjhITQe@Q*tr` zUb-Z!7DDBbL?Q(9v1AghQqLswW8kb$va=CXzohpmG@nb}rUha^a%wb~gOc*M0Yj2f zs+QcOZ?{3_E)8*j7Z2$%GT0@(d<3pNr7tZ2(@UB?00VESnfk3h(zznM5MOEZX{h)~ zy}t)DQ2Jdt?Dk2glmbGe+(5`erEmNSyF*gJR%Dy1G&xm}dbq4{!2+VBeCvUC-#!q=r=QY)fHI*+Qe8`8(!h;UOH^CPHQ>3phh zZ%a>7URN)5rZ!W9v||mJjnY>q0-B_=_99-Z^mm#XZPG&{PXA;E z0_c^lrp8jA)JzTge(6{Z44zAERGJJ(qo{HALMl^$8kVO2jd&x{=k!y#$})a{xw}k6 zS&)b9%^PsNOXfo#o~P{l$x!i;iQa{QuWXq!WPURF9|*KtW@-WzAS-T$Y_BZ*AT$GI zPu76BPj-|J)I(%>9`G3|>!14xy5e2jQ$vJ+I3q|0)gL1oIWo`>w5Z2LNxpO<~I9f7iB zF5Qsj%8WN5%aiT-0`c-?S0;d2AXCs`ZLzGDs@)P<_-0V0vfR;7DU%7PV^$$sZG>i} zEI%CP7iF^+0xrqsP^NQDmQ79O>$2~>pi(0X=>T;@*6}OC-jvP$2FyCydpsE2mc9Ny zT-VE1egKsQSu@!+%Em5-?2fFB^12q;t0PdkD+{DH;XPUH7cg&?{Yh(GyX=iQpgLsJ zlQEc2dfb<74eAj_rh$e?U1)qle>w^^V@WNWzy{fx&yq z<7fl(k}ui^J8!v!t{M5rQ>byhTmFj=4E*JbsY9?wzJgka0rErCV%sa<HE!_TQV&(H*12ay(ayuYiex3@W_dbUxrr)`boppXbkEA~|JRt1zx@!*O!*ut9L~whRzsF8 z*HUXBM;@Dq2)XjLbQ>p6{)q}yzWg`p#+1qr$RIS`^ z5+3um+(heHy?i2F(P@x>NNvJK`4wtMH_6!|7_`XkzQnWNl`p2=_C0y=13;^M(HzLy zX7$Mg53l8D5`88%ISab&2-8)ya3fDf8dL;Jdy7S2D4kfECW=J{J<G zd+mJQ##jRF9ybGm?cSLIl@Pl)I^hVl8#N2g!tAuqAPcvP-vOTo?LJz8u^h6CzX~{P z$D`ji!fsLr>>};lXibi`tJnf6#%>1L#oA4zWA`|_LaK<4+U>JKcHHiZctC<(GlM`U z?CSSoNGI*K(!z4ePD0&-B)bvnS)H+4@CRhcb|)wkPqBMU_X1MwVt#|O9J`yeBjnn} zj)g(7-K&>CmDpXP`mfTifC`6;c8705R&7`04KLU1e#ijavP+@lxz6qu1C?gGJUTzR zV^_8cnk{yEYB$`qYo(H=!!D0HJP+*Nq#y2yUCKN_x7`j}DtqkwX`c1k?c4$8fSoH1 zV$iPZ5+V%Qou-QYh28J>z#O()>;=scyH}~(ISBC-I?Cnwk-u9i8NchUjTGid6E*sraFd8qw?MyQ0@$L>R* zaJsXB#M6HND|o(0d#AquN9>bnp^ma|eh+rh_N%EK9b^CL&+r**-!U4HV1IWeG*8%f zKLvHh{<~d(Wc$f}zQ)Z?RWWZ?xThJ{3kC_BAe0d2Bzq z1a?pCmzx27_VH#g`|WF}2|H|G{UN9k`}Msr*yZriUO4k~*zJV~yB)R#LdD-QvYrbg)uaEXrZ#IWVIg-tUH&IEPQ~f;#HJ{RFb(4jokOCOCZ8 z59*XdQWoqI9kw0#B3G(Yo8K8oc*(DYU8d>@p*!euGE>`}B& z1v5aA<^uD*3Lh%30u`xup%Se4`fsR&C?>B36{^^31cWKJDgfb%ZPax*sK}>|(jmo2 zJ>amS?o%)$6dg5CiB$YVCz{cUCDf3LQG|y<7OTj{f2Ej-Q)~|g^QdCoO~7%*u{uD4 z;*btwIic_x1C^7C?VkfqDSD4%WGM3Sm@0uRNAdYaK)xc+24;a`PBW-NMI_}fMT#?2VHPVkEW-fH6p7QJ zQm(ji0MrG=8}tS%6yqd_SE(3H+x%t4;2Nl0QAp_Xy{dSXHt#A$h#xeo6%(>R)hMph z#&<*UHq}5k6@6A{)++M90P~jOBg#?g73If3H7Jy{J2fgseFBvx#SS|_v!Z1Xc6Svs zX)C{{IMxiRRk3Lz?AjDZE1_~>fnsqFR30k6c@@y9I7iz{m!k0)G#@GcNP^u{ zMM^N3&lF8mEcYq;Iw9*K{J{Ca6BM|)j7dSD(gWnGL7AJ9gxA^_60MO zEvm(1o@19&;dh=jP;)GcT}e&D0@n5eTo{{wW@Eio$tU| z9XsmT2Z~lOIEv%svvb(H3{YLlL+$nI@%JN;ne8{fM2eXrPdJW7jRzr*G zBX%#H+&yJ4L_qe8eS@;}K6V1_c>SzoBbdYNU7GVFteWPFt7Ekku+vdL6*4bJy#(Ox zxYGyDd>r|%koh`Rz5%A6qh=|*>~=KQAiBR}`*XS#X=l6--kh{qgDf-VUCTzg9>+?djYWDaVhP12OJMr@m`KN z{zVOv&iuXwNr{6UFj<>aeVJIn3awj*1+{e$6x4V@{;4MC4kG0>6`HER~&Oz z!Ap%}$UiW+;rJC59QBUR7K3VVG%kj-yN)BYUEgzD-~r7Cj%8-RLr2{dFgqP_BMHB) z;~QKAdgAyiEo zbXrE;P%oz~^hDlHS938IKd0|jVeq@1US^=O&nbC1{bEiA4={tB&YpqK5T}1>_YHU2 za}v~kCmtQgMmqU7f;!?f-~otox=SBrw9`ay*u^=yQ^j}GX^IWblAH>!LUzV!Y%9Vh zJIzT3q&S`Z5?<1sUV9gC*6BQ5^T}}Pyb4*S)4=~=kmb}#TXMEjxf3AAsn#Bl>-5M4 z1_e&F1%N`Q!r8DZa@s;OtJrBh-HIr4s=1Ho)eNUfY9sZBC~<0Qa5V?1Oo`Q`48AI-JTrM7+mN zxBojcbxQsVVY{8`e*&||X*(@Jy-vT*f@Ysn51n@QI~7r5>ABPIhY(@FDUiyKK_`3q zw1=JQUjd9b)o}r?%02;PJC*;VpV3XZGYr%&rJCA4o=U;raP6hs!UK3KbFaX)kMbZ5 zDL`qZSH4%NOom;cGK58-eago7L4_-`DYM$ItPceoP_Cn&>Y#EdRYXzBr|U5IXyv%q zA&XJ2r1C#jx%@fIg34CL41k%VJk}1#Ro+R2W}fnAI@rutuKycQs*L{uP^Q#m0?L&&QpCHU zYSCpSQ0j??y;~}e3jtif$X;OuXIrL%H$y! zv?+VbpmJY1oemS*mDRNEb|~M;htE#sWIA-|QpQtC{76|$SH;0xw7yJs0=7q(wiSt7W6@7NLfS&0VB#2)SPxzb)EsWQ{@#5nVaec zooMb-dDF>(r^=5ew3q57dw{pI?cah5f2o{_t`@^^lg>!z$w@M2Jw;kAas+)r>8m zj;MZA17cKH=xdBs{SXX@Q++`Fv!km1qX>Ik)%iK71l6ND$WExtbSLVhY9-~#$*Sv= z)267lP)9yhb!sXM(o_*|!e_cFMi0$Q)d4!$I;T3c0-EPlmA4Q*OEo-*;byD;kpS{k zv)+bzzUuHLXcnjfsMB4jN~W)>NHwhpDy6C-H!#aorMCg)s;VPFROx|Q3cnns&ciz=16X?Im6YeC&pjr$BLtt#1OK%2^| z1)3eIV?mHTQ0Zde?4jy)>acdIURwZWm&zjmuAitH$3WJt%KIG#Ju3Xa{xHE*?KJD2 zs>aZp=vTe2htKD#Ep(-EK=qu8gF%%^0~k_q|10iP`E>Z~svb+V?@slVPatzspP-%6 zU0vgYKtAf)r*Q47UeW>;KlQ>dA=|B}s>UMgRAhkXa z5Ug&Z;vh`DIv-xb)f?XdbH93B9H;~8@+iPTb>>RAj!+L!h7_q@L>G{bsBi3nEK04r z1c+Aq(q)V|^^((2IjTO?3hJ0zb{@lxSN}>`*KxH2&83s-Q*=&wO5K+RS)w{{9+;=q zcW%OGlKN&HoTaEgrCl^tz3~B5($rpmf=XA%{tho^)luGHo>L!WVRv5LvKdsCdc}`W z$yOhm4k||-z7LO?ua=2G6{xqp4b4JzD<#22>WPlfELOj?2+qpXtNxpAYRPvnxS)QU zzPJjtl(L{o^-tepNSD<&oB&tUb6ufwRjvFDP^GTk0;pDR`2vqpqaIK1^M=|Z2GmV; zGo75*s>3ED&@FYo17uC=y-Ptgt8Y+!bVprI%SDTNQW2n4ok(5OHg#7c%6E;*{xnln_`dJbv!hC)f;Gmf2uanhtRLS zY1S;m3-Q&Y(jPPUX%^6A->vC=3o?IAnH~mvHPS6`9jN(~F5vFdEPn-5kfyjBDxn&e zg@7Xf#xC2X!K~9!RFLh2LH5HV_Mr#IXGQ?2K8xLlICUGAO z3N>qKP84ZA{1;HFnZ62|Wg3S!K$UC8e+!ihnkhOkD>M4|B!K~LjTmY&; z^E&;+jhd@;lHH{F#T|AnnlZ(o?rL1A*L6?x%co$rYD`psw`u0`0Ua8{69jsoSsV`Y zhZ@CbFgrC9BVo{`xx@!N(X6GPqg#_#4wW9wrgjXsSCjM?m`^qFDS&>>^ic@(T(jf> zr~%DMF{nY!c{849NaOVc25wpp`WoG}_nV;Npy7JMGn8S`JyDR{lDueOiGMc0trv35(WZ_ze zH89_=^`J`efHt09@P->t=Ym;dAjnf{dX82KU z5p8bAw0zp22J(mG$J?WA?ST8Q52(^^phJRc?PYqfTJ2Y4a7(+s90qk-w@09EYp2kWNTb%T640cb zdkhB6T2I=2?`T(eA>KW0?pX|}Ra-M1vNr8D3E;lgg^JJz+IQ$Y{Gry-84)_QMYLOV zX)|e|exe^q=dXlJhj z3~RU1h2|0MDcWdVbzemxkh`we0Zb2F72P=7rTcUm;(6*`lmL8mQBjcj>RO|r>8DE= z2iUE9dKS(CbOF?H*{gdm6JrU~RZuRxPj{X6{t%t_ZJ3AZUOET}(>?kcD&e|{2^i8r zooWXFZ8Pd_9oF5Wy(&WYgl-f>>C$MajMgosuRliDv;pR^x)aowI;LAdxp%y-e+tHV zT({~gPzgHgn}Aa~4sAqFeX%Hwyx0jZLR2_$wi!|M=EpVN#bEKst zQzvl*oYUFofO%e5?hDA$sp!-sS9g;RgY$Hew*dLNrF5sJK-cgupjdZ^7J(Aos4PIK zZfiAE%CM7zs?a%gAyB1m9u-s;hj_U9RT_eqqMqLTzd`-IV=}T?Vy)g%Y?&{*G zXu79cnF?st1yI@9u5@UCJ_0Pjunb%<0x` z*aVfQx{5GR&vcoGVArS9*n`=xn@W4tpl)?B?1prCKOw>k-RlQI4ePcK!OKqlWEzB< zenl;0?)q=rVBn#DMhm=`e#imft(Q^C=%e>}1qQzQP7g5s^$BkR_UNzQ05d>8j!K%n z`j9{1B}gAZr9!ZN&RNJp^lh{igz9Hem+FxIbNX%%>+ettBSQc37K}4e&r$3 z`b!%zvV478DwqX&YY0YGsDG3GsIo+FRRBu$KL-QK^nO&Dl4%%Ztkb`I z2tFJ1!JZgWqds>YRGRb~F2h-~e%B_zU47zj@N!Ro^*#(*^-KD|Y}5BXfkB6U%u6tT zpckG8^-w>JzJN~sr<*}N)-R_j>xq7C2+X_nA>lCZ(bsMQ^O-)J`b2&D6S;tXeKdU} z&-HG{K@I5}Xls3;A59k(hxO}P5pP8Q9bGzbGxU~0=5Clmhjbo>PB%E)Wk{s7$=gud z1g4MS94(c;hCD4OKZ92RV2>e=rec8Mlb>O}*D!A`3<3==(!dNh+zr8SLk#t^0igzV z79h;3D#nmZ{oSL6 znqj~(!?aa!7H`;0S=VvH1^RqX8V*ty^_1b!4agD=?@&T;+HjU8O0wY=rA;Y@%>9U- zYAEUlq#5q~1(giL&N|p-8vJj8I%il|jIieo`>ErSV;DnC!(78lzd|L?aMuF6d_xfx zp+$yv+C_^EmPW`*4D0FOq152?2wpB2ex41g!cb33Y^C8Jn#mUpb6Vl^is9X57~oaI z$=Bhf%3x26akXLi8kjYPS12jHVNl0HcGKWN_fcvMSC#@A4G#CO{2<-@MhJWZ8?i;?Q9{dA?Hyy-1G;G}oW~V{)6_{Oy!?Y zyKY0(dQd%vLzGDL8dB&lGx`jp>2#pqV5frYx#8&%Py>dgXst6Z47=(1h7ErdLUY6r zu>#;~6w%CbH}0VZiifeuh+k!wvG6f`dKy1yJN4da*Z!{<$7#8^O`(f%-;-8L3=Kvr*jlWM01$bH_oA>(+;EfCVW0Hx)VG!M*jxrG#(fYcx-$`JM0tVtMnqf zjdL%-S+S}uBx<$hrH8BcQ%sNZOsjj+#+6EdMPU~KylDuYIGH3IE4IYoo%W(uVO zz}@8kGMFBw+6WlzG9CT~OmEYfLx|^N>ZRKvzNWt?gYq-gu7zy3DPcJb0!%yCf!b?Q zbiyvsBx3;kOxaX02AQThg9B<{e== z)C*ap>1|4FkC=R^RERRAeGF!-sf)7vI8%=Um`6=UT0f7O7GH*WylE=+SWcLZ+(o>T zrbR13oia@ugi4}GGaq)RO&)ZuJ=x?(gezCR7u*(TLFK%Qyj8$iBEzZO0VOb61SQfOi+r7AM5yA8Wila5YM z%Sy0X{-I63%H-pZA>A~+ z84qTy>Ff-sG@8DphDMWV<>!D_(@yHUwV60nLUo#sQSRMk`mGOky(au`Pm%tZc5H*) zGm{&Qy3f?r2x`D|r3VIsrf4M$hD=MSM0{cLNPvo~IphS)cbY$=&(O_$^-XwjH_xE0 zz|-6?7v^5(*+T$t^KweXe9XKcP`l0PwBz}khp5@O$6QOBRDe1AJ5c+~E9U@$%*Bsj z5NsYtD{Y9Gp-boC=4Cp-eskCg#5-U<-wVxy=5MY;CBn?7laWaC>ohfvm>1FCnnalg z4uctM_C5lLGpAF7_NaN>YEZ|_zb=JJf_djO1Ug~9PA!^~=9zT=<&=33&A}w|ihH2W znBSz|J=r|B6I6=1#Q;b*FCl}oW(T@dkzwwk4=>aFcRgfTX2G`@Qns0`LcARF%am2- znw@AHEHE#;4=6MvKgQ2*E~X5k*gW6@C^K)_4b5`%A9Rd+!R$c|?+WwwFF{>0`_SLx zT{eIF1S(g|3n>%0YF_;rRIZtGsX)DMmVOOpjrpw>4C01a!vowhHzh$u}xNbKOEriNL z^ENlAbeeyoQUxmqqz7jCD;rynv8%V#`LyDY~~0z55Q{uq*%#XTRC zujN;2_WN17>2}d>OAwtG`&<5{4#-|h-2%u0Exojw?z23i)4(7LmpX}|mf$d`gjt>k zAYQoT6dmU8xAf5qIb<>Wz~HcD*B_uFETbAAi?oakgNnA~$3hlknL(RktVKZPahA*r zP>HvcQDS}ElDQS5PO!Yb5OBi6P`@Y9vhE>dr!7w>1ClJ|GQb(j96CQbZ+Ydv9+IVO z7c{di0dzAU$5Jv3m3+%dU#JvV)IWnNw49_DQe-*)6I3o(ylM7SSPsyRUTKNa11?&Y zQSNrdGSY>|ylUA-RZo>AE*s2h%NXjT)L4F^e#i|=;Q_?EX-WAIvRX?f6;!t^$I4(( zZ#hJlO&TmyzK3R`#c&-ecPv|b!ECXtECt-P9?|;p$nsSesK=J^V*yVrxf21smak7k_SACyI3DGh#p5~b`YiR65MjU) z?F$&R2+xB#WNA5pLAVt?uyqUVBq7#~6d~Li z`V`cD>+N>P4p=j3OFn4jQa2{Tx}MgTNNb!Mm`AK5w3bI%-RaEcm~|%IA&9qbq2>O# zbtcWT1nW0+r{;w9%VSVUwCeAGI&IBTLNm$w;x?!=)~)o3CR<;o4=BwV=mc51HQfs4 zS*uSd>@uvsQ{Izl-ArjkmbIC>w%Jw{bt`hLSwDiAYh5Nnygche3R`Fmn++(kM!yc9 z#nx+7jFea-X+oD;Pf-GS!Mbn?0##Taq{DTkm3a#0MQg@-xV~f!qW%4<^?(DYDr>X| zP;DJ-hxs+@^2MO8TR(ma)J^N{@qk+EcFHGiSwEt(tj_AP4(7M5_4bI+Xf5)FtjVf> zjzG=U@+80=tIPmsvA#mh+g9s^I#6xahaW-nzI8RZZnq}T2}g%@EEhgItt+TN?XrGK zwc#V{i-~ak*m_S0%_r8+qv563`XQC^PpwZs!h3mU{en7zebxbgF#D}bgzz$Gol8Ie zkd?O$%okQ`EzF0lKT+N@Vs)lU(aqMc2GiXZN!>dSTP~#|yKIlP!@$$Fe*$Dawj%0> z_}U6Mu=BI6kA~~rwtrVZ=5HHK6D7#Tp9Lz|R(}RQLu~JD1{G?XeIA-&wh1wy4%m7k zp?T0Y;a4yZ*=k}D@376CR*eYT`DJh&WlN(LO|-3N4^(1ojfk!X=0FQ> zx=o^i!C6}!Rl6Cs>J-Q_Z6&>sowGflW0q{2{8tPq$M!1qjB{;^55geNw*M5Ud|N+F z+9KOgdN0Mcw|4?cZ2q4BN^K`;Ehw`!J_S`_8{Y=IN}D@f0>5ap{D=WwvaO=M__A%) zD_~aHT&ZnWZQCaYT(e#L7|iRoPU^zd*e+1FzSj2J%b;%Ae2>6&oo#A9;I?hJ49t3) z_f$ZWO-<=}v#oRy!rrkZRUvGPEnpnruI={^V9;ihHvsP2GTuV;c3U@n0UfrNski;W zc7aY?x@^T%r#`Z+KZUV8w&|%S_rzxZ3ko!J4zyWqU25GobU6GebZ z=M{W-sdD~#4WQaNrU=Yh=bz}7!Yya#!!U1huIT~Q?7Y?j>W*{DztC)Po<)m)tMl)) zhqpQNEQoO5dDj16-tIhxTGbDoU!k?G(^>TxvM%S25m1kuFSr4^owrfJ(c@fq2VQ!e zr+*ADPn{P?ARBbHcmjr;Z<}B@;=G!cUssnFYC(Crgh*lMmzdHh`_bbz_Xh4&{$9pF+}4{D!FI=zV?myE424|Z9l0fe}?d4hS+~Sz1|EzcRBlnp z9^bc{MU_pvUH*0mJMBKDqqi=*%JTp`vgO=qBvU)JgQUpYbb%e)ivpFhGBME#0CCu$NG( zvekYUH4TI8f8anEY@g5!%P4y%Iy~8Be_8{w+kO%y1kv`h?LhX~bLjwKzx{V4jJGeN z^OXeqEp$wsXg`IXM2dZD3j(FuUmb$8O#3ab!z9ao(;?t;?Ke`Eo@f6O0rKrn({+dw z_F=R!6xt_IL!-$4j|z}d`}DVAdBJ{404&SwuhJ@f(f*I4a9w4urkeSReO?)SUbR0$ zP3CL%tQ6pC?U&PsS!e&sJ&^163SS88?c@JzDcHwR^ZmAcT0Q{x>{C95Nt^wYeelw2 z{|8l=efG!Bfb`ot(CiqnuRQ_FLHoaH1`OFRHbOUS|I_#2M(qEh1E*2@gku=Xn0?U~ zAkXZ*4?s9>pIHLjW`>+Pf?F8xKLY2+n9Rpm{22@9Vi19hmH&WjW31f`T@ZssSza(> zE)QHNBa{AFfgOyQOA&7;<6abmVT>5M2N=mna{(8{c()jVb}54K4a+{ z;7%|C%V2VnaggrCo?@)0Z1fDnEgM`RW5eg*iWn2ofEP19u7l6>j2(2zp@gxU66;dN zLMovyFn*;a{Su>-1Ix<{Z8&u0jMi{iRxqSAAFePuMxndP@ShLeHOBh2Ak~a4%5CZx zKT>gUo#96NN; zAdeVFS3>ugQR@i!AS0Gi_aR2zM&O<>H2;8m%J?$^fKf&iJ?k;Xd&_~FU}U91=gWM0 z1wJ=3^KCHM!u;%C=mMCpmqNFd**gjn$c(=a*V~v%dY;>vr7aMKFykrR3T4V^)7ZhB zO(%unOzs495zNU7=pvb)8W1mv*+%_+& z!^FS9g^!3?O9}Z|X6-11=a_}mY&_3AJ`KVW=3W|g88b-<;YH?`l-gcmHqlafnHew> z@N(w7^@vx+{Gb%MvKMZn>`R02d)y%2Ra9zjzi!y=h%)8VRsAuk=hUpDv z!;c^h%qi4=X=2(@ZgYznJB+ZmnSV?Mxx-xX1f-c6O?A#)=06!QxyQ88C)~#5QZ99$ z`8TzW+nKXv&~-9L&Cqo*tIDB!$lOO~S>4QE>2>rlORj_TGhe3$$^i2-D&QV5Um8S& z$IJw}{5Z&5Q3o$0%ok~Yc*0!06P8bzlc@JJ$_!(J8)G`p2bo~b<>UAHvI3~Rv6t6)ikM(Xkgj-p&EWib_rdTkRZLH_1nh0V=1wt3h`hc>-P*xVLFFRN#?10RYLJ_Ctjt^6C~+{H?ujCwcge^X!?&B~n$Tny_iF9_pUZ&E{XFU!IL zd>`xEZ4mBfou37GJgaUFER$HX=x-I0S$&g04zSiNgzzA1CN;7SvFwgRn8tdGe#2qb z2Ls^JSsvXW87yCFu^nLyxavOc0)8Evx`J&FV~ou8{Sk0DvNvj5?RatP<)cpJn}c6+X|gJSdMWWtC8a z;R1_8Rb&}UL|!hkHrj(+Vr`>GP{CqRTcnb;ryit=HR%QDuCNqz3U!s0wj8(`mW|HQ zYgv2s5Z194$3l3W6-cXFJxlBlu94-L3zM6yXU8$5Ce{EwkXx(;)QZ2&dW-hP7M9C3 zn6$D^ehS=O*8Has-eWQIVA;l6un4X@SVh!ydcd+=hOU#8-_f2=OS8hgxoosiy1ip(crQ&=yTOR{mH2Vp)fMeKQ*AOE?gXDJIn!>&K9o&AcH;F4cAB5!L&bQva4xDIm(`F0Q?xcz#Y2d>;SqZlEZ%g z6i6<+I|p7)us^0%|0Mfs0lb`I3#r?CnmykNVF7#QXRs_{*V9BVW=B)DeU`l;9Nanf z?7e`WXL~<`@B(`gl>=q$9I8w&vOCv6c!}K*19F+&J{i#~+0&>8QN=z=gTKO#pq%e2 z+mU)Y*Vs7+A*^MG{|hg5?2At@mh0?RDuL_Sbwe1$4fY>CAUD}n)U#@0A7}%2i~Tj- z1iH-*wZh9Cb|mdGt?U#laCh12S0TK|et`o(8@q*C-1ph<{|Mm&_8j__I@#|3Lf6Iq za23cyb`*8UyV-W*fcLSNQ$^Iz?)x8b1MEYiAdlFpdw@S?H|>LNm_6ltL?2;KO$PUb zJ%?`gKV`o_AKxgulghGjHvY9Ud~WRGpMdk_M0^6*n>l7BxGkL4K5zk?Jo-?!a&|8U z7s%=9gtKj&!$x=s;!GWYFog3C?RcRaFCBC{ICBfY?c^+92V59uNdknCoH=t4D2nsT zAqaPIrrQAD&B>=EF`Cm$|7g)3PS08V%s5VgGss@f+S>r^w7lR>HbG0xf!csb4qegJqL=YRdc<#X=QGdsbN4MBI3b7DD!r#K%{gXj#$ zG9AJ~&fGTuDB}DY0%0*HpSr|nIa(QnC7epS3s=gK#)7-RX|utkjPs)ngSg0f;{gUy z&S5WtNd>3;We6)d3+}+Oit|wc#yJ}%Fu(~8Lkmk^ZZ_pvo4KZF==`~bhcSo% z?uXxkY~?PXL?V!zN9jl~cdrqE?cACRa2>)`zX?DncOB&+Vcd3VeS~wL&W7s}0N>4ho7UE7?%N`e81A{B;VhP$OQ*nlxvx;Ew~reU2H}40bh@Y*&)s|;5t6tc zQu861TRQ;m09SJZHWN}$G2(!6& z1u!|veUAfN4)+F~z2tKJsrt<0UZj09pF2Rw*D3DTbUuBWE1U~bz#VuEV>!cZr{%Di zd*T>yXSpkNAm_MVlmeXR{&WsLFL1XFgOqVuD*?F3eUq%3%8=LS^*-pOrhgRYDF zz7}2{a@}!4*@xb_?(7&mexhUAX}G^W2VMECIYX>4$CO{Z8$a zAYK*iB*8p+D9Co+j&C8{!Aqm%X(w;@NrVmK?Ig=^-u5(jiQP%t-U#go z(L5(=*Y4pRr339a-r-{a?ByML3f(^5pD%zU@LU!`m&m(9UqKQt{W);Syj-fX4)MO= zBTx#@b22Pbd5gEB~FEQ2nCH)RjV5#GvA;WLwWX&y)xZ{r#Oj`3#YLU)`ui>7Z5 zui_!NTwXlo?5BB*mmw_R)z-n;8Q%2`Acef|R)C!4g}sMCoZ~H91@1iWADXZwyhJ15 zWxOPsq8E7$zk^)j9izV6WnL#O;+4FL`5;xioN3Tq;R&g4bCvh?V!&&7GpX}c%X=*q zTpiDq+NalfexG0v4ZIuF_G#pWIfJ{&OZ^4#CfD=LMqCB~ohZ72XM7(awJX(3b z$3xi0v%d*)pI1g}WIL~74&rt2_8bN2;yM2Y@{sq{4*+!YuAGKR5ASyxXFu<&cfbws zLUzIA5wC#`#vb!#YCwj0zLds{@TSw!{Dc=xhagXRF?8Plj3;x$;KzACTt?Ul-UDj) z`SN#+!^DrjQ3BWgeAP{u1n|wT!P!>6JI&i5{(RcQgZWEGpxe&Z)4L7fAE9JuC;vEA zHer0rBzbga6YU!M#vnx8`De++;AT>#?v^4asJDnz%qv)7z|x5-~TxX^Z1P`K~D0&rfU?Z_{o%` zoaTR;fhSSGFDeID#P9Khmty`nbz{!**De8fjvqnMOZkQla2NO?OTm@#h2G#U@|V#> zDCfr&!)FEmhkS6A`~!5nUBzcob#{%v`ZWNm`L=V2Uc(o@ia@pevJMFA`DPBt4SwQB zfH&~}q71%~zj_OBxA^B4fxFGG4FI{rpP+js&HTny@N$>`^92OD$G_16(#8*e3-Rvr zr+NYRfd5Yz0G<5J6R_;!7tKJthkQpo*B5&E@6uTM_-|47uAlE$3~qqG?mD0I`%IXr~^RG3i2o!DiQcn*P&E!ke>AgL6rxDWrDb$A-pIUrHZ{= zaFD*23c;o*aFv2&YFt+d-lFReR|FoNz*P%&(56@;=-CKitss>O#yY`re*mrv0{;8J z1jj#z>qddQ51ic;h^R(y68!IbSl$xsdj?#ypyerqErPF=5Vi{HSs3+Q!JrXfL~xyo$i{Rt47>FUB(;jlBfKw%;+xWU4}VFA7cTUPK)eXyQag}Hp);+mQ9=`aJ-dX3D?nm|S?@y^D|~em0DFYKv;@ToEBZk8 z3ir{~lX&4d6HF3>DoXMbg^X9gB?)guV4TUqqmdwogi}^Sm?C@@jOeMtOZ3Llgd%zY zhlT%AEqX*4L%%&!I5HVrmM}CLBwP3i6)H!C|F*+2N4T7_%3NUB;u!Zi{mk1v?!Ln31 zl@7Qr2;C^ry(EmM`EXgdXEC^PVKm+GtPmC*MubY?Jet~9g=3TgTobNLg|lkm=x5N? z2!lTcpjKG;I!L|n5#6-7A?!T{lLlc2)enur-zjanDLhXZZi_H!BTQO_KD0{R71}z0 zyC=*cpKU_7$q;r3=e`T!17UR=0G-0@YLG7B!EiWxD4a&eR=vVLs&D&*|E|Mt=obbI zfEy6{(2n;=sHS;4B($#wH!Q5BZGJ?UOS$(G;hki_p9%wLRy`9YxFhVi&|Zq)Hz8d9 z3jn^NoP6jui=I*9;x9T+CsYBV1)l-FRa8Wac%bOYH{iC3)NTN57sX~n7$Q1pg|kpm z;}wt{qUWjOxl{C0FDxTOiL`}8irAC|MTz_<3Em|N8-#GTXgTeov7#i}miCB3Xrjl7 z_ENWLugG#A!hNDx>K7!4&ThlV5=Ea<3Xmk?e*ls!dYjsI2Sj;P^rndFIj~F>CBFj8 zG|?Z_AUZ7CPuDimMSu5z%M_*3Dx4+iqU|$VH1;RRQPDIDoE;NMcEcoB^r{-dJdy4! zeCCU8(#TGTBF})F6t!oDw{r00vp19(OR1Jk42Z(f((kjmjf^)3jYqSheaOL&l(Xeqq<{M6i9bi z$3*|5RpXf`j+WnXk>?F~nGkKG^TsXWMf+glCstEC#b5mV9q0nYO=khvDjubZAxP|a z0=i&vN2BemO_S@Ls{Mtamz=*Wr}~?29hQ2q1H~e*e4N| z$HgJ^7Xdlqxxw(6E8ZCbVV-y?mE8H_wEx<*;@(7fIV~RT0Ion>Rg1Bl5gRGjFBH%1 z1+GN=@pEupD*lUdunXeF-q4ka-xvYzvN#|Sx^nR_wTmjmhXwFbDZX?Z!mHu}TJEoj z+rEWmwb-%@@EUO;ox@%icfJQwFP>fi-3{?5)%Fcy2RgcG5`VA_&TffI900#9zC}~* zj<|+u(N?jVxVz#OTC?tnOMk-v+r+i=fa?&ib%4nO@y<^`I>kNYrAr*#3|)`-#Y%9! z;@STlIEgQR1K0iHx`PP&Sp4gcFc}m}X`L7n_k09ySp41>aQ#$#p9-o`@im%pW8&Da z5cZk)8%hX#CEq6lzFAV*4&fHb+Ku4+B$FDU+bZGEIv6O~lLVjJB&$|n)IpN9RL6x# zK6@X4P|1ACx^_qsX-U{Axm*h_LXv$Nx=6`o>O4nDUU>*^m!z56MKO{by6O@u*|-~) zdnDaOAaN4LVg%YR$uhu8ykw>~011*4KY%1kf~hrrK*FYjv4avRTHy;9*BAIdp zy2BFvT5#!-E}G#Pk_wt}MDfyN1`2xvV8qyg_Fm+=JCB^YDDU!TJP3CiwCI3KoUh-inEK4Ka$|dbvAgq#<(G0&L*|q>9yDE7`FXx(M9ksP;CBr+Q ztCKvS6yUm~&lg<1B%Qv`Mu`v2>6?;Y_JcG@BtCF`OY*b?md%nCG?`l@cWD)Fl`ww; zxhwhl54gTBVbcEBE)m^=WryTXIv;x=>8Ez)L&*S5-)_kaYM}H;)Rb!WN5wA8^Dc9LUSP;mn3cmZbI?`Esnm@ zOZ1C2OP_5336u&MAlsz-ga8CdU!;Cyu(XXX?}SNX41Nh2maQOqQ-)2H^o|L;<*iQvbgII3zu~5+p_H^9F>6rLTPqE?xTJAV`K(^A6xgq?NRS zWlJ9~!dQ+<|Dn^zV^UvAmyb)ObK&!hl(PfELg`()uw5kOrGP7z)>2pOy!87YKuV;q zMuC({%SM5_Anl-i?2_~sx}|VgdX5e(%B3}CkP7K{43KK+b7hELBaNc;pjIlShE$#O z4F$+`=`_kU8l?PP&^1c$Q-kQH^qpOB-6UNy9f591FHx4>ES+$LNsBb|1qfTEn+Gu5 zyV4JSLxg+Me_nx?PAQktyDsS&n(q&#U*3e5Zt0vq!SzTl9EPr6YCi}vApMn=*hkWp zbV&DDx|#>eLFrA}Ek>l}Ghy;Xx}g!=Q)xs#$f$H%9=wc6um1zw7Kfi`ZS`}Ie*v7o zLvTO10EgZF7|V8t-7ye`IP_3r9qMq9ZolntIH?EO=@3)|FS{IU^rm+^%oqU|?Vt(- z7vta@fUt26q5FZ`>)=xapZgrXrh~uz4w(l)4mez$3@-;AHo5|E$U#>FlH$2y((<)!!K4Nry&1n4EGz>5Dzb!EqT} z7dj+Afw0KIxD;HegY!j%z2Gn}1;R21*QJ18bSR<;RPJE=7`h4veigWChv%pSu5lQn zWxK)QC_UXqhr<~l_ZLc7DeSHX2S%>NF6E(gg$kcSSU1n9aQ z{&@+b?s52RCrGb@|KIR3;E+=e^2lK{7P!X_GH-B$4#B$t7;>0N<=mKq5A8tD92m5f zk2|!hz)d*xcf-)`W*O!yPHld|QN z(4CSMm%__w*T3}LN|N1IfgO#BkK>$11<;j=;Zc>{2b zvg5SJ-IVR3{%(`(J05VYvKTtazAMY}h3=lLs{?>e*{W6mx@0jQfO{z0bQC_jWkR~u z(j%Li2iLu_I1@;pOrHVLFPluI(17gq7?4M@3Cfxu%W~*T9+Y*S1bj$V{5Ht2>@;mJ zPi0G~S28M_N5_t1vcV7F<(Vwi3pzhXpA-oF9ha&B2zK1-4sN^Szb(*(JMNhPiEs?1 zbEinh*IYoN9FNm#8twQRHCJLB=YEgqv5x;x&c4TS?NpF`jt_DXVZY;#qZnYkW8^{L z5*(+x0g&wYzdO(!aD4f5kb{oLXjUC^Z1#jM!?E!Yghw13{V<43#~0};NS5OlZ-YDL zm}-ER?89n^g-a-3}e zuGsPH0hpY1{3#r|GDqG8M7Ze4`5C%Pj=nF$^0MOuEi6|ZBmM%p=J+Ok8r6;g^s(1C zHd7UO-7#r90QHVrXxF~sINS|kgJX&doHaSFdJW{3qnYN>ZO6W^!QF9OOsRCMqx-Lj zaM$r6ZG86}57BPe=2$?PRhQ%2TVVOnF)abQZpX?92>Tpabco#V`19+~4LE-EGK7PU z5B7r_a=c*1kcJ&MJ_O*YV=|?-qmHl8t(`H)=v(ezw0 zC3C%EzMM?3lcBE`+)A%XE1lPcEb4HDCT|C?cGYKe+^x({g8O zauvwOsR4FIUO-k!YYc(z%ZFz` z*e;((MSX{SZ#dvx@*iolc_@F8cHeIK+@}~zkNiifCi>;|z7P(`-_*h6k^Bf93p|z| zdkbEMF3~Rs4@K9Y4hm%5D4= zRn+SWP&{%5*{Zl>hU*~3!x89$6)#c-xLuJ+Cq^NPAj%+iDy~L?3scN}9+u&XIyyIu zP`q6M-7dv!352^9i)j^(R(wX64q_BncEKc0v9=1jy$aQN1lp(gRtCU+#l=W)iHey~ z7-y0qX9>7u1)KU42Na*tAxMhiixTKk6|GbUq$#HU0o-B50@^f=DAu<^m#N^b0w7EA z^(k=Kiu^X{jw_CMg3D1%I|eVgiqSa0^Av~ZV>qcuWdd+YkwRxq00oML-$PiW za39C-D^^rL5ALi2{}Cj)-2)G6XcRIL6g@J)l zH!D6bgtHdKyf2_@RXiz%?ye$y2x0Fj*3bssu3&wJKpl#Q(; zEC6~GMOtvZid4E^(5LXEI<;T%!gc5d6z{JD8B{cW1mTck#TszK3KQ+ABMR@`AWsyg zdx$WmXg&jzXNs1~5RNMbvfy(Z)j?-p(Sh<1POh=TH=}l)UMFIe_ltL>=wlbXR z@T1B>2jFs)H>f9&tMsJ^dCFu5=<=1XjDeg`Cj1X3r{|w9fN>@EdyE2BF z0UgR^7vbfBa+q$)JX9`t1Hx|Q_xHi|C==F!>s3CjLfAg#2}-FRDcRW&K2|Pl1~;gj zl?~mHa#1OS!^(3sz^BUJ&H+BE^lgH0OzHdtmd}*Az6d+6oKE$_W>xTQm~2t)UIG(8 zm97t*ziJ-UaRI7Rzk+O29ix_4km`{KddO{g>h$@Ooubrw--i5O;)n>Xo z60SP(E`$-P#J6A>scK4u>)oo6yCBi3Zrb=_RKXiy8LM)<2$Makkgwr-pK3Afar;#v zu^{oPtep@hsD>y5OjLbEt=t2uM#}sTs_N<7{*dYoN@`M6K9ozPs!p-tGhO9I*Nrn& z;gHVDuC-8m4!b2T$P+AUY_dgZRk#_*3cSx zO2wcq@oAOl8Q=w~8-CE8QLUw0Tx1Sqw1~AaCTGWzZI5Es#CNo-cjA5!-Zzm922+})i%GU0x-dYDQ z_f?Ke2-{WFe}Z(VD(SHMfokbY06JCf^j&wW3K;Oxqnb*cg`fIYBe3M533xi0T@wTq9*gG3fENexvAE71O80);VA&dRg)OtCR81? zbZt?WQoqMfZJ}xJukK9)EtBFDD^-h&rBoC~sWaY0*l6_+GvG5uJ^f2?vFc@ku-v1*@(+Y@ z>S=#rEc?~jO8|&hpV|ang8DFB$w^c%q`f~$UDE>GLG?$JrX5mei$GG;@=6RhRXsKW zE=~PzG%PdJMfc$Ih`M<{d}gW*RL5nh-@OjYZ1rl&f{v@-<$~m>f2MhqtG-Spcb>Y4 zYLa~QZ}TBMrM{93lhf*ta}c&b9X}VsGwN`v`wP|GRNJ3bZ>4)W=hXI>;QG9}y$Wca%)p%Fcqx1-_sXx`j zOSO8;6~Y?zgb3uiI>s0A>eaLUOWoB=>1s`b`sb(6HLBk(h3=O69qQZMR(sLxxT7{q z0%=yO=^?kMpQGt?Po4HWgl+0@N~!LvU78_mS1+N8p+jx?5SCqPD{Uwb)oj}4yVXsU zYxJmBnE~ikdr=2+K&_=4D38=>w?H1NSN{v$pt^$Ar6F}Gtqf1ppIM-Ls=k>3z^FQg z_P;Uptq!kwD3hk%D^B=oU|YtlVHA~b>PL3U~EHp1C%%^$yk zL~B@db2CQsD zRcfaF*X`8=Hvn)&(?Vz0)tY$vvT8JwTL7=sETV2`okoEC?u8o~yA!}QXi7rCHELE+ zUletWC3qUfg|6!2*zWP3d`%PR&y- zNSDT&`mGN&FD?YUTjO~jf%-IQOMvUwoT4j+1DfkL2p?&G5pWa^HdF4(%*j;CE_6sG}67Enf=~uANU`WR%vP&Wm?x<6ncx zZf*V707Pr27a`sr?ZaglM4UE}KFqyZ4y{Z3w5vP-NYMW8S6C)$nd{&sNt+f3lVq)C z1muu*CiM$av`c7XNY&<29+IY2(vFv*HRC&d;fS_~*78j4H>rSUX@ANEcTD?NIdsRh zmwy76qiwB$mt5^8N={B_tM6beC$%$LU~)=3=7{L0wTEfm7HU7G^ruMsiyKI>)`eE0 zv)VO(B6^9|+zV2wZR~^P1+C>faAn%{^kH7sp2>%>T&twN+NjV5`y)c7_FHNwUe&s` zLU&Ee$Vc>Q?VP_rYP25n0l%)DGYOXU+IS~e-p~fp*VCZ&_y(j&yO%nNx3q^S4ZW@1 zLC`q8Cpy2}^(7lJB7l(B;sw zwoMl~3SE%y8MP3DbrT=KWV?<UF?H>0VwA;V#|k z7eRLGHg5un*0o>3IQQsew1vd!`riQAtK&@rZlCViZ1_ypxduXbK&SA7mxH=>!2lf6 z&880}P515zcsZ<_M#)~fPEWahhR%;J`DN)=!~vJB`-O6pqq?~b;Ew4EsIio*Q`5SX zr^_n@m#;g12ctfr`)3^7X%J(4NsBIl1JbH{ zZ8uEXbeEc8a$ooR3lO&JrqWdF&>fC|&j-5aD4BhztA7>nZrx@oBYJcy3%Fk0K3dQE zbQ%RLAL&*v2kxLI9)J7xYcR03h+RuX;hMKb9#9N0Nb6~+uLFO z>~-4c0%!Z2%BaJW;B-U+lIZkC4|GXR)e?|or)#th9&ox(TThD987h-foqnzbNpoV% zh0nuIL0`d3y3@VSAk1|7gx-6W(-tSdvz<1?19#MEkhZ~NPQNrmcf#pgs$ow$_0Xqr z%BgP{YO4p2z%X0PNiPGlY-LF8%}qqf!W|ROw+f~=`@wq%}xtvK-l86 zYz}<3I+=1Iyz4aD0^xlphj+oXJH@PquES{!-9difG=&aeJDo02`=-ljIz9P*r|^6T z2b?wqfIM<~Du(Wf(`*-zr%ta?x6oJbkPEU|e|0f@ZqbLG2HB?Hqyr#G@A@KsU$8!O z2%`?uuX+i%aD8zQgwgujbg~trPxujmV)ZkqSF%U{OA>VZ^sn~7%YOZ@D?w8A=jkjt zO+Uy5Ijmn32g`JQH3@9@ z!hF3h6y%g%<_XTMU=WX>*sC8Snlc{=D^E6eV-V@ zHoap4T;JD^({yUrcLc#pr~U@5tzCKnt!@wXeXHTDTc1Q3QjdOwmh^u8rblo+pl|vT zJ|F2nF9ZCse$$T-4(czjhvlf=m)c=t`gL?c;F(-ouNcH3%9{pq;nnJO^b5Qp?4JR98HB$jPrOHNUZbRNf7RF?xd5#IOnOiVVUUM zZG-D1XR95!WaolcVRFFP@?YoK*}4y0y0h+m05Y884uCu292yDkxbr^x6my*4p*$ql z`P>=k@|^e0hO>OI6l&T( zbKXmDY}|Rur{Mfu`2T|#fI_;#0%uR$2@@*Sn25iUEv z1t8Le83sU<3y;3!JuZ7Lz%tHdemZoCE=|RVm*i3#20*II96FCnbNToMaK~N#ngz=o zmwBBK=DPG#`H|LMQ&n1C{k@dSI)4}aym&)(qWza=PefA+2Lms$c7pW0s#N~B1yo|ame+8CfE(__3 z;WL+4eV`k6Sx+sR374M#>PEv0Dwy~g3hDFqH)MPcX90$Q72viSdVhdrkfDd>O0Z$& z2;kcd{RJ=yF$7W9DAaJ3Qok_6#1jC*4ZQ~-j4;fz0vBm;xBy|4p`QMzCECEGZefgJ zY!E*s*1$5s%O1mUIiklIzI_g4zu}K`ka&ZM-fe;*kFI4V8Xo!ro@59}0{ozXvm3fY zh6i6lm||E@?C8scdXrwmW%BG?(jhfT07G`##Jd=?qJX(K8&=xyN68ce?cUSe3X z90M#hyuSy43x-N(=*kSY=7YOv2$%(~+_35td{!7jjv#uap&}2Y%3#$(cg4`P3tp-X zy8rS)Lzfer)fzUtBT$`z^()AA!-`$7Y%u&xZM#Op_OIajrlI%(giVHZDIm8Dw|I!q zZ1|1V$QHxw7ZIV=u+?3Dw`pL<|kMV8-Axw)QI6J?Kw{j zf-ezv%wJhGaUd0y54*hCfi&?Xjuz#y-VNj zPFKrrScbV4EJvW-t_L$=8SVP=0_gU+uA#+hziY$C;10OHPNmmDSFdkjneKXE9&{P5 zm*^_i5!W3y2s2$11#o@T^%1pwj=5eHBKir}1KYrzbRD#ZJ|j9z%}~< zbZ1=0*21#T^*Wuh6}c{40j}6}g3_k5uEA6gpL6|z()07K@6m#L$u;2~yj*royNcnK zyUJ+Yt#Exc9F~=?pB{&0mFq7?kSne|`!LQr*FShLx$f#lyJ5X+DAnjUTw|!rz3Cd5 z4PBFK4=v)iTpv?ib=$S$F}N1j$RprdT`w;J*XHW02f6S1h7!UDu4lsl?{qDv^1sjZ zg?%Hv(?NHS{>EPvupe+Z6{q zr5kQD>Y;0JV@wCv=+?asCO6%FqTc;&x7Y#b?zmOF4xi0#FHZ$&akHa-GtPIgza*h^e-$Qy6xn`S-0EHQ0V&HCR2C0-)#qF@B?nP z5C|W+CC&x>iQB!`fqUxqd@cZ^Zl@w49CJHCXZjOv9)E!Nx;y@fu$$e}>8xOjd&@j{ z@pFIu8Fc>cGwDqSyKk!jZoB)w7r=$PbLjm=xQF}<%NX~$beA#KJuw?xygTPSEEC+H z(3?Kw?spA<6!$e$v8TGPjD#@FJ(lvw!|tC?hGn|DR)nw_?#Tnt9dS>l$C~M$$OM<= zE}*`5wtEdtpgi|vdOZ2=8mfKIxF4o-^g?$TUGOh)|Ae;IQui5?LCW1f`2m0m_p|rl ztlB++uAbDmH&O<8-F=+`ssQPAH{SpmaBtZU^2oi+3%JMbia!u< z&^_iW2uIw@=U`+{+z0jGp1PaT5Mk7P2OE~-?)h%eO}P6|GsoBC2&Md+J)FLRiND8! zFTn+P{OAX=)#Ixw00KQ!@z4c(Jfv-YyT=l07KeEJN$X&!M=NcEVIKN+ScZFeB!fhF z40gdJ(nI?JOm=&0JP%y7$HpBXF&=-^0ubxb-wNGc4=-B5_Ib>uy<@+}auZDAJuaUG zIq30kIE05h=JD{uQam>Q0FvsVquG(>kxKpR43F83usq_isTu>!^l+v;Im=^O19Z6_ z&&X$AjWz!7D0F*_Qz!>ZFv@x$Of>#W4U{Bf>M_8RjWtdH95BAg z0heOjFb`a+v4YCEG~<&susm$sI1bn8#+q@MWE$Uk1ZP>sS1A3-Ha@2O^QduuE`-O7 z0d$EY*Z4Vow0XuiDKX18`fi7_6UI-sfSfcAR{&RF^nM9}&KNs)Ls)2Zp!=;w#*;Y! z6dP-o!`XS`7D~uVj6ZAypw##^rM4H0!BqH_8J}*&$SRGF=^$0c=cdB)itz(F|GR4B z(d4Qz22vBD)_B<+!a8HrT#)O=lYR))V7$K;T%)n{B1~=?cbx&C$v8;A?~d^&+RB@a zzL&wZ7H8H=ederT*d4nVgtmAYa* z#ub$1JvOeRB74wSodq&v{Ff4tVWSJJ!Xw7#?LbD2zbye7GY$rWJTuPt5GLcsS)U=o zgz)wL1FjECxZ3{Oop+-T3>6edzi!^PeM;B%C`2>JyQ*jaCF{TO~bg`y`kHGCQ z)zdAZIMWp>KN3xUQ}vQ$`k3m?WYhQ%xC5p&R2UsJwOas4H3>gLgf!E&BLEyWEv4lm z-Q+h1!VFV4HLJ2rW}5xkrbv2bM@{R?fjefBRe(Egx=F|Pd8P;}etW(tz74KVnC!;j z?4+rK_Qq4D?-Ag|rnfFach;nN9i+tMP0M|$=`vYfFtt%R zP-b#{9ps|PrvpB#O!@RBUolOmEM1SQ$;06pXoVjhdnZVKuhdn(aAVa1m zI&FDk(wus4Fno9Eu_gjVZuN9fE~g7%@&Z&=7PcZa(-jOhU~3hu}iZZ%%`=9cDE>)^PLSPLK%mOgiq0H0MqS zJj%TNZMcp$zvhZT#F+m$1QKhmS_LnA%;l8!?lT{t7q{R11+{?V&6Tu?Jk^lhG%t1Rq4x8gB6-zf?q)#!!Jd2vCS!UjHM94Ph`orX? z`EQye$IL>yUXp8erOS_b=5znGTFg<@DLrAHT8ap#&1WwIP+)!`62dd)cG{T>&FiVz zc-FkS9$wCwH!pzhy!n7DEKAJMTj8_JyqT_$Tr?;6!19v$msfzhY<}(<0F`DZ7jRYP znUon{G0*xMR5?V6OWdff~)<(JiQ(<^@!* zG?|su-ne5fKL(%8W_c2DE#?;}F>5ui3oJ?42G z5cZno^s)DuZFGIR-<(8y@qoGMDtr!_UFe$_GXHWEWZ0Zb_2!8AIZv28F)z~sH)dYT zg|lbotCW|In@xf6IbnW&D*(QhvKImHvsCDz^S4C!!e@YmM<*3qEh*(7ftKV0Aia*_gZEqz%t&F=?Yzf<>w(-CR(C?fH29DumL8?77MjS z4p|c1AWX3oE5M~%9#B@2X8H3^;0{}UrfTAdC9504OiMdeU|E*Zli;!~zik3JYFSFD zbdIGf3xHh9jNRb!ENvfPEcuoQ%HU5}(go0+wyYR{uE4T1AHp-1X@5glXkk;1Qe?@X zE$PMJTnbUkq}=Vx!-1$@0xCkjs`yUWia`SycqC!m^)MsY;8H zD$}c$ACdsSW?3`?uB$CSQX8$t^67if)mq%CWm0cRpf11-O9TtT21~|5kVebuuVH!9 zQbwQDZA;J+_`GA;K_5yp)?knp%h&9e+2v046-XNnE6}_V|i-@NEGs|QK$hc+XJLooBUx@?RVm%iE;%EKn9*Dp7E*o9~ ztS{4x+iqQ~9M@3H1mwG?Mv@D1R5t;62H?X$i{TX}+&MSDl0^~gR5ldRua08h5A zz5#N;>URk66f0vf;Hg%JZQ#>2B8lvNg5U-JN0 zWPP<7fMV;PwB(()ZZttxV!gE-!cyz+_rP7S`nX{bWmdHa+-0i|6&&T(Jh}x{VGaHU zzpv7Ikjl;~>(nNAxn`aE61ZxsFP${iSe-gCmRhTXQolOuxoHsIux1w_dV@8NW`Cn~ zFXenUtv|m3pH0@xUU<1J#?oRH(DjQ}>om&X?^?Hf0o{G;q;DZ?w|;dA z@DA$)EnN?+{PW;CtsT^N>$ZyMv+1!$*#p;W^{0}%&zk=)g#FgDbhQ82dTRm5pw*9x zpCPNe5V~P&<2g7Ru`ZxecGRl$fo{w?nUd#cR#7X+xK&E$ToYC`-74E+(@;_2XM2Mh zt^T$=Dxm^w>vm!gTWuR@zYVg5o&XnYo8ZFdc3bO4077h!sfiwHtEaY4n9Z3A{%~8$ zWC$Z{e=Ubeq;2*908zH12AD+KX3?h+V_VS&F4pE?gzG)FH|bnE&UT%e9s6x_-+?aP z_WUX65^R^g0zA>ShR&UmY^k(qq}nbeVt{G3Li)-N+hQ3IrrX3G5FWK1qxo>m#w>%E z<2G-4n>n^DI#)YoQ%=D0wC&^TAO$vi$_vieKBi@~&=$-BS7iI1c8g-0YZSP%w&Ro+ zoU^4-d*i%q5*NY}oBs(2OKrbw1Xph35TL^5o(!LrwwyVztg@|r3*?IJ6-R`&#Gbh!7_R!E1) zqqe`+Bl?&vneu{Xwg`FzR`m`vF2w;+(O=l($GHhZcmaq;&I>;)I#nNRa?tLNJ@ zF^E9VGAFp+=9xixa**fRyRZ!LwEqw!)N|$Y@Up{mB|Wp9oG%?K2Rym74j%NZqypfOr+frlil=h| zbVocLLIKG1G`$ShS)OIRFv<3;p&jU`=Q2vVk9odJE75V!rTyS?JZI6>s9euiQZR@- z&utdy@;z_S5_`sT2EC&~&nfG`6?uL^Ur(`T5N&nmJzo+7SK=9W5W35rYBS*Fo<0`n zu6e%c46fQUn*!bN3}k|9@I3hw;CDT1>B!)o=M{QpZJv=+p}X(-`D?Ij_uN8{uG{kf zt>rzQ$0y-E^m=x4A?))U{0q2#&!->5%dlr7C2b>~E9m__@jOS5=c(t1*I?=E)%!i1 zZT6aV4qmo+eN6?xHm}=s88OJKhgRrduluyHgn11v0wCOL7mYf?>mt3>7_V=sX&CEu zDF30~jq0^pEWGp&&+UY{5cJ=H5G4kl?{Uk}3Mu-98sxK8)-nFNyIHMAbO zBVI{V9AtVm(I=eaRk0rMT(3bRNS;^n2!#1w14%GB<@F`){inS;)qodx9iIxHXT0`O zkEPh_&{_b_dhMD7z&WqX7Xdi$l}azX(#uMTb(L4kW9Y7U8HND3>Sae|^>r`dGMLnR z{Y5+F4KEEn`3A4o=odA5=yr_i-{rPCSoJufD` z={B#ab76Ad>#JLc-tKjj3aSn-mm?5%dA-sBz(cR4)Q9f&>Y;{dkC&woq}OZD2IvO7 zPW*+ikGve{$mg+_A`g~>UK#H~H{^AVe(jjoLdq_ld42sJ$hg;6YLE%9{iQJR_3n=c z@$(+@0^Hv_p%1zM@Afw_maX2QeGmqEm(g+`?A=V2&313+FTsU)#|c0}z5k(Kw8Oif z`WoThWk2H=MR*_502k?#bn|zQsWva>Esz0DR;QawT$b;TH48Wy(f7bw#;r%N$Y>s%33czK0e^151QSWZN z#}|%y`+ow!3GYL+qK-lKG4RPBA4>i!yURy#b&bh5aGJ_huPrjy?>)a zf*aoRH^8L9J8LpXqxV(%Db3z*?t*2DcOm_)db@WV-3aOM_S_DW9&dX(f$Q~td>eWqJ|YL5aC;!{E=aG^db zZ#awenf)5LD4!qNKz8{I(k{B&C;cpZ?)BNd59&9rLlHqs#)I zlQdz^_!KULvqB%w0{|5HeAotIsn1zjX)pL}Nd+nM8Kzlv(PxPlaF={uIuBhs3t&>?({LWnYJKKXzo5=1Wgbkf`}AIdNxjdCThQI| z!GFhsY3TFDFA(1G`KlA7+2>0db&F3KO}u+PMRY*Y<}>|UaQA)UGN5bsIZxGRhfnSh zxGo?2KQYdSJ~!#_db)j1%|N^!p9t#0_4@oPhsmJNB0jhwpR|Y24g2i)0%IBRG5w#n z_W+9`YyO4l2|eWCx^7f4?1~u>(;9Y(f|8ufFibN8s3-yg<_#$3gknTdOc*hOC}zbh zMob_^%yAWS;{K{CN*16Q%uWZ%&e){2rZ{EImn!XomDo z6M!`7;GO_8rRV97#%D>(I{}+7?PdU2ARS0$*9>V3>gPgf{w5e(EL}}^6HBCXDUe?& z{p)9dRnmN_=&qKgl4oBd{bK;YI_b{IP+u>dKxM*Q=~=42ZjtVv0V{dZDqTqR z>+Mp@hv?Z3X;<1n`=ou=0qmD%?gc22E=>hEAoa9@!eQxDKiE7X9nu=uN$Id0sGpLC zyTJ5$XkanWaM;}T*`NQ-xsmE1- z=hFFJF!n~8vjGZkrP3zA-bo+YfcIXyfQqo6q}$`6ULkGp3wX)0<&bP38~*^;xpHR7;;Nd-*-OlqrkOLko*#-_b?K z5?NL$M3%~iJ^)xIyR3)$YMGF-vo$jN^$=Mr`+gU&Y?-Dbc&x3WvW z1A8YcYXdA*YE*xo01w9qS(pX>me=j10o06Q;Nc|!7n zyodsqV)+U>c1z@~c7u0WKAck7Qu#spO7@!EKuO_s`2nhj+>n=a19nrsf*QIl?-m5? zj$9K6a94i49YpTQV`+*X%8z@ay+`tebh-Xmp4$T8iF_Ul;xqZJEokq#{Pef5QZ8Ri z=fey6m4Og>EnlSo_D0^h5&H60eyN+nR3`M zin5PTAFoJKfHy($!wK|dqT+8GV3QQmao|l>44MSVX^P$NKulFQdjL#V+~^FOGZarL z%SuzsSHb3NMI8M-!W_jj@=J3SqlTgNd5RaOAepW>MRk-6MIIes3l&3Y%oZu6^m$OG z;(Ru+#fnP{fh|+~6$`Ljahv**rRW}tPOMNw(4JnY2>J;vtWyj=1>$i^mX5>G2C?3(8->Fzn+jp1Z zq8L_oD|S(UwMQ|JZsYeW`q9T91&YuuARbV7-vM?|(Rmvb4k=z`13Ri%P!4cRalAcR zKd!jc6ud%(oHF(kibHg#aau8Y4jMY6uv-D_tl|t6Y>E_vdjp(P%v=b`62+T3XyLNr z%{8c(Dq>g=uPEBnE$~%EF8P96iccDJ>9!(j62KjWPdaSgRlFuYe@{_Cd*!~uQvsV# z73Fh4{8!=c2jVNmmYp#DT5&lTV3hKr1Jp+=2UBvFqTEL5;~3?dFer>y?xE40psc3^ zHc>g7zGR-H6fFU9x>CIY*bL=)>Qb7rAvx-q%J%e$#Vq9usx{A5YUmPjp7IiThIHj^ z1&H&N2XcTdP=-+$x=1-?6F{a?M49Pgfw)qc zO)1SP<@=6MSgl;~6j-*h0j-I3%8n9X>y>vXS>B-B{1{-P(mV^2Im&_OU?o>6xe1$F zloMJ3|18=Kx9Hlh7ls|6 zcu0AQDhr2|4=4yeqI^mI`nXaz8vQI(UZldr31!4|@J=ed=rlZ~wA%yqv&v;ut1MDV z*MN6USuz97o>w*@pLIbQNL!{@`Pdv?Dp5WrPkUMU7XyV-<@c12T~U_)3S-xmO=)g# zCA+qpUr>_$M!C=j;H|RxQnc_+*@nIdd9O4d4J#j%?P#n= ztDNXHQ&h!tfQ?b5#Q}^}iD+CVt5~X-O;NSz2h&qk&8|UxrYgPLFRVOLp zT%fu+61)u6zWdNysw$-5a+&JN3y7>%Js1qIMwOle-dfd?mcX)AITxU?PF2ST#&T2} z{{S&pWlL9RyH&qufwxDM`VLn1s_c~@?o&M|M?(ixT8d^4suC%&J)~Oj1d@kU@=Fjo zrgEnn{^P21i+~lXI%xn-s2b9rHJ?@;rxfFi>V`RpXH`EA1hGiPodb43l}F*oMb)h1 zFm_3GnsS6<)dwmfTvNUH7JAoJ9XkNKq541>!%dZJ6gqKRWlzO`JE|i^P`|5c?EuMp zs{AdGe5e{gH%5SX|U&s8?GtIAcM=+5SaYCtcDyjDrc zvAj{O)q?j{b$|~2cdBxl#*eDQlu&(AooohPg=&Eruw=Dh1GQD40d-c)raUBgdPo7V*|RqfLX*mU(QElkf;*FOM}S?ZB=GdWvrKM^+P zsQ)+#;#_t8e(2JCb^jblE>PE{8?g+vWH&?>syk387O6`{!q^h^7%N~))#Y^1E>mxy zvwykz4egvPb?0|5wn}}yAtYC;zsm%+M*U|)w6|70p2CW3b^BxBZBYBsz1l`~&we0o zQg6-&*sM-!0Fa|@_76nz)Xr3n-m3o14a99~Wd|tas~6S>al5(&Ma_HFf3-#H`_w}z zIp44L*^7n>)PGXN^MJa;bzq0pS5HIoh`Kwi!J}%86?n(gy$t}z)z%H5eo{S*Y8R)} zS<~?jr`7&+(4J8bItt!dwIBJV^Xi)y04}J9($~=!)mQF7@{-!&GDM2i)4u^$s_sd3 zhAZlRLjkU;{oh03n))!EE7#Tcsb*ZJE~BLBmUP>XUy;IMP zfwA{$Gs-PKsAK8Ns*mb#k|2^Ca&Hp!Mun8qcR`~=>Z;I*l#mSzz?&Fi{U?Z%LL?)h zFgc_kA6BM>uvF+t3#pp`(=$U_(AhXEBr+A)?2se<(Zalt_cXWZAuZy8Wrmy|2ZhBU zHe(^OB&7XDfTbaKsJmGqztFy25%QF-+OtCvUjkbf;!bDB`jCr}u(Bbfi5zX4#2ge&jV3G;}L8 zWmF1WsTrROku{pdmH=xtZ;u0HYl0%dTc`1+^F2pnw+5Zh)r=tTy+y-q1eT{cLVjYa z<_vvFxm}|afVV@l=M)rnYDR{ExJ&aJh2Xn2EosZ_(+vF{3i~zn(m*WGv>1jrI-rU1 zM?(iS&-0*uM3YRnq(?QM96>y$Y1kIvxaP;Z&@0p&z6{tE7%k+EWphW1#A=J5uIT-UVx1=tPEiATV0YS#S? zP^MWZg3VhRdkxg@YILpu_cZHhhxBA3f2`qy_d=GvhqKznTYB z_IaimF$TQnnjw);c&V|V!0eTVT@CPBV_py7jb;IzYHu|Gl&gKv45Q@rqh{1M=;tSm zcYW|GG%qO1OAbBxCpwW5DyQ1-n9xD9K^z+zN=|iL=z6+F8XuZP>EqPUVFO`hTIfxx z!lj0eEd_6SXopl_GecJt!^*7Cb`*xr4(-?#yg8w^6m+MDPNk~Y{Lt^7Kx9GaV{-5r zp_i$?v^4am`;c4~YE8F0t3z$4fwv~K&K+1;8#>GpAUkvcl?FD4o_Pr?Iic6-+A246 z?Kt#hOKAQZ5VwVnqd*&6-|PhMQ0PoL zQwu{6JOg$jRJ98tMWJoU{hkYL9*BOHgs!60m9a>hrae??G~* zcKs~qEzFCMTKBNq6i#E3v)bq3x&Y{_@S{cRA+q6R-fVV^Y zY7IKEQyVY`3cIv<^IG|0UO>$7Hrv&_vcF%5r zquM?6N%%4CqdpKhuFc2;DAaau4sb%dZUjV%v=7oDa!#A_D~RW{yQxfZL2EGwow%qS z?+TkG+TqmQ%i7&^byKPhr%&FmXzS4z1y{9Wqo8*~JFho*H?^D_zq&j4;|UsRxj zx3y!bpZByK*Fo~WwjUMM9%y^e?f66OiS581X@9wcW}j+L(w9g7YWq{Q{+aeLWd+Z* za~1)VYp2r33a_*U?vQ+~^%)27M!S_J?ya^hrBLs*H+BO{4!caD(WtP0i(qAR*uivQ zDPgvB=Qb|v9Cc}YSm$~G6T-ad-hN_O4rSg`!Y}Y)`bXE^O~V&|4ptunHhItZ6rhYzeDJyErfGK^L^JGt8R~sa;`B#Q?j* zvgotE17RP2g~GwGe`%E;4~s8ECkn$R4g)wFmem8iqOhwoft?Fe>0$GH*n={(cOk3| z4c5glJ6e&K!j3G4NO4%5Y=DxmQB=XY9M&xW9V`v=4F+~4>@9_2SHpHv&~`1X!C>fJ z4|Dqtyc=P?Mndmqm}@S0WnuGkA$coo^i5#5!ZCi-!CAUHv<7GE z&VC0lN9RZhW4g|gJ{p;?+eBsh1-gHo0Wx$eh67~kn1<-!V%_(>ps+;e{|pT+)m6*_ zF-zw{WzZG6^Y$RF)Y&uywn}#>29j%a`XR8Ht-D4WVV$l%?YQ+ib1AyCN!Oi9JDYW> z6ny3ADq6x=u5Ko+v#q+}{;;x5H&YH?zOG|a5Vz~R$VcwdJ>3f4Zrx5wsP^dI)2DlT zb@yHY6zI&z>mShdqA2K~?iPJhb4YiHoa#}Xkg|efy0Z=d$927CbA2!UPDeYX}O#kw>)UP^TLJVCsyyFfWmsqRz-h*xy)lR><$>vJFA zhOT`P+PkS6dl9@cT|Awew{#a~p}o60U-ISmbR`kM?(14oJpVwqnl{iwUA_~VeWF`K z+2B*1RSZP_)lH*)^h_7h9N2T6lrBqO>R#Uh@s)1y2Q>6r=a>eCH@Y(t5Z~&|pM&^8 zcW)g`f7B(e2Kc12qm!#b7rg}{$=v8xP)Omf1VVBQ=dlFXSZhzD^z7faRb zN!*5OAWr7ywSbi=oYlXuGLbq6@YjqC}Lqg>8GfI{v71w1FXoV`##$*JjY2TpPO$D=Q& zxx+(1EaE14K;#^^APwL=HEtTmO6ZL)bLXx>uas+^hGwsD^BjR) z{$56H^7dEyV1E> z7~Y!>%oE{lUZA~`;pdWooeG~T2RI#mk508S;k#2|^KAIf`#~%UzeV4ho(pgO2Z-mx zTki(A5I(#uh!?|$uLgE0JhwG^RvhkA0-Gh_S1F{r96oX(L`uU$2ZMMeygr>kW#L5` z0Jp-&WkBIU_{2I;e;EFb?vI{@ho^z~JiH$rrfw=s>-DXun!Q1<>ke#-e)K-@^7NW{Xm+dKiSGEe z=`ALKxI=%OKKj|I-$@tf`}JC?^A+gR$ORqHe`J6i)bF9h_PBl&{onW$>VKl(>xBL( zU4op{FOmUV(l4WfuUNl(C$JLz`2N5y>!)8pCrb7C4}sm(?{0+l%JjXQfZfv9KLg3z z`cZZexuXwS2b+)dD=AritY1ow^NHS{Y64I7zfx@TQXff`(pUPwBS3tupOppT8~vCm z(0i{RItQjd=tHHzKI)HCJn>2Y>^n$~G7P1|X0%~qEKH{uTzm}Y3&6(XqyAw{Fp4dKnfn`x+f6U14Dos^HwHe90U zevYAeD2VBX7Xq|C-_ZOiBo`PwsOpztaH0}TreO?C|6;@Wa{x;W67rZ!4ZHH7zRECW z2#Bi<*CilsFqn~p*l3tQ+2LQ-Ij2sJQ_dJTv z+!MN@e_lOv(V-qcehYd}Ep;_uAzk1HUtEA++1L>LDnC-OSCh~LzhV7<#BcaD`c@+% zk#zg?q2(IAe=R66W9q?XT(lOHgzNvH#lf2?SRMA~GD!Y&zaySgnCFer{5WRweaKE^ z8pMM&h54x~^wXGbbTu-Yv7x&80_L0y3X7N-F%Vh8=qYnp#RzH3tz}LqVP!qj!2)e< zVzTA|%VXwwKs}#nN>|xCnd{#I>}6ijrY>MabZ{PKPSyo>jQQ3M?VV!SDA+v9q#Xe7 zJfjxDbTKpOBy5&4+bJQt&iIfkDr2@iLnrPt1yt*N!i?w*;xlF+1r0Bl(B1&AnO;Iz z`M|uRo>efrsJkhGLBl{CCy0~)Ocd;)!s!%&`&?kDf@-AH2qvVU_0@t2J;2Kr$my)xAee9xAV+YE_Gq49i5?310(ZJ+ z*(q>32(U-+2Nf#|1YH`y${|4s4c1Y?xf#F;1;Z)+IVFIfz^WBkE=3C$1YN0iTrAk3 zhGeOrrwUfC32wvzlnHtzK;e#HG!4&vK`#wN9tr9nhx${&OCMn6g3feCz7iOy!thq$ z&>xZ?1evoTQXyF70n;hM*53jfCyb!eZKCks{g9j@oM8j?RN;MceKUn;XwA+M{_qII zbm4Rgn=^z*UW1q^Y{sDT%Y?tU(YOfhXnm~~eoMGTyqnQ$Zr$veX9wrJ?Su+D23dn7#E6e3TBe+>aB7xpd( zcqLq#0`OMIQJvz0Fq%rG6~Yx=U^+!483w&^qB0t@iK5IGFg8V$qy{ln^p1)bGey79 zW%(RYo7qrE7d7z&$Pn33n4T%>u^uAJM5(`{FDpdcX&M*NhD=z=7X5ty#0{eSkHB(7 z%ijXzi57i=dcJ6j71Vc%4s8P1Big2fl>$*YRpSnc`u&Cu9u>`^q2FJ6cG6#S=wfu)K& zy1>dzu_OZubHpxf0n)|YA3-le+<-#4O!1%2P*^7J8$#nE{+m47YSh?)Ws9??fww_y zPi52`am02I^Tc74r00t#P6TnM_#q__d&C~^0Sd%n^FTZ#);5IcqvAx$#0rsJLgAD+ zo^Ha6#O%Kixgef!2*hG>SGs^N6<5%NUlW&609q#2v_J>%h&!$Yc3+&x7hv#5K){}g zn`{TKTpX?ecqMK|Upc%Lw-KP%AH)beF)rdW->51%=a{yD=9VGy% z?0IrzGg+%Yp)iNF^@2h=>w6tmGT0MzshG*WpuA-nyX$*Mu3+a6g5+wp`$`bA*%Rdu z*}$HqBrk_`oCT3Qwr5YYm(TX_hR9AO4jA8y#=sBqECaCQABH>ODoW zy#flUl4IsDJyWu5F%;%Vnym&eUDEO>co~v?^sjIEGx?Vzw)vXdf- zY)K)-%Nr!~XdC88MC3;EBx!%6p?t}2^?~h_bW}oek7Vs#*esBY8UfxRiRU};j!LrV zF0)XQ`zydH$udhcR3s@n2FVMOUVA|-mNcQ(OC_)97`!G~F#{belW0UB-jQsh$mPDI zUKdC{lEhPq;HgA259;NTnXSQlB@s)2y_H<20Q7_8UPE9Nk`;8LJ$*2i40&F=0hyO%QW9J9(v2nZ|4JBVIC0#u-g2{ zaI~In-ifAogZaawz;eu2-h^bH`JUDQ`R14B^JE_kL8*E33g}%kzbFJKGv7w@e#d-n zAq|Lm;4WZ~%#Tih!c+6K8NkZTBUZxnEAwfzeczfNpxoqxd2v%<73Su2n5I|^F@xkd zi=$LOooMl9F?drf9wvjBYN1F2ai)c5Cf;F=MdU?*bc+FXp_gId*aVW97OS?QpUW(6 zyZ~{9#U)C2S6jS}1uxs8A{}6Z#kSu7axCQiAem>eem}5$ixrfA?6kN=Iq4n?r#0wV zfknOfX!ejr=(oU*TGXYR)Iy81w*XFAJopB@B8yW2Xy}4PqyE5(EqdMnuhb%q?j)~S zB+US*iXyWc$L~H`~$YQ-66rNgG?*%BgSV%XwuPkE=cOBRT%bRbYP;5DwKD{lqWE!BoYnH1z0^G99qDsMC%Zfz+Pb~c?x_D-} zwhtP5VQKvkHeXx*P6_8b%hHViA1z0?03=)8B`23+HRC)aCs;icLvpeeI|){%S-qyd z%&>YYLMLWfNyr7wwOSJlz4=z`XkZJiinanQwz@-A*kxAYLiB8f)lNraxIVe^1hG6gk(SZZ zO|Vv208F;FCjU9j`gJRSH0$+L@0)G?kRrf&*6!V*u)um`Ghmt4O}oSNQtNjM(O#DI z#5^dhvffA0Q?_-3r@%H?KkWjr+1jZbl3T31?E){~I&VL0?zEmrTXc_gLT6z6t((#m z7g+hNl_9N2D~%z@C` zXS3-vhzD%4$#osEkyDO;+-74bjGeUk@jb9JHh)pQ@4U@qH%MNx8Oeco*=8zD)m0ns z-$1-+lQ|yPZJWAo0QYR7XF~m<&E0mup4v1x0)^)`BN_v|v?)6P@W!TjDfB+rY@|w0 zg-vcGS{QBnk$m!4+fkmtCfZt&cbj6{u{{)0ZADX{kY;$YQeLh_cahRUdSZPiKOJ+OU7lk?a%dp*>j*_O~X@C)0UbZ)-3&5DD_ zJKOTZz&_cQSOJW(J3*hEjj;+J@nfVbH$wG*%{b{#AM zw%G}8qlF!Izk7hU$4(gtoBQofrlKzg?RrE5J7PDrIe3M3uJeGMvXh>I&9inFJ<;BI zyH-@+FSfh77gkE`+71Hmnw^TiXSr#o%mVR_otmzC@7t~H2jU~UZtZ|Qwd?-^>g9H? z`Kx+6_rIX`)~?4!fDd-3xM-rH6 zpH&W$yl%N$7VvR}^v?6W`85xfKTXJ|SO+ba`c^O*gD!{D8?f7SvG zow09AY0^3SlTXn4Mf(;vAadD$jW>8#?eppC;D-H!9cbv5eV50;?%BIH2KLZCj(qYH z`yeVeJhQ(;#gv!!=R+X!#$MkY*n9iUdm#DA{^Dd1M>`y&)jHOpTQORg;Lwq#YO;gC z1|ZczPgRaIhZ#cwW;>Jxqc8IuJnaE89FBEE3z-g&5@B4e-FJ24$aO06gyn*1>zNlo|j?sxN+X$-9b_obXC%fvYCfRK-)l<8B49D~Do^%ubM+L=??#B!8?6F}GBt6CC|Gj>o z;K5tC3D$h-x#G9SX!4D|ulwTJU(*#gOnjNHTn}mr6Ltnl5L-+%lDzaEzp zK=;&9eZVJ~qyB_Yp`)h{+3fXKU&sWkr=T>rQ7g11T}ijpGJ65&N)cTotN!+cWJtel zc-9{Cg-n<`Rd2XZsty|(_rq_*I=W%)^JyPi>Dz)nEb4!R659c7HIN*bL-BO{yJWO7 zsQ+llCyM4kGU@x3_#Iq$8i}7n0!{$Dw5oK-%mU7=9KjGqGR98>HIy^G96s@q&*rU{{5!Yn(Bo?T{UFNDZdrimwb z*O)Gl41@pO_~@!Almz(y2XKa5)RSv4Z@c zfsGTWRs)+T$b1EnNrDkE08<2coFpCGp9eTnD?I&`dW{swa0)jz<*x9{`t`}3;Dkgq@F0F)anjz%{crcwCo z*g6rtZWR6&zfMtOq1AkcJ!D#p*bSMM*M0`2RRsCp)~>9ZvL6!HL( z{U{CVYem(e{=47ed7zB`?)z{ySY+{MnWK$tY&e2n; zBxd(9VA$SE0H!eeV<0k}iKI!N$!wfqa=4OK8$fS)yA8~^|B9W$51nKw9iuHq(|u*z zMwFbn3=BnA+aD)q+i_TZ=yaJvFW7xQ&7ism{hu^lM=V*kZ& zOkO9@6JrkHdE}K&_#MaGjl{T2V6=3_JCX5p12&17k_B6n8Q;zTQ@71CkB8S%!pGkmc|r!1enP@`2)mR%&VR-Hk(;Mn{f_vk6giArfEK` z%wzh}_Dg5lc7^18rq^#EE?~UrnmU77a2^T^8Gn8SGsTl&Gn45;@xWqcR##w47~489 zy_AW53x#D&I-Lf~nT2b?%VH9XL0rKorvY2ZI4=TkHRD}^7S=FbpTP85<`G;LKgw+z z(-GyoB@Sf8CCVR^8_DgeSn}NwUFcpb(lQUv!`G$Yc`7sDUkFWOhKHh&R3`5r%uZ)~ zQvhZ#f8@YS8pFteEn$i%EnLeKc!0Qt30e;$c}({=0ENuTN3c@DG^5RMlkuhRTb?sr zRzcxCBOe6yDT3eVTbnh4O_ZGE3I;uf!cIZzLWmp{G^FI@te{aJs9zDBD1^PXKHcy43+OU+7B}^BqFl zW-z@=xba^YJ0UCzN9&h_i59R@COo16cp{9x0^TQK?w`OWi7qt*SSk`ze!Wa|${kjg zi$P< zv70{>GQ|_r8IQ8_;0ESW{J;jgOwFx9i>ex#gphDTqT}H zAF{0$Z=%yC~Z4=wk2F^!518lq4i~@um;?CrY zcZx-CVS1O?;}Njk;`~1$xkr2_9AK}wgi_mm;%jt@9}st34E2Nf?{0v2NNo2z-r=zL zu_Jg##GRHv;i$OoDu83+)UzNS7hj_@uTU&(3*rg!4oZzqio5&)a7w(4&b-s&KEFWZ zjCkC05YLK-(?xKRc<^3e=fu4t(B66R%}hvM5YI0JxF~*B2Hquc&qKh9#m%T}Um`A| z+u_UN+5G`Z#jP&D<`wbrL||9NOaDge*Tm_RC0rMuA=h_998GEHO|kbvfHLuI3IcD5 zzYPZVOgyaM zBHQu?tW07>-5@fVbvX)=DeU}w*qq9q+XKmI><7AbNoB{{LSZ_4jzX6itaAroX>2+L zTr=7JQvhbMXMTplY<4fD#B_l(qonU8^<2%V_w*&7KE42XcG&`4W^3Je=!)WL%`;=n2BDQHP zc;{Hh-(l=L`$GdLTwooVfOnDYmjvP^b`~wnV)pTQNS3g&I>0WoSyb&RWusgmd4)~% z0P!k&`3Lm#8arY>I(VJEcNQWy*i_2-ZnB~DsYe+b+zTz-V)wa2@)>)aobhv3M#f&S z^(jnz$#%O2>=k>7e8Fq>(|i!$uj1_{;%)(qmlV*U zH$k$G{;2bVyuwlpMb(jN&f&yu9939p!L-fw~1(I zjbyhKcxxrCW#DB?j!^6CBq#SnZ@pxl9AJZ_-x`?SDEUSOkxdfUT!76I-wPn-NUr?^ zELZZ5?rFA24pD_7PvSiedRrwPO98e?-cublU$VapBHJZ}KLYHK+;)WCPRZzTXm6Kf z;;+!#Eg3Z*3VS3!&4 zHP0iFIxWdRf9tUx#=gHLMk7vl10d)+jJ{0jP(r?^^SmFy?dnBEyx)C3f$p)CvN-R0 zEr++&@6~r#R_Q6os zG%DwI%c4#8#|Qdu$31iaOnS7U6V5;EALs@}&H+Wy@;ZLCS%HuV^P>xe=&5b--2Z|z zWMZeZf_z-qN65rq`3=8`vG$-0dDslke`V0P|DAFh&;JD0g;ol)bS+FwVp7x4muZZ5 z5Cl@0;CCRVF$YILa5hu72o~lrp^bsfW#%0ONN18L;+)TTQq4Dm`IC<2MNBcZwSjpS z4v~$_iFM#@VwU~|-ezV@1zN~qx>Bif3-jYjfIKFGvYD;SWQ;Hp#iiY#_ksDX6-<9* z&QW#c6JwzBzk+G_J$T82Tslxk2{M<%=4ioIYhYsqtE7;eBq-<)Y_Xuk9$=+FMXPC} zVBT?RNbt@H3PpmqPI$4qf}RuR z!wjKl6%^(PTTO%Mbm7p4z!nR;*M-OmVQ;!{*e;w+4(qZol=@OC{O2mHlnJvb!nh?Y zp-a-c!pxSCd?qZ2g2;2>;B2UW5FW+~5~qnK_lL+VQKu+ib416#1DGp%bBfv%WzmPG z%S59n7+fW4a19Nu6OE!$%0|(zB7j`c$@37&6SeR`d&Qy-iQ=X#P z+2XfW0CU85N+GgJyoTzt>&4w!7~3Rf_CY;Iyod6pt>WByP|p|3(;>M_?3@m4uXx}> z@b-&WZH3+;aZXbZkBF^;0gj24PXJDe|G5N(GvZ%ZNS+lZ;}V0M58F75?&_OmV-xVs zNbs*M@FruZ2>FZ13ig^MQi-QU#%MgZdQA1QHq)s>?R?w;GOi>3#;@BC&gg0fhkAJK zl;{WfE?dZTbX#6%W^CuLvsuuY&g69!nXze1on^4Lh;i8eKez+&bwP`v=;B?$q-1!i z|DAK##ys#qC-ySsyJ7nv!*~K5W7Za%-prvdxy^4A$R+qj8}J*M^biXDmruYuZD#s> z2huh{{90i7f|M*^+XZ_mk=h|Ra2U3B3f|M5&o04A4~Xm*yd_6-Ou*QJcU;iJfVVCb z+?WEeKz#8>h-?)%kpOHLZ=gi*f%xTMfH&efyP)t^ENlqLclbM0Sa~mAGXca8;-edB zso=kl1niUeGS#D#*{GYqMzOc3LOYs$#Xv8G?X?-E$FMiIfjE}k+883^*q<-K*myQ6 z0}V}J?@;PAk!?b|dJ;P!6nc}{Z2F*R3fqL@z^QCss@qLt8&jf@%DR%Hna;Y81U7^H zX#ynESho%knaQ4|&k1L-U3$RSY__5tygBT1e-P)gS8hOI0qfHklFL~0WMFGq>1K3k z2dl0N$=&S03bc2C?TWq1uQR976x?>bHru#9#0h^JvaFucT3D`9znTk@km_hMq z=swdt+th2p7>Yh@j@5_kw{m2o{JZtuNHL}KPa1yNV=0Ww50lqb`gH(B^{X#zsb5g# zHpKl5=$h*k8ip>f4@vD@@=9TO7eLW*bP>V5rOy;1Y*gU(SyTtVeLG|4iW5pcf}40} z0DhAm4u;l{?D2RW*`D0uC?+u%$atnD6=tR~?`U<;W;&h$aUQdS3SA4Bsrx}(#5DaG zMi(>lDPmv3h^XYUl$jC-Y&mmgHjHI4&lkeV3TFIY0PC3+S0OS@T#yEAt$1EjfK%d0 zB~Unx|947YXT&?6p`o+l3z1O2ARe(B*hO(E9Uhm&`AQQ4#FBpS z{Xn{i`(be>P=21X5#sfuDN$-*M>iM^w?u&A*gXQ2Mm--wzVQHx1)7Z5gXg9zeZc)? z)IWIsb^R5{H*=<}%qh?d1K9j1T}ic=tjBZ9vE(6JHCTk-)?JX6@zd5iiE1HjLUZuj zei-em4)+RRrQ=j`iJh$NK=1sM1Z}%Fkb~>d@DDuqG{mC^z21a?;=h|5W$t8GPZlh!58f0(Db_pRv}6aRHTFFp!+-N7|+!p6fm|kzTpv4Ce<_owYfJ{Nr zNvJIsh!O1YeGt8B0jr&~G5hu>Dx%igLov|zQ&D5&HO#kf2Gx!a z+3hPscJWWg`aC}3VYwN-Bk@}=4 zEx4^?<6@&)N9hgG+UVGbn8A@z+JX9re$lZ}X8)fUPaK~3e``1<+Mw+l5mBr69ow{P z<5Kw!W^LNEHU2L#azI=9H#VV9`?e8r1LN_!u}O(-X?P;~>7!%YMhr}hPHby>`L8Gr zjEfwM;Uz1>+765y@D;IY_R+}%eL^&J1}8Az;J zhFD}m^pL28|J{34>u_wJh_>-D`lLQ_2?PIcXXuOT>hsGbxh8QKpU;FElT`gRNF*XI zwoi25{{!K+rg#2|Xzjsn8xz%6A2IwZ($#y>HZd;ZD`M3K(9}AIHA0igL5YivON=xl zRg5lYdTmZ0(Z2bVlf)$rkB>@h(~ovEmzWfZjsighhi8c38O|gkE+LAG z80ZEJVF`9oQq(|d3OL-cK0cnKbp{N_Y)o9l06f7$m1#UDL}B3@$rx`gDj^{*fm($E zEo-iC%-|@Xa5MkyZ$IiUy%gSqY?zyLOeh=_y3{s`mOIpaw!M z;>pHj$2i9zpmpk86>eK4FmPa8EClTMVKEL7hNWt_&`6&sebV3rekAGDW21&*k#V#O zu)m-R5e|xpgX4i1ht~iv00f4rh7u3$T|vXBn#SBO7Ow&d#+~;S9Gs5{HBO4p;Mkf3 zS@gh2qr~Tlsx->m=M2$FeWIgcB54B95)oavq&S+3So zaC!r}rH>%zQDuaSk4lJ+?-!MzkEv$awNpFS|8lTqVuo{ZF_E7~+uk(V(Fub{9hoQk zGmInD*fSWaqI3Gh#Ha*X4YY#d5+b7`V97L1AXTo(>dV4J=GUH4ktQ$>k2E>@*u-e# zqjBzx&sA#yQx+W+71f8P4_hSydxLsuoJoBGEj3JJ)slxu<;J4r5TDd9Axa-vja2yv z!OC7h5N~YU8!(6KMtTdryWG$yJ?*N>?LvzqGK%_AIZXPbxPj5M5I~?sjHMViI3a>x zkd<0hyN+MSrVWBt5jya{!j%HXv61tnZrz0q`v~=Igo7Q04r*Za`Uv~C5n4N%2}BIr zhW)*nFwW6mXxB$LsEsi9AIJ7W>z+cJu0lgsVX>K`aOFR3+6M?vnW=@F{%O<9OSl)b zKiahKChX)W^lc-sMhsEeV3`@y_}8ZOg+m>kTR8y^cNAK7ZQQ7Jqqahyu73(GdNyh# z9MPt|a75RZLW?$d>>wQC=_aT-xj~N73zBm?5mld zz##EyDs1i8NGR_r{HD#&MhzRaZ``ndaP}#LHsdp3Z{HN!z7FEqzRBLVz!+azBzD*-xAID#Wmg>e0+6Oce4s&!84s@hf z5|~$$y#iU>s(&7en+(&|trv>upGl8v%RC368!saB^x$N^o-!#H>>cPOQ@VS~x!|}& zM;B*>nLOB!!++({&x;F8kYkfZ`NhT$PT~R*2RJ%84|DNR_&YW?>k;f1#CiEa-!IU$ zeH9bDu3a5#F=4p5nOx;38ju59T^MA^1y(Yo%}1rcImXjy&R|Z|?Y6qLZUZ#F68a z?wq@{hm7M|;ccYuUi3Qd9{j(iH}x_7V|=HYB7D<6ex6dil1~sPbr0bG?bX4J#>kC- z9WK6oqFF$&f(w=g;k9Mnau@zl>fHk_%{$ik>K?>3y|xPno?lSlXSP|eR{$3*^Y-UF z{deXl1r4z@m5YsFMsPM+#QsbL&GKGgs?k-ok zb24dX&OHJi1%W=_JP8H{_!$>Ouu+It`3$<6~SILMxy#8v@=bD3zjwB)zQhdGYwW{do+}uzOJ>K zRJcpApsUumTrHCa1qXY3Rn4>UooiWXXKJAxwGb%v;b=)YIyqH7a&kmhEt)%4;U4ZX z@8)Lw5LX*uj4dXia)TPz>z8Ht$qQBme4clicW+D(I`8Go$)b`>b5m{5v1p9L;6|O~ zIGM^1i))yxv$JyzFW}-WGVLRNqevk^w z{=L35iZ$Pr)(yY+{H30~X#G_UG5J9Fx&I3A<4d!JQF7*Jw$P%Rv$G5Sg;OxAJ@jzC z&Q(^8M!zUhX;;m;%Yy>^JUJh4xu-9u4w5>>b4Z8vt%m3k0ylT$V;9WtAOsh41rrmsZZSsiEr2;q#X5SnDOeCdD^aW4Sq3 z`ev67yp8Ib&Xq2KXPJ2-dh&`g4DQQ$@kgj9=j-k-H?|DVMl0hhA~gn(Mu?np?SZcx z;LrTeYVhTi?xPyFg2a@rdRob z1g}a972X45<0JK!`Es6trZI3HW;{7RujkrgyuQjy&f%B{#82%2q6W*@7=OO?Xve#I z>Y&k+_Nc_oaG?9U`uKY}@u^AWc5rgympe@fPj_t6#F6|%bH`t+5nvDQ*T<8SyUIC# zKM&5`A4}gG@8jd=?eE0}1)2p1sjx8N&ge8#$mq#~2F8U8>eHvjk<`N(eS$Nr*-0aT zcbGUQOi>Q+Tq;M~e_xk%`r}rFQ@#vj8}JtS-J6|J|0=Mfm0`w;jJH zJDSeCj&$Z#+swR{(ik>LO#$jxY=4&G%^P2zA55+?HmDWp@JFUp?PWYCO>4wc>Pcs( zRBh}Le+U~RTB&!iw>y?7!W=iJSQMoalUf-g;A-)24{T~`MOJ^uy^<|u0Fq2?%cU! z?FCsmNv6Zc#kEGv!C_ls5eK-daq4#LWBjKoa^wA*Z@d4Tx4dWJ<8yK*{FBuT{@W23C9l+tz?RNu%c=W66NT=K8R?&ygd{>5Q^1sW&hrNF6WZl@m zR8%px%^&!3c=*app4d+9_@Uv?WL`jq8T>-F@m~Scj8n;+i-?I!j6&HiVlYa^L!z9B z*c?7MAu0*w#VA}_d{xSfR*P$U_)KTY>hKSriw0LEFzt<0N?>R?`(O}|Zu$i}I<+<~ za1+H{jYZem(d9qcvWs!)BKb1KgeIR$D+tLn@8+6+J|n76ut!u(RnE^(1Rap>0YM6M z!~`ni3{#--6)AqOjQIthjPgJH`19MDciV7d82C=rBWwOp=65QBU4K+x{x>t|7=TWHs@mJpI$HOCK*>1^Q(VV*($K~hI&-j7z* zc8s3B9onh6AL$hMf)GDZHD97bt;WAp+^8|0d>V{frg*CVbGU(s#I1v)+#3+E)c4ri9SN7$`fz+wgT7Q=veL>WiAnfmDI5 z?#AS%h|+lCaz_76kj1sCD)sOhfP|)=H)vH&Pi^u3*kdIX>dRE)#uC=QLHF0C#^BaDBb`OxsWH6rYsP+|jIB>dklbe@wlrB#ga+|1_591Ka~Wk@=!n zU#nAJklf%M0t*ul_^H%(`)0Pd_Kf^yjBqHTC26n0XMlETI75} zjbpl2I)h4+pF9MyR4_g;NE%*QGp;J)Q#Jz`)YDx%oAQ%Z@p^nCVoGgl7WZ2@{vYNy zyot)phAs=LwbSYUS$1RW%@>j!x=1h$8%kEbo%yZ)c^rA%tT$rR%K8>EEY!KAh>j^n z#tfraSc`sW3m^}L%*P#}RMi!bqOwZC zdnTGvzd&P6jIMu-2RNCotan#Re4g|wmxhu8+)T~Wg|3%;{CtAo*W_?+)emo!mwnxp z-W*D`Jvgr*g@->J9c2^Mid&Vrb4{ax$^Z-<>c8F!x|I3+6c`tTYi0f`71LU)w#iLL z27dxq4i*IfaHTbOQ!QrYn__iy9ts~HDp~SQxx-+yYI(kKH#qzI1^CHPP%>Tkd_IXy zCyvP>@Is#uNH#@A13giuuPH3bO)p1n{k?9ElecCGu(5(&c~QX+UyZ9PQ;EB_+xoH$|2AJf^WBUK<4b40k@cU$ zuzk()xTy?M>o^PwuANa-y+6(D=d{GRwj5Hl-n~D^m%J~)vXbH0G2VOq&*SdQk9()j z<4#u>{NTD)yS#9AhX7)KKK-h43X0%b%i}+6ejqFFO+5o)Sq&dn8K?27RprK_*TlyA zVk!J{t%c9ZYE?q2;<0u{hx?_uF|tfG+~6P|3*B)6y@MS+eM4Pb!rI`m>fod|m}z>^Ht(ry9Xlb?cw2dbJOeuSxOi=kug=u3b#v7Z1KcAU{i98@2fA zS5bVDP`j2x4piz^yOzV(v0Pk{)FOq&EMtZ{xVW{inmnT?|4%iNc1vZDkF%g!1t`E> z*2}nGd_!b%j{gaa^Tn5Mrc;PdpK9FY!GBA=D{txk$J@NbBwRpMN><+IePyPN<+JJ| zTs6x)%k=4kRE;__UV(nAPGq{pF&_8EOO3xZsnL5Jt8Wuk5dbt(!6;W$6AV<##^Zv5T?KEm|51$m2VUt17r{Mdp&Q+W?a z9*(~)GY%<#HdJnZ<5w)kT6fL2|B?j%`n>ys-@?Tyjv}ca0$*QeUxY&Z$w(P5UulCk zrV9;pz7~n&nwx!TW4l#3_y5J)yC}zXBiW+*lnPJG32H{tu!;{!cVapYpCUtinMF$S zbad>XB+AxpiPRw}Tb_lDB3;KeH|8AEMV`A4J7ipGGVG_4qZa#F^D-| zw%_$I7xgMb-QGEa`%6?3K58zcaTQKIgM8r<0k`k$Za6yLKRVby+JW%AxC6NM?bo|Y zaM>li*46dLt53k}<9(7U3SF2%R{wPh=wq@JwdWIx42sV2!7DiA8j-XE&v}+vked8E z2|SyVpvpojue*v~&q%+I=0oO&Ew=qEvrx!|LL({V_0vgQjpK`&ebfxxAA)(HkB>jk zzU{+_1=U`YjmNlAM5?q6QxCt_LxT_NWWl3i)>TdF90{yQaCbGycb`Q#AEB+(F+gYBPD7~j+euWeo z;~OG4HYIv4L$e0NVGn`-!45eEr0N9xVZOD2xEH?PKifOpIoscQaQpGcD{Q`hUG8!S zXi9h%NC_|J$V_uR`p*jgA%c$08fI875}tF>3OiUMSvDSU@?Uya?Zg;Wl73vj<*3=F z4;*RhXTw0Sum`lGW(lPF(C+O{9PeGxTZvwHv=zB7>M&o;`NB*1L z^&AQD7w4GEvVRqEfkGW#AlIoPrQ7;;OBr1%~U518?V zGY?px?3;fh?yIcqtEsUkH@3b%-$%5w-7>&<0!n4(M;nzjBho4~AeI)(yH zj5SDC>2L^n4;x4GeOk&ul(b23CN>awl4xHQ3B8q*`nv;oH}1!!o@sR}RX9Eu6H$=3V`t%tYfo|UdNhbt_&qyYP;ua68n{I*_730*e=~Wi zIJFe)GZ@^%i{tsh4ty?I4UhoMnt(Q#0<-lBLLA1iyN_<;Xv5T8&~5tU3=#@D5ry}3 z)WVj;gAi@VP_2J(RO4^NY~35x;ATkPeiHru(MZhF5!H=Tt_m_B1PlUr5j2p$E3kq1 zAg&!BR12kE4^4K~7$j^msD1kTX%68)QOK+!5$y11jppIv!h$^6(D854AE0!%ej2{% zYO{E}+gote!XvjaN6cgE<;bPFpp#yB0N8bg&|e?$+QM5WfA~WHyy^zX8@ncM8=?kY zoX3A)`X;`x^nxUQIGa#GSH^i61dU9E8L`N6R}XR4C2o44=r#=AoLHg2i_aVOAW^ei zfijc!MIV)A8o+9uQ(~7UK{Pd&@X}N&I=O z85>!!C=yYt2|-(?#Bhe$aKqul6_7fB4G>webq;L^i3`eN*@Am!jg>;)$fAdO^qmq{#Nmaqz!HHH;L_-0vZtz2%pi8GT&!sui;+?=1D9lzc(^Bf*a z2D6v+3p9k}$0u;OFk5%{{PyO2o3-%)L=<$N&HRFI2 zLId6E>FsOsR3~jm%lQR`!_`qe@i&9{buZg;47wkKjuARvVg@E)=6ZR4eh62_SET4- zK8Cx8OFv&DA04@_lb^4V-|{c`0$s;@i{MAvcP%Xpdpn3yOLJdce!d>H8QUaF5hzJk zG;8*b{(f+}3EcS6?gS}O%tG5b0}-7YDrwh1g3!!pqZp~p;s4zPKY})j3{b2hj6@oA z#Z|X|WN?G+jkAqK-Z}!npiXQ~`18r8s|?sw(cQTHk-&M~y%Lm5(rBu$hRSpIpybm{ z?1s~vz?Y*;&T6&tl1-I=Qsgc+92J#>by{oH+XbCO6WT5%L06N9!8P9S6Mo<`%tBhG2@t!!itmbF zkYyY$?!Yx}J|J;^7?v&`y#-(9vpd~%6Bh=ud(CJr z;sM$(Xr!9lxHi(mO4}#TF;+nq~aqgY%wDZm07(a$4Jrn^DR}fj9kQayDjvBdn>Hv{6DA zCJben$`z755x@}M5w1g69+a z9Dv+Fhi5;W>|jc7f4aW8n?Uz?AcX7Y%7FjT(Y0@EpWWL<=Fj2INxLWca?dHoJElA7G;%CvC=fmdfu6qwI}vX9Rlm;Or5Sa&nwo9F&+ThKYXo8;vA%B zYqPvKqRXzHsCqvYOmK1RaU*RZ46(VZ;Ps0Tgup|`t+qg_UXWKH;fNctm`o$Hvzo?Q z>90wnZqpMhdj{xJB$fdePdvJ&TdUFEN3iIcM;A#1x1uheNgUfViqiOE14hXU6up`b zs|aXQD@|OT6As&~!CD1S2STaa;6ccUS&fhuZoPu)ZN?>~IAf`xz7Uj{6c%{X8~E)mB^>47?uKhK^al}VK@2sqvXP<5LbN%^C{s@KYJ zyvRNM^};kKz}+IqY{t|R1PwCTXmL_P&hwUPLRf_z%}&;cW$PErs9GV8)ustc8-XqG zIka)5V(QEhzsMj221zl(LUg>G&W!a+3Ab^{y=*D${fZsU7)GAau)#>&-9;g)W&q2- zCuLqQt*z3`7jaxQk#;(0sfOY9*Uzci*AhNI`UvR9BUGmP_?}R>i`&mo(7$_v7JN7P zZt_oTpM3X-AMxenyZ`;a@yYsE5;$p@&sx9&Wl>f#nOb~$?s*ep4B8CE;_9RLt9HGJ zlt?kjv_D3fJn7EHOoAP^*lfbnQ&wOwWs}=|{cm#8uXUaDHL0}PN<#7q@I(%De15tG zEDuAtE~*xE@zvc$5*>gyaPX%r&L^wO)7kT9tLxCzR-a9uPA?y?uI)xOmn4~_OfBJB zt&K9U;j8AZ@23TbUdtkT`~|^)5wI`GTJ1t6kdB0{iE{wj97R%nI=1PlBh|^KeD2nP zSRcq{u1GAj7F9T~UuC3S+A!zjtPNs>ZEf7A!)wjw>nPQ;Jp_gJj!}c))eK-=%PM=V z=o|pPVb2Z!67Y zo0$wFIq;g@l%=V&u$S`}C|*&xSoLbb3)0+%ujek@eIQd&*`fUtW184I{${_fKK;C{ zEy9sCa8s&Fe2&Fmv~J+MLL%=UouCv&Y0U4XxluG=?A(cKlGG?*p&FLRExVtKPD=2$ zwB9speK>-C&;pU<1}2i6ILMFP*iY*Z`AMHm-KJXyDT%?R*l9sw#dvY?CS~7)g-PAl zCUr3}D+;n__c2hEuE|?NmIEhePENqc%{KmN4QA)jI_DB>?ImPvLs?X;4v^{1-%s$t z@=vVR0evUF8Io{eHL235*?{T0HiIN6CNM-PHAgdp<=uc!@f-8OiXkkS{j^H@?(g9> z(=Q{fq!5ZVIp^NoINzp22~SNQF225hj`To2e0h2Hlm#gimdqLU3FXJ!Gd!R+!Mi`3Dq4hR z5A=f4BP8jIF&jM=s!% z!8$swDYu;DgE6<)f&0ba))kDM;i9isSB!SrHnUyn4lL(*fW;z;q5ytl(v;GBD#djM zW4BQXgVq$gXqD3L(H7iRZ}MDKFsXZ1DNI9Yo3&v9fi(4#?HIY#9njt3k;>{aH zB2)on=lgRwgpuO!`5wy&pTg38NUY9Gwy@stZ4=0uJ?%$wg0aVV&n)>_;E$^6`r1;J zs3^@2)^Nb@ny!XVZ$HqVX5br?2$>IJhAck52fuL%ScyAiNMWw1e|de1=?YoAuLsE$&vYRGcK;0rHNjZ^ayjTF9K&jJU`ocgGB{VUO4sAbAHXv{$X5*Kv_X~euiOs>h-2V)wQ02q5`>(xj(5&-WF zM;Au}&NH}7yf}H7J>R10k|{rNX--tvIfTkQ2P`Op@);>K0N}QfOY*=QVx(E&B1ndf z^Rr{baG>A|lg*Vj42Uk`<{^3bL(v9_i^Dly|MCBj^1gczt|?!YdsvR(3VSfxd0{76K)Svdsac6vxnxAQM)eVL?o9Ksh9Os;#A1fG~ZEeSuhIwy$ckjQ$ZF* zZ{BQdKtHz~c)xX+XHz5+F;U!I+sCg_95uxlwdsuzlE;b_c*sTPsP-8=S9udA9amJS z>j^*GJ%<~bsdGdJzUpO@E$XFtl3ef@ze-i=?j_WlOwpW)=|r%xy!e&sf7%vLH} zUn*p?djsK_C$ed;6jIMFbA2@XTovJF&inTMF6vq^pG9FfZ3s@V=LnAA=iHFyj3FVI zQ(kW=l{xVyQEJVZ3|tD2T_`KY2mPiLDo!2rK1e8sk zX5F0G*F>)1-vp?z@Fx?o7esbou*p;s4fh47+aOkI(x!TQ!!amr(LH-g!9+m!$acMp z)@~P@ofuX{P7AZ20(`VLy635U0F7Z#VJv>4iG`;4!2o){b-XA^`+MjnFg`6`Hj{3hYeMZx$CJ+gd9Pr_Q^MPs?D+x}fBO zK5eKyiD)vMsqb5(#C(JBgw-~}Q0$A0W@_PA{&6X?!cJAQlK+VmNK~YT zwP?Whm@dfD9Hy?NR1S~qNU&*-Lr8RO7O5c?fdq0JLnGE$%#KF5C>YZ8Oezp6gVX}K zFiCMy>qC1w)b7>h_0XKl%HpP8%GBgB>{+iBmG`uqvL?u~K~hecvCHKCINn8lejXwK zEF5c>EM0&2aC>=&>Nj=}*T<_5*TdbTi3(m^ef+Jb(#e061Q`PpnXd-ing5_QdY0r@M%j^#Qi7;qw3(Ry`x`F{8X2i_qAsJ1#5 z#WeLc;=7d!!Ph8N_?ordIh{^+PX9RllPt(gqQ>)>)K)KZW-z>I>7;emq++VcpXd*D z31InNMNj6HM&mWhkleE(%rMpS2TrV-7-IJt&Hf8b1>sjDOFKOs;p3B(xU$U~oSzM=k2sot4_lkO%x$Sz za0WGm3n5WIe%xMGHv3_LE9+bpn{`pp6^!aqn7<*twDRo4&18~q$xk#$j-|>GSTAms zBQi}mr{9&}KkY%DEZeYQ5WP%Z`uts1q+rDANY|t2MMPo$WDnIhPWi-y#B}@hHlnRiw#l)o5O!h7eui zzAxO_K5n;tOoS6E`3mZ1+ZQJtG;0cgY?Rdz#kTcw;{`GXpdaw}`PJ+mZFt*JhN*TdlG18@ZTgR?vco8qsZZ(WgYP*xg30vhe%)(=~lCaGN@ zlf*e(k>?qCyFrmJIwh7S5|B+x%J6>{9%x&>UOqW%BCKZfSgHCI=7Z} z$U6n_LA=q^^>nA{%Et5Vo#5wgB!jG{c733z5rSF8V$0F1C(zg}(VDaU94L_2`>?T3 zzrUL36Er0Iv)yk9?{`#0()5jQ7oy6LM^fAoi`jYw!1{3u zyXnET)M$gTWn#n|k@5qg1=za23YusA$WgN%RbpBcah{hmN)TGqOSfP!boYcwVFz>Y z%DjQ1!`lT^T}fVZDs4(*0|}&U+QJ9M?Rs*TY{gat?MsxaWxH?lO~F}g^DVF19*66# z>n;aOpZKCz%g;4BW=1kV(S#n@VyGTZ^ZJRchG)C zG_tmIrCI9Qs$I8ijg2a~vJopl-W;8S=udHE)%Qrk`h4-|^GD`ng|GSL1m7C_a%f5g zub}3By>^xCs}39D>7UH(mjU)vwd*p|{cfDm$!7uPO{- z8#WR43+aocC%0Zko?sq0D;E!74*ST0E_PUlUI=AxWNS&NEE?g_(vhFF)hWRR2?a>d z(SSY^FBTcVwid+#ylhcY-AX=7f)wBDs7YFEa=^u_g*E?hCqNLLhdmI%DDt}S%&2-n z7k1NtgZPnk<>IHl#ez=u#$A7(ds3oKlftHG^1c5+xwgBo49{*p{ieF8t74Eny#4$Y08k}lC?;741xWQ)fR zHiN8LU#-89Pr)Stz=nT{r)JPsL3UKvIZP02e~Pp3P-_zv%}~Pm{BRTHz51`_!Ia*s zS(E&UB!H>$!{u-AC@%Bg7#hH&u_iAu`)Cof7bcrjgF{mcY*#r#t3PVemGrOLt3O1- z4KIMwW_pCk)Sx%!*^zn%cApYd@kxCoS1D!SupriG8wY&;02?ZA4?SeL3` zPEG$XX;Ik;VWflO*OS!}=9Ug&xfe;!YdfS(M-^nX;b(Q5#+D?p820{AYe@r}NK4qVQgO=B_5wTr;1VZwRW`ih7*W@Rv?9TfMd zJwV5JVj_AfdHL2End-)d44e>Q2;V-)s7Ub76HuMz4ts?8)<)8&JlqZb*tws?Fp{^L z*J8#+1WuZAxA15<6nF=sX9%4QtVW7VbspN$1kh3olur>6@IBlxh^84bnscbvmB4;Y$co9|bYG*JD`n38*`f;L??Su0ND$i1|r0Q;> z<6@z339C%VdJ{%on+)LTa_c%?)JxCmmOn*K3y_A%@u_$Vc~Oe>QdqER3~u1(OHs?& zKT`zlFeCy|P4|TgI{b*UGs@sGtk#DgkLs50(2I?|gbabi`nJ4IVPa{g#U;PDo6yv& z=MTc+_H7WRYSP@;Hv{zdQ=D*qp_u3T&f08seMK-1vJ_K;wt(?X5Ulo?0^pDjtQunR zNg;TB@^}Rr(aU^VyVZ=Yj%&{dCY{$#r996P_xfCQJkIAY;U7*^=?K@z@Y?A1?f$tNH68;SteGe)@={m_i@Y!V`hibF-k6 z-Yaf8aq}+1!s0sl_1%JRUb3T0IA8%A$Ia<}AB`kPE!#K#1$A)pHYoto+ijfE0O$j0 zh5@S&A@nWbE>{~peZ?kNIr{zOM*e@L$u}aPg2RRpo7J zOli}Lla|H3aY)u)5UuD{?78OfYC#@gsXU$WbOSi{Bjkp#t;_5xni{DWE-=xRl zibSkOq(>WNG`Y3MC#O!zQp`r8$PN~64@1riLV1+|?a`pO8$r9kLDft_2$i^;;-6** z!##ZixoOT2m1$-?pIJmwOGxXckHJ9Yj(?U*u-buJ#D4nX=RK3s1EZBmzHL?5JAC`J zHiB{ul(qh`t%-a}^8=QX(Z~SPnpi@v*~NstG6+N#)^*U0@&gy}bKOB!LLxy<7&z8w zLLt4GVQ0-FoK3Bc6ik0J<-WhRTm;D~KHsRUmnDb1&bWUQH@qZ@l2MIW$b zaQ=d2KMMlDRG*UhQvRazv&v`6K*gGqgu$1iL4uxLzLegXOaRUppcIXEN4C`V(6Hy& zIH*txL9hid+Yte6`-dk7OP_waeRT5^N>1PdQ%~_ZtG5bV#!MF4^bi_=lev`4wfl|NJ)`0P0r^AHH6_ zyTf68OHu8%V-4Wv$C;b*X5eG=X0XNG8OXl7(xdyuk*smiQ(O&pR%zIpPHl??BB7=w ziBWhAsstB^gay^y7W5RsNWfV{IDv!(wvz$n6)fo-o~&iLi{Rqc$==3rfKy6K zRg=Z*1pjS9&WL~R1J1uMIKLJoz;sq>L;;={Fnh&Td5NgAEEEUuaS?m8EPgaQnFD4+f7xd_c#n+Oc1%7cplZnXd)43@a0F?29 zateqF*B`9Le`fmB#t=_}oY{&J=v$Ow`IRvkSmL$g*%{PiY9a>)6CSCkVvaG#e#>cw_iUzKr!s$ZgvO8_I9}V zG$^EXd3SjOKhvF#gH_SV*T*z{c`fYwW`XM*{(iNiT6T;QFDAFnpQewhDP67Con zbB{Y6G!vCj&HCVK0C;rwa%m4mSxBDAa~OAM(A7;>OS*b-FUm2U?GgALj&X4H$J+~> zcbfvf0}fFAPdK;Ue*O3f?z-XKr~e8G>)65@Df?XNXO9})RI?Mjb7v@<$_1Gv&Y#pK`o$-wFY3rxUcYi*)xV`0jYiS$WfE^m3+`YwF zA7zy07{tN+eCHKaxI`Uz0;!|ZL9axQa}q`FZ)shJ!x3GmUjxKG>wuh6LUs`y&l!>O zd6ZHk= z!fpv#qIpuk-GWZK!H6HFw5!68B&Psva`l}S3QwW=mxq(o;_7>2#O5k<^=KCL(r}Eo zWaeiH!8k13qjDkj?gchKE1mViFXh_@a?GqEfnf(AL|7op-!9(1d!HOIA?2vlF@=xA zsKVx^?xRsAHpCr`lL_!$(vGx6(J zBZI6uw3I(;*0^d8nB;3A)j(Sm!1M{Jo?|_ITE3vdt_21fmlo!)kyl^Mg~3-dSxw)1GRzJh-< z{F~z6@}tSxbNqXTe^2pm9skzwZx#R0Zw>v{&~FX>R?%-2{Z`R$75!GxZx#Jk(Qg&~ zR?%-2{Z`O#1^rggZw38U&~F9(R?u$+{Z`O#1^s5|H$%S}`pwX9hJG{jo1xzf{buMl zL%%8dP0??PepB?DqTdw#rsy|CzbX1n(GT6w3!Tu1L&H;W1GM4n>9mOpf&<$6$*}qB z7pM&Gcw5Xsfpn@2MB5FG1TjIZFe%udqG&Y|)mLt+!gwac7u!PeJ}X&_uwIrLY!Ig%7AIdj3!>aekJ)zEG&l?<+tti_r?4 zn`}(D!j+KXf1h03e7yU0=jP`6#zE`Wvfgylbw3S?LKuoYAQR5X(*k<-NZb0p~(Td1M>DmDj>+j z=A*Po7U6RjNQPjMU-*SQx(2c@V+3{q>@9@yiA-!TiH1ZBtR_X#jd7n5jbQg(vNuWm z)Aikc@^_&r%W_zlkGt5P3AmI~T8Pob7_KXUym!y>~pSGvG700`;H%<{{4mL}{vGyxh4zHOf44972 z#o`Z8<;W>kk7?Ab!7PM3JwtBASMbXU|kZ_F$T^2MS~L z=gHG%?cnI-D!?Ycyg^+~xd_N)Tu@p(5>D8L!3CL5qNCXWcuWxKwcy4QYWM~M6S=x~ zHlBsqAMYqzzq7GR^vEo-iUXeQBQ#XhmBm&vN^i^Ah%{LKr!YA5?o3NLd>8#+=|!b+ zL2)Y&YUb!S^diZvRw1i|n&Y%3U289wF%>Z03^{QGsCh&DrxR=qajWho%&ZhVE*CNx zf%+n{y&slL8vBA?xr zTZd@BkH#HHG{%sITo)06T2wVy{`S;Qc(k0G+OsHJx!G%~G@Lw+`o1m6mh|fQ646+o z%fa^0D3Xy&N&haKR=1+`!w1URlKXoDUi`#ybL6>cF65!z!PPAw77f!*IuyF-JfU6zhc4|TQ9~b}W5+|hEQSoF>cH-|!MsnaW z@VM7%9QK1YpEjV`vT4i3CM`wZ3txCqpl8CDE*&v1Tm{hsUSL1w)3@3v4TVDN}{I#WMuCP%-G2_b>sv*)i)o|$^5d8!v8+5Px;7_a1o-nuLVM^664)5}*-oLJ1mge$F%NW#WKe)LNrZWwmM#p?q zIG}cRQ7>?twdaD-!`S=NI`m#6nIBDfTC%~2appA+;oW+PFE|ny<$mQu6gw@LXx?)j z79%g*IDIi6Hg@U4yys$Mk6*mIxqcJ@%Ml?>(j6HeiEntL5q4b49K%`(VUW0>3DgwY zEn;CR@k-=Glu|_jb%f+qK|MJ_gMNV_b& zt9%LXfoC8?Wt-!?1T*1N*-6P>7=Uevu{G+N4Jd=bG0<3saC{Sp%>yg7r-Aj1O5vb@ zK`LM7z>x29=*=ZC#Qmro>iG^8G#IX$EbtXzvDv7KjvFUw4n3|JTQqZgk%(eCImQyA zWs)+rZ~z731)x?O|2jty9R9lMWH0sAM(R<`S}QFX570uM;h;U%B-($4!CQK&<8pL> zCSf^ay-5VtcaQZ73sp?3P`bsud&6B(ldxn8{F%4sbY}#nt0|qQIJ8^^hdO`zc$ZZs zlX-OpSIrHc{{uM8v%d?6$t;wFXw_LV7=;9fcty912y8r4QN$6!$r7!Tk5??jN=3LZ z5R*@_dt9>wSH~%9&D@*ZY?C;kz9f}e(szulFbhadJbQEiS3I>$x_qTPCdr%`?iTy3p#1`^SY4!($Y24Wo$A5Ly?=VE$u;~Zt_F@vc(c^ zoNEaflHzc@Z{f)>obW~D{L0b0xAY6D-0d7}tBYQ*VZ#WW*Fzh(>oTQ$s3M;bNTI4cDLi46*y69mNSHZ}$~)@sT6fB9Xx&VkxOz#}T9coG(`R?H zXQ}+z^3$~~_x}{LcXMTMSd&<@Xnkulg=YbI3jS$YW0ot52q+FuJ=#Y0=LkiE(D9qQ zi%%}ax?-cI-Jp@nddjEe)!jHWzgvO2F5`$f416R3Q6eluBNEwS+CfV7y4P)5b2&Hd zJPIa5mQSn8bDonP&?5v!Fl_5yYpY<8%{;zL_N_gy+&B_!DYOf%(qa(XMUnBS@xp!@ zX{3nc46@CFL25ZGE{O_NxC~8lrv;mWTfWNeZ1#P-hWke=!5>&Og|oBMd759k_JFQubO~Mar|wiYHh*4 zQqGLj0G*(qod@y-3`ccN5!vM#Fy~)3uHJ8#V^oTGN+-c!oF0cj8gFU7?})G|&sI=@ zH*?{vWxbXJZrsX}NEWC{CTk)^CLXn-re2DL!s*RKY8pyRCx+8&El4tW04m0j&Q{^V zET8|D(d9)lTHDK?GyB^tFt4}KH^dUtF>W^kOpThgWmJxa9_v(#X7;S8pS+VF%~I1` zx|e%W8qFpyS7IQe{2Ekans1y<*Oy>@P9NdN4Di;!Ko7e^LgF2E4nicfHQ7A*kBPcH z{C9paM=|~HZf`H{rt6b`Mgabw5o!PD*>{uwXY$>D+l{{r$ze<#NWa&8#WK}n&FgJJW0*W4f3FhKVbsB|fmeTDSY28?v& zGL*?X@OavEM6EgU=&+UXB04m8W^2Co`{=b6%E{R?7nBv$#VF9Lq?iotuaoPQy(O}y z2cSq4K1H1ZRec&J&lV_7C^(mPL%dsJOr`mr^{ydVjTD)Ub-bX=*c&GdTpCHOKCllp zh<4tSG$`$wbD|C)GbUSme_Z|36MVv4z!Al8G{8xFI1cXM2T8*)4C6B9*m@Nxj*F%>kUckN3 z4M(`vDCa&;dfhr)#Xp?JoLq5W4*N$^oLA55)Q`jqSKAxMcL|+FlaboiMtXy@2gZrI zX9$-sW6ao?=2Him7Mfka(DvGm>rZ8X6Wo6soGM>FkN{I)9)=Nov1vKnZfTqK>OqIs zQPr=IyMDYyXOEb~+IcxEh1@Hfe=UFS8;Q|7j73fSuPXlPFG#m=sn!_1B^kxs^3fQ2 zf>q$2Y~{<=Ox3rHc8nQf0io6EKb;PRYD?q^(j#*5O7v$>2lCxAAMO%hX)ksCNHL|f zUoB?oQ$$iNK;Oz{F5a`W)@GtE?4~za%y40+9bG56B4%Gg4_rKR4&o+$7I10hpxewE zUs*0}*&Eok6e7A(w{!N0FXZAnJ4OZMQ((D*km?iHsQ&rFZhC7`2+jyO+D4cQf^F&I z(Oc!UT|7hy`K2ZN@o$qELRH9Ydkah1=wOtN!HFI}H18;h<3N}+o90T7E!h`kL~>iyw}=nx=cPj^rvlkj$goMworRUVdc zEN|4ThlfgZh@B4mpihNj#m5TML9qyQhw3Kmi=lv4hmGtP&pxWqr2FS}-@QA!zM9`% zf66Kzh7jtm@l!%{npeZP0`{7&eUEU zQN61Pg*>a%NiR3z*zK+37e*5Hg@zx-D_ znyt@%B0@ooH-tu*_?kJR@3y2Ik)g^~sOMqStQTp>q(1LPuDlUmFRU=R4-a=B(Je#7 zn|*+6Q`1l7Ne%jGJ)nLcjY>QoKlZn$!wg%5=czLU%D-Qw^-W&Y`#bA?#w?mMs&Q!0 z$>o)$y$em7HyCFydN}L7+);cE0j@H&2k(B5kj&dH`D;L%qwttKCI^!@F+}C-_ExTFWM~+r8xxo6va^S!@1PAzscQ@m}@*Pntt&O*^$) zC*lxcd4Q|Tmmob*MoA{pa)?ZfeKW&?87T0#{CM^C-SG2;YJnIKnUMR@0guj01HzG0 z>*5-OScStyzt}(Ew(v)mI7Jj$$wX%L!Dbf;7p)`Z3_&$<%`t4}))pOYXSa0AZUN*F z6A7oNoHPy7hmWX?GN}3@@^0yi?2AYcOzE9IOYuFE(;SSeUgiazwA$h!c@0aTI<6r% zyAq;UY@kmV!H|&Dr&^h6JyiEJ-l zw7h3nAtc_0!z7;RED7=6bV?7ymIphpaMW@^e|nPs?AhOv(w`F?z^OxNS+b}FshzV_ z+H`sO`L_F)xe9P{xTu2_rh}sc>^m(J`5o8ySwv#SB}u+KR5ip}y3b7E0BwDIM-*yt8^B^S^mIE4tvQoZGT?wCo<0WfHg{Z@~Mo+fVsaSUZ z)rh$0@x>E8SknXD)5+{LB+Ki|I{MO&et)H#(Ec;9k8%1@~vgKwClQkf{2xP{cz}*fc z><%T1h$u=jZ&Ojq)oIKhbr(yBEbtGG3_TVEf-P};|Kf;Ga`vBXMQsF|Ii?G$Ot zY!$G_vYvCX;N@?g%Jl&-De3RJGRuyRa#v`s)m|Ht)P9gvP+URg^K8~8XqKAM6l|^^ zP6JClfl15745u67bB=Mdu?zUCH-<^+R*4m7u%BuY?`e1+;;ex&LJ=DXGpB);PN7nX zjnj?SEZc>+a#F*J+@Lbmyj||acQf*5uc*%8##C3%lWcL3LR%o6cMc)!JB8v+`NhQS>lLpvsJz7~DFO8jJ_#0u`~@H^rbqG&7Xz zAXyQXdrKw7E_yK)WWK$)c>k`h<8QUq%a$HVsx?KCMj;}SnNcZ$QoD3|;c& zmFQ>I2T)n4tJWN*9;=4+tnL8a_+9yBRccrg3vR;b{{LeG94LeA8I$vj%F%}p=S%?! zEhoqG*yNQR*xir;Rr?&An-k8h>e+EqcwOG zNkOD6>w`u>-!(Kl<90UY?7ME!JELY%w+;NBOwhG;1fW=Z_YhkH_fx> zY7*V$_4cqsHt4s)@M=z0HEB4x9tQWCyDM$?FPSe~alHNAV<08Y4w27S&SyuBS=X4H z?sI*;u$dy4^OVfgJE>Svf6AmxNrSxBG5Vx@*)vdK0g9Mcq`DR@pKnJCAZiAQj`Zex zu!ibs952NglZlhM9E`0>I-!e*tmKCvmI(#9PSbB%wG@@jlZ{Hiin93Zare+}qSrI~ z__m-U_(5tC(436=6w-uyBcxQ;*NS%Q4!SU$4FIFbkDL8;ef1+D&gL{vWevJ;1a}KZ z*2FEq`UzT1O{lC;A9yGyt`1t1qT6e=Rahggw{3fR?_sTs&r(RccTqRJY1Y>Hh#WY9 zZ!WKqX_dW|!qhaq5(SbAb;>+!(Fufg1PDMog zw3H!;jg$@tDdr8ToA+S;Nsb*7SddC<#_dV-#_vjtPqoQVQezm74<|ogf4aD19dT%9 zJWAknXB&8FJE#p9L*k2utsk}ycIJ~*-NMf7GA3nnK@nydMs^#mDSedS&?ybMb`Io? zg4llAz`fZo&F1EW*2rn6H*b+swzO;QJMr61)CoIY7Z^{F?}qF#Ci zRU2Q=t&^P8V+OZa8rlCS@(U5bQuvLNHy|(wzxZ3diL_>ux&hg2i3Lp1q;PLtSJhn1-`HD}68r56^LU9N*|ns5n{P&}(9nr;`3DwuHChRcovZ}y1qOIiC6oMSy3@%Z#@ zvDnGgp=8)k8jZz6T&7`Kzl+|}JXN)J?n599`&mX@2cJ^;vX=OT&dbh?M36%Qxw(`& zAa$AJvMfZyqTJ5PnqE=!f+lV+PHA0g>6cH3s%#xISei05!;oA=IcS-KucT85-WXoS zb5KEX4?&W-tQcT%&i8==#d5ohnqGRl6rCeXpY2L>u4ks( zMHfpoa1CgFz6nx0h1m2;?k;Ki>jNf%JD`g^cgA=ci^K=v!0m1q4I;5a=l>?cOlM^1 zW9JPHDagP0&}E!c4Ly&MgA(dm-nCGdlRNd068;XnrF^MFeu+h$pS?6>iAD@%m5#Oq zRLzZf!t&9KzFyS9v%rd!k=Rk|#_IRc{rSSX=LpeJRVD8672*Ebnd4E3MZI(j%XKd2 z>`kAbuI%#j*|X3}kdKi>WHz$)}|O;8!&RGFPX$Om$OW-u0Zgt+B z8kee851eMded4@$=XT!|rWSpnlpxt)G=A0yYRz0QR-eY{>(LN2|K=9b>q0a;RVF=% zS!2}L`>FyojW9;UFLj(v&0P9@+NG~?Gr^wrVh-Coo98(;$Q{^U{pR(^zG@U7_F_G} zZkIB;?CeFHUGW^HLJx0DRjJ>l zk#OEeFN0M$OnL$>O1ibr=RCQ*nU8xq>PAvHWWvuwibi`5bLBhs+6lNk*(U9>^v{#? zluk}C$C*X4yakd>n1pd{r%}51yqE~(N@_qT;Itdo_(k&Eq#lbT{ z+)kgGxD`ERF6hpTW}9XeRg+53G}6w>%U3pDR`Kxx1l5~uwM6VFlE?`w(9h=4C{1(o z<8X&la)!H$AD8&YpFdu`f5Zpb^~|C~Koh(CF; zN4;=|99*GVgxBev)lwV&t^qW~PeiQ~oS7uAqfts~D z<-b|gb7lJXfH@cGV2q{@yfYC~dV~oD-EHm7++wphYKXMKkWO}nm|Eii?upZG7d=pf zS4g8)z-!U4aej7;vxN~}cU{b=$aHmWW`#(2w-4}kX!lnevbh*NhMzWwqAFw4qPW#kl}RqJo1cs;ZBQ8hb+~OGXU3@ zkGI3LMsO)TisE`YaLel_KI0pGMAWvRyO62m(94j#b#S2UE&Q_e?gtm@A|Yud$ln-1 zX}niPy^P?cAa-I=W>k&a3uzZkH>R944@5&nbU>8dO^V;%6v$9ZDhm~kC}5!`!^h8Gz5+n_s6(h^zJ3JIgwPeloW_TH(0xH8N_qufO69R9|FGktNfC zgAC=3=k48_Ur!)&S6CLT?W z9xgTC9SL#xutH`r0z>Zj+h>(5=%vYGfX3X$_n+!G?o&vs7?B9iR9+S!xlQ_kj_QMA z6RUA8b7b?3+x5IIr?9AC%Q)}ts++m%gm@e#dB6Mg6~2++Nh@F0*}utxX=Ld8i#y~} zy06A+SiSpneY>c>RcL#VowEd$px7j9K6}y1!j2KRD(tugn`F*}@{i|Ngl&5DS+PwV zch943n>v>E?J%G|N8)qTp37c!omdJLm`m_Q?quW0%{p?A?qMjI8y8mG0{OaKv_iSS zoNmt}jcUE4<^x7!wm(}1$-Hr?)C(#*m8le|LDmOskh=r?{WA`mqi`HHhUcw89UcV| zwDXRc42^%~65&jWTIkrc47PItvZ$A4rpx3Q+H_R3sUwSe=@wKDws965uzme_fyA=vc$=<>(u%!|VAN<-07TfnYP_6TMl7um850HkQ?!`MN*2x!9w5^bO_o3}pN!QC5 zX&vEBquyIoE|;3X%8+YEibHXWSlN@rUdZjiVMcCTrvRKCV#zHi0+T+S1M(#vCVn2= zWvus+Yg~DsLvBjiZsp32cclx*VzdRJQ$BhS(1l*L_>FHu;4irE1ZU*EFMQrb|| zS&?ntJ!x*eQX=9AvN{J&S3fEMO$@Xx{$V)ZqT`bO?3nwgrnHeeWr^r|JI^aouL(!H zv3y;Xm)N^=Q{r|V(nxgSTy3o)+dgSS*9>QJ);2X9T1Y8kT(n-nMZNS^bUnX7{l-)O zxqbQJ;_Iio7Z)Fqe!Kk5YFm*vWM|hRQvbeF)+6ky@t;I*tLa@4Fxv&0b{ zIso$eFTcvo>T$a+Bg1hWNW3T1@VY8|Eh@dZY_g=P$9X-RW7F}NHk=7N?fs;aZl4lP zIA7id_C|cC;S8)Zc$b-jxv>eh-OG6<1-ao$YYP6Kqnb2IN@JKO5|OB3*3421fEhJw zHqy7$IUwL$K)|S37x6XHqVvN2qmg3J*MUdb-8qEMt%yHLUUb}UL3>j__wCm+Z=G_O zEZ014NU08{v zJs0n7L3N-^^2y`%6*zCxA#T}OCoj0JmSQLu2BSub9Py^fk%lX*f)!uV{4zP;z2vUJ zvm)>fstg6ajnkogyJD}1(S?vuU5hVR-isGYcg;nTnrT+p1Mvhe2MR+bo@j$VttiRE zQ7O`_0d?Iiw&$|>-0G@YTsX%OFu``wul=jL*{fAYJnF5BQ!PruWoTw<5ab+ec~ zU5GaYyTFi)sAL3y7XVd(`sCGS$Qs`$A)<}hZ=L>d0{Y|b)22jr?AbQTd!qiVAB32O z?qQXh2Af9FUZpu^y9DKD62p$XF>Tw@9El%cCzwtAQ9m}2e(9ozGrRXiI^!?#j)X3X z+79Up`FxHqvI)?!?s_HjHPqM|p4z*SPVJqpR}-EM94uZv)cCTomqr_Q5jhH-4-KW; z5qs$W0299kC%na#>uum{zu%<7(zjrlwL=B#o|9QzYr>M%9wj8kj{?8*Qs0q_)+j zc03vI(K(#Z1!{RH`FuD#Gwny)`7LKKG*6kgUBE>fYY8F$BFOhsL{};OUANLr!k_^34H}{8bT@M%Lk2uO(>RRV4IB9p{1B7RBZ2x`ix z^vwb$VG)uplTcHrslh#DF0FcEiqos~&~R=qKjCbZ+u_U2_1*Qm>rc7D=GlgdCZKLH zaoOp!-}Px_PQ(uJvmQ`YwGmEGX>eZDX0bg`kAHDLJ%n4C)tQJiK=LCB?qQd5DA1_3 zc3>}WbIrObEHBrXo`AfFQ0+XaPVVy#!HHo&Ob%f^lxBK1+zsz9Kj47T_t3ME)wqB3 zVt@CCVd>|KPY);mgr9Z2atPbw1B@gqEUp@zplH{?)5i~BMWPbxQ}|e{g39erDATi` z#)`FBT=B!#E4W^+ubN6Lu%i&cm#M>)Oqp~mC{&f+j+8)9O9TMaUfdMgtkx`<` z^4H|67v)El0azJFL!GWHI}(dpg8C}fj8k)>yQGY7V`Xq-L1LxPw1A^Z1Br8&w42f_ zW(wtEWB$YTA-v&Mu|Ns;&mMGaqgUG~6QGzEhn$fk;IsDn&GgWwV@8zQbV`m2Cp_qH zs%ZVAZdTghbbOaY2k2DO0XLTURaX**P>wnhN_JaVhXuaA669lgQnBd>v7RpTbaTKK ztt?0j8W;RwxlNImdfV-aNJ53Fs%b8yw-kje=xi`{tj6d4-rdwk^OUQ|+kSDbSns!f zA6?F?Fb_+wA8qoCk=G9={|hGx{@+qL0(_M^)zaoe{FlMKybRoK6dEld6*qvHisK3l z-j0Nt(rA(=>BZ6c7I?=~cUQKveG({cGhtoF1S$x%s(<7d78a!Sw%dN}-1aRiUzV$kGQel;P+0)|` z2^M54ZtY@HJH5=zQy-1E?1Na9i)#|EPprY3S|>WL?l({yg0N`uXkkC&Uq}6NSA|NGwGbvd}0 z`36ePdHzJC7Lo#0W=y4fhGYk31@2&X>)<8)fy$O@ct^IR>(eNl5K<=4f?ir#+q52v zaUIitp*?v}5Tl?^6o_?z;L0Z+-UG%W(oaihgfjx3w73J7gW7uO$AY5#WCSpzLjgGj zURwzr8X~|}tm%E+t{0@9tD@TlmN|7;W@c(Up<H=Da9*S%7*O zchjIN(;>^#HAA-c-@R;?2Yfnuriyz zG{%aTEPF198lyL>QXJ|zNQJ1TG5eN1cO*_@w!VZ6b@g=-(6Yl#!0KvZ?Dx^yGKy=y zRO~Zo%Z7nZ-dg@XdAu?`ICvQas=cK_m-bA_uq-Z7J=m%=5)Xmd9B~L4&El-N97H6$ zi+h6xMcEpudP2T9D2?I=_swfKy|g>IrM&_B+wt)E6qJALDfCZa8xgRG!_nu%#x83F zOqZJKEK7!Tgqh?A+CQ4~&qo1^BAR#ugsU8T`vX|<2LSZ#!^5_{JV~Fx9b$nvZVncR z+lR8AtC1$@6V+g`M&tJZ{+E+1Y_l^9%(d znKQn>aWr}G;qvC@`ex}13pU*5Kk^W42eodCG=kKs7pC=eyi1LIdA1yPq2?AT>dkT` z$s;qmRWh4KJ)kn;<8g6%M{_&TE1Qe1zWo&kgI#}xhm}2GbIF5BL98%k5w6^mI=*z2{W~eGEhGG)lOE6`{_1q9Lyr*^#J;Pbh!tmbB9TXSVX=CC<-_tBCgGdKTc%upFBojgCL`^aS8|M0qEM1&JS}g5ey4ee zH_YlCTUv%*s@~Z23FnioN(lz@BDSfKQH#yM8B-c3{WhEPkrJ>!-@tYqiIub{m|156 zQrZd%aIyYqfWS0uzOi9xmV*z}R-9e6H$8Y!MG^M?tCh*#aiPXha+@Zga6IBN9}ZxO z18r_tS8c&))BjOb0-Og~*U*M4a&3TPt-KmFYg0-L%Bto&Vb9mO|4cz(4X}e9AOMb7 z2G$r|@)&m7bz4oOxqMpnrfO2>Ko&HYU={i=G(?Xv=v--bEFHJwIbeA+)-Ive*WqW+ z9N~~9i-XkOwu~_F$5pH^X=s;%{k6wBOO9 zp!9cKnE%rB`$k$6f5*+qC$>yC4}Vo&%y=dWi8Sw%YNbVjosGY|NF#R61I@m09w9Ca zX2U%3@@cv}|NP}srB9`b0w^2SOj;mFPKumkHIv9*2o55>M!lHbE?UgQI!Wx#!)hK+-#}+PKk@w_D?0Iajt|z)C@Xf9ftL+rvqk^j33z{p^Gb?N z#Ft7sQN1qW!!kr!S&W=6Ey~CaO&zo(ib(T1T0PQK(}gQ3rigI10{NwfuH-_{{uyiS z;w5g|#sb_8u`SRuN;&VYvXPrDTCXjowN`wso)pZ(c7>k9$mV*NVZM!1&^%YPt-mJI z9EGEZz0-LH4o}S?#I}RxBb!qkjrj`jy`!q};IjKMYqT&M@tZjSuP+|Prk5!ZKDi!< zUDZZxIu68H-Q9eAcQ;8Nf`1&zt2Q|EJXP6g$q65JLh!REXT0aIPE#&h^u?EeDk;%D zgRq}n>L)hfjha2;zt)(^TtJe6(#Zn1Kv|Tbgl8IJTEhx@sd0^;H3veLwpQSaN7gX@is)N=*`&JtY8#}T8X495& zWXniKwB(N_2qE<`_i3BU^8%k>Ef&H#|rsV;AQTA+8-%0$D8% zz-do)UD!>JNsHiuzt0h{NTuE0wq$})^9~7MQpBmc)!~Gi&d2S#Ypo$*Ecg~|T4rG1 zLai64-^XqZr*2mAcrwde$E-!akZ&Lb!u8~^qxg56a1vfIr0Y?H39q|pAg1Hb2As?2 zmu2AIeMbjA}N*$%BNzEBVATZ`cam6UiVw)wm3tE3FbfIU9rf*)kLDt9>MlpjxfJ=R-WB}TY{WjZ(aOl z)0z3p^mTIUZycT=vdcu2`$6wkG$wb{s?-c3W6sUqp+)x^K1kR{kv)ZF)M(?WkO%XmZBcpzzyKMbEU+rW`2=?2D z6Z@S5BG|75Z0b~<YQ>xXU;Zy~nNLi~)mTt4Usai^0f})6UR@{WBX9ODQ z4ooilj&r(JbkvjKATOJZY~L#zkE3PQQd-)#4wH`lx{PyPJ z>fKLJh=2abY>UZkefoU)>7TySmWDkd2_yE<#8PLCl~{A7|7Mld+5gx!B)3zBhk&!n zZ98rKcH?#}Q(+<`tW%`oF9?LL7{jB1iK#WUmQcC?YgU^pw&E!AQ|X8@dP(ZPiac@y z^Ea;uWTj1D@-CHdsOh5Sn|6|o$rh7B`3BT*k55h(If!*&nloMU{BZT4Grk)oHSbUk z&bJ=6P17sMTIU3gzR|U6y}(i37KB4f~OYk{{5q8vC7rJ`fz@5hNWcH zr+K4z;QlKHhU?iZP`XRGinFTiPp^*Up$V6#mvOW*=pan_>Lq2hnIF#L$O{DCi-R+= zBI8Eny7eV{MOv?ohOYMDt}J1;YMENkb3MIbT6*X44zHEK6x08* zlc0#s7J?)dWP5WL>6!MkX5FLaGFMtWt`8Ou3Pn9u%}NPab#a)nS+^bD{d~s)EliZl z#Emj8Ijad#ikQ|^7(a>Fbewwly&ig$NR<^~ZkkfQ@|aZhdgBVF+80kpX!d41Ag>T6e3>O>q=}M`*fwjr-M}GaH%Mu*m4smv`5nzf=f5t&3--htkg^ zg5<{G&S4c9-Azsr%2*O?FS4F@GB3+|a0VQ@>WC_tS8oAPVQo;$yDXml@@cy6PJVkT z&m;HuEJU-RPT(lj1wM`3H0>QZ6cxUWy51^%i7+|T`*_v zU@iQ1#b(k@GFUv70MZ$)8&EMHTrY0hVfwcCS*XjIQKUMbld+I)l|We6Y;r*-y#xzE zZL?_fD;TY?7<8pCNZY0hBIC&LgAuUZNW?U_1_fTl95MtLbvyXzc=yDXtjT?glABsi=kPq zE?mG=CTq`Fv+*+&*jYXR+M9%qw?b}lKdpzqm@^v|wxy?|-jsTS)6Em^`ucdd-n;p= zDUT+5K)L5?!fJh_?4dy=!A!;q;Sm{fDR{`kd*-iHGn>(!Ky+3zv9#-+i@0Dtq(!~* zupla&`QZ@a3isu%d1uINC*7b&o@=EbjafM@M^{{-UJ$Ai{($-G{cWfs{%f`hh(QF| zjG8~*EnugstvryP=F0a$4wv9Pph-vvNPL{EXNy%?>!}Vnq)`91`qI}V9JJcLdBpChEUmwHm!w*$%0MEjIwAq%iu(t91{-U8 zf4+NsV1f4;P7I_dVN$AW{X|`W7#&^DuXi1N(0G0lOuMGeibLn@0&_~o;U@NTa_pogP{d(R6-7avzK?4k3WlN0--Zer;Sgu`9P_{UV#Me$2gH zZL3HNv{O;GHjrclbS9#jTTpC2AtlcBOJkxA2|L?^XxihdzG7yh9qnxHLaWa;7`qrI zJe%GUN%xNc=^_N+KX*=#ceYWD;^yJxFBAPnKI!eY%4clHdSHiCEbbeaE~y5lOYR$( zE~y5lOYR%k#ryYRw&2?R_sL(Q?nIHicTeV>*UT99aCR4t;)Q59+MtJbDi1>DrI6k9 zC=^XJGKQqNK{R3`!0Bwc)X-IpCm5Gck*tP0^sTb!r<2MBM9kUE#k$QlXiikNk6Y*VqF%a1 zb&zX5_tnQh=QAz4yU)X~mlrqq#5xNH`!A6kbH0CYMhpW|CQp7~Rm+7At4Mg+mDK7o z3p#0xMI)WV2s${R-U%L~L5`Hts*7D-vcpZ0veFlA${rLf=ny+6D171jq2O+(>YVY)YeU6!ZeUL@(Z#gYI&d z@Gy>M=az{)Jcdj{0F3*Mvvr_RHnFj~_nbEL&7gy7+Vn^7wEa zx4*4Ls&3JY^d8y0Ru@NU_ZZaBklsyY7@#wc$q;@PLNff5O0w5+*aTF9k+!5p@PMF< z2%yS!ntNzU^kjFwwd7p4sk>{X1T|zRbUe(ad}&TB83s5oYJ6A&Cnu{|MTx^xCQFLF z2A{djays#@nPplzO*J%ELkpCv9~Kz3>lM4JX8x&B`I|)KZh^MS+o-urId+C$G%91= zT!nUB*4UhzKb)X~obzINc`%zQuZpdm6DrFWgh0zz%Ow$<(|;#6X@runp!DjtJvEGA zRN|;mlw13rM~3S6%Ipv+?Kl}5^ZyOGe_Jrvvdd30jIZpn5HspecJ7x&Vckg&z56zN zxViWYoa4m}jxHkMtglYK`}FC%QVWel0-kC!qQ{!fJ4gQb&LN!!DsH~sCFURSzfP*8R#eW692XoZK}!;M|YFOg1m zFvSNJz>Nwk{H~&=Fb(=?yYf0EcV;T;m0^^!`fHKDaRyKy6eyqfu3C`#lsP{>M-f?^ zJ)W>uT!3;g`pALZtOV@oz(W`BYB&LEa=5pvM%Oo8VKo5R5(Q~8T{Gk;iVhzk zjkR^Rk^yDhO!fTi+yqRLFA@ikB)K(pHGvg(n;AiU#({KKFy?SLm}&rte;*#t5wZrm z&6+eSTo00=;Yg@i3b{qmJ z$z|eow6M(wr9`RJM4XPKxIIsLzI0e}G)W>kH3<}`>0#Wi*9wlW05PvG?f22;(nWH8 zV!*jlOz!&b^y}5t$EzQmRVYyP<(JP*v=h6ciglI^&h!7;(}w;oFqZ%M5kms)MGQK} zI$dA-hn|cyUHjWA7L0k`!xn1o!ne^x?!g3#%FP{`xEU(AB-p50v-LW{MM(KT3@q&c z-KP{kJHtV>iDXlhwrLf`CB1@d6z+1IqFAHTOfZ|({F{eA@h09$H-}hfro-Xp>DlpX zV9}A(v(uCui*A?CZ*T6de|cw|hJx%IMaFhn%Zpjp+1MkS$R;6N$C0D!bt=0wwsx6` zCKCEoHIcke9oh958>AFa(QrS!f{`(tA#7rShY9Kj(Bd>yIJ{^|P;1qhUJuQb8A0hB zjr1E$D}IRFvL2l0piF>sYDJtPhdb7FOBNUk!w^mt|YsfR=p(m!WHx*D0tLT~~lzdWpJ^oN2u z&(~3RM3b!|ax;NtQ2-PFEh53%FpICA=D0;L^!w-y4NbSZgIe{cJHz%CpuJ4Jg3^=9 zW)yHG8cv3jub)1>zy1YhE{O%e*hf;e8~al;_VFETF#isTFLVINXP=Twad37!#BsZZ zm-Vioba^(BCXHHP0(GX}zP723bFYi;-J6xsNlFVk+2Bk8pdo6mjG{p6?@?TFdw;jB z%1&29PA)y8tD&e^V^@WNl+yOH6^O)Jb3`{{sRolj{BW{^%HaR+^-X&^9SVx}`{*ru zNnn6Fj#1|KbkvP9H)%O)y%`sF3!}kaQT5RD8^OencW#hq^JV*^Z`sCX-Ikkgg~exW z>tXYyJ)9)f(;Ap3tx6gjJCrQudTk8C1lgE5L=ujnMFZd}>UNwQPvU0sLnGc56H{F> zn3<@V!vh~Rea?A$?Yh5{CUd;tc+Ki+q1f=Pop&g;d%jiTscrh1P)cqcZYd$M6Oc~^ z5od{YT$=as=QHl;pvy2KxiTKmxm3dooL;~HbiTl--3VobO2F62DWi%7I1FmC^jL;G zkbzc-0SV=BDC5$+7PjS@VT(A5`y>kMYC}8nE3dw5 zztp=twvx+HZ=prK^aRI=p)kDQ)qL6D%EO-qAcMZi@-5nCVEa}lUItnZ3(=f4kv5Nr z8HI_L>&=s(ZK=*EupIRZbUJA4W0Vtx74{#rNvtkFTM;|W7Hkt_`gN8M>Q<6;MbPq! ztm}>3C~4Ix)?43o-KN22*`*Qge2<~z`0>Pew@>8;(o2}8r;g!-h~p$>12p>QJ7CU& zUlKbfIVI~k?Dx^g5c0w?dq5fNAMNdv_0bF%&W?G;0OJCTLMR7rIcD)TYg9I>EZK(- zMkNL{9w+%xqfL=XP*$ETcGX*wHVPOAY{TN%WJmGX)fZ&Wtl}~{ zp1ph-$r~PLELkmZdg@9WXY2Q|^{`~ojl%}1H5*Dn%~GHEWMk{)#tZBt|Lycwdu0}P z*AoqNm^qrTyEPs6dg#)DC)JR1BXp1s^?SZr7rRg1mS-tG+q(5BcvR!qOEStEGYmgn z+zuZuFYdnHT(aths{=)CXhVbA{;}CdsB-Ek8_T+FlcW!(?T&*kjRE9+?tJe)ao6o) zi>J+$>uo%(E!Cs*$=37dlmBV<(&X{ycVE8#$2(+jU4DD~c@=-({q*(oTl~Iw`|&?6 zF220G#pe&7t}pH$^BbJ=@t$Acq7PqwUVVJ~lElBSu5khD3_M1O!i!sas2+d7mv6s*{B#HK$Kwx5E_-}=_4RXI zS}_kgxrvquN<}@NKY6bIcAl?q?~c^LXV|CZ?1IHS0Lhe%+c4|5Mbfn1-)<9}PUWTnPvK%&t(UV(E(#RocV;%D1`d<{>v$>xf9IzaIjH)`4o*M-%0TKV0&r!wtkDagQSjTvYz;`O)J_C;b4? zn8rpZdP{W-!)$FGB=-&HJdj42g($Xm3}umCy_jSY6c@59GbBaeQcqFp^X}r^pUeAvgg!^Nj3?>u;%pJfLa1uhz#vSQ|N%yy17kw}Jv ztdwIJCNr~4<)5%0MbL1H`gNjWU<6{4=;+gJFdjL0iQLH*gYN68iJ}E9ei=D=LF#j3EeT52y6k#L=P>yDUk;{oZ#Qn`dPYS3F z8Zq_~VL_B3FLpJ}kW{C~fcm~zGi+d-=EU=@Y0Pn^Pyew39KprEsa$!FX+?E476Dx_ z31;(kCqYdWiXY}iiyE>e%;!40g^v^sn4+iOUrqE0YO4JyG<*uNa5po`GFr--fPqqp zv9fO`$jsWuKdvtiQ+H#A_Su4spe7MzbQkgQ@F&$&)SHMExAzssP+`U`u<)%tYAPrr znm!hK%UU@fk-bD!{)X+FoI>Z-mTOc>))kJAa4^CVcrJtxeiTh1jy5Z6;1TsZ1^GbZsCpWg|OkOGdNo9433Tfi$SgTkNFF#-%4C!P3Wj2c(!#$@)?A)ovG&6|k46iH~I1cQ9ffNSlI` zpYNP(ox-P-Kl$xyx5vWXx-(}!^FDA$tC$`KzJS;g%_{h16hQ2gIf7A~w0!*e@@9SY z+hoZL4g+}%vwFc%%?QKiC5^wm+$b^?kS)ED3yA+jXZ}RKl1pt+q6v$>`eBa zSOWP$VeRhwWhHU87XMGOiSf zuqD@{y_NhZIBWcfC6XdXC3ZIPFoWNcT5yhK6hXd@zZaA@fTP=93vwjcAvlC&;I{^Rm1cV zF8K>qIpl5eCBon6QvE%{mH7%ws46`yTdMh+SEO42>HyPh!g2j&`L7E;aRz)A?(aBm zo&1%z4{J97N`-prX;S{u%p$s5I3)|UREwn6SqizI8b}(I39`S9yvYNmBXu;PB&t3l zIXNBkV}C30mDi^}x||N1{VjM@c{Vx)8uCh(5KTD688ooZ?>@eB7l^*ZgocES3BcgD z7e8Kpmz-4^1{qX!;2WOfBG0ty6ZpZP({8-MArZlxA&==g{(aMq+ilCy?qPDn?mnR0 zCUKt@2$rLC7IG;Zr6kZ_vS*pOMO3v7OikIbcXSMWk|I)#s9_ni?fBYNwq3FHficQS z;9JN=w|k2@N`#a^dR-~F(Qvo?1XsV_PI(>PqV`(07B~nP3?o=J=FThl-B*fPGBB^9 zc`Op^bgTjAvFV{zpi5wWzO@BB#)8UbsDFr2%uQCrQPfZ9_t7)bT=69#BwMp+)U1oF z{t-%X#L7oRiR`fy27F~(5SAK(>k(~Rd33SD8!Svz850RUHw(0QRTgy8?N!o(ZX0Y0 z)xh>&=9Wp2nFYOc2RA`-1qYmA@O{!p{l&UndT$3nTa4|vg>kwAv*`dE%zj;dSOIAG z6;e=_Tb8Y_)s#eS6@g@&O+VMQ7w{h^pz(i=zWY+4Vw zayaiiI;C2Pt9Nc%0JdV#?&a|xK+(Ie|2X|qsS!^FfXoxokKlr6?eUj$oL61-aH=S$Ssh*P z{dx<`q2d5N<@|ZAoSReczm)^w2NenU< zV$^Ao$Qzwu(qK(DQK9VP?FI7RKp7%ht%=7!SB9l8(??%skG`y+67+QPPyaOeGSknM z=pd3vq~yNOMO1gvq=<(XS+lSNC8wM|8iiuOG9X3(7Rcw9G%sMwP}?;Nc3jzRPOoxu zQYoS47t1is{J=mBIis!+QK(5^)U5XmgaKP270#%y?Dx^-+-6N4FW3NIv#X11we&oFyKv?eCt7j@voi~>IJ==lMi=&mDSC9K3qBX_yf+|m1is4+fj8unYcO~ zR__U(K?D$(1rNmY^>NYP>CmABVd?Jj*_C3Dx-!rnp|S;+*PV%WTlCESW?vQkj+yI7 z=Im*6Q9o0+B+Mt_L?dON!Xw;SKxkZkL}GW?x!JIYg;UQ0ZJ>ClA54zf9+2L!{APNB zqsU+852k+s`$egVsTg-FnoljsUEd`B+uRvN z0oC8_t3qwp-R)RY>|dO_y@Q-8{L}X$34NW1jspSC_aUbJLiJEYrr3VDsa1I9-qz{{QjzCd_f=$hEM3Ni=i};eIjYbp{C-Bq+FdJ3OBYK zcv)S6jp$ZqEMVHx!p|7(IO^Kz39Od`FO!Sarr|);*;nZj6cnjIk|l0@#@VbCm6}C_ zAQpv3U$NrL@_PJ)AFPPuz}yRuP|yu-mGc<_UP3EvgXU$87mj2_x_b~@At%X}TubWZ zTp;7PxDg_B6^ra7pJaZ1N_-}~jt9qw2glODdK~o@xdpZs0Jpep z$-}&Ldhl=}bDn@*b$eAz^?k&!b))HyYdv!gVw-Z!r777!xiGR~2x%?7d9n%PXX(xI z=)eA?Mv$65){Jy>8G&Y@1l9n`E!z1!EhzVJI&2!#m#$kM=zGgKiLdRR z*ypwJIxvaXE~zEwm-RNAMasV2-F&?K^a;M@A4gs-Mw0GDX~n;@@3VVjeF{b0nr`jy zhz`JCAOfXtK&h!cFp&?~w!BS~gd z&2{?#bilt;d`t)JPd+Fkc$L1P7 ziQVPj#;fD6u*tO1MuEdjMwznSNQiEaapckFyEABl$l`l*GTWXnU9oy4`g|=d<=_8q z1a}WSYjCW^3ngISN*yd8n#IM)uy(lC04G3LR18ZV`#M6leX>_^=hh5fw#}Booht4J z%ny=@amLRMcHaS>@_OeGE>fz6VlmI*I+cEMXv0ly(*FLh(cV8F9#$Xi%s1mFA+A8e zQ^Q13=*z9nyiKILsfgv23jEr8iQpYlw&C5ON_rfe=2RKfT?wvK4+@$+DLOwmk;-#J z%L4?_%tbZWtRfxSgnw8beGtDO+Q;jwSOmulWNdEHsiZqkd|=cu<&~gfuJ%?=z&$Ue zJ_*_n#tdY69+)*K3QY8A`BZ}LfTE3((CAJWe@j#smPy~t%aLWS*|sctth~(pV*d*P zt@PGu51Q)q?BoczkHZsqm!%hhL?d^QT2P$h1S(%sCsaIH4l387COm6A!72U%aO4vW zy0n}Izpk4m?&=a0fp+vHu30W`NrZlKbPlZcayY(FVyl3nq&GHSe9^%KU;s_| zD6eJjs;S(i=p+q2A_JC0_AMNF)JP4#^7YAysqiTW6Av7kxy-;zEi>Fy>Ud^(1hrdg zJ~evw(a@5~dj2!tBy^Igc}YeC$(3_opYmPfy7_T{VU+KnPas$UHTcbyQtOz;giG$3 z`Lr`>TvuXCOxT$M&#oC{>#GA|v2IoopVn=ojX>7A$-HfqigiAhLdg};(1(;1cSL_0 z?s%yio=gE;f}P0{`|x893Oa5SIc>6}QnH{poS8d8bF8X9`gW`(CYfAm zqInf?T|S5T=VAN@kCp|VCKh?2caxX5w-HT__wP=G>%VK2A?)sC>>e5!Xw_kf=>B)jxKLA(|&rlg0-Gk8g^X)R) z(3~l2A>2|QdIW^2A;C>mzE#$q;o3w%Oa3vcD)CLNiM?0!1xBxjk~HHhhDp2tR}QCD zmR_u@i0(=tw3NPGZCsE}00YTQvuxH6)H7L%9tkaJK*ymVxe&aHRF9&TtCd3Kqm)VW z<@)OL1J2KSuPFy{fOhsIwN@k{981J~xTP9Hn%uJ;6xpfXTFeVjhF3fiA_*0bWW=hM z``Vh>8aQDS<5F%~a|*Xii2;Zdu))Ort5Vu2Q>$D;+0oKNS>3cZo{0pfk1t{_BFGN` zO@ZZ@1Br9q<*M3-^t%!^Zu_)xr4*GZWT zmtO~{D2_D{X9}`zq^-g8e|v`CC{jIzNnTe3Yq@_(1CU`#0{NstpVYXBveX)M>lze6 zcGbnlQtT(490-hB?7KRSA$tzGYCzuS+sJ4{w~6%2#r?-I%K7J14WSMRWauvNc_no> z7kq9|4rGs3D>9ul@n0@2km7qg2)&g7iTBsnUq;;~{!J>0%ocx85JVkX%W{zu`NzBWqdy{v;*Utr_~Yd9=n4tx;wwHfgJNS)2f34roA;IW{aPm7zy)rZB>?Jr-S zuviX$SstMg(cRkyLW06wz0puDoc2CC+RuyJ= zLoMTErGd1JQ`hYr?lLGynLhcpVcP$XfhoJ~mg@#PDW62R*Z&Ve_AEtw7JX`(1P*R!y(RC-IpRT_k z7v>8xE$C#~6A?1oK?`30&EL+$D}(d+nu$+jJq^xazNflV)fC93XZNO1+x9Lluf|r? zG^wCT|I}3yE(Q>U7>Q-2k?{*FP`8cb?rvA>6^Koh;U6k5PJoCk50x$?ql~fTs<^h& zR#~*W=QM~!$=iczB_ir>RmpYQ>{b=Y(_L)95S9mEU=m+Jy?au@?g06W$Qgb| zS^!sd6~0_9I{tP#%wjTP?-=buYys71r~43l_IIgkXM2pRCe#^daJVKnkGy2mKO z?WnPErMN=iJ17^NuIwZy`i?}BLsO*pYF9rvl`kXd521KT_!Yy(L}-o{0Er0z5{M_+ znz;xcoX0zPI>`AkSRs-@&Sqsjg4Q^{2r?y3Mm%TL9pez^n&ZyanAHYljRVa_YVw^$ zi-C%7CVhi@>~;z2U{}gB1OsZc1EYpoom!0$0s&a33eSq3N80fXyow;pXX~@4EBI$w ztAresKo@8Z<&7Hbquq3|;xRIb>mu5ZY7B^64FyM_HK2}w#<_NPq~$prC`;c6JbCid z#h3fV@`xwv;_mt@@|^FXoqt-u1>N1=zxR0~od}&u8}oMe$OeIz`oo_~TR}{PpD%8% z(6A0OKKg7a!O_~HUi8v=G3~$tz*b9MtJjL65=GUn!D(EuL;%8xlV7x_tx~3jtgJF;G#DBQ-_Qi^ z;3PgoE^U1{M=50U2+LRDP@$(Z+cD4qPP^CJD4vG$Ak1%3yVgj zIz2gMN>tu+FPM_mqlO9urd<+Mdk&vbD5})w5;gVmDiH0>c@=R9q3)G8tOj`}bdN^< z8L%oSPV!}Wl>Y4acgP@-1%hnvMGX9+M%ak2;kxjaWjHz9v^H4f&7^Z9*G6hsdPi)a zTfejCj&+)H^FS*T3s&3_>gg1gU(aYhw3ka!VqEW2{~D< z37^vNa!0mMn-o|gk&u-(?`MQOAtB3I%`BeL8gbs2=WAdRUy0rgApyzwL{hp`GwA?X z5PzS0z(~?CyQIh0;q$dAS|{&T#du?TuP@KnF1q*kpPUKv`Q+u~kVuEUa=mcc5)~!) zM78Uf6Y92^cdopQuCHco0c>fSjMgonw1Nbvu}}yvbYqlF(n>6aih*rV6+h)qWgpCO8wN zQbTvTeJaNpEo^XNSJ%csUO5fP9$AP=^3wPk>WNealL$?0sq!Tn6Zi(sg?VXd-cgDK zCw($Ou+-|R^6lOfUN_+c;4ed6MbK_7Wm_}&Rn-j0djuB2X7sR?71T0bk4Y0^E(yA% zq%>JCw-4mK#ADP457e22s(e*+9my)m2{@F?8~P%OiViu!eNq2p(7lD-m;QYPdl`=dt^KhEWzj^=w>)(O81)O(T<;!{v z779L*p5Efjo6|ntJfE{jfgmC+sTR3M4>Uj+cmiR>=W|IwKu^=HSC%|QoU~aR5RJ&d z9m5DBU}LXVrtdFe~Nt z#NQ#z4(!o8yrC#QSoYs8(+|WkbAQOjPT5ZoMwA37z?y!bXrHhkj6%2^IiK!dZW%7!?XwNfZ|~ibY?mo-c6h#KWu9}K#B&T6pDymd+-X`7#J$HlvqOr3b?y?6mmVQGoH>c} z0JYKb8!B6)!s)KO>NSV?8jf0pQE1MEmnC2usqPk5bqsa3G&R1W^G5vU_x(z3+8*_-@ie`eRmg{qojLn zXuX$QKS7FnqeXjF3n{3AzwU&X3^^be6-PQ2@Fv(JVP_KkfAQ(lrvaIbQXGDCS|U3S z172axTUfhj88!CwZ(_<(z$mk0$hO+~;isBItyPti+_L5^7?Ds=w}e4`z5OiO?Zxfa zu3LKi%=(Oto_)T$e#qEd7{}}!29Yn83r@0P z8Y)^6k2j^Qm&6SG*4tA>kRikEJO1lBjbzjsuriV7r3T!S>`*-3+O6xL(pdamTqj*7=9KlCR~?XtXk!wW9>5B;)##2+P!0TSAKsSn(lOM4-lrWt4kesr>(AQkvg^Lx05$Vz<;&%t z7fYYM-d)^$e)8$^=JxKViyQpDY|f0;$=jke<9KC)8djk5w~A!~Z@}$CwbNE9ABEHU1is>Y>xyd3)*19;ko3vPu11JoqLlu%lqXO(vIa~s zp7bivU?}3R%2Sc#)yG;n*HcxC=RGMd!l8E$Pu?gUkV@cHb0b#$UPrg@cV(@N0w{YB zw=v5YxZP-yv6v@7C^&u-yi85K(oVdjgw_nMk+TLhA>fuQ-$S?$Vu>(2$$ju^R2XH% z^hLb^>0U@gb1WsISfA%PVn~ED!$jYKk|h6S-CKZ18E}Aw7u={mqYd$?`v?RnO6pwfS19o>AI`fPG+3gNk|F=a(ph#`o70?L=pqI_>KE!&+;21uDb*m)o;TuxRNEhJrdX2?^LqP-em4s}ntl zt!MqWSZ`0-nBiB38Y{tNR4XXk+^sm!a{ka$-^^74xx;W>d4fyCi_hU{U|CHoEBbtm zQcnpiAqE8>68Q%~ncJIEWkvrjDJ-sm;}xEpzXtpU(mta)WuvWlP_+PsQwQI+ctt*C zt=p`pVQN15*2aBo34)nvpJ>G1B0xK^JW-?4(w7=JxP9BI+n?x-%bD zGL9}PSlZP)#j|JZhGMFxvi3%y5^My+=XP55pZyq9&Yo}_BHqrb(6 zyI^4mej!x@tuQ2(Hc9D2h~DfRP6Z#KDX=S#5VD54gPp)?fKJxbZZK!J1s|i6`~ksJ zBzHM2$v%LV>TJ5+Fqev-dgSmFc#iHAot5s<)R*{_5ZjTMM`dOcpY7-`vqMGPvP|fdyJJ)(5BZIgHCBVY>h zkEvFG^KCZ{16G)Fx|*!H_HPeN;+k#kqb`9Z#f{fAD=z4WBt&b|EH9Ydc?eHSI|Wg@ zg!X)|46&`glVS#M_bZVGzl!OTzZ#ZUc9A?iMx8Ke;o0Ti_`KBC;sSV3qK@;n=jcP5 zIgPWE{UVVkD(nTwmh_9y&zSbNMa#Q03K= z6ogEWg_jUke}P)FxJ3R^t~1oc*dOQDv=mlx; zD?%sOm)!mUKPdrsq``1NJMaQ!ku z%p*spJl}$rAR=3MI@dx~n(ocP_SwE5dL~-2;{M_WnWSaS3ZAHyPJR@%D}oDZMLWu38`h83ivtxVY#gw+3&Hlbl>!V07hk$==p8 z_)y)OE%dNKy)S@N=#7z*vhI!%=wvX5L^62LO1%+zrrQT65J)9fwkEJZpn8_QhFn(8np?{gr>^)&OUmHt@JVY1ce50ps;yURnAl?! zjl4V0d$os9Z1l-2)QjBInP|aMDlUUqb^)2(@AO~Wiqf~+p2|n=q-xun89-79&*J#X zK`BiHD*D6-`bI(o60Kz`wJ!%(r#IK1 zxeid|lbBCf#$g#Br|>>K>QJK#InAZT9F&uRkaKyOgiPiuzY!i%Pv(;)XiZ?AJ-?YA zfgW&eq_woUDcR!@#am9L5XlI|7Wb6aG%0JhRtIwT$nZRqdYM$Ju|BFE5&J--w(tb& zW9VhYrKXX%l4ti<5)w3?lg|uHHayz!R>;CNA)8zKaC>P5k##gOMgvMtB z2=0L=!;I$Ec%YDq@2?8m=kY}Rzx#%i{ZH37()juA{#&yp1uRlF)hhX-3gR*)#b@&7 zzJtO!+c-{auMP4GJg8#pVgH63{0+=Xw{*d&%&tdue5!1dpr1xjPfZ|Ms=RF5(h2xGy;}Q^3nXP+GmRcT&x3$;uc~DzgHvYjPd^2kIGmBP>pxL(4_9v4y-xA$(v@oHomB$wyM zM-8b`y9sNaopwpta&MSh#IoUvZMJ_=H^<{P0o{PKrl6uGl%AegL+Moq+q?2fZ*`bz zqsOby(8&6+Cy0vg6xc5zj?7i`166?qCR_9jV2s?IO}RTj;YWu$qJDIg-g=-ZNE)R< z9?yrA;jdYgJ>M#xSm87If%$hcaYkrue69w6X4-hcP#r4}imW8UE%XuoYKo>$;}*7i z@cG;4;&rC6A$%B~;6cZbxl)PI65U!fR8y^~tOnJZe`}l})$>YfZ22s7VKh+(lU^{e z+3*E^Y`0?f+-Am|XS!T00qD*z9v&*+VkC%uSzq>3u! zJ?b?8kBfP#)>ze8I9uT#B<0C<3MCe73~u`YMnCF?H!(MB4iHbR+_ml`QOZ`d-{*4{k8ahdAHoF`Vp%h zfkSp@6J+3_Q7tY7oCK<*qLUpy2b>3*`j3|%KQ5%gSg0f>)d7CGyuEyX`2}za0pIDY z$5K$tt10w=!7~Y@g_q(RRnoc1g$bhX)x+vg&+y{or2k}B$N|5*N9_WsR^S^ntlOG5 z8Hz6#K11F}e+0!<4=H9^=6Z%-E(xYNh1YMsA$A>oJ<*=(!w2-BHw(oNi5@UKU*>x) zl^!Qhvdmouse}zYUb%^VKsJs?4%Yl@oKmU7>_73ec3NpVOJ`x}%S%6a@rSb*v;Bj) zT}&(f;8IXJE1`9(vuzfJHp1&rx<>0kv>aOf$-JKQqCJChy>~VjW0hhG^~aL3lMiXR zS)>sNi#*QDm7=o|;AXx6avru11YkR{96w`w`|TQWi7j%R_sQWIX*1`QuFB3Uv89U4 zf_GWV&x8C^uBLQJ$@>fMNcoz@5bsP2Ey=@vc{)8jjHD5Vr9m|XP!`oX9++6YBB1cy z>dKN8(*Fxe=*Jb2i%wqtzN-1{&DOHap>b%2|v^H)g8+uxf2 zWI0NI{mbOvp5cSUIPFjXEKyX`l6DFNL4#xBb-OwXGzsG%wKs3+H>?)-8pt%Ve>F7-8bbi zD)h7yvm_A*ny#8d+|)$kD;gGY*jUwmoHk68ghm@opW65;D$|wk^59gyQYnU*vry4# z0U}o=gKt$EoB3db{sAO7+F?Cj34B9T*^KwFGi0_ZMwulc=Ir{W^Dvt%?_A8o2eHPV zibpS5J-9;UG_FqDwcpz=y}8>Tu;Z#Fv|z~>?y9-aW-pc?};9c#`P7`$s!SQO=G{ zY89&0)o3bO3H$mX+W&z46Ne8m+<#bj4N zqGr(}waOstwvOfqYD!E89iK!bEdhkb`pP$D6wYny4KFytG9WeP{|I<2;lZ;j0crR@*xHBLp?P&UHRipa&;vY4SfonXgzom}? zCh-E5Oa+WBTT78td_kTkUu$J;iSMJPB9{O<#`_gYTypQKjTZ0cKKaB*aYr?3J%e8*keJ#QbIfOTrLX?DZjrN7$-gzvMm9bEcYApKUTZ|3MtQSTyl>ca$y-)C z9-TUco(S^c+XpETRUcH1OmI=dW~9V;X%JZD&Y9vRF$Q>_`&Fm%hpx5BR5kAN5AUNgcE7%TK&|9Ce1r#_JoWB9IF%Pm&PUl`tb+VqE<|evzbctu8vc7;hy`b{ z>opfF*cvuXqgHF`PmMgsub@L&OEm8XmK3r9!1IsSKRe1tC0DYaP%q)=pqFtvFo|#D z8mrJVoUO2%@G)@hbSaYqsqLq=DDVM0ot>SaY}`y51D1>S8@|B1RtP59gowG|BPfC= z5RD!=uRX4Fn2`@Box@%D>j}KqqvWBoH$%bxd`dmUrM1+c155G&eJ}_rLv5izv1La^ z9Z?wguckSTBq-V%)i7i-^Zm|xa;?8RJO@zAQIFv9moJYW;iyPrmh^mn1$V-HOM~88 z?jddK7R&~|FL%ZCACLorZcY(DAB` znuSa9b6kmSy~*_;am%e8oW>R44ZWTR)x5V|Dv?1hFE5w8YosAB{9TsYZ;}85EHBFV zN`6OV6I%Dr@{wCJ__Se>nDG?xt=tBCv0fjZQkAXs{CZT64kZAj9#r1~dEBG}4JEFZ zgpjBz`+aH=)SSZmlg%nagx#1KqVOu<`5$blF0=#g47;sh?#{V=I6^AxvB-nvjnkRU zVS$pS2PP?5i^5G%-Il0t@>WSB=8bbM0-UoUB40ShbN%A-&I6NpXY*@B+^P4`c^kwE0vi9e!*C%&&K1`;khfzwcW*jshi4i>4_yFg{|GqzpPQm^y{ z^{VdikoDmC!{-rGl?Vo!i`(0#IFTSkWXI~lySxZu|0reoaRD&5uNqhbEYfNdf~FCZ zjkGFphXAtd&#t)Dy3vcIpy&O;%|=U_I3$!e+XNB{^$VcL@o~jzi6R))5TDR!*?8kd zogyK(n6Ky|;7zDV^Y|&7=>>MnG2SEEz?%5lC{8dii5HOSVjlq@>41zc92|xpq zd|$aOAn8skdWHr)fN7YERB*H$4kXV>DB=T5K=t6t{pVMxTPC%4w_GgCY%*Z7Zm-U-w~PF4X( z6LPccS1qV7{_n-b6#(}b@ziL9$h}`EDinFW|56Q28iQ+fKh8ZU;ywgXY%CSXj1Vx|!)vHl5;dpLD-Avd7{sW33qEOIT4uA( z_vBmEJ^0^)&7vEtGzq zG9OOUeY^Qfuix7#-XVg_7A*6Q3v`3lb2>C3&c5BHE|5h@e2PX1DQ41I zWtOyH6f1b6lH20T$K}!O<$s_(?9!5rjlz__U6!sd%TNDNr`DJq2NPc-HzvUbZ0mpBhz5*d* zm(l;=5mSA{SGFQWG|?b~g=7xlZCzo(`Cc!ht!{q`C#rlO862vxu)JCZWt&qq0QbDahPOh9hZpN4+5Dw#2O7ZU!S?pf@knAC)n_@wR(41^e_JXflFgGI+K(I* zZ@ws8Yw1~S)d)O89$P=$38lb4O*}#<0@W_c4RZ!zgK?8A-$v%=U4mYS4_GQd0g&is zrud|lEi(8Xh=1b8wb{C}3Zrdq1uI`$GQTFkYqInc%N5IyzY&pwn~l%Kavd)(kNyw) ztN+tT=Sl%`OhYl_@$%^JMq~6-PXj*zY9F=9r4QHlS9eboWJ@hFj&?TOh<6YAczXss z_yZ9Y2w96Q@KMz)HbbT?S78rpfs5u8K5b-fFt!Sb7^p=!s7GeY zx2WW8Ki8nCC2D3vFNMx1Uowpp2$qim|E_|X4Gm}wwUt4x&qe=H4XB_nx0Hef6HpbE zY4V~0rcY3MVXDZe?K6Y?VL#~NsJxPtf)!6Ol)!u3p{N&=22i>ODijHTmNpmIEOaYB z*ke5HIlMk<8UElNt?}f=0B>K@&}fUT>rmV0$9>i`R8dUL`vr}#=N+_}d?e;w|DXrX zzGEM8Sc$^pvBHhv$057M17_;!5)$vVS|$wy>B2HRzqn{R#AJz>QUy#p#K)Frma5>UE!d3I%3Sg*P-Xww3R;*Knql&n zqD-^UZ(Oq!ipcOlf||B>cEN>HIcP^t7GvQxwx@uL!WS}4moZvxyzofh-7G!0SMX0Nh7i|S>`p69#c7onttn1zMN=@A{M=H5=CUl?&g@)z6*vt z>Fezo3~QcQD`6wpWb2-McqnKf)?9{QL7j3f*eUUKE!pQkjQ)K2_5QtCd{*YjNeh@Q zpxL%(T86Luv!v$V@C9C2Fc@rdrX5URxid}t^6Jaw)gmYHjLRgRu_p28s>q9>7=r$n zG@sJ^BO{FZ2n7jrbgd*P;s%ew$W9eK1r4B*F!PI2)10NJt1B;3ImUE4H4@?Kww<7) z9m&4IiH;*2Q@k05Vo!3IMlkrKP2}#8U`Mf9Y2Jv~z%l}q6{%Z;5JV%~Q)BtXY5NjEXETf?$W;~m4-omZz4iY&ay0F4f z;;QS0nW2Z3Kq`bd67i#?p5b|XLVs*Lb5AmsW1QY%6Xh!zKqWy%As2T5Wr+b|xFVTJ zEIXpLaM_r;qx%oN0N`&pK0^uw;7TWK^!eltO^lv=U3`7NK(fG+{Ds=POG_Vqytu(H z-|lWkPe*R@bNI=~W&`HZtbI_ERuLGrP;gdHd?j>qF`RZ+SfXrS-NF~wn{J(AC`TW0 zo{-uG*5LUlYS-dYqa_BjNDX#9HSy1ue8nM^ibtS_FfZtR*(RcGYI>~i+wiR%}M^_ou}jV?QN|~1?I8Uq5K5u2g+6@0g(5zTHBsw zq+Lf3D~rDMy70+V7Ym+Cp?>rB)x|Z17!|^{`e+7yz?d##_|lxU5iTE?#Kq3%pXcoy z1i^-SrD?&i6uKL9vsnUk*@6o9JcW7$AK4KLS}04ghqBaccZ65(D^QQ4*Sj<6ity7V zpbtifo>p5A^NH!!;KoqyPLI&GrQelS&(fawtm>jj{5$70qO}}g!I{MTsL9pANrBA@ zNv@g2-JvST{O zF7n@=j4b^kdI%of-hGthJP2yUyHQPpe_N3c-0Dp?2t z57(mfJZ!A@6l%YCoTk9!B$u&kt7_qtQ+RLR8QGQsvW7PV+M|4Be2_dL;unS6vF{1V zA4X4zidDtHKTtcWoTjVO;2BUoE3)T%B#UAdRhsTni8zMDsKN#XR%>kF4!~w`LRNw0 zAr!gF;Uci-6kIvYXpCv(2Kny$=BOceY~47e!5M|O=kWDifRTCagUX_Y=({I_a;|EL z&>!bD9rZGQcf94W7c`aNY$zvwwc6(HkMn9hcC8@e2Qp;zU3?lZqKW-6t2f2E17M=( z_pL0w9JMV~gu!XNH#u=gDT@VzXyzr6m67WBq7x3;eF;RjyTjTP;oq3R`<7eVYh$B2 zL%R3&$?;B;>Y>6IRTgE_r0LJq=kDY6y>xNDLo*X18Z4rTT9sPuo85GbZJpr53^X-|GKh3{f z+<$yI8N+ifhR6y!RJsRU94=6c+}|P^QdnFWvDFRu1k}0K0wC#^!^NjN(`w`j46caI z-Efr_M39_|s6LQ(AyPsLq2_Tb^_tdb>kNLM^koh4#7m?c@No{qnojITQ{_>XwKfc# z{D&Wdokxk0qBPYly-}7 z+TPf~eGh=++_PTe|Kk2T=X=>R8sxMHx{?3cT06fh#<xQp1&(7rxh8g!m`vf04Tb0dDh*tJV3om*34GKn30|a{Gsdr+ zaD^V#Xvr>udX3A<2Oh)VO(k4;j(qKRu&e#IqYdG<8_I06ewokWM*l26#KFf$ay*aF!iU2 z8|y53t*5rdS&0inx7D4d+Dx8S?kGq3uza3$toro`N;c(OOx4ah%ehE?OIdJoFSznj zE?%Q!0Ut;EpJ(2b;&sEkY2xANnl~9ROVVsxC8vh8wvtW6rF8}e)eaBa+vBpZe;X8~ zFG=5}+K|~D4ZkQsN|jKE58xEAp0q|Ycm^i%DdSio4v1(ziegJ0JD5FAlV_93%G%oV zm9>|X^%cMf$BU=q=K>$Zs{W8d-5W~h#zBm=sQnjj>kGG0`-$>K3mAR!{8!_*oVGp? z!)oVKv0H6J#GingQphe+k4dzF=P0iQY{kfcf>ZM`dlv zP~hqpX$7pGjD8~^oR^58`Y$9fpXs*EU=r7{ z>#i1ykKNCRA%)x_Wgw84+#rQciB_995&i(g;Q-Y!d-W-X=jp~l_?XR#oP!AZPJ5T9ipOr%7js|ByyUoKZG(iC2`=K%0J`O#J3 zOjz7TwVR?GOY4!BgT?B~$Dx4Z!mIMFAzoITJp}uF3w*2~!gi&R?mGnI8Q0A_RR?Bd z0W87*i18+}X=mjMSr6UItINA3DS{+WRHT=oDK>JB!R{AqNpVZI}buypWBP<|FW3bcaMgjBKU=2*DhTJv`K(~tr# z9!8LAZGvQpoF;G|lK?$A-qdV%u%7ABYRY-4uoptlrS#^N`*{RXk+$;}F;NxhzcLjQbbP|pEDLGnYt`|W2-9pV5&O{>q?`#t4cc( z3CO^=K&kn|Y9$-3Wl!XG)$s~Kua`U@yxoOkrT{@Om{o4K27<5< zQvPN-N9BwoNw*}FVHqZPGoq8j>a8Q2MtkV)>={0}nW4nO;`UDLtz_q!-oQmjPN?#E zVNGReiYyx0Jj-Tl?|B4&S@0<2lnn`>@6#lh`{1%(ol{Y>pJ$!oK#?&rnc>Db}|#kTv6GCn<{>T@dG`k*&j$;sYKo+ zUn(lhTrECi+OX3pm}MvwjZj_qG}-^g{vrc&FQ1+=_B4euQJpf1Il6Z>eN9X(Au_%Y zY%s*ctiS<^QX-LlEU8WKL#(KPgvBDbCjKkmGB#>vL{3#^;ktQoKVK7*!h$*BI6^j!27eKVZ_>6U6Ksc;e+ z(js~!C;K&T0PAo|q9;IeyHk|kl3x{}LMe}gjs7lAfJfscG*#9O9XZB1zrD7Qvq*p# zEcrw+#N8ol1`n1?LhQmJkzdTX3ajgmyXmQUhbC3tZaRP5S1>aXPlTJG&`eniPY#@X z!gr#QtoKOb$~)hdk3BGnFA&eWGnKVEx;rq57sw2yWIN>}49(&tz)hfF7Tg5Ux?b@{ zvhPGl%t8g0K_X2>o{Z2znx*#M7^gk&OMIK8Dc4ZRfk}K`p~Wep%*Z-Mf(>39N#h5v zca&J{AP>#q1t|BoH+ONW?ccIyVI)_*G@%`<^-RO@iPey+nA?pph5{r^cbSOT+~3t=~LtkR)bu| zzG()x*Ys$TOtVA^ZW*_o0;HBWg;!7RL^<0huV=f5Z^FT&4hWhp0L4rO0S`F7_SPp9 z9;EL?lG!s-zTwa;K8)PX?!|p~na%4rY1O$+%C+GGWCFf2!dxYW_dfq@r)d%R7a7Du3I)gq7Z^7>AmU3VeVN>Rr(qGne zF_jyVJ>VZS>FM=OiNy(o3Zj=u+Gba$zp1z)YAJ`PwNN_LSJ0AJGiHUT3Zl!xN{gttkhL=}_r8+^wb(qNOqCDXmqj3i!F+KzdH zF0QVwfi_<(eTUK?IPziUQLD*KuMwwj> zDsG<7=5;z_dk!C_;D)tCs|JfNxGWcw?%O>)7TQTi}j=LktZ9nyn&2TMPd*DP|GvfK(ZpN)KdB*0={(AAYWP8 z3N_nGK3;5QqWI@pG9>P#Q#^+33I&42Gc9$@_L1kbRv#$`;nFBiz=Kl*(@B~YFx1Jo z=JClsLO3~#RC!}pbcm{oWS@Y4Y4pf=uQ)OqNp~z>C7&N0%fE>^sV<}}N-JFDNyQ_R zPgbf)c5Sd;nggMR@dyl252YEad`jZhBsUmzDEL{gQXSswkMku|4A5(>fe+}$lcV<% z^iUh0>JTgvsg}1Z&NdIcS}`lDo2kjROHWsxtsl*6^EUB`_Jtd-Ox6m-fI}i) z^uTh#sun3!)<3R@Gu8UpdAo&dqXcP49=yf`zMvQcReW4C#$;2M6ctiyv!p8MjH-be z+|X1|t4vK(zz?4J9v+y;M-lI=RqQ%J7orA|b-;Sr1o&#a=Gd#1Re@enId0Qda2vM> zC=UneC1-iMD|(w<>un{ zp_T=XRnmsSDiYKK-2)PXmV|&%@C#`pC+Ob$;qLOM1#yN{SnjWGFF#)`KH`9)ZzFQ- zj-)H=Hl}x@_0gXSE*nYbBa=ZA4?9jbgd_^NZgwCbOHLoCEUH=_E*<>lDdx;5@t_SZQF83a= zi$9AYghzi1b4mKNoX?m1S#X*E9+`>s^lAT=d0Z?gVIk--2+)m(HYPb61lSTd`%4e_ zFy(@umpa9RQ+c}S|K8d{+GK`XLSCOf-8kQWRTn`=#P`d4_&tG|Q;}`l&HAq@)d=3W z)*AL5G*-V#IzJ-Y9Q{z9n|L570f%H86ex9~Kgg(hv$zc8r@yW}3{K-kaA~Vk27c*C zs_KpN9APN4G_BmXp;^3*s@6#zd|uN>(59%e!6#Qe1db)lnv}PlHi1lR^?!qjP5v58 zEHd__vG%fi)Ihfj3m=x2R+a!@l6Yq2sL^M zyeTY4VLp`?R*Ilx1N6kzxTovFXs?ihpy_@sETArC>y7zXN0Ay1DA} z!xQv6*SbB2PapZ4i-V0`&+a%YoqqE*X)|UXp1flSw(%t=OwkCh!NW6shn6Wl zmF#Rfe}%}`{@w&7Z_^+Dg6;u3$J%K|f=%n)<;}k%aD4Ug%i?B)U=#e?d*{G?=U`Lo zX;HT}JP@KdlD8mbzEOAZ$5CM>irUfhoG26k;3DjFcealh)c(mK3dN6Km@m#3fUIM! z^n!xR!{hu0|Kz6>{1zZ=A^_tl1l@DnM-E#56PryQCvuFRDV-Asn~Wec2FJD_!LP@ z_wsBdf-?H)`U{G^A*KSe;}-pF+&gOW74FeZk-c^8%i(!kAZ~2(_P}&4SGG6IFEzxr zggmp@F24qwqqOMvQtcLAl%>{$v_p}fR@@yCqD|vLC_ad2-IX|b0O)Zt#|UFncUYp^TW2P>n?6@ z7dLlka)$DJi;uPkng*3HF*?VlaI9L(V1)|(i&9O(&rY?>mi{q7C}HGCVG9 z{Cfk%Z9iXO(em)bPrA5A(oKlPw+?nsKz%4rBCYLM&D~j%)wdg(#RVj7qgmcvhkq_V zjwPd#(yOm?Y57<#D3U-g#!oJwb6(Wk93h8Fno?rO5j*pWH8?rjUI#_7T@m1x z%s>bqp}a^1K`vpa2uV}RlRh=zPV%)S_)B4{(N-lBq&6!>XRGIx8I=}tMG1=-Q5cES(X$C{aXES`J9Hi4#RA#r@uTnl^3Irg3Bd0 zfiD%pt5Rf~)10C;j&G9DeZh%1&Wm>4xBHs~>{7UmZ!X{8qjg@lgp(zoLV;w(QUEid zXN#I5C?EU2 zY)|A8D=@)MgE>{uf(R(V1f;Nd?n5)fr4kJ!4^62n1|$T@Q8A2E5nU}vr%qh*<+p>8 zDBM9ND5c0{;Wh)Uh|LC6#qFJV8ccJgc9zqAO~R92f-8EsDH8IS6>=%@%`}a7CgCg5 zNnN03yNmW5em$wYVKI6PIhDshoP~RUE5e5HR3-Wn$vf$W(q?M3)u$??&%^%SIra?FZ%!#7kP zM`ap`GN?2wX;%14W0F&kMpAP3_36#c#V?1Kw+>ANm6EBEr-8Z2&vNTa(@l_0i6xo@ zA+$@tT;pTFUuvfH0E=ZU=b^&o8@2ea3(wAQ@B2}W`$I& z;78bEN7|KZ5Q)HN@b$BvA!`GMhRVVD0fT6O7i$~E^6ba>IU>uWhlVfj3Z2~_9J!kH zsR$Jec&-D9$ln??830aiZi;9kaC)+)Ie|^pa4nlx&E-tPvT16^KNwu>&EjH`g(YhT zUG|C?xw8kMnJuX(8_{>dhzX^V(chM6Lmi=)Fi`BYcUZI~H-Zu_vr#~D5?%|f-Of;n zSBUQ=I!nigV*YeVRsC@;!?*6{d-n+id>4zWkI`)@BnD-mk=-2xt4y+*M0F7fdAD?) zl&49cNuG9zp^U(9^aN*G(sNttXf7^!0qyiM|K;}V5`k~jjPAD1k=kU9aZyF8Z-)isndlTDy>v!CZAe6vgN+?hI!|_7CC9I|6YaoJVXe>!Pq8<6_X@&#(Ssxzmqzu!}o{H_jc$np#JepC_x$A^> zScX)_G|UA}TnRhD45iv()|kLA%0gS_hgT0ueK-@1iGKeH>V4$^P&D%H4zQAlPMjZG zloG}S+osf!^luODBW zUDX9!5teq7fQk-q^aD1JR)^S_@*htHN!V{^+@gjB$|WV7gyd=J&SEH(2ZlElD;{z7 zqUTEiqnS)f)Jf@m`5$5dC*!{qYm!=s>kE`)tJQs$5 zMK5#JlE#%y6%%?^II1y$WS3Z(Ts9ZYNsP!&_5+Rq%%E#92vCoDcmbE1N4w%x%H^S1 zd`^-ZqSK}ol;5ld`GqK$Qq&Pgqx|O3EM7uHHRS?jf&mmjF&PT+DXG4AIuo9tM0jizHlF}f9Hy5Ay4RSwyN`0}2QQE39a5c5;3?S2bI% zU453b`2o3q*Fn4E&rGJq&LmA&k8|d4dWMspL=oz`R7tG*9TlNl`>t%ay7X@P#q>xwMH|G_Sxkciu{8uQSunmZ8T+=G6!IBCX?hrSSG6v%(Q2 z85=1S_?OK(>=@Q?0;~MNXeL%(*+(w$UoLQCLf#eAP*=NL*SzFtee1To`mfFAt!Nv8 zT{TL*RMha$OkPUfhSVsup=`4xjzZ^vsh}C;&}_sp8JR+|Dw8V!=WFW-Oy~$MRTL(g zvG^M*!HSR=@0=G1Y)DRpV&fNEk&Ho-jxTiNq$%l_y!6S{*J!%nheBQw%50T$oRuyn zl}Pd{Rj~9IZerR5%^PD%mVc3*H((tSv}T%*REq$Zn*U>@yRv1or^@BLuSoD5O1?UJ)pVi6Ih1dx4@896Tcdcg$ zo6%3!+!yrgweOjJNrO+fezx;kWhsib!V&*zas6rOo7@R{2s%;XcQpR`^yT97E$)j> ztY~bxJo-O>8m+(pkROHAa;y{sZZ>u~Kw0)zwh=bv z?ttKfek*zyB5D1yX@HAI22gqS4luW8W z&L^lXDiO!no~U5B&W!ZJGXPlewGj$25tWfBTnf-Oywa_A2gsz{w>ZhQl$RQz6sDv2 z?14#q2fkWjCF-}%->gUZ723M#>BV%a2$Eo(&)+Vufmw)HWVxDnJu(m(V4m)7f|&K? z`|jdPce}W||E9jt+E+M73)uPft|>Y3jhuMUO}^ZskTszSj>*Kq_BzBS! zZ(K$+q@cy=mWI!~vVL0i)(qa;&XF7}#r5Tb#lKm2K)O!Bf4wyiD*Cos8j1C(#qcnD zEav1L2eHctxW;-A-O4$!F31#Ifn^mvWzb+@_%#~TjMpDY*cDcf1g-2xQ>68$>3Voo zNDE8%f^;H;g0swUe&uGj=jdKXFG7;&2EdO*;u2klQkYd%DXs)fDJc@kJ)+BMybi&4 zeyD=4R#X@&tBJFzsMySMRfPV$jWtF>fib2%aT`rG;QQsO$X!~qbX^Kp;{Qhd(pB)_ zo+&Y;AVWj$>$KuYbZuVV6jt6w!C>1~9#A~N%BvC06E*)Cu?;>(x&A;XpxZkn+lv+nc7*RL1IqWFA;-=_1e9Elbz!R}9TMrQ)w8tc zw6GlE$Pb34>q&f0S$oqT_ooQwmGoJU@+%C}>2g>rI0o}p?hn-kB_&yQ@1)7=H}uz# z<)gL*$IHPH0;gKpVEp3I0A8(pf`LhVkbW#xu%Jdg27cffGN0DiyYd9vaM$kz+JZ1-WyTM^IRc(C@6?m==JFXVkS6RMs*L?onM54Mz-2!P7!bcN?4( z)+5!O>gB&++vl})KU;iQpg;(M2egMu1H4(0JT7Drj0$0ku%EpOCGq=MBCtU=;e{&I63JS;vNn-Ir;VFJ_jq3B z$~CE~z2@3ojUkoz>*CV|idfhtNNI*0v9}M-q|wa~v~R_sF0^*59m!UuE**2GgGV`Qy8~cGfFDF)#^_;-Fdsu+mhqhGY7m;=Ujiw#I5)BOB(Z+%!#MaXv5{C+`5|sf;(ofv7&_z8UtB$$&{hkSk)7PC%P#WR@h2IZ5 zxFX?hg6s7B_|@^rn`4jh;X{q~%~xi?yc|=(KKX>WDcKgDS2S1)qUFEj@mpKi0d| zZ)(6WEYt6?#Hgyr+OoSRq=vDdQk(*c+41%Pin;Q66eOS~9~|UD?dcC87q#$Q#SmYP zl?_!g7ClryctqV9QEI4$=v4sH%^5(h)VHSSQVGz|oWf(|`HNTBnW*o}^TO7pJ);pV z{de>J_7|r{_XcGCE&fVkM-XVph zxydKw38oar!KIxN%7s)%qh6!ZACS0F*4=7AP7(fF^=8q($*x=Va;Yu5u8zdnd-Jt% zot$m-<9gIxhE)#t8RLNH^<@rKr$T3m>HBwG99k0d2(P zjyyHVGRE2Uyu4gZ31$3WZ#EF;NmVviUc&RLCOXk6ky>0fpzBPzc*%vm zi&7p|YgOI~?IC+?$TUUmXo_7X5JwaxEq+mcQVU!UU5r-(LF&HW}L)n51vZnoQ)?!a=sI zV?QXZH!Wwd)>t0rgX=b?2TwcB#i3i= zxvjhxY*6G$C4ZEHg#}w5@-xUmTlMan@1Qd>=28?*ltzE07|y zP{7J1yuar0-6wH$87n%m%L|vM&8)WaBM3aKjDipA)3Pk{% zxbe(!n9rQb3#B6MV0F`@acUw}Z5i-fjmyH2u`R{8Ue}@~@-ejW8U+UX0Wz9%Vwge9 zir%NBZRJMlY(=PF$`w^B2hP=Nt-}MbRvO>Kq=VfcsjAwb{#D~%#?DbgPYO1aEAYTX zE=AXEzB)Yv(*4W*LR$Dren!HSjJKoTbm!{B%`e}i8n78r@?czU32vr8q7AB} z)DU4OvIx2GeM2OFxxV^LY~IKVa}%UYFOl$Q93ClT{`M4JqNS&+FWRNTP$(k=I8(#~ zug#>R6l9=DZoDKNJXX7zuj~UNM2YL`A^Z?t?#vL&mcUAw!kUs^czkj?+huRfLbUY9 zNgL8Q5&sF^McgpqSEFXPGc?pSJY6Ym7uK|7-H_63zXkLRFpavrwLkjKmFG#61vayZ zQYja%S_^qd6QOFDUO7q_nUE5zOY+rg>|vXL@kod{`?UD5L9R!MT;1qEhMc@DRB%;r z;Ybu`!p?S5!;SZu6C$gszPEXti`QoI>uZBd9z*tqZTA$IVYN=e6WR#M->hvs1Jr1Nj8chx zQjJ8-kz`vmbgr!F$~wLQ*bGGgsj;$nfz2FGgvj8Xi{GFlo@h?T@?!7%hlkh?{L|xsuC8pIqDJ=N;^HHE2bjJ=4jI-+3$NxS6cvQFG!+ghMl6gI75|6n00q*t zU;&&Frd&F}jJjpT$dfcboS#Q05I{?5DEHzKN<`O^Vh3Ey%l)~G5=|gV7LZx6Sw!jw zb$mr#Yvlr>t|rRNTbDsydSy+#nJ5-aa1m_ztJHBb=HKG&1ObH>b|r~8+HBA0vwbyihVxHA6!!lj@~ zhveu|Y$}yz%GKpYY8ZkqosyK_Dd4|w-ABG>$?JeF#Xpe59*jrF}*r3faG zb9CK5|1@t|cF%1T&}J&QlpSbI`H)uRt7sCcX_n(AX;{yZ)I>)1SY+$x zli8_=n+7wM_E{PJt+pYZzB%-DIh}lNT1m=HG?2SFt-1_Qh>ES4^Y`;NlQToMKl;V| zy>&GQr*UP4gwfm!7ueCr<|(ESvYH|GaaE^jwLQE3|**`vw9>o@&H}2{Ezq)*7a& z=kUHrB6>H+eqcTJ;W40bW)tZ=ml8$QB7zE!niUvcP1ad$XT?0aV(JTfg}iztQE2{` z*UdSvEF0vv;f3+u_!>#C+{%(2H74+VNCQ@o%zorMW{DuOww5PY?YFK4a4q}+Rqup8 zQOh1K6fdQ<6s==Ma;K0W4zRT4Ne4AqsZF6mDNBc+SmY3VsLIm8E5n4)9m3rtHB^7I zH8BxOjV%x6-*l&^QZw-4);NUxfwIZpHELfQ+Sv7ygj&`(Ch^L-lq(ZmORMsKR#HSr z-<%f45~WhL5(I!mU}4+HQ};;NiYlufrU9Twot)fx8a1F+-m6(;w!-IP7_!uxI@1G;EG4W3$p%2k)C^>35m5Lasc0 zOBb2`;A4|xC5DojPLOyS{cP1PIKIByUZ95Z9ZT2CX^YY^g;Eo$lwpr%ta0;~Z>X?s zhQ@GMVuKwIX);tQ@@czKg=>Va5pP7Luv^^RTwS}{OI!o1s*kCG4)1-?Amyl~Q>ww# zlCB#Qxcp>t>6=#SxMGBH*^*M>Y>dg1&5Heq|IMW_fE=mRc__pxOVTTO7E=20ZBP4H z()Nx4QzhN9(Hnby40Y{HkU~N(>*txR3x(@pm-qvTiDw|+hBK^1bCj+y&13m@=?nt< zxoSgCi}>5mU#k-G2wTVRn!S*g7u^34g+WoFkq<;KjuM8GTY&gf}K#c5FXVSG<4%UM*=1`4N7u; zPCuG`7j=UJ!ze2mOnZ(lMM|L4?gd$n9xuTUlWNr*!W2Le`slVSt3mK#?J-mFw>lSE zUZ_8Z4>H8M*Hc890!NP|H&0gG{cX5}iGauu-AYBmN5wWtduBYBuE*B*fdDFEL+v?y z4U(;j@6Gf1I~r;E&(NY+)D(pPqo0BKz`tFf=I0a5Ap9TcEKf$fL~H~Z?z8^;oZsJj-Oo-&Wr<_{9G@9{-oW?=AcJnx1h{;~Xa{8K}#t4k1y% zH~R>r00ta?&(xR#u`uTLAX~PJycJ==ft1xEFG;OAg_Ksq>+C4>AQNhMks*uq=nT2M z&A=qyD4rGba9VQ;ML{1*k2aJ#lfx*DuFEU8=kUU9mkCOv$@;}J=8BCDsWeQ2!(mw> zpo%Ld9~QL8bo}FG6MM+xkEDw0?&hSF7`r|_1#*!I&gT?xorWkWhQ77a2!=FE) zUDeVj*3gh2W%&$bCM=KsX|yu>d-zObc6agN-|sKcAmL{5;r`|pX>LpQEg6MWaFdWy zXJ3&baD32aEfyeoq1N@8;=B8j>X2n@CcQ<Aw5`Dt@C9m{h~Z$rVJW|^Ps|AUtyF2A82ZXcJJ_J^#ULIIkE)?2n0vB+9y8G?vM zvEaabjd;M$ve*YY5W$G@ws2Qp(VPDIgQ*dFl35N|Y5!E@4>QC_oW!m`^2VCJfI6Bg zBoAIMO`mMCEZLOpUzYI4{|7*${EOd-rpYQVJd4WPqRHGaR(mUy`C}PY0tNO#soYL-`D^m=GGlzCpOG4e=_r`%j-1H%UJ=KAvVzNNRA)9VR8av~6 z{EOf534bjd&OtuQ)(n-A9h{c;K*|b$Orbu7FawD9ZRn~$r+bCQ31 zDeKccG>i8_pm#nE4H_UQ{&OV7?Im9Yk`wIJS}}~R6~j2RVz|A%h@BW(9T*NCkCsr~ zmmL>EH-@t30vAG=W_0PuE~?*JAR`Ty+n=Bdi13aTy@Ge7qIHIXQCjI_c8079yrBQ) zM@~=N;56Pr4onKt=z$T9+9iTsVqg+4P%+S!J)u+V{2KH_yJ<=1%=)z?#dfxb5_^CH zc%T9S(fQdnnsFeAKhn^c$X~^o`VTyg$x|_cH6J2`P$`&N`2$@0#57=%!-(g#RF#W~ z#mVcjs`_UXiSG5#p;nei4=NwRXOeNUnw|2>(y0|o3)E7Sr0pOVPB4anJu4@1&q8=c zllAo9!=2rtZCEk3_;hEU9C5{YXXe>=GcUUChtv@mrNtyt1%Zx3hd!846_GB&C8b1< zhoouv*#R=6FK|%h2$_TOZ&qbbrb)F{f#U`ENOh93?_d*|%TD~Ois$iK!*nBYoNGv` z@wz1bb4yax$-)P}(3-#n$h(j%BfdGZ1h4LITh@wxPHRQBCF<&E z$GB7rzHQgAW4N0BkZ)T=33m ziTEKW@i0;m;Uk<|#BSa$T|9X&)(vVd|KHIPe*Gu@`}gQ$Ib`0gSX#hvF(?!`&~rz0 zLP5-NRcuynWt53RbgxjR{X$+Mm0 z=jqeu;KqkDmWVuq1O4pg;==;9J92sO7jC`4UZ`dSdQG+9^ktSNbdFT?HqksEJ;_~n z_al0cPN124du)2Y^aA-5=n~l9V)^2%^)280VU`d=$w_~NoR753CxhlimH?ytMKP*( zgO<&^6~)y8KMU5%%2DUYOcY1bGTqCCV zb=~o6G}C4)6_{sp2x*iu2Lk2%RMf3`_hvdfMjso{Mp941X4Y~2tZT@o$kB_N4aYsI zD5vtz0zttA_Riyn`lNv(N_q+Or_3P}DE}^Aw5i71Vv*-AKSILD?cK%Q{cVkDk!LS8 zj>_1S2|RcjrIT(CJBD@R?_9o9q@uxpbhQ5;CO1WO)9?u@2^1+CQbwfcaY&1Hf0f9Z zgvNKVRavuX8lOm93I{-%rt)BX5&5e1AkdJJfUxZq`%#878RgYVu8P-o{V~?<+G7p} zSYMwd2t%|_2Rx5;d4PBXlY?EI@3}GntvEZ?S~)T3T@rsOUdYUo}UI-A(^HK zNK|$Z%j4|qL{PhWP2Yn4FfW_sDnJa(RJJf!+62j_Ncfs+5-*`KB(I=iiNi&@uv(r} z3gw{950U<~^A_%vN*ooYiBK%LWVJQ}llUlcD}ppE7k6M1FOaxK;YH|Mdm$<)Y0xPw zsEWs-b{i;k)L;w=md#N9_%$YD%ZS1J<5RlAxB=QM#EMHwp{`QzOCX!*#@t-MAN=!Z zXzcpuG{#soQj9V;Wbb zC2QA)y<}AwO?2q;g&S6;1v!kzq=m`}@dN~KodbW2ns;c?YroiqU7=H_!oXasJc(rWKa@-_yVjOZdlKR-|M%+!0=fH-W_XN%pN=CT2#o-QL6#orw z_jOBtRQ<7C`fGI~)&64XFQVuCRO(R$Z>U&pw#p=aULykKv)_#hjINoeGy00h9d;^# z!DYjbB&LN(C!#hefpIcL1^bJi7Q4VynolFGaLJnMof(`~Qj)BTrjgRGF)G_{B`tsxL zlbauJmq-0EG!!^JV`K2cGeJpV?)Cm=%y*J5p(raLFu?(aL~mU4 z+3Bgm8Lo<2_T|(Vaw@5(!*WWek$~IBB_i9xD}xcP7<9g3Cr%%}k2QDikL&Si^9+53 zCcog2_?+jgrUK2YnXN*_gB`*;$@-QEIZ$TVWiY7R2(zbYR>tt1UpdA@dxTbM1fn#t z>CS^&*_ZA;-NkpEE5%zSBxK5#Y4CbDnIc8YtIr<8>1!+Z~+e?TrJtSeP@!mmxWWOzESRvLTmQ{fA!k(K2%UMy9% zQCCs{xJ`}QTw0mqIDzJgXkK^D{udy(BgBmtUHg4(yp2lnoswM?M!H&%BBD4}DK$s6 zvr!kaDrD(kC_g%edk-dxHJyf~&+Ee(VUz&#HUuQQ4#|XfXfV6DK?}L>Pi~)lWx%g$ zVGZ9M@|iJXY3UXPV^@HW#k{}my6+ML{LTPiZjX&APeyeYY-u-uugX`Rj=Jvp)2G|T z9j0im~dH@`ICx5VAzx;G2S-&mm4m%wx%n5*lOEeUf0 z6O0!5SF+&hx^8_Qu3cNwX;AfiXc|v}?~Uz!EZ*XWTeeM#nUvIBVox0I1J)jLLm5R- zNhydL;3a^e7F-Ql1}$zZ4V&lCnQZBH&q2D3Zi*|Dbp14(@fIh?o88GVDwNdbUkYW1 z8C(h#RtDaAu>VhUre4DnW{(b#pCu|ee@Q!{hLi3K1d&FP%elOcs3?s(U3V~F@S0tQ-ibN9nPdtGnsl}Cc83y4|a_WIee2H%NwHAJ}Pglfyz$Y%vhLZV9CkNZ1L~+ zi`$$z9T%#qg5rJzs^{JQ!QQ@;l&~vfR*gyAVe!CAkPbg3s!IA$|GVGO&#`HT+Har? z8*7}Pq^;Hr-Z3tnOMrD(OL>Sin|*f+v@(bOuI&h_t233on!?LZQA{Ygn;{KO4qwA* z6e*N1mh~BA2FB6yxdCoTwc)AxmcIC{*$FZxndM)K*XB!F48U@!he|D|3uv|g%5rh_ zVKFf|+kLvaZNgIe*o2y{YHvn=oGZ8W_x}nAg#7muZBB14ZhrY!^rijj;%*_IdHd6U zy}0@M`NIdog*}Rty0NKwI|{It+^Nl-J?V61Us>PDUMerxqH=j@1Pc5GnLgrC-aR}? zH4i-MA$DRth3~1QhdVfXddQlhN#|1hze-Z1K2&`h^W+n@`E;{W3(Yn3+>0Q zKY`b57-rxuW}{gA&5xgOOb+Iju9a?}NUutui3Wn4iNZ%BO`ySxBW=2J`m$~b))Ebf z0spp&zBZBX8hi-CNf0S8H$psgIo%>)gUS=%oWeUet*8|X8#rtHvWN~Im0_t)SdIc~ zGI269117|INYo?gaHt1TU7)-P`dOgBQAy~ef@J%{ z1ySbD8+}nTKMf#XI|IF8rRd3A@cp|7dr~k5Fl+yTQIlL>7hhpqiLWK{QZ#TNS7{CNgKnsEcAV>>+Zqs31WKEY&)?bmDSZK zzahsNbZ*v!P<*TphCfBAM%JiDL*%FxZ$hFTSn_NeHkUmOm)2eK^TQz^XDWWz?F|e$ z+fR=szISui)YeFQ4i`YQ!F-eocaM@&s_-~}hrWxsBPk-}QjMcGtXGu;%@XGUT+rTm z{E*qgN3VBbpv`~0zPamuLL%w)%`GVe#1#@&sTIcxfRV=u(@y|j$PDQiA&~fz%?-`s z<0_{k=qV@egdDg0Ax;UZrR^fCa}1`Bzksws|3X1d2`Pz;%VwBN5y2qvud=C*OGf{R zraCzLuc(LEDlE-a<86!!^XWkw8Z(5a@;S0L>a)**I-TV7bxpaW0sLbB#PuobS zi9&G7dwtA*avSkPh$yFaGyGi|&5%ch6)U~Dw)p~RV|HiQcg^-pFc5)p%;~PqHG^+w=0LhPz&VwSS2{4v zy{c;DUd|Zpv_$Ti%zo&_r8-dGPVEoA36Vf4OP0Ejdq8!`Q#MTp5~Eg`U=2ngPJcGY z7mRWfZj%_~cq0{TosQEVLdcOCOq^D$;+Hk!QXx}ViY#!GW&saLsr1NZ3`9%qySzPiZS+CqNG*qZ=nT!n4t6 z00fq}1`Uvu9TP7=qA6jA1ZW%*>0^KUe)(l)RaU)vpvIPWY!N_L^?P;XefC`u@S5#) zv$sp{-rqbdzk_)megMcTOG}>n^PEG-5(RbSR3!O<@}8c|pO5|!KRKGtUp*iFd;Mj3 zWc)JtTlHo6M8Etz2Z74nrl^eMAvKUs%4ju_(w0n@CEI`VSWwHNBLnkp3(3yJ_lL;Sh8T2~o(<0D(F`hGPqI zHWU{gzi_mi32hi^q^mK33)T==W%=jb{wQ6fP#dvViNihHtN572?J!uboOeOt&>mFZ}wGJVWM=NCxzdrpo>G!>$sY70W&evCCa6=>|{QZ z3k$YTggzXcp!l-+0r{`mRx$FF-R47Gdx)6Wk--waF5)FWk5SAwOUU z0&)>!Umjq4j)9r?Nciz|7sN#c{=(uDr92m#U-Z&D<1EJ^^rq1khJ-7!u5%kqtk@NNbethZo&!*fZG@j9=||h66EWes4LI$TfHD9wX9;BQ%#g zn*MLZ=fdM5rFlnNM#3$`ExU&Z!-{y~9xAsa7?;Fa7ho`p`yyCS)|J{t)7~m-qfk|oo&TMBUt!MN{ z1&FLtQ_kR5mFkG5p@G!F*P!xHl!oFoeKB~H6uscW_YvY+(zYm}#qP9HK+_|uBZfJD z9wRBIVn%UVBKOSW1sbsCrCC2AT1norY~MafAJ{eca#wq}A(%wEm!kixQ|yiN6bk)) z{|$13s>N&O?s_++)ME@Rh<#pg+ka}|ZKQXse37Vi`5)_U3qwnEIkjsn*GR6{pTvbG zmL&I;SW+!H49(&roT74dIT4sDCU&5IZNB|q_c=Y(n< z1B9`M$^0yZ9=pq37)Bc7rf7jComO!{(=AQKsRsFl93Fcye;E@aWvNK<@2NXwKu*`e zRK0TmTa@>lS)GtIE156)^6KVU)ppRT2}rK!rJ5GEEX1ndbuQil$4YF4&UTVo={6-y zwuAfd!2noE$IeuALlz<}ZKqHvwNC|n4F}U>DICtzK??wChhQS#{rp|;q?)>45V{E` zu_n5nSy?(NROsDNhXN{M+LjV-i!UgGij-L?a&taK8Gd%B z(G>FRgB2g{yRYbCar=1lz&#>E1$&h6VBKEQY@wF-47s!)OLL9f&}Vvgclq@YS@{5a zxq0E)lK3>;hLstz0D?)xlEQCh`$_)o2q8f$UnWX96dwj*2Vz!H;{?#ST&34mFLK4@&a>aO?NJyN5Q-1!88d~KL)qJ9ojAfgq&?=q52XyE= zv-_&(LI>eF%Ln82Z&q*|uMJdhRC~WA+`(E2-Ql!*y|Xh~iW~-!lKY6asW=Qj$W~Re zGM2R_yQ}-#?&r&!tB(uKQF?O#BG2&Oey;DjdFm$yx!hsGbG`i*c)s(hQ0D+;26apz^E{DRHSqL9*`##<(xFNI!67ybnO zYI{!ly3p|%&E(GX++Bp}*-CoD{L=YE0-Mfv&yLXxar?(}lzKWo8;w^UEAq5n z`O%UMA=Ng_72(QtKQ)s}h=A(y+h$JyQ2Fr+*JvvIx45NtknBEJ5zl8G3e_?|vB$3RhP?M!3 zTsS5^3vG60C?PBuE_M!i28=_)LK0FO3u2$>bYY{2@k>qwtpm4zo(pZW9 zagxLSi(2X)n8b|?uz>uY_~pa~(y;LF?0VykCW%A=3UUU;1f+sV`#rf!9QEu1Mx@uf zOYc9TwbrtTGt#ss&4`}|!CfFle2_J>sYp|u>r|HdnI9bOtWJcnH>@86VMNBKV)B4| zyN;+5UXYDy<*IsFAE5rBVLh#0Nn*A4naz>nBXtEB8(AkH{aoBkK25>sDa-NrvmG2< zH7_P!qXWw3Z!t+>Qh@kmqpyQQ4E{-3(AHXmv0@2yq!Ih4%{sZ2{BV2%EoLSr3&Bk$ z)1(k1G{cfGB1!)ivNQoIWg$)cgcEOYURrOWS%%X#2ZXiKIc)K_`7Hfnf&4&7S;CUm zvo9iGtVL?c%vv*eyb?W{)CU4Xh3V?{+Jl#EE2?&bf;yKKG-JQH+WO<05s{yiu-6L}8iO)zhQc@xZ=VBQ4t zCYU$Dyj9Fw#XM;834Yv|S0^W=)@Udyf;sA!C}FTUg+Q2P6``~yu4up*PlTdKi||S0 zM#c}B;Ucnt6{KW|YAYwSV?Qj5tUG@E`0?`H$HhpsI$n4PnZhk;B>xi?zrq}DF2SoT zDTfkvlqF@d=zq|hFC!{i6 z**hdZ>-YeDdijrU&SL4xL5CTXoreew`q8ZS5Kcr1sOK6an9=l0;a&TC5T{?GRpGg1 zZ6e=2Q*~@9SnNKFvFM>&{POT@_hfIh^!*q$=>VS+3O+{rsBZrh|JVj>{rz$70loqR z$RN1`c}h^kyU|*ZsI1DKpM=6nu#_q%;&Z}Qg*t#&7<`vs9-O8J+VD-Sx64zhI)%_% z`WO5so?>@Nr#MVk`2YN2ty7r9rmd@{r>C}G-4+vj;o_E?J4F~AjBv-(qutR`O7BP5 z)9qb%ee>bAdqC&APWo(hHXI^5&*H0yQzoF;KKK z$ug^`Ih)_KTFWf7ylA_f(O~xS{z1BNLPNhSLu`D6)x0e?F2Z*$N{58SrdU0hn{{)B=R{%!@BPJ{?3xFsGWO4r(D*- zalH_^l6nSL5%wwHME|7Ff~6D?E2m&6%C~CLvD$^(Mft`r;G4nXzpSjR?M}_Ri(P03 zjF!!#_KG!5#T;Wo7RmLjAV?>zw;_!|7WDwfGD|NB(a4?@jA~vchYC|&t)ddp&5UQk zeJUJCi-b|QuO8cS%n3?Vj;u&bXl?a;qGyP z<5K&wG%rKzIw>qtawSJ_fg^F&z&jff)g1l=I#stX0#S>rsC})~AorU{xI8eC@Y-BG zG>eZAW~BmX&UInb0&_ zq8S-$5&>3~&qa)P9;8Eo{fMO%F_Tu!R;hSt%Ky&>Vs8B&fta$vvu&7?Sd@iVfrJ)R z8NB-{o~--s9@PRDS6bZITW;Un+@gcRVoAq*ZujBw=KaIZYS8j$ zk^$1)U;pbOT=eQURTG38wbc@$9_@|5&_e_`iMUnbQgaG%sYx5fM12P1fBBFnr}AM= zz{r{pj$Ef{u!Lrn-mLwb$O<_8hJRkv^!uS#%D8c=7Tgeh4^yhiI}Z z*hGggRd*2YGhr%+gnptBPR$|-`A5$ZPA z1_ij7Y@osZ_C!FmV|Q~&-$LUPxz*v*$;ef=+|}of9@o)%xijm(m*ezm7*qki+w~-Fv^*?DNIwq zJ)YUkwh5wo>|3|Uk)%8dSB5pIkdg|;arfu|{c(<8YEJ+}n39;al6qfMZYMR%@7JUy z6GN&3yN7T@c%>nYa_{cewld=>B(DsCwtGDrR=B&Mq6=2ojo02?gYQrxSLu{7&(fpO zJ&~Yyb&XdipC(xrp@4d+w5lT>K;l&I-|b1lts%Gwa`X>aG@bbhQ0@2cpO4a@M>_^v zDSeVq(v1H4u2K}d>|RyR@{TfOO*Xy>Stx7JhAh(0>Dk#7Vi!!4OP3Oqi;4xp9Z^)+ z{``lL{cRt>It$65uEHT5c~Tx1>kpp2k^;2waEIRZh2r5H|-L+fiptk`-+mJ$7a3XfG;(V z7TB}q(fKUGVe!d!SS57&yQvM-+EO*h#X@HN)P1_X$!MSD%{sCm+$Lk@UW0A!zXEXR z;g~UGcotungqMQ6#5ku0JDoy8{$-IBc!3LQTIk328tq_3AL0eML8dXzw>M=u+%CjE zh$rvgFYdv&X_)ZCl4U~OMVL>_#|6meL{~)Qz!o7YeKfp8@JzW!y}ZMrSzK-HAgK+t z{c(P+S~?u5h5(cPJ7kgdqAFo-0cR*dEBj9Or^h>}sIeqBFI`ifkFIV<|29Hw zW2f#?jY7Zc-a!dk%F|G0{Xa&({tOQ)0KRD9rW8p%=lcb^C>QAwprqpPu7pVlkCFdy z0)|cn@yJ@}T90@n**57{Y$Z$9g&4EMx=Ip|f@tCtp1*`=tcZmm^@#+YNbw@pCqpCx z7aecjR{$${dFU!UAMZl5**h(aWf`PGc#4|yDoWuY@;n*7It_F3wc`5q9BUGQx^@;{vYDBftz6_EyAR=*TS}PUQ+|o1!(NzYotR#}NhD-`H7q1?^b+LaYruL(h7*tTd zA{thiPQB+6A?U?=9#bOrrPJkH(9SD!3uzKgOt_MVN@xhv_Je&qNBSR4k7wCz2>)0V zgXQaI^Z5aGT}hl3QTIu_3mETME@_MoD9|FlNY(qVDOwD)`SuZ!l-=jM#rx}fm>PN7 zTiGpd*8mwOiml?#RPpxh!5(zaVm(|F= z5YkstANYso^;xRFqVkop@$mc{g`#mA*-SR3T?&;V#O$%pqER5If=7AL`a$qL$xXZO zDc^NPu^}+#vB*7KXO@>Nu}+q~YC_tRf-CX@a*QW&zKo10JxH2WEE;P+euN|eFfPG$ zY6d88v_H-}6?=*ltQ z|A5+qGUEM)bQ~U|*GDt3C=^9tqk-_--6awP6?S&#+Zum1m+ah~(aIz1;{pJXTx308 zKYS?7iHRMpB{{axk)0*w;Yio0(Ve3OrMZ@*v~a%1GDd-boMdw6vhH;t@Y8skN)tE& zCR=JX%tO@^C&dv-UdU@L3|kzlx3`l>*0w%bCjeCtUK9P^Fc-v6v*d*{$Y}8zLjD{b ztCxO}D@VU7DI&?B8^k+Fmhu+t-+%Xyqm}2Q|25LdTbPJxfBF-~U>e^V{m(q`3+y^= zLRtcPdk$Bdh5$H7vl{rG4)P13s{$$|b#NJ74FJ^oaPW=}J+ppTiLnbWP^~#D?RzH@ z(g5g1+C!4%WmcJPLEvi3$7>raE+B8N?`ij*=j6Rc8}_G%2+>G&3Sr|pha=HL^Ty9o zX@nSy5~ZA^RiWoYd4fZ;T96XsdjnJp@ofS&0WD|Eb*UIDQ3)6Uw*u3Cv_w|qp{XU>2OkdDj2pN0E!6D>M59W|$Xs9V!C#hfIw^n{VoRLq(Y7Qc zLeKQdm&&9drf7SiXQHuIz0YM~(#Wo12b1Ui26LHD!;_3%96%XHYn@yktPD>fOUZNi zJ?g9^rgE~mSyBnrL$mmZO#Z9JtL)~Zgc{P4RlAmbFKJ~qQg=v-_cs)vC>d)DMa1BIkQUT=-RIjZi;bMrV#uEX%meL{i zqXPK?sk66HrNoHTNdx3iO~sLy9fFQ=T!*TAnIMu<>a;xnSz)Gfa$LHEy6}QAU;a;GU)S$*FoFki=78jk;IC>dGZ*+EyUb@&Z@PE11<;mZ6gR4xUMmr;;s1_)TmZlsUc; zC${?<0c}E6C>2#Ni_b`qnp{ZHD$|4!D9nY=AjzV9-`T;-m%GSLL$FBp;~i?x zq7Jzfq$jbKozZ???Jbo9l+ERyL^Z7#oW`Bz!Y{1hCA$nEq4H`RdgcD}OE;)g316)a zU_P3YFZ@Z=2>IER;F>eJB;H9@nu~7D<*SP)R;W|5Oj{P#X3OU`e14L473vfRv z!t71SMG;Vth;^ay8fYWJrQ8-dh1_@$3?u|b$sJh3!6|}*;4w%2UQ$HHGDG~_g8Z2i z>cgvgv_d#tXb=RZ<5rYvUn`o$_6HKQS`3rnN^B+rDpf^k;g5eU26Z55HoWg?0 z24cJ}XoSLmYAIpoUBKmWYAyZpHP zadGq0!_Q;;IiX3*KEv_Ij^!BnGytJCW(Z#&wR%lFhXL#lu!$|{gYeM*{DZvbmFIx_ zPaM|T)%GW{N}^dfPGu=clZ<>tkg?aohw=tIQH4aO&nuOz)&-U`c!yE0a@i7b*TaB~ z!o=mHBh+J%PtD~5AnIDIw$wrqaJ24eM%7R&M35hp8seE(nyU)UX@5=^1YU|6A5w&@ zBG6TSP$+dtOk8;i+!Z6-UQ?k3D2jP#NKQ+AL8B4FT_(#=c|ffhJS9@Smlv`9=y;8C zlxEA*M21jZ5=(!kh51r?C?@ccIqpwlW;d38|I`Gj)+oiLB}%%R?un~yytEB3CwfYV zoGboLdo6HldDM<9WZg6vGemmbeR~Q*2YoTo~phw>7i6R{YQ`K0>WY895tWBf?f(%_6m?G;M;5 z$4Jl3U*>#oj84V}$rGuj#Xb}o$@@Ew5An{3K!&E3$PcZr;WcO8n zMi4To$8xp!a4kM{mw2X94esQaO-ag<(YO)d0hE1uOyz$3k6?d&jUH)JI8^+n?WAF! zrn%Oq`A z4=WVlgxbBc3n3fL$^k3re6@7N0F>uzKfG95*;wCLTi;qk8UBs+wFNr5u2ChEZ^@ZL zuT!?Qx?=rB< zOkIoRp#iL*oGzUi;>HMO)WO)DzJkJ06wCN^arswh7xn!}Rm9w^%5*4}HDF8a@di^W z2pj>RAV@l`Rts?4kkbUTB63Vs#xXF7P)(sY7{U<~-IGY$bNF=$2E&J zzC7jYuoB5P6DwpEQs>i#1dpDvu>ZaE|decq}4o=XGr+?gz@9Z zFqi*P#!laW?yIect38A8?#?Lq13GsQDAkQN6D?Cg8?>zYQgBjxf)21DL80(>F!BO9 zCtl#b%*T!ib%LD}&+G*md_7gA2AvNrl`rk)$?!ZmAWk6Qh!HB+?9RWvnyjk>!jueW zf^b@XPZUoW_TA-OVj~ zSag>+mmk00U*7{vBy}Et-)NdVCOrInx469O?k_(q9=@iTZV^qCbGE-}>R;3#Wg}^d zVtU`OsMsi7;K^~%F1kHbFfb|+?2oL7oik*dB@HbG%O zb0QG0ldnYJ5-t@m4pShL7t;eJ+VK)Sm{qtcGm?__FDp%zNusS^R1a|gnb}R773GXn z{;h|RdEL?O5nKI8Ogrve0#&`5=YX0zLXt)m*?X4EXqlThy^u2zDY|g5^)|fPFvSFF zi1^UcSBR%1$5`{KnQ3QG?(K__(%PEoo z^r9qAmT&gAjyG7=e-7acv{*=sUuc15`E3*qnlE#Oh6tuead51;1AB+lm-E8Lkgal3 zmuC1}5spxpxZ|4>m8%0}2PW~N+kMJQ7-`Sp*PU?+I!Rg!VT1VG+=C`-+BD5mii`7$ zLd-$Af`Lg~f*>sMgti&^>r}~=;Fmc<(Ex+`0_E)Yp-g$Id?#{8e2Z&gRzFn6>O+lO09PMNPwb8a#BU*@|kSG-9+1KN$3*=eT}9$~>$Z zQevuRqm0~zr!xCzQ(PBPpapVXzFk zs$oe^yq>|=J%#qx-{K0YMEh%Ty?S*62Py{zB8j>~_!bQYw)J;*os zr>-rWZpK3eh9~lToN36}Q5h5Xu!_h@9yy(<-@%Kep-V_DXwv4w?~>y%I7sNcg!b-?QREVx)Glb2>Cd!#jiY)s&j90Of+YoWdv+wwTSMqQB7f=6qh?)#fM ze6Yz}sdV&I+b5^;m6=Gw){*j3)z;!yrTo1Y4A~nlwl0(>R&GpqAx|4p>Y{O28Nr+5 zV1WrRAScH>JiOGDzG#4lX2QG4wuby$6DkI0b5luk$ZA6<^R6Tryr7xbx{18jLViqY z8rn~^xVNimc{q9~*77aoyUQO&1EAiUYuG#}?TbS2w3y>UY-<=PaRvbv)sWco?4doP!u$4ovVg9;H86ApZE7 zh^-==z!VMdZvG8*?>aOG;9Zg#kmHjqwQ?85Mf!4jaC~O}Ci06b zoarKopvVCyQv^tCb;c<*Q0AFfRvH2bDk%f4Hc-{0TwOl+-_UGpvN_VO3v5S(9^^XJ###hVO*!4!aLf>FjvS*!uOxAKXofb>2KjQzqwx!Lz*-NOcDh0Z`tr;22K-6q`b(;F2r9WAm-G-NtP9EbL zkbRkY5P;kSd+pPP?V?J02zsl5$5VDXC{5?ndbdd`wozR})9|~h>QsyA=}>jGPXz(W za$$$9j=tKDOyGK&udESeU=kl7_L`)6sl}|k^u`4Lq8ZMa#sHYZ$B0x4%>AVVNrf^_ zdaY8)Eq`c&({T9HcmwfIG)Jo;9y&_*dhZlQctm!d9OV}2>AsFnaAVSXN){xY#z9x! z3%a1zmgLbOsr>f(>N%=1KmYzu-G`5tKi!Z1@QF29lINTz^{JRkWOza@3bg zWqbSLxAwZ*uLMhjK!M8;50c?Be^`d@S!2pdN}$S}PCA!VuBIgwOoSl`>-t7cnV-iC zGty|Zp5Woh@!^l7rIAH=Ey+hBU%?E-$nn1m11f{bh=H-bI#o$ZL5jh$=?!fj5Aoe4j`_A&&&0I{0fhL&iZ_h57!^N_jlJ1*J$U{ z-7S8SYCxzJzPMYGv;t?^Le!K`LC$BP7c$EP9AK!CV+V3>JkI>F`N(@yJm|M1&x6yP z76gp~Tmxh}uESB&<%&s}DRQ$|Ty&CQ351B z`2wak7%)bXAcj2H=R+?@J1yVXz$`A#B|#GG>XfNvGq!|g0-z%9M2HG}rpU$M*9%QjP6T!ixS=#CKX&foNxbbx9TIz5q& zjotY^xX%8`;ZFB@f@}p8?*JH!8x2S4{r^POmRu~WDlH=x(151pO^e;?`0&iL5lJ`| z|D>@>dGocoQk$<3UMhkHPh3_n6CWmqkkpqsi}GdXfikCd{zF=GRi>(Ng`WAjnR>l$oyt1{oI{8vV2BHOm zTIjf=94407@eu`v9JQkKD040_M2QlvKq*3;%PO0J>MJkzcj()DDZ5N*y?d$f6G%JTdKn#!ES zomGP8z$7liw+(t`SWMOCJ-VNa{?PH`2IyV+CiK1-iX|4jPe+tZY_(20IE}~Dy*xbGra&ex&hDp= zx9=c*U4LS>_~(baXW;9oQy>AHhuY7$kGnk%Rw_m+43!62THC=|keG#ku_PBzvw=|{ z#FeY+AXO#Z&jV8kI7PK^N^CcBgLBhV&KnzWE1Buy$zGjJPxv|nR2e^}Ei*li-ga?@QmE}1ke zcVkJ*oR6h61%4NafuHbK*w~wjpd8iHAq{B;4@e^Ilfd3Gg)}Dci_!HuftV^`9`3V6 zY=XvqxwP~2WInHr8#Rp_n#D&54blwppmSmb1{x}BOqY@3fZe$V6O#R(!Ho+Je=ITb zC2*w9Tse`Q%Gzisj}mZg%Em`E6^ zGSmBe5DsY_wGesbRw|!MbIft~Mha>7BSi!ESH`^Z1F#ufpBQUK_L|ITi|zan`=&ZacCP8cwty@I?Co z2cn4sxSQmaL+Y0`?})>Gb1)#4_fKd{POF-r)T3N@l(m&yA~YZ!u_6y8f968Z&VD?F z<_Y4dS(}jPAhaU7GA=J^DJ!+)**<^`MXLA+KL8zyDK22^(5=gDw<%=|wPO<)Y2+xk zCYFXQ^05(c2eJD80dXD=DZO%BtBCTo^1Z)7LEQav`TpVcB^*lhPl{G;CNvGl2AAvm zVTNWYcrubP@v%^bID+_Ri=DY4_|DQ&%W^?~siJU1+ zPv!Asc~czGbdGi7@siaEpa8twQlfriBg5qp9}&}R!h2T!w$cH*nt=)efyj9#zyHLQ znCRF&-aOvJoOXAKsu zQ*v7)?{Wpa_I6pEI>Og2{%UMCH*QiaY_t6r(ouOd+yCbmfB6Bw1+)>;hW={5e~}e* zWcwY8tnk!8Q|wgB@Caf1OfTHuqt0T{BE8^Ki7_o%2vvHuTS^r)xn#lwiRUnaRL)zu zeq~c7e5>s9soJz@2J<*h3QUzH{hG2zF=|%RI(#UF> zR;qdCfn}RA&(p1t+ z53wjB6eN)LzAUgHZCJdRB^)1q)VVJb1lsA*>dA)>_Ab!v?tFr9+qV!!0a$p-9MvgB zOm>ovF}eywXyoX>kE&raK*%?%vB)nlto~iXZ@yWnwMR^#c~JyL1#p)t%k%TYG3-Q5 zwrn-$ip5il7Ce&jcmhASLmy&2SmsP()axmPIf=>0{UjV_HA!02K+)XAuX?h^%JkA1 z94+E|<~uR^=q_ZNc02{cY0cm=>b7sSjiNu!uK@^nd!Qsa{v!@=)SOFg$dc zgd*@rJdp!cz-$yhlnXcYDkmO|SHTX^Z#_wVLfrt={5RC7cjuvBot%j>PCsS6oBlYr zmR4xX6Du|bOp@cBi))@RYFrm&L72RFkk4l0jWdXSv*xkN@7dC5|#SL)zc`h|-be5Id-fjQK0WiR!jvhi9(TMLLQ!KD;keGX5Lik_mK z=TvHeL{hHSK&@ziuJ8gi36bR&HXw~=3V=+|AT&z0^D#=QVzKs%t<}iRlT*3mCK9D~ z#K9vTY5-KsEMBeKRW_YxQM?SxaIMdOYCSjRB2Kg}@vvY5f{pqyo2BI5di#c*A}M>_ zlH}p??qRrL4o{y;pvy>9w2QfxCzYm8N%thUw7p?9=^UEH^Vbu0sa<7u7jOXeUn*rN zGQ|?XTt@*YOq{V7HDrx@zX*)0>!5XZ4+s#vz8d}gQ?Y!~RnHYBo{p~D3^8iDpvf4t zuL2JdK*uRTZ)b)mNDat>J27(3*%a~cNNk?nB6=8Q$qnayUqX>2Dv}pONk{^1{87*5 zxEpvxU!I*@oQC55N>OZ?ULAgW#-&yglFJ=nt=P{{V6fJ+RC{kG$6D1sx_Qua(`zY( zY3W0rHRX-W|5lvA5VoSgM$Tn2TS<(DW)ZeXF3pKnmc1k4=j-LskSC=vQWSG5>r1;w z^S~s&JD(;um_@k~YPm$wNkp8Mr4~A^-d-x}lj78jZ}x`y<`Q6F!KmOdUq>zE#t}X$ zn_NV99Ir3^{<`CM?f23hYx%$0cHm91fc^A|0`?rsBApfhEqVo6P0#`L9i+Ov?R>xx zEHafNyjPc5JMcB)Vt#UgV6zU2to~fwtbdxCVYq_!SOASf*<4o!-$w?L?d}6$qir8x zv8r|kTOG}GrGp{`Fj-g~~VDZ*1JYG!%Hi?k(PUPXW(mu-ZeHY6MQI7Rad zJHj!@X0&5R5;*KV;032)30XlAx-k$vfY@tP*~I@#O`IHyqXKBZXdh`2<#dfggBDW$ zm3OHmEfHqsah4RfH3Q%8)u2jcQ_8QG`^&{u_y~tn*}7;daBZL`y^5wO-%6W~l!z4s zEOs^FKjspS4#qHO?&D|h1QTxB?MCy-D1IiMv28i@XkHNgoXBL6~6+h;RlSpbEf3>WEBHM`*zkX;0n`;vu;&8aA&|k=z6(^-}LV8 z^6Q~Q{Oaw%ym7p<216*cAExkqxmqmVTUs_5V9~b4EdMA{(TVk&6)&C#xcjlhv5#-#ihY}O zb5%RKT25tT25Dz_OkEw*VT|%Dboi=^NFc2;gTyxqrRiwQA`=oTUDJ_AWDD3pjCVDG zdsTYj>>bV(dSVS=#~o;NB1Hx8xezkw=aV&f}&6 z3lofhjqc=?6AGi_gwX7SsSmz4%;&~~bQlmWQbeEz;BM=e8Fab0o+BslcsHK-Pc@>f zioJmCvS=Uel>vu15lm)bJ@US)VueGP;Gy_KD}3iUvzRq%hRydo{rBSL{xiCPf4G)* z)hk5}D;}I$?!d9aG8oYKFIS8AMLeVGr#WP?%>`C9J|!N%a{wK(OqJlRVTU+ZJw^%; zVVKSwQ%n?YbR*OSEK+pwg|9k;D_hb0&6>7rD zjjfDd&<02T(Ik2>Blw%+u{+e~Ve+5E$1GA!EF2{bN+g{uLB%AqgIHmoB#WC*M`gAO z9`zz6HWB0*CRd~0wEsEIiqNwfK`#!#fD?eyFVpx`pSx)Ybj}5>hmo{B6dLCqs%Pm8py|0Wmg}SqsKX464?BU;2K$GC}FH?!)I>B)L^G zG@ur8XA+~S3V)N@ZjjurtZ6xA#q)K}Y zLxL__9`Rd;6h?cFHY6lqDnLPnYPRUIZV~9V;AT^sKfu*(0mc(Az7_B<9Z-8-GZ6f&K&?Y9U;W(yQ! za2(@On?|GTg!awpG5698dc%As*|ga%Olb+~;up1}(2b;2nB6VmfWwy!I4LYAvL^0r zj?tzqV3&s6`cab1<`gb15mPaXdvlEP%grg(m-V}nfj#uyJcD1&HVw#zaFRHOP}SIv z5#WUgWo}io;VN;=EehpJ2PW~o0aGY4Svi}-pz#`GU?>F|tDdUFu%yjGkhd$f_Zhef zFIx;;m2(;}K2)VnLq(ZdF^;VB6W1d5TtQ_CRjIeH;L zQOhezKjq-zpyyqQEkl|r@V^9?zSO{t%2lZ&%M`-2Dy_a-5vQ~`;+Ae8rR%!+-Zxpy z;Rd2?k)@v6WHB_0j{puJ0uyebqk|n-2?MMsx=+lxTP>WdKU-I7ovAm>cLgBj!#yE7 zDzBmT#_8E!s=a|qzltC)X}nUs&^{52;Rh6^df*6Q3hq!du4;Nx;9AKeoPIgn-7B&U z(m?v#>q|h}DApq#^{g3sf`o%mrvAyF~tzn4nD$3bLPQX%qAgIjg4B17-H? z4RzTuQVvtfl_0s(#Om4vUx_gCOe0TCVY4xbOD9>+O^8!lV(CV_iFU*kk5_iL_e{4+ z1{g=M-JKddn^X9*2)M3!Z`LM(fk}KoC7>#l$Px{;`s4gs#&9{N=>;Q|V1TDg?a4lx zGo2-<&}(NP&lWlkE-4c%kQXVqQPDKAJ!70q!7K@VZ@70-u4FLeezcU#2kApgw( zVHRI8Jv&4B1F?ZGUEYamc7Oe^(oULx{KLrp;-CM#@)xLNg2{Wx>=qpMpSBm;Axv?R zFj|QSLnJS~4f7r3OzfZ{2>u7Lh3vioBG5!C*k;bYxy)5^4SHsiKo#RHfO!EGIQIuq zl(ehae}mAxlI~O6pCKwoO8qh;uJ{8lFv%tb(Fu5E8*sfJ54Dq79&K06k?`m7mYTEJ zfKNQdzHid3k*^?j$@VY4Tzf zQ~LJehZo=a-|ohEZ4E}VK$u~)0f*Ip%X21rN=%u?RzW9}CeJtugcYVUZjptpnzas0 z;)%3vDezs0pCEqGZ7CSCbtY9oZVuH0jrVBv9U^d)Ec|{f<}6*4`ci`Y0j0EQY=8|- z_8r&cG(d-*O?MzCB{R{;n0)%+$wG^3Fz?ExKHiii^}u|JV%lJ2ET|w zS-csmsC;P5zd+J_*MNv$!3ScFsA+yVrq|`1O}L+OHVG)sPMu@))dX8a>;wqmfhrRa z^vKW>Y3$k3gFK|Nv&%#I!6t=B_~DkD%&vkBBY)^33}(W*M_8TkZ1pV}r?xN7koT(- zXq-Xw$g}B7G!j1B-PzqcI7UtJx98Hx!_y41*M6{Im;fUVQ)_;BBbOo9cuJy?enLp4 zYGqHwd?-%yTz_b)sR*c)q2P5TVG(s2FCX6jEXGepH00ZB{~sZrg&2} zM%Y8@Si0&R1UCN?3rwNpnWxw5EhHzVfJsueNzO%R@t83GN!a8lFV}+R;I9eh!F&j! zDrqQvdG@UtJfm}Pu>-^uZX=lk#e2`SdWgIfD4W=4hu_3*1C|TxKy{4e5r7b8jFlyq zVLDiuR>QLeY=T@ADOGR{&K-p|n%mrubsZ zuNGTPUQ=UvahJ~1bx3txr!iW?CLy(5PMvYB)s5nn3!d%m!BTjBcm31BO|8IIqYA)H z8ALgBYq_eK*BtH~RYTBePh2i!aFB}TBFKrz80vfFgCv=e$VafSGE&O;uLxR>NX}YP z#=yb+3`mE*P6#YYC1eBlfEtvB3`{R8`PDBPlF^QAJYjg4%#-C}p%UZX+7Es<9kE{lQ zo>j}5(6eZ;DmE-NgJj^eupW3GlaLn;BLJ%?Yn3tPnvYScr%LH^yM%jHSNhxhYP z^W$ozCysC_xk=p|?NvW6y3gQ+=yweR_Zw(%s@yIVw$D41~>(3gK+JI^B_|tIuL^LrpTz>bbF*FF=?5l zK;B43#hc!G^QgUDX^aG6cnO6+FCQ)ut?=>DD}r7=+IDEIzDvTd%LcttkUENQ@->mQRwEAa3Adhr(w51qSHV zv3H|g(G)2$TR6nYvLQMYiF!pez}U6d-&DN%MKfqXni0Z?i5ym|X||&IbVL4k*>Q^M z3I^A5%LzQQwA{!6HS!>miRHv+f}tNg^9Xt5&ZCb4X@nWV%aK47lw>F0(n=la6***_ z#YbEKS=p?cxp~O)#QBsTNks!1fZSVlCN(-)sHS2}$_%#QA(sD~zM0moWUidT6=dTQ zqUVoq?> z(b~9j984%VUCEV-(Zx_eRgW!5*0oi!!m9|&e)#A#Q65XTK;`_O7B}y|QoKn}XuPsV zq@|w^YKy!?A0N8B5cJP_i%(RX|Lq0mQ_7&rBT(yLu#x{K$Z+q&0I@z=Vzjaxq8ow8 z98SPk_y8282II)EifA8xKA|g(m|^5faK_4CI!UtY8=A#Cd`(})>SXB`X(53o5~#T^ zrakfgUPu{)@y7G#qrdyd(aQ7D{~GD+@doGp=}$5^`Xl}w{m(q~3$(LTPD1T9vZwiq zeprIm6BD^SrK4KG5T#Sa3!^qps4%j+{y4wJBPVE}Rwhcjl`pMF`4uO#0=Q++O9Fpx z!#bAYJ4U@V!~(}i+mru9XYFfbnlP&$7ZB2E2l`zGWHu7m&&zEg$VP2%9hk&T!cnph z&{885GO;%7rWzHnC+4mdvkc18p8t4?g7BCB=k`uqaRfrdL5PoxRBjH_nrneWDFozR zRsKMk)#{M~JX{j*#V-%t^;L3Wfri&Ed&La^h71)qNlb!;rq0^&o6h6>8u?W)ZAhd> z{j&BP{d%-TB@KQ(IpPYRm{YerJYZn0YYF;pW$oZ+W_CdfDo}~m#iPeM4^tEQat!$C zboPo(VD0B@UyvSUt4MD$f(R%QgGwSSGTI@UzixkhSRlt-@yp0fJ(lkzJBDg~l2;Ok z(~U$u-#c?f9_R;x(?AADiRa}QA4wzQ!QKffOq>HJyZ{ z%eyY4<@Dx{pFe;6dUCVam9nHiOBn<-`w+QlNCO&E14?R&+`SM(GbnkG$*)q@8#~!j089uivM|?Qp)`fzw_Jr^P>i z&{zq?mWUUPppoQCxZ+CBq+^I(N=y-CA-|y8425wCfH5a>$gZ6gbby&u&Je)W%F2#P z^7=Z8AOuRJu(b5c95o#mwX&j*_TGfdF)gU*S101|h$8beb z^gL--B(;@Gl8jpB>;wNhiEBzNjDJv`BZ;w-e1r7iT{xa4F~Eq;nDVj{8$1vCPKjZ0 zQ!%Pgu9UiKj)3J+r(#AuF|`EarX;IJ*0N(TAKct2o~LEi=w#EXMRNgU4Zdm_150(G zjGQbuGdL_szj%B_#%S@?$BM!MCKk##*e5Y!TBx*fsOm|O(~nLMq>Jrzdwwn!LUEm; z$90qZaig`0@zhM7nctv_sUWoFCCc{`9xGgnb+uY(G0QBH?G%UftT2n~;He?n42Gy< z7$sgJr0aa?`tD!4o5xS@7Kj{`${na?I6R}4hBjkV3BGJsEp^s+kF*-1-jYNp(k=VP z(Heqnn6Cd;p6W!d((dG=Jg4lbAkyHvULagi{zA>3+lEBy&EljrDExCVU)CxXP#}Vq` z<{^>@F%Q*;64lQnd1$C{i$!x}I5K4mc8pOF?8<&L_vf(4FNw-seycakFR89T#U(e(;by27$*atN zD7X&d)Hn9fG?IHX=ikN?>(=bXrAox8AS2+Uf>t%2L`hjm3fBnLHdI~|1}i*tK7-O~ zKkX#U^Wl9c8x!?R5KkbPfatU+b*K?82ex06mEMGK2Q1{KQjVUwgb(uX0qaRD#^Wql zb}1}4w&+erkoRA+EPg$!^{@~|3DK5g4lnm6jPfdj=K&}%Jv+jGu(@m|KNu@T#lsLY zN-QDOw(p?tWJycfjHag*1zlF)T5ckf&-vH;^XvD2?a(~*U>mi$kB^{~01*-ISRF$Z zALP~*h|gI4^c?XJ7TpYcb^TpnQc*b$l@0H}z&?Du{_A4t!zaluUH))=cmIH_di*Xq zrA5byKZ@8UmeBo!X(|w;Hxk}*trNtmUuubSV%nJ z-6pLD>;0}uJPCUa@0!)XvlEtP;g09qK#`SRH+4T+unV(U5LnMLh_SSd4m2TIr!QB7_R!z z@UsoyWkE=qHnRjxaf(E!7XiRa4SZSt21nLoV$*S?u^==APU6sqF?^xU=`My89xjej z&dDJ%HPQI*;OO*lsk{E^MjA9PcPq=?c)6P_cdN_Y+H$wP+-)p(o6FtSa`ys5GK}#t zCX6v-j45Nx8Dr8Iv&NV<#=HsUP2@S4H^ICK=1nkff_W3nn_%7q^Hwo$74uf*g_yUB zd8?SWig~M;w~BeIn74*`YnZo&d26yV%v-~}HOyPXyfw^Q!@PCOTgSY0%v;C2bHZo~9;6tNLp=vmgZ~fNCJ&7uPEL_Nid5;h4=D&IC_Cq3 z+2cPY8%doUSBlC<0(|IZhNyAG+avRt|KfLi!e1c`<@7c|hPuX%7&RiqC-qt+p!;f0BUqF&%MpUFY$2P{SWvb*TfASO_=m@`lb^u^ zML~KsI8js|uDfp6lyqnoUtbWcq{wD=)*^UmG>3$^vJJBm&w)hV*K%pijyyizg)f%BK-PQ|tmP15$Au?J~9{$Ap5Ox&Gs7lay9hq8%oXFyD9yZ*D-r zIDvel4`^$KUu!5Il}k$H{m3%Xqs=PUvsI)?LaTLB@*40|RI`BHN?`5O{w;%`xRrP^ zF4aLWswAgMPstJaC1wTzP}T)zTvx{~)*Z}*)%#>x&$iSzO{U5av(p0xt16ViP{CAl zd@6|}_Crn>s#8&4gKa8H`JzA0dvn=Q_6512^d~1bsYsT`Cor8vPw6)p^+pL>vUO4> zI{--!B5=^m(8k$1JRx4HgFCtz>SK8A1$rl)#^wxv-AvoVV1-aBMhCZ_53ll&&V(U@ zrCLF4`ctwAfZm7a(S#P$#LZ0xgjsxwd!}kzqNmMj%6NM}iW( z30{O=Xp))S(plnFybxF4UL?g8&yg6cfk}kk(x%2x`SK0LeI;^l(HHT_iAHdr>a0*q z_1wjJT>{XC>3IrQC5k@KZKCZwSH7TJ!9+?HphJ!H!&R}vtK?2JKt7$6*h=y-;%v1& z;F$!c2`6~EJ>VV8(y2XQe}QeEbh%F@0b1084aR0a&H@oa8h18_ks6|D?aN(cYuAw+ zZ{Vc|v6RTJkQE6VjENayUIVZ4h*`A*#yfG4*sMAHz$6|%AUp`!#q_)vFjNwtcQ8$%$;CLZx0!Jw`~oYcE#-fBxsl>WOU8e{{K zEWwvpf^T~ck3Jl+tPy^A^Yxa?9EM|%fUaPSm|;aZ!n>jo;>a&+PVC^sF~ zmg0gMC3>JOW%UrL;|T#l;9t#Ox*3x~Z5#c1HJk>gac}1OLc`$G`N1K4?%hbLGp($x zZLF-lK)%rGcy+w^e!Q`_I{A`>5xHNn8P+7Wk_$1OkursmK|w5GirU$$7etR* zHZmJQ^lK7Bzm`JiF(4uI5saPBZfw?s)SDR0cR}IkXB|&g5pPhUNF4;whSOZJz-*Q< z#UcAb;bOd|fKgG-7{TZ7REkul+-55o@+`~iD~5P=NJE_MOJNzxCUYS;enU>M%W7DK z8kBUPk}QB%KP*1YV1S!R2~*1LG>4$+0W29Xgfvo^lhwAI#8p;H0;(16tXzgcoIT25 zNJ}97UEMyqj0G}7Q*`T0e2lMCdEJxF@R428e0C%DB82e(I-zx-CeNGg3$*MNR&Emd z&>TKEAf6(yW~LriWVMv!S)8a`b7KOZpBx-on`80*?(63VBnF$QrAT~azu(DH(X%P! zK)wXQ*J?i9cxfB)0M z1m1~;%eIjBMNNwbCh^cJ-~#(9+|6%bWP~oPRUd-MM^Nu+qfnZ`*8~Y!2B@D!IjOu; z4Ikh(8Pygy;8MU^UFST}8Ue24NbC`^x>;!ySTluy&$y};>WQ3A z;n^u!Oa+VZV-Z(0sI_Aa67%0li4*nY9Uh#YL-B&lc`$`U=&2k@d$~BRUXnlSgjD{Rp1quR)4g*^csAd;}$ zUHG&Ol1h*`h)+elpb~0{_$yQIj8h>x1Tv4rh)7Vy= z=9{P8{we+u2ttrUAr87Jte4pqw-P3Co8btAR%T1O!f1}p9*S3QrxNqqB%Mzvn>FWq zaw=CV7CI&S)+84}V>_cIYZ;odOZe%(k6!+hWKxOy%X{kDz%DXwdY?aU7c}lmGciK% z>!bn_v3KwmBIs#KkMkrc6)EF{{(F;2HUtF5_d1y6;TVNZR+1DxJl(ZM zAVe_H`g|qt{g;8H4GE$=Pk(!G#wJT%P-o_H(qL?jxkpQ^xSLqQ!JPCOB zarb%$SVG|pC!B3yUf#KqWV-o}bI&-WnnhigTG63c%m#XbDcoqCRMb1{j_*EnF=(kc zlLSDUJr|6_P7)b$4DZoO7?Ij{-KWK;cb9id4EwwPguVZA`|{DgcII#x=#%jAc&9{1ye5q13*~IGKldGT^WZTq&m(r#84=Pd=lkp zEnBPFEC_-Lt*|4-Nlp%Y^hHcL2>zm6@{v~Ma=XLxxan-v5ERY+p=@#Hc|jPI?uhnd zd-3u1SGNs7k=|Lnd;AGeGDlLlZbl=aN{@m>@OUOs1rK0v3y3M@DqAzsrKmG<5c8zy z??1t0ZFTFtZ2iTvpbwG$oTENdiL^E0OWb~lGCHS^LH3Cvudy>w;RbU&*CBBrG4WD@ z#Owh4O>rpgJb`K0yqu{eV+wP0qgN=O#yV4CUAVsK?u1?};h*pSq@QTL_avlgSVvDp z3&d1Ow-VDsU$xs4Sa+MNET?e?rmnF$g35{Z3D~p7wb59QrdANv7&viO^ay8L-5ByX zM#!TnD{4XM4v`{ozOgp??&HVrEVss_D*;<{-hgKWbIz)DYjAXXCYK)Z-0yJxMUCe$ zuK@H$=ufm(mcdRS0e`kbTp#5I~9#cWSCbAf<~hpKtFT5VX?^ zoc;xs+1_1$gbId0>hDnQ1ci}ruvHXz&+3duO7#JD0 z4m80VhWJNwoXKyilTVn$tX6+HA8~NJ6(Xdnp%@9Q=jh?7i6$A&)uY^Hynp9QC~(E) z^0q`^KrVR@AM7vn{v|>Kmp1}dD}JVAGR^sBw;y3xK8edCYi;^bVOXw8KzY6ceo93! z*#cOICDbaBAOEoVVsqmK+$3vT8rni}hBrv|a2R&d){uJ$;ksMg)z3N^xBA2SQY^-Txl|;l(ATFL= zpaNkTod@?LB*tuDrbM*gv$#1x0g-!_|JH#;8cpHA>1eK##QVYpi6a(u zFQsdD8S?uYkY2P?v5Ha^@ns8AEmjD1u-CU_7fnKUcJz*uKNqaOm-VQSax^^!M@9RD z&EMA3U}GTlpLUrYBs$ZlDsdJeK}!D2`B7a^a3GF_i2iQ-@D*kEXzO!EhL>2Y z5`zY(;dD(66o-(Oz{$U90P5M|1FDhSykEd6%nNrqVvHI*AuB*MWAk+e0q_%U!&J-q z`1zL+npRacKe?lkc3`t*{p|zs}uVn6Ec}MjquLg?F z5segGtQCe$)cyrA3M2U)85l>`H`|XN=KspIA^2T#e_6_JWOXVKR~| zC6?4*mx}sCGsHsOp%C#q4SZyA784#&e5GN$kbPGZ2tz_(Xm&b*z&&N%ilf^bR2Ry~ zj9X&3j7c1-vqT=`sJdlhvKkwUla^RP#+l?32*)h0ShmQBjdk*ep^J|kq=llFmsW6Z zbn^Whcs=^@HXYK;(FN&`tMA5g1yMmeM#5t7q6?3|z*8Q1O#wj%jzo<{4N^j8HZo$wpj- zs}<;oQ4yX3`I9wN114Kf;E2m!VnoTRLJ-mNhaiPHFB(OK5#0iTLG?SDa*k=lYfR}Ez)|v$pi=x(+M6MB1o5!DvZYy`>RRuAZrPt{Hfj`X ztm3~3{u|@JmE{rAB}Yh`93g#jgfz+#(kVwss~jP{a)dO?5z;M3NV^;%{c?mf%n{Nt zM@Y*YAw6@1G|dsxHAhI>93g#kgfz|((m6*+>l`7ybF_+iNcS8e?Q?|m&k@o7^s2nZg@||1b~frzjeW|1b|}sUxJP zj*zB0Lb@u25X$CfTeS}; z+bX|prUbp~E5g#@H3#0GuND0dORKDUys&HM*>01SQ$z)5Ll;o|qN!YoFCZ{rc-097 z)WxBDwoCaK<+kLh(rM5!n9ETGt-;tDWkI=Fv6jtXu-G}%lGx>+d?q<=3;s-LCJiNc zMI_%#Vx7B#CX!Y0XF=P8*Bzp=7>R52^1ojyY;Ow`|j5nnoaF}T46PI(#Xs);e70?j}JWRe%-Md#O4 zq*UeKx>ky58C)6Qk==hK(sW;JI$k`pltufZsJTB73^?u}jq+h6!@6+KhPUJ8>AL&< zd~Q|Hw8w`uM`#8;0j6auk)#B0H)6-b>d}7n@DI>2$8Dndebg9sU53qNZ*8W!R zB!U6&pQug=IbAKXV1eCiem)ZI zd=HA<>I6QPlQ#%-@vvrkbP`s%cv)?s0d`q{HqbPL6XijQ5cZ7g`9o#y#Ea?6eXzXU zGw`9kmsVvmSrjw1Q>!GY7CmX<76ma=mE~&LEXA1ahYh8XcUn!db<>3|^+0$f0Bt zLG|WRg33{&CFdHN#WxMAo`3}sqF{$8*P?ew^pW6JUXBQA;Agcg(jWI-B4tO2isY$C z^vvGj$s2K*?<{a=6czYio^3-L41DxDFc-Eo3kLYAT>-EnKF$fhjI{r|=ijh;m*Q3UeLP0*5F)=(8P zW3NR};qs_0s1r^gh6@~vb$~zfNjgUtY8`Fe0YKyk%)wTIV0#YFIVq!AVcn_`TYxkX zHo0^}l=l2O4j{dNg$ijxU%jv@l%9kK!Za1O)044a|)3F&BMy$tlcH?1oSM+GhfrbEPDiM#l#jvPb+m$ z;wS-wwLo`v52ruUVO{6wr3C>`oA|OVQEo4 zL2|t_9D8`z9q+yo0rFIu2^=C@=@616qES$Q;w5s0C8R~GmLMtp-N&J1NZ53ualCbq zwJ(O39fBrIn^en@vd&~6GozA#5oFu*wf?#HAe@KWf-sFB7KT6(Akiigwp6yM zr#@=6iM2+xiB2GJYGecnl z`$6=txQ@h`O}v9~$!{VfVK`c=cd;08^6iub?@U%Tle<x1$w&$e}^O zLzO@DTQJm-yUzClticCm&)0?-OTS>IXwStNbvL;_sVE+TT5k;dLtC?jjRqctfzV# zmt3P1S$dM4`h!qMQ$w5g4xxg*?e@_dV`umH1Tc;y!IoAb$>8OqWw9v$LV2u}SQm%v zHj-*zAIuMg5j4#klF!_KU}XcbYLO_eCfp1OVaF%OqESLk!E)^ZayPjRx_~&WLcs{1 zIVZG9W(K_l-;xpn_P2nbMq7vbEo%W((pUHC9YW83eQ%*>6*%^Ux&#|?Rk}s7GezFl zdqhbkjY1{y@!&X`SRFnqnW<)U^?h!?~so zK@LEUCJd4mx4dQ6{%`OT_Zy5tB)ssGuG{}}NDW8=J~7e-{CLYDL_ki_lz=*-WoQ$n#EVpur))nF9qWC3*VvaKltJ0 zz{D&H#Hp~+aYtk^Dit87`t>H1Y-oB^JR*k@3>n@fnT^oS?h1z~O;yc3u&)^b$-m{L zljUZo+j>`a-pr>fFCeiwJV9PSw&a5C&48n;OP|NfpC`+oSD%mkqfR~>;lmL=ZA*bT zB_uqo)V{D>i1a`V&>iA-o}J>%z|y7c5B^e_t*~cEHV|Z%vhGG9yC%ITTNdK+x(b;_ zqYRK8-$4$@AF8B|-rW&RC;{-sx$eeTTDu5bQ>QR2BwJ-3S z3M2#HW#q~!T-+r^4~P-SMOoiWlzg*DwbMDWJ$sH+X_Tqhkq&&)z!FaEfxdBLZ3?Up zmRGCOs@gy%l&o%Lc9&(fxs>GcKWXlf5_QkIbi5x`d_r~41`TV#TF>A5Q-6|YR zq01fWa^eHXG4sRiJr=QA8K>r9P&W%18)jzr=h=`hDv4A=DGUk5|DTXx<#$8^oWk+x zxts!AvNivQKArCmogkqdcMn=YvTVpz8>|GN1e{kGHHoK7T{!jF;oQ-zT_e~sJ5Ir@ z;K>>nxb*$%*2Hng;=bKItL$NBrFyB?S;$_`_%h&2^XgEDkwY8zj|((CTf9dq|LqOQ zAgV-Zg&2*dMhUTM4eCH_ER z33QZ4-GJ_|IdMQJg=lr9wXjD@w8G?m#b-%Ml3Jq%>`6)Dw!p0 zRUx50iQkVlddVw7c7*)9v4&`LvYyCaqWoPFLrMzBb(~Q|+wi8A(<4`BEw#@?U!HPx z`AS1mkO1Q{n6m%=`RKi5PPv?sopj|Il4>L5?Bq3ETyGNXOD3}Wt7}vVK@-nX_C?lQ zDfYD)vvJ+4KU` zdDKtMSh8n1SZ&)QUP>$b4eI- z5_G^&gp9@>r5c(krEYf(I{3Rp$fJQe&~NK#|6e_p5pk-a(VbS_QgciO6VoPexlqCh zUwKS$J!`t0LNdr1_(wFn)w41|{?9eNeR?v1O8Ry#uC5`!ez^R2zgR-a8Y%eo?(zEL z1F9I|Pq^im_%Sj_z-Oe;pXAxHzTe8@p{b|YkrY`!;8`F^C{VR1ZC665q#Bqd~Usr{jZKe$&)sWfuBWTAX22z$-tBf^bKQfOvv<%<=SM<>q6I5iS+d^I^wj6!XQg_~7{v_B&QDB5snUfwYEH{& zY&HQ7sylqys4;s6;mkz1KO`aUX7TF}kmMFO4?lE2Egrxj7ad$l;M){$$A|)+Uu=uR z0mUx072nDa5R9Mv-+1@?jq%#*4)Bv9W4(e>dIRF`rA*h3Cx0;+Z&-=u-R<2SyivZ; zy!E>7f|x55vrzeV>2j?72QSB#X`*ReU}MRO2pVWX3@Nn83^}EEWnqZsg3`{Bjf z%EtP}+WOYoc(S&!zP9*&b+UH&4B=Djtl6(SD~k_W;&{Pg(I~@e-fQtWI3e9NH!VO!1#>%d34BV|*Y>`amCyOcWInJ395wlB&*9hGHOR|lwCC{a4&8R;AUoMp2))~+??#G1 zl^3U#2G6A^Q|&C0#WU7|@5ov<-VZdQK^#a;*Tuo{!TIRlU@AI1WO}^*`X9@qf6Kpd z6dxgbe&s*@L#|V!fFyu9)p+W=`yg41w@9op3WCBJ)c@$*P1Xlv`w1_7KNk6|WV?w;Oj>orlRIn@YfNCQ)XpAIb^Dd|w5VK}31w<8 zc@wYb@a$u`7qP9Nu(X=#DQvpw?7!C|irn45$(r`0M^q@T6E&e|lWd?iycBq49VZt} zbJ(szJH3Txh-y7UB&XtIt!XBszli%qO2%;BPKmd}^9cGP@)*UoAJu({DW@~%6KV-T zJKqLn*_|DTV!i`e5vI`1W-)A3H53DC^}H++XV0YP%0?b4NFK7NC}5FA*VYw=GaDU1 z(viEth_R*?TD-g-F`twqJ()3}TMoaiwg^2nlc(G+(O;d6Qdr3kvVt@5*IyQQXwNuW zN*U9oDjG9Y!U>xIJfT)Kyvi7I9%y->vkspWHCfG3i{!vQIAns#4KgFuaH|?fyTS7_ zX)+D(;NtG)@?%5GPHh@Yc^OW2oV^0(NwQ-)Bmk&-2F>-QcKob`$n7hT1x-F6Hh~b* z3UIr`DmhEX{xFG`Ez**d+?W6CTR{*{L!3*uOU@8=Ng0q)*Qb;>Qo^(M%9=`wK<1eO z$`HkU3C~i>L&vHRpN{JH7f_df48IX8h!lZ6(h8p%U2zJ86yO%ZY{@!M5Jn@CCRgiFr0`o6wLApva1|Dlons!FFtniP9EMebQ zrej>}XH?Px1|v8(@U%M6LGVIAozZkCh|2(A#q73hZjtidJO;^Pu^X_ zut8ak`*EDFsdP2SC-SfqkzKy7o3IcoOcyQDBK3c(2s!DCedFRZxDm;o{P@zvmLQao zki_ZRi3Zg)Zi`OlhhML|m7+|7P#Z*XK;=ZLx|pHZs+OPdR|Hj_{dr$&Dnxq#P(vay zXcEBl&1T$0353D?Ki=L&IgaGW66L4VYRqKdifTg@9t3x1XD^-vjCctJkZjG$($FN> z4HF4)0I-|d`}p>I&T)?jkH{!cy=skSngA-JG9x2A{CV8HSq=KU(o1>pPgp4hW9UwR z4)F#`(C!s_b7~hcwFEb?u4VNKHI~TsCi2YKO`%LrR!t}4bq*nOg}SXe(ISW9bE@Mr zw&{rP;mwD$y^H5r6OL(LscfMQFoKHlm~5M_m$ML8E$P|ia^`vUiRDq3koL*FWW z^8RUxZKJD-vFf4xwsZSST29X}+B`ysr;}qq5L?ejXMo-9A~7ZK69fu3^U=VJdARL=`^JKHl@GP)9U&MpD~HLvQA(Nofc zc@8OImSa%1*l9ZC$mmjePeDLS$$fq`Ln@*Y6*oC+Jg%e8Oi6`)K>zi;#FROqB-VIm z*uTx&^hWgF1)6n~2pIxW%@;4c@La_MUoU7ld~uNP8&+xim6a-AU4B7pA>-G|RvsR{ zYSjf-&#dlR#W0d?E|!tDOb(Jtw@plyGO1a#>XPO;&eN|KqVHew=8SP`)A_Yak67?N zR|{^yb79hZfN-dvJiI|GLC_QotUYw1-a+w|_|6ep9Oscptg?b~?bxH;Bh`)tCEL=N zR}O}qN|`bF?O(j{JJ_Kg2Vk-y327EMC8~?O14J!C(aZZlXiL>@@Bxc3V%^O9t_IK@ zX?tfuCh|w)B!x5TZ(FaV0ucKe;x~~u=UBOER=wramBf+h4=hOslk(W|A>(Yhat<^T z>(MBkVK~{_v)ah67nCX%v_G)uV0aDTY7rhAZVO_OZt!NH4<}W(5S1l{$2PVLN#sPW z0s9F~QGC(qnv(z+M1A(h*ENIem*YJh`d7@U4&}_3=b#)3Ve;q7s;T_Krp8=;0ghDtAppPN(O!K%DV)y<){U^8o`Lan*Y#tjM5B-KeMxg~f;5 zEUR?NsbE#*zxAu_Xz5fnM(w)hDIp0)KdEB>I(hvZ zwhHx?%klqRu&-GHiA^fp7X!@9mF4E|E?YZtZobVJM1Oj6_&teVKhs+UJFP!ZueU(u zNNeK(k42+&3#tL`#J@n~YPdMILj9WuXc8Jzm*zXK=wYRLYvc7&)^d9p>2JIdOp0m5 z*Art8U_@p=TzE4BJJk+-%jw?_g)Ryqln=XpPzRFMWNdCW=k7EZ5ri#>Q&b`lTZghv z#q$C-NN&$^0=-_on5vV(`7VO}Nbf>8fiT~TU6{r|{^+1)pHNSqM9Lg-%9I6kF%W;{ zEl$w84tfX!2%D5Fx|Q?Gk)GXLv<=*Y!javuAw{%iquhgRH~1dFU4dJ;omq5ow-y=u zU@wNx@#(Qi~>t9;lqLf{mtP=|D9 zlow1*KJNQccl=HMxP69lkXo}1eUYPA025C>Eo`2u z!N{qlTS@Gqwi-^+lTn01B!I_BQ5}A{yMDMTXM~hx`_rjBh%3z6c(Z+mI&SQ)0)xEC z+lY|AsxwQjyTR(muu8^8Z~0JUSX)W0gSaCO;cTw9#7`GjzG`31if?R$G_OFnsuU_4V@ZZi3WI1I$Xk5(GksAj@E9 z42UFBHmrHuSnx&!Ccj6e#;pFD7DTCEQLV&8!2``Ls>Q~|a}Ai$`_+-?>2eA}|AzS+ zFMegjxnHqv_;ABK+xsknxx5hA0MQq7hHH0V4zmS+@X2 z`o0l)*KRHt=I;HIXCtJ^%+h|>>EW?DQ|?L?cUhG*l6?6)#Y^e?MU95wr;d<4*gf1Kx27bcvguw`lr}g-Z6Joyv>u7(d>i5}KzV|_eQ~P% zm|^GpV;mE7n^NAifLB&KXH=Y4L2Q@4QVJ6B>VT$}$DT0bRg*o<((@~oE~1$^B)_&} zf{RAFZf?h+Z8kQa>`>9tUG!d6iK^YB17s5& zzsyzVoTV=qguA$DY9T9Vp~ZPopJv0Az)bQgl>b~{2@I^?4p;)w%|grg&s!4tU+hNI ztS%jG)*vW43kLThy2tfG29RP>GK`d9JY6vZD3KWyF9^k2F%RfNl1zm@G^(Lk%t#83 z`FnZCxQcUeVfrMJMJ^m(@v#NR8=MD8H(%shOOCa!D9yrb3obbHiD?U%l=}9|lkz)d z;WjLf11NW{4kHWu;C$)n1cBb+%ft1@;T;s`>vxwQk>K(p)1%9h`+?FDtMeZMrg{xIsiID|C-(Qr;5w3M6`W?i{Uv-1SS;OKdW zvI@rl{wcnb4<9doyiWmCm-(!8&*qTIw3~mA2CwdzF~`aq6a`ydoN{Mw!=5e#tP@_2 z{SX!+^s8$kHe1*1H^>LIUImi!0bB2Ztk3$_OBUna0o9x&bN%}LG1`WFh6eq{^`0-=hkAi}JA5x#TD~Sw?PhT?RzchH zbl#m_ibb{PPO%h-LMPX6UzKF|2iiscfe#HK=eK%CwF_4?`wnmpgD%k#avsN$2;saqY@ zt$I*xuvq#;iyPbdHOZ~g}}l;GhwJ5C{>GzG0zkpNQ3HioKYrq(~8CSHU^ z$S6%ND7*?Mhp*wGuUEA{M(>vG?P6?a53?KF#$2pHR95gTI3z}ul_=%)H=1f!N=j!R z6@}FDFt>|wIDY~7lBpI{mkLRgC21vYvtm+>QUD=#Zd%wNCyR9>Id}4=n*<|_@^zX`R8*NxA?vggg zo_IQ3&yFS3_@3EThQxgdj~!#Py2GsYNR<-;=ldv2W>me?SqtT8xuzMTZX1eGyc_P& z4EpoK-81~lk^uhUhSIaT&%A$O^swt60RjVZos7`-~yib@E@YBeoA)JxVOpU2HXcC*rdWz#CLpI>;*RP^4< z+NCG+zuli-qq@2ZTB(uK-r?p;Bd27k^oM9${P^#q9`~SsYEm9u)|cvc4y{?^NvXmS zDrCBnT!!Y04*-i;++%4`+nHyej8a=?GQR)p*SQME5rN=FGQq-=!HDd)wrMqJv%S3v z*J-E^r(ld&5(@IY6CBaQlQVYk7*rD8Q@_?NE|dk$+1vAzp#&v9-roFZZ9Z~YBh~@z zlhI19{##*%(!nT~&5f{qfyOPz=cskEV`SC*?KH9y0o&jDjtHkCI%|6C4A2+lYT5>L zbFTKVNzzeo0G8tV$DfkeX3pK_{QI3Fm@U>+TrCyQR4a2mZf)Xv5X$ug7LC#@_*i)H zU853%JHKQvD7nKj+o$ZvUi=cv33<4|=$+PDPrgaKL!({*uG%{4$eSegrxSr>Oj`T7b^!rELck78w*^|rVh);;oMv#C5fD`Q*L5fBuwE~| z%_vZOif|N7(R~HL(CSRiz~b!G`i`bllWu)MdzCUon|gY-`4ZG}a*EjP!Jkgmj1Z>c6z!p1?0Q@R%K{%sh}ID?2>w^0}VCf zv?#Te;q6Ro0?XPOHao(omWQmah3Md*B5WsnDIl0P8NLZ1GV93|`9e~$5t>%(x!Zk> zRtoUTN4XPuLlwsA;liL6brNZHi*QEW2AwL*CO1c(zN&lH3nL!3NH?>{&;H)_;VZ5i zQpdK3v+YNy004MVx;@;!p5jIJbzeJoCiX^X?Mh`ER3ByF3r(ZGKDWv?AD#?bx3?e7 zKS2Y#6u9xbyGzXE}t;}XftEsnF%U57!2?t<{FaBPrk|!opB_vO# zi(LRAFLszQ6@Eb!VeNS;d$znQEO6#pU3a51H=TPU`CfKu+8LL^iwBms^5AYaQ7B*2 za58qiFhHu7bu#Pg)b2yJuvSDfWE~dOK`1bKEDo3a?5GfmN6ygkymu7l09>u!Q|=4y_Vbnj!i!Qb`^z-~IIvg5{x!@z{~VsL7Jh zXN0L7qSK!MiH*t-fa*wtK^4kFmL&II=EyhRJ()Z{M)`f_F%{fC@WDdx^~ehEm`slbB5ddFUG>booZE9TKW zOOh8E4XNL-HbegQ?91jWVIQ{7zDG%t`lu`!B zp30w~D8>978P^OJsw)jV=El`p{i%h5vFfro{(l+18YCvxYI4a^2LbJ*^Nli z8or zw_-0tqGFK3+Rs}}ZNVVTqT>wBnc8BO0@%hPZvexd= zf=P$8A4#ILbqJFh5-M%waQ&$j95$-K|ElxgP+c~jXF9es)HEXcoGxrsmr z=3Ye%EFY%$E;-6rt}k#jt(r<6znoUfE~&%G!5biW&OvGiaMxgUCP1Y3XS5jkVZe_Z zyl-%M&814IG(eUYwJyipnrrpN5rtiMWuZxO>M^hN&cX? zTEn9{WtX!ZVPa{FaB0Ph0~m|wkjH!-9$@rP{nNz(^s{56&9H-n6}Q}(BN%^nbo>5F zYz9Sd17^OoOagA>KLyM@UHf$ribt#DrBf}fl&yv1G2OnsyV|+>;meQ1&OR}0b8E3? zM<^Yj1!F{BVkdi8z1^EVR&3JxRaV>rzq2XqmXteRX0{b+-Yj!X8J-oNayKX(FWXFV z$r^r*j-%jz7WkZ1vTns0=h~w;k8?-dqQP3Lfzf6-U<>TfGi1aJp_U%P^~w-L zpXcA9^4FGMe0#}zbWl77dr8qXb@L*k5;2CVU z5g?^p3GM{or5qEov&#K;nb1Ruq!N8_2wB> z*#xxu9g{kPv>3KLDf10!Vo`h+osX=_wRFY@m%sWs>W$ESjcsPEch&U6R13;Dgp27j z|6U?4s|S2oc9ntCHKck{v)q$I4bgLxXTy~KS;IgI<9wZWP05WNF+!`dcpW?~_u zmsCE^cF$)+`prayZBW;RydGdnQH4ylMY#SXh`!w;2rx8fJwDl{SXd4?ldkATBFhIX#)1hS!0r1f{k&Wi-W3t0OP3w;cyN2(mCxC!`S zII_?wMAW)@7`$D z0J6^R)XdGuNz?g^jt*Krki2m|kRv}+m;q023Q@HF(3;bLPd9HIi7FX6u%zmZUoc3E zTMja(kyvSA?4d-^%D6-j^vzNtNE=um{UjO<%h(y9IZ!VFI~b+&bH&Y$u1sX(%AFbs zIT$VIAS>~C@LWQH@S@GLm-AtBk6k1~|Db>HA@Njk610%A1g5bfl=rZA{DDdGGg z9RlkSKa=>4UqqTz{Z`}adY`~^-P{0wKR9D;KXEd&Nyw}$J-421b}{nu<4(vhLJx;c zz^r04S7FiIt&6=!ieoUN7s-P^eEHx`Q-}rO9|a15mqFG74qH?DA#PX}IxQkni<4u` zt4w>$g@U!IY3X_1xb#->BH)5N>O3=nXuiwg{sX#qv5O^B3!ptdMc1CsPtmPaiB;N^Y5}M=N++$!7MHc`VnBWO(#l5C9G740thwf> z%swio)KD!gAND{NiA5f^BC0z?Q)tV&t!E}UZuuRAD)|E_(&G%ZRbj)37_#cPFE_E@ zz8ns5=?*bUgl_6!frI0n-K~q4?Vssk#xDM@u?zC61%ODmGY-+iFRe;$r#EmTJa_x~A27)Wz2^wwY_T|IF z-SrP&(5-M-0!X}E#@}C`g0acH6WBG5d9;(DW0++~0x*D4l!uXpXVOutiGR=mb$9guo8{`EbF~zQ$eMf2 zEh>4cxGY4j(}>HL>?`48D*c;V$9FRIZ1t4L+fRmsKAw+xF^@(rZ$h-s;45g9*#26=hs9&i zTT8nXjy?&ca;jN%o1VZy>7EXZr*h_&C!hJ1`tJNy-7wvqtK@aARA?B^Uh8%*`i$b7G`D(Q@^!y}pqEWg!-L~ji&|8IgpyCJCQ@{xM4tfkk7eUh z8bj#e%3!#D!^G{+3MQQN4?kD3f=jFL)9o|Z&}SB+vG~o??)GJhttbDD418U5XBV|;b_4`IGT*L)HOu}Gg0B#^z-=Y z$Lad}txuTp*FC*hIcmI(fn)z}b{$tJza4#JDF>}y00Kb{!6df+_Hc#Brihi%?S&Cg zhrXzrAKx~?RXJ5AQR#h^uE@~u9F0ezDVbB(tVQBXyt#K?dFWDOw3sZ%c8KV=xTLoB zQth&Iz=EORl`KqhE`Fmnq?<^BZ{*81CW)Hmxa=$H|LC&jjXFm-%>&ebG{;4uqr^xO zM~M0Pny(eQvx*`{$2OtR$u7|-$LC~R8^y9Kk#E7jV;Er1+IahX@AQw)_s^=6miWQO z$SqxoeP~CXN?S0_k*y@>)@r^in#v9sC&(7f5u-MsWP3#MkHsexmf#d!y+*OjMs+39 z8I|a1(&lKHF|O$7I)xZR^Jo1{n=Pfo%X0#LHFjE+52a!!(fEivWSR;@A=NE`ZN?|8 zSLeMxgDRxXl8W&04IjpFjQIYmwJ~jE%W4G+DB3THnvI?1Fh)H3a?{HfHV^17u?DoG zgHt(}Y9xN*rfR-aA~tpmH+z9yhyT;@-_(Pwg`sXHcy?!~-i7#&4Z=U*7jtM2pu4+} z-p_wc0n~LL44bQD7ze1(+3q%ryY$c@vBai^t#$52{Ia&MXp^(Cdst|uoc$YAWiEA6 zyBmShH$B@*H{o7GUD?06UFJrGiIN+M0+eJj44Vqdf?tt923slh zSWz~Q7$5I54lNmwj#{&<^%ieDx~#pXf2WQyX<8D!T`3_msOj(;A{8m@yS@L4!C~GE z!icIz{gj#}$JaGEJ5-)gKuc5mWeca&n8h<(Z|$^UK&_SgtB2bUORP)q?8Eid$M;Vs z^@kJt)ZB-8sS9qs*;pN=nDCH05ULB)7+(2zd_gY`rAMgh*m|z)%Q!CQX0diH_`|dM z>^gpRNe_zb4{^>I2gmEvp<-^t@`sg$)7tO^^;}P4c+H6Xw8WnIWdiJ?Oz!LHqadv& zZ6?w4XmJdvlDK_=A{n?gOe~jox@MWY_(>@+e}kF;s3Enyfj>Tfxx1QyJ2gI1v zb3|f?)?EW(%E`?9>F6K$=^tqzh&=-86~qJ5QC=_qQcCY1C&}}U*^zK*r+dqqsW^DG zeOXsCZ(Q8Hq^IeMI>|oF;EBBc0iO9m$kZ$31P*Xd3gav!y)#a27Y}l!Fwpbbw-%5V zDOunHwD_O|Km(YrG2cGS=^Ef_p!wudmr9aPVlk@67$|!Bq{{80i2^btSw75u#lzg_ z#0qZjqcs@%Fz;GT1Nx0oa3W=)?Vw5}{|jHSiXDJRY4NAuJ%90h{l&_3Wp!hHwlZCN ze)Wfy<&8bG7tRS|Bz_>L3;M^OUaJRSXQSwGwQ)s{FS0)FUOIcox=3nv5PhQ{ecmzP zn;sjdt>MA34GZRJEhW1{^yS*ZLJhs*EkiY)uMZ#Wgxr6=9%hvviUA1t&HyITzmNdM zDdm1Rk&FG4cQrkai=LLkqb7snLlEOAh-XQJbFW@pagN>J@EEro0f zm&w$mr7|Eo(ui)Zr5T1qaWHremONhW(GWdjF_RlLnt60+p0J-)kum3-OM&#sb1j!- zsyxhzJj-PVW?8|CRN${>|Ic%gJ=15qWAj8CEiTJ2`8cFrTpatpVvEynUv6&DQP>Hj z=%M7K);c080o@go)(J|$L~t9iC@<`To!vcV)gP1ktKYp?U0z>XUtQZ+oz7O**H*9o zxH4N6w@C8EIrwTNVPNM@xqHwZQm)DRbaj@BjU)$E`UywX>ashkc9bD6JPCF5z+oXL zGEq5@c|1UiOIsTXGjQ`>&f)lkE~H;y%E8NakHQy{)^&Bw$l8*p!`qDl8)m;vh*tz3i#fHDTUbYj>Ueh=mZk@Op3yt)E1=BHLYIaM=l3OdE%t0Mg^ny zc6{DDgY*ENLnRz}aY|RH!^awCPIUN7+?!5s3F~&ux6z1}t;-exTi3=7-|}hJrA4u~ zd;)bQMB#Ay$xlzDP{}c?tkwOLEncrce~c{vR&Yd9I+e3-QfvzWOG9-tPP4WH>X<^L znLwEME_ZnF}_z52wP;D^wAPFXC8oqaZshUtr?|C~Ikjo&co zBko`#4< zYuU3oSuX(JLn?G-P@jX!E=iem^5HCa{98vHTWLI_8PPYPnI|a5LA6@zuDfI;1{VO> zYYXpD~vzD5RT9`MOZd8+mJ~lFMQCPOxo`_}-W7tp& zZ8=>#SD_EC!TILPec&2)5lp@S*=VL>M~wvaa_jXR`(re_P83@AmOr;hT6PR}?6Uzi z;k$G1o>f^xylzhsyH{@H0Dr2D7bCa%2E+**H>3_I)S`kOv^ioLw542KGukt08m7o4fK7m zqtXHTZ90c_`Vkc-1Mkg|yvUJye7@Cw`(reHoeFtU&=EL$-|E-zkI~m*D!sMxakgbD z4yA4=j8}ocQxjeU08!T&nT;;scHmPqxU9snu_~;?$svFz$cjZ~$S_@2ZkYxcn@Skb zgJZY~>q&M7XomG+vRRgcWSxyp&-V!a1#)GqKa*5&f5HHJ+dde;GyDLCVM_#waqN3 zlTqNj;bpk486_HU7FZRLiw{gdh8F>u9byN`@^x5_nTml8;&Gv@@qh{!gI9oi{-o;R zcX<423b(tY$cs{!jMLlR2L`l04r%}LWxbQhX$kV+?L5D`{_)4FJ7U-EtO4YjnH^nY z=&M~I9yTV*7-sP>Q>UImZ-ge%C7$`0&!0d3?fjS9T|(f0x@UP}bbq6Zt~>th>cdRL z=90EBS;w>;wiMl)Po-uw8;*@B%{Yv^WCq+um`0cNq7~T|KfZB-NJ802(xt>Q;S&w5 zx9Y{sW_ui>hlcady&s3<h5e@ zFi5v1ZtUpw9`w7N>z}W^miR20h`^+I>{mA8ib71+;bq(I6dE#;SR1zC zePThL%O5^wrrb>^YJ@f&*U)#CqvzV2l;${A2x56o)l_Cq5J~XJ7tWWK%f%r40?joD zIA2Osm5bh8p4lu$+Z?07M6M{aAA+0wIQTHCZgMyn+V#mWo_qIXXx@6a1I(ee)Z-wVk)^8 z>p_w5vo`nlR1IAf69%jcqGk^v2k_T`_Q@X=$WLLJe7(B5c6>qjrciDlz2I_JLbyCu zD+qQnm5}gMYnSnD!XRx}qw2PH$LO-rOpL~6Ccctwv??N-)~{SnA`}M~Nl& zUSMSSPby5d3a?LFcVuB}BXrO6cucr;d;aZQ+{=s3-DpXzk&s+<4K66Eu zrc$ah@l&8?;*a2)hp)r?%ZJNoH{n)&Keh+MNSH89%*l!BNcO*2>7BF~#CJ_jHOYoy zqKz7RVd>oU_j@DsRq8vsx!g`r6Aw3mFh&CC@CJGNCvRS!9qbfqDmGz7zAymkbTrx8 zN!93)eK$A(Zp#;@6Ekw+%HGRvig8RV9jOu$3#F_9V@{_rqhOlLdUt9Q=oB?7>!J6N z)384s2sjSuzN?+3EZ9gwS+EtKnK5xD8nlN$UeP3rI?bpt|9iE9!pO^CEmykN6>b>V zee#cgL}7pr^rhZS{&liE`JaHq-YY{owUxsM82H~NRw(7ars1Fe@CSfoMHH;^A;L&- z*|TlZ#waWO8-w*Cb^Ux{Vj4-`5hx1FEtV5W2SaD)=3bgnattH$L2ohNVdEtNQI@AJ zzmu}^wX{`SL0TeSUlDa3}k|9x@C2Pd!uN70O`Uo=gPhXhA~3`#_)&xr1Zs3c8;ygo=A0&Lzlh6n5anQLA6~*#V>OPyuH)O>1nEyC?zkI zLs)=q?P{n7UTSrwM~8}(GSfOowYgMR#1QO`O;gmg&EJg&HmlY&Gb<~xU0yfwtZBzE zyko(E>-V83Wg`X>21gram^RLrZ&lDxNP|)(jJ~hJciZAzxj!VTI=nnbWumVSS2ypo zQ5#<>i~Ug@wak!6=O?ev_72}97U^Wt8==MSDXU<+W`swQG=m9oxZsS9vr9a?_4N~9 z==^1W6UM(5PN8CFhV(?yE*=$4Djz;#0r~#PLYEkHmDR*#hrvMYgVcvry?G4h9t$K3 zR>H$#kXlci!&yOv3_!tv1k2a7$AEIv=Ryx5>;j2^UIqOHgS6_!3G+yGwcv-!C}lgH zwzXX1GJ5fNSniW)beULByl*o_f{+ub~(VoW&q!x{MQ~k-s~nQJHI^dhmwSi-Bw+&^)g=$;Y#WqMSWIjiF5B zI46c1hhBXp+QL6Lfaowtl^vEUWEPrDeL%OstB==zy;}P4iFIC(=XH1g@bl#zerNTU zCfDb~?cJwKB>POD)1+??!?THh8Tnx4+t77F^~M@UFVe2i^I6OhXM$p>2;6C*k@DH3 zm!+^l+G6tnIlNg_*sBV)Fj%>1uHyT{!hXRzxWOPFp$%5c$BoewyF;{&#*4hv5Nd7~eI>HNVg_VSLO8Wq7%qPWADW>r zB{Y>M5_Ac$VepLu)Tw{Hk-mvsRcMAskPX8*`UawgE{Z&C?GDS6|L+;9WAzaPNMQ>g zf}%FLZ<%ExPJmc6g{g#_iuQtt)8)gvpR!>frORwP%g7o5i|k%{Q~_+mwGS^91!wyi za%Y2OEAHet(WoxgW`1g=S91>IUyUA4^omY>(FQU5alu)SASgeRyrDLt71MF_OrzJr zIIsFVWUj+k$&9C_ul^Y0AxOcydOv)AxO;#7;R8xO${z5S0zwX1%V@I#8p!h3lwoxX zIoLaaZhMX{b$n%LV#DzB?MD;{0A4Nq5*C}M#PH+wLUwGkxB%Ic$v4cNOfd3hO6jaX zs7~7|#`QJ5xekDo{UeqH-NEZ8(q}NzTrEa(@Du_1f-3yZpHDCEt{?t(!V>Y6=>w40 zf5s_+-*Nj85Sg8cMI?6rDhmD^^ZSTet+e`vq^ zyu+O_!N1$T{ga|<#jEJ zn^9HwVP(V1k5q(NlNl6)+9!})u{%b#8g)9gii8@so+YlEnNfDWSYfzcH267~EG2z{B0ommj}eA;o#r;JB z2G2(J;_h&MI29;!i z_TUPJHF8(HAa5gyujtu}nJbQ714P!SK)>rx}_dR@sVlaaDx0=@E-neRBBvr19yyM9_UWCH3#+2|iA2U|^}FPy9t z?L@}fNcpf5qov3Z=Zv`{+h#=JiDmKC*cH8p`6DAiby*YwL8NHnaDrrBjZY1C%r?sS zZPx)&gcWUFNF&dP?bQx@HX%a5Dvk})#pr-ZbJOKtXVUgD1xX3!34&Ud;xi(y%0E#l zY#n*h@Rr~lk;62H)GS(|S~!`ktK_jvo^niKsaYJ3`3SFu-J{(zR14bv-gI6^Tv|Dl@dP{I7VyfbV*C0)*N~xFb76+Oz#PXG1VZUIi9A5r*8RK<%^#8 z+jvOZp1no(@tj$_gt1MSL1*XZZzCeHg*%J>EcPbwK`Psr&t~(MH7$$Bq_3z8djQxL zOQd|*l&ia7znq;>$8LMqViFzSeHZ{gdnJ)(sVyoHn~B@pWp5n`ykw>9$09jFAgn87ZR4end5;XEGxl#0bFL zOx00CN+F|gy*igkM?;b4{C%@fZKGOOa(yoPWAv)F6JADAOuT6is-${th7exg+eiBx&EQY{k&FwuYh@u`GdcIAhpbCntwX z{AU-=LS*e->^_~mzn%QwW_?e-z|MzP?qLFPz@|-c;N*Wyns5I1$u+z&|D`kbDehPv z4aejTYGf=eKmG5M{}=!F%TM$@;ZMm6BB1sN?>s+ibm!U7SbD?W*vlycSujt2B79*>J+K|){Q z`z3FBg?ZB%mYP)ih2!)pc7Tw&`TfojgpT!)qUUR};ouS2#|(?Y*w(+BXuNg%qcQ=W zdp}^AaQ)xdwy1>t!w5eSwDRGv|_#_nyPljAn6A^7M}El~2* zk_-q5d{KL9(`C{lFT4I_2)GfkB^+jR9*ZTY6a6;1DZ#62>gIGis%z9YhT*5ndq^kw z1eX98VGKHagf2Y%&||iR*TCIL8%YFa1qMmN_OJwcK*+HMJp}8;W-g@wBDZE(3x{ci zqqInlP7SG9#IV?$3$iA?Dnq@5Q74WSAB%5>-%gCPevkf`LMA!l zQF}+TeyMq-wj%Yd`(qZb&*<#LRV=!0Yl<%#r3aPQBeAC?XqLHmhZk*xinyt)KM|?G zAZh)=-65LBP(bY{+z6xugiM_*3tIr<$%vIb34BC0GnqE^^EFpl_E zNh2wj4UA|2WC|F%q1eT^oLhL^9DLL)7d#d8(#ypNV>(jb%xQckBie#m)ZLW@7pfBR z^%wK0!tmWW@#xS@tI7GyohH5$7hH51;)In=r=!A+=mrC1*_3H$@SM_!)F=A2>Ym3F zUnoEc-$lu2*a6rMVlk12)qy&B{>rye*ArPBY@_UD_y~im)gIgt^^9tI>y6NAl&SJ~ zYO3`&I|KAZnnOb2tQssy&hF`!IlHr8lnX4IQY$QKEVc`zN%of{5YA7RKVKo9g8C5n zZ+#Unw*WpPwXH|E+0AYGQnLP7mW4JO7Q6F;ol$yAQxNF;n+|XG56)rDH(&3K&{s=I zw5^M|O0h03nifas%hpTlez+pa?*97ct0n2{-eT+W=H?bf%h|ooFI#ghb_9C3zP&+= zxKw~2^|0&OsRg#|wyF+NEdZaV?een{WDfTkh{}t@bGZPt+d9~`JUO|%jq^0ONVyuL z1oy#Wy1#%MK;LevFzblaa*o-=sp~;fQmCP{&3o!=T6%qyWf_EiO)jI$`AdnZ@F4L? z^^Bw@N-7Kh;Wd1+qVb$Vv=-vQkg^~m z;7y4U_pn*i^gSV}Cae6?b^=omA>0z!I7RXSbrGN{y}OYI5xf;>tWSa~g~TR35)9W< zA{!myrg#-jufLXd)Nkd>>54 zX>i4hQ&iXVGDcv~&#aKw;mMn@X2bFB8$xOMoD)C$ln*`R9ehO7 zoVI9Qz~9;jHC_EjV+!av&IAFgg^OAPVLi@g6mjf4cLeSC>&o(%C+uH!ehSm~?BMkg zy~2sD$=Q;XAVzUnXQmY;odMdGqF9qpotr^S^RyzzmaU1O6UG-N+zAw#N;*;-VVZMeX z6t=;$H_s06qCAsXCIv__Z`c9UXH!KR7yqEWwUFS z_)2ny{{wFD*sMpMUEr60O_HuX8h(+iBeJtkCPvXqBG_h$hs}rEPuK63&e=8R?#sIe zcrMQ~E)KT@4hLS#Z0K<#pmmL~YiXIwbrGlOAKGPJSxoZe<85_^ve47#-vrp?K1l5{ zEbQx-j<%~TruHlxt=Azi1^7AUWg>bD&T(+_o~3`ju&29RoHC$>WPI47Bgsc4QktdD zL4U4Hfecm=&xRw6V+w#r<<*5hn+Ttpr_`8|y`xPgS@Ao{*3UJAX_Rm6(Iviw9e?)2 zyPJn+KcKY_k|aQkOUW_*lvkf3Y2sgzDZ$H6C%>QId*qc%`qIR{1^jB3iWW6OF+{Mz zgB?8n7`-ySeEe9WnzEB;R2?j;DRGo-i33Wwh!9{cY-O;%Wxuc#GVpspDen@>+$NHd z{a+q?x&a8-;79be`wv{gV!_x$EMFhOd$atORWmm((CDtxu(BHl1_l{aoON=+;F9mo zlTX9_$Iob`{2%!5-4*P?_oj)+YNB32txMec;?vQ=ry<+4ZN-AD{hvb$5-1fJhO& z|AesI@4mwod^Y*rD>F6AxK zaj0XQ#fuR$I85wF{PH-~0@%O8^mq?Yl$+QGmX(~3e{pjd-6`Nu{an5#5eQ{YFEir`@noDD(yGZ96mr#?e00T#jB4HC) zAEYWSDE@nRGKcf<`rZpGO0?!pm#TmciZ7uB?zCA83=S<9Ao(_w5sGUh5-cZo50&Lo zUeqTKt}r(k3jMJ=$D6=7ihgC=Jd%Yv$I+(^B<^r=e1Ui(>~g@^9c8oh_UXhwR*A~3 z!(DvjDhc%%#ABc&BSN2bUenx(#u?sUeYghx5wbDaAx{{K+1W#K3;fi`i>JOres_7TTE6x=6$gsqBGG>2LLE(?vr!%n;d^td#e}zH z3C^HU_QMjUgyNdsAd$W6wXxhGFD05MX;7!AVvvi^+nOy!PIOGSF+Io4Q4wFEQ9h!s z=BDLz;ecjB>>}!wp;xBSPt0%j;+e)9Gyk+9dh6P0ZdJtkW$!$Zi3+av`TSBL4LZ$} zA+0mN-!jAYYIyEqxDdcVTC={NCF&z(-T3%Lq4hqUO+&q<9-)QnW^zmELb(Jw%%oZ( zv`=4M=X2@BE!h8Yd}OA7uZ7f`5-$1Cc*2GW zW^}onlz=pw1tTdSocW*%>n$o4t;ZM3n%1bbo2Txwz~|-iSCs&QytHP zWA^7L&BYcU&(;h4*hsy*8JnriUi!&)d){qtZ<{9~zF>kWZM3_xU{H8Yv?tHMy0n5I zFywJcci=+9HvkCX0hs;B@tvhwP-U{K#HC6)qg=jD`xuhA&Y22Ty*xPp>AH*Z8{DpgOa~G%Zk@u8O)+uEPktVQ%v=Q^lSz zUb-MRMxnoWNVpFD@OL_BX@q8)mX8{2`|@(9{Mp{vkClI9FBzQd+DJ!;F4V_XE7Fcm zQ?cb^^i!QbT%`&c3%Da5jh&a?Y;%7};1N(c;|NvxuuIkJEzjstH3sQra|F#J123s7 zyu7@=nGPt-BlKq~EE79jPG@T#N;Dw^N=LgL_QvCHYtz{myp7HTSjIV$I+?dhyD-ir zDGz{v)=X}}ps=w@nrn4zUpFqBg5Lp;&3$y|8tem055;&XtZHGUZ zqOhRYNWvW?t;oGbl@cFSjYiaH?$*W2_D^n^P57JRH)PO9ejFMxX?hiYNNA8|U zH@8r-CygcyIn7pCt^U)=r$%FN#jR2r?c4MX`iX*nS=G%Ll18zcV~)cyTtT7z$jOsy zwyjy4EXgoKK#-^1RtUubIu0>bKSvEY_4l?d0A`pD$RVABrq*7gR`mlNq* zr*D$#WLYriS51bc^^vev?=s!clIwM`sKzMO66!6bi}ISq&dx^4V5V9qe)K%-VZC~uj(t+sBsy!hMbvmG`&OzqI#B{0h(msfGNTT2q&4c zzVBMm;!(PZy@XtdO8tc9ke=>{oz4h-)10p8=LW3LtS^PA5*LMPb+kIU zA=#cTfwub23kK=_i5PfuxI;l*^>@eD^|i9!J}bn+;=J^=%tc_oB7l_ZDT$|r-3C=B zi@$z&e~c!;cWS(!l^27+6)@fCicd8?+&E9^*LxSo+e=@c0g-j~>}dY-nP+7SnD9(k zr}&Ba@KUl1BPn?E)Kv8N_5=>^yDvAvPiUKBhdZTR)o;mH>5bBRVsq(1w8CHM4-}MA z*)KTqjA}qk=WkAyHZ78mAHeP`BW(YB#`XCzha-xQU!P9?hpu%vQ_A$ggCPv0oMqBU zi`~ft=3c`baa|f*hYi_-EnYhuxmJ6`tksLKV35{;g zV-$?@$10SpCc5B0nP3)q`>$Jn`+Rv1>d+C+GL<|atK=<2Rjh)r3mqD#5@^rcn`b+w zk5z2&o5QuLuT|}Ky7V&(d@k_^h?{Zq<>>`Y`DM_#oq_*3!6*L>1vm^jQMhH#K`A{g z^V8s;bRUpPN4grZtosSD{eY~a+8b06To)#&j5S|WkPwhm+5Kb8q?z8U;;BD z6^6eI-C-;O4!d;n3d-*3Sn zJtBzg?K8v+uvBku7E1LvL|+Gd>hR#@@zRarnhy_m*FSuDxI%Pu3GMz^lmSz^Q$|oq74N>m~`{# znXbx9RqhZ4R#dqgb%vx(DA%MY`esHMk3L(+D zGW*A3tq7hTuPB#Qe*|d&@D7lRw$(Z9Ggaq&eS~5YXCTH?R{AQxIjz^8mw+m*N9)C? zewc0LOnQt>VhnMcy#)_m4``uwHja1JS9Ra+tFHEJ`aq8@Z%sR#2t_*_Z01V7k0HFt z2I1FDN-@>B_#0!YE8X%i4@bWFa&`eb&hB3`%nP{xd1NS#kX9w@mBg#r%Ir<4B&a(VPcN@OUZX7!C717)^4Dp|Q` z@qsj2X^N-?5Rsj)nIog1u*zqS_b8b3Jt|l&0SI5Nun%={qI%I54%5=vMqvZtiZDU6 zG?OQ+I+Zzu7^o#qGJC;G7%?m8(bTAC)*Yf*#d{Ygax0YHXaH5#HD^UH8*+eK-t03FI-`S(GD(oo&3J~rr~HR2r-8g5ve zoDsKQlg;?LZX7lR!5Dl5=`DX;Nvkn878(nj3@3YV&my}4-Xi7(OqPI*zrT98{jdbX z@%9eYD?Z!~56>p}3!u68SGmQ27XxAmLNt>u0ivBhCGf;Ln&81=o~=0Ym#)L;uFp+z zX(ei+aF{(&YmP)Ws2=&` z#R03GMGI6oD~C;2&3PVAVpd*t>B(@Yj)%Y_ZXE&*L^Y8cnCks&GegS^U6V!ZrFO0cc6h3`~hTtl+lPH;DLRB);4<8B}1|}8P zF_RcHz5D__It$mCOi|2o6Uigq(GJksi9eqlAAXPGP!3&N!e^9S#Nf&I7CyJuzPtdt z272BmS`=H+W{y8MhD8T-joCZ% zY5AoqpI#u}=z#I}BdjdgJ}1c9Dv8#4e;kFbPrDee9#F|qgZcN+!hkm9-KMc{D;Fud zxgszxNfHiV8g*?hYO$HKXjn6$1>;ZJA*BSx@GXftr)Hz>tkqM@lQwO+#jXFPEzJ z$V+=oH;N}XM_^4YUb3(ejuUDW3@UlJ!|H;Xso#@?QeV|wo3ILGw269Lgccwi4v&W{ zHl2+O@-4?~8$cCrbUn33*Kj0RvNw7aV37||pxZ4FSjY!&k@9jXVsm{wYzd!1;ofYA zNC!YmW-u{{IsEF|;W4L22I_soqo}xhQG$9)`(yNVSzs{Y@&D<$RYqmF9+tPQ%{fBW z1tNJ7Ba}J|nd=uJtle>d&3qpoRr*8#u=)BN{a?Ki$l>1h;VU4Uw@*&LhwcNEgB>Cl z$>v}h4brU3V^jLJxgYA|oE?&cA)E$^OPqXi+{ zzQ5-m>@hmrfB1KO_Lt1e_&6{#n8$?#KM$WSt$FSM*VT^?KRxNVq=S}IHqUSg;gY5o zNPvtaQOaQz+pFal#vyMK$Q9$4i|Sl2v`YA#%v9YrlFDCz5aqYVL$&7U+>%7h4nn9W z(96?~3D_F8vY=XjSOl(lYI$`S)_v4t@$$<(t3A@=6~11tp{Un7ST5Gpoj(YJW_PNw zf(?T}S}uV9TS#YJ`r*Uy4kW}>UjJ2DyMCEqm{(6H|MqXp$Gs9QFZb=$*qo zo&vTCEyh)?iTrtWpB?*)K0NqNQ)Vxu5qQOS%gpK(=P5qTDtVOZ6)JWR^osCq_^p!0 zi$>`jWhZ-T4%#f6v`1kG=G!=2-ng%3BK zmD*I*fvS0L2r>Msig9C-nBDI z6N(x@R)~X+UptyPXR=AS_ZZcMH0Pm&O57>m0*M=sD=0AaFK_mXMrO&i0gy|5y%Pa0 z8w9?C|Ay*Q^+*c5!2)5;a}rrPuLtNUIQfctgxWyV2DFAF-N`R%>54=XEyN<7^#(jL zP&YC)25OR@it<~1pT*;J8+;oM2i3`e`8a>LeD~MG>rdAVn-g=93ZufDdI}pz;pw zF+&w|b$5eap(sG;c6j@XpAa1)cc;H$xb7<9*|1LYqvOq^-KBRom!GZ(mjI&w3_Zst zY;XuDC2|S2cF|Q)_Ad0Vi3SRuoyB{ynVQ!bfFOVPa(8n-Jp6L~?h5yAx)4{Faqm$5 zNP2oI#8XtubS}6!j4GB?EF;|t#i_MvHF$9SWGpw+btP5V;-bc&weoLMIb5pk?V^|> z8ldkieIDLlK3qOSg`F>0KY_z8Rh?=00;ToC6t#8~N zqB}~gw=pJ#J4TrJ1(8XehTh^yTww83q9ax8Wg-v?bbPnEJe|GRSY2OTnXPV2pHHv; zxVpB7+-_z@_)=Nw#Il?N4ZHV>U&Czaa{BCY_H=SNGga$$`eVu#)tc7`DAdu;tR8*T*`b{>S7{DLbBIU;EB&v2si2w3Gq~|s zR#g}@dcAb*`FbmS3;JLS$jd~ZEAW4r52d-TI|r#Q7VeE*3ug+0lcwe4z33r#R0QZ= z{kGULedS|zd)|s3q(930ry}$B|45N}#^#c-O(2x2VAD5OYSf}DHEY!3M@?VS_xy_g zNc$F`BBdxz{q5q2F;@W8VfL~m$3Xd19CSz6pAH}3%X|-iVRoG-M%N#t8x&e5soGl= zZdd%XtbA#u0#WYKiRU-#FxB*`^XL4^4o5bd$=HHrxy`^j0()tAvr8-LT>yQ&+&`Zb z?~`AH&Wxy~B$+&RSp_ma+$%ZLwdGa}$rojCGUM!0>ZVpBGy(CP4I8iDCyA3GLfpLU zj32u@OsoVz%gGHqF|u&ToQ*NsE6~!d39vUpw3C=5+1OgJL+E4&h6o18*(h?PBh)4p zU}6*+qc#fP7_3RSnV2iz(L{tYHm-X&;(~3T#ow9>&i>(+i%+Zc*zH&Bt2!L{&z0E` z0H=iOqP?>Huf^@wI6tu=mlIxDQLdPJw-=4lVuKgKCkA*UN}%PR{{839ZMtLh?H)H- z|IcA22sr(!qP>qY6O~^8uGnAP-#dM(SCxm*P4VrqL>OJxm(qDkN=MW=l2OyNBB4~0 zNsQvzfIG|avb;PKOscDAKX*G$UA@Hoog%2lNVLGmS*tUVNfn4@3zo>Nh*B!EqER8n zl2vtm_aA5#rOYZT;DN%J`}Q|Xxc8Uu-?JucsxD<-9mk_n^DZud+MSYOGT`lP-8NX& zrFgP+bnmF9Q`UY_Lo4Ty^=6?2RefMam$jF1(UOS*%vZg#Ix1O&1R#UZjG9~5PzK<$ zs==AVY;U&r5&vWDidB!Qqx{x3Y9b%+p-iQIS3S$qT__N%NBjTV-SI&@N(}&@p`IzV z{yfpr8W*qX-IIg2J;U96{iMLSRtBY9IT429|7KMlgC`KhDIfpl(Qt8d|M}|O^@nQ( zs=gK^zd10QdPxje#%R*wC3PU7{CE;OE)!$qrBN5sE4#^}5ICH4I$4V!gyJ<#&u4~W z)6)P8g37T=N`bahESkU4rnHUM}Qrv{>Rjs^b~4&Et0anrX|E) zn0ImlmUDIa-rPa98_C_-oCJlH)FBqY$ei$C_g0~o@@aI#7xlWnUtMPw_4hV+&~)PM z+qdv!lPus|gwSXFoM-3Z)9};nmplAsM?GOk;m0b{6^h5Va1_%Q3osOtL2j8=HoB>h z6f!1iqCarhnj_-}-W{->(h}Rw1(eaai8@=3g|kvlq9eI)`RwFYMxkAlT62H&bB z*Pkv9FzOi1LfMsT@sUG-qB(NDTY@f#ki+)z?nU5_|6Jn#b7ebZ9WN%W4@%#~4lBurUx1mPPS>CU(lS_{?yS=`pkL&<3a6SY0J zcpH49svNq%dFW0KWPrO{l;+O`8@fXRcwu#YNi!GE=9|@h-I;*t%@iOu6z!@$A=D^t zO^(C(KCfu$y1YfnF3=V$RV+AUQpp3p9^OH$3)0IQ30hcU^}83V%j;|Ft7{vp)7k3! z+UnIG5uey&W(jjJ2r+`{mNZCc@3r*k$4_DWo_)Ewxkh=%8e_xtfwA6JEfkHHFtv0c zxbdr^x8vg1^(p+;XdMEn>N{I|@D13%+IlT=Gih+X8=2V1i^p|t)l(&;xPukYGjJQTQ8E;gV6HZP6&LceAn3+M?lP%a#%N5uC` z`{t=23RM^*#D+8DyMDnmJCbCb$YpW;T>#}I+KtNd32%BHHpUC!K3;yPCeVe$^cop$ zaB{pgoE*d4fT@dr3f3Uama+^J9wo$$uj|IzANH}Jue96&a0*pf_US=AXa2Kgjsol1XB=yb8AtA% zlMNh+Iu;eH&s2y#1*NNvBP|-G7YrMtP@evoA8x+h?N{WRe?$}6;tkgZnE3ZPJur%; zFvC*5z$)Y)orbAF6ju+EV;g%^b9mEZzm6ju-+)Spo)I=5`eU`J- zaU?Goq-C+av^KTc@1*$4o#0#Iz;e#FY!ZOw_@GjblGGWMK9YhuCW$4`lH70AZvtW=WL2V}A9v-sPPPD$mqEWg<0ZwinqV76tUho$bH2ZvY zcggO=$rlvV$)t~ovy}ERiDMqI z=T}-NOIIs2NRbq_r%&W|{(Sojs(8Koc>C_JRkLxE{jOvbC$xR%mruj29{uKMhJ32* zs}9Nu529r#d5pHvUdbwU#r_^GI=X0>wgL14Bf@Jawzu z;D_N)j0vvt<;tmg!Ty|QTc@-`5C8^gbvpMS@G` zxIKkQ1}Dk>JmjFn@8{SAbjdtP16n9N)r7TIf2H=+k7QdJrV5RnPNu5_ zUJ_xZAcCS3;xQynvhwiOkgDVB)nSLs0p`B`N_w1K3&URuQwh$H#AwB~)2F5NWkPni zQDt7Ag38)Nlq2W~lCuPUN`3I3HA&T^n-m5+-T_NBTXrzju4?VaEL<*O2iDXbqBq|< zIXP#uNg!xlfw78xJRE=gH#kf7-u(OWUo4QDS^e2t_wyI;3*~?Sp8;jBWF4mPr+$I9 z8n#aGhPRCm+4>L{(fSZ_rR4=+hnTQOYeE{FP|Cw;$b+Mw2uMh)+y$7L?|uojP#bFp zX)#bO|LcS2i8ElMl_%ZJr?v^QrgZRwH3m9bc8jf+uf_*6);F;cy~c{}LOlilA#qgA z(iI=yx%zMklsd%T*X4~>1;*+93Umby5a_DAw;lemv-=uZ>!O(;e(~i)Z zDEj3wR_{HiFe|_gdJ!b5V|Gl7Ba20p4y1(AxXtay*L8E9UMBbl}10eX0LNQy*u{{-&T`nO8B`0ncy z805!0sFZhv3eT+fud_6gR(9yNF(k4sv3@qUB@LC?A=_$9aZoJ?h>2h3`EP7D> zUQZV9eZ&T7=*t`c=_D`;Z|d)s*-Hs!E`g3n*~!VDAJwkM`c1 z=XSXGQg|oZTEc}MRhUEo<57AkX%87P$%;A;bv@(0F}^zP6PpxbTyj1YEHn6l>vayY zPwp^Ju<6J1jc9V=-o(3;5~nSe%hB&j)5xS|d5r#cH6~`5%@V@K^ogL7|6N+lvq~#> zl_uVQ`s*T1W32`&6a6804``HF0yrY`F^PvQF`;lB&h`5rk(e>oEn0IdhoqA7p-^3F zc2;;;c9AdQVeTY{oMl~rH{0_wRFGhWS7tdaeSWxOfm{ASQGlDPU%o?<41y@%4Zpm* zzeC>05BJ0U!{x&j6P?+hXoJyRPnMRnfDd5yq>4)^a0T2(O=sO9qWmCYlkOL7{Ngw! zv+%a}5>3q$9vP_T=z0t31BR1HpU1bSmv`3>e*?3om>U_?d4uhG_N^86G6pk}q1UnZSz<6G$~fxKeifh#hIBXNN7-`yX}2&_c*juQX#uccD%%7h1ALZkTAe0GRV>g z!_%5wbp14K7$B`Gcp4xZ&64A_Ggind?pX~jRRPUd5EWH76*KFd5`nIF)~oc$KrK>_ z*&w`B-(29(EBcJ zYt)R|+vXuFooRLa(@t}1kbM#xf1)Rz^;i}IJ=mV)4<_W3*YBQ`rdP=4xL^Pc8J4Fw ztbamj&mA~S*OtGG+QPh=zhtbjla(6|K@Bsysf%u3{S3B^((k6j>J%n!+T6BB5v@LJ zlo7;driy=#bpDmZ&=byX*(jZ8{^bW&B$K*=J?Q$~6c(?M$2zjmy>rPs#6yZG;Bt0y zjU3g{G@=!grzeoyK3C=$_{^ z!^J~QRzUM`*MF#l(IAiu!fAn37j(TmLbo^Scqz-0ozMkrj>YexMW;_AT#>FqxF~8$ z3{T>`qz((z?sgMCBTph)35 zROs!KGjudTedwc|0abM}UN8p+$p)J0aQ&2*&LG<`If?@dZ+ykSwSl$z>*RTlg@;IB zE=fzd$!$aBTyeQ9*-ZOo)!?WaCN5)gyJ0bLrIk3N*x3Vf#gSu3l!HN1r%RoPk{#%Nqo&hv*LPv$}}_g@6?s5`wk_`>(`=c z`n0AI2gLcbg9j!^k8Su^bQ~v~S?Y2d*DJDtZ(dsz4HYCY!GT!V27u8OzX?LuAh!aa z8Gtf8K!gQgz?0*)j&J2RfJ@T+2CJ(QQ1SgTH_32`#w3K;%w8c0jj^JZF*g4kP3<=* z)T6yxoDh3_yLWx{G1d3d;nFvDUjrd|uzQGpqUdQaAyA^yc9?1c<+8WgZE~riqz=32$Avt9GnUv>b56gVc`ace$*rMEwebhDH}^?!E1t7cs)a^m$ks6 z>FB+m9r{^+Lg?oL3bGJmmx6NMlTykpIDxlKu6LY?5KA>u!lAD`dilgVSKtC-l$Nd$&W$(V+-CzHF zmA=KRs2r1+qwAuuA@MY1F=?f}LSo5X=@wPQWyE>wqKf>?iYk&%#oY>h(~@|A^A0XN zIP%PeXJ(LnC#i6)+fm%F9inv#ypD`hT&~$S*gx3VUca;cpvbJGW>++xIWkQom(uK+A- zcbhpZ8QC=5{p0%1UeU7o&7x(;BB@EB_+A5b5wiZWD77aOIzv59)S$S}Vu1rMQ-#^C zwp=X}aHE>qyF;{pIroXX7YlAiu5E2Vj~ugnY-X?tXN5hETO?>Yw0e{> zs*8P3t=6}Uo2-%INNpkHj|j@r4`f%&R`niiWWT5TKw=#Zh!WQ$#42Q1IFF%|xjr!F zXDVeHtKW9nvBKGq*0K#JnL<@H#psU1W7%G!CYr}aYHLNtrp1rqUG+5$k2IZB;&pE| z(H2bO>9K-oDhS`K38Ld!a3Mh0sjCy8m9*Ix8?6M?B#SErxi$3)jIZlO(n0Ym?FPp0 z2w2rD@3n{2Q(O;UILIEA4VYtKKV$dD7&k?;M@==el-f=hAfXssg4?nHP{}6~0JX^b z-nvbf1(KXBS*@XH@21Q`8M#kb5v>@HiKIb;jR0FrSoOTiI@1fBs+<5mtYcrCG*>@A z49JAS+X2IBz5wJu;*htFl-$|R&rERQayb&@xF&)HgBFu?pUi@TIn>6$6k)HjQH@%u z9_&okAbcM_4S#!o`L~jyrQ@*sCtroui(GDUgd40VOeg>h(%Zl>5d%veT4Sd9?5J~d z{kFXkdR528NW;}XR>NB|7y$tVt1FGNq=gsqVysSIK0I60utqRnBRF?f!N*|v#UZ|4 zyi_hGqGQVdC*j-rAbcB=D8bL167tLmb@F9yt##sgws(=ZF-gDu`}8kWNGo%i^@fXg zDc+c~C&!q#jH_Q*TiaNhlZ5&NWgfUvfXouN^0dp*&a#C!bpPzw7=!qiUqj)-b1WJjr z=IiDO$#m`NX^R}CJMi@Tvjdd!S-O9UK+@fpcMq^MenR=;qVf`U2f+Q?1aK3G?v)%& znvMs*Y-*t)i+w=04E^~RG}HO?gBt{7@ycS{dUfiB5d-rtSG1gB{D?7%45M?}d4#9P z3QnyI#c$7m>rOriJ^fxXo#;2Pfnf%J^>ru!mnc zkBy61(+K5CfLJ`lRMRh-tGO{VAe&%>S!()FL$M>qTEk`w&P4x>Rt?+S+d#V{t-?0P zl|MAp!}ipK$DQd6!n*+97Sn7>JHSLcMg>NnRfhc*`le|e6h+jY9-Yjo?koh;7hyvB zSH@D_FS}RDP9_3YA(FeRAFuBpuI}!&ISA;sQZUy63Fm46oRxoZA<5n?l72{Sp zGmKlsxK)f>#kf_BTgA9Fj9bIFHH=%sxHXJh!?-n!Tf?|Dj9bIFb&Olbxb@kyW6Kmc z?x~|>!3K@@t(APutU)RFY!9F_AOk59X3{mL@X^;BvS^g{Bvr7^oZcO6*EiP>!OJH1 z8aOzrp^z_Ia&DAhh>$2Jvw6nYn?m7^KB;ID4Yl@k8tU1R2Ve-aw?H>)pQ8G)GCaWX;^vw+Q(}06* zYOLus_4XG`Uf1g6-?aAP6|Ma$OeiFcE>hUR*UwaJbG5tg6v=>q=7sWQ(!_>A6|5^| zA~AaJ1?l%ivMZ|7&`qPfMYeR}{|;?8tqx#o!89NnL88p0Ggb(%$H6g5$)}&bMpaM% zg>nFSZw6|iv<&8_aBVwSvaB6ZU|Gj-k3KU*mTX$E*KO3QpDooiWhE(}O^%-~HCL?0 zEO2jj6C9z$0SE55rV>oS)tJScwo9mrl^V5xld+#0jlvyE86$m{yxoM&*m)#b;Z8fN z2z7X$DndzHtDg9xej=|y2lTJ!z8(A5=eLKc{i*4^aQjgpA)IUf^6VRlX;1Pru{kFQ zjf5Ib)%T&G;rpjUZCHx&<#9e6b~7AaUS8i!hn0TKGPLjW=4g$~i`+3Ql`RIb-t8Rf zGdD-tLkdLVUp9tf;8MFZAqhnXJtf260q6eQ&({I8pv1d{0Zo-jiz79>DI-}tntHyG z^Kv8~kjSCs0C}9cssbf-C?#UnJaD8n^)fI?y;clzlbssr6t#Yl{4JeFiv=wlrqk$q zskSWBXp{>=jd#?J2dx5tqLjPv$X+I9v@)tOn6#YfuosKsl1-xH^@5n+T8V8$ib+o? zmm#yzOabTo^8S+rOS1&epy#G$PD4NlHiv4`WQCtUUOs%dz56s6+R)1)x}c>5-#uc4 zs{B@1zR6Y9*Fbi7vtC`3LB3x&-l`Z#4_W|xCc#a5mf#w6IHXjjlatfAf>p_X(t1w} z&Yn6Le7U*5{_zGy``ExhvgWmJ%nk8SVg`@SiA@=!M|)F5Cak&|^wjC>9f0@7iawQN zTZ~$=(?o5NQRup&<)w*0&FrXm%=AB#kvM>_YF3Kc1*F z%hfLP(cd^j4d()5NH9H18${0F*)EiobC$O+NWE;pa^JE68!|-_gaFm4NYT>!lTApRLfzQEPZ|UH%c1&YpOr3mKd8+ zfvpdxhzahw)?8DsqHDcjdM6o|$yh5dU`M#7`TDlK5xQeqJF3mw`v@4`7$tr@!2 z7L1VMZ3m3#`k00M+jy)x&PBMgUfHKkFiI3+{RtqR_~8 zrUHW>_3}l&lEUw(I9adWfZO1T zSqlwaTId#D*lA(sSX zCX&+*j)3{tg%cFIhM1fbC522zQ2NIJI^UklYU{95UUrFJ=h{ zIyXS%#KI_oSiexjZl+M`CqyFZKtX!qtKL_s1g@P_ULjBofhh;alchf@bBce-xu!J7 z*XVLMr_g&k(Xa4!>n{Z?VWA;zupn7)>dAV>$ee*BPvhBqj$TO&N7vKL5&St2_LMuF z3KRV?TJ1)^gi=xE=G4nn?1_2~HesEnMT&WMh-NSVBnh;ZoM&fmPR?GzONnaHP#0CX z2rV4Xb5L*9ToREfrsHpyo(ykLIdq`o@x=?e5n)))dAo50h>|KQEMkxMpRYeP{E>VW zS~t4QP6u)g{fpo6iGHn^XCg>a9c1cy(ALWdhXcIl`Ho&6xU>7QsNnR+1P`ThcU>}WzUKO)HZ=b_j@fqe09U_?&)NE z|KFegm9 zrp76(gr{XwJOA$0IrB_B9%Xqu0T&U}cAPF?tg7g#=DUbzWD+Bx<_G1%Tbf{Tu9Jju z$SUu8q6nS7Sv-B_M;fP|OgAP=Ds;+|r)MV9rfamcBeX?}wG%{-yV{AWSEr>()^ji8 z4_;(D4S=52umn{9tjQ2^$XA0$mDPH0Uq|NPI6R$CFr7(qLxHtj;N%P|XJN>M5#+`*#j{8cre){ZmcIjIrSgOObkKGdVpTv)}~axKEC@f%#MDz zE`Ue){xV{vvLnC;Ed@2iBjWa_@N>Bw$`YQ>SRy>rd_md1x9TOe$&w!hzZy58_n6{x z%MK+8v*-)Y3NUxn<#5YZ`(w0N@v+Ir1dYF;SUg%JGsN87gs|JWK z{!B)*81V@^qu{?pN$zl{u4!F(hJ?tsoED;P43p1v`O>s z&VM5yL6=Hi2$hDW;9X;NoCG0}lu@}HOzrVV>tE6%w?%AmNF4+F| z_WWRHjKOZ+qaX7sdxz^<-ONikxy(!2)b*g3>LmawAjlcjW3tLS?4pwACjE?7d7Q*x5mks$RI9#! zkPVzgk_{e?HW#)Eh+Y87ogfy11c1NRw+fq2CYwyo$b_k-h?DbKi0*@#$Ee^i{BV2w zaR8S7YWQ&Zk;!i;mXrE4P7M-~zmG#tgP>L~xijSvK14ffs&)Q0bnG{qXUEMuD5q%? z7}m_FppzL>50@UHlo5mK%fqPF4$={zLPLR6p;Ksx*}=g|Z>XcP`}P!{F8y(38F(L& zcCk01y=%{B%1xE&(w5Ad!y2<3cj{ zv|R)r9KE(pp$Kd`#*s_0z(!`C3Ds1@9aqpYjaVvaj*UP%%<}T;vT9ivx=&@l9q5OO zZDrTRkh-B%fJQ1TSu=Sk1Tk?xY!MVxdoB55|9|%G1KMh8efV|gz4yN9Ah21z(0lK_ zZ&s!Ej-Vh&6%dgQ(m_NKr3e;^ih^_z3q?Rgic|ps0lDu?e%b65@qFjI_kYK@W89B% zj?aEq`b;u2Gcz;utdyv_ic|mR?9uPJ6SMe#jwbW4^yHCPI!^LxX40anSyp*AII&R1 z0V1y*#XP9cb8I=Es%G`c^}dTPWiW>YpR%t0Z}WD#p}oE#=#$^10cz_5MR2=Yb(oVbL#= z5F8fOi$gqtdG->ybRVD}Oi4;Y`pO#bC0QG{NW<#BdgJjjBQ340)jP%vOGuER(m_<+ zR*ty3tvWlp8Wq@yXO%oMmTwTr-M^*aX}PyHPmI_DD(RD6$y?;~k!Oy^A!~e%#k?1x z^k7f&q%`SeHnINKMrltkZfH5;~)^0fLmVdJ{S^E352{J-25$cOAKRet6p5qILL zU^FtW1Vl~HzZvda~i2hkKNkm@0G+Og|qzM#?lWL^(cq({Ihpn_0>pHzI}Fg z{HzTRGdI&y9Tiesm*M(s^`DJ4GfCU;C26~u^eIJ>x(>Bp>NISvs(O;v|EQvwz8x`z zGnJRNZDQPkTMN8;{w=qfF$Lbl980ZkVtthLdAxpU;(Djw^;+}E+R|{VR$J9!wMwb~ z*uS#W>sTHJ{>!0a^r!W_-R@4jfsJXsqF46GR_Z%T@8m<>{KmXD_>UI7iG?Ax{oKCE zEG%bQY_=p1Vl2ZGm!ozTJ;%l#m}++=R>>M~^4ocnUky^(rhN5UZ~dg_u1RWg^Q7>a>Fbhs$f?9ia>zZq*?U%~cKl`QYCNxzEn{)BNbsN*qd zqp8FM_-A*U=Jv~rzQ%Rgk{25l3Q0?&>Qh&VS+!I+)^62Ndd{#MAW5=nNhLS0`K`&{T~~4={X6YWr%gTuqF3DL$DRH;Cso~LZr!sd4N}@w&7Ts-F!oz} zxxbV~w(@jW{Ut|-5kB|W`$um3ykpnQa*boJzIv_2_q?|=p8Y5VPdX8#Tq19+>Zu_0 z7KCq4-Y1ESF(k)h_WIQ{1easT*T zpGYqHiFYk(A3n=~BxxuzspE^%y&PS}R{@M;EH;PHpRO7OFXNR1%OPmltFFZTV;p_| z7_9Fcqc~@F@$&4%Q-?-ukGkgGHsi+v9BWvo zNnQN~p{DKXwUQIH=K$%DXt*>B93b6Wkg}Xc<{P5cvGjyUl7*-}WcG3{z1k+JEr0fS zHX`lzPK7Ek@+OvW?QbQ|GA5F^E#-nSmGmgb(HHnS^W}w z087!djx?=TeV->j$9c}(o_R%4bJ80{B<^J69i7-i@3~(EFv@?=o|D%eFs#4yB*Gn< z(b{!HuLM`j$5SPDl8Zw9wyvaa1SCy0d0elOOcle8B8YyUV3g$AG^{7}+Uh^%-eh-L z2kWUNr*e|I(6+5ryC2DJV^6Vv8A+dkdZmMXNuPuIT3J%cdbGrJS1t32D`WIV`rJ$B zvyVPcHR;+Agxj0Lc9J};gpdMCA3wY^;*Y0xGX_EM$Qa@4}HnV>1)x4x74%U|! z^UOr=bmjl$582rN2fk$U+*%!@D%<+}^goWOr+~^`)mKtg54Gz5&vxCj$D}IF%nF5P zd;QsPb$KDf(s+%{ z(~1%1QNddwC7W@!DKznb$~tqhG5ht^B{t)4Ndrj;;fEa&!nwB@o_zmRmy3f{I z-$rwAO8Fz{xVa^+nK|XmWU`_4GpGE=xGH_jf9mg=KKV}|5(xIvlEurPsieYTXk=uC zrY2awW@?B5$&M$?_*FkgQ;`!pV!tCj8@H(%({Qx1S~*A%#{Y zX{e>ps#@Y|mbki>J?59{S}uvzGSq%du2t8{iRa}1#=q+3AG#^DnwIHn>f`^KI9AQX zztkTowAz;OYwP3ZOx&p2mhtOa#;>c7Un6m>x`}_OKT>G*E#ud>3~y)|-q13l7X$#5*nVZcF^RCH~S9e`SgHS>kUj z@j**`*b*PL#NS%t?=126miR|Y{IeFjhm&c0W5!6XJ&cJ{XpY2dl^T#zi^RmKw6|j7 z)Y{RQIE|J(dD0oC)toVLIxQR%r`P7i#2K_NW8#e3-IzF&Rysw}`DfNT$HZB*Sut@| zZBI;`O?wm*XV+XQlg=-PHZUg6sl6W)=hAM(#JRN+sgh2gN9z_7=hYU)#QC(tF>!t^ zRqCYE7tnk$aY3zbOk7A?5)&8J4#mVpw5Ks~oK`VS()kzF5@ODXmpZTw0qL6PMAp#l&T`-(unywA|^E&aa$SKPE1(4UdT{XlrBQct+}%JutW@9DJWZ9m~mN<(g&T5IXS>o)LIEN+9X^GX{z_WiTwA{}E`74E% z#}enY#Q7|7eoI`y5*M_@g)DJlOI*Yf$64Z{mbjQDE^diSSmKhFxRfO>ZHdcR;i5-^MX^CBy*lmeDme^~FeU{j7i7Q#+%Gzs54taI- zuCv5jEb+GztF!un{6D+g{amsPPoLzJQS&Qei5-@>rX_A5v6{cCKgcfks}alazLt0* z<2TUPr>?ewSmh+y<$iRVW&E!huFjkmm*?hw_VjK1!MmMI(caxL4So zpnqmYA74d%BmH{}?I}Zt^c^VORS!~~SnC~!$xrIrQ;{JdZA3p;ye;1Dj0B<{x6S47 z+5EPE&l~YXBVoVK7IX(Zo`_o?ZAiFJg3(<>e9w4KyvrL6`fcuzJ!lUFoF0eA<@9<& zzF@@WcSPL|uRe{u_dY^x%y8*AU78ijgsym>*B$hG{Q;NDE45W_S19at+rl=#+Ytym zqhXIeA-z5gktv+cc%R$r4n(3+SHR)6`+VV`BM`NPY(cLt6bS}BL1S6+W=%k<$P?64 zQZ=F79q({?BkoXGs?s8|k}j7c6pXrEHkaMz3%UGGW5d)JvW5&BsHU*Rhe8pDS2o_~ z^0`7bU%=^~`8*-hjs^+v9WFz22xKw9DZLORD+Yk+3;|Y>(P}f4tuxblV*Eh{G54 z+e6`?-D3~=eEx{d>yX8Ho#q7Ubt9R=;g7d@!=Z@BA96|(`D7hjuAn#S4TkN0mrr^h zG1f_*JjvKDXMEJu;(TF4L2}FWEr#m94q>mpOATK+GMe&PmVEHz#jDnLk_8{^II3= zkhFFBZBZG)7O{E!E`QMObUS2eL6289(c_H7Zi++FJ3iua2kh=}z#j;>+%9j>9Sqo< zK93}`L?N%SG5W?Q$g8bU^{E+ZIr(9eHfkUp^R<5Rz_gdqj+d zdL-?FazMyhh67~IG{a*+F6 zHfO{i@u?#{=y18+vV48%vZW&>r%*5Ks~zczk4VppvT1fX7R(6jZY>uGHm*jBv%XDFHBp8VXosO{I6;0e!c>**fGFY9=Qt$4N!yxGL_ySRf zBV>2Gf)RTpD(M^ahwV0LP_6RI{sRVwdxf5Dp*P+Ww98=^maH)Ba0VQnfKQIYuuD!? zIm?}PV^Ok!3B!i=QfH{k8y|HgfLpI6wLN2>xVeY6i>JPbtc6%Tciu!yGugAzYWPBs9 zl#DUn?vL2rUZ>OPlH6KOu&~1~xt?FnNWbK}PEHURUq7V1@pj3*!!|i623G%TAwMwpd*}?KZyC-A|IsDF`Gc1{rLyk^|&+hWsNhJ0C2bDxJ zdyhRVXKBkUW} z`2zNc(_!-%Sy;>_yPYnT2iVkRM|?7VFdDRlqLO90BF?DOWm$$#F8NN6-7cAqN3y#} z$S)afFcekirIZjPXE83`gCh~+8t0Ao%eKn-9d-pml0>11-|3Y6F(hdkjXK>?BT>!k zuFDo5@rCT3h&K{)dV&GBY>JF;^VmH$ITao5s8Q}P$CrH@9}RkB?Ii<@x}(xBmSn*m zxz@`mAC5TW(qtr)d0fclBp`(pxwuPbT5@5RVuz%YWD^ltj4vFu2h5$OA4WcVyhBz( z<`$F-wH(Z@h+L{9X+mDV%k7J}0-QP0e}UdUMRuI5lFu86N-h%#%TZ(x*}QIhRMyE6 zmNgB!<*H#ULkdOuH2Trxj#otow;UFB*=KgYC*pKF-BR#~hNE&~M54y=s&WEzL6TYd z0&-9Uqb^@48jMJeE-eh?5O>?%a(+krTu#m0Be4f`bujwkUH-7!CFKWMJ;|xuVW%S$ zk_=f+Nt-&e60boc6Uav77>$p}iK7nPu*d0jxr1_=%0)Zs3(0BVcFGp$TShKEI1-Mi z+`$#^ut`?t4F;o5Z`fm#YpWzs$SapVuaq7_fh1Fzi}HHn9TBgZ#vhcGk`j#H6$*vr zCn=OU!U3oMpX=w4^>evxQcCc;y-~YA5R?+7hT#*9ezfLfulhaQY=jjYzKOi2B`br(E{!PMc)2ktFHBB$7=Aoo>0X z*rcrMlxt;BGTDgR6ZQt=s%vw)-DWm6xL1Fb$@t>s8X#p}yJWdG$u%Tr@v7~PNb%Yy z<*blVM3mVl40Xr{XnF-@B8N9VBo`G)FwUk|~d|L(&uLv*pS=1qsspaM-{klgMEGUWC$kaMaL<6zU>! z)5CO9a*hn{Z%i4xO4eDarEC2n{p9z=snzX-OsjuHBqB3X%U9!s298Ry60w8LCI4gE zq^re1y`^N5IYsn_xz@!Gk%pE@Mj9$^sx~A2ADZ@t@{XoI zZApJu)9cjJ-!sNf|EDp&8m=!#s(ke2$nx~%NEL~`oH(92OBIW%MM*C=_|=qB0sl7h zKdhAH{jHSwW|SLYHDC4oIJwLxuTrLuQ^RF?Rhyy4uOX`8b(B=$6n<>nL#J0WlYfQf zt~#x#(jlc%s`hkc`%|k}s!B2{rAkB9swq|Nm@=PIsz!<`r3ysV8YP#i5lKRQxT=Lw zI2jfdP4b`&~HJ??u2|vYscmnUB zRBYq zzc5WIV>vmoD8^%DY=CVshy!p8PQ`h+3OC_SJcuXpGCsxma>D8R4L9Lu_znJwxAAYx zAm^@{-htJz33kLN4#9Ca0~g^s+=A*EkedHtJcXC>9wwIzMU9sQ3t|Wd;yT=nM=@VI zWBQV4$6DA4yJHml;t-sUvvEGYh3oM=Uc&2m2Or{7OeK#q)b{1WB3Kg3p&h+g71gsR zHGM;Df$gyyMzBAQz?t|aF2d!go?WT=e~i2FJ3NW!@Cx3G$>SO|zqA;KrLY3l z#m3kYJ76CiffI2hzJ~|#C%lFaFjYoleF|Y&^k6gWjQ#ONoQVr@HEzc*@fe=R3{oep z(xEuUVo<19l9ESkcx|I+Hi)yqAagzQ;Fx`GG$p!+d{lsSxVFP5+74a zx#fG}GfK%jt`Xl?s*AtWm8;)VD&_i|Qz^d}!eR{fU|npdl=*id?uX;?4Tdiu{s2Gc z_kG0Y@xD^FKSdV9Tv$MvSJO%nmsjSNd>`xbdvj$@P3uP7Qz_fgk9Y*}7~*M48GkwP zT4i=k+lssReZNxve_ko``;Fgk5#J+zN}MvQj3?=sPAS`yTPedTC}saRiE9wIR?2u? zm2&J1Q_6Bi^ZP{Nsl;=L7Z9%`UPruDDa+qUypQ3BlrsO5N?DJq#5a^O{u9hB1vfQb zX{?QHFrt*{2NO?F%KFbxN}jibc&$>lV*~M4;*W{HB;HSaN-5KyRm%F@z^DA4SN4q> z?!uZ%S&s(99f`xl{qQBFtlxB8fJ<-{uEUM^A@0D>@Jrl>NANhF#GmmZUcp;<7a!qM zOr676-}IObb74V@Lpg(u^A{!C(_=a0baSJ+uTkfD2`qyZ(TP>DCN{t(*bX~k03$d6 zhu~<`%jL5CDa5bg9Gs6Ua1FkToA6`YiFG+|RC!v~D;?&- ze5e{2tKp?kwQW+d2diUkY>%BWj6HEEj>Pf!D$d5asM<=Y<*mex_#u9RyYRSD^0goE zXZ!`P;tf>IJ=FLqF#~47JXioLDCOX?qYtZKJ#2(6u`MQG2zz6H9ETHeI?h7wfckxyN+;rzcpfj}HN1uQQMHj%>z`cR zM~SI17v{q+{yK2XM=Ogs-4;R;-X@8Tv* zt?~_rrpe_}KQEXjd`f??1Lfit|V0Wb~Jd8cDFOI}9I1VS` z>-Z)vz$N&$QqtvJ+=N?jJAR7$l(L-%@u*VP|2zCqDeHS0&nad8zv5-3r2jR%rOZkF znfO4(vOgZ9UeA+P_J_(BMLB%+daAU_N>VOXN;yJ`?z&R+(2ZA0lRFut$91t8w#Niz z6)EQ{`tvC0~nHbGfa)21n9ruy}nLGI!UZw2gQqOcmwJYC4R{G`gS+u`qxO>%c$*dtZ4<5O{AVq*;I~arA+5j z%Esx}z2=(Mkl$M>TWFeo-IMY3dassJ{#L)YlI>)?u{cB7TDDKwM$;B5+e$qq!?)s2 z{2Grar4aHH@p+{r@+D;lO}nn_sA+eUQb;lD%k=A8XQ}H{`FIzpmsHAn7E*SV@-DG{ zJ(J&S66@EoZkpCY#j+DSD!Xf%ejN+Q{#0>LniwfVnl?fyC((GN%x5Of#idF)2=w~4 zsO(36->U4PX`d*2%6%NaA5iwvv}4NNa$Q%-!E;X8SL%tCvYbEgv9h1!w{id{?=ST# z$^o*y%7N0>ODW@*L;boWaTOI0l6r6DU`^AnNAi1H6-!~Rt5VXhmr@Sefl3)Z9QEsw z#IF!f!I}6bE<*i!B;&6oeiu`5T1v{!5_&9$ zvaTF@x*btJKV^BcYxLjs^HX9;55^1NCyQtWObQ{d||W9I+j}SQYj1u8e1%|IPWm zEt>g2fVc`CYx!pw1sZR>!*781?eA zjMtvHE9&KE`MnqM033!dqL~j(CZ2(Fa3L;3y&Ns`-9Y>SeuST)UY?fm_7fk$6L<>G z<0ZU~=5vIH#7{95`9KEDhIz0M7Dv6DE$dN{*o~F3CYsM9%zVv!F42+U=JSaNaUUFn zBXBJ09ST!-)BX4K2wGM`<ck%kRgCf50<%0k7aqyoY-ETc%Hz zlJg%kVGh*G;WAzk;*wYn?WmW>WxT4ywXq>K$F|rR1K0!m;Skizm0Dgp%!0YF0GiK>N)uN= zC;G8E*2Tuy65C@}3}G)cpEC_3ei6sxWSoI>a3Ln}V`~vsm5j=sX@H}3^ z>v#trqWN4)z00K1H3MeDJXi>eV_B?-Zmf(ou|77%*4PmfFoJz>5RSmHI02{PES!sr zaRsi!_i!`rz+Jc(58yHU0ngwCyn;9J9zMckX*mBe6Xw8tSOiOAIkck}t72_zh|RGr zcE$kqz^;sCuly0{F?X>9><^XEMCOl@iyMa zCzwKR4E6IL&F7c7hznp*ER7Y=iGHk(b+IwF#P-+~&F7=NhzH;>d=bavWSoI>a3Ln}V`~vsm5j=sX@H}3^>v#tr;!{kOj`JV0VIC}m#jz|_L^oE(nyB6hRQszb zw#JT_fD!D2gKz|n#R)hSXW?92j4N;*zK5G}2kyeXcmR*#4|oPI;1#@y_wW%WUF#>y;1Vur%uRI&!?2_mfg} zqmN%38(~Yd&i}vNP8-vEQL2RW<%SCN;|BZ?ci?XP8V}YvqGTr}l`usm#4-e~A73*MQlqxj+IOvGou?I?3k3M`Dj=>2y4QJy5T!w4W zte@CI{0V-J`|&86^%6f5Uqrp$RnDI~#DAe#ACZ>(=q#8U3tml^#ZZbbVzt_O} z*bL43hc3i=J)}(6n|KiF&)?*Cv)*A6zt6xoaWU%8-(-BVzF`x;e}rZ|uPKM5>(i{Z|A_VFh%dS+8Zy2c_y&pT0dxm8Bj>P^t>`SgP`LN8@;$f@VF||MYo# zn)zGL-`o8D0H0!N(#x!`(%Y{}`WN7L{kg2fWr@xDX|tZH8o$@YCfFJ~p;bl^{Iaor%G<@=Zu&G^P^dhT86kHdax?iL9_m} z6|w%@M%JS{aSzm=+sN-`z3CW!pMcYFHZDN3zH}|I`5Z@Ye<1VIpW}$1Gu*5vJxZ)U z$C2Sb6JNw@Xx5MF?G9wTWGRgF(c2wJtUuQgbMw1dFKV{GHlOb}81Ba!Xx4`|Bi5hu z$o#qxhp;!A^`LtD0~zmSexHOh(0tBg)_<<#_w~35Kf;}8)_Wc#K8||316kgA;>&2( zcm7HI1XEH!mjSb*SKKE@PsVN=UZk7hk&Ug9EH3M-%s&HBe$#0{|pw#RO0);p@cn$&WK;Ak9=Q_!q$ zoJYJASK|i!5Y2kV-NawxVLXAS(X3y*N_-n1pz0$^t!L^qhGxBDPT~Ss49lVo&HBV@ z#C6ep|DZK-Cp7C3dlC=8;W!p2qFI0V2Ju2%j^_Ic8;Q+&!%vC7z;Eyv{)lFM;cvv( z@h(0>^ZkZ&)Nf_QJXjb@VtF*{2P+ZR#0JbZ-p9YutoO^9&N!ZOV16u$Wzeke^AK0XI@lOnp;^yo zz6a8S-}~b*9D`>4-ZWzKy^saO%TVzIK=Zv42eBV(V0~g!6a-yB?oEAVZ6AGhIWXugkgfLQf~ zrRMh&p2JIc1MlHuOd&V4YC6?dmNFaWMb&4P`n?oZKo?fVTG$XLP;dduQ<|4)5`kq-Z-zQIU`|5M*!q{IJT z>Ko*CP0t79@}~RWK6f+gS+n6?j5}}_?!^OW)>E3#WzO*X1-ybc@g6?L6x7G1$84Avi=h5Kl;qbHh+SA2 zYhgodf$gyyMzAjq!O=J#r{FA{hh}@w)x;Z6e?LmrZwK*iG~0n5CO(0u@dE1aNy+qP z`_Bjb{uEPFPnQXEqS@}V7;#y&p%<&6+1|4Wack^^0qlupJI~?7V^M!!O4fHe@f&Ei z?_5s24maXfH2Zrn+jV}!?|Qp)neUIpXYn^Q+jHI}euT-Xk4uMH(QL6yJ8s4cAI8D6QlTj9O~~$$@ZA-(#`go`g>CH`zprM-;XtvADPh1rB_oQU{io_l?+hf)tZj7z4BX&o# z9j5+%l*~_mKS~_K@Ci5#&GwfIh?n77d=Iyv+3xal;{A9Of54y7Y;Spu_zs%=ZzQ8W zE-jkvEOQeVLe+=5D$kZf2b%3GYY^ARX4n?HpxLgnH}N1Gi7(?MG}}|YNxT?W;(9du z_b}U0n*Dt2Ww_bj$8ln_-w(6>q}l(+O@{x8PcS9*ab~;8?8Nyn4ohP^n(Za45ZA^= z*b+OS*-kP_+z*H1i}(tf?IT|&o{w+g8hjVcc99tYi$+a;R))&%&yCk{aMO#^j2o9z)N5}Wt~^@8jQ?ih8+>m;>`;Q7nTM(Sud7 z4x0Vxv?A_^-LVJu$6+`IC*U-kjSFxYu0^vSo-M?m;ODp>kKzybGhRfq-<~_fe_=A} zQnmguQVPj>MO763)OkaWSsM^|%Q?!kxGm58`n= ziDrL7mx*uUpZEk*QXgmbJCvO`AI4#6H2WiR6Ia37*a%x<2TZ^y_QRq0BI^AXN&aN^ zTl6}=&&RiL4Ze$;@nhVB`|t?H^lK!``IX;)$3O5P>irz4>p%5~>Kg$n|ICF2u{gef zcJyI&tcOjp4R*#L_QHWU0$;*c@im--i*Nb0#N%)>&cwNB_FJ`z_#ONJx8pAS3J>9T zsP|(f%l(D;3f{u|_&27K7ZFrCWyBnqAB$octcV`0igmCtwnDvMESZ0I;vU!^hv68U zfYWd`n*C-iBVLQ|;THS^Kga!e6n{W#|5(@f{SN+x$)v%oN}sft1#@E|EP>_Ffqtxk z^|2YY#V#1a-Z%(H;>$P*XW*N-7+2zY+=L(DPTY$J@i?Bu^LQC=;-B~gQ_6!dl};Hj zJLbbUERFH##wu7F8(~ZAfC(7IemE3g#8+@CzK-+pEnI`|;%599_uxJ}g5TpA{1t!4 zKky-{j)+zIq`}OX3kza#d;#s~!|GTMn_?U6j6v*$191etgssAMe2S^%g#dLtWx||T0E=N+w4oQPVO?y3t+5jZ zuqO_{;W!p2;&gli7vgeUhZ}J#eu`hGFTBkSQYDFV{C;Tu{-v_{x}TB-~^n8vvC0~!?pMxZoyCRbKH+d@dx}F zFXA=4gMVQ%c~M3kM`4`vT`+{baS)EgmvIu#z&CL*uEh1Y z2|vP}xEBxNaXg9V@iN}TKk*5sEMTNl2F#B6Fb+#&Ji4(8*2YHI5<6f5MzJ3b#TW4v zoQkjGe0&Sn;JdgPKgK<{50BvYcm{vP-|-K8h^pf}bsVL^%$N%cVsU%{?dZemSPz?G z8|;ih?1cky1ipl?;%hhu7vTzg8{fxm_!)kQ2k=|`3D4mryn*-dF{UVFq*Hp#hIz3F zmck0?!pc|+8)6G=kKHhWeQ^km#_>1>XW=|timP!0euz78H-3$W@dTd63wRZ8;{$w( zsS6wFlnHZU0W5}P(S}~EhIO$Cw#H5vz@9h&hvQhBh|}>6T!_nY9d5*}_$hvY-{3L) z5zpdpcpdNJBTQbzNS}0=74u+WEQ#gOiIuP>Ho)fC4!dF)``}<4h2wBC&cwO61XtlZ z_yKOmUHBCq!td}D{(@KV7T(9dF;$$AP8l%==EtH~1}mZmt708&jIFREcE=vrABW)> zoPg7CHZH(rxE9~TE%*t3j{ET_{(wK@MZAW0@GneO)JUJSm<4lVAuNIA(1Cuef%UN& zw#6(kTOG$9x!v zr7<4eSOsfiBW#HsFae|34~OE5_zF(N*Kt0+g=_F#+>9UN9^8jV@OwOizvA!s2R=lt zxRE|-Ff-=Df><11Ks)-dI@ZIc*akae5PRW39Dy(4tN0qu!9}Kx3VLXAS@d94O+xP&VV(OAcI%UF~SOAM*S+t=St6^Pif~~O=2Cyd%z~ML+ zC*pK`0~g|QT!$NRD}IV!;5T>-f5fx+8(zn|_z07iGSVjM7)xS#bYdl}i4Cwh zw!^L%#y&V0N8vb}j5BdAF2Pm!4t{{!aTk7thwwW*g}>kxyoLAiZ%kF%NT-aL1M_21 zEQ1x%gH^E(HpW)i5xZj#?2p563{JplI2#w>GF*%A;THS^Kga!e6o0^<@giQsJNOqS zD`TWjTFipEu@IKPa_B%m*1-DM4BKKC3}J5^gd_1~oP;y*OFUqCzhusYVmrq~8MV-S1cKpcTD z;j8!>&cQ{v0^i2>aT|VyU*ZA$7JtHXcnNRdJ$#HQ${FdD9Wy&v+59;T`-7lT|d*CoN{d+*k-pU^#T4A8TNJY=&*I3x=>a4#JW6 zGETx7_$DsKmAD=^;YYX=_u@f3jwkUvUdEgFCqBWHHY1%fV0O%haabDTQ9gXF*Na!d z+SmwNVh2pXDE7ml_#(c7Q}K12k8j}`d>1$4$G8Xg;Su~E&)~24JN|(WQL`J{nFcds zE-Z+}@ddP_536H6Y>I8LGX}924#W}o626MB;T&9qEAVZ6AGhIW_$3~|Z}BHQhnMgM z-owY3!eOLSdd!A-u?UvJ3h2VhSPL6s3v7?wFoJz?2#&__I0a|nJY0&aaRYvcJ8(CC zjfe3Bp2iD!6>sAMe2S@^MmlA}oLB&hVOg}H7pq}iY=W(^69%v+4#43!7AN9#d;=Ha za$JWSaVvg`U*I=*41dJ4_#0lwyZ8vzw;%QA|Ckl?U|}qY<t?xa5B!sxwr&Z;XC*NZpU5t6&}Lx@D%=nSMV0z$G`;Q7nTM(Sud7 z4mQSC*b%#95A2V_a12hsX*e4f;4)l`@8K5w1V6|9cocuYpYbAI!#nsFCi58SlNPgJ zZY+c)upBzjk2SD9Hp8~q1w+^y2jNJ387JWkd=nSrN?ebd@FU!bd+{J1$CG#-FXK)8 z6Q5v8uaQm}FgxbMI4q6v=*B8o8yjIu?0^Xv#eO&xU&L2%D!z{M@hx0~@8V|s828{l zJc8fj8T=K0$3O5Pst@An&;Kzq=E8zl9A7{?`mj3I!=~5oq7A)R z4eMePY>k~TfIV>l4#%-L5vSuDxDc1)I^2j`@l*T)zrkbpBc8?I@H*bbN2tDus9*mv zE9Sw%SQ5*l6Dwg&Y=F(N9d^Yq_QAn83diAOoQZRB39iC-@B`eAyYMSKgx}#Q`~|Pz zExeC^W2!1fI%UKhm>-K`8LWsNtcrE8F}A{v*d2Rde;kHmZ~{)l*|-3g;aYqTx8NuE zIqt`!_yhio7x5b2!M`wBRU>`UViwGeg|GycLkIe?2G+-B*cQ8B2z%oo9EmUEB%Fb7 z;$mEh>v0o)ggbFB9>n8#63^phyorC}6HHmnNT&>#9rIxvmd1E=V->88jj$zlzyyq9 zKOBlL;wv~6U&s0Q7Ouf}aWj65dvG5f!SC@5{))fjANUZp>PGsc!OWNo3u1A60qy9+ z>R1n(VjJv?LF|PCaRk1Eui|Ss2N&TAd>h}#ZTJ~}i3jjo{0YzDCA@+6@G+*SVWd-f z%!YZf2$sSM=)%fa3mak!Y>(YAf_-raj>hpg1!v(rT#Bo41Ad4*a5sL9hw%iS#tV2A zZ{q`eim7WF>68g`VgW3MWzmLStcG>53AV;g7{H!50EgpPoQTu$4P1!JaUE{Nt@tT^ zf#2XU{1MOMZ+IQ=;v-C6%SfMem=*J2VJwN|(TSC?CN{w4*bcj582jL09EIa>GS0-g zxCB?>JNN-^$6fdp9>VYN6#jx&@D|?3zcE#9Bb_p04$O~5u?$v34_3uG*ce-3N9>M0 zus;sNF*pII;cQ%h%Wy5ehg0Gi-}pFoeBv5RSx`aT3nJH*qnp#PzrdKf;~37Z2ibJc;M=GTy{L@d>7^Yot>K z%#Qgm4ohP^y0Hq@#zxo@J75Avu^$e_7x5LGim&5*d<)m$ySN!Y#yz+XkKp%s27krh z@eh26T0J9u(qLxHg$1!VzJPZ0VRfvBO|cDj#vt~>fj9zR!dLM%oP&#S1-^~%<2L*Z zzr+LhE&hb(@Dkp@d-xbr)Hl*8J!ZqaSOiO91$1F$tc4A+1-8d-7{R_c1V`g|oPx7( z9xlbzxB)-J9k?66#>035PvZr=ins9rKE>1xjC9I`Ik5m1!?I{YFIL04*aTZ+Ck$Xu z9Du`dEKbDf_y#V-<+u(v;#T|=zrb(s82*T7@i)AVckvM>Z)l`XI?RfBurQXy^611$ zSQ8syb8LrQF^qk1Fpk1;I2mW+TwH>y@E!aBx8pAS3J>9TcnW{PD|id<A`X9^F_4Yhxp9i5)Njqu39J;*0nSPQ}-8 zKE8!(@Lk-DALAa}hez;xJcGaD@AwBkM6IcjK4~yB=E8zl9A7{?`mj3I!=~5wRrn5mfZK5w zeuandJ3NKI;1#@u_wjE`)zV0(jF17ba_Eq)%GRg1NB}mcVl8KtI;N`q&KHViycy zZybap@nxKZGw@AZj4N?HZo-dnC+@|AcpOjSdAy7_@lSk$DO(%qlmWA2K8(ZC7>{nO zg0-;`w!{vYfKlv+L-9p?1*hWcI3M4_HTW)W#*c9i?!zPaJ)Xf|@pt?KAEMUANS`#A z8FOJlERHXr9er3G>tR!DgPk#my>K9oz?bk5~q#Vje7vC9ym@u@ctA2G|_iVOI=e z9~_LMa2!s?nK&1h;3|9vKfvv{3%|lc_#K|YU+@av!u$9)rs`m%Q%1~z`LQUL!HVd? zs#pgbV=L^4-LVJu$6+`IC*U-kjSFxYuEqCo3x0y1<99iX2wP$YOu#7i!=d;hzJgQnb)1iH;Tn7wH{-{+ z2lwF-{2tHXulPIufe%sZY@|;b%#69PAQs0L(2hQ=j`gr9w!zLA#9lZMN8n5ND!ztu za1pM+xAA@4hM(b=cmTh}pYR-B!W(!GA7hFxMmnX(Y?v2|U@5GCF072Tupzd<_Sg+0 z*cXT3XdI7Ia2C$PrMMb5;D@*acjMQ17*F77ynt8nHa@_on7XTxPMI(#7QkXy7H#Oo zYFHPWU~BAz0qltba5#>|i8vkKz=gOR*WpIoil5>a_zfPzAMq^yhS%{fKEmYPjPyx| zSuqb5#*$bbomdHLVgqcB?XW9`u@4T$Q8*4K<4l~3OK=swgCF2_+=XA^A^Z+c;V*av zZ{dCX8&f41>68(3V16u$Ww0W8uqxKU#@GrwVt4F;{c#wM!3j7GXX64~hHLRX+=8Fr z=eQq_;t%*UUc_s72miuk-Hr4~i&-!?7Qzx(4jt&n8dx8jVO#8iA?%HVa3sErlW+#U ziHmV1uE$OI5$?pjco2`{Nj#62@h1L>PcUV`NT&>#9rIxvmd1E=V->88jj$zlzyyq9 zKOBlL;wv~6U&s0Q7Ouf}aWj65dvG5f!SC@5{))fjANUZpppialFf-=Df><11Ks)-d zI@ZIc*akae5PRW39Dy(4tN0qu!9}Ho)fC4!dF)``}<4h2wBC&cwO6 z1XtlZ_yKOmUHBCq!td}D{(@KV7T(9dF;&z^r;L~b^J7sggB8((Rk02>##Y!7yJHXR zkHc^bPQYn68yDa*T#N7F7W@Q1$NhK|f54ydB3{Eg_!lPYVWdx5%!0YG5SGAl=s-W# z!1~w>+hP|CVQ(CSBk^UNgfs9>T#PGmJ#NB}a3}7?gLoWI;(5G`H}Ow=f+>3%>68Jp zV?K<-(io3!tb(<%5w^q*n1E62hePp2do_0Z!Zr9VZpM#s5AMSw_&uJ%U-5VR z10SN+%SfLzm>F|nK`f3hpdEc!9qVCJY=fOKh`n$ij=-1jReTNS;38asZ{z#84L`## z@c@2{KjAsNgg5XWKE@QijdV(n*)T5_!BSWOU04}wVMA z;BXv^6LC7efeUdtuEUME6+gu<@EbgaKjK;Z4X@)}e1ysS8tIb`vtk}Bj3u!=IV;>xhqi`Hf#+f)5m*6UV2S32=xC_6+L--w@!e8(T-opF%H>T=mq*F%B zf%&m0mcfeX!KzpX8)GZ%h~2RV_Qzp31}ES&oQ(@`8Lq|ma0`BdpW}W!ia+4bcoDDR z9sCQE^*7QdEoQ;oSO`mCIdq^OYhZnBhHbG6hOjpd!jbqgPQn@ZCN9R6xE?p*N4OLB z;z2x)C-FR9#+&#jKEad&jC9I?*)bo+VQGvsTUcwuA4m^k7x2gN?BjcEs-31N-AJ9D@^Z8qUTA zxD40gd$pKu@XX!NYh8FX9cn zkI(QeenF#?yL}uO4x?Z!Oo%BkJ!ZqaSOiOBC9H|{u_?B}PS_I%;BXv=Q*jP1##Oix zci?_JhG+2#-o{7x5;@F~8*&-e>{y13gZEJj9uOn}KT9cINmSQtxTMXZ7K zunD%tj@SeH<1ieHQ*bse!j-rIx8puMif8aL-ol6Y0^j2|w03p3j}s$cG>n6ZF%@RS z9GD-AVOgw#wXq>K$9C8Sd*dJ+i4$--&cmg+1~=m_JcuXoJYK`Q_yk|$C;W+KH+MUQ z!AKYr<6|;Ri&-!?7Q&KP0jpzOY>ch219r!LI26a=WSoTyaRsi&ZMYYY;Ay;sH}L^J z$9MP@E#2MiK9o!0|W@=i(Avjhk>M z9>C*x4zJ=Je2lN~BmO{p4|h9-Mn8;!@h~Z-!OWNo3t|Z@kJT^$8(~Wf!fx0Xhu~D-q2N&Zi+=x4HKOV!gcm;3cBYcS;@H^Uix!WleM#ShC7n5LW%!E0y z02aq`SQYDFAhy8v*cJQWU>t=LaR$!EWw;i%;BGvGC-DMa$9wn`-{5Ebg+9IA?GzRx zqdz9VRcB8yDe9+<@D0A0EXsco}cuLwten z@f%wExZB5x5ilCY!NiyfGhz9iX5SwE=?1H^<5RSwNI34HVQe1UaWNvwd?u`V{oR@ec%V?P{qI^2qT@Gzdji+BU?<1>7VU(gufZXXAR!zdUF z6JiQXkJ&IU7Qxb332S0~Y>I8L6ZXUbI2^~}RGfp0aTRXF9k?Hl;aR+bxA76a#1Hr# zZ3Ery6bd6^bc~BhFg0evoLB&hV>zsfbubWHV0-L}eQ+?2!ihKo=i@S5i(7Cv9>SA& z0k7jde2Q=IGyX!KLGE@6i;>YE6JT;ohgmTX7RFLo5o=&QY=W(^Blf`lI1I<)6r7EV za3yZQ?YIw*;u*Y*x9}mp!1wqKt%Kd|f zro;@G9rIyPEQ6J?7B;|U*cLluFC2&?a6C@Kxwr&Z<0jmR2kf1)ALA?hh(FLi z%-v3*(GO!_JWPsdFf-=Df>;8}V>JxGM%WUAup9QpAvhW*;Y?hB%W)lU#XWc!PvJ$p zf%owlzQr$S40pGW1H)kyjD-m?1*XSrm=}v+X{>}bu|77%HrNS!;s6|u<8Ug@!Ns@= zH{uT5kH_#VUcuY=2w&m{{EoH}?sf`=5ivT(#Uz*-Ght3FfW@&KR>e9Ph%K-^cEvt8 z7)RkmoPqOk8Lq`GxEl}QNxXp9@g6?KH~1NUq0dNnJB7u_=#L37Ii|y`mMhez=YUdCJa5MSVX{D#(1?)GtF1dN7pFfpdW zjF&f~#>8 z?!*Il9M9oZyn~PN6@J7YXdmltr_kt!F)$t`#Wa{1b74U&f#tCp24Ev>i9y&6`{EEB zjgxRDF2Lou4!7bSJdCICBHqCJ_zd6T7c|DX+sA?7Fbc-PgqQ-;V>Zl-MX)qh!kSng zn_?U6ggtQp4##mg73bh$T!kBP2kysXcowhVZG40;@dJKG+jw_7g~Es!9phpWOpTc^ zClqK|^I57f7!#J22Q(;ETf%&l*mc=Sq8yjMCY=>R2Hx9y)I02{QJY0%va5L`0 zgLneZ<2AgCPw+Kako=w^urh!50hdV%#69P zAeO-LSPcWP5w^r2?1p`D2#&@{I1?A(a$JX7aStBGQ+N??;C+0CZ}AHnQ{C<3z;GA^ zV_`x}f$1?D=EWjd8Y^K4Y(cm;ZZz;m+=-p#25G;zoB)8yM3G(0i$6YOpK{8Bj&*TSPaWz6|9X7u{pNG zF4!9f;Ygf-({Uay#WlDYci}-if#>lW-o+>Q8b9GrG-tZoDGWx!m>3_EVOq?Bxv>zI z#0pp)>tbVUg&nXv_QRn#1}EbzT!<@hJ#NFjcmz-5CA^6b@HxK2uV|U&ZXaI^k5Mr; zCc>1M0kdO1EQ)2YGS$8&fU@8Dy6g&*+; z+Go4lDKz?F42*|KF%4$MTv!lGV0o;D0oVvzVi0!2zBmL&<0PDk3vfBE!>zan592Ak zh&S*)KEt>81&ulG_Hkf1jDoQ+A*R6em<{t{5iE_BuqM{Wrq~8MVNV=@!*Lu=#W}bb zSK&t7f&1|op2aJ88z13I{D9xlHrL%wp)evw$GDgTQ)4E~i3PAYmcy!82LrJMw#Tm6 z2M6OQoQN}UJ}$$xxCM9PAv}o}@H*bZr}zdx<1h4?=WeI47#aOB0VcE>MAfCYUcn$C36MT)I@F$uJ z-0c(wBVkO8kI67CX2IN82uorGtd4cDF}A`E*d6=fP#lAkaTYGb6}TR^;a)s~r|}Zr z#0U5s-{DuZEOfVzFNVjc7#kB|O3Z-SF&`GiGFTaFVFPT2ZLu@J^nge@@$yJ24( zf}?Q~&cp?{9M|Di+=GYl6kfy|cpsnPTl|8?Vt4yEFdRm~SeOt~V0z4kd9etV#!6Td z>tj=FgPpJ^4#43!4yWQAT#T!5BksWccnr_t6}*j)@Fjl0?`T`%Zl_Qf5u;;VOoFK~ z6XwJMSRBh?Rjh-7*aF*QSL}m>aTHF(88{!8;ac2+yYUd7#0z*G@8MH?gP-vi`Yd&~ zQ&^0Q{+IxhV>--=d9W~+!irb}>tPdYjUBNE_Qzp37N_8BT!bre18&ECcofgzWxRzC z@ddueZ)jcSZXYK`z-Sl;6JsjOh&eDn7Q?bw1#4qNY>w@)3--oAI1(q|bexAvaSd+9 zU3d^r;CZ};ckv0n#!vVY&E@WP3WJd_CdS8Pm=?2OZY+c)u>w}dy4V<7VF&Dv{ctFb z!O1ua7vc(BkK1rB9>LRi32)*9e2(w%D_T~#+s7BfV^oZdi7+K*!0eb0i((n9jJ2=< zHp8~q8GGSC9D(C;8qUQfxEeR%PCS6e@f=>oJNOu1;Ya*|_Lc5-3XOgk1LI**OoN#* z7Z$`4SRSik05-yw7=+!hFAl-cI0eH<7L zqhKsdh$%2VX2ZN#1WRKjtcmrpDYn5**b@ifa2$tIaSkrVRk#s%;C?)YXYmT&#z*)P zKj3$?t#-FlD2#~FF)k*-)R+l#VgW3U<*+K&!9Z+*?XfHN!NE8RC*lm8kIQf^Zo%Dn z2v6b#ypH$qDZata_zQj3xZ5c#Mn->3fXOi(X2m>M7)xPAtbz5g3AV-K_S*(J!u^~3c zcGv}b;~*S~6L31t!=<)q|+i{UXU z#>PaL5;I_S%!fs>3|7Wk*Z`YhTkMRza3GGr@i-0V;u2hqn{X!{z~guhui_nijIZz` z{y_T%cRPhfKa7F#Fe#?N%$N%cVhJpd)i3}XVM`3cZrB%x;AotLGjRbf$91?B_uyeX zg%|M#-p6P77Qdje(cL}{42MxL7AC|Lm>#oXUMzy8u@ctA`q&iPU?=Q}18_Kw!>Kq2 z7vn12h&ymU9>cSE1#jace2E|MJK8q6+bI-A#ON3olVEDhggLPQ7RPc}73*Liw!rq- z75m^|9EB5c2F}N2xE8nIZajo1@d94Qd-xRJ;Ai}WKAYX`6c!_+KPJHBm=3dI9xRNd zup-vLde{V8V@K?P{c#wM#VI%&7vW0WfZK5&9>p_w8E@f3e1Y%r8(O!x+sBC!FdD|e z#Fz>*Vh+rY#jq?^!P?jmn`1ldg1vDNj>HK#9p~XvT!Wi&7aqhDcpk6eU3`MC@e}?; zbE~_Z!eAtfiSaQRro}9n8w+7ctboRS9VhT);*)T5_!O~a> zYhrzDifynH_QU}=9LM2QoP&#T6>h{GxF3(M#0VG-<6vS;g&8pi=Eq`K7OP-wY>3UV9d^OqI0#4L1e}iZa4D|A z&A1B>;t4#D*YGYr!Poc+f1%m<-cm7R-%>uq0N%>R1;WV=L@{-LW4I z#W6S;XW>Fzf$MP_?!_Z`8ZY5Ze1OmK9ezd2UU&QWVt9;-u`v;*#0;1n^I=gegO#xs zHo#`s7CU1v9Ec-uJWj*8xCB?@Cftb!@Hn2st9S<=<174#KhVC<-AmASOUvqH4MN;*b;-V8}`K^I2tG6Ok9A=aUE{OJ$M*T;YGZG_wgCN#V=^=cejrN z!(kMRg$XeQrpIiU7mHwNtb{eOJ~qWR*a>^$0343va4OEh#kdML;tt%8$M7s(!Q1!< zU*ZS+jEwDXy#XdL~N8v=Af%9=0uEj05 z8xP@0ynxs79zMl4_!)np&p~%Pg~iC|j|nh2ro*h52Mc2`;F)WK!ur@Zt z=GYFqU~e3RBXI&w$9cFE*WhN{g$MBjp2ur=7oXs3{DeQzJnU|#Fc=AAVth=7X)z1t z#zI&UD`0i3i;b}rcEIk~4~OCyoQ$(@A+EsnxDEH>5j>5T@FqUM=lBl4qUDIYeS9%I zM#b2e2vcGP%#Qi6D3-y>SPL6qGi-~Uu@?@+5jY;F;apsTt8o+V!~=L7&*4?PgOBkQ ze#9SWKk9C$(CCLTFdinwG?*E4VL>c`<*^zDU?XgaLD&uZ;t(8-lW-<3z~#6Ox8fc= zjHmD--oX3#4Bz4xG>*C3$ARH63dX{Om;%#dHq47furyY}nphv3VjJv)J#hdI$8k6n z=ip*og&T1P?#E+z7O&uKe1tFY1Aa%_ad$g~!iX3h<6;s_jhQef7Qo_I4y$4v48#`L z9=l>69E_uIBF@11xD40g7BmgE4~&S>Fg7N{VK5TL#Q2yD(_$9PjfJozR>1057aL`tc4A*8MejF*b4{Z z2po^oa4s&v)wl_F;sHF4=kO}t!N>RtKjIIx%MM-lccIY_V_-Z?ifJ%2=E8zl0?T7H z48TU%5`(ZC_QfGM8YkgQT!71Q9d5-vcoZl-MX)qh!kSngn_?U6ggtQp4##mg73bh$T!kBP2kysXcowhVZG40;@dJKGo5S5s zp)evw$GDgTQ)4E~i3PAYmcy!82LrJMw#Tm62M6OQoQN}UJ}$$xxCM9PAv}o}@H*bZ zr}zdx<1h5_b+=PkjEw%60Fz@n%!+xiFqXoKSOe=}6Kstgu?P0YVK^42;A~ukD{%vE z$9;Gd&){Xeg%9xszQ=E9b-LTfi4iaw#=*px3NvC3%#X#eELOqV*btjzJM4nJaS)Eg z2{;|+;Zj_Kn{gKnWPui;&Mg0Jxt{zNmByPd*dB#epiF&U=CESMV$VM(lj)v+!% z##Yz?yJJ5bieqpx&ccPb0@vd<+>1x>G+x4+_yC{dJN$~4(C+r}#qbyvV`Cyri5W0E z=EI^`1}kGNY=F(MEq2CUI1oqRc$|iFaS5)*O}G;e;Bh>MSMd%$##i_ef1o{#yPZO# zAI89Vm=x1sX3T{Ju>_XKY8Zfxuq6gzH|&c;a5PTBnYaL#<2u}md+;!x!i#tV@8dIk zi(k+P>uw(hhQlZr3lm}rOpn3sJ z1Lxy1T#H+9Hy*;1cmc2DJ$#C9@H75GpYZN>3X74^9}{45Oov%94;IE!SP^SrJ#2!l zu_N}t{x}TB;uM^Xi*O}w!0osXkK!4;jJNP1zQFhR4XqK}?c>A<7!BiKVoZe@F$d?%ZbGMH#hR3KF8xvtl%z)W39~Q+jSQ%?!18jzEu`~9%A!ku^k zkK;MKig)lazQT|A1MQLA?GzgQFb2j$xg2)iCqQXtSNH#E_kBInDTi;@^vUGAjEV8l z6DOxZuKxe0&QCw4ZwQXU2{;Ys;38as>(FyQTYgsNn%@I>4A0;tyn*-d3BJM)_zf-G zzjdJJ{&pm~V`6+vhG{Vi=Eg!;5-VVJtc#7Y6?VYx*bj%|7@Ul=a3QY1^|%f9;t@QJ zm+&S&z~}f5zoLcvx4sx2qhf4KgefrtX2*P36w6>`tc4A*8MejF*b4{Z2po^oa4s&v z)wl_F;sHF4=kO}t!N>RtKjIIxb00S}`e6)=he8}7v;cp5$D0XOM>fY0$Aenrpm-&f~#>8?!*Il9M9oZyn~PN6@J7YXy^DA8vQT^ z#>1qT1~X$WEQlqrJXXU1Y=kW_2)kil9D<{963)a0xE$BvR@{S!@f2Rf8+ad|;amKI z2FFbYhQlZr3lm}rOpnzsfbubWHV0-L}eQ+?2!ihKo=i@S5 zi(7Cv9>SA&0k7jde2Q=IGyXy!*@5eQlCT&V{V@S1$8?w#^I%~tg%z;|*25;)8arYS z?2p56EKb4MxCmF`2HcMO@FETI1iWN8r+P#@F1SR^LP#K;uCz0pYSJ|e197TBVkO8kI67C zX2IN82uorGtd4cDF}A`E*d6=fP#lAkaTYGb6}TQ{@w>K*|MYsmb6)d5nkO^U@!$J< zK+kypyxx(;<=);gGWufzOpfW$lkY!mkKl&?-!(n|X}kW<`kmzj+I1Sv!9}M zhey$Kopzb-Tlf%P;CuXrR?fGa7y+YU988RXqPc%923WJd_CdS8Pm=?2OZY+c)u>w}dy4V<7 zVF&Dv{ctFb!O1ua7vc(BkK1rB9>LRi32)*9e2(w%D_S_;^2P8N6=P!}OoS1@$dG(iO81{GoYN}x%T_{ zuqc+n%2*2Y^88HXu$6{C(t6*(x zh|RGbcER2_2uGslcr~5wdAJnUU~uyjT>by5?bdU@=s!JP|KEDw=>N8M9`bM2oBvbS z56^Ua?)!SS56^z@fA(@H@V~j8>N5Vu`0wt!26sIBckL3~aQ=)p0seV?@}Ca(=W)0& z`AcC%tbz5g3AVOI?E zaQ%a;f9#Osy7a8>D>@BhvnnT1Nu_KJZx|I-`Hh9^_r-D%+DW^k8pdEXx?#AkOFI~b z6kU0OROxn|e@Kk$I3(S(wO;Zbr@!m|yySmO_j6Uo^O^4Nsw9uB$79W}%6MJ(-KE?0 zy}OPyP5Uk_gX+~6gW9=m;wc3Mb`CPqG^`g?&q!0heS0HKyTBG{v@%Vrc8xj*v})8K zC?K$7P(YJ3_$@cn${1f*CL>OU{HrP!S`-cuU);CfkA=oH1Kc2 z8iy3tKB!*15VEunY}GLMakLL=9m0e*Y~3o*J#4$yzndjZOKl0)tTw3EqDB3B4Vt@} z%e%gHD|cH;D^lIAb3pwLjT&jI{jO}lout&&!5z|~sWyzeENhREra|kLEdyJz7~J)ZbRipQ@>eLGre07`yQcL! z1nGDhw`(oy#$Uhm@9^~8(euhA1=%c&JMz|jlicogz5k5MC-t7VP9emV@`}@KFt~ix zyyEnnGPpQB5A}?E5^$8(vzgJw}5aM)y=xM)xA;ewxit8UjobFdW`38g#_ueaRU2;$g-|!IP^gfv=5FzdBx2MAx`h7dd539gt&5E zaeB`pxc1ZE1A6k!49zvY{{=t)PMF?@{z2a7e5O>=vZdC|zdjHxp-ql`l zdR-}-j{X_5>-AaUykDNq_)@8HHhTUUr~UMmRNlVw{&#WdB<9cY>M_&v(&eGARPvU~ z9V4H-^%X0Luc7BycjTQ{-WsRlD<^O7eEN6)pQX<8^5iQe72f%Ryz=Fjex7`-rR<&W zzVwKt6}&w8^gg0@zT=WFpVZxvcR_i3^65UuL z^7iE0A!YA;`n|d*hxGbg-Xdl1e5oX#wwLBBN}P7<`Hy$LQj#ya)OlW>+?S=oJ6|TR ze8r`oXZpTM**l+pukXpBIX&f%QufZ5%PU_=;&l4-yv93U6UnFRv*+c>r`NUK`3iaE z(|ZS=>C@knYd+8V9VGd5Jf4>)pZ@;LJ6|cUd}SrrlP{0N>F}QQzI!n9tKSR$O`P6O z|1&OvXW|&IcIy50KjT9Ei#Yur@y|G^`u*~h^?Sv?iPP^Fbr?_k>HO+&o|mWn_DYa< z`&II4zp4`KX}{~_^92i-tyY&3RJ6|oyr+-6xNBZf#AWy!>f8<*hL+-73 z7LLS8Z`VtIckd}{MMk+nwaYW{((UP|zti&`@A(YAMjO*T-dYmp8Lv|s#50WMdmG`u{4Q#Oo`J902rh!aXm~xPIp&ZhmfG<8)Ye zzJ?O)ncv^jM_h~ahEc|oObjkxP)T3ceCqTy3L)QC$(P_S@|~A_$s|tmH3=c#b;(!d zFYmn|WTrP2YFPS51O+`aJiW{?+tl z$R^u&I8V0V*OyB2z>_v|z|?%b>V?)~iu^-ysz}JW%Ni<*I5b~ucVHi7) zO8~Fn=J$MKSzn&I^Yxe5;HK|!REv@LUmOSjT%W&}cN(9gxbqDRAz#=O7T5IuyXkx1 z*s1@@*)_j|L&!Hu@+I*k6N8)I=}nx!_oqWc$hSfAt@)efA^DoHJcfsmuaW$~;cxcO zsai@t$)n46WC;1TOTM_$-}4G?e#c9`coL-fM$0?6<$F!?>2J(DFHiB$<-1<;b(Wr* zPp=Vz%V)?9yQrRIVsQCVwRXDB?=_$2e$&6&-l9rA{f&p`y@Bp0{xt`fMQ0H(B1n<;yI881gs!zy0#So??)`zvmTPzD&LUnZC6W99+Ijl5eCZiNtw|f9|g;Nxs9<^Nz%<4J3$zT}>X zk~mNC&-RPlPmY(;Q>Slx2>F^7kmVu$J+I*Mb?@(V&8OzuDevIs_p{`i?MWsEmoL+R zf7)-i#08gcvi#6d?}K?>!R50LbpAd+-7CSt<+~;M278i;!R4zZ`E>5|X0e9wlE@1f-To8`OSYx>TIkk4A=@22m( z*YsTsAz$>r%=g?Y-(|0Sd9D8}L#Z~>NWMvWBaN45dE}Hh@B3@^A^)7eT$NbQ^3cCW z&|%Us`~mp`j->h!R9@Oo-&OvIdv5x=)|Wry^m!VdY0!Qxe0=qk{P#=8t1tcgTkq*B zB-7Vh>hH+=ro27V*GA&xG^CAGxn9#9zD9OWhv-SIO6c#`Wr>Ry!7%Ph%}aS}KIwFg z)9dud_#DmGNDxkX@bct~DBa$1`(^qDd5ypmC%^a#sMo%IV7s6+4brqu8!vt4Y?(8r z&zv!9mU!{f#nYGjGkv;rS+Yo7<}4XAWY3`Wbwl+kT&jr0;>o1*lI-{V=;S=_?W>sX z^V4KOWwID=MnWgA+66R@BEYth*ujOL$ zE>5|GsNX$keJOeC_Xb)nBX7OuujO*`*86l?t|0GLpxrx}6ax+oCm(p>ykatVUtwhiFTDn5DrM{im zo^p`bfpSN&6Xnig7s_2l-A8Nw?(*(Ixu@8Ra&NH@<-TG+%KgOwln07~DC@p_2<4&T zFnJpr`mJtOWW{G0)0jf~hv4ylefs&!wz~Fb`?tBW@#s1&pOzi%Ek>B@@2$p3eLBn^ zFRR7pT^-Y~ZI;%zrqtEOYVj@8+hSO)<`Ma89NV!McKHhoU2ayZBT`!l9Uu=ux0cq{ zwaVkK4O=Q*_&yu8+a`ZEY)hvL$Zf*h3`Z)RNL$x^Hp9_Qi(xXfw;8^Hmhn;!Yj-Tn zFN-K#&RkZ*u~_aS8R6cAwHuBlT5E*YrqI9Yh@inXBciWkp8ipqU+*juvtWc&8h#1o z&xajL6G*j8_N<-Ku}lZC{kk9{U-eW6oGDMnaYogqiPZd#T-DncgvLJ({e1o0kRc4^J_Vt5ydKB4f$G}B{t}oTgwu5 zmeiu*=ZGg+OKG`x)JwzdhO>-LvtfzaUcPj4me&ru#R&gc>JCVdYewQ4QHHOQyF(Ud zZJX?B-Az_Ui-upT`7&65j@9nhLe^?$J*U+B`soBX>+6gUmd4Rk;q{3}`X*;6dtB*= z7U_LBtKsYHP*^(rN)D2F>!?M;FGm=ew@$9il0jF3NOQYejV8_w9b|io9MHyaZgfRP z4rpgMH@N~M2Xr!=o3-fccT1i~@7z*A%3=JDkCqnLs^xHz1L`+2oZF-sq+O%Rp3%8o zhPohtGBbjD-()mdhGW`m(S63QcmXgF`>j9D5}(hnwXDj{^L3?MFmgZ(8TOJ6 zTgY$yD9L$M<4gICuOji+bl+9ZFL5!czd26kyOLkaOwulQdq{mXzxx{`=DuzXwfwq; zk?DM(!!|UcD;gfd^`SEreoGHCb?G^K^{Jtca-C5VFz5S!ef^yckx}y9u z%AX)Q8(TNYRNDQAM3U_84qyL5Ph}F^9bx>JK9*@~>KZ13|Ec>jLCviC(Cx_n=W9xz zmaabO{B5NrTPv$Rb~}^**t(LfjjK;K{}KMur=4||^vUJ_N`6o0>|oXHF`xgcax$Kd zuCPM>aiz-H$SNEi>((W@VYftj>VF2g;l>QzHN==|3ijb zWxXr?!X%*9rB9Q zD$m04lU}ZDx^DdwBSUAA3fJyX*BsYoWOaA9@pQ||-dfk!xR#PKo3XDxkOPi0p+z4m z;P2YgC3ck!|EThu3}+IHPMqC;wmiJTnbe|7+E+%S^Bu~rIb*LIWg6j}c`a`>L88~v zn)zI1!+%p(X`}qE4j=!7ax`@ou;{TVj1gY3I(|=KoYT(UR-3e*|KoX*ps!W;l}{F@b& zY=f=(4*;t9_td@O5UXwqP5ejdraaWu(Z>ITJYm8)%+=A^zo71}hFc@a)b#d$B>yYI zIl`JyItKfnlP{*7BVFT}?cZytBpBryX1@QP3eqv!)v??^_huPpjH_e4|G0G0XRNDZ zyMI+3={Q%%e*ab%WIW?t*^V3GbuH0Rx)xascejq!)$MS1XY_POGvd5Wsv<($Z`;7*GXDzh9;rQAds zGE|mOu2UsjG1+m4%Bs)HPjIJ!X;>t??33kfnLbr26Y78#AJ>3VnJ~0$!WJJLHnnsn zYIaNp@zpU{$FxZtRjPELB$jfvP}Y1&Eafffl2|HO^-M5n)l_oYEAP-r4NF8z;&72d z+rs&Ws~jz&B|@|*1)@oYL`LY)d8AfOAEFwza5+Nf3|(Bl)5tK4Kn9glqG*=Tp>=E)>5wON|1;|F z7EAF+S{ot0j4`dGiWtc&*6*L#Y|<}MB#SNFKbay+Utjl3Xx}KVzNTk9QN4zh6*gKF zBdJVR^eC33G2Ek+f2E=m7xVXErcQ`IgUN(z(pX-dk|p-u_ivE+^6p8KRRB3k@>@%3py-EwlTy<{`hvs{bK8pd?os4rz_SerAP87gZS zt6>XcHGN9ml5|u z?87B-Hwl&{?u-eE>g+(5615}7{B{B)LM_;{tV&9eNL%;d?P*j2FQ z@ueAGS(2EM2FR0~?6%jIq;{Y8mPNCr!z?&mPO@!flX<1I+ae|>O{5fNyb)_<> zxnE(eFW$+N&&t*09LDI9K|jl_(7tK9X}QLBE!UUdyO>7F_8NIpo)2Xj+vUW_G=9l5 zN=>7o+$}PVlk$IuOrzcnjj#1VKLhV+%{2KxO{P&ho7QAJq>)RaY0cxPnm>PK&Cp!_ zmyv0lktbG}MqhdUmua-JYW+9)s@XL1uGPq!ey(v%(a$$>oo^a*MrvgL>{{NCNh24v z(#Yraw7zC@jr5bJqM61Hxw4Rh!ElXi_DCZW+|{ywJgpxfCl->yHD5L4zh9X~d3lzV zX{^kqpN*SnhBHGna;;p9n#RMKI_&hvS~FRm!e$yvi)-ZYjhcB)3N0s>|1M=3Ph2hE zslV1&la<&sLd!4kOe2CkGuAXp%cZJ}Wx1C3EY&=pgy$f`xC-Jqu z%r-4ol;^0)^jFZ&$GNno%P!5(XryL4D#tU^s3XsmGmWpVIbC~H%aK~^XEwPHB}+}t zl1(F3W-S+rq|+AnjP^a(RL8O>ziS>{HL>1mOhq~5n?@0N&YWo+mZ#vE#=whOUu=Zt zd3RE4>MYQ{+gvUG^{mFfuC00M$`Mo6kS7{bQ~pDjX=If%D${s5Tx)92)yNUawC0v; zdF^&hd6>|e?W6psEz{`qOJmZ>(@SJ7!fUx-WzE^yHML!nYvjPin&Cwi?Ym3%l(Htc z)=Z0>f5`kc(LAFp`kAY()=bEz@$KqrImt^MYT63zTVB3rHI1V3gkRIRQA6u{Ra! z_R*ZBj%&^4^BP~#)ptTlEeCYhp*DBW&up%>YmS_?o5tvkI_$R8I&A&A`k8#0YpSMb z`LO&Ij%f@_sxj5%$ug!9tB{r_%hU2?yOyg@*^0MoIcY8J8?~YiJ5%l`$!(|7I^M>v zt@XA%eb6*A6x90IpR~N{utrXDEuCB)v~2IC@lglsXTz}inXanl-zs;cWl0^@a-(qi zS@4DSeG@@H7oXIca%VMWLR|eEEVrR#dJ5`io^cw}Vvd$$$(5FAT#)}WCw*h-P%q_( zWg6M#K9sEIc{HY57oC!v^1tz9FCnMBrqN3ds?yH#6lBxb7hcO_?zuk8>u2dAI#fxy z7?QDfaJ8pgX~^9!*EZ5Aoo2feq%|eBxW?t$C-;$OCz{5knwp`R+^mv)T3C&Yol47V zTzlO(@_T35H@ddyo31gojiWJn<^QxvOPkGN|y*-C(xrA%a|Ld#veUedg?sP5XTq`x_oeNsOTAn^F>)Tz;)5NtbsyEb{$CEUF zXNyLbeyN}FU2~Lqoz}nZrZMTeYkh>-I&2qtTBz*bTzk}=ep)_vP5buCp`RB{>Db@M zm7J_m^7jpb*~ER>=)W*^?B}@}SAC&xu+yd6DLMx4~6*YM#9EY)SbHuTy@@ z)M;2cP|Gjn*-NHzO78v1R|3VlxVp*n zoMbLGYMv35wC_5(kty@ORb#@smP^$jjp^XpA3jK`<#CU6*ov-gA(y`nn^`V{zXHbpngUysqwR1N0VmVbqYV3T64y=|1aiRA{|C)eHz!cJWg&1%euW00lXIefqPh;w) z(!K#jwLZmbEw5~#efyr(F_s>x`M7Hi^_j%l)YjZyQ~G5PH1G>{#GfM zb*(c|yJ$^oxk8jyYA0K}QMrPazbDc99#?eOimq)TlHLn4ZmiV$^zxrO<>;_S#}((P zPDA{A+V|QGt!dL+Gi-6KEsjkZx%-JlGJi;^f7&#rqE5+zd^Y(!m{@DZT6EZ{*>voM z<%r{BG?H%ueT)<(blB9cqs)i2nse?*t(h=Urz+ARt+~8X$2iS(EJ*i3>t~hI`g?Mc zDd!b4HS+=29)4{ptshfWKZm&X1M6hXbe&(?Q+}3cpG9xw)3GgwjKeqOa49?cmLej4 zV!5+!CL7gWhW=s+omKc{d8+%(0oFTuC>vqTbx6iF!8-rCFwI)FpHwche%3?(8f%5v zQn|}o%Pt(XmY*Xx8gx4eKlOr)CFZD9GTrf?_mR-FpNdF*?&gD~zT(S5Qr~jE+}|>X zTi$0C7Fr(dlyPpibd@`R=1I%oL(+=xEsbU8=}H*t#8~MaVVqoZ`J|W;PZ~Pq8ad?n zq|zICKB>#e-DqnX{dUAB?TlB_-08|rkddZ;CX2z$(Ef#dW~`;Bp_y9x%4g=B^1xFw zOVE2MXFXa>%K0+Kkk6ty^n|QzHTk!+W{rIEP&G5~%TAfIRy_h_syeLG6X>3m{iHm= zGEvXOM_b;=1&=x2a<+_A&a!MuDP2n~@sG<0*I7O!lPeA%u_RJOfT5R#9 za6%2YvLsCN$qAMqLTnZ~56mLJn9G>4*+mYU>g4yro2H3Atcdv0idswA}KO%7>N{ zx?g!@$x%!CJ+|x_A$+v-S|qysN>v~-cCR+FW zl8{N((@SJTldVVNOUOd&5j`3#vSydBy3EDaT=j&F*2}tc-DFL>QHI-W{d!v}4_Y_s z6~H0uxT;cl%9_2V40p+zN4~x^Z&(}kmXMp)rx~T+UF)<(()G}~sG)Q{vu>U*X-}y=|+9O4k6}SG`;rZhLrJLPpq3Jw=>si?7GODYh_riZ|aDpnHG?wo`i1xW<-w zri84u#i%Llv4zVf{r1|9%PEX&@upmoSSr#yjw0ho+kB-=Q@Xr4rJSpk?9f~hxeJVw z+F_Qgdi)=5Nxe@PVM(FOY^3Ghed!uyIbT@1Mq7T#0OlA=Q8|Zo4Vt3(M;U2}R=XuZ z$y}o(v{dQ(Qd9PWo)6S6dQ!@DLa&x`KukS%on#3+j_o;-F+ftju(ob3ytIC{N$&x6 z=Ne(DebodRWTkoSs&wrzBUKdM`kvIY^e?_AqsaiimIni6;)Yv; zhR8@qTBGSX@hIziy)+qX{h{mQIP2Vdl487dt?p|NSjUVP4q6`t$pD9~&-csVN2~?q zKl+--tnWe#$F0e9&vnxJP!Cn7te5rZc*a^aP?DXsKGSRM^VWn>rSgI`x^8U`tFRIWE&n7~7I0B{cC$IiR`;?@;9%QGJ*o}04b+S2VYa9_ z1zGB8WVn&G&dDU%XxkFKFd1X3rTgu1wwijN9&an4E7U|=B)uG+WScukm}2`N-z%9@ zZT74Zvcgv3rgW{eje06wt8MT4NR~CWM0&ow&h}hS|JK_=*O1B!wo`iHc+u8OuTOv3 z94(|@Z+p_((yy<5kzOeEvwv(NO*ge6qqJ%L`~ zFSW^Q>oX+hO8X@}-C1Qnb5*+5+P8g?e(UVtH1!61!_zYMjrQd7YeaLiJ(2G7 zx7bHDm#%I0`8%ZFcKdYQBzD@H>51nq``luZWskjMTgkH5-k^f?+i%aVw;B%EuQZd& z!}f(OCCd?eKV3DB+3V`z`MBM$m2{o752`LXPucGk6wca%^hUxtd*fFUa>4#^gpA>$ zy>?FNx@;ePM7Uyqs5`@J_Ksns-*x+%ZNg3a^W`%5Eqm%OGNL>7NV>Y+wU@gnT@UQ* zh6)euZ*~9m*j_f1L_V>X@|EG9+27ul@jbV@|8V-UNyh5cRwM#wO7{` zd~f%uE;&EgqwN$v+2iVV^w~akpzzh+SPt{%H+!MO68S?8A~K?%_GlHPvbR}Vws5nL z*<8LoH2axVu1QyabMPY>-#~N9abb|TT%Nja4l(D*jbC%9Su?eaVYoT3t3-}4lk}2) zqs^>(Vm8M7bW*y;nLQ6m$ar&~?#U;b*M>>IN#?_{!c_C7o-|G~r|UWE3^SXol;%vc zx1MFpHb?0Vg*oQkLBf1ee%9BgTyLSDPmODmY@kn~ zuo5!Jr`>nSGQ_8UZmAsVlUDD+4fi>#C%Geh?9nA;k55LuMA_@JDU);^@X5DP`W^Ji zqalZV&Q+CuM|@W4HQh0vpWmeGxKEc#lI5h&2wjCw`Fz#8erJ3-$X8hAS)YA+JU{QV zLjIA2dBG=)Hr*wkpsdn$*=KuU3AyT1P|vQe`3%*A=MA3@dQ7|Nv+tXP-0^w5PP*>; z-2W+E4}5m(ErEwVPag_Td{R#qp88z7EWGfEa!w*&`pkMFSzh~;{3IiK<5Od!^n2%% zXuI&<=VurRndSJ=TShzE@gc2*%ykUbvygd?VtUoNz_D_m%+W$e+m6z;*s(_s3`-n6 z<4edg$H^nowcHVToph~q6x3Uss~nZr3u_#c^i*N3uYHx6N_1uCT)~*_6R|Iyz02;dVPpHvL*-8}1Pv0H{a z=LmI2x-L4_>G&==qU*xB>Tv3A=9(igk%ZiIY(6UeZaGF~lFGY|^|7S#o+Hyj;h|&o zCmG)(M`OLJdg_?3x7eOJChPsPmyUM?%~b@cL46aG?@)%gJn5Xnr5YGebp?>p{z_Rhs;oBM35O}QU(V^R0b6%KtK?X zQAHF5MN|Y71e8&J&w4+y}sX9RoSfbE#*V7LLnDg?_*oOqWrUFI;3n>K{6;w|IWlZq$Dk!~#Qz9XG z3v*py^AY}>gz@}^MO-vg0)%nCpllazp_swK!YV+BaQs{_LxsO8VG9#}uo@69{J#|V zMGA8$V~G-0(mIO~E?xyGR=D&rV7D;U1AcKrRBqUsg?{%TB?+&uLd?Cwh15Do7S7^B zIxh^s|GfmaNSIAqY>Dt%E0h<6OJ9RjCbU=)?4poODQu;1IvqrpggZY5R104@0H_fT z8eyvwK3@W6z3_uufUCm!^dd9}--(BGU3iLu-4OaLg7T(N@jm=+37ynGg%WC=_6hr_)Ady7Jrm4l!htq02Zd975oJhNpu;3aga@g`J1YE`-jy-o8~KpD zM76&H;zje`1|>l>sRWQDI#3AZ9?=ifsY@1BQR0#!GSQzZO;k=V!ah+k>PYSZQ3nUg zgQ99T_#G0NC?UuY75oZYrpWOTY)3?Xe?vMZa$5#UmPq;=+;c>WegiXC!G|R zN~Sf~C|cwJ<_*!LMNl@0_&1@vCAv_GNi>VLY{lqzL=P@OY89RJ0kny{HDKNol~F?Q zK(rfo%G`EQ4)udNMD7uwbc()N2=EnuKOIUxaSNqQ0pcycff*>iO5LvQV)=O}gT*sj z03qU6?68H3KcuG=CKh}S2p45f_=jOckHvLAg(yJPhW3arP5P>EgGiBjzFT z+C@+v7Qam`zD#jBb+?a+Bjy3J#FEd!%n^T>1xl{il^)~?@jQGN$2}>&v=bwo5|=KA zbXshij)BgKmkeO?=fvNN;8!4?;0!1f%jpd-5`Rv=wpg4^whQ8UcLAkh!+h8-ic6?k zDi^P!6ueUWStFuc67&A|Zi*`k!K@LxIAMf3@se^#_2S0mfGgq^W-zacb??Caf%tO) zl-=UJuK-5HBUB$f7pK1oX-xd*Jy5(PwLWmzD%n8$;5La_1<6}dm=5rf%!$GXeiGw7 zNdA&3li(L1`9=c>l!%?-5F{}kgl)TIm^xm;lBH@$J0zdsduZ-X$?F0v#V|?FK17L< z+`Ib`V1SXIVN+Khn zyP+gT{XUd?nEhdp_A&8!dA!(Q!;;^=~w}WBBsI&Wij(U)s7|11Q{GIFtcQkN|||1pp-G6^g+7F zd^H`ma%K;mI~9ySIaD$RT8@{P9J;rwVy>S@uxe%_ZKXBLJHLQg%dE}D1nZc^bfncY zJKsW-%gl)>P+nmiDHXlSsHln8!0e$$=QYMkt?WkTMmu6&XMW0nbc6Xl6b?;{A6*FD zWR{GF!!2eV<(bV)I=zZ5OzkdEZZnJiL6kep2^y%CaiLS;E^~y=>NdtX3o-98-~KOq zWZr)l(gWtFm%wai+N!{O$Sk9zr-PYkMzBuis~ZT`#k`RLc*LxkfhdoeX|&sRGw#{& z>tUwQJNSgr(l*=6*yw%eW8837|E~r0;!+0;GSS2X_pMdG!Z@k-Q#ic6lXjDLiu&O- zS>g%K`=OKqd~hci)+tR4{`ZH6CPP0hhHke<&cnWdmvpQk@I>HG4g1Z)27ba0V^_$2Ma2AOums@=60`yDH z&<6kAL`oc&sy5)C%dfo!=86(4E43}(IXDtw|@V~&6r)`@??k7X&S*4V|umz#Kt zSl7Z~OJH>*LP}))^ctchvCh-ckjz?7du9sDa~vqCtj`_-(pW!K0uHd0C5Up6l|bic zI;+MD?uS^apWu+m>YWGa2y6SBh;o#*#0?I|SgATVShuS2kSx;6%I>Y+Exq!2*n^X@Lu)Z0Et&sH~4#Cc|p7vm%B33nZAuh0LHiJ^i z>OBNo8LMI!lowfZ@&T2s0qVe9Vl`6!RK=R`68x%J?exRySPLkpu4gTM69Zjl$;vUM zE3E0XrCwu&T!pfcRY5EFIxFTcC~vUtMnQRtT{wwJ+-KP+r+&z~*#lb#%fky&Cu_<=P`X(4Igq+pq15;6VZBHd?h{r4 z?cKes*M9=#DeI>(*q*W8K81k>SizAPc96xVi}VqeY6mEzEKxOn(Q}sib@+|3*3w$s z=77(xu&+DZ9Rv6{h%SNY>#%+?m;nxZDG3O4xUK~RIm~zmBW!n2b1_1QL%#8korr2Ty{M;xKs*V4p)W{l5JUg)hM2fWsE*tRHlk)DK&RL*^~O zVTUj1kCW-JauiXHICu*HSq}R?fJ3%JAAMSp<8U(>lw1ck6)q1n7}#1I7^;G9I}}m@a>qeNCr6vZo+gZN&*9ivINWzw(1>6U z9C94t(BbgquW;{l5Vk_u<&gd_q(=_@l#li}tfVH*69*9=Qm@13?tngr%hZm0<}fx6 zp$8n|zlAjDP*Z@=Lk__rxQ{wqrkKwia{h#J%;D8VV0y9FQ48Li{U&W-K5T6Q#`9(W zOkjR8v8yRLv)#%`rQV+C79UprN@pAqE}`}No0SH=F~Ge9ky z-3n$MTSEJ1Jv&nlxXjkk$<@GqksAEh*yF#zbQ{?xjzPN4UQ6Alo9u__fLrWY8^CO4 zo2X6F!sgC|dn@}kCB=8y5n=cZZS3zJK)S~kJ%X*B-B}0aL-zJgICQY52SMs&k5B^e zm|fWk=w`n|jfx(&J9QJDu)n4>u%BJS13YEFKuOax_IWC>2H4|V;4sW~rz3NOeV)?w zQTC-ZaDUETM178}oWDMWZ5w9-l`7txHcH@pIHOY#+Ml!d2iO8Q8tS+Pa*iH^6vRot z3)>FPS9Gd{aE|YQ6v`2O2+B^5=Pp15$73ICksR}3j1a}KwPM(4j^KTSj_1s!dN6_0 zOCJv;a`w`_coHY>ARvuXP7R!WoaNNA+0S`QnfC$CaY~O5aoAz7WpG|N2IgT-1tkZW zoH=`;JjPjmAH!yGssk`=Hs{h^NI9HW=K)S|-dG0ZNzO?*q&!aCMo6bP|2G2ZEaxLy zS?4(W1d#GM^G$#P&d<~iDB>8R0L7g9bo!QX-l2Q43!JE#fQy_1YvEVU`NIp83eHRP zrdD#i`vKJ)6?KhkIG)i6R?9if0i}+!jy`a?!bzkw<|?Pgg$6<7}jY=LY98 zt*j;v(*(**&dvAWev6aa1GvqJOoHtWr;w)G%K4}Uleo+Aj7RAE9G3~8Jm9oRVQc5S zy#dUJ9L*F^x;SAPNRK!_TOmE>Xy|Uan^Sxr(98K|01kbek7goDKPUVohJDI;GZD%` z&RS|-4RMZF!+n^OM@8@m=O1bwk8v(2p!DMQQ*Uo8S9%4bZ{uv0seSZ=2cu#1~Tk9;@xGe68Tf!j>KJ(2tNM{r2u&T|3m;eJRNU1Fx1o!kQxS!+>wnIA2oklt!+C^0VvQtUXZP;Ob^WYT}yymnd;Nte~`T zs~5s{o4fk~M!3Um2?RXg&Y)eYo%;a~BRu4eM!~Ox`*9)Q5jVO9w#VFnhxiTM+-0aL9byh~&qu&E%#A$)X@q-@(&tgGpcvrA6Iu{# zD{tl?9JcXZ-GpG?yjJRf`tg3th0UM0cmZMt@Gj=U7RY1$2W2qtJxXnN@OW<{N(iqj z70OUvUKt>q_fIi`MevT$hf|Tf@wBN%@iergVtG@Fpxni)Uk=#Ko0pM&$owtyV-9x+uY{bmq zxl-5vFmETdOpo&ZN(SW^&*whevv_Yg!7rPaa16HNyq*bw6TE0@Qk>*Dr+}Hq>--VS zGrXB6U^~mR?S=9jZxS6E`Mh_8fb+Z*%9@LK<^Et6^NQ%GE8*>-)V7SbfS&wCo^3v2 zmh+y{Ra^xxU@@dBo+E8R)x2GFxYh7XCivCz9BI41%=_p65c3L;@daGvS*idHyu-9T zUFUgGjc|h(M=xv>uVp0$y2<esm2R`{@z|Fk-RB)P z03PuA=um9umD7IR$@?JeW5O2Oel*2q-4y5P2U4wuzp8p< zXca{BHS{@441X(~qOtsNdQ*4t18)K1`7f=9ErI_^I-(@S{5n% zwfTTlesV8y#E$M}+u0a^SGe3FJ6tHtjkSRQ{iAAYC!>*!oL z%@2JAaE8C(ML<6PC)&CS_!~0eSIFP67}9yZrwgEnul)sZfxm>-U@8BnMnoy&S7$(Z zkzd*bWjTKi9d0%JKh{91<@?3s*VgernhIq-{~J$8m-$QSd~e|UGMLLXzJ?O4M*jBa zh;p6puE)>3!7uWH@)n<^1T^#0sc>lFtEslQ&A&sJVR!f&cK{yngXwy`ou9D|?hpAd zP*vB#&q@HZhrgIglPCN`tHJE$&!X49k3Xv$${~KB1LiW!{{nqK?g)R7ijh(N1zO2o zg01u|)mFj&&p_EGcVCsc*L=aDP@lk;v{Z)<$dZ?$8C2+7H zX13sNJCw%-sZ?y85I9ohc2Zz{ACMLB;2AKP#xC%Z76T9UU6^f^Te)3It2( zdZAFTEdf%oV9BR&C=pEUg7Shu8G?aI1)GdemI*G>X;>w=H~?F<;58+Ntr0x<4^S)k zssofd!KrjeR|GRDWxOg-DZp$H+?<3c*94I_VQUol(3a37_<-7pHw7_NwcZk3NB}eo z@+ePk5nQGZOj-pi5&?GwL+`<_O~9oB=bm8BHc0mcXQ&t2B`^*n^drH+i%>om%%b*Q zx8U18gnlaMcpdOeaCZ}w1A+)TA_oQDkKi{V82bf&qk{0$7~#2KXg`=^0&^`WzQU=u zA^8a#DnRiUcD)QrfbiKlj1VGROmBavusau&ox+o}+`@zhA_0-Y7!#OL!daB$M+>K) z1H=eZsWBHTv~GozD3sCfOA=;=0QLxv*`VAjJjnti3%w@8{ebZErGSINxnE+Q>B1`N z5g!r`QGYH|xNHgFh>%qUhoi!N$~cb+SE?Z82xpf7a)n8>D;^g*(FSos7*!1ANg<1h z>~q4|lpyB|FVWFcB7BK@2^WOV=>;kkuDl8;6aFy`%8SBx>flf*?4mVsNqBJro*^N?_%_AqWS}f=`Cvi z1AacDiS#vquPA>D`~pNS0#E`)OYZ@KL^Hn!bGv9gO({eq7>6jKqPlBf?i2|q;|vpx zQY{)I>M#RhMa!mwxl3fD9>i`@^E*(+iFVW9Hc?bV9-VNr}f95O}1XuuJXJr7Zi zioX5}l;fiInSc|br$)d@(bOSO@SX~giMrkdREgv# z;9f1d^F82-XtNCiT@|gn0*40CCuRh@h9?2#EzwPSw#e(iKezedMx@Q50q}vAGAw7 z6?IZi?wQD%1!+JO_Z^snq8Dj{7!noApd1#RGXO?J1yn6<6F;C+&0Dta;Z;rvN1I5?=03}F#=50{6ixajALD^6{L?YOv|KB_t)UR46Wlj6fv%jAitQyzj_ zzHT@ai!GEVmx$-65#@q-PZIn}#cS9&`^&`T`(djPZ!|zzDgK8lw@czzXuVX4g>Haq z@l4uJM`z=WU`87*?rXtuKNt+j- zRnkKZ{kxL)C>d&#T+jjTNtV%G*Di^dL3$`z_#TGskQDer*(rJV2$Wrt25S0tOTJA8 z^hlQ72IYz5t~;b&$>|>eeUcUQay^smH)DhW$#4{;LCLH$U=B(CQb0K@iKV8~bIA=# z1ji&!2~c`TBkB9=t_mjTb0?A+6M{jC?bp1C_21?J2LK!UG z^eP;7NZ+LnL5MV$a-vXa7q!@SO4C2X2ocg*AA=Gp9seF6N}553RJ6470i+mdE^WrU zrSX&(#7VEvm#Xp7-={)Kkg7r?{7R)xMZTHh;u{e4J%Qb7=g?U!ay%0D1Yp?7*vs{RF`horxEK{+hl zJd6=Wq|+#q8G+Nn%qYimbi)ztIBy?< z#W;SH4qL3_cnOqoj9CeJ32j!UKRV{2; zj)k=do$Yw(8$ga@{5UYrIeJ#XmhbrWFqkEdm*zux!EqX;ua%C1>tJ4TToj66^^OhW z!MyDFaF zfaK-W^Dkhl(<%vIn^W%^K!DTw3b+S4@z(-^oC>GGVY|~Slf8Xy zPKDHtPH`G|3cpmRDFuLZr-o!O4>^_pje(9it#JWlIjQM#@1)Z~I(GA%ST%rrr=KWM zDsZZ!TdxaF;d=n3PRl6mz2vmxDEz9Ny5=F~Wv4{Sd#*U8QD%I@=`HFfH#rqPhjhou zhxYPTr%BWsZFl;H3ZsWkOK7+0cB+02emzcK#v#}~lWQ4>;(&fbN#kojW4naM*dm3``=^Id=-A9OsWX2%YPEj@IW%=MNhJ zdCs@qhr=1?qc<_$S?3r!CJLPE-QZW~>>CQdQs?QERhBvbavBa5&YS2~q0;&2IY6~@ z^+rIA^KzYXD-@xv}VPwaqn-PuUJt{cvu)4qGl`QNROnw@{~g6)p8;u)B&&eLeI z-*cW%@7R6k&Iy1X=UJJsJ#j8B#dy8WU(jE=&$);ma=-Jm$xseBk5Za8=p1(yFy#Ej zWJDQue)1=z5$8oT!7=B-5J+CKb1L|4mCdCRYMX2<6W}cirhU*)_8R3q{<6R5U%?8H zDXDWAC_6SE6AY3)y^JV3WM9@p86s=pLm4W&CxQD;Sq>kzFqv)zY>_gi6A&fqq_i+v z7L)^sk5HkV=ugM4L{k zZ1f4nOOtKmLb*>io$9fJvSl*>>9VQ^n8YEOJrq)g>_jHuuq?)ns!1Q7ktp|8B^Zo|Yhj}hQ6n|#*L?{CozmcS`i1{@Nlw#(0 z>YS7??R3|AfkBmv6~auR#JZeOQ46Yq`Q95)$qZ3hc8Ss3V5?yQ-Uie%_4Ja|G25O% zs%H*Sx9~FaNj8+%nfd(_?=dq? zPp7FabWf>(B zu`Y9e2JCXl$p`Fq>GZ=-+2i8W57_IH_Y|R%UDz)`N^#-8fMHWzR?sa*noB$-bo*R3 zyo1pDUE0=ya=^u3hA0PJ4x|FoU5-+PdDP|b>#!YjxpEa!mdkRwRnNwM5ED^yTyz^D zopPB*N91XjJvETdxI~chtjjwWp)7Ja&VpaD%hY9vQsPqeCWgJ>GW-RUl`gw3KzYd} z<_M%Jm#tlZYL^w%`>k;)UW6!DUH(}NhX$8hu~6Q0Swc;xTP|D55&EtRm(qhamk&0B z+2P`70Cc+SqdUbemoLwP`N*Xy7k)i1Gsh76iOc*mkOo|?QF=b;(%S>&kW0#az_80a z8t=JFZ$2nvE>SeBuY5--lz#G{T*UO3_tOq){@`b$;1{OYAVaWl#p_Z?u?jcJtad4utpn^<6y5;DDcDpI?NvN! zLFi zcUd~roASOp=HjcINjC?6%Hy=4{guj(U<*)MzlLqQa`YgS!OFLSK-r^ujERJzd5j8Xnw0ryyC-EAm$DJRjI->qCkYd1mZZ2%-HFAsp3 zr2NwaX^-;1GQ><#j;FUVRk@vd&1uS5`m5|yPE{ewL8ahRNa@OQYDFAU)=>_Yp>(1= zIZIhXIc>HwGak$w<+U70xynN7>>XFC=#Xf^`hwX~8>pwVLRZc2F%m$@?1t`~)KR02Z zMrCF#m`%!GXd}C+td)a#OZgqmx>@bULJ3l_}IsyQ}5pFbXWdR`AZON9m=lhuyrb#g&40(IWQB-M@sxBII+zrt(2lZQJ!dnU#~LaV^I2( z=V{gTE7#DI7*O)E;XbI`xDd)ArHxj>uyS)GU_`l=&do7pIbA+`sha6z-l~c!fNh&9 zmrhD=m3THr2vp6c$|gu<*#?L0s^4f5!77Fh=pCwu--5DJRX`V_VX7C}zzkQ-B* zDqe%FQ5EcpDP31Z(u>}rn)nf<+p1xzkM5{aX}f4u#k>l*r}~n*sP|RBSHt!|)w~l@ zyXpeHshz4f8TfUn-gyh~NcEut@L1LNBbZNAKhdGst6E4&cb{qsZSei7qNRvApqg+P z%0bnl0Vs!59(ORMVU?H~8qZZ%wgSdfljniyrM^`S+g5e^PjK*2UwH>gU-cz=N&M8g zAHv;VJ>?9PLF#rRl-t#lXt4*Y>&C;jLp>OSU^~^Td4Mo=)g3Uy)n#YEj8L;%K#5lO z%mT!yqiCM7>VLlj>{7qj0A{@U;EM>BpmzQSlSovTQRgH{y^gZjWcACm7*f=`Cc!;b z{UhBJrm2+@z#;XzD;O_BU6%qltp1T|yi9d7t%4)!Z(qa+S?bjkI$Le!LdsDup?*)U z`Vg)1+?2KAR@B3MGAr({w>c0j-DO5||fOKB%6$q(F zU0Z?)7OTIofmy2lh3-Df)Jq>jc~SlG7oe1@{iC6*PhKAm z)T$FHaj8>(xC(w()b7t=yQ*%d4-y*GLDUbqrha+_Giy}y*8!T;9n^ohsUAmb_m(;| z1C(aX5d+MzszG~miFB=%|fao_i66a;kIAX zK=17VjfGP1gPLEd?$6NJs6%jA^Gz6(nVL!Ia5$n_{vTo<)g0OmDO)3$3ucZcje1tO zn*a2e=W$KKM%Yehs#~BurAeUmd0O+a1aL-kosPP*nn6l}&uK#35Ufzcr3(7I#`QZW zi!_(1JzT6=mIzyk<|o?c%QUBGpTDSiD;$(^jlmhAD>QAi1}imJ=`U7|rwge@lS!XE z)M`E*0n};IZ^5BnGxjE8Hfqk2!*$KoJUHCYlm;VolV*WCpjoqHHK0ZF(sEF4YyS3! z`yI`flI^7}N({Mzv-PhDmr{;lX1r?zknssy^-l>VpgL{|eUpg%wX+mhD?$KPM z*3J`+_fMeoYTjNCWuNBO6eyo*_M8R`XuchXV1t@238Wzn-w0_`GjR^!xhC!~#v9XY zq8-Icn@fkHw>G>PHXrS#dr!zsn?Y}XiguwI4yoEu>Pzj{PNdxXfHvz5jCW8QlmjVUI}{E$ ztgWQHE>pX!1g+EuMk)@k>ZV1#;Y5e;-%Td^6G25qbka80}L zdqio}PGrEmuD!MwBiz)Uq&obT)`Rk%X6-7<`C7E7KCowL*U@d#UG1`UaA?zRdLM94 zTUZB&cI~TAV0)ay2OZ+D-vx z_EcND0e;W4&W=zHXjju&HLMlR0gPzpJ0bL_*3Ay-x%T&?h_Y4JzZTLq-TDo%dF#Yq zB8rdBjY9kDz6${a==x|%fw~=i2pyzzc?RVU-B;;=5M5*lqJ-)?0wL|xY3PAO=qf4m zh}11u2V0b`iH?G3-FMWbiq{o7L7AZYntD%(x~sooo=Ljv?_tOo1<6)piB&?#oZ{h)3t9oOl)i{~-IA>H<7IArQV{{`iUPI3pzqq~qlukisz-iq$YGj?!EvJU?S>45tK`GFk=>rt%cF=#h z^So~D0=O6H6pIk-g3dII(51T0HIT}5Qc3|X>fC|=mAVf@p}eH?q2#1WcYZzms&##7 z2v(p;K_Ur+Z@qr29H~C>$Q@-uW5s9l8sPAa&}_rNO;R zcf}P_x6UaawjSM59^i@YwZGurt9x7p>8Z|lGbZs&cj72uKo>_Z$)L_70n&(Wh8WVQ zZrNf4d#+1ciSfpC7wDsdZTbTVuzBlW8waJ2K8WrneD%SUHU;RvSq5dGzMrMSTj^s%%QL-n+kX8BCHV*R z2M%M{gL=UwK!$#e3z&!XpHh0BsW($Xa76!vPJ}G|PD-1y^=2vG zPo4o=o<4gCq*MC7EpRxk4}TeOPXEhV*z)x+--N9|Kf@J%h5F`C0LA($Iz>zLt=+I) z(5KMFL#h788c5~(i2aZ%^x3qpddqMt$M(QWr%`;5+p5kAvB%_nr=Amwr_RqCC=<)1la-UoVB<6MgkJoG==VL>yZj60n7-r%fS2JTT3OzPf2o1u zV~Cv{C+^pV8hq_kU|ZYsSem_aG)mN2p`1&%Uq#8bc70h(Q;lD7KLk0yE02ziqsL(uYh@|a4)3Ew&D32K?*Fu?P zXrs43+Ym~-X^!Da07l3)Z0m<~(y(VLYWW85{}?L3GuA-oT#%N|9mk7;F~|r>I+4YS>2?YGsBm>Azrb(QripWw{~e1eBKy zc~lTr8Pp#kO0{8jB5XB=7~1P<4Vx)VyKKl?1ov@z?V`_c_%$e> z8NUAo0}UAdI}O{Qq4XobkYO?HM8k%ZG{9EZH(!Brn`<@|0N$?Wq)_^}mO8=D*A@Tb z8@4CcAF1&f==u&F6G5&&yas8zYsd&}C~GPA4t1S43Tdb7+y(Fpb4~X_uy9u`ZB`Mk zZHbVgUCX|J6yrL>04dh>TUxZcTyyD=+U~(#715&DMCuR3(u4m6fxzBa@6~x@{x+@v(2V8%+2IV1F-;Xef4A=3rgB^A)r9?2( zbyG8>Bd*1N!j|RQM}=RuD|b2|$JI*xiCotgDaknQx}*upJlCLYkWRUNNH<2OU4Nvl z>x^r&E8Nez@@#+t*C|y{7P<~xfOOvVEBa!h$aRMlQ0)3V9PXvA^D|*9b6wU3xaj&D z^`XmMd%nbY6|Uh;P*%DAK)umw*Bg`ISL5n_0+d?UB}&-pTqnGNSzmK?*al^z>sQ+W z&8}A|A8c_=)B^6g_Nx)>zUwzsymq;cG=lQTwVWP7pX){H3-`NnLMnd|Hy zAPu>mX@SGAtK=>yBd%+yL>zVfM+@*W&YOc_w;D}U-)=L0tOCW`_y-*Ye#WvU*!+zP zHzP`bG3yYNfyN0YNWsR(bmHwWPM|Fz#CU`bsZgVkIw#@A6gs3Lj3)}=5NUL$jU~z$ zu?geF8o%EL*kw%k7US(UP6~rVoU!#=1WPpH8DlXR_tDbWWArEkbFcB&2q;sHx1s=P z#vp3Y?lbmN(`mo)b?RWH8`rvHghNKfTd-vqk5jMYurZa^;8Ek_Yk*_M;(9=qaZ)~P z*~Zzln;tjDQ%~oF@ii|*IcXfE7cbA)aRjzA#^30DIBR@qE#RDS24$7`#!+fApEm~o z3@9=Nu;EZ_oKG1Re2s!MKX98m=3k5%Y#|+D=Tj$(T!>y_?4W=$&pc zF0uk{8%yY}?v8N{y$G$wk7j{#&)7lx%Y9>A9macL6#tGW?Z!!Cuyq=JW&pa32kF-2 zk@2^sfX7DN9=Ja-{-*-;8j~16pD{%Q_kQD@>7Wc4f13&zH1cWH4H-Y9e0SJrdlmD1 zZv5_lS&(sSEd0Do_v-;$O$#=H;$!OH2Jkg4)WgruWVs9QH)&r11esiD*Vu02Oh=Sp z(-}LIJ4}DLVuCwO3(H^&GaaVgG~D#zMEs%%)8nmRMw^nlz>G14PQ*a5ruLh#?J^D0 z1Bo|%O4q6hrkt-JC7O;!B3P11R}CrI)Gmi#ifIlVim9fR^wmI`sh6&l514jRVtvpw zMF{tFlei7GLnasM_hg#dzlQCIX)B#FM@_$M2OKjUZ-DKzY0dw7NTz!)K{{*lnvH?Z znHH^tL!qfp3FdjzwCjK((dji%>RP&JuuZ-v86Qv@Gy%k=qI zU^bgFKY;rkQ}F>PTTM^B0C!DW%b{#D*^}Y-z%+xl`*zdERj@rYomvQ~!<3o=+auFY z^j1AKO`?pf+vHvZWsm8aMO(sd z^R6!;#hE+AuqB#{Xn#pEk2piQ$E-VoV0+EW>CR@q`L|xgJYZ%VVLNEvOzSM&yo3wN zA@f`Dpk$g$C^NU)ce_ljaX7 z@5wX2OKHU!vj_DY&zfB$;c(7;p86B{=K2OW6qxPQz9}*bX)zR=NB3baCFb>1j9f66 z(t<8E&!Gge+&t+%pu(Iw1(Ztj=LaEOG9RQ~LX~+&I2>xtw}t?9=0AG@_2v!p;C|U$ z_BJS2%qKG8cg;L21JG!`N%_Qe^9m}*`YMy!?%xC6yA1DXRJ=9tqHn;x{+lcwnS|~@&<5Cgj zxp|EK0AuE|uMupU|@rrnlLrh*w~nfoe~@fKzf z$^?rO?HY-e@26tey_QYXqDi)_`2v&_%Smeer&{vqT}iW)lmQM{%#~0cw0uaNg>*|N zWnG6Xejh^0uq^FE%p(>yH6M;zyw<^X%<}MkNLiLa6+&lQPSA#X-17H5jCaEFwGot) zmK^$)G0(DL3EWRvR?>cU*7EW!P|jI`sb`#T38imb3M_hGNQIUqw4jSEP4rw!EK@cD zE?6E;1(aGsXfG(U#NB~ZVVM&TW~C*Qg_xHtkIw?CEZ1q*sJ1lk1+&f)NgermOWxnG zUACM(2jvyZ%+DcRwTy9KYqWIif%3W~|0Tc;OC@z7YEctfby{hh-KOIFBq_sZM=t zS&9E6K5n<=!yHIGmgrC6@Wk?P4DS7wxsMU-sU_45hi8@>^e==ASl*~XltIfc&*3m; zX{JZ#<@WOXki6aACZrxxzo-4f62339Ua$^ zZokn^6y+wSKWenwMk-Wd-1tcdw%hFsdS&9=vT2_2ZkuUqPH@{o|9!v|w}$uNp6X^v z0VUmS;Ac1-a_ghaBg3t42b721o=$@l*inZbZ%z3y`2jw+imvn>K4`lWtm$|6oOrJTOS2mxm!PVD=OUb>CmWj!(R%2TervG z1M1zT&4#Vf?NiD|ue<&G1nw5kjk^-1DiTjdvH&6-<(QST%y} zaqp%ReV_YX>Qe1@-#`a@y89T_YKPpXuZAtty~zpE5%+*?fGqd-CqbF*{t4ZT=08t9vFVd@a-u=NvOrp&F%Q+bKqPrWd`AYZ4x8ZQfJ(Ir1 zu5k~X4`!|V(il)0+~ppCYwmrUz`WtU;8if2-2b`?WwZMP`VB4aqq`CFw!69;lsoQY z&mpzCk84A)d+z^G(stkdzmXSC%q&i?*DfPwlVh_>Lhx3@E*YC?GbnjQ}Xd> zTn&n^$Gcge_<5v#4N9PgBoxXZkD0RokscJOkC_edm% z6COSBu$}aHkq(A)9yPVF<$DCZ1!ajxF*WTkc=S-yu+(Fl8c^mj`W2K_9^E5=YLBnt z5wpf4Y6Bc=4V0})F$Uy64 z%JPD&uGitd!+IkPKPAL!eGlV>T9e-aWv8`@R$YWuN;gfB)=xJ9qO5z?z!q&?MHAd* zjic4G+v?#CDbA`q5BGR$(o+nZWQ}C-t9_PY#*!l;(E1A}LT1dyNrIZ?FS-Gij$hJn@ghP&X>Savn zg!Oa?q?6W(DErM&f;RY8A%8`io12TGH55@pmkt+$Q?Zdre&?!j$q2;BqS zvHnNbC9T#RM=0-FV`%HTZ}of#+XE}-SJ>LE?v(RAv|6c)+GQPl6(c;d7Sp1AZ1tuq z$!_b!FG1nz&3#;j#; z0K9A;6d>kSThnWxY_rApKY`ZCI-eVj23y@-)HxVPG+E%^}W}2;ne&#;gJbIo7ZK;&xr`tLUFtbCp zPpLA^ux+8|nPq!^0+4Nc@g2m>v90e1qJZmeSixCQJ{*;gx+WL-Q*z>lGA3-Uyy~>BO*!C{Xy3`h9g0jr^(mE(F+CHGI zvfTF89!M27|4NKkWgDV=K08Q3~)(R-lmvMr`R$0OUy{{!^cKBWfA6Wf8G zVe7S>rfjs&#^WGLzs>X)+y`vq>3kTpRqFvmwlAsoG;F(<58H^XnR1jd+so8W@v_I& z0k+yd>4b8d{qQmj>uqI3@OE)m;+|2-Fp(0Y4&T>$l7N=HXpWw_O%^= zbo=|X{T{N{1_3hcxhBA2`#(JR9kn-XLg-_5C7n50_O=%R+4hZ9fE>Go(xwx3*#S75 zv{z4pGSB{_9Db+lKluVq+r{JIe$GB49JYLWX*iSx_El6L724l?7s~T?<1d&}iG8gc zaKYY6tFQVe_3Ue*icE&F~t8*kee z{Q>EYz4U$fwc0<-gz~OkauCcmd(uV3d|*FEO{aFduQzND?Q&W@9rokPpzO5Q_JR4> zp3?$bw_QVvsK=g2mzPiMzEmys+F$+*%BS}2zXG1w4U}08*rk+Q4%&4M@EfvwjUeWz z{m&;*KDYl!HPD#7w+mD9@*MKVquc6v?lvSJ&$Uu8eLW`zK=Sk4LiL}&=Q%6<0z4Dw z5p4II=Lrb*{Gt@0cX%%H0yD(Zk9LMo&smv}qC6K+aUShC>k-^zJm1iP8SDA#c_?>z ze)2US-t+EyFcUnT!vKk%@n2(vB+rr;!QA8d(KRSjJUhojnd)ijfilf=T|7qE=Q;E{ zV87?XF+@rC{P74Vhdfs#Lz&^(dIQSCo_snVGCeoYj&jV?MrU}I=i(`_WqYQ$!y(7B zhYq4#&-a5d&`HmiEPy;uIy|8Rw%2JjsbQV)Av85POG%*^J zSZGp}q6kP45d@^E6p^A7QE3W@0xCr*(m|!E@SVwD=XYk#%zeB2-o10@KGb9lZ3ik% z_(s5GljJEVub7O}fyGr5zd0Da#^fM9;k728K8LtE6AP;Ou9>W&iSxS2+1-Ff6aFoP z-!SP-$5l6(IP}1H)8uX_lHD@NeF|f%$s%7EZ<|DV!g$9dv=JlRHHo7RWSfcIZm>?1 zvA3bTXYxM1k8OiMYcI*+xJF z&`Cu(F3ShOyu^KrG|yAQ+D^4!(wUU`ooyWc&id{ zg7Fm{NE#SI+Nw7)%I-mOgV8~~peDu{s@<9y=35|XVGJz=Yh@h%80Z_UoWHTIE)V%?Ij5B zV;tND=x3~+!mvY(^;94aGp;>AzzCy~x+9~E-|1HwW9-X;agyQl28>gTdK<(|GY-=d z@`TYzQ_u{fuMZbE%h-J#%00{{RA=pF{z!))UQ9~^jNZ(W#Q-1X-{$c7GM7~V{FzOy zFa|Jxibm={X6ykZ3u1av9deLqOQ!=N%#&{-btrQYl@4LdQB%YnW?IqQ8o}%pKzW4u z&AZ4F$y`1R<5A|$U>IYVGOEO5nJ2e^#W6KUFhT+ohcvjsn17aoB{G9k0VkMcj)*(S z{Dt-bDa;$xT1{mhprtj98AL@!2GfRK3e9A$e+QB*<~-WGW;1`g0LfYAwp{pfn8~#J z%w@LFx|zo;q~fc9xlRpsp7~`TB!$e|f8tt-m@j^gxC_h==q|s=bp9Mr%G^mMKpB%k zy^>4Jwe&tpC3B__0hgI8Xzjnkj2c7EYG&{=_-dGsECIDlb6WK4nB`PD)H8z*BA|ge zQ;K05nS%{rH<%k}iM+-9>UBUfbGshNT9|BFpj(;2bRKt?>G2DawK30q17ADy5(~-> z=F(P#-)Bnb0=t-3VxjD2PA0(D!yKcJ*~eUx4_`mCh_(s?%$Zq82AM%r7mP4}w1IJy zdHQ!q#+ZMQ@gWoc7bVtB=7L7}CYXz94WDGb`45a!%%F5!#8YP3X(*pDzdDX&Gt6Jx z;hSY%r*`c=Ru|1Zo~&+KLcCaMCvYv^tjDhb_On`e@cFUQXf^a_#oPl6V4ZJ8&R|v- zO-~0{Khg?xkTpum5Z1?@P=>S8X%lmZbxZ_ynAJ)vLImqeYS%`wq6F|ov;O@U0Y_Q9 zD)?epnIka9vtHE$joPr}ms#QcfGaHdIvB6ABC3$H zhV=o}A+@Y$KJeACj@-xRyT-cQgYX7cAe6o`bKMb&uLUEv%v; zjMvJF>xSeu%bFgGHkLot$?Yr*j+=;ejVg~$*1o@B>|%MO1G-t+zR1$Ul2EU?mu3Gm zvh=gYWPkzI0A1i9E10U^A=bNeoue%ARrtnOcRT-_OlpahP6EwFw0W40QRuw(q!ey-gOwMz1VNAK^AZJ(LjI?doew? ze(dL82KckTpp%LK_9FUi1KA!_3>{!c($3}}dr35g4Pj@zgPfu4{W%!#5Zi?bc9@-F z3tt4gp0@u-*e0}iN3-u2!FQDX`ZOSh{pubhi)G)ZiR>8rH@ZU-*oT+kl8&=qdjSE7 z?5$MxCbNsG;5*5Fjt59#>uHKjWv5YTo6cUp5-fv#rW!1h{mm>SS#0?#WI4kQ>xS+6;2H`i^mXq+^Vw)5|+01@*2aGN3tu#;EVV|Kx-Mj2KYS6Z^ z&r(;coju%yf$p(`^N{5}yW<+5i~Z6+NZrj|O&9-w{lo>($A0?<82j1JQwwo`z4Be; z9A+EoT1MFCskj_vpQbj`7<-W=*kiWmVX$%b+O2>I_Noz#H_5L27BIu!NVo7TyMgw2 zdpPUM!1i)B(;Tvo(@uAfH)q>Ri1XoWjmLPtoQHN$?&ma8b??UsuYxgjC&$H91v)8`6E;GCmv%W+QCCIlpMrm4ktf>T5f!by(le6SSG zr!?E7a?&4yrE$8GFv2O0DH{QqoI)xfvp5S^f@O1lTm$84&OMq3&u|zU5s=F{O!ZP8 zXKEfGpYypr!q0J5kHJ^K*>@HJMVw$->WVoPC4dsnbz0ajaGc)&T;vo|F;vcJq^?5+ zXYM+LU*Z%O!B@#?HG}ao=QUc#syL-|zf^Mq`@m{ASLoB$a$d5-uyq{%e#F&tKD9zz z11Iih7#lgouY=v-^wK)k#Chev8;sM@0AmYha4C|ta&FRNahqfE1mSl$uRTCqJLl^S z20gY2?s05r!MxA;#|hBG86AP7m(xSN-v^u&+86h67SL-A{T#PY#0_z7 ziji}e^FJybMmSSTU>xNbzkzX#vz{7mk2wpb0OK4<3&JNjcj#Pul9Rm$Y>IPjIh0R1 z|IkwRj1xh<^cl|oR>C*S(S#sw4>#vm7(KZu^ijOHbqpB2xsBmqK3tn~7|)m6KohP% z_jnqN0o*NJV1e9j+9m~Ym(vTC!Q7=YU?E(C1Q5#guZJ>>`xDKr;oNmUVuVB7R7=Di z;ohl+FOus!AHFDVFikl=7xMwwM z&jh>1WnF~iI`^IyP|tnM9li$ce0mY3k^3=qiEna0Zh`L>_iyUkG;^QQ{-K5YPCo`} z<^Dz$ZU^`Ic~Ew814wy~JL`hf_qk#nSQnQ~$2z^-9}h$Ffcsq|0{Xb!rGS3!nxBzu zfcyR;1PpUS#4wI<1H?!@$}MXH8{<~cef^MILC@_tmstrm!8Pke&PlEV)!tLwvu_}L zntQkd#%J92F@(=>O=!B9<*I)`z#iTfsyO%Z*wn7|;tkRXl{b%V0r27F(j@N7GtzUbXDt-T0kDDMs}Az{3wR0W0e(y0hO#Ct6i z%EP>cEJz}G-_WuY#Y?6KJ(~ABb(@azJgM-E;Ymsn5YJok7T_4qgmz~MJhuV(j`K$D zV4y_aue9k+<{hLLf==@0(OUy4yl`p|rSd|C0cpI_06+#Wj%MLZUJNatS-eZsi_PY( zdJ8#E^DfFE$>D|FM6z6-_B0@mcZjYmpQoi0m2=|OE6vy zZ~qM9YI&1a5pbPnvIqh7yvrM4Y~Wow1Zd>dya(kC-f%bKZt>a=LDJ0AzYl2Pt)m@S zEAKZk!f*46{(z*77x*??W4#35%1O&z++xt6)s|& zr+))5!SkVh)+EoI_8m`n|9*#@PkDQ3)_BGnsDWgLw_J_5S>9jo!?=$>KMWF2ehIZx zy!cHt&3N;r)VA~C`_hiVkN=1Y5P$wu3UUVUU9Nxy^0TS87Q|nL((1*7{2lbb7{~vd z&Ku+T`}Kfhe7_zT6Zjme08jAed<2%nUr(KrWIm?|aFU<@A|QqT5499d@z2=-()q)q zfDHZ>9a3lVU#0VvEPlpeD9`XsT9EoIpUX$i9R6F>rpV=2Qx%)X=e&u40)FY=2tUs^ z-HfIKMoov+kFS-vY$-@(o-_9`NrO z0Db&c%F@s0(t*VgUq|2IFu#VLxDo#L3B--^GdE(`NBoWS#?oUxgXW2G{wQtdCiu4W zkWBM`rhdp1{))x8+^76Y^n%Va{?Ix2_6QcziRNCxpETj_6U){P z!9)ad?iV-}K;kFxrhQzXV0R=WL4y3ZU_Wz!)azqt0`hKjTWq%gKLQuY*HdioZwSx#m5VJ+c3g0LFE8^iGue_ z;5#9>Pv>z-g3)5cB@0rFkfaKxoWRlqy_*nsO3+46T)N;FY7}G%M()9vEm%pv=4rtc zt(#{A@6jtAxq|n9hcQo(q=YhGaQQq^pA%S8Jzppor%Ngl#85Y;SfHkMUx`5S5WZ5u z_g}zQCioy1tXyy<79&&$EJpyB1+fk=UJ-0N2FX>y+o=ez67Xocs1+2`6J93}#3I=> z!HSn4xh}AwwpOE{*&Dtaf=(&{ngr83z-|iS>HcgHyqX5qD(Ir8|F+=sdx*Oui2fJK zc0m|D%pHO#x+gjXVmj}?C)oE1B;A4(TK{?krD0I^3f`mhu?K=1_3#Y{R?_1;D7fwi zHYC_ZrRK1Jy&aMzW9+~VKP3$W5E~np4FtlfvTV>L4FE+(}F}Q7M=)p zG(s{Xh**obS%Cw6+C9R2`b2w$8T5=%ljLE|y+gb8zDOcx%Z87xcq-Wgm=wlFpm;irXwY9K$-BPHz6q(R!IRBLcbowT@u<;Np(fIVHqS>h0optR0&OJy{#6$phT8>VKfyd z4MHC(4;qEnXdb*Fw2KBb34>{S+${WL9$1UehHBwf;hg}azAgNz7+LNJSB%2gE?j#a z;T^(7`7m}0&9{Nw6E^k3cVD=9907g8K)RNG;ZZ8`2ZVN2Fb)bupWdxAmU`PygmJV-e=2mQZQwpr zTbf%vP2clCfS0LeD~#Tz-)b;yfay#yjDe<4Rfr2R{b(G+2AiHr030ywUW>RxrcddY zK5QBv1{Ps@SPOQ<^t%aUi8k$~_dJf8-qC=?nC8*JU#w{>tt5%2`!*xX3DXC!A|T0h zNdO?()Qsx#lcquMATGo7Pa}+(rUH5%vP?PtVA-bMEQ0T}>E^cq`KI6ffeSomI>~^f zz!ZfC)*RCVxyVv%YD%?6iRm$#SSm~zP8jx*>Cz^!N>dL7!Y`Z7&}v_0>a7A)n{t0f zK)tCu{YDL@Z)=dO*|d+o-4@eCGr)aQr%&MPGEJf;Y`5t}dJuX{Dy zPSk+u7woe5(xahMcK$7TzA3j5}=%O>&Ns)Uw zSc*tNyMj}q#D5W&E?UdM=oz9iAB>PGYQ6#AY0;k(FrE?p?FV*Nbf^x#9Ff8TkS`j( z3^*t1j)$*6B>fIq&WlnPAa${51s6~vTC)-17exLFC@+e5Yapov-1}K=gj8DKvG~fpSFSL%`)tev)Jr>3w$MJ zVdG#I%-a5i^bIBnJ#0pk<1)p;;JHQT-q;rqm&(NyUvenveVFYzzbxA7G} zcnPpyyk-zaKk+}WA|OzFkDk;Z@s1!Q3l>|_BpxDGy#r;axV;Obhlv9PU@_vKVgRw? zpl6W8iM>t(;>G)t5qDhtq6w6V;+=FHbV8g=i&T<$wF6S8h_j*qsp13Wkfez>J_ei; zCyW9z#rxMIOP08cj$pFI7idvDEq;Xx)*SH~YF*`uUyeg~o_H?3xRftmN6+7Rarzqw zC=~CY23V1}o*u4Zal;tkqBw%)vr@4MRS;$3Bw85C#WG*8O7ZJ~V3)gk1$0z< zRs6Lzj5XrlsXbLIUhx{BPJE4SscYgy+M_p!wP`Rminjz|gd5^@^aC`BRsa3`;$m85 zTErpLCTtaddjq527I#{~*d|WR1+X zJ1aR%|1?RCWNZcEawQW1FrJf~dJHI#ETlHmc}Z9W0tzL+@sXuOViO7D1oX&B<}=3(k^*KD_4iaK(BOk zN~-^Y@}8uJx(@dxp0q&sNH$W>zgJTG60YTeq~rq_`y{LhK)+=9IFb!XDyZu|EMd@w zV?+`{`_xfM9G%sTNg}8%@>s(82*z>Aue5wlNRl=o=cJ^K8cS1>tW6mGsU$oTl4p{8 zKf*X8IZuB=Fe@=@1KT6LLhWczX|x5#^ODZb0P~h|jv|?l^eejSe5F3mLFq5GqOC%J zw0IDg6evx~f-gvFF9i#hzDyr1M7qTtaiP)|sRt1z&1(dNOFQYv=a96KIub{u4>}=< zl!k2wL`hR=V-ziYmFBaf($+={6em^8g)v^59}jj+YNVY;f^-RO|Bp*cUWYPC+DzT$ zWa;ORpgbuxKMR&3ZAybARhqR5snex*|A8+hOQq9R z2rrX{{?`wYo|J-BNT1QOc}eQA9&uNsv-C||l@`?@yh^&I0mf?StF%tnNONu@*)?ex z^$V^`%jo5Zda05+Q4LbzHAotzHFUUlQ(8`KgIm%Ku3*hl?d!=iI}NIlH89JqKmC^fX;WkCbJAvR8WI7T5!+$#t*+ zsdq3WgHn4spB|D%bb$>^RS6h&L^}8uj1Q$RdALBqjZ7O z(t7&rPoxSv7@tawT~I!gzWXYY%}Ciiz+TxO|3I=&cC#AHQ?@z?%uD9Yg3?)u$mjD0`2lt`OP5tAJ2h$Qw|G$zERw z;P8R7;(deL#g^nrw?HjHhH*-a_hh*%fnyXUOIiLXs)l z@G;=D>|FySXJnpl!gp3CYDVfDSyDQTxw2wvXP%SU(i~YJ`or5$=YeBQ7vnF7X#JEKF$QI zm0hFa^194WgAwXwE>way$j+4@u2H6S0^E=l_riEf7C^0?W?7{*phdQ5Ey7!6sgsc0 zmfiXXtWEZAD}3#;O?2wjA^YViK0~Lhl}f*RGWh_YTNXioanvL0qlQMW?BY?3{y=8` zHH>{SAG)sxWuMV9KP3D4CFC5IMTo&hWSS>Xj>@|GA$cUz1|$5j?3@GGxGXOa;S;jj zL3q)upoJB8Q2-U^79S4a?d zOg>KQW`cZu1maG}pZtlON%H*_kR;1jQWNi_yw@DQ6nXqMC{M|sdj?6mdE)Ys&z`0!nqPYohch3}3$m-;q$Ark5eIZ@) zdS9?^`Ir{0M}C%e488IYs%`q^Tv|{DUz$MQbB@(LKWZBJaI^I_znUND^AiTCPJ~AI;BSxO}+3%Dvr@6?Wp2^ zRItV<3LJ3}v5H~|#*0%lhGK+xMc|uICMgoAbWc{qQ?Ddl@!m1SWhgFF{drcgly2x8 zMZs-At|Fumae0dEuOgs8v4L*P^NNB(utG&v6`)9=Tn@Pij`bIm*QXg;kp&p=a8jGu}Kfsr?9UF>sRzXg=9c+Lx=D|h1v_o5yf-V zkQ!CI`#qFn3eFUUeW-Yo-XR!QI4PiZ?%?pJOKMKV9-3$(cTD~YO1tsPv+(bBJ=13Z-!6X*a+jWyC9x99Evch=2&Cd?&_>Qcf#z5z)#Hx-pL` z@BIQvjPd|YCGkq{8Yqt`Kbiwyg0gN3a9r8;EsROZaOxK%EB~Q|;iR&T>W~y=$47`e zrCiw#NLMatM(PY@un>|=rS(=APb>f34CNW+A`#eGT>1sFQKeZD(*sgNx9<=jFrk7 zx-qXRccsEurPPK%QmtIH9wXE!`|J^ZO=-9e-*si11p?}ov5No=%F&~MCS^cBd^eTW zG+EtJ%INNCR=!M+=xya(T6XU!*U-0nSE;iFv?g!`P?%&=;xum3y6F98xl9p&eG9q)$7dG>DLFRQV76R1-?)i%?D~ zZ&G!*PqnNJ;Hhf+2-oSQdSwZe-l_Zz&+knD(ivJ;$4^_Kd5%);-*?Y+OSoH?gFXO5*6_gXI zN-Bn?Ro>Lxe4?69Ta%}%=4F6qs+ai4GNT%#2f@pH!XMymu6q+kA9I&ljOS|}MO)JS z=HnI!2r##9Lb5>fB3hY)%-_y~FW7w5LKqL2^Qes$Y92cTUzmATCXC_cJ1EN`^WXY$ zEr-p!O(2OhAKeLKl=)IKNTSV|YXC>hvlEdl#{4zv@Wh)(&O&+2e6|(71oJF5;JEp+ zw;@S1uX8}MWb?^)p*(5+KKAi1rkJ}`0aDFv=*LMj&-ffH18*>6piFZ+DZ;bNr>Gg1 zZT`apu+!$NXhz95e@J`SbLJnVW4r?M53T^to3j_dchNj~F<7ZNXCHEwnSZz)tlWHQ zAlOy&6?B4HWxg&8?7I0j`c>-9zo&wy!MvZQ`$qHUzrjV^Fz>$x)@0sK>)%cDj~ijU zWxkHCy4n1V>oB&MucmEvyZKZ4r8~@b(R0*k9u)}VJ@XTKux|6H=fQf+SC@kIn%CX~ zJTN~jDlQ|24a0jAC8BtW^xqJ`d$-fOX3 zj_`dJb|HZM7Rd_{;Ae4hKE57*i;Yxq9<*@U4+ybH-wR`e#Q`}aM=ai+xE3`Pc5~+(UmR7=d!Qyp# zm@it~JOE6xsXr+6p+QLBVSdB&b9zdli7Nxfl z*JjZ`rF*+Y6#dfoEKbm4c;DiEnkTv}0uLde+oI1BtjA(oJL38*)+~Ue-y+TqFktaM zb-@QM-lGlNki{1t14b?ClaXx9BJX8kRIwT9T?017P-16mVun0>* zE|frv%H;kHC&wUN36h z8fu@`TE@|~tj_We>VICdJo6Lcu3LUHiDdPbUmDlZ9r1cuy|D`5ZN=FKrH|Eonqqye zZpT96Z#9S3@Bpibi!g>*{T2=uYSr>Sl7(3%x*#CjYA@Z%QC7dxS{!W^>;-nrN=hqG zf|aZX0Vl1t(|KHq)!5HqXRPjqfSt8k`4b?=>Qo;f*Q(6{Bb>9^nt<>EtK0v8owqu- z3%)|DAZpl@SS2=tU9hsYLY9kG+y92L)ar-{loeJj^n_osDt-)QrPX%YX1npQNjCZKM)F<8q^Hb01L%6^CkNXG@P;a$GK%lzqI9QOnn@Ycf>JKvz5Tc%r zgfUd@Hvtx=uG|d>SHJNDk_h#pQm`ZHUn}uZBGs?oM(QZFffk!+^#W@2#HyDr1&dRg z(Qg~C4%vc$V`@P&j0x(MHV8PO9twmnNxicK#$>f%HH;_KTwfSd)aPj{drEyt4qv*u zY7#6%{pT#QWU5{12hUQM{DskT)pm5B<*6BTXXUFOSOdyXNB5wD~y-ap>jZ_dRIQeFRLAwfK{oTmqS^t z*3mJ1jr!h7K&^UG3}2mksvo|3wcCI7p!$`UkgQSd!NNc{)Dm|E)V6C; z^B*C+Red)P#@lL-qkubVNiwdwUA>s*$PV>C1yFXX(}ZC6)W02q@4h;QK3|Wzi=LHU z_1vA1JW#)$j?w$nb#z$TufFaMHl)6`9XW^9L;ux->aKG5M%7-l{TNe6(81AT^%^bM zxH@qoloRSW+PF`u_tKg(rQT$NVV|l`3?tbybp*XyGovmJ0?ewf{sh=#-TXd$KGxoJ z0OV^u_fN3>*6V0m^Ru?FLe2x$!7HIWXuV)Q20CnAB7ri(dijU&#aM5Rfic!PiFV0} z*0prcpRgWU3}c%0jX{il%DR}Uu5|0}No2{e{woxzv#nqI6q3`{J5!M?-#U*AcFy_> zD!L1-2Xc_*ytT&-ZShB}zoYHysP#u|#En@8#lrW{`V%T+#;v0g5inuBWF35y);}jf zIb|L6-*jR9zYFj^vo`5Qz#f}#I^o-E^C#`6y=*4EaV_39-nYO4Y(j$(9%y6u6OvFH z+a&nHY(AiQKGJ40%}Y@>yG}w9V`HS{CD!KO=Mi_z=4u3#2{t>aNvRRr6$hLWop5n7MFVc6NW3zt^Sia3!dWz54;2%B0Z)Ee^Joqly zoQZ_-l1&Q_l1iJ5x$s@K*)f0%ykawEK(cC^w`e<1W5e%;vexEDDm?3K{O3VgZ}T42 zyA3veWso%5)YA6qh7C6pzFRhr=uU37xlG?ui_J1RlxVd%Wq~YrZR(QXYqR-kCs?~p z>Mwu}8wIsw@7rWvg|ExzfCB=$ZFFVH~x|q6&V@rqL6|hc;F8Cc>0WUJ9;y+Q#cs_@3CjM8(Ndo1|C4W^Gnbjk(9x zjqdrqwinLBx6iiM8$M54e>!#cvaO|89{g=Z)PxPNeL(k1h;8`^qz<(`b`_E%wk_1x zh_qEveH~|8^*5C9w$JZ?G09e^fF#-WJKC|IwEfH%>i}fgdeQ@wYkQr(o;=%aRMQsOUU&o7QfzD6fr~GTREvAfUnaYw8m<*(OIL*-hK;>fmd!y|xs_R@)H-zIfNx zEgsNjyYL#I)3)>=B=>AX+z{SvYf7bNk8L3B9{1+?p`w_VRlP$;5%&B!o>&?c0WD`IAW*#9mYtzl^YRo)Gp_F z1jN|QrL`m0PFDhBoZYt^C{Nfq?}stTj&~3&*=}_u;G|t06(=cn!f{;EDZ6@W7}M<> z{lPNq5;Ne-v|F|VkYg8-3uCTbwH}aX$8d%*-!720|D|>}RwAy}_^;_k*?D6&^=ihn?p;fKIz|YTEbOP0_>kz|NFPj)!*g&k^v*Zqa41$9Ao> z8jjmF4nZ)RUi8;YQ2Xm{BIAW*+$<)q{i+eghy$oxwqcNWa z)M_yC;V#g~J#b0aG^5mbY0zw^CPJgeiq@4InsHA+ljbIENt-o~uYk2^ZkPgEHTKjs zx~)m1&)25;otE-;O@Sv^hvp)cF`b&@zad$dX50hHZjGGw{XLrDZBX`V(&%IMYc|uq zctG>A5dnjmIO>WGX`bYPJ<@d2Ci}5wKnfVwWKjV!q2bXiJgG^KgYk(bw;J$Nvy8s1 zXBvwkNMam+q~b}tF`qkkdbxcz(dh$h-U{TOh<-iP)pC+)wb z!;=(yH>w0u?SG)@BF+BTWf)J{XZm6EO#6lO^k>=ExC657ub4q{+Ftw<*ctl>JFr~) zM|Z*U>)a-F>$bpdYJi;lzCWWVlh7;oF# z?S=7<{mmSV&|^Q3YT;h{N$Oxdu+ODZrO$rI18mUVgN`qT>?^40G;A;O0gTx1PlNHH zeaALrd1TL|#qF`ZmIoNO|79;EQ}${)F`BktuYvK2eJPa(PwnIBVV<=QJ^|kzZP8+6 z*{e+-fpMSq6>~6eZ7FRPe6(3J2=LW@ss`Jy-F6+m0IeYrEKr+jgfB>IHip!}+6h~* z5bbkw0HInw9e0IkTmJwH*G_aJb%Zuq3FQ&(+Gs$eHg^n?C~f8@_+qr1=!c8dx={-_ zP8&;eNW6A4B|EPDgsS^QZ2@)GPiXz4;Y-r~!9qZaw%-(xs+Cj0lcu%$2XIR3^e!Zs zS|959XK5RlfNX69JtU{KmfrB?Xs>R?K)Kq_|7*2qr!Ir#Yrp3s{Jgg8M+6jVGpKQ0 zr2V=Nms_melmOpF?a0^2QmTDGul1E_pN$|{xz3^x4_t>{mm87tledWfm*be zHsO+5wJWy6cw2kH9>zB9%n9Ud*RJFsu0zYE!mLw!hk71eT4@lB-CCC)5Z9w^r6y3X zwu{<6L)v*hFb-?y(ak=h<^BTSs5X?=;xX;V7m@R^Hj?g%ac!U+Frih`?=Y!7Rt7eu z^<99tr`mOIAlWnRVl%KA?ROmHoYlU&1d=_v1zQo|se9vP_`GySsc7@oh11IAqw6~l z@YQ9|R^4BB<$VMM=oV3V7pUV<9TKF|FpxS}xAZhvh|YBa%23@QDl5Wt^XU_X>o&9i z4(UATaXO;g>o99}XcrPRC)w7_ajU#6ZV%@2`h3K{toi zfaALD3lN^9)0o4UtUJ*S$w}Q&Dt}UR-+qO-RGo%)6Y07v4_ris?g8zX!Q=AV;V96f9Tw7R||dx~kv7@^v4-1$JJybQ@Tq&igZj7wHCR1uE8k z@G+o7_vAVTD%Dle&tIl1Jq#$N2b8)b*|J#YtXHsv)x8rdsIX;Fd1oGf0|sJN1AT z-6LBlTXmy!OWoBK&qY9+?pL}|+I6*2U>!Qi6yiE{N_u6wOLvfN;cnfVv@Pq=t>1<$ zy}Hj{2YaB~O&33)Q&N>Ts7t2#dPrwSo5^8a6?M}_bjx=G9_kYRMcgA@$!?7BSoh^g z7{_&OH3*;3)ocb#>;CG5?}_e1`i-9IuF($cnJ(BLFr&+vhHtNa?g}XP>2u!!czpU4ubcUcT&nBFEDEL`tH zTiHYU(l3$auwL*NjFI}+>3fONC(>>yTA!jp&ZGKoFF_KcAEbXuI9|V!){bNP*#7|% z^gLQ~j_WJFfh19{wg*er|3!VplX^Sq38d&dsT51q-=jTcn*M?fE-6F5n4Zl{{V%`6 zm!)4!6=$}7mjiO1*0+6&VRQ8O=aF!`=}%Ifm8ZA;0ls`aGXTnS`cG)-Db&AAqZjG- zy$fTpUO^8~i9R_I0T=W$R$yiNM(Ti+>wC>%tkAzgErm<^vcG<7}D*XqFf^q)Nk$)NrRx^0H^F%OYtSl>slSd8eMY4V>_4XZi~fxQH2jQw7+pKFJI@_ZgaL zQ{ic7pM}xO5W5x%Mq-|$ruat0VCuOTkb;6a<1AcGIJeGVG* zwBZji=#GMg8Upem2{XL<4ItdGh!&d&!_rld95MV81WBafMGjI&8DbN_q75&nKpATo zxdLUJq4NNI@rH5Q;~g`6Jqt)MY?=?rNyCP9kfaz+(OsTuDE|aG(+qL+z-AkQ=y^D8 zXrfCxV+f&N^Q>VLRV4+6bXpD18&+Nd6dHo*wkbjj7paR4*|c{mF}z)kfD4AXGWaeU zexY)!)bQs6urh;PGbH7PL!ZM~VMzWE#wx>2YG+m(jvqnJ8pE137^v1ze+s@j!?O3m zt{HMeFv4|%V+uoC5}1 z8?Zsc0_qG688+Mk3>#KHfN{*ArN2&iXs}iR9vR+GgzvFIt47?UL2@0EDZ_r68Kw=d z*1`A0FiPLjQ^WUE7d$hV6Pq!(8UV9~fU^kL;}EbLzP%3aRJeFK{BZ}&+o6b_KOcu! z>W%t3JXi$S?{GO4;OEdpo9sY`vRsT1dG7;dh$yZaMhA3fAn9+lhhhIk?*b?mLJopzLzU8-=9X!T1WG$03crxR^5dR~_8+M4BLimWo-!CC<)Zx#|fC-2B&45XVj9MtC96agknRa;B58-r62W(V@xC@k&dy+ zU~!H?v@pawhSM65-fnnNXi_a(LSKYF^LN6TF2B5`05<{)*#@T;~LslH#u&20lu4# zpM8b#ZaEg&0Gb`=&?jnfRCz*j$I*HkaMv-FiMTe$SW8IS9k;r`*x|Ti25{eTmVW6j zNAVH(x*Z>f;mUd(TWCYo>o~d$as7_pH$gezc;gBrgN~n2qhQE!Za5^vj=C*iV~$Oq zA*lDK$ zDN^S<1=Eyw&S^E(ZUs(zs029g)bR&cvD24SHJ3Qm(+XSR^v_DbC8zcOBD~URAN}(j zmz`2>BB09Y`;%bRPS@$gsNN~W1gyd7Rc}C}6TTSC$xeeaNY>=E{W#c7r;{&Y*jrAi zA3)OVv?>Rz#ffbJXm^@F58rNw(;C`x_c+bD57z4>5I{NPRB;Pz*eS^w@YsnTjq%2v zO6V@1aB^!!>Pe?xDd&vSiVlo7>*R3_%-h*y5a8py*bf1L&OLMj7vyZrM3yjT<^`~D z=e3@Y9CAKPtLR~8*UHZ09*eV5gnM58x|wUNnHXBIlScq%L;; zbP0?l&Rg=JtZ??Hiu00lst^NJItv(3UUm-6faHpEDs7Xhoz3n5YMl2KLsIMPwhEFu z=bx+*cg^|DKjCX|jz5BAjm}!?7u;|jTn0&#^Y{;t+;pzXgzt`XKm_2f^P%@(Y;!j3 z2edoCMpxb8+(!@IedmEh4BO=_Z3XLg{>l-O9%ozXmGn9n(=Mdn`P4%A2An(Mpd54_ zzYE`x^9Jg|4Lb|EknFK@-x3(dojs3%O*sEXy{<{;pW48toTc>OO*>1j!nns}n!2ca zU3Sg@_PJbFAkNd}lUVq?Tt4~%MqiiAzrgmp^i}};TngcAp4A9J;>M~B9*>soZ{#)@}LT2GR>k>lU*BqB{>R;!&uo#f!x%^cL zUxABy3&uO|VnPqlMVGF4K&eX_y*yXu;^hM+*OW%E>yH zcN~!HhKtuNK$FW4vk18DvaJ~7-Er}v74M$Q9NJ0Uck$SOxIUKCP6akGVFtZDVGXguxXcDO0Xv`snmp>ahaxV{H#lwAAEaUBRXN+>w1iO_g=2S zR8M%jZgB8^$Akn@bIWIKFkUDIem zDR8}Y58>xs3kD#$=(>s;yQQwnpF(-r)#wYj;=0=s0d=l+-$QcE^$Xf?G`Y?Z!+6tG zQ;AEu)%jmYx?H_vVEwMf01PzX+DHrSn5*Mjz(d!~T`*3$#z?`Y zT?13V_PD*!iY$BG>W2ZoZm&%v%YHW=Jr==kKJ{P++ycHr&O>hVscUrDO|=;pany}N zk9~~WB0bn~wX*4y}#X+`K;rYjC^EN6tpK3t3<{+`LXe+2pp6 zeyV1-RkZB3xRsm(w7Lay@fmKr{d5t^Hn-ihDQtIp;WH%baQmwt#!k0F8mP-{=QLQi z+vzDlk6Zj@D0|(^?t}Ha{Y2fE0XM~KFb=vsq0YjP+ne)X9CZ@~VW2U$dGy0QbnB+| z;gOsB+fYup)t&*HbX!ZM`;^= zoktMv>wbgIwfDOh({X~I`$vHQfA?Rhof+W%v>nP|cgY680r!8YB0K2b^bxXzxG%c} z7V2(AxA!6U+7U<&yD!Q{T!g!f-h@5k{uec?BHgp8k8;#~Ast%BxR>leK&-pdmtb-3 zAO8pz@2(32JMP}K5s>Ka?~Z^I?*8+TEXn;oJ=4kV>*(Mi-Ch3+j2Z6n?*KC0{f{C% z%ROfjzHImXD-n0rUHBdXa@?CHz;fMxSHhR)o^uw)e0M#~pXc3!>7FliU-CRyk^8nA zfMWOeZbDMxo?e2SrSAWGgb~WzZK%#FcmMZ$1XQ>u1w(nseZ&mxiu;$;D&?D}Mkud@mY5bV> z7E#7lB}Rxgnot=UYy6Im(Bg~}^x5N$lT^$eGqwdIF2VRS)$=Eehp5m^GIp(jG1+*E zwlpV=yQnHjF&?=HcFOo8eG}=%uc-&0VQi(%a;EWE2_VZT;$yrs##t4zoHZ`K4oQx& zYy}|KSi*%b&v=Gz+9IQw0#Iz+PPJ5t@y-BBf*hS+G9+H(C)l~je82{*m@scs) zAdHp987hJ=8<}+UU1j{v7S~yAoS?=;jq&|0@YNb$qSdg@xRl;Ds5joFUS)%^*Au=* zo7G^vgn{VEVXW(G38l ztzI5FXMnfIIQ=*Q9yh535A^7tL|l-^>;D46JYrffP`F3g4!|LgwN!CNdpt)Qm7^Yg z=Mk6SAx=QtagQ7-$WuIg|2yIEc;F9Tnn(CWC{KB;*bYed*hWYDXFNV{1Uu`IOI!S6 zkJmI{B_85#WVzt6Lk8nT57UzvsNCZ)RWlVH(~A&y$)i00%1V!jpw4IWcTkTiKzQ#o?eS4BOQX&kuO`C;sAKE@GMAf5g<_Q29ZTpNYlVfZZ_akqtt?x7>jumS^iM;i;7!uAm*rvMxveEX~ z79?)6Jx==huWhr)&vC153gO^3TgG+tbGvOI>Bx52emskw?Y3Pa>&_nA&;iKXYx{iz z3VdUm*Bg2JY)6SM9k9KVjNqW{?Y;=Uvps(RkcVu0M*!rot*$X*$839uG5g;3P&Hyd z*tXE=d)zjYaPWjJohV4qLDYSq$(MSgG&DWEeZ@S8CQy0 zhLoHoWEfeJYDLwfO2X@rH@f5+E#sJyBXm}cFKM?2T>fXpm;jP~uUlACu>%lwjdZzA?t$*+$fwxFa-7}{G{@+7fhi%PEcLoLfn zE`NsN%S)D$U2RQCdJBMjRWf}BVw*|==OS-&$pkutca-cT{(5Igza#{^N~VM(*j+M) zrtaI4gm(aWpyZKGNIY6H!HVEm$uZ)(PL@3T9EzVR`GvTyb0wXF(UXg^~!e zR$nYBUy0zil1U^}UM=ah4IqD(d^`fNzeCjkf=Tz9KQk z{s!5r#@mnnjD{xIA3cxCCfX;{P$t>$mLczBdr#uTr`Y{pL@?DphmNdi_EQdk%&>nz zh%(dOm>Atz_8COrXWI`A2FR!Oq1RB^Jo{2QkLKI|eGPe^**~H4eW~65VRYhi`{nLP z{KEeHV~8!YhkOFa<@R4!pxIS+zh$WUOZ(EbD6rZ-cOMeh*h7c~|H{6Gw#<6__W}xR zu#YN4;zoPhZ;-gjzMHH&o9#g*2)5dvBT0XoeKNU;Y`5Roh+v03mDuy0_S0`8wg+QJ z)qCxUCy@A!J)j+8`|NL%sdm4;?Q+z4(BAe-Bz|YVM!fzZ`$-Z558Gd(J$A%CzW}il z_BS3!>_>a?AjD4EkNgM0DSPX0QRivdind%C$ZAgOCR_Tg1MzX5Ym2H`pOCvm{;07 z2*LbPf6^!|DSgC+;!8^(>H?6@OS_Rf(if!%Xrr$v{oyPiSC+m+9Ov57=XxP=UFi*? z1?x)%Vxl&b&T*lZ&834$3ACm3O*&I|mS)dFY*%U6D1huQts?UKZRysHXyHhyi$s&7 zr5BDN_G4)WlE6-u)_w@cpGyajHSk>N=Fv#JT>3pJNq#N;-?J!wy|nQe^z24y66q5D zEG;Ah*I%WX4*=wD>Fv#k^>REt4zWIty5orTbqxOs<@z~F$YRvrF_~05LmaPFqxewA zkk`@rILEmX1mhh!BvVdsd`ZmuM91f=0Qr%l%kwDrvE%z^kvGLLhxB|?9j||Y*eu5? z5`kws{z?4j97l5>1alo{1CjWtW5Fi~K6BLGhhTx@q65J~$K8)m^&&^dFHn53<1adT zK6hLp3G53;$3M{CGDnly2$nl~5f8n>@%ka;ed%~B2c1~$u#xg%jpM;l$ot9>J`jm( z9p9}&Y=a|#r1Fi9w*N$IlOvus$!5oD62P}Oo+pL(Hplpp2(~-M-$33DM^Rhk?R1P@ zi`Xv5uuF*TceK2STE2B$AYST#V{dni=%8a4>2SYuj5&eW5l8%B)N<7E$8E%pIj&ts z?0ZM*lL&rr#9l|aA06M`LEcHn(EX_Ml;fHmd8Zu_#B%)Pm`VNo*%8|cdFLFpq*Oca z$Rg|b1;@tuh+TBForL0-9bs1i@~cCjnZM$gK~lkQju)>Yxaz1SVtn24Kqw$@IDXGU zaMSVnLKMH{NFf3B562~n^(qV9hrIX8dJ_ffT{ih8bg)m^4dQhAm;Jg2eHlk-jXFn_^@>1jblKbI5gSu> za33n0RQ4J1`jgAX(!uvpSp<>1kIPJ_Q0I)YdE*hxEDOm;WwXj||AM^vWjj*Q!e?c( zoG7=T%t9Q*=Vf`z0Qp5(Fzw=HWpy8;!0NJ7#A~c6o4*`Ye^u6Y5g<2}-3ddmxy($$ z@{Y3J{SfRdyYeD}ePwSpN8bLj=h`FoZCRItsPjPC)O~30VA)B+^6$!4jY8Fj$`-i* za=6UZ0|kzhJwO_)qh(W$qrGEgOE#n2_hs!#EA~TKn;i&_mu-0+#ZQ!tB~<*eY))Gg zKUtRb5@M&yj<*NM=`s^>u|Ji4vJiD%D9b13@rz|EVi3Djwq`Rxeklw45aq6vjUzhv zTUjpgyI0FPt_S4rWrv!e6MvNj3`XAFvJYDTvX^s^6M64Da}Ob}uQMtRv3^dG9GCh# zBbT7g0nSVLC@|C+P>lk^oSly$ak%rNT>$yexpg&K80Cy~B5}0y1mWNqr_Un9#yZb7 zLomU)`2rFrIzL&5;**^JIF4Yl^Ty-Io8nv+fZ|h~-w|Io&AIyrH1vtH-y2Ar>D*fZ z$XU+&MXNyf}Z@aVnZ3H`IC%Xxcy*oVy|r{NnT_ zY4Ec1XWI6^I_JHG4qkEI9*Wp+&i7wKaLswKHxjQq&z(g>H=NU|QQ)RCX%(uzD@lYou z4iI};5DXGm#i00L@zt3a$`G+T-5?n%7Uv>1Ogun`;t0`q9ma-+p7 zlL0wK+(&%fcrkK1ViUwR_W@+0c!HEUlf(>?Oec#aCjs(_xH=aNO&1-cN0}j3E<)l= z5$qr^OWZdcvAN=PBKMz)hYzE&dEz><;?Ead@hJD1*na`aEfNn51IS`=>{CcwBIa#E z;!@H5847$ZZrzOn%f*ihk+(v8j!xK>V#*ODt`hy)BluD*pd;%m(f$%*YsJnFBeqUF z@BnIAFD`f%!3Oa@q70kGz}W!VBCbkDWnYU;mjiOED9{eyCbpl5V5ev$hG&;JE(>|P z#p?Zl+#^2P4t?1xMw^kiU;O14#J&~hCIRw*_yqB^2gOCCp88HKrjzT4_#%z{sCX;} z?Hv^E_5V+2>l=seW&yZF!uT2b|f<;w|a$Ch6uJ<+)GK_3BfeEDy*923evCaHCD z`3n?$R6esEicc>uBz9{?`G0qy^E1m2)uO{TD7U!$ zjibn0Qa&UEv8CmWNSCmzyy?4$EiZQ$Ay`u$`!f3ZRe7&h0J*mO!#sejD-TIQEgQ@C zY(n0q^0FwzwwF(Jqri^xgM*Q{v)n`n=C1Odr1sfe{y3>3_LRrxAaQSb{SCyvDKBh^ zI`@@FFGb@1^08JF|F(Q9`P3XJ-zXyQV0n{IQQ3FpciJI#sQk+j06AQ4CgJ=@dF=hj zJ6hg_bP31GLta4Q_vLTX33RGFlvKH=%THWE-ud!y@>RJ|zLM-mSIXD@jKtr{uY82y zW_fXQ|z}7+3R$kvP^Bv>U-x*Bd02PjfZyjap{AnvxJR$F-lNf(0&t zwAl+?z84Yu!u8TCsB@XCTM#<2+|`bR`W3FZG|Vqu(WLxa?RxTgw6MW-AL;ovx`Ji` zWRolR5^CA(`jQ0O9j>#dQGBPXn3%6!u800W@!hURwjns=8vQ5|54+}mi~>hoGh-1u z>KeNRv16|PHbHRG`{BDNu*}_wj>MqSgu+81RKWf?T-t{%A-sQe? z1dzMkt@;CUkGn-vfb4b8>4{qQyEnao;9GYTv2X|6K{P!F-KS?FIP6|UMCXXR=?RoO z>V9k?${lle+>XSP?%9C|PPrePfW*`8XKE4q$sIxG=FjdOgiGh#e}o`*-hFf$@-Dc0 z|AOF(yF)Aze{(PU3m{kB9nd8oKUDwByAC8IE%+M0Uzk9qikB*hKTF=R7r)ackpd&5 z*A}F_a+_#NPX15$o&RE6jufd=IqPeM}8tdQN;7fB(0SSpPmk=oLiz3m0Ao)Igz}93h4XGlc};?smz;t`u8+$9=2Pv{fs+VU3#rtbb;2{WavOz@Nm=r>U_XV*whPIG?R$g}@}t-%#OI)v1HuW?!yFdg zZjHQSLRXTnP6*p80CGy$xe?{g3Aaht`HOI484|Aub$I~!UD!<9>W1(K9T#_mPpD^i zg&x%1KBj&C$m?(VbsF*pnxfVqHpKM)lL&^J{L272%5?uu1Y=F1A0U`uI!RjC$)?Zz zQS}tl9P)seZu-6(iqA4Nbt7-CX~Pl3=9`YuS+~&Cyge#gYWkJ-=rYrRW+<@IIY``Ss@jEMiz%!W!8TJco%%aXZxa8r$5c+pvfuPQ@nQ!}SNstiHsv(| zK(NC~(Gf!#tPVYp># zvjwp`rV5f}@0wn3hN}Dc-uMS%{e73y={C^U`W!%p_?|h2yy3pSv>iwJen&Vv*7xdE zv@pT8Ggo9y>AvFTI%Qpg8=y5HhP0GZ|Y({!2_Kl2#W zGT-m9_mH^IulYH|min#y3BfWy*L$dCrQh~7$Xo4qdo5a6>-WRw2sZkqk~VIO-=d`e z+2;4JFVVtIzeXnz+vE4hZwU7L&FBP>gMLM1IzQ~^CiFh$w~UOf$Nk2UKIN2OOB2eS z@!LtP2d05Kc+oHZ03a{>Jv|%6uli**0pxYRF%P5MEkD1F2=4erlVb6%Ut`)ieauh% z3yJ;BtIi`f(ENTolpA9HxiQKOH@`dvu~Fu(M6<@43%|!uCYak)qlL-lEMn!Rm`(E# zn{K{M2tCU@n#@;o&GCf0^UYIy0J6~hlnup~n!A(cWtlm0A=+Ds&uJlXwR!M#B(62P ziBfOGD``mFVy@_ia@))YViD|wkr)N`m}iYgY`^(GLjiKoJZm`;51X%kg7%J?Ptt-P zH%})9^pttfIs|9nUyb0Lxr!-Z%+~8@;WD0?0OVEkwhPF+Za#58f?MYQ5zleQ{G@>3 zuDM+RK>GOaFG7L-{*GOU4fKEXWwbuTzlR0EaR1Fj$VT}es6&CV{_lNHqaJ09oz7ax{Xq{`dU^$c_FRNv*QQ|B3%1*yjK58OYn|pGUgRJ^pj2qL%&s-?T>Y zgZ?$dd>{6YC0*_@|C)}79rvFaj&i5`+l@uy8UK(RfLJ-{YuO|Gw6rEpv0!GpC)<59V7YGIhj3Ci_NI(lRvJVe9 zLW1h3faPS(7#k4tC-No)^je9$$pNRyBsL|WLnacZ2Uyl4m=*98k(s#xaRU*XAMg-K zO$!6c$!BG0fY=HxEDLxj6y;V1bS7H9I$#R%jB5h|52DVE0f9lt+Y&Hm3?R1!oOlo* zI|Eky4v;+oBL^aHf581jKMw}H{WkIr2OQ5pL&pMsd=CYV2mEmsu~Pv;UqGG-_=z}| za{-|NfV>!RAE^i~2W%LI;#UKL?nCT)KyQD^G&DA_oOt31fnS9pHaT$cYBV$@aQ^>LZhGL#Qb5iM zobm?}=LR-;8NvL(OMz&1VPMlfh%F7gIUMDd1^&?t1y%;0CAHt`z-bjoTpQ>!93UG5 zqjsW&ErBQL7~B@vwH<<;fg)*-_5}8L7s39(hZ`X{7@d>VNd1Dg`-csX#q39+kzgGo(wJ+K9B-&=u?{)WUmfhT`M zfxCe-T?qOFJ>3f+{e!w+M?VJ!op1qiNYFE+039CGhrAU=1G#KuesN}q?=^q{_ECY=@3b`3!02DM5=E%Spm(?Pp1sDxCPOM@ON2jsG#y#FF` zWl+0|2v!HZLo(^wpxD3AvyDMeWnhP?5Oo+N@asnPM@ z5&O8&q(2dx)~Npt#AY;_8;RKLMvo_;z^9G6I}lscsCqnNOB>B1zJ6Jw4Mg%*Hp(V; zWL=|V(r9dKbes0r)<)Ojkhi0eh1jj#jRNUR{if0OJ%}A_^w)e8INa!^5r`dY^yEae zcf3);Vg#ofy-b>dvyCo=qrfkXLWsM#(x~_bD*L_B?hg>$Xp}?(=O2xhcSZ1LqpH6E z(ku8UB659#|5b|O9|UKR*T~@Dm^+9K3;rw~T8uBWlD557Aa!KUEMlSuqJ*ph~-w+9!KhsUnq)pY*t4gSE6*#2M( zNudXWgWLc)9DMgtlsgvOq%mU0gC9N#kdwi$Q@NjlA0zSleDLlT$h#DLm-y>ngFF0+ z%B}{lCwqV&kviMqL2%en=?cM8A-K650MBm(9TU=?q`UDU86>?<3i%(|(58gk*A&4gA@4qoW@m;}&qaYbAt#A@njaGKJz@(( zK5B_zNyrXTl6?{KRXXxkhWw)gT3;QqmbU2Hkm9qbdPB%pg!)@T((H(B3)xT7`OXl5 zNZy{1F8dMe4-x6GIT#XOh2U^V?{5HkETnrbDmxJ}meet)LNeb(2hW7GA#6Pt@;n`N zmqJ#MGVj+A-;Dsd8d6WzjO!se!w~x;B)=6P{|t$|jXHaU{_H@|C$uOQi339SMFC_` z=u{ft&``f>#6ApNPomrC(1AYya$M-aA5huE(7vysp^rjeJ&wF-q2Cd4oDo_y7p-Mhvt!W_RG+eD}eke^i&aI>qD=vL$E2d zoJiN!(4kKt*b%zx9f0f(ZT=2o--M1Lecyr5s^KLhsTRJsEoY z6~ulDJ>`So=g_BG1LQ*JGKyUe4ai3=zlA=&5{cJB`;r)OGxYZzD0e6HJc%uLL&wv; z?Hv|M8sUCn3uYo17&iYM-=7t?|puqevUplTAg*E#Kc}v3{AdUC3u%R(XTp2cd03g?d zHK$>&3;TuC;2XpCzKUkIgju_xh3#S2Xru26OC*ik-mqj+TI~UNrp37w_VS}BekH62c_aQFmemoEH^P1* zI{!!5u{^~73VU%T>U=-Em50hncW_S~tGMIwNmhcqplA7KCSJAa6Yyf6j9sE83KQ0Lf)pN=9nF=BiQTK_2GJ95;S8gc6ZBuPm_}#uqKKm;_zrT z5W)6{x&(miju=UM^P7mh@1oqd5sSK^z;_XQ$TD^`;yYqMeu$Vtrh^|N=9ZzM(-F;% zBKC7c-Ch*95OH`0f?pzPNr!kPq95sfu0>pU6(BbwvX&!uJ0jeLa(_iQXoIP&rhNzIr1hc-ljyJI*U3# zi5&Vhf|-%o(a4({dH<6L=0`3%g<2Lyt|OLeN#wIH0AyL@Izo+=kx#5b;_ApMGa%PS zwz-Vh#>ij-*%H~I6Nb4h@@eA4cSbh#0qd|gvf@SbY=7jO`vG|{vdek|ha+9FNc=uB zXe8=95t()su~U)vJ%!kr$VQt`+4;!kV-UL(*_w1-zeYZK1?8?r+TTFzMr6~oi2V^c z`xqMfGqM})xL%FFCX;60#w}2v%^f=N}qlx~fZ6)Tx^21}OC%xZ%q-Vr$M@r_Cq#MajJ#UhF zaP&8%_7Qr#fnb2JmGyp$ z4Ji)>(lkdmc?fWECkY2zzVjx2w@!Kx>21EagE72(j8-`%MG{*3LzqlV z@B5|?=^z+jvOXj(mxlM) zle7qrI}RbW$wr9$tobj6FjB^>7RtlW-8I6^X#o04n2?5Gt`)2}VpD^|k zhWm@Kbh3|+{|MhtNeme4Yx*6DlYJ9gLL8^HcgQR>BvAxLTKZi|%U>>RY3(&Ist%Kpb8{LMRGiU*gV;!6UI_rl2(w;9&0~d)jSw3ryf6{L1Yvmz zASVjfN$#C2JUR>@9}8`Uq1-|tGYue%gdzDD$6~=o`t2pcYHN|Mpnr>}F-gVQELnwF0 zw4fCd|1`bU1C{mlU3VC@4D#(nN`eo4D~U54;rr_m#K!y1O+elR-yUmFe5&sXTG3g) z{~|tdmG8wDka*Ph5K-V`zI84Hr+nl4qWEdwhnpgH*7wRfKwk0v^)O<;`MyU4^^R{0 zP{%ydFY`949__b_(0HuhxvfYX=l2_($`kz7lYMZy-~HdAg*kp37bE!0?=opu7x`_V z(SGh%-WDLs{C1E{^|0T%Rml6^FR2K@kA8Q^(*2WP?RkKl^P5PDj!S-{2O{r^-?jgs zz%@U6f5h(iZB7EnpMGCGg93l~<%~n#UB9{YNbF^PZ#~M5F`v1P#Ia^yk}l`K8y@8r zm@iF5Y_WNKb3iUNZ+sY#%gz594#<_}XGrE>V;)bjb>4^Q|Z(ZZ-cW z3&D1C+A##X&7*q&9rJJj@0DhdoUoqGepa8o78%^#W;jichGXNkIuG@T*y)+p0aQksr7wI((27}GaT zAsB1wOxm__rdJ~{mhq;aW}ub{rsG+HkN+&wOrV|rQB!NIl~)R4p5}Y$3@5MtO`6gM zdn&F@A}ppK35T9YrxUYj$q{t*slKEdefq*JwEOI5ThVNb>%>mQEQkP1T(39rH{r@A zz_eWZPy9}L=o)Hx;r<UshxNC{SRe66vDs3%#0KkR{-W?p;rq|X7zI=_1%2J zbhImmaMm;`4b;yYE!2#}yh2Dih)%2%G6=;t3q@gQV!JQ^$imh)@=^_YaNp`ZNY1RW zqo*Ctk!<+de@R$cBK(s$juoc2?jp9*G>o{=Ri@367{!;S;`dQ#wW-fa)V#*@?F@i? zWqLdwdD~6%a{#%+6hmgRou+Xl%uh0(?+uXU=HA^9tTNxN0mymt%!g6nrny;9RCdc8 zyBonD<}gxn+&24@Ch3lO1|8jhn(xvn|Cc$9n5SO;*9Z^Z_n$-TL~s8YUm(`Ue`+nt z_4V&H4~hN!_mKg%zkjb6kvG79NKeE*@Ne@KKnD7YM9T*Gf9sFfVE>Ou`#r>eI%$=M z`ezb{Gt7Vc48(@}k0Db2q5oFWAdc|A+X0Xx{U3=1$SD8y#KnyE|M~_J$M}D;8Npcp z5gU*=&cCb!f=T`rj{$PJ|JGWxH_u z6Jq;?@^1inNO&~>EgTk#h+qCeC>e*uO3uU?u3TU3CD1zu~C>D#LS1@+=&YA zn+>04mip=or6^RekJNM4+Fm;oO_|^xWLk(}PZGqqK zbbk`R-xpT2M`VDIOC)Tl@c3>N93#XM$2VSZPC#suFkva``B=z3kJwbl0yTB4RUyMkMaf6i$&mJ4^VEh@-8NVEaL0 z0+{c&tpvmapTC6P4?aa0_)xzyNcq>k0r>szT{hJ6=>4yu{9|e4mk@RODteK_rUM}WZIGb)I;(39sSNM{BE|KB&?_HU*Y#NJEHLW*%71g zxB2v)Qh%E-oP~N@%o~JS5=W6%tfi$ADXoG??UIy#3x8WLq8UqGm5tviL-!;1m6Fp) zX%|+2laaF|FR&~Jr1i)Wq?E4D!(YeIcaTz+ zPDHlj{i&#-)6yAZCi`cReHRhq4U8lrhm=hA`t+zIlzjGbyh?TO3wp-ihugbFXb zOPcnVL^3kuPTPR=eE&K4>pD-C-HH~Z?yT%d;!Njq%I(r}7k<|PTYy^o9>d@M!X*b9 z|3ElSrloBNa$FJ8V3vS(B2&)93|`MP@#KsR5wgGOy<<#!i)zId?;l610W-W zuV}lD6m~y~V3ZKL5|E>X&@@ywMi{sZAY+Ap=OGv;tR&fdys&OIViN@W7l=(1ChtZI zlY|e5Pn;|`yCe3IFyI6#`&d}h3BeTMi;ie-s?hHV#HI;X=)C$w82A|qOc%NiLM_1W ze#o0C#L;BU63)Jc*lgjKT*T%IJ!4S(Q{nx=sCu3-2Zu7--ce7@MA0XEjs$Ge@rwca zLR->&v0Q2(8uOs)3U)G}Gcd`?aK2WHxLNyn$flWTpfcuz)+hL(T z0{#O@!9Srzi|6?Nbsb)Rj{a5Gc1(P(qNZ!Nn(E5x`nu=3RJm(iwN;gM&vmb_>f%%J zyrWxfO{ZE{*QkzFT`KFMqI)K_Z06`x-94(lrlw0>l&iLDRApCpWrq%x9UQHq;$mWx zn!gd(ynD9}uKG#`a+_D9=H`_yca@{6x}r<>4wa6su8Pi8)s_EmDzAI1&gXAONqw*A z?5e7MuA*yQRo!z^&l)J$wWdRNjGgAH=W|^Ygc3QQFZxJic(f@4| zUMIeR;b~UCLv7WYm9=#uMoOa<>uM^*>YBO^K0Xw4)zwwj*0-o2OcFa(c64>`QZLrO z)vdCwMQ0$Kk62gVp{lwb5m1bZn%YXSqH6+TKt!xcePvgw7jd9ymzs*#Ih;}f%Ah=| zMLLXrwUxj}4&v(8O{}b~t*J#6O)F8cN2QBuL7Jv}EUxqfH7UOXe0(}qy6U^vvJRs? zvAVJc7DJ>lSJhObDu9R>LS6T6ylJJw_>mZgH1u5>SypvjJ@p8qV@M=oNu?+sdV2S2 zeS)>BYX=UYPKR76t6y|i)px9_?9zcI6fIF=tXN+|^QH%^c6G(Fs^p-wh}fXD zqRWkLxhm@Ucu)xqzXm`wG*@+975~Xc&VN_bRClcEgx{Ews>;gBjx>0zX9X66dcs%6 zRZAe4Pi4gbBCkG8X1DszwUw?8nk4xr0Oelry4=`*yfV-rzP8kmw2<;zLupoBQ`@1c zLd590vh@K3s2%F_@p&-ek>pa7FQ}U}HAS>$rdkWcd`oVcwS97Gp_pG&7Zn>*c-Rs4iy4RzkV|vD>7iC8^^U281$`jMFP(CX+E>X!uUYj;i zhD=o4%%`w8%PMB&7Fu#svkS!Hf~-P|m{wKia(AihfO?CvoB3GLaB5y|dRB&*SCD2Y zFx2Xi&FU>kPNSYAr?7ugC)1_>cz5(5tWji`KKW@mV!qXqEv9C-BdA;p zCONgHy1KHWUKul+s=T5?niQJK1TnQsrK>h7I+kXnpfHus8G0b*wJ%7|E;dYxHwZRD zYjSd$RG8{c%}mVB%E>CsHcq;Btzwfzw${>m

89ECqaEqO~Z+T9{l|oGe-kT8qgQ z6_s^3+gLXVFgGWQFKs@Dj{%@zeOXgkxirprF}tRR)J#4td!{89f8vcrlJoOL^dPT5 z%oN2apR70y4i(z+3k!^DSx0vhy(;YKfOxh^H2E3(`fxu&C&0`KMS2qW^TWC?%efY_&A=VNb-a(k#YkaaDDPnjRUoRoKdG2E6Z`#MZ21+BK5*4YH(d z#r9o6GDwq^lcb~~^ffy>IW@H?N3`Tyv$FGYfeau;`OV{Fv$NZIn??Ot32)d|%q~c6 zNBE~qF;O#6EKdM7zqDGIr5KS`3oRzZ#KhtsXoQb(>On?hlv;VEUO`yBv}-NxL0Lc- za`KZ4Eb)e-UI17@dACaBB3{hvUJv3dt+lc}h+HV!Lyt|~VOZsbSverO*37(uLaW@d zI8ZN3a)G=!QbXDmRCie`!yp=hVvXrABu1vLu`O90+LAHyE@oLiso)gSD&5^XiD~To zN)my7czWxycw>x0XT&oKO+(|vFGG&|i0zZJXkdU~Gm)K78=-L1weKMFG0MJY zyRPnD12<3?kpC8@<5QB zWE)PSRvr0&3P;0;NM^jpIV3SyO18&LgXf^-jgC#CnPy$k@TA82Jo8zF%9+L5;pdvM zsCO7v-2421@1~9=Qop>TCQf=nE^Tr7$lzI=J@CrM#xb_bcx~Iq@B#UJ#2~mf{a|^lGy;`7)q&hHP%R# zby;k}h$eso77M8}*4=ACE}sms zC@opcO35ljJMnR>or0oV`J7OGvB_pDt-~f|suB}LBCA?7kf#b+GK(sTMG`;CF(9S| zs%*!^HOAJ_;3RQPE6%BF7$1vm%~X3hG?)yfIdvsAoneGm+F#%yT1lspwFs;b&Q1!F z-8FAkN|jqOq!UCeEXc~pumD$LC5a`Ltrw0WiH8xhv1Xui2CC?V`er7RkPfjG8CMV$N^BM- zBBdUhmdbh}xD7!05Msn7f@sE&xP$+gke07-_K7@|ID5j}bW8%|u&msu=;j=kr4)Rz zSBz>N73(2J#&Rr$R3dR+64518NX?96bgY?=a%N~(Dy6cEA`G*IpwlNgC$9({kplS; zmrn5ppx9(_E}ltg><^oNhVhJUbNM?-%fG5S8Z2ctv=p9vmu@i9vCI!SIwm0>N;^*y zBzYsCVXX03M!i{|m91X@v6{)MI_aHpwOszl&dbZUrsd^Y*f}llJPDuqMq-K?n%p?H zXB*Z<#A3t9N*fO4ZGXqNG5|^2H5N^@R=2CViPc0#KNSrKr||J;R9ZF(C1@{u0b;AB zA5lv~PpG8C_%S^UsR?wGtE*aO5k1L7gEWuU$=jTO<{`^RgCVJT+1XiEXu71eWgG+7 zuqlh1lA4oTmsgc{y3Bpk3L?HXLLzq%YJ`q(X`F z;^(Buw|af;TcWGtjqX*o4G{wm2u0B$^H(i({1qRFh9@rp`T3zTT8JBGXX`Q z_F!hihrU!AzE(pcFFmIekXwwi#&=VkQM}e{XXLS#HIqhqE55<>Naz)Wce}u10mZTA zBxgfsnQ397X;xu$xBA*juROJ!^Nr(^19bMK|le}A&ub6;#{jO z$q315X*hbV#)AM8PTCZv)>c5+;0#+LK}(ZW1$|19H9Z*zx}u^llI2&n-)LxAxm;~R z>Un@m9;Li#z?E6 zL3D}eu>(b(K8c*@G0Kq1#;&8HY*+KWO$*B{5CoyjszgTV@X$!w%3x{!i?l ztFExB0+y0!=@(P{MnUr{$C)Y-#tx%?K7r}yGYuHrAQ&5wc9e0tGo93Y64K?Qwu6r7 zRZ$M(<8son+Q-IX%spdUa=wR%uoos#JKdbGP-wiQxRh1OjEs|Xr%ay&VBC`4VfgxZ zHMk^j9Q1ZN@hlILo~YM~N<5c=sORMySzKlOw6ZFn9Akt7^Z)9rD#P>lGabWbI!T>r zQY*9RjMFHVW0C;?_LH&l$}AS;8xX=Hn@yc;;!4d$%{Gu3nLA+Iy^JO_Fr2+!b)Krc z&jbU5w5`ZT@th8NTMeG&lh1T?`FZI;KM{d(`cw)I$&hcHmYfH$7N%j`qQ#aU-IMrw z=nH$|pD05A&(vZNbFDauGGk-KLQ8&BYG!F{Y*`EZRE_}}AGIgM1~QRD1wQIwn9{ zyQ(=dlKh6C7=FmrV{$Nga<{dXEK=sG46VdxGsIE378KRl8yG<~DprVG79<$8225iT z3mOI06|FE$rM#HJj)EU{BrxsW`erhzUTY}7kZT`RlR zRo0Vgytdi1Q8CX()m6P+S<^B4X_nHg*$YvIf}-70kY`C_*)K%B%T?tC?MU;J#RFZR z*g*}AJJYD^z*w&cl~W4~vY}yNqKG7;$P$W2t`2+nB4H7c3TrB3{k7zeEQ3tuw03bR zx`$Vxox+gvVO4@6T=NTpxV3AyE|PMOfmw2s!Bb$1vOs^LdjVjbBVx*pF6ns%DOqVy zv_~?cZtKN9kW9U_BQ%y?igH6Q;{q84pdHS%{d^J`?bCB*Tyv-& zy*CIpe3CVox`Iq+Pz$4bh9q=Tr+k)iJi$l@WaA2`HknzhF+9|Fgr||$gdU1@hSLj6 zA#?}~57N}1Y_yC^P*i|~PO=J6<*>xuysX^ptXvCL#8qEkTjlOvUkP#%jgeK?R8@C@ z9~_%`&KiM*BTJAJ6u_HCX5JN>5}$F&pk$!C7APAP!i=MV;(GF^;lMy_T1s9N`bqH) z!=^-Ccp9$EFzTBiG%+X!i0jk}f|A6E!IDCR{ucJk>d=K0d~8xxv5rQ|QD5IjZxF19 zDvgy?>8co^%VNM>!s|zrWJ7jU`gzrQC7u88;i8lsT)}=E4l5`N!oc8Gj zU>s|@!@WuD29;$MdCSSHoTSCO2cXh>*;+t@gS{Y@+;0@$4iRK1M@+Y12Nb|3BOh~Z zO-+U=0O?tUnK_oitW=-6s!r8#H+m+J@7$O#y7#*rY}dMm~Wh@!Db)=~tqW$08o2eO`8Q zVOF*!njGz5sK%Kb&3-ondFc*6Mrb?4`exB@)^vdgiv9Iw4V+q@Mgl-p)0l+WeCikp z=i-sxc>-*x7v~Aas7OkBDPZ-meXQ|55*eSUpD2%PHbtZ?NX;+84l01(9#hUq#kkbY zwm#|P|6Zx=3B_7qhp#j^ZelKgaf`MFmK>frGGEdIxCSv$*n*!-l!Ckc};PX4p)gt(?#+I z*E1L>kC>K{tzm~_W8xAdhVySqDinye8@4z+niT3p<1o;SI2y1*Ga8U(xR|cphccT0 z3p9|4hNKaql2czT%7PWibBkG1VHaY>eJ4AwM;0wg) z1gBML$t089@%`SkPDs_lLUGpLWRa{u4zE5v^1txkj^HWDC)Nr6Q_gGv}V}@U{kzQVXtIdr)OVPil*5Wx$Li z!^x~|QQ3*eQEF_&F#B7oG! z%yu$)AzJ?qlK@A~>H-Cw4S|EYD+{zS^W)oahyr-`+ z>8WFxxtLp`yq=5Fu48BjwgMI}FuaPhF;h}Y^F}sApeTB#nMt1D-S>hYQVDuLYkAcO zc~Bo$#YM+5u9H@t$q*9xkMkT8--XEq=w~YnzPb+GJ?pwg!DAp}44wF7$#zFYMtRrDLpI}FD>uD8iau@)5O6{m91(!#A^(JTNm;VwuEXawlKjo8&)S6g4x zqoNKd2E~EVGXNa6zHNCGsdHFG$umG%KU!Qqd6#0 zI4d+}8`U1K?`%U*Y^qeH1Zh=e=Qu7(P*1hnjJ%yldNE>#m}yDQ7p+-#OH@Kk>y{iN z`I#))7POvGM3wjvpb{h&_bEt$Yi4eG6mAUAosHB&AX^dlO^}UFEtw*g3&&Py^in{Y zz&qh?6V_2eXH}b&mCG%aX~~7j4Jnk!4Ftz+N)-KVks=+X*k7{gg*0MmRpgt>S=-VbA9JJnmUNhRn?v3lT`MImHcBXJGNwwu?n4GXbT#Nb<|k7(~7~} zWLqrx=fwHZR?d=SeUL^|8~jU^Gz z)m@;W61!BrUKypuJ(0AcQYLAm6-$;?Be<}&<(4cy@d~mPSgp9VW%Nr|$vQiWxQ8bb zV-+b98V53QL+?P^Zf4%bZ^^VRO4s*NaFGwivXaS3HX%MPR<~@jd>N-3s&c?dhX*;x z>gUfJ1e-$8dwxeq^~r4Ky~Q}gd8t@_b?YseIrw2IIYq#2voMDrKoCLHOF(R|NcY6_ z-PP|E2}J5I)&W$vuq5At399Q{Q(GSe4U|uz1(vLoc>13$9ov>bW>p4;gP=Fm@(#lq zBb|Za#*v<9plMa`LgV_NhvLQsB8K#jBz(yST?vY^vwbqKZLImx&7yF>SA@4_EzVc5 zu3I$6Pw8wdfF6#nU(#O~24T5S9A0Ig=j-ZayT1`PExa!Gljp91~vP~G8F6$?|x!KWo9of+9O^tT^8bNK=>%Bie?>_TTa zEo9!27{>(TQNn15q|Pwv74&(mMIGl3r>qpLg+y6&;V;lP5q2YoJva+&+*aHsX|ib7z13BITOu02pRIyb{aHNFP}PB~+OE2; z;7OabKn~KPnnXQ8`B6=trJqQQYSOkXesOr+c_}kiNk&UCoil7mb_64U@=oyZLnjn~`B*ZxlOb7SY^NwUwl^#*pfB=uA1;vLT&KI| z9OaCBf$Xx!943fP9~ghe*`F7<*Pz15@KTGS|=s61V5CJ z6x%AcvUx&cy2LlL!4n?I)L*{=jGPahO$K_$4kyx8F%7YF(n`Lq4A;~Jk~G)wmGbcE zkOO}0^>_SEZ2 za@|;*(ZxtZEctSJ*^6A|C^@GUW32}%$>y6t&uA({Tr$Awb6E+Z3ryKh^GNiKg^HR$Wmg*PDARSu zftMpX%Fko9U(hn@>5ySqTni~Hy_gb@a{ww!z~YLsn6Hi2vy|zexj$c0SqrEEB(kcx z#H#%XN(Fwgr?g<|OCCUTOa%L*j9%>oE*vOwK3B#t+~TM%D>seYq*o9&>+(6lsXmw< z!{Tf1OR;O}X(<`)EF}!HNpR2*bE*O7Ahc&H2Pw}n%@rMGv0Bv(X#@(vURq0XQ^9@c z$dNIR^_6PV+Z{ZPHD=xO+=vGBYXFKhVvsCB5c1j?IA`OWz-pXqZ3m(!9d!~(y$2X; zN$1x?9E4%&4U^YWBbtjMJynz>*3)?jKcWtvDsU{Njvlhw6dum|djZ;P)vh6ZBTQXFxRZBt|;c zHXrwk)l8))Y&6U#?B16HWwr(08HSF~Cd*P@&|=595jzdp4WlO0W5aN0xQs3 zIZNJyU{lC+txvysz1gBQ52{)fMF!`Z14-w^*fW{@&>56;FR}=;yn@V( zucpbd(%vPlej5I%|75#z-rK__ZIy}}iDWE`OH{9$6EaKJ&DBk(r#L_^ zeXAbXtSx0H6A{AM%6GD1ZM2glYjKUbcUC>PNVWhv-x~E;pf=QZ${m*l6FNhLBKf<+HPh9)l(D=zrh|BDTIt?V5=Gc zVN)h=5?Tb})pY7?CewO|%AQ6D&JS|!1lV{8ZeCxDYXEoo+G>|Y!CjWc-C46T;nRSB zl68q8sm#V=9gKXwE{lznh^L1)9XG+n>BVYYKf(UPB8w9Zwi|iowSARQNX>>$Bg1B6 zigETvpXJpe=qGVKQ*sQCkzEGpUMLuQh1RJQP?IX{T-7K=tTtW$Ln!x$pDkh+cSpL8 zf%fxK;4cpEb+D*~nb4x>;*jatl}z>8w|bn=wQs|IVHhImWwngFVGG6>XegU*y?g?? zu4fPlhJ(nD?=|$M2%D+|Mus)B#1b?#HK(Z1V#728Q__=R6id@uzO89=J1U30=)>OR zWA>hB$V&$AqLC&_>*a+z7kKxGczhcSGKO2CAT1sjPd!zG`qh!$rQ!4>=DKo4yfIrT z6E%ZJwGKqVUF}49)oS3DYBpWm#x-SG*9@ObzBl!HUc(BjBwS1`D$IjX030jBF;5ps z#UMmDqq=f&p_!}1nTN2&hCRcq@9MAQYFC${OFLDX=c92jK?Psei4Zrg7nB`pyKpA3wu|OQ%Tt* z&bpvHOkqF*K0iuJP{rqD$)vViF(R=OI8ncNl8Tp(3ggq7d`cjzusnuu zB-HBnx}IgfomegG;X3Sk9;U;COj<3*-X?+e*lfwkU~~C+ zCDtVi_mc1w63k!BnL>^#v&oQD>qtU)nwD3Lm)CT%ld@T4ZjoUN+uY=WlU{@y2lTQB zxjeP^b^)XzC^iL*eoHFn)cibnAQLXCHa=M<%Lkf^`Q5v8!IPo5;?J7bUKmifC}Y+P zyG(;qTx({?*Rg)mg%FAJQ&fYBq1ih(gHu+&=4?-bi(~R4(-ng4Fv$f*SmjWn&Paz6>kLB%FC@@IeDMy$ zy5r5>77vb3_I~94iF)z9J$0Rp7d@b}$V)b>obFai?hjGYRZGPi!pox$+ynu?U?$vn zT!>N+N=a(quW|9IR_+KG-z72{)xm2h586scsaFs_J6hWv{Nl+^NaKp9rL%JHTFV!H zNvy^Aab{u6G<+pOcTBzd9(my-n9+xA6^1X$J2l#FD3pp2MA=PxYNok)44VxErfje; z@{a=YP?V?-Y)q&oy7<`?y6hEQ+>G4Cqf?sEJEUM+q4tJ~GYUZqgjx*+^^X+HeDxGX ziN-jRq){$263@m(o%Twcw6m*-J@E!At!yW%M%hmK_nsPnNoc4lO+5)l@kHed5@hK@ zew0qzUs)s9(uj_VaE!@Sek*=qY6`c_s`Q&?S9PD^bIiOR8YZ`JJmGMRf_oVJVx=ph z42D%DAMmU#&*#P2vBRWpT%c<}=2rN1szdgNG#onAV+Er%ZrNIt)}uXJi-KJD5r$k@vT zNba!)uMtTkU_EAi;bO1ex0qCT%xuTxM{ah61c^eNk5kPltqdZuVR*<^9?mneKb+xI z)nVN7t87oEnB-@YB1;fK(X)OM!^NHlH{?j4F3>#J!Z|JjW=MRYw5FgOOt#jatlnXO z#L`z27=qG^i86|MA*yU^6H$2%m+&juYPEplq1%JH4mV^$W20d#i|!Ps zVWeitPcs?T)>04~Whp3)En`BPf?AwSr29AeWmbp;RjLB9b!L6UR1=LMC8ESU#i?p| zWJ;SF7D~;gW~UCn*9(n`@`L7_Wsy-=f9)4psxy~*Fc@b>IbyURtg|vv<&y&%W0)7N zQt)h8nhyDel7_&%P6)(U4VNM>sWe?ivCbo#RY~*DO&~IbhN@0aKD|*mJa^JCkV1+1 z1D{|x_^mJgl!kKsjoB|r{?TKE@dBIz#`>$-O*BqfmeE(y01&IdyHFx2nz$FDjuDsm ztT<7nng+ZC9oIMSnXj&%LdHEFq-LeG*{qP}IOmup+l&q9C9kf$9VDKBzYM9e8)@<@ z;W^Ml>Y4infmlDh?9&ErVtG6A^|&GySuLR2Y4CEQt8daP&GHB&^wkadE*r-vMI^;hWcO$+DOSzU)P-DCT6Xs?l~#LLGl6p*kQe>NBRpvkl=WIWX)2jA3LC09H6z(hCRfFzN}WrJCu1t{ zMGU2w5bepel2(YhNLp+NnDGofz-!;QAR#H=2-8z>b5BdeHJGGuq1ubaYNApNrwblR zW#TGGn_R@qqw24mI2wfX;4|ZO7$cOPWywy%8GsA*&?w_(vUE$FI?dHlY;DyO7nUiN zBwACe2~Y4GC&mN@F(zzL*Si3wvze z2K%m6YqC;6Wk&%#116iRaOb}BaTy_99!huy^3T&FbNsnySlxl7vbdzo*cwCKW z2#QrfcDnTZRCs@6TS4@couSq7dVNEN&8hmZ15kt}jl@`^ZJPC6m(N=EiocA0GKVYK z^P4RjKPkO$o>-6!ws&G;68PqA=F`#DrLL0RrS_`n^>CGSYqD#T-j_sWm`oBTNb*kKb~JD`?wVFS?q`ul`;q(1VJRL?u8$ov2AvW!@O zM7_g4n34c`Y5^9rLBbVNjsrxOABU1ki5&Whf~EwEYf7*0pv*fl?>6v@ zdM?evy#YOQqu!>ykT(&N19O4-M%H{qf2}^DN2M$W>IbmbaBr9iU|=H9+d@k$6k40r_O!GmK9& z7o3aXAEUg7JFp&%wW95TYywU+K8oJ}5Uaoo25`>LIJCACgWjA@k=8wW*IE7DVuv6yM^w4VT_)rjWTv9(eHv{gX=Lh&B*0Uhk@ zT_KXg6$^|uiR>_Xe$i7sBM**77N=DuX)`cnt&)kqYA)h&!7Px3c9vqk2V`TJXSRwL z*cq%P-N(G-$5!w8@dn+e9XQ&fGHFc`bh3nj(&Q(XWar_7YHX^R)eT>}#NAQZBcI>6 zW_PW<9_R9-h|D&sMJZX3YAub1*(-VD#PIXa$tcey=$)%Q!8lezU|@$lWGv1}XIpYJ z;B@Bw5!=?1XDO-Iw3obKvL!(tI>6i-{4Rujjzd_Gm z!e&qZY1+ju!=-WlH@mVU8f8mK?5#2_TuDUt74);GvVDrLplr=kv)~A0;0V<`kL#Mc zEH(r(@)_E zmR-uAfRI0Qq(>lc5Ncm^^0SUzQN_VD$f}S-m_~6OOfjt>i#`>}V3^86 zm5=$l%P%v=!i7R-DJ!rM3Boa?xwt8fARdHIa_NKW$QvLVNW}E-5iTaD&WKY?t~DFy#p^tOeo~s5b>@w$+y@*n!Se5UixuGKzvS zZ$;RpTojUt0+az>RFEK8Fsdo%mgEfX>d?W!ODW%|mehpu4q-Hf-s3YMovsU##m~d$ z#Cc;cPh>!O$&?}9*?KT&4I+)=24Z~ef_-7w&`CXc0`+He$e)QMS^{;Bbl;v7JDU6U z6eW_Oe>a6J7IYy?YDJ>{nx_Zh!zdPJ7UiTw#Wke$=j^t({eTzB(LJVQB?O4os99sA z4|5yFLzk)yH|-RCHC)u`C}1PT*9^E%olZNfi|A389q_t*U4)^qffj%--+SqI1Z!6+?9K>4aOImt3UO~PgN6(+2A zda=m=N~6{DupUsX`g^^UqUTPiBPtWX-(;5$YTX+kTy3SFae2G(Jk~N>Y*i6xlmg^y-fIXB3fBWqPPKRYMw$>^ePE)13 zg{Q|%Ewz!Qyx16{;<+Ix)|TXgRmkjV zIhq?T#7s)M9}TDJLEG58D*zHPXMztqFjd=_`l}rXC&+Zqn3hCA`Q=4bho6~Fj5>8M zp@@G~mwz?hXtg2Zi1a~u$za1%kSG@51E(%#?QCgDj7w-4&zvMw)*==DtFmvC@s({{ zlaM~OtLj_eGM3*w9&A864{`FZy!l9a1nflNCN>KAZQNs5Ujk;3zI0Hk%tmH;u1ucD%=54@qw|LGD7HQ zp#dN^0`0zHgwd<>1ULLB1C_)~!*IMDn3697Hi{(Id52*=(HsDLpQzNF#q}2lW#$wI zp`jbRm}0QyQ_6h8ye}oLeS38Wl?b}d8&uOr@ArfFl9JRde{#mqJ1sGk6|(pUoB@ia ziOn`G2`77W<(^>&8;U$g!VaUy1`3d&ihcAx7q^`5@eFe<-kQhbTs`r`H$OSFZ^n>} z%e7p2cs_<6k6}%7T#)1+$#e_HTwDxg-D4by$1%jc0kaKLtt<%5wc@p{l~Z{|IGV|> zqx2dQX4@?|!y z@I;iEKd|hDG8gE1d*!i;MCGe^I(b({A2r*D-u+-bvx=Ef{Q4*t_sDaU%pXLaysDNw z?y)sMzF7@SgDayTp(tcQs7ZC(NY6y5KI?`#m39=>fzbu@UXUshaC@a}SCOyM z$Vfv=eebVf$PlbXPzKyMAUTytgV+(@X;NLVr89;!X_CJCPKz?OTK9|AKsQl`u45L zb2I?NW`$qE046EhQ`N(2@;EF#yrU%ggHAh94&{DW$ykhh*u@@N>24OOP?)P>YcI0CWux&2jMKGr>Y zkBD(sKut!txmo0QvAR4A9TJLy*>EkRg_4Cy_s=q85-;3WUpCHzZpDNyyMD z>`B!I2M_ik%a(d{QRG#w9>eUiA+XUYvY>I_8gtoXG1XXp7OC`8c_2}G=@owcWa6SM zn~*prCGb;Lb8C_1G1*hBc6aaSBj4}07Uz-dMcI%k4QrskHtqer7v-@Rwi2o{Vop_c zO81V|x8>srZPQ&&F92+?x+6s4P!crv8N6;cHDB|n1!0i4O{C!-IddU%kE*taX#f>| z4NsVReluCbbFp@4T-@EOx(rqpMf8m9NxhN|vN3}fG2Tz3vu zkdqZl8a#$MQ;j<;6&uQLRdtUYE6NwCteLPtY$@resZ1=y6pkg>A}P)w)^!Pwi1YAy z#Ix@W^8_R|Rdz1fVCiS33Thr!JZnwl6O=N*`YrK6sQ7rn=MZ3fL-Gb0F(nRu5!o2T zeg2!)n|x1n5mj^eN};)hzfp>5w-RVf$`5D<*w?2vEH{|1SG7!1cPW@GQ5$m;nV<0#E|h7W3pa%V(PW-T9G#l%))61vO{nBHKY_pGtPq8_wLi2I}50*A+=o;&%lK z9oPs%6srN-qD9?;H{+zfT}R3Mt-&*+N2)S}R$iC-Tqu802_n6=Iwfai@fL*Z@}gHJ zR*%2^|XaF|J}dr5~wu=QL6 zDY>U8D5J_~2!| zQ){=UD-p&gQD5>O&yj#*hJwD|bDaI2w6?d;NiC(WizU(BEC();E*wT2QoC z_W>{_1jS39qDU%&$Osb>Xe^w-=IL%th2m_xOIh*Mp|oi+RpMxR-vJ{HE0jb;_5TCbPuRc zBNIYF+)N025NK%a`#BV|R(tkuFHx#-2=*p*y9y)dwLB`|>2Io|s~0 z6yFSLC|SWp-|uh?0WN)Net@dfWBsR9A2ONL@T=7KDZgbWDf;4?f%%Pey=AJfqXWuA zQUR?Bd?1zyj~I%s(=pzs(8GKX#s^BNc_&>+l7I=H{8X1R?6Tj2%bvJV#TqiA)l*wN zi`D!H&SUwfKs4_eTOp~I-EfGyC5N1)oFiNp8ps_UoRG#!aKhUQklO9d{YTv&AMT=y z8`!1c+#!#bQ`~i_)Jl>M~q@<25;n z)9R2_5Z!gs6i7N#!f-Gq7b=U8qWffT??g7EVT^A*Ch&HLqM;< zt$A1l8Y!h(d>_u%HLt+Um>D-}pwceAl*n+Fwen%s*9_#L#T7HO+zeZfE@x1ASqMg0 z4Ui0RhBC;SVCcNbR zok%#BJf{T;yEQxDy_=64^=idP!HAs-f;p|7-oKMdW1u$1fl1o~aH>wT*1ELUa_PaK z>VBi+eCqQtt zo<%3`wa82bt<*H+r~9)=dBAAWQte5pa2<^ z?*}kwauI9mxj3ms7F;6@Y3_HYdfC7vo_@X;xz}`c)p12l4%vCc2{R?<{2Fh?h?_0? zil3veS0ZMHczJFiVBl!@zvA?~ogE@Mrsw;H@96FQ)fZy} zO}ZOl*U5Ks{BR~%fz&un7GzJ8ML?C5Kyb9Q!NPki1l4DR}=&?TRi~ zCNDJ5NfgS!*DypQNZNwIybl7cZ*6|&^C}B-U9A?&bfRK3Q5s*3bEPyY<)|iJ=U1yz zv8KMBW}76{LgmQ-gGSOX3{6MLv}$`}=>xn5JUlzwS=@_dM%|k3BN09U&3A`8@)h*-mpP{^t%;vl%=(A?HBn-o@ABYBlSto zQn9CWd{AoOcbV|87x0YY4$H}Eytgq~yd3|8f3{HQ`+q~AxknDjJpdBEJrvGCdzzQy z(cea#_T*pWgC&HG$9H#E_v5|MKca5WKcc?RKW;r9{g2V(zui6_A&e7WeIkF(@fV+r z@;y2tPkS=rb1>;&M*ro+=C`rud&XOQ&Z7!9n>9zoQ{^jaZQPAN(pHw;xBJ@BaB-r;)OW0e6X{>JFWDI4fNU`)p zu997y44@d6Vn@aRU=D>)8fSIq zXD7dqrMP7%O;J-*uToG34UJ68(edZ0Q%Hfro(nMy8AXy{>h5+8Z`<7(Ki_}9y#sUH zkdLLl((U^G>h{z6p3OUl=4JXUD~EC(3pd6qh!V>%mLZ!cL=foOQ?Uh%Wg%+~hi3BaG9lXq%}pZI zCIk!T2`M28j+6+6;W2I&-;qJ5qAG~VPQc?tYH3+-6Q!WSCv?$1e{|;sYRWTFAD%_YHy+N?3YO(#IJ;ve5Y_S2w z7zT^O*^|+S8&t`=`u43`OOQkNE3&A*tZ%z_Xm`6&rh&`-*XWO55oiw}F@eQo^rlrf zkMaEoUp?Kye`vp$j6fg{^E1kyJC`w-IYIYM{a}AMZc^`b)7WlLPA~!?c6iuw`)2s^ zp?8tVaiiF=#c&mn9Uy6_sgkYpL?e5vG~fgM@4vNt_=sGKen;pNE;QD}|9Y5PccO5r zczt$073M*eWcPBwlU!5y>}C-vm)$JhE{RJ`6fo4Cvki(tvJ`sqW`-R9S5s2&0f<;< zQ31p`RbVt!je#XK-D^zX(eRPZ#wvk`C80fV`)m_H%0<4vCd8HTQp86uE zs8@^l$TOK%>`W2$2d8l-!vq#q_^NAgv1rR}sRa)Yv0i~?l&h&C$L%WRCJQnEO@$rs zn|z$nO?*IO(CQr378eLhQ5{%QhMUm~prvuwZJ<+*B+c z`8*{SRU1pRRJd4QYbGTF{Td>PHsP>MJ;2Q}9+_9`!KqwQk$Q#Q4WzsGz^`URjR|}@ zRC^eZQN$cBOpt?(r<>1qj~Dtcs`#Z$dvaQwZwIJt2ay%L)||I^-R$ zl1VP{f@XH&`V-8C0kpO~ zKZAprBA*d^zt3#etEteyB;GcuX1>GX9rY^^s1L2D+T67_g)fjVC%&7i>tO)LBU1Tn zu`Nx=w*NgtDldE3A(w~w@ZM$ATW?I@7yXeWGwgm(aBb^YNSu}p_(x==okJ9f40(Mq z+=zTmKJ}BhyhKa~8Mt@1FczSwXo-i!w`G5y9AKKlcNOC7wbrx(nGPueI!!ShB#`Dn z9WYr9@)dz>DllxrI#wa0%xYVWrOt7(H>y#B9m+D;mUf8MBhxOp(!G9$51$}g)I~`t zv$9QhK~_WAe(bZ7G8d9++>3+iWcGS?LZMsm8Xob>Lk1?5U#`A?*h=NfHaA}!nx!%w zna}_^>yVey0yC5;5(wO&6QF0vN%?x!Stu%K;xcS!`UF*tp&^pohJuvZcE|9Nc{MLG zgLUSS@?n}PaSYJwGoLR2<~~*PtR8#L@~$l-goxf8f|Ng&Lg39Jqb_bwc}QMSs}5>G zbu5VoV$sFF;k*h(cj{T}6Li_VVVp}VRd^@pB``xE+Dv|l_&6}BJizG38>rN8*2w~# z5sCX@aF8HUeDuI1J{n}eqK5c{%y5Pe1--kfL9-E4X?I#3|4#Pftn6jFF7IQocwB5c z{yfZON^vHu&XnEyFvB7?Z-&a_B8O~>;Wf}_oW4e|LL(9+_7IX-qbN%%KNRbiyctnW zpgeP+1*BlCz+$VZ_+%P^u>}(BWR!6dnxB{J^lkoW84Mp0cT&XqOl%v1OM$IgGOIv~ za9z=PHH}qwV7Bs|NcZ^Wvj>)aLE6s`3e>p@N zQY{!^PWm+_q6tJ&Iw+*Jks6g}$;uV$WtNvjkbZD`fAd@Sd86uDby!*M_Ekpo!|ah0sE2vnXf=83>~|PVO}Z79V*cgl z>R%?BsJ`Mu(ZqoAdWCygp+=A-MOiSYCY-5%te(StI2onI*^TuIM{breC?9G@rljmJ z`ksX!Wv-Um2h543(V_~hF@ZUIhPdh3P*xLaHDzFu zp4t)YH%g9CLm% z^5XeP#akDD8Z|QEEAQMS1u;fOaHFS8t3B7|{4k_fy_y!XWws_`v09RZ{*U?5Ib6i6 zQ-EdZ8j5mn38;k5B1@|8BrhMf^d40lka0pustBDz-b5hv5>UEO8AEJZonF#ExRs=y zkrI{QZn$GsiC8A{c{R#Y3Gy|LL*;9PHe}A=VLMSbH?CZzRH&7h6{yA$2)W_j94>%R z0`l(?isE-06Zl0}AQl2T6NS3bmz(QP5?0x8vf@i-QS(SI04S-k93{L{$+5M%5|y05 zLoUK4Z;!&3zduD7F?=>_=%(8kn#Rp#p;KOwO~|l+=W9_JsdYIxjcS$>ZYi{aHpuv= z9EH+>s>aL(s)c?3W`=VfKS-0=R|<;;$vqwYe)Hw(UaDIfWtA8bi?lDaRzDbs^6+G` z4UBLBCbVyDw+vT`C=`_RJyrG_UY`S0BC$9WwAWC;$kJ}EN)Q2ZXcjL{IG}OQ=|zrp z*Mwc-J|%s;v!C{}SDV0`Lsq=8-_{>0v}#P?BK(~Nn|UHFLdYdbwt^E=?a9IUkE;-b zs#g{CTjWOdjP@PwC}U{dpAK18u^wHq_VhqG?Ldj6#DD~~m&Ol6UU4Ntn-@!HeQshR zY0To`H7`gwuGGYgknrHt{4l+0t&wic6sj}3`S|f}eSh3GmTKfzobOY$E~+!8TH@Eqf5Nm7|*AflcFL z04bE7{B36od~K*`iWl*in^Sna2C&DdhScdlf8E{hZhn1jz#4$DSdU&%e(}poLK}n> z11L6b$h|Bj6!59zIFilBsupbBK%LUuxM16bWAT4#utl=KxwwH$qK3sldRTML*7HGa{9FBO+qv;ET{49&%-69R_;Nqj^!dMqGHl{ zmQad#;)F((PJ|hjN~`6!Qu$T&htRdaQx_~@5@meIt2}?UAlU#W2pEn~wWs2e@%jTvEyH<-tE6Z_t2&WkC zzSi=H9m*>oa6VVFxVz+$kO)>8KRka^TQQqc_+|2NCyjy6gNs?1i4o@Z{eS}C37A6T zNK=xw>#-Qf<`ga>fxlcVD1`DSmAAyKJbR16wE2g;bQ8VcVBr_ztjmZ!QuDrnyU60j zbvG(#Ts~w_sf2YcSFhAV%@sq?lZ5>g^OXnlfmN1%(aa0I@Q{&cO`VU?p2O$MhL0}c zmOv$jPD<6hmdM$x-<#ogXciyAXuPg8{b;wpeuP?rTSyFg`S&fvCggUfrNrH!wNbL< zjiu0nV75#p;-N3KdeD~VmuRflp@F3STwjkjzfNEckoX9>`xJjI&>IWCXlXrxPvnm- zn7ULfTyn$U_-dS4(5l2tsHKN1wdsM0d=NQ4^-o{iuI{@Q zDsx%lF1~JX+B?K{yA22HVus5+k*aP}y%SQm=w%;1IJI0%fqNfp^N`w&oHs2>#c&ve zhB;T&W+{lD-Bm3Nss#zS*(DiNlj)HP3V6#nYfebvz=fjY!jdfArwG|WD8vhg_@EOp z%;C9Yf)Zq(TGJky#YfQTlnRKOr~A3GLV_AMCvP^3K!tBve3TU=O`&W)C|6mB#4mug z)@gIZ$A=+{QM{`Bw|X^NRNj8-?*ICRiqTh}Hsp`rK3{+MNq+hK;m@Dai2U(nq{R<` zf!%8I@)%SLp>^HmAwq6sEo!Y#tq73B$Z8De5V5{st(9Nwg*zsqBRKv`V;IvCXtQSl1v^Zu_`=Og(pp>{6J}Ep(%r9crG@mw$c18(=Zt^+W zroIP6Nq>%PVsIKa1sEu_X`I$kAAO zv--ZA8R7v$1dY*^=oC&wG2T0G2L=ILNVhn;^r#pwZeg5OuZy}Es`^=Qlnd)WIbmEs zehj2OD{nHjMDI%xD>Y~Y%*Hh;-~nE;7cL>KssR0QUSOg!LNReWKHxdsnhl{EK!l9` zIG-r;x80|fphDC~-*7uLtF3W{dKqcj9mOF6rlq|&!AegE4bu3TYxu+blu&@!n9?Y5~;p_G+sW0L2nc( z9du8Y=+qqcT90v`bRXg%3-{Rq_c7_;pf)vJRH)s2hL0CYuK^}>@f|M25PiIP3>#3^ ze~?y`44bgPmTN0$Cg6lRmpSl)U&Y2HZ>9VE_1kyE2cgtRG+bnE?P{^MSaum!Z)WC^ zwX=JO@I(+Fn#EJ}?=OCi+R1CQ;|%N=E`s<**Pea6 zxwRt|)AgjJsf#2DX({Hn(#Z4T>3DY`8wPp$k+X7O7I!4(c{Lh7+&rc~hhI;$#3UBn zzlTy)pvAI-%Z^Q?>Mz#$;{9>HuZgbe->GTUgH!o1lUpPzKoaWMul|D*Tgrzq6)SO! zisP7a{{G9&-B1Y-H})u7?8M`3RT`~0R?#%zswfCWF@?qpHmpOhMB@jO>kpUL5-w$R z?MScC>`}UQ^ctzlr);902n@E5*HoLK$YFKktYq6POXa3d2wDg8JiN{%C7BEs8dwMO zG^g-ZL^Wg&Ek1q@hgal!5kVEMtov@YhXQ``DAdv@M@jHXRBe8CA&sqWuCI|xmck5D z{G)u;;vca*8+8I>&$>)(JC{5){8zp*6!{ye&(JKMOq2SEGb@y)9Gb;P2oFXRZ3I$7 zp*o-VJ_{->xRyO-?bs3`Qr_Sn#FJl-@F$X-WS{&Lzhex)$7#wr3Miwzlrt9DEk+Tp zys^)&#$Z<_g{g3)>rDmc($BN?r}35*pF5&xz^hj49#kH;1gCGvC6^?Xc%FmPc%X&Z zo}MQDhN2ioT7X)<;+nsjWL!m9NQ7^$xA@&mAS_$S_96e|gv&yQ@y?nfHD6q#d?L9N z*=zG@HORdS1h|Mnxc|@Spb#|C)bhA2=sy*P8Wc>9`6>E)gZ# z_!BIob{(waA-a@9KzWWnM%WQ@O`3+edImS@k1zXx3>fJw4`zn!aY2a~0j1Wrbx1o1 z6oG}&ND&$&N(7-ST1xr*J1Gm1@Li1QLybIQpG&`jEVh7f#8){_y~a5b9W}hJ$Z`T< z+C|cH`r13fU}t_`IYI&ineeIC|Slt>3FENQ>N z)xB%Ip$tFj+qRcylMCpLDeK9Uq=)8ltLYZ?u;6L)Adoq_jAq>j!jO7wJ z&@5d8lHy;Yy3@k`JzRgh`u^qq<<(~-VQv1Yg);=#vn}LD3Kn1zs&#edFteiS=jFu( zUDvsa^%#=c9V(!HT_e8dW970)^>5>D9Q5E#X-)>u=sgMnb=X8N-;!2>7^xF)1zU;l z?+x?SQl&&J2k3lkKkQrPqU(?IYeFMT3Lv>~RYI;yj9|HbZ#T)8Vq{p1Qwc|u)DvBk zRHnM-a`OlrFq@y0;DMU22n& z?bNhj^4koF$1JhB#QwI9&vB$c6#FQ19Gl)gg*~p}L%JKZB)< z0%URUkSH7!iKWv9@ti8ZIg96X0@BB9lKPm9_riH*^#FGhkd1RaQMkN6UkgR}#SoAI5BY0{!$*)6R2V@$nlxvusWy zqgOE5OP|~$U@?C`y+mmQwba3?FClslOdPxr&=!ptvkQ^<2d@sDUj3O=p|t%RA{j!DE3b5E=~z_1t{cQQPWFA-^`1eTZI zUNsulCegvHlg^IYb?~5cQiJ_|ADs$5tUsdB)Cb6(T0;YPJIcNmNimECJRu&RpSrTSQW|IjS1Km=HTfiGROCCLLQ!eVtdK^8?@I0T{W zM!VZV97#eG^q=@nzkD}|ugsDpc@%T?8ij?(G+0LUU=zKR?D5;+EJ~fLaXvY~bGnR* z1{P%rQoT4mBWqNMu==#nIS^xV*2PpkDpsPGWrGDv9-^eYHwTpUO}5cY7wa>Y&?ZcX z#7ztFXO{$zGW@6^{J%a#AriVEg1QJ5SGQOlf!w)dEu>EL_|fOmF9=zVnPZi zJJc=tK*LGfFw9j+CCuO^Dmoi$Eqfb-lwZ?wE#N8ZTf-}7 z%%jknYByEDpY7nq3aWKJKvOAhx?AFNjuTHv`00$H`Jhl=CB%X|>_>g*bOH_Hh1$_1 z4t6O;`SuO!ofQXaF`RhPNK-4y`bf%#7E-7k_p&%oJ~u|Lc+4$2qk+Q~%j2a>F;D?! z;I3e1P~=4wA?7_var>-bqlmuD@efb5Lx(EGIu>IBm5|0lGjyg#c>na5syFWX>l!Uv zuHXNq>OI?(YG0)hdb^FK>#N_oyT87^yZO=& zis4`f%B2g+45I`Ec-EWLXI&2LHr1v`$Wqv*nst(ofrT33^OyTHZ3#Ql50q@`m8ZDy zG4Y5XVgt2vB98I4R2fJSw%USD`n|)75^0Pu>f;gL8*Qj2Dr$p_UO-OVZa-j$i%5rL zG_Ljx^oIGROg!aivlDSg!Jh(1<|ONu`qNi$GzBK&jy@ne!2Cd*e#T;|Ko+1Y57{`mXmzdxIu(NQ&l z*eTtsr>j>pltP%`kkv{gHZJ85;?L?&;~s*rpE=}ize{}LkQg!=Q2ez4*}$J7k#HeS zsBaC0Mot~9Fbl2n_0)RUL$VX8P-qvuR`lPU{kMh-?(`1q_>5?4LQ79l(xexc{!D=? zoB?D5D>JHrab8Nu$_|H;S2#32k-e$N*}!EGnh03!IlLJ0BC1spI0MCYlVQ7Zr|Ewe zPq)V>C$IXpe2qdmR-)&6zJjRHK42n}o{=mehdMZEfE!UMO5j&y`vSh3mTa8AY^sw8 zc>Ll+=js|?H4nFp*Lnct`T!qi=&C6sWdy4UFk1gWZ0aLKJk6)G`Qhd${RV{b6@CQd zq{7b};$|iH$Zn}PQY;Ah5=CStsS0u75K;09f zszJUW7I}O37kWS3e248%wg_#)F$#h@NVt&c+2Ikl&nuG3UJq5!^u{*Le+6reNMD)T z@+><80J31buW6PGo7H|1^-0FZx99NTdg|1WlMdw=ESsr1&AH4jA2y9^JH6=d!1_~0 zpq#RjMtiapH_>&JSMl#2O0n!Y8_Q~ z4qJlNK$2=me5TMb5zkl7iDG%7?D#vK&hC6vo#_18$nbRb&n! z2c`Lw6LI{t*KQPN6GEx)CQuaVkF6&=ephIb(MCGvw~#Q+kmug>a5cBlJ~ zf<)FDi^}L%DOi-E9DVbkTvIo3$Fmh|nVxVhLUOA?t$0yYlPO@#zXKR&czVb~;wBX!R(nFz8kL4P*}>1z`2vOI zJNtnFV+a*qO4G=yQMAm6CWcyfw!-dGLs*UM(VUZA$4)%&$-Vt3MW;fLDVV}d^dfeV z_O(hKaS9#k_)XcBi1B^7 zlo)1aPhUW^y14;XUCNO*`E?#v2PxrGRz9WABOVGCva~%PqC->0zDO-_5l&-jgO(akaK<>ZD2$V4%EknhF<*V5nB0hpkt?Q>6kC6=_ z84-5)&?ZY@TA08K;4#?QwF@MdRb=U(${a~9&jtpjK8>h{`L-IHr6iS9m8h`U>czp- z)1A&%$LEI~YE6#!_x8r!w%FaW5sIfM_4&MWpKz88Yuk((K{k zc9WtN_tKuDj|Mrw|jcKg>vs)3^|oKDRn_<;hoW9K{WV z!OPZm+JZu}?=<)Ms+t0?M ztHoR@`fmLJ#Ymq%fBNqB>iYe!kobQ8e21vl(bnGh z#pd&Wf6T(RkgrDq>_pgJGmqLr!UPOg)x=CB1zTkO3*>YTcyQ2+O6H0lf)%GMsZ$?t z$T6Bx4Ryp(J3E}6Onwo&Rs5@vOrx1Zwm2vLcweOtQ%R;)Thwk*y-7SLGDs(G=I-@dL0+$(e>;T<_hGWt^NOC!c! zLgUo#!`1zjRhriH1jZNVZhxqFG=D9n&Ef&VieqP>^fjP=o=AM@JISBU#5}B>z5}477#F5NU*4#i_b^ z1mlsU>nzRV`=iId{`&Zl#43e!K$qujB0n`27r4d>Ht<57%w`tYh6pM}FvTPoVGfW? z@v0IKn^OqC1nEKuw~dAp?S_xia!)rjc&N}G(nLlC#{;d}yilbo{8QeOoiHXZ4?8U6 z9c2hXnm|Z06gzXOd-crb6dq3XHg82rL!z)(M?n!FOXiY-Dl9neqdmvHl*z_;a>^Gf zs9!!b4&a(#)q$LPD`CR$t=CLPJRm4Rg=pSuM(R#i5GW4k%cJuX^|o!H;ft_j$ycK4 zXkfTRG{M=;^5)2#mX$`I0vsa%wY^t*xFc0{Z0q%ni1 zMWD^VpY+$>mmP&obNKa0J$L&-1WmSv9WdD=zUINHd>d~>{f{UcjR1BeeZi$Oz4`j> z%lbZ5=v7K+(I2a0Q2b-L)z^~vqK)#jvD^{*6@s_EnIA$@??&5O=4HvJar{86z9qs^ zsYQP*pzPU1>@njEK?=0(NdsYuetj}Q>7EQwPy{R`EJ7U^ADF=G0;Qb(KHfsc z#q1))PGy|YLY<}9k6*f*#CL0Dg!ydR?SlV{UU)Klo7{|MB}eXABu50BGROxxYELfR zGQVec6Lmx>hFoE8ImpvP~_d14PE0H++{9>OH^vaK$xjlq9LRxs+1Me0kiW8n_L{g z2`F~)bQ=;{f&Zxp8)xuH_+~R9)ZiS)INf9oap&dmG+#ixe5jf`<++2FG$)6XSw+y1 zMjH%Ua3(oD<PI%P5aFfH2JxL}N2imcPLGOoQYiZ4!_{A|M3d|<7hp0`xOudyh3rFvND-Js z#savo>n0V|@DvEwG1`17IoN*3Nu+v_ByeU>2(2NH7>Fh9kMo|LCBelaMjUU?;n#=i z<3Uwge80Z_eErFgwoQ?%Gk!RJf|ekdHWIEN5%~OKS;9D|(o_r~*7PrBF;FfdM^q+` zUyX8q>2U%c@PWV=Re}aM=7jxbtb7>fBLVJGVdC^)u{wW)_&P~Htg1|0um5-kv%&iM z{#p0+-R=F&AMX`lMK-@gU5TUA;4h2%0*YB7fI-J2TfNZzFxmuI@^Em|1g<1<@|@@m zFtakP$|7sNhKSj%U}02y-w0WEqHk!d8#H5f?x>?2S~GZ3Dgvj{zv(B2wylN3CSRP- zpd5g=D+WpevYzvQlQ!USE0HtAk=d}PXJAroRl+|a2W|F*m7!VOtXzWK54S-3oG)(zQWs1fQN z;eSJU;!!O3TTf?P<4b!Emz8B2lcr#3fuH32moFb~{y<@OLMPNYgejXkH&%5HpFzsD z&k(IEDxO((wEq&Cx7Ew&k8=qk1f(FE^wY>>DxBAv!tb_r)~m!mFo_Q^Mj@(%Dx-yS z@b6SsCC7Xo&X4LkmLbz)CCXyqwtZz&J>UsN^n^W#l5cC946M-95Rrle$A7s%g`}(h zb#q%cq(BX*_8dLphD#MOFcQ^UF{I&%d{CwcHC2p{8Jy-;19)exr}T4c=ioCUyRx=h%*_Og!iYFC!08DihEEkn=5lQFu;9CR*cszQLdbnZrhw<(C4Q?o;h$((_o|JM_5e|w{)tfkHmfu!-NfjOr-CRE9msxa`@N;A>0VeWvEI&+IUK3O#WgGh^HeI zrxzNMFHMoKx-UXqQHebZ4k&Ih2^mc6I@@!&?3@%Jq3Khp3xje_FS8|=(pg@cHr498 zWQJz(4HodQY33Q(tTWHxPmP3lqDeGFJP>-V=1G)mxgM)}y$LG?_aU+&2q46J^}X(3 zCV(7GtR5kVz#Q+~sf++EkX%sR0;(Z=THn9#ZjlU!=7c}tUzSMF53l~n10xwJOi~`0 z<+Pr{o9_fH!rZ{@XRI{)bBrt6fraMMu8?kSE{$fWs71v7UGf`9qyAZm zKgq5X38^zbff%GYTq2P1#6?TBK#JTj* zaD62ME;F*3m2G%B->8u=nL05-ECOk32EW=#wdku>prfp?$N_k5?n3aC|jA30uUETB=^I%LUF^O(IAVHjFPmMkh)uLDw=0( zwm_7JSXXlj-=+we3e;hVL6Ug_WRD$yKhfFpxwjMPuruX=lJjL z{=?tiV_{D}ree!a@6fOj;pceHM~No(e_!9=3zC-j6m@m3?!-><^dmle_xq!W`Il#NSj7(MZN|1Nv^sjB!M2ww zkcoZt@C;SbDyVMFu=g=m7|j>ZlF42pOwIRQkyd}SmZ^L@LoF2kh6khpssmb*Rf@C1 zM00WT$NF|E<$t6=tBMYVN2uYfVm!XMqwv$zVLl?LB3hOW(j@MtMG$*gac%jlsAB#h z#gx#i!IN3Pn2W*pEt8gHi4KW_$>kwb_!%5t>6feTAI2|`(}ZI~5vHBl#9R*90CRzE zC*&!K0;?;mKhEun@QcC~l4xx|%9SKxr3hgh)8-{=3+%N(U^BIg8NdL}k>mfg%dMUS zwb>PS_lK1X!8vK{F?7a&B7CYTLp?Gr1nd zzs^~>O#VTK=yVmq<@ikJ<63i^RuQRSHsQydpZI#St6ti^C~TsS)($%y1d_T#9?BhbmX zThrm191U0AERKJI2^3c%M@ciNz#F`(k zHjq>Y4*4C4`y$1>|81cCy4BJ3IF{^tI#hAI8Y7~x!AR~!JVt7aRWd+)D`&o!gHWS=ll#Ubp7kbjQvzW)eF@{J{K?qBWO|kx z5&LxSRN_^_o9pGqKQ9o*J;#4?4$^AR1RW8A5{)x34MV(AlT|ahgnAtNzV#(IV3-IibRAbd^NSkeSU_rrDuq^hQkj3 z6n^1Xn^`=tCGN>7)jV=G>Z>B``}U{*COlWuGMAG=wX$SDXQ)$Kb72_$oaY(Fkk9|Q zMq&l0sW9wI>iGK;oWcG1uREs$L-P1fr1i}ygpJ(1vy=IWZ7-k4fa2U`5N> z#dHB(KL0k%VauHI8(%8tp2-A=iB@X#dl~?y1e5A9W1N}<6j+CP|LgDT+r6DXN7~$! z&S$7ILqyoMxAVZ2DRCKpBnFhz*GG#@6#ie%PY>Z|{PbD+7~CZ^)F42igbC#Z--U-M zfEt|3T}N-Aiq$G@=UT?wp|J5b3mcbb8x>Ys+(1QvmV%1Pg^AFVaqI9LK(#9yu=!`P zAUHt|g8dtuH~^%lHm9>eH^%Pl>a6>E^I^?*!pbALLkQu$cV$EmnYJS?Tx9JKij0EQ zHb$F5(4o{)V26@Vg^do2_Pn&4^vdPsfuy#I=m11#EoZ9U+hA~L>(PzUFZ{E z4ILl_1zE>mp-d5kDVhkiHm(#FBHj>0^J=M52TZKjR=&&s8BN)#+E7GmyrSSynxYsM zB_Y=W2Wi}t!-O-k{{m~8rRVzSy$RS22l+%qX1;yfO%CQMrF}Hs9FH}c1JS@>_Q>Mm zkNhlq80Drog-;Z{C`Zsrg2HRR6mt;_OyUFROx=fz$a*krx_}gc{FLq)8AI9lLL;`2 zE0(oN`I=e&dpti?>JM|c5GvFeD1Ve|A>U7P_;pq2_nty)Q@9QX$}pfR3CT%s74Z%2IebSt zE7>GCdo48fhWRB;_!hG5QoY5Yc`R9lC{#HGN#hcXa@S+!)g_Rpa@)2*@P*3fq7CcO z4NT(WnN=vd9gxCxQLvLq-ra#&e1h=PUI79cAmIH9Av`ANsQZJ*^pK;RVmI z@O~7R!IkN=8(TzB0c<4*%fnYfR}m#|A7~BnGC}@^v+H!Eb$Ip_;+qW&^UbP!q7Whk zuJkx8zvT;7^5k?s+ktrXB;?v z#vPJQ6uS^Y6!kgVNd9?ao z51~AFNBYW|bPAgSPr+~9y%SzNA?S(ZYj4V>*-+jH`9 zw&@|_K?6P1BN3Cp{EQVtvo)YyS-MuzvzjNH$%;x{m1JlZ7c2d?U2-+B2r(xq+FZRU zi}m~U=ifsqwBGhW4Uy<*;JiisQg3BySuPUSgBOmrFQFlO|^ zJ#7-k-YhN!pB5G60QCqy-(4Ys9Q=$p;Z!|#9}))fkUkhE@eN6IF^UG39S^EM&aVlv z^7Ub>h3WA|kvSe>hROoC20nxy3UyL8%cX;FS%(2GDZ8m*>T9gn4|Bop!o@*hIeGD$ z?K%9q(8wz1<$?0oa+qqqu&QyI-hMQ>gxdqbAHvTG#VW9d&li(3bbBIc0yjrcmIp`a zYKlDc>a-I;%4?^`y&#+%vaZfLGZ>S_5$b$S)-Y`PSp{;6V2Xg_o_a6bK=>q7|9H{~ zs%)@4FW*~js*jg$ruz6HxOd|%`H9NheSv$Cx_*G*NTQ8!8wh_!7Rt%cGMb^Gj!#lv z@|%agRHVTq>7b~bG?f%fB~Hd!9CE)&+@VF1m-TrmKil5&FsKN^&?MJ3iDDz+!U4VgYga379FU3^S4kmFDwXw z`A7-Hl3umrEty3JDaj6cre?B{33lG8-iV}RXpWSzUcN!a=ol5?L^pZ6fEk*ikYM3H zXgCq(CY0AV9+w8r*t4|Xuv99ePIeyRM{<@fx$I!_@q)Bnj(FmgB$nIa-<8Z|wKsXP zD!XNkfah0Ws@RqEi8w#W3E($*?#tN>n(E~O8I3$dFu8s+>KAp?kiQUp<3k=kfjK)) zt#VOcf3Xn6&g3Ze%5 zm1INS^kV*S9#WZrV@+}5Jm^@9P$V!Kw2a3(#NvTOw28+d=LGYwYnlCjqh)rpVYc>z z5$0c7lNB5!78E)RSz z^*NK*NDvwP0(&u3sF%kxl!bvW@$xXoaVB%!&@_dp6ds#h#I?Q9Ib_X=Owrr|<1@vE zcv2l-jmYA;>oF`x#pI%cy}l5bCi$S6sRg5O_GDcNvq2T0e^%@HVXRkyWYA+%Lvg`c z+?InR*e%G~ueI^C5~VTGGUVZED@%=Lo~;IXNwWPG?=J_$Z)M>Ya%8WskuHd->RLc1YMXecptg^Bax9I~oclJ~K=6L&n z@`}V8FOek!Z^mkV^b4GruV<2RAqF_DmnXFwknbl2_3(VAIuu+Z0YHph&&E=P2=!{v zIaOV+E|s*7kTEzd!H1Kz#GYEo)_|V#P)sv{i>w=BNZpyn>RZ4e{r<`5{pcV5VT6WZ z#yhlDYf9ccm{P;nD`USpPdjBxpq<-2+9^KU7n1*qKw6(1BkR$MkBrfYFdiT@M}?s- zL8g`-&QfTY8>fR80&%aCwp^%MqTE62*`&G)g;~o_ycOtgSgG!V8xF@Z2G+CXW z&ZkJlhY^tN$Kkn{A(FAZ;~ljrv^J|l@q0z#b5=h8<%H3oo#`Z#KCHR2ay*4bQl3z8 zigMfO`Em~R;H=q*$DOCbcWS&Gk0HUM&Vhv^4*S(+`T<98TCQDQaX|CPQ_F_ESq(?}=MR#P@HFCceI3ZhiIswea;OCY!W%88A1Om275e1SIqtFwA;o|9M zd5J4|TVrV4YAHY*_v+6rK7n@w%xFXSyfH3Uz~yPtq1?QhyvPXWoY7(N9pF zK?9<9w+FaHUD?yGHVMc31t#s1`PwS1S^EQ5KtrWFBOnad4U1#0{U?A2QCf3E~ z8{fu1ecM6{k1?qAx2;FHPzf=?1i=D%Jr`Bkw8L2WF6nTEXMP_U9XD&kRLz;~oe_uz zCJ~ZsEG1~k6)ng5A(u;$mf#?2-Q-kqZPSmkAC&|8zLsK;govt`tkz1gDnUT6_MCMZ zIh3r^=)D%VVGQLnZ0d>L>FynCGT?4rTg2%kf68$HP-=O%H_W4K-Pnuzs{@nxfV_Rb zcOG(y!7(j($611FWQBZ0c>z|1xT_)r!pZ~*Oa3uPDe;HWH?1|Xx5ae8JGHj%x9BGE z0SU|Gf^o25FU6vr11@r+Pz4^2tH2vBD=%zCKTtzbWErqCL~ERWN#PL6OilI4b(*%Q z`Ys=u$=!w>@lIw)d#-lHz>JrdDbrklz-5}_&y;DN&W;DS?I~0?h7f(Nhi63tlD))F z;Ue|2;Z`>HJo%C;!~I(M5ujv$(R5V3v^QRW02&-OvZK5JbR9;^2FHgXD{_2R^F(cY z7bn%c2(0k@CevY-QcTF)IKFNucltKQ!EzB@xkm^&ztJEJ)*Fax1_+$G+j2{2( z_ObL)#*?4OpZb?QxcmjN2+v)AxmyoD?WfVz-RJ83? zpQdqqesz!Z>v!Mp*W!A^L2yWZ{|A|g>MV2)#$ZAtg9yuI8&Zi7j}wT7KzStdmwwXr zN74(SJo(}dISv42_V9Z1AD3KWduNI{sxplY?V>7=)IhP*uT8`d_A^`R#~czCTU8J)@v;0(2k#H8!Q>Iy-w?M z^upv=Kcu+9g+R3$Mkm<{N9-HONYH=2MDud?EDIjIGD7DlSK7Tu!TSag!b5B)!37?} ze6Fyf)>m-o%2g}$BDiX^uYGp0oWoWG)BqE?cq;C8v(9Bsoy)WH9R$bXI+6tx*AZtV zjejYYoFjzin}N@hdB?25FLm+MZD=kO!Gtx-x)G9myipGazGqcEA^~)&CeLJoAXUk> zr_Sjmh;T=&x)$-XKaSO@$d)uad4aic5>YUnt9iFMq?PFlyjz#`YCisoo)?K92CR(3 zsvIWZ)4PYkmB)vAIpF<``tZm-0$Ty)E6?l|LJULPue+caxPa(L#2CVIT|o}YI4?Bf zxG5@^tPzx^IFoY!=|_28@piy%6`UcNZpfaCNaD=3YH}6vCf$b}JJ@ zmv{ki@CeSSpwxqVl3GD(;!6i6@qHmnipc0uHQj|=I<)8)UKb=AH8f&vA@ODPTqZy^ zA4m)TdAyL}BkrNdgHyQ~30-!1%Xj&e({x5S!M`0wF4z*v4JdC#(e6q))6+ zPD#D0Aohqj$z?MX#Zf0inOF!25o>sg8f)e7S1{rDhHmPWh zMUXwA$yE6_O79H$Gdc+C(uk=0ez)%4|GIwvTNJ}Z3jgEk_8P4mFylj--9Z-ba!*f4 z$gOMWx#5UQW{ilcT+KPHY!>Y9Aq#168i6HMaU7_t*As%s1s^iC1~DBhPD4AlRq6LT zPNp{<(lD;dq%U>^kOnN&vI}__S%SxL^x_0k;f0iS!@mK^EWcnMHqAI0UJc=ePQmsd z;j83bxlM39-N9)YZL?g7Q`0n;2(#&e6XUA==KXO#TCFm!la{d-4I>ky9VBk64k@y? zv)Rd?;_iWJcyqfxT)+GNsnZPXB|VRbh2bV4m{2_&n#6TjY6S9APL>y;(;N&MOlc}g z0d(|>?)4!`EFtxnq46!o29uYNtBM-&7|j3C3_8hUTvpAoGoiLk^~d=&SQ;*-OO#Eb z%9)Pd_!u-s&*hL(g^G+wgFcbUC>M*#%Trj?&M%<)rdSQR)0B+;pezIG8{vvvOR1C# z+vFPM=}{8P0M!f0h7ei|*!~D1sKpmngM1Slen?3{D8$C^S6`lt@}C|5R`+kl5RDVe zOQBSoTvN$2mHwuyY6M?Zt;oW&g*U^VRU%(O`wTUzp!|Hway`f=iLVyhZz3l-IkNCU zYD1nJWsMF31ec)EqX`6T31DkJn#U--DPDC?(^{ZXlK?Ii;1t)tulkVX1sH%(RyAyob+qr`2>tmvt8LtFpr9FK- zF93hO_pGK@BolL7aTT*o#mDIRW?xf^>?R!%3~B|+z#QL=r7s`V;=u254a)EJL9LtM zh@w4I1Ey0Pq8IB-4^0EFgYcjBA8#ir2x$&4Cs8t^+5jBqEQf7uAxWthhZi8CQl%S| zetTz(IGY3%{GO6t(Ls5cYFKlg5J4*m>y4{wOlU1>zaq*BmPur!m1%scY^XPd&&}R( zsWx;Zz*zIS)(n1?W$Ta~qwyKzT_5%>s)p!~5mw}YlH4q3B$gDMPX%;!-j9kr)g%uX zneg^Jo#`S)hXmo}MW_|c$+SW+%&=6wr0@N!Ch+-Ln#3ETJvcyimSpz^49-cMb>j-< z`329b=PR(3{Pa_Vki?5`&9KY+w7y=W>gvY&&u_Os-T(C0`tGOe8}XEw^&EQ1%?;EO z8Q*Xpix=UpdynRuFb^a9vik;QMO_Pg7Q8X{_)nfU!UHpZHe~X2)?ZIHgVLTW0;ch# z)TJx^)NWndunHsaZ(j@gi%OaZkz8l5n0Fy$f+2-3MUg7OCg-UAsCD7WoV;8_voT7N zmLozSAS-EfgsC3X7N@`oaQk)3mSSIx=iylO45LU zgq>(#bJ1Zvj@lG}WL{4|_kJb*6?|x(cC0@QHvyOHIwVl!s<=T_q^^;Bv{N zSL>}QWD4Q@57avycoQyWb-9$_XEqkMGzBs`LndWiCwK3TKHo|$0m|4)B*3VVIyQ#- zVmzFj^aTA!zg=H_Ma0YrREAR|El%E=w3@mArJrZ(PurW;4oIw71t$e8fR;T2k)2U4 zfnuZ|I(ca%K#45z8mVuEaCvR4v%R_BDj^>aM>13;_A=I(f8yFaO8Z$H4+Br-=&fxx zK|&!l=}+6q9&ZO{nQNz@MH}gNuOg9Mk5?#}ufbf=;KG2waH{p7HK0_18ZT#YWp|791_wF*5N)X9bgS zr?opeQrZ&A5g($?3aII94x1ClC>2X?EDlIgvM3xc4<$wXQ1WbXJY)~$B~}5ZBGa-) z#NfY@a1Z`xFcE{mdb4nbfY}FxRy1qWB7n7Bg3d}_0kK9gO3<++NX=2hr7XYLg|bnn z+QTqO0v=U`#+!3NUJ*`Q71>Ax(zrowH0TZUNMe}q;;mi_Yqk~!%CBZoB}!X?$wd4N zLwuW4B4w40jtHmA0~9jaxI%2mhWz_a@L(PtZ6eO&U;lNqZ~a0ul()M?(%Xmx4ebUl zbVdB2&p&?rI1nd*#OnmZUieJOgO}>2obbWOv@du2i_;kJgfIqy5!pu_K~Bc!URx9^)4Vq@>|*x9i_O-+aG&Y!$>Zp=czF1RFtaj)9IY zLhE2oh5e2+Q+x!D&dG>TIRX@Hat5lt-CsH_8A5<#&lza=r`{}4vdocPs~Yh+z$>D# zwxunh02T)j;kGwS*p)SVMN0z8D!f+Go{!GNoL;gX!dBj1CdC`CpUo_z7Ipa(Yei zV7h{w1W-LcM?YLSycehsgyvbeAPRBnSHgIfR{&cs)Q-|z+~MSMbv`|pemE(nG7&MA z{lH0G!w4ajMHsFeZ21T-gUp?87ksurq6oou!$`qV1IN4Y(XbCgDR*u{Ja|Y=`{wqSO6!C!J zr&?Y1PGX45>U(P?1vwBBl@%;fMkE5FnOyINhQE5EA;&mBtA#4wX>Bb`6|b*uKi~gl z66vrYfGFdZ&|m_t&`9}@tI~U~JTC{A3$*2mz{9&Y&m!8G`c5zl`{ekv?L@&k)-APt`QFEEMBDEW$+^DH5UpK~ebi*jfJ2 zNgi9eamtqDPprFjdbygtwM@DR)Sp9NP_tcxPzw%j!Y#4<>u&WKv{kwapajH%eP-3I znoA7^iJ+(Gwy0r`O_;_My*U-2E0?p(=NwUFp90KXN3Q7k2g9JhBO*b24zYp7%;u7o z*^Tyg$gUK?LZuZ?AQ*OXcp%jQ^xp_!{tU8x%uEj;C{Qs=JepjzmAj}8plyfSEm+oi zO&O-hqPsTgV@KfR7W+h3vtKVm9nCN$~Q;`{_1-KQ!5h?skQYelYN=PAU>=o<6P3&l(oS9?H@8WHu4^Z%sNRn>L_-->L0+*SG zxfp&aIZZC$duHR_@88TU>5Ss?JxJEgfJ@2@nZZ)es`zAUMp=N`PrW=Z6k}^FncR}f zjsHC)>DF5vJ~8NIe5)a6UUDn0H|s0YU#6)13xewRByl&(vy)d`;${k{OufOt`#en( za&XW}DL(jRXTiKX9u))2B(%YHR7hB%ayy z7@5eHEi>NHbQu-SGJ?5=TV~i$)tzFhpu0d?;vMEXZp`58+RpZDYfewmc#?raJ1G5fx3vCZX7l4 z?$ej=>t+c?i;mKpr>vivS%?)(b=eosHR&hsg+ZYD6_L8qQb~3sO#R#URGBzc{qQC= zSk?0XPy>Xgo*xbV3k=a`Q%sVFCnJhLiReR~zP2HtE&bDOrpbhSgC>7CMh7m}E+)|( zEJ496(`|)dOQn7wdSKp=t)NmLpPw9dueZ>-K&vnDR0`9DOS6PdMSG*AIIyuL6CKil z%6>sMD<_@cJr9lgs~x*x-SMQDQIli+VfGLC zGQYmKz4=6thP0j8wFIDv1oQx}_EY%A9+0RWgx;J{O>{y=`!Xw}lG9pl{x<^#>d9PL zj*xH^x@Qv_2>P)`*u0*i=6fDhBC&qpYjIMUb)eu4RDY~%_#PA>8?k2szEW`a7@S;cHqB-iqv`SA zchLlGc0up_e2P9aXp1OO7*a={plR+oG0)fI4pb^azb$m0=&F7diy}6T3T2V4A|g?I zXeQs3?oG&MFVm_TO^Dy-M5W5+CBFo9_iiQ>iMpkr5s?7O&e=rUm3Ol zHtBzdA}r%M612L>_xqc#2x)!auni^G!%i)KK<0M=@&me1a}I&uAcgRogUdX6!$OlB zHU{i8Jew7es>B36WhjdY9XUHk4C?ayWWt{Ky56?MhTyU!G5?;vsX3)px{Po!ofzupuNh-IymvljZ^{3GM~Z4IXdY`zS!B#H3G`ifTK+B4I~)U06kc$5LK~0pDXgm`@36!`@1_7 zkGZ;EJEfeIMyvFLqPaNYiUzYp%QzT|6AtLZd9IG~RV{3h$)3 zL*)u}Lq$esBr1jV6)M$|uHoO5EYZX%wO z2)#-ScnW>c=5ao zp;m$^l})h&GiIt0@@xjPc&CAJF)D6r7iT(IzCudR@yjj5w~g{&f8YA|XZRpf9i!-o z#0*7XME#y#Tq20yBO7e_ijFiV9U#&^UF|k-t2QWcoX2iAF711iWfc(M-vi0GKKw#0 zKUt)*P^<`8?Kuj;opk5c=woDxC$mcOk)ZL7NZuJT(5cK!x+YlBmDaEnx?5GjpjIJ5 zZKSx|2d8q?kw(+(SHWS3yH#b83$Ils(k5=ND@CUXP}C-Rgaj^fFN(MebON#$w|Zi; zHftKFsp_^aFpkPzKWc~0aSPm_cc1Miqv;} zIzz3TVs>-VT5ai8;$t;PYo~TK$PH9Pq}{S3iCi@mO%E*Nd?qR!Fy;vX9w_QJD)k(A zz=*AL4v&PgdK)mQl{tKt0~(dZBaNgXE9L8hF#=`?d%avVjlYB%?|V`jwvnacn-Mkm zXa(284OA;&b&%qf8^2JmT*gf~kL;u_0cZWhE9u#l4>cWy!jxua=>(pHgI5Qv6)slS zSqbyq^mK_JC^7C|q7xm+`o`@Ot$leL-7#6KN(t~xORqzgPZ2gFB#j3r>`zQ=Z^46K z&zEzQ{6Z59&u6mtX7eFk&ClY-$p`I@OIe}_2ia6BXcKI?NVZ4^+nmH#n2WKbaF*Yy zWKLv#(Oqqqq_%`jWXV;~(;w%HB<5*bd^P*wWYTdzPYt)J9kUpM;q1Ui$Tb;8RGcn6%Mm5!G=0k!JpPG5LkTFnu1NVR$tRB^nFlNLorTGY%zzwv9@zcYp`s zWYa~ES;9PXqUHjb`IqWE)h(1q^DjIXN9Ov$`;jg^I5i(~vR}$UOJqTOhFn8EgI}#p z_jS3K{y4vu&Yqm39?(PzIDLoDocDHOs!4Ii2< zjZ@XT?}Y~nSsGYuw5F7~6rl!N>lefWhFVOi>_C*Cc+UNC?%Y$fBrx?$7tsz21r31@ z5_SSd8gNGBZ>pQ_^fEg*u3xXc;U%!q&EXOrP_S!L-Gfv4uxzuD`+wM~<7V)y9=Q@l z+0CYSqYQ0zW=G}t))RE72p*6?^gxHfn}@TL$uD9qgMH^mu-5+qcL4u3vJIiEBWf)ZbG`+dLuYjgE%tvQ(Boz$K zSanGL$$@5s%{pTQhN9_Yap=SO5qM;8btI6xr$qGs) zmNPNIQV{-f)DUI7P_#Ev+(uV?s zA$GF~1?mLajbM;rsu(3|*3_KB_md7=m;hQI-27oQsFG-)5C!9HBHzZ30l?K)L;OP- z3q61O0(EbgYm}qNXk3id5!44JDYT0m&zR|{NQkoCENG{V%AJYI8jX_PVk;hrB@58U zHwUwBnZ!4noq%z;N*^O~$=j2rCM%GHpPCyqHCXhAl2v%POmc}1HG+S20p}mlE`(A~ zK5aFsfqGQ0Mq7as$ODLwGHl$;f|wl_;(!BOByc8CIj)0*>roz&YuYe5zTnLudfDvt zymon)$mE%}YrM61p3c$Y?hOPGtKZkb5o+a-=BpKp(rYolcJ3L`sTayTjCq)S3YDkwAW5(s|mzxSX828jcsYcKDMuf^^e5O~3)tTH#K^~aI z2Xvp-_aESQ@7A}sk@;Y&%6t$Al+Un*6>j`dcX|Ln4KxtMwU4#NGy%=!E^)78W+OXZ zDO8%0$Z_RjPV~^wB7}TZV`iJ!&<+pDT?Rz5*PEY2qW+-ktKUo15&R}qr_D<5U2gF=y zI1MU}2Snd_V;-L#A~+yTBGJq2CJapK)3mts{y3jn-H}v%%&gEPI`R+xqc&rPr-+uz zgS9XPl_l{=Bu~9;>Es0BzG@8x@E(8y{dR7DKhAVV9Cv?=ng8D>R#fUsmb72v-F?)D9pXfOa}W zauN_iY!_s^NYSl*Cqylb4CWgtttf656<7dxbJl}hK5g-`m=AuGb$e*p}vODH`6gu zy?kl?i>nK1&f#68Wg0!GVlG_5V#~xLIeGON)P9)$INvLTQ7&L4zdV!QN{F-suud;t zDVavi!T5fuyJUNWe6&JMJ!lh#=b#yU;PNtZki#5x*&JWre?tn<$@L zyz`~Bi#h~}6A3KDR;jksoTxyiW>j$osZ>)SfqzHQFxw^2OGF^YOND8fIyC&f2lt3C zr*VokygoU2rG;&B+?e#xF_%{~*S}lF6Iw_J6!>n#SLQy=Xf79xT6^&x5{}!TKYI&n zm-*%JA(h|*t#~>^zG#V^*qoBIvVNM6mCX*6X3kLMk2#n+)lVA>4xmC$j{-Gy9=tb* zwn3w^zLbMXP6*)bsou0>tH>v8h}C4{NwsG1xLQ1Otny5J|De?F9phyi1h3qWOu%|W zwUq3_I069%1oDS1)(pbUDO{~536On*O7AX1=uUEu#a&cfs~>~glzuegx0eWFfF<4% zjZZ0%#=qH*$;5)h($i0^B))@6l(5|G31bfh;DmIzl=|}E@?qcj{KLpG65AYL#uN4O z8Sm`n**1g%UKkaXhS1c*!}c6LAC;hfP6mMS*7E@hwxbTD7Uk2Swq1nxQU?eR-yrRl zPaZL??`I_?W?-<7xMDr)t{6gXc_989fx?G9=Q#Vxw6! zyaoF8L`WopFX8*lIL>kHDFFTL>cfXySEnN@=K!U;Y*E@Hhq%VHHQ86M@=7(xFSti+ zE4Zk<@2!BDv!Shkpm^(kbx7o@pNsXbzrF{5Kk+;k5 zkMnDep2+howLf!GQzAY7NKXkVgksFrX5`M5r?@%P`F*yDj+H@J$+jW1XcA!a$CQnw zIeyQ?6z!b_TTC7>$x=kCyjv5k%(hZ$2sl;Td&(a&AwUaZI2eqrTDW-h_}9moKO4vc zbX*D_;U*{O)<9T~^3!?$6>{8CL*4GoKO7;jxUS#8y#(HLSiK<&g<6MF&On zkjU05f3l#e-@P83N%d;X7P}5?+VK{L=W!)zd02UmLBugmZ=q{ky7{Yf^cmR044!T8 z9Ev1NRxLf3lF)C2D7{=y)f_WJ$BJPt@7>MKmr-|se-+g&0jq9i2D&_%UI5&Rw*f$h z90}lS3UAiHX*_kbL3_35Dk_-^;@KrvgItTc^I^?8N(DFE#ULbF3{n~He&}fyij^nv z`JV13=jmQa>(vMw%_CA;K7KZSzPs~cXM1;Ze`{-N|M~9v>GuB7Kn_R9yOX#O4FHm& z5D_2j`l5so02s$Cbw#8Ir8|QE`9DWPv$($m0D#PzY4Eru>PFdrF()+*VijNw6xf!0 z_2tE^IQt>C?=lh-tgQh4!pavXy?|0<1GEu|k9T|V^stTBE=k4WMi7R70LIw=N2z}2WEB$x`+xQ{HDZX=!jKG%++ zg`JX6bQP#m9sV*-D3X*cLZhs(0HZK`e(`1@3^bT3z^<2~#2w9Xu@}dtZj;8eUYv6x z4D8x-xP4(Hp@!$1tKZj0H@9D{1ZHyME`K?jCOidBD{Z__eUi9EJzMI@0|42Tv&qe+>huxwq6p5gOLGq&X9!Z{Jl%uTz)(_!U zjR|_vK^5&4P*d~VxmHzd}BYKio zp4Xh6ItR|FTTNabLrx1#iMrnv*4uSCUTlqFAQahnQ>t>9p->cM zV0+S&1@WDlEF-2RJOASJcmm__320CHl`!z19Gs)lhVt+?{ zHT$G_ePh*YCZS}~j*(`+adKHino9A~lv|iqCAJa?3KMnM>ck(R15GKo9?#av>E=L~ zin9vEgnEHffy_sllH@5LDo43HuBY6yVb*89Z}}|S1i6&wP!b;GU9bc-Dt78#%H?PS z$#U)O?gfK7aDhvXTXt##xhK=)kx6({ji0dt(pC*XG&;PBu1I|KkP2NP?N+KCnK&Zu z1ejqZRG=Zw1guaxW-1wdlExz^cWgZ}*h)D)@H@UvU#zdMZf92b}HJcF?_;?i*aEFQBLs-<^s!!4oi| z+YAxz65)bir4a9;e@UpzkPxmyPE~mj7u@;8*fc)B;Eo(!Wf>VUMketAs7G~m^2^B*<7*l}f>*}Y$ zNG`@7AYYNh?A}&>P$m$-g?yi$>}c8?=+*Riqr_x4{!+ov%scqxVZf+VXR$LjmHUZ_ zTnY~jC#l^ z706g*S%cIhKHWTJAW~&iPf<}*qN8dg$;2=yeOPQlBEob7ugn#+sTZ(Z6m2&na(*Wq zxL#k9jC%BjeZz4s+a)SlOBQ4U5yJAd`s`TJ1BJ1dQ3aCYnNQ(S+R;NtK93*`k8`&7 zX0{alc>nSSepi$OC&w-pF4F6g8QF>-qtoIsFa%ER0QCqUzGIGM6BZT&a1H}4d(^8W zXd88?1=7-q!l8h-U=HJ#s})^2r}$#zbq&4;J~Bw812)KaMcz#+cJgo1`kpT#_;gK! zpDvIii$3qTg_aH9zd+R~7bO|LLuGUj&R5NJr}uLtR9-!>A1x17l=Z?`5YimL(?84@ zIAIp^kUy8%)^MD7AiXCqZ?3BUh)k{c_NoxzY*faHs?&y&HJ1S@bThoKak4nlI;FcjB6Fl5IUI0>)v=@Ih8CBHczZ(t|ZyIU9oVat?- zcz!MbzplZecIi}uk;a%}NlBfZcy_FlE^0h|pr7xKg4zNG_nKI_3Sk6LozHl)p*1dhx9l)upSpcw(l_#o5QB+eZArA>v{EU^gu zR{%)WT~g&O-9QoL7xT#=xS!Uk6z35~mG;01z+rYA$|hBEweBfQ~S#YrW-av17% zwqSov7;E(vJ6+jE zOO4YI9$ZV!$|FIupaRy7*=2<->G1nCltwdUdR-R-{y?BGEsTpKxN`9sK~?#L;}qlc zp@p7%S*eUVBa^t|cvJj3ZnmzWkx6_&y^8t5<|T~H;v;Ym7uBN4dkB}ppcWFzV8NWw z1v%3OVP9pIN^@%vZtFqB-8gZFH6<^2WD?ga$TtOJLIM=Z4q)3zM?FOSRzATl#Mm4@ zfQ&J=h;29`)loVM&eu?K1m7`=73GqlC`?9b@>9HX(BBu+^A%(uu6>Pv)}K#S{-4Pg zXrn09`3NBpTbRt3jaMH3^h!G~0#3`xMr*~eS9o7qvl!mq=5@$o5`y?b z5ydoTL%I<&tQ}2G${m@jxCjC+)o4(9n*S0IQ9Ka6UAbmu8~oeKvQ3ZAj%HFXNO_(7 z26NoD^xYwcsEDfPC-L+R*|2Wnm$$cfQUZVFx91asK|wyxU&yEz7=)apPtuK%?C$OE z!Lz@#A47sG#e|aLEEIPRj@euVC1BOFqCMF2YWj1ym_*@PF4YXCFE~RMJ*L*xf5K6h zw+P~>0l$drLYVSwve5{twdQaBqC$55y{7Q^&x#y!s+3Y>feJyPQJg>qAJN;b(g+sI z5F4392$VO3$LDYNX7H;Do)CNqQjTimz|6V<0^N+ks&iWEH$~0Y5H%rbQaCl;lcK4x z0mw(;IZLU2zPLDJydL_4ik+W^Ha}H6!Q2>ugC&r2A9n^3|?Ii%KLSd!R`QVHNtz(rF`@k>?jMDVh2i=-2& zdLo@g6?>_H8gUmc`{@w#aB0;P%?N#lCA=Bpmo?j62cA~lL{uvl}k>wbH2h|6~}l{|{I zm|evj%}%0bh<&J2xIrlm4T1BaU1a!{a5Fst7K%Amw^IIykr%v*>lS-a zB?Ux|xV<&godm#g?pvC~8=>dtjv;#j@k&XNy@RXf09(a|=rHA6FmOmTOz@ zJ|LWdQGu25sA$>gw8V-uGk7Xy(gCvma)0^oROMF>B&gAcz&49SfGj3H(&!BV3i#+Q zI(?yf-{bYKR}AtdC-!=(xK{{#64_Q-5bYs|c{zEx5j_^xk(Q8V%miay9RQm1U5|m} zsfZX^It&Ju4IDus{p#RqM)63{M+vT`*lOu-eKHWu%!F*Qg$JNst);^1wBlE7CZv-2y0K2oRf z(Mr(midknKg|Gw+c9%x|yQ!E8mZg`ttDxy3x<@L(2~&k6$gZ!Nw%&f?a{PYn^6<8gj}d98kAQ)@zYvPfz$@oFp}0>+S_ysm`Ic$S#jQAP5* ztTGNw5)v&}#qut`kxPQKHTE8-DfEUIwb2T1iH}zeMT-eV7!(bqR83KfS&^|Vc0WTg z`<`uzBB3Z+ReCH$B@@lop=FsaPRb$rtIE*}Mr~8l{YykPM6z-j4jGr8yVfia{W>0S z+uOWKmK8n*pP-0ec|PtJ0|coZHV8I0i_b?}Xk)XgsUqmX#!z3Jy<_y~)E?eiD2I8fE-umn$H`AM)B;Cr%0ct-H zlKK)@3?;f!5Dvvkex4+;v8ICQH-aM?{%26r<@o_jn+K;ysB*aW%G7CIQ1_;7X43=6 zFw8vP;h)3#w+ENEA8xMh#dCfP^92M&j^TIVQv75v8Nx2;7cRx1&_A}})ef+x(2yaY zYEc5>6yluNs-ZmWT!;<`Dm$N%*CQXicaShw5hz+lNkVDCk80&O{xKd%YhNZ3^zba? z6euo*3n=~&tFPMNLMTgPQm%pR zI}#k@1_#GfR-qbFoJL|s3Y-) z#F?t%7MnnBJi)^gr<4S{?ad{iV{dhVW^^>JqDn(af!Nweq|_CJ*TuwX#<6&N!z2Ph zlTSNN-_6z6TjKQPfv^++o7hR>!;il|UcJBjQX7)M7{z2_SXi{xU7n@TCixrkx%Dla zEUj7js>9ONkYkb;2s$;_S92&7tm>SODP^DyT9h1=O6Ei`IQQ0}*{1 z640TB2|4DUUBDJ-yuk1)-3*TNq08mxFZWltp-uC3`IJ{t#2>FO2cLHG_wt!ly@`Qx z-R+Rv0d$RisYSJSr_%2V*OtGyPDCydNwVZeROdfzFf~MYK0Vpn+_vc8;NSDYivCjz zEUvRfC98XyRhsA_=D{4NK9YsdpQzY4qMc^$9K1|KDlgN*M+AXeu8hPBpo$PT2E+S{2doXdd1H zk_Kte!*!e7-||#24OSE=cxHkbxrbaN6+J?9KqhNw7;*+=f@_b?ZvbB{gta@@^#1vbliSHLSscx%>3%*vBQn26lcKmNCgEhP z8jceTK|&X#0yFF+1qB`@lPnp!P5vtsPUa|zi>;cMgBDRk7KstjJ6o|wZ)%oNoF(xA zn&!Uv{o(xj{V&o#Xt{fVC`W6!g52|=V7!6k;;Kr!#5p>zVg>9>vIWRU_ncVEE{vRwe=wpS-@Bu ztHPI@9FXC_iQJG4B5p&qvT%we!k@GX(esfW5l{%iUWRoAcyOA1)&m?^XkHrCFiIo{ zf9c92y%~faF{hg-6V!GmyRz@7+febs6GFOojSJ693C!z$6?`w=SJAjDD(l_~YdRa~^`CsIg7^GlX)$PlxY5<&$iSc{4y|KUxBn*KV$iCy++NJK&&DHdz# z=PkDdl+aPm&X6y)P~+I4Dtrz!4GRGm7O&^G7kJ_i;kV;WEQu*_(B61$f((eYX}bJD zOJk)_=eHL?ko_N49)JIQ_4Tip_do~!{v1{)adUlsM6vYeE3`aUL-enA*B>VT@O(wS zP?N-Dmmv5#qPa*8RRT9sZfWeD2|Uem^X0KMc&OZ417e{0Ze{cWC{Z-v+Js<%n`Txr zHmlx5=v19lfYvF>OFK4|4~qo6IKqKyc|BvZbUj_W)RsOYezVA*9O`g(DR1XB95lZr zRj(Q<*@NN1p4=q3?Vc<$1>Vyf(=?r5p=l+pVNz5Td`e0@g8EBJ1|Dk{446@((TZoE z!xSOqHe^p^<}*2C4ZbIlNAPedw`>MR+(?)qr{~&BQ3j<~WrSndlj)&a{DviNPfr%- zfA-$Q>!Mwoq{q3$GC*~Nrb^{e{W`KvMAIxJBejlq7Dy4o%w5@%8PSOR2Id zc?)8GR5?^gD(p+(h8GR`4aWhND5UT@MrV8@xqSzU=N>ozT^w%W7Eu?x6ahLsTb!X~ z_Q_k3giTPH?M)ag-ps>n58_Miln80C=kYZw}Fu+oZggNdx6=KQvTMzO23 z(|EQrI+k9rfe3!uUIIr^hPV0WdP({;zA4gLyi#rB3RJ^iy&P>0h2*{FB!_UFzu62b@TksvEF?<8W*iA>pTJ(j z&DSN%d(FjO(}9{*@|Imbc5ghos? zAnS5Po5@GvG=d?Y$s`$qBclB|87C2cIU0f6)@r-4aOtp0p2k}3Rs0>1k)BsZRfj~mtNIa>*9Bxm{V!T$e{Q38= z6kVLPHu(M=eqHZ?hA*CfkDOJDJ=d1G$BUii#`?;oNS^ZVf5XD}t%B?G$-n;VWPQ_} zpnqIlkWuxd@Ot#~**j$#C{zS9e|o@s(N6>kAcXqU~EpRw;gzLGXaW|oE5)TCrG_L zBaXS?br@S2H*R`q7^-QkXC97w$*{F`ZE>}R;L*j)s% za!;k>Ld68v&i{bYr8#_kU+BvLok|TsJ&;GXin1xuR3X`U5X;3BZ$G-MXgEzb?*(AY zbvAH4^jWDzH^w7%R;okA`y<+YTrNfWHy3LmO=0aN_|3)96EXZPDiK|}dNWg%A?*9M zg!_0GBy89MS>*erE{+pr_zE(@j?D^Kqh=Zi^4m#B@1JRI7~IBUSztIFG(m6{gc<2N zwA@+jc`70z8zCr)U4DTGe)ynMt;r6B))CvC6O>A&l#XM$0eh)CW9PHWLfS7KOlSHIe-F&IEoji6IK}3~HG` zVr3^%%NWC}LHWFCY z4loK|f~C2XKg8-DDB60liy0;rq9%t}J#6mj;Jf4Z2l_n=$H%bhMmxT*^5RfW;OY9dYuYud+A&VNZSTLQ` zOYfz43jY*u4+ev{9mmZ&tYp5@$T1U$kx-sP^t{M!YhA8pZFIAZxWlN2h_Xh364tNs zdo0PL(xxcKZf`3{XHrx1O8tWNzXP#7M8P2*gt`;8t_p0KQdqJ4A&0KIi}PzMGb`7r zGHk881Ct#Z7+vD*aSflbdM%POaipkHxjd5^4|WMXFsV0%mk?SVd~LI{$?m~l*VfgV z=OoO*y_M8HO7?25*ME1uJpAsGOmStwadSF{dLb>;83XPKRoDu$1k+`XO8 z>gsD)&bVelMd5;4)C>E?95nu+H7Fn#2n9lJiFYdW`6{TnwLullAaTCfg9J8i4~hz1 z@n(4CB5SdTVEED7=fCZcg z{{zyt86Zkq?cSFNW7>F(mzWLViHUlUQalY-pvC!Ht&1QFMu0S%Lmuv0;xSUf>^JU* zQ~*@*6d^tQ#|UH3drx3m;tax&$G3DiU!5Gz|GN4g@$?WC$2V%aUk`0W8rH%Fw&RgW z+-)#8JVkElRwW^qV)OXl6DCd=&hz`nrG2%r*KS*~1ktG49XKhh<6OKRB~eHSS~+zB zi2TsMD8qO#!t{$tMq~T{tj7?AOuE>s>e$_b8p;ggwH43u?d*pXmocXPzbfiKbqy}G zc)}nbWe^UAx&M&jJJ3%Gv63wn!Ust(-|VC_U?(NnbIfGdiu8c0CfOcB%}Hp-HOFDD zx#&%$up!vY^mwh*`EHJ^-)OixQ!%d3O|dA0B7<^%Zs!q*h@A(=*8l^P`k@`0St}jK z1^}4Yr~F;5hDMZ%6`0~khSd`jxx3|Z=k3`!Voqq7!53*i6!S$pp6-&Pl7n`=38uC40F8X5i07^^EVh1`4d4k=w(x=>I zNRdilP=v3O)3f=0XzFCtemKr^l~xn@Pl5r&1`&QWOmqoe-7n+Q`7}zB5)vNs7$ll; zI^Rdtjt0bm6O@O#R$juX<|d`Xih_XtR|SBe?P%hdZB+50_j-wD$oQz zq4GY^s)~(~aJ%BZHBg)U34#@%I4ReIV?_pm5T;-znWBk}$Hjl9x0g3}KQV9xgp3jl zeT_0mm$v=v(w6Q_!ozRB>|H!rb$3Dyi=an#GH6TKox;cNA}4012C&utc!A$?Np#gq z8ipLb97S=-HW-KU_Mq2R-bZT&8j3(L#Gt@)5rKlIy4EBgTwb{}k~_fCfk#)DA8H3W zUhM!b&FiYr6#h-MjEqt$zX{ob$!`3ZQDH=rFcFbr0*%`rD9k|Apw^<1UtbZ-~0OjGz}rzzwPseilz2Pp_r&qL}P zHds;(QZV!d!)rdOe4APg#>us{G$7FemT^%vm|ZZ;dR-^RBl?LSXj_j=7?X^1ll=oj zB(bkG(qA{M*5TYouN=6#LAqa+WvP5}Dh=&Y=|fgVK)8{u+!pSi)j) zj@}bbF<*zGv5Pi-Z7|GNQ|gv-c(BUu9!*gPVUo9B^vum*qKrWByC25+goyQWqBv(E zWoO7?rs~1E%M*>H=2~*cl;VrJD|j4@A--=@dfu2pvYkfA3I$*% z*z=hh@lj?ma+=_)$d3&+wlIg!MA-cg3&0wCJrF-1+H_ri_;7VQM37&vAFh8yb>PR} zfg@k(tgi%BfN^~>{w+GcldYS{PLxL>rTeGzx8e`ve-%bO69hb5NJoOUn6i6`bome+ zEftQP3EVAD-^!oZkNW<~3k17VI;j#%V6z;CS({&_*Q}`EJhwvtGcOppVWq1Cr=-pe zegf};4LR4jp%^s#9#IPn+Nrk(i0R zLb=n>$KWF1>f9iH`rV1T!3Z^q^gtY!Ex#;zl2Nt6$y@}r`*YxD> zaqkKR`D(A<$c~B|s)Vo6xZL&X?EL;2WetsDjT?Wr+1w!n!WLM2+s}qBenb(}SUJ(3-dpS5lo} z?VKXJLZzeCR25kM29CuKEwr-04O)q1hw@EatzwE9y`-KeZDi-ypRVqKD!gz=3P$8Q z{*!NsTvHY&^bRx&ey@SbJ2DB6pa`)zUG6U5q31HPjwfrcU%ytQsRo2rdLdPb!+er+ zeG>vB6gEPGSACh;d%6?2aluxet7%#i7L_qIaULPKkak-Ir5=(1 zuwrvWwB_>S-TkM_$K~byPb(ikJ>o}4NUU=pEBG{s0Ck;yrGV$n#qChWK^kO#P635I zyp&~W6XawW!#Vu=7$JOFykL~m^q!O}>2@AG!U$?4KdxjUPP^HwBP=ak$&A(ee%tP@ zKZo1Mn&`l|rb0>^-{T7q#Mc>Og#Yf~@O*|IcJcSRPdb}Xr$ChEHxk~G0-5E;1t6)a zl4*Q*itfQviUb>D+I#~h`=wNo--&NQsP(Wgt=`=eA5$)at>}^U=kl&5U-9ulLA}&XL}0- z4$P15K3pkje@V1kb+Q!z6LX}Ndre-W0y@{uXK@*`*KqWtvp$$y`$qGhv}Ret}U zU#?ND_5SMpm-`2_Wm>Us$+JKu9C=BF4%BZ7jgV0s2>=D8=pQ)-QXMjUF1$}_8p7Dt z5J@+bH`V!_;H$(pGKr94V!=6@I~9uKL62dCGeM}u86g#9(INEAYQR+T+qc0W;;ofa31Gr$4-x*(9so`u57p;tw8Srwg71OiT5 zjdDj6NtT>Uni;Sov3Wpd+0=+ZhM}zq*F{ny7s%VZ*oBCsX04=Hha~luBs$p=Gaj?p znRKn*(Nf8fMFRz2bgM8S+}-}9H67Fut5IJPzVw@vw5VG$_A1r>8hAB%C5|$F{LOGZ zw?Z9N{>@CAgsDn78iazIz*--AOWs&yu58CX5p%QV(MDQORRhgSU(p-`e}#)oe-!Ra zVx)|`xcBss#cxNTGJ(FX1%>7Lu@=_$yDeyib*=}5@}(G=!6EQ_YBlN0FFJvetG!T+ z?Fa(ZMCK0xB=tkZutEx~Kz_by`Igueye<1x3QX60k0jD+RgmBuN&nly$wHYIbkoRL z2p90~QTZ4b-;axQX68`rO3u~Xs!_QkA<$UXV!oXtO4?Iu12%@^ z1e?iK11-PZw)i>@XS>UnFHsvKxmhZ(ogtQT!wyby9}6rAY-+y9N^V=MQ6`|0uqOwc$W zOBAD4%@CREhjb7cRK+-=^jzOeNwtPE$g-^^Rb4O;y-H{>SST5a5`QEj?nvBfZC2BnKvb-E+l+T(SHFgk zh9CfV$zaaKfBvo38B-?kWf6J>gCVbFypOXS#;geTc;b^ZFl{} zI!vptHnv}G`#klte-dwx!y3*U?9X z$|t81k>V?bSxcl0NL{?C3Qcu8Kp+#pgT2MYkj@M~oZvAZB(&6QHR)9=;B)2JVb*8w z03$)gvY_VphJqILU%;X86H5h_U=E)@!b%WIN*A);T()yyJnajx)eEX8AEX8qH|ztT zH~)@q6M_0zxw62P^Rx^>CQi#s-p z?}F!NF%1Q2|4c2~e+Ivm$j_Cp9v&`Z<=I}vS*t3`3bUd5Y_2g&Kq%#nW(rt{EjUa3 z!yU3boS*=N=^45g=z);222IH_=d&~PlK_tgqWW)soAl0(PU9PTBz?P8S4JlB0S#^J z=?6N+uAcdMmn_$R4hKZLEznZb5=KQERCaV*>#Bl`*b1P)8*h0V8D7O`qcot3VQi zGr75HRWIH_N}2<511>hbW6d~MMXd1zZ~wU~`G_QW0{%g4JWdiV&R%C#MrJw}7ZNJ9 zE+w-k??d6wSOmA0$t-qnS>|^494|q1mL#L;R)vG2YGHRmJOLq7uJv$} z$O*aq^3c<3^mFPp)I*D0#3oj8?Ifwyy096q-Vo14uZrKU&IqrI3P(F10fqgU8i%S? zNGpTp;F@Z_91ku6xCPJfpq0z7f0UYlC_eaqPgd~jf9JpdG{Gk$Djb>|6ZOaIRv@30 zGf+?%Ht(v$2obOZThZ?tq#dzAYQ9s%2q#Q0IW#mQ4V@o2=Mn+BW+9-kNgY|4K?5j@ zNq88%{5<<+3ncSs9$kEb(Fjeo0yr6`MXEoC1sHvt-(MkT8p#nx!(O>MGn^3+vXrjT zJ+G|Ove6*ax(157i2eBUmrp;gLyY&H>6P)~h^iD{Sxo*VxudvJMLg=p#hGMk`AL3?GCihwESD$m&|VHP z6sJq5N((Sel<$N)`{GRGqs8*O>HOsI8Na;XDGX9<5(yjMsa3NuYg$#}pmNhs5b*M)$%!VjOQ20Q+ zmO@%&w^?Rr^go%v>!!{n)NI?GSvVhmC&15;!2qx1@xgx?=M&+}!jaD3rhsY5@J?M* zWEGJ3j5s{GmO70q64G&q3JRrC3coCj%qsP&r$d4&?Xg2%elDYYS|+*Jwo8CocGixJ z5!9fILqZ_b(mowA7PdK_@v;!R^-4m#AwJ z=}P25v}o>J!5;j#$=KN4-!$l!w?5VyM3|U`nVVwfM~w4@VrqnL?Qn>Rc% ziSKeC+C9l-RTwY0dx2|z45LB$rHg7Ka}~T@J`?O)5zn00)Zs)1{XTBAd_O)TOwwI? z+yzP4*&vpy_(94zzh*d^!)-I7c{J7T`5g5Gj@~X#FW{%92)^#6Po-XD%Z-fTP~5(@ra&JSNCXk@!QuAUw@)srD@@|TuRF~ z%vf1@K#jFkgw|o+KP;EONteIh*oCjQhr^VwCv6uvDb0tk%2&RDSLW{H$A_y&Jn`Y{ z$*1Sf|M`D~$`L$xZ|YrrurFTxqyGQl!!Q3R|J~kyeEE;>?>>FLyG42Phkv}e{_+0u z{`%_SAHO_a-^lP1nFUw(k1yWCL?A(Z%g6iQmzRUEKgRoEQTJC8M|}18hjWX{FG~dj zF#{h1fFdy{;&L_h9|ZaM+ChzdhHK_!PR*q;ScahBS2Gv zAOOxQG~ZP^A0>zC{wUeC$?Q{PxH4S5*rz8u%hMC&ZMU+0@gjy9ykAPtEw)1LtNd3$ z6m_QVOnru@G94cxx?do|U;0n!&(*sQqXuRC%_l3VXe6P>Ef=3s$%Ss{k)(Eu8D3a} z{}lRxe1?cePVGTvZQ;fvMkaB^%l&s+`~fNzf?sFTm>9M)N%| z4GH29_-dR}CC?)1x>ZM`G2=>#eFiz@2#(Ok5XoC&F^|@rXKim z2mV6jj3bG7F?q(Qsz>1$YU8(+H zapoiJOHmfU6Cv&huR~+nM~vF+eY#raBZd<%+mD}MsA-DTa|9<=Nz5Mr$$~M3M`{(=XUoRi8 zA4%Of$!QWq7d6=wOZ&x*8;NCSf*QIhfMA&A# zZWPN)DeYG?hN!<>Q&-9q?o-S1En&^KZzyZ`t5SzKo0k9Y#PZKhtk$?puZVI6q)F`N)`%CLgS82jKDBUrjge^pD4IW_$5%tD!K!qi1HFy7 z=_2Fnwqrz(!lF@#V0wo06BD`DpeE~KdirI&bk)xgf+m{xlm_l$3g6(kq8c3ZzbeEF z5VMXJiFgDtl-Cshm)V&y^#;ZfkcE<&aIU^}?5oDEB2c+`Kz<2IHlq$iR>`=;qW41m z&dHO8UlgU!Lgb?fSA${2Xvpezb5Uymv8PgUKz?AwM@0ruzQmS`h`*`N>A_Topj5GEZ_kw@nlp2DEgw%8Tg`G{V=7%l14US?ppM|S zn0@)(WFa)QD?0X_ZJsa0;qd+v$=^g*IK3par8A7jB5D+H-9Owvg?~i)0T%kB-)ctr z4Y|iaaMJ<=)nok>{Hb1;Vx5*dmUcoxcLd~Bi|c~D4Cf;&USKHqV~%(XIlV7SOWBP1 za`)#D06@h~X)zv>qEo3?wfF)3cxtaIJBq=uK2@@?vZoz>>o*tiLnaL#zuTXqjN8w5 z_m9h8kxzDa|3D0Z7!)FBC~L$DNs+h-wJ%m&OTg{-dhgBRa+Sm}bfhLhOzcxI$ok<^ ziz3j7+3Zp@J7QRhV3_c{s%W7*02zgZ;N&{;&MEyT>d1p6yro>yrWT7e@cqq~4_A?) z!9Fgq5-a#>v>i7J6jpO&9hT>LmLus*(`A?oM zRs?HoDY(wxrPMl!e}GuFY!=tLxW*R*E8l3ezcb924OwCd*sy~N%(x~>=KI>6!pBpr zC5Yh!Kv5GO5owUz@evC@$CHJ!^ta^qiL(!K2Pc17Yr{(5I5XRO<@+QW`DJ_%aj75y zj#@B45(@J6dNa5?ll}{yA>g55>3Yl-oUELaY4T1E4aSk)7EK;vaOVE)>r~y~P8eNL z(gP{_xDAuz!9s67seaMwI>-%1kqDNM$S%K=DtwNn8>skW?e1&f2|!WV zY>_9VUDM^``=1fz`A3nhE_sMh5drG5*rw$t>T3eKmu2i)9T2!=Ljf!z4h@;drwddi zf_=kL!!>E)M|lPzXYnR%4-d$BpawH(x&5+3X|D2E_DGa0j!aou@$9y?vawlwza)

0~LcPd%6>`&o-_+n!OW-V8pzhAe3_2kqhpaVcYLohB%0_>W*c3x`zNiU4h) z*JvU7Z*cu>)d*49z1~R)LU+Kca8$Txo>Z~cFu4U9VPD2Ly^7tG`=-Ybokh#fFC3f2 zM+n0-I4+J*v9$T_V3=Q$E$H@Y4s3Uyok`^Z4Vx93p{B$hi?hk%uZ>tvp%T?w0?XG{ zDJnfQS@ohli~Z{KS5!9of?D4yQercGpIz%&lx>gABItd-#C3dlM18_X zNc@5#7(wYZ5}9g*t-rm6xPra^wz~G36>NkWIK45+-RM9`qLP3px!cgIYe8>>O}xLn z{djZz3#vM>#LL$orPB1TmvHkuQSJq$UhJ#O<<}GY<|KZ#6nCHiS5vZs09QX$Sqcy* zZ=%lV!LU`kz%?56hkhy^CsgTX^AO;e@pxQ2W9D6HwiQc zDF}$81~EB5fx`nDBm`rEUub8|JIrLJGeI#yNLcALd@v~oDVJUjO#GVF5JFoWX8z~K{in}6#YQYQ$e0O;p#*Wu*Wfv2oQFmyF|W5BD@qt zwCw(Hj}WVe$k_~B-l4%9ejRb%R(JS^vl&Q`3ZN;3G~OW><6aNauHr1eX^_S;=On^a zb2}CqG5smO*0lPy);39-queMr2VyFAWQJt1G8}@W>>wTuA^zI>k>hX!2IuJVbYB=j z3$We{f=lFklY5U&PtO*N0iwaMIm;?8%G!8+8(4^3TV(8#T+1z}@f>vxq+$U6MR)OT z!PYSDMO))B6cKS?d5*K(I*JTNg&%cI$cX0brf(Nwz*y6xQnR*Wl2J*k?h>g488bT* z2$L^~j`8Y-kk>lzAIGryc z3M`;9O}4?C=erAOFT!ZXm51k(hsXObD7b?fsVF{CT@@tVdc(w3kvA#Vk4tN2ai>I- zLf*BPts()`UObVpHMTR@dsR}1azdQOZ$|+n_Qj%Fax~k+!K3;de_*}| z{}Cq(@!C-0rPk8Oqm{G=0P+K4?j8n)=FAQK7Odi(EkU&nKnCO7kkT_#nhw50{Co3U z%{qIzoRWRpj)H9CRV)3cRPcI*%_B`o@yk zi+M@Sa@S=V)1eJ01GAdMZWsy+X za8GTqsG??zE?0yGjM?|714Ag1x%<(+o2BtCdYiR7pfYQDM|WpS@k4o`I(w{+nolCy)Tu@`(r` zxwlXS`0u;Dy~zswu|{1G1}2s1+EN7J8%7dplS+&T)IKEe79Q!co~e@1HA955?OakX zdBFlG__q}jMIW`LS03@BG3{T^STA0j#sem9Wr+=e2mS>8YJX1ox)VRPNW|WojhSwp z!mbO#wHgDztD>Tq1cfPjvu-lT;A-ZW9bM2&LM%omlX?cEG4fUxlcm@RlX2efQ!}}1 z2INp^Wtq>pDPyElJj_BfUN)|SqN|pYR<&ACUc^%~xrm67E?K=Ah(;>%Rq9fItH)H7 zLc6;Sf6+%s0aNSAV+%ER_F$N+s{|nyaL6ap-GO^Z=uR@E1*`2333^JyCHZ&=Y>}ER=`eMY%}1${z>$5L#DQH_bXdLn7fD%!-(N+F_HkV*x7qHqB?> zD*78($L0{c1~##$slr;}$iG8?!v5Lv>bLh-pOJEK`S~*}c!Z_MSi*dv^FvcVxo6=E znw1>X5h+~cft>WmnDe@ohvSp{f?yIiGD7R?d*YW1y)VO!WRPW8WTi-Z7CAmZclQGJ ziFdP=_czGJ_}Viq%8dBAJ{20eGlIwms)Q|wBYv!w$uNXsxj#c~Y-nPO!{a^l1JVu( ztxi4?2x0GyAXmY-L*>z}6J*2L`Q z+hC3L#qo&zd|#D-M?>e4NdcRSiMRE28*Y~w#!^3575_y8#-&~ptvB9;ed_{Ual ziyn@1;ToQRnC*RZ$dMq0&1jiZvhCYf-A)ir$z-gu9QG%76?E5-I)8a|TEGoMKEn*} z57)p+bqIvm(Q@rSdHQ4#bT-0>g8$bKTS3~y7VS?lJv(zpo)mz`7WYrZe<;UwhJYcM zgHEQ$v&l+H#6>8{-95rRKHe=Kzn*+vmi}1?me6a2`z7TZ&!F8&X9{Vh%hfRQpfC8x zIFG)}xe+9$9-#^2n$czetqFBZ>_b$nqlnQ&vZnFLJR%pbCq^`<-G>eTpCFA)NycVr#k!g0&39@;Wq-16b=2m5L1Sj?Hh>rmyeJ4*FS!Fyh30A zWO*NjhiUcBHA!qS7oDLi)iijy2bjf zZqBCW?_8FG>fJwCg^Rl_a+z0G!kWyfL8Nnl%za&k2yS^sBIb zN^TaBh3Htg7YR+N{Z?gQm1hzQgLsBaSnSROwYa2m;FRHtVk1w75IAV*!7>0BNS1+n zfDO*xi7i{@zvz}D_XeLbf2@X4S7#20#yU48rEk+(t6U#qq2huahGs_mM{)K?`pMg)G?XF2be%E zH92dzZZd6@Al?t|V1T`FDov^gU=(6Xq>~2&+L<-NK#ReaiETJdc*KQ17Tw&nW{11c zk7AWBYIWYR{^gjs@Z>&0pYx!*$eF0b z>eLes-mozw2vfMBnkg#f7v!O?ux=wP0C53&2qG5X0Nem-Hz%m%@Nw>*o}u0ce4UHm zZy$f2SlIxR1<&W_r*9d^txfkWrPF7d&18 ziMAtyu_D(8?QH|yC>zp~cw6DakwnGkbV!(D6iF};?+DQg!g(`_5b4?}M3L^5r#lR0 z%lwchr}ANbFO9dy@a?F=S}$ZI#fa|%JX18@1}wc(XuU%HIa~=s12&ozT(H467x6=& z&5sBb`2gF}h=hUe7jkLh0Tg`X5FRm|LkBrSQkK~4wP0>iWU=UdkuMCE zYd&$k;Xx{5^rmqoJUEF3JUwDaAbto;REV@#@LFG_ z{?=3)kt`ZUP(>4MMfV^^#;q50uj2z*#cb0jH zX5u_Vjp$q6Z*JGhDT#kpxZ6&RV@M2(8pJ9K;ol}>&=(V12V+qv#OK}^N)b*iWLdCS zrBE42;J{YeIikFtA};4{UKSr0Nts*#rz~k-Qg};Ql3v_?mS$%XH%(-!!sceWClyDlE@UTP`rOH( zTA#ZLiU2EOslh6vhQFEyv{vA32Dw&uZsPwJ@VoOl~mh}Mh;(L1!LB*a7 z1Qo(gW)OS#&yvPpaUl4BRS;>ewg+5tb9}YJg^WzR1s+q7sqn}op5MUJKaesSq-h22 z!LXtQTqv3sJDu+y9G-)nOOTK(T*$kB$+#+l+7cq_wl#Jc33_L4%b^#eI6GLqlo@&& zvkMoD-SX}fu8Smu;6gsQrLDCA7>ehl;j+FIxEYND3f8`z_SF_Lu|d6oIUCd0W~b$@ zYq*tSprVkFpC}4?OR{{BW3#xm9+QR&tflt6I@I12@#)e`E;$AHwaaax5C4vwg#daUr{oU1wU5Qp(@a=vV)as;CMm&z>GtYLef~PMIqrq;>6eV z)s(oJ1c*u&)Z`!6UdRH6On8`oZ+8*?S^2FLiZYTwSxs&v<*d;vx{^m3XA$aTMT!ii zV0ZF0o1`G(Z7_&b`JsO3*%>pOAk^4}q_8V#RG~WVv#2qx20Jo|YZY6#*|EycfLidx zEf?wno1HBVk3jsE5>j&Z&8vgA#Wd-Bfqn>_KkcPe+LcSF(r%+PNV*;pbkX>*dHD9l z!CcEFIK}jzL?E(-d|lc-x1-!_5MU0ZqYut*_Qs223Rx4jTj++kpPBs_imTL6)Z^*N zZZ!|Vffhwx*|D_q#IBo*+Asv4#N{Dd`}AZN`HQG4{`>REZ&F9Ig7$aWbGDwfm=nXhgBzxrc(vdr#4Th z_$$R@G_}gXBC zE*jJsn+uUSa^c)oU(dRs_K*(iSmZpeo$_#bw)U!Z5~Sj}tqZ6@SiJMu$(k+hO1On; zc0ABRE|bEYPn0Ahjq^;!*>^l$NCG$hkyYciDf1piSlAQY;xsmm4?cN!ybBhE^j6mE z4Zq!AK6ueLDg-=k$fY9z6}o9ev|Dehj~}zGub805%MBK@Tg)t=GIowuJ7!pyl7015 zvRAXuZ&BJ2wdoC&8Q4;CRQ);?>s&KT5Gnk8`}E|rVzESLAcD+WqDTx`UlnX?`ZxI6 zlJLPW7sO91u>%Y$lJ{v?#5f=xc9&w}Mn5^J3b}`U?;vS2Vgi6R6vSYJ)#~s7t0%{69v^v)r zJAt^cCo{%eU;s*REF=Q)Ar<1+f zpm$z~O?oW=?kvMW4ARa;4TeFa9@Hxf>Nm)@n`adZsc(&l91YK^+*E<-l&;>cqLg8X zX>8Nu9Z5@p?&lS?O2$I%l_(2=KX*XYU}BQz{0SSAPa{5%WFJByMV0tkZ$!P^PV<{N z{2uj2k}=9=rM7=Y#j#m@L?yhrH XH-5;zSG3d%>8b{!+UxRq!#H0Q9;l05TpXP@ z@pN6TlyToFC22=0RYXQ-Rq5e#nZB7J91vXaY%}W8{2|TE0 zp?hT}ZhWbVEr0@J8#tnhVk;h|A%cM01J{!*Z2QsRYr2tzzuGI~jVx-B3Q@j5BdQ26 z3juoA`$)rPEfYIWb|1g%F9%_Rd`SoddYR25UEx=2SuL)9y}JGL#jIXwF)M1ExJs$u z8jYU=v*eRg74{I;ONHk)*Bt^#rCJMcNhezQBK7C+@xnHE5lLr+(Gq>A(>dJ8$g-8u zZl8##A-J&K$@*d8oALs1#4IMsE3*8Wt@wef1bL53)VzW~PUQuzm^SDcF|e>cReqo# zbF+ni3Mx|Kc;Xoa*NPX!g$x*;`lSh>2PV6B5z6?!60+CY14pOv^o$7P>KC#ruKo7<3q-qX}QX9KT6ZC&yq&>V83ciW_!69yZ~4pDbas4J%~iDgHhU{|j%0J_A8JBfd*w%`ai=wJo^?rN zmyqWiECN)3t?@+pp&M4JoNO@%U@sK2g?q>^^0O-!fr$)fa!Gu7RRU9~i2Ul9nvCZy z^nt1BUF$wDWEbnKMey1ueO=WrI2b$Z#0m|X_4T|i3heV zp%8TK2`4%{LvRS3w)2096 z1a7e1xdiHr%gZRHn#964Cl|20&u1W-Ne(QfCeGT{^T}VpH~7i% z;R$r*<>X({?NPq)yv|sW@8*zU_pWRgrO}wX$fFRy3k}ul1jAlK{sJG+#G8ETgJD7h zW;HBMjB8RP1+6A0@;d`bQG$$-NaVH`46=u<8!Y3;Yquq}g23MC8*w33F+gRU=^o0s zdVZe&#OpUd?0h2gQC|Vy7Syw%@kD=}9N|FJAtOeKt0d~^>a+gP14*7is6>|JLKosG z$593PPkyu$C(53rKSB+hS3(9fZDx>PX07E@PuDk({-@5t$f# zSymA!6+cM&N|8fXpF$wTF}EW$h=x15sM344a$7_;<2Ma}dNT-n(vde8vE$R4^^qpK zCEGxhAjyvd-9ih86Zl9LNP#jda@OqoGh9gsprXQrZHSj9pKm2-i_`scsXc;j=Ms*s z2(uSMvE{-nPcDve`PZLEr-B#x^PgJm4;CeD1?kdgP&kB_(w`8q-j0-x0n*%X#`1TQ z*QH&YEqpQ<_q|-gAfrkj7lsM9z826H9UYrxpciSd;(Mp~aM%h|cv?(IHYoBLcQt{J zsDxk~KlYN1oT7Cg8T!PqhF}7u`x54PROUkshwqo~5XcN81A@bBEoA#J#&<;D`f0U& z%8?asSwGNe?=ks&EKF2s;D55T6eckXd2Mv2p%oVArcws^vYs)p}#To zf4>fE(;uXP-%IeZRMYu%_u=Zn!PjJkuS(Wo0HsFPm@9I4D!)H{aq|TG{2o@p8(a``p7$+2~>|u3vq#zE@Qz6)feYS09 z=yOF)MtI-Z4;b4(RqNAIppmE4Z5eCJ>)0E+)45vmDxsIP#VM3g0}^?S>JQ=a@`GoZ zuU)vfT?@w*bEfVdFF(KjG?w?P-`j`8!Cio?tryrJEP2ix1TrIFvYk`t1 z5t6%NZfBBh4>=+qaozq8+a&0hpJb1@^d} zf$&EeRZ%a@;n(%P6tj8DO9#V>t(yEapPuY(;u~-TJdyl_2>TbyPhW13`lM0?1*RAv zKhSkm|DMRi7f{4C=tjO#>ytJj;NPUd$>TGZXH~Xx&zi++5f_SlO3_6_00Yh72O+IU zP8*aR96X;$&MeRw1PxVEl3oF#d05MrFQQk_=^2zVBoIxIlsL)#2q=A zia2037I9mW!;Yqmwq!*YS~k<5r4{BTfu6{&o+K%uHlN3>MDS>=`^LK5U+7ckGg)%X zsVl<7X7xu!h`2yehux}i9LyTWja>uv%tWn0`tp4LC44KWw**9=p1heY(MTyM%0h0G z)-*(5sIRP}#E%()dH;&7TVMo}I7qJPRtpN`;n%teg#AV5~@FR*Pr0^0(3k=EEgQWdim~{pa$hm0zxJKYT5JJ#Vsh3lTLt zN<@06qB^8yRrH`FZjWzX+FRGQSk-m`=>->+9b@f4(>1GaBirr1%uQEMSPxKFA(=!g3>~Q4f6^u;c66E?3 z>uE=kzs{8S$ZavV6pUyTk11BZuX&|XDNO$-+&;d=)rz&%r`8f@aWPeYhh8tb{qikZ zD!@)?J#bY2FFtn(y7GQlbk1=3oW$Kk`(h^B1)mTW;;Ld?zk{+4t3ps%r5WSm%_kIV ztV=Z8WLj%|RpWQ#yB!g?TDfLq5+N68lOPj=q%sopX7H<(G_(Rv9O2Zsf&!vetQnc4 z%Y>9SJwH7@M9sa!6I5)3ckmo0Byh3s&sO9oG^Xd1ZzibZDDcbyDlIPcX7DvNo>^kL zz#%23OMb&>BhMS9UrXUnJj&`;MH}0@zuEVuQX|SL2AUjaAMwo+%L0>*QSk6Sq5Jz@ zsA>0yUF|O|>p|CyA-AS=(b%Ngd#EIdLP`b6W$Ni%N}cDEerhHcDQ&%ey@Dip`%@`& z?RuTSLvs8@Lrwb4mkHba48pjACNm9MPi0@`ttS=vya z-2->eIQ3N}-Zv}4S1j>V5J!mHn3@tTfl36?w7kl8;nW3I^eHaXD2ZUWdVu3NWue#X+IMM2(_2#`V#*q z1Zfr-+yyNjZLUfy%~$-(vZ|hH%2h+$pq`>mZxNK#deaON7y_zPQn`g=8dUfdD>7}* zDOcQIA$TIHQcSkPeK6yZtiJSZYZ%J%`eFJ0x633r7U9QKRi?kecpznmKU{shmUOiA z1;=+N*o5#J6{T||NFl=tjn`%;lk&%3rC92Twsb^92P~2B&VfhSZ+oZ|fSx%#BT{zq zF;X1O%FSmhF0MDz!;`uFTPTAbK|8ExP~$t~`k4Nx3#SXT&0}(nwsXgR+BM_1qFj6-S^@>{&PYW5_5WmXjsVfkoR27%VhKfA! zq@7WA%n5v;P<)q3a7Jp5HXzs;4evhe6~qFh8??qd#D&yVA*fX|8}&!ACdv|a4^Y;@ z&eCp^-|#Zb6*AC5i%CN#$70V;=jZa<`sV85)8`xO*Zc(EG@_uzG%ijMRS26nvf7Vl zNC#Pg9BiMxgOn#30QjgZRmpSAy!Q(!tCR}~?LQ{$_R4u6jb<1^ns=M2p|IRsBOzKw!9_P2WNSt+s??>KjU>QDv>*>C-Mc*ClFx@JD<8KY-_Wl2d=?t zTbR46K0|d;r}3GTMaz-8q04z2-3OO!JBw^=G)OdQci zx}RI5K=rsAqY{X^B|{)1e#fep3{O)`^`&MqgFVPbLzFdMB^8C$g}S0LPlW`;C2wt# zK@Z&c-*Vo;=jikrk)^4!joRhW0kX6m+JJ6szC($}#{m9sXpXq9pYOihyDEaCSAq=* zoS}K-8s!whSt9Ei!7G%*FD2o!zzM*B9>qsEPCIX0&r!{{S63gF%jK_^%NuDw3W_1_ z1mVXL;osN4Hfz1>F69s7!T*iWr9)&7yW;|K%9!)GK~51TX&#^L!H$(_bRHwf8<(9# z6YRj@1c3%EQc$Ou^;6ELZw}!3MHd7?IDe?0;@C7kzfq>=-=ClQ_f2T(%?L|t>&^@` zVp`Q|&1^BD(t`{tDjsDf2)9ujye48~ffZ3Wfd5brjBjjY78mEj4~bI)Whz6e=P6zb z+_@&{9R-m~Ss?WXv8_dIS`ZINZys2@${M!v($b>0H#?gCz!Pr&yuZ3)_BkJ%kg=Mv z)pQ#vSh3G?NX6lLox!2F2qhiyd&MXZ?@fz?1_9eltLzGOAvlhuE13MIzsP2}c2&pE zPNfIT^85gb*TL!0-tyf#Vu3YAQ4IJ{fxL66XhR|5TSm+=idKPLd&$Py(cH6ANKeR# z=BMb?oKLeg7HLUs;y^x6;6Ox`A_)XiXeXU8MUk^gLwN-;ZXz?%Rx;`ES~|l9A+u(mQRBhk>Hf|>qVz&k zCV>J~W;&n6vKjLHf;74mv4dyC8JW~l54&HKT;HQg_T(=TCkQA09PvDs_%O^UFFCadt`nl}-r+(FOos@|+_`u& z_|t<)m9qS~$CSUjOv-V=1(n%dvI2QXQv`q=i<&md{U$I#IMrG54kkJU>Th9^b*BrqW=T zU(zT__DgL8DkZ#92F99ZY^t9l?xwgPkguSeI(aDkjlv5Ykx@ZIT9h^@3B^}MG(MU1g9rI3Z3n3yoC7SHxy zOPJ~&9^rm|EDj&^aj$+8*|vNwzxPk*QM2DJ-#@;)gk7GV7Evh8qN34{Ydv%J*eu08 zh8h*KMW`tm*o2|{u<&R%J#^VAL>bN)2 zE%ZYnIC7$(ImM7<1_Xiv+I|@(%)=&8{uepb`tQc}CaRjzmZZYy@_6U`^gFAq^+c-3 zryn2h{`P+PAU(o+88?~jHpini=G-ashVho<0ZMjQ=TrWMIEG`xVXnM?K6$U5oc?TV zEdb~}WO<6pl`1O8H3}ildA+042utBn180c_8E$0zwv$mg3n^cc-_nlq;f-C(3%u6b z9!`>G5B(bPLcU+4`be_KK_sxBa|pq{S)O?+fcJt{fgkg#wH zp+&;06z&w-90B|RI!1!RA+Z8MY=H|wT1q#Rx&3mh-wwyQSLBB59U^)GvXrD3g+HaN zOr+JW?X{M9t5JY>N{fhCGgFL^1rsD*OFy*9?!jNtiWR?=+i5$^1^a}1FnQx(pYdF! z-Bqyq$rmMpDibLxcWNb)Qhx$3f|sK2I6^l57C{UpgmX`w$*-v@(nz742sUSNF9#*# z?H*g=Rws4_VDV4AM72X%_PgxD=v{Ws^rV5qFZ`(I94dk-a6Gv`iW=l}0?$f)@gcrsiwH+^QK$FE@xg+2PS* zq`XmgBzimC`?4`xRq&cHHx@Y3739Sd%rOEBnUrE(kz3NFUlvf;I{T1|rpFsKakv1W zLRu=$5Ho4QDG|JuD$zDY;MK)~?6x3CyUNcb!lG_V3v49mox)4y6cp}K2_)=J;kLxB zS!@0}-PI&6O{zfwZU_DPW{lbx7*=TbF5=3Fw>Jdt=nO8S?ZeUPmBVpA6ui$&naI+R zKfDa7#s9{ScytW@EqE&cRYd8O01_@>B5@7)UfTevM-Rawv*h8l3551mq;hQ7w(d(UB2ZU-kN6cl@1F-b4cl-IuP>m z(eMnr!oFYVA5jw2tF`bFo|#@pa_n4eK#i3U2?#w8ZSU&iCDME&#W2-B#d9OQ`u-F1 z&Csb1S=94yZzuc>>Wcnp9pX!MzFMbm{pFSvf6{}hY6yVtHRdw91XU%>NzijlOPL(Z z^<*Kua-ESJ6C@x>06RgIFg(?Mu@@T+d2%Y>-(J#aCJ&4o;-s5o8a@$E$<^9d&JHL9 zg+;jT;Xk$P3)4B)wb0%;&j;;Y%wdH1!)%=`gCU7@4f}7(m=o#wHX6?*lkZ}ooxUXV zH8?bq z*dY!$=~5$^NHKLy|C!cF5lnClYt0YeN+2HE*}xxgAORfd1OXv z*<3zk#19#yEYu_}r|YBu_0VL7GRREH2!3-KrdHR_Fk*-LA*4*KP%A8F^v5iKQ%z~WxxtDF_< z5DJ}2j)Qt&%OwG`z$=OO22Y-M_hs^?vDyk_`f8lS6{fwF5(W)*ux7)+O6Rm z5Mc9Pb=i7v3vprkNtPUXGw}UEv4+B(^VjqKYIZXp;m9Rh$IZtDt%jHqORmM2^9}<; zpMMWsjsKVjDEbq^pm~d)!SN&Ea+7{Hp$ZwjW`!{BSZ-WiN5sl<0ib=4&vM*F{6<4Q z9E0Rk!6o_>$uALx2|*(iwDGAh5J(38eh~vL(V@5=;59kAe0=|NY4zh&%CH5+ zeJZy2n4nS18}b<6H!$}%XMt$jZq$mPZ$REIL$%H*>6N;X4CMehM&Ttn`Z&~3m?=z@FkZ3Q=?j1zL|Ds>Mj?h=~Q z=F{%Y;8%-os&q@@T{{V#eP4}55mB0t3M6sCl*qh{u~}RS@!ll4rh;LE35{hCM7$g0 z=Qt)?0}A4HC+VeZ-NZM^P;Fi$SfkcMf~Oz4*i7JFGTnfo0H1>Skd-AID=bwnNW>1O zN*sw4qb)m7sf%(s_cDM6gT9QJ;B}0vm24gfeEJRMko9CHcGV4KcJoQbg@#G?IH=vI z85&yDt^VA*n+mK)-UhSGDZB|O+=}>CEqg8C2g7`B1f-w1@Pzc_ z!0*kMI*O=V+`yI|c(W_n{!^9h4N*2(?w=;HL7W`KfMCA~>yh_039=daH`_}^IJ1`G zKCbz}QvbcUefW$feIKvcA3c?eAmG$k1O5}1k#16NZNez)C8-Ess6UM1xKcs zn`j{b?d*r^qlTEA?CRQU8d5}*PEafK2MVaaIj+o6vJO*JFOC9>_E^QW%LJlOSKi#D zAfs5(KFaYF0oQzzsJkUbP6SUy}`{W4k-so7UBY6_5dEqxya?maY2V^6AEc5jZjPv9@WwA?$H?xMp+ z9fXM5t&VRsa8W+msg3ZnHfkb|k={af=!KqRb))5KJn-lJe}1_}8wvCh{c`_+7VIk$ z0VI7-?ynv%?|-^_w2Eu}d8|9Nf3^#~cq$n=dS5^a#H&*qM(7xqs!FBX2JC0`MET|v z%!8=Y!7!gmiN-L)gy5il{H7{3ZP!X_*f<$he|*^tbQHeI-4#P5$7rGxHdBUNZ_ad~ zJB3Rt6vN}-#L<_(+?~>VSxJrb#TolHY`jheRn>AZ0=*E*#;s}sLL(5k zE}CCDGKudEH-${wlDuQGl2JeUQ>7`mayIK0j5H*vE6N}!cmt|&>8V5sAj@$Jwjy$~<%#_{3V2*a3nwo>1;FtB@k~SqmaeG$ui#&YwB}Xy z3Z%iJ%K~W)5h{!09nuYy>?XUK{W;t~WR#VKx2o>gtQHCM9iOT4CO7Dclh>60RFZx&xMozJJ}cmylw%H=)kx34^0|CcmQ z;~)PxvA_7|UswMFDdlo8_mT7_SJHplUZ}B}6fNOn5-x(eqqHY4_7Eq#hooTq57&=u zxq~7QiyPe5r1||B9G-J{<>o(}ou8_tS%XYpLr41j#hvJ?1sbdmzS~D*=%lq$(c6U& zUJ9x)9XkC1u$V|uLS_VCsxhxwV4CHDxXm1?zPh%pmJF5`mhw!Eob5nV$!j>SI1E)l zoW!OI&|e&M0sbw4BwjqXM!ZU)m*tzw>)W-Zc(7HpHiL-LpVu#5|Izn#A%>3H}L^$7| z$dgHA<%1{d|7^mZCx=`5yjq1A7}HYeLimSRyfA!NgO{9w(m1e@ZWpD0jdT@^TFeZY z_!YB1bO~}9(_?d%B{HV6ED7tYR{9fk>Vc07zFp9+LzU(sfstpz(HU80jL}h%)uomC zIFk`1)M(o+D$#{rKa3_#HzgCIt#@wcVvZCoIm#mWp3SFkj;Gkq-faKy1a%v~KbO+S z@Wc^3)i*j?UqcX4EHaz&U^90Wl^PX*l9xkK=;>^x%xC`0H~3=LFBF~wV_6=?c{BvC zYRYhhQ-x+Mv&b(ZOH6Tjs*0@dfGaGowRs26!hA`JFrLeU3!SP}6pxJnvuJ`OdBUkZ z9K(O|1Uc%5BqsoybuiF~grzS9v9n;$yxQIjLJ~bymxqYg*+IB5>Zqabsd!=Kr9kJx zJeK%Pj0#{&P+c2nW=McYRJ@iY=U9W74_qEEAg&xNuY6<@7f)VEx6k<|yrC6T%b%C1 zMss^N(%RHjQlUhV&=n#);PY8JOSZR2j=@AgVi}-^pKZi)3Rf<@%q8WL6pM{DQ3HLs zeq0-kKm|h;UTm(m#@5;glC34t0tmVVM@6fciQEwC#DOd?~6+6V>SPy4}H-+2M zg-sogj27X3d*=sB`(Z~ojGW8Uc!(9WT&RX>^+J)tX|JZ!E((TiQ+!gH*{7{W>#Zy_ z?$TKu%!3$GRV`oVRj1F869s?Tlrb%I5RpQz*#17w>iqutr^DNZji@+G+AM~saj!)p*>v8A?l+*jb2BI zQjxJK3f&;R6Meeg&VFzRh>T(a%3un?(5$B=JS2!!&W15>;HvC9>n*5}5&T8W7jj(l z^ow`eju|1YH-EeQ{Q=e^G)1|B{ZfB{pwcuKYGxu*Dw^(LU_OEePZAa~iuz{c5`e&) zB_NmMQD8Eb$?LvS@kSt!bp}36fU~EGjkc4#Qj#f z6zV;dy{e8vP^svc>&8cZD?0;ImI#4J7J{8g&b-4|B{dou(HArO*VgWrrXA+0bw|V% z#u=ZUok3;j?Yf=AuN#0RmQy{k72^E6M-lVR;{t~1Nwtl{0wj zO5_aS3J5;7f58_=*oE*#z%%$PYCL;}oEU*;?~tFxU8vgW8VX}|%ik~nL{vVvZ9n*A{5xReE!fQ(=#%PTBG4y7d4*z08M*mV^}66{+pK)t~F z!84B}MJ`no7~xOLBjK8Bb?!=7>S}NI` zE0G$KvgMvP|NVX6mkA(&gG0M-w?(pmItNH3a$i^V4zK+L7*gw&*=REfbMuU{G#jIF?wC8>6_3zBVF_^ z+WvL79J$CYC@rRt%nIkyr+rP!opa_L)Ny2c!q(73uagu{*FSZ(nN}R~Mk=aPU#3=Q zH?23GeM_1;^#I~S>{1Q`m@C`6@tv9%cP0S7>P)4+F%#M<&R3%m$#_O8Ze2ev9CkfT z54F)^bu}ANBhy5zPf{Gp?A8EWEI%*dyuW$>ha`fGug+lx^p&N;k^KA;CBl0m1f=cC zMq)ZW3V9g~v&%nz_?wdz1ql*=lDUD~XT@l{Z0o}If|m$-Ff^8@Lbsr#;-sQbiNN5y z3^y2?CD7(1SW5xt+vltHVSPf6Ow@ynepziEl>SN~4@zp{Du(0wnt-EYK)ELrEvaAH zj_NCk_{pM|Od^6)MoMkN`|032PBrb0fw#$Qk$$3!+clgV%6$yC$7#+mt*M7+TdkFC zWRlhooADve*9;HHC)jf0*(rdy=H(!%Y@=eD>cgE}{jb|QdMAVlaNW{vAhLq~q*-5R zOJGKzfn8JzXPtnQyrF`dsO92j`Fo7KX`PaWo{~Zp`&#NL2$^B^Yva+=YRb4EBlb|6 zE#SJUQKCP`zTU0Ey_r1|mBl2Hh%!84bZaZ*q}_7uU>~lNqqIE4s}k*aj?gA`7V@9F zhqTb5kA)Dm;5E@qh+osGO!0WI=Q`f=MPM7C92SFfak4l@hVafG4@-o47_9u>Q!I*O zf{`F!1i-FOAr9wrv12irIMv;cj3z8 zX#7cv0Sv_-&DL%rQTK8nD-OWwUJM(0QknhXdp$8Lg&r0p`X;v5WTI3oFdI0sJ zZ=H}5ErA}*F(YNsJ%L5cF6Qp6QW17Z;q2=r$xvoG35LEs_rNCpIz~I+{(g!5-Vj*%j*?`A6|unW)IkN*EGj zH1IGm(r~3vfMPsGh+oCa1G>aISew%|Xt3f@mTn|#rgPS|LVL1wKK0o7EG0t(LHjZR zfwk~psSs&l$nb|cTxTaIGdVNLq8YIYqdK{SiDo#C(IzG>`i*vL*&d^-2P&l(nz0%) z(@oK<8XE*WE!6Ir)#GZ-gs`6h9O6LPs9^^{18BKPiBxdSgAn9qP@D9ZGJM=FwttZR z&XSegez!5}tVJ>$2yakwYClC&7(z@XvnJw!=V3iH>c{oO5GS6-Fm}$Z16_Ht7Q(ty zwA=1=WeCD7)|$rqy;3bVZTh8-Noq}6wG$xMrN%SQ@c4$l)cnF!PIW1*(3Vmd8b~2( zZ3pEBVECeU15Mt>_t1*@kIl5h2WvC!CM7HG!?L2S>hSV~=UxrnXyj|QQ_XTqTR-MG zU)3;63u$0ll|d)K{>a)sRMc8~bb>MPtlqTQ;5Z{Dcf8_3Rh}Gw+iMKI?=8E<41g8IzSdzk<~fK`6*x)VnH&g z4|6!K^GlefFHw1|zcm=vmyDW9_P-R3gmcemn7C>%now+kLvUy`$<+yQ*171j><*2;TjsgHRD+>k(G8e2Pf%ut^kn7xSx zC=YHyhZrUSBJ>DRM|wn{B#eHkCWM1|Ne75&sCE<#TF~Ey5<%|Utl|d~d6(P>!6bII z%iAw|Z=(eC^`a7}jB^S7^`*FTCfUVn4^8?MH)(rzg8yLfc$R%Os{RoHkz6*ZEXas@ z(aJM@{xeW70{bVT^ArlTod0ouasB?E3$%GW+Cg#4(-TPdxS5C>YrxRNWh9dYB57b* z*}lLn@CeGa7lZHOYE^Ri0v==VwU3{!|G8ZG_?anW-+#QmyMOrY>JGm%vFz&M;qLn9 zuMf+`Vg>a)Khojzz<|>9-NoYjiGOtxzl%VPUiwaR3nJl6UZEGBt8QlImNfV4D@NkY z8A4wv728NR%0b3CMkWktYNd`r7Bb?#xuzqEi%d!;hMdJnGD~dVR3tQj9I&8tj1e@r>#V%TjMN0%3nFx2cs0CEz5&2bT!31^Ag%u=;j-{_OBo z`stAe+8HmP#~x&zz`}2j)kgzWfX~j6j)Xk9w+}gnAPB@4w5#Z!n!@mW+T?$d+jt0B zIZ}~QD|8D&3BzVq;IuyWV9@r~EbPA^wCvS3MA`}IDkS!8Ge>+eTfSc+2O9>bd_PkO zV8k^Qyfn44?IG7*-mzIaqw2b3-J=km-gV2PVo*90oGQ!fWTLNiJH>Vt%Zz`Pd7U_% zZ%~MW^Jg(Y+KR)k1x2z)7Rns!R-$zA4uh`6lHH*hSDX21TmNnR8ba@`5{M0}GSYQg z`1EE7t~nl_m6D}??(g-q`uuPMAY+AwNU2Gr2@YjUDsVCB41 zcpazxFFaA_rQ>QWg~^Cj-@XXFB#^(zstBIL2$YdY0#$7;QOIlkh6-ykTR;Uot~5S5 zQxXlCE!pmKN~~(vCGy)&(W+>e#id+RAsVrXd@~UFaK366nq#}81%zpLgR+1QM zy#FP}+$6+B=?{)*J zf*epHE<`iYjOyh+viwZ=1C^GHmJ0ZLbHtp132oXX3>rytZ{_5aif58g;#n&Rj7$>n zC5JexW`#N!wZyj2GPfq1k4)5q0&8Fv3;9C*fX)PckwSmqbi*h;9Slrfl^Yej5aE=r z(|QHe5iN*ajH68IRba5dUJ$Fv8<{_cwnoLc(ih1$Dv5EigY^;zkrF&%&b2Ri8TODYTLTyloN+w13 z)HBvFz*2EZ>A#E6Pn^1x?R4qS_)0Ekos<(IUf`Rxt6;jNP(huAX?IBlZVi=j2{|fe zScW0Dtt2QxuwTtzCPULBR**qyj{dE)R`IO$B`?L>P@$1UH&!<`wpKS@AW2|-x;|b0 zFx^t~;Dj7J99nf+k~thNm#la!T1TnA6^HdD#JVi@t-H{=wJFRxjfJBM!=|NI4!(us zlLsZ-Ud}?V3$LZ4IkEDyZW~#IBU+8B;W=TBmRGPC+>_L|{^nrVz672PjxbNXs`+~? zET=KAEZ4yDSw$v`XvCvPQJIbK74a-_xvfEi?o>f|zK_r?Ik2W{>u~4RINWMueXJ^J zv@qoGw(KhP$WC#(VaGVmn1GZ`oPSz`QG?A22E~K|HNS>_H6+L*@@n^x^#RmJc}_}@ z089)aGS?+dD%~Y3E;Sv3x8Py3P}IS5ML^nhDACgHf`e{i&aytjvDZMzqsos1EB%~A)#G~LVOPGq3WWM#gK;!OO_ zN>*Ayw0s-{SU!$AXREf?E_U9XZzIsIrbP|hVGY+ZEDUUc?_go(9bj^|{dRnGaRGrD zvc=Ih49O{0S>Wb!F3+2n3-=0qRyFduZg$Z}8{Sz(jl$rc+vc#kUR`F1ZkjZWIh175*FhX>oA-Gd-T#|wBq>k>ZVVzQnr|ds*c#eN~?C=p*f^Jqb+L(^Gl`u)$)Dp&` zCHr||LOEgnBTT*}w2w~JN*bzYWUuD<-6{IA8#j(lk+w>7`TPFtY<~}pgHU?y|4j7r zhyMrG3}gsS{`Z&=TOMOYHMq_)SaEYZNbsx;z($N{%W6nXdA&LG-Ih zCcLK?sP^_6VOWgmMUowA@U2dd(W&T^0eibE-xzTd{&M)Z*C@4xe7(1qGVoF{N`s6Q zqilqf#r)lT;WZVden^Frwr~vLA?XTjYMzPcMbo!P-o~1!( z{_oLm4UwRCi_go?KVRLgD9-5qGxq-5?Wd~;46S@O`EK%G^?%=C((TGt=eL_@*cTmJm@ z7lcQiu;S3JtXh~!DGcl$?z@coctG+k*Q@N!$cG|F{-ak0I4nzHNrUxQqu!Z=tlFXE`YaOb#d5s$!ULrm7n%s$ZE<6GgQlRrzG5m9njBJf zt50xrnl|H#ii$Lwuw7Iaix+xQV)?D8D!F?>y??B~>CaV*X*$SB>p@`4*qqpQ#<{*r*#= zWF&#RHl0rIk!5(G@Q9y8BGMbtwILAK<+m@lcMph48N_b>hSESkUw^uO z_ye(=-+i!dwBJ2Npr#o(JE`95pp99qZZeSixgMQK8Rc6sg6ce3rHOe!h`>1E~-ps;Fmo^ z1I@YxhWH{bunrW({(0c?6YAm=U(`su^s|$rS{!LvZO7Gd>Aa ztbB?-fyg$hRDW}AIc>@X6t*^+n6Wd+eQc20M2*4q1<|iqP$k-+MRs#g2{*9Dfvvko z4hGaqtUl2|iB2V(Ssv{MZQWHXSkiKw@#x|$l-bMkLE%z;zXas8+4h5BVK)N`Q$szZ zN#Z3rsX269FZGUqW9qox7^Y~YpscJqWV`>Z?ou*^cg+|e7FYr))vw|G6S122FYx~bd8u*loS`&_C(tI&ubb^L zjGApFGJ~M2NTvEb+EE>YLb=WN77E>9L#=_I7G&g|B2xS0`ex_r$N7I~orp+AC8t%D zb6WieAnS1h2bZV2Ty-M+#uvb!8dxP-<;Mz&RE{-`B|6qpnBlm#M8rpk0bH`eeEb?Q zpCgX?m5`dH;LTyQNfxTDv@XuKIM>oGuK9aSWV5TM&{1qOeVQGrzR$*anII^V1LsPC zPn0%OrEgn?-FwoN^5X~&J?8DuApbWSi1HyQLP^0xtws3z!PQK*7uiALgS-2+H4A_a24=8v zHdXz()W4E@v`Y&hzWJbVG0KdNEf&aDA9XXXL1c9Cab<~W3%)Q#vEATV`P3FY{MFs6dXpb*YqQ@vflZ+ zie3b;kx6=SBESTC%#LDYha28hk9lwBWw+Az$UHq;(!)q=o>lhdX*5}`0h(h_snhCs za`dRT`s#38FQyQmv>_Po7^&BM#DG1*eD{Dw!W`uI)sHNj%{o)`hJ2VL_{P$y!biEQ zGi6wcA&4SI#}Y3pL?8;2)(La{`6lk<``fRGSqvdHmXBtLDe;7aJOzeYg?Jc{XhF(> zqR(LYWt;eLiO6xNV;X68%_9lUr8&k`6iLv^LSqkOJAx;KR+&4T4cB7 zun=;q_#jFQh+A@jfe}nWROX*1D$5M`@)B)pti9&T%Qz1-9W{O=Q-^~AcS*{9eWUwx z3|#aWnM}3^Lj{k9kkGM6wsg)wEd_zo;?9~OSaH3lN3&PKJb0hr~ z8vW2@t!~oDvYU7TquE>Z_eG=)|1EDJ;zlXe%~SQ>M<(GZo~b6@+|AF;ahF^m%^h#g z!B1eM83;JJixkdF!Hs;Y9$c;+s}O1JZS*6RV~=cX80KHqThO^m^u5QX+pxR-9D$=D zQiBwM;kdr$7k~`fvMI`quoMbZ6tGN4b4|SEW#<3Rf0%B*@)Ne6!Sm@6YIYl#OcFCS zDT6Y^tP7TsPKxqJ(loP19@G&VaQeEKENVhWS!g9{D4R65+lcLufCNd}vwLo&tZG_p z4^4(kT>nBLgI2_qmXOyPWk;t8+%+Lh0!{0aEHEJk_Q^tjj$V=lV14_N9dP#`iwNdc zCMb)QVh1_%!>l}orH@W)_u`UzxZYR;0;uW+kwveSY#RlJq>Miw82uW&MLb;K+`UJj z6hJMuQ6W80u}c&M$(1Ag)DSJjV9Wj-Z8N?jiN~IKv@$IfvxyeR|Vd227PZCPf1$GY4^nKMth&++8b z8l8c#;8)Ea!p#C$`ffsFmSTx)S(7f9mSY-2ho#WL)4WT8z_eoQLV4Vq$-~n8sb`AS z_Atb?xLjZwQNF1&Jsk1|o}PmRM5;dS7XPITS6LFpnAyoK41+1NP`DtXEIMXo6=P|( zHZm2=)0$0g-+Z{dxm*74uSrf#2u}k9G8VI4hL^F@f&afA(>#JDM;; za)6d$tf`;=k;kf46Zq6@jEg&E%94zWTD2IHHtb)02KHFW4oQ5;SW@DEM7sTW=t(j} zZ_eupTUbnKL}l}fDgcWwb6KysbK!n-65#DQC4~o@Xxdg7a0NgsbFohlFH+x7=yPi_ zLX4&h$bI^*XkKs;!p-2&v=SoV9v5uLHTR+}Rs)=cle?i5Ku@kbQio(8#E@Sp>4$SX zRl?EP>Hb?lP8f?NSsDOz_GVlM?+(fR^ZC)SqOAI9Mes?|+vk`&MTxVFx+0S&XLA(Z zs#J%;L0G4mHo3qKBi}YBK6R(j@siv>9nfHdlH4Mp&9k5?NOow+a)f10>{@M2JY%y2 zHtJ?X{0qd3OFOeaqJrk1n2HAZ*KB*>U5nlM#e`Zh9d+w#lPAaX9XQKZt{9VofBqE+ zj}75}Yz0}9ha|-{NZ-M^BBwfD`sJdG&Vot93g>~|mgo}jlY^IDDJz?*idEXo zU7|kHki$Xn66OMu&9YTisvJFf;4-ncndIx10}++RhYN=dDSav$kSR7*Z3@T}%IW3%+8QH>5~pDh~u z<-E#T?99vrle~o#u)u*^86wMIc|7eh$msOj9~_^(p@VR5380C^>-oTBic$yYtK0AM zU5)D5qb5}?+g9E209ZVklfI6NK+j&>T;KkDpY4^@azNuY5=r%26R4QYNDXwg0&^zl zXdtoj7EsUC&$7bAAW0vi*)o`9_-OYF)TiE`qxEOCs(9_6w|9qlk-0B{iKwrEO+I+E zRh(J>l;w{k>OEn$P!GNfQz$5 z+R#}kZo}3|H;0tT42>>l&{4T$RK=Kzz~U=aGFM!@03$@kb|F&6t&cqX znet|_G3#rYLAep&TMPnsoOJ=G*8LO&f4u!pPO|3DH`g~G7`-a7MHc3Bmd0Y; zN7Sj_Ra<#S#}t}L8}o1ymKMcu<`c3X6LLUe7ye}$(QU+hR(a_Gl(qAo#Xw9 za@&y~iK^r!BtZ9w+F~vZpGiM6NO=c&!J%JT9X+tjJ0GtjoUVeD-g9W-cE2O zf5dO7!ejYlYzVTiWF}^)B+!^8%ONDN*YhHD$bI39&2FC}@rF;$uH@Db@A+lUn|RT( z2YD-NR@G%oQeqhvckHX&OL9lt@Ysr6zIX5Fpu>__)`X!0<*HFn&ndZn zrem{ounEBk0w5T0KUkL1AV*@2v+eWqWNxN$3pDxwX-sf-Ojho%|6_UkaRsSJw|9se z`FOi{K**^cWQ3^>aq$O-METG;omI{^zg&fV*o_KXNS@!ryEZ!{ z6Hw&zVe!k?tGf@&4-lCwL4zsRi>sTP+lQ-%CF<4G6Jjk2u)sf!?TK>)|Dv3x$na!j zw;+|CXv`FH$-hn{#;N;b9qkMVX1D*-+?g~@YtCMU!|geQQdra5 z1>}ySi+BDvK;TwAO%+GBHn%xPx1|E8W%O|S`+JWTX|Ql0&}5Y02^kpM2DpPq)MSgh zt83&&OI+kT$>h8>2q8@n;vxjSQsU65+BqD^2RQp}xccElpPeFr<@Nqp8^!{*yg$(# z+n}Eq04v{8rr||6v^?pKSw2n?diM7BBHJ3$o$2zT4KrKECnP>(LqO5PpF#|4mQZz@ zl7t#5dGO|Pzb?hL-u1CrgAEmhzqRnkMdw+M1}MZ?dQm{=K+AM;Vy*pEwxaIXEWJXu zkX_VMkWiR^(L2Oci5-R$CfLwShU?s1sdND-k4SpPvD(+W=ZexhV^*kAl_$$B&I4jKU00^~I5rn`ho=HvzCc<1$7hI~EtWm7tXs%|xr{H08r_tafq^N^|RnM%>>PZ z(jJ>Bd3BtT7AX9c*{g+LU8+WLQ$rgp3%K zzs{`p-p%>!=(PzJG?PU5*wzpM(+fNco{D6O6pFOY9`w_2N732cthjoz)X&Pld=Vw) zt#E;55iI|7Rje|s-}HVGJ(vGdiAIEM*HWH+Z@Rc}OTQL#k(zj{!}1)Oc%YUQJ^;6x zAMYHPK^B?^Em$a-ZD)Xm*WlOjhmFNheU8-2it52DSzg5~+gJvA7+M5H zp+Py=3bMMzrL6q0{(KE*S@OHFT&vCI4c?_j4ivX^lG9$A0%iZ!l zvf^%UL{w0`!h?7ezlJEDSgEe6@n~;!Jod?5 zXpB6y{M?%%94Z!&BNiw`+7@K8Bz#vCSPDj1|fIm%_zE1 z^X4dZ@O(U5pus3xcN1wW64gx9eeT-Z+Qg*CW*TXLK(ou<0$w8pA2PEs(Q1+)xI1ec zX$JM2EK(CjxdPBY`H;WHF@5(OUCsGEGqEM|L78j7K5OB|BbanC?Fotj;AV|Gx^@WP zM!oXh3@tx&?}5k3ni2t4Td$>D$a1xf(qigpj-S}&4c8gkOSQPGY9 zTMie&QAeYzsTnRlt7s{V;O9A6@_81t#Xr}g_H7jTc_PJ`3cvsL>W)5KsBvEA z&#N_(YI*P{Esi(?6~ogKJUSkBxASxIK*T|tS zXSctDb_Nzn&^`55-ZPapm!Hk0f}GqJ_it7eYOb5wo|*yBw!QJ=i;dN-&8>~i=Nr?t zjjhd%d_|(Wu zk(zLP+2@hG=2CS;m!vM$E(mvHk6PH>%VxcbNBWKaWr+_6+gM~8N`>N;5eqtW-?~B9 zfR`i0sC|Kh{#F4N-d-gAD7Z&AIpk<)%#$xlqU!ejA2++7{&{5D(I&McTm?n>#MnKn z%46jY7+rddMxOT1_TAmpAII1C4^BXuZALvAdMUyvJf`T)(H_cd;exK&Q}YE(7K#Sc zg$wAGp1o-ir!ztLl6Zhigmux-co~w-7j55~cXtk_@yQ~dW3Yi5uNl!Xm|_S$o<&rM z&?6mu%A^E)LI@f{YoZ+t>-B-W*`-c((e+$idZ5l6Wj%NGpKmM4mDQLYsOn}A=HMjS zJx1)t-XtpbJO&Gc$f}=$HD)^y?~y5bPQi%p(5H~$C^_uH=;EK7W@y46QZAC{R|8v% z8qCq^jQ_bdmjm~#HSO7XLyZ1pvL4t&j+QX>=jiMGVwd%H`g8R4gx!ARAUnl+4Xw__ zaur*{+`4(|tXG8X|dok7gcW~|bYllg~MWeC2sV{XII zXOK2K9>J5C4qpPc!%RNCK*k>$bq>x`1Jm+Vq%Sl>0!qaKbI8GZh&1Zm`!Jq_2F33s z5xz>O$se}%wFZM4ereifIy7lQ%@^u1+s@EVSOPCxISseiBoM1gKj=NK$9u3)rB^f$ zNL08+#F0r_WiXU$1;cFXZcY?~ertS75O9=+!#+4_v(bm6B>5mPNo`TkpyUS;T4YoS zCmR5FCTRV+B?6ypU&QCYaLVH4OJ%fPwX@Lv#_XP0)_Us{>EgG}+uTU;wo%GsZf%F@rD?p8p*_6g9M3}8_30o=Opj3x8Q5nC zqM0E)MZi?lR$N>*DGm(N<3X=APL9vhv6AmP#ql4_K2@K{4b;;u>UKN0HC1En>9cMr zI+Ry|tD=Qlk;{rPKP8MrLQX^qA;?lSx|o5Zna>=@r6M)AaMycZxwYIpqFLPFT^vza z#^2W36CRr>DECWdHjuV;Q|$Mo>Gb zkc6L-cT{%9GKyhMs&gEQ0iy&GMTE`*Kk2;Mqyo~tcGESD8BZzLeT6&8l!6RC zW?jilTsg{%kW9QV^ee^`0x(^9p(proHilo)55AwLb2ac02u3VQda1D6V#(AqFR|4 zqYM+nUM$@g{-ng>jqf6su0ea9n*4a%=xUj`5dttR%cU6>Y!VC90mhcLVzAH`Iw=etWk>fuH)aWrSgmkRep z>-WWU?dNM~+m-WPh|*GRZ84Dn?a^aM?D{50M_VLWcfl2;kuDSshKs8BH_+EXTv2Bx zc2(|{XwIL9OE%L2BwVgjHDf7}LBX_zg`kS5eg+?YS0N*k&>=`y98%{|hImt@xhsZ`Hpc5wMDi*+$jyPhEEI7d{V zHcX-Ba;T)w@@ev;pGRqZkf`Fh_|b;dTw0a1G)lKLH#vD'd}H+mh3Omuk{u-Y5b6 zYJJ5$yIL-C64#NircwoEwwL+&L1C5zh(t{me5dsl0L!$t*YP9Vkx(e>Q z)kBn%lo;6rY?S=F8j#&gvM!(N&a}fa%RLF0UG;L6^mq2_>(Vhus-$aI2bslllNx&P z(@3=Nl!(Mhd2RX7J#`>~i4+dRg0nXWx~V2;SK6=h#gZaPsT;?E$fyQ~|8#n~WwSR5 z?wYPvMVH}NfudMf$xX-a>;{CPm!2msWt6(cBSO1YbyzuX$UNaOlu4SP{O*4d%4!8g~=f&=dw|)F2oP z3j%H2VA35sh9VV47~Htx3xpUQ7u{hzQSlAIT4Ul!@tJa5(CuGkD zex9ElzayUNXIg{Qgt(A2A^p^1MmT)h30VP58Wwe&y{aDDjSwv+}prL%2Wj>>|t;}r?N@2>btx_@0+Ci>oE09ZP zBcq`8Xu@B91TW|vvARMzAnWc0$f z^}TL9Oj>~)Z_&5Gv$57(4Gx(~QDWIrbX&#ll4W#kvVaoI_BNsS#RWHW2UyyC2Q*0zFz0c)MN~4&kX<;A8#JeZ}dKsc)kT1 z7{XDDSj#2A1BHA!gz~csv0g0_)V*h}F^?tV<4n{3 zB1tRN(#h)>n0I{iY9Ep9h|QZLs)If9lnuo+sHsPZvGFa?5oRug7=Z`7l?rKU$H>a2 zRbMEX*q{S2!Vhc}YS87m;;|Qd?@rMJ7KO@Zv+Z}Fjz`SL7fI~W9H;okUp#k47={cp^%ZW0tKW+(Z$}TA4?k)-q_&sCk!Xx$dn!SNDkDPG%s!m&Pv*D-t5ha2(3C zAcsFXNgBLoFNZX0UsZ8{{gWd^ub;lmMT?y#jZDH?>bSbt7zxap1>f+W*M#=JSPr^b z6wL`|=S;YZ`q9FTBW^CblsGA4io{GM7){qfp%GM=?ixk?9Kq-i2;nV+bQRuWz$RJC zUQ*8atCwY1+bl7>yojWPD1M|^5+*J!alz$NEA&1z6C@)+LqQCTnTYMcoPavUZ4PIt z*tj|=zmo=bgmAj{Yk7HOQaSsgGFIUkMtmsaLLh?&RsUMI66v`q%Gdav?|B%7r4tgp z>)umy>9qEWN2ivR2jx2kC-U1S%Q0Tz&m5m=h8|lS97*yPD%q_S!%%}xO0pnI?D#Qn z8r&nc$xp!VUU!0C#^dLcJWtx{moz9sCmAj8EclXs3Zx>O>Dm}k8^m2C>cuNT3wL7j z+KAphWZyf8NuN$P;eV#Iu{zy*1`Rj7>?P&cQmZZ8gYsc@od;gcMD5e6>WYnqPC2MO z63nan`{mt(7bOXCJluWl-4cXE+KCGV#JT5XEmS8kaz(h$6&1_sYe+6gI;ue4}BT z)tC^=V5n_#OZigPyQUZXXcy`g6d}J9tyHqCsa5gWcg2^g8Mlai-+saXw3VtKzqpR8^}|S2t%87oiIGRiek2K(VBJi z=V)vBS}gP%ac9129}46(#QiK;3bDpP9au4?xZ7n-{Gz;_Pp57;wz@Q~l@YCLKMI1y z28cbJp)ZRllGP#mPb^PAi9>+|=7L=z3f0DSVENwV{wYx^O0))q2Iim|8a<3CMOIxL zqHzOKu-fced(%$1V|-%`<64Sk>CfpFtA2*kpDg=X(lsX`s9(qIqF%l9zC{5M`e|C+ zTN1L$@ROt2+T>{VKiB?&9I;R^!;5HqnNI%YU%ssIpY*W*laJp9$r+ZiJ z<15mI+889d!W+6``BDz7a6Cg zKW_ZvNBovmN9To5GHV=f2pY!qpb}`bgC-p5JL{c5B#rAkSwK#=rGC?3SYOinJ+vnH zn_$d~l^A-(`7xN*P;QAsg zKF1vq5DLh74b7!qR)3Dxl-0JPvt{F$U(uo)>ocmwOQ8;VNQN%?K}?)E;BO~eiW3uxA6 zMQs*d)Fw?vPq9l;($iuxTWBSKc2wKT45`Rz5Fxft*0P)ytSKhw4J%U!ud_ae%^(D# zMvUGJL)D;k2 zaWUC4!*Zt3v$q$hjG6P>R3ldZ4k4n&M{uN~mKqPqYtn)Bu7X- z257Np2~?j{p`ScVo1ta3W}N-kDAmb=3!#r-yrQ6ZB_ymDCILR)$#T*oU`kE5DEY~y z|41Ru%U7YN8Ze?AW5R@)?n66J#I((JAHf0yW(pUldU+dNIq3$;CAw&Wue~xk32>wF zNUtL99%Wii!RcZanq8iDVBtKUzv;K!m@gT7KY9Z(7xHf zfhqoze|tLN@A8pY2#y3&ic6QswcS3pR3AFOju4Xd_y0sl&t~!(uP=6TQ@)}#nnzh< zT39V-w?X6WX!#0wto)#Ml%nSa$BAVb$DuD}EZfJHdj(Q9pQZXJtNV(Eqqm2|bS%yQ z3voTnE>KAlD!-+Zab7w;QM)=t2Q3fyn(rb4o}pigl0rcuuBL|?8*Y0v*GE^lL6$P9 zfA8BE@@q!Cf<24nsMuhwcY*)RQ}fJw#|B;p>yu>_AEx&>$cpvsa{h7{g$&KIKSOXb zIbwcxiEy38k^gM2C!G6vC{59qdtlfs5|nY`f=8m2s>7-CR>TOM?Z;@&pn#x!am_Rr z0xoyqr#?k44}jpm%#jARe>Qo1j&eV&W`PJIG1<~fw1PDSS`#`cxgAS9pt8zWc9j+g_6;TD}v#zRu|TTyF?3erz4f9()%&2ttjol)UX3H9w6~UfRtS+apSioDp-f1Ynkn$&R2J5 z?~r@#_~_bDe~w;VA6LpiF9+3*rv(jjV`RLZU%)*sW2)yS75u3|X~d`%qT0BCbjI8p zOvx6`8!m~H3}Km7DJE!*BumgErpo;z-_L74hoFP)y>j0b4(Dv!ja!WAWGSa%JX`p* zc&PPwlNmb!wYFsj?oJnP*!*LFU*T$k>AgG!ALPH~u!Foyz?P7Nh7J_d={7tf;Qu7YbhkqGw=$tRHYRTc9S z%EfYV%UWRXdfeM7x(2-Y`RzzrWcW#ahk7&g)mG(6rLtzMQS+ADhr1iR4Ulj zN;R)$hmz>6k{YASIV14#?9Y+Bnxmz}v(GW-SW~m%feOZW>9pa*ca@S5)HKZ*yd|Nh zn<#YhQb~DJcil&;pA`DV_1#Emvgqum8&u$j$myRR;R8f!95ei#mW5Qedb+xXBJJ5} zQW{=ejs51bTY!H;UaSua!{s^x zP6D#9@JLF|Xv--^@`sN3mUMdh!!$GT^>PtINd&lPCb2MWt{fd?sIl> z`@#B)uC0}l$BvtIG{o-h-EwdF^VeS%dxs24nOiSWLz{#&BXfi{xHf_D3m=I3 zR8sedb*~5?5TaHY(Jo*~ant34KI=>FdZOa6tt6+P^J`SsrRSM&rYk%3`1HOWn_}Wp zxV*4Fk%4rv8%jWd0};-2+uMKnKJ+Dnto}P_$7X3mivP=>xi*b4r3I2vxggDjE-AO+eUd3N3n@}%&FWwQaFv0FpF(Z*7@LG>Hp( zlhM}oPP8SHS-vbQ$B4jf(1J#jV(lrvy@CN9eB#^c)|S#kln{}F8Mr}Oe`l?UR;D|h zQ<$PXkfYXuezOuY9KR$fp6y?(p=+=GW<H`=8M9C@-e> zgfOmLt8LhT$uy2$&dw0rN(6&4*bp9g3!+3(A8u7PzDBS|CT2k9n{e?KbnNmx(#?TT z5lzX4NyVYOgJu!a6V+Cc`4OZi+#vAQ!3pHoPw0E%mDB6{osT1{TS%7VFkQM$r#v{ zu`-&M68S(9n;l1_N|GAH!TR7wCJDIZUPa1QXsAx5zFz)tH{5n6=)q)%xM*`EBO~!9 z{Fd2bmV)?ZlY&U_njL1hkD^LgJH_2pMtJd-jI%MnsCkN^XP$|C-yWSDo23m!C}u5K z;w22Xq9S5PZlXJUgXY=oB2tMz;hb3!AV~&aQmMarjMfKUG!00Xgdl#AO2}3$l#Q z4<1S8WH!*%Cv;BuIO27Xw1&IUq&LO`%OtxEgV7FioYMw#KXSb@9jYc20TZt&mm`ho zOpKkFIb(7EQSCcd(DPws1vKgD3M%10MIC%as-wX>3MB#qpHBYEe@F9&%8eSyLF*fk z9Kw>(@Sd@}So&eyK8blFv+PyiteiebF)klUGPY|8O!Qc)8ndFvl4j^F9B2Z) zhYC*QC#gL0z<=sSogGk2@pM<(&tzD}cvWpWGXJ0oyciop(Bo$ls3@iAj*fayEP=~u^Pv|D zC1jh*kbri(-el(C?(6dL9y746=uU-{t=g2eSX_NVt6l}inVNa^@Njqi^H;R$SgfqX zVf_8=DaaP@Ii+c;g=pUai@_}F5ReUwI}<0&X&(7UB+%KIWmXhw2oBD%S%M|6n@uPg zjkpoIZ+%l63==o#ypuCuby*=|f{4I|>-Ev`x04}0#E~_6J!aRx{F;jN_l>L2q%-@E zTS!t`7|-n8X3?j4*vNyZj4=xc|Pv#3iy&{VW95Y*q5h zVxi8|cS|Y^%N72?P9b;82Pg?k=n?E&U5%C;*K7o+iA2Q_+H)6iLxF$Y+JJ!8p!P z!5NhAM$GKP4mKL1dGB>L6s|83p)wjxmAl?!I7rOz#E&(op-O`_UN*AU&b#w%xU6bv zu0O}{BN(tm%hAdP;J_G6+O*(+Y5cPI`1R&}_s~2LXDYo~7?>q)iS3QZoje68FV~7+ z6tTMpm^A}Ty=#SnuzXF5q`|L&_!!~oP12?l{&2(08;Yz;BDRoP;Glsy4u?QQ1PmTR z!8tm|imb@;7st9Pbl=3#xQ&3QgMLw#Vq$=Pamg1(wa z1b2o7qK;eKnnpdo?0Hl}2}iSEy@ose)xMg&3q^45X7~kLW#+^hNJQ*VZ2IPkp@;Qo z-B?^?|4vqG_Tfds$M}D(GfS@Ad9bw9@En_^N5t!yb^2bxpQH!zRchpvv0pg7I6Xc( zeKppl1=y49OAUU$fm4|dF={1i3oO@rZGvud6 zb+ak2emQAez+lR7Y$2b87Ndkl6b8*1PcGjs&adtuFfO66i3B9H5r~8~T8w$TOme_V zRNOKBJYD`W-TJWenbO{YHdgKxA7imy&5FipbA(XdU6M~}TB-p+U+^&~!pC@pDz=G`O8;E3X8blVJ%!B_|DO72Vzf5uc52iV#f>QAs7wgIb=!7hvQR(vBg5P zbCAlA;CeN{uZ!}H?+7K--@jinOH9_Z3^W4YP`(o?R1+L$bVXht3~SwUkc0h`1>|*f zghJ7VL`D*kiH+j(GJFl8Sw^Z?!ztOHYb#|Yc_dV@uvpZZM;gCQ%`BCEEo^)X;2HK77n_F-Dk%R7e1UmNKnpIR zga{%*IM+ZN3=4p?N7TLyElZq->+Z+1H+_VH-F*K9-n!Q)G;;U~%7XR?T1?JczolN= za9oQs%yhF6wJ4pJ0ZxI<<15^1b&|#6_T$I<<-_gA6=qU?|M7bH>BG}W{o%wuH49h< zb#Z|=2P@=`F|Afg3~`glD?WNX3^4i&U(m+E^A+|NJ3Ewf8Tx-*1=Ohp>ui=6UBIn| z^rA=>;3SJqRBRsxnO!q`l|QWNi(tbWWEPbAG(|O<8ToCAu<^?%OIPuyzF<1=@M+Pe z4?K(F_5xtvy+jT##ugLH&YEsoZWeyBMBMz%j8QJDtIzSrm#=rrHCQ(qn;zQ3+#?Yd zwmhPxTe@;oe*Og>>BEc1qw{4F6WbFs!u20WUKQiZymiwwo5=MNU$r6i$j%SPVZ55Z z#Qm=+Y<7Nlxd{K^hn95-26WJOP|woAb?=!3z!t?Go2f^cEW{qFP%2Vnj0x5{SJ$Hu zJ6_Vptcp=;?1>YA6sgjE45!$itUUn)5cP#~$Eho&ihn7a>W4`(^5Vh7IqH{Q^V$r? z738zXVMd||_#|#t-kqZs3541tT%4ov@wriMQ7KPwMof#2Y5L={7tgj{tWVcBo^P$KPdA?} z|F*vR`~WSx;w^+u$gZybV{HR*Yw-pShOZw?s?HJuDB8fCSs_5adI%p*wUOP^Sn6YWn;!^<@!e*gyFIlY)X< znZ@-a<7!tp5vL5@GkrM4rd3ulqQatt6bYm^$DkOVL`U$(s z#;V(0_GC$~$tc)HmZv?;XE;G%r3UGdG?pBDdPWuQKc5(jocb>8#CPk8S&>2|{r$zj zgdaExLFU1s)mg=$zrqWLDWRDemXuKLo8d3P>$qoLM@!*Fu1NO0wCTMX6Z8@#($!%_ z+|E500ptX`%{03^^1GZ%uyrQs1-yZZVe<-A=_}=9O*30CtN?BlPFc`7)fR^nftC5j z=CFZ8zQ-CG^AP2`r`u2o=8QlookuFY(|dDTOY35B>d!F%jB0g|xrT7;D}b1)hjE#; zL|>c6M-UU=g^5&F!{Y9mzeZpj3M^dMD*Rx2<9c-+rfeNQ>tJUWH9e|;TPwn+aUrw8 zLsSBlT%aMErxO8=o&sYETO8NBvj%2`20K*-o_4CgP?=}%u@YEsvX`o~^OYe;CI~zS4_!|3j^!3UP;z2tIeL!=9p6j#c zBkt1C{?(R3g>FW5d-0M z7Uof3%|&YGA(E!Fdkpeo5NpNp{;Y6xm?Q`#0s+asRHbsYEv!LEd6;U?MUlV`AJM_7 zvqm{llm9cJNflLQOdPK7<6|@PK?dN~GIM|dLn`Squ-obq=wk8J^K=@+U9yqZi)k2# zR}R%OIvzr8SH{M|JaR|VR6vnEzRmPxC0yWGIgo4Rl}qf1?-L#@4q-JitO~CG5=&bb z2bP4H;aie*3Vx(Z!SkbInpNM6q(Cy5G#(;GioU7gF*ZvO(F@GhR)<&@Cc}3dlZkl4 z%r_Q>hdt^3kDJ|3|2(>BkfJw3?_4a-W^7Gyd~|Vv;xZr4x&UVkxQnD1hVyC0j0u?;*Xis^U`fWOg+Vcr*Wdd2uev?Qn zgksXM(cFqA#7^FJe~y+IE~b{~v<7YCd(DwjD#Wx{EQ;@PK;xx7wDIsnfw7qZXerF? z=oE&f8i@?5)dYR9Mqxvf0&J^We|~XDhlSgWnr@Yqg}2AMpKmq1yEo}s8woWsNo(#4 z_K*#^@c6|n*k#DaG#Z*Xok!rYkavYOic>h+g~4o}6ji@~kv@8|ZNV8A70^|iR=)IO z`Eh;m{PhRWBn2N7ay5t_t%kH^GE$?zyv?epr8P?zso;^d!V(h_L6Q{1Jeu9wB0%7& zRecwcmqPxkvG~~J&3zF$Z+_CrBld28L@#(86 zEV`o5FG|f3yM+jJtC2o2*E}p(8nY8O3}&&5yX#+mL8IoxGAN~G0_7pm#m`+XaH8Mj ze0@Q;ouNf^QDpx0%a>1oT>O5!&sg1G?^z4*{naPha=P#Hh4Au<(>q~dr)W{?~y7UvRMTt| zb9+ZMq-WVDiOWMDu2t0_TWiY~s11wnP0WTHFjb>2;~!UkSCthVwEy0Les9yq^^GT@T%~%QAN8OxO%+IXExB=tC zqBLHNJA8a7hb&CV$-TJ?OeUn8W~1*BQX4X=VQ)fQHlO2w2$1Vu5cM?B#byHT#IHPNXptfIskaWczmZ#8C1Nma=ihZMt!K5WE83Rf#1C-^k*0ws2G@uFjuJiRL`Bv5 zd}c;Ql+WJ#7*`O^nRAM|+g#5u50mw`&mi5MRQgXjM+lL zBTEe2P5#?tb@D$D>vyj-#xxHfVB&wBSUrM&OVhvn?Qe+w6EZO%PBm0h9N=u1>dc^2 z%b!fvM(VPzDihq?ln#xRhi9C0Ciwy#>6&M0MahtfKq;#PyT>a2^vkbKojoO{=c4*% zB{%kuA3Dg`@!L>vChDqOSR=Q38+>u{9dj1dgyD<@?g>>0Ih-S(hFrsM(dvy%w`{+4 zkB{hrt5n4a#1PQSy=?qhU$c}$RZJMvD9c%?wgu}Y%YB=ameP<%q5!+fw1sZ862PNV zg*nwRWZ4{j`4YV1O-$Uh3|q{uNAJip2g+8RbNqgmC#hczW!3<9*K4fE^kVUz3e@$7 zP%bTU8RAzG1tQy}E=35GxDlj8C|kX+raK~-q?+6EAoKbqA_MooJuGiNWU(@CD}iNw zP7Nl>O}4F#pK*1GoVUho#$Y+=IoyWc zEBuo~(&;3PFjNNzWlPG3j~I6R1cQGoMG(e}dq_qQ0N@wdK7}!h73JYDI$jBstx)>cEDQXn#E;$y{uCJY_pXVdf^sb~h zIZec|RTxVbkrZw{I@L-pG{_JaG*_0d^mDqkbBzKC<^|v*f+~Vv*}?A!j!&Ji@!k${ z;YwD>MGmf|;Avi-tljW;In5Y%m{JdB%5 zLmdT}ND=x)Lurcp;_sqRO6>lRrEni)A)!7gM8JHGIO0p{biD}$?0OL!rkToI%y7|k zH`=-!xQ1uv7tCLPwgygi|8Vz_y;eV6|8u$W@iR*_Ag$%@{^7T)JN(YV4NYpp$J@Kl zS8VbHNhf`KvG{)CUq<>%`8G>1rLJ6zPFNr+b05kPAcWkn5l?$MH)D|=(y4N+RmDUo zY%5pa|LhGu|J!5*zy6O2JAOhI`x$R;l%fI2?&BiF(v18Oa}se4Aye5m}tu}&fzTdOGg2(s{P^_eN*EFKV4Ceol(Jm|3DOQX}U z4I=8phug0YkcyF2wt{~k3EM|?caQ`gIAgSZOYJkFZ{=10 z4hw0#6ZmuLccM|JBpL*D>g?l3f1=@mN1x1J9i7u4y_o;=`ioDX5c53byf#+XYiCZaTF>y1C+GYLN5!u8z}NK^Y6DhS(^E1rSEmEjzyY@|}? z6hYxAAdODl&M9yRafpq2Rf#KwAr{I#DSrDIa@>NVrc3CGT9kHbt@U)z8kW=-#IC6` zIWw$ajR+|aau;C+tFP+ENFVZSEM>y-3e-BG%zH-|5kjKuZ0DT9`{Epd&6%cX zWE-Uruv18Zam;RGve>t7OVNiis6`7YvMg#Lx%Jsdy9{7w#U48sg^6S{AAy$5d?10% zE{)wGQf2oLfkrh@>oKiMcg6}mu%O)@Ae!zOMPD;USi%r&DQF&oV#!AJ*K9+<@c+%z&BJH9!oEu-)yNd~Qj*TI;d>?kknN2(LAfICyOt zPtG{IfyPxntfUKJ<4biGtrw!ucBD(s_g|l#swb1c>N?7BRDjj$Gm!wBsE=YWtS_Yl z5{YG2MbTAMF#v2K);Q0q7MC8it$O{0w&@;*UM!4J&wx#U$+Q#w`=l58gj;YhNLV#? zl8aL3Uw9Bk-UuA^31VR#f(~`y?VM1qBwEozUI5b<>*dw*EsVJ*xd2r5Ct8WXM3V>* z+M?>`=!$h|+n@rg?32G;efqjYZseeSRdeT{4=&h9?kc(oWq1w6PyUx!p031PPjs;JxLL9eHU071q$@qaHC96>1Zflhpz1!F z%NpirD>}_g;oS37BYk&Dh4?(T-9s1(8wf*a2sOCLxtuggqRaPyYby6(;BQOS+}dec z0%Z3E%JtE4y!DSC@mtqpo@s~E9!f>(AhWnQLWp{^>};CkzQ~#)!;WX4MEil0dt{PU zY}`D9{CxXeuCeH)%>WI!$`Gj;ACpBZ_%mt@2q7v2Q72xcu(9aeX?Lx7EJXZ*r!E*X z3W4KzZ}EB!3P&h@C#OO-!a)U){2}qMI6lj7j!JgnLoz&UCbRvYE)fUtF8me%WWlcI ztois{Ro#o6Q?uKyy|&QT-CYeD5-pq$7qZ9?vi7sPP~87VQ-yOx*drwX>8D zI3FsJrZ9nV0){Ko6!%j+jt5*jtMZSu1wJbK;%$@baK&?>E4ozsB;8C}=N_>P{;g4g zuTLyAE)dvwT}vb*n~)%2FK>rQqw#)JB47&L%g>M|7STFiA&+XAM(ZoJ|Mi|MP(G~K zQ7I+Dbzyx-8yH;fL{7MTc8=@?Sk)XI2Fm8H@E8pG7Rb_7i|dEL+%Gs=<#q`YNf|YN z&x)0p?9j~Sd*xPuYg$5i0Ffq`KZhb)%)c86=d7 z7a+kch%QD<&37sJ7&_JHI7dM68z5BR8=*%n8eG9i2bIOFxOeJqUZqX=TDbCf%+l$3-x1Uh-38^LN z7Z2c|k$T*~&7=h&fA?RL~x$Ax+$85syJBM^)gY$kQh@`(4#_dF8FWS0-))$K_H z6jd|x_l+kTR9ov_jlKOj+G^elFHd1L_4h|6=>auMOst?;WOakt>^Qrc$&2jnaRGL@ zYe14=@4rRrT>g>IQKe!6p)~Ye^EjM(52MpE7!s+^DPFb?7bXdVL3)n`c2^k_RmcF+ zp`UFyM<~@S!4bGw#`IPpg9_5X_ZJu2FAu@m_Gjzxw8M-LH;f`Eqx0-Qlw^ryIr>7N zcqJS2KNCrl4re`ce8&CDpIxN zA#K{?QQJ{%)vORDhT%nR%SyuzY|FP#IYa||NHJ9n>UM8wGX7*~HQ|nKk)>BgCbU@m zdUcN`LNa%(z!6NpQuqjk@A;v-Y%)J)b~1%bY<99D(M_ zPQj02=cy#H4kV#tR)EZMiI$!Uh1YNI&lv~d;Pym7XYCJRs`YEeX6Z!*0=%dRO3g|1 zp75fz-!DqsCi52-6XXQf$z2cwAxaubuA!ArkO5;R&H5Uw<#szFAB1#l)kwqbj|)p| zt_(j}yo7WQk;|#fTo0u#U$5>;6}TIOeDV0 zVAs|jotkkkS-f~GjLgzXq#PBt$1&v!);!Ux9})9F0bt&ZQUC!*>^jbXhRc_R!U<3h=@)e0nqV)l#x$=W?!kaN|W?;tYM+l6CIUnGOu+{`$A& z%8x((NS3446IVAkw;X!}aYAyj-uh5%BVIt^$sUi~9L@RgDtQCTXYJ z!R1*xE*Gc*Yv7|{iMl+#kou<6)_pbb5$5Gd!7A4bGh{tWc z`+7`Z)JK_p3@TC&hVz%wz2~Lio9b0a@G^B1u`CVks_->ODWcH0`1Rd?a0z#}%)6dU;|VD$gG>L2WRs=P@A=eS^t7NPWYvzN*@EI6hrZ zyF9&|g9j<|4Skwc-ZC`fx7+IvPw}fEpUh`b<*(0x*ESTG^DjlFmJq!%vDQ|*yJ(FV!(bnJ5>?9JOCDe@M zMRu@6N*8%EvKX#$snxgu2xT*MhAH)II!~VM04Wsz+L_a6d3)v(Xl_-xdBnnbwgVR7 zG-9gkEG#@|@Nuy~cP-h>?a(AK^K+1Fx5~+iSP)lVf)GN$Q$|hvO5Y5ej^jx`iUM^$ zQclRn-t2C%{c!vF`u)lU%gf(=eg6O`USwk$ zD*$t3h&YLEldP;YNHyBIB~#4ojE4bfbhZ{kk1co)Gj!lOfj9T)<^ziyd}R|&aYu{I zPEv15Oxi^WJ{aKZz#eTj%TUZSG-<(!-C)>OfVxM}GEpIiCIcwL%|mmCSOUsB#MYOr z4%j|8*=D>wIjeFTbD2PDeeliQ+`*e*2YyQa2tO(5}vH zc1BrXyIM!01QhCs*p9TRb{>gA+Km&SEJ!&^rDLu<$$i8Wp0ZN;-tr-K=B#~kBiRJwxCZP`g&oCRau|sj2nJp+FszFiLR9CvCK>Zt z%e)%NxDoXdcCBeZZL~KZh%>7Yv~QxEbp5MOON3x97Oz9j<({d0Y3|22Q_P7(Oq8#_m!(4t&bK@bTxe|$#xB`hzsG|HzUT_i0_=so%Z)MFq{VPmMbjx#jh<#uw zVVMdPJ=f4r9-8JAAUCR@2<-YRO)OgBClle~34|1?qZF~MolkY~07$GKF4Y zWFsbyOv-Oh$}bU-Nk|pTuCH>!)0#NN^-1!^LL8H8%T#ZomXW0MEW#C9BFLG_sWWQ*W(^e=aa(%pml$ zo45^>%`LblNr$JTTdJJ-f^b-K(}P(7dGB-^QA^m3-x>?{=_V1)K^3T=7~w!)Knt(D zpy|Nvg{U=gi8804n3u8{4$X*8B`CYVXb+MMpMMrC7z_0lQ>jupmU@oNHQMk7)-9vxz+*VX*)chZ0(ge?j`FoLIU^VBN!H zmY7f-AH%oQMGOi{<16a2Gyz08gQL{hG+H3}O~7H`5Hs?bNvuuLu7g1V zIBaEKT93BP@yK~KF=NQn#g(*QDI>1q>RKPR^s>HkuHIyO8QDfBvIB7&(oy=>j3~Jj zxbgL4e$+mmO+%H89-@Wu%oZI#kZjb}Tx-KzLAetaKZfjHuA=vT3?)RT$%~iFb18M- zLV~gWrmgeo6Z#7$f3r-!gtdllEgu{rq;tXME+9W|Xz3_^GQ}bkWl%Q+hO4!p(1plG zE~Sd`6mrWp9YK&qwL#kwPh4YZAo>=}xvx#@pUMrMHsWil&n#DVx%{v=y*xgiJmKMvgoVq66mg#r=zCGq{Yf*l(gZk) zwC(0)F!{Uf7zCU9^^a_mjdvqeU>_B*HYzF;&6#4hr^{~-^M_^1ejcHPcr_46g0!t& zU23O2pF{bui_cpvDd^!dCTX*o#=D{WB_)HGSJyY_Z|-4vsUkpZ%QRiveCk2++b2k; zK%^qd>8IZ|r)yu62n7_qK<1I5=xVj#Bq|IN;^KnCq;Rmx7ioi*9Bw>vIZ@3(mxWp5 z{H(`ZPEv<~f7s7`eOC8#|H&jID_RqHu-(OeEA)hRy!xy$pi^xPd3*ph{7{`}O_U?< zJKJnmgD2E%EtzoWLFj4u5_ZnL78+x-1jT}ce1;cYGfk)_K)mrzNsbU>y>t1p{gZ^l z7XGL6WG#^OWU)7U&1$T2DS6+Ki3$+dF!1T<{1CH_(33<0x;Y8|CY#?X+(WXnP%x1? z>pz`{)hbkXrl1gv#;QC+XCn$0oKiu?bzDc$-$=`o5w~Wv-6?wf0;RgYetKB^v%>n! zb_R5*wiJfw{rZKY)9j@O@?cJ4q@2n86bP|DR04>Z6k!Y=inxT^mv0=h`K=vsOJS~G^?V%Cp6{}WXEd!C13>6XH$T^=^CbNefzIl`@?>uE_qEAmGv;$vqWs2)w+DPXGJ zhwXG{`*IKQHnC*N)z=T}U=&s;g%wj2ZdVmbF*<1xCSF#d+Nwk-REsl%*oxKaO$SQS z1uYO#ekgyD24xwG{tD`U7Nuj1fEjM}#p=z_Sr%%^^~~Nn39CJi&4Hstqu2DdmbC$t zTsLeH9+})oW;vb-IS3bMTZP63^(s3P?9$xcE%%l`fBgjpc5mp7Ks_wA5_AH|Yi0$k zm6=e4H_g84iJth8zh~EdFsv_`G#z!`fy$l-oqa=LODnP`^VB+% z^!$Vj2~)%>)4oD7-$o?uW=zIBgttQrYW;_vdHy>4XLr$>FpV;(|2EQf;GDKGyVtTW*Qa~OK zVJ{Su{y^yxm5B*Ew2Pi{9<&wHmh$mst07Lfxf6spD2t1zAmwb$88p}0h{xR?Fu14Q z#>gb?g6a4tuMcL^#lgeh7oTs@V8Op^F*pTQh07$0mOp&L@2#e&+3(RQV2*xQ2glnl=W&~X zRZbyoN?OTOrf0tE&Ctu-6rz5SI7GLF-2Zxe_fV$;z^QGutdGx7WJ3QeCeKaK&9C-K z49x{$Rw(Q&yDS=J@AJQUbqfYTF+|+9Af_0uOWFD55@I4Di~^KrL{NMM!|6oU-QhjsK!w!omgBHrg}m|c>zT^q`l zQHe!swZ|~qZ%N&Hr7PZt~F`*B?<>d;}WRC0)OBQZOuP?^z;c!VZ!mFwz77!$!<%R2H4T^*>W?Khg(ZP zf_xb_+=itZ@w7uN%ThDZND8g5Zr(2`zvmcrY#|9tZr!j-!cy5f_10iSDqjY62>(Vf z!^X5WQPY8wwmF`@HFC4CklxKM!^)+%KE5Q0ZM`-7O!d}YpP;Y{8c^?_Z=A7ns8aIu1V&nZRi_SyMs$`S@tJjO2=!)jDX5hutN)~;TZ+Rw}A7^mfhk&gLJsOdsy6{QUiM2efs(8 zeQi4{M$wkL#0SS&T5pJ9BMsysAkN-1HO=PU(8$rgErWcF2n!dQz?hUhYSlCIUr8&% zRrP!kQqmH&-kemvdBr@_Ml*M(XvfWV(0r;~Mc&n<&sj`-SCtt|(Ss!`!l+Pr(P>g= z@_mg%k51LY>f5k)_GUm5#0OmJw+6HIJBH(Wa5xk=7w}PXt&}VTv*D;k#n}POjEI1T zsf1DJla$iq!_iFMGy zm##6XvaY1`?T>5EQ=#V7XPXCZf4ta-;IoZ>D^3BlZ)uJ$aVW6k_-xKl1(dvM_%lzZ zSRdw>71Un&E8B{@(WwZ))?8_^eI_6>cPQf{pPyp@j&CwnbpB58fSi0;I{+dfz zk7*f=+EmO_4FM{5#fmxYoShx7@SlBjc2-c+W4S`rL?#SXA0XR@SwsKD(uV4xZnDto zAV0|QQdw^$!);Q~mCkh6rTVG%x*V|H8GR4~OW<-*8GiAqT5V8NonoAUg6HBgK+0%} z+}?ZFoJMDY9SKCfZKM;;Ac?^0t8NP-bW~C6sD*?V&>*yK^Bndyw6Zc@CuFg>j>UYI zaF_U}=~t08aD#|#qw6=7=8W6RU2`rLaOoIzm@dy2P`nsUA_Djq3Av5Wf&j$ETsFT5*yP968u92A&zh zC3@I~Aqp&3L5@E1^4s&(IfI8S!O4h^g=6e)!?;CP-O#Y*0UK$CfRU0KSe=hsHkq=y z%Hhgm!kD^i*j=bOhuoP)1{)EUS7-?d9+w*AsOsOgp9rMShWe zjV2FUeHxW5U^(7n6y4fm;jMgaAg~Wd_0-1e04{FA00to^fQUb3xbqwx?RWK`J{S7) zen@-M<&X@-@YQn9b`fpj9Ve&OanjWfsK!Tck;-u{gma}h9E->aM1bbtggyu+WM&Q> zl;KxD5_iiyGEsXDuL39k(~?zTj=nDS3)(yWKfSk7dc(Np+pgQ__d%FgK$I8JiKqJ@ zDnYmucC3?7s056qhY0YbF$8G{-=3lETrkt5Y}j5zLr$RKqf-z@jo;lV+81sQs~cL9 zFcO>gw@+5kx#Rn5G%$kg^cnbBN;<2;Ub z=ts@%aN>6C$U(d*XJxjDFr@7StRU@3h*5VM<}E7B3Gv*JMyrmL^_%q!;Q4qvZ#E%cg#AU&2R=oeW1M(jy- z2wQW5>&(#Iec6~Z;G8ZLJH!~#lusY7}n7v6AAII@x%&8(-$mpq)?&` z6=DWL*}Tg@w1ooQhy+0@(g%uz9e9*>Kpp_y$uzA&2xj#pb=jDU+et8T5|8yI5 zJ@!`M`TP25$?6^+aqpk7A8wk2TmG+;-sfzGGet8TxQiAFa71sYW0c5;%)kBe1X9%H z9Jv8e#3J^2`bhMusD^!At=B)&x`Q=C=xf#t$ieh6f`Jiz$KbmA>+=0S4-nnYc5Au} z#Tvn@R1(iRV9W%$%+i7)m(Jqppt@}%mrY@N3kwc+5?GsBn>Gio*kR^SH_w8QQHkmC zVe$$!&+*&9d?zznl~6!@p*a-h>{i8@b(y!HEp?QwdZFj1w-7uxc%i97G#iAMBHs1 zgL=phrtlHXi5LwTMB#SugADyawL;pe^ z-pSb|a%A6@UkE!5b)(ioB?|Z$VWX;5nnyY$G+S)DVieoSuq%dGqKo2sse%HU2L@Ne zF3iN{nBZ^N>=>_0AsF>0#^kjgRt=>(!8UUxVXkCj#@g2Ji8nK`ZG?H&kyPn+CyA+* zC5Gd+fM{OHmZz9T8VOZ0KZWs_rK8%^_3nf6pDI^(O8~$`T>JhOpAiL`X$rwZ#b_%a zIciSy`iXi`K%o14#cnMFW?UeCiV>VJa)(q_cj z!IcTEfYBX1qYkDONHx$3id2G@Ot-)L&UIm2xOzh`)G~!a0Vea4)9sV}mG?JSpCJM) z7KqTEp+$+hFd>c(l?- z*^p_HX1rrb7%-7Dj${`NJQK~^he;vL1e1T9Hach^{iqsDePbV+soNrbXb07ru!&4E z30iPM9jY!XGYRVlcc2Rj8;5_aLjK2FPGo!02<8 z)^#^FOONOQqJ2PrPC1TR8W{wpbfg;08N5B&pPsk5lkxRo(&z)8ZM7(0oZCAH&r7(r z4hxH^?ajEJ95|^+sn=W{D1p=gb1BS}VNDuQ5FAnut#QpYoA2IG^G6F|CSF~zipLzW zVuKAI*^+u)g{vTca4? z(7dP~)6*0CbLk0g*`T}B2T%S`U-O__KE`;+kh_8R#Vgs(=8nT!Y{!FLq+(~rBu(i&!yZmPqPV>o0?l+F{La-uUoWG zq;yGswxQH{q>cbX2-(`=`Rfmf?Bvtt2C}Jes0$ix3EuUy8>pZXFY`8Gi6uRuicudz zZ-(HPE(TFIdL^Wm%_t3`|3L!|VOAS{jD?YM3^MrtWU?0F@_8;CMcEu2cUU$mm;q0A zU$L##BK=%nJ3(w1BkRy*&i+T6kM`#wc6C?|@Y2f9pb(A9(D0{2?U`dTKZLhPqHw? zJB02@yF!0h#Q<~@@}9X6sE${7L7lC|;=|R4 z4=lHrihWcEN)}#Z{8~Q(w8kp!RM$Dnm}?>RFy84TX@+sgn@6SQr1gXpLO44|;2VH5 zn=)%(-6s5=*2jn>^~X*b!#d@-mLy3b)S-sC5H%CB;JH)cSQl9(e?!x#3aMoa_?z8B zxnJ>5cS8 z23x))$Us6oa=tyKi=zk?p){a@klFacXoFA&Z-ntc(|Cn$5T37 z9c#{P5za%Q&`=-t$CjBS$*Beh85Djcgl?>_lxRu zA#*%@nV8CY?JvsgJ*htWQg4wB^oepOdOKUKcGSE^d77%}d2#ZBLPj9{;QB;{^nj9Q zxeMvTEZsT~4?iK9$i^&wpxGxhql0 zTodo*>wQljyF{Yt#gN{Ox?HBtG(Al;+Fl~>V#M^B$VU5&WOj;Iv^C+YiY?2=G+#aGE1^f{2}Ba(E}EFEQMSV=2d7p+u! zJ?#NgQ&HuMDP<@^bYF|<$*USwUNu(MmMWhxafW1+PK8erw;J$kW+NS?HkLWh5>c*m zsILpp3Lg3p^e?O3wLKUrw$vgx zLO;hZ94r~g#axIOnIS4eZFdRcw_JXz$* z6nr{{il6i zzWiHu-dpw-V$)koYc9^cTj{v=om8n8xm!F=wMLySrxjILw~9-@+0FD%xR24EGp7IQ z&PtQ_=>clz$f-Zty;I52drN^CO{`5rLj%krg^r0dTcQO$0q55pPb zn=pPvdct_AjmvO~{8Jd=vwN$GFV#`!AEa;kzW!o;7UiE}t06fZ2DayR(;MsWvo$Gs z>b%OzrIehkR)6LxGkPZ^AJ%&q_qOP3%mRwe%4T+lx#FcF;+^oJJF4aWTs3{#H9bvo zZu?2~EB>B)?|XWeodl$!&PitpPt_|eLxFmIM|Mvti0YO!bJvf|@R2?|XPSTEGK)2T znK|q4JC~U{PPxdNw0+eh%uK21s+st^T3wyEo~`r&y8D&%ia_e0nyRv?6~$Kts#ze< zIMa1PCr#Ts7j5^VxdT+SVAcEy^?&&#awSv4nrxRrmI+2Ih-@n|(l@EX=+ zuwr;DP;6kY=)hK?HX|3by|LO=B+C zaV=@C_QU+Ank`c*rBPBnVxsE8PES;4%lStAmfTe()oLlGyl;9QZL*0DUH3V(mxe!K z+hWZYmI+<*n0_$#=bcFyQlW8n(qxYEEQ@rd%sd()qrX9!ENJop*O``X5_2BiC3=M_ zhSiNII%83*!>Ct$Uu{#G+D3{vZqE$s-do*)=A?|K!<|jv_at8VJ{jwF?WGhf_UFud zuFXg5tJ%8m{ZY|bWSG0?Cg!@Zy`1Wk)zj(dEHtpynNp zlFt9gAN9~5c~c(xBYB}2F))ckU2L}cphqWFWT;CdeWcea>Yill_k*Rk1~LdgM-J8LmVm*P1I46+L;r z%gI#_unW4$aE3ffi#v5_7;R!R9Y5-VA6YR~?x~w7ZBDZ&k~g^)+roTQ-Oo-HiKqSO z(K6GSCWWnTFRPmjIl;1XvaX%)I&J#C{&IcA>v$elSu;U>gC6Sg?piY&IR|EQvT*U7 z%$+CNJXMCkr6cmb=LE6WwWmSeO+c18iV@xOg*?T-lsAu+Y#tZ0L8b_N2Gd4 zt7@i{OD`NanTE7HT&rNJQ{*)GqW)5=aHp2a`gDfmzD-Mw0LVQbYGut(SZ*Jbt7%l_ zs0nhi<=KyAlg;&{4oZ&6oLV)hfR$03T_Dx0b>!VjAC!(QXEKetzu&k1VDUC+wCO|E zY-Lg2B8PJ|NThGk8_%wDr~6Gdb)S^!oj3Qly0d><(bA___e=4uszqQ5*TT=}u(w4m zFPUe$OXrW1;@s)hUc#>TWlU$uoCV8rO-plsh2H7;m#jvw5~2@7p3*6UNwS`&p8TtF z05jRuRR?Xlfj(2x=_*U^CZz+!-f*RJN@)Xzi$Cbz8{wY*eLVnR`lOrO#HQ*>?lxYb zLrz^aJRDSK%f3^(i4FAu==c3IU}X6eb#uu0hKZTRc}rJGuBFPcCJid*^qBXZzLmmt z)^zTV%Gta()R~a(o$$R;pQ)7_nM@atb2X%jTn$qDC^8uB+dY%&?3kcyiiFvF9T9pJ z!jTnKWi!;oTW`q8S8Sp1A3sruV5iosC$5>hTjWf86NC3udAHPGWiwHgm8pJ8Z?{#k zYsBBpY2!dHMH%$IGgZK>ZDUrX&`TRSqse;ZPhUk#=iF4ZyvMYO4fP=v+iB^2 z_I^n-8=2^pX+Tm8OUbJ5YfvS!Yv}{^4r?EV9;R zJf6Zl>`qMoi$udu+{UHPBGxW*8|L?K$<1fgsx>cc(?nPPe1)y2lde!pQwf5*>HHmP>I?kC@!sZyH&w~b z`c9&67ZOdgxI*3_?JTwMNU?7y44H*pT>^3pa!vdf)sbRbHMvNgTH_`=?A%-Fudp$o z&Vb}S{$(_rY(futj8Yk|bhpVrg)t8^W}@fJ$O6IA%_^0ybSPRoPug30CXrM8sNZmE zdfwq*l|7WJ+XE}A)m;**hDr2c*-YZ<3rd51$@L*Dy~r729H4xZ-QH zu3W3s>jw{v9bx(>^%zdOzHI$05S?*e2$z~=IJzu(2 zRcKzQTBe*MedAf-)lDq}p&A_eYo*B7cirhZ!0OED5=**e7)|4GWXZxkYwBD&T|e%l zbRURZXnLx3%4s5yd{q+n#g^*S@un@kXEf~0jllA0-Q`ofC28*C(g{!RzG}%xzqjNH zCb`j{;d;4A8gnw2puZoxkV~o~_tv0%r&8~?#H89#BGxzHShS?E^D%tm`mU=hT`ubZ zk5G?ED6&{_Gi|o0&Z^xf#JPr?;Xz*&SPBqlIeJ}$>Erg?6X6MrZx}T%x4^ClpohHl zc~xbanKJ4(ntIbrq?<1DqLJ)P*rM^qnT(mif z`aQWKrS2`iX1iPcajdd!j^5^j3u~7&E^SzmT_Wo(XO@~VZHDF4`mJgjyT3O;U*_II zoc;jUMXh4J*eVlx@(}%^YW*guloc6BZEh|ac%cVFMFVNYSwNNaRH%5X zjZA&edj-q0XDZ6ogji=tI#e+@K5|qu>zzr4Qb;E@XXHGDJ@a%_j5&FSi8)n7>ocW$ zE_IYj(ld7hs(6>il4(##_iQ$sQx6DedfBK)Q>fdOyeZLh*6k)b=A4Pt>g>3m8|W_O z^pQ9n2QsCqNk5u92XG2>C)>5ry@>%$@BVIT=u_{uNmk`!^ump*W@27c zePU`^jsdc4qgv9@Y_qy#O-XKnthltgq{y1S{rjuO*vfREr2}Lxjoet8P7f;4uO-t< zOLmnPN6BL+BzqnzU$e$rE8~h)X~BIe^<`98U9a$0$SkvGZbr5JKxD`2js)*@VcOG$ zRaHEHRjAcq$xWJ1#@^XsMYofmZ6>erUl?quPp?j`rs~+FW7hkg-rSvZZ51xg13`3q z$YkwKxnmY%P%R%{oapbC8l1h}JquU3=7`#ASIYIkR#OGl-%zMa$%*$S>89`NBkOL}Rb zFf&NSnw=R`$V1&H$HrSI7&ZB%lUDV)v1wHjystQKK5cpTs}PT>XR6vxYwmBRf)~jl zBzZCZO=@>_GpXb8P2nPjo7t%O#8^C?xca(WD|a+gPb*}_?iejnK~ATaE=;eg%d9HQ zD_13h3?>sL{ys6)^R7p3W#+x)iE56y9Eq9>ZnDclA*UYnVM3m z7W70{>7mrim#BUP=gSLsdlk}->LJ%3uBuiKD_1#!)7Ueu)6sfzl}Sy!BF$BEr;)eD z>kOta-sz4-Ti%(sT*E?zo&^dJeuI%d6&J2w)nlcuPfHnPCs&ZVoFjIf4u4xyjZK$V z$i&nV``4_{Y|lh7dl{wTzWl4`Wuj{wlFoJ1nH~H?=;>7Q66T?wW-s^*2rEXL_};)R~&G1e^Ft!7WyMTGQB3Ric# zsk@l9hSMvuOB)u}QW8%!5sw zZe4XQ3*EZ@v|Id;Wsam1HN}qE^}c@YTKOpHiIT@E>c_6hpK3pnQ*?(KHFmj~Sf;2{ z$*$Tz^u9WMS$g)Lnjq&yd(=!>EB5o|tzttIn{ErKb5DaweqAqTTbj&fnRguDZf+>50`&44dz=cPOd3PBoR%*V~wzGfzEw zc8QM8rX{SYn0YS@*Tt>QyV*n^Ymxel${NfpKMqG{wN3)ok4IDsIqEZ|>&EVr;p8yV zo#@?(=C;!NL9fQV`gu!TT^hPCL$VdQS2oi)C$}`MUXUkDcsA;*x3aU96Gpo( zGjCDv6sfL~*E#CCfaKirPzvWM+aYJxPO5Vgo%Ebx1)H1E?_)ZVT}w;o0xz9jo>3D$+^5E7U{Tu>ufJDX zp6WS!6lHn^X>|{DRX`uSSr5ctDRkAO%{YoLX`4$(8jbqliB=Mgr$bXuC$Sq?bu{V7 zFwH#oy-E6ISKT=3zG22jpsEeZ=(y+J7&-N0oad292a>uzoC1O#nkMaF8tv-ICuX`5 zxtqDGy=sZQt6gXKo?lOSe_g#-WLeQM^CmY`*(atrJsK*Tm)flPSET@x0CZeAyDX74 zK^{Trx)s{Do$WXn*R$>;j1jt%jZ<9vCrQ{-p)wv7;YnBLSYgOU zN{JcGO%Y3YZ&9!bH0B?#*u);IUT!t%M(>PCE}myw%W1DN({UO0kpX$r*I1?=nxGy= zU>?$-{<105^zSyind>rE_p3;+pt(+U?7DJfy^OGVy1nlLamTMc>PVBdP;3byk7M<% zf>a{Unew(^oMJRq=mjnID}}cuGnBO+Rn>0ELfx1%rH87@IP;p8PLyZ2=j!dc4Zbrj z2ku%^%XJ2%Pr9(!r0HL&nITVHA199(k|_@MFCA+-ZQDFiVpHyE^F>zC+L7)NlJ>o) ztF+iqnq;ufb7b`xqiVp|X}S|8Osvs;*0urcZ*Q6DYIa4CW%WFVP%XZ!v)rlr)T^^$ z4pVgks=EYKFS-nL(9rc@P2$kT57tED)X_@2*M!mB#7^oW#YwEF4_=`_(3F$!}uoVvO?Z%$p^HILKe`1($Dy)p&& zs;gxw{OL>{u}1cz`^(&O<#qK}&w}yR*NSiBJ_jsO>>$Idu$_x}`L4XAv5)WnfBYS{+w`u5&;3dcrwx4X+(EvS&R_j45j zs!cl1H*?fmtQFb7IR~a5B(v|-?5{#kD>V=u&4o^1-{88tv~jl;c({ zG@LX&A!DSBOzVGi?CCPHNdDS%;KrGGATwv2=BY2z?BoOYCOYEO)T8m`Wt01_9JET6 z1SS;HL9?ucdJddqyXlFN3Pw?V^rkM|G$^*2{sm8)qHt*D67$Y#_I_=#E%Z0qldXzK zsfrc4ZVH_+y1ZfdK=l`RPqimnDdUPi)ZQt#TwCdV&^eQ&1kYHmuAneUQcv$Mp7Q)% zLCtNYe@NXNq@Os)zl9h~r{`vtq~+eOoK#Wdc32b6=Q6V1cFvhHppu}fsnY;7&rCB> zON~gHbJgl`SNaxkZ$!DTZ#$4C3*dMYS2G*wy-!O~X4~FL)tr*m)0#}TEF4m?nv#^o zdVTAn<$aSSrV8I>V$~_~Czj37J=@J6pske0o6LVV>s;{E7@7WEIZozRR;YVkLMHFk zzoL6nn@lj=)VTM5-j}GqFHhI*>^OkFZA}dg_)BBgfFl(J=Vm*(SksfsMvsv?LPd2% z`bMNiZ?8M1D(T-Sww*qu&8K`BW%14bvRA4~gzDks`s|n*x#URBxhXQRsd}61>z6H9 zkd}wxss@Yu3G#(E({^$UrKP89K)zp%x5^WY`w!NWv{guV-|LC`s=X==1N)dw)kbjj z*Qb9RzG{t>WS$-kZ_!ey#(Gmy zIz4&JlyT;UiB1q|LM)qoyV6zKHFGVBQL(r^yODX_=f^o=T<7lap)I*Gb*q z)*aOOW%9VYptGl+j5_Y>EO46l?v4*v$3@(!KggQzoVAE7^&U=!Bhh#~l8njoP2;(N zk>n_;lgl=86NXv^Ri0|AI-q9gBQBR`+{lCRO3KH~O3W^mzf6gyav**G3Z)rMUwcDO zp}nB?E$x1&c)6s>Tz08b(%SD%_s6~ER(a;D`59HuCHOhcAu(Lg#|(>XDsvYCKXt_&elvdBaII-FG?za&#4 zLGDT9g%<;o%+-_cbjPO5U!5)w4UrojtEvW-NLyM3^;?7VD2X1flL2&DM^%qlm4pk= zF6UtUcNXeXUFhEA?m|8MWv9@oNADDwKv#Sl9f3}5RwV(ax#|Du=C;ybq5Hy|JX%IS zCrC$*Gg7E4eXhH})UENpU?H;gQi$e0bXU_#-Fcy=2I}qfylD9~r+wr6&5}3Ho-nd! zCI_1;pS=!EM&q8br+2ft3*KqMswCxnzcVXb?^mIxQ6)CH6g^#y_S~sAr1k2$PE4D< z=^Wiq()B1i{hQ>X#XC;?Tj(>>d=V_eJw=OPy~a+Vac?q;Vz&fHKb=XpGBH4pb?O~Z zDOXm~@m{}D7MV!n-T$y_?k)r8(%)5i|}t6O2zf3lKMsEUUH zni_~ZqtdrRt5MU*<@BlQq%Jvvs%v=WTa-5}T1>pNL8tC4 z`%rYEqD~a9_J|Y;Qu=wu{^UF48LiXgpUUb<^(E>nrQ1Xv45nX|zg6`@zv9}=+X}26 zHHAVPp9ANPU_A~sa>5jO1fh;;y-ti=v+`Zi;h^BVQ{5Hz!q{$Z0*=&QlGBgrn&uOhHWg~prco7qTzxz3JdsfH=C#zWdF?@%tg z_sf=?p{92Mo0^ex*_AuXaQGTPx+-XEqt?Q&6>n7f^zo>9V0xB?-0NK-Q!LCh z3p4KGy{XS#vvXF$QCF5+Vk+#e6gp*&dfzFT%5QhKR=DBzUR+$pkwK7125W*V|8-NrZO)8TRYraE0HNG;nnuI!m)A$gsdSK{j5 zb)SAazE*hKwGztYuv${jNjAm5Sb9BFSzPQ(3mqc*HVyYP%UOkGbY`AfJ!_?mVMvFY zb9;p@J3j-z;of8pH8`#N@= z3txTCx$yntzOMSAJ_zS|Gli;Wx}vIbqLU52ut@PpFZ_8<;|7Dy6QBT z%YV3gLF@!&@3*+lyUGl0L(QF8s@jAuFVxS-sHSAPO-DnV4t4L!2r9Y@AC~J=Rmi?V zm4_CoeDM{og&*zwA9-f4H-^2<7kS&nu1bAlob%>g{h-h|Q>nr<&b(h*YzzIpzGIbv zd+!IEzOO%~`ti9K%ra%N%r#eo{wdvAB9(Mi4|WPK-*-tluHQ3@-xn)X{MmtL6_~2= zV?$~-x_5tzZlZVDQ*;()OEf>}Fs9!!(|)9$EuFdiEAzTlJz1|6;H`kmzDUZF$pPUY z!-`JT&D?!fhzv8*S1v<Zwp_9K)M~ zIQecN(^bjelULakC<#s$Ake!k4Wg!_asB~X`#H6L@=-m=;G1(P7mj{*u{*~qKIdA0 zT9TBmmFSdHC`6>|R_~QrZ_oR)?kisnhB+CM>M~K&BV0v?i;Bi z-OhA!%%+>kcld7Qbt<2(n@rr5G+X2s-fmK&`{vQ<3{aANm0G$Ec<7(a%8P8G_e|B) z>HYul5k5|01I1^52iN@eQ5TVh@h!pRrv$TjPzd9NcqF}HZ~H_6yN_t5U7 zd8f(kTTR~92klF{lA*iTb5}{q8CNtHZPOu2Pxp}&tga-fqgr>hNsU2IVx7+HEyCXH z@jq^E&Xeac?MC|*}DY}>D)+v%R+sr2VD4MA<%cG}NjY1blPBvS}W1VFQsE*i+($u=uq%}`jr=F2g zCB1Uy1n930ZF03If2UT_GC@v++}NrI(@b^Hl*;BbdG7-^nYekMHI?;hZgQ&38LE-1 zTUJSS=BzJhkf(|0f7B#Idw66?)HBUPv`p35)OpofeXcsUsd6{nKu5e>ud1`NtfpKQ z&2pQ^sQ$7*lv=((A`S}K!ArDeM?m#$rw*JV9@=Y?ChO8T31@m}a4$->f7 z*8A#5z7%Cswl_t%N7E!rmGtjh-`7@SRfIJFF=tiFEa4wV8RLmn9 z##WdC4|R)LGxbyRuygkp+m7y-la|d?JoU_Es?<8^6#Iv2>uc0ic`G!higycp-q&BNquKPMyJyk3W)Qjio?R_tbzVkR)~G%KcWqBq zKJ;;TsvjydoKj_)Z2p>^zKW&J=EczDF%|PH ziRDdYwu{d6AO}~>U+$PP zrKB%oHH}zz^-ryYuJ=`?An9u=ubv_It@;v(E8mq^iv6c&n7g*o$5uH}3g}Atzl{jp z&M5TLsF;+DSQ2JoUVtx?mTt#F1UuMbloBtg~3! zm5uZ_o3uAt&E$3-O6#7}PJ-Ibg6GWBzE4f*~{ zWr55=(%;&+bWpCoPAUlLbVEMBB)8PQSE64YT+-xq{UIsAo2lx`0Z{)@>8mg`(X0$= z(wH}g(F0)SqA_!9eM!)FR{alAY4Wy?!osDd5`%Y{G<$yi7iH5e3t#1Hr^y{$&0gTm zZK}V_lb<(J*O}9cxCuYqX)u&cH zQa<6dOLd7?xb`=_cB3uF^e#A~m@22yr%b)#O@LBQ967RLlv=RS>4GoPcCsac`X=e> zXi}DXlbp$KowI711g0`p7>X=!SXM804ZFKv?NlMxYFlz3G|Nm?X>#Q#>A_V0*mJ?j z@V~8NKsAT;)ur~kaO1T1B4}nK{RL{~GJ7QxnAOSAQQ ze_N&%-uC3J_Ub6y7e_?O$m2BiL~MDGneA591}jIEr zH+@VW-V6;$mkDRl781_e`rC?j0GNJ9|Ip4RH`QOJvJUlN z7d5Fz{l9RGg%KQm2{!4>F1)XDN!HoZAJ_d^s>m?YH)_gj%wGdl&-cibjzP6&CSjXe zybKqsB}{#1%|?Lw1y$ja>3Oa^*uG2jNZwZDOh`Mg@V-9efUJGJTSRohDy|{ z!FX8K4r2Ah0VT7_s>cn<@g0K`gKMSMlAT-9m|H$i-yso>hf=}B>_EFfAn>pJ*CNpJ zfUVoL59}ym``^y;N7>e`PiRrL^)`EL-8xW~lov9&Ozpfti&FV>Gim27&8-|fE*jXp z{TAcev~0Wiwyn2ov3-lqt#)kDrNvGyy0+N4#V##-wyU-U_q|?CK6m zc%Uyl$QK?Q*xK{TU{9!3V2Cdq@`b~`aMTx$`@$+B3jehV99Ia)U#$Ws_`R~o^Y?gpPq2^V3I zdv;F3Tl#*#jW68R7v9PjZs!ZP_l3QsSF1n=-}~G6!rrU5RbX4+`<;B@?R??wec>H^ z;m*GBj=pdgUw9{9xT`O$Y7&M2S_Qfl0`ga@z%IUUcVD=~7w+K;_waA`Z=Z9K<}l26$_)K!O~D9m&wOt z!Du)cOa(K^L@u7s)uocb+E^wY&&Bj+YJ_RQyjtllkOiPzRAMoW{4SZyki%0#0H zskp_W*}6n5SQkvi!kM~AzAmmm$PKSc0GWpep^G!u@6lF7Q-a3&wj25S?^ zY_7I8UTc1<&OD7Ut$MT62Sc&aa5RyNW$UC?nagG((P%hZn~z0<(NHj%jiw^(q>R5U zS++=hB3LR5Hir{(AjxPlnhhp1kw`u!_q0c%*+{T1T+1h9&ZT^aPlTiSOeB+!)Mi78 zU_2fV*Ve{jsYpg@rMYA@q<7BwKsZub7mEa=iA+2x$CHeO5{bM-S~MK4i-i-(m^}YU zMX*G=e0?-hnvcfw*;GglHj&NO$uUSrWmM|OxtP2dP9^Quv9N3(50%zt>Y}N5ZEdu! zPU1Nn4&`&RIFZW*Q;A?*GMJA`x4xXv zOr$mwt__-xEL<{gNqSB;E1!wPOCu@SG+P^uWOLElSR|Va$D`3qD4(j$hNWJe^8ZX& z4m^?y=4FFmE*MWmQ?;Q;OrHOltc@q+5aW@Y_fWzT8Kt>sEE9^=Wm1_;G!{+N#%eRc zNHU(StCdhTVGc&3N*et#g*&en{a0sRMO!vpmkq{(8MRyScx_64Cz;Acv+>%X>_Nnd zworO$Lt1}@`CvAZ$>lPsWIUM5gu=BcX}d)8iAXjqCnA`SnA2%~vOXi@rP;Zv6V-e+ zE^$|zO4Z6<)@AA>>hsA|wl#HE2k_{7thNjluSti48~)jpu7<= zC(m4lvXjZuyri6{T)WvwJ}lR7D6YaL$PE`Hj(Q)63?Y1UBtt|OemgDk}lL0)L^zxWP`b| zT)?4FG#blC@~LdBHWbQav-xB)oQRu4mhB}&kPn2Cat!%=I1-7)!m&s)8&`=?WhvQA zGM9--o?uc#R<_rvCs7)bOS&#ttFDRKR9!Y7lK7PKnvlJhD^^yXQkSUn3z1Z5CL9T= zJTMrQ>t0f6tz=|j$((Y@WG)uWo1`rtkTvquF+@uv@`+416wd~;;Z&qHQYTkUI49>W z8Hy%D#Hw00Pc{$B{=`FdlAQ8MiS&FfDPLcgl_Lr!@^Q(i@_K&?8DBV7Dp8-zL{qVR zD4)pXBk@Eck&!q}W+WlrFe*ElM+w)R9#Mf zCRvvcW%T#k&6A8IP%4^~JDv+yw9MddXjF++m5-TxB9zS4#e%s|HkgQo@^Td6 zIytu5m}H1%uj=Nf^=EW~jg_h*D<%mkBxgC4isvG+NK6X6d|h6yyf++;UJ~CwZW%jHnu4Tj^#{HP@C>_fR#= zmQsyWHAAJU;VD&3t))~)RgI~oR6AA8r=@H>i1$ONdLY#H6{va)RCpSy7P|`1!+Kne zhvLzA2A+dg;jQ>6zJTxGm-rX9kV=;N-FDa+2Vxv&Vjh>^fp|2YimLsozV}vq5TC`r zv5hn>)OOor4;+jstiVY)8|UG2T#M^)Jzj>7;EVV!eu=+g4{744{ThrZRP9>z`YJpe zH{j)XGv1F+{YoQE6nYP@X|s_*H7eKCT=aU9OX zIk*%L!4q&jUWPa01Nbbyg`eXuI85>kwO@PTA$UAqfj{EE*hQMY>T`WCjKi>8DREd$ zcyFcrKSOvv;YEZGRLb`232#KX{PpXS9JH!Mt+rDwW2I`LDpgBUsd{{sIzJOtf0hdG zjw5jl%IOKTl(~P5eW1mtn=HR62B_k zD-cL1dj|r;lzrrQ*>1eDZy+#P*)I^7t?VBN)baiTr9|Fx!bjqXc$RY4KwzVCU?6ar zQVOeUlyZ`9QkDh+cPobk0*@$z0o{M7j^k|=7C*wTl#(cZP|C^rQyG!53-ZzS%4i_a zNg0#&yfPjL^yU3Q%7koBJn3{O-!G?F|Bg`DL>USk-v^IUnF6yv+wRR|$;br{Q`$A1}qL@kX@i>R!SRui#okY z{QODSmg`%n^C7Rd$4;ozpS<3ka38egzD|eozD|dtPKRPS+g0KOoPx73gL7~p+Iqt( z!Uy5ucr2cTXW$0B5HH7T@n*ae@5e{+X?zi1$9M5#{1U&%U-55L{pKn@ZM~%fVOxLc zO4!z8`VqGEnFwK9ui2BZt>26#Z0kAG2;2Hjmawh&G!R~ftML##5|77I@GRVj7vq(9 zJ>H6UAui$CL^*g^_gl}@+7 z_Sgx#V0Y|;12KdN9ERmsi4$-N&cY1N!G*X4SK&c;I39~9;TgCAFT~67TD%$W#QV|K zho2_=BEF9Aq8cw!@%1HskH6yI*h&g2ef?txRO3tP^{&_h`{7`WU<&ud3LJ}*a2n1= zTmPO%xB-{pYCHsw#N+W4JPS9X8t+p3e-9ZzK@^c*Z3p;j{o9j z)aSRtj@TKyVJ{qjLokMF98Mia8IHzrSc5Y#jX6}~cIxxCeX@eEZKtdyd^E1Z(@>4` zsn4H}m*UlUBi@ep;=^d$InNV*72n1W@pJqZe?~QqsD8hN)V-BkV0-L@w!PGya356T zj_PwEOyDpq$4Z=lQ*ah$(6+M{5?+F<@E|-Kk3}`UsrKUx+<+J2<#;XH_Sv0;@5e{+ zX?zi1$9M5#{1U&%Ur~*xs{Lst4Hjh^?10;2SL}iPa4<$Ng?pkJ-%{T@7AN5}oQ+wW zhYh$4SEFrj9!c1?J5M2e7H-6g@k+cNZ^gUuL3|va#h39-d>=o>uhF)5eZ$ z$G;VJ#Ln0ad*J{af-xM5WjGqgVGYi}H0DsZpXEF(CcFaI;95Ky*Wqbs+u7$6z7(&< z8}W9$7azta@p*g|-^LH|bNm*6#y_!z44JF+v<0@uPS^#zV;>xdAxz*fEXPWmfKzZ5 zO17?l|39_o{M$*BUVl!SgnwN2D}Kb#2e7IQ|}}EC_aNPo%c+izg|{XgdQukc6w18skS z?Wa=n%~bxe19rpSX!{Amgi|;iN2BhqlJBwo1AFs&7Wc!&xDsu@z!8LxM?1c-p6~@| z`va~cd@J6A593p4`vKk{{NJV%yInittrMylIx4<;V1Kmf*`_1=`TvwJ_Tuvc{=@hD zPxqs_^eoj!eH`u4_RDr9+!O77{kQp~?RT5W=QB7L8*n+U!Nc%aJQ>fz^YBu<25-i@ z@Iib6ZU5S9gx|$a@N4`DZNFMe(n}lM20P;}X#3L!5su()ScYTJ_M=TDJR5Dl?tH>( zo~KG@w*Ty4!bjo>cp9FAw%_au!q?+%crWVyUip68U-lBOzlk5<=lC7kezJcE+y38n z^zZ2YU-{mh2;2U#{)C5M9EagZwEbe$glFKsI0x&|_JU1`UX6$1(Rd=-eyVc`UyN6wnxCxF zhaIQ2{Zo(e`m^{7zKtKD?U(w2@b9PwPSp2qfm@^PkJ_1VFWePFm_*wTrRGJe?;D4c zaTeC1?SHc4z-s=q`rLtdI39<#-|1|^=i_B~E#88*zv&^uPvQ$`$C2M7Z2OtMA^bD` zg{|lpX^XahX-C4{u`dqBDB6Cd5rivoB2L47(Do-SAiM`Z=vls`hxKH_#6Ivd>&sz+Yj^!;ji&0{1eq&AeGK- z|IaprJL4|c2M3|;_t}kb8IHjTI2CPwPaWa;*odp}V6^=_ClEdj&%q1P&O5OEJGb%r zz4!<|jW40?*ZF|(=lC7|ivOVP&uK@0L?_${dtiUG{Wx*LcD}<%!ei0Sd$9dC`||o6 ztjA?|0NQ?=V+fywXQG`KaS37DUvm@TJMjU09G^qmPxB7pkMS$~5&uBjKeJ_9lb$-@ z4%iKQqwSXo6Hei99F61A_Q&i^IE(w?VqA#_;SqQ|o{H=70=yir!&~tld>Eg?7x4{z zA3wuy@fZ9XTT4SpU2j|Aw%7$rupgFU40p$J+zTh+bWCF&7vfS}jfdjVcp{#G=i2&`8E0WF&cj8x z0uRK)@i;sM&&KodGQ1XV!MpJxd=g*4*YQ346u-fr@h@y84O$gXZLuTnh~2R-4#p@B z#SvJE6LA{ugE?G)OK^WY1dqaXcsg#ti||Uk0dL3q@KJmQU&goaL;M22$KUW@++5;F z#Z!CS4!dGc9DqSg;2v0kRak>FF@sX=Fu#ZY+uvZ~{z}xXYwDbF(A^bAFg&*Pxen z;x%|P-i3C)-V=nM$Jg*(`~>Yhy`KpGi7n}mYlG^3e^pPg^YeBg+y@6?1b0I_FK-Ot z2{;vJV;$Q0c#VWt;lX$$o`7~9-Z_LX#4GT6ybbOAyGIDy`!rr6{3d>YcHW&@uRtCD zulNscM!#G;wDawDBHRP};}DFaoo6?a@K~(I8MrUn`E~V#m*D}p7LP$YukK938}Smn z8gD{7pY8#|kK=RrD!zku9^F@jf5bnq1^sdM{u4WYZUjpu zH`@7f`w?D@EAb#a0_{AxQwguf3-EHh4(K;C3fmj(g!GoQ`(>Tb}SjT#BplP_*;jP9%H=o{JabRcPnC-9h+% zd<>t(SJ2LL`-t$D_yhiqY6%H-{n`0#TNB)2A(avY9B|Hxo z;R-wu?L4;Q2%myy zSUefe!t>D1FS~~D&3G3+h)v!&mVg z{20H&AMp=tL4Vwq*a3IIZrB?KVi;3697p4LoPv8}7Wc!&xDpS-Bk*`U71!ehcsX8& zx8gncFg}GZ;v4ureum%TFZegMrax{g+!niF3HHNMjN$HBj(g!GoQ`SC<3e1DtMO1g z8c)PC@LaqYufiMg4!j>9!)Ngod>cQ)FYyQb9o2$_y8OqjaeLeud*QAa!X)mAqi`Hf z##va4^KcQazytAcJPuF6v+;bq46nso@NRqvpTrmNb$ky$#c%Lu{0m!kF!9tDJK~Pm z9sA;7jN(unft5HBr{O-B!v(km_s2uw=wb59=F4;*b@g}5EHluR$vv@;7rWmTx`JQxCRfyWAS7>3(vz#@fy4t@4^T1 z349)3!*}r${2G74KT+Ltt**B=xD9s3U9b-h!U*n$WjF>W;8dKAbvPdzaTOknN8$;1 z8lHm};uUy3-iG($BltAFgm2;p_&I)uzv4f*nM`0;@zf4G;ZE2C`{NLd<1ieFW3d`% z;J!Eq>v0(#fNSv>JPFUljd%%OjW^+)_y9hR&*7{14t|VZ;g9$SwvY+4Dt@-a4!8q$ z!`?U$!g?8uPdim*Q$X6pzLe@eDi{FUG6zM!W;> z$H(wld6Eip$8*n+U!Nc%aJQ>fz^YBu<25-i@@Iib6pU2nmUHk;U#-H#{ zRBNWF>#YrLgPm~~?1O_ag1ccEj=>2y6=!1|&c{Yvg$Lu2cmke==ir5S1zwN0;l20> zK8-KooA?2Kj^E+0_z!N@#l%xP?1VdE5A2UaFpk4;B#y;uoPqn|9IVG>cmS@&WAG$A z6F1@|cs1UHcj5#1I6jB3;yd^;euY2cAJ{@}l+ooscEBC58}`P57{(M1$I&<*r{Lb0 z#r<$GuEc}z2s|E7#r1drUXIt{t#}VUj8EZ<_y)d@pW(Oo3;vC*<%Szw{^PdT1xv6W zmSPNd$8y{YC*gEVV;&dcQe2IP;?Z~_o`L7$#dsCoh&uJckvVa8h^q+QLSmF;-?L6gPm~~?1O_a zg1ccEj=>2y6=!1|&c{Yvg$Lu2cmke==ir5S1zwN0;l20>K8-KooA?2Kj^E+0_z!MY zV&bVCcEX*o2lmGy7{_5a631dS&cJ9!)Ngod>cQ)FYyQb9Rs~g{A__+-Zjiir?VR_!qY7ZQ`jd zcElaAJNCuF7{#GD0xNMMPQ!gLhYN5C?vIDyQMe9I#|?N9UWqs0?RXzPiqGK7_!fSM zU*Pxn8~%%%_c8I*9=F4;*b@g}5EHluR$vv@;7rWmTx`JQxCRfyWAS7>3(vz#@fy4t z@4^T1349)3!*}r${2G74Ke1(B6F+Tm8|;j`U>_WW5!?;Sa12hssW=!AzPw^Z48UMmo15G@&#g4cmcE`Rr7^658M_?sR#A&z> z=5PTn!Ts?NJPOz0>9_$e!YlCxydCeuNAVea8Q;PW@eBMOf5U%q^Fbz_+T(WE6?@_U z3}OQJzzVFw8k~t4oQn;(9M|Ascr2cbXW@BxDPDs&<6Zb5K7r5UYxpjHf?wlL_$R8h z!FBr|x53W13--Z57{T4J49DOEoQkut4(DSduEK-yNIU^g!*lRLyaKPs+wfj|1fRy2 z@J;*xKgaLzSNsPzD>d=d4m;sa*aQ3H5RBt69EoGG8fW0XI0x%-86JRZ@fbV_&%}** z30{pi;hp#ZK90}ftN0Fnj9=l8_y@KaV&Z2@?0`F9H|&iAF^nl3j-zorPQkq~i~HeX zT!{za5qLbFitF(Lyd1B?Tk#%z7@xuy@eO<*Kf`bF7yKJr2TeR}h1+5mEWv(QiZR?B z%W*H9gwrvN@-QBKeJ{kNxEc?|qwz#M1JA{a@hZF#@4)-CYu`PaK8ga5B!qTAYWAa0MQShvRX03Z9MU<7Id)-hy}I zL--`VfUo0w_$hvaKjUB6Dr}CoEq25mu{-v~!5GD%I07qiB2L47Foz3p3GR=F;8C~^ zPsa^-5nhQm;O%%HK8nxa%lH<4h+p9M_#6IM9 zI2RjmIj+IO@K`(<&%*QYQoIIl#=G!Ad;*`x*YI8Z1i!|g@K03F@X+NyZiAh17wm(B zP@d4HKero};TW8NQ*k!d;e2ewRd_HSi6`J`cn)5OSK#${8{UhL;M4dLzKI{;=lC7| zivQqdF>}1_uoLctJ+MCx!8i`XkvJBsaR%;-bFdzl;Q_c7kHM4hOx%c<;MI5&-iZ(3 zhpg1^31*?uUzUB_4!F;PH4W zuEz`Ta=Z?2#e48!d z-@*^^3;Z5`!+&w}-Ap{S$L+8y_QU}g#02hv6UWEH}M1f9KXX~@gLl5cN0(TuoLctJ+MCx!8i`X zkvJBsaR%;-bFdzl;Q_c7kHM4hOx%c<;MI5&-iZ(3!AzPw^Z48UMmo!%aN3#g4cmcE`Rr7^658M_?sR z#A&z>=5PTn!Ts?NJPOz0>9_$e!YlCxydCeuNAVea8Q;PW@eBMOf5U%q^D+}p?QuKo zial`v1~GwqUw1f4m;sa*aQ3H5RBt69EoGG8fW0XI0x%-86JRZ@fbV_ z&%}**30{pi;hp#ZK90}ftN0Fnj9=l8_y@KaY2s%~?0`F9H|&iAF^nl3j-zorPQkq~ zi~HeXT!{za5qLbFitF(Lyd1B?Tk#%z7@xuy@eO<*Kf`bF7yKJrSD1L(3b(~BSc3hq z6l1tMmg8PH38!Nk^SBU~;%Yn;kH!=63_KSv#;fo~yaVsY$M9Kv1>eSx@Jsvwe@FEI zXx;wDt#NzY8GGTb7{Vm(iKB2FPR3bSi}P?1uD}EFa6ArA!L#vvybQ0!Tkvjt2%p3k z@O69-KgDnGXZ#CWjW+Sr7CYjO*d6=gV2t8W9D$WM5vSokn8O9Q1oy{7@F-k|r{e~^ z2(QE&@OHcpAH`?zWqb=i#4qrB{0;xb&BvH{YLDAtSL}%cFo+4<11qo!Yj7rJa4t6B za$JLl;jwr!o`vV(rFaeAjCbLK_yj(Wui?A+34V<~;h)&D(!@_2+y*=2F4zYLVFY)> zG8}^wa4OEmI-HM5quh7!Z+~){2af-U-2K@Y%dc} z?XVN>ggvl74#7AM!;v@^t8oVIi*v9Zm*D}p7LUP`@J!r@m*CZS6W)mr;N$olzKZYQ z$M_Zgh<{*skk05z{~ME zycO@khw&+V5#PY~@iY7uf5E@8b(M*yt#DiHf+g4wOEHGKV>#}HlW;nwF^>yzDXzvt z@n}2|&%kr>V!R4(#5?eQd<>t(SMY882*1Q1@OKQ1Gx4(pZjIaH&e#ig#SkWOPaK8g za5B!qTAYWAa0MQShvRX03Z9MU<7Id)-hy}IL--`VfUo0w_$hvaKjUB6YP^Z3w%8GO z#O~M^2V)e6;s~t7i8u}S!5l8YCAdExf=A&xJRLXSMR+CNfVbm)_$WSuFXLPIA%211 z<8SycZa%@pQ+wPFyJAlqfI&>)9$0}@Sc5Y$gLAO~m*W~d43EW=@hm(KFU4!{X1og@ z#3%51d=1~lPw;E}3ID{F6HWZI!ELZJ?t*=A5Jqq}EWbC43V>lTSdBAq zUz~&WxC{@#wRjAkglFPLyacbtoA6G203XNa@Kt;VKgO@{NBjd@RGawO5>rMMao#iQ{=JOj_gi}5PF5%0kJ@iBZB zU%|KWBm5G7z~3=2*~HHlxHWE%J7X{06+@WBJ#iF{!^t=cYjGYf!WDQR9*)Q1DR?%X zkC)-KcnjW*58;#e0=|y#;ivcw{)~TNt0^X)+G0oC5xZkw9E?#MiX*TRC*m~R2XnXp zm*D<*2p)y&@O0dO7vYt71Ky7J;iLErzKn0-hxi44kH6u+xcO8QPwjC#?20{c00uFE zdte1tVGYj249>*{T#jq-FgzAd#v!&mVg{20H&AMp=tF~h{qme>Jzz;4(Z2VxjgI2=dgc$|WJV;1+r#kdj= z!Xxl_JQdgD1$a4LhqvNA_%J?&FX9{cK7NMZ;xG6&ww`I?X)D|oyI=|S!%~dl?pTg{ z;Ut`nY0Tq7T#BplP&^t>#53?*ycn;-8}SajA0NYK@fCa+v?c7azf=@g;l{KfurNJNy;@!OhYpp4wq2+zESN ze;k5w9EKxtELP(T+!yCyJubroa4jB#C*hg65ih~3@g}?zAHc`)IeZo0!H@AP{1N}a z7WRF#03+syUlb?dglG^dER@jpU3`fZ7r?6wIxe3#KBJ3 z69?dM9EVeJ4lc%3xDj{YemsU}@e1C;hxi=d;TQC(ZI6>1{V_TQVqy%!4457BVNooN zm9Qo@z~M9uBwoPlco(1GEBuH?9eaFyFf2yKm>3_E zVH(Vgxv(G>$8uN|>tJJSg&na6_Qzp37N_8BT!bre18&ECcofgzWxR1M9!PU44cj5s&j_2?y-o{7x0^j3TbTzQY$qyr942+9OFcoIR9GD+}!!lSI zYhgodf$gyy_QfGM8YkgQT!71Q9d5-vcoeBl7+YaS?1BAp7>>m$I2#w?O5A|k zaUUMVGk6(q;sbn!Z}Brao7m$N8Y5sdjDv|VC8o!0m=}v+DXfS!us$}!w%7%G;~*S~ z6L31t!=<17+>NC{D$65?Qsf&kuU(`VNy(unJ_07z+zYyt6*(x zge|cHcE^4=6vyCXoP`T<1+K?!xEGJ$X}pBL<9+-C-{2>7G_%Ji6o$vB7#kB}3QUJt zF%K5Tl2`$&V?At&ZLl-;!htvf$Ky1di%W1dZo-{-0FUE2yo$H+5x&6p_!V8v?Q!zM zh!_LoViHV+88HXu$KS9FR>oS`5L;k-?1p`D2#&@{I1?A(a$JX7aStBGQ+N??;5~eb zuklYbTiD~{i{UT|#=-=c9MfVJ%#DSx1eV8YSQndMYwUzQaR3g-aX1y{;9^{b8*vBj z$76UFui!0wh|lpIenGF6_BgrGAERR+CdMGlfY~u07RAz732R~lY>w@)EB3*`I0`4? z44jY4a4l}Z-FOI3;sv~pckv0n!jEXQvd6~z#T#cJ>Cmz7# zcn+`PZG40;@I8J-S37&0{4gTMz_^$MQ(;ETf%)+_EQ6J?7B<8d*dDuKUmSv?aT3nN z1-Km7;a1#(hw&6%#2a`IpWJ262s2=I%!fs> zG*-fz*Z`YjJM4;ma4?R-i8ur2<1$=}TW~iX!jpIbuj5^Og0Ju+8XfKN@xibd8DnC6 zOonMNGv>mASRBh?Rjh-Ju@!d29@rm;;aHr4vvCow#0|I|_u)}IgO~9pKEP-A7C)o2 zlRZwMF#<-zIG6}iVtUMmd9etV!irb}>ti!)i(Rlc4#JT*0jJ|UT#9ROGw#BJcmmJk zHN1n5@g;u1Z|L3G9;YxE2?H=5CdJg433FlrEQV#V3f9I(*b+NnckG8laSTqzS-22a z;CkGKd+`XK#!L7+-p4=i4SqsL7khj{VR(#+u`wa0z;u`u^I%~ti50Lq*2AXQ20LRf z9Ec-uJWj*8xCB?@Cftb!@Hn2st9Tn9;R}3^U(wao9w$GHh%qoOCc#vg5p!UE{0+-s zWvqn_u?4orZrB%x;AotLGjRbf$91?B_uyeXg%|M#-ovN(8vjJIn>{|h7!IRgEKGpO zF)e1n+*k-pV0o;Db+HMy#!lE12jFlVhf{G5F2+^35qIEzJceiS3f{tp_#EHi7xe0G zkCPkyF**ifVhq9zm>u(BQ7ny>uqHOZ=GYFqVjmogqi`b5!1=fg*Wwo3jfe0gUcl>k z7oXrO{D?*mdwhH_EJntd7$1{i8qAEjupk!4a#$7XU}J2B9kB=Y$6+`Yr{HW{ge!3a zZpVFi6wlygyonF+8NS8O=ETI1iWN8r+P#@F1SR^LP#K;A4D=AMhJ`_p-++3`W8LjE6}vHDM7)xRWtd8}tDYn7R*b4{Z2po^oa4s&v)wl_F;sHF4=kO}t#z*)9-{V(w^|8mv z4jEhMy6=uX7m>++`GFTaFVMARS9U~){0Sui&i!V*{>t6^Pif~~O=_QU}=9LM2QoP&#T6>h{G zxF3(-r|2jeK5 zh%<0LF2l9B1$W~iJc$?ZI^M-6_zFLw(cc~)9}J6;F($^xWS9msV=gR+#jzY##X8s+ zTVY4+f&Fn9j>RcB8yDe9+<@D0A0EXsco}cv1AK;W@iRIH*y9u$BVaU)gNZODrpIiU z7mHvitcW$RJ~qR)*adszARLJka5~PzrMLz+<1RdiC-6L8!#nsGU*ZS+hTa41aSDTx zFaYCWQcR7RFeetkVptZdU~O!KEwKZ3$9^~z$KYg~g$r>7uE%Y-7mwg+yoA5wef$I8 z;3sqpvd1SBhR3KF8xvv*Oov%94;IFfSOKeJJ#30?urv0;fj9!k<20O$OK>%A!ku^k zkK;MKins9*zQFhR6?i;##-1ATVQ+ahJA4ej>bti z6BpoeT!&k64<5!-coA>lJ$#C<@lP~|*yH1i;V=ru!UUKc(_$9PjfJoTmd9#X7n@*f z?1VjW01n4-I2GsMVqAqAaR=_lV|W&?;4OTJ&+#38L9e0qIJwauqhlZ@#vsgq*)bm$ z#nM;_YhnXzj_t53_QAn83Mb+WoR7`dk7x|D$Hxc5Vq}bo z@i7^u!OWNo3u19BhgGo-HpW)i5qn^N9EM|Y3eLtwxDq$ucHD~q;amKS z&f)erg~kXN4dY-UOo{0+8|K9#SPCm*4XlsNuq}4M-Z%(H;sl(I^KdDy!Oge}58??t zkJs=HKE{{$0l%U52z#8uU?dE{c$gGZVR1n(VjJv? zy>K9o!0|W@=i(Avjhk>M9>C*x4zJ>Ee1tFXJ$^;kD0`gzFe1jlxR?Y}VMfe>`SCX_ zgO#xsHpCX#9=l;*9D<{963)a0xE$BvR@{S!@f2Rf8+Z?&;%od9&C&Mw_+mJWg0V0G zCdagx1#@E|EP>^*8rH=o*cv-wPaJ^5aU4#?Ik*^C;YQqn`|%i_#VdFVAL4U-hhNZZ zj6F_n^vCEJh>0->GhlYihefe8R>GRt0Gnew?23JGFpk2BI0NV7GF*#Wa5o;plXwBI z<6V4$uka%pW9{+r!LS$^V`6+vhG{S}=E8zl9Lr%iQ6?ViP*dK@CSe$~haS^V> z4Y(cm;ZZz;m+>Y(z-RatKcjP;Jx-x90!G6)m4`vU9dL} z!jU)ur{g?aifeE)?!tq30?*?$yn~PNC4Rtf=sn&Zr!W`^127&Y#nhMyb7BE3hGnq| z*2YHI5<6gb?1w{f3{J*bxDZ$1dfbM4@d%#AOZYq9$3O55enQ6tdwfD+c#Mj%F(IbF zbeI+MU|}qY6|g$i!=~59Nf|3q_=JwCn|4x?Z!On}KTEoQ;oSO`mCd8~$Yu?e=uPS_I%;BXv=Q*jP1##Oix zci?_JhG+2#-ol6Y9N*y=^qOpslN+hP~&je~F`PQd9n50~N^+>E>MAfCYUcn$C1V|7dF17^p3 zSQJZRC9H`JusOEFuGj|$<0zbnGjKjG!?m~tcjF;Ei5KuX-o+>Q3O}MT+a4bu42zL5 zCdS8Pmp_w8E@hPe1>oF zGdkzk;}jYrU^I+_i7+Lm$84Avi(o0Nh&8Z2Hp8~q1$*Nl9ElTfI?ltTxCS@lE*F;emE4z;AEVI z3vmUm$8ES5kKk#%guml``~%I8L zGxoxPI0DDxG@Oe|a5Zkiop=C`<2k&FxA76a!1wqSUGwd6^23N21LI;6OobUS2j<7$ zunbnlTG$X!C65pUoXNF&U=8%$N%cVsR{oRk02>##Y!7dtiSYhGTIG&c;Q! z5;x#>+=oZ;3|_{Y_yC{bTl|d9#r8Ob#t0Y<<6t68iRm#L=EWjd3M*m_tdGsGEq1}) zI0#4L1e}iZa4D|A&A1B>;t4#D*YFNL#+Ud3zoGXMdz`{xBn-fKm=sfECd`Qiuo#xb zDp(sEVN2|Q-LW4I#W6S;XW>Fzf$MP_?!_Z`8ZY7Rcpv}3H~0x1OYQLqh2b$O#>Rx0 z0@Gnu%!7roBv!!cSPz?G8|;j|a3GGr@i-0V;u2hqn{X!{z~guhui|ZdgfH+tenr@fe=PD|ibZ;&Xh5U(joXJx*@)$LJV{i7^N>V0O%hMX@wi!kX9sn`1ldihXb} zj>3sJ1Lxy1T#H+9Hy*;1cmc2DU3`MC@FN;4?eX!!uoxL*Vth=7X)rV9!h%>F%VAZl zgN?BjcEldoABW*soPx7)5w64yxE=T5Q9Of}@g_dNXZRLBqjQx#PN6XZM#DIm2vcHu z%!YZf2$sT%SOe>0Gi-}pus06EkvIXT<2+o7Yj88}!h?7M&*L?`gOBkge!y?&z1kk9 zFc=8~Fdinw)R+l#VgW3MWw8p@#zxo@J79P0heL4;PR3cd5Le)O+=hGc2%g4E_&eUm zKkyBHLdP0=d_rM(jEb=_A*R4|m=*J2VJwLiusYVmrq~8MV=o+tBXB%U!@0NwSK}t! zi3jjFp2Mqn8z12de2-tzwbmXdKa7YmFfJy+RG1NSV1E1!%V1@!g$=O5 z3AV;g*b@ifa2$tIaSkrVRk#s%;C?)YXYmT&!iV@A-{BYZT5pe&8~rgl24Z3i!VH)l z^I=gejg_z_Ho)fC4!dF>9E_uIBF@11xD40g7Tk@8@FZTq>v$KR;4A!y#s+(Qd@w9V z#+Vo%lVKXnjJdEN7RPc}73*MQY=s@M2lmHdI2NbiY+QsZaRYA0eRvel;AOmt5AYej z#n0&6Xpd89jDXQF4kp5sm>#oXUMzy8up-vL`q&KHVi)X9D|c_ z7B0jUxE{CRUOa-Q@e=-y_wf&WgP+i`*&d%z7#^cyY)ptLFdb&aJXjb@Vg;;@^{^?n z!OqwV2jU1EkJE52F2U8f33uWFJdWq^D&EFN_yXVKS9EQ$$H@;PVhoInNiY>=#2lC( zf5S3Z8EaufY=P~u8}`K^I2tG6Ok9A=aUE{OJ$M*T;YGZG_wXsc#y`>AYLAaEhQlZr z3lm^+Op94CHx|MYSRSikU2KA_u@m;h0XQ7T;Z&T1i*Xfh#2vUFkKtLog17J?KF4?X z1--V}wYUX$;~_kW z7w|gX#V7a*KccbS9v>eJi;*!V#>Zrs1~X$WEQrOi99G3T*ce-3N9=+9aTt!pDL5M! z;Y!?q+i@Qr#WQ#rZ{h=dhHvpRI(OLP6dEI7G>n6ZFeRqPY?v2|U@5GKHLyN5!?xH3 zd*dJ+i4$--&cmg+1~=m_JcuXoJYK^)_!wW}2mFTKJMD1_gOM-*<6%-vjhQef7QkXy z7OP-wY=kYb19r!LI26a=WSoTyaRsi&ZMYYY;Ay;szvF%U1K;2$bnLRnClrRqs2Cd) zVhT)$Suqb5#*$b8t7AQEifynn_QHWU0>|StoQq3vHEzP4cmR*%IlPLu@e#hj_xKfE zyX|rE!-yCI<6;s_g&8pi=EvW#3|7Wk*brM_d+dgNaR`paNjMW1;Bs7tTX7E_##49^ zZ{R(Aim&lcH22u!kn$7R-%>umqOJYFHPWU~BAzJ#hdI$8k6n=ip*o zg&T1P?#E+z7O&tfe2CBS9ezQtz4kb{(I2B@AST8j%z)W39~Q;ZSP5%l18k1%uq*b# z!8i&h;tZUR%Wy4j!QFTWPvQlb zu?{xIR@f1HV1FEjV{r=3#znXiH{f>Mhez=YUdEgF0H5Jo{EW{1_Be&c2pA3HU?NP3 z=`kDT#UfY=D`E|-kIk?xcER2_2uI=soR0HwDXziIxC;;B2|SP2@D4u4m-qp{q4xoM zoWfuv48VAp6jNg+%!viC7?#Bv0?I#UpqcFX8Wa zAOFBN_z4{c?ePhP;V~-4#)Oyx(_vQ3gN3mqR>10551V2e?2Nr|AdbNCI1T6G5?qa& za3>zX<9H6Q;%$6{FYrBnMb{yFocu5%#=y9k1XE!~%z^pwH!Opdu@*MO7T6xUVP71A zqj3_>#09t<*Wp&&gNN}HUc?)C51-;|{1eT?_W1Z>IE;d^FaajVw3r2RV<9Yo<*^#p z#U|JqJ7G^8fWvVdPQ^L67+2v&+=2V?7@ox|cncrmb9{$i(CdgjPHyza=opBJF$gnY zcFc!Gu{2h~n%DrFV>|4MeQ+?2!ihKo=i@S5i(7Cv9>SA&0k7j-e1fm=BN|8T@$td1 z7#U+?d`yOEFf-=Df><2OVO6YyjjQp6Hx9y)I02{QJY0%va5K6L zo*x(iqhc(Kk4Z5VX25Ki2Mb|wEQ^(~Cf3KM*cv-xckGLUaU@Q_={OIUqI{feIKmo+ zysd1x3*|IpO`pK?cn$C1V|?i;##-1ATVQ+ahJA4ej>bti6BpoeT!&k64<5!-coA>lJ$#C<@lQ0p z?D6r%a2N$+VFFB!X)z1t#zI&E%VRaHi%qaKcEX-G0Ego^oQiXBF|NXmxC8g&F+7V` z@D@JA=lBl4pqCub^?2t-e~gZSm>7dF17^p3SQJZRC9H`JusOEFuGj|$<0zbnGjKjG z!?m~tcjF;Ei5KuX-o+>Q3O}OZZI6!+hQ-Jj6XRntOoN#*7Z$|gSPrXV9c+xPup{=s z{x}TB;uM^Xi*O}w!0osXkK!4;j5qNCKEt>88J#}%IEBUt7!BiKB205j>5T@OQkAf8ZPZgpN@5 z_=Ljn7!_k(ZrFAl-cI0Qj31Y2V#?1=+# zIF7@qI0qNwD%^-Wa6cZyvv>t>;X{0m@9+zH`Pt*-Mt_WsftVPBFau`Cd{`7qV-r|2jeK5h%<0LF2l9B1$W~iJc$?ZI^M-6_zFLw5yl=L9}J6;F($^xWS9ms zV=gR+#jzY##X8s+TVY4+f&Fn9j>RcB8yDe9+<@D0A0EXsco}cv1AK;W@iRK*8d9$# zLt_MthH)?vro{A^4fA3VEQJ-Z2G+-B*cQ8BZybapaRN@qdAJnU;AY%~2k``+$7^^8 zALC2>fZxzNoIOrqFcJn}JWPtIF%#y*0$2>oVil~7jj$zl!0y-&hvFEVjI(eduE6!U z4fo;^JdKy|cf5~(;2Zpej_~&Qgu?I`6=P#UOo8bzE9Sw%SQ0B>b*zU?u?=>{UN{g( z;CP&db8!i-#!a{r58!b;hgb17KEfCH9>1bX-gwjJn;%BR7#J6mU@FXrIWRx|hGnoa z*20F^0^4IZ?2AKiG)}^qxB!>qI^2qT@Gzdji+BU?;ZuB#f1>GckB={g!zdUF6JT;o zi&-!?7Qzx(9;;zpY=W(^6ZXUbI2^~}RGfp0aTRXF9k?Hl;aR+bx9}l8$9MPzy&~G< z{%?EV*V9iqeOvvL?pwydxaf(K%OGq1&(r@~>*s0r=l;se zYO7yzdbNy%0T>UHVrul{^Q_nZbUi{^{{L@(GSRP`SOAM*S*(J!u@Sb!4(NG*zaP^> zaSVpk|NplAv@iV~j3aS8PQ}@{5Le)Ol&|Gl`^R29f~WBk{*L$Y4}624(82rgp)fo~ z#n_k-Q(!vGig~awmc$BJ9qVCJY=fP#7Y@V`I3B0rTwH>yaTD&u19%+I;Z?kikMIS) z$FJz(eRw~Nh%qoOCc#vg5p!UE{0+-sWvqn_u?4orZrB%x;AotLGjRbf$91?B_uyeX zg%|M#-ovN(8vjI-_u+jp97e%dm;jSwTFipEu@IKPke=t!*pK62B20&exClHo)7+~pVxZY`}ut4Isg8r&LjUWkEb91 z?mU=)?UfwUViwGeg|Gyc$7)y?n_z3~ggtQp4##omIo{7@iqR5Ci{&qhQlZr3lrc!9p`@<7oUD4#Z>q|J&yjT zzW4I~YQH__PtWtnbA0@7S)XQX*ZSrQ@=y-xy@SE4L(`BP3;y9>yzDH z=I=GlYu9ZfDYXN3NVCQ*>U)}y{u)7bTQ+ZAzlHSoM@)k@wVO*&o-)OQYPW0Grg5G2 z?X;bSZCbW(C2RhVZ?7a$&zGK?UJLuleXQK|dPj!Vy8j%PTk1V=dj1S4u2`@*y}k}9 zu2QhL?7xVs7c4HvFXD6%JpIf0i@0vV;&S~WPOsrT`EvgvPOsfOae00bH!E12?#CgG z%d%i``F;_%DOgp_{}pJS1L8m z$;_YQbe`T4%iUM*|1K`I#QfZDOsVtSbo}&|LhhmDo?GsE+my@4%CF}^eV<6mbj|1e+pDn5qxOY;@`MZVFJuawO9+(PPKCCR7fQ_WXG?jiN>mgH+7&tcCk zqf3}cMv;4g6@o#{)^C`8?M-|7!bQPGcB5j!6)=kn*K& zDckq4ozHWf^RMzv{Aw5pBYLuh+`medeT@&1?R=i=oPU*XTLjbS^CMk|>sv~`)VoOH zbbUS7IsYnOPI*%|(Lc!dLGmRbpPp|*+8*^JpKP}8w~)p!u$Axk^GCH`mb1)!3jDNO$4|V>reY^Z3-)YIW!P7Z0r2bu!e5Yi- z_OF}VL+al^`Qy)j^88)Y<;U~4$1n0tmwfv7qn?|m`1APfl6;LAzg`j?QvVLbH2=}{ zZjx?4&L4XI3Mt=1$v48&IWeUEm6Cj$WWJ7HKe>mLuY-K3GfU=sZXxCKl|Op%Y>xpF z98$h0@+BAjoYHgi6n`GSOOj9DYta4;l6y$`y2UXKeZS0e3n^c(p1$9&uZBplC*Nu* zdzR50dnMlsoypBJZ@Ekde|>aG^7%@g_HUTnJ^8jvS(iyIcfGBZH@c4ddfOB7sPpuG z2t9GVECTB%W0sM zVBJzkK7D`MbMv&NIy{wPBZ+$*2Br?woBfz3b?S^6GX@4` zOq(TR>hxM)D|F8S#R{6HCzIw(@;$FMmA{Mj)^O?TZA0drGtOG`^?IzT+;z!na@Y5R zw5-=bH7VB;Yg4Wx)}>retWR0b`wb~K5*t(2^R0fytvU5v*_^VT=UP(M4bqyjo?qHh z)^kI9%6eSa_YpLY9+Nv$*5hec%6cs9PFas*Jt_ARdsEiqM_y6t zjghTp*jUd9&rwSZ`{;e+2C zruWrnF2iB^PMl*J4u|V;ccq)mT0Rm3gV zp3`vU_t7V~k6$-0!#l24`+DhK9LneX6w~lYsWm>~lDiC_AgfqvkkjxfYZV9V@iKhM zX)&~yb8Jm-!#Cb%YkB=Pth;zi>3lpdhY>1;u7R_0A}=FU8!h@>JL)t-*EjVb7RD>o zg2ECW)*(M35o(cs&m`=k4idRoYmIQJ<)M<4!fUY8h~OJ)ZfBYFk3Lso=694z!+)3j z)pw{Rdg&ZJ*8^{*{ov~_2(yZdFD^d+m^(r)v}pLJb4rhEYO7xUm1O63*A6fBzW&+)cO6~vK{7a+Dx4lX17#*TBks)QFnrzZ z3uwx0%VecGXwmR5J5CnuXr+`zb?Zm0wB2Dea<6air5mZ0;oe|HMylDyaBs8%BS{zB zo3!Zbe>I#ezd4hX{rnrr*~h&_%V8tctkb}7Z?$^hKcb&(YoIYDqa?q?*lS*^9bte*e%LBC!ynCg&;hK`NWORs?Oa!_Ex zH{jNA+28CregW0w*yC<&EfYSVS`i6r;?O^Sj}*{OI_7R}%}E_FyT63BaOj`Erw_Pz zR>E3Yb20}MDK2x`ICjdM>;V&7$ujL7`b5kfu=;md)ecrz{($yUc{j|^eJI$*(JIfz@t0XvHr=;=#K_Va zrNTN6>Yigge;oF-{>X(jEj#;a-CyIF@+353m6dPR`?=$rt7U$Gb%;x7m5qRG8)Re> znc6w8fT(F@k;JBMX?elprR(kMr8#3YO6YBbb>}i)X@aiun=S6#R@n$>D$fXa9&5ro zVBr86+Pr2?$>wK-Qyf0udl>tayO+Z$qZhE;Aqo08ddPeuVE6-BrmwZ3yaHAQ${6>v zqI?6E`pKOB4n2Y<2#C>A`ZmDYxP=2=^p!>jI$Fu{r2;<4Pvf`;IrN7(Dg|`xB?$&Q z^r_G&V7oq*hgcJ>0^Z4BxrbU4odSIII5o@>QF_xWz|bSia7TQZ7!;78sO*I!tafGv zyp?Z6xkp;d%nOLRTb3DRO)Lx8Aip=_9&Jsm3%ET+=8Ul>wgn8(M#owc`vRKF&)>Mm zS=o*m;dC$2Cap~r-JaG~t!W>7I-O@asuBBzd$OaVOb3LQUwd&+v&u%mggCOk(;W?E zMkgbj9$>U+<%ng3(?Qk!oas0(+&!$PVW2+4-Q(p#LJnp@^1O1-aA;e0tBXBLM3ZfC zOimVFS`$~Fi`Jee73-wz>!nY~xcc<)?4{SN>Z9@)PWO~oQm~qK7~bj1%E|{h_4wi) zx`>QdD(7S=JBv(p8h+7dN!!l1^7A`>F|` zN#muFZEjZosp;@?%D0=AITBS^;uC}qXDw%CF>9I$lgo5tsY)0q1$`;@qv;LW^mc7p z{}fYiR?|nBh}UAf%P=eb)M)?jjmjKpbd)TXK>rk3Z`S%&F%!0z>D4lwu!$7*OW{YO z9VL5W{ZrB;`gf#yvl<=7M7-@~rO~oKHL5@LV6Umnkw!1cVhQF2Z?wFbaHmXvlo<)v zNg<5v1wR_itc~WUhC%cy? z(kz|qU^)pbU@ezCP7=vc&?=jytfqAbNft`p!7&Sw+Secr*t#oPtBeA-dseh zxjf&Svm?#Y$*txR(nK<=!9>>7Pt7GJb&AchCgz)la+K0r3b~upbSkB_!@kkAk~KGi zR#%Zhf7?Rln5Bb;kCat>r+c|sCe^y|x}<%R6-jtSru)hw2~SF4f)@5kVX+iaYfzed zGPO;n66;?xr}@lO8ebV{t(Kd^blgm+&tGjJMY8vj^_J%;W$i!HE`IOfE$!hcSuV&L zcPq{;oz@zM`?^p%YY-k-owWx&lGLWP2K}L(HAN}e4(5r!Hjy=qXAn+k6ED9vVa1uH z(^*ZNwezO68abyE)v0IDm zIzO7&Llea#%gjag1W&YSh8#`oO}R*$&@WQhO;~ZBwODTFwbx>WPW-eMEA6Z)2IrFv zKmM;x)cxMXcx|HG_a>}3&st2;h3vJMs>Q#p#Wb4OFvr_4`~0;DJy_an(MOxmKX}DW;H959^mXu)qZ5MX4#o)|HxD)Sf;vUDk)b@W)Uls9<}UDMSf(mX4#oa z{K!-?Sf)~BsxRkmGozL1+4oEte`K;|*_pEa$do-;rW|&r6jxTtIV-+Bp|1#KU8j|n z=c~Ob;%mwY-#3L7=h@mxbs>9eC(|NZThCbb04Ar2%Nb=;g#T+3dO)$82(L|S{N98W z=h@nk?Y#E3iJ}wM*8b5%R6A>?2=dpHjt_cFb4He9j5V^357v0-LCx_|4{Fwicl@b~ zWNuTxt>O5r3s{F{>7Z68ixJmLj!bsBPgf~Btg?~iFK3x)<)jVk{BlY9omI~`X6bDD zLl|bM#OHzzh+E`s&&xs#(aXm|#r4#oOvgwa<1bfybo+#6@_+QTE z)6VGPdLGxGe$1+8c4;EG*v&@Sa(R7CWktveQ^|e<*=SH^X zx;*Vf3AU`h9Exlvj&8RV&1J1*^h6dhzAxs|js&onbX=3l?*>`D)k(<`>w6EZ;MjE5 znux<%S`&dkmXoVTZCjJa3o>&>xASRdBAEW(xuZ?YYt4)A`reX5HdcaIW*RG?lDo1r zEGOMG5w$*%TQ;t~c1xUIYUHBGO`Kfc1j`_&&163|qG2TeG2xJTDZY2a{kM*!B#mrd zZ7?Xz_Af z3X-j?TTy#v2W6H-Y-xF31+m88hc2r%IMPBks~b|t{+AeiI?2ttKru=w5+d*2y*5kf zX9C}EI>%w8{#%{-m#b2K?V%&1KSU z-ICgQ&rSQJH;=l3^!A@pcQ3u^j;c40x{K;9B<;C&f3>&PS=Uo3div|}Khw0qJ^^NF?vTTriiV-uCy+H6sXPC(Lq%*8n@OoF6578Zl zq}Csim5uK7jY1)GRIA`@tUo6!ZB8&#WSl(Y&(Ioss2{2Hs36eLT@v!C%HSG zD|xw=(uF+nsUpTVfy--We@PrMAvKW`xx4~fOK6zHo~5HFu}iKv5mL#=T5)|`e$mRy zGU4TOrwDPqoc>Ae`N1BYBh92<&QWGEmtPcl_3w6JQIw&3IKjGxApG`{@6voj?F&Y}rId$|%XeJ-DR zB}q$FcDKvjy}mRQCZ(4%tn@ZqC|9Ixy(N9LbT03Wa&7G8jP4t(A?Hl>N`jT=@zgl=e%?H#Kx^SXwTX0%yuEwK(Hv z`AdD|tS)DY4d3H}&2!E-^_PWZmTRGz^o68cYWmUiOEWX2(T?oFmhp0i%Hi@lKU_bB z^m4iO$xpAlLXBypE3fZDN-kMjxuiAT%{S#SqwMA`W1_s#<1zv>>0_d+T664{#vG`r zHFqLwdEpwZ&nn-vaT#yr2U=W4WcgVamyvjj&b=x>!QwJ<9MSS7KP}hw(#Ua-UGg|F zkCp>_%jcX%U-?AQWh5JR zqm~-EVuMB&ZL4#8m(u!^;k5pDU1MXDynX31_Po=_1oHN*%V^`P@n7WqahDOHxMp6o zOe33~*9_nGYUH}r`Z!2F6muC%$7$pp`7z~cl$HA90^6Br|hj|;d?7dDOFIpoXM*dlW z%a|ok2bX*oMKfJWu&ztf#adG$wmv5Aq>=Sc=u&4ZYUWte^zmsjjc@DL z%;T%++_5ROrd<+Us_Ox3sa{%tV6w(v@2QzH$-&BH{N~h}xP>&PK^v`)(m|Jcep(+B z_0YME<*h`QF-AVoav7!N15!C2%8yOS_LWnr%g7-Ah(P-JNbBe9(VP`iX?bXSjsJE? z=N{jrt=GGwOC^>crg9nYLut$h`M}9#bjhf5AJo=m$4}A6!)Y{T*L5vlxvu3zqph6! zEe0d^YAp|tAM%%DOI@8iFQ;b6FK@iKjA>Q0SHAKU37667nm+n`(E6TxblIc5v>dm( zF8k<$MuxYxPRnju_KTo>?iNcQn~v9IAGXu{S!d`{gI{U6qA(CumDI>Tmv9-Wx@h@PQjIKjU(04ME$_^vOI=Q@OJ$VrGPsP*@==)Nm-q1G znA1ypvTnZiaK3zEBWowu3ohgS8_hXK{#}CPY@{_ec51n34_#_+0?j<}gvO*9qd9w8 z&ygq@G+V=QT63qo#+;n5Dq9 zD5j5>tgReIUXipPs+m^QCg`DOj0cm->Z4D?A5tho9nWF z1ZX*dwOt;p(Vl#&rZF3;YWevPUAC(ATo@Kh>kBs3nmqE4TjaRCR%xpAX@7zm z>D&PMcMeixJ%4AU)kyCyT2o0rbaWZjtfS3K>u6BiI$m8`uNj{CS}n?VCuECX(QN&# z=hq8sE9{pG7MBs$8k51+7P;_P*XWA%T%IgnVv?iQCtbF=d=p5HOV*KUl64$@eNOxN zO1^R;$F6DGhc>A-vYGsho6D#nKN=_Ji#j@Y?jwzC*+$EE;AbBguC8u9h)9wJsHEzkK#;#J2XCX=k)1 zLuzenx6@ndcURVO)lOP}%o?j5)^lO+EzNv6thSY)mOjRPqP?2bN?SQ*?ayOFYs{O0 znqj@Qw^WX%kzJx{iwjffUn)P4PszQFq~o3Pm@}!?yiTo4WtD3ZZ)1R*alDOpef6=| zEbT*I`9~bG=cL!1PHUVar_`F_=e6d^CGA6KYlKV6zvPf}L`AKCD;KXW!y1w+jE#Jy=MaZgGhjD6rlO89$C*S^XrGDO9md{-$lGK+v zaze_rrsR;dt)K9=#JB1z|9Z&P%X}7Fsz#ZCZRJfq-TS@jB$9gX;eqn#^J%!`@%3IU z@u5m?p}n|S<-29BBwxEo^hdLEHsPC@X_vIy-*HM$jKdvc!b!wl}^G-*x^uiHGV*N)@+HSbdhE&8jxLLXrci&YBP1UEk)aR(WTv{)CSH8*T zY933E=EKZVrKD4#Exst3bKjdIkA+t2@N4@e93Ducae@*eQ?EFWi)t0v27A7UHJXBXh#U)1^Lf>+I6y zl%IM^2h*&Uw**}2TCA7)EGu_NeU4-7e zx+GS8Y1S3LXGbX~9ad1v87@qe$6VXwlM`2w7yBe{*|y82+{@}Ld2`Gnz}jnAo{QW+zFj&)sCBv&iFM^GPHL9b(qqC_Nl%rqy%q zF!Sv_S#Y?yqLx&SFvtCWEM0kAl-1XFmS^?>=7HH~m<7^YGTXH@&C*hH%``Jj&C*}m zYPpmaf@~s)EP^7tB7({yD1so12q?13;tGNyARs6Rg2?+lect})>)G!-bDw+9IrolI z`L>@^R{*9z=Q(X+2RPSHVUk93x{iR#;xtlja)GmEHGF1s?*0wC9L^ZpDCjv@19}L0u z6;5q3RH``dUSqX2YCi$9D*afUl-uS*j%|M{5a7!IxvXX$keZ69Z<6jEPK0UkZR zmMgSp?&mxU1Nd{k-iXg~fHR!;|EA;I!|- zJ}{q%dZ`3^E^65T<^{<-Y~xIUL|p&>t4ags8l?tMrA)w&JC+Kb{oFySI|I1Iv^)fI zH}%0q5Z9hI(n9XG9>8U83Il^;?%hu@@DlErX`rrhWkH}yxwq(LD&wx9Jfxf(N2yOG z_ed0+RdJ(nFPOQ`{WB8G8ty(mG+VeI(a!adYjX@@4oKm3UxnQn-e;aL zNaNL90keiDrW~r4w4=Rn!oBV1d8F;7%-%x{8#BI9pe{JCUBhJ zodj7tUrp_s1bzzbT8aEsC*bom-`gD{OX63wgG%KmMM8FlpF^(G`8~Mv#hm58HUlad z{Bg7epXdMK11gI@eFkI~_=)sNzLIjUQvN?LL6z}MB?w#2zn=`K;wMm-sG9#L7p`mgr_TUt`L#1( zP|v?o2e`p6qrK@Cf36j>+k7jnns@ox_<;e_z&CsgFZcLw9>rLi_+Gi79`F+$AbJbm zkG7LmerzO|ZF~WKY{)#~hf{~DgCAlK^Dh33YoH$UZ_*~*!{0vuc*0*mPq3GNp1L%B z{Kl{0x}X0Fy+;H5vG)Ljd^coW%n(1z4%9Hej%tUO{0J&zTo`#bsNIYdcT5>KX3TU% z-@`O+hBFW5#X^856Fd&!&D7(Zo!QF-W`f$sjJ*y!U*<1bF8!Gp%DN9QTbDo<$OzYC z5JAjn+E@=VAJh68!kE1QhnZOm;3bT?K}kk9^VcXCL@>Wn(ig=fyTE5Ov!e-clsVD~ z^J7fC8>r)qj`GNOCS?;e6But-L`Y<~H2F?3uRnz5X@fIaA>Ws)9+P%(04z{1vXN8Q1ZMSHqlifvlFfzZ0(OnZLKd z*$rka&D|d6U0-NEVWt^j*UMOF)9hoKW<#Z)@%;k^1I&T-fI&u2>(UT2+7IxOx%W9> zgqcX+gR4M*WtnjkJQo5y1mmgm;wi9Iz_quabs1oJ2hmN1#gf_vB2j~$VvoXPeg>Pf}PYOD;12agqJdbA5GzMfqXG!m4Yo1 zU{(pVlwMsI=;r}y1orrMBbhou<7QCxf(f5P<)$Fg9|pGsPL#{s5!g{r>aHMZ4w&}@ zv2S4zO@fNgK|K&eQNO1}Q28dPHo^C_p|%TboI!O8?k|I8m*4^ZrA($<;CTe!N{?W0 z2r5qnp**Pc3NHT%S)_2c7s5sfr{G2@6C?DZq%c-kM$_$>Fd`A3FHV?O4k}(~rH$c) z@E>fiOro%K0?bbe)2Q)vS}5HDDoOa-+whVi%YS^U*p-aFl5lV8wydqrM3aV6iVFc#agzh1La^ZFhWEH~uouI0OE~^l|TDZmr%v#~! z6t7O$OI!R+p_-PNTS8e1T;CNA%?C6H=TXDBNthW2s#&P{5auny)3Xrhq44)fFmD&; zoB;JmxSX1!UBZI1(0nXRpxUHISWFxK6QL(cY^GQE*~8-q;sNCwSWxK^ySdZ5}l%|_=4!?|3M{3WJi14MNtsFw|SyC>VxKs z#QmTyi4N&N6^c^HS+VFncQ8vt^&eokS4G+>a8@d^p#heQCXWZRLZmx}K-Hp`^jcgO z)zK`d6}|Qgn02Ce(x7=muZl)M#GgUFqpOQUGu9cVU*Hq`=} zMW4`{^FTB@9L`!qOQ?C*Ci0z(2<;-xR5f`MK&Jr`CRljot_MeawtuGA&Q_BU|1ws2$d1h544rLh%aS=aue_W z4hDO~H}R_p##6kRGFdON(GMy zDzRTBsA}<+@8~-cC(?^hC%&TwbyIxzBH)(T`7gw~BOV(7^Sk0jbD+{Fc40u>6aT{n z+!uec27`DYcA!SxL-F=0FlZI;DgxCm{-P4pBe9gy=}s|61cNT|3k_U%i<>Dg?-6gD z0eC8Q{~w@N+(d26XW~3p1nL(r+62w#;x^n|W(LKVH^cmeIPhIChsAeLM=>Mfa_UXE zNM40E=7=O{A5_96V_pRnDGB-nvM7lowP9lFJRN$FN-rc1tPhk1r% zYdYY(Wd83^xgZImE^oF(OWli$lH-&w<%N&5oG3MDt*16-EmsGw3J znN17s70LEOK&j-(62LXda6IhFB~y9OtdMXiKdX|&?E+OTxr3unrbd!Q-J)7aRu@KA zFS$T-@P_0%uIMngB-}DEZ%g`S0Pae{zlXC1$;ns@qDk@t6(7x#d^O;KBz_N6S|n{Q zFmIJSJqndJ$@^c!%OgoS?e85D86`aK(#m;YdPqNxf`OM*kG~*<@s_FsAoG!qng!S= z{pEXrpHw>?vi(v!dP)bR`>7ujAg!MaS&+1xdcDEYW~z@uq(T~XsB}vsKG9+692-z! zQiTZy;nF1yFo=+jqGnZ;blN3AwA6A8vRG*W?Nvvm3xr_CNsmXv*>UMz2C@X{N-D_` zrBgd#eoE?3U-4;aX&E3zdLsdlDt&($?9!yY^I?}R{gYn9b5aj?P#MyB=int%TK^2@ zSyJa+^rcEA|ANYqc1d8DE2V#fi^-F|o(#HG9)FG=YisAMinS3Cn0Nte>jRf6B# z0j@};$%s%YeZC6RHR;&@-c4yuIW#Mzcd5NpB@L|yRW04}GoVKL5N$Q4R$4{XaI3Ug z1ZKB%>RW(eX&3{8m(nla2Q?zC@W!ZJWM`?O-!0oo>!7P_Xg4S~S>70!?~&!xzUd*` zMlYAA?E7i3^OBv=1H5IQkB7ltnQ1g+KC-u|rrsxWM@NeBl@)#u6@S@*Bt$qMo74d> z!Lk)JO%KZ6rF5&%X4|j-U+}glo^62kL&Z0rLC~rgMG07&VtYNb+ZAQ|0gn_1*1=PU zB9LmbPQ_Yu5tuH;&8d(*R{S~#1L;;A+6d@TOpZpdCkkZR3+=hxjYyJgx2iPk%fB^P{7!b%> zwJ-=`uTgy%%zkKv=0Ucb-ntO>dJCAL?C;dRJ;d%zg0sV{-zpe{v6(dUj?3+07ufD9sARK6)W*qS z=hHrSk^N{5s9g5%T*&g+P)g?W*}8ssDPYCaj=97xrrNQPwfhSOm)XT*Kozm8L!eU3 zuC&6ugxyB%-79Pf?K@Z5!(>p(N*+OWjjcQgC}U4ML#3P@sfN!A_TwR#SF#L!{Z;Jn z4j5FkV~;}RI(y<57}T(d`{1mWeYyggb?n@40rhO57_u8|z&r4AligkmyIbtCP^Aeznb?3{=C9vF08&8c@NT6 z8p=vne@YAdw>?zftg$`@|A)61L36`43;zF;Xc;&^x2?dRf9bS^=U=OCLUZdgddl10 zqit(f)?xhrpC2j7*~781f!Lkn6a&hK;~E9SeH=F`aSn0>G_OK9Kh{A%lv9=m*&)vJ ze2n@C$Bj0-a86h=s0hyaaJY};D7HZ}hI540%vjFro-jYkX`?dc7-z#~Ks@K|$MAB3 zlfMTB2^^0+m?v_AH^bmGXEv3+Nu0||Axq|@oQ8P{XBoYqX`BVG!F4+4Pamk9<(#1I z#W~KUE*Q$3i=`TRRT9w+flM9=3~XwNI))KC}VGDp!1 zSrKRYEXayEWBb7@;q;loEah#Q= z3%eRlA1$f%oIoxHc!N_yGxsKEtpJ+0I2$iO^DbwYGVBIU;caL(au(BR%stLi>QLP0 zl+nS+1CFK+DlMFkbbyB(4&~JCoN3bmk2v)=03DnKMUZuJL@GcxXEpUbdpJ_6aG!80 zXzhN=5#5CB8OQPipr4b_j6ee%(RhsIIY$wS=r1@i0>CgQ_6|PLOU|VYFdyMamqW#s zJ3SsUH!d#@)E@3ni$J+^yVpb0i@S6InBLs3qhRjkdiWxq57($e1V8RiW1+I2+fJP# ze{SqgfCJoLA3_$y^~;AWn0w463wqu??D! zxMLC_>)?J*ozqV4njBDF+9^Rd|5YL@wItB3HZBGZ& zo0mjgz`eY$X?^zLB{jidAJ20$!tUpZDJS#iC3e8)0bU*@2LU{eA0U{=qpK1JdEL}t z4B>rO4=R+`Me9o#FYgSfBfQsb;Vhiz+5j&RyjzKoMe_{*9q#eEX+??Uef0|9C~r3v zU&ncZd2klbbD0e41TRShy9D0vze42{FHa8HY2N)h_)Ow4PZ1%R_strpoZ%hM1~ZN4 zTm#K?-heZxv%IBr!gQV&_!aCjd57p>W${92hFsu%`!DP+@-{U?mdjh`g^}g)GCzfR zK2H|~s*pF863WXw4-Xg=@j|D7D(1cUH)L0N-_upLQl5d<&uhH>b75D;OW6RZgcmTY zc$IWNu$t$!9&nvEk#?>+Uh)56UeDXU7t{@2z~`WD^1`V5bcbi|2HfRM4~3To-h*wR z8hI`z#8C9qx%6vypL(ie8KyP()D3p z=xLb0Xs0Ou{|K1!> zzWlYctNHQOZP47$zjq2ufBqT=m@R!l9c9;M5c9`GeFBt~2iSI~l?PmU-1`Ogpzk_z)hx~P6&}`+GIl);Q zzoZhH?fe6?pwh|b2ZGtfZ=x;aF@Kp2pqu~hAmAzAe>9x+@;|wO2z~s2M?gK}2VRE3 zbAH5CP=ow#N(yn3Kt=EizT015j_{+tM+6rpz80{XY5NK?S0;*a9kuIkXg- zALtL{Ujx z&1{+t>N@iVOt>D@V`jAmD&33?EhRlnSu13{ z3`UDd#5|{N>@$WnL9?G(bPS(pkckL~Y=}|b0rLg3^+&)k<3@)!E`qwrcp$q4Uw;aw zt01BW=57K5_1!%LF4V5_6tta!%u5jc4xD)lY@@;4heHvl_zJR%!1NPjmw~xoAb1K0 z5bTw~AW#rI7F3X6JuRxif*9JQLIs^2U>*{qeIJObuX zf?HG_MGJzvV1884V*vA*z;YIvae`zj*p3UvQ?nsK@OC<6iGp<+_&h0?83H&Zm`g3w zWWmR$p^_rl^cGZ71!B6`dPb0R6SA{{4{4Vn|ECE~+n5gwqD9}@CTP#@Z2$d2+bpfa=g4JbExhmLI z4ysHrpL!?df<6jcAs9?R*h;}ds@Jazde#i6pVijK5q$z z=!Ly4D9QzON8k_ys!{MA)d=?lZ&PvHB*>J(u36x^40bJokY-R11yVDhRbYG!gEm1T zt=Aob+0!uUPQf>{RCWoP?qCp)1*d4#PXvK0A$uxVLy@TG&}5yAH*i0&f%djMf~3#(|o-Xpvz2e=FS>JZ&Sm`_Q* zr*Nx1H1`Tq#z5vH6!ap(K4JYzP`<*ARgn1$|M?8m0pYeQunQ30S_cRedeba8C=B=x zvJl}_+C@W!E9oTmknrc-fFr_h|A8!A_|bKEi4ewmKqXT68Pzf|!c)|;h!tMD1~@AG zfR0^`2|HKAE=hRK4I@hy_R?FGBDACdQiYP^kYxyWErql5!h1s4WePu{eI-j+lm)mT zOwIsY6mFoUD_1z@H9($lCUr{kh0GB^fzXLcugk(|GzW`>>)YU^Sa_c*`x0TqTF9;l z(-(nRAzax4l}h2lL44XO;f6V2Rtr1maR0h6g!cD3;WnzW>V=aj!MY(#pabrk!dnaQ zF>eWbV!*sBTt5NOARI&8rAFawsx9sb`z%mt62_&1*(%KM0o5j~q zyhr$)N|PtT&*;wAQ{gsx?R$j*(=doZ;gLmvA)y9+Kjwu{t^_qK6jI{iB1)&LZM#Lo zF<`oi#;wOkaT96Z2Y86AH2_c1Sv@qpL|dsF;w^ID1?GOy1$uA&MLTbRc~G=84OEC| zb||QDQRx3LqzF+z&4Qz%jQfxs69vowl_YYap!(HCHz z6*<3#A)OP|QVO0SicA2U7j31*F;leUzp`4Cp8zj8q7_@<^P=dJ4?*RMb~ZsXPxM|P zs7s=0^)M(Dg=_&_7G0&nuSm3%-r{1>dD;!jM1N9)xLo8xM?e*#&7OctQMVtWSBZMh zf~pa1qLi^#)I!NnoydcB%6d^?CuBE7s}o^>h}+|3LH>(F1Q#4@Hx5L3N61X9BuJ&2?Zt7A*~f&u)?6Z@@Fr z^hiYS7lq2;b3pVVZIRDK-xx5K7ozkNfMJo)2$h$j&nPb+5k>w66?bvi51>56kLU~c z6x+A}yu?fBJ@OMTrni5;xR|yHe{l;bLw(Li>K1nml*LQIuVZ-k3R=EA+GxgA1pz<>VJSlvAhm& zTFl!CND{AK4A;qGe>YGm;+gb|t2FU+YDTAvdCS2(EADkcgmYrgPr=L(p9_Raj`(j% zkS~h6Y3nHzzZ(kKW%1hAU{EAJ%E1FE7H_QvvqW6-C7@IsLv!MqcrLvpW#T@XyXE5P zZ^FDn+*A#pmEtbi(Qk?KsqVimZk_|pJL2=SsNNNiqPOav_*^%rCh?xFZ|DVVKdCpS=S;z$XgT^vd4b*DJ~DTdo69!vS>WAU>0!R!`ardvu+#SQ6z zUU5S#n0?|yDG2mToD~7)3-N}-Fc=o^8U@))aq9%Yi1?)+0=Y`$Q~~D*2=evO|(P#V|iC>G}f9Fo}Y`+XzWJm8+4G=SKL9lKeFjKBFZHO8sIa0cyyO zN&ZfPW}Kwm1TV)WNg6=Bw+W*@CGk87gVPdU>Psa_^62hkvSdyN zRL)AyW&qAfwo-|aA<26c2InPKN^vqJ(Hr42Tax6DLF7ojSqHm|k|&oS%au4Sg=U_l ztqULQk|f9;RH0--1H4?8-0Oo~kz^a4(iTf1*FttxlJEq~Qc0dS;F_em08l3RK?8$w zNk27nY9uM+5U5u2*bJ&pQcTA@^%6OqC*75drao=G-G zz@T3;nQpBPNZcMn^SQ)<7KlMf(HJm?B>Ub5ypW_)wd5*or(MlW`a}#bd!+A@fxGl; zN_0J>uTq1-Q!0K2125^8XpGuh`UMvcWUus{IH>qYooPouAl1{82#`AF0fMFJo{$}s zo~F$Ii1eN7V1`RKd;!e}Y4qEWMM@(gz>JZW(XJgU9Vx_Ej!K)afI22sQtllm&GUu% zN$G@bh<8frdjKk@rEgf^B}wY&4$XAwD9X>yN~^|!Iw$?=RoG=nH&PmNUb>O;kWA?l z9=u$V<{W`tp)_S8sLRrf_n=ZFZ8!lamTJ==yDI(nIJ}ffQ%57vHR%G4te#4%Mm*Mk`#TNMQJVA&gc;Paqt4%Pi3 zvgTKz5-R(RUf3{M0UvfpWLeZd3zx;7gGz*KQ4?g*va1%zVq{W!-(zKKU&6~#*(|!V zbX->YIhgUXmy{izko7gdXM&7Lg32k`dIR9JY#UX4NwPai7$nO|shx60#-$P?O_oO` zX}W9%)g)(SsU}e8Wr7UIGG+g4f_awgovpCDAUpI3vWqg;>yYKjOmh$~Pd4gxF!N=C zaF`d$zNBjRvTV$+po(NN3sj0_XTJsWs%)bcnx(R9K`_52%UT2|lLb2^y z+YKtUvh}T?>SPh$A#A;@lh&78vM>$|Zp%vFhwD4C=#QXsS2l(08e|cxAZwD@#{!yV z!TnIVFMH!1zyq1B$Gledz2sSl&Xf;X(PEwD^a}Cptjo zusntGf-w2Ar3iaOJ{$@vT;3`I6(M)N1%qh$ooX;+^Y z0^;Sb(s9-ax!q_`3G(^^xK5O>q1^4Hyx|3$CCN8ZPLwPk84oH&9z#oPs(cgm2hYge zs^R*qd@FS|&&fB_%8()NeF)}xdG=C3rhGe1lx%t4IzWzmDdkxg<+YWda^(|PP|ejkp+W#d2BJL8}e6P$H%-aua1Mt9r*{0k2ein4oCeuL`Dj|K+U1kqf!!ndDynQc#nZ-)b|5dC33QI> zZIiwcVfWgMrk;SW%^3y2&!+EdMA&a5mZU>^*e0G%I1buO zrkiUaHk+t|4z=n313trSF0VzPBR2izfN+~LtHF%0DSrUFNSiQPlVfcDM>|5S%>lAI zYU4`B?#FC4QAHGIQ(}Vbgw4*QfCL+M7|cYQ5^oIYq)m|-VNcnZ%3zmlv-lEJQfzK- zf-Kc$`tNXk#>S-$Drq+T8(^1X^W7VOi#FHC!l2M*$0bmgZH`ZXU8&7-Djcra?5KjQ z+Gb!kyj-^tqyTQ(4Aes9mQBkDoZYiobP{$=Hl1sr*=%!_+70(@GN`0!w;4zM5J>Jd1oHriA@tNl}~N*XrA@jWbOoW&?cD%F=P`|ga|KeKKTwXY%`?+%$GJ}T%b8( z<4?zVF1E+%lxDZ>+tUE9wmGK(Uba)7!Kb%v`~kpT+XpEal8@~l^uF)24WOG2zP3V2 z==^LC|Au%$w(&IegKa;h)1-s8lk4F+#CE$60)^Tx<3Qz*ZK@fMAksFTenb#un@S6H zw5{9wu#2(%gWA!twlBVc&!e_w7C?e6_XB7q+SYV}O0nHZcal?WSEj;QhV2U4cF)^B z)gsVETlL#8&$W%(i+Go96DGs1$o36Nd#~B@CxKaJ`xdQ=*KHT(gQ~GzPMPs7+deAb zZrd6r!da8;4qD5bZTF7@wAps#B2c?+wE&QokVAPC;jGd3G)Bpo;9eOQ2G0cl$oZuccE)D?DuU!YQLw~mF%b-Q=p1M{X`K_TFl-Ln)7>8{w?`GkHJ+V_ffyz_6FX@}^wOf`4W}jU=rU)}&*G*~K zbGxly!}Xxu6gnpvvU~6d%olc{G{6zNE3~1wDEjDD{%%F;D1>!Yuyn=9P0>m@ria3d z+RC1aac@G!OJV5(<*k^sADVj=k0yiiRaln8i=U$7Juvqx%B?W>S7d&P2nQ76+fWHo z?E4)m!HNb-3lAzhlz>Fx)B(a3t3C%aLNWasR3a6=3*an9 zVVeP2tm5H8$c`$ee1!oXQ!MfYGfpv@R?HKM6*Yhag^LPfNmN8RL*=C6-#LI&3Nuv` zXB3m>!e^S|XFD*{6>m}V=&Ztpj!w@hSTfI4DEXkW6e-_B^MZmW0F|xS%z-RN5kaRy zd5RZiF!L2pZh|UM$ObULONy_k!YowOt;PV074vDqEm4>bgSw)~djoJ);VeMBQiTm| z^W}=aX%VkbNa*#gRK(EcU8NBCK(ktLEe%wyVv{>m>J+P}2C7#)GD7o)V!;A1Zz|?d zj&euQM@iIO#cbN08WihiL#0vin*?xAp{FJNzTy~d(qH3QDkhwvvy(UivVuy(Q-K3!v=`q#gkof30}O|-vkomADXwwL0UFd~9$ac|Pju18`6A?mLS87^>u{o5wA7MA|286SjMKF(G zfBGHf(X2}-RASf=3+!Uq$;ZJw%KjvRN&?&XB@7bTAL&ryB>OvEqCCas_Jetv&7A?e z6n0bzG*em0Sil+fD;s!8W83IKrn8T!+{s}5_kekxjjG1S%w!vX05gj%r3C8&yPKMZ z`Ro_eXDDFr)kEbH`;s~*g>2uupf0m7xrkoE9-+kg3Oim2&8zGOmmn);N5x|h*VyyZ zAgf^SF9uYyN%WFbu^Y`$sb-BI!QeXkE*&l2WY^Pq_$^kq8Y;Kh`O^V+*!nLJubCA; zg6uxKlL7UBb){0Mh0S*YvxDuRvZa$S-j7Z>|!v{>!7kE6pqPkS#ez{~z8S2**w-@6mCz4n2l!Su2J z?rV72XaDVWMEAA-x(_lx`ypF6+ixE+2o-<(Nf}T%U|(|;DgpL6i$Mk2zd?mjko^ft z#e(ho8)0zJ{%bnt3bFqmB@&_bkMjYC>}S%Bci4W59?vDpej+u9qV2!mg|WogH&UNH z)_%c92z%828#=`}X1{2Q7AewYV?c>7&z;p~L{Pdun3*bBb|B-?*X8Ci;b zD)oL-?c?vk;Ees{ZJ^TZ7r21Rv}a#Oge?0eI#0b|f0>xs_G>=}v%tP&A$(r4A5ZrZ z3hl-87GJhk#)DaEZ(R@9*X%Fb!daQUn7+7j`-#;1t+1a)H+pLA^?$>l&ff73n0M@d z`U2El`@H#ZcHh35w(AG>1#ZxMWWU)2=&(1w31+8#K?^jy>~{{quE$>R16)6`FX18V zfPFiit39{hzZ}d#`?k*kL-yCcgZWGQ4U|KV*uO(z-5rW*!1Qof_YQn|IvD5^c{wc3 zgijv_9UV37b3ks1Z_Hu%8!!VMQs|~%ki%T6+=3k*((W7TV2K5F$YB*7$3{AI)PRa| zFi<)Y?XZ$|t{8`II`25UV9tWU4Tm6VB;R!Cr$ykF!;B~}Z#zUB2G!s&meR>a2Lr7W_Z;+;pEWtG z_!b7u4y&jq(BfcrN4$p){3bxF!#&#V+Z^_O2CCg*9<9RN4)6STX6o>e&WoQoaA_@n z>M&tHob@`KrCh4t;rH+0Y`~#=KMbBb03OQ2l&pIyU4{|QOZj~XG`*FxXh{2&Yd6E(Us-q(b_bN{ zECK~6Us5g{s&t~v>X35$D=6pw>StWrkh|54>%{V+eK ztXTzvIOXqD;2c-(KMjL;<;8EIazgn(5ts?eU%rJ(vT`kL`YFn7D*&m=Qrce5D1V}U zL7MV|9f**wtgXU0&nm+|2Aop{9EYMP`8vjjgZ||7N>%`qimr|6%Un%DVJ(hme8uv zru3w3w_TZ$1)rVD6?Ev*rA(!i__6X|%7VI;Hp4J@sw zwVo?~qDJeWGUIy~3@IIYpz=bQw+z&Xat1Z0T~smjj_p=0_lL|?_3f(|i@VB$P7XX& z7idCzs=P%2FV#u<>^`asV%Y6d*(V{auWIjCF!xjC--p2gRXcUQ0#r5hbp)y^Rsw=l zT`ABEQ6=Aid8mp{KT|lQTD1pW4y&fn+znTG|BMI`D(M(_iBy%*p-YtNnH?Zj)xQZU zM^(x~z%kVi^sU6HBBBuXgzA@tpb}JH(-)qo`k8XDlPY`4lT%fr^8sg6zfng%O_lsM z4ANDavG93TwLuTf^Qr(k*~(PSUIEQ4Rs3~Czo1$|XQtVzR+@FWDs~d6JXJ?2H1kz0 zc7OuaReGx~sSf2rrAWnb1+!Rn?O$E2>rV5a_DvBMF#gs;x99%2n&0fmxw? z(g>P^njav=*`(s@Aumd0XW|3)vl&_bh1M zRhenj4XU|xE4NYQNt;x&YARh`zOO3&5!3_K%}=1xqI%~iz(dtrcc9s>Qc^?Xk;?W6 zoOP(KP^I6gI<*+gF4a;Wxb9J{pcM6q%61bBo~o9ugG#R|mS$a_Y6m@u0o7!C_KPRWL#pkzfETKTv~P~6+UUBfi@KI7_TB20A4BG0fxOj+d*FJn zdXOq2A9c+l$o8pyXoL1u9}0qszZyS0$IMd4QkyA2&HfB3P+dkn_#pK+R2+n;W3%BU zRQ>XGFb}Ciqd^^3_k{z()C*R@b%Z*0DP)oA`_u}IQqT8+ELuI09&(Jj_y@o-_2Xku ziBtRD2X$P1BMrliSMQ>%>x6nX)ki1Qj(Z_HrM{X3*=hCcnP4WVJuBfeS#7L`&ok<( zzW{0KF%O`Uu72e=P-oT4C^J5%_T3F;raGG{qAc~Bw3c5`SFeUjwtB~VpmNk3{qQmK z)YC?wf?qgI1Qe)aDG9!$zDbK?q1s^qoE5A8`ERiQT^_3CXKAiJUd`W*ziss4gq z^hP!38&LPuqXmE_^*^*+G^=~E0WE6%5F$KOi|)d_Rein#RGV7r1G^6Om{(!YsXn<7 z(4{^)9`IQG`(|i9QHyC)e5(F*EHr!7*+R(r)QR2jIiP+`4(4-pj10^{bp>4^8B)Kb z&dE#l`_BO*>OY#H>7uEiPR4G{%L{-#n&=WR-8D`0l6Yu-`xJpZH7ztl_G&(+6~#x> zO_P0}X6f6I`D(28F!0yB{R?~^(Adxg+yKogM^J&9s&1$p)Wj_TglP8AI72ln&ce$f z&0pK0c|`MUCY*(9k^}I}A~Xl7a}uf9vIJC&W(G}$SWV6}m><>r^9rbAnsy5yQL~V) z0G-tQKvmBvO(bQ9r!~)M79?r@-h~LMnlt43jAqIgpwcw9)bB~x?4ntIR)hQTSh_T4 zlHod2bA-l{rAeCzXBRYS^uA|nzO;bK)nv(`nWtG331+^=5CDS$%_y1^mo&-S0Yw_k zDrgpK_KpEnqB-*wRIX?O6<}V~e7X#FWtu588Ok;3Tzs$!&2ws)R%#|w;!>rFjsvqs z^ZjdpTFtMUpjoGRgZd%$npt$RbwgvK8ydGYqtd{>^a2YCd_0;r438bmOy6^A$Df1~fj7(0r~L`vBCS=FTWkLz-a?KG6$J z&|?_5YM0U5=%!7+36(wCltF;IR_y?phgOsV@YeoH2PAv7{i84lAMNT^sO-~5>;cnP zTS&E&zjiu(sRy*zoj?U>M=N0$s69o@AngVZP$Am5CdfjyN#Dc#kT#eq#lu?DyMQq5 zBjnjkgtlLgkwt19+Hc*%GjCQ6sG}E-lsArX~eS_M4XSIu^ zke$=MJps&2Z4S-PEbY^kfD2kT+Ul~kyC@0H(F(@ES)R5;4rabKhpNE>?RjbsU(&uf z0a>AT+HKesYk#55rbL^b0hKG-))@$URXbrCWTo04=^HE8MpHgfp$()Op;G&644_K8 zjt&W`wd?4E-OxUp3WJ;4)*=|(((+nC-PWG|6VRZ2gBos)+Kn+VxTh79q3+T8z6q5l+MqsA zPqm*=kE~bw*=#WTwQePV0qqhcB0Se_&_On+t)Vw(SX)RvmY3RVPY`HC`#P;CE;@M_ z0=em0Wnk{nIX?%}UDxq0;(6!-F9N)E7mh%-R~Jqxm5*-Xc)&j0I68*duRBd07k{1I z8I0wCPW3gY0NpCu`-64kYhZp*cQFtUq6=IEl~7&4I1DLF_x&G$Bf7<#!3@`J`Wwz7 zbQbD+N9*RVfO(8AgWmpF-LD&AepL4n^`(yMSjxTQb-&QxC~!izeG#Yx-LbKNQ#w6u zM5lF+!3<1QBeZt5~x5aEt);5Oi{ZX)G94LWNcWR1Gb^rklJKADC<_jSwK0S|Of696r`Vk4Zj z=}M{AZr6!wVem+&q5P~vw|^#_J=Tq;25q*#NLQN$yBE4IzeR*$T`Ju*dZ{bxgO}a<);3VCdSMl0ZhHAW80^tk z&;swNpF}NSFMaw~Q1RABSz)kOe{mO>zWOi50Q~eRm0<4IujPXB*PG~p1nR@6R0z^P zOo1#|pFk7(pgwCc;D~-7y|>|dA8KJl=x1-kI3x9ORF%i*IkdLM>iyovLq4iM{1coV z(}$-6;`P(MhxrM;Ep68c`t(_VM12z-Ae`3EtOF$J*KYtbS>Njd%@qA-l=r0Rr#1u9 z^{-M79{2F5vN@+ONrh&nzB&Yfvh*TqWL?nTrG{{}z9SsYa`m}igPEs)bQ;WjeGk<+ z1^V-K$$-29PG;VFZpm*rC+cJ zRJFc^(!1+=ds=>L^sAnMs@GqjD)NTD{5L$1oBH(IVBXT-4u;RW`s#l%qz3)qr%-9s zi!Q_2J^e{)H{90`{|{as=r_^fLW_R-V=y1;|D=P5c75@4$R6oq&VcIBC$_`9Q-A9h zP~G~cRAu$(qv@wOPxR42Fn_B5=@&4c>BV%dpN3=zp-1{uozFx+6nuIYe-hE!VaLk!7OvK=;vUxRs= z;RfwaM+}=Ra2;+an*u7zFhp}O+Ay2;;uyo(Xi%|+PpH2eXXxw$95+<0g0px-Rvp5g zFx;ot_oU%->Y|=91XVzG+VC?a1WAUUDOpH0G*H@f#vnR`=xK(#&j9HLDJ6m#hPP^9 zciu2i0V>mwa1mj%3^{87Ifl2XX?W4VUk8<3!%;Kr@(i;nmA+))(x?j!RrQcvHYC!) zLy@7d1zxTgwoM0h)$k`Rv84uaKUA(6Y#QOS!tm2q7+|I0^$GA&WtjddnAL`YYhczI zT4_es88*d2R&S`(!0v|O+HydH;d?rCX*4XS{pg;-^8pN+483JAe_*hm0fQEUC+!Fi z4K?%`S`8ZN!9Ox=SHrHu;Ia$MPQ%FsV0IaPqfN2LkY)qBCx$Wf{yjBJlt8oBP)qHN zegl_I2L=o?<&Zr$)E@#hXsB9(2*ZYO`g|`9CE3s%F;uJuxEQ~sndN32p$5tx<3aS_os~~_rWg3n6nf@Ud98yEfw%}nDnN?5auRwtNWFmAjB=0#)herVne@myTH6|^z?@78so2f0JX+<=}e=}xSys*z0vX-;FeK% z5YBEJU8!|^#~4ku(_LdsE|?9*!&F8z8FPK%y4mFVe}PteWrxD%Lr94kX$;O;nYA(&o{ zqv$T2x8s9ixZdm7O{uhx@8;@}wcAU2oUcw!h(dHfDxPdCuNXP59Ad7OW9|SYn zF?TwcM;$+I1{`x-B!Mi>@!(tVdE7DmD$L^@gK17AI{r|Jcqbj(z5#X0@wewtIqjHF zoy#OgKL$@e)$uYFerFs_-vZJcGn=83?)at=24@|Isl$5S@h`gclj%6d49IfaOH0=U zN4pbno$c66Nn)B6qhvGjM?J#$=0{g-~n`L&=1 z9q;tOV90Tg0tPP}yMM)4h8?FIg^G*m=c5R_+jN0mLswG*U43>l?V+u}!*q5A%sowq zo&vl~g*9M$n^L?%?K4U50(?zAv?TbM{-RB4zlovFNq~tr0}yEPcm#tWlRK@n!KP2? z(s`(9mjmFCNwp5~4x9eygl3rO)K#cNn3Qxf5^35-QzObGqTiZCo92XrdDL_}5^&7K zpE{igj+;uCLnXoF^Z^1Tn&u;|WKNp2bpPd)$&Th=vdQ)ys1(yz^u4E=gl(YC zn3m`PXHD0sr*qD=1*%s0h1 z018YvqQ%#5n)MCL3r+Tv_Y|9cquZw?rr+oo_ln6(P2Z~~&v~HAOzKww<)*fFs8pCz z>C34!wa`v>miwil%i&vZK-O_ zF@H7yl{|CVZm8s&d#F$;FuT(OxnyQ2%e!LM)9krwj-nmC)Eun_TryuG&OQ(33v=i( z3}V>qPHC@;Wv3Z|c3X6G-_+IeDrJCfmZdx}JuE8P<2)@=dgHt-|4>5bZE5@iu+OrK zk|T3VmJsU3L|F1@eTlT(+5u*ig*z7p(UuN+eUDojX+4X#6wz{j!tyrFvjocox>J*A z`85tIr!7U4oFrKm(-D2LC8-uvisgP8o=d7_C%r)FmeJHZJZtGRfqBkS9}K$;%g2=W zoVTRS0CmB#iF%INmc!!^J;(CFS}-qKzThKXuBDN}7Fe!-1h`~*>;#{MmZwyVT($^k zLKj*1IgnkknBGF5tCqh{!F8!+dl#73ER#0Db(y7!4mK+-&bFYcEIrhfuC^pK!~D9X zZV9Lwi`Q$Q>Me%xfE$(t9|CS#4pCWl%QAWs%x_zM5FwCwpiY&0`<_cZ3Z-3E%D^K&C>rqnC+G+!-&^u>97NIS#KBo(ZhO!KCQQvUj=Hf_1Or($LdAL zocpYQt%S_innM#M&{|8cT9EbEc=!yq`uqawptYK^-wvmcTimfG2L0z@hHo~sdy3-pzuURwbSD9tjX|xxYTT_f+R$1Spwq3RL zfDmxq`qf8Z)>uEk3#hfSUtoYYtl3nI+_Wk~;QE$z33X#`TQBB=dB^HQhmeidWxK$< zXPraYeUr7Y6k(gK7bgJjTXSe5dT57;>&H4%5!)>>UsJH#ddjAgWx~$u3 z;OwzAi;gS1t*=v0uE+Z8dKf&hc6&h9XLVl+FVC#PS76X@otpra0qX~PM1OAGdlb}& zb@~PvxHu`V!NASwLz+E%oK{XnAWx@J_>b{&D*6zVpVKT_#P>U0qGZV5=@9KC2b|(* zyAE=?L@QCS(>VH44?1Pg%N*hql>qa@PF8wl!kh&0kR5UIr=>aEsgkHzr?>n;9d+7g zf=YtZ{$%wnh8c5qhW?m2y;1J&fz{SP#oog&O|*5dRk?cood7O4@T z)#>35jHS)Vky_OqPT$d5*Xb1323eQWl;@xxJ5^BP^28~K3XZ2vJE(Bzby`R(L!Z-Z zN{NS@WUhc0PHGkGMw~9v^6TRK9o?7ma88oK&eOTu2TU*L+1~)Xo##=pywCZ#0M2}! zM<`48bI$q|qu%eFR|hJ<`Nuto5a`^q1Li@_KWYHM&J*aiYnby^s%Vcmugih6Naw3> z!daBF-UZA!=Zn;(I__*w8+(GY*&PuQorg|TTn^P@(Z9+ofE?m?~L;!DxuCh ze@F)_na*B=P|0>aM6J0T=Z3GKlIOhR9t`rGt<>Kwc3yf1nkCK~XwH{9$IS*@bDl5` zDizKmx`bNk+(ymoMVO{d+c03 z3efHB8HIQ~&Ows^{mx?AdIp@QQl32Iyo*-M7tT5Gnz^9mwo? zW!5k#_g7Z4Q1N)>t>2;I{mLF{Qto}_|2VqxxGJlspOt2drq!>~vfQ})4j1kYcLDAW z*q&`xmT6g;EmkH9h-@m0vI&Za$_~n|C@7*RB7&%(2qFT4>^@EaXd zCpnxMgwI0`zI5aBu)~)FaF*pz@d8Y;9a8oKm+x?ua_<5M2>}Wn^5{B5kwZFd3@04E z`~ZNH4$4Z9GY;40z_QZecWN4*b*Q6N_?*LgX>eWRaEB`BOAa$D;Ir1jL=i4K)a(Q9 zio=aUIBRgIZw9&QF!mRO*Bn0YfMv78f{) zCmj}+0=JoAr;gwjMq&>*KZboN#^TR-gDw>YGGaG^Y-4=71G*rFj32VT_aX z&kAg31b={dI~Z+Y5bk7rCx9@LVWN|!C`Mcf0_|d0tHAAM9HIfnG6eMX#4*192wXhl zE)|S>7?zC)yO$A6`R+c(BngukpRd8l_A`>`JRpVPMtzinj9q_$9AfPF0A3C=d>UYq z&IpTyvkbU4%F!H{D zu$@swOIIhORSK?)vHo}H?lG2A&ezSTZvmi}VT?zFKE`X*VCZK&cMQ=77_!?i8D@O? z5p*MrwIaYDFwRiwKFSdM2HYb?$nW6B8L`^|c+6Nq>(T_{`g_1lF}U=YzD!dMd~RmS zEHK%^e2YHs0Oo(RO>AZAsk0EsT-XBF+nCApdxkLQH$oW7bfTV47;_G78rzvd4|oY@ z>PDf9V8)A~i)8-dig-~>$wK&yX7(^ZVwgVEc8z6rmV(4FKd0X_fvKU)Kattl4zic| zdMcdlV+!f_OlB&mv3r17NK11H^9ViWL1r%fo~g`rVbG;9k9A{Y=}ZPyrWwo}`aO>@ zch80JD07n;Tpn}DztH6~wUpZwFl#BNEoKg_hp>eC;dkJUF@Ls#9A_>SV$`L~TL%%L zjJbpwZskk{_3}Ple{8@lsV5a_v zcs0zInqhK@IYO<@S|%d}4QDKKegR?)k?%XFt)s)JcXt>aE+ zjsUuD=3y;#J-) z#WestWKN^~;SuxAt*{(tuB6`6W9Irr;3k;jc_35FG$x+Umo<;t8=F~$J`iqU^-KfY zkENrZU@NP{2wWg*D_gk3kWc`~8 zXW^`7DYz)s7RpL?u`W|ay_=Q)1Y9)h+3CQ=uu`Z|9?!D84fq~bJrjTgmYfQsMAqWj z0PJOXybjC#teiZUB(pTs<2=A>@rE#k2#Jj1H|A4nyugj$_vSwE4N zbF9ckAXTi(^b=IGcKrq1Mb?+KAT_MXDLm07){k@wRm)nt47hsMOHt5WVXdWxMg!|6 zb)K)X?$GLXjrILM;F?&xESTJ2O`{XWn=H{Pgl%S3e*)btRut`xw^?1ZX|%EUl>Xdd z{Wc6?JIf;Uv)}z25q7Y@xDMPd_LdnSyV+y^-{fNNw*nr+R@5SFEW74&kUi|{(J)D1 zZ!ZK%WJ@#QY%hE8OIYq>pQ5%wGJ6(fc?Z}(Xh2ffufK^12if>Z+g<}MjUD+i zxO8^vj{sz_+j`(SldYlsA&Y&LR+Lo~)dtNTQ6tQ2X z)16{=6bD{P*uPS@_ZU0F0O4`=@(*BnlKovjxH9(WONdv_HV1<{#a zXqRbYJDPyI!~S*_gzfB^pTOr`Hk(@99qf01gzz5wHTssi+13BRSr0p$4oL2^b-@t! zvOgXJe1Kh50&bA)`yFsY>;)qr!|ZR`03Tt09|heQd*(NY{*b+q=H?^z6C=;>5I zN=afg=Xp;E<2Y1xyw z&hzc?a+LEC6=?;W4gJ6sa+0<{SHu~m2*sTDmqS>>2@eG+<>bEr;R(*1Spb~m+zo-S zjPs!y!g9_}VhAfZopcxO4CnP-;3_%a+hB5*^M(O~ILC>&jX_-C?DK(1HAnUggcmv0 z_I5YXM9OSgoTN&b{ybj?*&W~)EJmQ?Y z0>C)uwVwfh%=w3wph-^J6AW;QQ|tswUv4GkS(~}(JE8OEMkiws0o?qrK(=xXlt=_} zEtHM~a}T)!5W;mh4cDREpPvUHjJt#KkeysrGj!qHiPzyef}4C3B$CTs4ft;Ev$VEG zb6+Y0iQz7zoFSH5^*gvd+@(taPvB-f4PhepwMhu~a*v-xg#BFcv+$D4-PH%~05|Ch zND5bU0)Y;5Uyg+9RIXhMlQgaewb9bKk{sx=xTA7dW^?mshUaj<6Tl>wJ4M+=9yg!` zx_s^=Ri6dio3w8ha=HJ&q=dVg&Zm!Yg>Ql!=Pr02BP->8_6ID>xb0cMm2>-)Ag8#C zC#*O75C|kh9z}T0_op{b}#7;+~-lqMG{^?Nt}KBUv!1;X2c%e~DX0sY4z2 zMM`n%x#y^Cxx!sTC+-d0X380^bED}Xppko(T2M{gNdty-gWE;P!Y%ICbk@_t{p4@x zTDj45gYP!?g^hr>a~o)Yce#NRuR8wT?p(FNlW zUc^@rZs#@9^0b2|rX6S}&q9{ryuuV%M)96d<-Ln{(*|xgZz+}W(Y!ipPRH?BF3`pE z{>uPh4{wMjNdm9w8IXOvie=Cx@pdnU<$fM|GWfE1Td2x9$ZKRE&>`Ly>I)y{>HP5w zsl3LI!DaB?iv`K#Eu^X^i}yM;va)$EeGR}7o;(k_qr7h@U&`YZcZ194Isb&gALC_H zX?dJ??-F#SJmET!6TCe7!<=&7o>wu5Q@jJ8gFDR&rU_fYEAa&UEN=x((Q~}n>p-e_ zdDM41&)Y+b_(fjITOc*OyORK1;?1SLO)YP>4O~5sPo1wTyx>G|4ZO_u&^HZM?X62=DS%(?M|u&)5cB zColI^#OvajGeCNH#lL{u=dD}~Kre4~9!&ap92(~!FY6U>L%h$zVKU5X{u1#K=_0=M0L(2?@A@e6t8&(OnmvLCSc;ne@w?n{`{27 zc%lIQf79V?D}NIeO+kD!?cu?E@c?uod>?wZq5N@rRXg}~RN3t07d=FTaDLuSkO)5O zF7E z@Td2}%Ta!w1(tdI1%c4z^Yb5pE8t)I1iE6rkgidb@KagP9pi5~h+pD3pLGu0N&bgM zcq!voQ#YoZU+4|)6kkly&+sc+!Bz5|J^*)?zk%w2bNm}L5ianph45L;|E2)kMgBkZ zk8afP?_Y$;W&S5G08qytIfm%<{MZ)}=n6lo4Z>^uzZf9b`H61<-pHSz48DmUKHyk>9_c0n?YLmho~#o%5V4-UheRFsFTsoU(yP4mw#zC;&t%X(%|p$?V$j4^PhPH z%O1XIHsamqKPLj|=g+3G4DgdFxg6v_a}3-NzvmLT2mDu?z>V^`bUk5=pZ5XcJ>=gk zg3n3*M*4)O_+1-d=_{Di3vRPu+Ch*lf;BTB3=n9jlfPBqnt>q&3SLhHZkwQh3jQF$ zokj>l1sgsD2@{krh2?g^3~G4q5PVLTMs^B{UxBkoK?0RfQG&l`fb0_Z(fYYtaG9J% z3px&i#0iF}lNc|UqDpv=;7eBs69l({VVNk1c81R+!6Hh?_X`YEwj>LRZ@}3BK?J?j z6v1cDA^Ks#?>k_UDmYEwZknK)F6*TW2ByO@Ll8_SD%pY$*J8Lig7!*qxq^mRSRN6i z(ACSM0_6k13k0vy+bk4>JqODoLAMrTDHa@~2P+XQrsB0!u$mIO6N0FFASVUAw6T{7 zKK~b_To6nxlM2CJH7w5v-k?iqm4Z*)AUrF0l}e0rf*n+`Ul6RL@1;!m^fH4d3KEbMB45?pml2V5OLCJq`HYiwd6v81v3VpN>1om0bjS6xcARH5X zP7BgQ!T05G_DI0k0O5q-W7_JT2!;cpn-qjm&tpo^9tXIu@at(1`UzLkqxcIWDE$c# z8n1)fDy&j~3lw(Hf*UMs9Dp!H*g*??sIZaBq%dK?LU`FOoc%4haG^Q`@gjub)Ypg< z8fa~e64q1meV0(U93)0K_&Rj4La$!{h!c){1Yx`|jQSyagcH+1_6ptgFxe-(e+}>? zVfM4&_6x&8G0tS+iZGCa!q_h$JS1EjjOd4jU(g#%6}El_k|sPrwP>brkQxP9!e^hr zXSVPcO3ZSEPpF8=6<)ao%RHeIWtI6t1MQmy!VoJg3x&_>;Il~h^%;<3!n4PqJ1$&6 zi%zNV+2_EW5YD>?-AUol|1WF82iGAyEnLaL$SQ=#WUxFVY^y_{O5qqKx>Z6kO~dm- zjTg8J!goJ}Wwr2DD!g12E~cqnD?Cjpz-6I43C`+-(VszAFBDh+xFQ^)bG2*2AvH{{ z3r|tsrcwAbt-DRaHz{AbAv{AF?rq_PwJ>QDex?DrBb?R-T)QxgeBKovc?8lW{9`VJ z_k@q>a#FW&fNJ|5;Y1jm-4}jA$5#EqmsAiA2*+sV3<@I#zzqolXvZ5C2GYD86|QUm zHzxd-w)uy`=P37nB;2_N@NuD#X4Mm+n;YUy3RC%bzA53qzXISZ+C(e#X3+v#e*HzK z-#~-_QRF9pZx!{@A|5Cb(ShhT(Q*R-AtF;cgrTBER1}7Ze9wVw7d`zPeC`kldtezM zs-i6n_plmq6}``nP~|i}ut2-YfdK0w#STzt4c{7j2@-W!GM+0|1Xjhku683DE;uHJ*qVDAuM= zie}cq%alk=XQo@k$$MbpCstEC#b2CHy@UXMc7NQBsS4z44`pVFCrlsG66!f0{bBuI>SNhAQV;!RS( z@xDC>dqMn&+C|mkUm5UnQM@4w!dmf*zk<6gUbzyMb>bJ70bVcO zL+7wp#fDcwu8Flp&|McV_!EFealRScP4SLEIBORF#smD8xRs__i+C=zYum&c;_irF zj|9A3{Ql1v;9ar)9pJjemmFboPdxVnkZ$ppbOoVDti1_cpIAV@V87T>0&YP3_vdgu zC?45|up{F8t08(CO6{$+>ml{3L5?q1!4sPnlJqq%R3Rw@L2N;vXa#q&hBCQt%o8VUlRdy0%N) z${^e!ak>mHLb5R*x=6{}BXAZaIZ55gU6Rw(E{c&@=`ToPC5aKRjFYfv-o{IQ_d=jV zNiS_+dnE^q0PK^@S_zUQ@uJrF0m)w%p-Yjx{|CrHNo*Hzha~Iis#2Q7_eXH)5?`9( z8Ipf##$`&h{($F5R?S7&T*(r8n@1#Gv~M1j%%m$F1(M7o5Ee?_O@o&rNqQAr7fVJd zpFb{np0>zR$zkfooRC z_PodLfu`JAS2uViOCEc+x!*TAG-valDrAxSsQ+hIxEB)Abt zFa5iI4V@f~_& zfsU6PK(;wvrtmE5M&c!*_F9sLyxRMU@_c+E={gB{TMK3kc@!Tj(_By__7{Yyym##pV z>?rew@POmOLU1XLrPQT5=s5o?kVB5{vmi`!-01@@-SLHCkPOGnHvrFc^rICl$8jc| zUFSO1(szBtF^|&aqmHhx!Dp%C0AvRNacJ!qzz18uu8t~hWW3&jgIZC<#xZ}8K z2XyU@UC+Zyx8o%mOONB{Zou7l+)TAvuVd(XaD9&d9Dr`n@oEORA;+o~z=s{1={RA; zv5O1K2aYdOqvxUH)C;hD>a!w+1H z)2sg>Y`jx!9B_M_o-TmT1SbbN_)B!+#Dg4g3V95Z6sJ5l01i66cM;@}(+ku=KI}9g z04~dEbO*R>r@=dLo#XUVHMm?S(L(5sIMwh$ik#y9#Q=+)=52&Yi4!Iq_8g})`lF;1 zPV*_(IO%kp7M3$k(I*hL(rGna7CP&6>;u5hIlWI4=z`N2{lhlZPW6@G>YR${9o0Mi zN9kRo(+t|cnw-?hAni`xnHcq5r*x`%I-CM%B6K<(uLjrUw1#$)9;dAbK<+zr(p|V- zr*+dY>OLo42uQzE{d#yAa+-b$WZ3Ds-N21FwNQrpz)8CkfKjK$ZLpkhDy0nRiPLTR zl_s6^)UcUyxInKv-D6=AARRvlz*gxT%FhC&|D((!SnB^WoP|g~ zrOQyE(xX%the=(RfNYn_GT}2yT5o?Q?dO4ulm4>~T)edZS$Nqa zT~P|33DU(u(Cw4TX{$?;Zm$EkUmEcUBw701p9pkN%A!|wNNW5E!o$*UUqXabX(Lqy z8Pboaz{!+8mLPhTbQfKY$d)Rn0dPb*@&ZhbN_CX2Rdm$Eb9vsZeD zZejFE9q1IeU%J2qG9V322N{&6TLBo7Zr=_vEd65`xDhFfzUv3lzw!Vdm8QG~GA8|G z9RiI@U#8FPv9!J&gP4#OzXLB%q-+~>e$He2AoO=$?gBut^IBJMARd$?W}b5cm9qKHGv`BBb{Zd%bh( z8xUS~KI9L;HRmzfwXZvecS6|cJR*m)o6f_u8#X(COY`WK^L-EKTAcqqj|XdW-t`kA z+;I-2t+n0xsU?8lb-oh;%N}PtC42XspX`OM*STjqgaghKK>!RoKm9y(L(X9{A$;Jx zeiyh==TSO39drJcy6)r7PpA}n?7ZPU2q&B`9f0K)mq1!7{amsXApS0$)VB$A$)m2{ zHWwKk?FYF8Mgte>a*1YYn9JhbaJJp$M_RdZ#Eu)YVE;PN(Q0*Nje z$uQaL;$jEc=W_WdaLFz)e=_jK+;_PV?vki^22hF z3>VEuAlWXz48tP4S3}ffCkym(6r}pui=OlD0yZFSjB>k;{8D|Bku* zX8_>1OV9``OI^Haa-DDyMT3;PG`|GEDVLwU5aG1T-?TAQxLl?gch;ph2i!Rq^SjVh zxh$un+Vd{Ie*xh|7m*EIjms!yESFpY&Olh}vXN@^dKX{nOI>kkUyKM1E?M*fuDUq= z|Lb@0`4_I6ToR~Fc*EsEI|g{u<+B(FTU;jIfUwnN^G(2SyR0Wbo6Dw8A-wCN7{%Z_ zTzX!Fu+wGu2)Hhn7s3JWaal@j$@?z9((c>q5=Q4aeJ%m?4Gy}z<_qDFi%15OVV5`S z;B3StY7TT`F4YTQ`Oszj5~4qH$$cH%xXZWB;GVerRt|2`C5`UeOu1CdMm%5HYRYu{ zWG>FBzqh(!2kQkYLAza7H$||7SBWo-Nmmpg$03cE390@K- zrr3#b?w2`#1TI939R7qWUd+Tk}umF0eFF|jy{HB*~ujUl*qiO&2&t5u^xcqvZ0j_ zo|Gkz;=#&fwUe+cm;G4;?v!km5af)kfdW;^Ry_djtgQZN;LgeReh1+N**w~=t7W}| zu)HWsoePs1+42~W%d%64psSO;N()lGj8EnG71_$~K(5JnmC#+6MFfCrl)XC>T$Ajx zg&1|Ktd@4J+p@RmN=KV)1Dyxlk=>xKL%S@7Ht0@S?=nQ_lKpQM#&SkG=P$obhou4Xf>t=& zD(BHx7bst6hh?z*D=HO28MwXjXV-x2lm9UZ%Op96 zDvkZ}U#R>~mQTD2%Y*V=D`9d-e)(^NJuHu)QXy4-uK^}$@{7@Mohg^lo6eFSPW70N%SeX~gZfU4wU^2OAXJ1(!MM5$Ch zOpS>X^5yg|51f=|Xkd9te&#d4Ps?jMG29CIv&+Dpk$2MEtd##qJ)|o63i>6^%Wdxg zenI|&3!GKU$2G9LC_he}#9H~Qdtq`}E~Xu}PJV0+g!OWd$1u4fx3D3+CSOWr#C3Vk zS_m8EOQ}cQB#)? z&!Rze%FhP?-X&k90(Va?dK;GaDeF4C08MwkS`yD(>;$#;%e}$gv=>SFDZvbpl2&m;1q&P;YO0c4n7NiixG`fr*s%WOtYlmX< zEAY8faqw$!;fnrO0fiC&)2H|2jB3u9*D~EK3!U+2Bqq0x93EP`KR%IivV)JxHbE&-w6q zRw35F*?Gl$Hn!MgxlR;-|8wobvM6ue&Xz7Du6ibATg z8WcyJpu4WPy&t4eaV-v{Ns&fB$PLAZn_ziU(Lq-%S`^(H09qBtJ;2>o?B0)OXj3>+ zygQ1}AK|(~;k5|DPDSbOAYF?42NC_AVrnh`-HHxshxICo-+`Atg>Dgq{R&nvgaZl= zm0p92JFd`;D3&t7Jy6`IQ;bnX>~xSZMPdyA4;4%PKeJK#vwkTgqgo&SWB~5#O<$+{)2~c`iVX{@}vr9=)Atn_^amLW=QETV@h z-yQ-Nrd;<7bUT%2XafsZdeBD{p=6Z9BvSbUktpT%*$5l09GMB9F-kLi?6J!8TVWZe zRQ&~EymE+siA3ecb^!J&hyQ|ZpK_58d?qQgY46{!jK2X~igE^}X$O^mvOo?gA68K4Ol35kwPq>TQ5~19jG~$=NBJLRK}VIpIDq6SWi*fSl`n5Z zyaJ^&)g*<=jCUa{QEp9u$uZ?Hwe*fFXTJqusZvLE{|V*OH4v67)3Sg&rR+Eb*Qb>k zHGo$rXHo8bM%hFQ%QfugQ?Du$|3$oO$^-wOx+`C!t2K?vQx9R%r2P3Lbj`{v>f78>X3zrB zqKtS1VXHEOe&pLq9Zjcp<=6y-ca>sFsXCM+jSzM!H&DgUrR=8}(4+j3HkA9yM%w0k zm0we?(Wk7^1JJK5s>Fj0Da!;f8CIUY0WzYjqxbhfX{4~DN)KA~A1QB{p&M5|mjJ+H zrHdM5LV53XID4X8e+wqQs<&y1ZdS4AAbg7|dl87AYQY8!$zPSX3?xwH69JQLDhs8{ zL8=wsfeTi>yx75S~`07lW%*&F+Ejtmi1u4Qwb>N_e>IDKf&lf|O#p0D zU+#nJAoUE|+=A6Xqzh4-UWMg$bt82TcBoe#g2_(xaw?s})qMIQqtp-6!0l3BnGKWO z>S+20!K2lo#fTTD{_6kF-_>8yhq*`n4XsNF>Sq$?x8H-$40S0NT&DUct>szjwnV_Q)i39OJEC4(0o_rx zj&5M)sTbG5OTPLJB_~Ddpd0u>iq)BoFey<#5Fz?8_3DK%Iic=f0pUsYE3|i%sh^{j zs9c?}9?>h*Oo)Mxu(S*iB=8r)g+F8VOftMdyWyr4cyR~oC;3pXReMYY>Lz-!ez zTA{nFF33dmI`wzIfz+#`-UR%r+A;~uy@t7=v8&87g9=mPi+f)!mO_vPDyMAH+}dWiaCTYdXJyEU~*ftgagv1 zIkN*^?rOs6T3Ck$y(6qI8dsWXU7B;-;q#t`Ny+Se&5M-c^lFBSG0r~C8S0<)YrdiN zY(O(5h2^kj+H&AVG;Nf|JkY#E`OBy#E**my)69Mrx^a!1THKE{-%xfjp$TyWd7=rW znt4*Qn*IjI-&ILfYk=!B9uRJI4W&c!K-c3`l5TVThibzR*XxvOhPsx}&K%|{p$^t| z*Foy3?r@z)ZL|p2xpZ?g(p6syVU+6;^0Ldd=T~^y?aJ4}B-ZuMdYHtyI-0=6yB?rD zZjb9O6`UowD*E7hpKG%KB*}HM2fF>PD8`?0Ak1=2@PRPfwM+wej_b7Dz~#EKC%_$XokJ^1k?U%zVT)ZIuVE}D zuKR~Uj=4I12wk~rCIj3lSMzJ|dD?aRE^rmD>ni|1?`r!NxC^do3v`!V<@8qSTsf5R z)VrqL0sM;V!rd4|gR6rR!d`W~O8eh6*Iy|Oz3w{aX;?P8GA1Bwa{ZUe>Q>jl84%uf zji!0j=K6asgm+vIQM;kT^)Al#r+2z4z69xVo#&{aZ* zbVIHyH-ikjD!9-+a_v%pjJxuvTj=X1%Ldu(=J`G%Y;ikN2(ry>18qG)ZXe9V^98&8 z(uYy+bc=WjxNtXd5QNcgAN~rTF>WGSCt}^UQm-V=tuhI^1h*}x08ezg`#H#Aw{|+2 zOm+K<3zFu>jDlsl+khPKEVn|Mc-d|z>4tEQ+u+X-=DK|oia>d8Z_U#LBa&u3DuG+2Q2XGhNm=8f}+(PMNzvTAM9auKF-QES^RW~hd zDA(M+EdaUh=D7=`(QO(1f;Zh7UV&w^+xIl_Zn>>^9*@%E7FrHrtJ{XZF_t@S|I##U zcdHgcc-JkMdd3}YKQ9N@>6YRLFWqj;Ut!YYR*?sC-|hStaMtU#o{kp#+z!%`KInG; zK3osE{rVYv4!h-?27JWrl^-B{;5Lt1)sNjc)DD|))6fNhCvKno4Q|ry?rd02xjpkQ za9gzNXw-gMBPF{2+CDpk0or%Y1F%(FKo`A(v{!ur2-Y4Chh>P?mnz{jWO{z_|Pl=f{Sgwa|<3oK)_AJ9=ztai;Kn8ayQ=nO1g zyYMC~leEzWxZbaQVJWy|t^0Gp9ndDwN0g?0aW}Yht;ZV}M22=}0=P`AIt<)V?QHrK z^RziXfy>uAQH!lWJAqYhdZ9M_CL$DRD_@0|Vy$>5!j@=Vj)EN1o}sPaxK_IamZjRF z9Uv#P&2%;6q_&9?>oZ!@^Ke$FwfqiUwbtee+(oTB6T%wpV{e#T(#|{uFSXitsrhhO zYxx3}b=s~~(A8`2EP?Kdb}cmu8nktkJYUuNevEP6)TYz1PP4Xe765m&{6dg+?W{47 zZtbjbzl9tTXnv)#0KiD&M*nq6`ez%5M9zG2*Y)~90((H zt!v>dQa2U?K$LDFeaUgUfHGLd>uiVMEJiI zbn9$X$Zs6_Ww0Jvki@gbNT*PWz>%}JdPee7jAhsz-4x=RVL zJf&;p!SakQfM$55?$mNvp4IiydpM`_--zfJbSV^}T9@(x#(7a!^8rk1bPH5~U)D|g z52Q}FVi3A|-Qp%#UeR&?fbg2`<{b#H>nas+-Kdj41+Ga~wgIGB$KC_WTe=^q_-WDQ z2qA3M&HD&pJ9ItUAnerj(mLOz`*Hxnd%6`t@Y$`?P~zFEE7%7>pYBj3NWZS~FX#qz z2@5f@L0vq(+Y#NRAK>MIZj}1$qq?u@z;H}wr9Y;4s9U`lULNa0X=9ks&7nWNd!pOn z4c(+JpIS6iy5;|0H|l3P!o*L%ojz}W{Ri~y0s30n@wVzWt%PNeK9S~1u>S8+z(e$b zM`04G&!nzVnEqQjj@_yE83rI+uRj1`gg($6xJdoRG6?5W9`F=>3^jHS>VNnQ z!bAGm`4Aq~U)TX*sy>K%2^snbT7ok5b7vt?mi`+XOtSSC=!NI#uf;=Hpg%eZlS2K0 zUqFiVvyLEovA&iDQKCOg7r{#P8?M9hgg$5me4f;o(?(RLKV=11u76B_?NOnh`UnF& zqfe$~ty2G@7P_)o!WLydg-TdeM7H21K~~m&VwM$dd^ZrXw@&K zHS)H8`!qyo(?4egcSpZ*4s`AM@?79L^$aRkyYz};IJ>83dLnwaetZ+S9{tR_us^8&9MF%@5&fY45S1T8`aow0AL#Sn-Rq42ls9BGWMH z0b!QGfeqKWhE;S$=7?bf6VZzd*EfSJHoQzpcZuPL)9`Z4a3cWRaf8Q6=t>PfKfv;Y zVMGS_NrU%?;K~du=fR}haF`0>Q-%eUo}V_DsX10?IJ&zj06E~1c2KQKuv%#RHOPW^=QM4OgGw2RNcio_&GWUjIJ~iKO8ZznPM6=<= zB>>zqZ0Q4c+n_%TuFbI42JWt5nH;3U;3S3co*@&>%IV#P2`c{w3`10|4jMi#fyt1; zG6CJNVSr95Mh#K-02nh&41#-T$UOqfM}}vf0XJdTLfhvPgV_QvzV5-p&~0|lrh3}n zy(;5SR@HqF^v@l6<&q#tW(Y;Or zFG=ouPQY@%`{$IUr??v$Aw1~5pSpz^?)5?7GTkeW1CZn1^gO)ey5FZ}YM%R>^jqh< z@7M}qk^4g0EsEWL2myD}{g1bxD|5F{QD5o4oQjdN?tiR-?wtGbDGad6-AK1us@*@O za^RwSSP(30+`TFByyU*@XIR#`ugHb2-rczu&aSw7P+VZ0L)Ym3Dg8=K z?#XLma>IQ|2Dn@9vni!&ai2|pXwmBa`cojc-Cw4DWV`$6*AU^Z`|GO#=y1;v!LrkR zm`a*%_c=KT+v7fc11#^m-&_o5z3xwiK{w!Dv=hQX_iD=EhulwuKsfB4^d{hs-2b58 zW!(K*766akgLgnU;hsZh`cv-z{s!Xfkxt+IW)G)lK(=_4%!3y{kN8pO{5=e`F$8-g z&;_dykD3WM3-^e+gaJl)Xja2A#^W`*%NXm?m09i+k|i?-G?9t)>|T<{2`GO5}l=oXySd0e;$K)uHZWq?;b zR!cC>YaXA>gR>?N!8;J%@ZeCl@RrA-43HKNOAAPwM@v26cRb{@#CCX;P_^6XVVwZ! z_Q<3QT|FL!wcz?ZJY^vL9>G^ZhCD=xAj2L;8*n2Y)xRO$1CKYpf$*Wn)NG9Gk%yxj zxN(n=1Bme0<1iDJlO91D=%zeAr)G|?X9}hKn?3bk!o=Tm!E$f`p059bZ1p^S9)Lj4 z58|K;_H3tZKE$(00H2|rvuPa+^Snme;7-rUTJv+28+3Oj53?#*~cPE4gJu?}2utT2G zl^};b^C>w=^_)YuXfiy{QKvN1b9*%gnB}>T^5ksKjLXpFd%k@L@B+`(0ninC8V*5N zYttVoU;mCg{M6ouFrVJ4uHGhS@Q;lRP8Ah!{nl;>{IA!Jl{ME?viH^ zZ9NU1*J$aw>Zx;w@S5kq25{Fsb1B7X^gKn+-r`w%9?n`l_bCCt?b%E9SexfAYT9>s z8fkLf^IS|R$FS#7762ojk7#*%;K?fiZq)NS<-22^&Ko4--hLc=ejs} zdEyzf8{DMle%kmp8w+SiTZ}5|WcV4E-vaSBrddD&j8}EgZ8I*SmR^uCJp`_Ujb|xK z4>2-cN4!uYKLkE^7#~u0vC{|{-lmaR3zG=>26xGah)1~14cJG;5uZyOU2e<<8dnIQjM8k z!!pg-J_={)#>Ed|l4V>p2xr;G5-!5#7++0-FxPl055gnHh;P8<8~x~`Eij&Fc!QH za@F|oUj%A2M*IM-$+-0tOl}yRjstMhXrSk7F^1As-fE1h0C(H?2JJ^}#@$^Q#9d?3 zm$2+GrcvGBX?$%uNSD!<9<#?Nqq_LMF(m_lUgPHn;JVLfqbzU4_*_271LMkrAfv`0 zNw-F)L^duF4G*bsH7wM)q=R%laTH6kiZCXOJKgaYP z{bso)Cra{nJ8BBM3a-H9smI6)P2ab`b&*Lk24}^lH^tyeOkSTMdZ{V&B)Ai% zkEmI7(sY1+k#j&QOcAu)pD`^egJq@ZBPs{Znv$r}IA`ji#!HRq>OSZ$ znJ(;tu)*Y04dGRjrxC)Nrtu~Sn@vMLh|p;gZ-TJP^hXg)?wLMx1lMgk_&c~>)78H) z>ORv>1%&;k*0Uf3CLIUDVN*plyo{KV85q(7(AW4n$EH6h zJ(w^(MU#2T6!R>e$k)7Q1IT9c)^`BdVs1|d7htwisj$_2=>PzM=G+)?+sq^HBVLI4 zm1kfQYQ9Z5dzg8}bU52??ri`UZm!t|5@B9O$6b-;ycvK;nSY^6q0wf69)pN6$EJY9 zn*aP5UgFFvwgR4DcAyuRXkP3LZm)SGts(o&a&(fXC!4!}f#m`7D?fruF?YWSp9jsm z{{tY^oD&F=WB9W*=o*;0q(YW zIwfXpW>F}}U2_F>Upvh5a^O16$-l#-%dDk-d7pV-D}?>#?F2d4y99*QOuMB~rEK&_{yDY!abc(S&-3DQ-CJ{op5=`U0P-ziq2LNE{qr%FLd%IB za77k>9(2bnv%A3^xA1a-E474efbfLnz6;1ni>nm6QnavmN)36 zsnRloo}tR}cs9s+ODdflT(Hy^f~&T?dmrSY#f>V{TFc@+fM2%UqE@|8QeeHNwjSi#uHn9<}Ib>lw4$Yej@{i`Wz7v1JCmqX~g5ly3N+`aF8w58-5^u*1v9m_*+GbU>RU#QRy6Fol?McsCDjg3^2@Ep9-?w z>L`HE9oDZYos6)476U+}^&UNKlyz=DMz+fudLGVpTZ`z(C)Vmfze}8zLDf>c)$bel z++*El0xrSoMqBwlEB;0apPMyhH-!7G@8|$ewwBa@9I(dj1^kfp@CSe&wyvU{K&n-c zjIpFyS@iX!TTAI;a+Won^5kr5OD#-ttb<(uXAJ;l)$c9Z!=%?5K%Y&Y zHD?iU{Z@}4L?5uupz3AN8o3EW8nJTbgFLX_q2g!MI*SS2m~}%roISLDM5XLwYo#5! z3F}?DjPbh zGbt}9wf#fO=m}fqTH7}2$X~WyI1H0I+cEl+uXt?g+59@s3@TzP1-P)FjCEt^(`ahpj3;bYsYbmTK(yG?n)6Ptm4vq{?r zH(@emOK1kb*Zy)KberupRC)W`*Y|=8usc)1ztx`k5(W`y&r>4aHv42hNRYjPitJGP z|K0=%v!9#>FWc>#=r`M8pSBI$PJ8PJxN!T=-Qc3__3IHY#=iG|;NtDpa&UX>mnjL} zZ=d-Eyd>M3pGKeq_83|RQ|zI%DIT=X8G!DP{h=A7&a{tj10c)(%?ogyZU5*VOmgfO zXa~x*Pf^l+#J-JIqNDc4d*Jfym9+om+g*|ow!r>^1-e3eH!ZQH_GjoFov=In0`8=J zE`2>^_IF+e?zFv@1zd%_wz1zCt4sJ zv*&#d^3dKThVYSn9sNAx_7^X}(${P72RPg86<7ib0N`CTU*RsArY#6)*{5hS%)hKr+1ws5r>-8l_J-&uift!1KK_s8THODjkKe z&}(iYOiH|7p}qf@SEw9bj(c&Rg3nSf`xOK#^U{9@K)Kh!rvNzRwf|WFPJ4yK!sMdY z+-1pLfqDX)1IF!A+vr_9mMdm}a6{Jllp&;@u`&Bj=^dfV?q80bAl z%YCr-QgrVLSI3Qu(^FM`fyZ0pZHNw5$T7@Ty@HSHB5$XNPIdDs8J9Dh}d(>`^^S(wsk9coR4@~xW|6K>N-+M1*s>$A;{Q=zp@5A&%rg&HD zfJ^sIy#|uu{W3LdGQIz!wq2I@Z7L3Oy_eIm)e-N%5o# z1Iv2v)E1B{-u8U}G((2t22Fu&t z|D%$-)BE#4e3)I{@A<>B&-*nxf$R4^+zOKi-raPnKI*;t94yDY-|KA0Os@LuIt7z!KFy8LHT!%{hak6n;@3df;?qtgRI5+!BLHsuEP4&D z+kI;3faI>voUg!j_^hYfwVggQsaMkFv$h{xkI(vb80USTNA!0+y*^nl0^a9yo4RoQ zKJ&yddEmq6f*bYurUSY$pRq46mWMv4InX`w`I;u)xX-%l5c)2=e;y{Am#wryw`H01 zL*V?DC8t8?zs#3@g{w zxpQaEoO5PKU~Har2=#Nm^y+$uER?o+1KuL(TMFctOCNp@utIu{D!SQHW(T^oQd$@V zutqv%EY#OZ*Hf8rlQfH}ubZW=Q(z@mx|k(lxYz_DVNWU-n76 z>;~8`t(^{VKw3akW=nC$g-$*KT5Wt99R~tbN-5KuZ0UPFA-ou=ldGCcu*AXDNjmA#bVx7%OKN z!pb;#AKIBG$whQLlP>>#I&996pQ6rZ$Qw;SC+5lDQ8YSV-nTcfEcrn4&`ab$d<(sm za=(56tK`k)XkoMb&v>Zk%I{EZbc=lCJxFeq=TS_%L*9h$>FkvM^(Q2E$@>L^m?tl# zOK^MT3QE%U$*+chxL>}%F+>i?do=}iSRPs%;E3G%6O84{r%)hxR30dW$Z`41IQ062 z{BR&TaZ>&*W!{DI=TvJhlKZrU%`!Jc_ZH$4D7AE1%>YK z$4OY1(2Mr*hzJid5TWMK%B35Lu0l;u_FRlregGRU<(z$&IgvI7~UUXiK6WR zC@fWsk)spK6hmoEFIV)hgBI2(emDZ+TE(CkfOU#ml!dHUWG?`3gCgTF^foC{euTni z#mHgkV6H-20I)@o-3G+1iWTHPcPO0cHFqi+(DL1-IBNkbd5X2SA+lT1kao&_idq^N z+pieD3B&`6IhTPQRK#wA!XZU!7O;FpzypAzirKBu`Z2}xpTR3o+^3BFxT28GGzt|r z$wQw~bfvX&S}}qOHbsgCegJ0__h&)!qT<`yXyKA#-6g1(DEcxWURLy9356?)J>&~+ zDtu`qZYgG`0Nhs0p9-6I6s5_KysJnW50QI{M?GNkiQ)m><#?)iM*I6~#a}yM`i&wy z1Ym@+pAFPUDxXnum!j+EA39tE@vWy+s*8Da|hB zx)s3klt~mz?N&CTsB(|ekv6cs%9(z^4k*{}1Mi@6`DhRiDMeHaIILXt5a5XNG5PCb z%BW%JXMs{pIo@&Q_B8NLC>PLfcv88RZX29d1_VH)NI7g7cxRO3#-Z7>$~ok-ij^!a znG4GLbi8&^*^E5xC1on5&Lzs;ze4Y_(!&|Xt|?E@x4o{sa{`h#ly}lVELF}u0?C_7 zmcriq%6Jt-9w>dwq4!WJDh2jPd1n_y9xJoSsXkNIibQ+Qm3?SGdZCP=g4;{wEArQ` zlnIn%zg6b?0K8M$&P5CFl?y5F|Daqj3|7jNucNSWe9)xmR6pQcLW(Cg`{6Hh>#p(-1X_U5WoN#M;>h24VQVimg8iBZ4HTWcmYg9MsXg^2weR~i$sYZ>1NS>6q+cvRqkZvjw;Xzl6O^_^^km^3i$@$p{iLu5Fe>JQC}Xb zG*mx(rdsePc+XWHRN#A|+P)h@@KWXI50N*jk>pt3s!9~#y;Jq1P5-^h))!Vjs(M91 z?~}@fs#N8wqaVOaR?nc~N{agUL13fQ188cDR@+hXGe#YK5xg|@j1urBs%O$M{3P{! zDk7w-r%@4avf7nS3a6=u?19L1^(H!*oS}A4hF*qx;ZYE0s!!?Br8(+Jbj4||x;~wV z%~Q|c1d;jbXzIiQwL=n&Em9Abp!LP-zO>P1sV%85OVl&-fGt(8dK64NnXPU# z7uZU59>ol+)E~*^tyaI@58gWUbDH|=)kmX2+@QX*8DOKj^hbak^=&HOh$9KA*T z(GA3{>gz3_uuc7jLjLXQ&2$d5N4=&QTHmXdP;$Ocy>%-Z+OO_RiSz;Wne)I7tK*MC z@`&28D?q;bfE9R0)mc;{JEpe%9_lC5&8T*9Qr&bi-l0(a6K%Ao)N}WPcUrxl{L)#q zwh*9LJ)CYwpHrW>3CZ*7M`s{%LA{=~f)aHG1!kAksRMysQJyK zszWGgx~cZ3`#rbR5lhjD+iFJ&*6*nMQQiB#dM*_WAE?LBq2NQcGq&4p!EMuC@eNA3AaptmK9Y&O?1msH+XYuF&4e;N^w>O*znk z(3f=Raxk>G7O-QXGbz3<2yGb)a6Gi{9{?vpH(WsrXF>z!LGo;9Ru+htLLbtxV@YT> zMO`;SPY!|2($GfDVC7!uh-v7{{m|RL06YlYehj>ap{KuxmFJN{ZO~Wwo)@a(&{+^>bkcG}~(sU*7y;<|kN?^Gfmt&CJqRFD0l-o5+KEU)2jr|EI z?9>cag1AdFSq);IW+^S1y_&mqp|DSLdm@PYHFKW!0_1BR ze+%MK&Fv-t$24beK(9d4_Y8O^HD$3dR;Zakhc2fyXSPD*v}Vn4G+U$zlfh=OW(jxzPk77d3}gz{)kvy~e<`fZjg&j z3Cp6|@2D{CBoIf3v2;CeOxVL-0As_NPz7XSSo7YnGAZmGRpHXZGB1KRIc$9<)Tf21 zivXsFb)_(LMwqN4co|`>v(et{un{6C%n3V2C&Y8ZQmEcKFRTG&ON+y7??5suES*kw zvcrUU+Z)QV_K-tRdB*4~7k`gq_#`>}lAl2&lgf zi%mr*-h>H$1o#-%>napJg$@4>BIRM<(KeB+ZMXP&z!S_>-e zq-l#kq1lPr6?8~9Q=3CI<5^lq3MXf4mr<}jNBavEedlXePlVnAZJq5<$kbZ&23V*) zMU{+YTHJlW`qMg5{JBCqsTDxBcHQ4Fwo?0T1Mt>pJ%OtJBy-B72TvI>p9omyw=)_K~XfhObY46*?be{G^ zDTuqZjcKjy(@v+Pe!n(=?m-;T2GSxqsI?}~cSsvg1@C;VS5v4T)y@lt$T4m6H2?+L zpBn-k*WRG>#v<*aX%IQ1O|B2(S#1EtV8z-&Gth~1+V{<1^P=`f1h7lm+y1~xwDqP# z;j;EV^ApN>JZxlPU5~4*%vS^kqbNU-Doh!`Dv*mJ&X?9e88Hh18|7;aT4Rj0@ML zz-DUr9ZE^l!q?M~PYl0L({xh!Df)Zu^l-;jAWjYcXFJ-P7H&Bo#OdLw^mm^#!tZ^A zmAT==sV+4y+|de>^TRXfzhP!jXkksb?|;x+ z8@^x(z^3qD+d^b>_`1tb&kZm2Knpv=KhY+&E4=?Fw4N8fpRV;C2(SAK6b^+` zeAFTE3c_zB0Gtl*?*m>@_}X+}XTrN{VDoHvn;U4aIQ%%x@^j%WX-1w8FQvl#h46h; z>bw{(qzcxh@H^z$OTsVs0lOUj7gfcsgm-TV;??lBiO{*4(eLGMQRK1wu8 z!w0W{+9IY#$wEns7HF={AG z)LE{E!X%yY4-nIJKhXD^tm|+aElkrr+Jp{H*A1cgWrnUc)o?R(|4_m>Th}!n#^&gL zr@&>dt}hjf=jl8W05Ww0==^q}?jXf=i*%uMZn#+2WD1B&bs-DT-ZI_za{$YA^?m?Y zp^J@#1!B>v%d{gi?>3Y#T+oEIr zVP&hXT_AYd&^n0QbwkKU?$S-(2wtAfhE581>s-mj?$JFU2fttUj5hrPx)W4;I;d-F z0Pm2_kLFpvZVP1vM|I!W0vywIrzu>ZJ4d5*TGy6lL6L4{e}FT(w|eNE)qVT~P^??I z2qG7BF0{Q|)cx`YL@w#3?nci_bhlrFcv;5`1@W4$bt%AgUD7GEcSCpaIC!NxX)O?M z>c&h$dv|pG$(P^N%?Sr~Pq#ZCR_^PT(*k;+D{2DpSeHxL;1k_$TJ}$MZnTb`>Eas! zd#$2aYp*OlNlc4Zc=g)xnPFLqCh-JF@%OUwu=R)DnC!Kl^nl0CL zUkj0BF6=ibq;OmiBu8rP85XW*)djO2*?2AF1z};#HD`{NZJy@B@ z4crOhByJd$VWx0X$c;|rawt@p#uYSx-gIseRmEm-=T3q+i?g{4Y&KUn8m-UazFP(n z#Dz+OqPv>y&x#=6U)=|K7oQv85g%jLEDo&r|)}~-23c08KK`i2a>jaTA-0TSeXSrV~ zrY+`r`vIKe(rM?q$k|%~T;jAIXs?8uIR)S{mr2p~6|P5L@UCrC6(U2g75VD~s{Dz4n;R#8CrfD5H_nTK2o-CBLlWqJX;;MVnpm6u$O z4S27(LG<0j`;0I@J2-BF9dH? z#L>UO8y#`3Hi%;)`rd-b*oeGLba!IJV^4@oif|r`aY>KZNC&r*BX-fG&4~Cf62zGi zejgw?D`E&$rDsPBr>u2e#E%5#N3eV)KVk}nTT3D)*Ma2Hh>^ztmPPo?2FQ;1eHz+Z z8L`v>V12~q-(hn@M7OT!%f^VM(_tkiVsaSNb0hBAgSRDO_Dv9XMOdzeNM1zoLRi@y zkw>ZJo`@-7=K71CMcf((o2Mgo>;bVTVisLEJQI=M0mQQrnYjSP5kLP5;<*UvVqoVZ+S3ku zA>wCB&M!t-B%^1SA`+;UP!dtZ=jah#X$LBe_>D@rHzPV{K;eEweOstMh$zYicoyME zN1)Fm^t742jY!c$@?AtW#kA!SPw7fdvi{x+bZLzKz$lm=tH1OYu!(xdEJ#k$Yr|kA zT^~S^++=+O71*cgeOh1$rt8b-;C+U^^ONDD-aWa~;6Dt#?@ln-BH%9f3X4|4WYZ zvHr#gfG7HYDYki~&!I}`Ykgq^h;Q^C7J&FxpE??PAM~%%V7g47(gWB>z1APRPkKXL zNRBW(q0MHbp>rQ}KE;q=4KT{!I|RJ3hVmfr#u?^NT`|@04b7MF2K!R*CK^_emzrew zwLL`A4S^JmPB#4WD|pikC6_>)Zn#4E*bGC9V*nY3Qz{T=8-95YY>wgneMrtVl&%AD zp5ZDL*fR~W^!XPW>J$MiGQ1cC-eN-_#mOrSOOrs%Hk=WHxXv(`9K?Eq6=k0r3=iu8 z+h`a>m7&ds4|Mk+*YFQ*XIl(osS{fb2VMi)VR%Yq!kva0mN2%af9WgwlsgZAR`5sn|8g~8$)5i^SB_N(K9HJV_ zNy8uHa0?ANROT%*Y`F_?#!!nwxU&Wa8lGaqrjq~{3|8cHE*f6aPI<}D^Chg57=(R6 zykWRI849Ha^<@xm8eY<^>RX0vitFwgM$%lmXV|0#cHiJY9_4|dJ^dZgGsEZ<5T6?s z2LQYM0YhJGfB7Cdz!8>_m_gwuhbqkYG1SfRa>%;+fCMEPq6cxRbKAHlo8xE+Db66SA8$gVMdg8Ip5JzFrX z3)EK&deUCEPH^D>K#m}V)@ZKaVK@}F30_hacBdeNUTn9ZAr&k43x?K%l|zDEG+6n9 zJ>!8D2u}1yLnj5xX|fav+Acr~#e!8-JH8-@R6w#s@H`M!t_p_r0w@*C`3nlS1s!O3 z?g?HfA@We*l@IkN0(X8B7v!9T$ZJ6_J-|D`LE25r1a_3mmJ8zkfaw&Wh`jL_;Vs(T zQiU^jLNZM_+6uL{Hi#F5%_#~l5x#i>a8;O}31g+gLt&7- zEv!R+`=0RF6Bv6a{N_i9JP{U98uCJT{2{<=Vd8%P?}V%C1C$9DP^q+B=tfoT6j8?C z&>JI~83miEqExzWmnPcT6U1~;Ed90JG|_GfIWt6yCO~1fXu$6P^F-_EvRI~Q#tMjJ ziT>+|zAO_l$7x(d!c16MEjpVA;yTfz55RInd2ax6MVntieVeEY3-z6%axK(%iwZ&j z_KRjyHSUn;uh!^bzGwG`H1Mh<9BTerT(NDBGu8Mk} zLhGd>{}7nIEecNsxF?cP2Oo+YLLvD?WZxCo3(@=bkbEt=MW@#9MD~=vl!*onfd@?qo+(0o*0&L#zk=d2>G7iZZbRwyq8eC)qA^5?;iPy$g|IR(KG^3#>x{+ACoL8bIu$4-OMu;D|KJNSY!`1}PuLaf(fJo_)1CmY*)4Pq@s6En0V`$fKMeHB z+1dlZOA$+{!Z1d>I|Qav#o`kHY2yB80Mf<(kt3TX_WlxP?#+)zXU7u#M2uA z%M>S4i6Bc{&k>T##5)E+GF#l2F6pio4|@QSbz(6;U&JrdAd)K{>x=fbiNEoJ$WC$R z9RRz<{V5UHFMcb7!Xa@s-TufI3r>JnAl`fs#FOHIbPcLV{C6DGi^aJVd|wa`cnGXS z+;9xAtKx+m^h(8-E`WGjys!(fd*Y8D0UnA!?S#k^v4jKfh1g~sjJ+0@*#W#0H>LSm zCU$KDk#g}-+P_jH$7wl@kvLN9mn!+5WWtqGY*fPuGegN5)_mj|x)t1ZaquF(q<@!b$(@#|FJNr9y%C z2r9N@M?m3%<&-~Qv&3>0RmZMcTGB|ATJE6le%sO_p9aK|*#hjLB^5x^t%Ru{ zPP1w<3-6F&)wB>`ww0td^yXQ8(*Tm0R!LjX&n&CORR3OPb(YfIY^%7w;H|d0F#}+o zRpv_Ql^R=(|NT&$Fbps?F2g#PAozg3TEX!elRYzJWZR)=W^DzN&s z1mL7qeaeW7tU`LAp<=6FsAzS;%8|~BO02g11(B;(^@?G$)T)@SNZq#TDgt)TYVImv z53O#9A^F7W5|wmcSV^p5<+YVfC+NMi`ZyjUWmfUzlgq7|9YY7lSpi6G=-;H ztEnnF%Q~Ih?_BE%{{mZJJ&9^;i>%#;0b637sfWUH>njxZthDZ#18lu@^kQH+*4Iwc zyIY6;425mh_vxz49_#j0(b#W&PKVZySl6Y5<(PF|Ur3&?E?WTNDeK59z>2Ng&|%00 z>x*>tw!}J&mgrUMRjvRxtuN5H+_8Q$6X3D6BSja_ti!0#{?dA4DQv#6j-rI~y>;3u zfRENIDZom$sZHhH6r0<}AUV#aO*x1YY@UyVl}R>b)R!qX_T|8)+a!_;nrR~ogvcD5 zPD7wD-=@!6fQ2?ubs>^v)9fgEw#;Tf)kU&x8Yy9Vwau?fz*}cyK`wTq&G*T8qs=zU z??ZB{&D%c#cGzT`fkK|m3_AVVYg0!8n+I%OP*8K&CT0V$qc*>cgZgos?`ahm+WavX zSdoqUedrb2j5`Yv48Q;=l-S7r1$NbD0M#3A*mNMhTQ*xMy}obLm;#qaHfjCowiG9iSD-T(i+%4TQz;+L$)`mo|12?pzOTBc4r3E zPug0P0u(thqcy!2@MshxqP z+8cI)mu7#vITX_ElO{l6s{QRA;APkwX!D+Jf96kgaGrf;1GJZE-;IVR%l;qQwU^m9 zzXH8%`^6L|ueRS$(`~)|h3nAEvHzRmtX%sZt{`r+FD!@TF8ftf*4$&?lSX2{{e8L& zaLE28C7nm@htQ3nsZ2!I`uxs`|&>XyJ zU)~++ckBx(RJm`zd^;2#+3Qz8{h9r3ItG4e-|iA5-`M~37ewCM`_o{3vhOYe7~x=< z2a!<@9rr+Ctb?i$>JuDx(R!ccaElJ^r#RGE0LkeN)5-bIa=1^YCvzQqa$#(N!`PP4 zTjU@b4s5AITN>mQ4omi+gR2}ED%GrYNFd%uhjH$}Haq+w0@&)%j26NU2VEENb~{-3 zz~(-OU((Q*gAQ*vU`HI5P!X`eVLffTCmr4%hRxFs*`3hdSqC4g?_Y2jx*JwX9JciZ z@2bP)DUiJ3knwlsb#PA>16DZ z;|@wxt~j1w3cc%&qv;gurlZRPV0Rt=`2pAi$0Di>K6Z4aa>FynL4L6L%25>xk++Vm z+X4IF*mx@>KRNo*vKZ-=8=aOSxo2~o zCeYN_<~02uDC~6lPX@`|PJar}-hQW1Byz|pMFFQ3k3!B%RR1E=2oo>@Cxb0L%_YCekEjkbEk(1R)NIrG)7XiF* z`Y;*bjnm9`u=3t%2AzI=bV{ccmt1SMJ@iJ^x}?T4UdVIHgwK z91y41`atIpGizOlM+fKBI_e6rpqB4`h%Bnrp$}{>sbyCiV0o>3mtb>Mt;bZ!SX*n& zROoG};eg{(>Pf+C5=@}L0 zJKv>xYL`)zf^{`?hUq`nQT*unGtHfDC!?V2HM}WgyvI=R;InllD89;O`0ei20+N0o zBJdpW-a}~W!8|pnDU9$a#K$uKk^`K;6z)fdCNbx_T9~BXbVVOp`A|OS+JK^{*8jbQ zUYi3O@!R%4j{09>NqwG6PNidCTE|@`)Ag3FYCHUPYtp}f9D4rhRIvB0|Z9!3Z!ksCQOl-Fpze$Jp zf;;e50NNe0o3hP+GU=M|e|5d_Jk)YLeurJkgzoTvDdbOPrqPZ!g1Jix$0$Zm+skO? z@&RCDnM5BL8^^r81Fcj>P0M0D6Wt780(0av)F4^3%<&~qn9X?6l$ysRkr$rNB%}i@WWuN-yomX3Ak>#I zkyNi;%1FsetzZ<50kWCgnINuavT4NEFkS3`tz#~c^Iy-LxB&GH%zMh;bC}wfz}v() z1i;v4MouC47RI>kIW6q*grAd6TnLrG@@O5q(HJ5ywQT%-vApUNTI8use)%uATnOy zO=XHS!TDimVWQxBIBx1XU!a3Zk>or5S=7uHa<|I%*&}OVGm^ zV6I?9Iwa=_V#VMs5ZrV#E!dxzOoD3bW0bM9^;(H%zqki@?$KxqT2-z;4XwyeRJZD9 zn1kOz^;Y6Hh50WJU@{|L1dVJaoi6llVV0`JMmNBITnoZR%MOnqm1DV_s*#&4XCb!d zOa7v{YM8aX)CS%CW?~(fs56hwIeu(<8}dJ$raiO%5(AVP*!}{F^9b?@4Ldx;uZzua zXf^e5fXr_#=m@wOL)Ubh7n1*Nku@Hc-MZBWrA@>+*ld@77nI*$J^;5f+Y4HMSWw-- zOG`(#z7{lq{!7at8B$0YldKkAf>nM?iJjja?%w+oL)0lbVO%7Lbou)=Jj*5kz-(#ilLnryYD;oOEoz5#;eJsMWo4XXvdF0cP zQoGYMfjW+&7wlq9Sz6Fqs=tI3Q>9bC8OL}mgpE{YCk5%_nN>RX9iQGIfJ=Ji!p;)OVP+oW*dc4vzUuiZ=21ydq8py zQ|t!fT*igcs(DPr3A8t#xoih)0TV|KC6hVy8j=f{w!Z^g#I&ph(~FrMl#ORGrL-F? zVe*%Qx0DH{!CJ-?j0d)yaiMHHn^|9o7FII5Xx^=27Qkikquifewj04uXBzK>*~!eXe*mU1wO7N;RA$wm zz!ot-&xD~>Ok^hzH#7gPghDR!^aVfx6LuR`E;2{fqpLTVvS^ro&S;iH;RCaRqJ}iV z@kIbD1=h|`-z50u4it6@uFQi-z98{3c&7!v2B=>aw9SXj8-mkx#{5w5(+&_{2uAD z^jd(v92B)x06QdlHWHGDMQ!Fn@`%W{B_vOY#MIr>q83yIyd(-c39Lkv=>?HHqGxp0 z_)^s9D0s_OLzDAi0;_N4xj|w$*H?A7t&jfOv=+jD z#@?houYhgc48-GX&tYij1beYFz)99)82VYrX4Z$uDYpA#5KrSI5yT>PJ=MX_uzv}m zcNUQp8Y*TV>;pK*F1QNbd3GzUjtgwqd-UZZJDyI5FR}hn043}*TI82m18uHX*cJc4 z%2oCU+AglKPokiHoqe+z#2f59O7ctDyx%~)iGOzxuxBhwwY}$T-~V9cCHwvF03*e% zsmPWh7SmPE(c-jzFg->*#Tvx1Vvi%}%Q$h`0Sr&7_{Bw7887}wXT1}|=P1rg6ZhT< zn-j&Ob0IlNJeMw#ri-_+P?#)!L7~eOaqCvVriw37z%@sr^)d14J7}mttfVuQ zGLSqe-pznlDDFTfd8fof52B&dVwWKRMdE@e@Xm;zw}Y{>;)dVCSh4u> z&)}UCZ=vk)yf}>}<^^%L0!UsIkF^4JN!;%jU?t+WR7trk_UQ=X6>*c==;u}OR0;*J zi8c8Uxh|gZEktgJb?rec6({?kg_~l1M@T*sH&lc8T>L#5dnw*bf86~_JdY}puf@gW z3*Lw~(-q~n;+FI^--&}LNO&*aa}0VP#II=&D-&l>%J@m(QH3g~*t*;4Q}NZPIiwowvZ2rD@frY^8elFJzwv(1t@RH4X~ zT%tL+MH0IJV5{UV)ls)eB1#~#U1I+Yzz#_sT~OI6SuzwhcS(xr*g8)#F#`&_C4FZ@ zVUI)=3dy~ay6eE(Cuu@AefCQxb^`B!WIEl8I4GI(0~8KPN~r2_Legt4jvNtOvcHa` z+d6FzorTID1E~%hG=T~g+8L+u+cpm!KQOKk+k5NV8RV_b|NUc-k+v@zg z2FAW^O%(x`$$vu7-HNVIwCzJBunuwGgWKt^ba1;Kc>{VkYhN_tvy@!C-y+J5dYqU@ z&DM!CMRS3Zd?0;L=tcR(5n-c)rg*M|sdY+VA-=&RJq6=7Q*I08*G$W$=*xR%+YAs# z2$s?%-?4(*|C)?7_<_#yoKLp^x#6=}`2D5#c2Jrf8Gz3FWKY7cf9)o)^kN5S`g3!lH&!kC}0Z(?P%LtE@(irX}utnqKI5U zZR%i=V1x@^?2cdu1qH7K9m2sY6Evk|^HHGMf@Vhvr*uX?#|xiQC^K1@PDcz=gsybZ zG)pKO3)8cO9(RB(6#hqFbeXX82^)lg~m_h7RQAa&Qri+eK$dw_AZ~~YqT5yEg6UEPmLYByn0)`c$ zvlr3O8d07Xu=S$G<-j(H{7yn7SLEe`_AZD{(W%a5(F96duZy0^LA)h$pgDg}w2@BC zABnC|_V!G)rY(4{MC=hrmWgsGuKg&wIuz=kMBa|bY62D1O^q`xWoqwsu`Sw|`AT-!U`@z3zPU zD8f`~XXvLz(3#Bmw-H&0r!sB}U~K`jb;tkU4%lmg9s{9zM=<$6c&h)+Ic#Ox{fe17v{2Nf{tEZ%3Bh!b9xLXCYmIB)* zIJ^YdcEM{(q;?4G_QBRp!K?3q?GkM51d%*J5;>Zqf>&1H9TSvV;&od<94%f|AH*@@zfZu}SaI?!G&D|JN~u$- z`0_(EG+taN2bdrZ>HuDv*q-9RiQ)-Vx0@tRr$i%N+>4yrWbx#9U{k~!MnQ6_c&-OT zriq0Nc+cu#x#dE#@+PDSO?{XejL3%{a=zJHXt0 z0rh<5?gDiG1fy^VR>WMF13S-5O$WHZ#I1nB6=q~DU{{&VxzM}GOpQZB_n0K4b;i{a zO*bCwZ0kX`&OVuG$Loo^V5kodq2ZTh1jD%e7H%uHn3yhEcin9KPf&SL7(ZZnr@z87{DFpa;3(S=N{z5t7u zp;U5N%!vL1wuA{zgR!N|eL5ms#_Xmet+kAI2}CBbGbaFB#oqb>;3V7Y3=|4kJ)Llz zVmsYOL#NplVNfq-ALar($1b5aKhO5RYg&@_ov^;yx-k~u*6Zkv=Wj>RLELx!+(G$q z-)e~ebm=fm)Ze@hlm-$#D9(2&jB42K9^`*+a0AUX`YsR8jT61W{UvW0o|}A=ghBgt zc{@D2a9!}+H1`3Xe``d+cr&lYcy8W*K7LyqL|Vpw%of{N*ljs>9jtgH-$y^%YIdU4 zc6G@mw$HEwy~DV2JbNybL9<(GJIMdJAqG9@@lPly0UIUI3RKX6T9C~>(1SZQ!EeYc zy6@2QemZoe(+@yWb|eV>2-|-Jbgez*NZ}sjLVI1;qLto_l0fMrISy+vcMh6@FXtT% zA>H`(R(i{y-hkJ!H*H#-ej5q9on=&z>QcT7R=U2T*#B?8Owj*z-w%2!(__79(3?CA z2I1FE%^~R0l;%y-0rV$Nzd4LYqs<;rQM>ulZ(+Da9Bnx*|J(#gSt$AM(4Gq*6V-#F z!^E(9_)V%y>vs6CpJ8VNV<|w3Gnwg+0hTf^Y(U(`Ozi@lBTTQ~fE6-FE}GWPbY{H^ znwrfxP*{}3cy2LuP?Se8ZR63D-u?2f3!VoKd1X>s&lLEgz3oh3Lns|!yiP&r1QS3P z1I{ytU;*W;e%(NP$lSec+SA@LWo^JHV_v@j_{dzli&y!?{ImgL7oWG(LL~6HwiQjK*slxXCgv;Rm&?yHNesrWP@#niO zKxs61B`A$sl5_ZV;%NN3#E}1K>QB4TZ=H{tUT=orzgHm45hT)l$rKEs)9WRIIgX}2 zh!i8x#q&&r3&2CBNg2Q+=4U^cdd&204D1P$<_FcMjI9Q$&lsOcAU*=2D1woSFYW0RZ{kTp5nApgENm0?-{`$y1F|pDA-xyCAl3-Eu%SFcZAAr8Z zCML9^zDM@a$HcaZ?4J;m(8|=auP9a-mHttGeMPL2{eCfqczt{fMlvZerr-b57lk!# z@5sn%quH)y>y~cjG5t!HD);;=A~CUv(fGcJF>$f{XD6}9xY%AXy}zPR*=DP#_?Usw z@d^J=->}Ac@|E_hcequ*=-&FsAzzWM^ifQGK>mL)@ zGLn`@B?3q7#mDOVwd_M2E-Jd0KB-?KmpEiVbOLCIiMWKsC`bXps*Q|`kLDu#w*iI~ zh}oDJ-JhBS4iBdvFo2^e0Sv3MUtHv0My(2SumEdl1aygoMSL{ot5KvHs; zJj?jeK)|IoENHF*x2_QA-#;!E0x02B^cZbaJfn)xD_WnJ6wkjHdcws<55nZ*=o4dp zKouf1APGqWfEfE~z*+(VLltj{2M>49@WQ6y@Q+PMj0Xin!ml_E^Vx(N$ICY?rJ3&^diVLUcTRZ%mHZxcI1;NLVs`HIU4+ha1pOpV%udzQ0kD$kFlf zaWue?qVE}pNBUO!*n}A4qw!N3pDU&YzFG9_r^26YsKV* zh==;PCq7yqRf%MNgrK<>WDB#A?;qKwH^bsH_t)O|Ry@iLiq_M5Fp3PutH(!2 zMN{7}Y-9uzGcGAUlAoStp$fy8)z}r$vW11vk@;3=Jz2nD$1{@}G2aQTs*>80)PgEf z`4(T&N@}1zsev|<`BHaA4YU7-4NjXuZA4>*mx*Edur4P)0agu)2!$dC=d0KA{@@GU%g+F~W~bRrwpBPXqml zi5}$ZCFgvk(qO4f?kQKuIA5t}ppUmtfM=-OKhP&wA?HHk5MsD00-9RLL;N{!AEm!H z7ZfkY8jbdk9gvjB1t$Cjfx&LRiU8-P7TrVqgE?=1DHq}&g^+~X7vj;Z#U4>OB&1t zdWLWvToB&G$2HeCZ`rbCQ)eD=(KmJeEYaTCB;w3*N>9#H+FizR&G9x;Pj7l1&u;ub zrZ@F9{m=MLRYmxwef_E1(BS1^M7hdd0=YMn@)o9yo6< zgvQR#6Z`-fr;z!0JG&+Y7{3y}E)6+-s5YEezi55Dvx^&jXsO)G_(jk)E?6n`4Nz4Z z@|q<0SFikq-P&{f3^9GhKo#B?bB%u!PNwKaQ%>c{$)p`P&q%ls%rm}u5)2CTH%^EU zqr58wtIZ0&DgPkqe`_uvZjh-N;|u!);jLR&SMdx9G0t|*kK>#z{M{?fJh?hVE>(nh zR~d=QAECABBe-E&<6WIy+&j=cea|PbbQ|8muf*;~a1B|i7M=&o?<9w}IhW~gW zs=&|RUFOph9|)cIcI9NziKcH;Y0xogjKk1|I>~V|l|LreV0Twn*D7AXtxc6_z`LB! zZYD6;+ozJ(;ZapgY*gH!?(s2j0Q?BlyjVOs@ExoCjm!%LEYmvT7n;;lCgo&&ciq}6 z(AR(fPcJV;AmW8aQ#B5j1dCG$W{k=F}AGwzwrw*37 z4B(LP>6;JKBN%Gxj2+$_P;*J}BfX*%Bl~cJ<^}iQ?b^_r&O%9s&81c3~!uvBYZ-?V5wYY?wC7#pO2^1 z{H09|Rh|x?mu$OgFYz@gzOgFP&DHFi-P-atDr>r$T>{Ut@IrLt9c@VJ&3XHIVa9oJ zex3nxW6SVtG&8;;Qe^;XgvcpZA9(Wsf98KygD*FeJ8*t6z5AF(!^^AElIA!1Y6DS& z0B_YRAQ*F-_giGxOD@M|YTD^duTp~quSydY-UDOfEA^52ab7{DF>oDh+&PW1 zDsMT5Z6XLi)dPqsEMsH*{?@%U@9wFCMo-$^j9Xws4{-Ml@OI%d6Z3L#ap9*seG;DT z+^CT=`G=;?O)3#!uc?J6rdY)|1O?0SPQd}nFFkm$3NsWNzA2KzQ#TqRH!iqWuPS>} zcUN={Ua@L#jRf9vV#6?nJG_@M54*{^&@`wf!H+~h2rXC|bqWj2Ai9?u*IpTA4y7ub z2){~If!L7!;emsy??-%8W?F7iA5U-W*Rl{2tF{T#Zc;HgmGV*;6YIdmDi_FAVRc2cz#SXz87Dv z&&ncv^XB!(FV1$R&95D8ew9`=ucb7`Pf}B)`W4%srFiqk*XIY5GY1LPMw{O|rD|{E zhH083UQ#dGHKl4}kNDl#7}`pGLVP^soNus{YvU5j^@~YJY;FvND+R*cvAT^hFX!gc zP3q~@0|Xzn98YqSQq!i*D+k8r$hngF)-En&hYy@RedSmPU$+Hg#!ghRX;I_!^^`_q z?eJsFp){lKml%V>fg!1vMdi<3;pSc2RiB^cZ)Vz*+}x|gA{^E$=5(N^8oP13UdI1b zM0C90^K1RZKFoU@K5i!`!%wlQc@JVPnvpJuH845&E>w!OtK0jM&rgSnZIb)gPjD(L zyPLMi_Fn|!9Fj?|wrCFV6X)#a)1r0z*52M^{f1*Z7C%hX6yknPPMrxrwBzkubzmw|vo;;R7#MT3X=EAW#u^hG4=yOq zoZ|2!#4qFuzbRMq@h~558{dSFdO7MGhY&|5N9=*B>^@bl)xo*(hn{5$eHGcO>+ z$EqRu_)@_1ajCY=MfQtJh(_5iG6@Cbfzd8RYzj{tADxKmVss+8*{?`h(8O@}fU9(+ zBoF`NQ`3-&e8$5_#b>mjnd^%|K=$b$WZuC+K72r7UU{bZY+Oj@eazjRU$bAYz_?+-RP`}6 zt?$+5G-|eoA(w=@sylOAaLGG9SqvjM_9~EU$X>#Dr z1N>#E<(WP!A0ilQh!x+pn^&MGW${(qo+{9l!&VUL9fVH&nt?a1erpVE_eR4(*kQhIN(?|ZD6chh7AMu4mMb*a~ zY_+WtY#Cp&Qce+yS&8Xe?Q4HkjQ`%80;7cTMdkh49OIc@zjC?CJUxsK1bJ7bDrcpY z>|VV*P?g8GTxrm#h-X}PRVQkVBF1h@eSGMH1$qXcV(aI_r`G;*mjQ|KO`R<&<=T8l z|HoXrLc-W9I2B_}KF~AB3#l%u^VK?4g9NW?+DIvLHom@zV8*M(!S2=UnupdHCA-sf zZc67!HB^}rk_^TrP(5`)dC6ZMir^>&NArn8%!T5L$~|Q+kUT0USU(@ujDMrbZ|Pno%%KvkX~G72^+0{ECufGO-2=V-m2Pf$=fQ4mjiDfIKsCh8 zm519HYE|rgrlPa?Y=Pgxs+=~N1gg8Enq`dNr^chUHpWj|QxIcheX*T+RIO;63KrEi zwBV5HQGdnz)A#tCI=EJsLq62A=jRxZU-+0SGHe{iqpJUT++F!`Z~uAR>9m0#T=z=n z4X&OLKzz>UIu%n=5mIe>e8J`i(tOnE6$Hy_cq4Nx#iu{!NkgxRrBh?|`g0kC&z!0i zAu1Bc)<$<()3-6QEJFBdYe=v!289EtKA4|9EmCLWGHPp#hGcSYyf-RbAufX{@j>Nx zF#h2<3yvHbUk8w3PxfK4##rTxBH`!BQr+EG=J2n`FATp>nVjQ)g5vyeoy_Fr`BbOM zF&zB3)W>|l_CFrPB_!hP$Si3-iu=lpFjloHe~F6Oz_U!Z7NlxahB@PJ#WqX_FvdOJ zxR@&o@h^zV2Suv>LCEz{7{f6MBGiAK`$zXrh)$$alX%o`U7I^6#QYN-*URNMp3<~w zN9SsaoW@5Q?BmU|J30?Fo}x&5P_5112&(kS+$;XDrHUBeD;fzexikPZEj|-8rHJMP z@e8g|sfIYxt^NN$aim+j|JUQlicH;9U3TYF$I2Tr9i{W-3RypWMcJw+3Y+*gAvih3 zVNaze)No||2lO*lgLyHZAWsBkaDzOk8Zp!$z>hL*Gsc5OMA6;+y>Xc2%->Tm5-BX< zf)s(y&5)_mHG%kOKB-KgaIIewUxVULBTRXxJ~lQk5f_zRD%gBJnqbz8a;bDhr>XIB z1U!W4ZUp&?N>?KYHQtQicT?UslJDbBw9tFp;ox_Ls$s3M=v?*fYm(q!pYl<@(gjaU z=LqOtH4?`)t#VpnUaW1Zdr0#EBK9*nN|d=8VbzWpX_OLGmEt>N^rSv=l%M!{;QjfL ze_KA9s;C~5H#Ap|KS!cmP_TbcfPat=_C|eTVtkAtDKQ$3)CF%A8y6GX5BJ3JO6Eut zp)TDUGXW_sADYv+igQ!rh?@>gtL;cWNK`7eENq&DSKT-&MXK05+VatCK(%nCW*L4+ zK7V+6U2|YvwF#Og)e1}$ombx7O;riJEY;HJS3cMu#Uj+w_(4T8rYwfu);RU6ez=+> z_#S@VV@zi+pYyOPEMB;>*Z;grn+iUBvD+MmnRnCbJNTD;-Y|#4LFJ-Kr6D!$ny&v_ z9#K_{H%bG`e_gCN{{9+9wd&Awa{j`q@o*0_&Uk;-n8g{_q45{yOuKANfxfv6&tJ&$ z#cmL&^pzsOj!VKVLT&)6-7$1q+B|DaJK+BySkZg)K@+_kj+)%)zFNg)C=Lgnfw*yl z6(GeWrVzLQnU^O{kw8bv73d@P_p(Tc=^cv;dCgpFbOH!=_*Zk|vpjyX8f$;1-zqmQ zzF_m+tn{U5?(yYb-ljVRyp<D6vnDl~Cj9->Kx8}rCx;1NO7Si5Z-;aUF)3fM#mO4KnVR0$cr8MTb55G- z_=LEcW}d000Ox-d=iA|T;_iwzmC`*o$_MFs8E%VK?0z*nK}@dCp4co>&0EuY{^ANJ zABtBmTm4Uxd}mFr&2$x};vDa@$K$UJ8c+9(H6qh+QD$YVuT=9SoOd_u3l)LCo2z>p zQzZPqXjXU>T0U5^xYAeQZRkC!`2cr%;R+w{l^ESvo~XR8%-cS$AYgtOPthxOK2NWn z3V$3ZR$nr>Rf)^HQvS8*j$*rB)p8m4nlqM)SkZJl;s4_8U6kWUj%-1GN-f7`3>=Yt zQB`>Hj!AogLV+kFUP1wUNGnSNNw6C$65s$}H+yt-zkQD%_lR(hj6_SL*2>fbP#KjO z8R6lN<1b(k>yVyQ#Na^gwvUzzad;yRb|ffdpE$hmYp67si-k*kJbwiBt^-PW2xF%^ zL#%N}Lw;x8jYt0n#)1MV>du0jsf6kh`cqe?Gl}aUW>sn|qI@IThDt6PDGcR6H;yWu z$10%D)@VfUvYsFbhX>%Y1Z(MlHL6>F7X-JvOJK!hPzbM?tXc553##!ts>PXGw9I7M z2sI~{VMyr`dm<~spP33x(1?J6k`2+&E#~uiAM2Z%cH}p&MT6LdzvW~zI2$lW6}%9| z-T`3iH>1r2c8@`3@BxZ$kNm??InU)o`qMJ$7tMgDjt)_7x%ofW9Kjgmwtcj%nl0!! z{boEHNR-6%Jur|PT|)mI^q*s-{>d(ezcX0sK^KFpR6^>7eJzf9VplB0rsHspUV6rt zz@z5<^!c;msrL^OO+X$L#cOK>7dvYZGQkp7KK|Qj5dJw5$Ar|84m~Jst5PS8+|=XJ zAVP$N8)X`TDEKO1w(TXn60?^*9Mr6>dJtc$Ls01i9=CR2X&pv7`rYqbU@32ae5@OZ zkA{E&kt99a%Fdp3{E*gZM(m490jQ`E7ugl+hm0cMJqJ=PA&wVM`<*L}Q3q|2SeuDt1H=TLQ!2Oud>0A+XQ zOU#8Zq8mHE$9RQ5Kru)m{0_*7WTbYV-`(Bb-~9Cc4s-+nr3457P_`xxZCIGC?UUL%6J zxft^M6TlN+kq3+Q>F%Gb{CI;@{QJ@A4uZ0e_C`oNl3cIZ8EETVgME@ObA!OWQs5GR?GCsR zG*Bc~Vpjn#h!IyXw#AY9_;2fzqb@*hk4o0sNN+gKX>!>yWCtN5lo41I=A({Lubku%!CIvq7(|+&rck^}U>h#^ zp&3%V{3?e@lP?{FlQa@Wxql;5?LDKXEd&ou!qwT~LNI4|J zivkOi_CSq7ermFQ{Adg!Q_SgdiS*7o=OkEFqL-JTpMOr)!sPD^_GzUp6vqH1sYY@W zqsXTb!8j7P0h~!(6O7rw?-sAoSxWN`lAH0h1+3?8v^JfL1GOdaU=lVZi)WMI5(po6 zx;28E4Kbt{kR|pSiI3S~b_$=-xSclLp4X((33AXwAZGJisX}0ZLI~nMJ5=d8-Z+1> zeFTU8`4Ivnaf;}N8i7rHeSbf?yF@XNn@`|ZcRvX&{&aa;(k6vy6KEBeAX*=md~0g} zf&`_Wy*-&@(eHk^xxF7jg?PmH&z)(9|6`zuDjcwak|2;xPfmPRdkh~YAXAk!8MP?c zs(e6Pj8Gh

h_dbiNV{lYX8bAiWzLWO=gkWQ3X?9U{GNp-OajUsj$l<=NDLQz%lB zp)UWzXAaj@4wg^G2h%G=KjJc|uPiZPk@eW1nt%5$^X&yCvm{)unBki4V;$B;+u{<$ z6IbGCi};hHHbd~&&K}*xFJg*U|Eg{fQ~mP}q!`>vGfak$T2a+Sa!|v$YQQO8;}e6m zg2+uIUmW#t;ON{1mqKdDnh!RSqgNtgt9glmBbI|kF(L60wBB!ecj(fIRm1* zz(V8MdT69Br-aZ&MKcplL6x#~jDyd00@J}FP<)yOA;}wvYfKrB97UEDHf%qEZF(1K zJ}mqo;kYv(`MZ*QlYh0BvCye5dBOTT>fN%KCkJb zGBDN7<&0^om*7jejfgE?5x_8-EvczTSk+BaPIGND7Z-^pG%@nGgBR5o!YRVDfMn*#7asZue?}P+QI0dQImX!gC#SP|tH* z>Re<&o~1mv+Vpg?ObDy{umpniF6)VnDs&bx>H|rU_(j2fqEM4?U7@f1GwZlTwHlmg zu@NQ%27;@rG+Aw&Sjmhac0B=jEwVy25HKWrdBDk$^;U1MaB~7cUI?PCI!{r8__w*F zl6TMtozrum!M$65`VOFtfn@D61;=2{(2&2`wwLlwIF-iH$j zI$Q_nPpw2Zhj7aivr}mYU9X}emtxBFkKLUYFXwM59K^bEbSig%z( ziwc3zx(|I{2zEKJcit`QvF$Q3{xYZ7ASr0T7I$GlFmLP-pr#EIuJhJnP{mwxd^|$7 za+}=bqMDi#m10FfBraHgmEZFIhk7wXqjeH2GotECa{WAz^a7rt?xB4-8wh5^J8T2{ z67m-Bzc>Yv+Tyi@<_LaySPE5cGa0B;)bpP!7f__T*x0)tE>LJuvG_VLRF1kEN2XNnDEum)89w_A`)nClIBdLdLL&w0wN!dxKLMT)<5h3@5l&F%L zK1&MTn%1k!!-P)%1$@m_lt==Cke|A}NGBRSA~ub;rca#8%mQh=C#?D<2K6vnY>`PECtxp%hDcLkKfQ`0XOUq3uYa-TeWb#<~SB_HUal*lO(r;v6iXcFHx zs+eJ5Cb49@kj@eN0KOD&A}72th&RYUt9;w)hZNp`ia#@izIAE_uAttRq01ez|KI;v zRna0mTQM#u?jgzff@x5tMER#wWKG^32TB=IMHGIq6P#cBj!3gmgn9E2DOaSMJY%-M z9&nBZyk%w-Q67cP4h&lZG|T{YaY}u?z81%)9y2|a27(#KLsTq$HT302R_0V9mb^NH zRb45C14Fn=>2%5_$w~&l29by!z|nW2sWvO83~5fKSV|p0IB*7ZZ~e9;rzDEhfZo)L zw%A9Q4gNC+Cy6~N;n;`TR0)?dLARk1FrEmFKp8BG$P1K=wZ($%dh zE?O}=Bvl$eBVBc$-hGtZG7Zl`5sqa?W+=tJ_klf^VjScSIjx{NZKN+13iw{Bhse*% z0bZ?Gzc3cl7+9eewi(>~QA_sBM-Jm~2`VYAprKXH2-FW8XFohYo4>&(fzI}};S%2U zaUIKD$rU|hr8>qa(QWt}3Hw6vP(dGk3IYOgQO{3|m^bk2RjKu0OucOVI*nedf}-q+ z`>rvyYM)Z_1fux5ql=>sJqRunmrm%mC1Qjj`Aj7KEw^vtQ%L$hkWwEgY>^xfi0D>A ziZFk*_eI2X+vjJ;@DM?vC$;qhth0AesHwyzDxRSJ5vCkSK|GBIDZg!_1;Nets8Dwa z|JwV@O@jv#6DMs{Rf0mOd84?M<7k3F1N>uMXS^T^#HZ5ER8o=(l|ydSg=*LSp zu!j~Tv;D={Qd9ZG-huWcI$C{=8&@!5S+C?M$)4^kd*$6NMlWVbGm|0f37VEDVR4ZO zRMJ7}e6fT z;0NOz$ZTTM!eI(sl;5ysHpqCaM3-oIQ(?ON+ndTf5pPg$H7<(Gh3{jkkA#HA5f$aZ z?DOWFI~MA@j$cOZ)#vW&?!(>vKmojIF5PBEkwt%{>Ez4Vqi8x(*gRk{sDC5Xxe><{ zwK^v|agFpQ2thpS)mM7g`88RHggnV=N`(`@6a*D>&Tgl^pQP7lm$Z$ZO!u2>2D(@A$QW!JRdUFW3vF!`?#xSDI z=5L^2r2Mlihh#D)hd3EhR)g!o)QPickKuH5XH3_#eVH;kM_*WT9U}zsN_K}&Fsrz6 znKzf@Y6*S8eehuf^Ye!3%}>npFHNHWCA&J!uN4ILJT0S9}8 zAK&3;y@y~KMm8Djajv2*2c?s%^Sxx!O;+PqH(OBCiZSQoB)7?dn6VJ!z?j-opNF~o ztkwLlr~+%r)JO$Q{d^OuxKWd1wzILg`S%=aSU%B82!(IS?w9Z>J^qYy;n@bOY{VX~ z* zQ?OtpX>w^3i2JYiX8N|2=zc3Q+Yn_i2=%0b3F$z3f@qxa+2WZXJ_S2X^$1`^1_5;0 z{S*@Q@`^<)B2LJx0M3|mQ5zVp9ybp`73svJF{w^in3MQYjel)+5gM&t50`V%)mr0G z9kU>Bs_x|DNpMwVQZ^r$sTN9*n99Y58c`2SI)tlarAk)1VC!jO-1ZclgNAmLW$iJE zTSFTYgh{IHCK7EhRjy3&ja4o?9=a3ORu}&k|I4<4Y&nA?mE6Xg=R_1SR~ctNc%;g< zTI2_CGp}7c6+s9vg`nus=9j@E?R6j{ub-~rt7O)lgVUj zbNv!Uy7v&aSIYU)KrfNr5`~JZ*O!+cx}VVC4aqps92oK`QUjyP5~R4i#w^#ctM+z& zIv&kW|2Y0rE2C7>&f+#_CLuRf<|c!<&xx?fKY&M?@-(3l<6aaLU+Fq&#{6iVnn*w!Q!TpAkIT$O0cS0rDX zbG^&FrEG%wauS>F1tzuF9BO*S759TOJJt%(&^lbkV+#L>l{V_cPP%->+=(PJIE?S& zj+@X;GF{>=1_tp4Z5zhGCk;#unz|{|ldiDUig3Z`Q8Zm6YRwP9dtaO$pPyJGww0x}XLFwc&_$0nh?!l) zy&>!{wIoO@YV657cVtpJV@(CKjK(-8en0Q0Ps7mjpWj`z=J;jZb}LgN-avI$3RVc+ zLD9nY819y-w*q~MZ?aJsAblu;1289W1})84B^lW@2&Qk6%GvamKp+Ywd+v}lCB4Ly z5wlGTc$kpi+F_4v12ivA2=1a7rCKZI6)OA)E^Wug5byqVu0V*y)6yJ-b?g}T za&|4`g1XEQ9nfj>IrbMVD4_drn0!nkG~gPdB<6DJ#^ceCtUso_z1p zUqhWBc}F~Dy2Hn$@X91Ujfd0jlRLSP0Dq&+jdZ8+bo+VpPJpE=dC{arXl4=^89bz= z1L+C$c+G^C)qV~Nk)~tX^&r`Z#3H#FL2ou53h!$?5!HFbb$r+7UQ21j#mVvnRLy>x z-8LLc>`(Isuj?eb6&Y?aH7&jC@;KCF*rb&)fWf_<~oSNyNM4v5ty)GOaEYC(=j z6jJw1%i=o^4&#G4nJhJGU#U>2>cZrW$m#+0A{IJ+3yNucWbZN`RUAvQJ(haY%K;i1 z#aoC+a_>a)z7Ce)mH8xjpt@OWi)%`s5a))YWr|BX5IJ1ulbiHQUdqV3M8c%Phfd)b zEJjb^>Y5&QfMZRVyBA1HaHHSygs+yLtJ@_-1}vQ*?hKDLchn8x3nH=rF?*h{e>A*)R2sh2o$c1U@dkU_ z|&^YX19 zFSzLH_Y-%Iuw;Li9OP8@JQ*0oM>Y0Zm7T#9u_R?pZy2is&q6v%+g(6R&nK(TCtJ_g z*H<^U&|CTP*`^>&&HmQ1qAS`4%j?I`Mwo>0fMzVRiB+w{K+e7s|B-l5D&Ek;R1(e6 zs`ZGfzW_Gpu-2ShCWP26U(R1E9Vfd<`#N+e8=5Ycy)`8vR=-N&;1BlZ37vbCba6A= zleGYgSNO!q_S=JF^!wye(_e-fgvk1}y5^QE&n)?-WJ>i+>DBZkl}w1|R?CZ?Mt!&_ zNZLd!;}9`Wygy8-GHW1789bODy*S$snpmkn{T#}igsBQ9=5y=dY!!V7pQwP_Zi&fHyhzw#YSJc@NJ{^y=`Tt3up zF`1RB>J=@hn)&4%hsZhyhq)PPh(_A0Mu6#NKc~Ps1hHb$uF!r=)^@IvGqA7{AxC|S zIK9>g&dkthI)AOaKdXGY=i(S4-CzGbxcrERaw|nc%PXg7iMWU2>J?2Thx{c!T)BkE zluC&y*Ls<@;Syhg9N8F9`n_--X^&9ii^RMatDbrpXD=%zr6-zo#7dNd4bcZmw@wN8 zMUif%3d3U2!`)w)gvcr=4j7e8JG+AA>Diqhx2!#e6Ci8MM#S@$Ln&X)$KSYP{4OOb zm~>J)VRNVUc5ybX5oIpLbcTr+603?uvkP+zDz%WO)R>&T^ejcey>9n(Ax(`rrZVw# z-;)tOM({)Sah3eZqDdaD99dhdM)563HiQNpbr>_UQjjjTR=xhGZ-l3TtXz_;Ey)y| z)B^fY2eLJgD>H1uKwx4701aA54S$3OBQqTkVM$GEE45^>MN{0x!C{=R6I-;_Ol-wl z3=GmYZsLdV63AY8!Ikhs7impEZx5E{+3nT$i)${>);Zuy+Q6>7g-*b5iR2&Hk&J8T zEUmc?4CRdJNun?E@OTL~p7T~D{80{j9Fe|>egWdeL=fb0c4Ni~5K!n3(i@cg16l1- zq=Xs*&2oOzJ4x}yq^t>b(dk1C#P}}^WbdALymGY&LdDTKX`+}871|(yYAFB!N&anJ9~$2v(d3Aby41-`y8Km>jQlR@eZrgoaUjOS ze)Ekk1p5#kq!4g&2c)=ady0t@S9}v?(onGZ{BUP98N$<5e{Y=!b?XqQ6apA4&b9gt zQ01!p*N-NU9I+}5F=M_6T^HHHqlC^Ts;LEpHT)4tml{L39{wN+SG)j)Ph&V!s*cPOP8tS5R0m!-g$ zvM|`?z1Q}yy=_#A+278O;_Y|do}qTNO#k)q=`Ip&k)9-_ZHsmgYV1V%hbfF=P;g5f z9KRl|6|Xw=4o$U4x&ekRmbt6L68rA(yT|fd%8q5ZN?&WMg|y-*9uGhth-$8} zMOpOwrfM*Mo8LDTX~$dS#H1$!D_AnF^gKFyNrg|UEF8Je;4sp0v1)`0L*ipRgr3(X zqj|hqCz=h7;$W z&MsaHbB@(M0pI(Yx6%wb{DIoF^?dn)U0dsv%JvBEc_rVHvKeX12k8kZ_X?gj2Jr6O zrfh?jnBjN=hDPxg*>oWNr;25@18is%Z$S+q&jORjrxs(}Y9v)@SGqz1^&qYrToQl} zzaIisg;&?QW%m)OA)l_08B>UZDtMxfxC#S<_y+#O$vUJ0)Nz~CNt9pI?D^_-8j6HP zKj74u0o@+MnK+T9ho;CM{v8u79W+o)112B*@v}~PyscGA`cxN~L3HhnMXrBO6idjX z38WA{>t?J#L76fCHf}CpM{3el&DAr_goC4@Xf;X}Ez5dkk5XrWw8B@>=PE*0M~r}< zN;tsjHp14AkO}rc!S-9^St3&M8!G;*=oqd(B(;b5Xr&CZ63yw0nx;sq;q<7L_2iJY z06wUeI&X-qbq;g)*1X;bss;K-x6y^oj4nJd){z_646dAN#dCWiIT9%{yBeb*r^N8L za>-WhttD}$w)y$Yq=Y|jVhYpN;_D5jeHtyGT<>JP-GDWbPr3X^O2=?!h!z@Y0<9Xw z5@IW!BVkreM6Z|YH^9t!2bu_p1ZBZy`-lOF$0#ZrCCI;8Q#(KepIOVmE1~=#waG~R z28C9ZV-1UpT+c!!TLVb|uvE})K~tF-Fd(TrC6lH6C2*+VNcjjF=2)`|H~G@m!@WTH zkXmI@@MXnB(~Ywiy|W&{X)kdyP_h)9eg?L3B&=lt_xH-DAMT#s{($-s_(ifs@q1QY z>CW_-DPplhDugr?BVW9{?UUhk=KjnV_z;-9r>Vq%Oxy-$IMX`@H=bmA(J` zcXZ^mZ*(8OUcbLbW4#rxRBOl#kbiUnCsu)9Jys!C46H!QhHE~$UFk?K4n4(G72}1L zZXiu3;!zmj`Ed~CB=CTc@dBPdP?~LEBngHVjQWX_FN~piG>@$AC5xM{uUw;=`rZB0 ze^PPtwZE?eZL7GsKNWKFA8z`SM(DXgk7TrV5y}TM>^`CwN5K=!=nxl7#gTAI4&|`z z!R0tR+28IC&{4FMC>h=-ptC8?h759n+<*mh1Gpgtb$|^NKz}^Zh58jsKiH61I(FS9%=9aFqSYpCnhf<{SDhl$C0CIe8m zFR7!L0Q72V6I?!MWYkvcN-de`n}-RmiXB0lYBOwj9A7m_Gnv9{A3;-mzB>BT_HlYr zPJ!2`vqzm@%45hUyiOW8L@0F!*@|-VXUi3dqB;jQ)MZlEtI{ zFUS;Zl)Cv*e9PZW#&4 z(ezvbu1+??o5BC?Di@oO>&ZfZ%bET#QD{v{;%1=y2^Vxiq+j(Cml(@y^bU8|@4kK{ z8q5whC+H%4CXkVqKi)y-@6qbxC;#y%)szSjix+XPOK9lf z)%DKTkIVl`5+1XYdKQfVT#g`Fv(~rfVn3P(x?HbHr6h3BA5*81tHPEv@`4}|d(J8k zdNUmF)x5Do(ds{7N#A|F`UFI-d;jUb6ej1_aE>Jwl^c>>yJof}pgE>B9U*QV0q3i40Uc;V*p=CK;ittuz8W<@e112^0de4Lt}E|54SXlZ~=%VdXY#zbb(YaQCV%oUIjVR>`OMNB_4 z!|*PT)@yWuT0fkH!jX*XcVWTyd^U~ zO$|OmAr$3igk{^6s_wqN>tJ{hZdpUJ!yNI5FgsSiUA%k$A;BsxbE#A!1#+QOplcDw za8`-!Fxbz6AXFvdkxXr3@yB9ULwPhl6*!H^n}I>RHMokBCwZ>MDP!ii29`&;?siBt zBCGZn*R(xGR}@O^Z2tvVeJEsbNsi3|2Fa82L4;ee{1*;Kn@ulP-S1x}hfd+bC zE2Wf}t?9Y#H1a}#n)oo7x;qSJA`;5T(s|&#dzP!KcmdB#yH<$tl-4?q0SiT{9U4W# z)x9F!6PGyNd%b-MZ?Q8>N)T#FE}rk&$x$kJE&0O;w_!^x`3EFO3(CV^aZbI>OkR&F9mijRS;~Yakk5qe|)SKWsLYuTyRUF7_ z#pRsf7h)1K01+)tu1D#O-Qr9&Zi{4SkgM;B*`8Xj{!-(>7N3t=Sb+_mE#S~8Itw?Y z*t+Gnmne=cy67rWV b*7;eo4cY1DL9O(nRJ)+Pl?PR8^b5)ng;%YW1<6Kge3G*3 z3`d`_nQKX%&2b_bN?`0NRkw6vVq6+Uc7np(VA3ih1SJ2g8LXUi@_|vjLl#?PI{F-G zZbW*}!XFfYQX`POAp77Ru#EFfd74ca;->yUdumEC!8Oj=9>aM%BK5AJE*CY#ilDT? z>bFhn^JVyFG>m3WcmBfnsg%{jdP1)C2LCLuc|C$k)VntXHMa5l2}SYEAK6$ znSaBEW#9ntb2!OGyLq_R`=?9vLDfF*!ko18?=;6>;4y&F^9;GiI1Q|m&f}-HYC3TK z61SoatruS8sM&-Xw}&yQvvaTylyvFxFI)x=u+mUc;Tx5q9Ij5WqzMD{p&A;+iDu61 zZcx8?X1c;MGSlrDfJEk0PKD5-A;oO`+(c{Qs^mygYu4reRZKgTfvg&<@qEHJ65GYS@|v7yy#(X+~~D*sMmrhF)v`Y zpDF-WlmZ=4VtuO^s&%m&mc&r+%77$`mM~)qp9;h_zg||33pvx z1**GFJ>L*dI`y{O)PPQPq{t8!4Uc?6rog8DYaAjn< z>4#|A07M>SDG7q`H3LR7SqA_b|L0hqm=<}g*YbV!l&6-_QREJY(xu;Tf4J z3dLy+lpNe4lm!B64k0D=SISEw33)sK6Gc<{PsqTapb^TZcH9d%nsZHX76*e{RtX)q`DlUY|9W)SekAAR~=9*eJ?yLor(3i;nn(teu zAq@=T>gRnC`b%3^Etm4coXTsKx;bf@sRHq3iwRocRjJwpois2}geMf{s~>pxJ~P@L zEUk>A$+}x-wswYO01HhzfUxqV*YyrSWiz2UUGbUdikGUFYoSi7ePKEt37x^ zFAc&rrdQM))u>R|b8ZFMQ#%3UCa;QJ8Q z5%Z=ZkUXN$0Tr^}E!v1VTL;H+7Jj!A!eS@G`r5z>FG>+=JjtP1(b_Ujt(W5*vK*^3 zUGLB0XzF0LBJR)5PM2xz1mmjJ0bb|KamroT;_(4oQ}YpR1By=Dn~p;CjK+!rB=QH zUDee>av6#X#Jj4ahC}(dq-ri8fB+2SL4^?SpRTd{426 ztkXmC6t|u2k8(f6nk41I!i6FB$;ib+qC4D4+tt*1wGZtk^>(vodkjjl$(D&S;x+9h zMR6aQNNY9q(jyQLs(&K6p5=|Y(bjEcfZdmqjA|G4(2@9-Pe0tnAStmti&dOAKjcuT?MrlAZ$ zF~NgEt%hkHzZO*^B@E@XqT|$IN-05bzMf+_TvDQBL+tMM5-!-gOZ}R)W==ujYwN}>h#JuFu|ZO12vmtI3|I3h?$ddqmyP7&*5T}>_#?aYb2>TPUAzMOl5_=Vk@7z|biw^2c-m?bL(1{#2zr22>+JO5)i5JN z1{4F2mY|@#1vU;dnxu*Xf15+9Ra`(V)cnYl&k7%3pZ&E)ab<`27g95wxDUqqZ@V*qF8X~M;a#4isF;til(TUeVs_cg+7 zO2CrpzO51bYGNvKXqqU$X@bPerETf#a1H;VwQ$17z(p2EREF2iW9mbq@3s2I{$1Rl z-eRO{>pfk-_=xybkjUG2tCSCYTPo$Qn--K=Xm)_}?bj#Vo+`dTh>8AF%ZHCB&gj90 zu>+7aZKl~G^<}=g)5}wbg`?w{h<{4A_49!!gjx_}x0x`*>^3uTkTyIq%1{#3UQqDb zQoqon8>!&UhnnGfk4|K_c1Cza6vJ{+Ni})p2R@z)WYlFl1Cc_*S#@5kIk~j)*gy!su-r*+JueP`dWYTpO ziEX9qWH}5trXn>I{udn5^f`P$4C?F{g@8{H(xiGFE$x2&r7}VD2g%^imIGp?jB@dVniE0IW8>SwX}B zWM+FyxaaQP^0eFEJ~+elxSQ%*5R;aqV;GZ?kwcmE1I3xNxE$_tHaoe!lo~tv)Eg6q z<7Q7DR0eQybgP(RU$T>It2FB*^wu(JvvBps0N#}NrEPDnA8B7Td!;#qU;af+WXjG8 zK0r!&sD<&KFz=(tzYwxtjo|~Ep{DWlX)W{pZ3N>k-yVre3sIn_bCh|M_-{CWCh%?r zn56Gj>s{u@fhKo}_s@i}cYkzq zy}ZBq!W&J`OXtUfQwFiI>6tmBowg(#g`v`li0jb1%onN1B({Sg9@mRxb2}{DM|vL!(XB#S zh^`HNXh<}@O)k-Tp!`UW{OZ0IjRAaNAO=9g z;U|Yj80=u>+3I)e`=|OZ&<-@P&M>Pmnwo9_G|ao>(*=?;C6FSSO?HlAT^b=0XhI-y zMi9Zl4SXVzRiZ^7QCfsMG@-1B>WO(^iS0OOrjXb5O2~j$_R6Qw`&qyRZ2$46$K`EA ziPR%iLVZNRpt(VPMe^LT_<@sY_wxSoz4JqfQ~h-N#o_kKk1+O6ZZE$7e1TufBl!CI zj0Z9St5Mv*%o5~cR)al|n`M?oZX?+PCb#-LEry^ugty<>>t<5XdiiqY-N)|zwD$6w+=)foSzPf=7Jyn?);A!=rtX z>9cfC@os0y7m$1kU@YHXe|_Kmc)_w38hrauG^&g1lchC53_|Qzgu>AQ9|fLR+z4J6 zC2Lp8^%O~a8!rV~2jp*AafC)teFa#~_ON5#&83zb^7< zAIC>P#Hdl4Eb;G|Fl}(I$iN2%k-*55^Bxu-v7dn)_Ry74&S88+a|j3u!w+dQ?;IQDXq# z(C=NswYPc-eA_lfc?wx+e~S!u@Df!+V@tj^e%xE9r~5&iUXmY!iVQScy~g3$Lo3^u zWNCa_a|pj&PMEj$(+uQ}-~uoi3*?f{9xv2^!_`GTLWnivdrGy_emaVbYAX%W_8=J7 zdU*5sDt(PBMwH%yGu?;A-3B2?)pb_bVq*pJ>eRMIypRX;S19&fLN6^f zeX{lUB(mo;0C09!GgVmHKo;aA)$CkdeZFhZa@qs~ir`0a(a|l!ML|C7lz|gHNG+v8 zSF5XsOB++7z@y$lK?>O3lr*70wYH`+mh?jKzhCNJXtXJTEI?SYvQ$XceM+?>C(NAh zAY$*d-GNalFxTgxBP^nyMYb6>Hs))2rc254;z*hqSIeTaBP389QZAUISfxD|OQKiX zH(clWDOLHm4^UAM5tDQIC7$qsLFERZ(d*3-~GW!T7j6MsyY=MlDqf%5WH-GV*&dDGqAvPnv|Ip;u?}*c$Fz% znwp!7)vkWCtfY?!Jfd38Z)OhP(Af%Y)kuvI(^E>p+Dgd(XsiF`5(qb*UYY-`C{ z^GX=$_BoZ~9V=c<-5~3ubNqd4v50vp*NBMmeI`YTt=k6ML3>`_p#S7$I3&QOMK4|cQ#9&3iG!sqoEzK2t=T1tcy{;oMmk^SXEF?$c0 zX2%)>{HJDPK8W#wR;OiO%OFm+ke_fwcvc69Z#?Z4)Cc%njME(dEVNUy)O%7aHC(EP z8GYU&DEJDV3M=M$`Jr9S)72RLfH7XDe4>8NOvf63np5D&p1s7&HcoA04Ghv%%U&?N z>j6q~xFl|2)hK+|XIj%x^wD=U1 zu5&PUO6bLAG0*&oOZ*=ZK}oi%+_fY*c@+=|aEhb=nCKVT+#6HqP9khd8e;q|2{R7h z7hPR?SWDI)_0!Gu_YzU8Ab3j7Zw3w+%|pAer$<()_fEyGqv_z!fiQKew`% z8>VMk(D(-nZww9Nn`T8`9+4d(z{1lWG!W~wQ&^g6l*&1&iq-KSz6;56CIAk$5Wx~( zEh_uTzjYlvKGG_|I685dqIQk!V^AvMRC24OhOC8S6!kj4egVg))h9^;xLImoQG)U( zslm{&g)~WpA^jN@3yGF0;7GXQSdI$?B)abSaP;HNr;B?jh0Q<~nEdly#5rpxF@y<9 z#85YTJ3E*!N2!*BHkDFN6a~($_82^+qp8PlI*uGd#q|0t}s$ev&#|O!_1V! zTA|y*P@6hwT>Rz`F1nqyeH0Ds^q-&a?$=j8zp`@{&QU>HpHu1eFPk)dXr)GG?DUlz z%S!N5k5*z1e1sZp5*SDRhm-u~p$uNAMLh=}veE6BWv4-L11x1}iV;WhI~ zT~l|Of6XMcaVfoKgbJ+sM?7lEtDRrH78Q*F+@3UNQ7eRtK969VGl8lwNou__>Q zv3$nyUHF~~cts`NrXaD_TUqgO>QMPGvg$(T<>Y!I$h4j>m-+*;4~o#2ji^|ZnPXXJ zEDB@M+3m$ejyJi(g$|`#8 z`YXnQ3{!g|^3v7_&VU8JPzegoC};=BHWnvflDKeOk&<%>*WavqbzLGF-+%fF@XwKF z`7n}a=1<;oXuf|@85E#vrJ*l(;f@9+)gS?>MCI`V5iiv!E=2MLN`-8>&Q#{Oh|MAV zGW7&gqn7F(hJ>}%F`?AnO7Iih0YiutHq9HGgvE6?hj0>!a|#WB?W(j>#Z&w&^Eas1 zpMS_hSJ9OjzBTbx6vx`?o;ud-_>0Q&OP=%;s=7RTsnI5!F*II*_Ieldf>k~?0d@-a z@syD`SDlpB9>e>~3-6u7At?H;1W&$_Y=8_8nIB|m6mOyV%%!p1_&ZczU41^;dcMBC zy19jJLYL1r_bPoAsETJVQnDQhmM{J|L9|pt-TxM3K8)dX9P?Q7-b!_BFOSdB|GGmn zc>Q^KJzo914O~b}V)<%HP!ZCOmCicWtrg-$w*M(pKoOYgwjWAEUk;WJVF-T@M-#X} z_wMHvggbVck$G`_eIxY@SHe|u%Vy6&V#;{O`kWMg=h0LVF4@XdR#(Yzu9UN2H~n(8 zXa;J3bqlI|&R3pFcpmtyF;V8*S_JeIH%ItS*~uj`llGW;t7CMHGs3Mv!|r_NJbNGI z#I<+3dcA_Frh1!Gt4|(_$_(K9vbCDF5aHA0vHsiO z)08smp&3hAD)jJ1wV3!WmGHAMd>QOQFcbwC*Y*w7dOR?YH=-^=yMO62HtH_J0e!Z5 z2wWb*1HCnG_fvDR?ShMDX`S!UGj0iy^zy^S+16TMA=!~I+L}EgLoQA)7N4uNYaN5pLabGad4_&!;&G;=P}tmFI0*}e9(m- z#&EOSqPzwVhDPxgbortki<7+LO_jVPwag$5@p!*T6?UIyGe$JC4GjMB6|oRhuB&*{ z6DvJmt$MBYF(qNLD%Ie%qIU?Zo=#RmFUkng{5MN-PRGASSao4?%6Qrlff~DBMz{qB zUBY{1cAz?WotqEld9q%}4=cdAC5f9u_`r&EqOKmL%J%u$G1~IN_fJ1n4n^SRl=u!5 zgYFh5Xo@1pIhAfn&_I-}X0~LN*jSnf0HtJS9%Q~(P>d+=dWR-~9egPev$9S(ZY2<< zqPO(wgkb``8s|5=n`ROb*y;ulh%rnmbZ6()Sbe+tzN{HSqgxEJN@8h(SKJ){GT4yr zbno2rl(=!HMIC?w$`h%vXJ4-Fx^WCXV>*^d`f(>Lf&b!ne8OMhGA@B>$c(a>P?slm zaKJ<{{LuX#kkn=qPPpl5Cxrijc>oa)?M{_?y$3z9Ftxw>*9&PECE2IAR1ZYenn+&# z>%61bH%1g%dJz$5ePn)U6mL-yq!3!R6#JCVD$|5ES%h5NN7qOX_cE@y{Q2(e>iwTP zcs~|9D36Bb7y2@yDvNR2xXzh8pc|o1^ZWOg($I(u<{?FB>I|?hyalJII&oGLl(@gW6Uiz)Se}S{muNa$iuxLtp>EQ4bK8*T z6UcM3(0F_#9NK3{u6~D)PjxFnz;wv(o{E=ae{~;_V75__>zZ(GV8&&m%XybGu)D*U zNL@iOZ?PJZOxaz1{_+*^j$iI?Bl48>A6Fp0r397B8heoWEEL2LFw-5)Ukg1g&vw!7 z1?^=0-HPk0#`%pU9YH@?YQ`;t$nY_8Z+CCLzLTa0q$$3zCg*`ebmh14>SRqsK9>vt zh328sBeD7h9q{%}o{iVh_&z8D>;gm^3(i$EJ?|zfalq5j(UX@tdJ@9I#BU?q zkI|41h8+xyB29@~N5STYdmZ-2@au^qn6d~$34jYi$W^+5Dq{Q8`inV?$qqpOI-l3% z1nMoU8RLUPd9$p=5jW`e7cce`a}|x#m{RJ3=?3xJS?Qa|*_)Oog%%(@en5Ij9l?a78RUoA|v4km04;ZlpC&YIY~z4Ql)oHQyy zN@{vcYbZ(axzu+NwV%pTlHw9a)Jo|F7})#gSa6wz#e!pccGYRUJud_g;IZUb$nU4B2F zSpC<%rsgI_V$<%E?JaqRaV4F37>98wi_|_mOySk22W99j4wc#26c~O}5~hy$pGesx z%Hp*&RkXZMM*jSr(0uleBmpd|iFi=z%%uP*Z4g$!KWG=v-bBy%fOwTjb% z`BpJYBZciFgtpTCRen21JQ+C{nYzQsH1jXIb`n9PE@y14C#Ik~dI9>Bh-#SA!A5r| z=&us7Jzf1r&2_a=^xdH?T4+loPg$ON1b4^pd3J5uF=Eh3z^Jl!5GCE@0j+QAC2?AN z!;+Dak%T3*3-M>#8RF|ZDa(}DlZe=%rfsoDy`MqW>A>gWqg;%NhJ};*G?KWet#T~1 z1H9K&vNGVaWcYm*V2OHFH0q5Bcm)($Rmi?qMGjPMtG;>fCrT+LP? z;#yrt%4h%seL`}Q_&eiH42|MjiAwnbbsA6Y-`&fP7hgZ!zqq(U67T9aEomiZJR?)T zWO{;H0nSys0rVcl#LrKpvC6W`Z7{}AIqy(d{qtq_?(4_Ox2B*~){hh6mIhULQW}#9 zH@Z1P+$>(A4Y$NtxVS5LmAIO{%Y3xd4-*ImA;0#+zGW(X|2n@W+`}{*G7?w6>$-#j zm+ki`R^Ak2zXBDe)1#7$2RW&iYH%-CcVTcVYA(UH`#I^P1YMYRnf&(Ws0NLa(3r#t zr5UP|Rcla(x%4h8rG@A9dRL?`iH-wPz^cm1E2J%zD~qF^3~wu|y)^LbeV6luKR^nW zKRQE7ggIvgPf@$%5qlMN_M ziPs8N@{)?9{zsA%O2& zp->DOa&u#+=Ee2>>S9ZdCNLi*AnN8g_lT`WS@A&lSrO|cKh*y8+3DL8 z(3+ey9zdkcp6$-MeN>T!XH3f2q-mNVmibvS5Y%ZX-Ok&QS52hnp zE>}V;hTM|PO(!U`w|F75TlcDnkOy+}EYe5ci3!G>B^V<_EG%ALP_q+!Wx1VN&5k$1 z8fH@h#XaIn`}zA(e0z5YQbC4_Q1$EBPGJ_K#1R>h!hh^xR=%$1wct~zDuNA4uA2Se zYw=9FDaexa%0lq0oeP^0H3+3C?YH=rL!c)ei2%Z~DqUG#_~_MQCdVE>c;-j{{$#{I=(nvXvKTRX82xVMbHhvR!&mKQQQE=q zRGe~R>Y%KYrHYX1tGcxP#Q-d3fqka(m4^ynaV;p*5!1t5A9f9%DeLA;KRr7WJ3EDI z6J(RLKzcy(FGeac_+nrz-^x_69QuJb=t?C?V?F0Ez0VK3N^wB!$je9F-g4*QrG&-8 zuQZ<>z>wb6`X{+|#r|vDv%H)pFG%mwxtZeiQ)()F!UQ2L=vq2$-2hTH1!x z*4g33w`7^E^GvFvNF(4&jfIBW^?`w$jXT`3_eyaUO)6cny$fjm5MC4 zT#ifR7nf5(l6GI}$TQkwIPbJV2ee;NwyIts>Z#jHWGbGmTz>n4sup)wH`h-_{f~-k z>%mwcG7TRw#CbSHqi6gE$yNA-IwP8Vf|pzg4LL@NAH{_!2vZrs>39J;X~hdjNHnV@ zAY0e}Zuj2|rhy;ef9In@IUC04`D!-9x_x|;;c=09L=(;rP)3kOxHx*T*n8WpKs$Oe`WMlWLQQap)#HO#&|M-c z44#ZRN~Uf;QU}~!clQH|?+jE<(Q6te{qgHH5RjYes(uO#9yq#blSmFEZLjI7RsVIa zIqxTr$Y_dsj0@c!(KhXH97LoRPK}G3N%96(C4~^msMA-%<_`-e@Nf$zivaF&V ziWQ@J+WIu8R!QlDT^66|r|$;wk=bOU$fFg|D7#ncAMC86)ui2inq9Wlb>2CUblQxH zr4}O&s*Jd+BYCMLwYvCFG!f5q)Wy7Fnge1ll4a+r%IH+%ag8#+?P+J4J_SgdHz6QC zO~kKV(qqbH@=T}^+sn7RhX^6xej~D!8S-0?8b-{kT@(SJFDaO~V5j=})$#~ENP?0X zU(8i4Kpwa#UyB8&YEf!ZkqAp|2unqx}~i468pQ%*aB%+~6g4}+{} z4A-g672|npkKxzllwloKUO(NDHXpB_jQ$NB`TkF-3jq~Vw5OFFw1l?+^s=7bqpbUBht( z^sGoL&|qxnXFI8KB&mU0&Rd2MlLh8|W|3uP7X2A+vTlKQ2j*U1`T>D6;# zJEBMgj5>V*3WmJ!{DQe|z&WbL7tSX_Kv$f z=6_{%m0qSK0ZNT?I#!#r%Gn@Vo#c&Rw_!=iOvs_Ih8 zyXeC+|1ZHBUdkrJTOSRGsd@p~ad=$vWiFR-D`uSQGz$Mha0bRGR;JsV@+QVS79S+L|oDki(uAQ~pCA-)O9r7bU|x*R$ZbmIZDN356|Ze8zqa{%nxk8dL-3 zPTLR>8-=_qw$D#7Vq@b3dd19+8XJ2)G6>Cko+XU4&=|-xr>+>jLj#VJkzF|0n;pCa zmd6yMigADm0P2HdW&02{Q?oO@whH9Jc%> zy>Y-*corhz^a3?%|Gv~_n!Q9go;+oY%5H8zYO*IwUwLk&4yBX# z_6w9Ju%EMqn68u6RkCV1b1bu6QnAMt?3g*9w3FDAR_xGT@z3$ckG4P^zKym=$XFG> z=E*+Fcpog~2%n)1$r4=W#p?WMl>hkS>Yu)wAJHYZy*NcU_$!F&mvfX;L3D85yu5vg zCg~63%LVfx3*B{%7i|e$TryTu{M*7>a)bj!TI5BVjPcV|H;K6zuNSa8UYt*S&V`GH zL)#6QQ?j{`#1d+FezrL1PM`-N_Uf?PhYDC6k1_+zS%~S|PM15~Mk+a0UH)C$dSj>7 z?aYpCjq>mnO-*z1NEw=@uelNn)HA{|W`xKRFZ?oQ3ykrWros+IGB&slSYjmF_89K1 zxLg%E#d*YXX@GgcX65I}v+4NY;H8(k`j#3Jw9ns36xHeAn*Oa8fi-8W$+NjM!TnGyy}CNZoCaFEiZr55!?VLb%*wHec+nj?!=75x%CI z91*GMwt!w<2?60;3K$2E|3D4ntx)?(ZmZvor=HrS`mIpYK!_$2t{G>I5hPo8B(?XB zY4oblZ-2V_{`%&lmMOT%e^gdLPw!5`MW#F3PinW=he+Z(-vwKkmtP89Vv* zhCPqk*L3xJf;-vz)xxLU!^+wLr@B>w?S?f!jk`VAt6X(2WTTZcsUP(q;TUpFA#} zjEDeGjci!wm?JbmQjbOi)^bQdt*Db=2KkUPo5++64&mwH;UTAM)rI2_&ON9ud6|<< zTV3V$#rDiLp(8Z8 zrWb@4IeKQ>qT#$iY`-AqoNPeXtddYQt#go~4{jBuzy3^gf05@Ft7SjRDk4^Yz-Nw| z3wM~QD((IXaHa1~A{;B9j^q&KqX>XBj{9m;QWVb%6!F4u7!097FaSXycb#=2Sg^ z>yZs4nT=ZY_}oI4g9(sg+T&z12axXX2n4cVl}Ws0vI~5UA?~_rC%KG z9X);FOiU8E?oNU$Nt;0S)Yl{Bf_aR+cu9p4+a*-M$Lu0kPeR#9zPk|QLmQLNK9?w* zeF{cyuNg{~jhe~DFiQ#4v))_l+Iy?kr1p@OEy_UDq~y_hykOXYECxu&lWbPASL-3LmZx6sW#RyQq|8Rb`e~k0I zMBn6%jd3>>SXSk2QM6-&9SWR;yG~;$U=l{$0LdbVQml8mkm>Frd_uFc(*s83rSBzd z*jR+nb2U)!pqWu2Xg5CDFuX8=U!@$tKPZ^eO@KTIZ`D#NNYXFU&xIpp zn$Yaao=mPrvPk33w8!YJAv3aiCyh^*Y<1=_9y>ksb@RNs<5U~Yx+TAuTpWjSBC5wv zI`l@g$!D?UE^%nu8@UUPEg~%o4-$P$m2TA*PmrT05nuj=S^>CGrd2USiEHAFyWVc< zrpQHm#%sTr_>fyqd7&eY-JZoDX@z)KNMVM_c=ntS8B`pPlXe8H{p$1vg2V~n#kVea z*BCBLm|PmKZ2U}Xp@<8PEG-fdzuz9nT~Vq?pb`{VK3?O+i?bn@(nDy<$xBffQ12N) zeC*uWtGD+X!sSwMuPBvr^q>I335s|KzrR5(fC0Ti`1`5Tlj{+k4QY5_To{AUTTyV< z9MvhZkk8TVbawo*W=o=cQP!9Sk;r`Pl(TT1tJ#%-(w0aGs zqy?5BlIOr6E~1_cMC6)|#WI{@mf@rkTWwz|2;$ecs2TFwW*x*=hO#+F9~dB0-RSY= zchm32qsi)cYjtBX8Lix2N(B4mlhGfyPY?gc<>z;oA5eSy+2*r%w-?v%e}Jz0*4=058jb%sx2HJcbY`+x%B>q(dj-5p^ znK&l?<;aRut^zF;Hi&c)8KW=9tYRkLKnh|VpPYc3Rmx-~aLpue-|pB%yZTl>f-*Tf zTm2})Buwq7PfYBH)TrwS-hF-7{cx8VhrTm-N0GjXZ%{0+nFJ_RhzbU@}M ztZRg3MKKQ{_srgw&UE6ejq7U37W*h3jGpQeAJR)vtr|v)v6fd`d|+kfPI$^nl+@A2 zqW16}2MNw+#44#y+c) z<#le@$_;zGF`~?vYi&q4UV%h?b_}C+8?d5K0V!Ymold$lXJuHZ!5lRx@iG3-o65rq zvmk%vZK(D84M_SpJf!jLaDCOi(+f?B*Qy5!1F}Tzpn@=Hq6wkt%&5zdj6QvNe}hs= zSGWJ!{cv&p;S;hLxG$DK?g}2uM543E*fzPgNLC@(mZLl$bWZnl@#4iCdAK=J(1a9< zMqa-|^og_^Qd^@y{N=athN1oXRA~`z?^!a^#Bnrl(RiGG&-+jzOY=9XTGV&)V9x{g z9)mqijeHH~E?1tet0o_UyhWh8@m^KMlR5wygCiM9Mr6VENdEn9d!ThyCR`ArIHpv0 zts7h!5ko{G%j;}w^!SG#9zT*;#{et4{8uyAXl<5|pk0X zguOf`2QQj&0vR6J)kgp|eE-CH6A7q%0-sjU&b5y=wz zsj0!93Cf0QT|QLb-d(BgJ*w!T>&_Nfxy<5>Fh;3L@j+TFwCI z9*W8CV!3yGpq}dqTG5HDmRu6r$3}unMCKgbEN}MAeP7`#DIz&im^U@B%02~*`nzl+ z7gdgEVcHd60Rg&r1?}%zt$bqy*Mzj2pO8%P-j^L-p2qO&QLwG|<8nxc5Q!lnn5H8# z+2Ug!9LlG0-PQe#J_*RNK`0`ee>13kpU|S+v@d0M7B6+S55~VQd-qDR`ZS=8kychW zs(ZcIg^$=esDhaQkHRml1y=m-6oZ14>T*c4VWqv_9%G z%(o9^$kWm4lmGC8Z_q#IfzjhQ8WGnIjh?vCD3SFY$o2C29zUS*cGu`qk1vnQR^tC3pO2)nB5raAC!BPiAa{E*hXxl6v^P)n7z>%}ti}#m1U+)lzf%66t zfXv{MrV1{ca#SM|%S=k-n@*@vAyP(Z44Ltxl zmq*kdQ@C02ZzAMMmcc3&r#L(a4CsF)!N8c9sU<_Bc>nxpS*Yw(se$65QM^Uy1De~0 z+8hMPMU#>4{&V;9<;5+2dvSC=V*~qCUGWeZgC}o6d@fq9f^$6KWuT?jSq27ijzxD| z!c;go5DgMMhLd#mf~t>Uh-ZTgB z`L$Oe!;Z~bj=&}EzFfY)`gnyFsGn}$Uwpbm(B#J(f6R5wQ-OwRrVq&I_1J8Pi{;J| zJn6n3G%yy#V3{97qOn}D6!}w>UaM8GgZKgQP9=3gLc6k?T;kGB^N6yC20_(i%UR`X z9Bw+Mc>0@4ZuwB594h)dEj2&$!Kkd^DWerSBpox=RZiW`s#O-~kcB?1(;x3ZqqRgf zwXyTwb-rSA*A%7cRd7i}ZZ=`9r1dWI0oA9ylF9w+{90uN?z}xgtuy1f#5^D!sixrw z=S;xzfkXrov6L-gbff)D>`)2C+*{3&oQ5o#De-6{DaE^_og{xwCx^(~MgwIi_1}=W zHiH3{fiKFcve&=R#oGiUmYok(S`hF7lTHPbj!?ysx2J}>u{L`A>C@v<-b;u^JUF;P ziV>#qzeGCp{7?-4@|&%J@jTUu%hmE5ClhVL8q-ZzcP6zPGD^vPaQtx2o{Ru@ZeC+ zt{v|SYvJ-fl&DF#UXIK0qC^QM^+B!YT7!|okZ^ZJXTtMyx;_F4fT;mn zN>HECyzClA9Qu8;?hX0*;qh|8lgM9ev5b8f{{Y;mZp%29t3{RTrw&V8 zP;+zL)f(P{C7WXB$pXr8fqNkVBE}tXe7jEagjhMjFGo_?o+tgDsc^#KXlV>&0rao) zwH&bJBCsPVwa4)5)J<}6-BM;3Cj05v>+7rQ?~RMmee>m~&y^1lry_U&%K=AxQ?Dsa zJm~+;P>_GT6AdHVd;<=d>v$uEt+;6IU!{T0c>N!ZU_o9nYpLW|V-v=eX7X@6oSogw z%}}@`#a8kRWUI9oAwXpWo}qLC$Y4vsFI3Ss_<8kPitDeglH!t{A%apg_&S8kTg$)L zBS>yygLre8w(x{^hdZZd$FC8sjS3|5s?3&$TRy+Ly}$YCy>c3{%Fa<(Yfn-8t)Y@4 zfL|R)=j%8fdMjLQG7~N&^slgxa-ZlhG!GP{6e>{xc6bG&qdS8$0y>0j6%yvf($u(V zpl56_S38SZBe+s(6r~;x=_f8tt?Ae*ADj){Vhej0L=XYK)JQ@fJl{)l2rK46(jk-`D{|q~m1on{O-sqZ&eJuOQvywy zOJrdec`K*G`uZ==H&8%>i_OAm6GoJxrX&70M1s*!^RJ%PxK8P8kKwc!t=8?$QE2@X ze(V|IUoD^{6@h>TY`Qltz}@=w)29zNKcS@$SpbZDWEz{1KUO0jpTQ=x&yW`)hQA@h z+OQN&luXB2$<)y&K^wY#!sw>I4bB29Sx0A(53Am62mG~A` zul2EDMxw%t^!?*57-wl+=lzW|CCdqbYF0-J#kx%=|A=9u+2V?H2> zBCdcvK#WKWHFS?8Ho4-hXHm zpJ3rnC|1rGW8r@eo5P0Q*d(z{DU!A7a&Sngat zd}Ja8h|1CF9~4@_Ek>v(oMW+Z7xRbFSDx?~C=9_tBVR)|+@vE&@^l9kVa=b)rY^09 zc?}^yIFxTGL{j2czy{QzEbPU%%X}f+RBDZ|xaNmZS%&hVr*lBU*s@4jE&ZYnhiH${ z{YZpa1tg&nLoV1jTouyjFTy0l!}&ywk;@#4A*PXzTO;^YXv_1N^5xv&?fF_PsGi`D zt>h@Jad#}aMp;W|D?|#74o?Tvl{TxdSXT8WwZO z?_cMdi%vZ|+LIV5`%&m3sYpe)<2BSf0M?`+W3ML_o9&)RAQBvI`~PJWR^184wd;`M zUp{&9*J^$+Fo^Gv;5^Xu`m)0DzO19rV&>kJa71Z>EF7CWf*4$lKs+>(^I?$vf>lf9 zA1EnlD}o!dKaAhDW2N4-C+iy^xo_wwAT%lob7X7BfT^X5 zvQKz$+KH&~YWk}TEZB=jQ;wxmVB|Z1 zm#e$F(72=qv&JX?4fV!rx^1+{Xb$0v6FtR0Qp?2GG5Ld22Xxl&%&mpRDr zV8o7i|JJt?WD_m$pX{%+*S51DMp}}7b~wW&5@ky3L)N_X+3nCEAyQFmA*RW=5>~}( zf-|uA*}iBp%kpcXx2(0xBQj5GzmZ3 ztjr+=VeXAm@p8P7YxGcS zZ$LYoYZLXvum0<@yHDMJlZ$8{p-cs_<2}@#^Ys(`)(elo*86)qUl?PA`A?LY%^@T! zL`b6UIubYE!^nL^Z3|k!a(*&91+bKV8`jOrnDQI%EGwURA2_5I?)DQOAhtxa>aAsT ztVAR!y4(3*f4sciSo=0gjpz{v46}PkG`ZH;p7^qSD%T$gns@kW?-UMt)P6qPMb^Rh zSJEf!E*(=1Jr>$YEJ65z)Bsfi9<`IM;Gub0NyNges zAayV9ff>B}dVkqRE1962o0zYy_E^GMmghTCmO@Iv;ozd^+jsOfkLS;x zuLE*{f-LRTebEY8DnRYfL4&3#4@yA`GGuY&Q`HE6@H7Dy<@UmOtXPy(kadM${h!f? zlWIIh9)U-c^O}a}NfysX(L-IEOH^hdC5xKECTr{(o$hOqWbq>Jh)Df6Z@TTBMJlH= z8n2GgjV>435(5a51-krd(p$8f2Q4@C`=CV@d>zPs=8ybPdn(o2IE0TD6F(;sNYC6? ze$L=Il5jmRh&Nzl!4{mK){$-1=TW8Psnw^=e5rrqbxE0e`1<%4u1|0sk9ho;Kj_Tx zzy0zDe}QEra}+m--sdm)v;F_S^1}EjzVa9Bvdf~9mvDb$Ncek#Z{}}kdQ%ypxl7gG z@{M#01Sr6C8_B%>vijG7-(&@B6&@bgzdHJ>+}>ajVnGOSdb2o7i}IIh6){|WDp{zd zx*1VtDdc{tq07jo&v=~hCi8;!)X}pUEI?>?VGK!*pk#im5bZh)4=5lwb6@Z}Qq9_pKQEi2TV>^)uvfJ278th6YG#&%;W? zg&)!<#cDgz?j5R%Wotnu0fV6zERD6Z;&Ttx59TW6Jyf_Nu}mf$*{%@9Q~ zP_qlA`!I{8$_hV=_=NTtJ|nG_yd;EVZ5H(|^GjN*2jwGtl^%)h*q8DR5G6AROHRS` zi27Q2bcsH!B3Qi2m`L!sTA=MzD>sO@XZZ%fY;Y)8xY>RvCX)hP21fA?W`g9}t4n5X z1Wcznr1f?PGxNTmTss10(*X{c{y(HSyiS1&I16l~(NJt5$}$6z z=+G^9UkWQm3Yu{P79L;_h-#1WH)O!!NDEVLDRnoy-P^2-f-FjeFf+-jIR{IB>we=4 zls8M^OZsY1sOYJ1s%~m}xDT$6nFUIIU>F~hOvlsT?>}54Xd7Cpq8^%#D2NBosppqb z&^e5v=Ed$3YR8cf5Rw2pqw9|5uMvhJ+A{Va7fCK#iSAO~MpVqiqM=PYOP>`=D7%fo zEIv-#unNTjtcSl2%O+mC)60)2c<=+AcII{=zcV1u;M;AHkhOa4h`SjkqjXPXPEE2v z+HjJ@)oT??N1zC5`6xc52IMPfn@e~New3cvEAr#t1iFWfp*<|d=wI=F{TBM<=>Hz+ z*f9oT*zbOa&@E8zc=SKZg&$Mn?y}qnGxN(`8|^QbXs;Yb7)24d;6Fz<`#;|yoRH3h zmInVkYVfT>aDQX^f;G|eW#fvWfZV3P3xQO7qTyYUlm?LyGo*;`?CtLX|9_!A5omI` zyr5`V1BO_M%#lpKVp`9^+7G@#PU3W{syv33HOUhR_HoeuPz@Sb> z8q5HUjLEn_T{UlL6eo)~|D~(W1D1jC!<0y-Zu-;9_m^i$wvaL-}gW zH3A1kAueM8T5ZbWvMi-m;y-1LrihM`BHmbKYsuOn@CvCZE23$2D1m?q80>t%)!oA} z_Z6L1By0SMzb+?iA{^~3=Q(Ulg=hNulaw@-o!-6s^k+c>E!ZiqmQ+_+96|8}heDAH zTP~*{dXkHBa$pd5$oV-Wn-W!{A`v)V-D^nFDVOrKA1M^P?`cT(^*V4=k)%5)uXS~I zf%G&`28ot?Vvf>jxAJBD^vmSwmno_lk4OLVFQYFL{yX(Z5oM!@@Q1LXIEagq6iHc9 z0b}aumHu^pP2`F1zEQn^ZbLM1;$r8sRF0d`E74A>*mP@^Do7J+TE{8n7%aFJA_?>^ zbHa`=U^Jw97V(?yG5oq5vnuUZFwXe>fkC{1ywKjZR5rl@6OD`1uI5Ad95@0RFq5H* z)C#RgB{^IS-fFcI;gZ4a*M*I;vT9)<=2fE>LK^lN=fgDYNAZog&c#fCp4)@*I*3<; z_~J?>si(8DKWJMx{Nn00HD;|Va zJ~FgjaJZWZ4Njqik<+ABQgr20+(W@iyly+>j!h5&e9*)ZQ0bQDdX; zyCbB#u_h(PT97VrUHJ#t?sRLqyK5FXdyAslTFhDHdxEn_zz`@<%39hknF7!l(4Yr~ z-Zgr^A@n4*r9gI&H|4ycnpdJe94#Ba+Up{wPv^4^8J8`+&1+&DmPGom{>AlPpiO85&{N5Uk!6*s zZI;Op{kr0Gk}!$;kWv$Csr8>I&y{Pj5v-yc_Caz2*RKi%op&D-XvZm*O4vw@!hign zl9M0jsc+9vcnNh{?TZ{CT0jN8m9HCS2fKgq20pJ7L#P-moDOWVrq{d|#4krm2Gku* z+6!1d0??1ofx#eon^u3FE&NZ8O!##J6Jk`J@;olQtdATk#C)mKfl^nku&}&8Kj8IdPUT(1~c=ReBiU=yWD9=wA z?+KK0adfaalD^AhpXeZufILRPoZr`sNY73d52quS0oWVF^OdNQSR7q6BI4two;d!& z1$jtsm30CXvGV%qjvx`QpN#(ZZ`DFlyU03f?x54XY8s+9m44!d)sUSO;RQ9;3ol+( zAf>I~=4<+@b*+Cshm6v1P=aDNYRh(D5bqsnq@fY#aeY=17Q0e^vNvxnmq)wl-+a)Q~E5cG6O@tvUoNEtxXH2+pR%EcVPK+XLogsV|r1ItsIRkODZcD5G%iIc$L0!S(qnmPtQDQ)$+V$qKq7+ zF@SG_&x_uh=sC)}v<6BAF}97IQW+$~cUOKJPsg9d1;is0B3ZKqpd8r~VzOCy<b$|x05W>!qUiR5y9U>3afzG-rOCKq;!&c zB}FnE9L9wzL@M(ThfJk9!WKiHGpj9!e%SAyjNS``h>qy`D(3Y` zr7(78#GiD-S&+|*D0wA_sA*3`NkGtZsWgIadp>np=f4E_#fa57t8g50U^hy4WqR;b znxebib;gu{g`zC<0ZyI!Et9KTXkD9Bnyj$fwgJ)RpL~G!p0LE|4#_Xe5ziY7B=x~_>{)uU(u%& z79ev#h?M4*CqC$|vu>WoFHTj>&?T{}%T72N&@-uhJNK)tOJ_*O05go# z>bEEJRrG1jGdBQV3dI*PNqrmRy-ZYUHh7esq=iT1z$n48nBnlF4yyaLuwrU_io~Rz z0AQw;TyCniKfOGRs$drCS1y1~jtu`wKg&a0@06OWlvpCUbLn6c-_@^MKEnp+P%7#S z1ngIQz>t?($uz4y^3lSvQ?EiYP|ct{+(tCK=BKSs2jpViBEru$*-TrGJS{_3bl)n4 z>wGCik1L`f8jld(e$^^Km z)piXelU1++0SHSi(&*pcImbQL8y8hQ>Yi>Z0?3HGON587$K6>2TvJGRxP*FeMFu8a zC@FR*Z+3qF`G*uq45(9N%yH9L^`JOpk*}^nX{;(ahV8(R?^*P`Q7J3HzIqO~n&Aq_ z(lR|-HSuVbhpKKSPoguOPqbZuJshZXGvfKlSE}cM)ioofiEy0+lP8w`MyM<^aM-~S-K_sNbghSlEeroDZqfm zFc^Zr)sHYSK-D`(EO+UM0_w8xHANkaSBe(1k%oDFd|M(#5`mUpCtun?KAA*zC9%2l zC0h_ZB|W}2IC{fc@%-*#V_(-_9hjsG#MjT?U2mtQ)?MAdvX;TAw29eyhgv8#3I3|` zt&-*pAq~Zn|2RRFz^K;5-YbR$M$3khD)U#&Cg}ot2OLyxFfFSb7FGuYd(tZtOh6Iw zrv3@FN_}81%2MjUPvxPXU?jNzjn4p!u@)Q>T!?}e9YSaV&+5H=Y^L6}cq8q}&UOzL zO49*5AL<*C2(tP{9P#4gVeQ-`&6r#h^HOd~bBZ3)HX8^PaJb0W&D9c%Z^jg^lA!p0 zrBkxHX>U9c0Zx}*#N9uJK5HlqaFLcugBk?@_R)P=e7#^|)PQ4J-lbj@+i-N901>=N zJU?I-F1Cq4mFt;L)w52`SOQRw(X;$I2Mtf7Uc7=eZkAWyufb~w`Io55R8z| z)##*0(v}{Uz!EF#7Fj{Y*7=uG8HgG;z#g?S5R`2U1!X|m%jWcK8wry;I3nSf^M@~E z6!K5$A9VTk1378iTSN!3WMzeFRJ`m8lbp7?~-QxcG>k``UT;HlELU;dcbP0m*P;~}Q{G7`MX)|Ua z%F4DzR`~VR_gL<)i`&K3=f%?9uiu`jt_^-!9-%|e;`R#B;QL>17S~@#99|xUaeSVo zwZW-_zgZH8Vcb7(CLTng>VQZ#V6t-o0QU*AZA=gqg5nXI*kXO7P}GipxT}v*`@EV~ z#HM(Z(hTEN?#1sJ(0jrvj%y74IqZ(wr2dDwD5xMvr0KHa#lC zh(ngju;?|QtKY-vU-HV{upT*?5M&%)wgDs1zT__#?bgCdoDXuXstM%6l7UEq%X?M6 z(ua|Lom-h#757vsM%NvWe!9LyTFNDo9^`p=Ls#)^2W?x!Z{h7i3}bMfUNauAr1`+L zs`oVKr0LbEHtU-yc-@=xiz}dM1zU+qUDV}ml^lS;>I!91knt;f%OHpRc2*G0P>I{f z!0vXfr~plu1j-P-xY-gb5S#b%WOkAGJJ-`=&^_%YC$D&*BQ{>%E^)EGOF?g1 zcJ?!d441#Hf;o60ptw&gAOZ*t8j8-yFANsrx)FMnqDK4~#QMp6A08qc4KUJ^=Y(tI zr8Ic`*!W2I5ILrp94DlhoMr@@Ii}&bTQBF1nXCc<+r!IJNiiG2SbkqOg>mR=U87yZ zZz$zeMYbCOKAJlQaY9}y%cu6jWagb#7#$W+$po_a7>qj?$Hk7T4sLnc`{Q`mJW)8J zSExn=7UP@Lq?CxO*BKS1xJ^LA-w`L3bV%wm97>TRI10E=E#;0hnmHkr7KM1AOQZ|; z3f2}wdTDBr#B4%_z`{oPWf-D~U^0p*kv0G}!yJV_t0GmD9i3TbL{l|QiEJoWcErwI z;fEeZw@a2ELO(@-hr!wt@Ob$D58*t0d40-ymRF}`L&N)OJ{okGsV*%A02GA>SMW%hq-Tweqg5+bbssypUG zn(K}(Ut`t&;~Kjxpg3>sEXwRxOfsqz+{WljLapdZKZX=Q9dkgyP##l`0Vw!sHJ1Rk@9-#Q* zP{09388Z7V2DH1wcGFM>mTo#$!F=}9`Q^i6c_e?``Tg}bWF9|2N&mWlTerJ=_!Q=i zY~qs+h6UTxb`MC)famw~kEO{#i{a<<+bcAB!;CLsHj84kwy_r*4D47|Q~?pIMR9Ca zTQ1k&G%Z*p5^~2#Bt6wk()2MmX<bcnA7(=q)6HCWs>uf99vlVF z=ULHOgJ@TP(e=<1p(IjBA=KY^Hx)(-KtAnzusKh6W;nzn`JR*lL-mv&H9ctYG&nmU z7u+!hr_&XQzFhmkXJ6bNqP=XZh{qYFh40emct{V zG|ptvKodCW-fg3z8ESSY*TgJbrw+Fo;I1MwtazQ4!NVU-lusPfW7V=fiUEJA{yeo|q z@b;3f+H>@byn3nCMS|*OH$dmDa*}&vSmGM;+DOxi?*j8g|9_hq`8!#*#Rw;?SiX+PPg@{#1co>h-T zB(mn4_j5v?h>&?!6PK6sfsXU?O$H|EmE=Dr2{87EOmUVN>EI)$Zx7_6XAwz5bz(cL zgO_X5Zu4xns-T;9!n%C9b`Ce-*T971F>UK^IBk)NqRXP%b$z&4ncVHaxcghtHE1v^HGPTRA6bY9S+X?sdbCA@#R zJMB5zoPyY`Xm9aR2lX#i=Z$Vfml>e5EQkfSBFiUqC);q4s~ZeX?CRPWz@$J!`kn~~ zn$~&^Gc2-3E&r=axl2p{k1qy}(UxYwytWPzoPT(_ztD2P4Q4|cl7-?8Fs^t(2 z+6Z$%X8Jbyi+jMddx=~ZcgOwh5D<~ocvNwVN&x;Lb;;cXxvmYoQ=r3uVmi z@gJ`@!UgLSoXdioCZgL~zy<2m&bUJ#4Sh1iX@k* z9(wYzhi{t^zVZ57DHS$Bf6lozox z0apiWUZs>wnF37J#{x0gQ5L~`n{_L;nf8z6(PO5YIMSlx`+P4*DYE*I(X&18})7aU{NI}CF&l-a8S|IN9^ zcB^_tfv>uFsU0u9VJ(TaCE0i%Y+Hy?>XJm(S98@&%VB4!kXq&WEQWNq%ns*MHhPd9 zBCQ71?$4Ekgn$2p5cclw3QBMx2Wi7gDa#Sc^5`sAjaJ}QQJ+jN`)vz;GGx6g%#0i% zCkSpTBluyIJB6zzJ72#P1$j1H_&KZ?>5_iQhRGv9i zEO+pQL#mBi~@pL4- z&_8AK&hI{7{Kx3uo<1Gn0;8w+WjPvs==%*aTT@|TP2-u$txtmDt7kJw5m~l-k$ai7fI)pYF(4N5uYSRQU1yPi8e3#+ z<7K5SSq^d%)KD*&PQ18BleL%)(4k-3=O0mG-ySM~vrbWH9R0Z>NJWq91}&XMS|?j3 z*zM*mKBOsuFu;PXwY)|ez30@@HAFHn0Cg}P7orbl?%qvcWm(+(eJ z)s=Y`PTQQm>uzp=r}?^adGX_7>Fc-q^V{#AeWgPA)A=oaUq)TgxIqH*1hclxyzZ5* z2^DU@LZVNJt0e7jb@D+4-c9#gdOlv6phgwN$JXqf{%Z>Pe1g_vzI7#CZ0i)7KM+pG z$GaALevOj&KxKk_r7>HW72{Dk#L3Hw@k>c8CFAt$^myx-?LGs5H}HB>w8iBLxwLz5 z{NAWvX9G75cC=~ghf!14xMVD17-W)k%#@weRg@Fx6)?sLUZz&A+046?y{#Eqqrk{a zZ%5>(d=K(mL?B@vi|69kT1_^Mqjn}9#T8pJD0a?q`S$xqy_aW(UG1($riH>_=eHO4 zzq)lWtdzmzZz*tYY*4`PKRuO^MHNZhJe#3sNx3H?4Y#JjL5fI@C4mx+v-J5AJ=UI) zO-E#K>?*}iarDH)nWW2K{%NFdjO-Gp`fT*iqd!&C%x#pySqMoa%`j=}rq6}LVau-S z#F-W-W8W2J33o-W56@5;-X3Ci3R4sb*lDm`N7!lxuRu|n|9W?N0l|c(zASVzV-%ra z7KjWL>Qv9K(bCm=GS;ovGCEm_Ydcw91|b6S{8Ny5){nTv$w1mF!dD)G_8grCRmD{A zCSvq3B*9T)jQ>9&)JQXnLaa#nU2mQut|GohnuMQ&5A?~m%~~J-?`?aTR{SF zfgu6lw@T;iw}ere30vZHh6?$+CtId`(Ff7uYx84p`s-bPS|?M+6|G=KqFeo~7mywz z3R5hwUr#1NayThLa`2`Fa0{|9GYO=1m$gbWp3&3T?ZM2uT$daw$8=V#FN68RM;FME zAaC(z4GJP3owAW3N9bSY@BcMgT;474i}$RA%e4E9K0%+*n$ODBLjS*C2HaZprk=%O zwxfT*hxDd(9jqa?w4k~%iOD(u%Iq9W38}DTR5Ujc&0;4YNI@rSW-Yl#&*PQ3pZ*cS zQ{-X=TH-!{mYP{bN!bv>#^VE+tzhE`ARyfdIwU=yd9NTXS#*e(-STD%HOxBMdn9D3 zM1do_1>2gTz0y;gPd~TeSOQ`)W(^1yfx!PBvu^5F9bPdQ4OdcZv{AP{ zZUN#9Igq~-)!6yb!CO7zN3LMK+s&eF#vm?a$49t&5V7Iz%^Wu8}*(3UtYmB3T zFnPt29(f~o?;2Dp(17!kJXx;Eh-*Lnz$C5N*4}t6B%F=cEaNQG2t{_@e3>tpJdlX5 zun7W{EK*t!c@FG0|4=bQw`-$fV*Ddq#ZVx>Fg zt*W-?*h5Q;HAa=gSWt8x~D8HUeyKy_7X_7U10wJQbTGkGrA>v@glzY>jqvgt1G6t_# zdh9pdr-6*dN!|tb+?c33b}7Jw(iSDBU&P_dpXj z>kd1P(Y9VECG=~fm(+~-*RL|S7~fc6aQh1N$?&M;0?J>xc?ew&LP??AHx4A zgG5t$$ag4*Py+VYwB_9dEtIT+o>nEvWkli=$g%WCd+yWS57)Q%%76+BD(c!ls$$F8 z^KwGK3gwHE6h4B^0T=3PKsnC@O}=RMaxv# zv?#l+_5Iw`x!7>h&O}0c!=;C6`T{&A?I|}Q-aIpE;ZdvJpqvsojH?C zxvWO@5EjgUM|FVKAeFF3bb)+|?%1P~(ATcou1tv; zh^Ck4O0r=k9j%hUt3y~%)|18Tyo{0eylDf7y3vv%?Ao5>2gzk%%E_SkgaV9vvfoTh z39fa91Z~QmXHp9m-7y%x)=t2%twJPNIh9Z%ob{&YU7kH$F*fR~CmkKwb$G7fec-Aw zVFn#HN=n(Nc&wvGF-mwama)HOUCoIfBA!D#;&yavJR^@vG^|oG=#-kcEpt;~ zqpCSpx#N$`)Sbd!kz2Nrpx8(s)E9I(Y5ifRh8y(_%!;=xmAf8&>8X;Hf^PRkWwV%R zH{bTBi=5o;(8dhi+ru#VCZxzrfk=l|X!Rgbx}fxJxz`fH2nry|S5{beOUaW*VgFxu z;2C;Pwr0+uvwdVE%{EWy9|BI14jRzEK1F2`cJP2D8$KUEDI{oNF0X=l8%Ljr1BQ0a zJ$_>vZ|~h)hh&wC=-vD>N}6T*@JU0G)NVp;BWySx8JY=kmy8wnhP6dX)--gq|BG5R zdE6!#JvxX6EiH+joVhVcS0`aayJI%c$R%{)V_S7F_R-VT7ie7lG^D7~#|oeuZ<+zYqk$O_i$q7yfprD&}ubnpbOs;HT zhct&nfx(rU!X0+Ssm0VR%VcJ}ovGAyu$X%{t z|J2E;y}DOLifpEm>u-xk1cK!2u9w(e`8}F&j7cwE*lgy4^sew?t!+)i4&Y>qU{qon z@1x;yH+?`hFgp>u0LZTp!aZxX<^o$IQ=@{#SnW@9jyA_BMZ_M#l{qhf^C$$#a?8P8 zm@;8l+CZm`G<3wY-1+grJD3hh9hTrN;_i~a`X15#q_G zT%_nIZ^NiXZ;Eb`FZjfn%j*zdlX4tr$4?8w$mO;BR6W zqC(gjat+8OR#sN0zy?Os1MJL0C+HDvVf&-&D?l?f!zeO(<%@Vp^bCj_S)&vsoKbHE$*UrZD}koMiZIXK=#G#*Ma`@JuPzZI-~{x7IWw)o}Z-rzmJ zNmH99upI>&@eH0U*L6Q!eEG6qX|t?^8S4@Jba8j_>EaSl48j#{)>Bq8L*Pv{9_oTJ z>oW%7vZsc6-fMQ(WY0=1=oRAXP`dE^%cTEgS4cU(e?YYa)<+0i)4n^ScC<{JtR2nR^f_B0$5-UN!udd_TBn5{G%5T#~Pdk{`` zSTg2sY{oyp1@(7k{lCLGWi{pSQ$(QiDpQ-?g-?){BOhb&N8ml~E6B|AVp{QsmO>I6 z|Iit>IzuBJAbf*!Y!=R7%Q@Dctn0B2?I{%VH>YzZiHVF+_$(?w>6n(AB{F~%N#nX) zDR;pcYNbq&Ib?!t?-Ppm&iLmXcyw)HrS4~}ZXUV{7!7+>1o zUYUntNOzWX7iqm;7thXz;&Q@pS%}Ucr-gd>$i(Uu*(+u>qDcQ6_|Y8L$cs*1|8rjMvDV-9#DqaKKl=H!_})#zi?WxW#co zyQ$i7n?r%j$rg1`#|xHcy*1<<7{|U6g-GhY!RX0oEt}U&iI9~ZKk51Jqm10O)9u=JyuzR!9^;R{nO9vzC0)Trs zqM*ksftJXXHs?L){LVDMmdcqv4d%H|<~t|Wg>U17yv(49$KUMvTTavJ*t^#h)!Qz+ zc`!_1;8jal)&brC;Qkz`$Q#ZfUi=p4B+(W9=)3hTBG=Kv2f?1NrSltN3u z>^;4GI>OJQELpI#H$q7h6&k5i1P%e{BKy@o(yvZZ{4n3|-tEu#QC||ZA#<@9qkk)p zna}wwZHmOUlU%BbS5jN_=JOO6?tWwvg-vfx(k&vycf}Y_#Z>OUH4h=^aN6o z!eM#_9`2;*VCe4tY`f|9fPd%7TCk}gn*QQ+W`H_DbQ z%IcHpN5QoEBFRu;H4O!pPjMx0Lce5%7xTM^v6+(Q#YloPx}>T?>x({O1ZLwY&dEE3 z0kUxqy~onf@t10?EjSoQ<1bpXj3f6GX-XFQ=+w9Y!EsITO0Y|L4Q^3ALthoHIP*Ew zwZi)2`dTuTsPaR>0}dfs6Mx?(S%0m8VNYYc_gT(anQavaOxnO+&&k*u*4t-NAlGJ4 zdJ;{4+}^Kp2-&)?N+7grzhNi=p5_2A@=uEogCfSM@Y&-Taz#uZSUQ3;)KsaOiX>jG zUvw61Xr5drw5dyi>5uEpbG99n)P4cKx}wJU)t6jR#9Nr;+^cUY6-Ue>n_~pmsyJfa zn%Xh^h+{{bc=<{6BXZXt_cUfR^c*>F2+VRlhLC*o3la&?Z<- zu$aTo>2yc_^|dknzi!J=)&Zij09Y7odsX2c4UJmbN3`n0W4P%$ksbm^0km+`UwC?X z`Sb}?Iwtk7f%98<59V7IAGh)rQL=8qO3=$|i(L1ycsee?uzENBbo~=t|GGs^g+a#E zLZdy$cFOV*8P@*YW+b_nsX~dz>F>ydjhT)&vMuS6j><78X`~%;`W;`;8vxybv~D;@ zPP+tdB-HWA5wcOyP!g<;)y|0EH0ElhyxDR#{gzfdC_y|fdHE&Ob;lk7YV7z_p(z`X zO4fGFDYm;qNk1bK3)aFrkz7|Fl1z5=Tw#4=nij#FFrp!@eJWs5 zp5MV~x`<3X!t+!A^{7gjpQXu>6-+3rt<{#AKgBC|OVz`=JFi`5Vd2u%6By_W3$=5# zi(Y@O5mI@ins@vsqANveb#g3Rc3acgc9v>ORX*carfICoia)6AYbMp~ zo}m&uQJmY;)9JgzDN9=8ahQ#u8^+y&;0hHZEuB{xi2-GoALPndxw2}pjwDuXTQ9g&uJ0K*T#?edXZGxW6CB8!t0 z9=p;AC|yP0VZT0<=%6UI*G)j=yfDiBn8V`x>ybPEvH5TpP&T-o+K_mLDjE`0?$1qZ z&|Bl*@?N4c$>RLSR-2=|GzsU7P>MuNjEZU_i+_KJ1EZ(U_&fU>ajS?wP#79dk-t^c z1$rp+QVFgtgnHDDPRjHtVarWAu(;xSNeGdul1Hc(P|Yd2zqql3T+q2Fy8OwjP$c%z zHtD1tFmYIXg`+(%_%NKKFzd0LRr8JGnN2Z*lBNeH8Cg?RR|eP?)j_@$kDlbbZkzyN z@YH4j`oc?|YA2U>9+;#%yT3!Gj4{LV6!GrCsw7(@BCTS)g(@7aZ8S0DQ{?0qln@&d zPYFq(MU~3?QEgq^H?Z*bw8vkO!>VS`HT+l5seb+uhZ@U9wjr^(V- zwoia3K-*&4GH(mSe0gGf(Cyx^RoeE#(fi}NYPCnKZye=x@-GsKu^KAUJ&wg#4LQ2Clh2i0$x!`c{(oV_ zXx1yAD$`gziPstj&)clB|;AY;$py8~mlBMe7S6H!|A5X)2$+}5= z#)A0_WVMsMLaa+lx6jgRx0c+2cO~!oLPFsIMUnV&MI>bsIhRtd z7#AWe+FzbZA)w$3Gf2<)#jDq^UcO!(udZ#poUD%5UoHLz${g>afD;l^~nM6cUw$ODJ)t*o2>g z9Du?F$U*fHFu7GZ0@U0pH_J{=G+n7k0`rmAU@CQ@yqrp`4g;^G>cdof*B{sV#Xiw= zmOeY&L9wV=UiLqXkCrrH`36rh6vp(BGv3AD8ZWO?qrU1|s`$ zKhk(OQv$rD%)C)rOy~q5QX6>$DFOvM$_)z)!UywB@&q8x&n|3mIfGqn1-+XyTkZQ{=_ zyU)lgT>SF+hap5M_9}CzrFRgCqEDdJZP4?X;Ti*PyDaMD#WDN|J&e(uqDRd656q!L zXk{BpCKuwqc2SC~GKG4PEyo z2v{_Xj?jYHxjhijZkdgdf#@sa;Z*}?AK)9pP-G=q(u1ZC`yrTs-Ye-Rv$G{BBFIlW zlxwb{fj391&~iiovV|M!WREwr=ji$^z#w9yx@T)ofm14 z@J?C|q;|nPJQMer&ru>EG761bjeZ#QXmxa()ql!LeF4bS%7+L9$$m({Ahq)r!gAoy z)OGiYA-!o>0Ip82FjzIHFJzZ+ABEXdd4g;d?k~+J-J%j30>E(oT^Ie`c5y0{RaDkJ zBX%6%(<={5c&0e4Cl$;wZed?}?~fNRNES!5Kq996BM~jY26nW;A^QDRLhX^~4LI`+JqB`P$E7`tYR2JH>hE@utdj?Zwn#_HaLs1@c zS;6#EGB9Em){6l$^J!E^{Ir$H3?imj|DmSLdQ@8>RRlsiR9=$s%hJ60cj(cQ`PsR1 z%5T6$uuxv-hUjE((tL10fRHNR!>-*iXYibE#eQ2%ijN3gr2^5PrDqjzhmCKxfOp%T z32IUOSrYB<_<}A>NQU~VS>G0z%}N@-xVpT!LMgbAxic;^cgB{vW2?ePM$I$(FDf!} z%g6H}7ME)pO|GmMP8B5u4X_72Nj|m;T6(^^@@hF0=!!RKrcg!)Hunt*_D0i3G8tEBt$&4WQ7Hm--pj9DV=YGWDKX2{qqZr2iyQMJiS@1!GdCSXx zafLfbk6Jbb)?(biR1=V7iS^tXFq_ZVJ0>aQNyXwA^rKxuTK$c}>@5 zFx_3QTD7NMml9S-fm@Dz!CVzkMVKVHBa~oQ!4#<=lOVjd)umk;uHgacXo)&afc$uP1{&4 zY0t>8m|qPCYrQ2r5Y@#pKb>?i^V0$4QdrkB2wmrRh+wnWRwGzEFiGp0j=GMPxn!~X z=sD2A^IN?#ffR@Dc4ur|@YBVY zMIgbDzgiFL1TAsJuW!O5Im2sA z&_kI)Op}7qF-xRCDaqUOZ_i{Fx5kf_SZO3=n;)P73j%VvDER`!uz6uJUqmha! zx_8(az5?R*iM!zqfov#W86QNq0abUn(fX+u>BHP@;A)5?H%rc(o#=9!txkh0K%1+m zTJI5+^i`BWx{qbr6lo%JaoK>F$mSfuM8oL-YM=>8tK>r%=o1g%+)vj}F=8aI79J+3 z7V{9W+B!jgroJiu*Sc}cEprNQ&(Z4#$t3o)2P(^zv9Nm_Bn{TAzCW&O1`x;muTBd*+UvW_mKpZa+)q;3G#V%Yz*CI@&BG@v@)Jjv$~SP2B+!X z;=5TMS6+HYmRl4`3FM702Eq|@FEQMHcNlP#>WvAyZ@IO-Rx+jx#CvZaAMG@$9=eJ1 z)vAd)k@DS_>jyS-zDGkI)R(lshOnz8HXOLnQ7wQ4Bw*`DF_20{WMi7kV>$akD-n4TJBKHy9}&e^pHA2L zUv4jOFu^KTXj-7yZ1+3STG2G0wFjiRq%H7TMVL|}pT|KCF?yJM ztph_V#b{j?T6%qDsAlWz@{xUCg@R<%zBk-&zR#SoIoBds)0G` zs9xi;@WB`N$Lpr4oPo21 zh|-Lk`2!@Sr_7NNq)%`z`rC-LrMKMM@*JQ~VeD^{Z)^}IYdr-spB3c=v~X+*QhKG} zi#eA&s!^?0O1PrX?w2bl`E6jwrXbg;(M5(^Om*YE;7zQ2F-1=UJ&uJF5+jLWxM3zc z2~p;n$vPy8nYOJWQ$zY?Db(_%4V?Ow7pkZzx-9)4-3(f$0Ijl@l>4+m{4joo5Ch>s zm2}hIPyi@P&sj4Y*&SW0m7O*)f67PzP#APO18xRnUh;5FUQ8w{Yilo8)?QE6R{+u* zFP@KI5}b#X`z}VQH|#JumvHuB)@Xj)us9pFUn*@Tk=~Qv*F1MJ)I7JzDIm)1O%*kZ zhU5EV+Kt*zmtNHnzJ&JbFkS$G%usap7Xftn3#N6QxLPFiZF;>V^w$3G$h=jU6o0ec z%>ybzc^U>LX&t-nYO(mz{frMWWc28E$7j|AHXr}qvmA~|2aI*HV*31 zj`?CKQmJ4MB?NTdeeejrYQ7RYZ1JxzSws&^l!-b$r0O{-mDAagVpjS7%6C0{; z$)6E^u54^x1OjOLWN;0>XU@H&R;(f1@!{%rasK&-bNHQ>as+bmxdMAktVMs&h(lyP zmB(4(EQq7FcSevzfF@*Gh5}6=Mn@)<;U>5=&p1%2@eeA>AI!4@6dXF(d50L+YC1dC zR*^AK(x!c0Q;fcKWaDTLA)UO@$G0<7 zL|EM2({j?rnwu@SbzuG^(TIc-5}fCKwYN5hYI$5L%aSjNqdzXHpolxaT%?Gxq;3rS zCoLt&a*l(7lCRKT#jd|KLmOW7%6q$i(h&pI&40YO8TH2EP>GC-u1X*SYh+w7EHUJp znF%kkisJ*6eqqUyaqRvY6e7~tvij0ICAf<7lI_kg(52zbh9s2h7s?8L$3z2FU=zRtGKWsc0VhemVmb zEtcLhu|qe$SJ1gdR!it zd?`!c@l^2>y%Q)VbY+Z!DAemLAKMrnsB=@f>mH>ND*tw_#AY6}(s1uFL6?bsFkNDs}?1ti|56AE zfxQ-M^q0%EOuBFEocnrKgIdPEkp&0V^l%c5tVjyM#%-JTqLzG$t{$C#WE{uuX1fRP zvm+=yuWePmf?F@NCXw6#rDs`H$;VN?;m|BS3~%Qaoc2A4Xp>7>ZQw}If$zkGm&gh1 zI^>eMqXN6RHQ(P}x+7Hh_TlqADB=fn%eB-%>4Hn&uD?7W#J{lf1~RHXKsSRc5&qUG zbXM5?yC+-5i%}P?6uQW_5m_5w6r;dDt==Y($C3U?1-ZY74a=(2-;A?(u0-ol+mJ(> zGW|?Cn3vvn0v!k-_%<0JioL^mC0~o$o`eixvjEKB5WUrNEnu!%2j4;Vw zOj3V)Ya3vn&VN+fLw{3!Y{;Ew5WCf}F=m%ynVV-bbi65_raeawQ*gstwpD}rf^b=T zA8`-;Q{Jc=)E6F!xtDKkyC~^CVg9(|D}ilAsCOsW{_%h+s&s%VtrczC($79vmT35i zM{)Y(whL6xjj^34!_x%m4LSV>Uq1k|<(m#n(i_J+WS1|$-I|dt7Gq^~m8$dt+7%B{ zxNpipB`n@PehtkVinl_9Zi@@$L74*oOU5QTLVf3FCqh4W@JyjvqkN%Z+qppaO4)%- zgKjTa&js|2DO#Tw)Y{fM}bp8*VFqi=biJ zkjh4Y1(>Ng6_(@#?PC{L!66tnLigq_l}p_l)@u?)qymj6$2dr|Ah!WR8~x|#0_Ejm zc;NZJZ115p@#s&Z_)(`%I>1_XHqYLGap7*u_+EKIs4%1d`uT^8OSa`BUSR)_)a9j$ zW0V^4x`+l9=@g3uOVgU2CaIOLQ;^+pRIba`kwMB%hqPVY!IO*V_y6u}ALMz&0&{Tu zXK*PMD`-?KXif>sxend)*!!EHwMnVW<6^27P>B^Tn%E+qZYlnehcqd0kYsX*WRp7% z4#oo1>nuXPN4z2!dXoDVj&XiR&q5lIywBr_<|YTA$f!q zf_LfS3Xx1Nz2r3|EhBa5`O1s+gPHw{n!xPOMGwT;{7vvcaD(XSHa{-Y>)7uaNJP|K zLv~4cOcyfHbaS9yPO{;^BVD&7dfLWO4}*LYzNF-W%_-S>Y)?HS7QDW&k8xcQ$^Ox3 z>3P+G{`qK3-e%c~Q&a@`a6(qHGFdCa=s|aQ)q}fb2C5tbMRnhbz+!osoex_(?^NZl ze8t8Dy&!e#DrjsOcHEdHgG$tjp(q2omU;3VFz7&Bue&T=IvgIbR-T$iChAcHXOb3j zc#Q5l4Y28eA+UM=@MWAegeB%k#$3VgqQJ-;t}Oye!&!zr&*R!}Cdun?5A{bBDM#T} z;?9~3QE|l*5Ntcapdamhk2VIk4`aGTn zdLj{VbPLUZQEd7C;-`g@WhCq$uI?_rzgm33Aw`=)>h5epx{c}GXni%0Q1wzhWx<36 z*iq$^v<>A(m)dXUjatPPMCoEDq#6yU!m_}Ftcu^sC6avQQr=VIb@F^m(e{2qkk z@hQ{t#b(I|*QeKe7cSCyB8MQfddr~U=UB=7OhuiWWrPYce1zhIlV1L|a2wZOA?>r+ zLlUd!`3Z-JP}a%wjkCSCIdPEwjhB|U z$ZX{_MVB_o?hFtLA~8&&95KIIx!4PUtNLwYl#IeHUW$MJEod~Q%5@)_rQ6u1M6K~)t@Iwkh}QoDrkP& zD01NDDYtBBmbOgFXVeyu3wnBcjvn*Kz6+j#tfC9e6F{g9rVb!7in>fJhV42TW3eb; z|GxC`^j(Uq(~&IO$JE=`6HoS9S=*VqdK+XT-EU)CK$X-K-$J?7$ z8xre8O;GwY{=o+CJ4e=c;qK>)ZoRv>{dWXLufAL^Zb$R?`*7C2IRip@_T8R+Sa?dp z)1pC9_CN?p5Mj@9&nRy9!>F*^Qfj$tnS9>aw%n-(F^(BT7P*5_C#4M5T+erYWdkJv6s~a52lw>hDd_a$$CA&Dp*9 z7x2UUOd;5%U!FlKWxBw#5osQ69DUN^{7qFNryQSyLmccBQvw){6L;iZ-04n#nLT)Z zi?RN?3Ybd=2?vPR?gZxxRtggV5*}Zrh#!!r(ybIcUxv_1#~&*hL(TD>F@Wcacyl4; zDy?_&v6cwj{^OH>O+KBE3Cqaw$_*XqRNB`8*QjprnyX4U!l**P@I^KnFQ8S93Z zztW&24|twp9Tyyv?EvI3WKs^#ibftp3&||s@=WSrsEFiBhhqkRz_!3W34Ew5QE@yz zpHka0UC$}Z%>=E>!p@^1{L$Ejo$^V1ax!+%X13>KA)x)<>i(4R%mzKh>#4L>d z2O=_I((>qeZ~Tw>I2b_5*?%qkxj=i>I$k7u$nL#iOPt;DZJQzV^D%a_vK+q!dnD{4PFP71t z5k9gYDyzeTfI3>f`6(_~bj%i2#)wRL7Bin%Iqeb3F~hJ$I2ws`!_!Y+vZ)a6_w2g6 z-Ti|d^KVB!gL<-O`!ht%l7MmhU_M9P4%!yoEd7dybB3HhMNmxEyfTM5S}7UKngWl9 zZbBz{xdyEndi|s@aBaZI&@VhaV8-#`Y}!UGIR7zziHPjzvEhsRLemZpjts$?*kzKx zkpiKo&ParRqiY#*U7Cjebh^1oiL*Aw|0ip*6ADfcb-cA?6g8JK4s+AY`hGOx)|;ip z6laPy3d$1|vE^{;O@RgZ=Wy2O3+3qV%lDAz@8Q;qoeo{|e941^2Tg7iaF&R5LMN_M z6q+URy++sJ;GpG0DOIsp}B%ZQ=>?bOI1JpKnPq zhK$45nN8GVCsk50DqBJB=>k^QX#VTn>BZ+C*|fLYIzy!bC_fxVv7x?6u?mwSd5r=~ z9Gs`+2&xuelf0txkLL6kq!WsDRHr1JQ$M8TPV|O#=d>2U_*J|W5FS}W#;M0okh?@x zuREk0RaFufFJFEsm@Js~ORsEON*g+}z!(sj-`O55&B5ZrAOCyw=3k*VXMgX^(X7vh z&1`ZoosI?Ky7{Sq>CVmtzyfv@&pTPW(^1X9=QU)-tkToz(Nn7P3=KZQtKjwi}cRg*b;FL*c>SLP& zRl>mX`wQKzbIGS4XfY7QF3v9BAHE9MieK zLglW^aD&qB?f@i+2*BBqM*?A@LXX-+1;-tD_mKCp2erNU8&Dd-axw4Z?cQdV<;YaW z)!&oq=(&#*&wZRIYteEaZ+`sYQ=a_zY;<{dIr`&jD+w}7e9Y4zC(HQD6D09Cx;T_G zr9{eIY{=B;Np|;5F{I@U!V5gW6uDLuqg|rDq62tuGeDXw0or~14|nnd5^hK0fT~zy z;dn`gY>hV*bI%I6uHWzW-e zW4^R&|-U@{JI4mOK+q0P$bdPTNPH33+ao z7|jlrG;@7EFr+a&$J)`BDB|gCffOtGVA+}ilA7>kRPkmOD4x#A00b8C-w|u1VOhUC zR3%KIl(cPG*)N0=L}kQ~D}^Ja>(elf>Sb`HgLlD|K0Sp7iZ0xIs0s2r@{Np(>TZC% z$fbj00@~$wVPgU+5%Mhbh(O$0arVxuU3##cA$E@u^U>jcnlp{XR@SV~_8F~{O6s)Z z!;7moK(>-+S?Ylq1e8ZY6V1-5Bheoh?IoR0EF#AlRVws*hzshHtQ`=3XSkl)t6_my zJ9GbJe$pN6AHAhX;(8yo+Quw=NttBu|0p+f0?&>oS05<38mepkmLhVpt2eXbvy-{0 z%)EB>SxN~9(EKBlKJ_yZw6i=WcG|&DJkU;iRFot`gECc;wS6Z5#UN&L<-5JF@a1If zPGf>nRw9MZ2;(WI-^A#KeO=Nd6{=>;q7F?)msmZ`h=8m^ln~E9vMIp~4vuFdw|VfEg?sW;6B!dx(@Ca4DKLg+X?dg!0iQ%=0(MUM&1z6z2y|iU z7dm#9WRuzM+>U0(N`c%H2)EL;GDejL9G5mx(Bv(c;Jda zE^t7l?_$HS-s&Xb$FL6JuNnVSP%+muSF_GCC1dK_qvbCA)@*G{Q@cTZvQRFNvE@;0*rfJQEOI1b$Gce1)a1&P_WTrT8@yvka zPXPy@rl}kimI(mxSpSpmS=pq?dE^dl?Np{(0?>&$dOf;Vb@2K4vk!1co}JWzruH0t z-A<`t>Q6tQqDVlDOXE3&A7*v3bgpOy{{0gkeAoM8ERX)>Uq+L);GX&Is^!WbwCJI> zezx<@M50P_-hEwMe_guaouE!EBYJYPjOJ4GSlr>hXrPG{*5%Rv^UtFd7!qtHeY$fp z+S>c`#$R6GH!WjahY<1r*;n#in+*<^p+NRlwh@xl?vx&tp?q&-mv}yI-Sd`J-^|OO z1g-wg{L=v0u>??Hunj_%8#_$XF6Bol>g#nVd|;AM39C?nGN4wX2(pq^c|ks})SaXIKU$lj@J_2}r^r{2+M%sfz8%^@O16LN7iA*b`qH zp(u~w4{>K%aJ1nlZoNAo8zr~OCk18WwMGBPbPQ54FiG#ASBtE~T*>L1^{Bogn>9W? zZKaAJ<(~Wg=HeO%fE>5WtI>OrK)ad8+*Q$X;rruy0x>Z98O64oJ+ZR8>bmQg=kFZI z3k|@_%MVP_JsSK8)^~x_)9OII2t-xZ`;4)LlP!yKg^KENUs^MCZ$D`~dIp+HM^Ast zxMJ@kIGwlVnJ&I@*hp<*Pmt!6UqvDbhsB(YfEnjrsvxoq_+*lxF$8l{8eQAuuPyv$IDx}6GB)3TbM$6O6%_vhcfog=&8`z!pm{DdgM z{X<1?Pj}wT0JvoZeKQfmThfN@8GH&0!-u0WZC*~d!6Wi21j40@$CV)g#kBmodQp=O zrAAwcBx_Dg=0V>2XdJknr03+?J2T#!BAQmxXFaN~C`x9_;jP6KOIvw3Fu)~ifb8D0 zY3L0)ByjnpS;1Gbe~7rHrQnZWJsH4#l}|7*Ne|KwWfDah(6$P>7M9mI-~uyqZrDzX z8C-=e=SJ2x0?+&}V}vHRgCc9gdQ53^GF$SBzvXMT=jiL)Hs7Js51=vqQ%P?X1qR>S zeNWFM{^hEhPg#pJfJm4oL;^4gJZ{kuqr)5=G3B$i=jd6J%o}LoK+qV5G+Cn9YYaer zs{<|4R`?hWug*C}^Hi9ZyM)^pvIV-F7nQP#L-`pE`Udi5f@N6Oy}}U#QwZsjuBQ!7 z%GM(mI_l+xl$La}s&u|eMRX_w{bie#deHj*qir7Y>ASmMzkRyC1SwZQPYn|2ncoMc zxvqcq@9Poq4>Rs2&>Qm%ymuL$WojQ8+7Z*9OTE`3m(v+C@v#p?9gSYS3YaRwuFMOc zXh?s%1W3gV-K8!HH%NWPn&C0hfWajqi%tBMWv`1;6*?b=ZAPj__Cl4WSazmGD}~!a z0eM-P>rK+??Ty_60oyI>`CvV<6z1Gsd_70Wg}8}eu{5yvPuZ_TI60ny%93G+b-ljK z+iFLW8EH$Gy4vFml7gT{mHJ}LMNb#Txoy(l0IDl8!zfmxh2G4=gN{p9hqxTn z#VrILM%20R9c%c;SW*0g!J>>WPNKupBL|T+7TPDoFrRyAtV5T7zh||@WK8&>-?PB( z$_$L`NGkL3SA;o5BzPX`q#nsifAR5JThwEbl5+6v09M8Faii5qo&wB=r=Zb8>xPBE z$H6dxKa8z5Xmvaf;)8Z-=oYHftm5KPo5&nI`6~sgO~X;fI=3BiS;b*dmUtRg&a{YB z@wR$4!C(f$!cy&M7E3#Y(HDQu>rI;KYVDbOvSSz)X-)3oQrbr~O>Kll=gl00qgpXc z1+`Q+5RnU6UqSNIW8yU=jo_#w+6U-X2V)F14X}%lk=+Rkius8Qc*fQgts5?aA|B?P z9V0iL-r1n!gcW{S*rBpDEI?%cZa&=o8q}Z|ct!N^cm||7gu@%qJs71q8sLH3Kod13rXDqVz?0ai@QpM@Flz+uXU29C@~X5AwMBOGU971n z3r0m&^P-}xdeKVbJu;G{W7UY4!U$Upjf8)f5w;qNX|{~8I@0Aa1A#bjRIxlfuv{2G ze@6UW9-)%+1P|=kF#h0u{q(!YM*#bei<6)AAYrocCnyeSwV#48e-(V}{`2Jd>iFQT zLNVi)9)e&jCacjx{$`HIm|&`%)23?YNXbdFD|Hz=#Be@Yg2MnFkg0t{aAx^>%q;8Y z>(z8nM%V3T1L2p@GInLd>~_r(@Ey0LvvZV=@QFx0I_H`%byKaV-0Bvk0>OX z%!kZT$E6C6y4;w|#oSujUlU;IQMec2a#)*40yZTlguwLCBBb1l=r`U)H43lc$+tq; z>>gKgea_lPVwVMTp@w64kn)pSl16%7$!N8H7cRx1B#9@Qv!Y|FhBdZl8N4F}BZg*) zlVSUl#$y~erE}1~8cEl@fVmAghpc(HynMJBElIjHH^H?TSvpXz_rB|5q^#w{=%J3$ z=>b4M^3bY4AVq6;JoEDJ~!8ki1dRK=~Eyxlv%1&w{X zgP8_LMVz`qFQovJ>NGc#{{AL0!$Hob(sp=cqU|c{07GogPu~igES&%e8VnC0qn6q= z%A2O&^gdz<;F!l)P5t9cvxQgDV%jThAq~Re?u}R4pRJ7cXMbM#OTK#mhU<=&yxk%j zYSTr<`hiLIwyGMWU63;BeF2e15F0ot)-DFmZdmwVI}9PC0HKZY-oy5TMM09|hNl%K zJT(3C3V|o4aXq+gV|?(q<64{q7D*A-yo0$xZHY|J@)*P*TcysM?_l0Kr*>-|(7oOH zSip&f91yRf144F+-Y~{PJ&`i;px&_FUqJ`!B-XQSBG=+8MGlxiBTy1_{+b3X^!*xk z5~M;bNO1WS-KoWUP$H{bPMUMd?dSkens2ieB}>T|5VRP8JVRqp)mi`&6d=$* z5zZa3_ql2$o(+_%Hx<45eEaJSOT=bIk(P>qL_UPATWyN0O2zZ0uHMlF{!BE=+(*O- z1|mDXutXJ^ey?y=DC9gMyjQLpGH5_&ps-uj^`+;luiBNYkf@o(8KcaH&``ck^!59J z)?&m{r`jy@l|7(xq+*1n;>m)aIl0KouB%3sj45kU4%ph4>4n$~ZI=$=>df$aaW>Lvm#) z6XO-!v-}iOta70j zLkm4~He7*gLuDtAAZskDgwTnN4#mgiDHT2IpXt^4<@NW17J}1DPoOVD6faB#ir?^O zlleAhYJmi(V;#(Kfu}<{F{BRBNg1Rruu`rQ!=Qcw`oAu7OsQ;cL2x%Y>zH99&>}$Ait)UG~)NnI2 zL~7Mk3KhB)Tu^MCpw{!j;`~c$fi7N}EvfZGNh?k@MvA1A78ccoeE9`#-C?ppDKN|L zhs(m1%iw2{ZdtMXDE1HLXSpW`c_?uxHF_c8q9sNVEeZBSYT)FA>ynd+Hg4x8S!y5D z(Gz&IP(`C$K$MF_adJQ0blk)DR93^zj7OfnXgrq0G&D;T1x*g1tyUY&aIS-#Sji9e z$!z-Oa2hJc7(xqNert!mxlwm=d;a~mbM#`x8;owQZ@O#gt9MV~md~xVP?OF7Cy*Q?|F!vVxw8sZ%bOPg! z^&NaIL(-}I4Am5UIV_hX{_A|LZ2ggr#j;wgcU=);@@>L}ocaGtYABgg?bi$<*ki-u zvmj(WRLbHZ5ic1gqfjQOYX_R~T5`Q{y<~gykbDrHHD^%48=r;;Hs1np&9CyYOy>k~ zb^K57AWc(sePBlUlly~*Pt&F(Wptx}u1htR>_G#;#}ultiZ0=r);w+!hjnXNQmjCX z*!I=|T8C|q%GZy9%eBngoi9RQFWI=Rke>jq2KJfU@&pw)Wc}%JGvf~cV~at>&^Djc zL)#TCR#MEka@bsx%2F?#v9ssw&U{|$Da|RiD$r%oA?W&w{abq|C?7-ZyI+=ph4MK1 z=;`HCE0#$4U+4g4dP(|+G2KQ-il5}x7E$h8FEb;VUNGG{fzEt@`zJ@J zl``r^FMI9M%5G_9dPjrHEnEawGXq$bT^S>;wjrLrIaGa~PCd7%-nfZHdpD<5`Fu%L zTA3+nK7BKKH@N+|J1xAo8?tX4AX87%%8~=1rxY%*17{S4B7Dk8SihP6>&^jEVvBGr z!bu@7NDGri%F0_?@T#7p`y%7#;~e{e^^l%RgUu%jMb7$F)gpol+veKa$-1i5thghX zP6D!52(Y&dRqMaBZh`Z1Js{8>@aq-5siKx!S+XP1W;sFcBO34$Uj8H9F-ZgpUsHpU zYLv-y4I?z7uPWdgoh#bCeJodbda0F(=ZUh|#U4|za9JFpEPpM1i;0!ifh+yy@9BbZ zaGKt>o3hs3`JJD7{(+Lezw}Ej(C)@gmSoW~&M`?>F46galjc+;=Q1^zRhDfJf%O^S z;_H@f(qiB?1a`8GJn;}rTQN;GM0$?jADIVKTPXvHMt~1*q5u-zqZ%|HYHYH@jW!ag zb{hUGt0I6TFQty#vIb42Kdc1}80I};nDK9*XLolw%OxTN>lA0%cUe2uIP~;`9BuYP zk1g&Mc~D|Nne;Ock{cgI?Et{uk`q~1$`?8YTO&GDk&|Y)a`V?4RF!sT)o^5EL%sH7 zPh{tfYkku0#t`(spe}FZDqy#`y}h~)&NC{ftA%n}=!-m}Vr{&X#be9!(3qg*7sHF+ z^oqX~Q_PnwDj0#KDeJLWr5+)~IW=k?#aO353wM^JSMo2U)Z^PBhhxba3)X;bw7))r z>UK(K5y@r!O!v~;aMexYFS~=M+h~S2_{}9P%>2CMIon^r7JqY=e`u{TZhrn&l}$(F zyO@@xz;I(A3A_P=s9Vk;o8qoomUowD_w?f1;uaLAbBkILNEOF_{1!F02>#(k!H)s3 zLMefA!v`j5)1f5IkI@YJ9hMlyC?Jntzpgz#wSHF7s(=nD(Njc@@<=NsmO3-}LkZvT z(@OcSno!+PzjK|@P$%!;7!_#IJf1%ab|S-#I;TX^HWZyl;>ddHP(!R2| zSoXIP|K{waO-+K82~jD)p2ppnEKJdxpf#?Q4Pp{YiZ8^jD}A+%mUfXVoK8hfuwD1{ z`u5xTeRqEQ{nFQO_xRD!lhYiy{K7OS%;`D9(S#4XVgL;e3Rc)w_yf-25$_kFD?5ZQI*(^co^I zcTb(oKaTKs@K)i^tfehI-$?#Pf;TGfdqcUK^V{=p?glR_sQ=Eka?eJ(L|$(_yU+Sx z=Lxr``Hh@HV|YE+Z{hsrBTQDpOa7g_{ICDIm+NOrhBSv7v^X){N{W{WdwMaVCs_@ef9(&OvRDI&0#)++MPO59WwZWfccC(XXVJ7}^bC|(K^2sn;yF35VLcV{BV1RR>@2LEslbPm&PWL zBV4Yfi7;yIC0(~U`MZZDwV}MjDPMyXqv55kB$#R4y@p*uT$|cIFi9(=w4PHmT#n)( zV8gQ!&4enz0I0PKI6SfFXQl}`zOy9aBt3mtm(r)fXG%aDaG~(D8f&J@_4*bo9b(g^ zf;>N)pI{wuDyu_fMa@*Q2?!BYl4ZrFQu%d-w5JJB6hO^f%kZ>gYD7qT*X`Z-hL+oK z>tvNzYH1Xh5}n8@&7w*oRjQ~8E{kF$M&ZD^Msv+e`~&Ng&<=yILTgz0g$1b6E4hN* zaHCY2d4V9p=2pYnm1LBwS^Mv5={_0iXPYjT3ArF<{=TvlfyloqG5=9nnh2OY-R!zj ziLBIeLl%axy)lM2o~FS)_D^VuSZRx#1tgQ$D$p!hT3WaT9{~h7Sgs|F+lB_S&mR2^ zj5)$1L}@Y_)3Z!0eRRos+yr)J3IxDn$&D2r#W^b|Qfo*--CdkzC8#2U(Tz#u8 z@+Di6^r0QQ!Yl@pD49#i7`*?O-n_Y{q4@r)_Dhy+75eX- zfBq3&mwJH;`y816fg~0j-Abl}gTMo{#l}VDN@y7=5M+1jg)HwN;O{L$0jtVm72D8C zk*?zH%C5ehUqXR!?coixxKV(we=5?4!XRYcMU#cLl#(4q2qNf}M%JQw)S@)mx&Lly z`fO7*!KUhKvV=eWUw|6;o-;ZWx5u@!R+%pfTo&c@(hcW~RK_So6U?p^XW69KEt5M%*qO;x|65FQp}? zCxrSz_*Na1Ma14F=zFczd-bZOz`$da{WqGtm>>%BYIS3M13xA!B1#J8f` zl&T7R&ic26=g3TA%z5_9=EK*oi`%HTI-`#|p!-$=`Nv^Bc*Q)5n$?-tm0#N2LNRq@ zo+SAJl{20?1fpIJb0cN(l~ltY-(2432;xH07d3-+D`$IKr6Ctw2J;? zO0uOrZ@}N`~rSl^&C^6+G^15~=!oP|lmhk)?AH{&SFWsY4wT$^KC7>;ck{>Wz zO^X^rqh;7IDsraUH)fd|%i>^KFD4qO14ssmTWAEWDvYi8?5FB=}g zTmNK-~W7ZS2>Ou zgawQaNp&l1QHw*=T z^U3DpDzRM0qrH^%=^vVi4RI69zKO@YltX7HBSP%BTQhpNwFWXw5PO`dmS`GO>{-DfFk;&zBj zN8G}|o|TidXVee^^yUC%)zBA{{~qk@7M->1Qy*J=y>|}~U3j78@az}2PrB~A*y%Um2tcb zFH)VP>T@Cb`JL=NmX^L%@quhM#7P=8P(W944f(vG_AJ$+sVfhi^G+Xkv!JX ztO!SYEd}sGX?cJ)^dN95z?uW8W$rvHz+R#`l2>^clZY@A4lH6kAC}IaeWKliQpo>v zw1i*(m;U`nv~C6Nce^fP+LPivSo;-hv-9MPsr6U2XZ#D#@HE{~Z{ORra=LIT$ zAcfBP;;Uf&^k-yb9jm+9J1te3#7whE9HOF zw8oL_o7U;WBtb~G`n@rY> zpyJg;-MY@J2nhW(Q`C?OJ-N1KA5Tt?i)-ZBf?ap?4vnQ*0K0>EH-`{M4SK*(&Q7R~ z&Aa#0*%8{#fHtD~nr&ts+)ug)H-#rO-)sZ~P~kWhZzc`}7kG1)e`rsdQA9~E8T}a# zNd&6Dix+LG`My|U&4W1neo|fBmq>{Dk)}}N+m<>^WwUaEj;h5A*`#5|)foM*1wTO| z99&w5d;j5*I0e;F!=Ok5VSSRKCM}-=N`^C*N1r84(-V>H?4x#2EEuLQQlUXR)zwONjn{_576W)830mJW<%NK!_L&Gdt$#Y;46U>E@)ML?riurotYV&@ z2#u3>FVo~05-Nha<(S^9bmyL(WN1XOSxR2il%OqhdU{N>xm`1=B)wr>HVN#_v$+za zJs=l1LHbO9q`SNC>vs|!& zNqWA>J4m9rET{x{7IS1?bqqS1mU3Vn14N6`boGdOLXS5F@IN?9UQ7&s2K0aaltH`P z0KFAuID@<-#gs~7MDFCy(9H$h&OeWa#;$*kenDIPI(ksa+& z>GaJW>JskE*hMWaM!``U(?m!uU)tKRFD>Rgn}tG!yL{n>E$Xop=NoaMQii>-!CPkl zvmC;25r6Ru2kpL5>9&W*O8;ig`*lx}G%d1SCv0^i77A0HF!7o4sDd|C3@n*&4AWcl zs`9g=C1i2N2-^YjhS20H3%OP8nuaa2u;Td+2P%Pe=EG_YfPgAuKQknb0DAiV{HMh( z;Fa$Ah$~#O=2~|eoK{j2x5?5?9kZl}RNxDez)e95rs0FSUb$oj9YoKa|0~cz)CBz7 z!5u0yCDVoE6vzcl33OmpRqM*Q1$WgE{#&~I;qKY(4|mI>{umksoE&Jvh%=%V%i|QN&#w9Gu=3F}irue*M@whCFQ>+kQ&Hs{ zmQyxO1l&F@Ic_d{WiY}ON6}a8B+y6L$C|tM$L;vEvy1H&n*2gT^i0ncIfmxdOk9!T zp`KqIv%`aTIyw&UCQ+Wd)|*(mD>kkB2PlcP=M3zE9LcCR@3r*kR+i%A(WzP~-74ZN zOY5NI_3%jL6sb!aIY$I|;j;?gHlqySa@-FVP&)R>#x0L$Og`nN_l?Tlu-=0pTmU*5 z9A=rG?ANARGCW;ZD-F2dSaZphJjw4QJ~ba$Yj?c zD*)JH-amBRFAN6$qDWxsMvW=Y zMs?F^HVMF2`IYCRuDkyF^=@&GE8d}#&hqj<{a04AN+cVKQsqzsVR!Fue{JA*1>D7T zZx_tgSlk=V_mR2v(uzBbmIQ0dSwu9_>Ry_CHf;@8BEe_DkHVaYeOZ!`#iPbrCnc4Z zNR)v70gKMkMCH*iiAQ`5&=Wvf6J>*zK8stA+Uq<8W?5=|@r z^NfA7?Xr}88}+n({Q+EGR(rjQ-NwQnl@O(*Vtj}aE&L=eL*7}w(ouJ=!Gi;n^b+0f zJ1Z>(I}vKx&h&8q7{1cLHiF!a%7Y8V{&-vxTlh@-dS$TbnvLWgQ*{&u+Ujgh;r0Gy zZH)g<)%0aOy}&(5~721F=v z#SN#Iga^%(;Qs5EFOH5p-~>y#o z)ChXEdvToFUG`{rA}vMR4S_ff*CAi&a6oJ*#RE#iF$XudzMUKx9*wCRt|g$z@$GE! z?+=SR)y$M?o-ajN#v~jM*BPPJCx=Z4s^KR?q1s;_#3bIsIdv_~dIprSUURpVw9%R& zn#f-~aPak@9!dqY1oPc3?DZUaw6zqduCDCzYKksDMe&~q?E*JHK6r-TeD;MD3o|#ipSow}gHzbQQMx75ux2a#^Pb@!b-(pKymmy4>qB`#pUyGbU&MtDc5r`! zuGuh<;BIzX!DCzban|6?Lr7ApWV_+ADuG021yUpmUj-W~gO}#C88pb(b+@e+YezG- z-&cXxC+b}@Ti!M^B*o{4jvz=6vZNjf<$gA&=njEa%$kHPn$&VGVt{&bn8gRvXnB^m zV}XtG#zV7oZwb`MZd|TcTamAKWO%O9PlVxMkU|&XGoq45b%F9G=v9F-MJ2ltu|!iH z9)mLT-N{f2_(_KBb&abxtXInpF>FR)t6BYqKQQXz31=+lwP>egg9!?jp2>Zl7Ut{` z#Y+#&>e^N~r;xqT6j-zCwAR#y5Tk!j7*8wESEu9ZZYI$3wa@35WkP9V7?%Yq8tb~dzk7_>8yj0kHf4rGHL7p$xZ@nSwZIdR z*$VvA>U+hg1&?C!CenTfmOR;h&1H|nrS%q(9I)zn=JM}Gbt$r))TWm{a(AoL)<%5% zVvZI7q}V9HsZBC+iWM8@AJOtKbp(aVTax3@)0OHEhR=Mg$VoJ^fDHI}H7czw@bOgAU+-PW)9@jXbAWA8ZXXKwk zj;0TBN>J=<7fGJzgv6@ez{Ttw2MWH9Ns2*wJ2dEp>M+rZ*olz9uJUr)@_o(91eR}ws3J6$2%It&v^o@iXNrE$`8^*#=Iyu%F#1BOE@+mRD{< zd_J=bMLP89p=c8#>R6k|n;n1Jn4z_E2$JdrVC{l4LizPo6f~*7<%V0-7cP?>Q^c}r zF@K*zDwDsmN=!dSQ1+2PZMyR>UzX6+9T3sWXV;fs^wT?YJjK}PkAHsp2Ood>muLFZ zr-eT?`{`f)?ds{1&E0P6!_ueESNG38!C{Zc0Ft3J}6(U&*$W2f{63c zlV~XgLsVSKNr|^OGpZLY=S2=$6vz!S%(~?z>xhq>x?q&IADX56Q{C}Zogbk5dRSDq zk9x!U5?{z()>22AY7T)~-gaP8yg;QSkO9|Y<3C~7H%Q^msQAfIU|8)m&T4ak!ydY98KfAgCK zdni}+)1>S-S^JOVK$j2Sa>~Jbt(*!ySmv_<-T`?F_U~lDVs5P~7@%<0?Kis*ug3TD3Ux0Udy<-8?46m36sc*V3=lAEfQ?_*DJhE@5O325- z9t|jkepH(xAwg+fDv0v46GHq_?LcVHaWV@H6h5BpfVr4aQQ*XLJu;}~uX~xF`K)k; zphTB)Rs@$&E_l$Kh98SrPE*VhaZ56`+!o-Ofa!rZ@~{D&Mf+eDn}C}l)yJBiYaAio zEEe8Donilu>bSIKXt`*!xh->We0(w&EKTm=YA=%sz-O~aQMnehRifSGmiokA!9pb{ zuEN?S_!m{7y+HK+{q4o4hx-L8!GqOi&BYma-xGR z5C{lSMIEmB#vw8#7IBEabcfUb4;Wow=2?|@wBg)4s9ZcoQCAIGNz+x;nx*#=S%?}~ z5(zE3S;n4JE0VS>ps3;v3$;nFQMXQ3a*_<%wn2KhdE0hGa) zGK^m?A)kNz4F607%?j4n=(mb8JfJ88iWB{D1!mqD|4sgn%Q6hp2I}xv=+&fM3I$h{ zBK?~{>(JoquR=)o&1NdOa17x*z+*D{6ZGAC)R;QGzR6@QoCp;-By)I@vYn0Al5=R5 zh{<^U_SW_mGHd)tMTn|xQ_j#=jqA|JFyM3+voKXCSA_Cuc4NpWmcr13?*Z{y^0r(# z%X{0h9A`jM=O-+P<}s3RDq$3ugDVyQ&m<&-`%j@yF`bz&o*d`)fp0)~(AD0l;29Ff zi#Y#k{d(hi|H5eBdyg!j>ai_TcfFfR)p604>3X5ccKB)4wvouO@(c0l{2vtsiL%I% zW2eXR83!imL8CC@PDNo<>kLD)bcsifYgs!XW>v14TregjN>TMP`#5cp{-#A3vh|WD z4L62du>z?r3svS3w%F#GV-d8aCMNS)%v=pYeTs@Wrk9dyTTI*(1xuE6yQUh{7gAc| z+5AnO*2qUd=6}z|DFHoQ2Yll8J{(Y?>CEaxHe&EckiNaDl)V(Uzq$0n`Gb-xPH7&? z4F_QrQZDGUI9=sAg`svFo9i}pODdSD4i*=|+D`zZiIv~-M zuqFW-03~W>XMg*5<3>bgWL6d^jpdO|ba!P|RX!r){c!eHn>dvE5LLt}WcU`3EQsB) z1Y1RMA?pVZf19~PyYtWnip$@YjvEz_JI^iU8*CaDg%mx^u^YjJ2(RRM!Cd$g^wxHt zbh{%|VpGrwG#L2}TuB+W@b2>c{Sry3#Eve+R8j={_vn?uHrt*c{e@IUC3@9$=4J?b zXWDi1lc?tOSWnJO*WLLT<92Y^?cnvAc@rm`kkDqAEO9*XNl}o}9_D0l<%&PnlP`+k z=pxm#9o&d?j13?|i`#_>DnCSIqWnuhCcN+@l+l^anSbGhNSpEu zXn>kL0r?@sEfy#}VdFv(5uKg%{ia_saV#bK3rZ~HevqNsFBx^zeDCINkFl9C8c3x z4UvPF?pjc^K;?aM$K*uR<@pjvU~t|E4A++-=6TIn$0zy0KqpRM0Kw~&(X;h! zZJ)AO;%RL_AdC5Zai@HnpC#8~LlOg>0x&1_dhI)6piQKK>ee@J-+#J7YPSsW2oUhA zYGQjVA!93jZp9&FzN!xT|Fr@CDT@s1W63#`FmO4A6NpTuh1Xnh^Q!tT;)LV$$xW8< zzHpVXnx`ejYW3hki$7@+7Fw!pwO#G+Ea1=;M72Slo5Ko{uJ2*3$MMMs_?Wv_SwAyp zi7oMrkda46EL@4HyT!@H6_V52n;W<_0!%a(Nsq6FckN#6t4S7ylM%AAhMkau?pQsV ztRahdbf`JQG_u2qDS%Ly zw6!{VwmRBg9X&^rG^2ft4rBBfqsth5#^^LguQ9rf(Qkr&6L}8$P0(+GeiQVYpx*@j zCg?XozjgFmM?Z+)2}oS`R>v#|GeY$Ma(<+BEdj0Bg^-rH4p~xTJk))RD?+BCg~}ww zXg&ne!&zE^9q0(e$F1y8C5DkFFRoI=O-)L198P5gj^p3+>eiC+2Nlo(+J0FZ=RHj_NN?)hvw427PK( zI5h(qQ+ghqE-*Pc&=t4NtsUse2D#Skd%XcFvhQ7csaLV%&{i}71WI>1>8M5-5;%dJx1;~ z0HwHnk5MqHJ2=B18?|Sa?X`2@>c4^rj(udD5igdFz!;f*)Tgf^YS3vajp1{``~)R{ z3K&qAHxG8xRczRgHrpwqR2qW!Eo}h)nb(^(Yv`Bo|9r6)35@a5VpTh5XKoK>x-JIk z!VE3jbp|grXv$7@j%TBl6ps(Dq}#jh`sUqj_ka?2omAH9ZZ1D8;Z38*2BR?I2yX9v_+Wc2-N1H*8|VjLNuXWX8mBSqkq=@xJBpNelI?Op%1E#eEyej;Nh3m(+zzEZh_8JZQ*@_ zDeiZfH08sogxNJR6_%OJ*G9AXpV$5}=tK3b9vgja6Uus-y@H-cUQQPbj-A{9VSx5q zdiTwfS#9U$>f+`uM-O8Qb8NvNjJbQ943i3NiHbmkwLk)$4JR8Ir^F15b8%&y?Z z^+Lg2fgRKs67N*^;;VQ_l4u&a!A1cS4NtL*BjZiVhR*QF8d(DF$cs9T(wPVCWYJJm z9?6`icqu;nqOMqaq54GI)+^jkR$k?A+)2s;DEWGM|9}XS_s}(8mYy%(!VKIN`erOO z>$i!IxtWu)Q$&=11A!0@+X5!|a;-1ag2j!;vJN=^g-+bS%vwO*P^L5>XHjWry^l*8 z-Sz#Q#Z*yj_IOU&&cl;>AhKz74~`7?4yonJ;;G>+U=0>sU$!D)AZRc)G#Or}d zQuJjF3T255%}|%9r7Tx6_V%;-Mcy2OHNy3=p~e`-A-V2Lo03?^%{b_n!(wKV3xw)P zS#YVY`btayD!E4sDY*sEMKg_SJcpwl0Ngn;z{sQ|!TZhKyU*SA4Y(E_4o%FTWL8;A zS8*f|QPCoqA5w>Me*Jc?wPLEufLJ0Srpbt7OL4SdT!frQ81;jP2~-1={dO--aY-ce zGB-|yOhk8a0`(3Wpsqa@dnxp-kuJg(ojqufFn#tKXca=ux}9$^0`p%2&*%@NGe~_n zdu3g*NpqyYt;Sn2TfXY$qPF`e5Q}3Y5{H~7$HwuK3gmnFK8TB(vM;ox3J+ZHhr3Tp ztPM#AWo^`wliVOBW%2VZKqDp+SUy95nQy2Z2$15Y&#*uvdIe42(EV_&xE->f+erRb#pCTHNSgxn5v?fP5iK#SGM~SVkK^`l;p0d*s$Hmkm{)~c zfiMwd^Sk?5>`3?BJ+ksGuheAVwXtt*ZXb}4W<}e)Z};xg&D)3T+na(8g#(bGAC{K# zND>DUEOTvE=%*wC0%_`u32m)6s*zXdsn8xIRw$y~_?!k>4x6|BMWJG_95RoS;h@ta zXvKwtD$hr=3(rRr-_alp<$L*%N4xT2j+6wm0Nuq1k*NB}F?(VZ1a1^fNwu~KONFhh z?W65J90@#Qke;$AuL1tx#YMab*n0!-E3t(QaDk}*B^V>_Vmf_n5}b(qhO@&X6kdAe z)qhz3=j+j!NE$r*fk;TpEk{QZTl1oPV$p3jzjSXts7Io0OqYW|>> zhAdLP40slk1|4jn)AP?X7NO(HHMY8OB;4bp+1}ad5eks;LzYjOBa2#rHpya<^0z7j zmIzbyUu;SmVEhxP#Hpv_mD_v#o|!-TU|q4MVk6;<3O1Pdb)&?ufhC5wpE-shi8)Gl zO0XCce!+FXZhBfJG#~Y!zy9~4S_a`S#Nf0q>F^*!!OybiO#@Lvw^%CGr65hD6>pIZ z=kSk_MFknb+iHy_7ke^&bT8F*-r7aBB$-jDF?2;m1WX|>&W;aJHs<7orqzdIClOvN zMD}@Q8d5F#L53-Xy^Ju)@6V24EI?X0g0p%ScXu1x*{iYOr7{HC?2V}qzcU{H1Gu1w_>Hf>p zQEGG#!k`JI-4JR}J9suLilfQSr1E9CM;WpvTVI4MBp_%*7IEy(e7*yA3!%U(m*VJ( z@RR%Nf0H!8_Vf26`)wDXdxe!C#>&YaNuin)8pYIs^OutQ6_zNbfKBMid2?B3^5!Md z=rt2vh>)0A3lFX?Z)|qq%~V!gKHPq|e%pP54eFug%JupNf`l-!&~B)OCT+mk&UdV7 zkmonRpA3ioN|=crGJE*mj=0K!bFO_#-~+q7xq@-Vs$0Nc;NA82@PZU3Aq`2;?6cK%&TA1Ci%+)2D&f`NO}U_!o2o_*7ToKH z?!)y>Mg*B%sr>Hyz$^c_%yPTH1!XRj zI(r3ukw`||AR9xgoo{b)fNB^9Q}g!i@?H!n$-I!Aq~L)Qg)AI`98C&~D^w67+#7mj z7-l32DZr-}+&9#Vqpb}hwc54c&f9#SWram%-!3a$s}Kk^T^N@sMqM#Bb2aj$hByKr1;v=c{TjTBf=b@xJl;?Z(<(XAfK$yb=`bm4^|tDBh= zODcp>8RBMhCg33*GSRZeiLtnzc>ZYWgd%Ce09xp$(DoC(&L(3x=HJ>`gj0unOGa!P z9x0G+Xi79#OwTxLVYMZOuIKBTFS<(re4s_!z34<1*M(rcS_zR$k%hOTRGXlw;V{>$Aug_Po*uWNvLf)`{H*Zn(cV( zWx3R={&?qPnw5m&abMx<`C@U1Sv&0oZp#@K(>uHqhlfJ0OgS`0T@lC*pC!%w&&j0= zxcT-Id>p%vcgwfe_fRYn5Vo@+-L?UIOlBz+VW#(K@E~;I@XWZr7ne!*bxX8>bhwe6 z&+sy_t3I`Qf8 z-N*_ww|d(U=>Sh3g;5?fFf1k)T=Hb6-O`lzy299C3Ugbe9xf^4nWGivnzC0-h>(&y zL>@r)zT}xNeHlu7ks@D_Q~MqtLi9;kL| z5Wd}ABGgV{XSUeYk|_&`bKM)QePY5|0007btXS&1cco%C>F9?u6eq=Ll)f2bbdIWx zWMY1+ms_u;@nMX6hB;#L=`Z zE!JT+6>Qh&zSqnF@oMI|um<@+yaIcVN{(AAKgwpK{*$C&6rODjzDa_qx1kgNZ+{rA zJsth8k#^ojM|At+AJGPN`S$34YAR88c{*fFkd-W*@1Kgx0zep135lVnBx(j|jfNncwVC9N ztu^P8pKb1I&77wsz=j|Wc8=hJkrMtvnR5=?q5Is8pQLmH(fcH_I!UxbrG|1bhkCVu zCVJuq02d70gmHjB>!E$ue>4|XpQ($a^s)iNU>o>cLrG0X9VFmfyAOFHni9f zSo#zs$QlE~TEGWuK)Dpe9&kQ^lSTzpV8o$PE{}1r(|};Oa$4U)DQ$ogs;Fe9-@2IY zrJB!ZlJ?LN zKZJ1G;orjHOZ`Zk_K-y6KD>`IC{d&wt6XiU1kj;gd_+d-RRz+VXj-_R!fBF%AaS;O z!`ti1@rHK3DD1Nic5!iZUODtqOXeuwcd!H($HGJ+Man%AO{=8?Q{2vr8T^q!QO~Ax zN&kL<=Vk%!{Vfvzl~P<^jVnQt0-ndl}u}1wrJvG zxCS7*l19TmDo`&Db0YBe%6OQQ45u8Q571CB9BhIsFc#=Yr6*%0Qn;FCyq_0}CPx#S zJqKmJB&$I|B@2Dbi+?>j>M=1*_FXeJE|xzoZ+>~o#%G$ckuoz5VUbLfTHBsJ_0g`n zA&}Bisfs#-3D}~H1s5Q+js#1%Y4_pdg-|DuBCW<{Q#kr!k`}^{-}l-PCOh~X=hK`o zCekY~3({LMZ}N(g124@Ffl7fy`I+PpwHRnX&+-Bntt*&~V3|lMB)j+^EX3>;svQe_ zav>ghMgqWOzi~bVC$ycQ7c57iuurTa-{-}};qz8*-C#HFo)ML#QIYs^i1qO2T_V#B zqe)2!iG$S(1f#PY&hl+M+5Y6)EwISYL_;l-&!<`g8t@=-bCKYXhWBatFI`BXxw>O* zK526c3Pfd0XPfHe=8mQwh+9^3ekb>nf z^qi^s?L0v|8BhvJl}P&_bZ{9TVfmY&EGI~@zPQ&SU3or1hEijA$;VH3$gYYc+cOlZ zBMz3CQL$akEv3Da$>r)jHLe)!#+l}5EVx3s(Yy>nJODG<7qz%{3x!XT6W8+6Le@NVv9};E6Ps zKoOgQ#rykIh4oLn#q3lJxKU;fW3 zJPAZ&(sgX;L{&205`BnWIX*mroZgN8h(gzL0}EDYj}^JMj014v&~}nujVloD794+2 zeGZ1!Ea9e!48{wQ8G>l$X4a$C*pi78P~wVdBU|S-qHRMO9OM7gD?pbmubXt*R%E>Non*PfX1#YHUkIo=?z zrSa%ge8B^k2-Tly-n-;5iVl3FEXa%bDg5IkUn6R~B&Seud)-?3?V}1LiJ|z+iZgV# zvoChDVZT{8&bFNcnP?1}4B>cZ-<|=^&ea{zBJ zhuM@6qi`1kqwXEU7=mgTP_Tw*XkZQ0e?r>RSBQy#1_|zoHWqSWF!eMfk$iQ)=ukqE zo>Ac|RGuY^;a^c!XX7)B_R6`5tKjX;BN8KIaxTw0iJLIU%H=Gk3;o6rs{P!oRVYEc z?<9^v$o7ZZtL42fUzM0-O%j(>J-L2VP8N{}%-Ucgp}HL86Cata#zwYHu)H9?DD_tYG#%%EhPSRiI|F?)4-BH6yU8qg_4Qj>ABSr4?p zZ0zD9U#Y?kJKq#R^F`|Ei6N11tc5};_lmERw{sOUD+oi1lv^}is5@=cuE%V%GeQ{11Jb+{*?Xk9~xT1^eV4VG;9a*w13*cYAC0h z-R*dRkO_XBuDpFZdMmCI!l9{5PnxQdUpVKy@I!HSw7_+=a~fU931 z0!SP}uyQLf5r6fEyRSz0Qf$5Nt1HUjRRW2~1s7-iHw(oeG&K)*?{Mt?lO>(>=0}P; zA!8*(^}QJwbCe1Pyr2}+$`!pyo^E{ed}D2Eb8BOBdjrYow>CGHsF%7yF;4E1HH9pw zWNUrRs&2?K;A8-LvrRC>-cvxRQe-3@zkzlnr+Ume#49fLC$Qd#LvWT{*(|raTBLTFZbh;_+*SYD+YT+%1#`XMo=WVVbj1&&klAvB(1%EFU7u8ch-~_)j(8= zi++{`KRqk;p?SBnLz5{PE6f3xfoz7>)ge#_VnJ?Z)%fHd;wnC&uQ+1I z=ktrRUTRt3<=K<~3+lHAr>Ji9EG?#>P>6y(xA5S^7;`5(kP1JAK<(n}PCPAq8EG9u z=H&2BJiZrLvdA7^35t)o;>iMuqFLA?M)@_<2)&$as!hXW5T}taYZ(X-QxLdmx)#+7 z_4g;I2+0(O)#_9>I&cKKN&M%U-szdqJO)p;m8ig_LY#;pH0du2kTQ|fMG;Y77pbRBO01LM&M&%s#IrZe412XCe;TDyTZZ7weX@z40 zSrcKQ3yWND!t3V76CozxI{15@y@Ypc;ceY-C&;R#W4a8JbKp7Pg*x5uY*;T_xb_(> z6-R5hC&&OYb=3>dY@i<7bb5gUAi&+BWM9V`oZ?U0>7UE*SN?i^bG0hJo>nOi68<$! zQ3QHNz}lp_#K3cb8xp@)zS-Y8-eNNR1voPtR6#84hYc-`S$-QyfAeL|*cg%4Oixnk zwHLwx6|P@>M41BDg+kRGUz*Tc?dv+wiF@2G*j<80yAN-7ibysGu_>~iD?G}>GzjJv zcGF;^I5^)U_$QPj80f?y$XVqPp-x7Aohhc0qh&@@REnUSK>qq|$N~-?TmKV=BEH3? zukqSrV~M>u7@{~T9iY}geXZl}CF(y3nV|NoHUNolq8B=)>5#)`!~|y(b*=-z2X7~5 zp5yp^DRZ+*Mv0o5ZZe_?u44AEXj)3J@}d&EFeyfAgAKwDBsXp2_Qxm#)Ye&(+pWkDkQVkWk<3oS`J@h zUhERzEWT<`TN%0!?}NMr7GQq{$6CZ@9_-BbUPJHIO&8E6ilO^N^eI9~_C4R>^t&m7 zWF!R~28}vkefm$0tPx&Se34BOfuCZZ^Y+m9q1H+X4v+QZOgbb-{Ba46^yd3iVZ()4 zSv1L;0(r3FAPSb#R4Hx2I31BY^AvR%~P=a-Px`N@FR?` z<-Wwzlkg*YacP`h$ohj1FnybDed0pSrfO>N)MhZ)*X$+LECN>X^S9>i5BBD|B)K391tDj;N1(O?P@f_GwBQbtt{Md#eC^`V za3L%=JC6tkAKrh?37pt)%qN%K= znB||U1}N9x=*wUyF1s>Ci|-Z?s}Nh)OIDA2(8H8fBNs!Kswm`8uVc}u z>!&s=nOQD*6jv>?bV${ic zhT&E?5&eeDbsdTpaxaPWNv?^OT9Jlg{d}==crv%Y34NI6uWHu4%03VnoeSPNa9YO` zLXibdiU!DV)f%TjKIvzi7qCFU{{)p(wy;({)vX*|UiiuO7fmq}PyZhGMrwG6M7A}X zTr}~O9EVz$!H#B@GpmDTg4Fif`Wk|0bkI3~lX8{~d#ibkZSibx`?v)PB0|r&AL$Mu zJC6!hR1MqK4@19RPZCwN%P~3z;T|2JnImL4+5N}s58Yakk6S3sFRr*l?XE?=NX>>O!D7!Crf{p`NG;O$TCO;PIFi4sy{sk`9)GNZhZr>KK}8U1TFd z>!?Naw5i(K$Nc?dxX`jzgQ?~t9XOumE9-+Y(1{Nal}wVGR7qSeL!*P=XlijrH2~&h z305Y7xW5qpr;u(*w^i8Oe9;)EVb7-V7F?T1byUp`S)_Zle+CshoH$R9b7kT#(jE!fFr#etUiO6uFY0e*MSp-TTY$???aqhtdCoLo_N`&;PG{ zobJQ%wypM4i{wd=lu~OS~_) zYd1cVEOpva=YdKFtB`g*A!3klBR^1P4|O|aBum-dipZ&U?DM%>BL^8l(wUcMnJ-s1 zkf{9O`or?B!sXP1BiCC>yxV8LwOZUk#aJ2y3S0(Pj|`XjqMcZRu=47;7k^iN2Gf)h zxnd=f04A)E_-i!+%s^lyo%hZHsor43L2CR*(s^IrBhTK4W%uUOyOLM)b$qVa$m&qa z`8_>3`fju`GN-QlU-bv^3{}vh$YGo}3{yJ#)T@hAYJ!Fj9%jUYmOn_f3_L-yY z1F9I9Qsq|?QH%y90uE5pYJqit-Pua@DLzq#GbsrqTZpI=_$l~CMFTM0Scgyc3+Yd5-FYi_)^1z9`AVZ~ZkPn#1g+w+TUl8I*=>r)s+GsY~yyU$uU##q> zPx5Y#LxLm%Y6J8h$KglX`H@K(aB{O;UUgmf<7M~$8irpWU@^SWfl$h0G->F@7fPXo z_E0q$Zi|OVD&ZzRFqlx-BfxHUgfy-fi`$SiP$@RO_M`^%rZPxq-g zW`5#wxPwS_m_;a|`DwNX)o)2C%Qh}!We68fq+J+v!DFM&A1X#L7ufmEivxf?N_olA z@P|6g4Rz!5E5*$G+w)Vu-}v}ek1(`44_Z&VLPfMjOG#xDu{y}{qZ8$j2N(~usOg}1 z7c%T0YT@*W4fNvR6xGt@qfY!$G-+oE50x=V0aVal#TDhvE{-L03RfMjilD7YRe zh-jrF8Bw;;i;m(=qZRsSQOiwTIDw4NQ2)GJE}8a(gDAi~nJCQ(Fw((f{-DvM_Gko4 z!9gfOjQ3@oZZG=OSIZ_->si1m=+YfaQC?X@{gM!?YJ$#ArRZUIegMjFaC)@Yy_z8W zKs}k1!*xS1r+$kGW8S1t&fl99+H?e0$SvxJYThvQu`1DDs%)B zTNWx4{gpLPL|q0h#-(%;x}9x!dRoD#xfD|+y4wP8iWy|#e(XM2!>(FmPk0 z%mfaLbn%^hGl84cv-R!GZG24D#^Tw#w!ObT`CNks+yH_^==-jyJtn~M7KMoHj-u1d z#D}E#yIL>OTB~OPaVt44-_bS!x>O%ssY+I(W+SOaue%i6FS7*#Ff}7}U=R`ZclTij zQ`dz&U3QUqOpE>t{nr zSt+oVy|+#<&)(2Z1tEP(6U7j;q3hg^b_I!#tStJ7-v zbFdqit$T5Fx=SWXESBB(?{D9LW4ivpK=F?ccTXf;V^N?i@|?0%V)3ZvR|-MOz?2JF zYSuw(5MPDA7?LxUS(zx)b3v+p5qkkKo*rGWrW^u8yKOp`6Z;c2na;nb=cpRF9D#q+ zGwKdgB_IxV(}yHYvJ5?Gk~z5$6_wab75v#uA}0yQ0k9qmY0f{$oTxBP|7%dszSB&; zSR*LtXd;GkHC;C@Ni(!`V~Mt0j3p}tz6+ngEBp$5dQ<3=rFz^$A@$$_iNky~`*M97 z9e5+jKmHtoRhvKyBVHTA#>sE6IwgZPpZ_<2m4?NsR=btcO_5Cl}mNXarau1TjSVQr#g^(rNnFB z;538G^Te7$y+(6o8)u&|x*S6xt{A8J;Hk8istE{qS~US~9gzo_f0a7HMg`oI$WYw! z62BF%{!(V2#P_&~j--T260N&C(Ym_E@g9)T@yumW&v4Fw*w!2{{SzJhZH3_t_4H6t$Z(0eJuxy zxhG4Bv(J}rA6{L;ctl&I$kL|vQpajgxxUS1sF#8#9TO8B3#p+asSS9k2?mPJj1gz& ztRCAgh<4De;H*&P)*T=G>j;`_&YvTZHHEsUT%OEtiqV;dux>nFv2g#7mv@&RRyFLb zl$usYzp&mF9}&Z0!h6#yWPNZa&>Z1$K9Nb7h`VxlYd}Z@SyPg8UZaqK1{fO^It zYu(jf8sC6tXw;6oq;RG3!4P~C!c5AI80^Mj3%&1|K52-8Xjk~3J4upxiyT_MrEcZJ ztKc|KXrIwygn9A3r_QTi2$rD;*!Bl#8!<0o2M_I}>J+RXczH$TbUGK1N3AX~Frq)G)=M}f z=622-@q&AYaGnB>Dxs|5r^K;F0_ReCZT>RfbhxaWL)O^Tah#;+kf#UFrONSWdhqAx zfB6RAf*MNjAIhQq_6b!`K{A@fRpmo zoT)*J&fb+4Ag{C} zNhD-aN0_2HEXJ;`KIMbWI3^2Fq#R-rMJ*eNTt9^=+Mxl?7)&{~550Tcg)ksa6S**h z!;nTGYYZl;r1;WLHlp`gPv=I9PGUwC)B6+6fLgd`Y7Y*hu8~$N7W(bH4V!}ZnX)`p zlz4Oql|d|wL7wDk-z|_NUgEwpibQskKrg$YP!>{NdZt66dnQoj$f5B%h#P90Ctgob z6(E#3C}n zqD38m$i>XVE(rq`uVl*HQyV~|nIkY1CZ5x3eaJYCzv)_&PN?}2a)|UF^8BQdoYY~t z3>zqV@Rhz6c19q;g(Qf`Zv$$MSz+Ga=#R)i>$zNba`Y0}M_lf-x`)aYi^&pt%=6yz z-Q}nE2zw^&;pl`KQT-7{hIA%(9~j$=ygF2G-i-JSlC1vJWNWQF#*@u0$#JP0mDSb4 z%$mDfdH#aZIP~G_QMA+3aCo9sku=lcC^K+nLoHgAQG`s4z!bp=eISZ6u!$0>e1iO` z7>Iqbu^P^Lv@3@kjxzXa*Yj1hCvKkWN}f51A+r%)m+$&&^e>OO-ASQ3mv^{2 z3IwJ{QOyG(0f?-6xv+-g6fU9;^Y3LMgSa8SarnhkPMM7~qz&t8+VVWJ$AYeZ>U8p zo#>KRk;O>FS2@R`O-Oq$ZfEoTgV**K0B9KQ@Ep;VXx+V7s3rxz+$EnzNy@PYJgeP} zEjdOiR?K9}=!sT}%Wn!W;yi`m#0IM&my`3nkQ7W+2CQW;5*uyr0X{zqL&yS&Ain_v zK3tuVuM+>4^fKB1MLEKL`a9yApy!8JQ2xrZ4E%|_pJ9_N#Fo&r2Uarcl4&4_wi9zx z-d@fxXDY!EvP-r+0EiFxDJ%3+c$1d=C=XBIhvwgcj`NRMWuuKRG@1qX35>B~XCAey zaORcKGnnYYu0^Gn<4|_RNM^E~(nL~rjL@DA7HreQz5GnTQLMsVLg#vlR&_Ot7+T`wE!+u6i%pBBA^bbjAq`sySx1PNL*&k>uio1-5XFiLNHROFtPL?o{d~+S{l=rNP3BCAo7UpF*M+5U3K7G6(=}7haCe~LBBLYmy!bv8Iry| z5pg|05qcb7vE|9`eh1-YS0b~KcX|Y$_l$QyAIp6xnu-AlQW~-kX&bxaES`u5^v$w)WlYEkMO)mC-K%~Wc+L;vqIfkBTB+zx&Z}_L`Io81kerACxDk)nmM9^D>GbQhH z-o+?a3H2NGY2tD+E7LyC|3RmJFK+HXqA>QmYnHSwGC^^DRDA~86ox@R!oOWD-xh9! zDtBg2LD6ABsfMr~SQ-J#NLo35uBuqwyI|sAH8t0LjC#$qfS6`PAy9`^3wdG0WW`PKU(AeZupNFveq1U@o~|HO7Pte|lRRCKex)rj@jty3 zu3VCq5^=l*yS}Iqsp0`@mgD!Q3t+78!nc>miYoAdb)zOsc>jWgCiDdjfCFd?eseQr zi$syk2TYU`B{oUOnaJJjROrPMQi}g(U01D2g}`+q#p@3vg=*9AFtu=g`hZ~InS*7;| zfX1X<&8RNkothYZwAJ zq%cQO8M3nn-!?4A{^7T%R(fW>qFRpxQLK`jo-U!VW<{4!S*HP2L6s9UcCTyuX^a33($(Qj62LcbB`JOL~R#{ zmh;z?EXN4YODfU4oz+CtvKLFd9dk zxu8%`W+3bo{0lZIBO{;=Ng+|;E0QiFehg47PkOJpw^Ai+uQq|P#b{C;G=`18+ee}Pyfr*t3D&T>Be)8<000P?P@Th+-z zT(6KHn}&eJ9wG|%kmCaX;VhD5H$Vi^L}3*{WsehZ)`w?6_74;D-bgEZ@JC{8}YwVKP1R?QvDc90sp-n=B9b7FaA82V zLlq3n^)Vl}o`yT{Jjw?<&yyc)oQC+FY`gmFD(EBfMQ2k)8g$D5*H+(>&(`k69MQ1^ zYGtRnNT&D~Ilu}}m*)e{^7EY+$2*w*9+>hIWZVAsT&i2RGk%U&8O*aKa4UPXA#e9X zKigS+w}M?BMukeStls_C%}R zK^XFz=(T~|ULISn5@NC#%El&nnAlo)68EU`Pl6m8^)2K!OM#vMs9TxFW0`+ zgKKmSB6SFlyj=t-AWQAJg0j36CoxgS4&Owz0*VR&zFNj|34q`7L6jkvPBxpNH9cO| zHWS}+r1d;;rVfMFp4{Gmgxyqnu6iE)vaqJRgg8Ezl8Ree9#vKu!mFP!Xvxwq-bE|DggHyV>+ zNHm%`lT+DI4P^?I|LAZ3j8$5aSQ?^~>3JK2Kqmj8p1$;bd`=g(7!SE_);C zVGXa=NX6L`r+$@}22VbO^?vazb4g}QPdfARlzirs=Y!*j?(+Rlmp|V_KZY8A66&sB zz&}+LI`K31gcqNQPCS4}i*N*6FZ1UW;)qX1<_c17$_>JF;AUiy9{t+We}{g$VqR6P zJOKVCTQ+1WDY0q4y$^?p!-k7ULiG_E9XW>fC+hsM(e08=sUT2i20I>sb3*H!fuFb> zsTv}~qxuF2pe;g~M^B80Vjn90F*Z)(<{$^sm7<*LKqt;O<08}$o*nHSor1YbbM0}v zWjuhuo&uh_LV(~(R7ykze)MeOWxa)lGJ^8yRE*&@2NpQZwG(3P-M9*aRB{j#$2v!fijx!m)%F$MXv89m88#3k;fw) zIZy?}$N5flGziS9IN1s{Ahk9M(*}dxI7`{j!DKp13mj)V;6ekZTV&A`uS4XP;Ml_< zR>%mMDuIh=!0q+X;;KD_kcgo?Nx`uZuLu_RTdyg!Ka+Yn;44>xY&2IbE z6$0Y8M-2=hfuQC1=(U9FG3SztTheI8Zt4L2FeP$UGp^AK zPuzbSNvM%Iv4nTpWNPkkTe+-ODd8Lq#F^Kw;F2!xiD&S;-p<| zkIHGX=Bh#(wh59%;*VqlTc4NN!Jv?kCurQX-~-Az%7B`vL)6DXo$jNSS1OsTo(^{j6pautiqv1L7Q%)JQ=2;6eiPhCpt>9 z?HlUFJ$yw~{`zF)N2xV{Y6D2Jj>#}(z=ee4kGGya9sRdIjMkox{?|x*kGI(GkAIZL z(eLng^gnXbkC2#BngLaY$eb2yda(qpM>=wNibpm59rA99kwTng7~(0T>$meZF1hk% zd#A_?R=%`uh8sfjK8IuiJE8pd|x9>d;xnz4*j?D!`NEMFrsf&t>#fDp_%Q2Q_( zWTB_ke4AGYDR3mBf#H^X3)j&>D=7BxF{6DvMVyPk! z!UaG~NW^jr=&f8x8}fITl?81poZZWqOP6W5xJ2IF&GM(DNJxTYHY)4M09c5OS?nXv z5nottzVCBx=WV1`ibGt{sAYu(*z6#+`{?aa&6C9I&19u2R8x-Ia`S-hvQ9(ivz4)f zwK7ErDir}oWH05jo#{&!`?SyLfy`k7FJkGJfnUIkr=vu$N{fN2Wn<6mZ=hm8TI$+` z2)rW{1Ks`kVTmYbJ!7^~!o$V3yfc~oZQ7S&bJ`c(78L`$j8mk8zARW1ge;WXUAFO& z)YBdApCVzwInE`nNmxQ_a}C+5k3PWre2#1-@X9M0)}lcN-Gqj9bw+HRPi;~G0jGq4 zzh}tdX~e2O-VMbc zgsn23!6l>FzL50!^4*vMQj%f>e3I1)xKPh)_miZMhVNNt)fE%tmeN0~SwnfZ_ChK3 zY%Ua=o)i`qxFMV#y-KbkvRI-eCdS&cBW*z8`juWgw~eSFr~}qQ)V~-hjMBP$07V0V zo|20vnyZwVvUz^H5NcJ5>%G*K9W8b{5aCN9!ua#g>SX}03^7n~T#4m`m8&%RRl|PX zWy1gy#09Yjy8_bLcW&bN#6E5+X8Yg(m1Ru=J3tIBk!;b7y?(+39-wJ90ezjf1s8+l ztd+hjkSlO`3*00-7ISE~HXg}FOEeWbC20@BQN>dZOCdeY>7!jSVNEqy%FpGP+Qtdf zipBxEaYCCk8b;8qWI6UvOk~gjIjOya&b#*3%on)QdgG4&0r$q8j>BjOb z4-B}(uLGUZa@q0MoQ(Q+b3B3kO`;Wa%J$eCQ5f3zG-dS?zYm@SS<@Evev51RRq6V* zo5B^1IwgiqKo+fhyz>Qed`l5qUu=ip)RKEph=J>|Y`{(`Yy&{PR8TwVga z9vk(Zto5-_RAM5@y_3{Sprn?_zJS9<<-ZE zya6H=@(F zbu;LgxE|c$gnVSiI(yNsRwFHXnQo`;#h;Ku=*59>+2Di)MbrtD5-%{G^OftnzjZgC zKD=4Nu~PC%d@F8RLp^GC=UMd1z|FSTlB9cfJPL6tAx-E8d{MD!bb>0B*#-#8zz6&$ zI9c%9j;QlXKppDE>B9Se^gFM6K%oc(u{=Pss1e@8w1rLXcN$RLQOd%(qTZrqMdok~ z(YD$-P~riV&t2xLHa}A@fsrAzYl$kVN7PfL=pX>SOeqVx`*+vxmp8XsX;Dio;=)ra zv1K^l9d#txY~iS6(<}%?zcXi?fZ0}GXl=RZv?e5ytYZ^RHAju_t z^SANDiu<~Xsp2#$$OtD+L91#{9HopTIZ}iK5z00SofQUkF@rxcK@0{QB)*JJgIk+(qW*lVivxIEe5Xtd^k?c3r;$E*ff}opVjl zD7Q|Y_wL0>Dingqu6GAA_U`@lUzaQIK1dkl>bvW^`v-)N11lWtLaZe&l@@iiuH=j4sJT6z$KDb(yB zZcvhsBvTEG@JS?RezoTfGi!V0K5MM8nAWMfMwo9*0vwAQ~{JPv#g~k%pyMOJA{ta zA(V?uoi5(irLvprQg+kWMs`U%Rp9Le38tG$XZd`7#57vqGfJfEhdp*sFx-J59C{%n zI?2T_Qqn(-X#a;5!}yhX03VO9ham&dD%orBsWleB$Py0YiI#b3$WSv3L+O4g2 z`*sYPYr8JzMRzSG(tEl4i7z(P4}pV{{p#&lsJ?=ru;SG5Sr=Zz9h@ zzX|$H&~Ji%6ZD&)-vs?8=(mo3>*%*GFGRm}^jk;2b@W?DzjgFmN52j9+d#h!^xKe; zq2C7jZJ^%<`fZ@!2KsHH-zNHPqTeR^ZKB^M`fZ}$Ci-on-zNHPq2CtzZB15BR_Swf zVtK5+(zjUTB^|7&QL*=u>6^R8(+sOBE?_OS{MyRbYu}E~o*lgO&)Vo_o<&Ivg;*s) z4jN3UF#t!b28U7xr)LN|Lf*7*ACkLJ5XjC&3CTYt$x8(b*NRL-0(>ai1%E!c-9ux= zzxa+T{0fdO$EV3*s1x3Z`Xxeq67ZRD9ll&ZgTm)+d31O}x32Tj(5sBx7k&JbN`KppeN4%0@RTShjj_U0VvlmS!qFTo+JW3roOGY7{}hw#w*~ z*X2vrYYB4}&NHr!vz6e?D%PzMkh1T7IjX>c%D7-it5Zi8;|^NN>V4A9`C%R=;oJL~ z(Ug;6dUi+$SzQ5WNEir?s}eG3AF?)(*;3jKj_?pHNy*^!+qrYjyvkiTa4?#2tyK_8 zW>^{PiRhZ5CH4!{cdLXhnL5#pEr3j0UnSesKDD)fMBG#dcvRvGBQ6ej5~{4v@a+Y- z_>(tli%aW%bmBU;UknwP2;8k3(PThYi`(^UeTxV%lB=FC3{)aUcbokxYW0d{&yG7} z@2&tg91vlui@I(ZTPNFf>Aseoe`M&Y+LD9A{o>ua?TKA--D-LI;_A7S`8>(jc#b%V z4RqpCl`20v^OtWZHZPF{OW_!woG=MDK}{-po|98ib?gM}4Ikt#9F-Usa407s4d6u5 z4xl27RD4yA!}HEg)OA0tlql8mGGcAD0Nz|Y=!9a-s%({D$iG3>s^!hYH^|z32c38- zfY%>j+j3scQxSzGreJlf>37qBswR_*tgRzPLRr=qGXznCDc6CUKgnzO3jn2lcQ%8H zC&fiVMPy9BX#YUd+^tvDlyOfSB%alrz(6N1KKT$M)splTidky@QNpuCn~!wlg92%g z7zzRJ@&Szwyb(NEHqa}#PywZE40O5u067qDE}`We9}w+F^;|LpA7Tl< z?LG>@xyX-<5#9nu9)2kt0~HhuX!r8N)a_SbF%yel^ICO9s1Gc6Oju64YVsH4v0gvD_VuE$;2 zOBx5mp_X1K=Vi&#*e8}@;K38hVIWxob$Cwb!{KFk2T_$+it^50v)%+!4nnbjOAS2) zdT6@Qnb@)D%N2C_}+Hjgv6F@yDhjGg2O@)i`h5|-~ zAXUMs;@$G|N9LK%IxnaS%B?DzxJ}SJO(i=hv5Htuk#|e9#?bLLBAb)yxa`DHRzpHko5FyWb1R6MNf`mvQr$mX z-F|Ym2`Gh1@Yb4m8(*gg(u3V_$u?v$y%FsX{D%OEkSdV<=JoCcO2`UzHl};14<8&3 znI2XRgFSMw#}IVMoHJKYIp#(OK0ldOG~veb?cL8G9}taf&n*HjS$}r2Qq-Rfv6sKF ztT(E&m@>8Vve1_4n9m>ox@5uqq&y+vlFJODkmH`HFK+9gKCkiaKqoF*1zaFi5#Avb zir1gzcL>@Z!A6hkgHjK^CNRCyK#?o5KINXO!vd#C!%Cch6ZVa|tWv@&0$j<2*+W`& zvChbYW()xMu#;fkrRQAxe9~-ph+AR zKiY%X^?xwH{{8UU+Nn(w@E`(W;wCtpcEWWpvl(k-pb z-M_M!BG;m1@z?Csn(Q<|roc{u%rfY=v;uUy-<|EiJX2DZ3It({;xN>Sw!=LbnA#2{ z%ZYwFIy^sz2nDwCa0g7EhlV6MN7<}KP+4fNnmM?g#$lFiA)%h;0&-k z>sE+G3;ZcRXmQ%vKZj4`7(z!znqu-o z7RsI$f#)XD02Tck`eE{yHj~oLx`ZIl#DPkaXWhXW{s;uYtD@leT&acFy^`|Asf13P zCWkQO``(U9CiLC3z3iV@BT5o3Ow73xtF1gv1 z=QzAb8V;WDEXBxqIF#~uu|xm8N$nYY0^@rf0CIEmM^_Hdr7iBX`Ew@6d16DPwUGt& z03$`IwI0FKL6(pZ4xK#8wb6)UWKuXK{ZEzifrUkqHHb zg9-n>+5>o#QNkh2Dkjf>TyYiM;=6^16;kA(j7bUQ2>amjNgdRyAfqWnku$J6xqH_| z*QEkx6YMBpP_m>Z7T7hQ>gjD(8 zSLk$m_w!fL$j<$rpMqkRAd+KWa}PoXu}ILQN|K7i@|nO8x6g%Om>H_&EFBD`1d3dV zysc(*Rml>#fItPwvUC%1OyI50W7t8k&E%Ylj7BV{JKT?x&dLNio!R`76}nt6$hA_@ z&pvjS?{9x{nH)mx-tx_-@8N%PEa~E=0GBzG`0_kCKQKGW%Boq_@M9-#lmi- zD}iOO8z*DDHp@pUGiFzSXVr0yx^2{A0s^N$!n~+D%|foxk#P)1MiT^xeGxhbhks<} z8GZHs{Z|$*V=Rn-;m&*pLjp@Sl;c)c=XOsHJ)T?NPFQ8Da|_i4pm2n;IvZuFsEo>U zfMx5Eup!&7^_XhXlZXP^$Pw-bGPU?{$BxVR~-RKO)uIo9p)wZ{QaF z6;ha7etQ4#)zg%Wqmkla9hi-aZ+i(6<0VbP?qUIM@AElUTp`t1+Ob$wz@jNA+OEFB zx|X{s=tA*u8Sc-WtdJs{_hW`!5BXjoB|H;y;U2+2SIc`8>AB(z{Lw^EFq%{mufd1} z%bMAn0}d@iR(%kB#No+ya6qb>qMxs>r<(~9nle3E-P(g&r#DDmC9_gdL>K`C zg9o<2eyPzc;T~9uPfo8@wn#84>9D!_ARBpU-X`7i57bF=&O{3MP|J z8JaANXR6d6X~Um;|wRnCxI%EYp@p?T~RGCu_yIKMz@!P5B+Ry%Nl zS&2(N=Aft5a)82t?rG9n3)Vx2r2LLl$w}pG2QOTepSSg0;_ zxK@E0-7XhD-#q*PaxJQ=q_@dR8<4&df3^GQfuSS4DTI;hLG`;q%N+F}qe9uYK5nM? z^F44Tu~Z=$K#IQ1q~-vk6rP%p9*b%-?2;g6)eo4`dz6s?{8hBt*gj7UB?!;xcB_9JFJ zFIi`|!|{Ig<1WgHSjwmh1EZSFY^vD^VSVk@?UvNnh*4`0hAiDgM3@L{&!rGW zUpA36WwHkD3)r`NcX|J?UDQ@fkmszgx#GG>fNJinCX(6Hj8(w{g!SP1to5s~*_i+k zxdzOh90YBy3?MsS@9=*@Vj^tL^$<^(M=5(~p5{OtMKN6MN8QS8fUtx5KeS?o#9x?D zt+W@FDzn_=h7U-x~Ast^EZnYrY%%7bjCOyZS6;#57Y@Yvq{F zwiWm-Z>zU)wh*jqeARE~lmzhM<^j7kAzys*1LRAKfqucfC+T3b@B5R44fXv=$`~26 zql}VGG4yLFJ6W7ER>nmAYBEjRJS#JB@-B*RY1Lykrk)4aROoh!&S%Fq4^ z;*KJ+O;c}V7sP(N^^djoZT|h%K9q??T#QAJ?Vcjx7wcI_f%MTt;T5J*CO1Q@%fifN zT6L_oq0ijxLttpJRt6Fo!y1eMjk(7rJ_$EWSRr(sUZ}E>rdEi!BaXs(8DzAouup*j zl5SJI3tLZMi!)oILdgQcC<9!+2z;1hoRKCNek0%zRH`G=MG%g;jc{t}jPLF@aFPZ~ z^chCxH%6zyX46L9#Duw#2Qo7Lc4XA+vV`|MY_KFr1~Lja7j~uvgMEm}*T#`I3_u>cw}6?~2wQKxqB`4m6xDnMxFePS3^lNXP+& zq9&mW1kz`=w%hbLnYSJ2#Bn$^k`0PMoZ>KKx>Z(4*xP;4ti&PPx%`GfXIY4y7V*M1oNHZb|=i4Q1pW&)u`RSZo!k;oV_~QTF1W${*Cc( zZFPib!x5qmM~FTgAqsJXXv7hs5=V$m93e_^glNSPq83MpUK}BcafE2b5uzGLh;AGq z%5j8f#}T3)M~HqLAqsMYXvh(wB1edh9Ic}tq9sR&nj9f|a)c<#5uzzah^j>91^h!l zL|Y;{5C6~)QJ5n{V~!A&IYM;i2vM3aIpH7rA$k+pVet?B5Y;(Cbms_Bo+CtiLPxqf zLO1k6C-jk_;VpOo+Ezzs{eR+)o247AS-!r#iL0#S9-ZJo5QHdTS6z?;OW>Z~TKf#T zSy9d=&+lg`XM(T~1APf5?K~Jbz^IXYXH16L5Yz+s{99m8Dm=}CMqdNsG6I@9@N=wG z2^bm_hOO3$^LNdpK~5^1^@Qn!6QS{(;!YTNPWh?QmUIK+5jRcy8v99$bqwGPKE>5s zC0cCv(d7~mMA`a1sOgGrp?G5CRm5gQLXgknV$j+s<2^Z?ztlgxPF^i8Fpz9E5Zi`# zQq&&twWAc}HHBZt{9E~(pCZRO3Nog>E$24SDNl}D*~}7=JfcM(31-uPc5OQwS&bD= zML4k^bUT)}W*A{ShUDqv*lWLJK+4#Bjw`N$ls5~s`CBF0qVOm4@3wfE+Q+rshs%+g z1h7wE&G(O9*L==5-~3+xe}DDY-^)LQ6>t9j!|m0l_se_v@%i`nw{Q6c42x73`Q|N- zxJD+KIqzFJtn%O4*W=BX?uvkvA&5F2AEL5{E|c=?DoLlM3_&JbOR{iTKOhL(ij0Za zS}ms?O$nt}&MdPm1wY1EdrA1%lK=n&epS!0qN>ekbtoAH9Pdmwg5XSUGdZJ|AUoKN zyA&LO@pk9>YGZs}6WPr6v2XX`OOmL>yDt_J%f1rV~!F*_U*+DiZ{n)s;7u1?3@ z>hzHA{Tc^W!GS$hFF3Nre7m*=WrgLJb(KJNec@R2l-bSwe4{8KSgK~#_JvuS4qsd% zh+kwCb81d6Ni7Mg)KBu`%i*Zd)*x77F|FRk$%-0n z3MIzgTr*pjKl#j_YQ*8s1nE?40uK9dMke0SY%8FH!uRXNWUhUjmUEJI)|MqE#2n){+k#|n!r+R>L3auVI7MVcQR-M?w zuJMe!ns2?w*pOvP)XbQsaxl#~;7!1#ECyFj zbGLc+Zm0+>L5i#f(~10a^-v@ek$nm2LI@d3A=@g~)W+$tdxpXiK_-UEIKGmbs&0J- zDxGuik&8;;ls)-KL2MsIfN!(w$ajeZHc(WhY>a6rkw{5a%ABE)Z8KaF>M3-ezfizB=egH!K}p!(_nVL2e{c zK0Mh+<-e-n#-H{@0f&McLHtS2f>fiEufQlScM26jSN0nPNHc?NG@9kn@mf)Mz00ddhC@H4NZQ+q+=Q9&MHN0z{>a^>!| z`*59;sG6O5w(B!5@SAX+Y&x7!d&Z#Pmco({{F8@25Z}fQCIUnvoUBRMa+yoS_s+~~ z6S+3m$DN*@AyOET%P!KiPRS#kxg?o&B23ZiOUEhpH45lyHJT(WFfrig0uw{RfLn@G zCG-PA(3*P+Dl5}ojNoS9T#?+)1X$umKwGjP^H)?I%S3Q4VoZviElUy55CA2#Y_>N$ z+WC$q>gwm~>zgZah3B|w%Q%x{jFQnAg)OG%7l?&3a12$`r~OV`{48RC|8qiO5AV8@ z*=tF{d?p0}j%KeAJ&r;?Pheg};2XlocaLV8)B^$DZ{9Dnry5U2P}fBMrL~Z?O9^*0 z1Sy!Ts7fInvKz@FHw)R~{M!+Z^=5i&0 zCLkA386566NV}xTD^Y=HPg2mG!Q4!yt})-gFD%~TO_bCE4|9crBi$0mb2$i@EOP#jbqSyLTa$iC{u769UkQ^LjzYRSOQ2l>9JSdjblodq62}v*~uyL_#;@a#q~?1 zbjgy)V3X^D8LrhupjE92uUkwP-dJ>35E)||#FaO-ha zvQ$Yd)1?wcP=N=8c}GrfgV>D9v;UY!=gQg~o^z{UbZwpA{U)t%6h4;nTeQWE>&|MWapA* z7lTtsI-eg39~GbH^AxS*hbS^$$*XZgZ4+^&oG@zBlageNw@qFD1(M;4|55OH7n0F+ z2Y(6<{)qwS{yGC50U#LoWfhG{uiaFJdU2WJ3YXzJ(`&i`3STg)xV zVW`mP98{d2%VN3t36c5S(dT%CXYL=>=oG~f0Mg0TxHIjbr@7LF4H32a$)dba4(ke<1Uq~KR>SYH^ z1Co%mwbxO)Di=utJ&#d6z}M;@Ta9G$?AI;dqt!@=fuJmy?;yWFM1z(4hr3U2AG!zR zszJe{<@+nddTXa%Gm09q{r96sJN$X=FOV(5xlk_2b0U_9Lxe~7&}VdtY@7Q|tIqmV z>tEkMCH+ddTH)U#Z0mMw0*{jIZQBocyleX|s=}F+czQR_)?Jb>XU613z>EkD%lKQv zUuMT=FbLs~BbGwHQ1#*aiKLY#y7dfULZyhI_l>)A({gYy=U$3#9rmG6iVkTjaRI!{ z;%IjtwnW&0#;L*=q|Jf_ho#KzbJnDTYE<2sjQ@+^eC;=cbF9M2*}1F&OsYvWbma!`rnq_gC<=p+4fp5C0JM>c8z|Ic)FxgQ-d4U9fi}?2Q5?5hLY;Z!{%}3Se0Q1~Gu32$qTozR&Q5**acU`Mpuy9q9nNzvhSllV_kBi2QU9 zTWC_ogNl?ATX2UK=SURoQc&wVz*opT?eY*qRl~q86v*uC~1L@Qcj+I&Tm$;eN?3P#|+eL8v@ z^yR_ER*oTwH?oLr7ffp1fiR#DfHRAJxTg}GZcDZoRjo?4SCE_Y`?BK}{pT3gJa8{g zB>NTo*5`-vij{9A&&ZFr*H=&R)fJ0XXbq`D^_*dTxAv%ctS7gbUIJ#G-k(A3jRbZp zAG@o|hs#wfKNT77PdGyFmSFh1+dJSSuvE9MbnwSTQe=Of^uqvL_oJqEd1J#KYbZqk6DVUl9OD(~F(h3Ro?>FFT z36Lch0J+^P4argc{^}Z;cu@9MT_`1+z|6Lq-^Gm`?z1>c+s6n0=01m}q>^o&9zbHu zJWJklb5#@ATp1Hsa+@67EU~f+F&%s0kH~53^D>(KPYxCyO7nPULI`Fug%WFh<>Pqu z<7D;Y`qPnL>g3u87e}}X+68iUbq#j)-R1lHd+}duyNIxPw%Sg_xI?7|^XmaXTf)*iL){nZ8L>Qz{wVcKVq}=i47H@5DU_ zdM-;EZP~$=)Wnpl02xgiSR>n{ku{mMEn9rHJqz2xKY?xgpZ=+-uEEY%7 zT%4T=&x9Z?RU(v-iQ*b1e;R3?tN4(hyTl9wow)Ta@JA@T8rp1cSn?B5x{KKkwljU< zL`WZ74>93wOc5fEz%?jv&>kLU=#+;!WJu;fH2bL{S}6_EOEgh5@u3>Fda(0q*6l$> zyAO_N|FGLx97CsHxUd+BE{S+44%6WfMcKaAMUw!=R4-aof|Aersf5H*{ zr->xS`|1yOUrE#*p8QmP>MwZ#9-M!{j)>s;{$ z4&;B1{-d-vL|r^aDhw>N6EN9>)tN)tZY=CSukZ_!p2Z?H-!C7)3oJWW(!imRy&ofG z-{N9d>@g@QuC@5qzJXxy=zrtc*IVO_^}Qow0eg9}6s$bN7y`$|9@9(ZFUacrd@|m$ ztj@FD*&f~~H#FhB&M*;OZ;*y&tOW=!0eei*luA?Nbf2cqN|`g0ilu~1^P)5hm9EFe zH_tcLwl=pmHn%s%lZ~y-jpf(tlZ~S%@QxDu#qJ?=T64IX^`f2tN+%uNPGN3_pBb{d zM3X^Mm?9;P52HE#)QnAgadI)0je>s?^qo;fsdk5ORs~5sp%_l~M@XW3>T!d(-a&d1 zVmlz4ITvR1IF=1?wojT=zYu+kyY7d}dsuC0d;~Opepo@N5jlzVb);aE7B_(>NlEG~ zXY9~kz-*amtzT*-U2xVOZi30|2d*PUrCDrr(rffkx+T2`Vzj_97E_hLmHp3?Qk4js zoW(#-BG|Bv$@ng+Bj`SgCU)i)KuVY8H-E3e`ItKjBNwK#!z1_syjs~=-4*d=Wk;MO zR`C7n(fH};_wp59Oi0Hi<$a-q5fCzaQC2w_sUsz8)0xd$abLrX*yOW6dV?mG)3Oy1 z*A}IOc}e)28F|EK6Vcjq>ZSi?aOq{6#ee2>D?9f;-%Q{C^-%Qo(e?d<83?OsM$H@e6_hD9VeR$d9+K$Ne8G{XwRSyl zB{OFF$uoWW`lR9q8XYpAfaUVDs!k6pnpQp@9k)fHK6>@;#^!(>AwDq|C&U-`CQw4Z zICA#7DA+$5oNSQxNL5gdgLuu?URDU_qBaTkn)&*G3<>I~{1Tr_6oCy|K(0oqXZ*8i zxx7%c@qx@d3{G1jyN}=sxi^Z+W!VOGK};GE`aaHg&qP9k?*N!ac?Yk8zePQJc*&*f zsxZ;uV?>2IA6+gs2i5Dt(J8ewB}H=BpTjOAfakL_wDX0|d|(SWYV_6a!`s_A&dcev z`|x(|Y4E&@eh1JnEHq5J5#dk;_i5rpORDXJSs4f!JY7);R!T~$gqS$g4D2{OIXoZz zJ5*&yM-1@Sum7<+`uF^et+)h6bnQR>L$*^DgE^iP&evs%ej$L6C+4o&;_Ap2_0xrd zzDf25tc_`6MZ>y(26tf*DoufCyIVEsZboJ$`8TWwpWhbB2<_(SV1lEV=*DZMHX{g; zk*8Z3@`x4|KMRs&#y@%f^S@WG;iZ>YGb1T8t^(1`7mfK!t~AwwQKFPv3m7G>u5NJn z)i&J%z+1(TK>J+Em;q@id#2VZQ1;SukdId`0q)R*3KcP{2RP9w&)q;Ljxulv9D#bB z#3Vt@D0K;cDvy`1fFW(T)79QpBpcB5we;s?s>%)=o+wTdxzQUr`>YVETFXHha{&<- z1?F3RwCUm_&I^^#HdVEzSd`Fl7{Z4Yi;WHpCEH^nn3q;-qp}1$1z)cndzxpK@X*H~ z!XgWLt01XmQGVtJ`yXpiMsV}5(p)}jP!?>S!~slolHGcY)kwMwXM1aRIa*@pa15Tq zNmkEPEyX5-3iK)^WDSQjm1l3bAK_crDK~Vey`$PMQI2(H*h2y)NdCLP4zuZ@2>pBD zL*a_~teHO?xgUiSsGgVE?QHe+VB$!hdvLfZRI#wpxYuSWm8UyGlW|GCFRN`7Fgw{QBea4wWHCD=DPBG}y*Sov^~j zTx+cP@F=6#e!$0MV-+7ee3Fg*dU z&LB3iFU?vxtk(H+z)YRNs#Wse#&ALt#Z*Ww@89CZlTR zn#GxbO*u-HGy1TzS-)=hbU=Sl`3|zs2V!_(lY^QLIXfZgBS#EuF`sTFWhYTgg!+WW zA?}u2lN}dO4Ql!M?eb&yaCiCfBfJ>Wlx@P`sE=Rm&)1}QRMnHS_XXv#J%GoaZUT?2 z7!_7_zctLgFzzruHQ{-=Y>n=2Fzis%~$LcC$9mdF!n9)$jRr@2d17vL#g zgDILVQ99HSk$Z`1b+vLOYI?te&^@sjfPu6e2lF2EkflD{n zQajG~FP$W0?uO)j^jZpQE347*vU2p`;PqJ41L>JdoJ91^3BWp zbaeExCcfr%r5ONx)yWSO9HgTp!Q<7^6ipmWKT$wA-NG7^G(=XKG2#rDMz!e?IoZal z<_YkvO7S|N*7D8WYZM#APC$&=NBQYJ@*s9p`? z6LuUDI%-M%cdP84)r{~Ti-5vW0MMDTc@J*Cu#m4Ez>S=2YvtFFGf9-QYsU9C>1VU2 z?j4CagBukY6;vm6;Q%CF5Av+JNE%O1PI zR4TxtxdUg#`5L{=2{wew$H4?Pt3djT-F2z>GU(ZW{E<>q)xoX>hHME!s z^_!6K_)Z&?v=e*9Y#*PrDlJ1-b>MN3bs`RIVYtR zxYyvlC=Y@ZF%C`-oGhFkWbKnlOgEvCj?uPUl;|N>M+Ss^v8jiIEun9bR5h0o0};ev zV6~2%ePX!5QZX7k6`45&(~k8MC#X<<;d}-|KBCi*LP4CBFJ=%q;nl-~oHQ>jei9LX z;%zOoKm-nXtQBWLxjk?wqQKt~%ECt(zU(p4Rikd;K3Ea5`y5l25KA;Fdys?z+XH|< z9$|h*v^Nk#nsg?%2g#Eq!ReziCx23Oto8Ick(@$@Jcnj?E`*o*ql5Mb; z`zv2;Nf=PE6n>PVCdd=?$|Rg$w&GxZts+$*()18!T1FvlVgVk>mv7`7wOa>bN`V1! z3Qkgbufy5hHAL7vCbXf7h>s#NK}rLhqU`PH76O6*z>6$w_tV|=!!n-{*+t!-hFJwI z;?VbcI!B5*DG37sxQa=M?tDc#nPj(=+{}szrNr$gSpm8D z8*+^-ySv-l2M!m~pwXf_lV(zW7#-1<_j_UZp^TrfR$2Nj)GJYQ55)y$k1}zalWp0o zwa@$_i$HDgE(nS7Zi$7hz%7BRfm_?A;Ui#NPc%KrL7TSXY*hlwj;QXMTF_=!P!DzH zqJ$CgZAySPYL!ig$oA#u%e%V~f=jh75=pE`72*L1!Lmy(9wbUh>XygGbUp+n^&^d( z%!aWhzlf3XI!mL->lB0u%vcdHBvSZ3 zO;(l{G#iltS)!4(hh-G$AmAS(McTRM#N|!ScSAe$94Q2Q&!NB6W(D%5P>rK*!D-Fs zduvD#QZ`Elk1g{SAy^H(c#NkH$8{1%>u4yC~2$#{SwJP zHV4?l+0mXLY$0aCZ<^K=u1rVBd?e{5xE{{$*fzvn0RIG$`{InTLEYYWCpadklEuhx z`c-M>pmt_4Bf#Vf9C&#^y->3S3t3WUiz3w$^CQNMiNI@lS#}@-L~PRIRRP;ZDya`9;(hbf#i7 zrRxP{Aewt98!>)*ysnPl4rbuJBznrDO^MBebD@BYtCccxP@G@7#Qrke(N8+vy}w}^M>PHinOpG;Gbe-VQHtklnd>W!NhGZ$|0P8haR?W!7&TWx9H8h3W;LD z=FPs?-Mog_)L~(mel;eLwA0ryaElq8>r)`W?{6_~>TsVX9UiNQP6qk$xE?R`Qnt^+ z%D7IvfNMeOy0(I7X@uQy7u{ws#QRE^`C$f4^bF~ zd$~#Ohj*9nudX4dnq{hMGgzx@FjG4>G~&{e>|bxcKS$S)acIzQJn#9keW?4WufwT4 z%j4H~q1!CB$ja|q-p;4foUv$iX{dC``z=;wv-V&bRbLf-!74^wCzk{tl18}m|JTzDE zNDl6$4OV~n2ES!ffD@dB75U1z9#jGsH_=8iEo7w=*r;)RrwUgdO?_Pp1AE+2;Yj+-Rs$Gd5<&a)Oy)9W{PnO?qxqA*Y1yuH4E^5*9Kdw?_l51$dH zG2;+5y_S|Zjzsw-O+k+Qf@91m&i34wyIpu*Kutv^S5%Zhl>!n5_+wQ!>+3{PTsgu5`zKrY&p@m3`a8WFb3 z7$x_Q>R9S{nA1fcoo@hkW=aCpPyvZ@BrTI|PE68Jat~PUKmX6~-v)k5f@ATX13A5T%RWJlr`P#Tq)ugFUU{tS$Cd4}wp?Evo!X)$Q{`Lv} zWhnsv@Zy&|fDb=4u@KOAkp2n^vRcrz%boIYRZAi>uU0S^P?Z41*-=`=tivb)(E;+P zSO@pEAGU5ZiwWII#i>*ua&25sWD_p^S1zsM^m)T;CYAR#YeSFbf4e)oLQ#2@>{9=! z-Ghx622M#i=}!?LIr{IRO!=UH>Wdr=>r3^XLsQv^DOKel)o5KwwnQ`12cO07k^l-@ zM?FcxOp?E7k++6=?T}*NjC%|#7H0Sjp6EGofZ+T_dEC*ZXXg+gv zh8ic^23F1A4kIfuc=}7<5#V%KXAN(i3HqXJP+JK2oFl5DjrWXE#mtB6I7-nAEK@yR zZK8Uh?)3r|&C)8!wP4q*udzCL_gQGod5`Y4iOlEwkrd_pLohqmV z`RH`x1>EY#CvfWS|Ao~tk1QO-4t4?$?ZuBDFP6SSxX|R&=R4F){B#eS^xaqBUp9ua zQXJ&xA5oNbvh*Kc9pC-OSC79+(O|7_y)JTuOsjb%mcCwoJv~{O zA{@N@mI}0Hegs!s%IK(%I=~Q*G|UsE*II`6VyyuzOKZ677-F?NWO2<(7RhZmsN~%7 zZt@i7ONKgxXw7RNf%-lhBz7);zr@Ou*n5!q~;VAeFGm6SS!-qfl zm&YmrEl-?9Cz@>rf7Bp21*;#V}Lh3P7@ z&8^h}+R^eA2Tn(C@s0xV#KsKqP)0Lc>>vQMS4(Z}X)2|*yrURohT4#DFR-`zm2;M% z6GNKM5#;e*$QDgE;~akRMD6Mbt+qWDcD{uKCPu|7fD{E875zl<-PvXq#@*g*joX~G(FmgAx8{VPH|rx`n>m0u15T&i@T2?;Th)>4WLru_#es*-hcc=A0&(Nv3}ss zHy2=Em?3%_x)j@Uo)<3`2Bw{O2f;X^B; z>|aL#M2~7;?2&ysSrYsNZU&=3zhwUg+BzW$8tffysP^Zq+|0v87lFy$<;SbHH;|}@ zyI(Foe7O0Ad6C)reLjCNP7Q*6rawanVPfq3_#9ECC?IS<8(OLir)G-2jGUwUi?@G0 zfGly}c=C+WVb)|x!H5i5IzTr|#iuq>V#r%Sz7)!=9?Wqz{>vQ6+&jmUhvz80lKAE6 znD9BMQol6`UnVP|G#>ixdG(SUB4Sl@~Ri(qjxJWAtxA9NSsaf`m=`>n^(|SRZ^(5jE2Chbk zl&@Bf+E+Xa9ucZ&R9^Z~C31W53+5@XD(5(iI(*vICaGRENJ!TkLOmc6sr)9Qx+l;4 z9GKHG9lmAv(-Ck5yo3nz;s{a=|1CFN$7)S;l$O~v>gd4#WTEisZw$#An^PEd7*sl9 zNr|^=4yw3SHsD_kJ8i&W6k8GV5<*sBid(9Ymh^Iw^jx5u3x($rF^+I5)DPG{U7wNr$wq)%B>K(-2l1Zzz1&Cpj{tv;1{DwrPl zsZqx@)!;#XVAlMjRF>iop0+aZf=OCM$Aq2FT0-xj*4{(~`jMG*M}n+!;q^wlQ}mAb z{v>#=wRf~&k{&?&1{R9QFBKETuS2LM$`MJs(nDT*{!~u`;d8#ErZ`>5mx_ovHp((w zkX6325$EhYcrG(C#l2*Aa2a1<9$YnLCw@7dmJO*N&;GXv$T|b4?Ze21(_vSL{)!fd z=vahKcFhsfC@?O8y=Yln8lPfoWu`hA*M)J880zs^p*s> zZsrqM$T&h$u;n2o`bk;XEc3Z{#`2O}3n}W%gz?iqU!y$(stTl35qtpI7583UT5c3FjyiBnsE zf092a5(-xvpp=^lHLnH8oMK#N99h(I3lA?XoIq}ib)-9@zwe*V_aSZ_A$^H0eyy_T z_8e75PY-Y2U5aX;;&8}lpkv5F%@HqiJ$yg~*n@y`|{1_ABXKd#ty>he1OOk!-#|r3+4z~L>+c#>nRfNX#B73eSXnen zH&h>{XK$?i*Ps;Al75rt^+A*x2IORS7Wiq^HP5Jza=h z1+-N=H%<*EXxzq(9}Ker53G-%!*<*gUqYIAuv>db;*0FFh;Uq-3*B_mW(l!3Lx*f+ zGe_b}P4jePmlVk3;IpT5;XARo%mhb5N`y&O2;+|s8@Tlv zmCO*f$rwTI@csL9Mzw{JljHfqK%XF7_TGhd>SFdsSp%l?rFF) zk|QE}uEC({AXkIp0~7O+XH+_S4f1||68YmuE=Kxq_RAT}mF8ZX=zcjpRINjKhCs;N z{3>uh+8)6|Z-&-Y)-CzYDo6+&^WnMdJcuF=A1*KMTDmxR_SJB4i$soK7%=2B!+`hv zoMtUGmZ|4u7lWQ);R0yaQ9nhg4RHR0(d+sl)w{rr6{VZMsrC; z6Ylt|?8;Z*B?}n3Z&K8p!CB}{H6J#1*?V+ax-Nx0ausdJNDwpEn|B$!=b71xU7!4- zS=x5ImTY=Tu51x7XNM|Ax%VxYSi6X`twF{E0kN6xpi2S%Z{c#K%d#{@I6#L&ua*ob zPSP{K$f7Un(W8*)jSnLGtlrglT>Ba82_Ve-r#pZuQ8ILp&!jB9w_a^F^>O%NV_=w} zmxB{CSsN{BSloB>eD{H}BTSS=PUf4>?-zKOCFVn#X^LvZ3(MT1jVIONWN-Q^vq^KI zV_j-Gdg2?8-fD^1btQB+l8~Llewhfse2>m&>~+eF3C%qej0+=A!tHW zcs%)UY!+9A(L^fKHueP(m@|!$7JlP(_QS-D6wA9{mX6;gJu33+CD6Rcf8|3dR!MAP z3MbtSHGD;xDb3JZve|)~v1g1G{m7V6ekX9B0Jv^)%4P7o$G zSZ@3;`hcZ%(7F+9I1|5#{s!%jA3h4@gwT)pI7{KsW&EZ+J z6o22}-(J1>jOLHS5@PSmF#i7aF=!o$gWCs7^rY5j4t}WJT03FFVyhyfjI=%5hTSGv zRIWv{be6t+Md9(=I;5fi=2QPp^Lb zDfMgXJ2HUCS#pS5ND^Zh&ys<&rgsB`UmTC?YbIK`ZE*T<#l^YC^)+3+hkrmkIX^{-t?y2DCJZ{~`7&@X8Md4l)E1d` zlAcOujf|q?p7|FH@of6`E*JDdvEvX;y3>@h2pO6mj^H=v@@`8C`&!rkU~CmV1r*FsExj!rEdrO5>_Y~xc9SNZ89 zN{C0Gw<>f@%`itk^oHU6=j*rK3-vfqvmMlOlr)J(3rOxe_9PWpsM1@JMSF)fvqP&- zM1gCp{g~qW@M|DkmV?=w)H#sVitI?D|Z5L?9xRMP%bFl&KkG zR$rOUD-Bf(bCS^ts_7#0u)fq*Y&kwglM=nj$y0ZNzN!sepjg<|TPah+9SG}tWcvM- zIU*PSEb7TTRkPT=LXgeN9VDM2Tpf>_01EQg2ut{;1;&AZ%DAK$?6cJY|DP|*t}2XZ zVOb{TR)amMKEOq@^oWSO+0f9dmzmTuzKTqtz`qtf93AW*yo+Xi{;X0RlVIXVCB3;cqCeR}`x$kj*Ka&t zJU}$_;-=$hHzOb@NVbJCdNRJ~_rvqo!^y=hB+pA|lOhQ%SMq|}-nuxNpG%m_!=hL) z2%Ch1ov5~Z`gwHu<8=Mq=0|M#*IgY$BuCT6Rv#ckKn_4* zHve{i2`8w4osMwI-Y`5^ay%20fC;jqc+iWgTk^D0tSOhuzSz1vH94S{c#a0K5W&oh zY?>(}Of3Gc+#H0oahzDuPKa?nMetvYV4J(C2wg(Zpn-U?5o5`Vchq`+6G(AKb^x6K z%9>s|Q!RSMXjtn;!75OL0eS_R7^7m9pbb9lL{X-plz`T26$`ECi{XJANyaeF`8k5a zX0h5u`dk8(BUwu3wADN;m})cs zh^&H`hcK*II}a-QCmb zsL3f4D-|5~tu513+zZ)=yKZb5Bb8OCFTmBCA^7@)dUT(pe^F(KU#)d? z16tOWSOCvHq<0xH%Wjj1_;L%#>NXEiUAks8_Ja{SXn;IE#!J;ysd$JC3E8i)Gefb~ zbTl+&J!@8~hb@|_y)bO;8RJA?fvXz?Km9FrY}dQ>`1N|49~GAF{0Hs@(nKiLM>qKW z6cr(hEjgR>;P@F?n-$fRQ?Y}9&67;Rdc!UH3Ts|uv?Bk zULj`%DW^2`+}$HJ>For+BI6DkR%(-@3@K4HK4lkyfbUd!b8P2%zj{2$Y%MMO9 zt;H)`Z?(H&K;aw6v^VdUSR~@f`>V?j?;cO;4=47i*&*{$m#B*7Vg;%(k0Cc+%)RF7 zJNP@kppBEP^1hs`>gobn)!#-|A^Rw-I(F$RH#l|(y2hvb4JP<3h5Ln6Lt0X#gTS-s zfX6|>fVO33%JPR5@Y5+0*UaKopNA-4Gb6t(NlN@OL-HbP?u+{)@30naYSq)IxF88| zYwP?_0i(mjQn06MmVk|)ta38{7Ns1R_x=ojeENKQIfKQivB9DR=-oZ4(LNS{CsLJi zvvQIaq8awi9?sE{2~!+jrx6InNCNGP6>Fv-Muwk63L7eA(D-m%#>@GOQNBv{aCxaN z|KXQ5@`Kg0ba7pb1hJc+wjlP>29gM;HB5njbTYH@ zX=t_h@gM26h&w`@ETWA8I!c-5U&@yLdXg;rMNIXsJMJZ@t}iG>JmYwKNnDd1@R4=N zgXTHORYF)eMDfjvqIc@5n<5Sagv}w0LJr#~TXKl<0S|bDZ&#mvGkpB~;l2_!k`hrP zZIDLN1l<>a2Vo3M@tAKN&`z%u50FftNwV@woXN;r)cJ9s6^)OQ2zSs*1BsC=9cRDd zWzPL^=v#Ye9fxk%J664drfifJNjYlUC_&2q!dI*|3*xYJU(;`%K7YFYd}X?_`fPo+ zGF^Lm`KOiTXS-#!W;Zvk1OD3R?Bgv$KgrXl5F(ZOQ zLT2P~B=J3PT@Onx;RYEF5UKUhJY5_yQDi5oz4e`Ur|8SI=7#c##e|2dOuyd0H;}|_AFz6vHO8?3TlA4*iaZtcy|KvMQ?}LPqRV8;1@ri`U?7t}29!#RA=SP$y zSn8sTXOJXu%tofg6~WqRItnQ+$<+uMUD66f)<$aJS(?Jhc$6;&P39?j$6~5EYDV`rx^iOLJA!DuHJi_7F2GNRmoF*8JpYiSpe*g(4iv98{{An-|&$ zkXgNv?L_j*`H|l%t~mYn`T80?oE<=l2rM;97Uw8cbWcnIFCYO6!DB?gyucG~cISxI zpRAJ0>Nn3z_alL9V^9553IGK8%HSb@SiET;T7Hc7O=Xc zY9wmOcJh4Ww+TFkSLJci)m`Q7j;xPok#CKuqmie~$IOngR@S}{=nXSSvrZ=uv*FpO8jl&Z(#Da;Y z5L=gZ89wGr$3Mv*SRMkTD^SS+ovl6B6IL!yK{VYmj_ci71+==QbIRCm zL{*?8$QgdT`1tVx7M>rk@f)f~(|Jy}ki6%!M+5vYo4Zg?I@v!okR~r`Ji}ol21}%qskRXDjl|~Q=M3xF6%6DnS4^;9&-A=-6_n@<2(07Iy+CHld zN0~&5$jf=cc*gp?7fjNz$CV_cMfQ44pimY5r_UeXD9^)8O8I2I9JVYN2qiimr*zct zjN_3DK6C;K+9m!TFqtAlZzxLQ@D}iW`At+|} z=p{L}~5P{dup8-DYsaI^FO^j*=bubYeyIq+Sf%;0TU=ff?*T-$BQ5BPO zsOLv&Pu5BUl9q4D7?L-2gT}pA#G2Z$;J})k$vX#Y5UHJq;54KaqUyvkbkjoQfs`c+ z%VH)|$6U$wh?vpp0>dL8mQH^O2&CFL+ zWE%P*%I=7!S*@Y(99AF0Lzli)8iZ$Xw((*QnWsB&VxI$Sv{DhBhEIA&r+SV3Ia*!E z`>kioR9yHeyGB=qz+vojbD&kcgP(6#p1zl^#2`ji7cX*c>kVNp5bFsY5WDN%8xp$n zv6t+dgm}4pR!}$!T#A9%Kw3&uz4o3&u1&E-fLcGsk1uX z{dNO^i}hwq(iu(C(*xP(SpZLtUk2E9+39Fb;R=83Z>wH_ew)srKz=|m&_F)38!r+S zL8hz~=**n$U-cUMbF{>O=33&O(WHZxYr8-PCmt$kBaP8n&GzNJN770tx2It{#|H>K zK?*FgNrvgN^3gQGP9%ko?jONqS1+|QLF=r++F+o7v^AMJIoo4|v0LMsLcf(IzQ@3y ztv3w3H|c2`CAeUc*4(#cK!oPP;}^5oSj=qUUO=5wO*%XlYJosPaXDy$h5$wqU;P4Z z+VF@(;f;i^Haql7JVjw-*iX}s%l9k8vsdrXZBfBCg@ldbva2C&ncUP!HQ%Tbq*}8s zzdDa>gf0i@U&hLh*U|cKG$Awr{HQeG0Qz*jo5VRf4{&=y7r3coS zYNlr!#lq`iA!$XHDmlbtkaAM``%%?l@!;s?6qa<6_~)g}nBgNLMr#a7SDJ^lO=F_s zvcc4Nc6;^XkC(TMbT{-vNNgrmv`Zm@p~P+2oX7x}2GkdgBhTdwEusrg^Ut3?efZni zFE=|3nE&aHHKNg*kM^ud-M7B?}yV>gVPYWAv&89 zgKE58G)u=*)wF^=R4nv?t~8>qVVWKiTrLWT0F*(Z5n16$m!ld=xoJw zcnC7PwtR-}0{Gr!Np|r7sTy_N0q(h&BfF27jndas-E^BjPfacjKA7Ag^+g!*~ETsY;3BC!s-M z3Ld02yNs!Hkiey26fcx79T$lSk83@Q;eD@BbnKYjlSzP2nck~FVtuRR%%FT+G`$j= zGlMZ6!49kO-X-;Bq8{3R2!3)60OesZMn99+HB;JpMtcgIuD zK3p_QXc0&pDhjfS3@Z!TPYawKr!6_%%=3J{o>!yU0=!IEh+%zcYj2tDUnX07XomX@ zelyu9t4=yd3wV!*l`K`yXAJ;0fMD|JVx((8Grl4TQPbsC)J$L_NL#BT8W*zwGL!%KF7{DX|1TDhHV9aF*AWLVF_RC7Aq zTMqd3JW2Xv8WI4p8AWd4L>>jV#E`=x#;yfiZLZInxI%ar)s*wG^yk_8+ui-z^2b+B zQ(Xg1*8Mhwbu_Fq)OFQZRv%0PYH;e-VNwn0X`(bQ4$gNxPl4X_Qk|*%_HwpnrEZY< z%>OY$HN=a>=3$v4k`WZQI;d}8KhlYvJfn&q9V4eK`^t)b_2o3&ESa_INf8UO(D(P$ z3SAYgMl2UNQfh%vLVWT^CC;bNPi{{UvpE1TZV>h*R8B_UzT7ktc8%3XaiK}c8dZ>5 zFi98INW`srG8#6RiGI^e(O0r|*HwqhaN@K;Ty$c@6DI~p40YDuJr~CCUsS(r6;9B$ z%E?kw;BCqLT1c3FGI_9mSpYWf%?-rd@zPYXRR(Ud?F`&8046+wbBgt#Ax&W(CiQL6 z?zSReLW40wC2BF*|6+-D!eZd%wdh-eY?vn4sN*9nN4)-iZ-%~_m8IDJ7V$`kG^h2+ zj!hy(52CxigAy(=i$u24__FO{?3hX@Rs2$+HZ=m6lQN%!P?}+Fi&6&i{J{=zKH*ctLEoY-9c+2hjmT>hhl*o+6Jc4wBz4&Te!oU+K0a8f3 zQXP-J?ZjcvoMO3YC~8u?K-BEculJYN@3Ishx0QfoWmFz6lSeM`_|@s|!MDu~^=8<` zVBt+26lmLS6o?4o_Tq(x=)^%9Ed>LID&=Ypo!5mpzSBBrCRLT^|bB zGr$FzmpL;1oUU(Pp>&M-arlU!`QcagDc^^eTQ_X%z5`r9NwYZlg9bDaOj%o=w3-9K z;ZLmzjkXnswb_ceWFf~)C=r1Yh-!ohVio{hTC;_fcLjf>1|)hCpPLY~3_l9@lajnh zz2E@U@jQiTuGE!#B$k432cQN#G8q@4Y8e8=@hug-$i-AjhGZ?@tS8_ z38abn0iy9|>fRvn)Ky885A6liSd@uu^Ne3eFyB@p0(zm7Br|}Z<;IQ<1iy{#ZD0)A zPhH))`B1PCN=4S8?t;B4z&$fThKdXbBHn>^i1R?cXOn!EoD1oe%o^ko$g0&|)v-ko z@zTYUH!P2b;@1C){_T$;*70`;ac>~BHCo|_@b)1O@@(F0gp&k+VO*3gIi-l_?zs{q zTB19_wBV|Td1&11%jUC#)fi3NvoW_&(ej~Z#`cqiSJ#(OiWfNf>+(}GRnSrqbS++G zDvq|-pyk48SSG>p{@u;zd-S?SzUC7Cfe39MJ?^UY<^lzi(EdPDh85OXSEXR^)owkN z?14@eA~QTgDpd;;;Ce&?{5f^1-l$^S<$SrQV#n{_`x6ZmJZ5PAa{q*IJcx8t zP(@L>GQQiBT@dphHHq%cFff3BXz%p+TQEP5==#=(7a{IfeVJpUcdFgOX=JBl4H89$V1 zmt{Z`U*ttE1w@=3iz{Yw<2`We!|YDcIYwevgz#)r2)LQK*7C2QHZwHy1XFkWmLZV@khpA%%O~3sNiLOCy6>y%>T3%}{wGN+p*VQO*gva@` znVF=8*m{ec1=(U;P{m8kC~=-hU2Dj4%y_&^BQZkruG%1=+ictebe~uwP(%UZI z4QPk??&|&f;ocRxz2j4e6$oELiZQyV;94`|G%-`nK-5kMT^3Dof)pev`*ib!dIcJ( z92C>HMi49R)ez$t$PPMRzDKv}%MVw7yisYIz+jP+L(-HQ0G!ij6_O{E3dC#x^=i&BnQW9%XU@XOSY?m zZ!`yK0s-9WTYJ+{?1n^$G^TY46Rofg3!QG`(h?3*@J~$;)dCxl@Nh|=sh$<{1TSu{ z?*Deo^7j-W2E=N!6CxA04iLAqJu&~s&YzuqoSv2=L^rKU52cG0QD;6((c)C51$;Jk{K!hENRlprRH?O< zaUU)nh^VkgQN0OROPJ6<(Z7#+VSPZtT>w@6HRHs&{X+hQH(~&z7=}OVicq&lwG!3X zl2y}qSo_FD)BT~6RJ3r0Od;k#*RQQr2pHNAa~(kQd@Bo_aFi${&xdxx`Z>bJE~OpS z|3)E%`&(JxkYGLPtJibH~n9?yMV98{(Oxh=zl zUc819KK}#HEwayS<|St8PMVJ=Val{qg@zq33%!_YT?zRCL#&UAcmw=4>B_ip{MKg- z5T!OV9GxG+@fQ*u2;#6a^ox0g)LfsV;}L{pdB#1aHQVY=(U%i$5f--gU>I(04U$l3 zQ$gM=$n>gx8P&M%V)7aJmQ&%-cAA#J3h5AP88or}!#DV?8!eLQl+!tidMfpgQV6J$ z)GRxfd*!~!d@I`!G$)Zc8U#)^Kr9g^cQBC}<)3e}C*`5SwB&Y>`9ZDEmzURR6yHFa z@9qw=#>vpwE>?gJprOiAEC_TxinSUmJ19drD(h|mTk(16&o$} zo497I9oar32u~!7TVpHsURWb}4$AGK3<+Fe6Ay(*#`!VU@W$+-EN-l8Jt>@siu!Dt zRP^7mc^>rs-xLQ*JMW$=_$4M)wgcFe+fIqV$noT9wZ)^}F(CbC>!aNTi~aO?gY+`%DA*!m9@nTL44 zc)|x{^j^NJKsQtBaZQ`bTp>1AZ=s@6#+ihm${`LX!7Yf+N9@b=AvwP~gBFwCm09(Puc|>uTzk%)pDlH+FZ8LkhXPv>mqJItsel9@XktexT0z< zy{?*?hP0dd4U4o#19g$0)T~Zz5`1Ch01xzw_E-p^fI3vXzZeRFf5JBbjN7#XbE46vjiR(xFhC(|=u- zZ%35p7&I|E$#G7No$9m6P{VLog#hFXb5Vdw9<4B5p=}c3W<_;(0mm+? zHIvt9)1RZQ+D(85L3$)%8D>p`RAu(-GDM{(aN#^{ z#SjSjtlw=Pf+tz99nEZ$XYR6t`b@7abaDN=fevOjKPuzl>10U)sx)?acJ)t~fxyrQ zLN-TAaQD zcM@dDxUVy@m*pH17*?uKi=fqr2%+V$xg=^zwQ!n_Hf}qG3qEzlL!uW~(o#c!LvwoJ zl(`mISG-z8O&Jn0@fKt0%3`zOw-d#z_tBqI;3GSDYJ+O$Bw>Q74l;|TTDpa!wm{mS zv-k){coh%2h{>%Mzi5_TR8D}1kWQ(&iQW^Qw?4a}gyW-VB>W-lfK+7zLlXe^F_2{M z#-Iko)XSAwsz%+9od|RxE-VQgW!PdZw|5RUz8km@8ll*xIoNMc6iww~qJ%|fxF-A&NyDDY(E8j+0KIEUUPqY{L zV~AZX)!>P!nN(wFZ$>_ip`;DLV^#A1$~K67+D84<&ICP_1_=chty;j8%;3{aGlOS~ zDGy)TrcPgUyr?Z;B-xUZVvByd`1uk|wNUl~|E;fL<9*0B&rA=fvjJS1_mK2Lct%B_ zaS@#jc4ldpCL7QyI32#-+dqSz-+a9{LtiZ=**4GTDiOQ5Xj`12FI(HOJ2VLZFT1FkXpY-Ijh&({(u> z1+K$KBQ6a!G-VvnZ42q`cyeZa>j4_fll==Q07U4fP_)pZ=3q>bPR$yU+(B;E4Q<4p zY)nV0>6uF*EUE^B(Qy7!A|yl*zNuc3BtLQJ0bzd)ZLDB72Wqbce{zK2tS6T77&FE* zPUR22D6FS;K-xuf36U0(*ee_!{vmTac?PS6=Z}Wi1^tDC7@}>>lQVS(K(u2SU1o9{- zEbZN3W)!*Khs%`$cat7mhVhh(M|-#lQH8AQ?`ZbqB1#Ra<`&3kjgP`Zltk!e4wrkn z3HVZ!Ah(Nb{E{=Z2^JLG4dGtpGQ?S3Dj<#tNY zcjM^%3@&{t{wGWPiBAzoBg95J#MvH5lmqLE*XyXpv<-tqNOV?s?BMv@aJXVL>Go$h zGsG5;y&>g8_lO4@(KKgdnicT(wn0r-|529%TDP+-g%#vQwS};K_TV}@0b4oUe|5Mq zf0<5`hY7thJBoR#2+wJmOlN|QyolK(R%gXiT7yTzYNu$6Y{z+6&u%Z+Ef&SrBs13% zNG&+)_JhW)G>3Mr^g9exG-;VdD}Bfw=!%j!-`Ya7df&EHe`RFo(z?Z1xgT@+HFTlS z4r0_iTf2)WO^TU}1~OBXw0jp~L@kp4$ZlFb9exhZ?}lR8$_p~c=khF!5`;cRV(K`L;5vt1NCxd>p{zkbI`N1rcJ*Fwt$O6o?+y|s!k z&C%xo0G><K>#o=B`f%2jy^{A$G;-wgTs#}f1KcZq?Jqh(!{=nz?LKQ=J@>K zLmkJIZ8M{iXHg=Fv+UqKgoH;0p40}a4Cbu*g(a2|!1t3v}d$5=A2G!oa!3N1*ug0>Mit@US8*Cg^CB9<0 zBdPPSwHggXpG+_WH7{a+{Jct`D7=TyWN1Gu3)+!cyBf-7{Ht6q6FD<;BT*m=YPO6- zutSvQW-BN}GKwFH4guW~;R82xGc*{*fc0-#!2da^}@?e84pPeGhU@qLr=)jgU)d;rxSi#0H zJaipAyx*kY6ZH0vMl{q&cD^hgGDN%8@ogdB`QpPR!tsXzQ9kqYlM@Bx@3VF9j>sx9 zbzrh3i`YMevv3>YJ8XMTGxrqh?Am9NgyPnjNLG^B0A9!%rX^KEBs)Ik-6NPBAE;E! z*)iPv8(DaDRpyJ+b(pdT+C!n2Zvw8{=k#XednnwW1$*p|1PKaE#@QQ7Sa?Qe~Zm1KJn+Fps`L6c^QBE zh*NgAsf-`D*e9j!(XN!e8;>RM&H483O8^0z0qr}Jux+5*L- z!aDpI-x^I41d)CxZHl+b^xBG#*EpP@uZHv}PDtLzLb{@MZ=wNmE9?~_B6eLAvyP{_ zCiaor6@^n$qyoum-0IE92O|i{)=2o;8{Fk~G)EGS@<56*ni5*YYrp^?&M5)`RZxOK4LTC?zhJDDo|cDrDx!B3Mqw9Lmk5d}<*$$S4yF0kz*g+CYG# z;9gN;78lsfpPhqbQ0Wm>IX^mwI}xCR7&#jbZQVl(MxBJ~Pf#)nH14ukXN)>0h1WQoxrZsMkE=&Q%Hg9Pq` z4bZh%w2?yz)(IMvMJgAh&oC8Ga~QcZjizS0Yg4rA+Ei}k(Mr;y-I2}lEGCM-y63YCc{ilCR79jDqBYVv{mh0@ z@{&$45JG*V&GZc`QI09+#xq^yS+6)`7H6iRnp6+a!nk?$g}x~CKns~fQh4@hoa=lp z#j%a_2Zc|7{Sf>kuxOFOi=hPjbn8z3Wa;S%JPoc|E;xof=Yb8-BP-inR?fgi!v#|y zL<<#QpPB^~Dug$4DOI4ipjaACJnJl>jk?Eq?lNoT(ZOTk9on@1$<3Z0h=OJzLY-Lo>B^W9g}fU;4>je%{J&cZ(PiH<)6Pp0&5KU{ZLG>6!NL*{{w7!3Zp_ zIORIxmsaqP|IsDOgPHp>-4U{O@MUoGZ%1xNu-i%a_^%vHXmvj zwU84y2?oh-XKo8u_Kg!WSa~Upsvs(KNm`1s})d3`=|)=5!$rQ z?=Mr;j|FrQuLb}~(zdm$sRF`r#-6A0VHcj)TWZmRXH3#&Gre~wzza$VFD|aGrvvH% zDXKFS!-)+vr?WNp1seMSjHAmB8}ISAwdw3Lb|Y*6h@K&r(?E2&T5u8@kn~oQx47Ui zDO{{_<=V(LKQ~^voZTVLn)m2NIYx-)F08r?{Id^>-pc8=Y+Tl%W;*=Yq@@J~Qd00i zA7A-CmeiEn ze1@kjnyOT9h#2pbWFJ`;o98duKY4Yw@Gs7j9l==g_US7|o{>SP{%0{`lr-~XZ`g(l za_3aixp{^?XA})lWr3lPB7N6?IvCYa7~PpVg9j4`@F9*tg9bXTpp`(R<;nD0 z18aAR9zUP~2zq`Gf32WCvz-xLs_lzgCCjIN;lgS5(p|d8k0&vl(&T^eAnp&fZeqq& z7`wQ;zr1bYlTw8do`*?B5lGWsp1Lg}X%<$&vM%wR@5VR`wKV` zjcBi^!y2^^0(pEkpFEi$I=~YuGX*WQbbak3PF{RLFDi;Zl&2_6Ef9AjRx!9jF|2=@ zAnQ64h$4TKYIRY=Fs&b=LJEDW_hCEj7YSPdqp%ubEEcA~$*M+}(MgL0@_IUf1*U!g z=)F**K;rHsUQp@XV4TiJOdB!$UMk%h1Zc1b$Ea2lYDJ|%+1sLPgmPze=`ba@UP^C< z0HBaqF`w5?_v)H<4V1_!Bp{zEMn7nQzEzKTZ^XGA!iJofGj!8LkEME*oe5fGpn$3B zAmTJhl=Xd=juy`fpf2O1GCv_fq_=wjj%~A=p>LYG_T+dM(g(D6_)se~XEdxY1+Nni zg@Sd|J9!~lqjsJT^{#&Mf=POQ0tP-D28R*wjmP!1a^Jox_{!qC^tDVypuxhyl&eIE zriI%ER41LkUU+|w7Qj6ezdjp>k|Z6ojy67 zzj)$F*@{JYqFAQ*i3QL%1%6Ub)1#YXn96TIU;8S!NV^n6?vz$l?{d*B?Gu+v+o2UL zOMjrImP(d^pc~Wxn9jaEUfM9{J$@iuXBnRQKhl-Yk2xKVd;I!%^1n3Jp-d^0N5^RD zT?U@qbScJKc(e7r%Q zDgUxXY2`(V_T+**^$QkE(hJ_Qg9I*a=@We-ci~T!rg5g}(H0bC1EiUK0L~C=J|bJ- zTmshN&Y_~jJ!6-riqZqYIvayj#n4`yuZ5Vv>2r&{FTC#X{A>q!b_^n*Gv3|UM_6KH zdh>^hkF2IUC%hWdl{o6ZZvO4l#a%+6%rA-G*$Ul8dy5RP%&ic@Apnx`*2d|!$yybO z`{H!n#w(~#bL6c4%yO7Z`~kqF&wP1$PIY}5Fl_Ms?-P9TKTuo4q9zI@?3o`WgJoJ7 ztdDNtQ4??6qnmi{e!986FTK2J-pRc;4vrCpqkm|KAiXcI_Ubwf&tk?q%ts(Jgs|{e zuU-unF^1iS*kO(iD@t|lfVHT(<`IkM=`nyY0oE4sidU9CO`m+4J^8evD$nI}v z`Z@xr4)$LhEnUk&dVha=_2%>aCETP-=x@jp4H!WTt}EaSGlnE-32-sYxO`X#6lF^i z>LKHOu1*^6r0v>JwkJwbo7Jw-Y?CI@>yp07 zz1i^a_UHQ#m)9W=t_U!-q;HZ})zsF_Q?46o_h5HP0*@AGR`3NwX^Z;eqsNa}*&41c zhOU96kQR``m)=#d7Ur8z&u;A<>Ez3}K|VrR5Gp>_B`tp$O*qju^!n{3wUm6pK~HcS zjurfuV%jM;%61_6%eZ|(XG3n%`fN;V6a5{zp_}XJ+g#TQ-|69f8Aj<##5pEPj$@rv zwMR)OBeoKOdTUFdZylA#IM#;j|Ca2NM(3@&lqPud7jS=@> z4e0WAI(BB(qk67Lw$z9@8Y+W8C-H_gn48W!NiO7{j%B;LqJN>h6Pd=rG}x-MGWYC z#grHD07`>s-#NacpsDwOeKq(W3=hLS)Fp@NLt8jaM`stM;sf4rW@#n^SA~(qXPF&< z75~ci>pU#^#)JgN4r&nf%Ccx4l?<&a9zd}Xw`^$9EIlFw`w@_NTL|{^JZ5u2Bx2>n zbYa`OQSwndG?&M~!M68zq1&s1c!zUW)v-=cPkTir)bB`cA(N1 zoaIp`jTEQ-;;4TcqbYhYzuY%NBFkjT<8eJW zTnctfS zH9@0A&cN3K~a>G<)pop+$3Xcew zMOY0z8V=NY5pju|2Z-*WILLcTodq5p=)qyxLtqi<=QJ%fSOl)|TLyHb-86}$uC zX9L~X9J#e`shlo#D4gZsc+LO~^q_5cG|{G*F{1YBZsN`aLyK@ zQfTo2gKzh@k=Hj&06Yngd6TWzz8=#u8pW#E)Ee57zM80AY%)j`upmP4%@m3C_>V}o zxVs&GzI=Os1NqeE%jT4gW@QtM)WF2LTr!#cfE(Pht3e}r} z1?lR8UpP(2k1ghg*09Qby!m-ygEx%z8oVL9>GS1RKm5n_SGtg`lk(Y0TjV@6Ce6^wQ_=}*z;erm6d9pR@(d&g z6SBvwnPu2pG(Wfsl!o!Bp4zx-ARw5WfkB8Mit&+3cZ|SW{-@7{VMIY` zkix$lNnB7F6fyM%pIUElSAn3O@4rT>&568%u9$~w;R7jMG*=?Ac>-b(Qf*W0EX>~0;rgac#i_~bhXUx=PCL~`N4Oj@H!T6KAC$^`EP zpX3ZFt}VsO_)8p^yHoTE05QlFzJ_QB{EJIHMB?V{lPfe!gvt4j{DY09hP(IQ>$4v+ zLF4OyQP9r|yh&+73dNSl_3ERJF+50H|e7?IJ<8*z7*czw{0K{`iKy$_c;7%PB;bse|BM}?&fc#!06l|j4!&~+x zgB0PvD*e}ofhhBrk0<~3Z%heJpPd}fA-~jV$Xf@0f%MN3F&X_3zYLyneDDg11kL+{ zsUj9fn?7fCGzFM-C?*qE%2~RQO53rguKOqX1FVo48E~DtCudd8K9}4kG9@B6As7+u zFPaZwwoS>y;73Rr`(~Mm!=gLIoLZ%qD?O%71vBAEtVwl=RhNO~O*x74mmKGFcfGl4Jm7%tAbIl}@ur3C@ z#84?iOh9o!S?coMToN_hlDLDIFqP;_&sct>sPaxQIV7H{NN0SRoM_E^5-d7-NBmSU zEEcH<<%K8!=-@^olMm{=3KJ6mSVa@#WA&*&Fi{UO9uDcqQK9@+KWFhgJqF)~kwX=9 zpr6j~FW&z3;OgTQy|f!=$H1@DGix#vUKZ8<X2;yfqKmN!fyb{f1X9=VeJigz^ zc{E4MQ3b8LU#?)@zq(#Dh2(11(#Ig*z~@#{$A{7>7GO5_%2trGE#A-tZViot5pnvKk}iyaV-^;< zTIn!`7%Km)@i#!sBDS7laO#s^B=`0(W-J{GKr_b_`btEYjr~*` z=xJdr8i2R1_&Wq9*I|5uKKT8T;MfXb<8fO+-%1lwq9aMpR+=&%bIjCrq27Hk5~ZFD z-Sk>m@oPWs@DXv#E$_yxf>$)-uB$Ip?4d|Np(YE;Qm@2xfs}QW=I9X2oB@|Mx3J$9eW;C zH5b9z1|o-KXc$p&Wa7OdPqm9FzrMK4?A1@iHfgFQVG(VHa7eSrKZrFq+9E=qjGGw-i zCMr;TbUe*=2E?-8f4;rG8}5I(ddr$f7cmN?8OF0i=_SebCfqCsaWvrs4~LG>k``uQ ztHVq2q&gdcLS*tqy>6E4f~_*%1&Kk@<=-^AXDiw64$3*A&-?b$r{Uej{lya*z#dOx z1<213bl+dzzx`Cx$} zyYL`8!qJ}%2QIukG*cLui@;&+nGExKfW`t0!69>NYozW@(Gw*(bk(j=bbjSU$%khQ zp?7#1!QrXQNGcx8)Fc!c`DS%_I(ze?>z%iH=r0K{?q9xpdG z%$6>uPcCMUCl@ng|9;A;;!DklM)_X#A&rK0Kz;7jdT|%c(j$6@(>|a-r(97jxIu8L z*WM8TQ$SOEasaXmQqmDPo$zgOR1-g5+UNlRO2vPed!+TgV-@%ra&E|=u0q;GuKTgsE3e*xP&6iT>TCvWufF@#kHVy|= zjdT$C-MUhR?yliIzp*Qlt!6S-Pg$-q5Q#uY8Y)bRl%*3Rp_HQASzQ%t#o_`Z6HD5a z4W*82b*v|XMQ6jaSML(D$VkiE$QJmqal$x42#-0whK(f~sEwxC7N~&M5S8}{O`Lp! zGXxBYzL5e&-=CEBQ|bn%BR7z6t<6rr!pIFq)k(fES&MM(Etk)u<_0-wTs&_?yxLxj zzc=yv$J;VKu5xI%Ub6SzU@V` zbl4y+uwWrr5*60+PyhZ?Cm-E8`gV^ztpC?g2l$VEQ!(EMse@`YAVS$U?)y%kYOAv6 zxt6>|N?#xHXjorL_>|;@C@v&@qiI;ecoJ!=sGyC&Y1u=TmuHH;>Lz_Zbrnmky+qBE z1_ucQ29Q%$*sLXyI^DakgJeQP)|3g+sLG=l==J-z_mx4`tkcC7J3tG5_l`AWQwgf< z7$vEp$Xs-F!E3g2q=;+~VRU)FMsyVY6GrGFA0Z(;1Wtc!554FSgZnLN@dZ9IC6e{_v!NO)%z>?t$rG zYlCMn=lG>a5(JV||6`I*dJDzAHo&tU(-Gn>%q}?r(Ym~NXOpf(7h`5E|%Chb}wZe;j_g`OF+A zL-iEe3KLb~&JbL_6jm{Pz96JRJj|8SNf zx>157S!Vg}WHID%v5a1WbIwLztMS*L&-XFw2pvb+`)e`E!SB$ZoZprr83f4T`uKN} z-fIJ@(|wOla<*DeF`&AIjd!*98~q^sIHwA>@E6j+JZn~F0@rDnMgo|e0sjk%K71!i_h<-(=|w|RAQE=+v`+gsKjJ(foGHGXt6U$Zk4!` zsu!G|t6usE3LafvC-x;D%ND{4;A%!p7b3c82>b(?z0HB31`GDygh z%%3pGDPBlI+!epw6&+odj6q|^r97*pzF-VWB^-PYqLL7tO#Sl*$d{nrxo$AE0Chj}$r?%7G=WNC5#Jkd(^NSx`Fv=LzRUmMSL~ zz}VYoSe{ATPqsp3EDwhp!gK4pEhkz_yo=7go~83{E*1h})bg@?N3h8HWvLqJp2mgK z^oqF8bZ?&UQsukgjPveY2T7|VNY)u_78$UE5Iso|2IUzPn?{Wdci8e4bR+XX%^b=! zZ6n7k93O2C$4Ag?kcNqWO0+^+Ejbt#Jjw(dkL$s@Mf}sNML18)CwYe+o~-8)Bf$yI z*opz+U8sr@?OBMAU|N;`z!xF`K)|L}zFylv=2qf3*Beb6abf{Mt&Yc&lMK}+n9UPn zmqDh}H~E&pCVo}+8+uEt|=lRf@Vg+ z++ZhbC1M&ZH&&xx@D&B2rSbP0M-v+JL;0-zjDbYe4V{cw#4$AlMGZJV=nmOd0MqK@%0WLYma(?23<$H3p&ia zob`0D@?pxXGn$a{>WmLW^O?%)QBv`MJ_s~UlNQa=1{C=qYQc+BbD>W2D24zFvUX19 z`%vXTY4G?HuwRV43`+z8*wKsV+b&ZLJIMDn)P#~Os*N;OXbQo zQ7a*+(%IR`duJ|7^JGZPv8-X&&>X|{ER#vrXdR1PyEU{GX!yR&T;R9oapYWY5nK^i zjt?q{B$4`29kqybVUku5#}Z3@mJ!mqg0_%pJuIX3G4!5+LX|aBTrf$Cpa_v44+~i8 zMOr^$(JVcp#5-;rpnN;4Yw#CTMf-Gli}rFiDS9ieBT=BWRzX-WNpGs5ZKXMj%KI`~ zbL&BHxrDxXDSyMGT4yQC12&f2TkoOF3JApG6B=c{D_`4W=f{s^nErJ0%Ox_((P8MX zm3`V6u5B@nx<<2yg+jI zqE_V(k!ph==26oY7v7PV%d5q{DfK|){70ax@Y4KsNX|}`uSr>?`ejqXE>wkk8WB1$d zmwzyCaK6aXWb7}{&O1;*XOOs+z(Zj!buP5iuycY*yzRHhj*A#X$3-xomS7~&PPJh} zYMi2whm(*SM+*+hRa^BigsC~e6n_h)yLOTe1NHfTz4v?!dTn&_guD6FEyouJ}Rq$QuDHLY#A5uwy{Rb$UsE%FlFA#wamE_muXRGqA(=HX@ z3S=U{RrhWi3?q@*-g$*w_7mv#@S)(h8jpH2bS83vxue#*Aj(&ZhzV^3>FZb>>j#hy zqv0Ju2}O9D$B)PLaAB_w=x~32%qOU*RlT$YlWcW?dd~XPGt%Ki$KhJl;%B-ALo^6E z2o1;Co@DJMDV*a8dU-Y#3PjW!v$Z@yJ3iV*`MyJxk7iQ=)vZMPfDze(?5-8bDOJ2? z!1{;XYI%ZIq!hclyMuqE;dm68C85T?-YjzafEv?)l+#8p`|$;5lK5m%to1%CO=qt}9iNqCu#xj@>o21HGb1AXHKBVhNsn35x ztqN3@0nqL4?;hi_(Jp-B%ushkN9ujoTnPYV&BDrQRD1|#+aZm;2bu>mw4c5xy0N-xSPmvi(rwNmmmOkCzvcfKZpfbSq zZ-!sq-rb^0_?tVFvAnpyWWKXJ1N7>8&a@Z=Yy-1Lm1{`8BRD8(IO|Rk zNeb^kXf4I@Q6U$DvVnZgv? z9LycfBog%6+iHKwz|{;~U%WwPv*W1=&d45apIFm~=N%f6iiq$aO_*XxNwYA+#cZc( zhjmUW=B&JAdSmLpGPO-G_r-6N^UvaB-B+%LmHNu-nR--WE!-w8hZB(;L> zCUj;=R2W3_tqz9BU3$FQTBGdm|-#pTae?Q+mIX&by`uqLY zm^jH1MKi(=tb$Z`+R6iND#ZEWPt3jekY&VLIeAXFS^mTu0l0TS`j9$Hc zR2pv~!{dC^eCFl+1wDYBY}-&%qK-2%LA6qcoA387xfNRNvIE1+7yZxS$G`D@d+gZN z7v&7iKfhsRGl?KLR!S!$3!J>tb&RsG5YqM#0+R~yCwb}Qz{7irsyKV_RSc}@l{hpr zD&by&|9$#kcn?g*H4b(cD|4Y)m%)2~{BZN;;=_Q(%%5*B2e9i(T^6-R$+n~}OTnp` zMQy1snh0TdV+)Z$-YHsQo>y-C;GJU@r|=HYsdteYzg7*#t zzLWT?1K-n}Q%IGzKh@X(amj=U7DQIOO->_pSP3%R0)4JGLytEoc_E#Wt=SdI9E<2f zeomjp07cpbVUMUGGQ5RnQ|cGk$;j=VMQhGDes#Kg&^99J&CpsSAb3Duv8wQ*@6{7C1(eDV4{_70A->HM(hK zZsLIuuG6wcUVWRAW$SBNwrRjc2t+lO2+Bn}E%j{w7<20=v|_?)U4DgG5V32mp@D+L zH#iXo+k|m+`Ed$qYa*!v!9iV_gMBz(5S({>)K(U*Oa~y;W;$416~~GflF3RAy0H7t zrXldrY#0)y*S;60lOEmdUR{1jRkIE^v12Am&hY~|MGNi!Q`rCIy$}$P#~HBJ0Kjdv zIoqmfFC#bn9dHj>8R{hRc=pn0To+Bk3sOl54DmZjOfnX{ygp$`73r2V#4y~hbxA{hW+e^V z8w|bB+;%j;W(La{>}O^th4MdvW9shxcI&LWh=Jx(=Qx5?Y5lKuX1cv8XYpx;d(k!?cJ|qGd6fm1U zUW-DqAw7Fp?eg2y>T64z$dVt>XcIC#oE1xNNW7R*>dn(ghEMho40SNT2V2h&d5{3_ zoPh@08c75m#ry;KDCM!t_*3d9R=@4AeT5(0VtB9Pi@Wo%B=(x9f#{)`f&;DFBvYFV8ei$0#= z-uh?cvrF-zh359oZMwW_lD}2?i8^OW?31EdbhiZQz_;c|7Q0^z;8gH9wq1ro0Ns$?Y19^Ve|xgF zNFXg#KVC*hZBX{*-`-vPt)w@R*da>BkE}HTmqi?60W0$ADMSScVIaqFS?Qi(UNuUc z;Y3}W>(lDZ&{28hilk-zV{PfgyJ4VLIY8P(^21B^b^$}W*7W6r(?wlw6s>E3=@ur~ z11t?VxWkKS$$3z;Mp;Nm__jU>-v*mY0b&kGdFA1lTg{p=Ia@nOdYGi&zMuY3xtKCV zT5mW(7d<4jfWYE-u?fp8X^l#LIi8BxvpGFZIi%g2dFpBN08bqF8H2S`61a67t#5xc ztgDACafTiuV3NnXbU8n{hsFNM$4j%JEb*_&zdT-AVpl}`f*J6L-K1VjnmqB@h{ve9 zXqEEMEkQ}9gpISaQveOkNQ~Dg885^Mcyao5PR)#wVbSH>HX<~O#|(x;to-W~cBas_ zm)7x!Z*&Kgy`BDwf02YR*asxpkhOn7i=B^eTzSY@f158)yc}U*49W-8hz?jpK3N`% z_ip`*>D5q_$Hq7oCW<5`K_!y$8nBeD9VTM7yFt-(MHTDJnvBD8XC$*Juq>;c!*G3b zb^YNAwSua0n>e*Hq*9xeuAT!i;M~l&9~Yhk<}esI%dm1Jq^vU!b>oQDuL;pbPwD%-;2Eg5 z_W&J{6Cw3H6fHPKNOrPm=xr9pn4J*3%i@G+aihOwMEZBHD(yLpTTh-{sPmgBxW9pz zI%@XV^|SMs-i1mch$B3}QqwP5tC<;dEHwiHW~n_x9Yr0(qcv@|04Mr8TDA6fy$iG< zNg}3bcsm_*-g{7!BDoW)CL?DSFHM?C`srH?rO^r{_ntU(MtQZ+DcFX%@f=ZC?kZhhwLArbbI>gCserhOlMcfy7%pE29ha3BN?_Cx45?2nCQ8~ai0c+=!_Ss#h_WJ;PbNo@bL$bS`-;dj zdk7FH(Z7NBt)&FzaW!Ttr%O{1#7Z4-0KC}C{YYWBCGVGTCiY?#7Dpy0aSCjKvHe!6 z>EJGvY7%NzZ+u?Akz+8w`PXx+jlHY0n}gIP)g)e656O<;Y_)$W%LpKXvdv}Ua*p9x z2^pc)_fPcwlc826MfUPMUk#fW4lgdQuBXF_M^i+amqo=ky05;g-1EW=8|iMy!x)D0 zELf?Fn-}5>T11AwLxB0yTGUQhvex=IAJ`6^UAZ8m!R&H`5x~KLCEvKKxT?V;&Df7g%A|imRjdmNE>sgf0a@=8LiXGT&qvX&#hxAsJ-*BqQIDr%fv_Jvz-a|N8b68!KKz3hP-YS1c z&$SN@!YCuXOW_hUF{A{dCK;y4x_?M+om-XmxAW%cLrU?pCvK}(^PIn-b zC|0h7(Pagd_m&mZkgQ>>M#YCzpUuk;u{c``XXxkd4TXS1sDAZ$T(1?12ZtwXG~GMkxo~TB;2Bv zPAIpizr9C*Rak%_c|?T+LDzM4`w)b;!1S7e0w?8N;nPQ?>$@l0bL5L}*FEX#k+L@< ztMKv^vdS7+&r)`K`NO2|I)|fXa-tgbgnS3d2tehYZI+!9JC)Wk7f;R51p8!%PfF&vUpXKTwV(-pKJnXa>>nll^Trua<_qN8D*PW>qylab7tOgL_BRTK1#^#$dF zSxjv~`DjVE=>u(d9~#?l6q8hdD=MAv7_Yg_;Xv)-nUS;uK$t z={iflKKUD2oBcJ_pH|F^J*B`$h7*AGt_$Kdx=5MpP174ff%ixTjb_wHRzJ2kLr+Yn zN{M}I56+Vsi1MMir;;YP1v4b`8%8t!zCN`4oMx8RAaYDQjhdl&1d1dsuRfzp%0AS$ zP_k&2UPOO5+~DUs{x3dsS~H+Y;oX4z_e;oJD9vJjzrVad>#NIF<*Yl;+tIp+2L=3; z9lceKs6MDD9w)CpN;=l(v|y4}KY%wUv(7Un+8%Z)mXs~^M8DJ~su6ijfa&#X_Tvd! zs4j5hJ4I+O-eA%))<^LsR<_L(Cy@cRe~4g^9auRbY=|65Qj$+(6x9Auw9c2;vf5hY zC9WpdqbdZ@8yUolTGxfSyz049D~JC{8`$h0ZnTnZA|d|5MYLQw0aRr;+jy}DOX$w& z3haZ>r4oTi%|}>IrZ-Qpw-`m?Np2#66G<_K=k-iX7fNk>2t=JYkRGF{?^QB^izk)g z2T;SPl>MX0($~sm;$J0~@D&;d&dHu0PxLFy+4@T!NvLRW3@pfqn|el^p@5@#G9{kQ z=V(zx7eT$u?3SNLCQ+k$e~wPO5ir62tIV1Dn2JJCub}`hO&Zhb7k!_amI75k0V}K9 zfDlT=b;)3M`t9-QOV}q-x*4LS$_%l>O?Kn0nuQ|LvP2JT=+W>kYHJQOHa>q&(;-yN zIUfg|F3E(19*TwF15wsc;M?7&tB+0O8_6N$aMZ(@4kRP`7r)~Z{W=PZ1BGGpKU%5U zIP!9GFnjmx-O&&PBX-|ooGnz*uonz->nW0e4ecCOSJ209_H4GYy1u^hY;E@J+4|+5 zR-Wxb^}`%w%(4l^K5UUN?l>)Wem5lq}1-BmP(MB zZy`F}U0_?Kd4r^;8kv)oyH8lAAS_-^L&{<03ARR?!El*?oEObonAJdD^CW0!Tf-^B z23n&iDG9NV)TG9*hG(To1;JoL7Bf>}(O_AMaGUf5gv2%2mCO%X-n*w@a`%`6sP z;63gNFRJ!j3{pmQdKr1JG8+;g+)P0!S^#kxg*$8L16Q&Q_^6gfh6N7l0Yi%y8nwMl zRUSNv7)i~u((A|>VMyJWf46-IVr9YQl*Q3xJ(9h|uGd?G+JZ^87goE=$tbTx*Z?v! zlr$ZN&HWcIcA%6aGwj9DA&Jd^cV?4jiWg%pF8_xTtJ8y1Q@l*vi}XO=>=Zqrt`THvrgTJSh=da4{W$yww&ZkFw-S_7@FFnXD(Ik+ zGOj0l)IrMo*K?O1@4JECJvS5bs!is1IOdKgItnEZshIb^UZ@DowRB~f(QZe z2yk8*GVif>ud8j8_ku}!u`?T5xN=H#ZUEI6>uU7iDR*Q)J-lm|D;E^kl^FJPT!zL3 zvW%cxxo%yf4NJUE5`Oww-qyD;v}d(wmiEIEKnk2gMYJ76%~|A}xwh+cjrBmAb2Uj~ z7UUgeR|kPAb0>~x9)^&&C_g8-5q5$IuT`T#WC#}I1$JdIJn*^g%>z_U&=0>-S1CIa zw0%39QtkTa_WdwBR93&(2Xu`Pzf$QKpo5k$8oU?r`jgwb42RNzXET-s&oJMAFic?N z+P#Ms>sYo3iIGJ+d6s#_TP=OFC8GT~I-2;}WF&&z-$1Ad1*BPZm9?@rI@fW)=fHk5 z3vMrcym|K-c4{cP-rUVUVRBoV^my{eKOzEFGpo;;mpDaUBFyLsf}AovtHpLjh9V2e z^=WQ3g!kglq)&_N9z&|SdMmR%BU<0}};Xe`+yFA(GW?(+7w;=u`& zQ4zMeeh~Sst@-};5*(;^_qU(l-a}je>Ei8Q^GWzV`u?!sxsq5B`h@X&nR2ut7eN%& zCUPI#a#+I-YHMzg=UDL{ERVH*usjQA9)^;YZ_n@okF|2u*!i~3&W;=P3lKuJh*3FP zC)T>py{)ac5ygX1)T(}d-~?Pn;soxhHfys=dNOkcKG0~kN?A7b69Wto3X`%KDKAA6 zF(1AX0RW)j(Q!BoZ*Fcr47kqA;r+!2=FOqNP3prqZBo#I`&0#L7Rn1Muka~CACs>0 z+aYFuyK#z+JRWTAe(D{BdT$_%HPb5yVkT{H`UP$z!dyV$g z8tpSK7epq~Riq)oy@~f)GSoVtQvw#!0V(^%h-IMSHFr{`K`68nU?BmFPwwR#G(rDB za^~1nnAT5GO5wG#NNTx-|G`IK*Az;%rfQIpN;M4N_~Zm>mWAu_1T9XFWsT;z2xP=8 zFRw0(b6E7B>T!D@AgZjDTNgp)+Dl&;gLkr#N>)%1D_69GuwZ7a(&`Gohe!wB~74Y)qi=# zQjo$~Ail2#WomZ_yX%@j3Uzz-MY}%|0sD6UcpyoHfByG1DFbdIyWD}&Dn!>6rN(#16D7@Mu`q){3y`5Yc{R{4Ru#Q&~2#}}s?q~#`e-08T<-H6?T%ahZGb!9rtCm1Cjhf*|B zT1e}!SG{;;E>njQ~ zmQ2ATM2p*vIY`{u(&ew8(Daef5|1YfM`ROqlRGS;o~zZMWSfDKqhdF%-<@CIUjFaT zm&m7z)}(%RAH=naVCr@YyNv~twKMDI+G#>Sd(2B57FrPCH~f#RE*GqW8xEmO$lgU& z(-5-2n@vHndc15IU-AN?_lTAejLWenlrG+7fCW?VE>zSnX}nRRIj}1ftM2gmT(`-E z!i2H*2!`BNQougyy&u5pYKN>xPamu>+8Qmplm+Mwq9>QJiRUJ-7?QBepAJ*fxda}` zIkEj{jU4?sIwWIp_IPivqiWL`E#{yc>}QE!0f^7bk3QU>JEs0&1a>+dCwHzkSZ6HL zbWDYT26YltDAS0Ek#ptVh1u6bPi{nGFa6p?fHS|@$A$r;C~>uOI-BgAen0ymw8Ipt?Hr)F z5#+GOIMQH3!pItuqjMQV4R{MTrvaMvXKCcxm|zh3d$u+&yO=3KlBwz&`==1(+5Yh5 zj=AiZZNEStL94vR)V(sy9DuXo$Ol?UK+@w-8gddCoG{JgEnZF~dsVI3gE- ziaBe0*IrVjsyLdEI6bkVBGkI8+{IB?FzK|UFz(;=MjTXwX*E5B+ou^cKMlX$+$!iQn}V%+z|^Y;@CHez(V1ESSt94bf~+wQ$r4an1k1KrF%m0C zW-CH%H9=5U9c|WW5P-t3$D>X9j16_Iv!nE(bSjXDM+TdEOl!%YMu-leR)(prOHL!HD87n_gv! z3l`3;)9+BNqXCRSqH#)VUT~z|R)3E6wtoT{WFP;fZ%9)d*&+at#Tb$34^J8N35M}< zz#V+Rdms`iW-)!!DO;6;2FY6ejpjR`Lh7Zyva*a0A+i(+?$p?c_O-lLyS2^?ySHzl z(4v4Fe8~I_&|!aeVQ)lbd$5a1da}XZ3#QsU>t&3T4*@{<~%A);88Xa;TBKn zYH(woTmzZW?|kE)6SRK_Z%pydZ{2*pMl}|O?;KsYZBer{w@sb{3qS1Uurw7(*oBoPOc)1*2uJAk{-Y;5Wh>WiKtaI$I!w| zRnl4qE|s*xqs&SRnsnufGF;L1gk>dZ+7BIJ3*Vxe&nbGDCwsmaS)q{4;$ekGG$k*q!H_6re1t8Jv0KCtw0J)C5ZAus?wq&l?B0q?iGZ-W7S8GDQ8j^{PsG=w0`PQoW&5CYvzY&?xFr)K5>V_PsZ=PKX)Hjg zA};^rPBzoup_L5*d}Q4-yLju2eQ>JIlP-7mLS+eE{#P!h_*{8riH?>#E#qVuPQ3JW znL~;77Qi*%n%i%GqR(ghxvrfnGu*VoGKlwJ%Ygr%y}OKVT*>!-uQW3=rWunqLvxy$ znbO8)N}8FOnVFfHnVFfHnfs}-zMAQ1=BzvS-1FjD>z=*#?9copTbBNo%5tf!=yo;j zU6ZdM&q>A4YybK<(&qA>il+SE>^|Z`_&?_dHdzsWk*8pA?y^;C2iFqg2_l32;>NdO z5p9A?i<=92y`mSjT0}c}PiOHF8f*_S8H|pQ4yKS!?Y;7E+XdT0&7sC%In`h@iMvp8 z1M)n<4Z~_wE+B7ED3=jp4{?i=K2P)D2w&$GfgjA7Y(}TS-dIr*D2nn&{1c=^>zOD) z!ax?20{^5C|CRH#fAl$(nDO`g{~K}!ZDob|#s5kqN-!|`plE}GY92mxFj3U?O{ZIb zeNLmB|DWVAhj^09=W@4Df40G2Q%Py7|R*^NZ`|hw0{r>E@Tv%`c&wUs5-}q;7sG-TYFz`K5L9OY7#B z(akTTn_pHpzpQS4Io2ecYZmP>}UH0m-UzeNfa!Xxqt;=n7 zxxFq&=yFG0?ySpQb-BAP_tfRyy4+Wn`|I*RT^_8yjGXj>+(ij-mJ@8b$Pok@6_ep zy1ZAH_v`XOT|TVKM|JtMk}S%UAl)ZaG^I_H`O%fA6^)@>i!wi^lI-sfKqZz^DoT#6 zbdHkaC`+Q`xXRTiIi36C6!a(ZQD zl$=4i8zpB{lEnIZd6|^3C^@qd86^iR3!>yK%7rL7s}eKz-^RC|M&77e+O|scuJSg>T;BW zLp(mp&Mk73ol~SJJDbQ+b{3JNyxAg0d2>XLQe2Uv6i4JJ8z*vT)_=POr-ubUC9gXVT@&x*V*_ zS#&w8E@#u_?7EyomvicJE?v&8%XxG;uP*1)<@~xFqRXMWTtJr%x@^>ClP;Te*`mu< zUAF16U6&n7gxIC!Q78ALWJ+OWbd+2~SxA<9QZl8evMWk1rkstEi!0BgxjFQVLL&^CA>sL;hN0xhTGNrt-J4&vgTq4Uo zKbcZdc^f5HQeuijSgxNszp|1wO0J?Pe_fIe^(fgpDqY%GLJgrtv(M$XSq&DG!{9Wy z9Cn}0@AEnx2DjB^v-zxnhedi@g^Qox`&%>%Z60b1wb=b`r@`tmx{V%}*=Dj?%yyf{ z;r1DvCco8W4?IWwhPzAa7H)CPLj3i%c5THIEujv()$O!9T^5U7beUK!9F^M+*`y`yEb?+i3S%{dSAV>!T=b+KWH57!mFg zf6pnxC4TLv9(!Wnvs(;yQ4Y7m?sQn}q5@`v$Lw`EEIu&;#q`%pi+3x$y)U9g`{up~ z@pCNo)h#}o&1wovO8^lgT-O>8hr+XLw%O3ef!qk!rQg>{hKg3gDc(=7+inyu})2tY?s9D>=e)Q+I?=H-)%N|ofiK; zHdWk^H_{g&4`$K%YZAMG+hTLL{3esfXtlU~MxS5Q*X{Hg4dOzmY%klki|~peJ^$E3 zd#KHA6uX^QG=*N1*=4f19AZE8TEyWhj&ie6eJQbl;hj3RkVmM+9_lyw+)lsS>UW9* z!{M}hjACDKI=u#u$Kn?I>OU*ltaT^(-ivlXyj4cO*KHO%x5w`nO^wB4vRTE>AQ}^= z!>1k)YKtqL;0X1LBiJIEc8}R_63x5OCXY40-)DA0G?YO~qG=kxhx<7cag34cB^R8#o_I@hz5n7i_ZY@=un$@tj*=LIjkO0Z>!H?7LA}?v^aM0 zY7VcuO5rYXQ#rW_#!#=%Dw<2H!zvyknsT$vCko}TnG7zY&F|p&l&xz!_3?I(K{R_F zi%~RTR=?Tr^jO_Sqs!y*I~*pvO>H;CoA}r_X4$o6Tm6XsyKo<~2D*tLGF) zq*JuJW)2YX_`oi04>gL`+iMUzh1fZqUXR~sP)jP-UNp|)*4Xkb7q1}xf`MEmOQ?8* z#lJ?I$KWwJ&2F<-G)5+|cbXhVi^E8fyU$+l*dul=>Z_hJK&J?e}^;CNWIL z?zf4L7=K{#|NO`@Swlr79b&h)`i*|O&u_Nb?RJ+ak;7&5nN0?p+Jr^fWUJXC+W>>y zY@b6s-tBiAJbuw+T6|`|*`j+5hxo`h+l)rh=-5Pa=kqv4L+keV<#8!)d?{KM^}{>D z=Tkr9?4eGvt>XChT3jAcB9G5$HjDPrBWmjRo2`DesM_bQ#SrRqc#Jlm-RCje+%Buw z6!Cb2&1f@-L(ydQdje~uJzl)Gp?PNv{)TJi;E*i{0v|G2)SK^c;mz>IqWXKXk~m}u@@OV2D{bh7wcs5iZyjx#ixe) z8selV@SMQjWDS+44py;S7{z;LblQAotJx|}Jp5k2I52#Eb$^vDf%Zb8v2wV?PT}@j z93H>hCt7rI$y)5a)RwTSZvv3jDVvU<%X zk4H4f;*d1RBkLcZL0!XXM#MfE>JtZ!+;zP+v)y8Ki^EiWX!{)=aTr+5VhaLWMk^lS z^ZI1#U#t{NFJ-rdntXQo9H(2XlsJQN zT09=Fm?TchOkS7S`LF9|66w6oi4XHqZDnu$7K+O_uKpyGdqK*y!ib^ zyG{|}YMxj@d#KMZmMV&Ac8j*fCC*R9YdA$EWi#pZ{PRuzM=eaE$rEj%!)`Q*7Tj+V zr%OhoRUATM?=m|~vZ4CNr$kqAqwc^+PI2Dni)iZ#G~=Oec?uyuO>N@n7WcmOi_=Y$ z-yl9JtTvxG_^f}QaQ`(1G*S%U@%_uZe?L_nH7W*;2HudrhLirAN$O+8y_5ds!T)aB zKgXl$7cIxA>Zh@6@4s30znP{!IqL*d>il0lBQR5Zr3&=MM9Z&U?~EqC+REMzsqD3o%HD@) z;w!T31&AiT3d?Wv(Zp9|@sz-P`R!Let{{eC8LWx*(1)!t68qs`oQ#WcJs!j}cpV?% zdyFo=Fv|6cgUK-yny?7Eunl&{K{ys?pzK4I%ioR1@iN8`-DGlp63l?P(SpUX64pa6 zw#QyL94F&^T!W|ZIzGnt_#3l`ZacYtA?UzEdEq8W>0Wo&@Wup{=v(KrK_;wIdWXYeLI!_QbmbkoZ9 zs(}4)6fVaP_y^;Q?p*o2RG0Y{T?{I8!Q?yF{8xQ8ttJ zNyT!GGyOXG0X}8=A51E`m*sLYNOLGkKC(?JmQ#%BRmt_RG1EKZV5wNnIB7OTnM+J6c%JFc@P||^Czj~5iv14kV9} ziseis&&Rd6Qz{;Rh9Y#fRKG@(-z4j*?I)UN0f0 z!yHoa_)u~oEG-pX`L)PRq#=spA-BgKI8-VgKaM;bmrBKl?-uetX^5g6CSSn2_(Cck z|B)OdE^o;7jxSBFDCx-Ar6HpJWE+NI6{&c9U9uP3O2tV*FY*wnSk6fDG+d19q~h`0 z$w%-C-p5z?6=Nh8&lBsN6tiG{v||aZf(@}1cE)}<5~t!K+=P4a1YXAb_y)g8McWrg zTpp6^nM^9`keZww3t(ZXczkJc4QwP8pAoIdoupzp-N{36BF>SD$1f#s!b5l#Z{R!p zg>l7uA=fvZRJ3s+Qt^7F$@Qh;m~GAUf#j*ugo?6)=?BPHq@os&nf{wBj+j9EEk1q& zibVz#4-F`qqJW}F4JfaQ$oAfe<)otBu7rW}Mv?21TVNaPh5c}>R4jZV&cHdi4maT; zJcd`LqMg2p5AX?o!(W&%NPT=#43=h5l`XERJ4~Wr?!>)#Rw~-Ri+BU?;5(^k*FND-l$U$u^kh=eUZui}m<7#J(GJ4@kv&%4=D2JA$QRy>d&% z>lvitbsSQ$yy8+(m%#BM>Q+r=QRl$-De-~d(mIh13`e(=7Qn4NRF|ZwC zd*sznxm^ya*sj7@Tq?G&6qb{U?F=0M!oYTl?X4xJ%l$?ww!0BFm5S~6VsojeLu(Aw zLDZw8%)+ixv0Xi}uT<1$AP$v^I*r6YokYC??TK)zoG$7%6X!}r{TAXY9nXQWAlZs2ot)zCgPZYP8%wiz}q~go? zaH)9S1Zg4GTNHnh%taJsm9(fhPnH%_l)X~%_@mO|qMeY6d`iqeqmkycQYKs#7bY$wxu z;V7xtStdv;E6OyfDB4oyZ$j<+`4RFNX;nqJM!qK%JKGa!bwzn4t)VC%q%{>K&<=|A z4t$@lr3A)?MXN2&!(}@mrU%-=I*O8uEXRt;>0wwwD&E9uQn3@)m)58KYarfFnH!2r z*ix}`cab($lzvjNoY6Q5XGxnV$^xm_DONFkle8)ApG%ytGyR0rttjWD9!0q#6$i~- zso0sGO2z90`Um`Cdu47W#-ocJI9hXY9w}`hwpS`17ij;4*~$5(Eyd&6AH<;^c$_$d z1Byd5ptj#kn(j^uTc_mK#a#hJ8@$CmTT? zhf{DCF2KO~idfznvUa@gAn!x%xIIO_fZFjJIDZk#dBpS=7&xC1)4z~^VRZKQxR@BV z<2gM!3u?!82sv<`BbMtV7sJw65vyZeY>a{P9r64YNb&ZcnF*RnyY?ucNpcMd+-oy{rKm|SMU}-z-RadKj9Ayip}wl2{Acp*8?(>b6`F+ zq8*E1Ni2_5u{JhD7i!lL+K?l$8}`9LI0DDv6r6<%a2c*a?fSzG@;*F*r|<$^!#ns0 zU*J3Zg1<0&9FBiXj43faX2Dz-f@XALF)WQ0u{zep#^}Ko*bX~k57e%w3?XaRRVI+9 z;T&9qD{vid!CiO&kKq}-gg5XWKEYS0UC;SO)~@TsjLY$lNij8M#B7)c3!oLX>px-S zvRD~wVtou;7ZUA+kK777U>EF#18^9Q!AUp+=V9P_l34yK@&??7d+-pRz;k#7Z{Y)c zhHvl_{=guv`^3hCm>kn$X3T-w^(rITjzzE}mdC(#EU|sH$qi97>w)(7f9m^O36@tL z|I_8`AD4{hr^R5*g@OJQu|3-NNzvsI*p8A|0mWDSz;ylc|I_cS2A*$6(UlTdUTG9v z6aTH{|KIX`SQ~#ai|ts5D{wt(--Gv(kKk#%gb(mJ{&(yCKV6^yuj?VY;sWn;5iErj zu?A}W6-~*_uq}4No;U!9<2VfTLyG--4tX(Z{SzC>J8(Z9!?URMOWYy7mIu zhpjOZyJJ5bidsL`|MYQtljZA=-*-&^ihnQ`_0syQQjpW5yn%&0ug`;8zp&O%Rg~$a zF>swsJiaEm0c!nI&B_1O`;(gW%837OAD4jVCC7A_1#_d;E@|z-fBHJvcz&M$hvoct z*WT>cr+=_efFlzm(f$PHJIJ?30`}hpsqSl}Kn;bKm`aUPX4hf$9wn`wf@e}(87`UW6-g18U<{ zwSLT_Oh1E{@fJQrt^e{p`5Vd$Z1Qt6Hp+WA$gK5SW*}$7yl6xRYWRkJYd)HbJf5u{BxV>q5SMckG8lQR{Dc;FS}^{DkT z?j;|=(|8GQqSn9oocs>I;vb9^TU}4BUoizaJ!Zu`Xh5w$u_(DTR>GR7jYHP@5t}o; z9d^cEI1sh|!|~*4I2V`TYSj7-cajg_aXg1tQR^>!On!wQ@drkue=9B~!PJ-ub6|ed z`UeY=!>}Ay#X6|<3wp_|Fao<_U)1^oN0TSvOk9B4_-m~na4XaI;9)$47g6i~yH9?G zZ}AKMMy=m3L0olzN{;fLDDpVSg1J%a@3WDMU@5GKHBjs4Yf5g0ZLt&fM6G{sIC&gS z#W}bbwSK*g4`BFX2k5M_1~GuPAraP zu?lMac8$m$Y>6GPEA~NceEKNzM4W;1aT%_~Ew~#G;YqxJ*YO@c#W(mFe_;&K(J1Q{ zACqBP42*Xd^K+3y(Tat!Bv!!cSP#R|k8Q9c_Q3u)49DUWoQ;ccC2qj&xDSuw8N7^v zar9!l9+F?+d;Es-wl=apu`v;*#0;1X^P&+QSPbPoJ>>E#V=ZimZft?=u?zObK{yg8 z;B=Jt{*bS;6xZNp+=T~G8=rrkd=2m76MT)I@FzwWUufm)#lxhS1~X$$3_%NO;B$P3U-1vd zN~G2)A*R6em=(3(3mC|DEQ+PE64t~9=t6PY5qMw3%Ln}5{sy%U|J&c7*5QBq8`L`d z|E0e{d|eB)1LET?;D37EP3vdXuJ396>R~LWELKLXKT^B?63%oVw!#kB1+{+30pww* z^|MbR&%k-O1Xtk()UK25As@mMsP$87*JW-o{Q*A1H~0yEVs!fB;$c!ugPAcWhM)xt zVF@gc)vzu$K_9lpNbHXNa43$!!0%SX=b$zobRpAM;CkGKdr=z)dYXI*Z{h=dj@tOo zujD@%i+;I;m;$wNpIOOy(13Of{H{f;zc$`e``va;<~KkW%6o;%^AT;FXJ>LR4E(-D z%pXA>kJ|Xox#T6d8aLri)W&rlCkMuRi{%D>_aeN*bnW-x+IY^7O#gw==#PtwNl+Wd zsg3{6!SulRZ?SwcxgcueH_MT$VjXOZUev~IMv%Kp<0&VSXW)EXhHFt9N4cAP2v6b#ypG!V z$*1Hu_!)m;4Ep1=ag)i&X)zdcVJK?jB@2^FVg;;@^-vop=_j|rj@SeHqc%QrEO`pf z#znXiwQ-T#$@}mqp25qgjfZ?leu3}t8!Gh2#l}RK5;I^n%!}IiM+dnWmchzc3$<~N zZgLB3k6o}gYU3S8k|*GFoQK-|6|`}Vo0+}~58??tkJ|XgyW}VM8b9Gr)W$W&qhBs5 zroqgZ6SeV-7IGmhf#tCpYU3E2kbT%1Be6Sb;}?gL$KYg~g$q#|x453X4fo;^JdN6T z#hc^@_#EHiSJcKS#-d*?A*R6em=(40i3YMAi(+Z4gxa{o24oio?pGn&-*)8AsEtPq z+`mH1AHnqTI1T5bHV$z$c@yr$0~ol^g;<_8{_rZ(@8Dy6g&$EHcNmTSxVV@EQ)4F7 z#vA4*o3S8GT}%O$|%m=3dGZY+Q{EP|!5BG$nA*c6*#TMXRCL#$s<@&Fu; z<8Ug@!Ns@=H{uT5kH=8nOIV&iT*2G;2(|lvd?0_vAo}C9`+y`Sr^3MfK*Vyhlk;KV zz93?{lUy9jVil~7jnISIeL^~ryJFyeA!50M$)j*0&cOM&4A77sirU0jpy@3`ak>m$sNJVz z5qTwU!0i~gZ;7bWQSuqQjJNP12JT}b9{-;F4HfziV`Cyri5V~(=0zhquo#xX%2*2< zq8nRad+dU}aS&?vL770Fj`MISuEEW?3+3OMlKayMJdfA#EQ)b8t2iChyKpbML0JM4_Ta3GGr@i-0V;u2hq z+I?Vlk`Lf131)H=n(q?iUXV@?b~3l_o>SRSikU2K9r zY>koF9sA)>9D|c_7B0jUxE{CRUOa-Q@ef1)ALA?hh(9oz_(6-T zPh3obsWB7g!2D>&f*6M7uqxKU#^}XX7=hieFAl-cI0kn#7R-$W(1t~@6jsCKiVjmog zqi`b5!1=fg*Wwo3jfe0gUcl>k51-;2{EWXaMtZeQ@i7^u#bC^Zp=iazSQ0B>b*zWs z=*KqL5qn^N9EM|Y3eLtwxDq$ucHD!>AHx9y)I02{QJY0%va5L`0gLneZ<2AgCPw+K1x>G+x4+_yC{d zJN$}&FjgkDP6;surpK(92MuV)qF5R$VNGm+E^LnNurv0;fj9!k<20O$OK>%A!ku^k zkK;MKig)lazQT|A1LZvt1E2qx1XE)s%z^pQj0G_a%VAZlgN@OPtuO++VP71Aqj3_> z#09t<*Wp&&gNN}HUc?)CAD`h{{DQwRX0TeP1ehGtVHV7d1<-~?uoPCr8dx8jVl!-u zov=|w#81^69?dM9EVeJ4lc%3xDj{YemsU}@e1C?NB9yy;CBqlt=1-cEldoABW*soPx7)5w64yxE=T5 zQ9Of}@fJSB7x*5(p^{gvPi#ztDKP_P!@Ouj2NuIJSQ%?!Lv&*cY>!>AHx9y)I02{Q zJY0%va5L`0gLneZ<2AgCPw+K1x>G+x4+_yC{dJN$}&FjjuGP6;surpK(92MuV)qF5R$ zVNGm+E^LnNurv0;fj9!k<20O$OK>%A!ku^kkK;MKig)lazQT|A1Lfa_34H&@B$ygA zVGhiXW-N$dSPrXV9c+wVY=sfn4g2B{9F3E3CN99`xDL1C9z2Yv@FL#8`}hps;urjl zF+SA&0k7jde2Q=IGycLD2DMJ{F&U=CV9bS~XvM-<5-VVJtcT$!{#I|`_-=z8 zu?P0YVK^42;A~ukD{%vE$9;Gd&){Xeg%9xszQ=E<7}fQSjfpTNX25Ki7sX#E4{WCc zi(wh8jJ2>Ky0Hbe$1d0#2jNJZfYWgvF2yyt8F%49Jb~x&8s5bx_!>XqPmC_EwaIon z9wx;!m>F|o2#P;$A9!9NEP>^*8rH=o=)=|+iQTau4#hDz8E4@_T!HIx8}7v;cp5L^ zO?-gQ@g07}KN!obZf`~#cpbML0JM4_Ta3GGr@i-0V;u2hq zn{X!{z~guhui_nijIZz`{y_P+hytJgm;_T}Cd`5PQT#df!1D@X7?#7TSO*)U7h7Qj zcEi3n1V`f}oQVr?Ij+O4xCam8DZGd`@IF4nxA+BrV@#{My$LWmro$|l8w;Qfi(o0N zh&8Z2HpOPx7CT{29Du`d98SeKxENRAM%;n>@fe=PD|j0p;Y<90-!aIh)+Y`o##ERQ zvtvFqp%aT^S*(J!u@QQ(C3e8B*arvWD4d8ha6T@>wYUX$;~_kW7w|gX!>9NLKjSZq zVOQ%EACqBP48~j-idHO)C9wil$9fo!er$ssu?P0YVK^42;A~ukD{%vE$9;Gd&){Xe zg%9xszQ=DU|8Sgak78pYOoy>SqZ#0fYZ=iyRZ zgPU;|9>f!P96aK{LPPI<)Fe#?N%$O5H(1L}q1eV8YSQne14_jj-cE^4= z6vyCXoP`T<1+K?!xEGJ$X}p9t@c}-^clZ_mV61{_of2XSOpjSH4;s*pMX@wi!kX9s zUDzDkVQ1`x191e7$7whhm*8sLggfy79>;Td74P6tbz5hDK^8l*a>^$0343va4OEh#kdML;tt%8$M7s(!Q1!< zU*ZS+jzLA#`ozJ+ml6=@Vj9ehIWYt+SO`mC zd8~$Yu?hOHHAZ50?1w{f3{J*bxDZ$1dfbM4@d%#AOL!9>;B$P3U-1vd3RCNp5K~}! z%!+x?fOag3rLhv$#0KcX=GYE9V=o+tBXB%U!@0NwSK}t!i3jjFp2Mqn2Or}r{D?m= zS_!p2aWM&|#!Q$4^P?FHVi=ags#pgbqZeCY1a`x|I0Q%IB%Fy0a5=8St+)pd<0-s| zH}F0_!?*Yae`Cy&YMl~ba!iL=FgF%J8y3M*SP^SreQb)&uq}4No;U!9<2am(b8s=P z!i~5C_v0}9Lr)Atc{J(gDtTGcEvt8 z7)RkmoPqOk8Lq`GxEl}QNxXp9@g6?KH~1NUVT{sho#JCMOpC#o3q#S0g|Q@7!0K2J z!_kjzup{=s{x}TB;uM^Xi*O}w!0osXkK!4;jJNP1zQFhR4V5x#ePUxGOoy>SqZ#0fYZ=iyRZgPU;|9>f!P96aK{LWz{;x z!=#u7Gh1XE)s%z^pQj0G_a%VAZlgN@OPtuO++VP71Aqj3_> z#09t<*Wp&&gNN}HUc?)CAD`h{{DQwRW(BoQ2{1XP!z`E^3!n{)U@5GKHLyN5#b($R zJ7G^8fWvVdPQ^L67+2v&+=2V?7@ox|cpD$#OZ+=oZ;3|_`t_z+*LRi32)*9e2(w%EB?V)Rn}bu>rcUIkv;j*b4{Z z2po^oa4s&v)wl_F;sHF4=kO}t!N>RtKjIIJR!yx>Tug$gF%#y%{Ak947>4DrD%Qcq z=*3nTf!(k#4#Ckl31{L0T#oB-EAGL=cnUA#4ZM%f@GXA9-x#yHTBih<9MfSI%#8)m zhDERxR>T@uADdz`Y>S<+Cl0{jI1Z=c99)d6a3k)({df$|;uXA&kMJda!0#ATL#;Td74P6D~0!)tSFbn3!0%*e`SPCm*4Xlq%u^G0-PS_I%;BXv=Q*jP1##Oixci?_JhG+2# z-o{7x5tQ(hu?=>_9@rm; z;aHr4vvCow#0|I|_u)}IgO~9ZKExOJ9>1Z|P_0jFOoS;h17^d#Xha7V!!lSIYhgol zV+(ALU9dL}!jU)ur{g?aifeE)?!tq30?*?$yo*opHGaaM7`>5Nr+AnY(_m)Ii6Lmg zLRbRJV>PUcP0)v}F%r9DKOBl|StoQq3vHEzP4cmR*%IlPK@@G-u^ zkN5+lHBsvm7n5LW%!D~GKbo;1hG99ZigmCtda)HoU^ncGLvS=s!kM@Lm*YCzihJ-d zp2CZG1MlNAe2ZW3H^vNC>y!YKV>--&xv>D+un3mIidX~dV^eH~ZLt&f!~r-Q$Kh0* zgNtz$Zp0n9ACKW#yn?s!5x&F^_#K0qs`ZJ3i7^#s#O#<4P3XkpSQe{bZES=dY>6GP zEB3*`I0`4?44jY4a4l}Z-FOI3;sv~p_wXsc!O!>$W4P2h#m8ir7K1SthN2Y;t4#D*YGYr!Poc+e`0ix zTBmrJ6w_d4%!wgr!9rL9%VRaHi%rmntuYe2V?P{z#T#cJ> zCmz7#cn+`P9ej+h@FV`fXg;+*aWM&|#!Q$4^P?FHVi=ags#pgbqZeCY1a`x|I0Q%I zB%Fy0a5=8St+)pd<0-s|H}F0_!?*Yae`8F)TBih<9MfSI%#8)mhDERxR>T@uADdz` zY>S<+Cl0{jI1Z=c99)d6a3k)({df$|;uXA&kMJda!0#B;Os!8GOpK{8BWA~ZXhJ6z z$Ff)jYhxqyU`y;Td74P6tQ(hu?=>_9@rm;;aHr4vvCow#0|I| z_u)}IgO~9ZKExOJ9>1Z|Uae1TOoS;h17^d#Xha7V!!lSIYhgolV+(ALU9dL}!jU)u zr{g?aifeE)?!tq30?*?$yo*opHGaaM7`=m9r+AnY(_m)Ii6LmgLRbRJV>PUcP0)v} zF%r9DKOBlX+*kl@SOiO9MXZ7Ku_-pgw%7@K;s6|u<8Ug@!Ns@=H{uT5kH_#V zUcuY=2w&m{{Ek7L)cVB1#Fz>*Vs^}jCUjzPEQ?jJHa0>Jw!{wD75m^|9EB5c2F}N2 zxE8nIZajo1@d94Qd-xRJ;Ai}WF*>VtijT=KEe2yQ3`Hvz#*$b8t7AP3M?bc~j@SeH z<1ieHQ*bse!j-rIx8puMif8aL-ol6Y0^j2|RJy43iH(UcC1${Em=}%cz+zYiD`PEe zh;D3w?Xe5?#z8m|C*X9Phf8q{ZpK}B5KrKFyoPu23BJZp_!FadRqGTFlVTdoj5#p` zEm#OkV0o;Db+HNhur)?vckG8laSTqzS-22a;CkGKd+`XK#!Gk;AK-I*hhOmz#_Fcl zDIuo7^q3X%paJbz6iZ_ztceZKh0U=YcE(;f5J%v6oQ89839iOXxDyZHaXg1t@eV%5 zSNIWsV6^UPed1ygOpTc^2j)jJ7Q`?thgGo-HbyVD!U*h!eQ^km#z{C67vOSShg)$E z9>!C65pUpqe1>oF3;xEKJ=8iSz~qRcB8yDe9+<@D0A0EXsco}cuLwten@f#|=)%wK7M3@pY zU^dK)Ms#2?EQ6J?7B)mTw!rq-1$*Nl9ElTfI?ltTxCS@lEQj31bx^VBe6U7!=X3^C*v$!h%0bCZo|EJ1W)57 zyonF+IljZM_y=S4RqK=xQ($_`ih0n0b}Wjeu@ctA2I#`(*bX~mFC2&?a6C@Kxwr&Z z<0jmR2kf1)ALA?hh(9n|Keaw_F$t!|Oqc`nqZtcg7?#7TSO*)U7h7QjcEi3n z1V`f}oQVr?Ij+O4xCam8DZGd`@IF4nxA+BrW6b_)of2SjOov%8Hx@t}7Qs?j5o=(5 zY>LgWEq215H~@#^IGl=ea51jJjkp8%<1svoSMWAI!k72~zhlqu(> z37uFR%VHI*jg8QQEwKZ3#XdL~N8v=Af%9=0uEj058xP@0ynxs79zMl4_!)m;jDc#M z;$t#Qi@}%+L(z(bu_RW&>R1oM(T{DgBlf`lI1I<)6r7EVa3yZQ?YIw*;u*Y*x9}mp z!1wqKl|gEKVq+pqi5V~(=0zhquo#xX%2*212m)jGw)q?iUXV@?b~3l_o>SRSikU2K9rY>koF9sA)>9D|c_ z7B0jUxE{CRUOa-Q@e&f~#>8?!*Il9M9oZyn~PN6@J7Y7;UIppSYL=Q)4E~f%(yl1u+cE zVO6YyjnRv(Fao<_UmSv?aT3nN1-Km7;a1#(hw&6%#2a`YpW$2lg1<55FtttzFgd2f zESMV$pbd*)DXfS!us$}$X4n=xVNV=@!*Lu=#W}bbSK&t7f&1|op2aJ88z13I{D9vv zXt-LRIG7kyVMff3`Ot(;ERJQd3f9I(=)soQ0lQ)!9E_uIBF@11xD40g7Tk@8@FZTq z>v#{J;v4*ozc9uKwNCLd8K%Wx%!Q$7#lrZ1+PM>WtLA@i;JZW<8j&F?Qj|)Ql2VEi z(nt}_ljgY;QfbnxiRMvK6d_8IN(doj&YXEB6`tQZ`@J}?uDx~t_ul8;`+A=Bvb@ha z>$^Yux4wJtwf0_nb!xLd8?iZCu_L>)ABS)_$8s`favqm*B_HHte3IMwB46V>{D@!h zd;ZGA_+)>w^H?6oQ+PVh=DDoG3wa5zU{hYpw(P=Q9LSq_8^`kw-o=Gn&inZ=ALkZ6 z$CvpA_wZAG&7XLXM@&feDHrpx5KrToJckuojTiA!Hssa3o*meYeR(5q)4)M*@uHUjH5Y`(>aHWxq@rBj!$qKpXV;V#Si!yzvVCdokvbi_UULA;7KgXk}S*0 ztj;>TjE&iXt=Wk^*q=i=g5x-avpAp2xQc7Jo}2j$ck*?<%a8dbf8c&*nUd^J4(8$U zEX*@lisf0AHCdMp*o-aNjyJG32k{n;;sj3PY%bzGT+K(gkz2WgukcO2&%OMHKl3+c zo0{xXZszBSEW#2j!%Dn>wOOBy*qp7{k=@ykLpYpcIhiv#k4w3d5ArcS$?bfRukjsz z#4q?ge`Vs1WPh^rSRThycskGKxvatqc?qvzQ(nuq?806g$eVc^$MX)}#f4nX`}r^* z=N3N4m-z&^FBVr4Sb5v@+I!(d;El7@kbtD))~qE| zN?yh5*q&Y4hl4qcqdAe&Ifsk6f@`>rPjDNb=Ptg*5BM3s1Y<apF=r<<2Z%0IG@Y7ifg%^oB0fP@^!w;kNG8k;C^PAmF!Or z=Hc-y%rjVuiE3z6d;-zfJt9d;;up9gGM&8OX zoWvQN%O$**5AacL;?sPAukvkv$j|v5_wf%NH7D7pV_1+Uvl!1}IiANFyqK4>39n%r zc4kiw;7uIK+c}kYaslt=eSC-;_!OVzOWe)(_zAz_k37JvbCdna$-F#)r?NOpvjWd& zE!N|eyo%ScJ-f0G2Xh!lb0Vj64i|F;*Ki%5;5I(bU3`ll@H2kPU-&zZoR{p=(Ja7| zSd=AMmX%qZb$A&YvjtnT6ML{fhjIkRaSCT~K9_M7*K$2K^BL~s>wK3V^Gp7~{me2y z*`FND!{b?)XRs8@vnp${E*r2JTe2N*U~dlMEgZ!OoW|K)#Cy1!k8mTmatB}Gn|z;p z`3-;OZ_KtJ*{9sh&l6dMC0K@)cmZp(J{z$)Td^a%vmb|WILC4_XL25wawQ++V|<#ob1yvEXb2tjAyYN&tnZ<%*)w? z*RTycvnL1eCXVFooXR`7fOqpgKEw@tiqG;T?&f>^gkSMT9$?lb$^PVIUY@{HS)8R= zf#+won#p~FfUD=0&IgFz@k<&Sci@AbpxQhyo`<6g00z!J=mW^IfCOjg|j%H%eabbxt^Q(40rN%zRQpKC4b<4 zW?7c(PY&kc@hr?USc>IYl{HzH4cLq=*^W1`HwW<+j^YGP<7_VCJzUL4xRG1AgRk&S zzR$h)ABS)_$8s`favqm*B_HHt ze3IMwB46V>{D@!hd;ZGA@??Lq^H?6oQ+PVh=DDoG3wa5zU{hYpw(P=Q9LSq_8^`kw z-o=Gn&inZ=ALkZ6$CvpA_wZAG&7XLXN8FR_Q!eIXA)dxFc@8VG8ZY9dY{;v5Jv*=) z`|?KK$}yb88Jx={yq6E~QEuYXe1Wg>ZGOnl`5pK14<5B5*{5SzkSDVk&tf^A#~Qqt zm$M14VH+&m zah7HUp3hpW$18aiuVZ_5WgiaaFplO#PUjph<_fOiIzGW|e4e}b7C+!;{FcA)cOJPi z*{7pffG4piOR_90vpVbWGB#!lwq_^xV1Ewf2#(_v&f5ViW4}Ev$=@(a5W#{MsDQ} zzQQ;8KKJq){>b{Zs1dVmM?KP-{U9zia+uIvp$gQPfq6L z2|Sg>S(+7iK5MZaujEy{j_ui%eK?rIIGPhVopZRDE4YU1_yo7{dG6v{{D7bFTmHh| zdE|r1J{`>hJc&hFl4V($)mev^u`ye)H9N5f`*SEqa2%&_7Uy#rS8*-Zb2FdePQK1} z`7yub58TfzYm@!S!8|;kg?R=`u{^7?ChM{Ro3SO^@dozhAl|}JoWN9PUcL` z<5I5VgM5rnaywt-YkY?v@e6*>UzvC~*`Mq@mdEiFp3bv*F01fDUcxKbl-IH?yRa7r z@@C$~@w|g~aUqxUem=~{xrNX1Wxl~Z{FGnwCm!Sxk0kq)i}_fHr}0dl!-}lNi+Cv; z@@ihs4(!IhypgwZ3@330=W+?}RN3Bct=@=H|$t=dR zSdQnh1~2C2Y{F~UhMn1y19%fh@^((;om{}Xc^@C*20q1S`4V^YJ$}Nk_#+Q6>!Zp3 z|N?yh5*q&Y4hl4qcqdAe&Ifsk6f@`>rPjDNb=Ptg*5BM3s zD-37p2+T*Q00 znvZZJw{izx;hTJ)d-)B2=5NflA=#(g%+C{9ge6#pm3RSbvpyTKIa{$KyR#pMa5%?u zGG}rgmvSW^iE3z6d z;-zfJt9d;;up9gGM&8OXoWvQN%O$**5AacL;?sPAukvkv$j|v5_wf%N^+d8y$FLwz zW-*?{ay*YUcrh<$6JEnM?984Vz?(Rdw{t4*llALEnU&KLO_-{D96g5UF3CblH|lby%%IG)1Oc{a~w6<)|ocmPxA%7%D4F;Kj(Mc$3J+~wq&1< zVL_hEVmyoGcphu;VqVTByoPPqnLRmxH*q9y=TzRw1-zU0@gZ*DQ+$>$aW~)NC;W;( z@&L0wo$OCe=H&@ImBm?_6?i^tu^zAFRlJVv*_C}bn8P@l6FHr8xR@)rhU@qQxAA%I z;#>THpYdD%!ryu1_GF)qW&xhWqAba>tjy}H!^_y1E!di!*n|B!lp{EfQ#gzBxs0p0 zmg~8h&u}MS=ezuvU-Ad;XO?G@{mH>RJf4Ml21~I#tFk8RvH_d1CEM`^_U0ho!cm;S zX`IbPyoam#2sd&ockmUy$@jUJ-|%Pt#%#|f`;?pcc_NFj1k11zFJNufXCpReD|Tde z_Tvx^=U7hWOwQv{uH=J!j8AeqU*v0ihad3^e$QW-crMwW>^zpo@f4oUvw1G7@Iqd~ zE7+9RvMsx?7YFiY-p28~gLiQum-Bu;%*VNf&+%ox!9DzxU-Ks(P!g!l3RKFT8#ULVZI6Iht1^DLHSC01iC z*5ws!!WL}B4(!U_9LSqEf@3*}GdPz^crU}($%(Ax6NzwIIm1m1hnbA!7x*gQ=7;>8 z-*F%R;89tUImfUdPi8Tm#d184HFz;EXA@q-Htfuv9Kf46lDBgz@8kmB&HMNeH}EMw z%a^#D@9`6U#UFWqS+gellaqOQ0#9XemSzQ>&swaKV|#XG9}ea)j^;#8=NvBP z3a;TgKEZ8#p1b%KKj3HlmcQ_K9(hEvPe-!=PhwG)WLZ{bb=Ki!Y|IvH%}(sW{v65? z9LFh~#ra&uRb0#U+{|aVldtn#e#|fV1NSpawq$>DFb|JsVV=QKEYGT}$+~R7W^BoJ zyn($rh_`SQCvX~Pa}n?1YCgh^+{zt%g>UkG?&UZ9nZGex*r7-JyWGsr6Ip~MSca8& z0c*298?iZCu_L>)ABS)_$8s`favqm*B_HHte3IMwB46V>{D@!hd;ZGAQOW*f=dnDF zr|@*1&2w3W7xEHb!KS>HZP|srIFL8 zZGOnl`5pK14<3~x*{5SzkSDVk&tf^A#~Qqtm$M14VH9@JCDqj?9`>%C0Ul0S)Fxw85^?&TeA~; zus?@#1jlg-XK_B4aTV8cJvZ|i?&Rxymml*>{=ogrk~`U-9L&SxS(s5x?N~{FRAglKsif zV|g4;;pse^=dubfb{Zs1dVmM?KP-{U9zia+uIv*u0qCnxjr1fI&`EX@i$pS4(zSMn-e$M)>XJ{-(p z9Lq1$MOl(%S((*YhnKN2 zTd*}du?PEeC`WJ{r*IbMa~W50E!T51pW#lv&Ug7SzvK_x&n)?q{mH>RJf4Ml21~I# ztFk8RvH_d1CEM`^_U0ho!cm;SX`IbPyoam#2sd&ockmUy$@jUJ-|%Pt#%u+Weag-J zJds5hJ`N|(6EL(nWBGsDdEa=P;qaX?zA*d@PhwHVb>VGL#`^!M+cU!W#&Qy;b2b<9 zZm!~kjL&C>%gQpgcPpRcOMIR0@I!vaZ}<}rFpKlI*%_bTE@1g27G+76Wo1@p9bU%9 zY{Ay-#2)O=p&Y?+oWfb0&t+W2wOr55e1<#uI^X5T{E|O#KeIT0n}d0HJPY#-mSTBU zWlh#)12$tzw&M-#%|X0{qd0-nIGc-j4_EUMZsb<(;46HS?{hD|;m`by*__AC&HOx( zMOcDmScw;~HtVwyo3j-=vOD{62#0elCvzs}aVb~wKWjg8f!A4W)@LKe_mgVB7q9>S z`@7`+@87$>`&plVV>Ykn+|17tS%j(jp6d7iw~wdYY)@a_$Xhvv@p0$>weL^=qmNT% z{j1J8yo`+*f6v|8awqm+e-7mcj^h;0;(RV+{C*((GuMpc|9WobGu+AeeZaexKjxSG zf%_RB|8scW=i%`z%rjVu%-j^`b` ziwn7&_w!*s&Mkb7FY^uV;ivqXKk*=s@cnHr=3^nA#xr>iE3z6d;-zfJt9d;;up9gG zM&8OXoWvQN%O$**5AacL;?sPAukvkv$j|v5_wf%N6?Wj!d6HvTkSDVk&tf^A#~Qqt zm$M14VHla}F1C1=nyLpWrq=&s}_rAMi7N%U}3AkM#ZR z(Ja7|Sd=AMmX%qZb$A&YvjtnT6ML{fhjIkRaSCT~K9_M7*D^f(8Lx|f_47ddzUDug zQ^`2~z1IWA_j~B)o$zoaU+*l)lUa;su^i*Pf3+W}ZvWpko`1Ek|5?{rPS@AjT*$k5 zA0OfdKE-Dle@@$N`8|HZulOSmFst`lIhmIy@KhFOX;$F*ti^h~l2`FMwr5xN;b0Eq zXinsG&f#LN;2N&u6Wqqq1$MOl(%S((*YhnKN2Td*}d zu?PEeC`WJ{r*IbMa~W50E!T51pW#lv&Ug7SzvK_x&n(_=S-yv>`3S>5bI%yxR_@>{e3S2UFXQv<|LS?C5?3^S<%x zBi`@*XFm=X{WtfiwfpbHe>(4)>Uj1~eMxn@lihDI9{Ty@U%lO-$Ki|RU(P1HhHcoH zJvo5?w0(uuoz8eW%37|(YK-^m@$vc!zmLat=zi{>`VfyV)%%&E@=G!vSNwT4K2QH+ zxTCBgEPM7~m@_`-=S(Cru3HapQffuNKPHiA7MUlJ=n@$YZZ}01NF*{oj~|yvERB}K zi#+3c@)Hw@=cDEDV80(3nw4=q_-Tp6DLKM&7+m?t(7gJQ#S@9PktGs|8zVzAGd|CT zgIl3!{9$|sYQA8OGhza`VVA{*2>gsP7ythwbQRea8Es zustI!kBJQ1HO=y@$gq7&EH94?+qu^Ay2!A-TP$yn4BNfS^6tp6{XbgX7a97H@x59Y zPsaBMp&tdagx`ndLXn{_8Q&j-<&x2I=ubuKt3-xA)v=uMc`bVKmFYXUd%reqgZ=s> z?HvEqufw2zi8Afm^lOtS)3$HlM49LpWqS1KIH*;dem%Q)Y1gWMh4QUBcIelxbF04n zdiLp%q9Wb8U{9yyc*j$*I@4&-N+rGv%7(E&KKlZT{OH zre4#od#_d<2DR&);)Z_)x9Xhg@d?>~zp`VuHl14a?a(jz2>#6-#09BVwd>ZQO`r6) z&L~Ng>C>TGnf4vp_V3iHOOK8{!*eR*=QeHI_USMn^V)7b+htyC)2B_3P94IA{dN0) z`Qqw=icjA6i#4{64Pho~G{dOm)%m zHdS8pOm#ie)V0r47j0##yl7oKzTRo-qVHPcy6AI6s=R357}xbpQ#UPBUB5JS3o_O9 zPgA!tQ(bg?PBp%0UmTBbV4Auund%0msoR;UZg85qw=>nY`&sydBYF zlWKbt;TRCt-IS*8=uCAtr>TpMJ#k)iULe)|M#rYOZdjVSa+&IGO;ZTpM zb#dK@G<8ig)s0M3*E&<(ZE5PdWvUyMrfyKCy3uLsMrNvuUQ?<1JULU{*fe#~In}r? z(dXS%dCN1^-JYgyZKk^MY3ep*s+*9eF1nT=9$$1GBGveIXR4c&rtYImb(7Q7MdyO! z`<;@e?qH_6scGt>YZ>FbJJQq@%v2Zc3sY@xbPYtDH$6>V=}dJq($rPSR5vqCU33mR zzTa7C>KbOMyE9E)bPZ;lcUPLa&Y9|Fr>Tps-H7v|^Hiz&H!M@#+%$FLGu6#YQx~0k zkMDPWnz|*K>K3G_i>@7w^A@J5+mNYlQJT8wnv*zhai+TPaU(huME`_4W7-w!GEebh z(eTTtF1+2zm=3Lr)qsf+$r#NXvb$N9|n+cHJn!9(hzYi$26 zFS=$db6#}K&EM5kKBR6*in@k})NM#n*XxkFT`B6KYstfZe?G6i40X|C9#8S(5nWT3 zxh}dk=ajf87}rI|fy{N$H9CJ+7k!tPxh}d^=kNNGD?C3k*L6-&SNxE=VJYhB9a1+d zMO_zl@g_!pyW-gV@ltY3(Nc#{7fwwiYDNoT+H*pVM7>NqSI=S@8%uU_#Si3MSOl-D%Oab9%Ie0-bm>%`k3Z(a0*DPFhqki5MiFM94q zc~^xw&Wpa|jBgV~ObcJ4RQQ|ijjqklyuDRQKh}nxCH|5xozO@;Zh&)%a$Hy!Yae zP#6Dr=<{Vy$SWD16H#90Gf!YkRz`Wz_M{r$hvy#N^JRJX z;~FJHPL$UzP2Q2^4=-mylB4?jjvyrQ;qMdkk|3pI4$+_Wp~Kyl{c9;AWdGAT8YHe zbo;;XoQtM<4Ra*&hCxN+8x-bLy^jU3@7f9{{% z@8Wz}62-&0^LR>?H>^g^#J+G&JsRK8FsB;d_Hd$McpMf?l{ctq&cyPtKFW*FZ~Wc( z>W7P<&OD5~BF%FC`T7_Z>QjxcSIAp)IFCoji{6_=C{uR0=XQD``it^&~8-Lfo86hvajx3(y9}nGMRS9{~xsWJtOqf$` z@A|W|B*I&rKc`fAtFO(O@%}H$8yDtOc`t>$=sJ#gihn$GeA`1_bS@;y8z1IWdFPeR zlJR}kKjrlbe`F{6`#(|M#58%|hKueFXa85PW6nSKSCiA^l?q=9MaTPiO0~U{LteBW zh{iWH%&E3_Y{)AVhXqsR<>>U+<1sBw-olVq=`iw&guE)DE*jsAGwf~@0=N*YoqO58|GBo`(Vgh5Qha*<*f^OasQ%zr^;(mBTJ(0VdRYof7~s4 zT}IQq^7e$h=y@FFJ(eb~*o9dV;grOmQ>yJ9)Az5(cSD-IY9a5GKhsk8 zZ$Zc_66&JyZ47g&{#_mN4rl+oDdbg=w<%5D_>eawtdFNu+q*C1MSYIOw>iwI#+SWj zmWZ2?r?<>mu@Q}LEzh+Mi`?Kn=y6BuyJcej>j{Vs)&P&Cm(fvkKbPavx@g)Z2 zOq2^ZjK=punB(z9f8!w>mU~6FiKgZUvL~u0f6VxQd|#2F^v`KpsLPutk$5@$=G`zy od3nPe-Djq^1BoTMawMvT^HT8?=jA)3uH3+!iRc`8JjHeY2lZL$`~Uy| literal 0 HcmV?d00001 diff --git a/libraries/nfc/src/cortex-m4/nfc_t2t_lib.h b/libraries/nfc/src/cortex-m4/nfc_t2t_lib.h new file mode 100644 index 000000000..0082d0735 --- /dev/null +++ b/libraries/nfc/src/cortex-m4/nfc_t2t_lib.h @@ -0,0 +1,271 @@ +/** + * Copyright (c) 2015 - 2020, Telit Communications Cyprus Ltd + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NFC_T2T_LIB_H__ +#define NFC_T2T_LIB_H__ + +/** @file + * + * @addtogroup nfc_api + * + * @defgroup nfc_t2t NFC Type 2 Tag + * @ingroup nfc_api + * @brief Implementation of NFC Type 2 Tag. + * + * @defgroup nfc_t2t_lib NFC tag 2 type emulation library + * @{ + * @ingroup nfc_t2t + * @brief The T2T emulation library interface. + */ + +#include +#include "util/sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFC_T2T_SIZEOF_INTERNAL_BYTES 10 ///< T2T internal byte size. +#define NFC_T2T_MAX_PAYLOAD_SIZE 988 ///< Maximum NDEF message size. +#define NFC_T2T_MAX_PAYLOAD_SIZE_RAW 1008 ///< No NDEF-TLV and no implicit lock bytes at the end. + +/** @brief Events passed to the callback function. */ +typedef enum +{ + NFC_T2T_EVENT_NONE, + ///< Not used. + + NFC_T2T_EVENT_FIELD_ON, + ///< NFC tag has detected external NFC field and was selected by an NFC polling device. + + NFC_T2T_EVENT_FIELD_OFF, + ///< External NFC field has been removed. + + NFC_T2T_EVENT_DATA_READ, + ///< NFC polling device has read all tag data. + /**< + * Repeated reading in the same session i.e. before @ref NFC_T2T_EVENT_FIELD_OFF event, + * will not trigger another @ref NFC_T2T_EVENT_DATA_READ event. + */ + + NFC_T2T_EVENT_STOPPED + ///< Reference to the application NFC callback has been released using @ref nfc_t2t_done. +} nfc_t2t_event_t; + +typedef enum +{ + NFC_T2T_PARAM_TESTING, ///< Used for unit tests. + NFC_T2T_PARAM_NFCID1, /**< NFCID1 value, data can be 4, 7, or 10 bytes long (single, double, or triple size). + To use default NFCID1 of specific length pass one byte containing requested length. + Default 7-byte NFCID1 will be used if this parameter was not set. This parameter can be + set before nfc_t2t_setup() to set initial NFCID1 and it can be changed later. */ +} nfc_t2t_param_id_t; + +/** @brief Callback to pass events from NFC T2T Library to application. + * + * @param[in] p_context Application context for callback execution. + * @param[in] event The event that occurred. + * @param[in] p_data Data to send to the application (event specific). + * @param[in] data_length Length of the data. + */ +typedef void (*nfc_t2t_callback_t)(void * p_context, + nfc_t2t_event_t event, + const uint8_t * p_data, + size_t data_length); + +/** @brief Function for registering the application callback for event signaling. + * + * The callback will be called by NFC T2T Library to notify the application of relevant + * events. It will be called from the HAL_NFC callback context. + * + * @param[in] callback Function pointer to the callback. + * @param[in] p_context Pointer to a memory area used by the callback for execution (optional). + * + * @retval NRF_SUCCESS If the application callback was registered successfully. If one + * of the arguments was invalid, an error code is returned. + */ +ret_code_t nfc_t2t_setup(nfc_t2t_callback_t callback, void * p_context); + +/** @brief Function for setting an NFC parameter. + * + * This function allows to set an NFC configuration parameter. + * + * @param[in] id ID of the parameter to set. + * @param[in] p_data Pointer to a buffer containing the data to set. + * @param[in] data_length Size of the buffer containing the data to set. + * + * @retval NRF_SUCCESS If the parameter was set successfully. If one of the arguments + * was invalid (for example, a wrong data length), an error code + * is returned. + */ +ret_code_t nfc_t2t_parameter_set(nfc_t2t_param_id_t id, void * p_data, size_t data_length); + +/** @brief Function for querying an NFC parameter value. + * + * The queried value will be placed into the passed data buffer. If the buffer + * is too small, p_max_data_length will contain the required buffer size. If the + * buffer is big enough, p_max_data_length will contain the actual size of the + * data. + * + * @param[in] id ID of the parameter to query. + * @param[in] p_data Pointer to a buffer receiving the queried data. + * @param[in, out] p_max_data_length Size of the buffer, receives actual size of queried data. + * + * @retval NRF_SUCCESS If the parameter was received successfully. If one of the arguments + * was invalid (for example, the buffer was too small), an error code + * is returned. + */ +ret_code_t nfc_t2t_parameter_get(nfc_t2t_param_id_t id, void * p_data, size_t * p_max_data_length); + +/** @brief Function for registering the payload to send on reception of a READ request. + * + * The payload is considered to only contain the NDEF message to deliver to a + * reader. The required NDEF TLV will be created implicitly by NFC T2T Library. + * + * The pointer to the payload must stay valid for the duration of the library + * execution, or until it is explicitly released. + * + * If the pointer is not NULL, but the length is zero, the paypload is + * considered to be an empty NDEF message. + * + * If a new payload is registered, the previously registered one is considered + * released. + * + * Passing a NULL pointer releases the current payload without registering a + * new one. + * + * If an invalid size is given (too big), the function returns with an error + * and the currently registered payload is left unchanged. + * + * @note Provided pointer must point to RAM region. + * + * @param[in] p_payload Pointer to the memory area in RAM containing the payload to send. + * @param[in] payload_length Size of the payload in bytes. + * + * @retval NRF_SUCCESS If the operation was successful. If one + * of the arguments was invalid, an error code is returned. + */ +ret_code_t nfc_t2t_payload_set(const uint8_t * p_payload, size_t payload_length); + +/** @brief Function for registering the raw payload to send on reception of a READ request. + * + * The payload will be delivered directly as-is to the reader, without + * implicitly adding an NDEF TLV container. This can be used if the + * application wants to define the TLVs itself, for example, to provide a different + * memory layout. + * + * The pointer to the payload must stay valid for the duration of the library + * execution, or until it is explicitly released. + * + * If a new payload is registered, the previously registered one is considered + * released. + * + * Passing a NULL pointer releases the current payload, without registering a + * new one. + * + * If an invalid size is given (too big), the function returns with an error + * and the currently registered payload is left unchanged. + * + * @note Provided pointer must points to RAM region. + * + * @param[in] p_payload Pointer to the memory area in RAM containing the payload to send. + * @param[in] payload_length Size of the payload in bytes. + * + * @retval NRF_SUCCESS If the operation was successful. If one + * of the arguments was invalid, an error code is returned. + */ +ret_code_t nfc_t2t_payload_raw_set(const uint8_t * p_payload, size_t payload_length); + +/** @brief Function for registering the sequence of internal bytes. + * + * This refers to the first 10 bytes of the tag memory. The library will set + * a sensible default for these bytes. The application can use this function + * to override the default. + * + * Passing a NULL pointer reverts back to the default sequence. + * The data will be copied by NFC T2T Library, so the memory does not have to remain valid + * after the function returns. + * + * @note When modifying the internal bytes, remember that they must be consistent + * with the NFC hardware register settings (see @ref nfc_t2t_format_internal). + * + * @param[in] p_data Pointer to the memory area containing the data. + * @param[in] data_length Size of the data in bytes. + * + * @retval NRF_SUCCESS If the operation was successful. If the data was not NULL and the + * data length was not 10, an error code is returned. + */ +ret_code_t nfc_t2t_internal_set(const uint8_t * p_data, size_t data_length); + +/** @brief Function for activating the NFC frontend. + * + * You must call this function so that events are posted to the application + * callback. + * + * @retval NRF_SUCCESS If the NFC frontend was activated successfully. If the lower layer + * could not be started, an error code is returned. + */ +ret_code_t nfc_t2t_emulation_start(void); + +/** @brief Function for deactivating the NFC frontend. + * + * After calling this function, no more events will be posted to the + * application callback. + * + * @retval NRF_SUCCESS If the NFC frontend was deactivated successfully. If the lower layer + * could not be stopped, an error code is returned. + */ +ret_code_t nfc_t2t_emulation_stop(void); + +/** @brief Function for releasing the reference to the application callback. + * + * After calling this function, the passed callback pointer is no longer + * considered valid. + * + * @retval NRF_SUCCESS This function always succeeds. + */ +ret_code_t nfc_t2t_done(void); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif // NFC_T2T_LIB_H__ diff --git a/libraries/nfc/src/ndef/conn_hand_parser/ble_oob_advdata_parser/nfc_ble_oob_advdata_parser.h b/libraries/nfc/src/ndef/conn_hand_parser/ble_oob_advdata_parser/nfc_ble_oob_advdata_parser.h new file mode 100644 index 000000000..a0872e20f --- /dev/null +++ b/libraries/nfc/src/ndef/conn_hand_parser/ble_oob_advdata_parser/nfc_ble_oob_advdata_parser.h @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup nfc_ble_oob_advdata_parser Advertising and Scan Response Data Parser for NFC OOB pairing + * @{ + * @ingroup nfc_ble_pair_msg + * @brief Functions for parsing and decoding data in the Advertising and Scan Response + * Data format for NFC OOB pairing. + */ + +#ifndef NFC_BLE_OOB_ADVDATA_PARSER_H_ +#define NFC_BLE_OOB_ADVDATA_PARSER_H_ + +#include "util/sdk_errors.h" +#include "util/ble_advdata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_ADVDATA_APPEARANCE_NOT_PRESENT 0 /**< Appearance AD structure not present. */ + +/**@brief Bluetooth Low Energy GAP device name. */ +typedef struct +{ + ble_advdata_name_type_t name_type; /**< See @ref ble_advdata_name_type_t. */ + uint8_t len; /**< Length of device name. */ + uint8_t * p_name; /**< Pointer to the buffer with device name. */ +} ble_gap_dev_name_t; + +/**@brief BLE Advertising data that is relevant for OOB pairing. */ +typedef struct +{ + ble_gap_dev_name_t device_name; /**< See @ref ble_gap_dev_name_t. */ + ble_gap_addr_t * p_device_addr; /**< See @ref ble_gap_addr_t. */ + ble_advdata_tk_value_t * p_tk_value; /**< See @ref ble_advdata_tk_value_t. */ + uint8_t * p_lesc_confirm_value; /**< LESC OOB confirmation data. */ + uint8_t * p_lesc_random_value; /**< LESC OOB random data. */ + ble_advdata_le_role_t le_role; /**< See @ref ble_advdata_le_role_t. */ + uint16_t appearance; /**< Advertising data Appearance field. */ + uint8_t flags; /**< Advertising data Flags field. */ + uint8_t * p_sec_mgr_oob_flags; /**< Security Manager Out Of Band Flags data field. */ +} nfc_ble_oob_pairing_data_t; + +/**@brief Function for parsing BLE data encoded in AD Type format. + * + * @details This function parses BLE data encoded in Advertising Data Type format which + * can be generated with @ref ble_advdata_encode function. The result of the parsing is + * stored within @ref nfc_ble_oob_pairing_data_t structure. + * + * @note Currently, module can be used to parse BLE AD Type data, which contains + * AD Structures with following GAP AD Types: Flags, Shortened and Complete Device + * Name, Security Manager TK Value and OOB Flags, Appearance, LE Bluetooth Device + * Address and LE Role. + * + * @warning Before passing \p p_nfc_ble_pairing_data structure to this function, + * it is necessary to provide buffers for AD Structures Data, which are expected to be + * found within parsed buffer. This applies to following GAP AD Types with corresponding + * structures: Shortened and Complete Device Name - @ref ble_gap_dev_name_t, + * LE Bluetooth Device Address - @ref ble_gap_addr_t, Security Manager TK Value - + * @ref ble_advdata_tk_value_t and Security Manager OOB Flags - uint8_t. + * + * @param[in] p_advdata Pointer to the data to be parsed. + * @param[in] len Size of the data to be parsed. + * @param[out] p_nfc_ble_pairing_data Pointer to the structure that will be used + * to hold parsed data. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_NO_MEM If the provided buffer for device name is + * too small to hold parsed data. + * @retval NRF_ERROR_INVALID_LENGTH If any AD Structure Length field contains + * different value than expected. + * @retval NRF_ERROR_INVALID_PARAM If any AD Structure Data field contains + * invalid parameters. + * @retval NRF_ERROR_NULL If any function pointer parameter is NULL or + * any expected buffer in \p p_nfc_ble_pairing_data + * was not provided. + * @retval NRF_ERROR_NOT_SUPPORTED If any AD Structure Type field contains + * type which is not supported or any AD + * Structure Type occurs more than once. + */ +ret_code_t nfc_ble_oob_advdata_parse(uint8_t const * p_advdata, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data); + + +/**@brief Function for displaying values of basic BLE OOB Advertising data types. + * + * @param[in] p_pairing_data Structure containing parsed data. + */ +void nfc_oob_data_printout(nfc_ble_oob_pairing_data_t const * const p_pairing_data); + +#ifdef __cplusplus +} +#endif + +#endif //NFC_BLE_OOB_ADVDATA_PARSER_H__ + +/** @} */ diff --git a/libraries/nfc/src/ndef/conn_hand_parser/le_oob_rec_parser/nfc_le_oob_rec_parser.c b/libraries/nfc/src/ndef/conn_hand_parser/le_oob_rec_parser/nfc_le_oob_rec_parser.c new file mode 100644 index 000000000..cf9a80102 --- /dev/null +++ b/libraries/nfc/src/ndef/conn_hand_parser/le_oob_rec_parser/nfc_le_oob_rec_parser.c @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_LE_OOB_REC_PARSER) +#include "nfc_le_oob_rec_parser.h" +#include "util/sdk_errors.h" + +/** + * @brief Function for parsing LE OOB record payload. + * + * This function parses LE OOB record payload and extracts BLE OOB Advertising data structure. + * + * @param[in] p_buff Pointer to the record payload. + * @param[in] p_len Pointer to the record payload length. + * @param[in,out] p_nfc_ble_oob_pairing_data Pointer to the structure that will be used to hold + * parsed data. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval Other An error code that might have been returned by + * @ref nfc_ble_oob_advdata_parse function. + */ +static ret_code_t nfc_le_oob_payload_parse(uint8_t * p_buff, + uint32_t * const p_len, + nfc_ble_oob_pairing_data_t * const p_nfc_ble_oob_pairing_data) +{ + ret_code_t err_code = nfc_ble_oob_advdata_parse(p_buff, + *p_len, + p_nfc_ble_oob_pairing_data); + return err_code; +} + +ret_code_t nfc_le_oob_rec_parse(nfc_ndef_record_desc_t const * const p_rec_desc, + nfc_ble_oob_pairing_data_t * const p_nfc_ble_oob_pairing_data) +{ + ret_code_t err_code; + + if (p_rec_desc->tnf != TNF_MEDIA_TYPE) + { + return NRF_ERROR_INVALID_DATA; + } + + if (p_rec_desc->type_length != sizeof(le_oob_rec_type_field)) + { + return NRF_ERROR_INVALID_DATA; + } + + if (memcmp(p_rec_desc->p_type, le_oob_rec_type_field, sizeof(le_oob_rec_type_field)) != 0) + { + return NRF_ERROR_INVALID_DATA; + } + + if (p_rec_desc->payload_constructor != (p_payload_constructor_t) nfc_ndef_bin_payload_memcopy) + { + return NRF_ERROR_NOT_SUPPORTED; + } + + uint8_t const * p_payload = ((nfc_ndef_bin_payload_desc_t*)(p_rec_desc->p_payload_descriptor))->p_payload; + uint32_t payload_lenght = ((nfc_ndef_bin_payload_desc_t*)(p_rec_desc->p_payload_descriptor))->payload_length; + + err_code = nfc_le_oob_payload_parse((uint8_t *) p_payload, + &payload_lenght, + p_nfc_ble_oob_pairing_data); + + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_LE_OOB_REC_PARSER) diff --git a/libraries/nfc/src/ndef/connection_handover/ac_rec/nfc_ac_rec.c b/libraries/nfc/src/ndef/connection_handover/ac_rec/nfc_ac_rec.c new file mode 100644 index 000000000..0cb6d1e7b --- /dev/null +++ b/libraries/nfc/src/ndef/connection_handover/ac_rec/nfc_ac_rec.c @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifdef __cplusplus +extern "C" { +#endif +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_AC_REC) + +#include "nfc_ac_rec.h" +#include +//#include "../../../nrf_error.h" +#include "nrf.h" + +#define AC_REC_CPS_BYTE_SIZE 1 ///< Size of the field with CPS data. +#define AC_REC_DATA_REF_LEN_SIZE 1 ///< Size of the Data Reference Length field. +#define AC_REC_AUX_DATA_REF_COUNT_SIZE 1 ///< Size of the Data Reference Length field. + +const uint8_t nfc_ac_rec_type_field[2] = {'a', 'c'}; ///< Alternative Carrier Record type. + +/** + * @brief Function for calculating the payload length of the NFC NDEF Alternative Carrier record. + */ +static uint32_t nfc_ac_rec_payload_size_get(nfc_ac_rec_payload_desc_t const * p_ac_rec_payload_desc) +{ + int32_t i = 0; + // Initialize with size of byte with CPS. + uint32_t payload_size = AC_REC_CPS_BYTE_SIZE; + + // Add Carrier Data Reference size. + payload_size += p_ac_rec_payload_desc->carrier_data_ref.length + AC_REC_DATA_REF_LEN_SIZE; + + // Add Auxiliary Data Reference Count size. + payload_size += AC_REC_AUX_DATA_REF_COUNT_SIZE; + + for (i = 0; i < p_ac_rec_payload_desc->aux_data_ref_count; i++) + { + // Add Auxiliary Data Reference size. + payload_size += p_ac_rec_payload_desc->p_aux_data_ref[i].length + AC_REC_DATA_REF_LEN_SIZE; + } + + return payload_size; +} + + +ret_code_t nfc_ac_rec_payload_constructor(nfc_ac_rec_payload_desc_t * p_nfc_rec_ac_payload_desc, + uint8_t * p_buff, + uint32_t * p_len) +{ + int32_t i = 0; + uint32_t payload_size = nfc_ac_rec_payload_size_get(p_nfc_rec_ac_payload_desc); + + if (p_buff != NULL) + { + // Not enough space in the buffer, return an error. + if (payload_size > *p_len) + { + return NRF_ERROR_NO_MEM; + } + + // Invalid CPS value. + if ( p_nfc_rec_ac_payload_desc->cps & ~NFC_AC_CPS_MASK ) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Copy CPS. + *p_buff = p_nfc_rec_ac_payload_desc->cps; + p_buff += AC_REC_CPS_BYTE_SIZE; + + // Copy Carrier Data Reference. + *p_buff = p_nfc_rec_ac_payload_desc->carrier_data_ref.length; + p_buff += AC_REC_DATA_REF_LEN_SIZE; + + memcpy( p_buff, + p_nfc_rec_ac_payload_desc->carrier_data_ref.p_data, + p_nfc_rec_ac_payload_desc->carrier_data_ref.length ); + p_buff += p_nfc_rec_ac_payload_desc->carrier_data_ref.length; + + // Copy Auxiliary Data Reference. + *p_buff = p_nfc_rec_ac_payload_desc->aux_data_ref_count; + p_buff += AC_REC_AUX_DATA_REF_COUNT_SIZE; + + for (i = 0; i < p_nfc_rec_ac_payload_desc->aux_data_ref_count; i++) + { + *p_buff = p_nfc_rec_ac_payload_desc->p_aux_data_ref[i].length; + p_buff += AC_REC_DATA_REF_LEN_SIZE; + + memcpy( p_buff, + p_nfc_rec_ac_payload_desc->p_aux_data_ref[i].p_data, + p_nfc_rec_ac_payload_desc->p_aux_data_ref[i].length ); + p_buff += p_nfc_rec_ac_payload_desc->p_aux_data_ref[i].length; + } + } + + // Assign payload size to the return buffer. + *p_len = payload_size; + + return NRF_SUCCESS; +} + + +void nfc_ac_rec_auxiliary_data_ref_clear(nfc_ndef_record_desc_t * p_ac_rec) +{ + nfc_ac_rec_payload_desc_t * p_ac_rec_payload = + (nfc_ac_rec_payload_desc_t*)p_ac_rec->p_payload_descriptor; + + p_ac_rec_payload->aux_data_ref_count = 0; +} + + +ret_code_t nfc_ac_rec_auxiliary_data_ref_add(nfc_ndef_record_desc_t * p_ac_rec, + uint8_t * p_aux_data, + uint8_t aux_length) +{ + nfc_ac_rec_payload_desc_t * p_ac_rec_payload = + (nfc_ac_rec_payload_desc_t *)p_ac_rec->p_payload_descriptor; + + if (p_ac_rec_payload->aux_data_ref_count >= p_ac_rec_payload->max_aux_data_ref) + { + return NRF_ERROR_NO_MEM; + } + + p_ac_rec_payload->p_aux_data_ref[p_ac_rec_payload->aux_data_ref_count].p_data = p_aux_data; + p_ac_rec_payload->p_aux_data_ref[p_ac_rec_payload->aux_data_ref_count].length = aux_length; + p_ac_rec_payload->aux_data_ref_count++; + + return NRF_SUCCESS; +} + +#ifdef __cplusplus +} +#endif // NRF_MODULE_ENABLED(NFC_AC_REC) +#endif // NRF_MODULE_ENABLED(NFC_AC_REC) diff --git a/libraries/nfc/src/ndef/connection_handover/ble_oob_advdata/nfc_ble_oob_advdata.h b/libraries/nfc/src/ndef/connection_handover/ble_oob_advdata/nfc_ble_oob_advdata.h new file mode 100644 index 000000000..e07458040 --- /dev/null +++ b/libraries/nfc/src/ndef/connection_handover/ble_oob_advdata/nfc_ble_oob_advdata.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup nfc_ble_oob_advdata Advertising and Scan Response Data Encoder for NFC OOB pairing + * @{ + * @ingroup nfc_ble_pair_msg + * @brief Function for encoding data in the Advertising and Scan Response Data format, which + * can be used to create payload of NFC message intended for initiating the Out-of-Band + * pairing. + */ + +#ifndef NFC_BLE_OOB_ADVDATA_H__ +#define NFC_BLE_OOB_ADVDATA_H__ + +#include +#include "util/ble_advdata.h" +#include "util/app_util.h" +#include "util/sdk_errors.h" + + +/**@brief Function for encoding data in the Advertising and Scan Response data format, which + * is used for NFC OOB pairing. + * + * + * @details This function encodes data into the Advertising and Scan Response data format (AD structures). + * Encoding is based on the selections in the supplied structures. This function uses + * @ref ble_advdata_encode to encode regular data and adds additional AD Structures which are specific + * for NFC OOB pairing: Security Manager TK Value, LESC OOB values, OOB Flags, and LE Role. + * + * @param[in] p_advdata Pointer to the structure for specifying the content of encoded data. + * @param[out] p_encoded_data Pointer to the buffer where encoded data will be returned. + * @param[in,out] p_len \c in: Size of \p p_encoded_data buffer. + * \c out: Length of encoded data. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If the operation failed because a wrong parameter was provided in \p p_advdata. + * @retval NRF_ERROR_DATA_SIZE If the operation failed because not all the requested data could fit into the + * provided buffer or some encoded AD structure is too long and its + * length cannot be encoded with one octet. + */ +ret_code_t nfc_ble_oob_adv_data_encode(ble_advdata_t const * const p_advdata, + uint8_t * const p_encoded_data, + uint16_t * const p_len); + +/**@brief Function for encoding payload field of Security Manager TK Value AD Type. + * + * @param[in] p_tk_value Security Manager TK Value AD Type payload. + * @param[out] p_tk_payload_data Pointer to the buffer where TK payload data will be stored. + * + */ +void nfc_tk_value_payload_encode(ble_advdata_tk_value_t * p_tk_value, + uint8_t * p_tk_payload_data); + +#endif // NFC_BLE_OOB_ADVDATA_H__ + +/** @} */ diff --git a/libraries/nfc/src/ndef/connection_handover/ble_pair_lib/nfc_ble_pair_lib.h b/libraries/nfc/src/ndef/connection_handover/ble_pair_lib/nfc_ble_pair_lib.h new file mode 100644 index 000000000..9452fe3de --- /dev/null +++ b/libraries/nfc/src/ndef/connection_handover/ble_pair_lib/nfc_ble_pair_lib.h @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_BLE_PAIR_LIB_H__ +#define NFC_BLE_PAIR_LIB_H__ + +#include +#include "util/sdk_errors.h" +#include "ble.h" +#include "ble_advertising.h" +#include "peer_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @addtogroup nfc_api + * + * @defgroup nfc_ble_pair_lib NFC BLE Pairing Library + * @ingroup nfc_api + * @brief @tagAPI52 High-level library for BLE Connection Handover pairing using NFC. + * @{ + */ + +/** + * @brief NFC pairing types. + */ +typedef enum +{ + NFC_PAIRING_MODE_JUST_WORKS, /**< Legacy Just Works pairing without a security key. */ + NFC_PAIRING_MODE_OOB, /**< Legacy OOB pairing with a Temporary Key shared through NFC tag data. */ + NFC_PAIRING_MODE_LESC_JUST_WORKS, /**< LESC pairing without authentication data. */ + NFC_PAIRING_MODE_LESC_OOB, /**< LESC pairing with OOB authentication data. */ + NFC_PAIRING_MODE_GENERIC_OOB, /**< OOB pairing with fallback from LESC to Legacy mode. */ + NFC_PAIRING_MODE_CNT /**< Number of available pairing modes. */ +} nfc_pairing_mode_t; + +/** + * @brief Funtion for initializing NFC tag data and turning on tag emulation. + * + * @warning It is assumed that Peer Manager has already been initialized before calling this function. + * It is also assumed that BLE advertising has already been initialized and it is configured + * to run in the BLE_ADV_MODE_FAST mode. + * + * @param[in] mode Pairing mode, this is the value of the @ref nfc_pairing_mode_t enum. + * @param[in] p_advertising Pointer to the advertising module instance. + * + * @retval NRF_SUCCESS If NFC has been initialized properly. + * @retval NRF_ERROR_INVALID_PARAM If pairing mode is invalid. + * @retval NRF_ERROR_NULL If pointer to the advertising module instance is NULL. + * @retval Other Other error codes might be returned depending on used modules. + */ +ret_code_t nfc_ble_pair_init(ble_advertising_t * const p_advertising, nfc_pairing_mode_t mode); + +/** + * @brief Function for setting pairing data and BLE security mode. + * + * @param[in] mode New pairing mode, this is the value of the @ref nfc_pairing_mode_t enum. + * + * @retval NRF_SUCCESS If new pairing mode has been set correctly. + * @retval NRF_ERROR_INVALID_PARAM If pairing mode is invalid. + * @retval Other Other error codes might be returned depending on used modules. + */ +ret_code_t nfc_ble_pair_mode_set(nfc_pairing_mode_t mode); + +/** + * @brief Function for obtaining the current pairing mode. + * + * @return Current pairing mode. + */ +nfc_pairing_mode_t nfc_ble_pair_mode_get(void); + +/** + * @brief Function for replying to @ref PM_EVT_CONN_SEC_PARAMS_REQ. + * + * @details This function is used to allow dynamic changes in the Peer Manager + * security parameters depending on security parameters + * obtained from the peer. This is essential for dynamic switching + * between Legacy OOB and LESC OOB pairing modes when pairing + * library works in @ref NFC_PAIRING_MODE_GENERIC_OOB mode. + * + * @note This function invokes the @ref pm_conn_sec_params_reply function. + * + * @param[in] p_evt Pointer to the Peer Manager event struct with + * information about peer security parameters. + * + * @retval NRF_SUCCESS If proper reply has been sent or library does not need to reply. + * @retval NRF_ERROR_NULL If pointer to the Peer Manager event is NULL. + * @retval Other Other error codes might be returned by the @ref pm_conn_sec_params_reply function. + */ +ret_code_t nfc_ble_pair_on_pm_params_req(pm_evt_t const * p_evt); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_BLE_PAIR_LIB_H__ diff --git a/libraries/nfc/src/ndef/connection_handover/ble_pair_msg/nfc_ble_pair_msg.h b/libraries/nfc/src/ndef/connection_handover/ble_pair_msg/nfc_ble_pair_msg.h new file mode 100644 index 000000000..0a1db7527 --- /dev/null +++ b/libraries/nfc/src/ndef/connection_handover/ble_pair_msg/nfc_ble_pair_msg.h @@ -0,0 +1,279 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_BLE_PAIR_MSG_H__ +#define NFC_BLE_PAIR_MSG_H__ + +/**@file + * + * @defgroup nfc_modules NDEF message modules + * @ingroup nfc_api + * @brief Implementation of NDEF messages. + * + * @defgroup nfc_ndef_messages Predefined NDEF messages + * @ingroup nfc_modules + * @brief Predefined NDEF messages for standard use. + * + * @defgroup nfc_ble_pair_msg BLE pairing messages + * @{ + * @ingroup nfc_ndef_messages + * + * @brief Generation of NFC NDEF messages used for BLE pairing. + * + */ + +#include +#include "util/ble_advdata.h" +#include "util/sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Types of BLE pairing message. + * + * Use one of these values to choose the type of NDEF BLE pairing message. + */ +typedef enum +{ + NFC_BLE_PAIR_MSG_BLUETOOTH_LE_SHORT, ///< Simplified LE OOB message. + NFC_BLE_PAIR_MSG_BLUETOOTH_EP_SHORT, ///< Simplified EP OOB message. + NFC_BLE_PAIR_MSG_FULL ///< BLE Handover Select Message. +} nfc_ble_pair_type_t; + +/** @brief Function for encoding simplified LE OOB messages. + * + * This function encodes a simplified LE OOB message into a buffer. The payload of the LE OOB record + * inside the message can be configured via the advertising data structure. + * + * This function was implemented partially according to "Bluetooth Secure Simple Pairing Using NFC" + * (denotation "NFCForum-AD-BTSSP_1_1" published on 2014-01-09) chapters 3.1, 3.2, 4.3.2, + * and according to "Supplement to the Bluetooth Core Specification" (Version 5, adoption date: + * Dec 02 2014). + * + * @note To be able to encode the message, a SoftDevice must be enabled and configured. + * + * @param[in] p_le_advdata Pointer to the BLE advertising data structure for the LE OOB record. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_simplified_le_oob_msg_encode(ble_advdata_t const * const p_le_advdata, + uint8_t * p_buf, + uint32_t * p_len); + +/** @brief Function for encoding simplified EP OOB messages. + * + * This function encodes a simplified EP OOB message into a buffer. The payload of the EP OOB record + * inside the message can be configured via the advertising data structure. + * + * This function was implemented partially according to "Bluetooth Secure Simple Pairing Using NFC" + * (denotation "NFCForum-AD-BTSSP_1_1" published on 2014-01-09) chapters 3.1, 3.2, 4.3.1, + * and according to "Supplement to the Bluetooth Core Specification" (Version 5, adoption date: + * Dec 02 2014). + * + * @note To be able to encode the message, a SoftDevice must be enabled and configured. + * + * @param[in] p_ep_advdata Pointer to the BLE advertising data structure for the EP OOB record. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_simplified_ep_oob_msg_encode(ble_advdata_t const * const p_ep_advdata, + uint8_t * p_buf, + uint32_t * p_len); + +/** @brief Function for encoding BLE Handover Select Messages. + * + * This function encodes a BLE Handover Select Message into a buffer. The payload of the LE OOB record + * and the EP OOB record inside the message can be configured via the advertising data structures. + * + * This function was implemented partially according to "Bluetooth Secure Simple Pairing Using NFC" + * (denotation "NFCForum-AD-BTSSP_1_1" published on 2014-01-09) chapters 3.1, 3.2, 4.1.1 + * and 4.1.2 (combined), and according to "Supplement to the Bluetooth Core Specification" (Version 5, + * adoption date: Dec 02 2014). + * + * @note To be able to encode the message, a SoftDevice must be enabled and configured. + * + * @param[in] p_le_advdata Pointer to the BLE advertising data structure for the LE OOB record. + * @param[in] p_ep_advdata Pointer to the BLE advertising data structure for the EP OOB record. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_full_handover_select_msg_encode(ble_advdata_t const * const p_le_advdata, + ble_advdata_t const * const p_ep_advdata, + uint8_t * p_buf, + uint32_t * p_len); + +/** @brief Function for encoding any type of BLE pairing messages with default BLE + * advertising data structures. + * + * This function encodes a BLE pairing message into a buffer. The message can be encoded as + * one of the three message types (using @ref nfc_ble_simplified_le_oob_msg_encode, + * @ref nfc_ble_simplified_ep_oob_msg_encode, or @ref nfc_ble_full_handover_select_msg_encode), + * according to the @p nfc_ble_pair_type parameter. LE and EP OOB records use the default + * advertising data structure configuration. Only one field ('Security Manager TK') in the BLE + * advertising data can be configured for both records by specifying the @p p_tk_value parameter. + * + * For LE OOB records, the default BLE advertising data structure configuration fills the required + * fields 'LE Bluetooth Device Address' and 'LE Role' and the optional fields 'Appearance', + * 'Local Name', and 'Flags'. + * + * For EP OOB records, the default BLE advertising data structure configuration fills the required + * field 'Security Manager Out Of Band Flags' and the optional fields 'Appearance', + * 'Local Name', and 'Flags'. + * + * @note To be able to encode the message, a SoftDevice must be enabled and configured. + * + * @param[in] nfc_ble_pair_type Type of BLE pairing message. + * @param[in] p_tk_value Pointer to the authentication Temporary Key (TK). If NULL, + * TK value field is not encoded in the NDEF message. + * @param[in] p_lesc_data Pointer to the LESC OOB data. If NULL, LESC OOB fields are + * not encoded in the NDEF message. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_pair_default_msg_encode(nfc_ble_pair_type_t nfc_ble_pair_type, + ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + uint8_t * p_buf, + uint32_t * p_len); + +/** @brief Function for encoding any type of BLE pairing messages with default BLE + * advertising data structures and with TK modifier feature. + * + * This function is very similar to the @ref nfc_ble_pair_default_msg_encode function, but + * additionaly enables tracking of TK locations which were encoded in the Connection Handover + * NDEF message. After using this function, you can update the TK value in NDEF by calling + * @ref nfc_tk_group_modifier_update. + * + * @param[in] nfc_ble_pair_type Type of BLE pairing message. + * @param[in] p_tk_value Pointer to the authentication Temporary Key (TK). If NULL, + * TK value field is not encoded in the NDEF message. + * @param[in] p_lesc_data Pointer to the LESC OOB data. If NULL, LESC OOB values are + * not encoded in the NDEF message. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * @param[in] pp_tk_group Pointer to array of TK locations that should be modified with + * @ref nfc_tk_group_modifier_update function. + * @param[in] max_group_size Maximal number of TK locations that can added to \p pp_tk_group. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_pair_msg_updatable_tk_encode(nfc_ble_pair_type_t nfc_ble_pair_type, + ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + uint8_t * p_buf, + uint32_t * p_len, + uint8_t ** pp_tk_group, + uint8_t max_group_size); + +/**@brief Function for updating the Connection Handover NDEF message with new TK value. + * + * @details This function updates NDEF message with new TK value. This update is applied to all of + * TK locations in the Connection Handover NDEF message. This function can only be used + * after calling @ref nfc_ble_pair_msg_updatable_tk_encode, which is used to encode + * Connection Handover NDEF message. + * + * @param[in] p_tk_value Pointer to the new TK value. The NDEF message will be updated with this + * value. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NULL If pointer to TK locations was NULL. + */ +ret_code_t nfc_tk_group_modifier_update(ble_advdata_tk_value_t * p_tk_value); + +/**@brief Function for adding new location of TK value to the location description structure. + * + * @param[in] p_tk_location New location of TK value in the Connection Handover NDEF message. + * + * @retval NRF_SUCCESS If the operation was successful or if buffer used for holding TK + * locations is NULL. + * @retval NRF_ERROR_NO_MEM If there is no place in the buffer for the new TK value location. + */ +ret_code_t nfc_tk_to_group_add(uint8_t * p_tk_location); + +/**@brief Function for updating the Connection Handover NDEF message with a new LESC OOB values. + * + * @details Updates LESC Confirmation and Random Values based on its locations set by the @ref nfc_lesc_pos_set function. + * + * @param[in] p_ble_lesc_oob_data Pointer to the new LESC OOB data. The NDEF message will be updated with this data. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NULL If pointer to the new LESC OOB data is NULL. + * @retval NRF_ERROR_INVALID_STATE If pointer to the LESC OOB data location in NDEF message is NULL. + */ +ret_code_t nfc_lesc_data_update(ble_gap_lesc_oob_data_t * p_ble_lesc_oob_data); + +/**@brief Function for storing pointers to the LESC OOB data inside NDEF message. + * + * @details It allows LESC OOB data update without regenerating entire CH NDEF message. + * + * @param[in] p_confirm Pointer to the LESC Confirmation Value position in the NDEF message. + * @param[in] p_random Pointer to the LESC Random Value position in the NDEF message. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NULL If either of pointers is set to NULL. + */ +ret_code_t nfc_lesc_pos_set(uint8_t * p_confirm, uint8_t * p_random); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_BLE_PAIR_MSG_H__ diff --git a/libraries/nfc/src/ndef/connection_handover/hs_rec/nfc_hs_rec.c b/libraries/nfc/src/ndef/connection_handover/hs_rec/nfc_hs_rec.c new file mode 100644 index 000000000..aceb697d6 --- /dev/null +++ b/libraries/nfc/src/ndef/connection_handover/hs_rec/nfc_hs_rec.c @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_HS_REC) + +#include "nfc_hs_rec.h" +#include "../ac_rec/nfc_ac_rec.h" +//#include "../../../nrf_error.h" + +#define HS_REC_VERSION_SIZE 1 + +const uint8_t nfc_hs_rec_type_field[] = {'H', 's'}; ///< Handover Select record type. + + +ret_code_t nfc_hs_rec_payload_constructor(nfc_hs_rec_payload_desc_t * p_nfc_hs_rec_payload_desc, + uint8_t * p_buff, + uint32_t * p_len) +{ + ret_code_t err_code = NRF_SUCCESS; + + if (p_buff != NULL) + { + // There must be at least 1 free byte in buffer for version byte. + if (*p_len < HS_REC_VERSION_SIZE) + { + return NRF_ERROR_NO_MEM; + } + + // Major/minor version byte. + *p_buff = ( (p_nfc_hs_rec_payload_desc->major_version << 4) & 0xF0) | + ( p_nfc_hs_rec_payload_desc->minor_version & 0x0F); + p_buff += HS_REC_VERSION_SIZE; + + // Decrement remaining buffer size. + *p_len -= HS_REC_VERSION_SIZE; + } + + // Encode local records encapsulated in a message. + err_code = nfc_ndef_msg_encode(p_nfc_hs_rec_payload_desc->p_local_records, p_buff, p_len); + if (err_code!= NRF_SUCCESS) + { + return err_code; + } + + // Add version byte to the total record size. + *p_len += HS_REC_VERSION_SIZE; + + return NRF_SUCCESS; +} + + +void nfc_hs_rec_local_record_clear(nfc_ndef_record_desc_t * p_hs_rec) +{ + nfc_hs_rec_payload_desc_t* p_hs_payload = + (nfc_hs_rec_payload_desc_t*)p_hs_rec->p_payload_descriptor; + + nfc_ndef_msg_clear(p_hs_payload->p_local_records); +} + + +ret_code_t nfc_hs_rec_local_record_add(nfc_ndef_record_desc_t * p_hs_rec, + nfc_ndef_record_desc_t * p_local_rec) +{ + nfc_hs_rec_payload_desc_t* p_hs_payload = + (nfc_hs_rec_payload_desc_t*)p_hs_rec->p_payload_descriptor; + + return nfc_ndef_msg_record_add(p_hs_payload->p_local_records, p_local_rec); +} + +#endif // NRF_MODULE_ENABLED(NFC_HS_REC) diff --git a/libraries/nfc/src/ndef/generic/record/nfc_ndef_record.h b/libraries/nfc/src/ndef/generic/record/nfc_ndef_record.h new file mode 100644 index 000000000..76480c183 --- /dev/null +++ b/libraries/nfc/src/ndef/generic/record/nfc_ndef_record.h @@ -0,0 +1,311 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_NDEF_RECORD_H__ +#define NFC_NDEF_RECORD_H__ + +#include +#include +#include "compiler_abstraction.h" +#include "util/sdk_errors.h" +#include "nrf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @defgroup nfc_ndef_record Custom NDEF records + * @{ + * @ingroup nfc_ndef_msg + * + * @brief Generation of NFC NDEF records for NFC messages. + * + */ + + +#define NDEF_RECORD_IL_MASK 0x08 ///< Mask of the ID field presence bit in the flags byte of an NDEF record. +#define NDEF_RECORD_TNF_MASK 0x07 ///< Mask of the TNF value field in the first byte of an NDEF record. +#define NDEF_RECORD_SR_MASK 0x10 ///< Mask of the SR flag. If set, this flag indicates that the PAYLOAD_LENGTH field has a size of 1 byte. Otherwise, PAYLOAD_LENGTH has 4 bytes. +#define NDEF_RECORD_PAYLOAD_LEN_LONG_SIZE 4 ///< Size of the Payload Length field in a long NDEF record. +#define NDEF_RECORD_PAYLOAD_LEN_SHORT_SIZE 1 ///< Size of the Payload Length field in a short NDEF record. +#define NDEF_RECORD_ID_LEN_SIZE 1 ///< Size of the ID Length field in an NDEF record. + + +/** + * @brief Payload constructor type. + + * A payload constructor is a function for constructing the payload of an NDEF + * record. + * + * @param[in] p_payload_descriptor Pointer to the input data for the constructor. + * @param[out] p_buffer Pointer to the payload destination. If NULL, function will + * calculate the expected size of the record payload. + * + * @param[in,out] p_len Size of the available memory to write as input. Size of the generated + * record payload as output. The implementation must check if the payload + * will fit in the provided buffer. This must be checked by the caller function. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +typedef ret_code_t (* p_payload_constructor_t)(void * p_payload_descriptor, + uint8_t * p_buffer, + uint32_t * p_len); + + +/** + * @brief Type Name Format (TNF) Field Values. + * + * Values to specify the TNF of a record. + */ +typedef enum +{ + TNF_EMPTY = 0x00, ///< The value indicates that there is no type or payload associated with this record. + TNF_WELL_KNOWN = 0x01, ///< NFC Forum well-known type [NFC RTD]. + TNF_MEDIA_TYPE = 0x02, ///< Media-type as defined in RFC 2046 [RFC 2046]. + TNF_ABSOLUTE_URI = 0x03, ///< Absolute URI as defined in RFC 3986 [RFC 3986]. + TNF_EXTERNAL_TYPE = 0x04, ///< NFC Forum external type [NFC RTD]. + TNF_UNKNOWN_TYPE = 0x05, ///< The value indicates that there is no type associated with this record. + TNF_UNCHANGED = 0x06, ///< The value is used for the record chunks used in chunked payload. + TNF_RESERVED = 0x07, ///< The value is reserved for future use. +} nfc_ndef_record_tnf_t; + + +/** + * @brief NDEF record descriptor. + */ +typedef struct +{ + nfc_ndef_record_tnf_t tnf; ///< Value of the Type Name Format (TNF) field. + + uint8_t id_length; ///< Length of the ID field. If 0, a record format without ID field is assumed. + uint8_t const * p_id; ///< Pointer to the ID field data. Not relevant if id_length is 0. + + uint8_t type_length; ///< Length of the type field. + uint8_t const * p_type; ///< Pointer to the type field data. Not relevant if type_length is 0. + + p_payload_constructor_t payload_constructor; ///< Pointer to the payload constructor function. + void * p_payload_descriptor; ///< Pointer to the data for the payload constructor function. + +} nfc_ndef_record_desc_t; + +/** + * @brief Record position within the NDEF message. + * + * Values to specify the location of a record within the NDEF message. + */ +typedef enum +{ + NDEF_FIRST_RECORD = 0x80, ///< First record. + NDEF_MIDDLE_RECORD = 0x00, ///< Middle record. + NDEF_LAST_RECORD = 0x40, ///< Last record. + NDEF_LONE_RECORD = 0xC0 ///< Only one record in the message. +} nfc_ndef_record_location_t; + +#define NDEF_RECORD_LOCATION_MASK (NDEF_LONE_RECORD) ///< Mask of the Record Location bits in the NDEF record's flags byte. + +/** + * @brief Binary data descriptor containing the payload for the record. + */ +typedef struct +{ + uint8_t const * p_payload; ///< Pointer to the buffer with the data. + uint32_t payload_length; ///< Length of data in bytes. +} nfc_ndef_bin_payload_desc_t; + +/** + * @brief Macro for creating and initializing an NFC NDEF record descriptor for a generic record. + * + * This macro creates and initializes an instance of type @ref nfc_ndef_record_desc_t. + * + * Use the macro @ref NFC_NDEF_GENERIC_RECORD_DESC to access the NDEF record descriptor instance. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF record encoding must be done in the same variable scope. + * + * @param[in] NAME Name of the created descriptor instance. + * @param[in] TNF Type Name Format (TNF) value for the record. + * @param[in] P_ID Pointer to the ID string. + * @param[in] ID_LEN Length of the ID string. + * @param[in] P_TYPE Pointer to the type string. + * @param[in] TYPE_LEN Length of the type string. + * @param[in] P_PAYLOAD_CONSTRUCTOR Pointer to the payload constructor function. + * The constructor must be of type @ref p_payload_constructor_t. + * @param[in] P_PAYLOAD_DESCRIPTOR Pointer to the data for the payload constructor. + */ +#define NFC_NDEF_GENERIC_RECORD_DESC_DEF(NAME, \ + TNF, \ + P_ID, \ + ID_LEN, \ + P_TYPE, \ + TYPE_LEN, \ + P_PAYLOAD_CONSTRUCTOR, \ + P_PAYLOAD_DESCRIPTOR) \ + nfc_ndef_record_desc_t NAME##_ndef_generic_record_desc = \ + { \ + .tnf = TNF, \ + \ + .id_length = ID_LEN, \ + .p_id = P_ID, \ + \ + .type_length = TYPE_LEN, \ + .p_type = P_TYPE, \ + \ + .payload_constructor = (p_payload_constructor_t)P_PAYLOAD_CONSTRUCTOR, \ + .p_payload_descriptor = (void *) P_PAYLOAD_DESCRIPTOR \ + } + + +/** @brief Macro for accessing the NFC NDEF record descriptor instance + * that you created with @ref NFC_NDEF_GENERIC_RECORD_DESC_DEF. + */ +#define NFC_NDEF_GENERIC_RECORD_DESC(NAME) (NAME##_ndef_generic_record_desc) + +/** + * @brief Macro for creating and initializing an NFC NDEF record descriptor for a record with + * binary payload. + * + * This macro creates and initializes a static instance of type @ref nfc_ndef_record_desc_t and a binary data descriptor containing the payload data. + * + * Use the macro @ref NFC_NDEF_RECORD_BIN_DATA to access the NDEF record descriptor instance. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF record encoding must be done in the same variable scope. + * + * @param[in] NAME Name of the created descriptor instance. + * @param[in] TNF Type Name Format (TNF) value for the record. + * @param[in] P_ID Pointer to the ID string. + * @param[in] ID_LEN Length of the ID string. + * @param[in] P_TYPE Pointer to the type string. + * @param[in] TYPE_LEN Length of the type string. + * @param[in] P_PAYLOAD Pointer to the payload data that will be copied to the payload field. + * @param[in] PAYLOAD_LEN Length of the payload. + */ +#define NFC_NDEF_RECORD_BIN_DATA_DEF(NAME, \ + TNF, \ + P_ID, ID_LEN, \ + P_TYPE, \ + TYPE_LEN, \ + P_PAYLOAD, \ + PAYLOAD_LEN) \ + nfc_ndef_bin_payload_desc_t NAME##_nfc_ndef_bin_payload_desc = \ + { \ + .p_payload = P_PAYLOAD, \ + .payload_length = PAYLOAD_LEN \ + }; \ + \ + nfc_ndef_record_desc_t NAME##_nfc_ndef_bin_record_desc = \ + { \ + .tnf = TNF, \ + \ + .id_length = ID_LEN, \ + .p_id = P_ID, \ + \ + .type_length = TYPE_LEN, \ + .p_type = P_TYPE, \ + \ + .payload_constructor = (p_payload_constructor_t) nfc_ndef_bin_payload_memcopy, \ + .p_payload_descriptor = (void *) &NAME##_nfc_ndef_bin_payload_desc \ + } + + +/** @brief Macro for accessing the NFC NDEF record descriptor instance + * that you created with @ref NFC_NDEF_RECORD_BIN_DATA_DEF. + */ +#define NFC_NDEF_RECORD_BIN_DATA(NAME) (NAME##_nfc_ndef_bin_record_desc) + +/** @brief Macro for accessing the binary data descriptor that contains + * the payload of the record that you created with @ref NFC_NDEF_RECORD_BIN_DATA_DEF. + */ +#define NFC_NDEF_BIN_PAYLOAD_DESC(NAME) (NAME##_nfc_ndef_bin_payload_desc) + +/** + * @brief Function for encoding an NDEF record. + * + * This function encodes an NDEF record according to the provided record descriptor. + * + * @param[in] p_ndef_record_desc Pointer to the record descriptor. + * @param[in] record_location Location of the record within the NDEF message. + * @param[out] p_record_buffer Pointer to the record destination. If NULL, function will + * calculate the expected size of the record. + * @param[in,out] p_record_len Size of the available memory for the record as input. Size of the generated + * record as output. + * + * @retval NRF_SUCCESS If the record was encoded successfully. + * @retval NRF_ERROR_NO_MEM If the predicted record size is bigger than the provided buffer space. + * @retval NRF_ERROR_INVALID_PARAM If the location of the record is erroneous. + * @retval Other Other codes might be returned depending on the NDEF record payload constructor implementation. + */ +ret_code_t nfc_ndef_record_encode(nfc_ndef_record_desc_t const * p_ndef_record_desc, + nfc_ndef_record_location_t record_location, + uint8_t * p_record_buffer, + uint32_t * p_record_len); + +/** + * @brief Function for constructing the payload for an NFC NDEF record from binary data. + * + * This function copies data from a binary buffer to the payload field of the NFC NDEF record. + * + * @param[in] p_payload_descriptor Pointer to the descriptor of the binary data location and size. + * + * @param[out] p_buffer Pointer to the payload destination. If NULL, function will + * calculate the expected size of the record payload. + * @param[in,out] p_len Size of the available memory for the payload as input. Size of the copied payload + * as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_NO_MEM If the payload size is bigger than the provided buffer space. + */ +ret_code_t nfc_ndef_bin_payload_memcopy(nfc_ndef_bin_payload_desc_t * p_payload_descriptor, + uint8_t * p_buffer, + uint32_t * p_len); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif // NFC_NDEF_RECORD_H__ + diff --git a/libraries/nfc/src/ndef/nfc_ac_rec.c b/libraries/nfc/src/ndef/nfc_ac_rec.c new file mode 100644 index 000000000..a3054babb --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ac_rec.c @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_AC_REC) + +#include "nfc_ac_rec.h" +#include +#include "nrf_error.h" +#include "nrf.h" + +#define AC_REC_CPS_BYTE_SIZE 1 ///< Size of the field with CPS data. +#define AC_REC_DATA_REF_LEN_SIZE 1 ///< Size of the Data Reference Length field. +#define AC_REC_AUX_DATA_REF_COUNT_SIZE 1 ///< Size of the Data Reference Length field. + +const uint8_t nfc_ac_rec_type_field[2] = {'a', 'c'}; ///< Alternative Carrier Record type. + +/** + * @brief Function for calculating the payload length of the NFC NDEF Alternative Carrier record. + */ +static uint32_t nfc_ac_rec_payload_size_get(nfc_ac_rec_payload_desc_t const * p_ac_rec_payload_desc) +{ + int32_t i = 0; + // Initialize with size of byte with CPS. + uint32_t payload_size = AC_REC_CPS_BYTE_SIZE; + + // Add Carrier Data Reference size. + payload_size += p_ac_rec_payload_desc->carrier_data_ref.length + AC_REC_DATA_REF_LEN_SIZE; + + // Add Auxiliary Data Reference Count size. + payload_size += AC_REC_AUX_DATA_REF_COUNT_SIZE; + + for (i = 0; i < p_ac_rec_payload_desc->aux_data_ref_count; i++) + { + // Add Auxiliary Data Reference size. + payload_size += p_ac_rec_payload_desc->p_aux_data_ref[i].length + AC_REC_DATA_REF_LEN_SIZE; + } + + return payload_size; +} + + +ret_code_t nfc_ac_rec_payload_constructor(nfc_ac_rec_payload_desc_t * p_nfc_rec_ac_payload_desc, + uint8_t * p_buff, + uint32_t * p_len) +{ + int32_t i = 0; + uint32_t payload_size = nfc_ac_rec_payload_size_get(p_nfc_rec_ac_payload_desc); + + if (p_buff != NULL) + { + // Not enough space in the buffer, return an error. + if (payload_size > *p_len) + { + return NRF_ERROR_NO_MEM; + } + + // Invalid CPS value. + if ( p_nfc_rec_ac_payload_desc->cps & ~NFC_AC_CPS_MASK ) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Copy CPS. + *p_buff = p_nfc_rec_ac_payload_desc->cps; + p_buff += AC_REC_CPS_BYTE_SIZE; + + // Copy Carrier Data Reference. + *p_buff = p_nfc_rec_ac_payload_desc->carrier_data_ref.length; + p_buff += AC_REC_DATA_REF_LEN_SIZE; + + memcpy( p_buff, + p_nfc_rec_ac_payload_desc->carrier_data_ref.p_data, + p_nfc_rec_ac_payload_desc->carrier_data_ref.length ); + p_buff += p_nfc_rec_ac_payload_desc->carrier_data_ref.length; + + // Copy Auxiliary Data Reference. + *p_buff = p_nfc_rec_ac_payload_desc->aux_data_ref_count; + p_buff += AC_REC_AUX_DATA_REF_COUNT_SIZE; + + for (i = 0; i < p_nfc_rec_ac_payload_desc->aux_data_ref_count; i++) + { + *p_buff = p_nfc_rec_ac_payload_desc->p_aux_data_ref[i].length; + p_buff += AC_REC_DATA_REF_LEN_SIZE; + + memcpy( p_buff, + p_nfc_rec_ac_payload_desc->p_aux_data_ref[i].p_data, + p_nfc_rec_ac_payload_desc->p_aux_data_ref[i].length ); + p_buff += p_nfc_rec_ac_payload_desc->p_aux_data_ref[i].length; + } + } + + // Assign payload size to the return buffer. + *p_len = payload_size; + + return NRF_SUCCESS; +} + + +void nfc_ac_rec_auxiliary_data_ref_clear(nfc_ndef_record_desc_t * p_ac_rec) +{ + nfc_ac_rec_payload_desc_t * p_ac_rec_payload = + (nfc_ac_rec_payload_desc_t*)p_ac_rec->p_payload_descriptor; + + p_ac_rec_payload->aux_data_ref_count = 0; +} + + +ret_code_t nfc_ac_rec_auxiliary_data_ref_add(nfc_ndef_record_desc_t * p_ac_rec, + uint8_t * p_aux_data, + uint8_t aux_length) +{ + nfc_ac_rec_payload_desc_t * p_ac_rec_payload = + (nfc_ac_rec_payload_desc_t *)p_ac_rec->p_payload_descriptor; + + if (p_ac_rec_payload->aux_data_ref_count >= p_ac_rec_payload->max_aux_data_ref) + { + return NRF_ERROR_NO_MEM; + } + + p_ac_rec_payload->p_aux_data_ref[p_ac_rec_payload->aux_data_ref_count].p_data = p_aux_data; + p_ac_rec_payload->p_aux_data_ref[p_ac_rec_payload->aux_data_ref_count].length = aux_length; + p_ac_rec_payload->aux_data_ref_count++; + + return NRF_SUCCESS; +} + +#endif // NRF_MODULE_ENABLED(NFC_AC_REC) diff --git a/libraries/nfc/src/ndef/nfc_ac_rec.h b/libraries/nfc/src/ndef/nfc_ac_rec.h new file mode 100644 index 000000000..14328c385 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ac_rec.h @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_AC_REC_H__ +#define NFC_AC_REC_H__ + +/**@file + * + * @defgroup nfc_ac_rec ac (Alternative carrier) records + * @{ + * @ingroup nfc_ble_pair_msg + * + * @brief Generation of NFC NDEF Alternative Carrier records for NDEF messages. + * + */ + +#include +#include "nfc_ndef_record.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define AC_REC_CPS_BYTE_SIZE 1 ///< Size of the field with CPS data. +#define AC_REC_DATA_REF_LEN_SIZE 1 ///< Size of the Data Reference Length field. +#define AC_REC_AUX_DATA_REF_COUNT_SIZE 1 ///< Size of the Data Reference Length field. + +/** + * @brief Carrier Power State. + * + * Possible Carrier Power State field values in an Alternative Carrier record. + */ +typedef enum +{ + NFC_AC_CPS_INACTIVE = 0x00, ///< Alternative Carrier inactive. + NFC_AC_CPS_ACTIVE = 0x01, ///< Alternative Carrier active. + NFC_AC_CPS_ACTIVATING = 0x02, ///< Alternative Carrier activating. + NFC_AC_CPS_UNKNOWN = 0x03 ///< Alternative Carrier power status unknown. +} nfc_ac_rec_cps_t; + +#define NFC_AC_CPS_MASK (NFC_AC_CPS_UNKNOWN) ///< Mask of Carrier Power State bits in a first ac record byte. + +/** + * @brief Carrier Data Reference and Auxiliary Data Reference descriptor. + */ +typedef struct +{ + uint8_t length; ///< Length of the data field. + uint8_t * p_data; ///< Pointer to the Data Reference characters. Not relevant if length is 0. +} nfc_ac_rec_data_ref_t; + +/** + * @brief Alternative Carrier record payload descriptor. + */ +typedef struct +{ + nfc_ac_rec_cps_t cps; ///< Carrier Power State value. + nfc_ac_rec_data_ref_t carrier_data_ref; ///< Carrier Data Reference. + uint8_t const max_aux_data_ref; ///< Maximum number of Auxiliary Data Reference fields. + uint8_t aux_data_ref_count; ///< Number of Auxiliary Data Reference fields. + nfc_ac_rec_data_ref_t * p_aux_data_ref; ///< Pointer to the Auxiliary Data Reference fields. +} nfc_ac_rec_payload_desc_t; + + +/** + * @brief Constructor for an NFC NDEF Alternative Carrier record payload. + * + * This function encodes the payload of an Alternative Carrier record as specified in the Connection + * Handover standard. It implements an API compatible with @ref p_payload_constructor_t. + */ +ret_code_t nfc_ac_rec_payload_constructor(nfc_ac_rec_payload_desc_t * p_nfc_rec_ac_payload_desc, + uint8_t * p_buff, + uint32_t * p_len); + +/** + * @brief External reference to the type field of the Alternative Carrier record, defined in the + * file @c nfc_ac_rec.c. It is used in the @ref NFC_NDEF_AC_RECORD_DESC_DEF macro. + */ +extern const uint8_t nfc_ac_rec_type_field[2]; + +/** + * @brief Size of the type field of the Alternative Carrier record, defined in the + * file @c nfc_ac_rec.c. It is used in the @ref NFC_NDEF_AC_RECORD_DESC_DEF macro. + */ +#define NFC_AC_REC_TYPE_LENGTH 2 + +/** + *@brief Macro for creating and initializing an NFC NDEF record descriptor for an Alternative Carrier record. + * + * This macro creates and initializes an instance of type @ref nfc_ndef_record_desc_t and + * an instance of type @ref nfc_ac_rec_payload_desc_t, which together constitute an instance of an Alternative Carrier record. + * + * Use the macro @ref NFC_NDEF_AC_RECORD_DESC to access the NDEF Alternative Carrier record descriptor instance. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF message encoding (see @ref nfc_ble_full_handover_select_msg_encode) + * must be done in the same variable scope. + * + * @param[in] NAME Name of the created record descriptor instance. + * @param[in] CPS Carrier Power State value. + * @param[in] CARR_DATA_REF_LEN Length of the Carrier Data Reference field. + * @param[in] P_CARR_DATA_REF Pointer to the Carrier Data Reference field. + * @param[in] MAX_AUX_DATA_REF Maximum number of Auxiliary Data Reference fields. + */ +#define NFC_NDEF_AC_RECORD_DESC_DEF(NAME, \ + CPS, \ + CARR_DATA_REF_LEN, \ + P_CARR_DATA_REF, \ + MAX_AUX_DATA_REF) \ + nfc_ac_rec_data_ref_t NAME##_nfc_ac_rec_aux_data_ref_array[MAX_AUX_DATA_REF]; \ + nfc_ac_rec_payload_desc_t NAME##_nfc_ac_rec_payload_desc = \ + { \ + .cps = CPS, \ + .carrier_data_ref = {CARR_DATA_REF_LEN, P_CARR_DATA_REF}, \ + .max_aux_data_ref = MAX_AUX_DATA_REF, \ + .aux_data_ref_count = 0, \ + .p_aux_data_ref = NAME##_nfc_ac_rec_aux_data_ref_array \ + }; \ + NFC_NDEF_GENERIC_RECORD_DESC_DEF(NAME, \ + TNF_WELL_KNOWN, \ + 0, \ + 0, \ + nfc_ac_rec_type_field, \ + NFC_AC_REC_TYPE_LENGTH, \ + nfc_ac_rec_payload_constructor, \ + &(NAME##_nfc_ac_rec_payload_desc)) + +/** + * @brief Macro for accessing the NFC NDEF Alternative Carrier record descriptor + * instance that was created with @ref NFC_NDEF_AC_RECORD_DESC_DEF. + */ +#define NFC_NDEF_AC_RECORD_DESC(NAME) NFC_NDEF_GENERIC_RECORD_DESC(NAME) + +/** + * @brief Function for clearing an Auxiliary Data Reference in an NFC NDEF Alternative Carrier record. + * + * This function clears the Auxiliary Data References from the Alternative Carrier record. + * + * @param[in, out] p_ac_rec Pointer to the Alternative Carrier record descriptor. + */ +void nfc_ac_rec_auxiliary_data_ref_clear(nfc_ndef_record_desc_t * p_ac_rec); + +/** + * @brief Function for adding an Auxiliary Data Reference to an NFC NDEF Alternative Carrier record. + * + * @param[in, out] p_ac_rec Pointer to an ac record. + * @param[in] p_aux_data Pointer to the Auxiliary Data Reference data buffer. + * @param[in] aux_length Length of the Auxiliary Data Reference data. + * + * @retval NRF_SUCCESS If the Auxiliary Data Reference was added successfully. + * @retval NRF_ERROR_NO_MEM If the record already contains the maximum number of Auxiliary Data References. + */ +ret_code_t nfc_ac_rec_auxiliary_data_ref_add(nfc_ndef_record_desc_t * p_ac_rec, + uint8_t * p_aux_data, + uint8_t aux_length); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_AC_REC_H__ diff --git a/libraries/nfc/src/ndef/nfc_ac_rec_parser.c b/libraries/nfc/src/ndef/nfc_ac_rec_parser.c new file mode 100644 index 000000000..7100d48ed --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ac_rec_parser.c @@ -0,0 +1,205 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_AC_REC_PARSER) +#include "nfc_ac_rec_parser.h" +#include "util/sdk_macros.h" + +/** + * @brief Function for parsing Data Reference field inside Alternative Carrier record payload. + * + * This function parses Data Reference field inside Alternative Carrier record payload and extracts + * its descriptor. + * + * @param[in,out] pp_buff Pointer to pointer to the remaining payload data. + * @param[in,out] p_len Pointer to the length of remaining payload data. + * @param[in,out] p_ref_field Pointer to the structure that will be used to hold + * parsed data. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_NULL If provided buffer for Data Reference in \p p_ref_field is + * null. + * @retval NRF_ERROR_NO_MEM If the buffer provided for Data Reference in \p p_ref_field + * does not have enough space to store it. + * @retval NRF_ERROR_INVALID_LENGTH If Data Reference length exceeds record payload. + */ +static ret_code_t ac_rec_reference_field_parse(uint8_t ** const pp_buff, + uint32_t * const p_len, + nfc_ac_rec_data_ref_t * const p_ref_field) +{ + if (p_ref_field->length < **pp_buff) + { + return NRF_ERROR_NO_MEM; + } + p_ref_field->length = **pp_buff; + *pp_buff += AC_REC_DATA_REF_LEN_SIZE; + (*p_len) -= AC_REC_DATA_REF_LEN_SIZE; + + if (*p_len < p_ref_field->length) + { + return NRF_ERROR_INVALID_LENGTH; + } + VERIFY_PARAM_NOT_NULL(p_ref_field->p_data); + memcpy( p_ref_field->p_data, + *pp_buff, + p_ref_field->length ); + *pp_buff += p_ref_field->length; + (*p_len) -= p_ref_field->length; + + return NRF_SUCCESS; +} + +/** + * @brief Function for parsing Alternative Carrier record payload. + * + * This function parses Alternative Carrier record payload and extracts its payload descriptor. + * + * @param[in] p_buff Pointer to the record payload. + * @param[in] p_len Pointer to the record payload length. + * @param[in,out] p_ac_rec_payload_data Pointer to the structure that will be used to hold + * parsed data. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_NULL If any provided arguments or any needed buffers stored in + * \p p_ac_rec_payload_data are nulls. + * @retval NRF_ERROR_NO_MEM If any from provided buffers does not have enough space + * to store its data. + * @retval NRF_ERROR_INVALID_LENGTH If any length field exceeds record payload. + * @retval NRF_ERROR_INVALID_PARAM If Carrier Power State field has incorrect value. + */ +static ret_code_t nfc_ac_payload_parse(uint8_t * p_buff, + uint32_t * const p_len, + nfc_ac_rec_payload_desc_t * const p_ac_rec_payload_data) +{ + if ( (p_buff == NULL) || (p_len == NULL) || (p_ac_rec_payload_data == NULL) ) + { + return NRF_ERROR_NULL; + } + + if (*p_len < AC_REC_CPS_BYTE_SIZE + AC_REC_DATA_REF_LEN_SIZE + AC_REC_AUX_DATA_REF_COUNT_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + // Copy CPS to ac record payload descriptor. + if (*p_buff & ~NFC_AC_CPS_MASK) + { + return NRF_ERROR_INVALID_PARAM; + } + + p_ac_rec_payload_data->cps = (nfc_ac_rec_cps_t) *p_buff; + p_buff += AC_REC_CPS_BYTE_SIZE; + (*p_len) -= AC_REC_CPS_BYTE_SIZE; + + // Copy Carrier Data Reference to ac record payload descriptor. + ret_code_t err_code = ac_rec_reference_field_parse(&p_buff, + p_len, + &p_ac_rec_payload_data->carrier_data_ref); + VERIFY_SUCCESS(err_code); + + // Copy Auxiliary Data Reference to ac record payload descriptor. + if ( p_ac_rec_payload_data->aux_data_ref_count < *p_buff) + { + return NRF_ERROR_NO_MEM; + } + p_ac_rec_payload_data->aux_data_ref_count = *p_buff; + p_buff += AC_REC_AUX_DATA_REF_COUNT_SIZE; + (*p_len) -= AC_REC_AUX_DATA_REF_COUNT_SIZE; + + if (p_ac_rec_payload_data->aux_data_ref_count != 0) + { + VERIFY_PARAM_NOT_NULL(p_ac_rec_payload_data->p_aux_data_ref); + } + + for (uint8_t i = 0; i < p_ac_rec_payload_data->aux_data_ref_count; i++) + { + err_code = ac_rec_reference_field_parse(&p_buff, + p_len, + &(p_ac_rec_payload_data->p_aux_data_ref[i])); + VERIFY_SUCCESS(err_code); + } + + // Check if all payload data were parsed. + if (*p_len != 0) + { + return NRF_ERROR_INVALID_LENGTH; + } + + return NRF_SUCCESS; +} + +ret_code_t nfc_ac_rec_parse(nfc_ndef_record_desc_t const * const p_rec_desc, + nfc_ac_rec_payload_desc_t * const p_ac_rec_payload_data) +{ + ret_code_t err_code; + + if (p_rec_desc->tnf != TNF_WELL_KNOWN) + { + return NRF_ERROR_INVALID_DATA; + } + + if (p_rec_desc->type_length != sizeof(nfc_ac_rec_type_field)) + { + return NRF_ERROR_INVALID_DATA; + } + + if (memcmp(p_rec_desc->p_type, nfc_ac_rec_type_field, sizeof(nfc_ac_rec_type_field)) != 0) + { + return NRF_ERROR_INVALID_DATA; + } + + if (p_rec_desc->payload_constructor != (p_payload_constructor_t) nfc_ndef_bin_payload_memcopy) + { + return NRF_ERROR_NOT_SUPPORTED; + } + + uint8_t const * p_payload = + ((nfc_ndef_bin_payload_desc_t *)(p_rec_desc->p_payload_descriptor))->p_payload; + uint32_t payload_length = + ((nfc_ndef_bin_payload_desc_t *)(p_rec_desc->p_payload_descriptor))->payload_length; + + err_code = nfc_ac_payload_parse((uint8_t *) p_payload, + &payload_length, + p_ac_rec_payload_data); + + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_AC_REC_PARSER) diff --git a/libraries/nfc/src/ndef/nfc_ac_rec_parser.h b/libraries/nfc/src/ndef/nfc_ac_rec_parser.h new file mode 100644 index 000000000..9f45f2739 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ac_rec_parser.h @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup nfc_ac_rec_parser Alternative Carrier records parser + * @{ + * @ingroup nfc_ble_pair_msg + * @brief Functions for parsing and decoding Alternative Carrier records. + */ + +#ifndef __NFC_AC_REC_PARSER_H__ +#define __NFC_AC_REC_PARSER_H__ + +#include "nfc_ndef_record.h" +#include "nfc_ac_rec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for parsing general record description as Alternative Carrier record. + * + * This function checks if record description matches the Alternative Carrier record and extracts + * its payload structure. It is required for the record description to use binary payload + * descriptor. + * + * @param[in] p_rec_desc Pointer to the record descriptor. + * @param[in,out] p_ac_rec_payload_data Pointer to the structure that will be used to hold + * parsed data. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_INVALID_DATA If the NDEF record type or TNF is incorrect. + * @retval NRF_ERROR_NOT_SUPPORTED If the payload descriptor is not binary. + * @retval NRF_ERROR_NULL If any provided arguments or any needed buffers stored in + * \p p_ac_rec_payload_data are nulls. + * @retval NRF_ERROR_NO_MEM If any from provided buffers does not have enough space + * to store its data. + * @retval NRF_ERROR_INVALID_LENGTH If any length field exceeds record payload. + * @retval NRF_ERROR_INVALID_PARAM If Carrier Power State field has incorrect value. + */ +ret_code_t nfc_ac_rec_parse(nfc_ndef_record_desc_t const * const p_rec_desc, + nfc_ac_rec_payload_desc_t * const p_ac_rec_payload_data); + +#ifdef __cplusplus +} +#endif + +#endif // __NFC_AC_REC_PARSER_H__ + +/** @} */ diff --git a/libraries/nfc/src/ndef/nfc_ble_oob_advdata.c b/libraries/nfc/src/ndef/nfc_ble_oob_advdata.c new file mode 100644 index 000000000..9a3e50551 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_oob_advdata.c @@ -0,0 +1,402 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_BLE_OOB_ADVDATA) + +#include "nfc_ble_oob_advdata.h" +#include "util/sdk_common.h" +#include "nfc_ble_pair_msg.h" +#include "nfc_ble_pair_common.h" + + +/** + * @brief Macro for verifying basic parameters used for encoding single BLE AD Type. + * + * It verifies if provided buffer is NULL and if there is enough space for the encoded data. + * In case of NULL pointer buffer, necessary space for current AD Type is calculated. + * + * @param[in] P_ENCODED_DATA Buffer for the encoded data. + * @param[in] P_OFFSET Pointer to index of the first free cell in the buffer. + * @param[in] AD_TYPE_SIZE Size of the single AD Type. + * @param[in] MAX_SIZE Maximal size of the provided buffer. + */ +#define NFC_BLE_OOB_ADVDATA_INPUT_VERIFY( P_ENCODED_DATA, P_OFFSET, AD_TYPE_SIZE, MAX_SIZE) \ + if ( (P_ENCODED_DATA) == NULL ) \ + { \ + *(P_OFFSET) += (AD_TYPE_SIZE); \ + return NRF_SUCCESS; \ + } \ + if ( *(P_OFFSET) + (AD_TYPE_SIZE) > (MAX_SIZE) ) \ + { \ + return NRF_ERROR_DATA_SIZE; \ + } + +/**@brief Function for encoding data of Security Manager OOB Flags AD Type. + * + * @param[in] oob_flags Security Manager OOB Flags AD Type payload. + * @param[out] p_encoded_data Pointer to the buffer where encoded data will be returned. + * @param[in,out] p_offset \c in: Offset of \p p_encoded_data buffer before this AD type encoding. + * \c out: Offset of \p p_encoded_data buffer after this AD type encoding. + * @param[in] max_size Size of \p p_encoded_data buffer. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_DATA_SIZE If the provided buffer size is too small. + */ +static ret_code_t sec_mgr_oob_flags_encode(uint8_t oob_flags, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + NFC_BLE_OOB_ADVDATA_INPUT_VERIFY(p_encoded_data, p_offset, AD_TYPE_OOB_FLAGS_SIZE, max_size); + + // Encode flags. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + AD_TYPE_OOB_FLAGS_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS; + *p_offset += AD_TYPE_FIELD_SIZE; + p_encoded_data[*p_offset] = oob_flags; + *p_offset += AD_TYPE_OOB_FLAGS_DATA_SIZE; + + return NRF_SUCCESS; +} + +/**@brief Function for encoding data of Security Manager TK Value AD Type. + * + * @param[in] p_tk_value Security Manager TK Value AD Type payload. + * @param[out] p_encoded_data Pointer to the buffer where encoded data will be returned. + * @param[in,out] p_offset \c in: Offset of \p p_encoded_data buffer before this AD type encoding. + * \c out: Offset of \p p_encoded_data buffer after this AD type encoding. + * @param[in] max_size Size of \p p_encoded_data buffer. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_DATA_SIZE If the provided buffer size is too small. + */ +static ret_code_t tk_value_encode(ble_advdata_tk_value_t * p_tk_value, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + ret_code_t err_code; + + NFC_BLE_OOB_ADVDATA_INPUT_VERIFY(p_encoded_data, p_offset, AD_TYPE_TK_VALUE_SIZE, max_size); + + // Encode TK Value. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + AD_TYPE_TK_VALUE_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE; + *p_offset += AD_TYPE_FIELD_SIZE; + + // Remember location of TK in the buffer if this feature was enabled. + err_code = nfc_tk_to_group_add(&p_encoded_data[*p_offset]); + VERIFY_SUCCESS(err_code); + + nfc_tk_value_payload_encode(p_tk_value, &p_encoded_data[*p_offset]); + (*p_offset) += AD_TYPE_TK_VALUE_DATA_SIZE; + + return NRF_SUCCESS; +} + +/**@brief Function for encoding LESC OOB data in the CH NDEF message. + * + * @param[in] p_lesc_value Pointer to the LESC OOB values to be encoded. + * @param[out] p_encoded_data Pointer to the buffer where encoded data will be returned. + * @param[in,out] p_offset \c in: Offset of \p p_encoded_data buffer before this AD type encoding. + * \c out: Offset of \p p_encoded_data buffer after this AD type encoding. + * @param[in] max_size Size of \p p_encoded_data buffer. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_DATA_SIZE If the provided buffer size is too small. + */ +static ret_code_t lesc_value_encode(ble_gap_lesc_oob_data_t * p_lesc_value, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + ret_code_t err_code; + + NFC_BLE_OOB_ADVDATA_INPUT_VERIFY(p_encoded_data, p_offset, AD_TYPE_LESC_SIZE, max_size); + + // Encode LESC Confirm Value. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + AD_TYPE_CONFIRM_VALUE_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_LESC_CONFIRMATION_VALUE; + *p_offset += AD_TYPE_FIELD_SIZE; + + memcpy(&p_encoded_data[*p_offset], p_lesc_value->c, sizeof(p_lesc_value->c)); + + uint8_t *p_confirm = &p_encoded_data[*p_offset]; + + (*p_offset) += AD_TYPE_CONFIRM_VALUE_DATA_SIZE; + + // Encode LESC Random Value. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + AD_TYPE_RANDOM_VALUE_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_LESC_RANDOM_VALUE; + *p_offset += AD_TYPE_FIELD_SIZE; + + memcpy(&p_encoded_data[*p_offset], p_lesc_value->r, sizeof(p_lesc_value->r)); + + uint8_t *p_random = &p_encoded_data[*p_offset]; + + (*p_offset) += AD_TYPE_RANDOM_VALUE_DATA_SIZE; + + // Remember location of LESC OOB data in the buffer in case of key changes. + err_code = nfc_lesc_pos_set(p_confirm, p_random); + + return err_code; +} + +/**@brief Function for encoding data of LE Role AD Type. + * + * @param[in] le_role LE Role AD Type payload. + * @param[out] p_encoded_data Pointer to the buffer where encoded data will be returned. + * @param[in,out] p_offset \c in: Offset of \p p_encoded_data buffer before this AD type encoding. + * \c out: Offset of \p p_encoded_data buffer after this AD type encoding. + * @param[in] max_size Size of \p p_encoded_data buffer. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_DATA_SIZE If the provided buffer size is too small. + * @retval NRF_ERROR_INVALID_PARAM If \p le_role parameter has invalid value. + */ +static ret_code_t le_role_encode(ble_advdata_le_role_t le_role, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + NFC_BLE_OOB_ADVDATA_INPUT_VERIFY(p_encoded_data, p_offset, AD_TYPE_LE_ROLE_SIZE, max_size); + + // Encode LE Role. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + AD_TYPE_LE_ROLE_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_LE_ROLE; + *p_offset += AD_TYPE_FIELD_SIZE; + switch (le_role) + { + case BLE_ADVDATA_ROLE_ONLY_PERIPH: + p_encoded_data[*p_offset] = NFC_BLE_ADVDATA_ROLE_ENCODED_ONLY_PERIPH; + break; + case BLE_ADVDATA_ROLE_ONLY_CENTRAL: + p_encoded_data[*p_offset] = NFC_BLE_ADVDATA_ROLE_ENCODED_ONLY_CENTRAL; + break; + case BLE_ADVDATA_ROLE_BOTH_PERIPH_PREFERRED: + p_encoded_data[*p_offset] = NFC_BLE_ADVDATA_ROLE_ENCODED_BOTH_PERIPH_PREFERRED; + break; + case BLE_ADVDATA_ROLE_BOTH_CENTRAL_PREFERRED: + p_encoded_data[*p_offset] = NFC_BLE_ADVDATA_ROLE_ENCODED_BOTH_CENTRAL_PREFERRED; + break; + default: + return NRF_ERROR_INVALID_PARAM; + } + *p_offset += AD_TYPE_LE_ROLE_DATA_SIZE; + + return NRF_SUCCESS; +} + +/**@brief Function for calculating the size of Local Name AD Type. + * + * @param[in] p_advdata Pointer to the structure for specifying the content of encoded data. + * @param[out] p_len Size of the buffer that is necessary to encode Local Name AD Type. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval Other Other error codes might be returned depending on + * @ref sd_ble_gap_device_name_get function. + */ +__STATIC_INLINE ret_code_t nfc_ble_oob_name_size_calc(ble_advdata_t const * const p_advdata, + uint16_t * const p_len) +{ + ret_code_t err_code = NRF_SUCCESS; + uint16_t device_len; + + if (p_advdata->name_type == BLE_ADVDATA_SHORT_NAME) + { + device_len = p_advdata->short_name_len; + } + else + { + err_code = sd_ble_gap_device_name_get(NULL, &device_len); + } + + *p_len += AD_LENGTH_FIELD_SIZE + AD_TYPE_FIELD_SIZE + device_len; + return err_code; +} + +/**@brief Function for calculating the size of AD Types which are encoded by @ref ble_advdata_encode function. + * + * @param[in] p_advdata Pointer to the structure for specifying the content of encoded data. + * @param[out] p_len Size of the buffer that is necessary to encode AD Types. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval Other Other error codes might be returned depending on + * @ref nfc_ble_oob_name_size_calc function. + */ +static ret_code_t nfc_ble_oob_adv_data_size_calc(ble_advdata_t const * const p_advdata, + uint16_t * const p_len) +{ + ret_code_t err_code = NRF_SUCCESS; + + if (p_advdata->include_ble_device_addr) + { + *p_len += AD_TYPE_BLE_DEVICE_ADDR_SIZE; + } + if (p_advdata->include_appearance) + { + *p_len += AD_TYPE_APPEARANCE_SIZE; + } + if (p_advdata->flags != 0) + { + *p_len += AD_TYPE_FLAGS_SIZE; + } + if (p_advdata->name_type != BLE_ADVDATA_NO_NAME) + { + err_code = nfc_ble_oob_name_size_calc(p_advdata, p_len); + } + return err_code; +} + +#if ADVANCED_ADVDATA_SUPPORT == 0 +/**@brief Function for verifying if BLE advertising data structure contains only supported AD Types + * by this encoding module. + * + * @param[in] advdata Structure with BLE advertising data. + * + * @retval NRF_SUCCESS If the verification was successful. + * @retval NRF_ERROR_INVALID_PARAM If there is any AD type which is not supported by this + * module. + */ +static ret_code_t nfc_ble_oob_adv_data_check(ble_advdata_t advdata) +{ + advdata.p_sec_mgr_oob_flags = NULL; + advdata.p_tk_value = NULL; + advdata.le_role = BLE_ADVDATA_ROLE_NOT_PRESENT; + advdata.include_ble_device_addr = false; + advdata.include_appearance = false; + advdata.flags = 0; + advdata.name_type = BLE_ADVDATA_NO_NAME; + advdata.short_name_len = 0; + advdata.p_lesc_data = NULL; + + ble_advdata_t pattern_advdata; + memset(&pattern_advdata, 0, sizeof(ble_advdata_t)); + + if ( memcmp( &pattern_advdata, &advdata, sizeof(ble_advdata_t)) == 0 ) + { + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_INVALID_PARAM; + } +} +#endif //ADVANCED_ADVDATA_SUPPORT + +ret_code_t nfc_ble_oob_adv_data_encode(ble_advdata_t const * const p_advdata, + uint8_t * const p_encoded_data, + uint16_t * const p_len) +{ + ret_code_t err_code = NRF_SUCCESS; + uint16_t max_size = *p_len; + uint16_t offset = 0; + +#if ADVANCED_ADVDATA_SUPPORT + // In this mode, you cannot count the NDEF message length. + VERIFY_FALSE(p_encoded_data == NULL, NRF_ERROR_INVALID_PARAM); +#else + // Verify ADV data structure. + err_code = nfc_ble_oob_adv_data_check(*p_advdata); + VERIFY_SUCCESS(err_code); +#endif //ADVANCED_ADVDATA_SUPPORT + + // Encode Security Manager OOB Flags. + if (p_advdata->p_sec_mgr_oob_flags != NULL) + { + err_code = sec_mgr_oob_flags_encode(*p_advdata->p_sec_mgr_oob_flags, + p_encoded_data, + &offset, + max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode LESC keys + if (p_advdata->p_lesc_data != NULL) + { + err_code = lesc_value_encode(p_advdata->p_lesc_data, p_encoded_data, &offset, max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode Security Manager TK value. + if (p_advdata->p_tk_value != NULL) + { + err_code = tk_value_encode(p_advdata->p_tk_value, p_encoded_data, &offset, max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode LE Role. + if (BLE_ADVDATA_ROLE_NOT_PRESENT != p_advdata->le_role) + { + err_code = le_role_encode(p_advdata->le_role, p_encoded_data, &offset, max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode remaining AD Types or precalculate necessary buffer space. + if (p_encoded_data != NULL) + { + uint16_t adv_data_size = max_size - offset; + err_code = ble_advdata_encode(p_advdata, p_encoded_data + offset, &adv_data_size); + *p_len = offset + adv_data_size; + } + else + { + err_code = nfc_ble_oob_adv_data_size_calc(p_advdata, &offset); + *p_len = offset; + } + + return err_code; +} + +void nfc_tk_value_payload_encode(ble_advdata_tk_value_t * p_tk_value, + uint8_t * p_tk_payload_data) +{ + for (uint8_t i = 0; i < AD_TYPE_TK_VALUE_DATA_SIZE; i++) + { + *(p_tk_payload_data++) = p_tk_value->tk[i]; + } +} + +#endif // NRF_MODULE_ENABLED(NFC_BLE_OOB_ADVDATA) diff --git a/libraries/nfc/src/ndef/nfc_ble_oob_advdata.h b/libraries/nfc/src/ndef/nfc_ble_oob_advdata.h new file mode 100644 index 000000000..e07458040 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_oob_advdata.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup nfc_ble_oob_advdata Advertising and Scan Response Data Encoder for NFC OOB pairing + * @{ + * @ingroup nfc_ble_pair_msg + * @brief Function for encoding data in the Advertising and Scan Response Data format, which + * can be used to create payload of NFC message intended for initiating the Out-of-Band + * pairing. + */ + +#ifndef NFC_BLE_OOB_ADVDATA_H__ +#define NFC_BLE_OOB_ADVDATA_H__ + +#include +#include "util/ble_advdata.h" +#include "util/app_util.h" +#include "util/sdk_errors.h" + + +/**@brief Function for encoding data in the Advertising and Scan Response data format, which + * is used for NFC OOB pairing. + * + * + * @details This function encodes data into the Advertising and Scan Response data format (AD structures). + * Encoding is based on the selections in the supplied structures. This function uses + * @ref ble_advdata_encode to encode regular data and adds additional AD Structures which are specific + * for NFC OOB pairing: Security Manager TK Value, LESC OOB values, OOB Flags, and LE Role. + * + * @param[in] p_advdata Pointer to the structure for specifying the content of encoded data. + * @param[out] p_encoded_data Pointer to the buffer where encoded data will be returned. + * @param[in,out] p_len \c in: Size of \p p_encoded_data buffer. + * \c out: Length of encoded data. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If the operation failed because a wrong parameter was provided in \p p_advdata. + * @retval NRF_ERROR_DATA_SIZE If the operation failed because not all the requested data could fit into the + * provided buffer or some encoded AD structure is too long and its + * length cannot be encoded with one octet. + */ +ret_code_t nfc_ble_oob_adv_data_encode(ble_advdata_t const * const p_advdata, + uint8_t * const p_encoded_data, + uint16_t * const p_len); + +/**@brief Function for encoding payload field of Security Manager TK Value AD Type. + * + * @param[in] p_tk_value Security Manager TK Value AD Type payload. + * @param[out] p_tk_payload_data Pointer to the buffer where TK payload data will be stored. + * + */ +void nfc_tk_value_payload_encode(ble_advdata_tk_value_t * p_tk_value, + uint8_t * p_tk_payload_data); + +#endif // NFC_BLE_OOB_ADVDATA_H__ + +/** @} */ diff --git a/libraries/nfc/src/ndef/nfc_ble_oob_advdata_parser.c b/libraries/nfc/src/ndef/nfc_ble_oob_advdata_parser.c new file mode 100644 index 000000000..9302c7dc4 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_oob_advdata_parser.c @@ -0,0 +1,524 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_BLE_OOB_ADVDATA_PARSER) +#include "nfc_ble_oob_advdata_parser.h" +#include "util/app_util.h" +#include "nfc_ble_pair_common.h" + +#define NRF_LOG_MODULE_NAME ble_oob_ad_parser +#include "util/nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +/* Workaround for using NRF_LOG_RAW_INFO() macro only when logging level is "DEBUG" */ +#if (NRF_LOG_LEVEL > 3) +#define NRF_BLE_OOB_AD_PARSER_LOG_DEBUG(...) NRF_LOG_RAW_INFO(__VA_ARGS__) +#else // (NRF_LOG_LEVEL > 3) +#define NRF_BLE_OOB_AD_PARSER_LOG_DEBUG(...) +#endif // (NRF_LOG_LEVEL > 3) + +#define EARLY_TERMINATOR 0 /* Value of AD Structure Length field indicating an early + termination of Advertising or Scan Response Data. */ +#define FIELD_LEN_INC_VAL 1 /* Incorrect Value of AD Structure Length field. */ + +/** @brief Values used with @ref ad_type_counter_t. */ +typedef enum +{ + AD_TYPE_NOT_PRESENT = 0, /* Value indicating that AD type is not present. */ + AD_TYPE_OCCUR_THRES = 1 /* Maximal occurrence number of any AD type within the buffer */ +} ad_type_counter_values_t; + +/**@brief Internal module structure indicating how many BLE AD fields of the same type are in the buffer. */ +typedef struct +{ + uint8_t name_type; /* Number of Short and Full Device Name AD Structures. */ + uint8_t addr_type; /* Number of LE Bluetooth Device Address AD Structures. */ + uint8_t appear_type; /* Number of Appearance AD Structures. */ + uint8_t flags_type; /* Number of Flags AD Structures. */ + uint8_t le_role_type; /* Number of LE Role AD Structures. */ + uint8_t tk_type; /* Number of Security Manager TK AD Structures. */ + uint8_t sec_mgr_oob_flags_type; /* Number of Security Manager OOB Flags AD Structures. */ + uint8_t lesc_confirm_type; /* Number of LESC OOB Confirmation Value AD Structures. */ + uint8_t lesc_random_type; /* Number of LESC OOB Random Value AD Structures. */ +} ad_type_counter_t; + +/**@brief Decodes and stores AD Data from Flags AD Structure. */ +__STATIC_INLINE ret_code_t flags_decode(uint8_t const * p_flags_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + if (len != AD_TYPE_FLAGS_DATA_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + p_nfc_ble_pairing_data->flags = *p_flags_data; + + return NRF_SUCCESS; +} + +void nfc_oob_data_printout(nfc_ble_oob_pairing_data_t const * const p_pairing_data) +{ + NRF_LOG_RAW_INFO("\r\n"); + NRF_LOG_INFO("BLE Advertising data contents"); + NRF_LOG_INFO("Device name: \"%s\"", NRF_LOG_PUSH((char *)p_pairing_data->device_name.p_name)); + NRF_LOG_INFO("Device Address: "); + + for (int i=0; i < BLE_GAP_ADDR_LEN; ++i) + { + NRF_LOG_RAW_INFO("%02X ", p_pairing_data->p_device_addr->addr[i]); + } + NRF_LOG_RAW_INFO("\r\n"); + + if (p_pairing_data->p_tk_value != NULL) + { + NRF_LOG_INFO("Device Temporary Key present."); + for (int i=0; i < BLE_GAP_SEC_KEY_LEN; ++i) + { + NRF_BLE_OOB_AD_PARSER_LOG_DEBUG("%02X ", p_pairing_data->p_tk_value->tk[i]); + } + NRF_BLE_OOB_AD_PARSER_LOG_DEBUG("\r\n"); + } + else + { + NRF_LOG_INFO("Device Temporary Key not present."); + } + + if (p_pairing_data->p_lesc_confirm_value != NULL && p_pairing_data->p_lesc_random_value) + { + NRF_LOG_INFO("LESC Confirmation Value present."); + for (int i=0; i < BLE_GAP_SEC_KEY_LEN; ++i) + { + NRF_BLE_OOB_AD_PARSER_LOG_DEBUG("%02X ", p_pairing_data->p_lesc_confirm_value[i]); + } + NRF_BLE_OOB_AD_PARSER_LOG_DEBUG("\r\n"); + + NRF_LOG_INFO("LESC Random Value present."); + for (int i=0; i < BLE_GAP_SEC_KEY_LEN; ++i) + { + NRF_BLE_OOB_AD_PARSER_LOG_DEBUG("%02X ", p_pairing_data->p_lesc_random_value[i]); + } + NRF_BLE_OOB_AD_PARSER_LOG_DEBUG("\r\n"); + } + else + { + NRF_LOG_INFO("LESC data not present."); + } + + NRF_LOG_RAW_INFO("\r\n"); +} + +/**@brief Decodes and stores AD Data that is common for Short and Full Device Name AD Structures. */ +static ret_code_t name_decode(uint8_t const * p_name_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + uint8_t * p_name = p_nfc_ble_pairing_data->device_name.p_name; + uint8_t * p_name_len = &p_nfc_ble_pairing_data->device_name.len; + + VERIFY_PARAM_NOT_NULL(p_name); + if (*p_name_len < len) + { + return NRF_ERROR_NO_MEM; + } + + memcpy(p_name, p_name_data, len); + *p_name_len = len; + + return NRF_SUCCESS; +} + +/**@brief Decodes and stores AD Data from Short Device Name AD Structure. */ +__STATIC_INLINE ret_code_t short_name_decode(uint8_t const * p_short_name_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + p_nfc_ble_pairing_data->device_name.name_type = BLE_ADVDATA_SHORT_NAME; + + return name_decode(p_short_name_data, len, p_nfc_ble_pairing_data); +} + +/**@brief Decodes and stores AD Data from Full Device Name AD Structure. */ +__STATIC_INLINE ret_code_t full_name_decode(uint8_t const * p_full_name_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + p_nfc_ble_pairing_data->device_name.name_type = BLE_ADVDATA_FULL_NAME; + + return name_decode(p_full_name_data, len, p_nfc_ble_pairing_data); +} + +/**@brief Decodes and stores AD Data from Security Manager TK AD Structure. */ +static ret_code_t tk_value_decode(uint8_t const * p_tk_value_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + if (len != AD_TYPE_TK_VALUE_DATA_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + ble_advdata_tk_value_t * p_tk_value = p_nfc_ble_pairing_data->p_tk_value; + VERIFY_PARAM_NOT_NULL(p_tk_value); + + memcpy(p_tk_value->tk, p_tk_value_data, AD_TYPE_TK_VALUE_DATA_SIZE); + + return NRF_SUCCESS; +} + +/**@brief Decodes and stores AD Data from Security Manager TK AD Structure. */ +static ret_code_t lesc_confirm_value_decode(uint8_t const * p_lesc_confirm_value_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + if (len != AD_TYPE_CONFIRM_VALUE_DATA_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + uint8_t * p_lesc_confirm_data = p_nfc_ble_pairing_data->p_lesc_confirm_value; + VERIFY_PARAM_NOT_NULL(p_lesc_confirm_data); + + memcpy(p_lesc_confirm_data, p_lesc_confirm_value_data, AD_TYPE_CONFIRM_VALUE_DATA_SIZE); + + return NRF_SUCCESS; +} + +/**@brief Decodes and stores AD Data from Security Manager TK AD Structure. */ +static ret_code_t lesc_random_value_decode(uint8_t const * p_lesc_random_value_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + if (len != AD_TYPE_RANDOM_VALUE_DATA_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + uint8_t * p_lesc_random_data = p_nfc_ble_pairing_data->p_lesc_random_value; + VERIFY_PARAM_NOT_NULL(p_lesc_random_data); + + memcpy(p_lesc_random_data, p_lesc_random_value_data, AD_TYPE_RANDOM_VALUE_DATA_SIZE); + + return NRF_SUCCESS; +} + +/**@brief Decodes and stores AD Data from Security Manager OOB Flags AD Structure. */ +static ret_code_t sec_mgr_oob_flags_decode(uint8_t const * p_sec_mgr_oob_flags_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + if (len != AD_TYPE_OOB_FLAGS_DATA_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + VERIFY_PARAM_NOT_NULL(p_nfc_ble_pairing_data->p_sec_mgr_oob_flags); + *(p_nfc_ble_pairing_data->p_sec_mgr_oob_flags) = *(p_sec_mgr_oob_flags_data); + + return NRF_SUCCESS; +} + +/**@brief Decodes and stores AD Data from Appearance AD Structure. */ +static ret_code_t appearance_decode(uint8_t const * p_appearance_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + if (len != AD_TYPE_APPEARANCE_DATA_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + p_nfc_ble_pairing_data->appearance = uint16_decode(p_appearance_data); + + return NRF_SUCCESS; +} + +/**@brief Decodes and stores AD Data from LE Bluetooth Device Address AD Structure. */ +static ret_code_t ble_device_addr_decode(uint8_t const * p_dev_addr_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + if (len != AD_TYPE_BLE_DEVICE_ADDR_DATA_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + ble_gap_addr_t * p_device_addr = p_nfc_ble_pairing_data->p_device_addr; + VERIFY_PARAM_NOT_NULL(p_device_addr); + + memcpy(p_device_addr->addr, p_dev_addr_data, BLE_GAP_ADDR_LEN); + p_device_addr->addr_type = *(p_dev_addr_data + BLE_GAP_ADDR_LEN); + + return NRF_SUCCESS; +} + +/**@brief Decodes and stores AD Data from LE Role AD Structure. */ +static ret_code_t le_role_decode(uint8_t const * p_le_role_data, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + if (len != AD_TYPE_LE_ROLE_DATA_SIZE) + { + return NRF_ERROR_INVALID_LENGTH; + } + + uint8_t le_role = *p_le_role_data; + switch (le_role) + { + case NFC_BLE_ADVDATA_ROLE_ENCODED_ONLY_PERIPH: + p_nfc_ble_pairing_data->le_role = BLE_ADVDATA_ROLE_ONLY_PERIPH; + break; + + case NFC_BLE_ADVDATA_ROLE_ENCODED_ONLY_CENTRAL: + p_nfc_ble_pairing_data->le_role = BLE_ADVDATA_ROLE_ONLY_CENTRAL; + break; + + case NFC_BLE_ADVDATA_ROLE_ENCODED_BOTH_PERIPH_PREFERRED: + p_nfc_ble_pairing_data->le_role = BLE_ADVDATA_ROLE_BOTH_PERIPH_PREFERRED; + break; + + case NFC_BLE_ADVDATA_ROLE_ENCODED_BOTH_CENTRAL_PREFERRED: + p_nfc_ble_pairing_data->le_role = BLE_ADVDATA_ROLE_BOTH_CENTRAL_PREFERRED; + break; + + default: + return NRF_ERROR_INVALID_PARAM; + } + + return NRF_SUCCESS; +} + +/**@brief Validates if Length field of AD structure is correct. */ +__STATIC_INLINE ret_code_t field_length_validate(uint8_t field_length, uint8_t index, uint8_t len) +{ + if ( (field_length == FIELD_LEN_INC_VAL) || (index + field_length >= len) ) + { + return NRF_ERROR_INVALID_LENGTH; + } + else + { + return NRF_SUCCESS; + } +} + +/**@brief Validates which AD types were not present in parsed data and checks if any + * AD Type occured more than once. + */ +__STATIC_INLINE ret_code_t field_type_validate(nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data, + ad_type_counter_t * ad_type_counter) +{ + /* Reset AD type fields which were not present in parsed buffer. */ + if (ad_type_counter->name_type == AD_TYPE_NOT_PRESENT) + { + p_nfc_ble_pairing_data->device_name.p_name = NULL; + p_nfc_ble_pairing_data->device_name.len = 0; + p_nfc_ble_pairing_data->device_name.name_type = BLE_ADVDATA_NO_NAME; + } + if ( (ad_type_counter->addr_type == AD_TYPE_NOT_PRESENT) && + (p_nfc_ble_pairing_data->p_device_addr != NULL) ) + { + p_nfc_ble_pairing_data->p_device_addr = NULL; + } + if ( (ad_type_counter->tk_type == AD_TYPE_NOT_PRESENT) && + (p_nfc_ble_pairing_data->p_tk_value != NULL) ) + { + p_nfc_ble_pairing_data->p_tk_value = NULL; + } + if ( (ad_type_counter->lesc_confirm_type == AD_TYPE_NOT_PRESENT) && + (p_nfc_ble_pairing_data->p_lesc_confirm_value != NULL) ) + { + p_nfc_ble_pairing_data->p_lesc_confirm_value = NULL; + } + if ( (ad_type_counter->lesc_random_type == AD_TYPE_NOT_PRESENT) && + (p_nfc_ble_pairing_data->p_lesc_random_value != NULL) ) + { + p_nfc_ble_pairing_data->p_lesc_random_value = NULL; + } + if ( (ad_type_counter->sec_mgr_oob_flags_type == AD_TYPE_NOT_PRESENT) && + (p_nfc_ble_pairing_data->p_sec_mgr_oob_flags != NULL) ) + { + p_nfc_ble_pairing_data->p_sec_mgr_oob_flags = NULL; + } + if (ad_type_counter->appear_type == AD_TYPE_NOT_PRESENT) + { + p_nfc_ble_pairing_data->appearance = BLE_ADVDATA_APPEARANCE_NOT_PRESENT; + } + if (ad_type_counter->flags_type == AD_TYPE_NOT_PRESENT) + { + p_nfc_ble_pairing_data->flags = 0; + } + if (ad_type_counter->le_role_type == AD_TYPE_NOT_PRESENT) + { + p_nfc_ble_pairing_data->le_role = BLE_ADVDATA_ROLE_NOT_PRESENT; + } + + /* Check if any AD Type was doubled. */ + if ( (ad_type_counter->name_type > AD_TYPE_OCCUR_THRES) || + (ad_type_counter->addr_type > AD_TYPE_OCCUR_THRES) || + (ad_type_counter->tk_type > AD_TYPE_OCCUR_THRES) || + (ad_type_counter->sec_mgr_oob_flags_type > AD_TYPE_OCCUR_THRES) || + (ad_type_counter->appear_type > AD_TYPE_OCCUR_THRES) || + (ad_type_counter->flags_type > AD_TYPE_OCCUR_THRES) || + (ad_type_counter->le_role_type > AD_TYPE_OCCUR_THRES) || + (ad_type_counter->lesc_confirm_type > AD_TYPE_OCCUR_THRES) || + (ad_type_counter->lesc_random_type > AD_TYPE_OCCUR_THRES) ) + { + return NRF_ERROR_NOT_SUPPORTED; + } + + return NRF_SUCCESS; +} + +ret_code_t nfc_ble_oob_advdata_parse(uint8_t const * p_advdata, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data) +{ + ret_code_t err_code = NRF_SUCCESS; + uint8_t index = 0; + + ad_type_counter_t ad_type_counter; + memset(&ad_type_counter, AD_TYPE_NOT_PRESENT, sizeof(ad_type_counter_t)); + + if ( (p_nfc_ble_pairing_data == NULL) || (p_advdata == NULL) ) + { + return NRF_ERROR_NULL; + } + + while (index < len) + { + uint8_t field_length = p_advdata[index]; + if (field_length == EARLY_TERMINATOR) + { + return NRF_SUCCESS; + } + err_code = field_length_validate(field_length, index, len); + VERIFY_SUCCESS(err_code); + + uint8_t field_type = p_advdata[index + AD_LENGTH_FIELD_SIZE]; + uint8_t const * p_field_data = &p_advdata[index + AD_DATA_OFFSET]; + uint8_t field_data_len = field_length - AD_TYPE_FIELD_SIZE; + + switch (field_type) + { + case BLE_GAP_AD_TYPE_FLAGS: + ++ad_type_counter.flags_type; + err_code = flags_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME: + ++ad_type_counter.name_type; + err_code = short_name_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME: + ++ad_type_counter.name_type; + err_code = full_name_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE: + ++ad_type_counter.tk_type; + err_code = tk_value_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_LESC_CONFIRMATION_VALUE: + ++ad_type_counter.lesc_confirm_type; + err_code = lesc_confirm_value_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_LESC_RANDOM_VALUE: + ++ad_type_counter.lesc_random_type; + err_code = lesc_random_value_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS: + ++ad_type_counter.sec_mgr_oob_flags_type; + err_code = sec_mgr_oob_flags_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_APPEARANCE: + ++ad_type_counter.appear_type; + err_code = appearance_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS: + ++ad_type_counter.addr_type; + err_code = ble_device_addr_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + case BLE_GAP_AD_TYPE_LE_ROLE: + ++ad_type_counter.le_role_type; + err_code = le_role_decode(p_field_data, + field_data_len, + p_nfc_ble_pairing_data); + break; + + default: + /* AD Structure Type field unknown for parser. */ + return NRF_ERROR_NOT_SUPPORTED; + } + + VERIFY_SUCCESS(err_code); + + index += field_length + AD_LENGTH_FIELD_SIZE; + } + + err_code = field_type_validate(p_nfc_ble_pairing_data, &ad_type_counter); + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_BLE_OOB_ADVDATA_PARSER) diff --git a/libraries/nfc/src/ndef/nfc_ble_oob_advdata_parser.h b/libraries/nfc/src/ndef/nfc_ble_oob_advdata_parser.h new file mode 100644 index 000000000..a0872e20f --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_oob_advdata_parser.h @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup nfc_ble_oob_advdata_parser Advertising and Scan Response Data Parser for NFC OOB pairing + * @{ + * @ingroup nfc_ble_pair_msg + * @brief Functions for parsing and decoding data in the Advertising and Scan Response + * Data format for NFC OOB pairing. + */ + +#ifndef NFC_BLE_OOB_ADVDATA_PARSER_H_ +#define NFC_BLE_OOB_ADVDATA_PARSER_H_ + +#include "util/sdk_errors.h" +#include "util/ble_advdata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_ADVDATA_APPEARANCE_NOT_PRESENT 0 /**< Appearance AD structure not present. */ + +/**@brief Bluetooth Low Energy GAP device name. */ +typedef struct +{ + ble_advdata_name_type_t name_type; /**< See @ref ble_advdata_name_type_t. */ + uint8_t len; /**< Length of device name. */ + uint8_t * p_name; /**< Pointer to the buffer with device name. */ +} ble_gap_dev_name_t; + +/**@brief BLE Advertising data that is relevant for OOB pairing. */ +typedef struct +{ + ble_gap_dev_name_t device_name; /**< See @ref ble_gap_dev_name_t. */ + ble_gap_addr_t * p_device_addr; /**< See @ref ble_gap_addr_t. */ + ble_advdata_tk_value_t * p_tk_value; /**< See @ref ble_advdata_tk_value_t. */ + uint8_t * p_lesc_confirm_value; /**< LESC OOB confirmation data. */ + uint8_t * p_lesc_random_value; /**< LESC OOB random data. */ + ble_advdata_le_role_t le_role; /**< See @ref ble_advdata_le_role_t. */ + uint16_t appearance; /**< Advertising data Appearance field. */ + uint8_t flags; /**< Advertising data Flags field. */ + uint8_t * p_sec_mgr_oob_flags; /**< Security Manager Out Of Band Flags data field. */ +} nfc_ble_oob_pairing_data_t; + +/**@brief Function for parsing BLE data encoded in AD Type format. + * + * @details This function parses BLE data encoded in Advertising Data Type format which + * can be generated with @ref ble_advdata_encode function. The result of the parsing is + * stored within @ref nfc_ble_oob_pairing_data_t structure. + * + * @note Currently, module can be used to parse BLE AD Type data, which contains + * AD Structures with following GAP AD Types: Flags, Shortened and Complete Device + * Name, Security Manager TK Value and OOB Flags, Appearance, LE Bluetooth Device + * Address and LE Role. + * + * @warning Before passing \p p_nfc_ble_pairing_data structure to this function, + * it is necessary to provide buffers for AD Structures Data, which are expected to be + * found within parsed buffer. This applies to following GAP AD Types with corresponding + * structures: Shortened and Complete Device Name - @ref ble_gap_dev_name_t, + * LE Bluetooth Device Address - @ref ble_gap_addr_t, Security Manager TK Value - + * @ref ble_advdata_tk_value_t and Security Manager OOB Flags - uint8_t. + * + * @param[in] p_advdata Pointer to the data to be parsed. + * @param[in] len Size of the data to be parsed. + * @param[out] p_nfc_ble_pairing_data Pointer to the structure that will be used + * to hold parsed data. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_NO_MEM If the provided buffer for device name is + * too small to hold parsed data. + * @retval NRF_ERROR_INVALID_LENGTH If any AD Structure Length field contains + * different value than expected. + * @retval NRF_ERROR_INVALID_PARAM If any AD Structure Data field contains + * invalid parameters. + * @retval NRF_ERROR_NULL If any function pointer parameter is NULL or + * any expected buffer in \p p_nfc_ble_pairing_data + * was not provided. + * @retval NRF_ERROR_NOT_SUPPORTED If any AD Structure Type field contains + * type which is not supported or any AD + * Structure Type occurs more than once. + */ +ret_code_t nfc_ble_oob_advdata_parse(uint8_t const * p_advdata, + uint8_t len, + nfc_ble_oob_pairing_data_t * p_nfc_ble_pairing_data); + + +/**@brief Function for displaying values of basic BLE OOB Advertising data types. + * + * @param[in] p_pairing_data Structure containing parsed data. + */ +void nfc_oob_data_printout(nfc_ble_oob_pairing_data_t const * const p_pairing_data); + +#ifdef __cplusplus +} +#endif + +#endif //NFC_BLE_OOB_ADVDATA_PARSER_H__ + +/** @} */ diff --git a/libraries/nfc/src/ndef/nfc_ble_pair_common.c b/libraries/nfc/src/ndef/nfc_ble_pair_common.c new file mode 100644 index 000000000..3d995c795 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_pair_common.c @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_CH_COMMON) +#include "nfc_ble_pair_common.h" + +#include + +/* Record Payload Type for Bluetooth Carrier Configuration LE record */ +const uint8_t le_oob_rec_type_field[] = +{ + 'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', '/', 'v', 'n', 'd', '.', + 'b', 'l', 'u', 'e', 't', 'o', 'o', 't', 'h', '.', 'l', 'e', '.', 'o', 'o', 'b' +}; + +#endif // NRF_MODULE_ENABLED(NFC_CH_COMMON) diff --git a/libraries/nfc/src/ndef/nfc_ble_pair_common.h b/libraries/nfc/src/ndef/nfc_ble_pair_common.h new file mode 100644 index 000000000..ff9c264a5 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_pair_common.h @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_BLE_PAIR_COMMON_H__ +#define NFC_BLE_PAIR_COMMON_H__ + +#include +#include "util/ble_advdata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @defgroup nfc_ble_pair_common Common data for Connection Handover and Connection Handover Parser modules + * @{ + * @ingroup nfc_ble_pair_msg + */ + +#define AD_TYPE_LE_ROLE_DATA_SIZE 1UL /**< Data size (in octets) of the LE Role AD type. */ +#define AD_TYPE_LE_ROLE_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_LE_ROLE_DATA_SIZE) /**< Size (in octets) of the LE Role AD type. */ +#define AD_TYPE_TK_VALUE_DATA_SIZE (sizeof(ble_advdata_tk_value_t)) /**< Data size (in octets) of the Security Manager TK value AD type. */ +#define AD_TYPE_TK_VALUE_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_TK_VALUE_DATA_SIZE) /**< Size (in octets) of the Security Manager TK value AD type. */ +#define AD_TYPE_OOB_FLAGS_DATA_SIZE 1UL /**< Data size (in octets) of the Security Manager OOB Flags AD type. */ +#define AD_TYPE_OOB_FLAGS_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_OOB_FLAGS_DATA_SIZE) /**< Size (in octets) of the Security Manager OOB Flags AD type. */ + +#define AD_TYPE_CONFIRM_VALUE_DATA_SIZE 16UL /**< Data size (in octets) of the LESC Confirmation value. */ +#define AD_TYPE_CONFIRM_VALUE_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_CONFIRM_VALUE_DATA_SIZE) /**< Size (in octets) of the LESC Confirmation value AD type. */ +#define AD_TYPE_RANDOM_VALUE_DATA_SIZE 16UL /**< Data size (in octets) of the LESC Random value. */ +#define AD_TYPE_RANDOM_VALUE_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_RANDOM_VALUE_DATA_SIZE) /**< Size (in octets) of the LESC Random value AD type. */ +#define AD_TYPE_LESC_SIZE (AD_TYPE_RANDOM_VALUE_SIZE + \ + AD_TYPE_CONFIRM_VALUE_SIZE) /**< Total size (in octets) of the LESC OOB AD data fields in NDEF message. */ + +#define AD_TYPE_SEC_MGR_OOB_FLAG_SET 1U /**< Security Manager OOB Flag set. Flag selection is done using _POS defines */ +#define AD_TYPE_SEC_MGR_OOB_FLAG_CLEAR 0U /**< Security Manager OOB Flag clear. Flag selection is done using _POS defines */ +#define AD_TYPE_SEC_MGR_OOB_FLAG_OOB_DATA_PRESENT_POS 0UL /**< Security Manager OOB Data Present Flag position. */ +#define AD_TYPE_SEC_MGR_OOB_FLAG_OOB_LE_SUPPORTED_POS 1UL /**< Security Manager OOB Low Energy Supported Flag position. */ +#define AD_TYPE_SEC_MGR_OOB_FLAG_SIM_LE_AND_EP_POS 2UL /**< Security Manager OOB Simultaneous LE and BR/EDR to Same Device Capable Flag position. */ +#define AD_TYPE_SEC_MGR_OOB_ADDRESS_TYPE_PUBLIC 0UL /**< Security Manager OOB Public Address type. */ +#define AD_TYPE_SEC_MGR_OOB_ADDRESS_TYPE_RANDOM 1UL /**< Security Manager OOB Random Address type. */ +#define AD_TYPE_SEC_MGR_OOB_FLAG_ADDRESS_TYPE_POS 3UL /**< Security Manager OOB Address type Flag (0 = Public Address, 1 = Random Address) position. */ + +/**@brief Payload field values of LE Role BLE GAP AD Type. Corresponds with @ref ble_advdata_le_role_t enum. */ +typedef enum +{ + NFC_BLE_ADVDATA_ROLE_ENCODED_ONLY_PERIPH = 0, /**< Only Peripheral Role supported. */ + NFC_BLE_ADVDATA_ROLE_ENCODED_ONLY_CENTRAL, /**< Only Central Role supported. */ + NFC_BLE_ADVDATA_ROLE_ENCODED_BOTH_PERIPH_PREFERRED, /**< Peripheral and Central Role supported. Peripheral Role preferred for connection establishment. */ + NFC_BLE_ADVDATA_ROLE_ENCODED_BOTH_CENTRAL_PREFERRED /**< Peripheral and Central Role supported. Central Role preferred for connection establishment */ +} nfc_ble_advdata_le_role_encoded_t; + +/** + * @brief External reference to the type field of the Bluetooth LE Carrier Configuration NDEF record, defined + * in the file @c nfc_ble_pair_common.c + */ +extern const uint8_t le_oob_rec_type_field[32]; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_BLE_PAIR_COMMON_H__ diff --git a/libraries/nfc/src/ndef/nfc_ble_pair_lib.c b/libraries/nfc/src/ndef/nfc_ble_pair_lib.c new file mode 100644 index 000000000..98cb49eb9 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_pair_lib.c @@ -0,0 +1,576 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_BLE_PAIR_LIB) + +#include "nfc_ble_pair_lib.h" +#include "util/sdk_macros.h" +#include "util/app_error.h" +#include "nrf_drv_rng.h" +#include "nfc_t2t_lib.h" +#include "nfc_ble_pair_msg.h" +#include "nrf_sdh_ble.h" +#include "nrf_ble_lesc.h" + +#define NRF_LOG_MODULE_NAME nfc_ble_pair +#if NFC_BLE_PAIR_LIB_LOG_ENABLED +#define NRF_LOG_LEVEL NFC_BLE_PAIR_LIB_LOG_LEVEL +#define NRF_LOG_INFO_COLOR NFC_BLE_PAIR_LIB_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR NFC_BLE_PAIR_LIB_DEBUG_COLOR +#else // NFC_BLE_PAIR_LIB_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#endif // NFC_BLE_PAIR_LIB_LOG_ENABLED +#include "util/nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +// Verify bonding and keys distribution settings. +#if ((BLE_NFC_SEC_PARAM_BOND) && \ + !(BLE_NFC_SEC_PARAM_KDIST_OWN_ENC) && \ + !(BLE_NFC_SEC_PARAM_KDIST_OWN_ID) && \ + !(BLE_NFC_SEC_PARAM_KDIST_PEER_ENC) && \ + !(BLE_NFC_SEC_PARAM_KDIST_PEER_ID)) + #error "At least one of the BLE_NFC_SEC_PARAM_KDIST flags must be set to 1 when bonding is enabled." +#endif + +// Macro for verifying if the pairing mode argument is valid +#define VERIFY_PAIRING_MODE(arg) \ + if ((arg) >= NFC_PAIRING_MODE_CNT) \ + { \ + return NRF_ERROR_INVALID_PARAM; \ + } + +#define TK_MAX_NUM 1 /**< Maximal number of TK locations in NDEF message buffer. */ +#define NDEF_MSG_BUFF_SIZE 256 /**< Size of buffer for the NDEF pairing message. */ + +#define BLE_NFC_SEC_PARAM_KEYPRESS 0 /**< Keypress notifications not enabled. */ +#define BLE_NFC_SEC_PARAM_IO_CAPS BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */ + +static ble_advertising_t * m_p_advertising = NULL; /**< Pointer to the advertising module instance. */ + +static uint8_t m_ndef_msg_buf[NDEF_MSG_BUFF_SIZE]; /**< NFC tag NDEF message buffer. */ +static ble_advdata_tk_value_t m_oob_auth_key; /**< Temporary Key buffer used in OOB legacy pairing mode. */ +static uint8_t * m_tk_group[TK_MAX_NUM]; /**< Locations of TK in NDEF message. */ +static nfc_pairing_mode_t m_pairing_mode; /**< Current pairing mode. */ +static ble_gap_sec_params_t m_sec_param; /**< Current Peer Manager secure parameters configuration. */ + +static uint8_t m_connections = 0; /**< Number of active connections. */ + +static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context); + +NRF_SDH_BLE_OBSERVER(m_ble_evt_observer, NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); + +/** + * @brief Generates random values to a given buffer + * + * @param[out] p_buff Buffer for random values + * @param[in] size Number of bytes to generate + * + * @returns Number of generated bytes + */ +static uint8_t random_vector_generate(uint8_t * p_buff, uint8_t size) +{ + uint8_t available; + ret_code_t err_code = NRF_SUCCESS; + + nrf_drv_rng_bytes_available(&available); + + uint8_t length = (size < available) ? size : available; + err_code = nrf_drv_rng_rand(p_buff, length); + APP_ERROR_CHECK(err_code); + return length; +} + +/** + * @brief Prints generated key to the log console + * + * @param[in] lenght TK value length + */ +static void random_vector_log(uint8_t length) +{ + NRF_LOG_INFO("TK Random Value:"); + for (uint32_t i = 0; i < length; i++) + { + NRF_LOG_RAW_INFO(" %02X",(int)m_oob_auth_key.tk[i]); + } + NRF_LOG_RAW_INFO("\r\n"); +} + +/** + * @brief Function for handling NFC events. + * + * @details Starts advertising and generates new OOB keys on the NFC_T2T_EVENT_FIELD_ON event. + * + * @param[in] p_context Context for callback execution, not used in this callback implementation. + * @param[in] event Event generated by hal NFC lib. + * @param[in] p_data Received/transmitted data or NULL, not used in this callback implementation. + * @param[in] data_length Size of the received/transmitted packet, not used in this callback implementation. + */ +static void nfc_callback(void * p_context, + nfc_t2t_event_t event, + uint8_t const * p_data, + size_t data_length) +{ + UNUSED_PARAMETER(p_context); + UNUSED_PARAMETER(p_data); + UNUSED_PARAMETER(data_length); + + ret_code_t err_code = NRF_SUCCESS; + nfc_pairing_mode_t pairing_mode; + + switch (event) + { + case NFC_T2T_EVENT_FIELD_ON: + NRF_LOG_DEBUG("NFC_EVENT_FIELD_ON"); + + pairing_mode = nfc_ble_pair_mode_get(); + + if ((pairing_mode == NFC_PAIRING_MODE_OOB) || + (pairing_mode == NFC_PAIRING_MODE_GENERIC_OOB)) + { + // Generate Authentication OOB Key and update NDEF message content. + uint8_t length = random_vector_generate(m_oob_auth_key.tk, BLE_GAP_SEC_KEY_LEN); + random_vector_log(length); + err_code = nfc_tk_group_modifier_update(&m_oob_auth_key); + APP_ERROR_CHECK(err_code); + } + + // Start advertising when NFC field is sensed and there is a place for another connection. + if (m_connections < NRF_SDH_BLE_PERIPHERAL_LINK_COUNT) + { + err_code = ble_advertising_start(m_p_advertising, BLE_ADV_MODE_FAST); + if (err_code != NRF_ERROR_INVALID_STATE) + { + APP_ERROR_CHECK(err_code); + } + } + + break; + + case NFC_T2T_EVENT_FIELD_OFF: + NRF_LOG_DEBUG("NFC_EVENT_FIELD_OFF"); + break; + + default: + break; + } +} + +/** + * @brief Function for setting the Peer Manager secure mode used in device pairing. + * + * @param[in] mode NFC pairing mode, this is the value of @ref nfc_pairing_mode_t enum + * + * @retval NRF_SUCCESS If new secure mode has been set correctly. + * @retval NRF_ERROR_INVALID_PARAM If pairing mode is invalid. + * @retval Other Other error codes might be returned depending on used modules. + */ +static ret_code_t pm_secure_mode_set(nfc_pairing_mode_t mode) +{ + ret_code_t err_code = NRF_SUCCESS; + + // Check if pairing mode is valid. + VERIFY_PAIRING_MODE(mode); + + memset(&m_sec_param, 0x00, sizeof(m_sec_param)); + + // Pairing mode specific security parameters. + switch (mode) + { + case NFC_PAIRING_MODE_JUST_WORKS: + // Disable pairing with OOB data. + m_sec_param.mitm = 0; + m_sec_param.oob = 0; + m_sec_param.lesc = 0; + break; + + case NFC_PAIRING_MODE_OOB: + // Enable legacy pairing with OOB data - TK value. + m_sec_param.mitm = 1; + m_sec_param.oob = 1; + m_sec_param.lesc = 0; + break; + + case NFC_PAIRING_MODE_LESC_OOB: + case NFC_PAIRING_MODE_LESC_JUST_WORKS: + // Enable LESC pairing - OOB and MITM flags are cleared because it is the central device + // who decides if the connection will be authorized with LESC OOB data. + m_sec_param.mitm = 0; + m_sec_param.oob = 0; + m_sec_param.lesc = 1; + break; + + case NFC_PAIRING_MODE_GENERIC_OOB: + // MITM, OOB and LESC flags are changing dynamically depending on central device pairing flags. + break; + + default: + return NRF_ERROR_INVALID_PARAM; + } + + // Common security parameters to be used for all security procedures. + m_sec_param.min_key_size = BLE_NFC_SEC_PARAM_MIN_KEY_SIZE; + m_sec_param.max_key_size = BLE_NFC_SEC_PARAM_MAX_KEY_SIZE; + m_sec_param.keypress = BLE_NFC_SEC_PARAM_KEYPRESS; + m_sec_param.io_caps = BLE_NFC_SEC_PARAM_IO_CAPS; + m_sec_param.bond = BLE_NFC_SEC_PARAM_BOND; + +#if (BLE_NFC_SEC_PARAM_BOND) + // If bonding is enabled, set key distribution flags. + m_sec_param.kdist_own.enc = BLE_NFC_SEC_PARAM_KDIST_OWN_ENC; + m_sec_param.kdist_own.id = BLE_NFC_SEC_PARAM_KDIST_OWN_ID; + m_sec_param.kdist_peer.enc = BLE_NFC_SEC_PARAM_KDIST_PEER_ENC; + m_sec_param.kdist_peer.id = BLE_NFC_SEC_PARAM_KDIST_PEER_ID; +#else + // If bonding is not enabled, no keys can be distributed. + m_sec_param.kdist_own.enc = 0; + m_sec_param.kdist_own.id = 0; + m_sec_param.kdist_peer.enc = 0; + m_sec_param.kdist_peer.id = 0; +#endif + + // Update Peer Manager security parameter settings. + err_code = pm_sec_params_set(&m_sec_param); + + return err_code; +} + + + /**@brief Function for preparing the BLE pairing data for the NFC tag. + * + * @details This function does not stop and start the NFC tag data emulation. + * + * @param[in] mode Pairing mode for which the tag data will be prepared. + * + * @retval NRF_SUCCESS If new tag pairing data has been set correctly. + * @retval NRF_ERROR_INVALID_PARAM If pairing mode is invalid. + * @retval NRF_ERROR_NULL If LESC oob data is missing. + * @retval Other Other error codes might be returned depending on used modules. + */ +ret_code_t nfc_ble_pair_data_set(nfc_pairing_mode_t mode) +{ + ret_code_t err_code = NRF_SUCCESS; + + // Check if pairing mode is valid + VERIFY_PAIRING_MODE(mode); + + // Provide information about available buffer size to encoding function. + uint32_t ndef_msg_len = sizeof(m_ndef_msg_buf); + + switch (mode) + { + case NFC_PAIRING_MODE_OOB: + // Encode NDEF message with Secure Simple Pairing OOB optional data - TK value. + err_code = nfc_ble_pair_msg_updatable_tk_encode(NFC_BLE_PAIR_MSG_BLUETOOTH_LE_SHORT, + &m_oob_auth_key, + NULL, + m_ndef_msg_buf, + &ndef_msg_len, + m_tk_group, + TK_MAX_NUM); + break; + + case NFC_PAIRING_MODE_JUST_WORKS: + // Encode NDEF message with Secure Simple Pairing OOB data. + err_code = nfc_ble_pair_default_msg_encode(NFC_BLE_PAIR_MSG_BLUETOOTH_LE_SHORT, + NULL, + NULL, + m_ndef_msg_buf, + &ndef_msg_len); + break; + + case NFC_PAIRING_MODE_LESC_OOB: + { + // Generate LESC OOB data + err_code = nrf_ble_lesc_own_oob_data_generate(); + VERIFY_SUCCESS(err_code); + + ble_gap_lesc_oob_data_t * p_lesc_oob_data = nrf_ble_lesc_own_oob_data_get(); + VERIFY_PARAM_NOT_NULL(p_lesc_oob_data); + + // Encode NDEF message with BLE LESC OOB pairing data - LESC random and confirmation values. + err_code = nfc_ble_pair_default_msg_encode(NFC_BLE_PAIR_MSG_BLUETOOTH_LE_SHORT, + NULL, + p_lesc_oob_data, + m_ndef_msg_buf, + &ndef_msg_len); + } break; + + case NFC_PAIRING_MODE_LESC_JUST_WORKS: + err_code = nfc_ble_pair_default_msg_encode(NFC_BLE_PAIR_MSG_BLUETOOTH_LE_SHORT, + NULL, + NULL, + m_ndef_msg_buf, + &ndef_msg_len); + break; + + case NFC_PAIRING_MODE_GENERIC_OOB: + { + // Generate LESC OOB data + err_code = nrf_ble_lesc_own_oob_data_generate(); + VERIFY_SUCCESS(err_code); + + ble_gap_lesc_oob_data_t * p_lesc_oob_data = nrf_ble_lesc_own_oob_data_get(); + VERIFY_PARAM_NOT_NULL(p_lesc_oob_data); + + // Encode NDEF message with Secure Simple Pairing OOB data - TK value and LESC Random and Confirmation Keys. + err_code = nfc_ble_pair_msg_updatable_tk_encode(NFC_BLE_PAIR_MSG_BLUETOOTH_LE_SHORT, + &m_oob_auth_key, + p_lesc_oob_data, + m_ndef_msg_buf, + &ndef_msg_len, + m_tk_group, + TK_MAX_NUM); + } break; + + default: + return NRF_ERROR_INVALID_PARAM; + } + + VERIFY_SUCCESS(err_code); + + // Update NFC tag data + err_code = nfc_t2t_payload_set(m_ndef_msg_buf, ndef_msg_len); + + return err_code; +} + +ret_code_t nfc_ble_pair_init(ble_advertising_t * const p_advertising, nfc_pairing_mode_t mode) +{ + ret_code_t err_code = NRF_SUCCESS; + + // Check if pairing mode is valid + VERIFY_PAIRING_MODE(mode); + + // Check if pointer to the advertising module instance is not NULL + VERIFY_PARAM_NOT_NULL(p_advertising); + + m_p_advertising = p_advertising; + m_pairing_mode = mode; + + // Initialize RNG peripheral for authentication OOB data generation + err_code = nrf_drv_rng_init(NULL); + if (err_code != NRF_ERROR_INVALID_STATE && + err_code != NRF_ERROR_MODULE_ALREADY_INITIALIZED) + { + VERIFY_SUCCESS(err_code); + } + + // Start NFC + err_code = nfc_t2t_setup(nfc_callback, NULL); + VERIFY_SUCCESS(err_code); + + // Set Peer Manager pairing mode + err_code = pm_secure_mode_set(mode); + VERIFY_SUCCESS(err_code); + + // Generate LESC OOB data + err_code = nrf_ble_lesc_own_oob_data_generate(); + APP_ERROR_CHECK(err_code); + + // Set proper NFC data according to the pairing mode + err_code = nfc_ble_pair_data_set(mode); + VERIFY_SUCCESS(err_code); + + // Turn on tag emulation + err_code = nfc_t2t_emulation_start(); + + return err_code; +} + +ret_code_t nfc_ble_pair_mode_set(nfc_pairing_mode_t mode) +{ + ret_code_t err_code = NRF_SUCCESS; + + // Check if pairing mode is valid + VERIFY_PAIRING_MODE(mode); + + if (mode != m_pairing_mode) + { + m_pairing_mode = mode; + + // Update Peer Manager settings according to the new pairing mode + err_code = pm_secure_mode_set(mode); + VERIFY_SUCCESS(err_code); + + // NFC tag emulation must be turned off during changes in payload + err_code = nfc_t2t_emulation_stop(); + VERIFY_SUCCESS(err_code); + + // Update NFC tag data + err_code = nfc_ble_pair_data_set(mode); + VERIFY_SUCCESS(err_code); + + // Turn on tag emulation after changes + err_code = nfc_t2t_emulation_start(); + VERIFY_SUCCESS(err_code); + } + + return NRF_SUCCESS; +} + +nfc_pairing_mode_t nfc_ble_pair_mode_get(void) +{ + return m_pairing_mode; +} + +/** + * @brief Generates new key pair for LESC pairing. + * + * @details If device is in the @ref NFC_PAIRING_MODE_LESC_OOB mode or in + * the @ref NFC_PAIRING_MODE_GENERIC_OOB mode, NFC Connection Handover + * message is also updated with newly generated LESC OOB data. + * + * @retval NRF_SUCCESS If new tag pairing data has been set correctly. + * @retval Other Other error codes might be returned depending on used modules. + */ +static ret_code_t generate_lesc_keys(void) +{ + ret_code_t err_code = NRF_SUCCESS; + + // Generate new LESC keys + err_code = nrf_ble_lesc_keypair_generate(); + VERIFY_SUCCESS(err_code); + + if ((m_pairing_mode == NFC_PAIRING_MODE_LESC_OOB) || + (m_pairing_mode == NFC_PAIRING_MODE_GENERIC_OOB)) + { + // Generate LESC OOB data. + err_code = nrf_ble_lesc_own_oob_data_generate(); + VERIFY_SUCCESS(err_code); + + // Update NDEF message with new LESC OOB data. + err_code = nfc_lesc_data_update(nrf_ble_lesc_own_oob_data_get()); + VERIFY_SUCCESS(err_code); + } + + return NRF_SUCCESS; +} + +/** + * @brief Function for handling BLE events. + * + * @param[in] p_ble_evt Event received from the BLE stack. + * @param[in] p_context Context. + */ +static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) +{ + ret_code_t err_code = NRF_SUCCESS; + + switch (p_ble_evt->header.evt_id) + { + // Upon authorization key request, reply with Temporary Key that was read from the NFC tag + case BLE_GAP_EVT_AUTH_KEY_REQUEST: + NRF_LOG_DEBUG("BLE_GAP_EVT_AUTH_KEY_REQUEST"); + + err_code = sd_ble_gap_auth_key_reply(p_ble_evt->evt.gap_evt.conn_handle, + BLE_GAP_AUTH_KEY_TYPE_OOB, + m_oob_auth_key.tk); + APP_ERROR_CHECK(err_code); + break; + + case BLE_GAP_EVT_CONNECTED: + m_connections++; + break; + + case BLE_GAP_EVT_DISCONNECTED: + m_connections--; + // Intentional fallthrough. + + case BLE_GAP_EVT_AUTH_STATUS: + // Generate new LESC key pair and OOB data + if ((m_pairing_mode == NFC_PAIRING_MODE_LESC_OOB) || + (m_pairing_mode == NFC_PAIRING_MODE_LESC_JUST_WORKS) || + (m_pairing_mode == NFC_PAIRING_MODE_GENERIC_OOB)) + { + err_code = generate_lesc_keys(); + if (err_code != NRF_ERROR_BUSY) + { + APP_ERROR_CHECK(err_code); + } + } + break; + + default: + break; + } +} + +ret_code_t nfc_ble_pair_on_pm_params_req(pm_evt_t const * p_evt) +{ + ret_code_t err_code = NRF_SUCCESS; + + NRF_LOG_DEBUG("PM_EVT_CONN_SEC_PARAMS_REQ"); + + // Dynamic security parameters changes are needed only + // by NFC_PAIRING_MODE_GENERIC_OOB pairing mode. + if (m_pairing_mode == NFC_PAIRING_MODE_GENERIC_OOB) + { + // Check if pointer to the Peer Manager event is not NULL. + VERIFY_PARAM_NOT_NULL(p_evt); + + // Set up proper MITM, OOB and LESC flags depending on peer LESC flag + // to support either Legacy OOB or LESC OOB pairing mode. + if (p_evt->params.conn_sec_params_req.p_peer_params->lesc) + { + NRF_LOG_DEBUG("LESC OOB mode flags set."); + + m_sec_param.mitm = 0; + m_sec_param.oob = 0; + m_sec_param.lesc = 1; + } + else + { + NRF_LOG_DEBUG("Legacy OOB mode flags set."); + + m_sec_param.mitm = 1; + m_sec_param.oob = 1; + m_sec_param.lesc = 0; + } + + // Reply with new security parameters to the Peer Manager. + err_code = pm_conn_sec_params_reply(p_evt->conn_handle, + &m_sec_param, + p_evt->params.conn_sec_params_req.p_context); + } + + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_BLE_PAIR_LIB) diff --git a/libraries/nfc/src/ndef/nfc_ble_pair_lib.h b/libraries/nfc/src/ndef/nfc_ble_pair_lib.h new file mode 100644 index 000000000..9452fe3de --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_pair_lib.h @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_BLE_PAIR_LIB_H__ +#define NFC_BLE_PAIR_LIB_H__ + +#include +#include "util/sdk_errors.h" +#include "ble.h" +#include "ble_advertising.h" +#include "peer_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @addtogroup nfc_api + * + * @defgroup nfc_ble_pair_lib NFC BLE Pairing Library + * @ingroup nfc_api + * @brief @tagAPI52 High-level library for BLE Connection Handover pairing using NFC. + * @{ + */ + +/** + * @brief NFC pairing types. + */ +typedef enum +{ + NFC_PAIRING_MODE_JUST_WORKS, /**< Legacy Just Works pairing without a security key. */ + NFC_PAIRING_MODE_OOB, /**< Legacy OOB pairing with a Temporary Key shared through NFC tag data. */ + NFC_PAIRING_MODE_LESC_JUST_WORKS, /**< LESC pairing without authentication data. */ + NFC_PAIRING_MODE_LESC_OOB, /**< LESC pairing with OOB authentication data. */ + NFC_PAIRING_MODE_GENERIC_OOB, /**< OOB pairing with fallback from LESC to Legacy mode. */ + NFC_PAIRING_MODE_CNT /**< Number of available pairing modes. */ +} nfc_pairing_mode_t; + +/** + * @brief Funtion for initializing NFC tag data and turning on tag emulation. + * + * @warning It is assumed that Peer Manager has already been initialized before calling this function. + * It is also assumed that BLE advertising has already been initialized and it is configured + * to run in the BLE_ADV_MODE_FAST mode. + * + * @param[in] mode Pairing mode, this is the value of the @ref nfc_pairing_mode_t enum. + * @param[in] p_advertising Pointer to the advertising module instance. + * + * @retval NRF_SUCCESS If NFC has been initialized properly. + * @retval NRF_ERROR_INVALID_PARAM If pairing mode is invalid. + * @retval NRF_ERROR_NULL If pointer to the advertising module instance is NULL. + * @retval Other Other error codes might be returned depending on used modules. + */ +ret_code_t nfc_ble_pair_init(ble_advertising_t * const p_advertising, nfc_pairing_mode_t mode); + +/** + * @brief Function for setting pairing data and BLE security mode. + * + * @param[in] mode New pairing mode, this is the value of the @ref nfc_pairing_mode_t enum. + * + * @retval NRF_SUCCESS If new pairing mode has been set correctly. + * @retval NRF_ERROR_INVALID_PARAM If pairing mode is invalid. + * @retval Other Other error codes might be returned depending on used modules. + */ +ret_code_t nfc_ble_pair_mode_set(nfc_pairing_mode_t mode); + +/** + * @brief Function for obtaining the current pairing mode. + * + * @return Current pairing mode. + */ +nfc_pairing_mode_t nfc_ble_pair_mode_get(void); + +/** + * @brief Function for replying to @ref PM_EVT_CONN_SEC_PARAMS_REQ. + * + * @details This function is used to allow dynamic changes in the Peer Manager + * security parameters depending on security parameters + * obtained from the peer. This is essential for dynamic switching + * between Legacy OOB and LESC OOB pairing modes when pairing + * library works in @ref NFC_PAIRING_MODE_GENERIC_OOB mode. + * + * @note This function invokes the @ref pm_conn_sec_params_reply function. + * + * @param[in] p_evt Pointer to the Peer Manager event struct with + * information about peer security parameters. + * + * @retval NRF_SUCCESS If proper reply has been sent or library does not need to reply. + * @retval NRF_ERROR_NULL If pointer to the Peer Manager event is NULL. + * @retval Other Other error codes might be returned by the @ref pm_conn_sec_params_reply function. + */ +ret_code_t nfc_ble_pair_on_pm_params_req(pm_evt_t const * p_evt); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_BLE_PAIR_LIB_H__ diff --git a/libraries/nfc/src/ndef/nfc_ble_pair_msg.c b/libraries/nfc/src/ndef/nfc_ble_pair_msg.c new file mode 100644 index 000000000..131514aad --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_pair_msg.c @@ -0,0 +1,411 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_BLE_PAIR_MSG) + +#include "nfc_ble_pair_msg.h" +#include "nfc_hs_rec.h" +#include "nfc_ac_rec.h" +#include "nfc_le_oob_rec.h" +#include "nfc_ep_oob_rec.h" +#include "nfc_ndef_msg.h" +#include "util/sdk_macros.h" + +/** + * @brief Descriptor of TK value locations in Connection Handover NDEF message. + */ +typedef struct +{ + uint8_t ** pp_tk_group; /**< Pointer to array of pointer with TK locations in CH NDEF message. */ + uint8_t tk_num; /**< Number of valid TK locations. */ + uint8_t tk_max_num; /**< Maximal number of possible TK locations. */ +} nfc_ble_tk_group_t; + +/** + * @brief Descriptor of LESC OOB data in Connection Handover NDEF message. + */ +typedef struct +{ + uint8_t * confirm; /**< Pointer to the LESC OOB confirmation value in the CH NDEF message. */ + uint8_t * random; /**< Pointer to the LESC OOB random value in the CH NDEF message. */ +} nfc_ble_lesc_data_pos_t; + +static nfc_ble_lesc_data_pos_t m_lesc_pos = {NULL, NULL}; /**< Descriptor used to update LESC keys in the NDEF Message */ +static nfc_ble_tk_group_t m_tk_group; /**< Descriptor used to find TK locations in the NDEF Message which require update. */ +static bool m_tk_modifier_on = false; /**< Flag indicating that TK modifier feature is on. */ + +/* Default value for Security Manager Out Of Band Flags field in BLE AD structure */ +/* which is used for EP OOB Record payload */ +static const uint8_t sec_mgr_oob_flags = + (AD_TYPE_SEC_MGR_OOB_FLAG_SET << AD_TYPE_SEC_MGR_OOB_FLAG_OOB_DATA_PRESENT_POS) | + (AD_TYPE_SEC_MGR_OOB_FLAG_SET << AD_TYPE_SEC_MGR_OOB_FLAG_OOB_LE_SUPPORTED_POS) | + (AD_TYPE_SEC_MGR_OOB_FLAG_CLEAR << AD_TYPE_SEC_MGR_OOB_FLAG_SIM_LE_AND_EP_POS) | + (AD_TYPE_SEC_MGR_OOB_ADDRESS_TYPE_RANDOM << AD_TYPE_SEC_MGR_OOB_FLAG_ADDRESS_TYPE_POS); + +/**@brief Function for configuring TK group modifier feature. + * + * @details This function configures the structure which is responsible for tracking TK locations. + * These locations can be afterwards easily accessed with @ref nfc_tk_group_modifier_update + * and modified. + * + * @param[in] pp_tk_group Pointer to array of TK locations that should be modified with + * @ref nfc_tk_group_modifier_update function. + * @param[in] max_group_size Maximal number of TK locations that can added to \p pp_tk_group. + */ +__STATIC_INLINE void nfc_tk_group_modifier_config(uint8_t ** pp_tk_group, uint8_t max_group_size) +{ + m_tk_group.pp_tk_group = pp_tk_group; + m_tk_group.tk_num = 0; + m_tk_group.tk_max_num = max_group_size; +} + +/** @brief Function for creating an AD structure with common configuration for EP and LE OOB records. + * + * This function creates an AD structure and initializes its fields with default content. Only + * fields that are common for both EP and LE OOB records are filled. + * + * @param[in] p_tk_value Pointer to the authentication Temporary Key (TK). If NULL, + * TK field of the returned AD structure is empty. + * @param[out] p_adv_data Pointer to BLE AD structure with common configuration for EP + * and LE OOB records. + */ +static void common_adv_data_create(ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + ble_advdata_t * const p_adv_data) +{ + memset((uint8_t *) p_adv_data, 0, sizeof(ble_advdata_t)); + + /* Set common configuration of AD structure for both Bluetooth EP and LE record */ + p_adv_data->include_appearance = true; + p_adv_data->name_type = BLE_ADVDATA_FULL_NAME; + p_adv_data->p_tk_value = NULL; + if (p_tk_value != NULL) + { + p_adv_data->p_tk_value = p_tk_value; + } + + p_adv_data->p_lesc_data = p_lesc_data; +} + +/** @brief Function for creating an AD structure with default configuration for an LE OOB record. + * + * This function creates an AD structure and initializes its fields with default content for + * LE OOB record payload. + * + * @param[in] p_tk_value Pointer to the authentication Temporary Key (TK). If NULL, + * TK field of the returned AD structure is empty. + * @param[out] p_le_adv_data Pointer to BLE AD structure with default configuration + * for LE OOB record. + */ +static void le_oob_specific_adv_data_create(ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + ble_advdata_t * const p_le_adv_data) +{ + /* Create default configuration which is common for both EP and LE OOB Records */ + common_adv_data_create(p_tk_value, p_lesc_data, p_le_adv_data); + + /* LE specific configuration */ + p_le_adv_data->include_ble_device_addr = true; + p_le_adv_data->le_role = BLE_ADVDATA_ROLE_ONLY_PERIPH; + p_le_adv_data->flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED; +} + +/** @brief Function for creating an AD structure with default configuration for an EP OOB record. + * + * This function creates an AD structure and initializes its fields with default content for + * EP OOB record payload. + * + * @param[in] p_tk_value Pointer to the authentication Temporary Key (TK). If NULL, + * TK field of the returned AD structure is empty. + * @param[out] p_ep_adv_data Pointer to BLE AD structure with default configuration + * for EP OOB record. + */ +static void ep_oob_specific_adv_data_create(ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + ble_advdata_t * const p_ep_adv_data) +{ + /* Create default configuration which is common for both EP and LE OOB Records */ + common_adv_data_create(p_tk_value, p_lesc_data, p_ep_adv_data); + + /* EP specific configuration */ + p_ep_adv_data->p_sec_mgr_oob_flags = (uint8_t *) &sec_mgr_oob_flags; +} + +ret_code_t nfc_ble_simplified_le_oob_msg_encode(ble_advdata_t const * const p_le_advdata, + uint8_t * p_buf, + uint32_t * p_len) +{ + ret_code_t err_code; + + /* Create NFC NDEF message description, capacity - 1 record */ + NFC_NDEF_MSG_DEF(nfc_le_oob_msg, 1); + + /* Create NFC NDEF LE OOB Record description without record ID field */ + NFC_NDEF_LE_OOB_RECORD_DESC_DEF(nfc_le_oob_rec, 0, p_le_advdata); + + /* Add LE OOB Record as lone record to message */ + err_code = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_le_oob_msg), + &NFC_NDEF_LE_OOB_RECORD_DESC(nfc_le_oob_rec)); + VERIFY_SUCCESS(err_code); + VERIFY_PARAM_NOT_NULL(p_le_advdata); + + if (!m_tk_modifier_on) + { + nfc_tk_group_modifier_config(NULL, 0); + } + + /* Encode whole message into buffer */ + err_code = nfc_ndef_msg_encode(&NFC_NDEF_MSG(nfc_le_oob_msg), + p_buf, + p_len); + + return err_code; +} + +ret_code_t nfc_ble_simplified_ep_oob_msg_encode(ble_advdata_t const * const p_ep_advdata, + uint8_t * p_buf, + uint32_t * p_len) +{ + ret_code_t err_code; + + /* Create NFC NDEF message description, capacity - 1 record */ + NFC_NDEF_MSG_DEF(nfc_ep_oob_msg, 1); + + /* Create NFC NDEF EP OOB Record description without record ID field */ + NFC_NDEF_EP_OOB_RECORD_DESC_DEF(nfc_ep_oob_rec, 0, p_ep_advdata); + + /* Add EP OOB Record as lone record to message */ + err_code = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_ep_oob_msg), + &NFC_NDEF_EP_OOB_RECORD_DESC(nfc_ep_oob_rec)); + VERIFY_SUCCESS(err_code); + VERIFY_PARAM_NOT_NULL(p_ep_advdata); + + if (!m_tk_modifier_on) + { + nfc_tk_group_modifier_config(NULL, 0); + } + + /* Encode whole message into buffer */ + err_code = nfc_ndef_msg_encode(&NFC_NDEF_MSG(nfc_ep_oob_msg), + p_buf, + p_len); + + return err_code; +} + +ret_code_t nfc_ble_full_handover_select_msg_encode(ble_advdata_t const * const p_le_advdata, + ble_advdata_t const * const p_ep_advdata, + uint8_t * p_buf, + uint32_t * p_len) +{ + ret_code_t err_code; + + // Carrier reference buffers for ac records. + uint8_t carrier_le_reference = '0'; + uint8_t carrier_ep_reference = '1'; + + // Create ac records for both message types. + NFC_NDEF_AC_RECORD_DESC_DEF(ac_rec_le, NFC_AC_CPS_ACTIVE, 1, &carrier_le_reference, 1); + NFC_NDEF_AC_RECORD_DESC_DEF(ac_rec_ep, NFC_AC_CPS_ACTIVE, 1, &carrier_ep_reference, 1); + + // Create a Hs record and assign existing ac records to it. + NFC_NDEF_HS_RECORD_DESC_DEF(hs_rec, 1, 3, 2); + err_code = nfc_hs_rec_local_record_add(&NFC_NDEF_HS_RECORD_DESC(hs_rec), + &NFC_NDEF_AC_RECORD_DESC(ac_rec_le)); + VERIFY_SUCCESS(err_code); + err_code = nfc_hs_rec_local_record_add(&NFC_NDEF_HS_RECORD_DESC(hs_rec), + &NFC_NDEF_AC_RECORD_DESC(ac_rec_ep)); + VERIFY_SUCCESS(err_code); + + // Create LE and EP records with different record IDs. + NFC_NDEF_LE_OOB_RECORD_DESC_DEF(nfc_le_oob_rec, carrier_le_reference, p_le_advdata); + NFC_NDEF_EP_OOB_RECORD_DESC_DEF(nfc_ep_oob_rec, carrier_ep_reference, p_ep_advdata); + + // Create full NDEF Handover Select message for Connection Handover and assign Hs, + // LE and EP records to it. + NFC_NDEF_MSG_DEF(nfc_hs_full_msg, 3); + err_code = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_hs_full_msg), + &NFC_NDEF_HS_RECORD_DESC(hs_rec)); + VERIFY_SUCCESS(err_code); + err_code = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_hs_full_msg), + &NFC_NDEF_LE_OOB_RECORD_DESC(nfc_le_oob_rec)); + VERIFY_SUCCESS(err_code); + err_code = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_hs_full_msg), + &NFC_NDEF_EP_OOB_RECORD_DESC(nfc_ep_oob_rec)); + VERIFY_SUCCESS(err_code); + + VERIFY_PARAM_NOT_NULL(p_le_advdata); + VERIFY_PARAM_NOT_NULL(p_ep_advdata); + + if (!m_tk_modifier_on) + { + nfc_tk_group_modifier_config(NULL, 0); + } + + /* Encode whole message into buffer */ + err_code = nfc_ndef_msg_encode(&NFC_NDEF_MSG(nfc_hs_full_msg), + p_buf, + p_len); + + return err_code; +} + +ret_code_t nfc_ble_pair_default_msg_encode(nfc_ble_pair_type_t nfc_ble_pair_type, + ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + uint8_t * p_buf, + uint32_t * p_len) +{ + ble_advdata_t le_adv_data; + ble_advdata_t ep_adv_data; + ret_code_t err_code = NRF_SUCCESS; + + switch (nfc_ble_pair_type) + { + + case NFC_BLE_PAIR_MSG_BLUETOOTH_LE_SHORT: + le_oob_specific_adv_data_create(p_tk_value, p_lesc_data, &le_adv_data); + err_code = nfc_ble_simplified_le_oob_msg_encode(&le_adv_data, p_buf, p_len); + break; + + case NFC_BLE_PAIR_MSG_BLUETOOTH_EP_SHORT: + ep_oob_specific_adv_data_create(p_tk_value, NULL, &ep_adv_data); + err_code = nfc_ble_simplified_ep_oob_msg_encode(&ep_adv_data, p_buf, p_len); + break; + + case NFC_BLE_PAIR_MSG_FULL: + le_oob_specific_adv_data_create(p_tk_value, p_lesc_data, &le_adv_data); + ep_oob_specific_adv_data_create(p_tk_value, NULL, &ep_adv_data); + err_code = nfc_ble_full_handover_select_msg_encode(&le_adv_data, + &ep_adv_data, + p_buf, + p_len); + break; + + } + + return err_code; +} + +ret_code_t nfc_ble_pair_msg_updatable_tk_encode(nfc_ble_pair_type_t nfc_ble_pair_type, + ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + uint8_t * p_buf, + uint32_t * p_len, + uint8_t ** pp_tk_group, + uint8_t max_group_size) +{ + ret_code_t err_code = NRF_SUCCESS; + + m_tk_modifier_on = true; + nfc_tk_group_modifier_config(pp_tk_group, max_group_size); + err_code = nfc_ble_pair_default_msg_encode(nfc_ble_pair_type, p_tk_value, + p_lesc_data, p_buf, p_len); + m_tk_modifier_on = false; + + return err_code; +} + +ret_code_t nfc_tk_group_modifier_update(ble_advdata_tk_value_t * p_tk_value) +{ + VERIFY_PARAM_NOT_NULL(m_tk_group.pp_tk_group); + for (uint8_t tk_index = 0; tk_index < m_tk_group.tk_num; ++tk_index) + { + uint8_t * p_tk_payload_data = m_tk_group.pp_tk_group[tk_index]; + nfc_tk_value_payload_encode(p_tk_value, p_tk_payload_data); + } + return NRF_SUCCESS; +} + +ret_code_t nfc_tk_to_group_add(uint8_t * p_tk_location) +{ + // Feature was disabled. + if (m_tk_group.pp_tk_group == NULL) + { + return NRF_SUCCESS; + } + + if (m_tk_group.tk_num < m_tk_group.tk_max_num) + { + m_tk_group.pp_tk_group[m_tk_group.tk_num++] = p_tk_location; + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_NO_MEM; + } +} + +ret_code_t nfc_lesc_pos_set(uint8_t * p_confirm, uint8_t * p_random) +{ + if ((p_confirm != NULL) && (p_random != NULL)) + { + m_lesc_pos.confirm = p_confirm; + m_lesc_pos.random = p_random; + + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_NULL; + } +} + +ret_code_t nfc_lesc_data_update(ble_gap_lesc_oob_data_t * p_ble_lesc_oob_data) +{ + if (p_ble_lesc_oob_data != NULL) + { + if ((m_lesc_pos.confirm != NULL) && (m_lesc_pos.random != NULL)) + { + memcpy(m_lesc_pos.confirm, p_ble_lesc_oob_data->c, AD_TYPE_CONFIRM_VALUE_DATA_SIZE); + memcpy(m_lesc_pos.random, p_ble_lesc_oob_data->r, AD_TYPE_RANDOM_VALUE_DATA_SIZE); + + return NRF_SUCCESS; + } + + return NRF_ERROR_INVALID_STATE; + } + else + { + return NRF_ERROR_NULL; + } +} + +#endif // NRF_MODULE_ENABLED(NFC_BLE_PAIR_MSG) diff --git a/libraries/nfc/src/ndef/nfc_ble_pair_msg.h b/libraries/nfc/src/ndef/nfc_ble_pair_msg.h new file mode 100644 index 000000000..0a1db7527 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ble_pair_msg.h @@ -0,0 +1,279 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_BLE_PAIR_MSG_H__ +#define NFC_BLE_PAIR_MSG_H__ + +/**@file + * + * @defgroup nfc_modules NDEF message modules + * @ingroup nfc_api + * @brief Implementation of NDEF messages. + * + * @defgroup nfc_ndef_messages Predefined NDEF messages + * @ingroup nfc_modules + * @brief Predefined NDEF messages for standard use. + * + * @defgroup nfc_ble_pair_msg BLE pairing messages + * @{ + * @ingroup nfc_ndef_messages + * + * @brief Generation of NFC NDEF messages used for BLE pairing. + * + */ + +#include +#include "util/ble_advdata.h" +#include "util/sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Types of BLE pairing message. + * + * Use one of these values to choose the type of NDEF BLE pairing message. + */ +typedef enum +{ + NFC_BLE_PAIR_MSG_BLUETOOTH_LE_SHORT, ///< Simplified LE OOB message. + NFC_BLE_PAIR_MSG_BLUETOOTH_EP_SHORT, ///< Simplified EP OOB message. + NFC_BLE_PAIR_MSG_FULL ///< BLE Handover Select Message. +} nfc_ble_pair_type_t; + +/** @brief Function for encoding simplified LE OOB messages. + * + * This function encodes a simplified LE OOB message into a buffer. The payload of the LE OOB record + * inside the message can be configured via the advertising data structure. + * + * This function was implemented partially according to "Bluetooth Secure Simple Pairing Using NFC" + * (denotation "NFCForum-AD-BTSSP_1_1" published on 2014-01-09) chapters 3.1, 3.2, 4.3.2, + * and according to "Supplement to the Bluetooth Core Specification" (Version 5, adoption date: + * Dec 02 2014). + * + * @note To be able to encode the message, a SoftDevice must be enabled and configured. + * + * @param[in] p_le_advdata Pointer to the BLE advertising data structure for the LE OOB record. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_simplified_le_oob_msg_encode(ble_advdata_t const * const p_le_advdata, + uint8_t * p_buf, + uint32_t * p_len); + +/** @brief Function for encoding simplified EP OOB messages. + * + * This function encodes a simplified EP OOB message into a buffer. The payload of the EP OOB record + * inside the message can be configured via the advertising data structure. + * + * This function was implemented partially according to "Bluetooth Secure Simple Pairing Using NFC" + * (denotation "NFCForum-AD-BTSSP_1_1" published on 2014-01-09) chapters 3.1, 3.2, 4.3.1, + * and according to "Supplement to the Bluetooth Core Specification" (Version 5, adoption date: + * Dec 02 2014). + * + * @note To be able to encode the message, a SoftDevice must be enabled and configured. + * + * @param[in] p_ep_advdata Pointer to the BLE advertising data structure for the EP OOB record. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_simplified_ep_oob_msg_encode(ble_advdata_t const * const p_ep_advdata, + uint8_t * p_buf, + uint32_t * p_len); + +/** @brief Function for encoding BLE Handover Select Messages. + * + * This function encodes a BLE Handover Select Message into a buffer. The payload of the LE OOB record + * and the EP OOB record inside the message can be configured via the advertising data structures. + * + * This function was implemented partially according to "Bluetooth Secure Simple Pairing Using NFC" + * (denotation "NFCForum-AD-BTSSP_1_1" published on 2014-01-09) chapters 3.1, 3.2, 4.1.1 + * and 4.1.2 (combined), and according to "Supplement to the Bluetooth Core Specification" (Version 5, + * adoption date: Dec 02 2014). + * + * @note To be able to encode the message, a SoftDevice must be enabled and configured. + * + * @param[in] p_le_advdata Pointer to the BLE advertising data structure for the LE OOB record. + * @param[in] p_ep_advdata Pointer to the BLE advertising data structure for the EP OOB record. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_full_handover_select_msg_encode(ble_advdata_t const * const p_le_advdata, + ble_advdata_t const * const p_ep_advdata, + uint8_t * p_buf, + uint32_t * p_len); + +/** @brief Function for encoding any type of BLE pairing messages with default BLE + * advertising data structures. + * + * This function encodes a BLE pairing message into a buffer. The message can be encoded as + * one of the three message types (using @ref nfc_ble_simplified_le_oob_msg_encode, + * @ref nfc_ble_simplified_ep_oob_msg_encode, or @ref nfc_ble_full_handover_select_msg_encode), + * according to the @p nfc_ble_pair_type parameter. LE and EP OOB records use the default + * advertising data structure configuration. Only one field ('Security Manager TK') in the BLE + * advertising data can be configured for both records by specifying the @p p_tk_value parameter. + * + * For LE OOB records, the default BLE advertising data structure configuration fills the required + * fields 'LE Bluetooth Device Address' and 'LE Role' and the optional fields 'Appearance', + * 'Local Name', and 'Flags'. + * + * For EP OOB records, the default BLE advertising data structure configuration fills the required + * field 'Security Manager Out Of Band Flags' and the optional fields 'Appearance', + * 'Local Name', and 'Flags'. + * + * @note To be able to encode the message, a SoftDevice must be enabled and configured. + * + * @param[in] nfc_ble_pair_type Type of BLE pairing message. + * @param[in] p_tk_value Pointer to the authentication Temporary Key (TK). If NULL, + * TK value field is not encoded in the NDEF message. + * @param[in] p_lesc_data Pointer to the LESC OOB data. If NULL, LESC OOB fields are + * not encoded in the NDEF message. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_pair_default_msg_encode(nfc_ble_pair_type_t nfc_ble_pair_type, + ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + uint8_t * p_buf, + uint32_t * p_len); + +/** @brief Function for encoding any type of BLE pairing messages with default BLE + * advertising data structures and with TK modifier feature. + * + * This function is very similar to the @ref nfc_ble_pair_default_msg_encode function, but + * additionaly enables tracking of TK locations which were encoded in the Connection Handover + * NDEF message. After using this function, you can update the TK value in NDEF by calling + * @ref nfc_tk_group_modifier_update. + * + * @param[in] nfc_ble_pair_type Type of BLE pairing message. + * @param[in] p_tk_value Pointer to the authentication Temporary Key (TK). If NULL, + * TK value field is not encoded in the NDEF message. + * @param[in] p_lesc_data Pointer to the LESC OOB data. If NULL, LESC OOB values are + * not encoded in the NDEF message. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * @param[in] pp_tk_group Pointer to array of TK locations that should be modified with + * @ref nfc_tk_group_modifier_update function. + * @param[in] max_group_size Maximal number of TK locations that can added to \p pp_tk_group. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +ret_code_t nfc_ble_pair_msg_updatable_tk_encode(nfc_ble_pair_type_t nfc_ble_pair_type, + ble_advdata_tk_value_t * const p_tk_value, + ble_gap_lesc_oob_data_t * const p_lesc_data, + uint8_t * p_buf, + uint32_t * p_len, + uint8_t ** pp_tk_group, + uint8_t max_group_size); + +/**@brief Function for updating the Connection Handover NDEF message with new TK value. + * + * @details This function updates NDEF message with new TK value. This update is applied to all of + * TK locations in the Connection Handover NDEF message. This function can only be used + * after calling @ref nfc_ble_pair_msg_updatable_tk_encode, which is used to encode + * Connection Handover NDEF message. + * + * @param[in] p_tk_value Pointer to the new TK value. The NDEF message will be updated with this + * value. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NULL If pointer to TK locations was NULL. + */ +ret_code_t nfc_tk_group_modifier_update(ble_advdata_tk_value_t * p_tk_value); + +/**@brief Function for adding new location of TK value to the location description structure. + * + * @param[in] p_tk_location New location of TK value in the Connection Handover NDEF message. + * + * @retval NRF_SUCCESS If the operation was successful or if buffer used for holding TK + * locations is NULL. + * @retval NRF_ERROR_NO_MEM If there is no place in the buffer for the new TK value location. + */ +ret_code_t nfc_tk_to_group_add(uint8_t * p_tk_location); + +/**@brief Function for updating the Connection Handover NDEF message with a new LESC OOB values. + * + * @details Updates LESC Confirmation and Random Values based on its locations set by the @ref nfc_lesc_pos_set function. + * + * @param[in] p_ble_lesc_oob_data Pointer to the new LESC OOB data. The NDEF message will be updated with this data. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NULL If pointer to the new LESC OOB data is NULL. + * @retval NRF_ERROR_INVALID_STATE If pointer to the LESC OOB data location in NDEF message is NULL. + */ +ret_code_t nfc_lesc_data_update(ble_gap_lesc_oob_data_t * p_ble_lesc_oob_data); + +/**@brief Function for storing pointers to the LESC OOB data inside NDEF message. + * + * @details It allows LESC OOB data update without regenerating entire CH NDEF message. + * + * @param[in] p_confirm Pointer to the LESC Confirmation Value position in the NDEF message. + * @param[in] p_random Pointer to the LESC Random Value position in the NDEF message. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NULL If either of pointers is set to NULL. + */ +ret_code_t nfc_lesc_pos_set(uint8_t * p_confirm, uint8_t * p_random); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_BLE_PAIR_MSG_H__ diff --git a/libraries/nfc/src/ndef/nfc_ep_oob_rec.c b/libraries/nfc/src/ndef/nfc_ep_oob_rec.c new file mode 100644 index 000000000..e805c9a27 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ep_oob_rec.c @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_EP_OOB_REC) + +#include "nfc_ep_oob_rec.h" +#include "util/sdk_errors.h" +#include "ble_gap.h" +#include "util/app_util.h" + +/* NFC OOB EP definitions */ +#define NFC_EP_OOB_REC_GAP_ADDR_LEN BLE_GAP_ADDR_LEN +#define NFC_EP_OOB_REC_OOB_DATA_LEN_SIZE 2UL +#define NFC_EP_OOB_REC_PAYLOAD_PREFIX_LEN (NFC_EP_OOB_REC_GAP_ADDR_LEN + \ + NFC_EP_OOB_REC_OOB_DATA_LEN_SIZE) + +/* Record Payload Type for Bluetooth Carrier Configuration EP record */ +const uint8_t ndef_ep_oob_record_type[] = +{ + 'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', '/', 'v', 'n', 'd', '.', + 'b', 'l', 'u', 'e', 't', 'o', 'o', 't', 'h', '.', 'e', 'p', '.', 'o', 'o', 'b' +}; + +/** + * @brief Function for validating AD structure content for a Bluetooth Carrier Configuration EP record. + * + * This function validates AD structure content. LE Bluetooth Device Address and LE Role + * fields must not be included. Security Manager OOB Flags structure is required. + * + * @param[in] p_ble_advdata Pointer to the description of the payload. + * + * @retval NRF_SUCCESS If the validation was successful. + * @retval NRF_ERROR_INVALID_PARAM Otherwise. + */ +static ret_code_t nfc_ep_oob_adv_data_check(ble_advdata_t const * const p_ble_advdata) +{ + if ((true == p_ble_advdata->include_ble_device_addr) || + (BLE_ADVDATA_ROLE_NOT_PRESENT != p_ble_advdata->le_role) || + (NULL == p_ble_advdata->p_sec_mgr_oob_flags)) + { + return NRF_ERROR_INVALID_PARAM; + } + + /* If Flags field in AD structure is present, the BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED flag + must be set. */ + if ((0 != p_ble_advdata->flags) && + ((p_ble_advdata->flags & BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) == 0)) + { + return NRF_ERROR_INVALID_PARAM; + } + return NRF_SUCCESS; +} + +/** + * @brief Function for encoding device address to Bluetooth Carrier Configuration EP record. + * + * This fuction is used to encode device address to Bluetooth Carrier Configuration EP record. + * + * @param[in] p_encoded_data Pointer to the buffer where encoded data will be returned. + * @param[in] max_len Available memory in the buffer. + * + * @retval NRF_SUCCESS If the encoding was successful. + * @retval NRF_ERROR_NO_MEM If available memory was not enough. + * @retval NRF_ERROR_xxx If any other error occured. + */ +static ret_code_t nfc_ep_oob_bluetooth_device_address_encode(uint8_t * const p_encoded_data, + uint16_t max_len) +{ + ret_code_t err_code = NRF_SUCCESS; + ble_gap_addr_t device_address; + + memset(&device_address, 0x00, sizeof(device_address)); + + if (NFC_EP_OOB_REC_GAP_ADDR_LEN > max_len) + { + return NRF_ERROR_NO_MEM; + } + + /* Get BLE address */ + err_code = sd_ble_gap_addr_get(&device_address); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + /* Encode Bluetooth EP device address */ + memcpy(p_encoded_data, device_address.addr, NFC_EP_OOB_REC_GAP_ADDR_LEN); + + return NRF_SUCCESS; +} + +ret_code_t nfc_ep_oob_payload_constructor(ble_advdata_t * p_ble_advdata, + uint8_t * p_buff, + uint32_t * p_len) +{ + ret_code_t err_code = NRF_SUCCESS; + uint8_t * p_ad_data = NULL; + uint16_t payload_len, ad_data_len; + + /* Check correctness of the configuration structure */ + err_code = nfc_ep_oob_adv_data_check(p_ble_advdata); + if (NRF_SUCCESS != err_code) + { + return err_code; + } + + if (p_buff != NULL) + { + /* Validate if there is enough memory for OOB payload length field and BLE device address */ + if (NFC_EP_OOB_REC_PAYLOAD_PREFIX_LEN > *p_len) + { + return NRF_ERROR_NO_MEM; + } + + /* Set proper memory offset in payload buffer for AD structure and count available memory. + * Bluetooth EP device address and OOB payload length field must be inserted before the AD payload */ + p_ad_data = (uint8_t *) (p_buff + NFC_EP_OOB_REC_PAYLOAD_PREFIX_LEN); + ad_data_len = *p_len - NFC_EP_OOB_REC_PAYLOAD_PREFIX_LEN; + if ( *p_len - NFC_EP_OOB_REC_PAYLOAD_PREFIX_LEN > UINT16_MAX ) + { + ad_data_len = UINT16_MAX; + } + } + + /* Encode AD structures into NFC record payload */ + err_code = nfc_ble_oob_adv_data_encode(p_ble_advdata, p_ad_data, &ad_data_len); + if (NRF_SUCCESS != err_code) + { + return err_code; + } + + /* Now as the final payload length is known OOB payload length field, and Bluetooth device + * address can be encoded */ + payload_len = ad_data_len + NFC_EP_OOB_REC_PAYLOAD_PREFIX_LEN; + if (p_buff != NULL) + { + p_buff += uint16_encode(payload_len, p_buff); + err_code = nfc_ep_oob_bluetooth_device_address_encode(p_buff, p_ad_data - p_buff); + if (NRF_SUCCESS != err_code) + { + return err_code; + } + } + + /* Update total payload length */ + *p_len = payload_len; + + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_EP_OOB_REC) diff --git a/libraries/nfc/src/ndef/nfc_ep_oob_rec.h b/libraries/nfc/src/ndef/nfc_ep_oob_rec.h new file mode 100644 index 000000000..348672546 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ep_oob_rec.h @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_EP_OOB_REC_H__ +#define NFC_EP_OOB_REC_H__ + +/**@file + * + * @defgroup nfc_ep_oob_rec EP OOB records + * @{ + * @ingroup nfc_ble_pair_msg + * + * @brief Generation of NFC NDEF EP OOB records for NDEF messages. + * + */ + +#include +#include "nfc_ndef_record.h" +#include "nfc_ble_oob_advdata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Size of the type field of the Bluetooth Carrier Configuration EP record, defined in the + * file @c nfc_ep_oob_rec.c. It is used in the @ref NFC_NDEF_EP_OOB_RECORD_DESC_DEF macro. + */ +#define NFC_EP_OOB_REC_TYPE_LENGTH 32 + +/** + * @brief External reference to the type field of the Bluetooth Carrier Configuration EP record, defined + * in the file @c nfc_ep_oob_rec.c. It is used in the @ref NFC_NDEF_EP_OOB_RECORD_DESC_DEF macro. + */ +extern const uint8_t ndef_ep_oob_record_type[NFC_EP_OOB_REC_TYPE_LENGTH]; + +/** + * @brief Function for constructing the payload for a Bluetooth Carrier Configuration EP record. + * + * This function encodes the record payload according to the BLE AD structure. It implements + * an API compatible with @ref p_payload_constructor_t. + * + * @param[in] p_ble_advdata Pointer to the description of the payload. + * @param[out] p_buff Pointer to payload destination. If NULL, function will + * calculate the expected size of the record payload. + * + * @param[in,out] p_len Size of available memory to write as input. Size of generated + * payload as output. + * + * @retval NRF_SUCCESS If the record payload was encoded successfully. + * @retval NRF_ERROR_NO_MEM If available memory was not enough for record payload to be encoded. + * @retval Other If any other error occurred during record payload encoding. + */ +ret_code_t nfc_ep_oob_payload_constructor(ble_advdata_t * p_ble_advdata, + uint8_t * p_buff, + uint32_t * p_len); + +/** @brief Macro for generating a description of an NFC NDEF Bluetooth Carrier Configuration EP record. + * + * This macro declares and initializes an instance of an NFC NDEF record description + * for a Bluetooth Carrier Configuration EP record. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF message encoding (see @ref nfc_ble_simplified_ep_oob_msg_encode) + * must be done in the same variable scope. + * + * @param[in] NAME Name for accessing record descriptor. + * @param[in] PAYLOAD_ID NDEF record header Payload ID field (Limited to one byte). + * If 0, no ID is present in the record description. + * @param[in] P_BLE_ADVDATA Pointer to the encoded BLE advertising data structure. This + * data is used to create the record payload. + */ +#define NFC_NDEF_EP_OOB_RECORD_DESC_DEF(NAME, \ + PAYLOAD_ID, \ + P_BLE_ADVDATA) \ + uint8_t NAME##_ndef_ep_oob_record_id = (PAYLOAD_ID); \ + uint8_t NAME##_ndef_ep_oob_record_id_length = ((PAYLOAD_ID) != 0) ? 1 : 0; \ + NFC_NDEF_GENERIC_RECORD_DESC_DEF( NAME, \ + TNF_MEDIA_TYPE, \ + &NAME##_ndef_ep_oob_record_id, \ + NAME##_ndef_ep_oob_record_id_length, \ + (ndef_ep_oob_record_type), \ + sizeof(ndef_ep_oob_record_type), \ + nfc_ep_oob_payload_constructor, \ + (P_BLE_ADVDATA)) \ + +/** + * @brief Macro for accessing the NFC NDEF Bluetooth Carrier Configuration EP record descriptor + * instance that was created with @ref NFC_NDEF_EP_OOB_RECORD_DESC_DEF. + */ +#define NFC_NDEF_EP_OOB_RECORD_DESC(NAME) NFC_NDEF_GENERIC_RECORD_DESC(NAME) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_EP_OOB_REC_H__ diff --git a/libraries/nfc/src/ndef/nfc_hs_rec.c b/libraries/nfc/src/ndef/nfc_hs_rec.c new file mode 100644 index 000000000..9a96f1288 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_hs_rec.c @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_HS_REC) + +#include "nfc_hs_rec.h" +#include "nfc_ac_rec.h" +#include "nrf_error.h" + +#define HS_REC_VERSION_SIZE 1 + +const uint8_t nfc_hs_rec_type_field[] = {'H', 's'}; ///< Handover Select record type. + + +ret_code_t nfc_hs_rec_payload_constructor(nfc_hs_rec_payload_desc_t * p_nfc_hs_rec_payload_desc, + uint8_t * p_buff, + uint32_t * p_len) +{ + ret_code_t err_code = NRF_SUCCESS; + + if (p_buff != NULL) + { + // There must be at least 1 free byte in buffer for version byte. + if (*p_len < HS_REC_VERSION_SIZE) + { + return NRF_ERROR_NO_MEM; + } + + // Major/minor version byte. + *p_buff = ( (p_nfc_hs_rec_payload_desc->major_version << 4) & 0xF0) | + ( p_nfc_hs_rec_payload_desc->minor_version & 0x0F); + p_buff += HS_REC_VERSION_SIZE; + + // Decrement remaining buffer size. + *p_len -= HS_REC_VERSION_SIZE; + } + + // Encode local records encapsulated in a message. + err_code = nfc_ndef_msg_encode(p_nfc_hs_rec_payload_desc->p_local_records, p_buff, p_len); + if (err_code!= NRF_SUCCESS) + { + return err_code; + } + + // Add version byte to the total record size. + *p_len += HS_REC_VERSION_SIZE; + + return NRF_SUCCESS; +} + + +void nfc_hs_rec_local_record_clear(nfc_ndef_record_desc_t * p_hs_rec) +{ + nfc_hs_rec_payload_desc_t* p_hs_payload = + (nfc_hs_rec_payload_desc_t*)p_hs_rec->p_payload_descriptor; + + nfc_ndef_msg_clear(p_hs_payload->p_local_records); +} + + +ret_code_t nfc_hs_rec_local_record_add(nfc_ndef_record_desc_t * p_hs_rec, + nfc_ndef_record_desc_t * p_local_rec) +{ + nfc_hs_rec_payload_desc_t* p_hs_payload = + (nfc_hs_rec_payload_desc_t*)p_hs_rec->p_payload_descriptor; + + return nfc_ndef_msg_record_add(p_hs_payload->p_local_records, p_local_rec); +} + +#endif // NRF_MODULE_ENABLED(NFC_HS_REC) diff --git a/libraries/nfc/src/ndef/nfc_hs_rec.h b/libraries/nfc/src/ndef/nfc_hs_rec.h new file mode 100644 index 000000000..a363e3ca6 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_hs_rec.h @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_HS_REC_H__ +#define NFC_HS_REC_H__ + +/**@file + * + * @defgroup nfc_hs_rec Hs (Handover Select) records + * @{ + * @ingroup nfc_ble_pair_msg + * + * @brief Generation of NFC NDEF Handover Select records for NDEF messages. + * + */ + +#include +#include "nfc_ndef_record.h" +#include "nfc_ndef_msg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Handover Select record payload descriptor. + */ +typedef struct +{ + uint8_t major_version; ///< Major version number of the supported Connection Handover specification. + uint8_t minor_version; ///< Minor version number of the supported Connection Handover specification. + nfc_ndef_msg_desc_t * p_local_records; ///< Pointer to a message encapsulating local records. +} nfc_hs_rec_payload_desc_t; + + +/** + * @brief Constructor for an NFC NDEF Handover Select record payload. + * + * This function encodes the payload of a Handover Select record as specified in the Connection + * Handover standard. It implements an API compatible with @ref p_payload_constructor_t. + */ + +ret_code_t nfc_hs_rec_payload_constructor(nfc_hs_rec_payload_desc_t * p_nfc_hs_rec_payload_desc, + uint8_t * p_buff, + uint32_t * p_len); + +/** + * @brief An external reference to the type field of the Handover Select record, defined in the + * file @c nfc_hs_rec.c. It is used in the @ref NFC_NDEF_HS_RECORD_DESC_DEF macro. + */ +extern const uint8_t nfc_hs_rec_type_field[]; + +/** + * @brief Size of the type field of the Handover Select record, defined in the + * file @c nfc_hs_rec.c. It is used in the @ref NFC_NDEF_HS_RECORD_DESC_DEF macro. + */ +#define NFC_HS_REC_TYPE_LENGTH 2 + +/** + * @brief Macro for creating and initializing an NFC NDEF record descriptor for a Handover Select record. + * + * This macro creates and initializes an instance of type @ref nfc_ndef_record_desc_t and + * an instance of type @ref nfc_hs_rec_payload_desc_t, which together constitute an instance of a Handover Select record. + * + * Use the macro @ref NFC_NDEF_HS_RECORD_DESC to access the NDEF Handover Select record descriptor instance. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF message encoding (see @ref nfc_ble_full_handover_select_msg_encode) + * must be done in the same variable scope. + * + * @param[in] NAME Name of the created record descriptor instance. + * @param[in] MAJOR_VERSION Major version number of the supported Connection Handover specification. + * @param[in] MINOR_VERSION Minor version number of the supported Connection Handover specification. + * @param[in] MAX_RECORDS Maximum number of local records (ac records plus optional err record). + */ +#define NFC_NDEF_HS_RECORD_DESC_DEF(NAME, \ + MAJOR_VERSION, \ + MINOR_VERSION, \ + MAX_RECORDS) \ + NFC_NDEF_MSG_DEF(NAME, MAX_RECORDS); \ + nfc_hs_rec_payload_desc_t NAME##_nfc_hs_rec_payload_desc = \ + { \ + .major_version = MAJOR_VERSION, \ + .minor_version = MINOR_VERSION, \ + .p_local_records = &NFC_NDEF_MSG(NAME) \ + }; \ + NFC_NDEF_GENERIC_RECORD_DESC_DEF(NAME, \ + TNF_WELL_KNOWN, \ + 0, \ + 0, \ + nfc_hs_rec_type_field, \ + NFC_HS_REC_TYPE_LENGTH, \ + nfc_hs_rec_payload_constructor, \ + &(NAME##_nfc_hs_rec_payload_desc)) + +/** + * @brief Macro for accessing the NFC NDEF Handover Select record descriptor + * instance that was created with @ref NFC_NDEF_HS_RECORD_DESC_DEF. + */ +#define NFC_NDEF_HS_RECORD_DESC(NAME) NFC_NDEF_GENERIC_RECORD_DESC(NAME) + +/** + * @brief Function for clearing local records in the NFC NDEF Handover Select record. + * + * This function clears local records from the Handover Select record. + * + * @param[in, out] p_hs_rec Pointer to the Handover Select record descriptor. + */ +void nfc_hs_rec_local_record_clear(nfc_ndef_record_desc_t * p_hs_rec); + +/** + * @brief Function for adding a local record to an NFC NDEF Handover Select record. + * + * @param[in, out] p_hs_rec Pointer to a Handover Select record. + * @param[in] p_local_rec Pointer to a local record to add. + * + * @retval NRF_SUCCESS If the local record was added successfully. + * @retval NRF_ERROR_NO_MEM If the Handover Select record already contains the maximum number of local records. + */ +ret_code_t nfc_hs_rec_local_record_add(nfc_ndef_record_desc_t * p_hs_rec, + nfc_ndef_record_desc_t * p_local_rec); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_HS_REC_H__ diff --git a/libraries/nfc/src/ndef/nfc_launchapp_msg.c b/libraries/nfc/src/ndef/nfc_launchapp_msg.c new file mode 100644 index 000000000..0fc5ec200 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_launchapp_msg.c @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_LAUNCHAPP_MSG) + +#include +#include +#include "nfc_launchapp_rec.h" +#include "nfc_launchapp_msg.h" +#include "nrf_error.h" +#include "util/sdk_macros.h" + +ret_code_t nfc_launchapp_msg_encode(uint8_t const * p_android_package_name, + uint8_t android_package_name_length, + uint8_t const * p_win_app_id, + uint8_t win_app_id_length, + uint8_t * p_buf, + uint32_t * p_len) +{ + ret_code_t err_code; + + /* Create NFC NDEF message description, capacity - 2 records */ + NFC_NDEF_MSG_DEF(nfc_launchapp_msg, 2); + + /* Create NFC NDEF Windows Phone LaunchApp Record description */ + NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC_DEF(nfc_win_launchapp_rec, + p_win_app_id, + win_app_id_length); + + /* Create NFC NDEF Android Application Record description */ + NFC_NDEF_ANDROID_LAUNCHAPP_RECORD_DESC_DEF(nfc_and_launchapp_rec, + p_android_package_name, + android_package_name_length); + + if (p_win_app_id != NULL) + { + /* Add Windows Phone LaunchApp Record as first record to message */ + err_code = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_launchapp_msg), + &NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC(nfc_win_launchapp_rec)); + VERIFY_SUCCESS(err_code); + } + if (p_android_package_name != NULL) + { + /* Add Android Application Record as second record to message */ + err_code = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_launchapp_msg), + &NFC_NDEF_ANDROID_LAUNCHAPP_RECORD_DESC(nfc_and_launchapp_rec)); + VERIFY_SUCCESS(err_code); + } + VERIFY_FALSE(NFC_NDEF_MSG(nfc_launchapp_msg).record_count == 0, + NRF_ERROR_INVALID_PARAM); + + /* Encode whole message into buffer */ + err_code = nfc_ndef_msg_encode(&NFC_NDEF_MSG(nfc_launchapp_msg), + p_buf, + p_len); + + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_LAUNCHAPP_MSG) diff --git a/libraries/nfc/src/ndef/nfc_launchapp_msg.h b/libraries/nfc/src/ndef/nfc_launchapp_msg.h new file mode 100644 index 000000000..bdfaaacd7 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_launchapp_msg.h @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_LAUNCHAPP_MSG_H__ +#define NFC_LAUNCHAPP_MSG_H__ + +/** @file + * + * @defgroup nfc_launchapp_msg Launch app messages + * @{ + * @ingroup nfc_ndef_messages + * + * @brief Generation of NFC NDEF messages that can be used to launch apps. + * + */ + +#include +#include "nfc_ndef_msg.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @brief Function for encoding an NFC NDEF launch app message. + * + * This function encodes an NFC NDEF message into a buffer. + * + * @param[in] p_android_package_name Pointer to the Android package name string. + * If NULL, the Android Application Record will be skipped. + * @param[in] android_package_name_length Length of the Android package name. + * @param[in] p_win_app_id Pointer to the Windows application ID string (GUID). + * If NULL, the Windows LaunchApp record will be skipped. + * @param[in] win_app_id_length Length of the Windows application ID. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the description was successfully created. + * @retval NRF_ERROR_INVALID_PARAM If both p_android_package_name and windows_application_id were + * invalid (equal to NULL). + * @retval NRF_ERROR_NO_MEM If the predicted message size is bigger than the provided + * buffer space. + * @retval Other Other codes might be returned depending on + * the function @ref nfc_ndef_msg_encode + */ +ret_code_t nfc_launchapp_msg_encode(uint8_t const * p_android_package_name, + uint8_t android_package_name_length, + uint8_t const * p_win_app_id, + uint8_t win_app_id_length, + uint8_t * p_buf, + uint32_t * p_len); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + #endif // NFC_LAUNCHAPP_MSG_H__ + + diff --git a/libraries/nfc/src/ndef/nfc_launchapp_rec.c b/libraries/nfc/src/ndef/nfc_launchapp_rec.c new file mode 100644 index 000000000..0e804fcd2 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_launchapp_rec.c @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_LAUNCHAPP_REC) + +#include "nfc_launchapp_rec.h" +#include +#include "nrf_error.h" +#include "util/app_util.h" +#include "nfc_ndef_record.h" + +/* Record Payload Type for NFC NDEF Android Application Record */ +const uint8_t ndef_android_launchapp_rec_type[] = +{ + 'a', 'n', 'd', 'r', 'o', 'i', 'd', '.', 'c','o', 'm', ':', 'p', 'k', 'g' +}; + +/* Record Payload Type for NFC NDEF Windows LaunchApp record */ +const uint8_t ndef_windows_launchapp_rec_type[] = +{ + 'w', 'i', 'n', 'd', 'o', 'w', 's', '.', 'c', 'o', 'm', '/', 'L', 'a', 'u', + 'n', 'c', 'h', 'A', 'p', 'p' +}; +/* Platform type used in Record Payload of NFC NDEF Windows LaunchApp record */ +const uint8_t ndef_windows_launchapp_plat_type[] = +{ + 'W', 'i', 'n', 'd', 'o', 'w', 's', 'P', 'h', 'o', 'n', 'e' +}; + +#define WIN_LAUNCHAPP_EMPTY_PARAMETER 0x20 ///< The empty parameter value for the Windows LaunchApp Record. + +ret_code_t nfc_win_launchapp_payload_constructor(win_launchapp_payload_desc_t * p_input, + uint8_t * p_buff, + uint32_t * p_len) +{ + + win_launchapp_payload_desc_t * launch_desc = (win_launchapp_payload_desc_t *) p_input; + + uint32_t temp_len = (uint32_t)launch_desc->platform_length + launch_desc->app_id_length + 7; + + if (p_buff != NULL) + { + if (temp_len > *p_len) + { + return NRF_ERROR_NO_MEM; + } + + *p_buff++ = 0x00; // platform count: 1 + *p_buff++ = 0x01; // -||- + + *p_buff++ = launch_desc->platform_length; + memcpy(p_buff, launch_desc->platform, launch_desc->platform_length); // platform + p_buff += launch_desc->platform_length; + + + *p_buff++ = launch_desc->app_id_length; + memcpy(p_buff, launch_desc->app_id, launch_desc->app_id_length); + p_buff += launch_desc->app_id_length; + + *p_buff++ = 0x00; // parameters length 1B + *p_buff++ = 0x01; // -||- + *p_buff++ = WIN_LAUNCHAPP_EMPTY_PARAMETER; // empty parameter + } + + *p_len = temp_len; + + return NRF_SUCCESS; +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_LAUNCHAPP_REC) diff --git a/libraries/nfc/src/ndef/nfc_launchapp_rec.h b/libraries/nfc/src/ndef/nfc_launchapp_rec.h new file mode 100644 index 000000000..a7071912c --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_launchapp_rec.h @@ -0,0 +1,205 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_LAUNCHAPP_REC_H__ +#define NFC_LAUNCHAPP_REC_H__ + +/**@file + * + * @defgroup nfc_launchapp_rec Launch app records + * @{ + * @ingroup nfc_launchapp_msg + * + * @brief Generation of NFC NDEF record descriptions that launch apps. + * + */ + +#include +#include "nfc_ndef_record.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Size of the type field of the Android Application Record, defined in the file + * @c nfc_launchapp_rec.c. It is used in the @ref NFC_NDEF_ANDROID_LAUNCHAPP_RECORD_DESC macro. + */ +#define NFC_ANDROID_REC_TYPE_LENGTH 15 + +/** + * @brief Size of the type field of the Windows LaunchApp Record, defined in the file + * @c nfc_launchapp_rec.c. It is used in the @ref NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC_DEF macro. + */ +#define NFC_WINDOWS_REC_TYPE_LENGTH 21 + +/** + * @brief Size of the platform type, which is used to encode payload field of the Windows LaunchApp + * Record, defined in the file @c nfc_launchapp_rec.c. It is used in the + * @ref NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC_DEF macro. + */ +#define NFC_WINDOWS_PLAT_TYPE_LENGTH 12 + +/** + * @brief Type of description of payload of Windows LaunchApp record. + */ +typedef struct +{ + const uint8_t * platform; + uint8_t platform_length; + const uint8_t * app_id; + uint8_t app_id_length; +} win_launchapp_payload_desc_t; + +/** + * @brief External reference to the type field of the NFC NDEF Android Application Record, defined in the + * file @c nfc_launchapp_rec.c. It is used in the @ref NFC_NDEF_ANDROID_LAUNCHAPP_RECORD_DESC_DEF macro. + */ +extern const uint8_t ndef_android_launchapp_rec_type[NFC_ANDROID_REC_TYPE_LENGTH]; + +/** + * @brief External reference to the type field of the NFC NDEF Windows LaunchApp record, defined in the + * file @c nfc_launchapp_rec.c. It is used in the @ref NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC_DEF macro. + */ +extern const uint8_t ndef_windows_launchapp_rec_type[NFC_WINDOWS_REC_TYPE_LENGTH]; + +/** + * @brief External reference to the platform type, which is used to encode payload field of the NFC NDEF + * Windows LaunchApp record. This constant is defined in the file @c nfc_launchapp_rec.c and is used in + * the macro @ref NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC_DEF. + */ +extern const uint8_t ndef_windows_launchapp_plat_type[NFC_WINDOWS_PLAT_TYPE_LENGTH]; + +/** + * @brief Function for constructing the payload for a Windows LaunchApp record. + * + * This function encodes the payload according to the LaunchApp record definition. It implements an API + * compatible with p_payload_constructor_t. + * + * @param[in] p_input Pointer to the description of the payload. + * @param[out] p_buff Pointer to payload destination. If NULL, function will + * calculate the expected size of the LaunchApp record payload. + * + * @param[in,out] p_len Size of available memory to write as input. Size of generated + * payload as output. + * + * @retval NRF_SUCCESS Always success. + */ +ret_code_t nfc_win_launchapp_payload_constructor(win_launchapp_payload_desc_t * p_input, + uint8_t * p_buff, + uint32_t * p_len); + +/** @brief Macro for generating a description of an NFC NDEF Android Application Record (AAR). + * + * This macro declares and initializes an instance of an NFC NDEF record description + * of an Android Application Record (AAR). + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF message encoding (see @ref nfc_launchapp_msg_encode) must be done + * in the same variable scope. + * + * @param[in] NAME Name for accessing record descriptor. + * @param[in] P_PACKAGE_NAME Pointer to the Android package name string. + * @param[in] PACKAGE_NAME_LENGTH Length of the Android package name. + */ +#define NFC_NDEF_ANDROID_LAUNCHAPP_RECORD_DESC_DEF(NAME, \ + P_PACKAGE_NAME, \ + PACKAGE_NAME_LENGTH) \ + NFC_NDEF_RECORD_BIN_DATA_DEF(NAME, \ + TNF_EXTERNAL_TYPE, \ + NULL, \ + 0, \ + ndef_android_launchapp_rec_type, \ + sizeof(ndef_android_launchapp_rec_type), \ + (P_PACKAGE_NAME), \ + (PACKAGE_NAME_LENGTH)) + +/** + * @brief Macro for accessing the NFC NDEF Android Application Record descriptor + * instance that was created with @ref NFC_NDEF_ANDROID_LAUNCHAPP_RECORD_DESC_DEF. + */ +#define NFC_NDEF_ANDROID_LAUNCHAPP_RECORD_DESC(NAME) NFC_NDEF_RECORD_BIN_DATA(NAME) + +/** @brief Macro for generating a description of an NFC NDEF Windows LaunchApp record. + * + * This macro declares and initializes an instance of an NFC NDEF record description + * of a Windows LaunchApp record. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF message encoding (see @ref nfc_launchapp_msg_encode) must be done + * in the same variable scope. + * + * @param[in] NAME Name for accessing record descriptor. + * @param[in] P_WIN_APP_ID Pointer to the Windows application ID string (GUID). + * @param[in] WIN_APP_ID_LENGTH Length of the Windows application ID. + * + * @return Pointer to the description of the record. + */ +#define NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC_DEF(NAME, \ + P_WIN_APP_ID, \ + WIN_APP_ID_LENGTH) \ + win_launchapp_payload_desc_t NAME##_ndef_win_launchapp_rec_payload_desc = \ + { \ + .platform = ndef_windows_launchapp_plat_type, \ + .platform_length = sizeof(ndef_windows_launchapp_plat_type), \ + .app_id = (P_WIN_APP_ID), \ + .app_id_length = WIN_APP_ID_LENGTH \ + }; \ + NFC_NDEF_GENERIC_RECORD_DESC_DEF(NAME, \ + TNF_ABSOLUTE_URI, \ + NULL, \ + 0, \ + ndef_windows_launchapp_rec_type, \ + sizeof(ndef_windows_launchapp_rec_type), \ + nfc_win_launchapp_payload_constructor, \ + &NAME##_ndef_win_launchapp_rec_payload_desc) \ + +/** + * @brief Macro for accessing the NFC NDEF Windows LaunchApp Record descriptor + * instance that was created with @ref NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC_DEF. + */ +#define NFC_NDEF_WINDOWS_LAUNCHAPP_RECORD_DESC(NAME) NFC_NDEF_GENERIC_RECORD_DESC(NAME) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_LAUNCHAPP_REC diff --git a/libraries/nfc/src/ndef/nfc_le_oob_rec.c b/libraries/nfc/src/ndef/nfc_le_oob_rec.c new file mode 100644 index 000000000..417ce9a3a --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_le_oob_rec.c @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_LE_OOB_REC) + +#include "nfc_le_oob_rec.h" +#include "util/sdk_errors.h" +#include "ble_gap.h" +#include "nfc_ble_pair_common.h" + +/** + * @brief Function for validating AD structure content for a Bluetooth Carrier Configuration LE record. + * + * This function validates AD structure content. LE Bluetooth Device Address and LE Role + * fields are required. Security Manager Out Of Band Flags structure must not be included. + * + * @param[in] p_ble_advdata Pointer to the description of the payload. + * + * @retval NRF_SUCCESS If the validation was successful. + * @retval NRF_ERROR_INVALID_PARAM Otherwise. + */ +static ret_code_t nfc_le_oob_adv_data_check(ble_advdata_t const * const p_ble_advdata) +{ + if ((false == p_ble_advdata->include_ble_device_addr) || + (BLE_ADVDATA_ROLE_NOT_PRESENT == p_ble_advdata->le_role) || + (NULL != p_ble_advdata->p_sec_mgr_oob_flags)) + { + return NRF_ERROR_INVALID_PARAM; + } + + /* If Flags field in AD structure is present, the BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED flag + must be set. */ + if ((0 != p_ble_advdata->flags) && + ((p_ble_advdata->flags & BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) == 0)) + { + return NRF_ERROR_INVALID_PARAM; + } + + return NRF_SUCCESS; +} + +ret_code_t nfc_le_oob_payload_constructor(ble_advdata_t * p_ble_advdata, + uint8_t * p_buff, + uint32_t * p_len) +{ + ret_code_t err_code = NRF_SUCCESS; + + /* Check correctness of the configuration structure */ + err_code = nfc_le_oob_adv_data_check(p_ble_advdata); + if (NRF_SUCCESS != err_code) + { + return err_code; + } + + /* Encode AD structures into NFC record payload */ + uint16_t buff_len = *p_len; + if (*p_len > UINT16_MAX) + { + buff_len = UINT16_MAX; + } + err_code = nfc_ble_oob_adv_data_encode(p_ble_advdata, p_buff, &buff_len); + + /* Update total payload length */ + *p_len = (uint32_t) buff_len; + + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_LE_OOB_REC) diff --git a/libraries/nfc/src/ndef/nfc_le_oob_rec.h b/libraries/nfc/src/ndef/nfc_le_oob_rec.h new file mode 100644 index 000000000..85bbc8a2b --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_le_oob_rec.h @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_LE_OOB_REC_H__ +#define NFC_LE_OOB_REC_H__ + +/**@file + * + * @defgroup nfc_le_oob_rec LE OOB records + * @{ + * @ingroup nfc_ble_pair_msg + * + * @brief Generation of NFC NDEF LE OOB records for NDEF messages. + * + */ + +#include +#include "nfc_ndef_record.h" +#include "nfc_ble_oob_advdata.h" +#include "nfc_ble_pair_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for constructing the payload for a Bluetooth Carrier Configuration LE record. + * + * This function encodes the record payload according to the BLE AD structure. It implements + * an API compatible with @ref p_payload_constructor_t + * + * @param[in] p_ble_advdata Pointer to the description of the payload. + * @param[out] p_buff Pointer to payload destination. If NULL, function will + * calculate the expected size of the record payload. + * + * @param[in,out] p_len Size of available memory to write as input. Size of generated + * payload as output. + * + * @retval NRF_SUCCESS If the record payload was encoded successfully. + * @retval Other If the record payload encoding failed. + */ +ret_code_t nfc_le_oob_payload_constructor(ble_advdata_t * p_ble_advdata, + uint8_t * p_buff, + uint32_t * p_len); + +/** @brief Macro for generating a description of an NFC NDEF Bluetooth Carrier Configuration LE Record. + * + * This macro declares and initializes an instance of an NFC NDEF record description + * for a Bluetooth Carrier Configuration LE record. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF message encoding (see @ref nfc_ble_simplified_le_oob_msg_encode) + * must be done in the same variable scope. + * + * @param[in] NAME Name for accessing record descriptor. + * @param[in] PAYLOAD_ID NDEF record header Payload ID field (Limited to one byte). + * If 0, no ID is present in the record description. + * @param[in] P_BLE_ADVDATA Pointer to the encoded BLE advertising data structure. This + * data is used to create the record payload. + */ +#define NFC_NDEF_LE_OOB_RECORD_DESC_DEF(NAME, \ + PAYLOAD_ID, \ + P_BLE_ADVDATA) \ + uint8_t NAME##_ndef_le_oob_record_id = (PAYLOAD_ID); \ + uint8_t NAME##_ndef_le_oob_record_id_length = ((PAYLOAD_ID) != 0) ? 1 : 0; \ + NFC_NDEF_GENERIC_RECORD_DESC_DEF( NAME, \ + TNF_MEDIA_TYPE, \ + &NAME##_ndef_le_oob_record_id, \ + NAME##_ndef_le_oob_record_id_length, \ + (le_oob_rec_type_field), \ + sizeof(le_oob_rec_type_field), \ + nfc_le_oob_payload_constructor, \ + (P_BLE_ADVDATA)) \ + +/** + * @brief Macro for accessing the NFC NDEF Bluetooth Carrier Configuration LE record descriptor + * instance that was created with @ref NFC_NDEF_LE_OOB_RECORD_DESC_DEF. + */ +#define NFC_NDEF_LE_OOB_RECORD_DESC(NAME) NFC_NDEF_GENERIC_RECORD_DESC(NAME) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_LE_OOB_REC_H__ diff --git a/libraries/nfc/src/ndef/nfc_le_oob_rec_parser.c b/libraries/nfc/src/ndef/nfc_le_oob_rec_parser.c new file mode 100644 index 000000000..cf9a80102 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_le_oob_rec_parser.c @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_LE_OOB_REC_PARSER) +#include "nfc_le_oob_rec_parser.h" +#include "util/sdk_errors.h" + +/** + * @brief Function for parsing LE OOB record payload. + * + * This function parses LE OOB record payload and extracts BLE OOB Advertising data structure. + * + * @param[in] p_buff Pointer to the record payload. + * @param[in] p_len Pointer to the record payload length. + * @param[in,out] p_nfc_ble_oob_pairing_data Pointer to the structure that will be used to hold + * parsed data. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval Other An error code that might have been returned by + * @ref nfc_ble_oob_advdata_parse function. + */ +static ret_code_t nfc_le_oob_payload_parse(uint8_t * p_buff, + uint32_t * const p_len, + nfc_ble_oob_pairing_data_t * const p_nfc_ble_oob_pairing_data) +{ + ret_code_t err_code = nfc_ble_oob_advdata_parse(p_buff, + *p_len, + p_nfc_ble_oob_pairing_data); + return err_code; +} + +ret_code_t nfc_le_oob_rec_parse(nfc_ndef_record_desc_t const * const p_rec_desc, + nfc_ble_oob_pairing_data_t * const p_nfc_ble_oob_pairing_data) +{ + ret_code_t err_code; + + if (p_rec_desc->tnf != TNF_MEDIA_TYPE) + { + return NRF_ERROR_INVALID_DATA; + } + + if (p_rec_desc->type_length != sizeof(le_oob_rec_type_field)) + { + return NRF_ERROR_INVALID_DATA; + } + + if (memcmp(p_rec_desc->p_type, le_oob_rec_type_field, sizeof(le_oob_rec_type_field)) != 0) + { + return NRF_ERROR_INVALID_DATA; + } + + if (p_rec_desc->payload_constructor != (p_payload_constructor_t) nfc_ndef_bin_payload_memcopy) + { + return NRF_ERROR_NOT_SUPPORTED; + } + + uint8_t const * p_payload = ((nfc_ndef_bin_payload_desc_t*)(p_rec_desc->p_payload_descriptor))->p_payload; + uint32_t payload_lenght = ((nfc_ndef_bin_payload_desc_t*)(p_rec_desc->p_payload_descriptor))->payload_length; + + err_code = nfc_le_oob_payload_parse((uint8_t *) p_payload, + &payload_lenght, + p_nfc_ble_oob_pairing_data); + + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_LE_OOB_REC_PARSER) diff --git a/libraries/nfc/src/ndef/nfc_le_oob_rec_parser.h b/libraries/nfc/src/ndef/nfc_le_oob_rec_parser.h new file mode 100644 index 000000000..466924eb2 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_le_oob_rec_parser.h @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup nfc_le_oob_rec_parser LE OOB records parser + * @{ + * @ingroup nfc_ble_pair_msg + * @brief Functions for parsing and decoding LE OOB records. + */ + +#ifndef __NFC_LE_OOB_REC_PARSER_H__ +#define __NFC_LE_OOB_REC_PARSER_H__ + +#include "nfc_ndef_record.h" +#include "nfc_ble_oob_advdata_parser.h" +#include "nfc_ble_pair_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for parsing general record description as LE OOB record. + * + * This function checks if record description matches the LE OOB record pattern and extracts BLE + * OOB Advertising data structure. It is required for the record description to use binary payload + * descriptor. + * + * @param[in] p_rec_desc Pointer to the record descriptor. + * @param[in,out] p_nfc_ble_oob_pairing_data Pointer to the structure that will be used to hold + * parsed data. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_INVALID_DATA If the NDEF record type or TNF is incorrect. + * @retval NRF_ERROR_NOT_SUPPORTED If the payload descriptor is not binary. + * @retval Other An error code that might have been returned by + * @ref nfc_ble_oob_advdata_parse function. + */ +ret_code_t nfc_le_oob_rec_parse(nfc_ndef_record_desc_t const * const p_rec_desc, + nfc_ble_oob_pairing_data_t * const p_nfc_ble_oob_pairing_data); + +#ifdef __cplusplus +} +#endif + +#endif // __NFC_LE_OOB_REC_PARSER_H__ + +/** @} */ diff --git a/libraries/nfc/src/ndef/nfc_ndef_msg.c b/libraries/nfc/src/ndef/nfc_ndef_msg.c new file mode 100644 index 000000000..4fc6df89e --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_msg.c @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_MSG) + +#include "util/app_util.h" +#include "nfc_ndef_msg.h" +#include "util/nordic_common.h" +#include "nrf.h" + + +/** + * @brief Resolve the value of record location flags of the NFC NDEF record within an NFC NDEF message. + */ +__STATIC_INLINE nfc_ndef_record_location_t record_location_get(uint32_t index, + uint32_t record_count) +{ + nfc_ndef_record_location_t record_location; + + if (index == 0) + { + if (record_count == 1) + { + record_location = NDEF_LONE_RECORD; + } + else + { + record_location = NDEF_FIRST_RECORD; + } + } + else if (record_count == index + 1) + { + record_location = NDEF_LAST_RECORD; + } + else + { + record_location = NDEF_MIDDLE_RECORD; + } + + return record_location; +} + + +ret_code_t nfc_ndef_msg_encode(nfc_ndef_msg_desc_t const * p_ndef_msg_desc, + uint8_t * p_msg_buffer, + uint32_t * const p_msg_len) +{ + nfc_ndef_record_location_t record_location; + uint32_t temp_len; + uint32_t i; + uint32_t err_code; + + uint32_t sum_of_len = 0; + + if ((p_ndef_msg_desc == NULL) || p_msg_len == NULL) + { + return NRF_ERROR_NULL; + } + + nfc_ndef_record_desc_t * * pp_record_rec_desc = p_ndef_msg_desc->pp_record; + + if (p_ndef_msg_desc->pp_record == NULL) + { + return NRF_ERROR_NULL; + } + +#if NFC_NDEF_MSG_TAG_TYPE == TYPE_4_TAG + uint8_t * p_root_msg_buffer = p_msg_buffer; + + if (p_msg_buffer != NULL) + { + if (*p_msg_len < NLEN_FIELD_SIZE) + { + return NRF_ERROR_NO_MEM; + } + + p_msg_buffer += NLEN_FIELD_SIZE; + } + sum_of_len += NLEN_FIELD_SIZE; +#endif + + for (i = 0; i < p_ndef_msg_desc->record_count; i++) + { + record_location = record_location_get(i, p_ndef_msg_desc->record_count); + + temp_len = *p_msg_len - sum_of_len; + + err_code = nfc_ndef_record_encode(*pp_record_rec_desc, + record_location, + p_msg_buffer, + &temp_len); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + sum_of_len += temp_len; + if (p_msg_buffer != NULL) + { + p_msg_buffer += temp_len; + } + + /* next record */ + pp_record_rec_desc++; + } + +#if NFC_NDEF_MSG_TAG_TYPE == TYPE_4_TAG + if (p_msg_buffer != NULL) + { + if (sum_of_len - NLEN_FIELD_SIZE > UINT16_MAX) + { + return NRF_ERROR_NOT_SUPPORTED; + } + + UNUSED_RETURN_VALUE(uint16_big_encode(sum_of_len - NLEN_FIELD_SIZE, p_root_msg_buffer)); + } +#endif + + *p_msg_len = sum_of_len; + + return NRF_SUCCESS; +} + + +void nfc_ndef_msg_clear(nfc_ndef_msg_desc_t * p_msg) +{ + p_msg->record_count = 0; +} + + +ret_code_t nfc_ndef_msg_record_add(nfc_ndef_msg_desc_t * const p_msg, + nfc_ndef_record_desc_t * const p_record) +{ + if (p_msg->record_count >= p_msg->max_record_count) + { + return NRF_ERROR_NO_MEM; + } + + p_msg->pp_record[p_msg->record_count] = p_record; + p_msg->record_count++; + + return NRF_SUCCESS; +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_MSG) diff --git a/libraries/nfc/src/ndef/nfc_ndef_msg.h b/libraries/nfc/src/ndef/nfc_ndef_msg.h new file mode 100644 index 000000000..8850bf387 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_msg.h @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_NDEF_MSG_H__ +#define NFC_NDEF_MSG_H__ + +#include "nfc_ndef_record.h" + +#ifdef __cplusplus +extern "C" { +#endif +/**@file + * + * @defgroup nfc_ndef_msg Custom NDEF messages + * @{ + * @ingroup nfc_modules + * + * @brief Generation of NFC NDEF messages for the NFC tag. + * + */ + +#define TYPE_4_TAG 4U ///< Type 4 Tag identifier. +#define NLEN_FIELD_SIZE 2U ///< Size of NLEN field, used to encode NDEF message for Type 4 Tag. + + /** + * @brief NDEF message descriptor. + */ + typedef struct { + nfc_ndef_record_desc_t ** pp_record; ///< Pointer to an array of pointers to NDEF record descriptors. + uint32_t max_record_count; ///< Number of elements in the allocated pp_record array, which defines the maximum number of records within the NDEF message. + uint32_t record_count; ///< Number of records in the NDEF message. + } nfc_ndef_msg_desc_t; + + /** + * @brief Function for encoding an NDEF message. + * + * This function encodes an NDEF message according to the provided message descriptor. + * + * @note The way of encoding an NDEF message may vary depending on tag's platform, which + * can be chosen with @ref NFC_NDEF_MSG_TAG_TYPE in @c sdk_config.h. + * + * @param[in] p_ndef_msg_desc Pointer to the message descriptor. + * @param[out] p_msg_buffer Pointer to the message destination. If NULL, function will + * calculate the expected size of the message. + * @param[in,out] p_msg_len Size of the available memory for the message as input. Size of + * the generated message as output. + * + * @return Return value from @ref nfc_ndef_record_encode. + */ +ret_code_t nfc_ndef_msg_encode(nfc_ndef_msg_desc_t const * p_ndef_msg_desc, + uint8_t * p_msg_buffer, + uint32_t * const p_msg_len); + +/** + * @brief Function for clearing an NDEF message. + * + * This function clears an NDEF message descriptor, thus empties the NDEF message. + * + * @param[in,out] p_msg Pointer to the message descriptor. + */ +void nfc_ndef_msg_clear( nfc_ndef_msg_desc_t * p_msg); + +/** + * @brief Function for adding a record to an NDEF message. + * + * @param[in] p_record Pointer to the record descriptor. + * @param[in,out] p_msg Pointer to the message descriptor. + * + * @retval NRF_SUCCESS If the record was added successfully. + * @retval NRF_ERROR_NO_MEM If the message already contains the maximum number of records and the operation is not allowed. + */ +ret_code_t nfc_ndef_msg_record_add(nfc_ndef_msg_desc_t * const p_msg, + nfc_ndef_record_desc_t * const p_record); + + +/**@brief Macro for creating and initializing an NFC NDEF message descriptor. + * + * This macro creates and initializes an instance of type @ref nfc_ndef_msg_desc_t + * and an array of pointers to record descriptors (@ref nfc_ndef_record_desc_t) used + * by the message. + * + * Use the macro @ref NFC_NDEF_MSG to access the NDEF message descriptor instance. + * + * @note The message descriptor is declared as automatic variable, which implies that + * the NDEF message encoding must be done in the same variable scope. + * + * @param[in] NAME Name of the related instance. + * @param[in] MAX_RECORD_CNT Maximal count of records in the message. + */ +#define NFC_NDEF_MSG_DEF(NAME, MAX_RECORD_CNT) \ + nfc_ndef_record_desc_t * NAME##_nfc_ndef_p_record_desc_array[MAX_RECORD_CNT]; \ + nfc_ndef_msg_desc_t NAME##_nfc_ndef_msg_desc = \ + { \ + .pp_record = NAME##_nfc_ndef_p_record_desc_array, \ + .max_record_count = MAX_RECORD_CNT, \ + .record_count = 0 \ + } + +/** @brief Macro for accessing the NFC NDEF message descriptor instance + * that you created with @ref NFC_NDEF_MSG_DEF. + */ +#define NFC_NDEF_MSG(NAME) (NAME##_nfc_ndef_msg_desc) + +/** + * @brief Macro for creating and initializing an NFC NDEF record descriptor with an encapsulated NDEF message. + + * This macro creates and initializes a static instance of type + * @ref nfc_ndef_record_desc_t that contains an encapsulated NDEF message as + * payload. @ref nfc_ndef_msg_encode is used as payload constructor to encode + * the message. The encoded message is then used as payload for the record. + * + * Use the macro @ref NFC_NDEF_NESTED_NDEF_MSG_RECORD to access the NDEF record descriptor instance. + * + * @note The message descriptor is declared as automatic variable, which implies that + * the NDEF message encoding must be done in the same variable scope. + * + * @param[in] NAME Name of the created record descriptor instance. + * @param[in] TNF Type Name Format (TNF) value for the record. + * @param[in] P_ID Pointer to the ID string. + * @param[in] ID_LEN Length of the ID string. + * @param[in] P_TYPE Pointer to the type string. + * @param[in] TYPE_LEN Length of the type string. + * @param[in] P_NESTED_MESSAGE Pointer to the message descriptor to encapsulate + * as the record's payload. + */ +#define NFC_NDEF_NESTED_NDEF_MSG_RECORD_DEF( NAME, \ + TNF, \ + P_ID, \ + ID_LEN, \ + P_TYPE, \ + TYPE_LEN, \ + P_NESTED_MESSAGE ) \ + nfc_ndef_record_desc_t NAME##_ndef_record_nested_desc = \ + { \ + .tnf = TNF, \ + \ + .id_length = ID_LEN, \ + .p_id = P_ID, \ + \ + .type_length = TYPE_LEN, \ + .p_type = P_TYPE, \ + \ + .payload_constructor = (p_payload_constructor_t)(nfc_ndef_msg_encode), \ + .p_payload_descriptor = (void*) (P_NESTED_MESSAGE) \ + } + +/** @brief Macro for accessing the NFC NDEF record descriptor instance + * that you created with @ref NFC_NDEF_NESTED_NDEF_MSG_RECORD_DEF. + */ +#define NFC_NDEF_NESTED_NDEF_MSG_RECORD(NAME) (NAME##_ndef_record_nested_desc) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libraries/nfc/src/ndef/nfc_ndef_msg_parser.c b/libraries/nfc/src/ndef/nfc_ndef_msg_parser.c new file mode 100644 index 000000000..6ce0b6c7f --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_msg_parser.c @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_MSG_PARSER) + +#include "nfc_ndef_msg_parser.h" +#include "nrf_delay.h" + +#define NRF_LOG_MODULE_NAME nfc_ndef_msg_parser +#if NFC_NDEF_MSG_PARSER_LOG_ENABLED +#define NRF_LOG_LEVEL NFC_NDEF_MSG_PARSER_LOG_LEVEL +#define NRF_LOG_INFO_COLOR NFC_NDEF_MSG_PARSER_INFO_COLOR +#include "util/nrf_log.h" +NRF_LOG_MODULE_REGISTER(); +#else // NFC_NDEF_MSG_PARSER_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#include "util/nrf_log.h" +#endif // NFC_NDEF_MSG_PARSER_LOG_ENABLED + +ret_code_t ndef_msg_parser(uint8_t * const p_result_buf, + uint32_t * const p_result_buf_len, + uint8_t * const p_nfc_data, + uint32_t * const p_nfc_data_len) +{ + ret_code_t ret_code; + nfc_ndef_parser_memo_desc_t parser_memory_helper; + + ret_code = ndef_parser_memo_resolve(p_result_buf, + p_result_buf_len, + &parser_memory_helper); + + if (ret_code != NRF_SUCCESS) + { + return ret_code; + } + + ret_code = internal_ndef_msg_parser(&parser_memory_helper, + p_nfc_data, + p_nfc_data_len); + + return ret_code; +} + + +void ndef_msg_printout(nfc_ndef_msg_desc_t * const p_msg_desc) +{ + uint32_t i; + + NRF_LOG_INFO("NDEF message contains %d record(s)", p_msg_desc->record_count); + + for (i = 0; i < p_msg_desc->record_count; i++) + { + ndef_record_printout(i, p_msg_desc->pp_record[i]); + } +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_MSG_PARSER) diff --git a/libraries/nfc/src/ndef/nfc_ndef_msg_parser.h b/libraries/nfc/src/ndef/nfc_ndef_msg_parser.h new file mode 100644 index 000000000..e3b8c2343 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_msg_parser.h @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_NDEF_MSG_PARSER_H__ +#define NFC_NDEF_MSG_PARSER_H__ + +/**@file + * + * @defgroup nfc_ndef_parser NDEF message parser + * @{ + * @ingroup nfc_modules + * + * @brief Parser for NFC NDEF messages and records. + * + * @defgroup nfc_ndef_msg_parser Parser for NDEF messages + * @{ + * @ingroup nfc_ndef_parser + * + * @brief Parser for NFC NDEF messages. + * + */ + +#include +#include "nfc_ndef_msg_parser_local.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Macro for calculating the memory size required for holding the + * description of a message that consists of a certain number of NDEF records. + * + * @param[in] max_count_of_records Maximum number of records to hold. + */ +#define NFC_NDEF_PARSER_REQIRED_MEMO_SIZE_CALC(max_count_of_records) \ + ((uint32_t)(max_count_of_records) <= 1) ? \ + (sizeof(parsed_ndef_msg_1_t) * (uint32_t)(max_count_of_records)) : \ + (sizeof(parsed_ndef_msg_1_t) + ((NFC_PARSER_M_DELTA) *((uint32_t)(max_count_of_records) - 1))) + +/** + * @brief Function for parsing NFC NDEF messages. + * + * This function parses NDEF messages using NDEF binary record descriptors. + * + * @param[out] p_result_buf Pointer to the buffer that will be used to hold + * the NDEF message descriptor. After parsing is completed successfully, the first address + * in the buffer is filled by the NDEF message descriptor + * (@ref nfc_ndef_msg_desc_t), which provides a full description of + * the parsed NDEF message. + * @param[in,out] p_result_buf_len As input: size of the buffer specified by @p p_result_buf. + * As output: size of the reserved (used) part of the buffer specified by + * @p p_result_buf. + * @param[in] p_nfc_data Pointer to the data to be parsed. + * @param[in,out] p_nfc_data_len As input: size of the NFC data in the @p p_nfc_data buffer. As output: size of the parsed message. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_NO_MEM If the provided buffer is too small to hold a one-record message or + * the buffer is too small to hold the actual result of the parsing. + * @retval NRF_ERROR_INVALID_LENGTH If the expected message length is bigger than the amount of the provided input data. + * @retval NRF_ERROR_INVALID_DATA If the message is not a valid NDEF message. + */ +ret_code_t ndef_msg_parser(uint8_t * const p_result_buf, + uint32_t * const p_result_buf_len, + uint8_t * const p_nfc_data, + uint32_t * const p_nfc_data_len); + +/** + * @brief Function for printing the parsed contents of an NDEF message. + * + * @param[in] p_msg_desc Pointer to the descriptor of the message that should be printed. + */ +void ndef_msg_printout(nfc_ndef_msg_desc_t * const p_msg_desc); + +/** + * @} + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif // NFC_NDEF_MSG_PARSER_H__ + + diff --git a/libraries/nfc/src/ndef/nfc_ndef_msg_parser_local.c b/libraries/nfc/src/ndef/nfc_ndef_msg_parser_local.c new file mode 100644 index 000000000..e621bca51 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_msg_parser_local.c @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_MSG_PARSER) + +#include "nfc_ndef_msg_parser_local.h" + +ret_code_t internal_ndef_msg_parser(nfc_ndef_parser_memo_desc_t * const p_parser_memo_desc, + uint8_t const * p_nfc_data, + uint32_t * const p_nfc_data_len) +{ + nfc_ndef_record_location_t record_location; + + ret_code_t ret_code; + + uint32_t nfc_data_left = *p_nfc_data_len; + uint32_t temp_nfc_data_len = 0; + + // want to modify -> use local copy + nfc_ndef_bin_payload_desc_t * p_bin_pay_desc = p_parser_memo_desc->p_bin_pay_desc; + nfc_ndef_record_desc_t * p_rec_desc = p_parser_memo_desc->p_rec_desc; + + + while (nfc_data_left > 0) + { + temp_nfc_data_len = nfc_data_left; + + ret_code = ndef_record_parser(p_bin_pay_desc, + p_rec_desc, + &record_location, + p_nfc_data, + &temp_nfc_data_len); + + if (ret_code != NRF_SUCCESS) + { + return ret_code; + } + + // verify the records location flags + if (p_parser_memo_desc->p_msg_desc->record_count == 0) + { + if ((record_location != NDEF_FIRST_RECORD) && (record_location != NDEF_LONE_RECORD)) + { + return NRF_ERROR_INVALID_DATA; + } + } + else + { + if ((record_location != NDEF_MIDDLE_RECORD) && (record_location != NDEF_LAST_RECORD)) + { + return NRF_ERROR_INVALID_DATA; + } + } + + ret_code = nfc_ndef_msg_record_add(p_parser_memo_desc->p_msg_desc, p_rec_desc); + + if (ret_code != NRF_SUCCESS) + { + return ret_code; + } + + nfc_data_left -= temp_nfc_data_len; + + if ((record_location == NDEF_LAST_RECORD) || (record_location == NDEF_LONE_RECORD)) + { + *p_nfc_data_len = *p_nfc_data_len - nfc_data_left; + return NRF_SUCCESS; + } + else + { + if (p_parser_memo_desc->p_msg_desc->record_count == + p_parser_memo_desc->p_msg_desc->max_record_count) + { + return NRF_ERROR_NO_MEM; + } + + p_nfc_data += temp_nfc_data_len; + p_bin_pay_desc++; + p_rec_desc++; + } + } + + return NRF_ERROR_INVALID_DATA; + +} + + +ret_code_t ndef_parser_memo_resolve(uint8_t * const p_result_buf, + uint32_t * const p_result_buf_len, + nfc_ndef_parser_memo_desc_t * const p_parser_memo_desc) +{ + + uint32_t max_rec_num; + uint32_t memory_last; + uint8_t * p_end; + nfc_ndef_record_desc_t * * pp_record_desc_array; + + if (*p_result_buf_len < sizeof(parsed_ndef_msg_1_t)) + { + return NRF_ERROR_NO_MEM; + } + + memory_last = (*p_result_buf_len) - sizeof(parsed_ndef_msg_1_t); + max_rec_num = (memory_last / (NFC_PARSER_M_DELTA)) + 1; + + p_parser_memo_desc->p_msg_desc = (nfc_ndef_msg_desc_t *) p_result_buf; + pp_record_desc_array = + (nfc_ndef_record_desc_t * *) &p_parser_memo_desc->p_msg_desc[1]; + p_parser_memo_desc->p_bin_pay_desc = + (nfc_ndef_bin_payload_desc_t *) &pp_record_desc_array[max_rec_num]; + p_parser_memo_desc->p_rec_desc = + (nfc_ndef_record_desc_t *) &p_parser_memo_desc->p_bin_pay_desc[max_rec_num]; + + // initialize message description + p_parser_memo_desc->p_msg_desc->pp_record = pp_record_desc_array; + p_parser_memo_desc->p_msg_desc->max_record_count = max_rec_num; + p_parser_memo_desc->p_msg_desc->record_count = 0; + + p_end = (uint8_t *) &p_parser_memo_desc->p_rec_desc[max_rec_num]; + + *p_result_buf_len = p_end - p_result_buf; + + return NRF_SUCCESS; +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_MSG_PARSER) diff --git a/libraries/nfc/src/ndef/nfc_ndef_msg_parser_local.h b/libraries/nfc/src/ndef/nfc_ndef_msg_parser_local.h new file mode 100644 index 000000000..33111b913 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_msg_parser_local.h @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_NDEF_MSG_PARSER_LOCAL_H__ +#define NFC_NDEF_MSG_PARSER_LOCAL_H__ + +/**@file + * + * @defgroup nfc_ndef_msg_parser_local NDEF message parser (internal) + * @{ + * @ingroup nfc_ndef_msg_parser + * + * @brief Internal part of the parser for NFC NDEF messages. + * + */ + +#include +#include "nfc_ndef_msg.h" +#include "nfc_ndef_record_parser.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Type for holding descriptors that are used by the NDEF parser. + */ +typedef struct +{ + nfc_ndef_msg_desc_t * p_msg_desc; ///< Pointer to the message descriptor. + nfc_ndef_bin_payload_desc_t * p_bin_pay_desc; ///< Pointer to the array of binary payload descriptors. + nfc_ndef_record_desc_t * p_rec_desc; ///< Pointer to the array of record descriptors. +} nfc_ndef_parser_memo_desc_t; + +/** + * @brief Memory allocated for a one-record message. + */ +typedef struct +{ + nfc_ndef_msg_desc_t msg_desc; + nfc_ndef_record_desc_t * p_record_desc_array[1]; + nfc_ndef_bin_payload_desc_t bin_pay_desc[1]; + nfc_ndef_record_desc_t rec_desc[1]; +} parsed_ndef_msg_1_t; + +/** + * @brief Memory allocated for a two-record message. + */ +typedef struct +{ + nfc_ndef_msg_desc_t msg_desc; + nfc_ndef_record_desc_t * p_record_desc_array[2]; + nfc_ndef_bin_payload_desc_t bin_pay_desc[2]; + nfc_ndef_record_desc_t rec_desc[2]; +} parsed_ndef_msg_2_t; + +/** + * @brief Amount of memory that is required per record in addition to the memory allocated for the message descriptor. + */ +#define NFC_PARSER_M_DELTA (sizeof(parsed_ndef_msg_2_t) - sizeof(parsed_ndef_msg_1_t)) + + +/** + * @brief Function for resolving data instances in the provided buffer according + * to requirements of the function @ref internal_ndef_msg_parser. + * + * This internal function distributes the provided memory between certain data instances that are required + * by @ref internal_ndef_msg_parser. + * + * This function should not be used directly. + * + * @param[in] p_result_buf Pointer to the buffer that will be used to allocate + * data instances. + * @param[in,out] p_result_buf_len As input: size of the buffer specified by @p p_result_buf. + * As output: size of the reserved (used) part of the buffer specified by + * @p p_result_buf. + * @param[out] p_parser_memo_desc Pointer to the structure for holding descriptors of the allocated data + * instances. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_NO_MEM If the provided buffer is too small to hold a one-record message. + */ +ret_code_t ndef_parser_memo_resolve(uint8_t * const p_result_buf, + uint32_t * const p_result_buf_len, + nfc_ndef_parser_memo_desc_t * const p_parser_memo_desc); + + +/** + * @brief Function for parsing NFC NDEF messages. + * + * This internal function parses NDEF messages into certain data instances. + * + * This function should not be used directly. + * + * @param[in,out] p_parser_memo_desc Pointer to the structure that holds descriptors of the allocated data + * instances for the parser. This structure contains the following fields: @n + * .p_msg_desc Pointer to the message descriptor that will + * be filled with parsed data. @n + * .p_bin_pay_desc Pointer to the array of binary payload + * descriptors that will be filled with parsed + * data. @n + * .p_rec_desc Pointer to the array of record descriptors + * that will be filled with parsed data. @n + * The arrays specified by @p .p_bin_pay_desc and @p .p_rec_desc must not + * contain more elements than the message descriptor + * specified by \p .p_msg_desc can hold. + * + * @param[in] p_nfc_data Pointer to the data to be parsed. + * @param[in,out] p_nfc_data_len As input: size of the NFC data in the @p p_nfc_data buffer. + * As output: size of the parsed message. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_INVALID_LENGTH If the expected message length is bigger than the amount of provided input data. + * @retval NRF_ERROR_INVALID_DATA If the message is not a valid NDEF message. + * @retval NRF_ERROR_NO_MEM If the provided memory resources are too small to hold the parsing result. + */ +ret_code_t internal_ndef_msg_parser(nfc_ndef_parser_memo_desc_t * const p_parser_memo_desc, + uint8_t const * p_nfc_data, + uint32_t * const p_nfc_data_len); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif // NFC_NDEF_MSG_PARSER_LOCAL_H__ diff --git a/libraries/nfc/src/ndef/nfc_ndef_record.c b/libraries/nfc/src/ndef/nfc_ndef_record.c new file mode 100644 index 000000000..2c6a54df2 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_record.c @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_config.h" +#include "util/nordic_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_RECORD) + +#include +#include "nfc_ndef_record.h" +#include "util/app_util.h" +#include "nrf.h" + + +/* Sum of sizes of fields: TNF-flags, Type Length, Payload Length in long NDEF record. */ +#define NDEF_RECORD_BASE_LONG_SIZE (2 + NDEF_RECORD_PAYLOAD_LEN_LONG_SIZE) + +__STATIC_INLINE uint32_t record_header_size_calc(nfc_ndef_record_desc_t const * p_ndef_record_desc) +{ + uint32_t len = NDEF_RECORD_BASE_LONG_SIZE; + + len += p_ndef_record_desc->id_length + p_ndef_record_desc->type_length; + + if (p_ndef_record_desc->id_length > 0) + { + len++; + } + + return len; +} + + +ret_code_t nfc_ndef_record_encode(nfc_ndef_record_desc_t const * p_ndef_record_desc, + nfc_ndef_record_location_t record_location, + uint8_t * p_record_buffer, + uint32_t * p_record_len) +{ + uint8_t * p_flags; // use as pointer to TNF + flags field + uint8_t * p_payload_len = NULL; // use as pointer to payload length field + uint32_t record_payload_len; + + if (p_ndef_record_desc == NULL) + { + return NRF_ERROR_NULL; + } + + // count record length without payload + uint32_t record_header_len = record_header_size_calc(p_ndef_record_desc); + uint32_t err_code = NRF_SUCCESS; + + if (p_record_buffer != NULL) + { + /* verify location range */ + if ((record_location & (~NDEF_RECORD_LOCATION_MASK)) != 0x00) + { + return NRF_ERROR_INVALID_PARAM; + } + + /* verify if there is enough available memory */ + if (record_header_len > *p_record_len) + { + return NRF_ERROR_NO_MEM; + } + + p_flags = p_record_buffer; + p_record_buffer++; + + // set location bits and clear other bits in 1st byte. + *p_flags = record_location; + + *p_flags |= p_ndef_record_desc->tnf; + + /* TYPE LENGTH */ + *(p_record_buffer++) = p_ndef_record_desc->type_length; + + // use always long record and remember payload len field memory offset. + p_payload_len = p_record_buffer; + p_record_buffer += NDEF_RECORD_PAYLOAD_LEN_LONG_SIZE; + + /* ID LENGTH - option */ + if (p_ndef_record_desc->id_length > 0) + { + *(p_record_buffer++) = p_ndef_record_desc->id_length; + + /* IL flag */ + *p_flags |= NDEF_RECORD_IL_MASK; + } + + /* TYPE */ + memcpy(p_record_buffer, p_ndef_record_desc->p_type, p_ndef_record_desc->type_length); + p_record_buffer += p_ndef_record_desc->type_length; + + /* ID */ + if (p_ndef_record_desc->id_length > 0) + { + memcpy(p_record_buffer, p_ndef_record_desc->p_id, p_ndef_record_desc->id_length); + p_record_buffer += p_ndef_record_desc->id_length; + } + + // count how much memory is left in record buffer for payload field. + record_payload_len = (*p_record_len - record_header_len); + } + + /* PAYLOAD */ + if (p_ndef_record_desc->payload_constructor != NULL) + { + err_code = + p_ndef_record_desc->payload_constructor(p_ndef_record_desc->p_payload_descriptor, + p_record_buffer, + &record_payload_len); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + else + { + return NRF_ERROR_NULL; + } + + if (p_record_buffer != NULL) + { + /* PAYLOAD LENGTH */ + (void) uint32_big_encode(record_payload_len, p_payload_len); + } + + *p_record_len = record_header_len + record_payload_len; + + return NRF_SUCCESS; +} + + +ret_code_t nfc_ndef_bin_payload_memcopy(nfc_ndef_bin_payload_desc_t * p_payload_descriptor, + uint8_t * p_buffer, + uint32_t * p_len) +{ + + if (p_buffer != NULL) + { + if ( *p_len < p_payload_descriptor->payload_length) + { + return NRF_ERROR_NO_MEM; + } + + memcpy(p_buffer, + p_payload_descriptor->p_payload, + p_payload_descriptor->payload_length); + } + + *p_len = p_payload_descriptor->payload_length; + + return NRF_SUCCESS; +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_RECORD) diff --git a/libraries/nfc/src/ndef/nfc_ndef_record.h b/libraries/nfc/src/ndef/nfc_ndef_record.h new file mode 100644 index 000000000..76480c183 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_record.h @@ -0,0 +1,311 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_NDEF_RECORD_H__ +#define NFC_NDEF_RECORD_H__ + +#include +#include +#include "compiler_abstraction.h" +#include "util/sdk_errors.h" +#include "nrf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @defgroup nfc_ndef_record Custom NDEF records + * @{ + * @ingroup nfc_ndef_msg + * + * @brief Generation of NFC NDEF records for NFC messages. + * + */ + + +#define NDEF_RECORD_IL_MASK 0x08 ///< Mask of the ID field presence bit in the flags byte of an NDEF record. +#define NDEF_RECORD_TNF_MASK 0x07 ///< Mask of the TNF value field in the first byte of an NDEF record. +#define NDEF_RECORD_SR_MASK 0x10 ///< Mask of the SR flag. If set, this flag indicates that the PAYLOAD_LENGTH field has a size of 1 byte. Otherwise, PAYLOAD_LENGTH has 4 bytes. +#define NDEF_RECORD_PAYLOAD_LEN_LONG_SIZE 4 ///< Size of the Payload Length field in a long NDEF record. +#define NDEF_RECORD_PAYLOAD_LEN_SHORT_SIZE 1 ///< Size of the Payload Length field in a short NDEF record. +#define NDEF_RECORD_ID_LEN_SIZE 1 ///< Size of the ID Length field in an NDEF record. + + +/** + * @brief Payload constructor type. + + * A payload constructor is a function for constructing the payload of an NDEF + * record. + * + * @param[in] p_payload_descriptor Pointer to the input data for the constructor. + * @param[out] p_buffer Pointer to the payload destination. If NULL, function will + * calculate the expected size of the record payload. + * + * @param[in,out] p_len Size of the available memory to write as input. Size of the generated + * record payload as output. The implementation must check if the payload + * will fit in the provided buffer. This must be checked by the caller function. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_xxx If an error occurred. + */ +typedef ret_code_t (* p_payload_constructor_t)(void * p_payload_descriptor, + uint8_t * p_buffer, + uint32_t * p_len); + + +/** + * @brief Type Name Format (TNF) Field Values. + * + * Values to specify the TNF of a record. + */ +typedef enum +{ + TNF_EMPTY = 0x00, ///< The value indicates that there is no type or payload associated with this record. + TNF_WELL_KNOWN = 0x01, ///< NFC Forum well-known type [NFC RTD]. + TNF_MEDIA_TYPE = 0x02, ///< Media-type as defined in RFC 2046 [RFC 2046]. + TNF_ABSOLUTE_URI = 0x03, ///< Absolute URI as defined in RFC 3986 [RFC 3986]. + TNF_EXTERNAL_TYPE = 0x04, ///< NFC Forum external type [NFC RTD]. + TNF_UNKNOWN_TYPE = 0x05, ///< The value indicates that there is no type associated with this record. + TNF_UNCHANGED = 0x06, ///< The value is used for the record chunks used in chunked payload. + TNF_RESERVED = 0x07, ///< The value is reserved for future use. +} nfc_ndef_record_tnf_t; + + +/** + * @brief NDEF record descriptor. + */ +typedef struct +{ + nfc_ndef_record_tnf_t tnf; ///< Value of the Type Name Format (TNF) field. + + uint8_t id_length; ///< Length of the ID field. If 0, a record format without ID field is assumed. + uint8_t const * p_id; ///< Pointer to the ID field data. Not relevant if id_length is 0. + + uint8_t type_length; ///< Length of the type field. + uint8_t const * p_type; ///< Pointer to the type field data. Not relevant if type_length is 0. + + p_payload_constructor_t payload_constructor; ///< Pointer to the payload constructor function. + void * p_payload_descriptor; ///< Pointer to the data for the payload constructor function. + +} nfc_ndef_record_desc_t; + +/** + * @brief Record position within the NDEF message. + * + * Values to specify the location of a record within the NDEF message. + */ +typedef enum +{ + NDEF_FIRST_RECORD = 0x80, ///< First record. + NDEF_MIDDLE_RECORD = 0x00, ///< Middle record. + NDEF_LAST_RECORD = 0x40, ///< Last record. + NDEF_LONE_RECORD = 0xC0 ///< Only one record in the message. +} nfc_ndef_record_location_t; + +#define NDEF_RECORD_LOCATION_MASK (NDEF_LONE_RECORD) ///< Mask of the Record Location bits in the NDEF record's flags byte. + +/** + * @brief Binary data descriptor containing the payload for the record. + */ +typedef struct +{ + uint8_t const * p_payload; ///< Pointer to the buffer with the data. + uint32_t payload_length; ///< Length of data in bytes. +} nfc_ndef_bin_payload_desc_t; + +/** + * @brief Macro for creating and initializing an NFC NDEF record descriptor for a generic record. + * + * This macro creates and initializes an instance of type @ref nfc_ndef_record_desc_t. + * + * Use the macro @ref NFC_NDEF_GENERIC_RECORD_DESC to access the NDEF record descriptor instance. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF record encoding must be done in the same variable scope. + * + * @param[in] NAME Name of the created descriptor instance. + * @param[in] TNF Type Name Format (TNF) value for the record. + * @param[in] P_ID Pointer to the ID string. + * @param[in] ID_LEN Length of the ID string. + * @param[in] P_TYPE Pointer to the type string. + * @param[in] TYPE_LEN Length of the type string. + * @param[in] P_PAYLOAD_CONSTRUCTOR Pointer to the payload constructor function. + * The constructor must be of type @ref p_payload_constructor_t. + * @param[in] P_PAYLOAD_DESCRIPTOR Pointer to the data for the payload constructor. + */ +#define NFC_NDEF_GENERIC_RECORD_DESC_DEF(NAME, \ + TNF, \ + P_ID, \ + ID_LEN, \ + P_TYPE, \ + TYPE_LEN, \ + P_PAYLOAD_CONSTRUCTOR, \ + P_PAYLOAD_DESCRIPTOR) \ + nfc_ndef_record_desc_t NAME##_ndef_generic_record_desc = \ + { \ + .tnf = TNF, \ + \ + .id_length = ID_LEN, \ + .p_id = P_ID, \ + \ + .type_length = TYPE_LEN, \ + .p_type = P_TYPE, \ + \ + .payload_constructor = (p_payload_constructor_t)P_PAYLOAD_CONSTRUCTOR, \ + .p_payload_descriptor = (void *) P_PAYLOAD_DESCRIPTOR \ + } + + +/** @brief Macro for accessing the NFC NDEF record descriptor instance + * that you created with @ref NFC_NDEF_GENERIC_RECORD_DESC_DEF. + */ +#define NFC_NDEF_GENERIC_RECORD_DESC(NAME) (NAME##_ndef_generic_record_desc) + +/** + * @brief Macro for creating and initializing an NFC NDEF record descriptor for a record with + * binary payload. + * + * This macro creates and initializes a static instance of type @ref nfc_ndef_record_desc_t and a binary data descriptor containing the payload data. + * + * Use the macro @ref NFC_NDEF_RECORD_BIN_DATA to access the NDEF record descriptor instance. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF record encoding must be done in the same variable scope. + * + * @param[in] NAME Name of the created descriptor instance. + * @param[in] TNF Type Name Format (TNF) value for the record. + * @param[in] P_ID Pointer to the ID string. + * @param[in] ID_LEN Length of the ID string. + * @param[in] P_TYPE Pointer to the type string. + * @param[in] TYPE_LEN Length of the type string. + * @param[in] P_PAYLOAD Pointer to the payload data that will be copied to the payload field. + * @param[in] PAYLOAD_LEN Length of the payload. + */ +#define NFC_NDEF_RECORD_BIN_DATA_DEF(NAME, \ + TNF, \ + P_ID, ID_LEN, \ + P_TYPE, \ + TYPE_LEN, \ + P_PAYLOAD, \ + PAYLOAD_LEN) \ + nfc_ndef_bin_payload_desc_t NAME##_nfc_ndef_bin_payload_desc = \ + { \ + .p_payload = P_PAYLOAD, \ + .payload_length = PAYLOAD_LEN \ + }; \ + \ + nfc_ndef_record_desc_t NAME##_nfc_ndef_bin_record_desc = \ + { \ + .tnf = TNF, \ + \ + .id_length = ID_LEN, \ + .p_id = P_ID, \ + \ + .type_length = TYPE_LEN, \ + .p_type = P_TYPE, \ + \ + .payload_constructor = (p_payload_constructor_t) nfc_ndef_bin_payload_memcopy, \ + .p_payload_descriptor = (void *) &NAME##_nfc_ndef_bin_payload_desc \ + } + + +/** @brief Macro for accessing the NFC NDEF record descriptor instance + * that you created with @ref NFC_NDEF_RECORD_BIN_DATA_DEF. + */ +#define NFC_NDEF_RECORD_BIN_DATA(NAME) (NAME##_nfc_ndef_bin_record_desc) + +/** @brief Macro for accessing the binary data descriptor that contains + * the payload of the record that you created with @ref NFC_NDEF_RECORD_BIN_DATA_DEF. + */ +#define NFC_NDEF_BIN_PAYLOAD_DESC(NAME) (NAME##_nfc_ndef_bin_payload_desc) + +/** + * @brief Function for encoding an NDEF record. + * + * This function encodes an NDEF record according to the provided record descriptor. + * + * @param[in] p_ndef_record_desc Pointer to the record descriptor. + * @param[in] record_location Location of the record within the NDEF message. + * @param[out] p_record_buffer Pointer to the record destination. If NULL, function will + * calculate the expected size of the record. + * @param[in,out] p_record_len Size of the available memory for the record as input. Size of the generated + * record as output. + * + * @retval NRF_SUCCESS If the record was encoded successfully. + * @retval NRF_ERROR_NO_MEM If the predicted record size is bigger than the provided buffer space. + * @retval NRF_ERROR_INVALID_PARAM If the location of the record is erroneous. + * @retval Other Other codes might be returned depending on the NDEF record payload constructor implementation. + */ +ret_code_t nfc_ndef_record_encode(nfc_ndef_record_desc_t const * p_ndef_record_desc, + nfc_ndef_record_location_t record_location, + uint8_t * p_record_buffer, + uint32_t * p_record_len); + +/** + * @brief Function for constructing the payload for an NFC NDEF record from binary data. + * + * This function copies data from a binary buffer to the payload field of the NFC NDEF record. + * + * @param[in] p_payload_descriptor Pointer to the descriptor of the binary data location and size. + * + * @param[out] p_buffer Pointer to the payload destination. If NULL, function will + * calculate the expected size of the record payload. + * @param[in,out] p_len Size of the available memory for the payload as input. Size of the copied payload + * as output. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_NO_MEM If the payload size is bigger than the provided buffer space. + */ +ret_code_t nfc_ndef_bin_payload_memcopy(nfc_ndef_bin_payload_desc_t * p_payload_descriptor, + uint8_t * p_buffer, + uint32_t * p_len); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif // NFC_NDEF_RECORD_H__ + diff --git a/libraries/nfc/src/ndef/nfc_ndef_record_parser.c b/libraries/nfc/src/ndef/nfc_ndef_record_parser.c new file mode 100644 index 000000000..5e47f7248 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_record_parser.c @@ -0,0 +1,219 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_RECORD_PARSER) + +#include +#include +#include "nfc_ndef_record_parser.h" +#include "util/app_util.h" +#include "util/nordic_common.h" +#include "nrf_delay.h" + +#define NRF_LOG_MODULE_NAME nfc_ndef_parser +#if NFC_NDEF_RECORD_PARSER_LOG_ENABLED +#define NRF_LOG_LEVEL NFC_NDEF_RECORD_PARSER_LOG_LEVEL +#define NRF_LOG_INFO_COLOR NFC_NDEF_RECORD_PARSER_INFO_COLOR +#include "util/nrf_log.h" +NRF_LOG_MODULE_REGISTER(); +#else // NFC_NDEF_RECORD_PARSER_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#include "util/nrf_log.h" +#endif // NFC_NDEF_RECORD_PARSER_LOG_ENABLED + +/* Sum of sizes of fields: TNF-flags, Type Length, Payload Length in short NDEF record. */ +#define NDEF_RECORD_BASE_LONG_SHORT (2 + NDEF_RECORD_PAYLOAD_LEN_SHORT_SIZE) + + +ret_code_t ndef_record_parser(nfc_ndef_bin_payload_desc_t * p_bin_pay_desc, + nfc_ndef_record_desc_t * p_rec_desc, + nfc_ndef_record_location_t * p_record_location, + uint8_t const * p_nfc_data, + uint32_t * p_nfc_data_len) +{ + uint32_t expected_rec_size = NDEF_RECORD_BASE_LONG_SHORT; + + if (expected_rec_size > *p_nfc_data_len) + { + return NRF_ERROR_INVALID_LENGTH; + } + + p_rec_desc->tnf = (nfc_ndef_record_tnf_t) ((*p_nfc_data) & NDEF_RECORD_TNF_MASK); + + /* An NDEF parser that receives an NDEF record with an unknown or unsupported TNF field value + SHOULD treat it as Unknown. See NFCForum-TS-NDEF_1.0 */ + if (p_rec_desc->tnf == TNF_RESERVED) + { + p_rec_desc->tnf = TNF_UNKNOWN_TYPE; + } + + *p_record_location = (nfc_ndef_record_location_t) ((*p_nfc_data) & NDEF_RECORD_LOCATION_MASK); + + uint8_t flags = *(p_nfc_data++); + + p_rec_desc->type_length = *(p_nfc_data++); + + uint32_t payload_lenght; + + if (flags & NDEF_RECORD_SR_MASK) + { + payload_lenght = *(p_nfc_data++); + } + else + { + expected_rec_size += + NDEF_RECORD_PAYLOAD_LEN_LONG_SIZE - NDEF_RECORD_PAYLOAD_LEN_SHORT_SIZE; + + if (expected_rec_size > *p_nfc_data_len) + { + return NRF_ERROR_INVALID_LENGTH; + } + + payload_lenght = uint32_big_decode(p_nfc_data); + p_nfc_data += NDEF_RECORD_PAYLOAD_LEN_LONG_SIZE; + } + + if (flags & NDEF_RECORD_IL_MASK) + { + expected_rec_size += NDEF_RECORD_ID_LEN_SIZE; + + if (expected_rec_size > *p_nfc_data_len) + { + return NRF_ERROR_INVALID_LENGTH; + } + + p_rec_desc->id_length = *(p_nfc_data++); + } + else + { + p_rec_desc->id_length = 0; + p_rec_desc->p_id = NULL; + } + + expected_rec_size += p_rec_desc->type_length + p_rec_desc->id_length + payload_lenght; + + if (expected_rec_size > *p_nfc_data_len) + { + return NRF_ERROR_INVALID_LENGTH; + } + + if (p_rec_desc->type_length > 0) + { + p_rec_desc->p_type = p_nfc_data; + + p_nfc_data += p_rec_desc->type_length; + } + else + { + p_rec_desc->p_type = NULL; + } + + if (p_rec_desc->id_length > 0) + { + p_rec_desc->p_id = p_nfc_data; + + p_nfc_data += p_rec_desc->id_length; + } + + if (payload_lenght == 0) + { + p_bin_pay_desc->p_payload = NULL; + } + else + { + p_bin_pay_desc->p_payload = p_nfc_data; + } + + p_bin_pay_desc->payload_length = payload_lenght; + + p_rec_desc->p_payload_descriptor = p_bin_pay_desc; + p_rec_desc->payload_constructor = (p_payload_constructor_t) nfc_ndef_bin_payload_memcopy; + + *p_nfc_data_len = expected_rec_size; + + return NRF_SUCCESS; +} + +char const * const tnf_strings[] = +{ + "Empty", + "NFC Forum well-known type", + "Media-type (RFC 2046)", + "Absolute URI (RFC 3986)", + "NFC Forum external type (NFC RTD)", + "Unknown", + "Unchanged", + "Reserved" +}; + +void ndef_record_printout(uint32_t num, nfc_ndef_record_desc_t * const p_rec_desc) +{ + NRF_LOG_INFO("NDEF record %d content:", num); + NRF_LOG_INFO("TNF: %s",(uint32_t)tnf_strings[p_rec_desc->tnf]); + + if (p_rec_desc->p_id != NULL) + { + NRF_LOG_INFO("ID:"); + NRF_LOG_HEXDUMP_INFO((uint8_t *)p_rec_desc->p_id, p_rec_desc->id_length); + } + + if (p_rec_desc->p_type != NULL) + { + NRF_LOG_INFO("type:"); + NRF_LOG_HEXDUMP_INFO((uint8_t *)p_rec_desc->p_type, p_rec_desc->type_length); + } + + if (p_rec_desc->payload_constructor == (p_payload_constructor_t) nfc_ndef_bin_payload_memcopy) + { + nfc_ndef_bin_payload_desc_t * p_bin_pay_desc = p_rec_desc->p_payload_descriptor; + + if (p_bin_pay_desc->p_payload != NULL) + { + NRF_LOG_INFO("Payload length: %d bytes", p_bin_pay_desc->payload_length); + NRF_LOG_HEXDUMP_DEBUG((uint8_t *)p_bin_pay_desc->p_payload, p_bin_pay_desc->payload_length); + } + else + { + NRF_LOG_INFO("No payload"); + } + } +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_RECORD_PARSER) diff --git a/libraries/nfc/src/ndef/nfc_ndef_record_parser.h b/libraries/nfc/src/ndef/nfc_ndef_record_parser.h new file mode 100644 index 000000000..d67cc84e9 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_ndef_record_parser.h @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_NDEF_RECORD_PARSER_H__ +#define NFC_NDEF_RECORD_PARSER_H__ + +#include +#include "util/sdk_errors.h" +#include "nfc_ndef_record.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @defgroup nfc_ndef_record_parser Parser for NDEF records + * @{ + * @ingroup nfc_ndef_parser + * + * @brief Parser for NFC NDEF records. + * + */ + + +/** + * @brief Function for parsing NDEF records. + * + * This parsing implementation uses the binary payload descriptor (@ref nfc_ndef_bin_payload_desc_t) to describe the payload for the record. + * + * @param[out] p_bin_pay_desc Pointer to the binary payload descriptor that will be filled and referenced by the record descriptor. + * @param[out] p_rec_desc Pointer to the record descriptor that will be filled with parsed data. + * @param[out] p_record_location Pointer to the record location. + * @param[in] p_nfc_data Pointer to the raw data to be parsed. + * @param[in,out] p_nfc_data_len As input: size of the NFC data in the @p p_nfc_data buffer. As output: size of the parsed record. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_INVALID_LENGTH If the expected record length is bigger than the provided input data amount. + */ +ret_code_t ndef_record_parser(nfc_ndef_bin_payload_desc_t * p_bin_pay_desc, + nfc_ndef_record_desc_t * p_rec_desc, + nfc_ndef_record_location_t * p_record_location, + uint8_t const * p_nfc_data, + uint32_t * p_nfc_data_len); + +/** + * @brief Function for printing the parsed contents of the NDEF record. + * + * @param[in] num Sequence number of the record within the NDEF message. + * @param[in] p_rec_desc Pointer to the descriptor of the record that should be printed. + * + */ +void ndef_record_printout(uint32_t num, nfc_ndef_record_desc_t * const p_rec_desc); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif // NFC_NDEF_RECORD_PARSER_H__ diff --git a/libraries/nfc/src/ndef/nfc_text_rec.c b/libraries/nfc/src/ndef/nfc_text_rec.c new file mode 100644 index 000000000..d99104209 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_text_rec.c @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_TEXT_RECORD) + +#include +#include "nfc_text_rec.h" +#include "nrf_error.h" + +#define TEXT_REC_STATUS_SIZE 1 ///< Size of the status. +#define TEXT_REC_STATUS_UTF_POS 7 ///< Position of a character encoding type. +#define TEXT_REC_RESERVED_POS 6 ///< Reserved position. + +const uint8_t nfc_text_rec_type_field[] = {'T'}; + + +/** + * @brief Function for calculating payload size. + */ +__STATIC_INLINE uint32_t nfc_text_rec_payload_size_get(nfc_text_rec_payload_desc_t * p_nfc_rec_text_payload_desc) +{ + return (TEXT_REC_STATUS_SIZE + + p_nfc_rec_text_payload_desc->lang_code_len + + p_nfc_rec_text_payload_desc->data_len); +} + +ret_code_t nfc_text_rec_payload_constructor(nfc_text_rec_payload_desc_t * p_nfc_rec_text_payload_desc, + uint8_t * p_buff, + uint32_t * p_len) +{ + if ((p_nfc_rec_text_payload_desc->lang_code_len == 0) + || (p_nfc_rec_text_payload_desc->lang_code_len & (1 << TEXT_REC_RESERVED_POS)) + || (p_nfc_rec_text_payload_desc->lang_code_len & (1 << TEXT_REC_STATUS_UTF_POS)) + || (p_nfc_rec_text_payload_desc->p_lang_code == NULL) + || (p_nfc_rec_text_payload_desc->data_len == 0) + || (p_nfc_rec_text_payload_desc->p_data == NULL) + || (p_len == NULL)) + { + return NRF_ERROR_INVALID_PARAM; + } + + uint32_t payload_size = nfc_text_rec_payload_size_get(p_nfc_rec_text_payload_desc); + + if (p_buff != NULL) + { + if (payload_size > *p_len) + { + return NRF_ERROR_NO_MEM; + } + + *p_buff = (p_nfc_rec_text_payload_desc->lang_code_len + + (p_nfc_rec_text_payload_desc->utf << TEXT_REC_STATUS_UTF_POS)); + p_buff += TEXT_REC_STATUS_SIZE; + + memcpy(p_buff, + p_nfc_rec_text_payload_desc->p_lang_code, + p_nfc_rec_text_payload_desc->lang_code_len); + p_buff += p_nfc_rec_text_payload_desc->lang_code_len; + + memcpy(p_buff, + p_nfc_rec_text_payload_desc->p_data, + p_nfc_rec_text_payload_desc->data_len); + } + + *p_len = payload_size; + + return NRF_SUCCESS; +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_TEXT_RECORD) diff --git a/libraries/nfc/src/ndef/nfc_text_rec.h b/libraries/nfc/src/ndef/nfc_text_rec.h new file mode 100644 index 000000000..50a1e0da8 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_text_rec.h @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_TEXT_REC_H__ +#define NFC_TEXT_REC_H__ + +/**@file + * + * @defgroup nfc_text_rec Text records + * @{ + * @ingroup nfc_ndef_messages + * + * @brief Generation of NFC NDEF Text record descriptions. + * + */ + +#include "nfc_ndef_record.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Type of the Unicode Transformation Format. + * + * Values to specify the type of UTF for an NFC NDEF Text record. + */ +typedef enum +{ + UTF_8 = 0, ///< Unicode Transformation Format 8. + UTF_16 = 1, ///< Unicode Transformation Format 16. +} nfc_text_rec_utf_type_t; + +/** + * @brief Text record payload descriptor. + */ +typedef struct +{ + nfc_text_rec_utf_type_t utf; ///< Type of the Unicode Transformation Format. + + uint8_t const * p_lang_code; ///< Pointer to the IANA language code. + uint8_t lang_code_len; ///< Length of the IANA language code. + + uint8_t const * p_data; ///< Pointer to the user text. + uint32_t data_len; ///< Length of the user text. +} nfc_text_rec_payload_desc_t; + +/** + * @brief Constructor for an NFC NDEF Text record payload. + * + * @param[in] p_nfc_rec_text_payload_desc Pointer to the Text record description. + * @param[out] p_buff Pointer to the payload destination. If NULL, function will + * calculate the expected size of the Text record payload. + * + * @param[in,out] p_len Size of the available memory to write as input. + * Size of the generated record payload as output. + */ +ret_code_t nfc_text_rec_payload_constructor(nfc_text_rec_payload_desc_t * p_nfc_rec_text_payload_desc, + uint8_t * p_buff, + uint32_t * p_len); + +/** + * @brief External reference to the type field of the Text record, defined in the + * file @c nfc_text_rec.c. It is used in the @ref NFC_NDEF_TEXT_RECORD_DESC_DEF macro. + */ +extern const uint8_t nfc_text_rec_type_field[]; + +/** + * @brief Size of the type field of the Text record, defined in the + * file @c nfc_text_rec.c. It is used in the @ref NFC_NDEF_TEXT_RECORD_DESC_DEF macro. + */ +#define NFC_TEXT_REC_TYPE_LENGTH 1 + +/** + *@brief Macro for creating and initializing an NFC NDEF record descriptor for a Text record. + * + * This macro creates and initializes an instance of type @ref nfc_ndef_record_desc_t and + * an instance of type @ref nfc_text_rec_payload_desc_t, which together constitute + * an instance of a Text record. + * + * Use the macro @ref NFC_NDEF_TEXT_RECORD_DESC to access the NDEF Text record descriptor instance. + * + * @param[in] NAME Name of the created record descriptor instance. + * @param[in] UTF Unicode Transformation Format. + * @param[in] P_LANG_CODE Pointer to the IANA language code. + * @param[in] LANG_CODE_LEN Length of the IANA language code. + * @param[in] P_DATA Pointer to the user text. + * @param[in] DATA_LEN Length of the user text. + */ +#define NFC_NDEF_TEXT_RECORD_DESC_DEF(NAME, \ + UTF, \ + P_LANG_CODE, \ + LANG_CODE_LEN, \ + P_DATA, \ + DATA_LEN) \ + nfc_text_rec_payload_desc_t NAME##_nfc_text_rec_payload_desc = \ + { \ + .utf = UTF, \ + .p_lang_code = P_LANG_CODE, \ + .lang_code_len = LANG_CODE_LEN, \ + .p_data = P_DATA, \ + .data_len = DATA_LEN, \ + }; \ + NFC_NDEF_GENERIC_RECORD_DESC_DEF(NAME, \ + TNF_WELL_KNOWN, \ + 0, \ + 0, \ + nfc_text_rec_type_field, \ + NFC_TEXT_REC_TYPE_LENGTH, \ + nfc_text_rec_payload_constructor, \ + &(NAME##_nfc_text_rec_payload_desc)) + +/** + * @brief Macro for accessing the NFC NDEF Text record descriptor + * instance that was created with @ref NFC_NDEF_TEXT_RECORD_DESC_DEF. + */ +#define NFC_NDEF_TEXT_RECORD_DESC(NAME) NFC_NDEF_GENERIC_RECORD_DESC(NAME) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_TEXT_REC_H__ diff --git a/libraries/nfc/src/ndef/nfc_uri_msg.c b/libraries/nfc/src/ndef/nfc_uri_msg.c new file mode 100644 index 000000000..1602922d0 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_uri_msg.c @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_URI_MSG) + +#include +#include "nfc_uri_msg.h" +#include "util/sdk_macros.h" + +ret_code_t nfc_uri_msg_encode( nfc_uri_id_t uri_id_code, + uint8_t const * const p_uri_data, + uint8_t uri_data_len, + uint8_t * p_buf, + uint32_t * p_len) +{ + ret_code_t err_code; + + /* Create NFC NDEF message description with URI record */ + NFC_NDEF_MSG_DEF(nfc_uri_msg, 1); + NFC_NDEF_URI_RECORD_DESC_DEF(nfc_uri_rec, uri_id_code, p_uri_data, uri_data_len); + + err_code = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_uri_msg), + &NFC_NDEF_URI_RECORD_DESC(nfc_uri_rec)); + VERIFY_SUCCESS(err_code); + VERIFY_PARAM_NOT_NULL(p_uri_data); + + /* Encode whole message into buffer */ + err_code = nfc_ndef_msg_encode(&NFC_NDEF_MSG(nfc_uri_msg), + p_buf, + p_len); + + return err_code; +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_URI_MSG) diff --git a/libraries/nfc/src/ndef/nfc_uri_msg.h b/libraries/nfc/src/ndef/nfc_uri_msg.h new file mode 100644 index 000000000..efc5746e0 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_uri_msg.h @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_URI_MSG_H__ +#define NFC_URI_MSG_H__ + +/**@file + * + * @defgroup nfc_uri_msg URI messages + * @{ + * @ingroup nfc_ndef_messages + * + * @brief Generation of NFC NDEF messages with a URI record. + * + */ + +#include "nfc_ndef_msg.h" +#include "nfc_uri_rec.h" +#include "nrf_error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Function for encoding an NFC NDEF URI message. + * + * This function encodes an NFC NDEF message into a buffer. + * + * @param[in] uri_id_code URI identifier code that defines the protocol field of the URI. + * @param[in] p_uri_data Pointer to the URI string. + * The string should not contain the protocol field if the protocol + * was specified in @p uri_id_code. + * @param[in] uri_data_len Length of the URI string. + * @param[out] p_buf Pointer to the buffer for the message. + * @param[in,out] p_len Size of the available memory for the message as input. + * Size of the generated message as output. + * + * @retval NRF_SUCCESS If the description was successfully created. + * @retval NRF_ERROR_NULL If the URI string was invalid (equal to NULL). + * @retval NRF_ERROR_NO_MEM If the predicted message size is bigger than the provided + * buffer space. + * @retval Other Other codes might be returned depending on + * the function @ref nfc_ndef_msg_encode. + */ +ret_code_t nfc_uri_msg_encode( nfc_uri_id_t uri_id_code, + uint8_t const * const p_uri_data, + uint8_t uri_data_len, + uint8_t * p_buf, + uint32_t * p_len); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_URI_MSG_H__ diff --git a/libraries/nfc/src/ndef/nfc_uri_rec.c b/libraries/nfc/src/ndef/nfc_uri_rec.c new file mode 100644 index 000000000..33320ea20 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_uri_rec.c @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_NDEF_URI_REC) + +#include +#include "nfc_uri_rec.h" +#include "nrf_error.h" + +const uint8_t ndef_uri_record_type = 'U'; ///< URI Record type. + +/** + * @brief Function for constructing the payload for a URI record. + * + * This function encodes the payload according to the URI record definition. It implements an API + * compatible with @ref p_payload_constructor_t. + * + * @param[in] p_input Pointer to the description of the payload. + * @param[out] p_buff Pointer to payload destination. If NULL, function will + * calculate the expected size of the URI record payload. + * + * @param[in,out] p_len Size of available memory to write as input. Size of generated + * payload as output. + * + * @retval NRF_SUCCESS If the payload was encoded successfully. + * @retval NRF_ERROR_NO_MEM If the predicted payload size is bigger than the provided buffer space. + */ +ret_code_t nfc_uri_payload_constructor( uri_payload_desc_t * p_input, + uint8_t * p_buff, + uint32_t * p_len) +{ + if (p_buff != NULL) + { + /* Verify if there is enough available memory */ + if (p_input->uri_data_len >= *p_len) + { + return NRF_ERROR_NO_MEM; + } + + /* Copy descriptor content into the buffer */ + *(p_buff++) = p_input->uri_id_code; + memcpy(p_buff, p_input->p_uri_data, p_input->uri_data_len ); + } + + *p_len = p_input->uri_data_len + 1; + + return NRF_SUCCESS; +} + +#endif // NRF_MODULE_ENABLED(NFC_NDEF_URI_REC) diff --git a/libraries/nfc/src/ndef/nfc_uri_rec.h b/libraries/nfc/src/ndef/nfc_uri_rec.h new file mode 100644 index 000000000..ad744d571 --- /dev/null +++ b/libraries/nfc/src/ndef/nfc_uri_rec.h @@ -0,0 +1,189 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_URI_REC_H__ +#define NFC_URI_REC_H__ + +/**@file + * + * @defgroup nfc_uri_rec URI records + * @{ + * @ingroup nfc_uri_msg + * + * @brief Generation of NFC NDEF URI record descriptions. + * + */ + +#include "nfc_ndef_record.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @enum nfc_uri_id_t + * @brief URI identifier codes according to "URI Record Type Definition" + * (denotation "NFCForum-TS-RTD_URI_1.0" published on 2006-07-24) chapter 3.2.2. + */ +typedef enum +{ + NFC_URI_NONE = 0x00, /**< No prepending is done. */ + NFC_URI_HTTP_WWW = 0x01, /**< "http://www." */ + NFC_URI_HTTPS_WWW = 0x02, /**< "https://www." */ + NFC_URI_HTTP = 0x03, /**< "http:" */ + NFC_URI_HTTPS = 0x04, /**< "https:" */ + NFC_URI_TEL = 0x05, /**< "tel:" */ + NFC_URI_MAILTO = 0x06, /**< "mailto:" */ + NFC_URI_FTP_ANONYMOUS = 0x07, /**< "ftp://anonymous:anonymous@" */ + NFC_URI_FTP_FTP = 0x08, /**< "ftp://ftp." */ + NFC_URI_FTPS = 0x09, /**< "ftps://" */ + NFC_URI_SFTP = 0x0A, /**< "sftp://" */ + NFC_URI_SMB = 0x0B, /**< "smb://" */ + NFC_URI_NFS = 0x0C, /**< "nfs://" */ + NFC_URI_FTP = 0x0D, /**< "ftp://" */ + NFC_URI_DAV = 0x0E, /**< "dav://" */ + NFC_URI_NEWS = 0x0F, /**< "news:" */ + NFC_URI_TELNET = 0x10, /**< "telnet://" */ + NFC_URI_IMAP = 0x11, /**< "imap:" */ + NFC_URI_RTSP = 0x12, /**< "rtsp://" */ + NFC_URI_URN = 0x13, /**< "urn:" */ + NFC_URI_POP = 0x14, /**< "pop:" */ + NFC_URI_SIP = 0x15, /**< "sip:" */ + NFC_URI_SIPS = 0x16, /**< "sips:" */ + NFC_URI_TFTP = 0x17, /**< "tftp:" */ + NFC_URI_BTSPP = 0x18, /**< "btspp://" */ + NFC_URI_BTL2CAP = 0x19, /**< "btl2cap://" */ + NFC_URI_BTGOEP = 0x1A, /**< "btgoep://" */ + NFC_URI_TCPOBEX = 0x1B, /**< "tcpobex://" */ + NFC_URI_IRDAOBEX = 0x1C, /**< "irdaobex://" */ + NFC_URI_FILE = 0x1D, /**< "file://" */ + NFC_URI_URN_EPC_ID = 0x1E, /**< "urn:epc:id:" */ + NFC_URI_URN_EPC_TAG = 0x1F, /**< "urn:epc:tag:" */ + NFC_URI_URN_EPC_PAT = 0x20, /**< "urn:epc:pat:" */ + NFC_URI_URN_EPC_RAW = 0x21, /**< "urn:epc:raw:" */ + NFC_URI_URN_EPC = 0x22, /**< "urn:epc:" */ + NFC_URI_URN_NFC = 0x23, /**< "urn:nfc:" */ + NFC_URI_RFU = 0xFF /**< No prepending is done. Reserved for future use. */ +} nfc_uri_id_t; + + +/** + * @brief Type of description of the payload of a URI record. + */ +typedef struct +{ + nfc_uri_id_t uri_id_code; /**< URI identifier code. */ + uint8_t const * p_uri_data; /**< Pointer to a URI string. */ + uint8_t uri_data_len; /**< Length of the URI string. */ +} uri_payload_desc_t; + +/** + * @brief External reference to the type field of the URI record, defined in the + * file @c nfc_uri_rec.c. It is used in the @ref NFC_NDEF_URI_RECORD_DESC_DEF macro. + */ +extern const uint8_t ndef_uri_record_type; + +/** + * @brief Function for constructing the payload for a URI record. + * + * This function encodes the payload according to the URI record definition. It implements an API + * compatible with @ref p_payload_constructor_t. + * + * @param[in] p_input Pointer to the description of the payload. + * @param[out] p_buff Pointer to payload destination. If NULL, function will + * calculate the expected size of the URI record payload. + * + * @param[in,out] p_len Size of available memory to write as input. Size of generated + * payload as output. + * + * @retval NRF_SUCCESS If the payload was encoded successfully. + * @retval NRF_ERROR_NO_MEM If the predicted payload size is bigger than the provided buffer space. + */ +ret_code_t nfc_uri_payload_constructor( uri_payload_desc_t * p_input, + uint8_t * p_buff, + uint32_t * p_len); + +/** @brief Macro for generating a description of a URI record. + * + * This macro initializes an instance of an NFC NDEF record description of a URI record. + * + * @note The record descriptor is declared as automatic variable, which implies that + * the NDEF message encoding (see @ref nfc_uri_msg_encode) must be done + * in the same variable scope. + * + * @param[in] NAME Name for accessing record descriptor. + * @param[in] URI_ID_CODE URI identifier code that defines the protocol field of the URI. + * @param[in] P_URI_DATA Pointer to the URI string. + * The string should not contain the protocol field if the protocol + * was specified in @p uri_id_code. + * @param[in] URI_DATA_LEN Length of the URI string. + */ +#define NFC_NDEF_URI_RECORD_DESC_DEF(NAME, \ + URI_ID_CODE, \ + P_URI_DATA, \ + URI_DATA_LEN) \ + uri_payload_desc_t NAME##_ndef_uri_record_payload_desc = \ + { \ + .uri_id_code = (URI_ID_CODE), \ + .p_uri_data = (P_URI_DATA), \ + .uri_data_len = (URI_DATA_LEN) \ + }; \ + \ + NFC_NDEF_GENERIC_RECORD_DESC_DEF( NAME, \ + TNF_WELL_KNOWN, \ + NULL, \ + 0, \ + &ndef_uri_record_type, \ + sizeof(ndef_uri_record_type), \ + nfc_uri_payload_constructor, \ + &NAME##_ndef_uri_record_payload_desc) \ + +/** + * @brief Macro for accessing the NFC NDEF URI record descriptor instance that + * was created with @ref NFC_NDEF_URI_RECORD_DESC_DEF. + */ +#define NFC_NDEF_URI_RECORD_DESC(NAME) NFC_NDEF_GENERIC_RECORD_DESC(NAME) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NFC_URI_REC_H__ diff --git a/libraries/nfc/src/ndef/parser/record/nfc_ndef_record_parser.h b/libraries/nfc/src/ndef/parser/record/nfc_ndef_record_parser.h new file mode 100644 index 000000000..d67cc84e9 --- /dev/null +++ b/libraries/nfc/src/ndef/parser/record/nfc_ndef_record_parser.h @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_NDEF_RECORD_PARSER_H__ +#define NFC_NDEF_RECORD_PARSER_H__ + +#include +#include "util/sdk_errors.h" +#include "nfc_ndef_record.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @defgroup nfc_ndef_record_parser Parser for NDEF records + * @{ + * @ingroup nfc_ndef_parser + * + * @brief Parser for NFC NDEF records. + * + */ + + +/** + * @brief Function for parsing NDEF records. + * + * This parsing implementation uses the binary payload descriptor (@ref nfc_ndef_bin_payload_desc_t) to describe the payload for the record. + * + * @param[out] p_bin_pay_desc Pointer to the binary payload descriptor that will be filled and referenced by the record descriptor. + * @param[out] p_rec_desc Pointer to the record descriptor that will be filled with parsed data. + * @param[out] p_record_location Pointer to the record location. + * @param[in] p_nfc_data Pointer to the raw data to be parsed. + * @param[in,out] p_nfc_data_len As input: size of the NFC data in the @p p_nfc_data buffer. As output: size of the parsed record. + * + * @retval NRF_SUCCESS If the function completed successfully. + * @retval NRF_ERROR_INVALID_LENGTH If the expected record length is bigger than the provided input data amount. + */ +ret_code_t ndef_record_parser(nfc_ndef_bin_payload_desc_t * p_bin_pay_desc, + nfc_ndef_record_desc_t * p_rec_desc, + nfc_ndef_record_location_t * p_record_location, + uint8_t const * p_nfc_data, + uint32_t * p_nfc_data_len); + +/** + * @brief Function for printing the parsed contents of the NDEF record. + * + * @param[in] num Sequence number of the record within the NDEF message. + * @param[in] p_rec_desc Pointer to the descriptor of the record that should be printed. + * + */ +void ndef_record_printout(uint32_t num, nfc_ndef_record_desc_t * const p_rec_desc); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif // NFC_NDEF_RECORD_PARSER_H__ diff --git a/libraries/nfc/src/t2t_parser/nfc_t2t_parser.c b/libraries/nfc/src/t2t_parser/nfc_t2t_parser.c new file mode 100644 index 000000000..654bb044f --- /dev/null +++ b/libraries/nfc/src/t2t_parser/nfc_t2t_parser.c @@ -0,0 +1,679 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/sdk_common.h" +#if NRF_MODULE_ENABLED(NFC_T2T_PARSER) + +#include +#include +#include "nrf_delay.h" +#include "nfc_t2t_parser.h" + +#define NRF_LOG_MODULE_NAME nfc_t2t_parser +#if NFC_T2T_PARSER_LOG_ENABLED +#define NRF_LOG_LEVEL NFC_T2T_PARSER_LOG_LEVEL +#define NRF_LOG_INFO_COLOR NFC_T2T_PARSER_INFO_COLOR +#include "util/nrf_log.h" +NRF_LOG_MODULE_REGISTER(); +#else // NFC_T2T_PARSER_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#include "util/nrf_log.h" +#endif // NFC_T2T_PARSER_LOG_ENABLED + +/// Gets least significant nibble (a 4-bit value) from a byte. +#define LSN_GET(val) (val & 0x0F) + +/// Gets most significant nibble (a 4-bit value) from a byte. +#define MSN_GET(val) ((val >> 4) & 0x0F) + +/** + * @brief Function for inserting the TLV block into a @ref type_2_tag_t structure. + * + * The content of a TLV block structure pointed by the p_tlv_block is copied into a TLV block + * array within the structure pointed by the p_type_2_tag. + * + * @param[in,out] p_type_2_tag Pointer to the structure that contains the TLV blocks array. + * @param[in] p_tlv_block Pointer to the TLV block to insert. + * + * @retval NRF_SUCCESS If the block was inserted successfully. + * @retval NRF_ERROR_NO_MEM If there is already maximum number of blocks stored in the array. + * + */ +static ret_code_t type_2_tag_tlv_block_insert(type_2_tag_t * p_type_2_tag, + tlv_block_t * p_tlv_block) +{ + if (p_type_2_tag->tlv_count == p_type_2_tag->max_tlv_blocks) + { + return NRF_ERROR_NO_MEM; + } + + // Copy contents of the source block. + p_type_2_tag->p_tlv_block_array[p_type_2_tag->tlv_count] = *p_tlv_block; + p_type_2_tag->tlv_count++; + + return NRF_SUCCESS; +} + + +/** + * @brief Function for checking if the TLV block length is correct. + * + * Some TLV block has predefined length: + * TLV_NULL and TLV_TERMINATOR always have a length of 1 byte. + * TLV_LOCK_CONTROL and TLV_MEMORY_CONTROL always have a length of 3 bytes. + * + * @param[in] p_block_to_check Pointer to the structure that contains the TLV block length. + * + * @retval TRUE If the length is correct. + * @retval FALSE Otherwise. + * + */ +static bool tlv_block_is_data_length_correct(tlv_block_t * p_block_to_check) +{ + switch (p_block_to_check->tag) + { + case TLV_NULL: + case TLV_TERMINATOR: + if (p_block_to_check->length != TLV_NULL_TERMINATOR_LEN) + { + return false; + } + break; + + case TLV_LOCK_CONTROL: + case TLV_MEMORY_CONTROL: + if (p_block_to_check->length != TLV_LOCK_MEMORY_CTRL_LEN) + { + return false; + } + break; + + case TLV_NDEF_MESSAGE: + case TLV_PROPRIETARY: + default: + // Any length will do. + break; + } + + return true; +} + +/** + * @brief Function for checking if the end of the tag data area was reached. + * + * @param[in] p_type_2_tag Pointer to the structure that contains the data area size. + * @param[in] offset Current byte offset. + * + * @retval TRUE If the offset indicates the end of the data area. + * @retval FALSE Otherwise. + * + */ +static bool type_2_tag_is_end_reached(type_2_tag_t * p_type_2_tag, uint16_t offset) +{ + return offset == (p_type_2_tag->cc.data_area_size + T2T_FIRST_DATA_BLOCK_OFFSET); +} + + +/** + * @brief Function for checking if version of Type 2 Tag specification read from a tag is supported. + * + * @param[in] p_type_2_tag Pointer to the structure that contains the tag version. + * + * @retval TRUE If the version is supported and tag data can be parsed. + * @retval FALSE Otherwise. + * + */ +static bool type_2_tag_is_version_supported(type_2_tag_t * p_type_2_tag) +{ + // Simple check atm, as only 1 major version has been issued so far, so no backward compatibility + // is needed, tags with newer version implemented shall be rejected according to the doc. + return p_type_2_tag->cc.major_version == T2T_SUPPORTED_MAJOR_VERSION; +} + + +/** + * @brief Function for checking if the field fits into the data area specified in + * the Capability Container. + * + * @param[in] p_type_2_tag Pointer to the structure that contains the data area size. + * @param[in] offset As Offset of the field to check. + * @param[in] field_length Length of the field to check. + * + * @retval TRUE If the field fits into the data area. + * @retval FALSE If the field exceeds the data area. + * + */ +static bool type_2_tag_is_field_within_data_range(type_2_tag_t * p_type_2_tag, + uint16_t offset, + uint16_t field_length) +{ + // Invalid argument, return false. + if (field_length == 0) + { + return false; + } + return ( (offset + field_length - 1) < + (p_type_2_tag->cc.data_area_size + T2T_FIRST_DATA_BLOCK_OFFSET) ) + && ( offset >= T2T_FIRST_DATA_BLOCK_OFFSET ); +} + + +/** + * @brief Function for reading the tag field of a TLV block from the p_raw_data buffer. + * + * This function reads the tag field containing a TLV block type and inserts its value into + * a structure pointed by the p_tlv_buf pointer. + * + * @param[in] p_type_2_tag Pointer to the structure that contains Type 2 Tag data parsed so far. + * @param[in] p_raw_data Pointer to the buffer with a raw data from the tag. + * @param[in,out] p_t_offset As input: offset of the tag field to read. As output: offset of + * the first byte after the tag field. + * @param[out] p_tlv_buf Pointer to a @ref tlv_block_t structure where the tag type will be + * inserted. + * + * @retval NRF_SUCCESS If the tag field at specified offset is correct. + * @retval NRF_ERROR_INVALID_DATA If the tag field at specified offset exceeds the data + * area specified in the Capability Container. + * + */ +static ret_code_t type_2_tag_type_extract(type_2_tag_t * p_type_2_tag, + uint8_t * p_raw_data, + uint16_t * p_t_offset, + tlv_block_t * p_tlv_buf) +{ + if (!type_2_tag_is_field_within_data_range(p_type_2_tag, *p_t_offset, TLV_T_LENGTH)) + { + return NRF_ERROR_INVALID_DATA; + } + + p_tlv_buf->tag = p_raw_data[*p_t_offset]; + *p_t_offset += TLV_T_LENGTH; + + return NRF_SUCCESS; +} + + +/** + * @brief Function for reading the length field of a TLV block from the p_raw_data buffer. + * + * This function reads the length field of a TLV block and inserts its value into a structure + * pointed by the p_tlv_buf pointer. + * + * @param[in] p_type_2_tag Pointer to the structure that contains Type 2 Tag data parsed so far. + * @param[in] p_raw_data Pointer to the buffer with a raw data from the tag. + * @param[in,out] p_l_offset As input: offset of the length field to read. As output: offset of + * the first byte after the length field. + * @param[out] p_tlv_buf Pointer to a @ref tlv_block_t structure where the length will be + * inserted. + * + * @retval NRF_SUCCESS If the length field at specified offset is correct. + * @retval NRF_ERROR_INVALID_DATA If the length field at specified offset exceeds the data + * area specified in the Capability Container or has + * incorrect format. + * + */ +static ret_code_t type_2_tag_length_extract(type_2_tag_t * p_type_2_tag, + uint8_t * p_raw_data, + uint16_t * p_l_offset, + tlv_block_t * p_tlv_buf) +{ + uint16_t length; + + if (!type_2_tag_is_field_within_data_range(p_type_2_tag, *p_l_offset, TLV_L_SHORT_LENGTH)) + { + return NRF_ERROR_INVALID_DATA; + } + + length = p_raw_data[*p_l_offset]; + + if (length == TLV_L_FORMAT_FLAG) + { + // Check another two bytes. + if (!type_2_tag_is_field_within_data_range(p_type_2_tag, *p_l_offset, TLV_L_LONG_LENGTH)) + { + return NRF_ERROR_INVALID_DATA; + } + + length = uint16_big_decode(&p_raw_data[*p_l_offset + 1]); + + // Long length value cannot be lower than 0xFF. + if (length < 0xFF) + { + return NRF_ERROR_INVALID_DATA; + } + + p_tlv_buf->length = length; + *p_l_offset += TLV_L_LONG_LENGTH; + + } + else + { + p_tlv_buf->length = length; + *p_l_offset += TLV_L_SHORT_LENGTH; + } + + return NRF_SUCCESS; +} + + +/** + * @brief Function for reading a pointer to the value field of a TLV block from the p_raw_data buffer. + * + * This function reads a pointer to the value field of a TLV block and inserts it into + * a structure pointed by the p_tlv_buf pointer. If there is no value field present in the + * TLV block, NULL is inserted. + * + * @param[in] p_type_2_tag Pointer to the structure that contains Type 2 Tag data parsed so far. + * @param[in] p_raw_data Pointer to the buffer with a raw data from the tag. + * @param[in,out] p_v_offset As input: offset of the value field to read. As output: offset of + * the first byte after the value field. + * @param[in,out] p_tlv_buf Pointer to a @ref tlv_block_t structure where the value field + * pointer will be inserted. + * + * @retval NRF_SUCCESS If the value field at specified offset is correct. + * @retval NRF_ERROR_INVALID_DATA If the value field at specified offset exceeds the data + * area specified in the Capability Container. + * + */ +static ret_code_t type_2_tag_value_ptr_extract(type_2_tag_t * p_type_2_tag, + uint8_t * p_raw_data, + uint16_t * p_v_offset, + tlv_block_t * p_tlv_buf) +{ + if (p_tlv_buf->length == 0) + { + // Clear the value pointer, don't touch the offset. + p_tlv_buf->p_value = NULL; + } + else + { + if (!type_2_tag_is_field_within_data_range(p_type_2_tag, *p_v_offset, p_tlv_buf->length)) + { + return NRF_ERROR_INVALID_DATA; + } + + p_tlv_buf->p_value = p_raw_data + *p_v_offset; + *p_v_offset += p_tlv_buf->length; + } + + return NRF_SUCCESS; +} + + +/** + * @brief Function for reading a single TLV block from the p_raw_data buffer. + * + * This function reads a single TLV block from the p_raw_data buffer and stores its contents in a + * structure pointed by the p_tlv_buf. + * + * @param[in] p_type_2_tag Pointer to the structure that contains Type 2 Tag data parsed so far. + * @param[in] p_raw_data Pointer to the buffer with a raw data from the tag. + * @param[in,out] p_tlv_offset As input: offset of the TLV block to read. As output: offset of the + * next TLV block, 0 if it was the last block. + * @param[out] p_tlv_buf Pointer to a @ref tlv_block_t structure that will be filled with + * the data read. + * + * @retval NRF_SUCCESS If the parsing operation of the block succeeded. Otherwise, an error + * code is returned. + * + */ +static ret_code_t type_2_tag_tlv_block_extract(type_2_tag_t * p_type_2_tag, + uint8_t * p_raw_data, + uint16_t * p_offset, + tlv_block_t * p_tlv_buf) +{ + ret_code_t err_code; + memset(p_tlv_buf, 0, sizeof(tlv_block_t)); + + // TLV Tag field. + err_code = type_2_tag_type_extract(p_type_2_tag, p_raw_data, p_offset, p_tlv_buf); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Further processing depends on tag field value. + switch (p_tlv_buf->tag) + { + case TLV_NULL: + // Simply ignore NULL blocks, leave the incremented offset. + break; + + case TLV_TERMINATOR: + // Write 0 to the offset variable, indicating that last TLV block was found. + *p_offset = 0; + break; + + case TLV_LOCK_CONTROL: + case TLV_MEMORY_CONTROL: + case TLV_NDEF_MESSAGE: + case TLV_PROPRIETARY: + default: + // Unknown blocks should also be extracted. + err_code = type_2_tag_length_extract(p_type_2_tag, p_raw_data, p_offset, p_tlv_buf); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + if (p_tlv_buf->length > 0) + { + err_code = type_2_tag_value_ptr_extract(p_type_2_tag, p_raw_data, p_offset, p_tlv_buf); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + break; + } + + return NRF_SUCCESS; +} + + +/** + * @brief Function for checking the checksum bytes of the UID stored in internal area. + * + * This function calculates the block check character (BCC) bytes based on the parsed serial number + * and compares them with bytes read from the Type 2 Tag. + * + * @param[in] p_sn Pointer to the @ref type_2_tag_serial_number_t structure to check. + * + * @retval TRUE If the calculated BCC matched the BCC from the tag. + * @retval FALSE Otherwise. + * + */ +static bool type_2_tag_is_bcc_correct(type_2_tag_serial_number_t * p_sn) +{ + uint8_t bcc1 = (uint8_t)T2T_UID_BCC_CASCADE_BYTE ^ + (uint8_t)p_sn->manufacturer_id ^ + (uint8_t)((p_sn->serial_number_part_1 >> 8) & 0xFF) ^ + (uint8_t)(p_sn->serial_number_part_1 & 0xFF); + + uint8_t bcc2 = (uint8_t)((p_sn->serial_number_part_2 >> 24) & 0xFF) ^ + (uint8_t)((p_sn->serial_number_part_2 >> 16) & 0xFF) ^ + (uint8_t)((p_sn->serial_number_part_2 >> 8) & 0xFF) ^ + (uint8_t)( p_sn->serial_number_part_2 & 0xFF); + + return (bcc1 == p_sn->check_byte_0) && (bcc2 == p_sn->check_byte_1); +} + + +/** + * @brief Function for parsing an internal area of a Type 2 Tag. + * + * This function reads data from an internal area in the raw data buffer and fills the + * @ref type_2_tag_serial_number_t structure within @ref type_2_tag_t. + * + * @param[in,out] p_type_2_tag Pointer to the structure that will be filled with parsed data. + * @param[in] p_raw_data Pointer to the buffer with raw data from the tag. + * + * @retval NRF_SUCCESS If the parsing operation of the internal area succeeded. + * Otherwise, an error code is returned. + * + */ +static ret_code_t type_2_tag_internal_parse(type_2_tag_t * p_type_2_tag, uint8_t * p_raw_data) +{ + p_type_2_tag->sn.manufacturer_id = p_raw_data[0]; + p_type_2_tag->sn.serial_number_part_1 = uint16_big_decode(&p_raw_data[1]); + p_type_2_tag->sn.check_byte_0 = p_raw_data[3]; + p_type_2_tag->sn.serial_number_part_2 = uint32_big_decode(&p_raw_data[4]); + p_type_2_tag->sn.check_byte_1 = p_raw_data[8]; + p_type_2_tag->sn.internal = p_raw_data[9]; + + p_type_2_tag->lock_bytes = uint16_big_decode(&p_raw_data[10]); + + if (!type_2_tag_is_bcc_correct(&p_type_2_tag->sn)) + { + NRF_LOG_WARNING("Warning! BCC of the serial number is not correct!"); + } + + return NRF_SUCCESS; +} + + +/** + * @brief Function for parsing a Capabiliy Container area of a Type 2 Tag. + * + * This function reads data from a Capability Container area in the raw data buffer and fills the + * @ref type_2_tag_capability_container_t structure within @ref type_2_tag_t. + * + * @param[in,out] p_type_2_tag Pointer to the structure that will be filled with parsed data. + * @param[in] p_raw_data Pointer to the buffer with raw data from the tag. + * + * @retval NRF_SUCCESS If the parsing operation of the Capability Container succeeded. + * Otherwise, an error code is returned. + * + */ +static ret_code_t type_2_tag_cc_parse(type_2_tag_t * p_type_2_tag, uint8_t * p_raw_data) +{ + uint8_t * p_cc_block = p_raw_data + T2T_CC_BLOCK_OFFSET; + + if (p_cc_block[0] != T2T_NFC_FORUM_DEFINED_DATA) + { + return NRF_ERROR_INVALID_DATA; + } + + p_type_2_tag->cc.major_version = MSN_GET(p_cc_block[1]); + p_type_2_tag->cc.minor_version = LSN_GET(p_cc_block[1]); + p_type_2_tag->cc.data_area_size = p_cc_block[2] * 8; + p_type_2_tag->cc.read_access = MSN_GET(p_cc_block[3]); + p_type_2_tag->cc.write_access = LSN_GET(p_cc_block[3]); + + return NRF_SUCCESS; +} + + +/** + * @brief Function for parsing a single TLV block. + * + * This function reads a single TLV block from the raw data buffer, from the position indicated by + * the p_tlv_offset, and adds it to the @ref type_2_tag_t structure. + * + * @param[in,out] p_type_2_tag Pointer to the structure that will be filled with parsed data. + * @param[in] p_raw_data Pointer to the buffer with raw data from the tag. + * @param[in,out] p_tlv_offset As input: offset of the TLV block to parse. As output: offset of the + * next TLV block, 0 if it was the last block. + * + * @retval NRF_SUCCESS If the parsing operation of the block succeeded. Otherwise, an error + * code is returned. + * + */ +static ret_code_t type_2_tag_tlv_parse(type_2_tag_t * p_type_2_tag, + uint8_t * p_raw_data, + uint16_t * p_tlv_offset) +{ + ret_code_t err_code; + tlv_block_t new_block; + + // Get tag field. + err_code = type_2_tag_tlv_block_extract(p_type_2_tag, p_raw_data, p_tlv_offset, &new_block); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + if (!tlv_block_is_data_length_correct(&new_block)) + { + return NRF_ERROR_INVALID_DATA; + } + + // Further action depends on tag type. + switch (new_block.tag) + { + case TLV_NULL: + case TLV_TERMINATOR: + // Ignore them. + break; + + case TLV_LOCK_CONTROL: + case TLV_MEMORY_CONTROL: + case TLV_NDEF_MESSAGE: + case TLV_PROPRIETARY: + default: + // Unknown tag types are also added. + err_code = type_2_tag_tlv_block_insert(p_type_2_tag, &new_block); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_WARNING("Warning! Not enough memory to insert all of the blocks!"); + return err_code; + } + break; + } + + return NRF_SUCCESS; +} + + +void type_2_tag_clear(type_2_tag_t * p_type_2_tag) +{ + p_type_2_tag->tlv_count = 0; + memset(&p_type_2_tag->cc, 0, sizeof(p_type_2_tag->cc)); + memset(&p_type_2_tag->sn, 0, sizeof(p_type_2_tag->sn)); +} + + +ret_code_t type_2_tag_parse(type_2_tag_t * p_type_2_tag, uint8_t * p_raw_data) +{ + ret_code_t err_code; + + type_2_tag_clear(p_type_2_tag); + + err_code = type_2_tag_internal_parse(p_type_2_tag, p_raw_data); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = type_2_tag_cc_parse(p_type_2_tag, p_raw_data); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + if (!type_2_tag_is_version_supported(p_type_2_tag)) + { + return NRF_ERROR_NOT_SUPPORTED; + } + + uint16_t offset = T2T_FIRST_DATA_BLOCK_OFFSET; + + while (offset > 0) + { + // Check if end of tag is reached (no terminator block was present). + if (type_2_tag_is_end_reached(p_type_2_tag, offset)) + { + NRF_LOG_DEBUG("No terminator block was found in the tag!"); + break; + } + + err_code = type_2_tag_tlv_parse(p_type_2_tag, p_raw_data, &offset); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + return NRF_SUCCESS; +} + + +void type_2_tag_printout(type_2_tag_t * p_type_2_tag) +{ + uint32_t i; + NRF_LOG_INFO("Type 2 Tag contents:"); + NRF_LOG_INFO("Number of TLV blocks: %d", p_type_2_tag->tlv_count); + + NRF_LOG_DEBUG("Internal data:"); + NRF_LOG_DEBUG(" Manufacturer ID: 0x%02x", p_type_2_tag->sn.manufacturer_id); + NRF_LOG_DEBUG(" Serial number part 1: 0x%04x", p_type_2_tag->sn.serial_number_part_1); + NRF_LOG_DEBUG(" Check byte 0: 0x%02x", p_type_2_tag->sn.check_byte_0); + NRF_LOG_DEBUG(" Serial number part 2: 0x%08lx", p_type_2_tag->sn.serial_number_part_2); + NRF_LOG_DEBUG(" Check byte 1: 0x%02x", p_type_2_tag->sn.check_byte_1); + NRF_LOG_DEBUG(" Internal byte: 0x%02x", p_type_2_tag->sn.internal); + NRF_LOG_DEBUG(" Lock bytes: 0x%04x", p_type_2_tag->lock_bytes); + + NRF_LOG_DEBUG("Capability Container data:"); + NRF_LOG_DEBUG(" Major version number: %d", p_type_2_tag->cc.major_version); + NRF_LOG_DEBUG(" Minor version number: %d", p_type_2_tag->cc.minor_version); + NRF_LOG_DEBUG(" Data area size: %d", p_type_2_tag->cc.data_area_size); + NRF_LOG_DEBUG(" Read access: 0x%02X", p_type_2_tag->cc.read_access); + NRF_LOG_DEBUG(" Write access: 0x%02X", p_type_2_tag->cc.write_access); + + for (i = 0; i < p_type_2_tag->tlv_count; i++) + { + NRF_LOG_INFO("TLV block 0x%02X: ", p_type_2_tag->p_tlv_block_array[i].tag); + switch (p_type_2_tag->p_tlv_block_array[i].tag) + { + case TLV_LOCK_CONTROL: + NRF_LOG_INFO("Lock Control"); + break; + case TLV_MEMORY_CONTROL: + NRF_LOG_INFO("Memory Control"); + break; + case TLV_NDEF_MESSAGE: + NRF_LOG_INFO("NDEF Message"); + break; + case TLV_PROPRIETARY: + NRF_LOG_INFO("Proprietary"); + break; + case TLV_NULL: + NRF_LOG_INFO("Null\r\n"); + break; + case TLV_TERMINATOR: + NRF_LOG_INFO("Terminator"); + break; + default: + NRF_LOG_INFO("Unknown"); + break; + } + + NRF_LOG_INFO(" Data length: %d", p_type_2_tag->p_tlv_block_array[i].length); + + if (p_type_2_tag->p_tlv_block_array[i].length > 0) + { + NRF_LOG_DEBUG(" Data:"); + NRF_LOG_HEXDUMP_DEBUG(p_type_2_tag->p_tlv_block_array[i].p_value, + p_type_2_tag->p_tlv_block_array[i].length); + } + } +} + +#endif // NRF_MODULE_ENABLED(NFC_T2T_PARSER) diff --git a/libraries/nfc/src/t2t_parser/nfc_t2t_parser.h b/libraries/nfc/src/t2t_parser/nfc_t2t_parser.h new file mode 100644 index 000000000..2a6f58cc1 --- /dev/null +++ b/libraries/nfc/src/t2t_parser/nfc_t2t_parser.h @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_TYPE_2_TAG_PARSER_H__ +#define NFC_TYPE_2_TAG_PARSER_H__ + +#include +#include "nfc_tlv_block.h" +#include "util/sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nfc_type_2_tag Type 2 Tag + * @{ + * @ingroup nfc_type_2_tag_parser + * + * @brief Descriptor for a Type 2 Tag. + * + */ + +/** + * @brief Descriptor for the internal bytes of a Type 2 Tag. + */ +typedef struct +{ + uint8_t manufacturer_id; ///< Manufacturer ID (the most significant byte of the UID/serial number). + uint16_t serial_number_part_1; ///< Bytes 5-4 of the tag UID. + uint8_t check_byte_0; ///< First block check character byte (XOR of the cascade tag byte, manufacturer ID byte, and the serial_number_part_1 bytes). + uint32_t serial_number_part_2; ///< Bytes 3-0 of the tag UID. + uint8_t check_byte_1; ///< Second block check character byte (XOR of the serial_number_part_2 bytes). + uint8_t internal; ///< Tag internal bytes. +} type_2_tag_serial_number_t; + +/** + * @brief Descriptor for the Capability Container (CC) bytes of a Type 2 Tag. + */ +typedef struct +{ + uint8_t major_version; ///< Major version of the supported Type 2 Tag specification. + uint8_t minor_version; ///< Minor version of the supported Type 2 Tag specification. + uint16_t data_area_size; ///< Size of the data area in bytes. + uint8_t read_access; ///< Read access for the data area. + uint8_t write_access; ///< Write access for the data area. +} type_2_tag_capability_container_t; + +/** + * @brief Type 2 Tag descriptor. + */ +typedef struct +{ + type_2_tag_serial_number_t sn; ///< Values within the serial number area of the tag. + uint16_t lock_bytes; ///< Value of the lock bytes. + type_2_tag_capability_container_t cc; ///< Values within the Capability Container area of the tag. + + uint16_t const max_tlv_blocks; ///< Maximum number of TLV blocks that can be stored. + tlv_block_t * p_tlv_block_array; ///< Pointer to the array for TLV blocks. + uint16_t tlv_count; ///< Number of TLV blocks stored in the Type 2 Tag. + +} type_2_tag_t; + +/** + * @brief Macro for creating and initializing a Type 2 Tag descriptor. + * + * This macro creates and initializes a static instance of a @ref type_2_tag_t structure and + * an array of @ref tlv_block_t descriptors. + * + * Use the macro @ref NFC_TYPE_2_TAG_DESC to access the Type 2 Tag descriptor instance. + * + * @param[in] NAME Name of the created descriptor instance. + * @param[in] MAX_BLOCKS Maximum number of @ref tlv_block_t descriptors that can be stored in the array. + * + */ +#define NFC_TYPE_2_TAG_DESC_DEF(NAME, MAX_BLOCKS) \ + static tlv_block_t NAME##_tlv_block_array[MAX_BLOCKS]; \ + static type_2_tag_t NAME##_type_2_tag = \ + { \ + .max_tlv_blocks = MAX_BLOCKS, \ + .p_tlv_block_array = NAME##_tlv_block_array, \ + .tlv_count = 0 \ + } + +/** + * @brief Macro for accessing the @ref type_2_tag_t instance that was created + * with @ref NFC_TYPE_2_TAG_DESC_DEF. + */ +#define NFC_TYPE_2_TAG_DESC(NAME) (NAME##_type_2_tag) + + +#define T2T_NFC_FORUM_DEFINED_DATA 0xE1 ///< Value indicating that the Type 2 Tag contains NFC Forum defined data. +#define T2T_UID_BCC_CASCADE_BYTE 0x88 ///< Value used for calculating the first BCC byte of a Type 2 Tag serial number. + +#define T2T_SUPPORTED_MAJOR_VERSION 1 ///< Supported major version of the Type 2 Tag specification. +#define T2T_SUPPORTED_MINOR_VERSION 2 ///< Supported minor version of the Type 2 Tag specification. + +#define T2T_BLOCK_SIZE 4 ///< Type 2 Tag block size in bytes. + +#define T2T_CC_BLOCK_OFFSET 12 ///< Offset of the Capability Container area in the Type 2 Tag. +#define T2T_FIRST_DATA_BLOCK_OFFSET 16 ///< Offset of the data area in the Type 2 Tag. + +/** + * @} + */ + + +/** + * @defgroup nfc_type_2_tag_parser NFC Type 2 Tag parser + * @{ + * @ingroup nfc_t2t + * + * @brief Parser for Type 2 Tag data. + * + */ + +/** + * @brief Function for clearing the @ref type_2_tag_t structure. + * + * @param[in,out] p_type_2_tag Pointer to the structure that should be cleared. + * + */ +void type_2_tag_clear(type_2_tag_t * p_type_2_tag); + +/** + * @brief Function for parsing raw data read from a Type 2 Tag. + * + * This function parses the header and the following TLV blocks of a Type 2 Tag. The data is read + * from a buffer and stored in a @ref type_2_tag_t structure. + * + * @param[out] p_type_2_tag Pointer to the structure that will be filled with parsed data. + * @param[in] p_raw_data Pointer to the buffer with raw data from the tag (should + * point at the first byte of the first block of the tag). + * + * @retval NRF_SUCCESS If the data was parsed successfully. + * @retval NRF_ERROR_NO_MEM If there is not enough memory to store all of the TLV blocks. + * @retval Other If an error occurred during the parsing operation. + * + */ +ret_code_t type_2_tag_parse(type_2_tag_t * p_type_2_tag, uint8_t * p_raw_data); + +/** + * @brief Function for printing parsed contents of the Type 2 Tag. + * + * @param[in] p_type_2_tag Pointer to the structure that should be printed. + * + */ +void type_2_tag_printout(type_2_tag_t * p_type_2_tag); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* NFC_TYPE_2_TAG_PARSER_H__ */ diff --git a/libraries/nfc/src/t2t_parser/nfc_tlv_block.h b/libraries/nfc/src/t2t_parser/nfc_tlv_block.h new file mode 100644 index 000000000..3fcfdf4e5 --- /dev/null +++ b/libraries/nfc/src/t2t_parser/nfc_tlv_block.h @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NFC_TLV_BLOCK_H__ +#define NFC_TLV_BLOCK_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/**@file + * + * @defgroup nfc_type_2_tag_tlv_block Type 2 Tag TLV blocks + * @{ + * @ingroup nfc_type_2_tag_parser + * + * @brief Descriptor for a Type 2 Tag TLV block. + * + */ + +/** + * @brief Tag field values. + * + * Possible values for the tag field in a TLV block. + */ +typedef enum +{ + TLV_NULL = 0x00, ///< Might be used for padding of memory areas. + TLV_LOCK_CONTROL = 0x01, ///< Defines details of the lock bits. + TLV_MEMORY_CONTROL = 0x02, ///< Identifies reserved memory areas. + TLV_NDEF_MESSAGE = 0x03, ///< Contains an NDEF message. + TLV_PROPRIETARY = 0xFD, ///< Tag proprietary information. + TLV_TERMINATOR = 0xFE ///< Last TLV block in the data area. +} tlv_block_types_t; + +/** + * @brief TLV block descriptor. + */ +typedef struct +{ + uint8_t tag; ///< Type of the TLV block. + uint16_t length; ///< Length of the value field. + uint8_t * p_value; ///< Pointer to the value field (NULL if no value field is present in the block). +} tlv_block_t; + +#define TLV_T_LENGTH 1 ///< Length of a tag field. + +#define TLV_L_SHORT_LENGTH 1 ///< Length of a short length field. +#define TLV_L_LONG_LENGTH 3 ///< Length of an extended length field. +#define TLV_L_FORMAT_FLAG 0xFF ///< Value indicating the use of an extended length field. + +#define TLV_NULL_TERMINATOR_LEN 0 ///< Predefined length of the NULL and TERMINATOR TLV blocks. +#define TLV_LOCK_MEMORY_CTRL_LEN 3 ///< Predefined length of the LOCK CONTROL and MEMORY CONTROL blocks. + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* NFC_TLV_BLOCK_H__ */ diff --git a/libraries/nfc/src/util/app_error.c b/libraries/nfc/src/util/app_error.c new file mode 100644 index 000000000..73a1234af --- /dev/null +++ b/libraries/nfc/src/util/app_error.c @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2014 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup app_error Common application error handler + * @{ + * @ingroup app_common + * + * @brief Common application error handler. + */ + +#include "nrf.h" +#include +#include "app_error.h" +#include "nordic_common.h" +#include "sdk_errors.h" + +/**@brief Function for error handling, which is called when an error has occurred. + * + * @warning This handler is an example only and does not fit a final product. You need to analyze + * how your product is supposed to react in case of error. + * + * @param[in] error_code Error code supplied to the handler. + * @param[in] line_num Line number where the handler is called. + * @param[in] p_file_name Pointer to the file name. + */ +void app_error_handler_bare(ret_code_t error_code) +{ + error_info_t error_info = + { + .line_num = 0, + .p_file_name = NULL, + .err_code = error_code, + }; + + app_error_fault_handler(NRF_FAULT_ID_SDK_ERROR, 0, (uint32_t)(&error_info)); + + UNUSED_VARIABLE(error_info); +} + +void app_error_save_and_stop(uint32_t id, uint32_t pc, uint32_t info) +{ + /* static error variables - in order to prevent removal by optimizers */ + static volatile struct + { + uint32_t fault_id; + uint32_t pc; + uint32_t error_info; + assert_info_t * p_assert_info; + error_info_t * p_error_info; + ret_code_t err_code; + uint32_t line_num; + const uint8_t * p_file_name; + } m_error_data = {0}; + + // The following variable helps Keil keep the call stack visible, in addition, it can be set to + // 0 in the debugger to continue executing code after the error check. + volatile bool loop = true; + UNUSED_VARIABLE(loop); + + m_error_data.fault_id = id; + m_error_data.pc = pc; + m_error_data.error_info = info; + + switch (id) + { + case NRF_FAULT_ID_SDK_ASSERT: + m_error_data.p_assert_info = (assert_info_t *)info; + m_error_data.line_num = m_error_data.p_assert_info->line_num; + m_error_data.p_file_name = m_error_data.p_assert_info->p_file_name; + break; + + case NRF_FAULT_ID_SDK_ERROR: + m_error_data.p_error_info = (error_info_t *)info; + m_error_data.err_code = m_error_data.p_error_info->err_code; + m_error_data.line_num = m_error_data.p_error_info->line_num; + m_error_data.p_file_name = m_error_data.p_error_info->p_file_name; + break; + } + + UNUSED_VARIABLE(m_error_data); + + // If printing is disrupted, remove the irq calls, or set the loop variable to 0 in the debugger. + __disable_irq(); + while (loop); + + __enable_irq(); +} diff --git a/libraries/nfc/src/util/app_error.h b/libraries/nfc/src/util/app_error.h new file mode 100644 index 000000000..1da0c01c2 --- /dev/null +++ b/libraries/nfc/src/util/app_error.h @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2013 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup app_error Common application error handler + * @{ + * @ingroup app_common + * + * @brief Common application error handler and macros for utilizing a common error handler. + */ + +#ifndef APP_ERROR_H__ +#define APP_ERROR_H__ + + +#include +#include +#include +#include "nrf.h" +#include "sdk_errors.h" +#include "nordic_common.h" +#include "app_error_weak.h" +#ifdef ANT_STACK_SUPPORT_REQD +#include "ant_error.h" +#endif // ANT_STACK_SUPPORT_REQD + + + +#ifdef __cplusplus +extern "C" { +#endif + +#define NRF_FAULT_ID_SDK_RANGE_START (0x00004000) /**< The start of the range of error IDs defined in the SDK. */ + +/**@defgroup APP_ERROR_FAULT_IDS Fault ID types + * @{ */ +#define NRF_FAULT_ID_SDK_ERROR (NRF_FAULT_ID_SDK_RANGE_START + 1) /**< An error stemming from a call to @ref APP_ERROR_CHECK or @ref APP_ERROR_CHECK_BOOL. The info parameter is a pointer to an @ref error_info_t variable. */ +#define NRF_FAULT_ID_SDK_ASSERT (NRF_FAULT_ID_SDK_RANGE_START + 2) /**< An error stemming from a call to ASSERT (nrf_assert.h). The info parameter is a pointer to an @ref assert_info_t variable. */ +/**@} */ + +/**@brief Structure containing info about an error of the type @ref NRF_FAULT_ID_SDK_ERROR. + */ +typedef struct +{ + uint32_t line_num; /**< The line number where the error occurred. */ + uint8_t const * p_file_name; /**< The file in which the error occurred. */ + uint32_t err_code; /**< The error code representing the error that occurred. */ +} error_info_t; + +/**@brief Structure containing info about an error of the type @ref NRF_FAULT_ID_SDK_ASSERT. + */ +typedef struct +{ + uint32_t line_num; /**< The line number where the error occurred. */ + uint8_t const * p_file_name; /**< The file in which the error occurred. */ +} assert_info_t; + +/**@brief Defines required by app_error_handler assembler intructions. + */ +#define APP_ERROR_ERROR_INFO_OFFSET_LINE_NUM (offsetof(error_info_t, line_num)) +#define APP_ERROR_ERROR_INFO_OFFSET_P_FILE_NAME (offsetof(error_info_t, p_file_name)) +#define APP_ERROR_ERROR_INFO_OFFSET_ERR_CODE (offsetof(error_info_t, err_code)) +#define APP_ERROR_ERROR_INFO_SIZE (sizeof(error_info_t)) +#define APP_ERROR_ERROR_INFO_SIZE_ALIGNED_8BYTE \ + ALIGN_NUM(APP_ERROR_ERROR_INFO_SIZE, sizeof(uint64_t)) + + +/**@brief Function for error handling, which is called when an error has occurred. + * + * @param[in] error_code Error code supplied to the handler. + * @param[in] line_num Line number where the handler is called. + * @param[in] p_file_name Pointer to the file name. + */ +void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name); + +/**@brief Function for error handling, which is called when an error has occurred. + * + * @param[in] error_code Error code supplied to the handler. + */ +void app_error_handler_bare(ret_code_t error_code); + +/**@brief Function for saving the parameters and entering an eternal loop, for debug purposes. + * + * @param[in] id Fault identifier. See @ref NRF_FAULT_IDS. + * @param[in] pc The program counter of the instruction that triggered the fault, or 0 if + * unavailable. + * @param[in] info Optional additional information regarding the fault. Refer to each fault + * identifier for details. + */ +void app_error_save_and_stop(uint32_t id, uint32_t pc, uint32_t info); + +/**@brief Function for logging details of error and flushing logs. + * + * @param[in] id Fault identifier. See @ref NRF_FAULT_IDS. + * @param[in] pc The program counter of the instruction that triggered the fault, or 0 if + * unavailable. + * @param[in] info Optional additional information regarding the fault. Refer to each fault + * identifier for details. + */ +void app_error_log_handle(uint32_t id, uint32_t pc, uint32_t info); + + +/**@brief Macro for calling error handler function. + * + * @param[in] ERR_CODE Error code supplied to the error handler. + */ +#ifdef DEBUG +#define APP_ERROR_HANDLER(ERR_CODE) \ + do \ + { \ + app_error_handler((ERR_CODE), __LINE__, (uint8_t*) __FILE__); \ + } while (0) +#else +#define APP_ERROR_HANDLER(ERR_CODE) \ + do \ + { \ + app_error_handler_bare((ERR_CODE)); \ + } while (0) +#endif +/**@brief Macro for calling error handler function if supplied error code any other than NRF_SUCCESS. + * + * @param[in] ERR_CODE Error code supplied to the error handler. + */ +#define APP_ERROR_CHECK(ERR_CODE) \ + do \ + { \ + const uint32_t LOCAL_ERR_CODE = (ERR_CODE); \ + if (LOCAL_ERR_CODE != NRF_SUCCESS) \ + { \ + APP_ERROR_HANDLER(LOCAL_ERR_CODE); \ + } \ + } while (0) + +/**@brief Macro for calling error handler function if supplied boolean value is false. + * + * @param[in] BOOLEAN_VALUE Boolean value to be evaluated. + */ +#define APP_ERROR_CHECK_BOOL(BOOLEAN_VALUE) \ + do \ + { \ + const uint32_t LOCAL_BOOLEAN_VALUE = (BOOLEAN_VALUE); \ + if (!LOCAL_BOOLEAN_VALUE) \ + { \ + APP_ERROR_HANDLER(0); \ + } \ + } while (0) + + +#ifdef __cplusplus +} +#endif + +#endif // APP_ERROR_H__ + +/** @} */ diff --git a/libraries/nfc/src/util/app_error_handler_gcc.c b/libraries/nfc/src/util/app_error_handler_gcc.c new file mode 100644 index 000000000..51a971e58 --- /dev/null +++ b/libraries/nfc/src/util/app_error_handler_gcc.c @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#include "compiler_abstraction.h" +#include "app_error.h" + +#if defined (__CORTEX_M) && (__CORTEX_M == 0x04) +void app_error_handler(ret_code_t error_code, uint32_t line_num, const uint8_t * p_file_name) __attribute__(( naked )); + +void app_error_handler(ret_code_t error_code, uint32_t line_num, const uint8_t * p_file_name) +{ + __ASM volatile( + + "push {lr} \n" + + /* reserve space on stack for error_info_t struct - preserve 8byte stack aligment */ + "sub sp, sp, %0 \n" + + /* prepare error_info_t struct */ + "str r0, [sp, %1] \n" + "str r1, [sp, %3] \n" + "str r2, [sp, %2] \n" + + /* prepare arguments and call function: app_error_fault_handler */ + "ldr r0, =%4 \n" + "mov r1, lr \n" + "mov r2, sp \n" + "bl %5 \n" + + /* release stack */ + "add sp, sp, %0 \n" + + "pop {pc} \n" + ".ltorg \n" + + : /* Outputs */ + : /* Inputs */ + "I" (APP_ERROR_ERROR_INFO_SIZE_ALIGNED_8BYTE), + "I" (APP_ERROR_ERROR_INFO_OFFSET_ERR_CODE), + "I" (APP_ERROR_ERROR_INFO_OFFSET_P_FILE_NAME), + "I" (APP_ERROR_ERROR_INFO_OFFSET_LINE_NUM), + "X" (NRF_FAULT_ID_SDK_ERROR), + "X" (app_error_fault_handler) + : /* Clobbers */ + "r0", "r1", "r2" + ); +} +#elif defined(__CORTEX_M) && (__CORTEX_M == 0x00) +/* NRF51 implementation is currently not supporting PC readout */ +void app_error_handler(ret_code_t error_code, uint32_t line_num, const uint8_t * p_file_name) +{ + error_info_t error_info = { + .line_num = line_num, + .p_file_name = p_file_name, + .err_code = error_code, + }; + app_error_fault_handler(NRF_FAULT_ID_SDK_ERROR, 0, (uint32_t)(&error_info)); + + UNUSED_VARIABLE(error_info); +} +#else +#error Architecture not supported +#endif + diff --git a/libraries/nfc/src/util/app_error_weak.c b/libraries/nfc/src/util/app_error_weak.c new file mode 100644 index 000000000..9a5ecdd27 --- /dev/null +++ b/libraries/nfc/src/util/app_error_weak.c @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "app_error.h" + +#include "nrf_log.h" +#include "nrf_log_ctrl.h" +#include "app_util_platform.h" +#include "nrf_strerror.h" + +// #if defined(SOFTDEVICE_PRESENT) && SOFTDEVICE_PRESENT +// #include "nrf_sdm.h" +// #endif + +/*lint -save -e14 */ +/** + * Function is implemented as weak so that it can be overwritten by custom application error handler + * when needed. + */ +__WEAK void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) +{ + __disable_irq(); + NRF_LOG_FINAL_FLUSH(); + +#ifndef DEBUG + NRF_LOG_ERROR("Fatal error"); +#else + switch (id) + { +// #if defined(SOFTDEVICE_PRESENT) && SOFTDEVICE_PRESENT +// case NRF_FAULT_ID_SD_ASSERT: +// NRF_LOG_ERROR("SOFTDEVICE: ASSERTION FAILED"); +// break; +// case NRF_FAULT_ID_APP_MEMACC: +// NRF_LOG_ERROR("SOFTDEVICE: INVALID MEMORY ACCESS"); +// break; +// #endif + case NRF_FAULT_ID_SDK_ASSERT: + { + assert_info_t * p_info = (assert_info_t *)info; + NRF_LOG_ERROR("ASSERTION FAILED at %s:%u", + p_info->p_file_name, + p_info->line_num); + break; + } + case NRF_FAULT_ID_SDK_ERROR: + { + error_info_t * p_info = (error_info_t *)info; + NRF_LOG_ERROR("ERROR %u [%s] at %s:%u\r\nPC at: 0x%08x", + p_info->err_code, + nrf_strerror_get(p_info->err_code), + p_info->p_file_name, + p_info->line_num, + pc); + NRF_LOG_ERROR("End of error report"); + break; + } + default: + NRF_LOG_ERROR("UNKNOWN FAULT at 0x%08X", pc); + break; + } +#endif + + NRF_BREAKPOINT_COND; + // On assert, the system can only recover with a reset. + +#ifndef DEBUG + NRF_LOG_WARNING("System reset"); + NVIC_SystemReset(); +#else + app_error_save_and_stop(id, pc, info); +#endif // DEBUG +} +/*lint -restore */ diff --git a/libraries/nfc/src/util/app_error_weak.h b/libraries/nfc/src/util/app_error_weak.h new file mode 100644 index 000000000..f52f64405 --- /dev/null +++ b/libraries/nfc/src/util/app_error_weak.h @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef APP_ERROR_WEAK_H__ +#define APP_ERROR_WEAK_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file + * + * @defgroup app_error Common application error handler + * @{ + * @ingroup app_common + * + * @brief Common application error handler. + */ + +/**@brief Callback function for errors, asserts, and faults. + * + * @details This function is called every time an error is raised in app_error, nrf_assert, or + * in the SoftDevice. Information about the error can be found in the @p info + * parameter. + * + * See also @ref nrf_fault_handler_t for more details. + * + * @note The function is implemented as weak so that it can be redefined by a custom error + * handler when needed. + * + * @param[in] id Fault identifier. See @ref NRF_FAULT_IDS. + * @param[in] pc The program counter of the instruction that triggered the fault, or 0 if + * unavailable. + * @param[in] info Optional additional information regarding the fault. The value of the @p id + * parameter dictates how to interpret this parameter. Refer to the documentation + * for each fault identifier (@ref NRF_FAULT_IDS and @ref APP_ERROR_FAULT_IDS) for + * details about interpreting @p info. + */ +void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info); + + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif // APP_ERROR_WEAK_H__ diff --git a/libraries/nfc/src/util/app_util.h b/libraries/nfc/src/util/app_util.h new file mode 100644 index 000000000..5de3b2e98 --- /dev/null +++ b/libraries/nfc/src/util/app_util.h @@ -0,0 +1,1326 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup app_util Utility Functions and Definitions + * @{ + * @ingroup app_common + * + * @brief Various types and definitions available to all applications. + */ + +#ifndef APP_UTIL_H__ +#define APP_UTIL_H__ + +#include +#include +#include +#include "compiler_abstraction.h" +#include "nordic_common.h" +#include "nrf.h" + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @cond (NODOX) + */ +/*lint -save -e27 -e10 -e19 */ +#if defined (__LINT__) +#define STACK_BASE 0x1F000 // Arbitrary value. +#define STACK_TOP 0x20000 // Arbitrary value. + +#elif defined ( __CC_ARM ) +extern char STACK$$Base; +extern char STACK$$Length; +#define STACK_BASE &STACK$$Base +#define STACK_TOP ((void*)((uint32_t)STACK_BASE + (uint32_t)&STACK$$Length)) + +#elif defined ( __ICCARM__ ) +extern char CSTACK$$Base; +extern char CSTACK$$Length; +#define STACK_BASE &CSTACK$$Base +#define STACK_TOP ((void*)((uint32_t)STACK_BASE + (uint32_t)&CSTACK$$Length)) + +#elif defined ( __GNUC__ ) +extern uint32_t __StackTop; +extern uint32_t __StackLimit; +#define STACK_BASE &__StackLimit +#define STACK_TOP &__StackTop +#endif + +/* These macros are valid only when absolute placement is used for the application + * image. The macros are not compile time symbols. They cannot be used as a + * constant expression, for example, inside a static assert or linker script + * at-placement. */ +#if defined (__LINT__) +#define CODE_START (0) // Arbitrary value. +#define CODE_END (0x1000) // Arbitrary value. +#define CODE_SIZE (0x1000) // Arbitrary value. + +#elif defined ( __CC_ARM ) +extern char Load$$LR$$LR_IROM1$$Base; +extern char Load$$LR$$LR_IROM1$$Length; +extern char Load$$LR$$LR_IROM1$$Limit; +#define CODE_START ((uint32_t)&Load$$LR$$LR_IROM1$$Base) +#define CODE_END ((uint32_t)&Load$$LR$$LR_IROM1$$Limit) +#define CODE_SIZE ((uint32_t)&Load$$LR$$LR_IROM1$$Length) + +#elif defined ( __ICCARM__ ) +extern void * __vector_table; +extern char RO_END$$Base; +#define CODE_START ((uint32_t)&__vector_table) +#define CODE_END ((uint32_t)&RO_END$$Base) +#define CODE_SIZE (CODE_END - CODE_START) + +#elif defined(__SES_ARM) +extern uint32_t * _vectors; +extern uint32_t __FLASH_segment_used_end__; +#define CODE_START ((uint32_t)&_vectors) +#define CODE_END ((uint32_t)&__FLASH_segment_used_end__) +#define CODE_SIZE (CODE_END - CODE_START) + +#elif defined ( __GNUC__ ) +extern uint32_t __isr_vector; +extern uint32_t __etext; +#define CODE_START ((uint32_t)&__isr_vector) +#define CODE_END ((uint32_t)&__etext) +#define CODE_SIZE (CODE_END - CODE_START) +#endif +/** @} + * @endcond + */ +/* lint -restore */ + +#if defined(MBR_PRESENT) //|| defined(SOFTDEVICE_PRESENT) +#include "nrf_mbr.h" +#define BOOTLOADER_ADDRESS ((*(uint32_t *)MBR_BOOTLOADER_ADDR) == 0xFFFFFFFF ? *MBR_UICR_BOOTLOADER_ADDR : *(uint32_t *)MBR_BOOTLOADER_ADDR) /**< The currently configured start address of the bootloader. If 0xFFFFFFFF, no bootloader start address is configured. */ +#define MBR_PARAMS_PAGE_ADDRESS ((*(uint32_t *)MBR_PARAM_PAGE_ADDR) == 0xFFFFFFFF ? *MBR_UICR_PARAM_PAGE_ADDR : *(uint32_t *)MBR_PARAM_PAGE_ADDR) /**< The currently configured address of the MBR params page. If 0xFFFFFFFF, no MBR params page address is configured. */ +#else +#define BOOTLOADER_ADDRESS (NRF_UICR->NRFFW[0]) /**< Check UICR, just in case. */ +#define MBR_PARAMS_PAGE_ADDRESS (NRF_UICR->NRFFW[1]) /**< Check UICR, just in case. */ +#endif + +enum +{ + UNIT_0_625_MS = 625, /**< Number of microseconds in 0.625 milliseconds. */ + UNIT_1_25_MS = 1250, /**< Number of microseconds in 1.25 milliseconds. */ + UNIT_10_MS = 10000 /**< Number of microseconds in 10 milliseconds. */ +}; + +/** + * @brief Counts number of bits required for the given value + * + * The macro technically searches for the highest bit set. + * For value 0 it returns 0. + * + * @param val Value to be processed + * + * @return Number of bits required for the given value + */ +//lint -emacro(572,VBITS) +#define VBITS(val) VBITS_32(val) + +/** + * @def VBITS_1 + * @brief Internal macro used by @ref VBITS */ +/** + * @def VBITS_2 + * @brief Internal macro used by @ref VBITS */ +/** + * @def VBITS_4 + * @brief Internal macro used by @ref VBITS */ +/** + * @def VBITS_8 + * @brief Internal macro used by @ref VBITS */ +/** + * @def VBITS_16 + * @brief Internal macro used by @ref VBITS */ +/** + * @def VBITS_32 + * @brief Internal macro used by @ref VBITS */ +#define VBITS_1( v) ((((v) & (0x0001U << 0)) != 0) ? 1U : 0U) +#define VBITS_2( v) ((((v) & (0x0001U << 1)) != 0) ? VBITS_1 ((v) >> 1) + 1 : VBITS_1 (v)) +#define VBITS_4( v) ((((v) & (0x0003U << 2)) != 0) ? VBITS_2 ((v) >> 2) + 2 : VBITS_2 (v)) +#define VBITS_8( v) ((((v) & (0x000fU << 4)) != 0) ? VBITS_4 ((v) >> 4) + 4 : VBITS_4 (v)) +#define VBITS_16(v) ((((v) & (0x00ffU << 8)) != 0) ? VBITS_8 ((v) >> 8) + 8 : VBITS_8 (v)) +#define VBITS_32(v) ((((v) & (0xffffU << 16)) != 0) ? VBITS_16((v) >> 16) + 16 : VBITS_16(v)) + + +/*Segger embedded studio originally has offsetof macro which cannot be used in macros (like STATIC_ASSERT). + This redefinition is to allow using that. */ +#if defined(__SES_ARM) && defined(__GNUC__) +#undef offsetof +#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) +#endif + +/**@brief Implementation specific macro for delayed macro expansion used in string concatenation +* +* @param[in] lhs Left hand side in concatenation +* @param[in] rhs Right hand side in concatenation +*/ +#define STRING_CONCATENATE_IMPL(lhs, rhs) lhs ## rhs + + +/**@brief Macro used to concatenate string using delayed macro expansion +* +* @note This macro will delay concatenation until the expressions have been resolved +* +* @param[in] lhs Left hand side in concatenation +* @param[in] rhs Right hand side in concatenation +*/ +#define STRING_CONCATENATE(lhs, rhs) STRING_CONCATENATE_IMPL(lhs, rhs) + + +#ifndef __LINT__ + +#ifdef __GNUC__ +#ifdef __cplusplus +#define STATIC_ASSERT_SIMPLE(EXPR) extern char (*_do_assert(void)) [sizeof(char[1 - 2*!(EXPR)])] +#define STATIC_ASSERT_MSG(EXPR, MSG) extern char (*_do_assert(void)) [sizeof(char[1 - 2*!(EXPR)])] +#else +#define STATIC_ASSERT_SIMPLE(EXPR) _Static_assert(EXPR, "unspecified message") +#define STATIC_ASSERT_MSG(EXPR, MSG) _Static_assert(EXPR, MSG) +#endif +#endif + +#ifdef __CC_ARM +#define STATIC_ASSERT_SIMPLE(EXPR) extern char (*_do_assert(void)) [sizeof(char[1 - 2*!(EXPR)])] +#define STATIC_ASSERT_MSG(EXPR, MSG) extern char (*_do_assert(void)) [sizeof(char[1 - 2*!(EXPR)])] +#endif + +#ifdef __ICCARM__ +#define STATIC_ASSERT_SIMPLE(EXPR) static_assert(EXPR, "unspecified message") +#define STATIC_ASSERT_MSG(EXPR, MSG) static_assert(EXPR, MSG) +#endif + +#else // __LINT__ + +#define STATIC_ASSERT_SIMPLE(EXPR) extern char (*_ignore(void)) +#define STATIC_ASSERT_MSG(EXPR, MSG) extern char (*_ignore(void)) + +#endif + + +#define _SELECT_ASSERT_FUNC(x, EXPR, MSG, ASSERT_MACRO, ...) ASSERT_MACRO + +/** + * @brief Static (i.e. compile time) assert macro. + * + * @note The output of STATIC_ASSERT can be different across compilers. + * + * Usage: + * STATIC_ASSERT(expression); + * STATIC_ASSERT(expression, message); + * + * @hideinitializer + */ +//lint -save -esym(???, STATIC_ASSERT) +#define STATIC_ASSERT(...) \ + _SELECT_ASSERT_FUNC(x, ##__VA_ARGS__, \ + STATIC_ASSERT_MSG(__VA_ARGS__), \ + STATIC_ASSERT_SIMPLE(__VA_ARGS__)) +//lint -restore + + +/**@brief Implementation details for NUM_VAR_ARGS */ +#define NUM_VA_ARGS_IMPL( \ + _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, N, ...) N + + +/**@brief Macro to get the number of arguments in a call variadic macro call + * + * param[in] ... List of arguments + * + * @retval Number of variadic arguments in the argument list + */ +#define NUM_VA_ARGS(...) NUM_VA_ARGS_IMPL(__VA_ARGS__, 63, 62, 61, \ + 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \ + 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \ + 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \ + 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +/**@brief Implementation details for NUM_VAR_ARGS */ +#define NUM_VA_ARGS_LESS_1_IMPL( \ + _ignored, \ + _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, N, ...) N + +/**@brief Macro to get the number of arguments in a call variadic macro call. + * First argument is not counted. + * + * param[in] ... List of arguments + * + * @retval Number of variadic arguments in the argument list + */ +#define NUM_VA_ARGS_LESS_1(...) NUM_VA_ARGS_LESS_1_IMPL(__VA_ARGS__, 63, 62, 61, \ + 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \ + 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \ + 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \ + 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ~) + + +/**@brief type for holding an encoded (i.e. little endian) 16 bit unsigned integer. */ +typedef uint8_t uint16_le_t[2]; + +/**@brief Type for holding an encoded (i.e. little endian) 32 bit unsigned integer. */ +typedef uint8_t uint32_le_t[4]; + +/**@brief Byte array type. */ +typedef struct +{ + uint16_t size; /**< Number of array entries. */ + uint8_t * p_data; /**< Pointer to array entries. */ +} uint8_array_t; + + +/**@brief Macro for performing rounded integer division (as opposed to truncating the result). + * + * @param[in] A Numerator. + * @param[in] B Denominator. + * + * @return Rounded (integer) result of dividing A by B. + */ +#define ROUNDED_DIV(A, B) (((A) + ((B) / 2)) / (B)) + + +/**@brief Macro for checking if an integer is a power of two. + * + * @param[in] A Number to be tested. + * + * @return true if value is power of two. + * @return false if value not power of two. + */ +#define IS_POWER_OF_TWO(A) ( ((A) != 0) && ((((A) - 1) & (A)) == 0) ) + + +/**@brief Macro for converting milliseconds to ticks. + * + * @param[in] TIME Number of milliseconds to convert. + * @param[in] RESOLUTION Unit to be converted to in [us/ticks]. + */ +#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) + + +/**@brief Macro for performing integer division, making sure the result is rounded up. + * + * @details One typical use for this is to compute the number of objects with size B is needed to + * hold A number of bytes. + * + * @param[in] A Numerator. + * @param[in] B Denominator. + * + * @return Integer result of dividing A by B, rounded up. + */ +#define CEIL_DIV(A, B) \ + (((A) + (B) - 1) / (B)) + + +/**@brief Macro for creating a buffer aligned to 4 bytes. + * + * @param[in] NAME Name of the buffor. + * @param[in] MIN_SIZE Size of this buffor (it will be rounded up to multiples of 4 bytes). + */ +#define WORD_ALIGNED_MEM_BUFF(NAME, MIN_SIZE) static uint32_t NAME[CEIL_DIV(MIN_SIZE, sizeof(uint32_t))] + + +/**@brief Macro for calculating the number of words that are needed to hold a number of bytes. + * + * @details Adds 3 and divides by 4. + * + * @param[in] n_bytes The number of bytes. + * + * @return The number of words that @p n_bytes take up (rounded up). + */ +#define BYTES_TO_WORDS(n_bytes) (((n_bytes) + 3) >> 2) + + +/**@brief The number of bytes in a word. + */ +#define BYTES_PER_WORD (4) + + +/**@brief Macro for increasing a number to the nearest (larger) multiple of another number. + * + * @param[in] alignment The number to align to. + * @param[in] number The number to align (increase). + * + * @return The aligned (increased) @p number. + */ +#define ALIGN_NUM(alignment, number) (((number) - 1) + (alignment) - (((number) - 1) % (alignment))) + +/**@brief Macro for getting first of 2 parameters. + * + * @param[in] a1 First parameter. + * @param[in] a2 Second parameter. + */ +#define GET_ARG_1(a1, a2) a1 + +/**@brief Macro for getting second of 2 parameters. + * + * @param[in] a1 First parameter. + * @param[in] a2 Second parameter. + */ +#define GET_ARG_2(a1, a2) a2 + + +/**@brief Container of macro (borrowed from Linux kernel). + * + * This macro returns parent structure address basing on child member address. + * + * @param ptr Address of child type. + * @param type Type of parent structure. + * @param member Name of child field in parent structure. + * + * @return Parent structure address. + * */ +#define CONTAINER_OF(ptr, type, member) \ + (type *)((char *)ptr - offsetof(type, member)) + + +/** + * @brief Define Bit-field mask + * + * Macro that defined the mask with selected number of bits set, starting from + * provided bit number. + * + * @param[in] bcnt Number of bits in the bit-field + * @param[in] boff Lowest bit number + */ +#define BF_MASK(bcnt, boff) ( ((1U << (bcnt)) - 1U) << (boff) ) + +/** + * @brief Get bit-field + * + * Macro that extracts selected bit-field from provided value + * + * @param[in] val Value from which selected bit-field would be extracted + * @param[in] bcnt Number of bits in the bit-field + * @param[in] boff Lowest bit number + * + * @return Value of the selected bits + */ +#define BF_GET(val, bcnt, boff) ( ( (val) & BF_MASK((bcnt), (boff)) ) >> (boff) ) + +/** + * @brief Create bit-field value + * + * Value is masked and shifted to match given bit-field + * + * @param[in] val Value to set on bit-field + * @param[in] bcnt Number of bits for bit-field + * @param[in] boff Offset of bit-field + * + * @return Value positioned of given bit-field. + */ +#define BF_VAL(val, bcnt, boff) ( (((uint32_t)(val)) << (boff)) & BF_MASK(bcnt, boff) ) + +/** + * @name Configuration of complex bit-field + * + * @sa BF_CX + * @{ + */ +/** @brief Position of bit count in complex bit-field value */ +#define BF_CX_BCNT_POS 0U +/** @brief Mask of bit count in complex bit-field value */ +#define BF_CX_BCNT_MASK (0xffU << BF_CX_BCNT_POS) +/** @brief Position of bit position in complex bit-field value */ +#define BF_CX_BOFF_POS 8U +/** @brief Mask of bit position in complex bit-field value */ +#define BF_CX_BOFF_MASK (0xffU << BF_CX_BOFF_POS) +/** @} */ + +/** + * @brief Define complex bit-field + * + * Complex bit-field would contain its position and size in one number. + * @sa BF_CX_MASK + * @sa BF_CX_POS + * @sa BF_CX_GET + * + * @param[in] bcnt Number of bits in the bit-field + * @param[in] boff Lowest bit number + * + * @return The single number that describes the bit-field completely. + */ +#define BF_CX(bcnt, boff) ( ((((uint32_t)(bcnt)) << BF_CX_BCNT_POS) & BF_CX_BCNT_MASK) | ((((uint32_t)(boff)) << BF_CX_BOFF_POS) & BF_CX_BOFF_MASK) ) + +/** + * @brief Get number of bits in bit-field + * + * @sa BF_CX + * + * @param bf_cx Complex bit-field + * + * @return Number of bits in given bit-field + */ +#define BF_CX_BCNT(bf_cx) ( ((bf_cx) & BF_CX_BCNT_MASK) >> BF_CX_BCNT_POS ) + +/** + * @brief Get lowest bit number in the field + * + * @sa BF_CX + * + * @param[in] bf_cx Complex bit-field + * + * @return Lowest bit number in given bit-field + */ +#define BF_CX_BOFF(bf_cx) ( ((bf_cx) & BF_CX_BOFF_MASK) >> BF_CX_BOFF_POS ) + +/** + * @brief Get bit mask of the selected field + * + * @sa BF_CX + * + * @param[in] bf_cx Complex bit-field + * + * @return Mask of given bit-field + */ +#define BF_CX_MASK(bf_cx) BF_MASK(BF_CX_BCNT(bf_cx), BF_CX_BOFF(bf_cx)) + +/** + * @brief Get bit-field + * + * Macro that extracts selected bit-field from provided value. + * Bit-field is given as a complex value. + * + * @sa BF_CX + * @sa BF_GET + * + * @param[in] val Value from which selected bit-field would be extracted + * @param[in] bf_cx Complex bit-field + * + * @return Value of the selected bits. + */ +#define BF_CX_GET(val, bf_cx) BF_GET(val, BF_CX_BCNT(bf_cx), BF_CX_BOFF(bf_cx)) + +/** + * @brief Create bit-field value + * + * Value is masked and shifted to match given bit-field. + * + * @param[in] val Value to set on bit-field + * @param[in] bf_cx Complex bit-field + * + * @return Value positioned of given bit-field. + */ +#define BF_CX_VAL(val, bf_cx) BF_VAL(val, BF_CX_BCNT(bf_cx), BF_CX_BOFF(bf_cx)) + +/** + * @brief Extracting data from the brackets + * + * This macro get rid of brackets around the argument. + * It can be used to pass multiple arguments in logical one argument to a macro. + * Call it with arguments inside brackets: + * @code + * #define ARGUMENTS (a, b, c) + * BRACKET_EXTRACT(ARGUMENTS) + * @endcode + * It would produce: + * @code + * a, b, c + * @endcode + * + * @param a Argument with anything inside brackets + * @return Anything that appears inside the brackets of the argument + * + * @note + * The argument of the macro have to be inside brackets. + * In other case the compilation would fail. + */ +#define BRACKET_EXTRACT(a) BRACKET_EXTRACT_(a) +#define BRACKET_EXTRACT_(a) BRACKET_EXTRACT__ a +#define BRACKET_EXTRACT__(...) __VA_ARGS__ + + +/** + * @brief Check if number of parameters is more than 1 + * + * @param ... Arguments to count + * + * @return 0 If argument count is <= 1 + * @return 1 If argument count is > 1 + * + * @sa NUM_VA_ARGS + * @sa NUM_IS_MORE_THAN_1 + */ +#define NUM_VA_ARGS_IS_MORE_THAN_1(...) NUM_IS_MORE_THAN_1(NUM_VA_ARGS(__VA_ARGS__)) + +/** + * @brief Check if given numeric value is bigger than 1 + * + * This macro accepts numeric value, that may be the result of argument expansion. + * This numeric value is then converted to 0 if it is lover than 1 or to 1 if + * its value is higher than 1. + * The generated result can be used to glue it into other macro mnemonic name. + * + * @param N Numeric value to check + * + * @return 0 If argument is <= 1 + * @return 1 If argument is > 1 + * + * @note Any existing definition of a form NUM_IS_MORE_THAN_1_PROBE_[N] can + * broke the result of this macro + */ +#define NUM_IS_MORE_THAN_1(N) NUM_IS_MORE_THAN_1_(N) +#define NUM_IS_MORE_THAN_1_(N) NUM_IS_MORE_THAN_1_PROBE_(NUM_IS_MORE_THAN_1_PROBE_ ## N, 1) +#define NUM_IS_MORE_THAN_1_PROBE_(...) GET_VA_ARG_1(GET_ARGS_AFTER_1(__VA_ARGS__)) +#define NUM_IS_MORE_THAN_1_PROBE_0 ~, 0 +#define NUM_IS_MORE_THAN_1_PROBE_1 ~, 0 + +/** + * @brief Get the first argument + * + * @param ... Arguments to select + * + * @return First argument or empty if no arguments are provided + */ +#define GET_VA_ARG_1(...) GET_VA_ARG_1_(__VA_ARGS__, ) // Make sure that also for 1 argument it works +#define GET_VA_ARG_1_(a1, ...) a1 + +/** + * @brief Get all the arguments but the first one + * + * @param ... Arguments to select + * + * @return All arguments after the first one or empty if less than 2 arguments are provided + */ +#define GET_ARGS_AFTER_1(...) GET_ARGS_AFTER_1_(__VA_ARGS__, ) // Make sure that also for 1 argument it works +#define GET_ARGS_AFTER_1_(a1, ...) __VA_ARGS__ + +/** + * @brief Size of a field in declared structure + * + * Macro that returns the size of the structure field. + * @param struct_type Variable type to get the field size from + * @param field Field name to analyze. It can be even field inside field (field.somethingelse.and_another). + * + * @return Size of the field + */ +#define FIELD_SIZE(struct_type, field) sizeof(((struct struct_type*)NULL)->field) + +/** + * @brief Number of elements in field array in declared structure + * + * Macro that returns number of elementy in structure field. + * @param struct_type Variable type to get the field size from + * @param field Field name to analyze. + * + * @return Number of elements in field array + * + * @sa FIELD_SIZE + */ +#define FIELD_ARRAY_SIZE(struct_type, field) (FIELD_SIZE(struct_type, field) / FIELD_SIZE(struct_type, field[0])) + +/** + * @brief Mapping macro + * + * Macro that process all arguments using given macro + * + * @param ... Macro name to be used for argument processing followed by arguments to process. + * Macro should have following form: MACRO(argument) + * + * @return All arguments processed by given macro + */ +#define MACRO_MAP(...) MACRO_MAP_(__VA_ARGS__) +#define MACRO_MAP_(...) MACRO_MAP_N(NUM_VA_ARGS_LESS_1(__VA_ARGS__), __VA_ARGS__) // To make sure it works also for 2 arguments in total + +/** + * @brief Mapping macro, recursive version + * + * Can be used in @ref MACRO_MAP macro + */ +#define MACRO_MAP_REC(...) MACRO_MAP_REC_(__VA_ARGS__) +#define MACRO_MAP_REC_(...) MACRO_MAP_REC_N(NUM_VA_ARGS_LESS_1(__VA_ARGS__), __VA_ARGS__) // To make sure it works also for 2 arguments in total +/** + * @brief Mapping N arguments macro + * + * Macro similar to @ref MACRO_MAP but maps exact number of arguments. + * If there is more arguments given, the rest would be ignored. + * + * @param N Number of arguments to map + * @param ... Macro name to be used for argument processing followed by arguments to process. + * Macro should have following form: MACRO(argument) + * + * @return Selected number of arguments processed by given macro + */ +#define MACRO_MAP_N(N, ...) MACRO_MAP_N_(N, __VA_ARGS__) +#define MACRO_MAP_N_(N, ...) CONCAT_2(MACRO_MAP_, N)(__VA_ARGS__, ) + +/** + * @brief Mapping N arguments macro, recursive version + * + * Can be used in @ref MACRO_MAP_N macro + */ +#define MACRO_MAP_REC_N(N, ...) MACRO_MAP_REC_N_(N, __VA_ARGS__) +#define MACRO_MAP_REC_N_(N, ...) CONCAT_2(MACRO_MAP_REC_, N)(__VA_ARGS__, ) + +#define MACRO_MAP_0( ...) +#define MACRO_MAP_1( macro, a, ...) macro(a) +#define MACRO_MAP_2( macro, a, ...) macro(a) MACRO_MAP_1 (macro, __VA_ARGS__, ) +#define MACRO_MAP_3( macro, a, ...) macro(a) MACRO_MAP_2 (macro, __VA_ARGS__, ) +#define MACRO_MAP_4( macro, a, ...) macro(a) MACRO_MAP_3 (macro, __VA_ARGS__, ) +#define MACRO_MAP_5( macro, a, ...) macro(a) MACRO_MAP_4 (macro, __VA_ARGS__, ) +#define MACRO_MAP_6( macro, a, ...) macro(a) MACRO_MAP_5 (macro, __VA_ARGS__, ) +#define MACRO_MAP_7( macro, a, ...) macro(a) MACRO_MAP_6 (macro, __VA_ARGS__, ) +#define MACRO_MAP_8( macro, a, ...) macro(a) MACRO_MAP_7 (macro, __VA_ARGS__, ) +#define MACRO_MAP_9( macro, a, ...) macro(a) MACRO_MAP_8 (macro, __VA_ARGS__, ) +#define MACRO_MAP_10(macro, a, ...) macro(a) MACRO_MAP_9 (macro, __VA_ARGS__, ) +#define MACRO_MAP_11(macro, a, ...) macro(a) MACRO_MAP_10(macro, __VA_ARGS__, ) +#define MACRO_MAP_12(macro, a, ...) macro(a) MACRO_MAP_11(macro, __VA_ARGS__, ) +#define MACRO_MAP_13(macro, a, ...) macro(a) MACRO_MAP_12(macro, __VA_ARGS__, ) +#define MACRO_MAP_14(macro, a, ...) macro(a) MACRO_MAP_13(macro, __VA_ARGS__, ) +#define MACRO_MAP_15(macro, a, ...) macro(a) MACRO_MAP_14(macro, __VA_ARGS__, ) +#define MACRO_MAP_16(macro, a, ...) macro(a) MACRO_MAP_15(macro, __VA_ARGS__, ) +#define MACRO_MAP_17(macro, a, ...) macro(a) MACRO_MAP_16(macro, __VA_ARGS__, ) +#define MACRO_MAP_18(macro, a, ...) macro(a) MACRO_MAP_17(macro, __VA_ARGS__, ) +#define MACRO_MAP_19(macro, a, ...) macro(a) MACRO_MAP_18(macro, __VA_ARGS__, ) +#define MACRO_MAP_20(macro, a, ...) macro(a) MACRO_MAP_19(macro, __VA_ARGS__, ) +#define MACRO_MAP_21(macro, a, ...) macro(a) MACRO_MAP_20(macro, __VA_ARGS__, ) +#define MACRO_MAP_22(macro, a, ...) macro(a) MACRO_MAP_21(macro, __VA_ARGS__, ) +#define MACRO_MAP_23(macro, a, ...) macro(a) MACRO_MAP_22(macro, __VA_ARGS__, ) +#define MACRO_MAP_24(macro, a, ...) macro(a) MACRO_MAP_23(macro, __VA_ARGS__, ) +#define MACRO_MAP_25(macro, a, ...) macro(a) MACRO_MAP_24(macro, __VA_ARGS__, ) +#define MACRO_MAP_26(macro, a, ...) macro(a) MACRO_MAP_25(macro, __VA_ARGS__, ) +#define MACRO_MAP_27(macro, a, ...) macro(a) MACRO_MAP_26(macro, __VA_ARGS__, ) +#define MACRO_MAP_28(macro, a, ...) macro(a) MACRO_MAP_27(macro, __VA_ARGS__, ) +#define MACRO_MAP_29(macro, a, ...) macro(a) MACRO_MAP_28(macro, __VA_ARGS__, ) +#define MACRO_MAP_30(macro, a, ...) macro(a) MACRO_MAP_29(macro, __VA_ARGS__, ) +#define MACRO_MAP_31(macro, a, ...) macro(a) MACRO_MAP_30(macro, __VA_ARGS__, ) +#define MACRO_MAP_32(macro, a, ...) macro(a) MACRO_MAP_31(macro, __VA_ARGS__, ) + + +#define MACRO_MAP_REC_0( ...) +#define MACRO_MAP_REC_1( macro, a, ...) macro(a) +#define MACRO_MAP_REC_2( macro, a, ...) macro(a) MACRO_MAP_REC_1 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_3( macro, a, ...) macro(a) MACRO_MAP_REC_2 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_4( macro, a, ...) macro(a) MACRO_MAP_REC_3 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_5( macro, a, ...) macro(a) MACRO_MAP_REC_4 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_6( macro, a, ...) macro(a) MACRO_MAP_REC_5 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_7( macro, a, ...) macro(a) MACRO_MAP_REC_6 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_8( macro, a, ...) macro(a) MACRO_MAP_REC_7 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_9( macro, a, ...) macro(a) MACRO_MAP_REC_8 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_10(macro, a, ...) macro(a) MACRO_MAP_REC_9 (macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_11(macro, a, ...) macro(a) MACRO_MAP_REC_10(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_12(macro, a, ...) macro(a) MACRO_MAP_REC_11(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_13(macro, a, ...) macro(a) MACRO_MAP_REC_12(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_14(macro, a, ...) macro(a) MACRO_MAP_REC_13(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_15(macro, a, ...) macro(a) MACRO_MAP_REC_14(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_16(macro, a, ...) macro(a) MACRO_MAP_REC_15(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_17(macro, a, ...) macro(a) MACRO_MAP_REC_16(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_18(macro, a, ...) macro(a) MACRO_MAP_REC_17(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_19(macro, a, ...) macro(a) MACRO_MAP_REC_18(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_20(macro, a, ...) macro(a) MACRO_MAP_REC_19(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_21(macro, a, ...) macro(a) MACRO_MAP_REC_20(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_22(macro, a, ...) macro(a) MACRO_MAP_REC_21(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_23(macro, a, ...) macro(a) MACRO_MAP_REC_22(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_24(macro, a, ...) macro(a) MACRO_MAP_REC_23(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_25(macro, a, ...) macro(a) MACRO_MAP_REC_24(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_26(macro, a, ...) macro(a) MACRO_MAP_REC_25(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_27(macro, a, ...) macro(a) MACRO_MAP_REC_26(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_28(macro, a, ...) macro(a) MACRO_MAP_REC_27(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_29(macro, a, ...) macro(a) MACRO_MAP_REC_28(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_30(macro, a, ...) macro(a) MACRO_MAP_REC_29(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_31(macro, a, ...) macro(a) MACRO_MAP_REC_30(macro, __VA_ARGS__, ) +#define MACRO_MAP_REC_32(macro, a, ...) macro(a) MACRO_MAP_REC_31(macro, __VA_ARGS__, ) + + +/** + * @brief Mapping macro with current index + * + * Basically macro similar to @ref MACRO_MAP, but the processing function would get an argument + * and current argument index (beginning from 0). + * + * @param ... Macro name to be used for argument processing followed by arguments to process. + * Macro should have following form: MACRO(argument, index) + * @return All arguments processed by given macro + */ +#define MACRO_MAP_FOR(...) MACRO_MAP_FOR_(__VA_ARGS__) +#define MACRO_MAP_FOR_N_LIST 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, \ + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 +#define MACRO_MAP_FOR_(...) MACRO_MAP_FOR_N(NUM_VA_ARGS_LESS_1(__VA_ARGS__), __VA_ARGS__) + +/** + * @brief Mapping N arguments macro with current index + * + * Macro is similar to @ref MACRO_MAP_FOR but maps exact number of arguments. + * If there is more arguments given, the rest would be ignored. + * + * @param N Number of arguments to map + * @param ... Macro name to be used for argument processing followed by arguments to process. + * Macro should have following form: MACRO(argument, index) + * + * @return Selected number of arguments processed by given macro + */ +#define MACRO_MAP_FOR_N(N, ...) MACRO_MAP_FOR_N_(N, __VA_ARGS__) +#define MACRO_MAP_FOR_N_(N, ...) CONCAT_2(MACRO_MAP_FOR_, N)((MACRO_MAP_FOR_N_LIST), __VA_ARGS__, ) + +#define MACRO_MAP_FOR_0( n_list, ...) +#define MACRO_MAP_FOR_1( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) +#define MACRO_MAP_FOR_2( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_1 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_3( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_2 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_4( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_3 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_5( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_4 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_6( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_5 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_7( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_6 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_8( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_7 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_9( n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_8 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_10(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_9 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_11(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_10((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_12(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_11((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_13(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_12((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_14(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_13((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_15(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_14((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_16(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_15((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_17(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_16((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_18(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_17((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_19(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_18((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_20(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_19((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_21(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_20((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_22(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_21((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_23(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_22((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_24(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_23((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_25(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_24((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_26(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_25((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_27(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_26((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_28(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_27((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_29(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_28((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_30(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_29((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_31(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_30((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_32(n_list, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list))) MACRO_MAP_FOR_31((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__, ) + + +/** + * @brief Mapping macro with current index and parameter + * + * Version of @ref MACRO_MAP_FOR that passes also the same parameter to all macros. + * + * @param param Parameter that would be passed to each macro call during mapping. + * @param ... Macro name to be used for argument processing followed by arguments to process. + * Macro should have following form: MACRO(argument, index, param) + * + * @return All arguments processed by given macro + */ +#define MACRO_MAP_FOR_PARAM(param, ...) MACRO_MAP_FOR_PARAM_(param, __VA_ARGS__) +#define MACRO_MAP_FOR_PARAM_(param, ...) MACRO_MAP_FOR_PARAM_N(NUM_VA_ARGS_LESS_1(__VA_ARGS__), param, __VA_ARGS__) + +/** + * @brief Mapping N arguments macro with with current index and parameter + * + * @param N Number of arguments to map + * @param param Parameter that would be passed to each macro call during mapping. + * @param ... Macro name to be used for argument processing followed by arguments to process. + * Macro should have following form: MACRO(argument, index, param) + * + * @return All arguments processed by given macro + */ +#define MACRO_MAP_FOR_PARAM_N(N, param, ...) MACRO_MAP_FOR_PARAM_N_(N, param, __VA_ARGS__) +#define MACRO_MAP_FOR_PARAM_N_(N, param, ...) CONCAT_2(MACRO_MAP_FOR_PARAM_, N)((MACRO_MAP_FOR_N_LIST), param, __VA_ARGS__, ) + + +#define MACRO_MAP_FOR_PARAM_0( n_list, param, ...) +#define MACRO_MAP_FOR_PARAM_1( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) +#define MACRO_MAP_FOR_PARAM_2( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_1 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_3( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_2 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_4( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_3 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_5( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_4 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_6( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_5 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_7( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_6 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_8( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_7 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_9( n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_8 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_10(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_9 ((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_11(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_10((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_12(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_11((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_13(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_12((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_14(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_13((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_15(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_14((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_16(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_15((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_17(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_16((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_18(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_17((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_19(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_18((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_20(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_19((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_21(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_20((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_22(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_21((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_23(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_22((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_24(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_23((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_25(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_24((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_26(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_25((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_27(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_26((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_28(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_27((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_29(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_28((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_30(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_29((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_31(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_30((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) +#define MACRO_MAP_FOR_PARAM_32(n_list, param, macro, a, ...) macro(a, GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), param) MACRO_MAP_FOR_PARAM_31((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), param, macro, __VA_ARGS__, ) + + + +/** + * @brief Repeating macro. + * + * @param count Count of repeats. + * @param macro Macro must have the following form: MACRO(arguments). + * @param ... Arguments passed to the macro. + * + * @return All arguments processed by the given macro. + */ +#define MACRO_REPEAT(count, macro, ...) MACRO_REPEAT_(count, macro, __VA_ARGS__) +#define MACRO_REPEAT_(count, macro, ...) CONCAT_2(MACRO_REPEAT_, count)(macro, __VA_ARGS__) + +#define MACRO_REPEAT_0(macro, ...) +#define MACRO_REPEAT_1(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_0(macro, __VA_ARGS__) +#define MACRO_REPEAT_2(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_1(macro, __VA_ARGS__) +#define MACRO_REPEAT_3(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_2(macro, __VA_ARGS__) +#define MACRO_REPEAT_4(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_3(macro, __VA_ARGS__) +#define MACRO_REPEAT_5(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_4(macro, __VA_ARGS__) +#define MACRO_REPEAT_6(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_5(macro, __VA_ARGS__) +#define MACRO_REPEAT_7(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_6(macro, __VA_ARGS__) +#define MACRO_REPEAT_8(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_7(macro, __VA_ARGS__) +#define MACRO_REPEAT_9(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_8(macro, __VA_ARGS__) +#define MACRO_REPEAT_10(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_9(macro, __VA_ARGS__) +#define MACRO_REPEAT_11(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_10(macro, __VA_ARGS__) +#define MACRO_REPEAT_12(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_11(macro, __VA_ARGS__) +#define MACRO_REPEAT_13(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_12(macro, __VA_ARGS__) +#define MACRO_REPEAT_14(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_13(macro, __VA_ARGS__) +#define MACRO_REPEAT_15(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_14(macro, __VA_ARGS__) +#define MACRO_REPEAT_16(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_15(macro, __VA_ARGS__) +#define MACRO_REPEAT_17(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_16(macro, __VA_ARGS__) +#define MACRO_REPEAT_18(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_17(macro, __VA_ARGS__) +#define MACRO_REPEAT_19(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_18(macro, __VA_ARGS__) +#define MACRO_REPEAT_20(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_19(macro, __VA_ARGS__) +#define MACRO_REPEAT_21(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_20(macro, __VA_ARGS__) +#define MACRO_REPEAT_22(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_21(macro, __VA_ARGS__) +#define MACRO_REPEAT_23(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_22(macro, __VA_ARGS__) +#define MACRO_REPEAT_24(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_23(macro, __VA_ARGS__) +#define MACRO_REPEAT_25(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_24(macro, __VA_ARGS__) +#define MACRO_REPEAT_26(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_25(macro, __VA_ARGS__) +#define MACRO_REPEAT_27(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_26(macro, __VA_ARGS__) +#define MACRO_REPEAT_28(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_27(macro, __VA_ARGS__) +#define MACRO_REPEAT_29(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_28(macro, __VA_ARGS__) +#define MACRO_REPEAT_30(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_29(macro, __VA_ARGS__) +#define MACRO_REPEAT_31(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_30(macro, __VA_ARGS__) +#define MACRO_REPEAT_32(macro, ...) macro(__VA_ARGS__) MACRO_REPEAT_31(macro, __VA_ARGS__) + + +/** + * @brief Repeating macro with current index. + * + * Macro similar to @ref MACRO_REPEAT but the processing function gets the arguments + * and the current argument index (beginning from 0). + + * @param count Count of repeats. + * @param macro Macro must have the following form: MACRO(index, arguments). + * @param ... Arguments passed to the macro. + * + * @return All arguments processed by the given macro. + */ +#define MACRO_REPEAT_FOR(count, macro, ...) MACRO_REPEAT_FOR_(count, macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_(count, macro, ...) CONCAT_2(MACRO_REPEAT_FOR_, count)((MACRO_MAP_FOR_N_LIST), macro, __VA_ARGS__) + +#define MACRO_REPEAT_FOR_0(n_list, macro, ...) +#define MACRO_REPEAT_FOR_1(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_0((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_2(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_1((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_3(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_2((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_4(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_3((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_5(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_4((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_6(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_5((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_7(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_6((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_8(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_7((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_9(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_8((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_10(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_9((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_11(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_10((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_12(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_11((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_13(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_12((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_14(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_13((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_15(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_14((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_16(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_15((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_17(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_16((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_18(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_17((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_19(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_18((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_20(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_19((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_21(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_20((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_22(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_21((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_23(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_22((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_24(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_23((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_25(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_24((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_26(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_25((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_27(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_26((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_28(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_27((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_29(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_28((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_30(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_29((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_31(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_30((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) +#define MACRO_REPEAT_FOR_32(n_list, macro, ...) macro(GET_VA_ARG_1(BRACKET_EXTRACT(n_list)), __VA_ARGS__) MACRO_REPEAT_FOR_31((GET_ARGS_AFTER_1(BRACKET_EXTRACT(n_list))), macro, __VA_ARGS__) + + +/**@brief Adding curly brace to the macro parameter. + * + * Useful in array of structures initialization. + * + * @param p Parameter to put into the curly brace. */ +#define PARAM_CBRACE(p) { p }, + + +/**@brief Function for changing the value unit. + * + * @param[in] value Value to be rescaled. + * @param[in] old_unit_reversal Reversal of the incoming unit. + * @param[in] new_unit_reversal Reversal of the desired unit. + * + * @return Number of bytes written. + */ +static __INLINE uint64_t value_rescale(uint32_t value, uint32_t old_unit_reversal, uint16_t new_unit_reversal) +{ + return (uint64_t)ROUNDED_DIV((uint64_t)value * new_unit_reversal, old_unit_reversal); +} + +/**@brief Function for encoding a uint16 value. + * + * @param[in] value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t uint16_encode(uint16_t value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((value & 0x00FF) >> 0); + p_encoded_data[1] = (uint8_t) ((value & 0xFF00) >> 8); + return sizeof(uint16_t); +} + +/**@brief Function for encoding a three-byte value. + * + * @param[in] value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t uint24_encode(uint32_t value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((value & 0x000000FF) >> 0); + p_encoded_data[1] = (uint8_t) ((value & 0x0000FF00) >> 8); + p_encoded_data[2] = (uint8_t) ((value & 0x00FF0000) >> 16); + return 3; +} + +/**@brief Function for encoding a uint32 value. + * + * @param[in] value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t uint32_encode(uint32_t value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((value & 0x000000FF) >> 0); + p_encoded_data[1] = (uint8_t) ((value & 0x0000FF00) >> 8); + p_encoded_data[2] = (uint8_t) ((value & 0x00FF0000) >> 16); + p_encoded_data[3] = (uint8_t) ((value & 0xFF000000) >> 24); + return sizeof(uint32_t); +} + +/**@brief Function for encoding a uint40 value. + * + * @param[in] value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t uint40_encode(uint64_t value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((value & 0x00000000FF) >> 0); + p_encoded_data[1] = (uint8_t) ((value & 0x000000FF00) >> 8); + p_encoded_data[2] = (uint8_t) ((value & 0x0000FF0000) >> 16); + p_encoded_data[3] = (uint8_t) ((value & 0x00FF000000) >> 24); + p_encoded_data[4] = (uint8_t) ((value & 0xFF00000000) >> 32); + return 5; +} + +/**@brief Function for encoding a uint48 value. + * + * @param[in] value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t uint48_encode(uint64_t value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((value & 0x0000000000FF) >> 0); + p_encoded_data[1] = (uint8_t) ((value & 0x00000000FF00) >> 8); + p_encoded_data[2] = (uint8_t) ((value & 0x000000FF0000) >> 16); + p_encoded_data[3] = (uint8_t) ((value & 0x0000FF000000) >> 24); + p_encoded_data[4] = (uint8_t) ((value & 0x00FF00000000) >> 32); + p_encoded_data[5] = (uint8_t) ((value & 0xFF0000000000) >> 40); + return 6; +} + +/**@brief Function for decoding a uint16 value. + * + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * + * @return Decoded value. + */ +static __INLINE uint16_t uint16_decode(const uint8_t * p_encoded_data) +{ + return ( (((uint16_t)((uint8_t *)p_encoded_data)[0])) | + (((uint16_t)((uint8_t *)p_encoded_data)[1]) << 8 )); +} + +/**@brief Function for decoding a uint16 value in big-endian format. + * + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * + * @return Decoded value. + */ +static __INLINE uint16_t uint16_big_decode(const uint8_t * p_encoded_data) +{ + return ( (((uint16_t)((uint8_t *)p_encoded_data)[0]) << 8 ) | + (((uint16_t)((uint8_t *)p_encoded_data)[1])) ); +} + +/**@brief Function for decoding a three-byte value. + * + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * + * @return Decoded value (uint32_t). + */ +static __INLINE uint32_t uint24_decode(const uint8_t * p_encoded_data) +{ + return ( (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 0) | + (((uint32_t)((uint8_t *)p_encoded_data)[1]) << 8) | + (((uint32_t)((uint8_t *)p_encoded_data)[2]) << 16)); +} + +/**@brief Function for decoding a uint32 value. + * + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * + * @return Decoded value. + */ +static __INLINE uint32_t uint32_decode(const uint8_t * p_encoded_data) +{ + return ( (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 0) | + (((uint32_t)((uint8_t *)p_encoded_data)[1]) << 8) | + (((uint32_t)((uint8_t *)p_encoded_data)[2]) << 16) | + (((uint32_t)((uint8_t *)p_encoded_data)[3]) << 24 )); +} + +/**@brief Function for decoding a uint32 value in big-endian format. + * + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * + * @return Decoded value. + */ +static __INLINE uint32_t uint32_big_decode(const uint8_t * p_encoded_data) +{ + return ( (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 24) | + (((uint32_t)((uint8_t *)p_encoded_data)[1]) << 16) | + (((uint32_t)((uint8_t *)p_encoded_data)[2]) << 8) | + (((uint32_t)((uint8_t *)p_encoded_data)[3]) << 0) ); +} + +/** + * @brief Function for encoding an uint16 value in big-endian format. + * + * @param[in] value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data will be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t uint16_big_encode(uint16_t value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) (value >> 8); + p_encoded_data[1] = (uint8_t) (value & 0xFF); + + return sizeof(uint16_t); +} + +/*lint -esym(526, __rev) */ +/*lint -esym(628, __rev) */ +/**@brief Function for encoding a uint32 value in big-endian format. + * + * @param[in] value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data will be written. + * The address pointed to must be word alligned. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t uint32_big_encode(uint32_t value, uint8_t * p_encoded_data) +{ + *(uint32_t *)p_encoded_data = __REV(value); + return sizeof(uint32_t); +} + +/**@brief Function for decoding a uint40 value. + * + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * + * @return Decoded value. (uint64_t) + */ +static __INLINE uint64_t uint40_decode(const uint8_t * p_encoded_data) +{ + return ( (((uint64_t)((uint8_t *)p_encoded_data)[0]) << 0) | + (((uint64_t)((uint8_t *)p_encoded_data)[1]) << 8) | + (((uint64_t)((uint8_t *)p_encoded_data)[2]) << 16) | + (((uint64_t)((uint8_t *)p_encoded_data)[3]) << 24) | + (((uint64_t)((uint8_t *)p_encoded_data)[4]) << 32 )); +} + +/**@brief Function for decoding a uint48 value. + * + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * + * @return Decoded value. (uint64_t) + */ +static __INLINE uint64_t uint48_decode(const uint8_t * p_encoded_data) +{ + return ( (((uint64_t)((uint8_t *)p_encoded_data)[0]) << 0) | + (((uint64_t)((uint8_t *)p_encoded_data)[1]) << 8) | + (((uint64_t)((uint8_t *)p_encoded_data)[2]) << 16) | + (((uint64_t)((uint8_t *)p_encoded_data)[3]) << 24) | + (((uint64_t)((uint8_t *)p_encoded_data)[4]) << 32) | + (((uint64_t)((uint8_t *)p_encoded_data)[5]) << 40 )); +} + +/** @brief Function for converting the input voltage (in milli volts) into percentage of 3.0 Volts. + * + * @details The calculation is based on a linearized version of the battery's discharge + * curve. 3.0V returns 100% battery level. The limit for power failure is 2.1V and + * is considered to be the lower boundary. + * + * The discharge curve for CR2032 is non-linear. In this model it is split into + * 4 linear sections: + * - Section 1: 3.0V - 2.9V = 100% - 42% (58% drop on 100 mV) + * - Section 2: 2.9V - 2.74V = 42% - 18% (24% drop on 160 mV) + * - Section 3: 2.74V - 2.44V = 18% - 6% (12% drop on 300 mV) + * - Section 4: 2.44V - 2.1V = 6% - 0% (6% drop on 340 mV) + * + * These numbers are by no means accurate. Temperature and + * load in the actual application is not accounted for! + * + * @param[in] mvolts The voltage in mV + * + * @return Battery level in percent. +*/ +static __INLINE uint8_t battery_level_in_percent(const uint16_t mvolts) +{ + uint8_t battery_level; + + if (mvolts >= 3000) + { + battery_level = 100; + } + else if (mvolts > 2900) + { + battery_level = 100 - ((3000 - mvolts) * 58) / 100; + } + else if (mvolts > 2740) + { + battery_level = 42 - ((2900 - mvolts) * 24) / 160; + } + else if (mvolts > 2440) + { + battery_level = 18 - ((2740 - mvolts) * 12) / 300; + } + else if (mvolts > 2100) + { + battery_level = 6 - ((2440 - mvolts) * 6) / 340; + } + else + { + battery_level = 0; + } + + return battery_level; +} + +/**@brief Function for checking if a pointer value is aligned to a 4 byte boundary. + * + * @param[in] p Pointer value to be checked. + * + * @return TRUE if pointer is aligned to a 4 byte boundary, FALSE otherwise. + */ +static __INLINE bool is_word_aligned(void const* p) +{ + return (((uintptr_t)p & 0x03) == 0); +} + +/*lint -e{568, 685} */ +/** + * @brief Function for checking if provided address is located in stack space. + * + * @param[in] ptr Pointer to be checked. + * + * @return true if address is in stack space, false otherwise. + */ +static __INLINE bool is_address_from_stack(void * ptr) +{ + if (((uint32_t)ptr >= (uint32_t)STACK_BASE) && + ((uint32_t)ptr < (uint32_t)STACK_TOP) ) + { + return true; + } + else + { + return false; + } +} + +#ifdef __cplusplus +} +#endif + +#endif // APP_UTIL_H__ + +/** @} */ diff --git a/libraries/nfc/src/util/app_util_bds.h b/libraries/nfc/src/util/app_util_bds.h new file mode 100644 index 000000000..4d1aeb030 --- /dev/null +++ b/libraries/nfc/src/util/app_util_bds.h @@ -0,0 +1,449 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup app_util Utility Functions and Definitions + * @{ + * @ingroup app_common + * + * @brief Various types and definitions available to all applications. + */ + +#ifndef APP_UTIL_BDS_H__ +#define APP_UTIL_BDS_H__ + +#include +#include +#include +#include "compiler_abstraction.h" +#include "app_util.h" +#include "ble_srv_common.h" +#include "nordic_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint8_t nibble_t; +typedef uint32_t uint24_t; +typedef uint64_t uint40_t; + +/**@brief IEEE 11073-20601 Regulatory Certification Data List Structure */ +typedef struct +{ + uint8_t * p_list; /**< Pointer the byte array containing the encoded opaque structure based on IEEE 11073-20601 specification. */ + uint8_t list_len; /**< Length of the byte array. */ +} regcertdatalist_t; + +/**@brief SFLOAT format (IEEE-11073 16-bit FLOAT, meaning 4 bits for exponent (base 10) and 12 bits mantissa) */ +typedef struct +{ + int8_t exponent; /**< Base 10 exponent, should be using only 4 bits */ + int16_t mantissa; /**< Mantissa, should be using only 12 bits */ +} sfloat_t; + +/**@brief Date and Time structure. */ +typedef struct +{ + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; +} ble_date_time_t; + + +/**@brief Function for encoding a uint16 value. + * + * @param[in] p_value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t bds_uint16_encode(const uint16_t * p_value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((*p_value & 0x00FF) >> 0); + p_encoded_data[1] = (uint8_t) ((*p_value & 0xFF00) >> 8); + return sizeof(uint16_t); +} + +static __INLINE uint8_t bds_int16_encode(const int16_t * p_value, uint8_t * p_encoded_data) +{ + uint16_t tmp = *p_value; + return bds_uint16_encode(&tmp, p_encoded_data); +} + +/**@brief Function for encoding a uint24 value. + * + * @param[in] p_value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t bds_uint24_encode(const uint32_t * p_value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((*p_value & 0x000000FF) >> 0); + p_encoded_data[1] = (uint8_t) ((*p_value & 0x0000FF00) >> 8); + p_encoded_data[2] = (uint8_t) ((*p_value & 0x00FF0000) >> 16); + return (3); +} + + +/**@brief Function for encoding a uint32 value. + * + * @param[in] p_value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t bds_uint32_encode(const uint32_t * p_value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((*p_value & 0x000000FF) >> 0); + p_encoded_data[1] = (uint8_t) ((*p_value & 0x0000FF00) >> 8); + p_encoded_data[2] = (uint8_t) ((*p_value & 0x00FF0000) >> 16); + p_encoded_data[3] = (uint8_t) ((*p_value & 0xFF000000) >> 24); + return sizeof(uint32_t); +} + + +/**@brief Function for encoding a uint40 value. + * + * @param[in] p_value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t bds_uint40_encode(const uint64_t * p_value, uint8_t * p_encoded_data) +{ + p_encoded_data[0] = (uint8_t) ((*p_value & 0x00000000000000FF) >> 0); + p_encoded_data[1] = (uint8_t) ((*p_value & 0x000000000000FF00) >> 8); + p_encoded_data[2] = (uint8_t) ((*p_value & 0x0000000000FF0000) >> 16); + p_encoded_data[3] = (uint8_t) ((*p_value & 0x00000000FF000000) >> 24); + p_encoded_data[4] = (uint8_t) ((*p_value & 0x000000FF00000000) >> 32); + return 5; +} + +/**@brief Function for encoding a sfloat value. + * + * @param[in] p_value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + * + * @return Number of bytes written. + */ +static __INLINE uint8_t bds_sfloat_encode(const sfloat_t * p_value, uint8_t * p_encoded_data) +{ + uint16_t encoded_val; + + encoded_val = ((p_value->exponent << 12) & 0xF000) | + ((p_value->mantissa << 0) & 0x0FFF); + + return(bds_uint16_encode(&encoded_val, p_encoded_data)); +} + + +/**@brief Function for encoding a uint8_array value. + * + * @param[in] p_value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + */ +static __INLINE uint8_t bds_uint8_array_encode(const uint8_array_t * p_value, + uint8_t * p_encoded_data) +{ + memcpy(p_encoded_data, p_value->p_data, p_value->size); + return p_value->size; +} + + +/**@brief Function for encoding a utf8_str value. + * + * @param[in] p_value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + + */ +static __INLINE uint8_t bds_ble_srv_utf8_str_encode(const ble_srv_utf8_str_t * p_value, + uint8_t * p_encoded_data) +{ + memcpy(p_encoded_data, p_value->p_str, p_value->length); + return p_value->length; +} + +/**@brief Function for encoding a regcertdatalist value. + * + * @param[in] p_value Value to be encoded. + * @param[out] p_encoded_data Buffer where the encoded data is to be written. + + */ +static __INLINE uint8_t bds_regcertdatalist_encode(const regcertdatalist_t * p_value, + uint8_t * p_encoded_data) +{ + memcpy(p_encoded_data, p_value->p_list, p_value->list_len); + return p_value->list_len; +} + + +/**@brief Function for decoding a date_time value. + * + * @param[in] p_date_time pointer to the date_time structure to encode. + * @param[in] p_encoded_data pointer to the encoded data + * @return length of the encoded field. + */ +static __INLINE uint8_t bds_ble_date_time_encode(const ble_date_time_t * p_date_time, + uint8_t * p_encoded_data) +{ + uint8_t len = bds_uint16_encode(&p_date_time->year, &p_encoded_data[0]); + + p_encoded_data[len++] = p_date_time->month; + p_encoded_data[len++] = p_date_time->day; + p_encoded_data[len++] = p_date_time->hours; + p_encoded_data[len++] = p_date_time->minutes; + p_encoded_data[len++] = p_date_time->seconds; + + return len; +} + + +/**@brief Function for decoding a uint16 value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_uint16_decode(const uint8_t len, + const uint8_t * p_encoded_data, + uint16_t * p_decoded_val) +{ + UNUSED_VARIABLE(len); + *p_decoded_val = (((uint16_t)((uint8_t *)p_encoded_data)[0])) | + (((uint16_t)((uint8_t *)p_encoded_data)[1]) << 8 ); + return (sizeof(uint16_t)); +} + + +/**@brief Function for decoding a int16 value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_int16_decode(const uint8_t len, + const uint8_t * p_encoded_data, + int16_t * p_decoded_val) +{ + UNUSED_VARIABLE(len); + uint16_t tmp = 0; + uint8_t retval = bds_uint16_decode(len, p_encoded_data, &tmp); + *p_decoded_val = (int16_t)tmp; + return retval; +} + + +/**@brief Function for decoding a uint24 value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_uint24_decode(const uint8_t len, + const uint8_t * p_encoded_data, + uint32_t * p_decoded_val) +{ + UNUSED_VARIABLE(len); + *p_decoded_val = (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 0) | + (((uint32_t)((uint8_t *)p_encoded_data)[1]) << 8) | + (((uint32_t)((uint8_t *)p_encoded_data)[2]) << 16); + return (3); +} + + +/**@brief Function for decoding a uint32 value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_uint32_decode(const uint8_t len, + const uint8_t * p_encoded_data, + uint32_t * p_decoded_val) +{ + UNUSED_VARIABLE(len); + *p_decoded_val = (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 0) | + (((uint32_t)((uint8_t *)p_encoded_data)[1]) << 8) | + (((uint32_t)((uint8_t *)p_encoded_data)[2]) << 16) | + (((uint32_t)((uint8_t *)p_encoded_data)[3]) << 24 ); + return (sizeof(uint32_t)); +} + + +/**@brief Function for decoding a uint40 value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_uint40_decode(const uint8_t len, + const uint8_t * p_encoded_data, + uint64_t * p_decoded_val) +{ + UNUSED_VARIABLE(len); + *p_decoded_val = (((uint64_t)((uint8_t *)p_encoded_data)[0]) << 0) | + (((uint64_t)((uint8_t *)p_encoded_data)[1]) << 8) | + (((uint64_t)((uint8_t *)p_encoded_data)[2]) << 16) | + (((uint64_t)((uint8_t *)p_encoded_data)[3]) << 24 )| + (((uint64_t)((uint8_t *)p_encoded_data)[4]) << 32 ); + return (40); +} + + +/**@brief Function for decoding a sfloat value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * + * @return length of the decoded field. + + */ +static __INLINE uint8_t bds_sfloat_decode(const uint8_t len, + const uint8_t * p_encoded_data, + sfloat_t * p_decoded_val) +{ + + p_decoded_val->exponent = 0; + bds_uint16_decode(len, p_encoded_data, (uint16_t*)&p_decoded_val->mantissa); + p_decoded_val->exponent = (uint8_t)((p_decoded_val->mantissa & 0xF000) >> 12); + p_decoded_val->mantissa &= 0x0FFF; + return len; +} + + +/**@brief Function for decoding a uint8_array value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_uint8_array_decode(const uint8_t len, + const uint8_t * p_encoded_data, + uint8_array_t * p_decoded_val) +{ + memcpy(p_decoded_val->p_data, p_encoded_data, len); + p_decoded_val->size = len; + return p_decoded_val->size; +} + + +/**@brief Function for decoding a utf8_str value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_ble_srv_utf8_str_decode(const uint8_t len, + const uint8_t * p_encoded_data, + ble_srv_utf8_str_t * p_decoded_val) +{ + p_decoded_val->p_str = (uint8_t*)p_encoded_data; + p_decoded_val->length = len; + return p_decoded_val->length; +} + + +/**@brief Function for decoding a regcertdatalist value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_decoded_val pointer to the decoded value + * + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_regcertdatalist_decode(const uint8_t len, + const uint8_t * p_encoded_data, + regcertdatalist_t * p_decoded_val) +{ + memcpy(p_decoded_val->p_list, p_encoded_data, len); + p_decoded_val->list_len = len; + return p_decoded_val->list_len; +} + + +/**@brief Function for decoding a date_time value. + * + * @param[in] len length of the field to be decoded. + * @param[in] p_encoded_data Buffer where the encoded data is stored. + * @param[in] p_date_time pointer to the decoded value + * + * @return length of the decoded field. + */ +static __INLINE uint8_t bds_ble_date_time_decode(const uint8_t len, + const uint8_t * p_encoded_data, + ble_date_time_t * p_date_time) +{ + UNUSED_VARIABLE(len); + uint8_t pos = bds_uint16_decode(len, &p_encoded_data[0], &p_date_time->year); + p_date_time->month = p_encoded_data[pos++]; + p_date_time->day = p_encoded_data[pos++]; + p_date_time->hours = p_encoded_data[pos++]; + p_date_time->minutes = p_encoded_data[pos++]; + p_date_time->seconds = p_encoded_data[pos++]; + + return pos; +} + + +#ifdef __cplusplus +} +#endif + +#endif // APP_UTIL_BDS_H__ + +/** @} */ diff --git a/libraries/nfc/src/util/app_util_platform.c b/libraries/nfc/src/util/app_util_platform.c new file mode 100644 index 000000000..8e29efcf4 --- /dev/null +++ b/libraries/nfc/src/util/app_util_platform.c @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2014 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "app_util_platform.h" + +// #ifdef SOFTDEVICE_PRESENT +// /* Global nvic state instance, required by nrf_nvic.h */ +// extern nrf_nvic_state_t nrf_nvic_state; +// #endif + +static uint32_t m_in_critical_region = 0; + +void app_util_disable_irq(void) +{ + __disable_irq(); + m_in_critical_region++; +} + +void app_util_enable_irq(void) +{ + m_in_critical_region--; + if (m_in_critical_region == 0) + { + __enable_irq(); + } +} + +void app_util_critical_region_enter(uint8_t *p_nested) +{ +#if __CORTEX_M == (0x04U) + ASSERT(APP_LEVEL_PRIVILEGED == privilege_level_get()) +#endif + +// #if defined(SOFTDEVICE_PRESENT) +// /* return value can be safely ignored */ +// (void) sd_nvic_critical_region_enter(p_nested); +// #else + app_util_disable_irq(); +//#endif +} + +void app_util_critical_region_exit(uint8_t nested) +{ +#if __CORTEX_M == (0x04U) + ASSERT(APP_LEVEL_PRIVILEGED == privilege_level_get()) +#endif + +// #if defined(SOFTDEVICE_PRESENT) +// /* return value can be safely ignored */ +// (void) sd_nvic_critical_region_exit(nested); +// #else + app_util_enable_irq(); +//#endif +} + + +uint8_t privilege_level_get(void) +{ +#if __CORTEX_M == (0x00U) || defined(_WIN32) || defined(__unix) || defined(__APPLE__) + /* the Cortex-M0 has no concept of privilege */ + return APP_LEVEL_PRIVILEGED; +#elif __CORTEX_M >= (0x04U) + uint32_t isr_vector_num = __get_IPSR() & IPSR_ISR_Msk ; + if (0 == isr_vector_num) + { + /* Thread Mode, check nPRIV */ + int32_t control = __get_CONTROL(); + return control & CONTROL_nPRIV_Msk ? APP_LEVEL_UNPRIVILEGED : APP_LEVEL_PRIVILEGED; + } + else + { + /* Handler Mode, always privileged */ + return APP_LEVEL_PRIVILEGED; + } +#endif +} + + +uint8_t current_int_priority_get(void) +{ + uint32_t isr_vector_num = __get_IPSR() & IPSR_ISR_Msk ; + if (isr_vector_num > 0) + { + int32_t irq_type = ((int32_t)isr_vector_num - EXTERNAL_INT_VECTOR_OFFSET); + return (NVIC_GetPriority((IRQn_Type)irq_type) & 0xFF); + } + else + { + return APP_IRQ_PRIORITY_THREAD; + } +} diff --git a/libraries/nfc/src/util/app_util_platform.h b/libraries/nfc/src/util/app_util_platform.h new file mode 100644 index 000000000..4bbc9a977 --- /dev/null +++ b/libraries/nfc/src/util/app_util_platform.h @@ -0,0 +1,278 @@ +/** + * Copyright (c) 2014 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup app_util_platform Utility Functions and Definitions (Platform) + * @{ + * @ingroup app_common + * + * @brief Various types and definitions available to all applications when using SoftDevice. + */ + +#ifndef APP_UTIL_PLATFORM_H__ +#define APP_UTIL_PLATFORM_H__ + +#include +#include "compiler_abstraction.h" +#include "nrf.h" +// #ifdef SOFTDEVICE_PRESENT +// #include "nrf_soc.h" +// #include "nrf_nvic.h" +// #endif +#include "nrf_assert.h" +#include "app_error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if __CORTEX_M == (0x00U) +#define _PRIO_SD_HIGH 0 +#define _PRIO_APP_HIGH 1 +#define _PRIO_APP_MID 1 +#define _PRIO_SD_LOW 2 +#define _PRIO_APP_LOW_MID 3 +#define _PRIO_APP_LOW 3 +#define _PRIO_APP_LOWEST 3 +#define _PRIO_THREAD 4 +#elif __CORTEX_M >= (0x04U) +#define _PRIO_SD_HIGH 0 +#define _PRIO_SD_MID 1 +#define _PRIO_APP_HIGH 2 +#define _PRIO_APP_MID 3 +#define _PRIO_SD_LOW 4 +#define _PRIO_APP_LOW_MID 5 +#define _PRIO_APP_LOW 6 +#define _PRIO_APP_LOWEST 7 +#define _PRIO_THREAD 15 +#else + #error "No platform defined" +#endif + + +//lint -save -e113 -e452 +/**@brief The interrupt priorities available to the application while the SoftDevice is active. */ +typedef enum +{ +// #ifndef SOFTDEVICE_PRESENT +// APP_IRQ_PRIORITY_HIGHEST = _PRIO_SD_HIGH, /**< Running in Application Highest interrupt level. */ +// #else + APP_IRQ_PRIORITY_HIGHEST = _PRIO_APP_HIGH, /**< Running in Application Highest interrupt level. */ +//#endif + APP_IRQ_PRIORITY_HIGH = _PRIO_APP_HIGH, /**< Running in Application High interrupt level. */ +// #ifndef SOFTDEVICE_PRESENT +// APP_IRQ_PRIORITY_MID = _PRIO_SD_LOW, /**< Running in Application Middle interrupt level. */ +// #else + APP_IRQ_PRIORITY_MID = _PRIO_APP_MID, /**< Running in Application Middle interrupt level. */ +//#endif + APP_IRQ_PRIORITY_LOW_MID = _PRIO_APP_LOW_MID, /**< Running in Application Middle Low interrupt level. */ + APP_IRQ_PRIORITY_LOW = _PRIO_APP_LOW, /**< Running in Application Low interrupt level. */ + APP_IRQ_PRIORITY_LOWEST = _PRIO_APP_LOWEST, /**< Running in Application Lowest interrupt level. */ + APP_IRQ_PRIORITY_THREAD = _PRIO_THREAD /**< Running in Thread Mode. */ +} app_irq_priority_t; +//lint -restore + + +/*@brief The privilege levels available to applications in Thread Mode */ +typedef enum +{ + APP_LEVEL_UNPRIVILEGED, + APP_LEVEL_PRIVILEGED +} app_level_t; + +/**@cond NO_DOXYGEN */ +#define EXTERNAL_INT_VECTOR_OFFSET 16 +/**@endcond */ + +/**@brief Macro for setting a breakpoint. + */ +#if defined(__GNUC__) +#define NRF_BREAKPOINT __asm__("BKPT 0"); +#else +#define NRF_BREAKPOINT __BKPT(0) +#endif + +/** @brief Macro for setting a breakpoint. + * + * If it is possible to detect debugger presence then it is set only in that case. + * + */ +#if __CORTEX_M == 0x04 +#define NRF_BREAKPOINT_COND do { \ + /* C_DEBUGEN == 1 -> Debugger Connected */ \ + if (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) \ + { \ + /* Generate breakpoint if debugger is connected */ \ + NRF_BREAKPOINT; \ + } \ + }while (0) +#else +#define NRF_BREAKPOINT_COND NRF_BREAKPOINT +#endif // __CORTEX_M == 0x04 + +#if defined ( __CC_ARM ) +#define PACKED(TYPE) __packed TYPE +#define PACKED_STRUCT PACKED(struct) +#elif defined ( __GNUC__ ) +#define PACKED __attribute__((packed)) +#define PACKED_STRUCT struct PACKED +#elif defined (__ICCARM__) +#define PACKED_STRUCT __packed struct +#endif + +#if defined ( __CC_ARM ) +#define PRAGMA_OPTIMIZATION_FORCE_START _Pragma ("push") \ + _Pragma ("O3") +#define PRAGMA_OPTIMIZATION_FORCE_END _Pragma ("pop") +#elif defined ( __GNUC__ ) +#define PRAGMA_OPTIMIZATION_FORCE_START _Pragma("GCC push_options") \ + _Pragma ("GCC optimize (\"Os\")") +#define PRAGMA_OPTIMIZATION_FORCE_END _Pragma ("GCC pop_options") +#elif defined (__ICCARM__) +#define PRAGMA_OPTIMIZATION_FORCE_START _Pragma ("optimize=high z") +#define PRAGMA_OPTIMIZATION_FORCE_END +#endif + + +void app_util_critical_region_enter (uint8_t *p_nested); +void app_util_critical_region_exit (uint8_t nested); + +/**@brief Macro for entering a critical region. + * + * @note Due to implementation details, there must exist one and only one call to + * CRITICAL_REGION_EXIT() for each call to CRITICAL_REGION_ENTER(), and they must be located + * in the same scope. + */ +// #ifdef SOFTDEVICE_PRESENT +// #define CRITICAL_REGION_ENTER() \ +// { \ +// uint8_t __CR_NESTED = 0; \ +// app_util_critical_region_enter(&__CR_NESTED); +// #else +#define CRITICAL_REGION_ENTER() app_util_critical_region_enter(NULL) +//#endif + +/**@brief Macro for leaving a critical region. + * + * @note Due to implementation details, there must exist one and only one call to + * CRITICAL_REGION_EXIT() for each call to CRITICAL_REGION_ENTER(), and they must be located + * in the same scope. + */ +// #ifdef SOFTDEVICE_PRESENT +// #define CRITICAL_REGION_EXIT() \ +// app_util_critical_region_exit(__CR_NESTED); \ +// } +// #else +#define CRITICAL_REGION_EXIT() app_util_critical_region_exit(0) +//#endif + +/* Workaround for Keil 4 */ +#ifndef IPSR_ISR_Msk +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ +#endif + + + +/**@brief Macro to enable anonymous unions from a certain point in the code. + */ +#if defined(__CC_ARM) + #define ANON_UNIONS_ENABLE _Pragma("push") \ + _Pragma("anon_unions") \ + struct semicolon_swallower +#elif defined(__ICCARM__) + #define ANON_UNIONS_ENABLE _Pragma("language=extended") \ + struct semicolon_swallower +#else + #define ANON_UNIONS_ENABLE struct semicolon_swallower + // No action will be taken. + // For GCC anonymous unions are enabled by default. +#endif + +/**@brief Macro to disable anonymous unions from a certain point in the code. + * @note Call only after first calling @ref ANON_UNIONS_ENABLE. + */ +#if defined(__CC_ARM) + #define ANON_UNIONS_DISABLE _Pragma("pop") \ + struct semicolon_swallower +#elif defined(__ICCARM__) + #define ANON_UNIONS_DISABLE struct semicolon_swallower + // for IAR leave anonymous unions enabled +#else + #define ANON_UNIONS_DISABLE struct semicolon_swallower + // No action will be taken. + // For GCC anonymous unions are enabled by default. +#endif + +/**@brief Macro for adding pragma directive only for GCC. + */ +#ifdef __GNUC__ +#define GCC_PRAGMA(v) _Pragma(v) +#else +#define GCC_PRAGMA(v) +#endif + +/* Workaround for Keil 4 */ +#ifndef CONTROL_nPRIV_Msk +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ +#endif + +/**@brief Function for finding the current interrupt level. + * + * @return Current interrupt level. See @ref app_irq_priority_t for values. + */ +uint8_t current_int_priority_get(void); + + +/**@brief Function for finding out the current privilege level. + * + * @return Current privilege level. + * @retval APP_LEVEL_UNPRIVILEGED We are running in unprivileged level. + * @retval APP_LEVEL_PRIVILEGED We are running in privileged level. + */ +uint8_t privilege_level_get(void); + + +#ifdef __cplusplus +} +#endif + +#endif // APP_UTIL_PLATFORM_H__ + +/** @} */ diff --git a/libraries/nfc/src/util/ble_advdata.c b/libraries/nfc/src/util/ble_advdata.c new file mode 100644 index 000000000..f4a7ca624 --- /dev/null +++ b/libraries/nfc/src/util/ble_advdata.c @@ -0,0 +1,838 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "util/ble_advdata.h" +#include "ble_gap.h" +#include "ble_srv_common.h" +#include "util/sdk_common.h" + +// NOTE: For now, Security Manager Out of Band Flags (OOB) are omitted from the advertising data. + + +// Types of LE Bluetooth Device Address AD type +#define AD_TYPE_BLE_DEVICE_ADDR_TYPE_PUBLIC 0UL +#define AD_TYPE_BLE_DEVICE_ADDR_TYPE_RANDOM 1UL + +#define UUID16_SIZE 2 /**< Size of 16 bit UUID. */ +#define UUID32_SIZE 4 /**< Size of 32 bit UUID. */ +#define UUID128_SIZE 16 /**< Size of 128 bit UUID. */ + +#define N_AD_TYPES 2 /**< The number of Advertising data types to search for at a time. */ + + +static ret_code_t ble_device_addr_encode(uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + ret_code_t err_code; + ble_gap_addr_t device_addr; + + // Check for buffer overflow. + if (((*p_offset) + AD_TYPE_BLE_DEVICE_ADDR_SIZE) > max_size) + { + return NRF_ERROR_DATA_SIZE; + } + + // Get BLE address. + err_code = sd_ble_gap_addr_get(&device_addr); + VERIFY_SUCCESS(err_code); + + // Encode LE Bluetooth Device Address. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + + AD_TYPE_BLE_DEVICE_ADDR_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS; + *p_offset += AD_TYPE_FIELD_SIZE; + memcpy(&p_encoded_data[*p_offset], &device_addr.addr[0], BLE_GAP_ADDR_LEN); + *p_offset += BLE_GAP_ADDR_LEN; + if (BLE_GAP_ADDR_TYPE_PUBLIC == device_addr.addr_type) + { + p_encoded_data[*p_offset] = AD_TYPE_BLE_DEVICE_ADDR_TYPE_PUBLIC; + } + else + { + p_encoded_data[*p_offset] = AD_TYPE_BLE_DEVICE_ADDR_TYPE_RANDOM; + } + *p_offset += AD_TYPE_BLE_DEVICE_ADDR_TYPE_SIZE; + + return NRF_SUCCESS; +} + +static ret_code_t name_encode(const ble_advdata_t * p_advdata, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + ret_code_t err_code; + uint16_t rem_adv_data_len; + uint16_t actual_length; + uint8_t adv_data_format; + + + // Validate parameters + if ((BLE_ADVDATA_SHORT_NAME == p_advdata->name_type) && (0 == p_advdata->short_name_len)) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Check for buffer overflow. + if ( (((*p_offset) + AD_DATA_OFFSET) > max_size) || + ( (BLE_ADVDATA_SHORT_NAME == p_advdata->name_type) && + (((*p_offset) + AD_DATA_OFFSET + p_advdata->short_name_len) > max_size))) + { + return NRF_ERROR_DATA_SIZE; + } + + rem_adv_data_len = max_size - (*p_offset) - AD_DATA_OFFSET; + actual_length = rem_adv_data_len; + + // Get GAP device name and length + err_code = sd_ble_gap_device_name_get(&p_encoded_data[(*p_offset) + AD_DATA_OFFSET], + &actual_length); + VERIFY_SUCCESS(err_code); + + // Check if device intend to use short name and it can fit available data size. + // If the name is shorter than the preferred short name length then it is no longer + // a short name and is in fact the complete name of the device. + if (((p_advdata->name_type == BLE_ADVDATA_FULL_NAME) || + (actual_length <= p_advdata->short_name_len)) && + (actual_length <= rem_adv_data_len)) + { + // Complete device name can fit, setting Complete Name in Adv Data. + adv_data_format = BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME; + } + else + { + // Else short name needs to be used. Or application has requested use of short name. + adv_data_format = BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME; + + // If application has set a preference on the short name size, it needs to be considered, + // else fit what can be fit. + if ((BLE_ADVDATA_SHORT_NAME == p_advdata->name_type) && + (p_advdata->short_name_len <= rem_adv_data_len)) + { + // Short name fits available size. + actual_length = p_advdata->short_name_len; + } + // Else whatever can fit the data buffer will be packed. + else + { + actual_length = rem_adv_data_len; + } + } + + // There is only 1 byte intended to encode length which is (actual_length + AD_TYPE_FIELD_SIZE) + if (actual_length > (0x00FF - AD_TYPE_FIELD_SIZE)) + { + return NRF_ERROR_DATA_SIZE; + } + + // Complete name field in encoded data. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + actual_length); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = adv_data_format; + *p_offset += AD_TYPE_FIELD_SIZE; + *p_offset += actual_length; + + return NRF_SUCCESS; +} + + +static ret_code_t appearance_encode(uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + ret_code_t err_code; + uint16_t appearance; + + // Check for buffer overflow. + if (((*p_offset) + AD_TYPE_APPEARANCE_SIZE) > max_size) + { + return NRF_ERROR_DATA_SIZE; + } + + // Get GAP appearance field. + err_code = sd_ble_gap_appearance_get(&appearance); + VERIFY_SUCCESS(err_code); + + // Encode Length, AD Type and Appearance. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + AD_TYPE_APPEARANCE_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_APPEARANCE; + *p_offset += AD_TYPE_FIELD_SIZE; + *p_offset += uint16_encode(appearance, &p_encoded_data[*p_offset]); + + return NRF_SUCCESS; +} + +static ret_code_t flags_encode(int8_t flags, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + // Check for buffer overflow. + if (((*p_offset) + AD_TYPE_FLAGS_SIZE) > max_size) + { + return NRF_ERROR_DATA_SIZE; + } + + // Encode flags. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + AD_TYPE_FLAGS_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_FLAGS; + *p_offset += AD_TYPE_FIELD_SIZE; + p_encoded_data[*p_offset] = flags; + *p_offset += AD_TYPE_FLAGS_DATA_SIZE; + + return NRF_SUCCESS; +} + +static ret_code_t tx_power_level_encode(int8_t tx_power_level, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + // Check for buffer overflow. + if (((*p_offset) + AD_TYPE_TX_POWER_LEVEL_SIZE) > max_size) + { + return NRF_ERROR_DATA_SIZE; + } + + // Encode TX Power Level. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + + AD_TYPE_TX_POWER_LEVEL_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_TX_POWER_LEVEL; + *p_offset += AD_TYPE_FIELD_SIZE; + p_encoded_data[*p_offset] = tx_power_level; + *p_offset += AD_TYPE_TX_POWER_LEVEL_DATA_SIZE; + + return NRF_SUCCESS; +} + + +static ret_code_t uuid_list_sized_encode(const ble_advdata_uuid_list_t * p_uuid_list, + uint8_t adv_type, + uint8_t uuid_size, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + int i; + bool is_heading_written = false; + uint16_t start_pos = *p_offset; + uint16_t length; + + for (i = 0; i < p_uuid_list->uuid_cnt; i++) + { + ret_code_t err_code; + uint8_t encoded_size; + ble_uuid_t uuid = p_uuid_list->p_uuids[i]; + + // Find encoded uuid size. + err_code = sd_ble_uuid_encode(&uuid, &encoded_size, NULL); + VERIFY_SUCCESS(err_code); + + // Check size. + if (encoded_size == uuid_size) + { + uint8_t heading_bytes = (is_heading_written) ? 0 : AD_DATA_OFFSET; + + // Check for buffer overflow + if (((*p_offset) + encoded_size + heading_bytes) > max_size) + { + return NRF_ERROR_DATA_SIZE; + } + + if (!is_heading_written) + { + // Write AD structure heading. + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = adv_type; + *p_offset += AD_TYPE_FIELD_SIZE; + is_heading_written = true; + } + + // Write UUID. + err_code = sd_ble_uuid_encode(&uuid, &encoded_size, &p_encoded_data[*p_offset]); + VERIFY_SUCCESS(err_code); + *p_offset += encoded_size; + } + } + + if (is_heading_written) + { + // Write length. + length = (*p_offset) - (start_pos + AD_LENGTH_FIELD_SIZE); + // There is only 1 byte intended to encode length + if (length > 0x00FF) + { + return NRF_ERROR_DATA_SIZE; + } + p_encoded_data[start_pos] = (uint8_t)length; + } + + return NRF_SUCCESS; +} + + +static ret_code_t uuid_list_encode(const ble_advdata_uuid_list_t * p_uuid_list, + uint8_t adv_type_16, + uint8_t adv_type_128, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + ret_code_t err_code; + + // Encode 16 bit UUIDs. + err_code = uuid_list_sized_encode(p_uuid_list, + adv_type_16, + sizeof(uint16_le_t), + p_encoded_data, + p_offset, + max_size); + VERIFY_SUCCESS(err_code); + + // Encode 128 bit UUIDs. + err_code = uuid_list_sized_encode(p_uuid_list, + adv_type_128, + sizeof(ble_uuid128_t), + p_encoded_data, + p_offset, + max_size); + VERIFY_SUCCESS(err_code); + + return NRF_SUCCESS; +} + + +static ret_code_t conn_int_check(const ble_advdata_conn_int_t *p_conn_int) +{ + // Check Minimum Connection Interval. + if ((p_conn_int->min_conn_interval < 0x0006) || + ( + (p_conn_int->min_conn_interval > 0x0c80) && + (p_conn_int->min_conn_interval != 0xffff) + ) + ) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Check Maximum Connection Interval. + if ((p_conn_int->max_conn_interval < 0x0006) || + ( + (p_conn_int->max_conn_interval > 0x0c80) && + (p_conn_int->max_conn_interval != 0xffff) + ) + ) + { + return NRF_ERROR_INVALID_PARAM; + } + + // Make sure Minimum Connection Interval is not bigger than Maximum Connection Interval. + if ((p_conn_int->min_conn_interval != 0xffff) && + (p_conn_int->max_conn_interval != 0xffff) && + (p_conn_int->min_conn_interval > p_conn_int->max_conn_interval) + ) + { + return NRF_ERROR_INVALID_PARAM; + } + + return NRF_SUCCESS; +} + + +static ret_code_t conn_int_encode(const ble_advdata_conn_int_t * p_conn_int, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + ret_code_t err_code; + + // Check for buffer overflow. + if (((*p_offset) + AD_TYPE_CONN_INT_SIZE) > max_size) + { + return NRF_ERROR_DATA_SIZE; + } + + // Check parameters. + err_code = conn_int_check(p_conn_int); + VERIFY_SUCCESS(err_code); + + // Encode Length and AD Type. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + AD_TYPE_CONN_INT_DATA_SIZE); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE; + *p_offset += AD_TYPE_FIELD_SIZE; + + // Encode Minimum and Maximum Connection Intervals. + *p_offset += uint16_encode(p_conn_int->min_conn_interval, &p_encoded_data[*p_offset]); + *p_offset += uint16_encode(p_conn_int->max_conn_interval, &p_encoded_data[*p_offset]); + + return NRF_SUCCESS; +} + + +static ret_code_t manuf_specific_data_encode(const ble_advdata_manuf_data_t * p_manuf_sp_data, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + uint32_t data_size = AD_TYPE_MANUF_SPEC_DATA_ID_SIZE + p_manuf_sp_data->data.size; + + // Check for buffer overflow. + if (((*p_offset) + AD_DATA_OFFSET + data_size) > max_size) + { + return NRF_ERROR_DATA_SIZE; + } + + // There is only 1 byte intended to encode length which is (data_size + AD_TYPE_FIELD_SIZE) + if (data_size > (0x00FF - AD_TYPE_FIELD_SIZE)) + { + return NRF_ERROR_DATA_SIZE; + } + + // Encode Length and AD Type. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + data_size); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA; + *p_offset += AD_TYPE_FIELD_SIZE; + + // Encode Company Identifier. + *p_offset += uint16_encode(p_manuf_sp_data->company_identifier, &p_encoded_data[*p_offset]); + + // Encode additional manufacturer specific data. + if (p_manuf_sp_data->data.size > 0) + { + if (p_manuf_sp_data->data.p_data == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + memcpy(&p_encoded_data[*p_offset], p_manuf_sp_data->data.p_data, p_manuf_sp_data->data.size); + *p_offset += p_manuf_sp_data->data.size; + } + + return NRF_SUCCESS; +} + +// Implemented only for 16-bit UUIDs +static ret_code_t service_data_encode(const ble_advdata_t * p_advdata, + uint8_t * p_encoded_data, + uint16_t * p_offset, + uint16_t max_size) +{ + uint8_t i; + + // Check parameter consistency. + if (p_advdata->p_service_data_array == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + + for (i = 0; i < p_advdata->service_data_count; i++) + { + ble_advdata_service_data_t * p_service_data; + uint32_t data_size; + + p_service_data = &p_advdata->p_service_data_array[i]; + // For now implemented only for 16-bit UUIDs + data_size = AD_TYPE_SERV_DATA_16BIT_UUID_SIZE + p_service_data->data.size; + + // There is only 1 byte intended to encode length which is (data_size + AD_TYPE_FIELD_SIZE) + if (data_size > (0x00FF - AD_TYPE_FIELD_SIZE)) + { + return NRF_ERROR_DATA_SIZE; + } + + // Encode Length and AD Type. + p_encoded_data[*p_offset] = (uint8_t)(AD_TYPE_FIELD_SIZE + data_size); + *p_offset += AD_LENGTH_FIELD_SIZE; + p_encoded_data[*p_offset] = BLE_GAP_AD_TYPE_SERVICE_DATA; + *p_offset += AD_TYPE_FIELD_SIZE; + + // Encode service 16-bit UUID. + *p_offset += uint16_encode(p_service_data->service_uuid, &p_encoded_data[*p_offset]); + + // Encode additional service data. + if (p_service_data->data.size > 0) + { + if (p_service_data->data.p_data == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + memcpy(&p_encoded_data[*p_offset], p_service_data->data.p_data, p_service_data->data.size); + *p_offset += p_service_data->data.size; + } + } + + return NRF_SUCCESS; +} + +ret_code_t ble_advdata_encode(ble_advdata_t const * const p_advdata, + uint8_t * const p_encoded_data, + uint16_t * const p_len) +{ + ret_code_t err_code = NRF_SUCCESS; + uint16_t max_size = *p_len; + *p_len = 0; + + // Encode LE Bluetooth Device Address + if (p_advdata->include_ble_device_addr) + { + err_code = ble_device_addr_encode(p_encoded_data, p_len, max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode appearance. + if (p_advdata->include_appearance) + { + err_code = appearance_encode(p_encoded_data, p_len, max_size); + VERIFY_SUCCESS(err_code); + } + + //Encode Flags + if (p_advdata->flags != 0 ) + { + err_code = flags_encode(p_advdata->flags, p_encoded_data, p_len, max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode TX power level. + if (p_advdata->p_tx_power_level != NULL) + { + err_code = tx_power_level_encode(*p_advdata->p_tx_power_level, + p_encoded_data, + p_len, + max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode 'more available' uuid list. + if (p_advdata->uuids_more_available.uuid_cnt > 0) + { + err_code = uuid_list_encode(&p_advdata->uuids_more_available, + BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE, + BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE, + p_encoded_data, + p_len, + max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode 'complete' uuid list. + if (p_advdata->uuids_complete.uuid_cnt > 0) + { + err_code = uuid_list_encode(&p_advdata->uuids_complete, + BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, + BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE, + p_encoded_data, + p_len, + max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode 'solicited service' uuid list. + if (p_advdata->uuids_solicited.uuid_cnt > 0) + { + err_code = uuid_list_encode(&p_advdata->uuids_solicited, + BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT, + BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT, + p_encoded_data, + p_len, + max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode Slave Connection Interval Range. + if (p_advdata->p_slave_conn_int != NULL) + { + err_code = conn_int_encode(p_advdata->p_slave_conn_int, p_encoded_data, p_len, max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode Manufacturer Specific Data. + if (p_advdata->p_manuf_specific_data != NULL) + { + err_code = manuf_specific_data_encode(p_advdata->p_manuf_specific_data, + p_encoded_data, + p_len, + max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode Service Data. + if (p_advdata->service_data_count > 0) + { + err_code = service_data_encode(p_advdata, p_encoded_data, p_len, max_size); + VERIFY_SUCCESS(err_code); + } + + // Encode name. WARNING: it is encoded last on purpose since too long device name is truncated. + if (p_advdata->name_type != BLE_ADVDATA_NO_NAME) + { + err_code = name_encode(p_advdata, p_encoded_data, p_len, max_size); + VERIFY_SUCCESS(err_code); + } + + return err_code; +} + + +uint16_t ble_advdata_search(uint8_t const * p_encoded_data, + uint16_t data_len, + uint16_t * p_offset, + uint8_t ad_type) +{ + if ((p_encoded_data == NULL) || (p_offset == NULL)) + { + return 0; + } + + uint16_t i = 0; + + while ((i + 1 < data_len) && ((i < *p_offset) || (p_encoded_data[i + 1] != ad_type))) + { + // Jump to next data. + i += (p_encoded_data[i] + 1); + } + + if (i >= data_len) + { + return 0; + } + else + { + uint16_t offset = i + 2; + uint16_t len = p_encoded_data[i] ? (p_encoded_data[i] - 1) : 0; + if (!len || ((offset + len) > data_len)) + { + // Malformed. Zero length or extends beyond provided data. + return 0; + } + *p_offset = offset; + return len; + } +} + + +uint8_t * ble_advdata_parse(uint8_t * p_encoded_data, + uint16_t data_len, + uint8_t ad_type) +{ + uint16_t offset = 0; + uint16_t len = ble_advdata_search(p_encoded_data, data_len, &offset, ad_type); + + if (len == 0) + { + return NULL; + } + else + { + return &p_encoded_data[offset]; + } +} + + +bool ble_advdata_name_find(uint8_t const * p_encoded_data, + uint16_t data_len, + char const * p_target_name) +{ + uint16_t parsed_name_len; + uint8_t const * p_parsed_name; + uint16_t data_offset = 0; + + if (p_target_name == NULL) + { + return false; + } + + + parsed_name_len = ble_advdata_search(p_encoded_data, + data_len, + &data_offset, + BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME); + + p_parsed_name = &p_encoded_data[data_offset]; + + if ( (data_offset != 0) + && (parsed_name_len != 0) + && (strlen(p_target_name) == parsed_name_len) + && (memcmp(p_target_name, p_parsed_name, parsed_name_len) == 0)) + { + return true; + } + + return false; +} + + +bool ble_advdata_short_name_find(uint8_t const * p_encoded_data, + uint16_t data_len, + char const * p_target_name, + uint8_t const short_name_min_len) +{ + uint16_t parsed_name_len; + uint8_t const * p_parsed_name; + uint16_t data_offset = 0; + + if (p_target_name == NULL) + { + return false; + } + + parsed_name_len = ble_advdata_search(p_encoded_data, + data_len, + &data_offset, + BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME); + + p_parsed_name = &p_encoded_data[data_offset]; + + if ( (data_offset != 0) + && (parsed_name_len != 0) + && (parsed_name_len >= short_name_min_len) + && (parsed_name_len < strlen(p_target_name)) + && (memcmp(p_target_name, p_parsed_name, parsed_name_len) == 0)) + { + return true; + } + + return false; +} + + +bool ble_advdata_uuid_find(uint8_t const * p_encoded_data, + uint16_t data_len, + ble_uuid_t const * p_target_uuid) +{ + + ret_code_t err_code; + uint16_t data_offset = 0; + uint8_t raw_uuid_len = UUID128_SIZE; + uint8_t const * p_parsed_uuid; + uint16_t parsed_uuid_len = data_len; + uint8_t raw_uuid[UUID128_SIZE]; + uint8_t ad_types[N_AD_TYPES]; + + err_code = sd_ble_uuid_encode(p_target_uuid, &raw_uuid_len, raw_uuid); + + if ((p_encoded_data == NULL) || (err_code != NRF_SUCCESS)) + { + // Invalid p_encoded_data or p_target_uuid. + return false; + } + + switch (raw_uuid_len) + { + case UUID16_SIZE: + ad_types[0] = BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE; + ad_types[1] = BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE; + break; + + case UUID32_SIZE: + // Not currently supported by sd_ble_uuid_encode(). + ad_types[0] = BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE; + ad_types[1] = BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE; + break; + + case UUID128_SIZE: + ad_types[0] = BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE; + ad_types[1] = BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE; + break; + + default: + return false; + } + + for (uint8_t i = 0; (i < N_AD_TYPES) && (data_offset == 0); i++) + { + parsed_uuid_len = ble_advdata_search(p_encoded_data, data_len, &data_offset, ad_types[i]); + } + + if (data_offset == 0) + { + // Could not find any relevant UUIDs in the encoded data. + return false; + } + + p_parsed_uuid = &p_encoded_data[data_offset]; + + // Verify if any UUID matches the given UUID. + for (uint16_t list_offset = 0; list_offset < parsed_uuid_len; list_offset += raw_uuid_len) + { + if (memcmp(&p_parsed_uuid[list_offset], raw_uuid, raw_uuid_len) == 0) + { + return true; + } + } + + // Could not find the UUID among the encoded data. + return false; +} + + +bool ble_advdata_appearance_find(uint8_t const * p_encoded_data, + uint16_t data_len, + uint16_t const * p_target_appearance) +{ + uint16_t data_offset = 0; + uint8_t appearance_len; + uint16_t decoded_appearance; + + appearance_len = ble_advdata_search(p_encoded_data, data_len, &data_offset, BLE_GAP_AD_TYPE_APPEARANCE); + + if ( (data_offset == 0) + || (p_target_appearance == NULL) + || (appearance_len == 0)) + { + // Could not find any Appearance in the encoded data, or invalid p_target_appearance. + return false; + } + + decoded_appearance = uint16_decode(&p_encoded_data[data_offset]); + + if (decoded_appearance == *p_target_appearance) + { + return true; + } + + // Could not find the appearance among the encoded data. + return false; +} diff --git a/libraries/nfc/src/util/ble_advdata.h b/libraries/nfc/src/util/ble_advdata.h new file mode 100644 index 000000000..52f99fd98 --- /dev/null +++ b/libraries/nfc/src/util/ble_advdata.h @@ -0,0 +1,326 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup ble_sdk_lib_advdata Advertising and Scan Response Data Encoder + * @{ + * @ingroup ble_sdk_lib + * @brief Functions for encoding data in the Advertising and Scan Response Data format, + * and for passing the data to the stack. + */ + +#ifndef BLE_ADVDATA_H__ +#define BLE_ADVDATA_H__ + +#include +#include +#include +#include "ble.h" +#include "util/sdk_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define AD_LENGTH_FIELD_SIZE 1UL /**< Advertising Data and Scan Response format contains 1 octet for the length. */ +#define AD_TYPE_FIELD_SIZE 1UL /**< Advertising Data and Scan Response format contains 1 octet for the AD type. */ +#define AD_DATA_OFFSET (AD_LENGTH_FIELD_SIZE + AD_TYPE_FIELD_SIZE) /**< Offset for the AD data field of the Advertising Data and Scan Response format. */ + +#define AD_TYPE_BLE_DEVICE_ADDR_TYPE_SIZE 1UL /**< Data size (in octets) of the Address type of the LE Bluetooth Device Address AD type. */ +#define AD_TYPE_BLE_DEVICE_ADDR_DATA_SIZE (BLE_GAP_ADDR_LEN + \ + AD_TYPE_BLE_DEVICE_ADDR_TYPE_SIZE) /**< Data size (in octets) of the LE Bluetooth Device Address AD type. */ +#define AD_TYPE_BLE_DEVICE_ADDR_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_BLE_DEVICE_ADDR_DATA_SIZE) /**< Size (in octets) of the LE Bluetooth Device Address AD type. */ +#define AD_TYPE_APPEARANCE_DATA_SIZE 2UL /**< Data size (in octets) of the Appearance AD type. */ +#define AD_TYPE_APPEARANCE_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_APPEARANCE_DATA_SIZE) /**< Size (in octets) of the Appearance AD type. */ +#define AD_TYPE_FLAGS_DATA_SIZE 1UL /**< Data size (in octets) of the Flags AD type. */ +#define AD_TYPE_FLAGS_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_FLAGS_DATA_SIZE) /**< Size (in octets) of the Flags AD type. */ +#define AD_TYPE_TX_POWER_LEVEL_DATA_SIZE 1UL /**< Data size (in octets) of the TX Power Level AD type. */ +#define AD_TYPE_TX_POWER_LEVEL_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_TX_POWER_LEVEL_DATA_SIZE) /**< Size (in octets) of the TX Power Level AD type. */ +#define AD_TYPE_CONN_INT_DATA_SIZE 4UL /**< Data size (in octets) of the Slave Connection Interval Range AD type. */ +#define AD_TYPE_CONN_INT_SIZE (AD_DATA_OFFSET + \ + AD_TYPE_CONN_INT_DATA_SIZE) /**< Data size (in octets) of the Slave Connection Interval Range AD type. */ +#define AD_TYPE_MANUF_SPEC_DATA_ID_SIZE 2UL /**< Size (in octets) of the Company Identifier Code, which is a part of the Manufacturer Specific Data AD type. */ +#define AD_TYPE_SERV_DATA_16BIT_UUID_SIZE 2UL /**< Size (in octets) of the 16-bit UUID, which is a part of the Service Data AD type. */ + +#define BLE_ADV_DATA_MATCH_FULL_NAME 0xff + + +/**@brief Security Manager TK value. */ +typedef struct +{ + uint8_t tk[BLE_GAP_SEC_KEY_LEN]; /**< Array containing TK value in little-endian format. */ +} ble_advdata_tk_value_t; + +/**@brief Advertising data LE Role types. This enumeration contains the options available for the LE role inside + * the advertising data. */ +typedef enum +{ + BLE_ADVDATA_ROLE_NOT_PRESENT = 0, /**< LE Role AD structure not present. */ + BLE_ADVDATA_ROLE_ONLY_PERIPH, /**< Only Peripheral Role supported. */ + BLE_ADVDATA_ROLE_ONLY_CENTRAL, /**< Only Central Role supported. */ + BLE_ADVDATA_ROLE_BOTH_PERIPH_PREFERRED, /**< Peripheral and Central Role supported. Peripheral Role preferred for connection establishment. */ + BLE_ADVDATA_ROLE_BOTH_CENTRAL_PREFERRED /**< Peripheral and Central Role supported. Central Role preferred for connection establishment */ +} ble_advdata_le_role_t; + +/**@brief Advertising data name type. This enumeration contains the options available for the device name inside + * the advertising data. */ +typedef enum +{ + BLE_ADVDATA_NO_NAME, /**< Include no device name in advertising data. */ + BLE_ADVDATA_SHORT_NAME, /**< Include short device name in advertising data. */ + BLE_ADVDATA_FULL_NAME /**< Include full device name in advertising data. */ +} ble_advdata_name_type_t; + +/**@brief UUID list type. */ +typedef struct +{ + uint16_t uuid_cnt; /**< Number of UUID entries. */ + ble_uuid_t * p_uuids; /**< Pointer to UUID array entries. */ +} ble_advdata_uuid_list_t; + +/**@brief Connection interval range structure. */ +typedef struct +{ + uint16_t min_conn_interval; /**< Minimum connection interval, in units of 1.25 ms, range 6 to 3200 (7.5 ms to 4 s). */ + uint16_t max_conn_interval; /**< Maximum connection interval, in units of 1.25 ms, range 6 to 3200 (7.5 ms to 4 s). The value 0xFFFF indicates no specific maximum. */ +} ble_advdata_conn_int_t; + +/**@brief Manufacturer specific data structure. */ +typedef struct +{ + uint16_t company_identifier; /**< Company identifier code. */ + uint8_array_t data; /**< Additional manufacturer specific data. */ +} ble_advdata_manuf_data_t; + +/**@brief Service data structure. */ +typedef struct +{ + uint16_t service_uuid; /**< Service UUID. */ + uint8_array_t data; /**< Additional service data. */ +} ble_advdata_service_data_t; + +/**@brief Advertising data structure. This structure contains all options and data needed for encoding and + * setting the advertising data. */ +typedef struct +{ + ble_advdata_name_type_t name_type; /**< Type of device name. */ + uint8_t short_name_len; /**< Length of short device name (if short type is specified). */ + bool include_appearance; /**< Determines if Appearance shall be included. */ + uint8_t flags; /**< Advertising data Flags field. */ + int8_t * p_tx_power_level; /**< TX Power Level field. */ + ble_advdata_uuid_list_t uuids_more_available; /**< List of UUIDs in the 'More Available' list. */ + ble_advdata_uuid_list_t uuids_complete; /**< List of UUIDs in the 'Complete' list. */ + ble_advdata_uuid_list_t uuids_solicited; /**< List of solicited UUIDs. */ + ble_advdata_conn_int_t * p_slave_conn_int; /**< Slave Connection Interval Range. */ + ble_advdata_manuf_data_t * p_manuf_specific_data; /**< Manufacturer specific data. */ + ble_advdata_service_data_t * p_service_data_array; /**< Array of Service data structures. */ + uint8_t service_data_count; /**< Number of Service data structures. */ + bool include_ble_device_addr; /**< Determines if LE Bluetooth Device Address shall be included. */ + ble_advdata_le_role_t le_role; /**< LE Role field. Included when different from @ref BLE_ADVDATA_ROLE_NOT_PRESENT. @warning This field can be used only for NFC. For BLE advertising, set it to NULL. */ + ble_advdata_tk_value_t * p_tk_value; /**< Security Manager TK value field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/ + uint8_t * p_sec_mgr_oob_flags; /**< Security Manager Out Of Band Flags field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/ + ble_gap_lesc_oob_data_t * p_lesc_data; /**< LE Secure Connections OOB data. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/ +} ble_advdata_t; + +/**@brief Function for encoding data in the Advertising and Scan Response data format (AD structures). + * + * @details This function encodes data into the Advertising and Scan Response data format + * (AD structures) based on the fields in the supplied structures. This function can be + * used to create a payload of Advertising packet or Scan Response packet, or a payload of + * NFC message intended for initiating the Out-of-Band pairing. + * + * @param[in] p_advdata Pointer to the structure for specifying the content of encoded data. + * @param[out] p_encoded_data Pointer to the buffer where encoded data will be returned. + * @param[in,out] p_len \c in: Size of \p p_encoded_data buffer. + * \c out: Length of encoded data. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_INVALID_PARAM If the operation failed because a wrong parameter was provided in + * \p p_advdata. + * @retval NRF_ERROR_DATA_SIZE If the operation failed because not all the requested data could + * fit into the provided buffer or some encoded AD structure is too + * long and its length cannot be encoded with one octet. + * + * @warning This API may override the application's request to use the long name and use a short name + * instead. This truncation will occur in case the long name does not fit the provided buffer size. + * The application can specify a preferred short name length if truncation is required. + * For example, if the complete device name is ABCD_HRMonitor, the application can specify the short name + * length to be 8, so that the short device name appears as ABCD_HRM instead of ABCD_HRMo or ABCD_HRMoni + * if the available size for the short name is 9 or 12 respectively, to have a more appropriate short name. + * However, it should be noted that this is just a preference that the application can specify, and + * if the preference is too large to fit in the provided buffer, the name can be truncated further. + */ +ret_code_t ble_advdata_encode(ble_advdata_t const * const p_advdata, + uint8_t * const p_encoded_data, + uint16_t * const p_len); + + +/**@brief Function for searching encoded Advertising or Scan Response data for specific data types. + * + * @details This function searches through encoded data e.g. the data produced by + * @ref ble_advdata_encode, or the data found in Advertising reports + * (@ref BLE_GAP_EVT_ADV_REPORT), and gives the offset of the data within the data buffer. + * The data with type \p ad_type can be found at p_encoded_data[*p_offset] after calling + * the function. This function can iterate through multiple instances of data of one + * type by calling it again with the offset provided by the previous call. + * + * Example code for finding multiple instances of one type of data: + * offset = 0; + * ble_advdata_search(&data, len, &offset, AD_TYPE); + * first_instance_of_data = data[offset]; + * ble_advdata_search(&data, len, &offset, AD_TYPE); + * second_instance_of_data = data[offset]; + * + * @param[in] p_encoded_data The data buffer containing the encoded Advertising data. + * @param[in] data_len The length of the data buffer \p p_encoded_data. + * @param[inout] p_offset \c in: The offset to start searching from. + * \c out: The offset the data type can be found at. + * This value is not changed if the call returns 0. + * @param[in] ad_type The type of data to search for. + * + * @return The length of the found data, or 0 if no data was found with the the type \p ad_type, + * or if \p p_encoded_data or \p p_offset were NULL. + */ +uint16_t ble_advdata_search(uint8_t const * p_encoded_data, + uint16_t data_len, + uint16_t * p_offset, + uint8_t ad_type); + +/**@brief Function for getting specific data from encoded Advertising or Scan Response data. + * + * @details This function searches through encoded data e.g. the data produced by + * @ref ble_advdata_encode, or the data found in Advertising reports + * (@ref BLE_GAP_EVT_ADV_REPORT), and returns a pointer directly to the data within the + * data buffer. + * + * Example code: + * ad_type_data = ble_advdata_parse(&data, len, AD_TYPE); + * + * @param[in] p_encoded_data Data buffer containing the encoded Advertising data. + * @param[in] data_len Length of the data buffer \p p_encoded_data. + * @param[in] ad_type Type of data to search for. + * + * @return Pointer to the found data, or NULL if no data was found with the type \p ad_type, + * or if \p p_encoded_data or \p p_data_len were NULL. + */ +uint8_t * ble_advdata_parse(uint8_t * p_encoded_data, + uint16_t data_len, + uint8_t ad_type); + + +/**@brief Function for searching through encoded Advertising data for a complete local name. + * + * @param[in] p_encoded_data Data buffer containing the encoded Advertising data. + * @param[in] data_len Length of the data buffer \p p_encoded_data. + * @param[in] p_target_name Name to search for. + * + * @retval true If \p p_target_name was found among \p p_encoded_data, as a complete local name. + * @retval false If \p p_target_name was not found among \p p_encoded_data, or if \p p_encoded_data + * or \p p_target_name was NULL. + */ +bool ble_advdata_name_find(uint8_t const * p_encoded_data, + uint16_t data_len, + char const * p_target_name); + + +/**@brief Function for searching through encoded Advertising data for a device shortened name. + * + * @param[in] p_encoded_data Data buffer containing the encoded Advertising data. + * @param[in] data_len Length of the data buffer \p p_encoded_data. + * @param[in] p_target_name Name to search for. + * @param[in] short_name_min_len Minimum length of the shortened name. + * For example, if the advertising data has a shortened name 'No' and this parameter is + * set to 4 with a target_name set to Nordic_XXX it will return false, but if + * the shortened name in the advertising data is 'Nord', it will return true. + * @note: If the shortened name in the Advertising data has the same length as the target name, + * this function will return false, since this means that the complete name is actually + * longer, thus different than the target name. + * + * @retval true If \p p_target_name was found among \p p_encoded_data, as short local name. + * @retval false If \p p_target_name was not found among \p p_encoded_data, or if \p p_encoded_data + * or \p p_target_name was NULL. + */ +bool ble_advdata_short_name_find(uint8_t const * p_encoded_data, + uint16_t data_len, + char const * p_target_name, + uint8_t const short_name_min_len); + +/**@brief Function for searching through encoded Advertising data for a UUID (16-bit or 128-bit). + * + * @param[in] p_encoded_data Data buffer containing the encoded Advertising data. + * @param[in] data_len Length of the data buffer \p p_encoded_data. + * @param[in] p_target_uuid UUID to search for. + * + * @retval true If \p p_target_uuid was found among \p p_encoded_data. + * @retval false If \p p_target_uuid was not found among \p p_encoded_data, or if \p p_encoded_data + * or \p p_target_uuid was NULL. + */ +bool ble_advdata_uuid_find(uint8_t const * p_encoded_data, + uint16_t data_len, + ble_uuid_t const * p_target_uuid); + + +/**@brief Function for searching through encoded Advertising data for an appearance. + * + * @param[in] p_encoded_data Data buffer containing the encoded Advertising data. + * @param[in] data_len Length of the data buffer \p p_encoded_data. + * @param[in] p_target_appearance Appearance to search for. + * + * @retval true If \p p_target_appearance was found among \p p_encoded_data. + * @retval false If \p p_target_appearance was not found among \p p_encoded_data, or if \p p_encoded_data + * or \p p_target_appearance was NULL. + */ +bool ble_advdata_appearance_find(uint8_t const * p_encoded_data, + uint16_t data_len, + uint16_t const * p_target_appearance); + + +#ifdef __cplusplus +} +#endif + +#endif // BLE_ADVDATA_H__ + +/** @} */ diff --git a/libraries/nfc/src/util/ble_srv_common.c b/libraries/nfc/src/util/ble_srv_common.c new file mode 100644 index 000000000..bb5553d18 --- /dev/null +++ b/libraries/nfc/src/util/ble_srv_common.c @@ -0,0 +1,237 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* Attention! + * To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile + * qualification listings, this section of source code must not be modified. + */ + +#include "ble_srv_common.h" +#include +#include "nordic_common.h" +#include "util/app_error.h" +#include "ble.h" + +bool ble_srv_is_notification_enabled(uint8_t const * p_encoded_data) +{ + uint16_t cccd_value = uint16_decode(p_encoded_data); + return ((cccd_value & BLE_GATT_HVX_NOTIFICATION) != 0); +} + +bool ble_srv_is_indication_enabled(uint8_t const * p_encoded_data) +{ + uint16_t cccd_value = uint16_decode(p_encoded_data); + return ((cccd_value & BLE_GATT_HVX_INDICATION) != 0); +} + +uint8_t ble_srv_report_ref_encode(uint8_t * p_encoded_buffer, + const ble_srv_report_ref_t * p_report_ref) +{ + uint8_t len = 0; + + p_encoded_buffer[len++] = p_report_ref->report_id; + p_encoded_buffer[len++] = p_report_ref->report_type; + + APP_ERROR_CHECK_BOOL(len == BLE_SRV_ENCODED_REPORT_REF_LEN); + return len; +} + + +void ble_srv_ascii_to_utf8(ble_srv_utf8_str_t * p_utf8, char * p_ascii) +{ + p_utf8->length = (uint16_t)strlen(p_ascii); + p_utf8->p_str = (uint8_t *)p_ascii; +} + + +/**@brief Function for setting security requirements of a characteristic. + * + * @param[in] level required security level. + * @param[out] p_perm Characteristic security requirements. + * + * @return encoded security level and security mode. + */ +static inline void set_security_req(security_req_t level, ble_gap_conn_sec_mode_t * p_perm) +{ + + + BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(p_perm); + switch (level) + { + case SEC_NO_ACCESS: + BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(p_perm); + break; + case SEC_OPEN: + BLE_GAP_CONN_SEC_MODE_SET_OPEN(p_perm); + break; + case SEC_JUST_WORKS: + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(p_perm); + break; + case SEC_MITM: + BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(p_perm); + break; + case SEC_SIGNED: + BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(p_perm); + break; + case SEC_SIGNED_MITM: + BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(p_perm); + break; + } + return; +} + + +uint32_t characteristic_add(uint16_t service_handle, + ble_add_char_params_t * p_char_props, + ble_gatts_char_handles_t * p_char_handle) +{ + ble_gatts_char_md_t char_md; + ble_gatts_attr_t attr_char_value; + ble_uuid_t char_uuid; + ble_gatts_attr_md_t attr_md; + ble_gatts_attr_md_t user_descr_attr_md; + ble_gatts_attr_md_t cccd_md; + + if (p_char_props->uuid_type == 0) + { + char_uuid.type = BLE_UUID_TYPE_BLE; + } + else + { + char_uuid.type = p_char_props->uuid_type; + } + char_uuid.uuid = p_char_props->uuid; + + memset(&attr_md, 0, sizeof(ble_gatts_attr_md_t)); + set_security_req(p_char_props->read_access, &attr_md.read_perm); + set_security_req(p_char_props->write_access, & attr_md.write_perm); + attr_md.rd_auth = (p_char_props->is_defered_read ? 1 : 0); + attr_md.wr_auth = (p_char_props->is_defered_write ? 1 : 0); + attr_md.vlen = (p_char_props->is_var_len ? 1 : 0); + attr_md.vloc = (p_char_props->is_value_user ? BLE_GATTS_VLOC_USER : BLE_GATTS_VLOC_STACK); + + + memset(&char_md, 0, sizeof(ble_gatts_char_md_t)); + if ((p_char_props->char_props.notify == 1)||(p_char_props->char_props.indicate == 1)) + { + + memset(&cccd_md, 0, sizeof(cccd_md)); + set_security_req(p_char_props->cccd_write_access, &cccd_md.write_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); + + cccd_md.vloc = BLE_GATTS_VLOC_STACK; + + char_md.p_cccd_md = &cccd_md; + } + char_md.char_props = p_char_props->char_props; + char_md.char_ext_props = p_char_props->char_ext_props; + + memset(&attr_char_value, 0, sizeof(ble_gatts_attr_t)); + attr_char_value.p_uuid = &char_uuid; + attr_char_value.p_attr_md = &attr_md; + attr_char_value.max_len = p_char_props->max_len; + if (p_char_props->p_init_value != NULL) + { + attr_char_value.init_len = p_char_props->init_len; + attr_char_value.p_value = p_char_props->p_init_value; + } + if (p_char_props->p_user_descr != NULL) + { + memset(&user_descr_attr_md, 0, sizeof(ble_gatts_attr_md_t)); + char_md.char_user_desc_max_size = p_char_props->p_user_descr->max_size; + char_md.char_user_desc_size = p_char_props->p_user_descr->size; + char_md.p_char_user_desc = p_char_props->p_user_descr->p_char_user_desc; + + char_md.p_user_desc_md = &user_descr_attr_md; + + set_security_req(p_char_props->p_user_descr->read_access, &user_descr_attr_md.read_perm); + set_security_req(p_char_props->p_user_descr->write_access, &user_descr_attr_md.write_perm); + + user_descr_attr_md.rd_auth = (p_char_props->p_user_descr->is_defered_read ? 1 : 0); + user_descr_attr_md.wr_auth = (p_char_props->p_user_descr->is_defered_write ? 1 : 0); + user_descr_attr_md.vlen = (p_char_props->p_user_descr->is_var_len ? 1 : 0); + user_descr_attr_md.vloc = (p_char_props->p_user_descr->is_value_user ? BLE_GATTS_VLOC_USER : BLE_GATTS_VLOC_STACK); + } + if (p_char_props->p_presentation_format != NULL) + { + char_md.p_char_pf = p_char_props->p_presentation_format; + } + return sd_ble_gatts_characteristic_add(service_handle, + &char_md, + &attr_char_value, + p_char_handle); +} + + +uint32_t descriptor_add(uint16_t char_handle, + ble_add_descr_params_t * p_descr_props, + uint16_t * p_descr_handle) +{ + ble_gatts_attr_t descr_params; + ble_uuid_t desc_uuid; + ble_gatts_attr_md_t attr_md; + + memset(&descr_params, 0, sizeof(descr_params)); + if (p_descr_props->uuid_type == 0) + { + desc_uuid.type = BLE_UUID_TYPE_BLE; + } + else + { + desc_uuid.type = p_descr_props->uuid_type; + } + desc_uuid.uuid = p_descr_props->uuid; + descr_params.p_uuid = &desc_uuid; + + set_security_req(p_descr_props->read_access, &attr_md.read_perm); + set_security_req(p_descr_props->write_access,&attr_md.write_perm); + + attr_md.rd_auth = (p_descr_props->is_defered_read ? 1 : 0); + attr_md.wr_auth = (p_descr_props->is_defered_write ? 1 : 0); + attr_md.vlen = (p_descr_props->is_var_len ? 1 : 0); + attr_md.vloc = (p_descr_props->is_value_user ? BLE_GATTS_VLOC_USER : BLE_GATTS_VLOC_STACK); + descr_params.p_attr_md = &attr_md; + + descr_params.init_len = p_descr_props->init_len; + descr_params.init_offs = p_descr_props->init_offs; + descr_params.max_len = p_descr_props->max_len; + descr_params.p_value = p_descr_props->p_value; + + return sd_ble_gatts_descriptor_add(char_handle, &descr_params, p_descr_handle); +} diff --git a/libraries/nfc/src/util/ble_srv_common.h b/libraries/nfc/src/util/ble_srv_common.h new file mode 100644 index 000000000..b6771e557 --- /dev/null +++ b/libraries/nfc/src/util/ble_srv_common.h @@ -0,0 +1,409 @@ +/** + * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup ble_sdk_srv_common Common service definitions + * @{ + * @ingroup ble_sdk_srv + * @brief Constants, type definitions, and functions that are common to all services. + */ + +#ifndef BLE_SRV_COMMON_H__ +#define BLE_SRV_COMMON_H__ + +#include +#include +#include "ble_types.h" +#include "app_util.h" +#include "ble.h" +#include "ble_gap.h" +#include "ble_gatt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup UUID_SERVICES Service UUID definitions + * @{ */ +#define BLE_UUID_ALERT_NOTIFICATION_SERVICE 0x1811 /**< Alert Notification service UUID. */ +#define BLE_UUID_BATTERY_SERVICE 0x180F /**< Battery service UUID. */ +#define BLE_UUID_BLOOD_PRESSURE_SERVICE 0x1810 /**< Blood Pressure service UUID. */ +#define BLE_UUID_CURRENT_TIME_SERVICE 0x1805 /**< Current Time service UUID. */ +#define BLE_UUID_CYCLING_SPEED_AND_CADENCE 0x1816 /**< Cycling Speed and Cadence service UUID. */ +#define BLE_UUID_LOCATION_AND_NAVIGATION_SERVICE 0x1819 /**< Location and Navigation service UUID. */ +#define BLE_UUID_DEVICE_INFORMATION_SERVICE 0x180A /**< Device Information service UUID. */ +#define BLE_UUID_GLUCOSE_SERVICE 0x1808 /**< Glucose service UUID. */ +#define BLE_UUID_HEALTH_THERMOMETER_SERVICE 0x1809 /**< Health Thermometer service UUID. */ +#define BLE_UUID_HEART_RATE_SERVICE 0x180D /**< Heart Rate service UUID. */ +#define BLE_UUID_HUMAN_INTERFACE_DEVICE_SERVICE 0x1812 /**< Human Interface Device service UUID. */ +#define BLE_UUID_IMMEDIATE_ALERT_SERVICE 0x1802 /**< Immediate Alert service UUID. */ +#define BLE_UUID_LINK_LOSS_SERVICE 0x1803 /**< Link Loss service UUID. */ +#define BLE_UUID_NEXT_DST_CHANGE_SERVICE 0x1807 /**< Next Dst Change service UUID. */ +#define BLE_UUID_PHONE_ALERT_STATUS_SERVICE 0x180E /**< Phone Alert Status service UUID. */ +#define BLE_UUID_REFERENCE_TIME_UPDATE_SERVICE 0x1806 /**< Reference Time Update service UUID. */ +#define BLE_UUID_RUNNING_SPEED_AND_CADENCE 0x1814 /**< Running Speed and Cadence service UUID. */ +#define BLE_UUID_SCAN_PARAMETERS_SERVICE 0x1813 /**< Scan Parameters service UUID. */ +#define BLE_UUID_TX_POWER_SERVICE 0x1804 /**< TX Power service UUID. */ +#define BLE_UUID_IPSP_SERVICE 0x1820 /**< Internet Protocol Support service UUID. */ +#define BLE_UUID_BMS_SERVICE 0x181E /**< BOND MANAGEMENT service UUID*/ +#define BLE_UUID_CGM_SERVICE 0x181F /**< Continuous Glucose Monitoring service UUID*/ +#define BLE_UUID_PLX_SERVICE 0x1822 /**< Pulse Oximeter Service UUID*/ +#define BLE_UUID_OTS_SERVICE 0x1825 /**< Object Transfer Service UUID*/ + +/** @} */ + +/** @defgroup UUID_CHARACTERISTICS Characteristic UUID definitions + * @{ */ +#define BLE_UUID_REMOVABLE_CHAR 0x2A3A /**< Removable characteristic UUID. */ +#define BLE_UUID_SERVICE_REQUIRED_CHAR 0x2A3B /**< Service Required characteristic UUID. */ +#define BLE_UUID_ALERT_CATEGORY_ID_CHAR 0x2A43 /**< Alert Category Id characteristic UUID. */ +#define BLE_UUID_ALERT_CATEGORY_ID_BIT_MASK_CHAR 0x2A42 /**< Alert Category Id Bit Mask characteristic UUID. */ +#define BLE_UUID_ALERT_LEVEL_CHAR 0x2A06 /**< Alert Level characteristic UUID. */ +#define BLE_UUID_ALERT_NOTIFICATION_CONTROL_POINT_CHAR 0x2A44 /**< Alert Notification Control Point characteristic UUID. */ +#define BLE_UUID_ALERT_STATUS_CHAR 0x2A3F /**< Alert Status characteristic UUID. */ +#define BLE_UUID_BATTERY_LEVEL_CHAR 0x2A19 /**< Battery Level characteristic UUID. */ +#define BLE_UUID_BLOOD_PRESSURE_FEATURE_CHAR 0x2A49 /**< Blood Pressure Feature characteristic UUID. */ +#define BLE_UUID_BLOOD_PRESSURE_MEASUREMENT_CHAR 0x2A35 /**< Blood Pressure Measurement characteristic UUID. */ +#define BLE_UUID_BODY_SENSOR_LOCATION_CHAR 0x2A38 /**< Body Sensor Location characteristic UUID. */ +#define BLE_UUID_BOOT_KEYBOARD_INPUT_REPORT_CHAR 0x2A22 /**< Boot Keyboard Input Report characteristic UUID. */ +#define BLE_UUID_BOOT_KEYBOARD_OUTPUT_REPORT_CHAR 0x2A32 /**< Boot Keyboard Output Report characteristic UUID. */ +#define BLE_UUID_BOOT_MOUSE_INPUT_REPORT_CHAR 0x2A33 /**< Boot Mouse Input Report characteristic UUID. */ +#define BLE_UUID_CURRENT_TIME_CHAR 0x2A2B /**< Current Time characteristic UUID. */ +#define BLE_UUID_DATE_TIME_CHAR 0x2A08 /**< Date Time characteristic UUID. */ +#define BLE_UUID_DAY_DATE_TIME_CHAR 0x2A0A /**< Day Date Time characteristic UUID. */ +#define BLE_UUID_DAY_OF_WEEK_CHAR 0x2A09 /**< Day Of Week characteristic UUID. */ +#define BLE_UUID_DST_OFFSET_CHAR 0x2A0D /**< Dst Offset characteristic UUID. */ +#define BLE_UUID_EXACT_TIME_256_CHAR 0x2A0C /**< Exact Time 256 characteristic UUID. */ +#define BLE_UUID_FIRMWARE_REVISION_STRING_CHAR 0x2A26 /**< Firmware Revision String characteristic UUID. */ +#define BLE_UUID_GLUCOSE_FEATURE_CHAR 0x2A51 /**< Glucose Feature characteristic UUID. */ +#define BLE_UUID_GLUCOSE_MEASUREMENT_CHAR 0x2A18 /**< Glucose Measurement characteristic UUID. */ +#define BLE_UUID_GLUCOSE_MEASUREMENT_CONTEXT_CHAR 0x2A34 /**< Glucose Measurement Context characteristic UUID. */ +#define BLE_UUID_HARDWARE_REVISION_STRING_CHAR 0x2A27 /**< Hardware Revision String characteristic UUID. */ +#define BLE_UUID_HEART_RATE_CONTROL_POINT_CHAR 0x2A39 /**< Heart Rate Control Point characteristic UUID. */ +#define BLE_UUID_HEART_RATE_MEASUREMENT_CHAR 0x2A37 /**< Heart Rate Measurement characteristic UUID. */ +#define BLE_UUID_HID_CONTROL_POINT_CHAR 0x2A4C /**< Hid Control Point characteristic UUID. */ +#define BLE_UUID_HID_INFORMATION_CHAR 0x2A4A /**< Hid Information characteristic UUID. */ +#define BLE_UUID_IEEE_REGULATORY_CERTIFICATION_DATA_LIST_CHAR 0x2A2A /**< IEEE Regulatory Certification Data List characteristic UUID. */ +#define BLE_UUID_INTERMEDIATE_CUFF_PRESSURE_CHAR 0x2A36 /**< Intermediate Cuff Pressure characteristic UUID. */ +#define BLE_UUID_INTERMEDIATE_TEMPERATURE_CHAR 0x2A1E /**< Intermediate Temperature characteristic UUID. */ +#define BLE_UUID_LOCAL_TIME_INFORMATION_CHAR 0x2A0F /**< Local Time Information characteristic UUID. */ +#define BLE_UUID_MANUFACTURER_NAME_STRING_CHAR 0x2A29 /**< Manufacturer Name String characteristic UUID. */ +#define BLE_UUID_MEASUREMENT_INTERVAL_CHAR 0x2A21 /**< Measurement Interval characteristic UUID. */ +#define BLE_UUID_MODEL_NUMBER_STRING_CHAR 0x2A24 /**< Model Number String characteristic UUID. */ +#define BLE_UUID_UNREAD_ALERT_CHAR 0x2A45 /**< Unread Alert characteristic UUID. */ +#define BLE_UUID_NEW_ALERT_CHAR 0x2A46 /**< New Alert characteristic UUID. */ +#define BLE_UUID_PNP_ID_CHAR 0x2A50 /**< PNP Id characteristic UUID. */ +#define BLE_UUID_PROTOCOL_MODE_CHAR 0x2A4E /**< Protocol Mode characteristic UUID. */ +#define BLE_UUID_RECORD_ACCESS_CONTROL_POINT_CHAR 0x2A52 /**< Record Access Control Point characteristic UUID. */ +#define BLE_UUID_REFERENCE_TIME_INFORMATION_CHAR 0x2A14 /**< Reference Time Information characteristic UUID. */ +#define BLE_UUID_REPORT_CHAR 0x2A4D /**< Report characteristic UUID. */ +#define BLE_UUID_REPORT_MAP_CHAR 0x2A4B /**< Report Map characteristic UUID. */ +#define BLE_UUID_RINGER_CONTROL_POINT_CHAR 0x2A40 /**< Ringer Control Point characteristic UUID. */ +#define BLE_UUID_RINGER_SETTING_CHAR 0x2A41 /**< Ringer Setting characteristic UUID. */ +#define BLE_UUID_SCAN_INTERVAL_WINDOW_CHAR 0x2A4F /**< Scan Interval Window characteristic UUID. */ +#define BLE_UUID_SCAN_REFRESH_CHAR 0x2A31 /**< Scan Refresh characteristic UUID. */ +#define BLE_UUID_SERIAL_NUMBER_STRING_CHAR 0x2A25 /**< Serial Number String characteristic UUID. */ +#define BLE_UUID_SOFTWARE_REVISION_STRING_CHAR 0x2A28 /**< Software Revision String characteristic UUID. */ +#define BLE_UUID_SUPPORTED_NEW_ALERT_CATEGORY_CHAR 0x2A47 /**< Supported New Alert Category characteristic UUID. */ +#define BLE_UUID_SUPPORTED_UNREAD_ALERT_CATEGORY_CHAR 0x2A48 /**< Supported Unread Alert Category characteristic UUID. */ +#define BLE_UUID_SYSTEM_ID_CHAR 0x2A23 /**< System Id characteristic UUID. */ +#define BLE_UUID_TEMPERATURE_MEASUREMENT_CHAR 0x2A1C /**< Temperature Measurement characteristic UUID. */ +#define BLE_UUID_TEMPERATURE_TYPE_CHAR 0x2A1D /**< Temperature Type characteristic UUID. */ +#define BLE_UUID_TIME_ACCURACY_CHAR 0x2A12 /**< Time Accuracy characteristic UUID. */ +#define BLE_UUID_TIME_SOURCE_CHAR 0x2A13 /**< Time Source characteristic UUID. */ +#define BLE_UUID_TIME_UPDATE_CONTROL_POINT_CHAR 0x2A16 /**< Time Update Control Point characteristic UUID. */ +#define BLE_UUID_TIME_UPDATE_STATE_CHAR 0x2A17 /**< Time Update State characteristic UUID. */ +#define BLE_UUID_TIME_WITH_DST_CHAR 0x2A11 /**< Time With Dst characteristic UUID. */ +#define BLE_UUID_TIME_ZONE_CHAR 0x2A0E /**< Time Zone characteristic UUID. */ +#define BLE_UUID_TX_POWER_LEVEL_CHAR 0x2A07 /**< TX Power Level characteristic UUID. */ +#define BLE_UUID_CSC_FEATURE_CHAR 0x2A5C /**< Cycling Speed and Cadence Feature characteristic UUID. */ +#define BLE_UUID_CSC_MEASUREMENT_CHAR 0x2A5B /**< Cycling Speed and Cadence Measurement characteristic UUID. */ +#define BLE_UUID_RSC_FEATURE_CHAR 0x2A54 /**< Running Speed and Cadence Feature characteristic UUID. */ +#define BLE_UUID_SC_CTRLPT_CHAR 0x2A55 /**< Speed and Cadence Control Point UUID. */ +#define BLE_UUID_RSC_MEASUREMENT_CHAR 0x2A53 /**< Running Speed and Cadence Measurement characteristic UUID. */ +#define BLE_UUID_SENSOR_LOCATION_CHAR 0x2A5D /**< Sensor Location characteristic UUID. */ +#define BLE_UUID_EXTERNAL_REPORT_REF_DESCR 0x2907 /**< External Report Reference descriptor UUID. */ +#define BLE_UUID_REPORT_REF_DESCR 0x2908 /**< Report Reference descriptor UUID. */ +#define BLE_UUID_LN_FEATURE_CHAR 0x2A6A /**< Location Navigation Service, Feature characteristic UUID. */ +#define BLE_UUID_LN_POSITION_QUALITY_CHAR 0x2A69 /**< Location Navigation Service, Position quality UUID. */ +#define BLE_UUID_LN_LOCATION_AND_SPEED_CHAR 0x2A67 /**< Location Navigation Service, Location and Speed characteristic UUID. */ +#define BLE_UUID_LN_NAVIGATION_CHAR 0x2A68 /**< Location Navigation Service, Navigation characteristic UUID. */ +#define BLE_UUID_LN_CONTROL_POINT_CHAR 0x2A6B /**< Location Navigation Service, Control point characteristic UUID. */ +#define BLE_UUID_BMS_CTRLPT 0x2AA4 /**< BMS Control Point characteristic UUID. */ +#define BLE_UUID_BMS_FEATURE 0x2AA5 /**< BMS Feature characteristic UUID. */ +#define BLE_UUID_CGM_MEASUREMENT 0x2AA7 /**< CGM Service, Measurement characteristic UUID*/ +#define BLE_UUID_CGM_FEATURE 0x2AA8 /**< CGM Service, Feature characteristic UUID*/ +#define BLE_UUID_CGM_STATUS 0x2AA9 /**< CGM Service, Status characteristic UUID*/ +#define BLE_UUID_CGM_SESSION_START_TIME 0x2AAA /**< CGM Service, session start time characteristic UUID*/ +#define BLE_UUID_CGM_SESSION_RUN_TIME 0x2AAB /**< CGM Service, session run time characteristic UUID*/ +#define BLE_UUID_CGM_SPECIFIC_OPS_CTRLPT 0x2AAC /**< CGM Service, specific ops ctrlpt characteristic UUID*/ +#define BLE_UUID_PLX_SPOT_CHECK_MEAS 0x2A5E /**< PLX Service, spot check measurement characteristic UUID*/ +#define BLE_UUID_PLX_CONTINUOUS_MEAS 0x2A5F /**< PLX Service, continuous measurement characteristic UUID*/ +#define BLE_UUID_PLX_FEATURES 0x2A60 /**< PLX Service, feature characteristic UUID*/ +#define BLE_UUID_OTS_FEATURES 0x2ABD /**< OTS Service, feature characteristic UUID*/ +#define BLE_UUID_OTS_OBJECT_NAME 0x2ABE /**< OTS Service, Object Name characteristic UUID*/ +#define BLE_UUID_OTS_OBJECT_TYPE 0x2ABF /**< OTS Service, Object Type characteristic UUID*/ +#define BLE_UUID_OTS_OBJECT_SIZE 0x2AC0 /**< OTS Service, Object Size characteristic UUID*/ +#define BLE_UUID_OTS_OBJECT_FIRST_CREATED 0x2AC1 /**< OTS Service, Object First Created characteristic UUID*/ +#define BLE_UUID_OTS_OBJECT_LAST_MODIFIED 0x2AC2 /**< OTS Service, Object Last Modified characteristic UUID*/ +#define BLE_UUID_OTS_OBJECT_ID 0x2AC3 /**< OTS Service, Object ID characteristic UUID*/ +#define BLE_UUID_OTS_OBJECT_PROPERTIES 0x2AC4 /**< OTS Service, Object Properties characteristic UUID*/ +#define BLE_UUID_OTS_OACP 0x2AC5 /**< OTS Service, Object Action Control Point characteristic UUID*/ +#define BLE_UUID_OTS_OLCP 0x2AC6 /**< OTS Service, Object List Control Point characteristic UUID*/ +#define BLE_UUID_OTS_LF 0x2AC7 /**< OTS Service, Object List Filter characteristic UUID*/ +#define BLE_UUID_OTS_OBJECT_CHANGED 0x2AC8 /**< OTS Service, Object Changed characteristic UUID*/ + + + + +/** @} */ + +/** @defgroup ALERT_LEVEL_VALUES Definitions for the Alert Level characteristic values + * @{ */ +#define BLE_CHAR_ALERT_LEVEL_NO_ALERT 0x00 /**< No Alert. */ +#define BLE_CHAR_ALERT_LEVEL_MILD_ALERT 0x01 /**< Mild Alert. */ +#define BLE_CHAR_ALERT_LEVEL_HIGH_ALERT 0x02 /**< High Alert. */ +/** @} */ + +#define BLE_SRV_ENCODED_REPORT_REF_LEN 2 /**< The length of an encoded Report Reference Descriptor. */ +#define BLE_CCCD_VALUE_LEN 2 /**< The length of a CCCD value. */ + +/**@brief Type definition for error handler function that will be called in case of an error in + * a service or a service library module. */ +typedef void (*ble_srv_error_handler_t) (uint32_t nrf_error); + + + +/**@brief Value of a Report Reference descriptor. + * + * @details This is mapping information that maps the parent characteristic to the Report ID(s) and + * Report Type(s) defined within a Report Map characteristic. + */ +typedef struct +{ + uint8_t report_id; /**< Non-zero value if there is more than one instance of the same Report Type */ + uint8_t report_type; /**< Type of Report characteristic (see @ref BLE_HIDS_REPORT_TYPE) */ +} ble_srv_report_ref_t; + +/**@brief UTF-8 string data type. + * + * @note The type can only hold a pointer to the string data (i.e. not the actual data). + */ +typedef struct +{ + uint16_t length; /**< String length. */ + uint8_t * p_str; /**< String data. */ +} ble_srv_utf8_str_t; + + +/**@brief Security settings structure. + * @details This structure contains the security options needed during initialization of the + * service. + */ +typedef struct +{ + ble_gap_conn_sec_mode_t read_perm; /**< Read permissions. */ + ble_gap_conn_sec_mode_t write_perm; /**< Write permissions. */ +} ble_srv_security_mode_t; + +/**@brief Security settings structure. + * @details This structure contains the security options needed during initialization of the + * service. It can be used when the characteristics contains a CCCD. + */ +typedef struct +{ + ble_gap_conn_sec_mode_t cccd_write_perm; /**< Write permissions for Client Characteristic Configuration Descriptor. */ + ble_gap_conn_sec_mode_t read_perm; /**< Read permissions. */ + ble_gap_conn_sec_mode_t write_perm; /**< Write permissions. */ +} ble_srv_cccd_security_mode_t; + +/**@brief Function for decoding a CCCD value, and then testing if notification is + * enabled. + * + * @param[in] p_encoded_data Buffer where the encoded CCCD is stored. + * + * @retval TRUE If notification is enabled. + * @retval FALSE Otherwise. + */ +bool ble_srv_is_notification_enabled(uint8_t const * p_encoded_data); + + +/**@brief Function for decoding a CCCD value, and then testing if indication is + * enabled. + * + * @param[in] p_encoded_data Buffer where the encoded CCCD is stored. + * + * @retval TRUE If indication is enabled. + * @retval FALSE Otherwise. + */ +bool ble_srv_is_indication_enabled(uint8_t const * p_encoded_data); + + +/**@brief Function for encoding a Report Reference Descriptor. + * + * @param[in] p_encoded_buffer The buffer of the encoded data. + * @param[in] p_report_ref Report Reference value to be encoded. + * + * @return Length of the encoded data. + */ +uint8_t ble_srv_report_ref_encode(uint8_t * p_encoded_buffer, + const ble_srv_report_ref_t * p_report_ref); + +/**@brief Function for making a UTF-8 structure refer to an ASCII string. + * + * @param[out] p_utf8 UTF-8 structure to be set. + * @param[in] p_ascii ASCII string to be referred to. + */ +void ble_srv_ascii_to_utf8(ble_srv_utf8_str_t * p_utf8, char * p_ascii); + + +/**@brief Security Access enumeration. + * @details This enumeration gives the possible requirements for accessing a characteristic value. + */ +typedef enum +{ + SEC_NO_ACCESS = 0, /**< Not possible to access. */ + SEC_OPEN = 1, /**< Access open. */ + SEC_JUST_WORKS = 2, /**< Access possible with 'Just Works' security at least. */ + SEC_MITM = 3, /**< Access possible with 'MITM' security at least. */ + SEC_SIGNED = 4, /**< Access possible with 'signed' security at least. */ + SEC_SIGNED_MITM = 5 /**< Access possible with 'signed and MITM' security at least. */ +}security_req_t; + + +/**@brief Characteristic User Descriptor parameters. + * @details This structure contains the parameters for User Descriptor. + */ +typedef struct +{ + uint16_t max_size; /**< Maximum size of the user descriptor*/ + uint16_t size; /**< Size of the user descriptor*/ + uint8_t *p_char_user_desc; /**< User descriptor content, pointer to a UTF-8 encoded string (non-NULL terminated)*/ + bool is_var_len; /**< Indicates if the user descriptor has variable length.*/ + ble_gatt_char_props_t char_props; /**< user descriptor properties.*/ + bool is_defered_read; /**< Indicate if deferred read operations are supported.*/ + bool is_defered_write; /**< Indicate if deferred write operations are supported.*/ + security_req_t read_access; /**< Security requirement for reading the user descriptor.*/ + security_req_t write_access; /**< Security requirement for writing the user descriptor.*/ + bool is_value_user; /**< Indicate if the content of the characteristic is to be stored in the application (user) or in the stack.*/ +}ble_add_char_user_desc_t; + + +/**@brief Add characteristic parameters structure. + * @details This structure contains the parameters needed to use the @ref characteristic_add function. + */ +typedef struct +{ + uint16_t uuid; /**< Characteristic UUID (16 bits UUIDs).*/ + uint8_t uuid_type; /**< Base UUID. If 0, the Bluetooth SIG UUID will be used. Otherwise, this should be a value returned by @ref sd_ble_uuid_vs_add when adding the base UUID.*/ + uint16_t max_len; /**< Maximum length of the characteristic value.*/ + uint16_t init_len; /**< Initial length of the characteristic value.*/ + uint8_t * p_init_value; /**< Initial encoded value of the characteristic.*/ + bool is_var_len; /**< Indicates if the characteristic value has variable length.*/ + ble_gatt_char_props_t char_props; /**< Characteristic properties.*/ + ble_gatt_char_ext_props_t char_ext_props; /**< Characteristic extended properties.*/ + bool is_defered_read; /**< Indicate if deferred read operations are supported.*/ + bool is_defered_write; /**< Indicate if deferred write operations are supported.*/ + security_req_t read_access; /**< Security requirement for reading the characteristic value.*/ + security_req_t write_access; /**< Security requirement for writing the characteristic value.*/ + security_req_t cccd_write_access; /**< Security requirement for writing the characteristic's CCCD.*/ + bool is_value_user; /**< Indicate if the content of the characteristic is to be stored in the application (user) or in the stack.*/ + ble_add_char_user_desc_t *p_user_descr; /**< Pointer to user descriptor if needed*/ + ble_gatts_char_pf_t *p_presentation_format; /**< Pointer to characteristic format if needed*/ +} ble_add_char_params_t; + + +/**@brief Add descriptor parameters structure. + * @details This structure contains the parameters needed to use the @ref descriptor_add function. + */ +typedef struct +{ + uint16_t uuid; /**< descriptor UUID (16 bits UUIDs).*/ + uint8_t uuid_type; /**< Base UUID. If 0, the Bluetooth SIG UUID will be used. Otherwise, this should be a value returned by @ref sd_ble_uuid_vs_add when adding the base UUID.*/ + bool is_defered_read; /**< Indicate if deferred read operations are supported.*/ + bool is_defered_write; /**< Indicate if deferred write operations are supported.*/ + bool is_var_len; /**< Indicates if the descriptor value has variable length.*/ + security_req_t read_access; /**< Security requirement for reading the descriptor value.*/ + security_req_t write_access; /**< Security requirement for writing the descriptor value.*/ + bool is_value_user; /**< Indicate if the content of the characteristic is to be stored in the application (user) or in the stack.*/ + uint16_t init_len; /**< Initial descriptor value length in bytes. */ + uint16_t init_offs; /**< Initial descriptor value offset in bytes. If different from zero, the first init_offs bytes of the attribute value will be left uninitialized. */ + uint16_t max_len; /**< Maximum descriptor value length in bytes, see @ref BLE_GATTS_ATTR_LENS_MAX for maximum values. */ + uint8_t* p_value; /**< Pointer to the value of the descriptor*/ +} ble_add_descr_params_t; + + +/**@brief Function for adding a characteristic to a given service. + * + * If no pointer is given for the initial value, + * the initial length parameter will be ignored and the initial length will be 0. + * + * @param[in] service_handle Handle of the service to which the characteristic is to be added. + * @param[in] p_char_props Information needed to add the characteristic. + * @param[out] p_char_handle Handle of the added characteristic. + * + * @retval NRF_SUCCESS If the characteristic was added successfully. Otherwise, an error code is returned. + */ +uint32_t characteristic_add(uint16_t service_handle, + ble_add_char_params_t * p_char_props, + ble_gatts_char_handles_t * p_char_handle); + + +/**@brief Function for adding a characteristic's descriptor to a given characteristic. + * + * @param[in] char_handle Handle of the characteristic to which the descriptor is to be added, if @ref BLE_GATT_HANDLE_INVALID is used, it will be placed sequentially. + * @param[in] p_descr_props Information needed to add the descriptor. + * @param[out] p_descr_handle Handle of the added descriptor. + * + * @retval NRF_SUCCESS If the characteristic was added successfully. Otherwise, an error code is returned. + */ +uint32_t descriptor_add(uint16_t char_handle, + ble_add_descr_params_t * p_descr_props, + uint16_t * p_descr_handle); + + + +#ifdef __cplusplus +} +#endif + +#endif // BLE_SRV_COMMON_H__ + +/** @} */ diff --git a/libraries/nfc/src/util/nfc_platform.c b/libraries/nfc/src/util/nfc_platform.c new file mode 100644 index 000000000..38edf268a --- /dev/null +++ b/libraries/nfc/src/util/nfc_platform.c @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2018 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#include "nrf_drv_clock.h" +//#if NRF_MODULE_ENABLED(NFC_PLATFORM) + +#include "nfc_platform.h" +#define NRF_LOG_MODULE_NAME nfc_platform +#if NFC_PLATFORM_LOG_ENABLED +#define NRF_LOG_LEVEL NFC_PLATFORM_LOG_LEVEL +#define NRF_LOG_INFO_COLOR NFC_PLATFORM_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR NFC_PLATFORM_DEBUG_COLOR +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); +#else // NFC_PLATFORM_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#include "nrf_log.h" +#endif // NFC_PLATFORM_LOG_ENABLED + +/* Static data */ +static nrf_drv_clock_handler_item_t m_clock_handler_item; /**< Clock event handler item structure. */ + +/**@brief Function for handling events from the Clock module. + * + * @param[in] event Clock event. + * + */ +static inline void clock_event_handler(nrf_drv_clock_evt_type_t event) +{ + switch(event) + { + case NRF_DRV_CLOCK_EVT_HFCLK_STARTED: + /* Activate NFCT only when HFXO is running */ + nrfx_nfct_state_force(NRFX_NFCT_STATE_ACTIVATED); + break; + + default: + /* No implementation required */ + break; + } +} + + +nrfx_err_t nfc_platform_setup(void) +{ + nrfx_err_t err_code; + + /* Initialize the Clock module for handling high precision clock requests */ + m_clock_handler_item.event_handler = clock_event_handler; + m_clock_handler_item.p_next = NULL; + + err_code = nrf_drv_clock_init(); + if (err_code == NRF_ERROR_MODULE_ALREADY_INITIALIZED) + { + err_code = NRFX_SUCCESS; + } + else if (err_code != NRF_SUCCESS) + { + return NRFX_ERROR_INTERNAL; + } + + NRF_LOG_DEBUG("Utils init"); + return err_code; +} + + +void nfc_platform_event_handler(nrfx_nfct_evt_t const * p_event) +{ + switch (p_event->evt_id) + { + case NRFX_NFCT_EVT_FIELD_DETECTED: + NRF_LOG_DEBUG("Field detected"); + nrf_drv_clock_hfclk_request(&m_clock_handler_item); + break; + + case NRFX_NFCT_EVT_FIELD_LOST: + NRF_LOG_DEBUG("Field lost"); + nrf_drv_clock_hfclk_release(); + break; + + default: + /* No implementation required */ + break; + } +} + +//#endif // NRF_MODULE_ENABLED(NFC_PLATFORM) diff --git a/libraries/nfc/src/util/nfc_platform.h b/libraries/nfc/src/util/nfc_platform.h new file mode 100644 index 000000000..a1279af63 --- /dev/null +++ b/libraries/nfc/src/util/nfc_platform.h @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2018 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NFC_PLATFORM_H__ +#define NFC_PLATFORM_H__ + +/** @file + * + * @addtogroup nfc_api + * + * @defgroup nfc_platform Platform-specific module for NFC + * @{ + * @ingroup nfc_api + * @brief @tagAPI52 Platform-specific module for Near Field Communication Tag (NFCT). + * + * This module is used to set up platform-specific components that are required for NFC, and to + * activate NFCT peripheral when all necessary conditions are fulfilled. + * + * @note Before the NFCT peripheral enters the ACTIVATED state, the HFXO must be running. To fulfill + * this requirement, this module uses the clock management module. + * + */ + +#include +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for initializing platform-specific modules required by NFC. + * + * This function sets up clock managing interface and other platform specific components + * that are required for NFC. + * + * @retval NRFX_SUCCESS If the NFC module is initialized successfully. If one + * of the arguments is invalid, an error code is returned. + */ +nrfx_err_t nfc_platform_setup(void); + + +/** + * @brief Function for handling NFCT events that require platform-specific actions. + * + * This function is used by the NFC platform module to observe NFC events. This event flow is + * necessary to track in order to determine when HFXO must be running and when the NFCT peripheral must + * be activated. + * + * @param[in] p_event NFCT driver event. + */ +void nfc_platform_event_handler(nrfx_nfct_evt_t const * p_event); + + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* NFC_PLATFORM_H__ */ + diff --git a/libraries/nfc/src/util/nordic_common.h b/libraries/nfc/src/util/nordic_common.h new file mode 100644 index 000000000..def79949b --- /dev/null +++ b/libraries/nfc/src/util/nordic_common.h @@ -0,0 +1,215 @@ +/** + * Copyright (c) 2008 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * @brief Common defines and macros for firmware developed by Nordic Semiconductor. + */ + +#ifndef NORDIC_COMMON_H__ +#define NORDIC_COMMON_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Check if selected module is enabled + * + * This is save function for driver enable checking. + * Correct from Lint point of view (not using default of undefined value). + * + * Usage: + * @code + #if NRF_MODULE_ENABLED(UART) + ... + #endif + * @endcode + * + * @param module The module name. + * + * @retval 1 The macro _ENABLE is defined and is non-zero. + * @retval 0 The macro _ENABLE is not defined or it equals zero. + * + * @note + * This macro intentionally does not implement second expansion level. + * The name of the module to be checked has to be given directly as a parameter. + * And given parameter would be connected with @c _ENABLED postfix directly + * without evaluating its value. + */ +//lint -emacro(491,NRF_MODULE_ENABLED) // Suppers warning 491 "non-standard use of 'defined' preprocessor operator" +#ifdef NRF_MODULE_ENABLE_ALL +#warning "Do not use NRF_MODULE_ENABLE_ALL for real builds." +#define NRF_MODULE_ENABLED(module) 1 +#else +#define NRF_MODULE_ENABLED(module) \ + ((defined(module ## _ENABLED) && (module ## _ENABLED)) ? 1 : 0) +#endif +/** The upper 8 bits of a 32 bit value */ +//lint -emacro(572,MSB_32) // Suppress warning 572 "Excessive shift value" +#define MSB_32(a) (((a) & 0xFF000000) >> 24) +/** The lower 8 bits (of a 32 bit value) */ +#define LSB_32(a) ((a) & 0x000000FF) + +/** The upper 8 bits of a 16 bit value */ +//lint -emacro(572,MSB_16) // Suppress warning 572 "Excessive shift value" +#define MSB_16(a) (((a) & 0xFF00) >> 8) +/** The lower 8 bits (of a 16 bit value) */ +#define LSB_16(a) ((a) & 0x00FF) + +/** Leaves the minimum of the two 32-bit arguments */ +/*lint -emacro(506, MIN) */ /* Suppress "Constant value Boolean */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +/** Leaves the maximum of the two 32-bit arguments */ +/*lint -emacro(506, MAX) */ /* Suppress "Constant value Boolean */ +#define MAX(a, b) ((a) < (b) ? (b) : (a)) + +/**@brief Concatenates two parameters. + * + * It realizes two level expansion to make it sure that all the parameters + * are actually expanded before gluing them together. + * + * @param p1 First parameter to concatenating + * @param p2 Second parameter to concatenating + * + * @return Two parameters glued together. + * They have to create correct C mnemonic in other case + * preprocessor error would be generated. + * + * @sa CONCAT_3 + */ +#define CONCAT_2(p1, p2) CONCAT_2_(p1, p2) +/** Auxiliary macro used by @ref CONCAT_2 */ +#define CONCAT_2_(p1, p2) p1##p2 + +/**@brief Concatenates three parameters. + * + * It realizes two level expansion to make it sure that all the parameters + * are actually expanded before gluing them together. + * + * @param p1 First parameter to concatenating + * @param p2 Second parameter to concatenating + * @param p3 Third parameter to concatenating + * + * @return Three parameters glued together. + * They have to create correct C mnemonic in other case + * preprocessor error would be generated. + * + * @sa CONCAT_2 + */ +#define CONCAT_3(p1, p2, p3) CONCAT_3_(p1, p2, p3) +/** Auxiliary macro used by @ref CONCAT_3 */ +#define CONCAT_3_(p1, p2, p3) p1##p2##p3 + +#define STRINGIFY_(val) #val +/** Converts a macro argument into a character constant. + */ +#define STRINGIFY(val) STRINGIFY_(val) + +/** Counts number of elements inside the array + */ +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +/**@brief Set a bit in the uint32 word. + * + * @param[in] W Word whose bit is being set. + * @param[in] B Bit number in the word to be set. + */ +#define SET_BIT(W, B) ((W) |= (uint32_t)(1U << (B))) + + +/**@brief Clears a bit in the uint32 word. + * + * @param[in] W Word whose bit is to be cleared. + * @param[in] B Bit number in the word to be cleared. + */ +#define CLR_BIT(W, B) ((W) &= (~(uint32_t)(1U << (B)))) + + +/**@brief Checks if a bit is set. + * + * @param[in] W Word whose bit is to be checked. + * @param[in] B Bit number in the word to be checked. + * + * @retval 1 if bit is set. + * @retval 0 if bit is not set. + */ +#define IS_SET(W, B) (((W) >> (B)) & 1) + +#define BIT_0 0x01 /**< The value of bit 0 */ +#define BIT_1 0x02 /**< The value of bit 1 */ +#define BIT_2 0x04 /**< The value of bit 2 */ +#define BIT_3 0x08 /**< The value of bit 3 */ +#define BIT_4 0x10 /**< The value of bit 4 */ +#define BIT_5 0x20 /**< The value of bit 5 */ +#define BIT_6 0x40 /**< The value of bit 6 */ +#define BIT_7 0x80 /**< The value of bit 7 */ +#define BIT_8 0x0100 /**< The value of bit 8 */ +#define BIT_9 0x0200 /**< The value of bit 9 */ +#define BIT_10 0x0400 /**< The value of bit 10 */ +#define BIT_11 0x0800 /**< The value of bit 11 */ +#define BIT_12 0x1000 /**< The value of bit 12 */ +#define BIT_13 0x2000 /**< The value of bit 13 */ +#define BIT_14 0x4000 /**< The value of bit 14 */ +#define BIT_15 0x8000 /**< The value of bit 15 */ +#define BIT_16 0x00010000 /**< The value of bit 16 */ +#define BIT_17 0x00020000 /**< The value of bit 17 */ +#define BIT_18 0x00040000 /**< The value of bit 18 */ +#define BIT_19 0x00080000 /**< The value of bit 19 */ +#define BIT_20 0x00100000 /**< The value of bit 20 */ +#define BIT_21 0x00200000 /**< The value of bit 21 */ +#define BIT_22 0x00400000 /**< The value of bit 22 */ +#define BIT_23 0x00800000 /**< The value of bit 23 */ +#define BIT_24 0x01000000 /**< The value of bit 24 */ +#define BIT_25 0x02000000 /**< The value of bit 25 */ +#define BIT_26 0x04000000 /**< The value of bit 26 */ +#define BIT_27 0x08000000 /**< The value of bit 27 */ +#define BIT_28 0x10000000 /**< The value of bit 28 */ +#define BIT_29 0x20000000 /**< The value of bit 29 */ +#define BIT_30 0x40000000 /**< The value of bit 30 */ +#define BIT_31 0x80000000 /**< The value of bit 31 */ + +#define UNUSED_VARIABLE(X) ((void)(X)) +#define UNUSED_PARAMETER(X) UNUSED_VARIABLE(X) +#define UNUSED_RETURN_VALUE(X) UNUSED_VARIABLE(X) + +#ifdef __cplusplus +} +#endif + +#endif // NORDIC_COMMON_H__ diff --git a/libraries/nfc/src/util/nrf_assert.c b/libraries/nfc/src/util/nrf_assert.c new file mode 100644 index 000000000..162465763 --- /dev/null +++ b/libraries/nfc/src/util/nrf_assert.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2006 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nrf_assert.h" +#include "app_error.h" +#include "nordic_common.h" + +__WEAK void assert_nrf_callback(uint16_t line_num, const uint8_t * file_name) +{ + assert_info_t assert_info = + { + .line_num = line_num, + .p_file_name = file_name, + }; + app_error_fault_handler(NRF_FAULT_ID_SDK_ASSERT, 0, (uint32_t)(&assert_info)); + + UNUSED_VARIABLE(assert_info); +} diff --git a/libraries/nfc/src/util/nrf_assert.h b/libraries/nfc/src/util/nrf_assert.h new file mode 100644 index 000000000..08771e125 --- /dev/null +++ b/libraries/nfc/src/util/nrf_assert.h @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2006 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * @brief Utilities for verifying program logic + */ + +#ifndef NRF_ASSERT_H_ +#define NRF_ASSERT_H_ + +#include +#include "nrf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Function for handling assertions. + * + * + * @note + * This function is called when an assertion has triggered. + * + * @note + * This function is deprecated and will be removed in future releases. + * Use app_error_fault_handler instead. + * + * + * @post + * All hardware is put into an idle non-emitting state (in particular the radio is highly + * important to switch off since the radio might be in a state that makes it send + * packets continiously while a typical final infinit ASSERT loop is executing). + * + * + * @param line_num The line number where the assertion is called + * @param file_name Pointer to the file name + */ +//lint -save -esym(14, assert_nrf_callback) +void assert_nrf_callback(uint16_t line_num, const uint8_t *file_name); +//lint -restore + +#if (defined(DEBUG_NRF) || defined(DEBUG_NRF_USER)) +#define NRF_ASSERT_PRESENT 1 +#else +#define NRF_ASSERT_PRESENT 0 +#endif + +//#if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER) + +/*lint -emacro(506, ASSERT) */ /* Suppress "Constant value Boolean */ +/*lint -emacro(774, ASSERT) */ /* Suppress "Boolean within 'if' always evaluates to True" */ \ + +/** @brief Function for checking intended for production code. + * + * Check passes if "expr" evaluates to true. */ + +#ifdef _lint +#define ASSERT(expr) \ +if (expr) \ +{ \ +} \ +else \ +{ \ + while (1); \ +} +#else //_lint +#define ASSERT(expr) \ +if (NRF_ASSERT_PRESENT) \ +{ \ + if (expr) \ + { \ + } \ + else \ + { \ + assert_nrf_callback((uint16_t)__LINE__, (uint8_t *)__FILE__); \ + } \ +} + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_ASSERT_H_ */ diff --git a/libraries/nfc/src/util/nrf_atomic.c b/libraries/nfc/src/util/nrf_atomic.c new file mode 100644 index 000000000..7303254e4 --- /dev/null +++ b/libraries/nfc/src/util/nrf_atomic.c @@ -0,0 +1,449 @@ +/** + * Copyright (c) 2018 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nrf_atomic.h" + +#ifndef NRF_ATOMIC_USE_BUILD_IN +#if (defined(__GNUC__) && defined(WIN32)) + #define NRF_ATOMIC_USE_BUILD_IN 1 +#else + #define NRF_ATOMIC_USE_BUILD_IN 0 +#endif +#endif // NRF_ATOMIC_USE_BUILD_IN + +#if ((__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)) +#define STREX_LDREX_PRESENT +#else +#include "app_util_platform.h" +#endif + + +#if (NRF_ATOMIC_USE_BUILD_IN == 0) && defined(STREX_LDREX_PRESENT) +#include "nrf_atomic_internal.h" +#endif + +uint32_t nrf_atomic_u32_fetch_store(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_exchange_n(p_data, value, __ATOMIC_SEQ_CST); + +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + NRF_ATOMIC_OP(mov, old_val, new_val, p_data, value); + + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return old_val; +#else + CRITICAL_REGION_ENTER(); + uint32_t old_val = *p_data; + *p_data = value; + CRITICAL_REGION_EXIT(); + return old_val; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_store(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + __atomic_store_n(p_data, value, __ATOMIC_SEQ_CST); + return value; +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(mov, old_val, new_val, p_data, value); + + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return new_val; +#else + CRITICAL_REGION_ENTER(); + *p_data = value; + CRITICAL_REGION_EXIT(); + return value; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_fetch_or(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_fetch_or(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(orr, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return old_val; +#else + CRITICAL_REGION_ENTER(); + uint32_t old_val = *p_data; + *p_data |= value; + CRITICAL_REGION_EXIT(); + return old_val; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_or(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_or_fetch(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(orr, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return new_val; +#else + CRITICAL_REGION_ENTER(); + *p_data |= value; + uint32_t new_value = *p_data; + CRITICAL_REGION_EXIT(); + return new_value; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_fetch_and(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_fetch_and(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(and, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return old_val; +#else + CRITICAL_REGION_ENTER(); + uint32_t old_val = *p_data; + *p_data &= value; + CRITICAL_REGION_EXIT(); + return old_val; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_and(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_and_fetch(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(and, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return new_val; +#else + CRITICAL_REGION_ENTER(); + *p_data &= value; + uint32_t new_value = *p_data; + CRITICAL_REGION_EXIT(); + return new_value; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_fetch_xor(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_fetch_xor(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(eor, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return old_val; +#else + CRITICAL_REGION_ENTER(); + uint32_t old_val = *p_data; + *p_data ^= value; + CRITICAL_REGION_EXIT(); + return old_val; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_xor(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_xor_fetch(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(eor, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return new_val; +#else + CRITICAL_REGION_ENTER(); + *p_data ^= value; + uint32_t new_value = *p_data; + CRITICAL_REGION_EXIT(); + return new_value; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_fetch_add(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_fetch_add(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(add, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return old_val; +#else + CRITICAL_REGION_ENTER(); + uint32_t old_val = *p_data; + *p_data += value; + CRITICAL_REGION_EXIT(); + return old_val; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_add(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_add_fetch(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(add, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return new_val; +#else + CRITICAL_REGION_ENTER(); + *p_data += value; + uint32_t new_value = *p_data; + CRITICAL_REGION_EXIT(); + return new_value; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_fetch_sub(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_fetch_sub(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(sub, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return old_val; +#else + CRITICAL_REGION_ENTER(); + uint32_t old_val = *p_data; + *p_data -= value; + CRITICAL_REGION_EXIT(); + return old_val; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_sub(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_sub_fetch(p_data, value, __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(sub, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return new_val; +#else + CRITICAL_REGION_ENTER(); + *p_data -= value; + uint32_t new_value = *p_data; + CRITICAL_REGION_EXIT(); + return new_value; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +bool nrf_atomic_u32_cmp_exch(nrf_atomic_u32_t * p_data, + uint32_t * p_expected, + uint32_t desired) +{ +#if NRF_ATOMIC_USE_BUILD_IN + return __atomic_compare_exchange(p_data, + p_expected, + &desired, + 1, + __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +#elif defined(STREX_LDREX_PRESENT) + return nrf_atomic_internal_cmp_exch(p_data, p_expected, desired); +#else + bool ret; + CRITICAL_REGION_ENTER(); + if (*p_data == *p_expected) + { + *p_data = desired; + ret = true; + } + else + { + *p_expected = *p_data; + ret = false; + } + CRITICAL_REGION_EXIT(); + return ret; +#endif +} + +uint32_t nrf_atomic_u32_fetch_sub_hs(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + uint32_t expected = *p_data; + uint32_t new_val; + bool success; + + do + { + if (expected >= value) + { + new_val = expected - value; + } + else + { + new_val = expected; + } + success = __atomic_compare_exchange(p_data, + &expected, + &new_val, + 1, + __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); + } while(!success); + return expected; +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(sub_hs, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return old_val; +#else + CRITICAL_REGION_ENTER(); + uint32_t old_val = *p_data; + *p_data -= value; + CRITICAL_REGION_EXIT(); + return old_val; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_u32_sub_hs(nrf_atomic_u32_t * p_data, uint32_t value) +{ +#if NRF_ATOMIC_USE_BUILD_IN + uint32_t expected = *p_data; + uint32_t new_val; + bool success; + + do + { + if (expected >= value) + { + new_val = expected - value; + } + else + { + new_val = expected; + } + success = __atomic_compare_exchange(p_data, + &expected, + &new_val, + 1, + __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); + } while(!success); + return new_val; +#elif defined(STREX_LDREX_PRESENT) + uint32_t old_val; + uint32_t new_val; + + NRF_ATOMIC_OP(sub_hs, old_val, new_val, p_data, value); + UNUSED_PARAMETER(old_val); + UNUSED_PARAMETER(new_val); + return new_val; +#else + CRITICAL_REGION_ENTER(); + *p_data -= value; + uint32_t new_value = *p_data; + CRITICAL_REGION_EXIT(); + return new_value; +#endif //NRF_ATOMIC_USE_BUILD_IN +} + +uint32_t nrf_atomic_flag_set_fetch(nrf_atomic_flag_t * p_data) +{ + return nrf_atomic_u32_fetch_or(p_data, 1); +} + +uint32_t nrf_atomic_flag_set(nrf_atomic_flag_t * p_data) +{ + return nrf_atomic_u32_or(p_data, 1); +} + +uint32_t nrf_atomic_flag_clear_fetch(nrf_atomic_flag_t * p_data) +{ + return nrf_atomic_u32_fetch_and(p_data, 0); +} + +uint32_t nrf_atomic_flag_clear(nrf_atomic_flag_t * p_data) +{ + return nrf_atomic_u32_and(p_data, 0); +} + diff --git a/libraries/nfc/src/util/nrf_atomic.h b/libraries/nfc/src/util/nrf_atomic.h new file mode 100644 index 000000000..7bb7e8e0e --- /dev/null +++ b/libraries/nfc/src/util/nrf_atomic.h @@ -0,0 +1,274 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup nrf_atomic Atomic operations API + * @ingroup app_common + * @{ + * + * @brief @tagAPI52 This module implements C11 stdatomic.h simplified API. + At this point only Cortex-M3/M4 cores are supported (LDREX/STREX instructions). + * Atomic types are limited to @ref nrf_atomic_u32_t and @ref nrf_atomic_flag_t. + */ + +#ifndef NRF_ATOMIC_H__ +#define NRF_ATOMIC_H__ + +#include "sdk_common.h" + +/** + * @brief Atomic 32 bit unsigned type + * */ +typedef volatile uint32_t nrf_atomic_u32_t; + +/** + * @brief Atomic 1 bit flag type (technically 32 bit) + * */ +typedef volatile uint32_t nrf_atomic_flag_t; + + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Stores value to an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value to store + * + * @return Old value stored into atomic object + * */ +uint32_t nrf_atomic_u32_fetch_store(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Stores value to an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value to store + * + * @return New value stored into atomic object + * */ +uint32_t nrf_atomic_u32_store(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Logical OR operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand OR operation + * + * @return Old value stored into atomic object + * */ +uint32_t nrf_atomic_u32_fetch_or(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Logical OR operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand OR operation + * + * @return New value stored into atomic object + * */ +uint32_t nrf_atomic_u32_or(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Logical AND operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand AND operation + * + * @return Old value stored into atomic object + * */ +uint32_t nrf_atomic_u32_fetch_and(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Logical AND operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand AND operation + * + * @return New value stored into atomic object + * */ +uint32_t nrf_atomic_u32_and(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Logical XOR operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand XOR operation + * + * @return Old value stored into atomic object + * */ +uint32_t nrf_atomic_u32_fetch_xor(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Logical XOR operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand XOR operation + * + * @return New value stored into atomic object + * */ +uint32_t nrf_atomic_u32_xor(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Arithmetic ADD operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand ADD operation + * + * @return Old value stored into atomic object + * */ +uint32_t nrf_atomic_u32_fetch_add(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Arithmetic ADD operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand ADD operation + * + * @return New value stored into atomic object + * */ +uint32_t nrf_atomic_u32_add(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Arithmetic SUB operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand SUB operation + * + * @return Old value stored into atomic object + * */ +uint32_t nrf_atomic_u32_fetch_sub(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Arithmetic SUB operation on an atomic object + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand SUB operation + * + * @return New value stored into atomic object + * */ +uint32_t nrf_atomic_u32_sub(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief If value at pointer is equal to expected value, changes value at pointer to desired + * + * Atomically compares the value pointed to by p_data with the value pointed to by p_expected, + * and if those are equal, replaces the former with desired. Otherwise, loads the actual value + * pointed to by p_data into *p_expected. + * + * @param p_data Atomic memory pointer to test and modify. + * @param p_expected Pointer to test value. + * @param desired Value to be stored to atomic memory. + * + * @retval true *p_data was equal to *p_expected + * @retval false *p_data was not equal to *p_expected + */ +bool nrf_atomic_u32_cmp_exch(nrf_atomic_u32_t * p_data, + uint32_t * p_expected, + uint32_t desired); + +/** + * @brief Arithmetic SUB operation on an atomic object performed if object >= value. + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand SUB operation + * + * @return Old value stored into atomic object + * */ +uint32_t nrf_atomic_u32_fetch_sub_hs(nrf_atomic_u32_t * p_data, uint32_t value); + +/** + * @brief Arithmetic SUB operation on an atomic object performed if object >= value. + * + * @param[in] p_data Atomic memory pointer + * @param[in] value Value of second operand SUB operation + * + * @return New value stored into atomic object + * */ +uint32_t nrf_atomic_u32_sub_hs(nrf_atomic_u32_t * p_data, uint32_t value); + +/**************************************************************************************************/ + +/** + * @brief Logic one bit flag set operation on an atomic object + * + * @param[in] p_data Atomic flag memory pointer + * + * @return Old flag value + * */ +uint32_t nrf_atomic_flag_set_fetch(nrf_atomic_flag_t * p_data); + +/** + * @brief Logic one bit flag set operation on an atomic object + * + * @param[in] p_data Atomic flag memory pointer + * + * @return New flag value + * */ +uint32_t nrf_atomic_flag_set(nrf_atomic_flag_t * p_data); + +/** + * @brief Logic one bit flag clear operation on an atomic object + * + * @param[in] p_data Atomic flag memory pointer + * + * @return Old flag value + * */ +uint32_t nrf_atomic_flag_clear_fetch(nrf_atomic_flag_t * p_data); + +/** + * @brief Logic one bit flag clear operation on an atomic object + * + * @param[in] p_data Atomic flag memory pointer + * + * @return New flag value + * */ +uint32_t nrf_atomic_flag_clear(nrf_atomic_flag_t * p_data); + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_ATOMIC_H__ */ + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_atomic_internal.h b/libraries/nfc/src/util/nrf_atomic_internal.h new file mode 100644 index 000000000..a773d3377 --- /dev/null +++ b/libraries/nfc/src/util/nrf_atomic_internal.h @@ -0,0 +1,343 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_ATOMIC_INTERNAL_H__ +#define NRF_ATOMIC_INTERNAL_H__ + +#include "sdk_common.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @defgroup nrf_atomic_internal Atomic operations internals + * @ingroup nrf_atomic + * @{ + * + */ + +/* Only Cortex M cores > 3 support LDREX/STREX instructions*/ +#if ((__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)) == 0 +#error "Unsupported core version" +#endif + +#if defined ( __CC_ARM ) +static __asm uint32_t nrf_atomic_internal_mov(nrf_atomic_u32_t * p_ptr, + uint32_t value, + uint32_t * p_new) +{ + /* The base standard provides for passing arguments in core registers (r0-r3) and on the stack. + * Registers r4 and r5 have to be saved on stack. Note that only even number of register push are + * allowed. This is a requirement of the Procedure Call Standard for the ARM Architecture [AAPCS]. + * */ + push {r4, r5} + mov r4, r0 + +loop_mov + ldrex r0, [r4] + mov r5, r1 + strex r3, r5, [r4] + cmp r3, #0 + bne loop_mov + + str r5, [r2] + pop {r4, r5} + bx lr +} + + +static __asm uint32_t nrf_atomic_internal_orr(nrf_atomic_u32_t * p_ptr, + uint32_t value, + uint32_t * p_new) +{ + push {r4, r5} + mov r4, r0 + +loop_orr + ldrex r0, [r4] + orr r5, r0, r1 + strex r3, r5, [r4] + cmp r3, #0 + bne loop_orr + + str r5, [r2] + pop {r4, r5} + bx lr +} + +static __asm uint32_t nrf_atomic_internal_and(nrf_atomic_u32_t * p_ptr, + uint32_t value, + uint32_t * p_new) +{ + push {r4, r5} + mov r4, r0 + +loop_and + ldrex r0, [r4] + and r5, r0, r1 + strex r3, r5, [r4] + cmp r3, #0 + bne loop_and + + str r5, [r2] + pop {r4, r5} + bx lr +} + +static __asm uint32_t nrf_atomic_internal_eor(nrf_atomic_u32_t * p_ptr, + uint32_t value, + uint32_t * p_new) +{ + push {r4, r5} + mov r4, r0 + +loop_eor + ldrex r0, [r4] + eor r5, r0, r1 + strex r3, r5, [r4] + cmp r3, #0 + bne loop_eor + + str r5, [r2] + pop {r4, r5} + bx lr +} + +static __asm uint32_t nrf_atomic_internal_add(nrf_atomic_u32_t * p_ptr, + uint32_t value, + uint32_t * p_new) +{ + push {r4, r5} + mov r4, r0 + +loop_add + ldrex r0, [r4] + add r5, r0, r1 + strex r3, r5, [r4] + cmp r3, #0 + bne loop_add + + str r5, [r2] + pop {r4, r5} + bx lr +} + +static __asm uint32_t nrf_atomic_internal_sub(nrf_atomic_u32_t * p_ptr, + uint32_t value, + uint32_t * p_new) +{ + push {r4, r5} + mov r4, r0 + +loop_sub + ldrex r0, [r4] + sub r5, r0, r1 + strex r3, r5, [r4] + cmp r3, #0 + bne loop_sub + + str r5, [r2] + pop {r4, r5} + bx lr +} + +static __asm bool nrf_atomic_internal_cmp_exch(nrf_atomic_u32_t * p_data, + uint32_t * p_expected, + uint32_t value) +{ +#define RET_REG r0 +#define P_EXPC r1 +#define VALUE r2 +#define STR_RES r3 +#define P_DATA r4 +#define EXPC_VAL r5 +#define ACT_VAL r6 + + push {r4-r6} + mov P_DATA, r0 + mov RET_REG, #0 + +loop_cmp_exch + ldrex ACT_VAL, [P_DATA] + ldr EXPC_VAL, [P_EXPC] + cmp ACT_VAL, EXPC_VAL + ittee eq + strexeq STR_RES, VALUE, [P_DATA] + moveq RET_REG, #1 + strexne STR_RES, ACT_VAL, [P_DATA] + strne ACT_VAL, [P_EXPC] + cmp STR_RES, #0 + itt ne + movne RET_REG, #0 + bne loop_cmp_exch + + pop {r4-r6} + bx lr + +#undef RET_REG +#undef P_EXPC +#undef VALUE +#undef STR_RES +#undef P_DATA +#undef EXPC_VAL +#undef ACT_VAL +} + +static __asm uint32_t nrf_atomic_internal_sub_hs(nrf_atomic_u32_t * p_ptr, + uint32_t value, + uint32_t * p_new) +{ + push {r4, r5} + mov r4, r0 + +loop_sub_ge + ldrex r0, [r4] + cmp r0, r1 + ite hs + subhs r5, r0, r1 + movlo r5, r0 + strex r3, r5, [r4] + cmp r3, #0 + bne loop_sub_ge + + str r5, [r2] + pop {r4, r5} + bx lr +} + + +#define NRF_ATOMIC_OP(asm_op, old_val, new_val, ptr, value) \ + old_val = nrf_atomic_internal_##asm_op(ptr, value, &new_val) + +#elif defined ( __ICCARM__ ) || defined ( __GNUC__ ) + +/** + * @brief Atomic operation generic macro + * @param[in] asm_op operation: mov, orr, and, eor, add, sub + * @param[out] old_val atomic object output (uint32_t), value before operation + * @param[out] new_val atomic object output (uint32_t), value after operation + * @param[in] value atomic operation operand + * */ +#define NRF_ATOMIC_OP(asm_op, old_val, new_val, ptr, value) \ +{ \ + uint32_t str_res; \ + __ASM volatile( \ + "1: ldrex %["#old_val"], [%["#ptr"]]\n" \ + NRF_ATOMIC_OP_##asm_op(new_val, old_val, value) \ + " strex %[str_res], %["#new_val"], [%["#ptr"]]\n" \ + " teq %[str_res], #0\n" \ + " bne.n 1b" \ + : \ + [old_val]"=&r" (old_val), \ + [new_val]"=&r" (new_val), \ + [str_res]"=&r" (str_res) \ + : \ + [ptr]"r" (ptr), \ + [value]"r" (value) \ + : "cc"); \ + UNUSED_PARAMETER(str_res); \ +} + +#define NRF_ATOMIC_OP_mov(new_val, old_val, value) "mov %["#new_val"], %["#value"]\n" +#define NRF_ATOMIC_OP_orr(new_val, old_val, value) "orr %["#new_val"], %["#old_val"], %["#value"]\n" +#define NRF_ATOMIC_OP_and(new_val, old_val, value) "and %["#new_val"], %["#old_val"], %["#value"]\n" +#define NRF_ATOMIC_OP_eor(new_val, old_val, value) "eor %["#new_val"], %["#old_val"], %["#value"]\n" +#define NRF_ATOMIC_OP_add(new_val, old_val, value) "add %["#new_val"], %["#old_val"], %["#value"]\n" +#define NRF_ATOMIC_OP_sub(new_val, old_val, value) "sub %["#new_val"], %["#old_val"], %["#value"]\n" +#define NRF_ATOMIC_OP_sub_hs(new_val, old_val, value) \ + "cmp %["#old_val"], %["#value"]\n " \ + "ite hs\n" \ + "subhs %["#new_val"], %["#old_val"], %["#value"]\n" \ + "movlo %["#new_val"], %["#old_val"]\n" + +static inline bool nrf_atomic_internal_cmp_exch(nrf_atomic_u32_t * p_data, + uint32_t * p_expected, + uint32_t value) +{ + bool res = false; + uint32_t str_res = 0; + uint32_t act_val = 0; + uint32_t exp_val = 0; + UNUSED_VARIABLE(str_res); + UNUSED_VARIABLE(act_val); + UNUSED_VARIABLE(exp_val); + __ASM volatile( + "1: ldrex %[act_val], [%[ptr]]\n" + " ldr %[exp_val], [%[expc]]\n" + " cmp %[act_val], %[exp_val]\n" + " ittee eq\n" + " strexeq %[str_res], %[value], [%[ptr]]\n" + " moveq %[res], #1\n" + " strexne %[str_res], %[act_val], [%[ptr]]\n" + " strne %[act_val], [%[expc]]\n" + " cmp %[str_res], #0\n" + " itt ne\n" + " movne %[res], #0\n" + " bne.n 1b" + : + [res] "=&r" (res), + [exp_val] "=&r" (exp_val), + [act_val] "=&r" (act_val), + [str_res] "=&r" (str_res) + : + "0" (res), + "1" (exp_val), + "2" (act_val), + [expc] "r" (p_expected), + [ptr] "r" (p_data), + [value] "r" (value) + : "cc"); + return res; +} + +#else +#error "Unsupported compiler" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_ATOMIC_INTERNAL_H__ */ + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_atomic_sanity_check.h b/libraries/nfc/src/util/nrf_atomic_sanity_check.h new file mode 100644 index 000000000..82b751025 --- /dev/null +++ b/libraries/nfc/src/util/nrf_atomic_sanity_check.h @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_ATOMIC_SANITY_CHECK_H__ +#define NRF_ATOMIC_SANITY_CHECK_H__ + +#include "nrf_atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Quick sanity check of nrf_atomic API + * */ +static inline void nrf_atomic_sanity_check(void) +{ +#if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER) + nrf_atomic_u32_t val; + nrf_atomic_u32_t flag; + + /*Fetch version tests*/ + val = 0; + ASSERT(nrf_atomic_u32_store_fetch(&val, 10) == 0); + ASSERT(nrf_atomic_u32_store_fetch(&val, 0) == 10); + + val = 0; + ASSERT(nrf_atomic_u32_or_fetch(&val, 1 << 16) == 0); + ASSERT(nrf_atomic_u32_or_fetch(&val, 1 << 5) == ((1 << 16))); + ASSERT(nrf_atomic_u32_or_fetch(&val, 1 << 5) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_or_fetch(&val, 0) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_or_fetch(&val, 0xFFFFFFFF) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_or_fetch(&val, 0xFFFFFFFF) == (0xFFFFFFFF)); + + val = 0xFFFFFFFF; + ASSERT(nrf_atomic_u32_and_fetch(&val, ~(1 << 16)) == 0xFFFFFFFF); + ASSERT(nrf_atomic_u32_and_fetch(&val, ~(1 << 5)) == (0xFFFFFFFF & ~((1 << 16)))); + ASSERT(nrf_atomic_u32_and_fetch(&val, 0) == (0xFFFFFFFF & ~(((1 << 16) | (1 << 5))))); + ASSERT(nrf_atomic_u32_and_fetch(&val, 0xFFFFFFFF) == (0)); + + val = 0; + ASSERT(nrf_atomic_u32_xor_fetch(&val, (1 << 16)) == 0); + ASSERT(nrf_atomic_u32_xor_fetch(&val, (1 << 5)) == ((1 << 16))); + ASSERT(nrf_atomic_u32_xor_fetch(&val, 0) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_xor_fetch(&val, (1 << 16) | (1 << 5)) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_xor_fetch(&val, 0) == (0)); + + val = 0; + ASSERT(nrf_atomic_u32_add_fetch(&val, 100) == 0); + ASSERT(nrf_atomic_u32_add_fetch(&val, 100) == 100); + ASSERT(nrf_atomic_u32_add_fetch(&val, 1 << 24) == 200); + ASSERT(nrf_atomic_u32_add_fetch(&val, 0) == (200 + (1 << 24))); + ASSERT(nrf_atomic_u32_add_fetch(&val, 0xFFFFFFFF) == (200 + (1 << 24))); + ASSERT(nrf_atomic_u32_add_fetch(&val, 0) == (200 - 1 + (1 << 24))); + + val = 1000; + ASSERT(nrf_atomic_u32_sub_fetch(&val, 100) == 1000); + ASSERT(nrf_atomic_u32_sub_fetch(&val, 100) == 900); + ASSERT(nrf_atomic_u32_sub_fetch(&val, 0) == 800); + ASSERT(nrf_atomic_u32_sub_fetch(&val, 0xFFFFFFFF) == 800); + ASSERT(nrf_atomic_u32_sub_fetch(&val, 0) == 801); + + flag = 0; + ASSERT(nrf_atomic_flag_set_fetch(&flag) == 0); + ASSERT(nrf_atomic_flag_set_fetch(&flag) == 1); + ASSERT(nrf_atomic_flag_clear_fetch(&flag) == 1); + ASSERT(nrf_atomic_flag_clear_fetch(&flag) == 0); + + /*No fetch version tests*/ + val = 0; + ASSERT(nrf_atomic_u32_store(&val, 10) == 10); + ASSERT(nrf_atomic_u32_store(&val, 0) == 0); + + val = 0; + ASSERT(nrf_atomic_u32_or(&val, 1 << 16) == 1 << 16); + ASSERT(nrf_atomic_u32_or(&val, 1 << 5) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_or(&val, 1 << 5) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_or(&val, 0) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_or(&val, 0xFFFFFFFF) == 0xFFFFFFFF); + + val = 0xFFFFFFFF; + ASSERT(nrf_atomic_u32_and(&val, ~(1 << 16)) == (0xFFFFFFFF & ~((1 << 16)))); + ASSERT(nrf_atomic_u32_and(&val, ~(1 << 5)) == (0xFFFFFFFF & ~(((1 << 16) | (1 << 5))))); + ASSERT(nrf_atomic_u32_and(&val, 0) == 0); + + val = 0; + ASSERT(nrf_atomic_u32_xor(&val, (1 << 16)) == ((1 << 16))); + ASSERT(nrf_atomic_u32_xor(&val, (1 << 5)) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_xor(&val, 0) == ((1 << 16) | (1 << 5))); + ASSERT(nrf_atomic_u32_xor(&val, (1 << 16) | (1 << 5)) == 0); + + val = 0; + ASSERT(nrf_atomic_u32_add(&val, 100) == 100); + ASSERT(nrf_atomic_u32_add(&val, 100) == 200); + ASSERT(nrf_atomic_u32_add(&val, 1 << 24) == (200 + (1 << 24))); + ASSERT(nrf_atomic_u32_add(&val, 0) == (200 + (1 << 24))); + ASSERT(nrf_atomic_u32_add(&val, 0xFFFFFFFF) == (200 - 1 + (1 << 24))); + + val = 1000; + ASSERT(nrf_atomic_u32_sub(&val, 100) == 900); + ASSERT(nrf_atomic_u32_sub(&val, 100) == 800); + ASSERT(nrf_atomic_u32_sub(&val, 0) == 800); + ASSERT(nrf_atomic_u32_sub(&val, 0xFFFFFFFF) == 801); + + flag = 0; + ASSERT(nrf_atomic_flag_set(&flag) == 1); + ASSERT(nrf_atomic_flag_set(&flag) == 1); + ASSERT(nrf_atomic_flag_clear(&flag) == 0); + ASSERT(nrf_atomic_flag_clear(&flag) == 0); +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_ATOMIC_SANITY_CHECK_H__ */ diff --git a/libraries/nfc/src/util/nrf_balloc.c b/libraries/nfc/src/util/nrf_balloc.c new file mode 100644 index 000000000..380d469e1 --- /dev/null +++ b/libraries/nfc/src/util/nrf_balloc.c @@ -0,0 +1,399 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" + #if NRF_MODULE_ENABLED(NRF_BALLOC) + +#include "nrf_section.h" +#include "nrf_balloc.h" +#include "app_util_platform.h" + + +#if NRF_BALLOC_CONFIG_LOG_ENABLED + #define NRF_LOG_LEVEL NRF_BALLOC_CONFIG_LOG_LEVEL + #define NRF_LOG_INITIAL_LEVEL NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL + #define NRF_LOG_INFO_COLOR NRF_BALLOC_CONFIG_INFO_COLOR + #define NRF_LOG_DEBUG_COLOR NRF_BALLOC_CONFIG_DEBUG_COLOR +#else + #define NRF_LOG_LEVEL 0 +#endif // NRF_BALLOC_CONFIG_LOG_ENABLED +#include "nrf_log.h" + +#define HEAD_GUARD_FILL 0xBAADF00D /**< Magic number used to mark head guard.*/ +#define TAIL_GUARD_FILL 0xBAADCAFE /**< Magic number used to mark tail guard.*/ +#define FREE_MEM_FILL 0xBAADBAAD /**< Magic number used to mark free memory.*/ + +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED +#define POOL_ID(_p_pool) _p_pool->p_name +#define POOL_MARKER "%s" +#else +#define POOL_ID(_p_pool) _p_pool +#define POOL_MARKER "0x%08X" +#endif + +NRF_SECTION_DEF(nrf_balloc, nrf_balloc_t); + +#if NRF_BALLOC_CLI_CMDS && NRF_CLI_ENABLED +#include "nrf_cli.h" + +static void nrf_balloc_status(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + UNUSED_PARAMETER(argv); + + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + if (argc > 1) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Bad argument count"); + return; + } + + uint32_t num_of_instances = NRF_SECTION_ITEM_COUNT(nrf_balloc, nrf_balloc_t); + uint32_t i; + + for (i = 0; i < num_of_instances; i++) + { + const nrf_balloc_t * p_instance = NRF_SECTION_ITEM_GET(nrf_balloc, nrf_balloc_t, i); + + uint32_t element_size = NRF_BALLOC_ELEMENT_SIZE(p_instance); + uint32_t dbg_addon = p_instance->block_size - element_size; + uint32_t pool_size = p_instance->p_stack_limit - p_instance->p_stack_base; + uint32_t max_util = nrf_balloc_max_utilization_get(p_instance); + uint32_t util = nrf_balloc_utilization_get(p_instance); + const char * p_name = p_instance->p_name; + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, + "%s\r\n\t- Element size:\t%d + %d bytes of debug information\r\n" + "\t- Usage:\t%u%% (%u out of %u elements)\r\n" + "\t- Maximum:\t%u%% (%u out of %u elements)\r\n\r\n", + p_name, element_size, dbg_addon, + 100ul * util/pool_size, util,pool_size, + 100ul * max_util/pool_size, max_util,pool_size); + + } +} +// Register "balloc" command and its subcommands in CLI. +NRF_CLI_CREATE_STATIC_SUBCMD_SET(nrf_balloc_commands) +{ + NRF_CLI_CMD(status, NULL, "Print status of balloc instances.", nrf_balloc_status), + NRF_CLI_SUBCMD_SET_END +}; + +NRF_CLI_CMD_REGISTER(balloc, &nrf_balloc_commands, "Commands for BALLOC management", nrf_balloc_status); +#endif //NRF_BALLOC_CLI_CMDS + +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED +/**@brief Validate block memory, prepare block guards, and calculate pointer to the element. + * + * @param[in] p_pool Pointer to the memory pool. + * @param[in] p_head Pointer to the beginning of the block. + * + * @return Pointer to the element. + */ +__STATIC_INLINE void * nrf_balloc_block_unwrap(nrf_balloc_t const * p_pool, void * p_head) +{ + ASSERT((p_pool != NULL) && ((p_pool->block_size % sizeof(uint32_t)) == 0)); + ASSERT((p_head != NULL) && (((uint32_t)(p_head) % sizeof(uint32_t)) == 0)); + + uint32_t head_words = NRF_BALLOC_DEBUG_HEAD_GUARD_WORDS_GET(p_pool->debug_flags); + uint32_t tail_words = NRF_BALLOC_DEBUG_TAIL_GUARD_WORDS_GET(p_pool->debug_flags); + + uint32_t * p_tail = (uint32_t *)((size_t)(p_head) + p_pool->block_size); + uint32_t * p_element = (uint32_t *)p_head + head_words; + + if (NRF_BALLOC_DEBUG_DATA_TRASHING_CHECK_GET(p_pool->debug_flags)) + { + for (uint32_t * ptr = p_head; ptr < p_tail; ptr++) + { + if (*ptr != FREE_MEM_FILL) + { + NRF_LOG_INST_ERROR(p_pool->p_log, + "Detected free memory corruption at 0x%08X (0x%08X != 0x%08X)", + ptr, *ptr, FREE_MEM_FILL); + APP_ERROR_CHECK_BOOL(false); + } + } + } + + for (uint32_t * ptr = p_head; ptr < p_element; ptr++) + { + *ptr = HEAD_GUARD_FILL; + } + + for (uint32_t * ptr = ( p_tail - tail_words); ptr < p_tail; ptr++) + { + *ptr = TAIL_GUARD_FILL; + } + + return p_element; +} + +/**@brief Calculate pointer to the block, validate block guards, and mark block memory as free. + * + * @param[in] p_pool Pointer to the memory pool. + * @param[in] p_element Pointer to the element. + * + * @return Pointer to the beginning of the block. + */ +__STATIC_INLINE void * nrf_balloc_element_wrap(nrf_balloc_t const * p_pool, void * p_element) +{ + ASSERT((p_pool != NULL) && ((p_pool->block_size % sizeof(uint32_t)) == 0)); + ASSERT((p_element != NULL) && (((uint32_t)(p_element) % sizeof(uint32_t)) == 0)); + + uint32_t head_words = NRF_BALLOC_DEBUG_HEAD_GUARD_WORDS_GET(p_pool->debug_flags); + uint32_t tail_words = NRF_BALLOC_DEBUG_TAIL_GUARD_WORDS_GET(p_pool->debug_flags); + + uint32_t * p_head = (uint32_t *)p_element - head_words; + uint32_t * p_tail = (uint32_t *)((size_t)(p_head) + p_pool->block_size); + + for (uint32_t * ptr = p_head; ptr < (uint32_t *)p_element; ptr++) + { + if (*ptr != HEAD_GUARD_FILL) + { + NRF_LOG_INST_ERROR(p_pool->p_log, + "Detected Head Guard corruption at 0x%08X (0x%08X != 0x%08X)", + ptr, *ptr, HEAD_GUARD_FILL); + APP_ERROR_CHECK_BOOL(false); + } + } + + for (uint32_t * ptr = ( p_tail - tail_words); ptr < p_tail; ptr++) + { + if (*ptr != TAIL_GUARD_FILL) + { + NRF_LOG_INST_ERROR(p_pool->p_log, + "Detected Tail Guard corruption at 0x%08X (0x%08X != 0x%08X)", + ptr, *ptr, TAIL_GUARD_FILL); + APP_ERROR_CHECK_BOOL(false); + } + } + + if (NRF_BALLOC_DEBUG_DATA_TRASHING_CHECK_GET(p_pool->debug_flags)) + { + for (uint32_t * ptr = p_head; ptr < p_tail; ptr++) + { + *ptr = FREE_MEM_FILL; + } + } + + return p_head; +} + +#endif // NRF_BALLOC_CONFIG_DEBUG_ENABLED + +/**@brief Convert block index to a pointer. + * + * @param[in] p_pool Pointer to the memory pool. + * @param[in] idx Index of the block. + * + * @return Pointer to the beginning of the block. + */ +static void * nrf_balloc_idx2block(nrf_balloc_t const * p_pool, uint8_t idx) +{ + ASSERT(p_pool != NULL); + return (uint8_t *)(p_pool->p_memory_begin) + ((size_t)(idx) * p_pool->block_size); +} + +/**@brief Convert block pointer to index. + * + * @param[in] p_pool Pointer to the memory pool. + * @param[in] p_block Pointer to the beginning of the block. + * + * @return Index of the block. + */ +static uint8_t nrf_balloc_block2idx(nrf_balloc_t const * p_pool, void const * p_block) +{ + ASSERT(p_pool != NULL); + return ((size_t)(p_block) - (size_t)(p_pool->p_memory_begin)) / p_pool->block_size; +} + +ret_code_t nrf_balloc_init(nrf_balloc_t const * p_pool) +{ + uint8_t pool_size; + + VERIFY_PARAM_NOT_NULL(p_pool); + + ASSERT(p_pool->p_cb); + ASSERT(p_pool->p_stack_base); + ASSERT(p_pool->p_stack_limit); + ASSERT(p_pool->p_memory_begin); + ASSERT(p_pool->block_size); + + pool_size = p_pool->p_stack_limit - p_pool->p_stack_base; + +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED + void *p_memory_end = (uint8_t *)(p_pool->p_memory_begin) + (pool_size * p_pool->block_size); + if (NRF_BALLOC_DEBUG_DATA_TRASHING_CHECK_GET(p_pool->debug_flags)) + { + for (uint32_t * ptr = p_pool->p_memory_begin; ptr < (uint32_t *)(p_memory_end); ptr++) + { + *ptr = FREE_MEM_FILL; + } + } +#endif + + NRF_LOG_INST_INFO(p_pool->p_log, "Initialized (size: %u x %u = %u bytes)", + pool_size, + p_pool->block_size, + pool_size * p_pool->block_size); + + p_pool->p_cb->p_stack_pointer = p_pool->p_stack_base; + while (pool_size--) + { + *(p_pool->p_cb->p_stack_pointer)++ = pool_size; + } + + p_pool->p_cb->max_utilization = 0; + + return NRF_SUCCESS; +} + +void * nrf_balloc_alloc(nrf_balloc_t const * p_pool) +{ + ASSERT(p_pool != NULL); + + void * p_block = NULL; + + CRITICAL_REGION_ENTER(); + + if (p_pool->p_cb->p_stack_pointer > p_pool->p_stack_base) + { + // Allocate block. + p_block = nrf_balloc_idx2block(p_pool, *--(p_pool->p_cb->p_stack_pointer)); + + // Update utilization statistics. + uint8_t utilization = p_pool->p_stack_limit - p_pool->p_cb->p_stack_pointer; + if (p_pool->p_cb->max_utilization < utilization) + { + p_pool->p_cb->max_utilization = utilization; + } + } + + CRITICAL_REGION_EXIT(); + +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED + if (p_block != NULL) + { + p_block = nrf_balloc_block_unwrap(p_pool, p_block); + } +#endif + + NRF_LOG_INST_DEBUG(p_pool->p_log, "Allocating element: 0x%08X", p_block); + + return p_block; +} + +void nrf_balloc_free(nrf_balloc_t const * p_pool, void * p_element) +{ + ASSERT(p_pool != NULL); + ASSERT(p_element != NULL) + + NRF_LOG_INST_DEBUG(p_pool->p_log, "Freeing element: 0x%08X", p_element); + +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED + void * p_block = nrf_balloc_element_wrap(p_pool, p_element); + + // These checks could be done outside critical region as they use only pool configuration data. + if (NRF_BALLOC_DEBUG_BASIC_CHECKS_GET(p_pool->debug_flags)) + { + uint8_t pool_size = p_pool->p_stack_limit - p_pool->p_stack_base; + void *p_memory_end = (uint8_t *)(p_pool->p_memory_begin) + (pool_size * p_pool->block_size); + + // Check if the element belongs to this pool. + if ((p_block < p_pool->p_memory_begin) || (p_block >= p_memory_end)) + { + NRF_LOG_INST_ERROR(p_pool->p_log, + "Attempted to free element (0x%08X) that does not belong to the pool.", + p_element); + APP_ERROR_CHECK_BOOL(false); + } + + // Check if the pointer is valid. + if ((((size_t)(p_block) - (size_t)(p_pool->p_memory_begin)) % p_pool->block_size) != 0) + { + NRF_LOG_INST_ERROR(p_pool->p_log, + "Attempted to free corrupted element address (0x%08X).", p_element); + APP_ERROR_CHECK_BOOL(false); + } + } +#else + void * p_block = p_element; +#endif // NRF_BALLOC_CONFIG_DEBUG_ENABLED + + CRITICAL_REGION_ENTER(); + +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED + // These checks have to be done in critical region as they use p_pool->p_stack_pointer. + if (NRF_BALLOC_DEBUG_BASIC_CHECKS_GET(p_pool->debug_flags)) + { + // Check for allocated/free ballance. + if (p_pool->p_cb->p_stack_pointer >= p_pool->p_stack_limit) + { + NRF_LOG_INST_ERROR(p_pool->p_log, + "Attempted to free an element (0x%08X) while the pool is full.", + p_element); + APP_ERROR_CHECK_BOOL(false); + } + } + + if (NRF_BALLOC_DEBUG_DOUBLE_FREE_CHECK_GET(p_pool->debug_flags)) + { + // Check for double free. + for (uint8_t * p_idx = p_pool->p_stack_base; p_idx < p_pool->p_cb->p_stack_pointer; p_idx++) + { + if (nrf_balloc_idx2block(p_pool, *p_idx) == p_block) + { + NRF_LOG_INST_ERROR(p_pool->p_log, "Attempted to double-free an element (0x%08X).", + p_element); + APP_ERROR_CHECK_BOOL(false); + } + } + } +#endif // NRF_BALLOC_CONFIG_DEBUG_ENABLED + + // Free the element. + *(p_pool->p_cb->p_stack_pointer)++ = nrf_balloc_block2idx(p_pool, p_block); + + CRITICAL_REGION_EXIT(); +} + +#endif // NRF_MODULE_ENABLED(NRF_BALLOC) diff --git a/libraries/nfc/src/util/nrf_balloc.h b/libraries/nfc/src/util/nrf_balloc.h new file mode 100644 index 000000000..0e6059f46 --- /dev/null +++ b/libraries/nfc/src/util/nrf_balloc.h @@ -0,0 +1,351 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** + * @defgroup nrf_balloc Block memory allocator + * @{ + * @ingroup app_common + * @brief This module handles block memory allocator features. + */ + + +#ifndef NRF_BALLOC_H__ +#define NRF_BALLOC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sdk_errors.h" +#include "sdk_config.h" +#include "app_util_platform.h" +#include "app_util.h" +#include "nrf_log_instance.h" +#include "nrf_section.h" + +/** @brief Name of the module used for logger messaging. + */ +#define NRF_BALLOC_LOG_NAME balloc + +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED || NRF_BALLOC_CLI_CMDS +#define NRF_BALLOC_HAS_NAME 1 +#else +#define NRF_BALLOC_HAS_NAME 0 +#endif + +/**@defgroup NRF_BALLOC_DEBUG Macros for preparing debug flags for block allocator module. + * @{ */ +#define NRF_BALLOC_DEBUG_HEAD_GUARD_WORDS_SET(words) (((words) & 0xFF) << 0) +#define NRF_BALLOC_DEBUG_HEAD_GUARD_WORDS_GET(flags) (((flags) >> 0) & 0xFF) +#define NRF_BALLOC_DEBUG_TAIL_GUARD_WORDS_SET(words) (((words) & 0xFF) << 8) +#define NRF_BALLOC_DEBUG_TAIL_GUARD_WORDS_GET(flags) (((flags) >> 8) & 0xFF) + +#define NRF_BALLOC_DEBUG_BASIC_CHECKS_SET(enable) (!!(enable) << 16) +#define NRF_BALLOC_DEBUG_BASIC_CHECKS_GET(flags) (flags & (1 << 16)) +#define NRF_BALLOC_DEBUG_DOUBLE_FREE_CHECK_SET(enable) (!!(enable) << 17) +#define NRF_BALLOC_DEBUG_DOUBLE_FREE_CHECK_GET(flags) (flags & (1 << 17)) +#define NRF_BALLOC_DEBUG_DATA_TRASHING_CHECK_SET(enable) (!!(enable) << 18) +#define NRF_BALLOC_DEBUG_DATA_TRASHING_CHECK_GET(flags) (flags & (1 << 18)) +/**@} */ + +/**@brief Default debug flags for @ref nrf_balloc. This is used by the @ref NRF_BALLOC_DEF macro. + * Flags can be changed in @ref sdk_config. + */ +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED + #define NRF_BALLOC_DEFAULT_DEBUG_FLAGS \ + ( \ + NRF_BALLOC_DEBUG_HEAD_GUARD_WORDS_SET(NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS) | \ + NRF_BALLOC_DEBUG_TAIL_GUARD_WORDS_SET(NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS) | \ + NRF_BALLOC_DEBUG_BASIC_CHECKS_SET(NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED) | \ + NRF_BALLOC_DEBUG_DOUBLE_FREE_CHECK_SET(NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED) | \ + NRF_BALLOC_DEBUG_DATA_TRASHING_CHECK_SET(NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED) \ + ) +#else + #define NRF_BALLOC_DEFAULT_DEBUG_FLAGS 0 +#endif // NRF_BALLOC_CONFIG_DEBUG_ENABLED + +/**@brief Block memory allocator control block.*/ +typedef struct +{ + uint8_t * p_stack_pointer; //!< Current allocation stack pointer. + uint8_t max_utilization; //!< Maximum utilization of the memory pool. +} nrf_balloc_cb_t; + +/**@brief Block memory allocator pool instance. The pool is made of elements of the same size. */ +typedef struct +{ + nrf_balloc_cb_t * p_cb; //!< Pointer to the instance control block. + uint8_t * p_stack_base; //!< Base of the allocation stack. + /**< + * Stack is used to store handlers to not allocated elements. + */ + uint8_t * p_stack_limit; //!< Maximum possible value of the allocation stack pointer. + void * p_memory_begin; //!< Pointer to the start of the memory pool. + /**< + * Memory is used as a heap for blocks. + */ + NRF_LOG_INSTANCE_PTR_DECLARE(p_log) //!< Pointer to instance of the logger object (Conditionally compiled). +#if NRF_BALLOC_HAS_NAME + const char * p_name; //!< Pointer to string with pool name. +#endif +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED + uint32_t debug_flags; //!< Debugging settings. + /**< + * Debug flag should be created by @ref NRF_BALLOC_DEBUG. + */ +#endif // NRF_BALLOC_CONFIG_DEBUG_ENABLED + uint16_t block_size; //!< Size of the allocated block (including debug overhead). + /**< + * Single block contains user element with header and tail + * words. + */ +} nrf_balloc_t; + +/**@brief Get total memory consumed by single block (element size with overhead caused by debug + * flags). + * + * @param[in] _element_size Size of an element. + * @param[in] _debug_flags Debug flags. + */ +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED + #define NRF_BALLOC_BLOCK_SIZE(_element_size, _debug_flags) \ + ( \ + (sizeof(uint32_t) * NRF_BALLOC_DEBUG_HEAD_GUARD_WORDS_GET(_debug_flags)) + \ + ALIGN_NUM(sizeof(uint32_t), (_element_size)) + \ + (sizeof(uint32_t) * NRF_BALLOC_DEBUG_TAIL_GUARD_WORDS_GET(_debug_flags)) \ + ) +#else + #define NRF_BALLOC_BLOCK_SIZE(_element_size, _debug_flags) \ + ALIGN_NUM(sizeof(uint32_t), (_element_size)) +#endif // NRF_BALLOC_CONFIG_DEBUG_ENABLED + + +/**@brief Get element size ( excluding debugging overhead is present) + * flags). + * + * @param[in] _p_balloc Pointer to balloc instance. + */ +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED +#define NRF_BALLOC_ELEMENT_SIZE(_p_balloc) \ + (ALIGN_NUM(sizeof(uint32_t), (_p_balloc)->block_size) - \ + ((sizeof(uint32_t) * NRF_BALLOC_DEBUG_HEAD_GUARD_WORDS_GET((_p_balloc)->debug_flags)) + \ + (sizeof(uint32_t) * NRF_BALLOC_DEBUG_TAIL_GUARD_WORDS_GET((_p_balloc)->debug_flags)))) +#else +#define NRF_BALLOC_ELEMENT_SIZE(_p_balloc) \ + (_p_balloc)->block_size +#endif // NRF_BALLOC_CONFIG_DEBUG_ENABLED + +#if NRF_BALLOC_CONFIG_DEBUG_ENABLED +#define __NRF_BALLOC_ASSIGN_DEBUG_FLAGS(_debug_flags) .debug_flags = (_debug_flags), +#else +#define __NRF_BALLOC_ASSIGN_DEBUG_FLAGS(_debug_flags) +#endif + +#if NRF_BALLOC_HAS_NAME +#define __NRF_BALLOC_ASSIGN_POOL_NAME(_name) .p_name = STRINGIFY(_name), +#else +#define __NRF_BALLOC_ASSIGN_POOL_NAME(_name) +#endif + + +/**@brief Create a block allocator instance with custom debug flags. + * + * @note This macro reserves memory for the given block allocator instance. + * + * @param[in] _name Name of the allocator. + * @param[in] _element_size Size of one element. + * @param[in] _pool_size Size of the pool. + * @param[in] _debug_flags Debug flags (@ref NRF_BALLOC_DEBUG). + */ +#define NRF_BALLOC_DBG_DEF(_name, _element_size, _pool_size, _debug_flags) \ + STATIC_ASSERT((_pool_size) <= UINT8_MAX); \ + static uint8_t CONCAT_2(_name, _nrf_balloc_pool_stack)[(_pool_size)]; \ + static uint32_t CONCAT_2(_name,_nrf_balloc_pool_mem) \ + [NRF_BALLOC_BLOCK_SIZE(_element_size, _debug_flags) * (_pool_size) / sizeof(uint32_t)]; \ + static nrf_balloc_cb_t CONCAT_2(_name,_nrf_balloc_cb); \ + NRF_LOG_INSTANCE_REGISTER(NRF_BALLOC_LOG_NAME, _name, \ + NRF_BALLOC_CONFIG_INFO_COLOR, \ + NRF_BALLOC_CONFIG_DEBUG_COLOR, \ + NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL, \ + NRF_BALLOC_CONFIG_LOG_ENABLED ? \ + NRF_BALLOC_CONFIG_LOG_LEVEL : NRF_LOG_SEVERITY_NONE); \ + NRF_SECTION_ITEM_REGISTER(nrf_balloc, const nrf_balloc_t _name) = \ + { \ + .p_cb = &CONCAT_2(_name,_nrf_balloc_cb), \ + .p_stack_base = CONCAT_2(_name,_nrf_balloc_pool_stack), \ + .p_stack_limit = CONCAT_2(_name,_nrf_balloc_pool_stack) + (_pool_size), \ + .p_memory_begin = CONCAT_2(_name,_nrf_balloc_pool_mem), \ + .block_size = NRF_BALLOC_BLOCK_SIZE(_element_size, _debug_flags), \ + \ + NRF_LOG_INSTANCE_PTR_INIT(p_log, NRF_BALLOC_LOG_NAME, _name) \ + __NRF_BALLOC_ASSIGN_POOL_NAME(_name) \ + __NRF_BALLOC_ASSIGN_DEBUG_FLAGS(_debug_flags) \ + } + +/**@brief Create a block allocator instance. + * + * @note This macro reserves memory for the given block allocator instance. + * + * @param[in] _name Name of the allocator. + * @param[in] _element_size Size of one element. + * @param[in] _pool_size Size of the pool. + */ +#define NRF_BALLOC_DEF(_name, _element_size, _pool_size) \ + NRF_BALLOC_DBG_DEF(_name, _element_size, _pool_size, NRF_BALLOC_DEFAULT_DEBUG_FLAGS) + +/**@brief Create a block allocator interface. + * + * @param[in] _type Type which is allocated. + * @param[in] _name Name of the allocator. + */ +#define NRF_BALLOC_INTERFACE_DEC(_type, _name) \ + _type * CONCAT_2(_name,_alloc)(void); \ + void CONCAT_2(_name,_free)(_type * p_element) + +/**@brief Define a custom block allocator interface. + * + * @param[in] _attr Function attribute that will be added to allocator function definition. + * @param[in] _type Type which is allocated. + * @param[in] _name Name of the allocator. + * @param[in] _p_pool Pool from which data will be allocated. + */ +#define NRF_BALLOC_INTERFACE_CUSTOM_DEF(_attr, _type, _name, _p_pool) \ + _attr _type * CONCAT_2(_name,_alloc)(void) \ + { \ + GCC_PRAGMA("GCC diagnostic push") \ + GCC_PRAGMA("GCC diagnostic ignored \"-Waddress\"") \ + ASSERT((_p_pool) != NULL); \ + ASSERT((_p_pool)->block_size >= \ + NRF_BALLOC_BLOCK_SIZE(sizeof(_type), (_p_pool)->debug_flags)); \ + GCC_PRAGMA("GCC diagnostic pop") \ + return (_type *)(nrf_balloc_alloc(_p_pool)); \ + } \ + \ + _attr void CONCAT_2(_name,_free)(_type * p_element) \ + { \ + GCC_PRAGMA("GCC diagnostic push") \ + GCC_PRAGMA("GCC diagnostic ignored \"-Waddress\"") \ + ASSERT((_p_pool) != NULL); \ + ASSERT((_p_pool)->block_size >= \ + NRF_BALLOC_BLOCK_SIZE(sizeof(_type), (_p_pool)->debug_flags)); \ + GCC_PRAGMA("GCC diagnostic pop") \ + nrf_balloc_free((_p_pool), p_element); \ + } + +/**@brief Define block allocator interface. + * + * @param[in] _type Type which is allocated. + * @param[in] _name Name of the allocator. + * @param[in] _p_pool Pool from which data will be allocated. + */ +#define NRF_BALLOC_INTERFACE_DEF(_type, _name, _p_pool) \ + NRF_BALLOC_INTERFACE_CUSTOM_DEF(/* empty */, _type, _name, _p_pool) + +/**@brief Define a local block allocator interface. + * + * @param[in] _type Type which is allocated. + * @param[in] _name Name of the allocator. + * @param[in] _p_pool Pool from which data will be allocated. + */ +#define NRF_BALLOC_INTERFACE_LOCAL_DEF(_type, _name, _p_pool) \ + NRF_BALLOC_INTERFACE_CUSTOM_DEF(static, _type, _name, _p_pool) + +/**@brief Function for initializing a block memory allocator pool. + * + * @param[out] p_pool Pointer to the pool that is to be initialized. + * + * @return NRF_SUCCESS on success, otherwise error code. + */ +ret_code_t nrf_balloc_init(nrf_balloc_t const * p_pool); + +/**@brief Function for allocating an element from the pool. + * + * @note This module guarantees that the returned memory is aligned to 4. + * + * @param[in] p_pool Pointer to the memory pool from which the element will be allocated. + * + * @return Allocated element or NULL if the specified pool is empty. + */ +void * nrf_balloc_alloc(nrf_balloc_t const * p_pool); + +/**@brief Function for freeing an element back to the pool. + * + * @param[in] p_pool Pointer to the memory pool. + * @param[in] p_element Element to be freed. + */ +void nrf_balloc_free(nrf_balloc_t const * p_pool, void * p_element); + +/**@brief Function for getting maximum memory pool utilization. + * + * @param[in] p_pool Pointer to the memory pool instance. + * + * @return Maximum number of elements allocated from the pool. + */ +__STATIC_INLINE uint8_t nrf_balloc_max_utilization_get(nrf_balloc_t const * p_pool); + +#ifndef SUPPRESS_INLINE_IMPLEMENTATION +__STATIC_INLINE uint8_t nrf_balloc_max_utilization_get(nrf_balloc_t const * p_pool) +{ + ASSERT(p_pool != NULL); + return p_pool->p_cb->max_utilization; +} +#endif //SUPPRESS_INLINE_IMPLEMENTATION + +/**@brief Function for getting current memory pool utilization. + * + * @param[in] p_pool Pointer to the memory pool instance. + * + * @return Maximum number of elements allocated from the pool. + */ +__STATIC_INLINE uint8_t nrf_balloc_utilization_get(nrf_balloc_t const * p_pool); + +#ifndef SUPPRESS_INLINE_IMPLEMENTATION +__STATIC_INLINE uint8_t nrf_balloc_utilization_get(nrf_balloc_t const * p_pool) +{ + ASSERT(p_pool != NULL); + return (p_pool->p_stack_limit - p_pool->p_cb->p_stack_pointer); +} +#endif //SUPPRESS_INLINE_IMPLEMENTATION + +#ifdef __cplusplus +} +#endif + +#endif // NRF_BALLOC_H__ +/** @} */ diff --git a/libraries/nfc/src/util/nrf_bitmask.h b/libraries/nfc/src/util/nrf_bitmask.h new file mode 100644 index 000000000..e5c924ee7 --- /dev/null +++ b/libraries/nfc/src/util/nrf_bitmask.h @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2006 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_BITMASK_H +#define NRF_BITMASK_H + +#include "compiler_abstraction.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BITMASK_BYTE_GET(abs_bit) ((abs_bit)/8) +#define BITMASK_RELBIT_GET(abs_bit) ((abs_bit) & 0x00000007) + +/** + * Function for checking if bit in the multi-byte bit mask is set. + * + * @param bit Bit index. + * @param p_mask A pointer to mask with bit fields. + * + * @return 0 if bit is not set, positive value otherwise. + */ +__STATIC_INLINE uint32_t nrf_bitmask_bit_is_set(uint32_t bit, void const * p_mask) +{ + uint8_t const * p_mask8 = (uint8_t const *)p_mask; + uint32_t byte_idx = BITMASK_BYTE_GET(bit); + bit = BITMASK_RELBIT_GET(bit); + return (1 << bit) & p_mask8[byte_idx]; +} + +/** + * Function for setting a bit in the multi-byte bit mask. + * + * @param bit Bit index. + * @param p_mask A pointer to mask with bit fields. + */ +__STATIC_INLINE void nrf_bitmask_bit_set(uint32_t bit, void * p_mask) +{ + uint8_t * p_mask8 = (uint8_t *)p_mask; + uint32_t byte_idx = BITMASK_BYTE_GET(bit); + bit = BITMASK_RELBIT_GET(bit); + p_mask8[byte_idx] |= (1 << bit); +} + +/** + * Function for clearing a bit in the multi-byte bit mask. + * + * @param bit Bit index. + * @param p_mask A pointer to mask with bit fields. + */ +__STATIC_INLINE void nrf_bitmask_bit_clear(uint32_t bit, void * p_mask) +{ + uint8_t * p_mask8 = (uint8_t *)p_mask; + uint32_t byte_idx = BITMASK_BYTE_GET(bit); + bit = BITMASK_RELBIT_GET(bit); + p_mask8[byte_idx] &= ~(1 << bit); +} + +/** + * Function for performing bitwise OR operation on two multi-byte bit masks. + * + * @param p_mask1 A pointer to the first bit mask. + * @param p_mask2 A pointer to the second bit mask. + * @param p_mask_out A pointer to the output bit mask. + * @param length Length of output mask in bytes. + */ +__STATIC_INLINE void nrf_bitmask_masks_or(void const * p_mask1, + void const * p_mask2, + void * p_out_mask, + uint32_t length) +{ + uint8_t const * p_mask8_1 = (uint8_t const *)p_mask1; + uint8_t const * p_mask8_2 = (uint8_t const *)p_mask2; + uint8_t * p_mask8_out = (uint8_t *)p_out_mask; + uint32_t i; + for (i = 0; i < length; i++) + { + p_mask8_out[i] = p_mask8_1[i] | p_mask8_2[i]; + } +} + +/** + * Function for performing bitwise AND operation on two multi-byte bit masks. + * + * @param p_mask1 A pointer to the first bit mask. + * @param p_mask2 A pointer to the second bit mask. + * @param p_mask_out A pointer to the output bit mask. + * @param length Length of output mask in bytes. + */ +__STATIC_INLINE void nrf_bitmask_masks_and(void const * p_mask1, + void const * p_mask2, + void * p_out_mask, + uint32_t length) +{ + uint8_t const * p_mask8_1 = (uint8_t const *)p_mask1; + uint8_t const * p_mask8_2 = (uint8_t const *)p_mask2; + uint8_t * p_mask8_out = (uint8_t *)p_out_mask; + uint32_t i; + for (i = 0; i < length; i++) + { + p_mask8_out[i] = p_mask8_1[i] & p_mask8_2[i]; + } +} + +#ifdef __cplusplus +} +#endif + +#endif //NRF_BITMASK_H diff --git a/libraries/nfc/src/util/nrf_drv_clock.c b/libraries/nfc/src/util/nrf_drv_clock.c new file mode 100644 index 000000000..f747e89ea --- /dev/null +++ b/libraries/nfc/src/util/nrf_drv_clock.c @@ -0,0 +1,618 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "util/nordic_common.h" +#include "nrf_drv_clock.h" +#include "app_util_platform.h" +//#if NRF_MODULE_ENABLED(NRF_CLOCK) + +// #ifdef SOFTDEVICE_PRESENT +// #include "nrf_sdh.h" +// #include "nrf_sdh_soc.h" +// #endif + +#include + +#define NRF_LOG_MODULE_NAME clock +#if CLOCK_CONFIG_LOG_ENABLED + #define NRF_LOG_LEVEL CLOCK_CONFIG_LOG_LEVEL + #define NRF_LOG_INFO_COLOR CLOCK_CONFIG_INFO_COLOR + #define NRF_LOG_DEBUG_COLOR CLOCK_CONFIG_DEBUG_COLOR +#else //CLOCK_CONFIG_LOG_ENABLED + #define NRF_LOG_LEVEL 0 +#endif //CLOCK_CONFIG_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define EVT_TO_STR(event) \ + (event == NRF_CLOCK_EVENT_HFCLKSTARTED ? "NRF_CLOCK_EVENT_HFCLKSTARTED" : \ + (event == NRF_CLOCK_EVENT_LFCLKSTARTED ? "NRF_CLOCK_EVENT_LFCLKSTARTED" : \ + (event == NRF_CLOCK_EVENT_DONE ? "NRF_CLOCK_EVENT_DONE" : \ + (event == NRF_CLOCK_EVENT_CTTO ? "NRF_CLOCK_EVENT_CTTO" : \ + "UNKNOWN EVENT")))) + + +/*lint -save -e652 */ +#define NRF_CLOCK_LFCLK_RC CLOCK_LFCLKSRC_SRC_RC +#define NRF_CLOCK_LFCLK_Xtal CLOCK_LFCLKSRC_SRC_Xtal +#define NRF_CLOCK_LFCLK_Synth CLOCK_LFCLKSRC_SRC_Synth +/*lint -restore */ + +#if (CLOCK_CONFIG_LF_SRC == NRF_CLOCK_LFCLK_RC)// && !defined(SOFTDEVICE_PRESENT) +#define CALIBRATION_SUPPORT 1 +#else +#define CALIBRATION_SUPPORT 0 +#endif +typedef enum +{ + CAL_STATE_IDLE, + CAL_STATE_CT, + CAL_STATE_HFCLK_REQ, + CAL_STATE_CAL, + CAL_STATE_ABORT, +} nrf_drv_clock_cal_state_t; + +/**@brief CLOCK control block. */ +typedef struct +{ + bool module_initialized; /*< Indicate the state of module */ + volatile bool hfclk_on; /*< High-frequency clock state. */ + volatile bool lfclk_on; /*< Low-frequency clock state. */ + volatile uint32_t hfclk_requests; /*< High-frequency clock request counter. */ + volatile nrf_drv_clock_handler_item_t * p_hf_head; + volatile uint32_t lfclk_requests; /*< Low-frequency clock request counter. */ + volatile nrf_drv_clock_handler_item_t * p_lf_head; +#if CALIBRATION_SUPPORT + nrf_drv_clock_handler_item_t cal_hfclk_started_handler_item; + nrf_drv_clock_event_handler_t cal_done_handler; + volatile nrf_drv_clock_cal_state_t cal_state; +#endif // CALIBRATION_SUPPORT +} nrf_drv_clock_cb_t; + +static nrf_drv_clock_cb_t m_clock_cb; + +static void clock_irq_handler(nrfx_clock_evt_type_t evt); + +static void lfclk_stop(void) +{ +#if CALIBRATION_SUPPORT + nrfx_clock_calibration_timer_stop(); +#endif + +// #ifdef SOFTDEVICE_PRESENT +// // If LFCLK is requested to stop while SD is still enabled, +// // it indicates an error in the application. +// // Enabling SD should increment the LFCLK request. +// ASSERT(!nrf_sdh_is_enabled()); +// #endif // SOFTDEVICE_PRESENT + + // LFCLK can be started independently by the watchdog and cannot be stopped + // by the CLOCK peripheral. This code handles this situation and prevents LFCLK to be stopped. + // Otherwise driver can stuck when waiting for the operation to complete. + if (!nrf_wdt_started(NRF_WDT)) + { + nrfx_clock_lfclk_stop(); + m_clock_cb.lfclk_on = false; + } +} + +static void hfclk_start(void) +{ +// #ifdef SOFTDEVICE_PRESENT +// if (nrf_sdh_is_enabled()) +// { +// (void)sd_clock_hfclk_request(); +// return; +// } +// #endif // SOFTDEVICE_PRESENT + + nrfx_clock_hfclk_start(); +} + +static void hfclk_stop(void) +{ +// #ifdef SOFTDEVICE_PRESENT +// if (nrf_sdh_is_enabled()) +// { +// (void)sd_clock_hfclk_release(); +// m_clock_cb.hfclk_on = false; +// return; +// } +// #endif // SOFTDEVICE_PRESENT + + nrfx_clock_hfclk_stop(); + m_clock_cb.hfclk_on = false; +} + +bool nrf_drv_clock_init_check(void) +{ + return m_clock_cb.module_initialized; +} + +ret_code_t nrf_drv_clock_init(void) +{ + ret_code_t err_code = NRF_SUCCESS; + if (m_clock_cb.module_initialized) + { + err_code = NRF_ERROR_MODULE_ALREADY_INITIALIZED; + } + else + { + m_clock_cb.p_hf_head = NULL; + m_clock_cb.hfclk_requests = 0; + m_clock_cb.p_lf_head = NULL; + m_clock_cb.lfclk_requests = 0; + err_code = nrfx_clock_init(clock_irq_handler); +// #ifdef SOFTDEVICE_PRESENT +// if (!nrf_sdh_is_enabled()) +// #endif + { + nrfx_clock_enable(); + } + +#if CALIBRATION_SUPPORT + m_clock_cb.cal_state = CAL_STATE_IDLE; +#endif + + m_clock_cb.module_initialized = true; + } + + if (nrf_wdt_started(NRF_WDT)) + { + m_clock_cb.lfclk_on = true; + } + + NRF_LOG_INFO("Function: %s, error code: %s.", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} + +void nrf_drv_clock_uninit(void) +{ + ASSERT(m_clock_cb.module_initialized); + nrfx_clock_disable(); + nrfx_clock_uninit(); + + m_clock_cb.module_initialized = false; +} + +static void item_enqueue(nrf_drv_clock_handler_item_t ** p_head, + nrf_drv_clock_handler_item_t * p_item) +{ + nrf_drv_clock_handler_item_t * p_next = *p_head; + while (p_next) + { + if (p_next == p_item) + { + return; + } + p_next = p_next->p_next; + } + + p_item->p_next = (*p_head ? *p_head : NULL); + *p_head = p_item; +} + +static nrf_drv_clock_handler_item_t * item_dequeue(nrf_drv_clock_handler_item_t ** p_head) +{ + nrf_drv_clock_handler_item_t * p_item = *p_head; + if (p_item) + { + *p_head = p_item->p_next; + } + return p_item; +} + +void nrf_drv_clock_lfclk_request(nrf_drv_clock_handler_item_t * p_handler_item) +{ + ASSERT(m_clock_cb.module_initialized); + + if (m_clock_cb.lfclk_on) + { + if (p_handler_item) + { + p_handler_item->event_handler(NRF_DRV_CLOCK_EVT_LFCLK_STARTED); + } + CRITICAL_REGION_ENTER(); + ++(m_clock_cb.lfclk_requests); + CRITICAL_REGION_EXIT(); + } + else + { + CRITICAL_REGION_ENTER(); + if (p_handler_item) + { + item_enqueue((nrf_drv_clock_handler_item_t **)&m_clock_cb.p_lf_head, + p_handler_item); + } + if (m_clock_cb.lfclk_requests == 0) + { + nrfx_clock_lfclk_start(); + } + ++(m_clock_cb.lfclk_requests); + CRITICAL_REGION_EXIT(); + } + + ASSERT(m_clock_cb.lfclk_requests > 0); +} + +void nrf_drv_clock_lfclk_release(void) +{ + ASSERT(m_clock_cb.module_initialized); + ASSERT(m_clock_cb.lfclk_requests > 0); + + CRITICAL_REGION_ENTER(); + --(m_clock_cb.lfclk_requests); + if (m_clock_cb.lfclk_requests == 0) + { + lfclk_stop(); + } + CRITICAL_REGION_EXIT(); +} + +bool nrf_drv_clock_lfclk_is_running(void) +{ + ASSERT(m_clock_cb.module_initialized); + +// #ifdef SOFTDEVICE_PRESENT +// if (nrf_sdh_is_enabled()) +// { +// return true; +// } +// #endif // SOFTDEVICE_PRESENT + + return nrfx_clock_lfclk_is_running(); +} + +void nrf_drv_clock_hfclk_request(nrf_drv_clock_handler_item_t * p_handler_item) +{ + ASSERT(m_clock_cb.module_initialized); + + if (m_clock_cb.hfclk_on) + { + if (p_handler_item) + { + p_handler_item->event_handler(NRF_DRV_CLOCK_EVT_HFCLK_STARTED); + } + CRITICAL_REGION_ENTER(); + ++(m_clock_cb.hfclk_requests); + CRITICAL_REGION_EXIT(); + } + else + { + CRITICAL_REGION_ENTER(); + if (p_handler_item) + { + item_enqueue((nrf_drv_clock_handler_item_t **)&m_clock_cb.p_hf_head, + p_handler_item); + } + if (m_clock_cb.hfclk_requests == 0) + { + hfclk_start(); + } + ++(m_clock_cb.hfclk_requests); + CRITICAL_REGION_EXIT(); + } + + ASSERT(m_clock_cb.hfclk_requests > 0); +} + +void nrf_drv_clock_hfclk_release(void) +{ + ASSERT(m_clock_cb.module_initialized); + ASSERT(m_clock_cb.hfclk_requests > 0); + + CRITICAL_REGION_ENTER(); + --(m_clock_cb.hfclk_requests); + if (m_clock_cb.hfclk_requests == 0) + { + hfclk_stop(); + } + CRITICAL_REGION_EXIT(); +} + +bool nrf_drv_clock_hfclk_is_running(void) +{ + ASSERT(m_clock_cb.module_initialized); + +// #ifdef SOFTDEVICE_PRESENT +// if (nrf_sdh_is_enabled()) +// { +// uint32_t is_running; +// UNUSED_VARIABLE(sd_clock_hfclk_is_running(&is_running)); +// return (is_running ? true : false); +// } +// #endif // SOFTDEVICE_PRESENT + + return nrfx_clock_hfclk_is_running(); +} + +#if CALIBRATION_SUPPORT +static void clock_calibration_hf_started(nrf_drv_clock_evt_type_t event) +{ + if (m_clock_cb.cal_state == CAL_STATE_ABORT) + { + nrf_drv_clock_hfclk_release(); + m_clock_cb.cal_state = CAL_STATE_IDLE; + if (m_clock_cb.cal_done_handler) + { + m_clock_cb.cal_done_handler(NRF_DRV_CLOCK_EVT_CAL_ABORTED); + } + } + else + { + ASSERT(event == NRF_DRV_CLOCK_EVT_HFCLK_STARTED); + if (nrfx_clock_calibration_start() != NRFX_SUCCESS) + { + ASSERT(false); + } + } +} +#endif // CALIBRATION_SUPPORT + +ret_code_t nrf_drv_clock_calibration_start(uint8_t interval, nrf_drv_clock_event_handler_t handler) +{ + ret_code_t err_code = NRF_SUCCESS; +#if CALIBRATION_SUPPORT + ASSERT(m_clock_cb.cal_state == CAL_STATE_IDLE); + if (m_clock_cb.lfclk_on == false) + { + err_code = NRF_ERROR_INVALID_STATE; + } + else if (m_clock_cb.cal_state == CAL_STATE_IDLE) + { + m_clock_cb.cal_done_handler = handler; + m_clock_cb.cal_hfclk_started_handler_item.event_handler = clock_calibration_hf_started; + if (interval == 0) + { + m_clock_cb.cal_state = CAL_STATE_HFCLK_REQ; + nrf_drv_clock_hfclk_request(&m_clock_cb.cal_hfclk_started_handler_item); + } + else + { + m_clock_cb.cal_state = CAL_STATE_CT; + nrfx_clock_calibration_timer_start(interval); + } + } + else + { + err_code = NRF_ERROR_BUSY; + } + NRF_LOG_WARNING("Function: %s, error code: %s.", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +#else + UNUSED_PARAMETER(interval); + UNUSED_PARAMETER(handler); + err_code = NRF_ERROR_FORBIDDEN; + NRF_LOG_WARNING("Function: %s, error code: %s.", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +#endif // CALIBRATION_SUPPORT +} + +ret_code_t nrf_drv_clock_calibration_abort(void) +{ + ret_code_t err_code = NRF_SUCCESS; +#if CALIBRATION_SUPPORT + CRITICAL_REGION_ENTER(); + switch (m_clock_cb.cal_state) + { + case CAL_STATE_CT: + nrfx_clock_calibration_timer_stop(); + m_clock_cb.cal_state = CAL_STATE_IDLE; + if (m_clock_cb.cal_done_handler) + { + m_clock_cb.cal_done_handler(NRF_DRV_CLOCK_EVT_CAL_ABORTED); + } + break; + case CAL_STATE_HFCLK_REQ: + /* fall through. */ + case CAL_STATE_CAL: + m_clock_cb.cal_state = CAL_STATE_ABORT; + break; + default: + break; + } + CRITICAL_REGION_EXIT(); + + NRF_LOG_INFO("Function: %s, error code: %s.", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +#else + err_code = NRF_ERROR_FORBIDDEN; + NRF_LOG_WARNING("Function: %s, error code: %s.", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +#endif // CALIBRATION_SUPPORT +} + +ret_code_t nrf_drv_clock_is_calibrating(bool * p_is_calibrating) +{ + ret_code_t err_code = NRF_SUCCESS; +#if CALIBRATION_SUPPORT + ASSERT(m_clock_cb.module_initialized); + *p_is_calibrating = (m_clock_cb.cal_state != CAL_STATE_IDLE); + NRF_LOG_INFO("Function: %s, error code: %s.", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +#else + UNUSED_PARAMETER(p_is_calibrating); + err_code = NRF_ERROR_FORBIDDEN; + NRF_LOG_WARNING("Function: %s, error code: %s.", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +#endif // CALIBRATION_SUPPORT +} + +__STATIC_INLINE void clock_clk_started_notify(nrf_drv_clock_evt_type_t evt_type) +{ + nrf_drv_clock_handler_item_t **p_head; + if (evt_type == NRF_DRV_CLOCK_EVT_HFCLK_STARTED) + { + p_head = (nrf_drv_clock_handler_item_t **)&m_clock_cb.p_hf_head; + } + else + { + p_head = (nrf_drv_clock_handler_item_t **)&m_clock_cb.p_lf_head; + } + + while (1) + { + nrf_drv_clock_handler_item_t * p_item = item_dequeue(p_head); + if (!p_item) + { + break; + } + + p_item->event_handler(evt_type); + } +} + +static void clock_irq_handler(nrfx_clock_evt_type_t evt) +{ + if (evt == NRFX_CLOCK_EVT_HFCLK_STARTED) + { + m_clock_cb.hfclk_on = true; + clock_clk_started_notify(NRF_DRV_CLOCK_EVT_HFCLK_STARTED); + } + if (evt == NRFX_CLOCK_EVT_LFCLK_STARTED) + { + m_clock_cb.lfclk_on = true; + clock_clk_started_notify(NRF_DRV_CLOCK_EVT_LFCLK_STARTED); + } +#if CALIBRATION_SUPPORT + if (evt == NRFX_CLOCK_EVT_CTTO) + { + nrf_drv_clock_hfclk_request(&m_clock_cb.cal_hfclk_started_handler_item); + } + + if (evt == NRFX_CLOCK_EVT_CAL_DONE) + { + nrf_drv_clock_hfclk_release(); + bool aborted = (m_clock_cb.cal_state == CAL_STATE_ABORT); + m_clock_cb.cal_state = CAL_STATE_IDLE; + if (m_clock_cb.cal_done_handler) + { + m_clock_cb.cal_done_handler(aborted ? + NRF_DRV_CLOCK_EVT_CAL_ABORTED : NRF_DRV_CLOCK_EVT_CAL_DONE); + } + } +#endif // CALIBRATION_SUPPORT +} + +// #ifdef SOFTDEVICE_PRESENT +// /** +// * @brief SoftDevice SoC event handler. +// * +// * @param[in] evt_id SoC event. +// * @param[in] p_context Context. +// */ +// static void soc_evt_handler(uint32_t evt_id, void * p_context) +// { +// if (evt_id == NRF_EVT_HFCLKSTARTED) +// { +// m_clock_cb.hfclk_on = true; +// clock_clk_started_notify(NRF_DRV_CLOCK_EVT_HFCLK_STARTED); +// } +// } +// NRF_SDH_SOC_OBSERVER(m_soc_evt_observer, CLOCK_CONFIG_SOC_OBSERVER_PRIO, soc_evt_handler, NULL); + +// /** +// * @brief SoftDevice enable/disable state handler. +// * +// * @param[in] state State. +// * @param[in] p_context Context. +// */ +// static void sd_state_evt_handler(nrf_sdh_state_evt_t state, void * p_context) +// { +// switch (state) +// { +// case NRF_SDH_EVT_STATE_ENABLE_PREPARE: +// NVIC_DisableIRQ(POWER_CLOCK_IRQn); +// break; + +// case NRF_SDH_EVT_STATE_ENABLED: +// CRITICAL_REGION_ENTER(); +// /* Make sure that nrf_drv_clock module is initialized */ +// if (!m_clock_cb.module_initialized) +// { +// (void)nrf_drv_clock_init(); +// } +// /* SD is one of the LFCLK requesters, but it will enable it by itself. */ +// ++(m_clock_cb.lfclk_requests); +// m_clock_cb.lfclk_on = true; +// CRITICAL_REGION_EXIT(); +// break; + +// case NRF_SDH_EVT_STATE_DISABLED: +// /* Reinit interrupts */ +// ASSERT(m_clock_cb.module_initialized); +// nrfx_clock_enable(); + +// /* SD leaves LFCLK enabled - disable it if it is no longer required. */ +// nrf_drv_clock_lfclk_release(); +// break; + +// default: +// break; +// } +// } + +// NRF_SDH_STATE_OBSERVER(m_sd_state_observer, CLOCK_CONFIG_STATE_OBSERVER_PRIO) = +// { +// .handler = sd_state_evt_handler, +// .p_context = NULL, +// }; + +// #endif // SOFTDEVICE_PRESENT + +#undef NRF_CLOCK_LFCLK_RC +#undef NRF_CLOCK_LFCLK_Xtal +#undef NRF_CLOCK_LFCLK_Synth + +//#endif // NRF_MODULE_ENABLED(NRF_CLOCK) diff --git a/libraries/nfc/src/util/nrf_drv_clock.h b/libraries/nfc/src/util/nrf_drv_clock.h new file mode 100644 index 000000000..a88a0e42d --- /dev/null +++ b/libraries/nfc/src/util/nrf_drv_clock.h @@ -0,0 +1,298 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_DRV_CLOCK_H__ +#define NRF_DRV_CLOCK_H__ + +// #include +#include "nrfx_clock.h" +#include "sdk_errors.h" +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrf_drv_clock Clock driver - legacy layer + * @{ + * @ingroup nrf_clock + * + * @brief Layer providing compatibility with the former API. + */ + +/** + * @brief Clock events. + */ +typedef enum +{ + NRF_DRV_CLOCK_EVT_HFCLK_STARTED, ///< HFCLK has been started. + NRF_DRV_CLOCK_EVT_LFCLK_STARTED, ///< LFCLK has been started. + NRF_DRV_CLOCK_EVT_CAL_DONE, ///< Calibration is done. + NRF_DRV_CLOCK_EVT_CAL_ABORTED, ///< Calibration has been aborted. +} nrf_drv_clock_evt_type_t; + +/** + * @brief Clock event handler. + * + * @param[in] event Event. + */ +typedef void (*nrf_drv_clock_event_handler_t)(nrf_drv_clock_evt_type_t event); + +// Forward declaration of the nrf_drv_clock_handler_item_t type. +typedef struct nrf_drv_clock_handler_item_s nrf_drv_clock_handler_item_t; + +struct nrf_drv_clock_handler_item_s +{ + nrf_drv_clock_handler_item_t * p_next; ///< A pointer to the next handler that should be called when the clock is started. + nrf_drv_clock_event_handler_t event_handler; ///< Function to be called when the clock is started. +}; + +/** + * @brief Function for checking if driver is already initialized + * + * @retval true Driver is initialized + * @retval false Driver is uninitialized + */ +bool nrf_drv_clock_init_check(void); + +/** + * @brief Function for initializing the nrf_drv_clock module. + * + * After initialization, the module is in power off state (clocks are not requested). + * + * @retval NRF_SUCCESS If the procedure was successful. + * @retval NRF_ERROR_MODULE_ALREADY_INITIALIZED If the driver was already initialized. + */ +ret_code_t nrf_drv_clock_init(void); + +/** + * @brief Function for uninitializing the clock module. + * + */ +void nrf_drv_clock_uninit(void); + +/** + * @brief Function for requesting the LFCLK. + * + * The low-frequency clock can be requested by different modules + * or contexts. The driver ensures that the clock will be started only when it is requested + * the first time. If the clock is not ready but it was already started, the handler item that is + * provided as an input parameter is added to the list of handlers that will be notified + * when the clock is started. If the clock is already enabled, user callback is called from the + * current context. + * + * The first request will start the selected LFCLK source. If an event handler is + * provided, it will be called once the LFCLK is started. If the LFCLK was already started at this + * time, the event handler will be called from the context of this function. Additionally, + * the @ref nrf_drv_clock_lfclk_is_running function can be polled to check if the clock has started. + * + * @note When a SoftDevice is enabled, the LFCLK is always running and the driver cannot control it. + * + * @note The handler item provided by the user cannot be an automatic variable. + * + * @param[in] p_handler_item A pointer to the event handler structure. + */ +void nrf_drv_clock_lfclk_request(nrf_drv_clock_handler_item_t * p_handler_item); + +/** + * @brief Function for releasing the LFCLK. + * + * If there are no more requests, the LFCLK source will be stopped. + * + * @note When a SoftDevice is enabled, the LFCLK is always running. + */ +void nrf_drv_clock_lfclk_release(void); + +/** + * @brief Function for checking the LFCLK state. + * + * @retval true If the LFCLK is running. + * @retval false If the LFCLK is not running. + */ +bool nrf_drv_clock_lfclk_is_running(void); + +/** + * @brief Function for requesting the high-accuracy source HFCLK. + * + * The high-accuracy source + * can be requested by different modules or contexts. The driver ensures that the high-accuracy + * clock will be started only when it is requested the first time. If the clock is not ready + * but it was already started, the handler item that is provided as an input parameter is added + * to the list of handlers that will be notified when the clock is started. + * + * If an event handler is provided, it will be called once the clock is started. If the clock was already + * started at this time, the event handler will be called from the context of this function. Additionally, + * the @ref nrf_drv_clock_hfclk_is_running function can be polled to check if the clock has started. + * + * @note If a SoftDevice is running, the clock is managed by the SoftDevice and all requests are handled by + * the SoftDevice. This function cannot be called from all interrupt priority levels in that case. + * @note The handler item provided by the user cannot be an automatic variable. + * + * @param[in] p_handler_item A pointer to the event handler structure. + */ +void nrf_drv_clock_hfclk_request(nrf_drv_clock_handler_item_t * p_handler_item); + +/** + * @brief Function for releasing the high-accuracy source HFCLK. + * + * If there are no more requests, the high-accuracy source will be released. + */ +void nrf_drv_clock_hfclk_release(void); + +/** + * @brief Function for checking the HFCLK state. + * + * @retval true If the HFCLK is running (for \nRFXX XTAL source). + * @retval false If the HFCLK is not running. + */ +bool nrf_drv_clock_hfclk_is_running(void); + +/** + * @brief Function for starting a single calibration process. + * + * This function can also delay the start of calibration by a user-specified value. The delay will use + * a low-power timer that is part of the CLOCK module. @ref nrf_drv_clock_is_calibrating can be called to + * check if calibration is still in progress. If a handler is provided, the user can be notified when + * calibration is completed. The ext calibration can be started from the handler context. + * + * The calibration process consists of three phases: + * - Delay (optional) + * - Requesting the high-accuracy HFCLK + * - Hardware-supported calibration + * + * @param[in] delay Time after which the calibration will be started (in 0.25 s units). + * @param[in] handler NULL or user function to be called when calibration is completed or aborted. + * + * @retval NRF_SUCCESS If the procedure was successful. + * @retval NRF_ERROR_FORBIDDEN If a SoftDevice is present or the selected LFCLK source is not an RC oscillator. + * @retval NRF_ERROR_INVALID_STATE If the low-frequency clock is off. + * @retval NRF_ERROR_BUSY If calibration is in progress. + */ +ret_code_t nrf_drv_clock_calibration_start(uint8_t delay, nrf_drv_clock_event_handler_t handler); + +/** + * @brief Function for aborting calibration. + * + * This function aborts on-going calibration. If calibration was started, it cannot be stopped. If a handler + * was provided by @ref nrf_drv_clock_calibration_start, this handler will be called once + * aborted calibration is completed. @ref nrf_drv_clock_is_calibrating can also be used to check + * if the system is calibrating. + * + * @retval NRF_SUCCESS If the procedure was successful. + * @retval NRF_ERROR_FORBIDDEN If a SoftDevice is present or the selected LFCLK source is not an RC oscillator. + */ +ret_code_t nrf_drv_clock_calibration_abort(void); + +/** + * @brief Function for checking if calibration is in progress. + * + * This function indicates that the system is + * in calibration if it is in any of the calibration process phases (see @ref nrf_drv_clock_calibration_start). + * + * @param[out] p_is_calibrating True if calibration is in progress, false if not. + * + * @retval NRF_SUCCESS If the procedure was successful. + * @retval NRF_ERROR_FORBIDDEN If a SoftDevice is present or the selected LFCLK source is not an RC oscillator. + */ +ret_code_t nrf_drv_clock_is_calibrating(bool * p_is_calibrating); + +/**@brief Function for returning a requested task address for the clock driver module. + * + * @param[in] task One of the peripheral tasks. + * + * @return Task address. + */ +__STATIC_INLINE uint32_t nrf_drv_clock_ppi_task_addr(nrf_clock_task_t task); + +/**@brief Function for returning a requested event address for the clock driver module. + * + * @param[in] event One of the peripheral events. + * + * @return Event address. + */ +__STATIC_INLINE uint32_t nrf_drv_clock_ppi_event_addr(nrf_clock_event_t event); + + +// #ifdef SOFTDEVICE_PRESENT +// /** +// * @brief Function called by the SoftDevice handler if an @ref NRF_SOC_EVTS event is received from the SoftDevice. +// * +// * @param[in] evt_id One of NRF_SOC_EVTS values. +// */ +// void nrf_drv_clock_on_soc_event(uint32_t evt_id); + +// /** +// * @brief Function called by the SoftDevice handler when the SoftDevice has been enabled. +// * +// * This function is called just after the SoftDevice has been properly enabled. +// * Its main purpose is to mark that LFCLK has been requested by SD. +// */ +// void nrf_drv_clock_on_sd_enable(void); + +// /** +// * @brief Function called by the SoftDevice handler when the SoftDevice has been disabled. +// * +// * This function is called just after the SoftDevice has been properly disabled. +// * It has two purposes: +// * 1. Releases the LFCLK from the SD. +// * 2. Reinitializes an interrupt after the SD releases POWER_CLOCK_IRQ. +// */ +// void nrf_drv_clock_on_sd_disable(void); + +// #endif +/** + *@} + **/ + +#ifndef SUPPRESS_INLINE_IMPLEMENTATION +__STATIC_INLINE uint32_t nrf_drv_clock_ppi_task_addr(nrf_clock_task_t task) +{ + return nrf_clock_task_address_get(NRF_CLOCK,task); +} + +__STATIC_INLINE uint32_t nrf_drv_clock_ppi_event_addr(nrf_clock_event_t event) +{ + return nrf_clock_event_address_get(NRF_CLOCK,event); +} +#endif //SUPPRESS_INLINE_IMPLEMENTATION + +#ifdef __cplusplus +} +#endif + +#endif // NRF_DRV_CLOCK_H__ diff --git a/libraries/nfc/src/util/nrf_drv_uart.c b/libraries/nfc/src/util/nrf_drv_uart.c new file mode 100644 index 000000000..5e50454ad --- /dev/null +++ b/libraries/nfc/src/util/nrf_drv_uart.c @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nrf_drv_uart.h" + +#ifdef UARTE_PRESENT +#define INSTANCE_COUNT UARTE_COUNT +#else +#define INSTANCE_COUNT UART_COUNT +#endif + +static nrf_uart_event_handler_t m_handlers[INSTANCE_COUNT]; +static void * m_contexts[INSTANCE_COUNT]; + +#if defined(UARTE_PRESENT) && defined(UART_PRESENT) +uint8_t nrf_drv_uart_use_easy_dma[INSTANCE_COUNT]; +#endif + +#if defined(NRF_DRV_UART_WITH_UARTE) +static void uarte_evt_handler(nrfx_uarte_event_t const * p_event, + void * p_context) +{ + uint32_t inst_idx = (uint32_t)p_context; + nrf_drv_uart_event_t event = + { + .type = (nrf_drv_uart_evt_type_t)p_event->type, + .data = + { + .error = + { + .rxtx = + { + .p_data = p_event->data.error.rxtx.p_data, + .bytes = p_event->data.error.rxtx.bytes, + }, + .error_mask = p_event->data.error.error_mask, + } + } + }; + m_handlers[inst_idx](&event, m_contexts[inst_idx]); +} +#endif // defined(NRF_DRV_UART_WITH_UARTE) + +#if defined(NRF_DRV_UART_WITH_UART) +static void uart_evt_handler(nrfx_uart_event_t const * p_event, + void * p_context) +{ + uint32_t inst_idx = (uint32_t)p_context; + nrf_drv_uart_event_t event = + { + .type = (nrf_drv_uart_evt_type_t)p_event->type, + .data = + { + .error = + { + .rxtx = + { + .p_data = p_event->data.error.rxtx.p_data, + .bytes = p_event->data.error.rxtx.bytes, + }, + .error_mask = p_event->data.error.error_mask, + } + } + }; + m_handlers[inst_idx](&event, m_contexts[inst_idx]); +} +#endif // defined(NRF_DRV_UART_WITH_UART) + +ret_code_t nrf_drv_uart_init(nrf_drv_uart_t const * p_instance, + nrf_drv_uart_config_t const * p_config, + nrf_uart_event_handler_t event_handler) +{ + uint32_t inst_idx = p_instance->inst_idx; + m_handlers[inst_idx] = event_handler; + m_contexts[inst_idx] = p_config->p_context; + +#if defined(NRF_DRV_UART_WITH_UARTE) && defined(NRF_DRV_UART_WITH_UART) +#ifdef NRF52840_XXAA + if (inst_idx == 1) + { + ASSERT(p_config->use_easy_dma); + } +#endif + nrf_drv_uart_use_easy_dma[inst_idx] = p_config->use_easy_dma; +#endif + + nrf_drv_uart_config_t config = *p_config; + config.p_context = (void *)inst_idx; + + ret_code_t result = 0; + if (NRF_DRV_UART_USE_UARTE) + { + result = nrfx_uarte_init(&p_instance->uarte, + (nrfx_uarte_config_t const *)&config, + event_handler ? uarte_evt_handler : NULL); + } + else if (NRF_DRV_UART_USE_UART) + { + result = nrfx_uart_init(&p_instance->uart, + (nrfx_uart_config_t const *)&config, + event_handler ? uart_evt_handler : NULL); + } + return result; +} diff --git a/libraries/nfc/src/util/nrf_drv_uart.h b/libraries/nfc/src/util/nrf_drv_uart.h new file mode 100644 index 000000000..e8978fdc3 --- /dev/null +++ b/libraries/nfc/src/util/nrf_drv_uart.h @@ -0,0 +1,661 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NRF_DRV_UART_H__ +#define NRF_DRV_UART_H__ + +#include +#include "nrf_uarte.h" +#include "sdk_errors.h" +#if defined(UARTE_PRESENT) && NRFX_CHECK(NRFX_UARTE_ENABLED) + #define NRF_DRV_UART_WITH_UARTE +#endif +#if defined(UART_PRESENT) && NRFX_CHECK(NRFX_UART_ENABLED) + #define NRF_DRV_UART_WITH_UART +#endif + +#if defined(NRF_DRV_UART_WITH_UARTE) + #include + #define NRF_DRV_UART_CREATE_UARTE(id) \ + .uarte = NRFX_UARTE_INSTANCE(id), +#else + // Compilers (at least the smart ones) will remove the UARTE related code + // (blocks starting with "if (NRF_DRV_UART_USE_UARTE)") when it is not used, + // but to perform the compilation they need the following definitions. + #define nrfx_uarte_init(...) 0 + #define nrfx_uarte_uninit(...) + #define nrfx_uarte_task_address_get(...) 0 + #define nrfx_uarte_event_address_get(...) 0 + #define nrfx_uarte_tx(...) 0 + #define nrfx_uarte_tx_in_progress(...) 0 + #define nrfx_uarte_tx_abort(...) + #define nrfx_uarte_rx(...) 0 + #define nrfx_uarte_rx_ready(...) 0 + #define nrfx_uarte_rx_abort(...) + #define nrfx_uarte_errorsrc_get(...) 0 + #define NRF_DRV_UART_CREATE_UARTE(id) +#endif + +#if defined(NRF_DRV_UART_WITH_UART) + #include + + #define NRF_DRV_UART_CREATE_UART(id) _NRF_DRV_UART_CREATE_UART(id) + #define _NRF_DRV_UART_CREATE_UART(id) NRF_DRV_UART_CREATE_UART_##id + #define NRF_DRV_UART_CREATE_UART_0 \ + .uart = NRFX_UART_INSTANCE(0), + #define NRF_DRV_UART_CREATE_UART_1 \ + .uart = { .p_reg = NULL }, + +#else + // Compilers (at least the smart ones) will remove the UART related code + // (blocks starting with "if (NRF_DRV_UART_USE_UART)") when it is not used, + // but to perform the compilation they need the following definitions. + #define nrfx_uart_init(...) 0 + #define nrfx_uart_uninit(...) + #define nrfx_uart_task_address_get(...) 0 + #define nrfx_uart_event_address_get(...) 0 + #define nrfx_uart_tx(...) 0 + #define nrfx_uart_tx_in_progress(...) 0 + #define nrfx_uart_tx_abort(...) + #define nrfx_uart_rx(...) 0 + #define nrfx_uart_rx_enable(...) + #define nrfx_uart_rx_disable(...) + #define nrfx_uart_rx_ready(...) 0 + #define nrfx_uart_rx_abort(...) + #define nrfx_uart_errorsrc_get(...) 0 + #define NRF_DRV_UART_CREATE_UART(id) + + // This part is for old modules that use directly UART HAL definitions + // (to make them compilable for chips that have only UARTE). + #define NRF_UART_BAUDRATE_1200 NRF_UARTE_BAUDRATE_1200 + #define NRF_UART_BAUDRATE_2400 NRF_UARTE_BAUDRATE_2400 + #define NRF_UART_BAUDRATE_4800 NRF_UARTE_BAUDRATE_4800 + #define NRF_UART_BAUDRATE_9600 NRF_UARTE_BAUDRATE_9600 + #define NRF_UART_BAUDRATE_14400 NRF_UARTE_BAUDRATE_14400 + #define NRF_UART_BAUDRATE_19200 NRF_UARTE_BAUDRATE_19200 + #define NRF_UART_BAUDRATE_28800 NRF_UARTE_BAUDRATE_28800 + #define NRF_UART_BAUDRATE_38400 NRF_UARTE_BAUDRATE_38400 + #define NRF_UART_BAUDRATE_57600 NRF_UARTE_BAUDRATE_57600 + #define NRF_UART_BAUDRATE_76800 NRF_UARTE_BAUDRATE_76800 + #define NRF_UART_BAUDRATE_115200 NRF_UARTE_BAUDRATE_115200 + #define NRF_UART_BAUDRATE_230400 NRF_UARTE_BAUDRATE_230400 + #define NRF_UART_BAUDRATE_250000 NRF_UARTE_BAUDRATE_250000 + #define NRF_UART_BAUDRATE_460800 NRF_UARTE_BAUDRATE_460800 + #define NRF_UART_BAUDRATE_921600 NRF_UARTE_BAUDRATE_921600 + #define NRF_UART_BAUDRATE_1000000 NRF_UARTE_BAUDRATE_1000000 + typedef nrf_uarte_baudrate_t nrf_uart_baudrate_t; + #define NRF_UART_ERROR_OVERRUN_MASK NRF_UARTE_ERROR_OVERRUN_MASK + #define NRF_UART_ERROR_PARITY_MASK NRF_UARTE_ERROR_PARITY_MASK + #define NRF_UART_ERROR_FRAMING_MASK NRF_UARTE_ERROR_PARITY_MASK + #define NRF_UART_ERROR_BREAK_MASK NRF_UARTE_ERROR_BREAK_MASK + typedef nrf_uarte_error_mask_t nrf_uart_error_mask_t; + #define NRF_UART_HWFC_DISABLED NRF_UARTE_HWFC_DISABLED + #define NRF_UART_HWFC_ENABLED NRF_UARTE_HWFC_ENABLED + typedef nrf_uarte_hwfc_t nrf_uart_hwfc_t; + #define NRF_UART_PARITY_EXCLUDED NRF_UARTE_PARITY_EXCLUDED + #define NRF_UART_PARITY_INCLUDED NRF_UARTE_PARITY_INCLUDED + typedef nrf_uarte_parity_t nrf_uart_parity_t; + typedef nrf_uarte_task_t nrf_uart_task_t; + typedef nrf_uarte_event_t nrf_uart_event_t; + #define NRF_UART_PSEL_DISCONNECTED NRF_UARTE_PSEL_DISCONNECTED + #define nrf_uart_event_clear(...) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrf_drv_uart UART driver - legacy layer + * @{ + * @ingroup nrf_uart + * @brief Layer providing compatibility with the former API. + */ + +/** + * @brief Structure for the UART driver instance. + */ +typedef struct +{ + uint8_t inst_idx; +#if defined(NRF_DRV_UART_WITH_UARTE) + nrfx_uarte_t uarte; +#endif +#if defined(NRF_DRV_UART_WITH_UART) + nrfx_uart_t uart; +#endif +} nrf_drv_uart_t; + +/** + * @brief Macro for creating an UART driver instance. + */ +#define NRF_DRV_UART_INSTANCE(id) \ +{ \ + .inst_idx = id, \ + NRF_DRV_UART_CREATE_UARTE(id) \ + NRF_DRV_UART_CREATE_UART(id) \ +} + +/** + * @brief Types of UART driver events. + */ +typedef enum +{ + NRF_DRV_UART_EVT_TX_DONE, ///< Requested TX transfer completed. + NRF_DRV_UART_EVT_RX_DONE, ///< Requested RX transfer completed. + NRF_DRV_UART_EVT_ERROR, ///< Error reported by UART peripheral. +} nrf_drv_uart_evt_type_t; + +/**@brief Structure for UART configuration. */ +typedef struct +{ + uint32_t pseltxd; ///< TXD pin number. + uint32_t pselrxd; ///< RXD pin number. + uint32_t pselcts; ///< CTS pin number. + uint32_t pselrts; ///< RTS pin number. + void * p_context; ///< Context passed to interrupt handler. + nrf_uart_hwfc_t hwfc; ///< Flow control configuration. + nrf_uart_parity_t parity; ///< Parity configuration. + nrf_uart_baudrate_t baudrate; ///< Baudrate. + uint8_t interrupt_priority; ///< Interrupt priority. +#if defined(NRF_DRV_UART_WITH_UARTE) && defined(NRF_DRV_UART_WITH_UART) + bool use_easy_dma; +#endif +} nrf_drv_uart_config_t; + +#if defined(NRF_DRV_UART_WITH_UARTE) && defined(NRF_DRV_UART_WITH_UART) +extern uint8_t nrf_drv_uart_use_easy_dma[]; +#define NRF_DRV_UART_DEFAULT_CONFIG_USE_EASY_DMA .use_easy_dma = true, +#else +#define NRF_DRV_UART_DEFAULT_CONFIG_USE_EASY_DMA +#endif + +/**@brief UART default configuration. */ +#define NRF_DRV_UART_DEFAULT_CONFIG \ +{ \ + .pseltxd = NRF_UART_PSEL_DISCONNECTED, \ + .pselrxd = NRF_UART_PSEL_DISCONNECTED, \ + .pselcts = NRF_UART_PSEL_DISCONNECTED, \ + .pselrts = NRF_UART_PSEL_DISCONNECTED, \ + .p_context = NULL, \ + .hwfc = (nrf_uart_hwfc_t)UART_DEFAULT_CONFIG_HWFC, \ + .parity = (nrf_uart_parity_t)UART_DEFAULT_CONFIG_PARITY, \ + .baudrate = (nrf_uart_baudrate_t)UART_DEFAULT_CONFIG_BAUDRATE, \ + .interrupt_priority = UART_DEFAULT_CONFIG_IRQ_PRIORITY, \ + NRF_DRV_UART_DEFAULT_CONFIG_USE_EASY_DMA \ +} + +/**@brief Structure for UART transfer completion event. */ +typedef struct +{ + uint8_t * p_data; ///< Pointer to memory used for transfer. + uint8_t bytes; ///< Number of bytes transfered. +} nrf_drv_uart_xfer_evt_t; + +/**@brief Structure for UART error event. */ +typedef struct +{ + nrf_drv_uart_xfer_evt_t rxtx; ///< Transfer details includes number of bytes transfered. + uint32_t error_mask;///< Mask of error flags that generated the event. +} nrf_drv_uart_error_evt_t; + +/**@brief Structure for UART event. */ +typedef struct +{ + nrf_drv_uart_evt_type_t type; ///< Event type. + union + { + nrf_drv_uart_xfer_evt_t rxtx; ///< Data provided for transfer completion events. + nrf_drv_uart_error_evt_t error;///< Data provided for error event. + } data; +} nrf_drv_uart_event_t; + +/** + * @brief UART interrupt event handler. + * + * @param[in] p_event Pointer to event structure. Event is allocated on the stack so it is available + * only within the context of the event handler. + * @param[in] p_context Context passed to interrupt handler, set on initialization. + */ +typedef void (*nrf_uart_event_handler_t)(nrf_drv_uart_event_t * p_event, void * p_context); + +/** + * @brief Function for initializing the UART driver. + * + * This function configures and enables UART. After this function GPIO pins are controlled by UART. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] p_config Initial configuration. + * @param[in] event_handler Event handler provided by the user. If not provided driver works in + * blocking mode. + * + * @retval NRFX_SUCCESS If initialization was successful. + * @retval NRFX_ERROR_INVALID_STATE If driver is already initialized. + */ +ret_code_t nrf_drv_uart_init(nrf_drv_uart_t const * p_instance, + nrf_drv_uart_config_t const * p_config, + nrf_uart_event_handler_t event_handler); + +/** + * @brief Function for uninitializing the UART driver. + * @param[in] p_instance Pointer to the driver instance structure. + */ +__STATIC_INLINE +void nrf_drv_uart_uninit(nrf_drv_uart_t const * p_instance); + +/** + * @brief Function for getting the address of a specific UART task. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] task Task. + * + * @return Task address. + */ +__STATIC_INLINE +uint32_t nrf_drv_uart_task_address_get(nrf_drv_uart_t const * p_instance, + nrf_uart_task_t task); + +/** + * @brief Function for getting the address of a specific UART event. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] event Event. + * + * @return Event address. + */ +__STATIC_INLINE +uint32_t nrf_drv_uart_event_address_get(nrf_drv_uart_t const * p_instance, + nrf_uart_event_t event); + +/** + * @brief Function for sending data over UART. + * + * If an event handler was provided in nrf_drv_uart_init() call, this function + * returns immediately and the handler is called when the transfer is done. + * Otherwise, the transfer is performed in blocking mode, i.e. this function + * returns when the transfer is finished. Blocking mode is not using interrupt so + * there is no context switching inside the function. + * + * @note Peripherals using EasyDMA (i.e. UARTE) require that the transfer buffers + * are placed in the Data RAM region. If they are not and UARTE instance is + * used, this function will fail with error code NRFX_ERROR_INVALID_ADDR. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] p_data Pointer to data. + * @param[in] length Number of bytes to send. + * + * @retval NRFX_SUCCESS If initialization was successful. + * @retval NRFX_ERROR_BUSY If driver is already transferring. + * @retval NRFX_ERROR_FORBIDDEN If the transfer was aborted from a different context + * (blocking mode only, also see @ref nrf_drv_uart_rx_disable). + * @retval NRFX_ERROR_INVALID_ADDR If p_data does not point to RAM buffer (UARTE only). + */ +__STATIC_INLINE +ret_code_t nrf_drv_uart_tx(nrf_drv_uart_t const * p_instance, + uint8_t const * const p_data, + uint8_t length); + +/** + * @brief Function for checking if UART is currently transmitting. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @retval true If UART is transmitting. + * @retval false If UART is not transmitting. + */ +__STATIC_INLINE +bool nrf_drv_uart_tx_in_progress(nrf_drv_uart_t const * p_instance); + +/** + * @brief Function for aborting any ongoing transmission. + * @note @ref NRF_DRV_UART_EVT_TX_DONE event will be generated in non-blocking mode. Event will + * contain number of bytes sent until abort was called. If Easy DMA is not used event will be + * called from the function context. If Easy DMA is used it will be called from UART interrupt + * context. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +__STATIC_INLINE +void nrf_drv_uart_tx_abort(nrf_drv_uart_t const * p_instance); + +/** + * @brief Function for receiving data over UART. + * + * If an event handler was provided in the nrf_drv_uart_init() call, this function + * returns immediately and the handler is called when the transfer is done. + * Otherwise, the transfer is performed in blocking mode, i.e. this function + * returns when the transfer is finished. Blocking mode is not using interrupt so + * there is no context switching inside the function. + * The receive buffer pointer is double buffered in non-blocking mode. The secondary + * buffer can be set immediately after starting the transfer and will be filled + * when the primary buffer is full. The double buffering feature allows + * receiving data continuously. + * + * @note Peripherals using EasyDMA (i.e. UARTE) require that the transfer buffers + * are placed in the Data RAM region. If they are not and UARTE driver instance + * is used, this function will fail with error code NRFX_ERROR_INVALID_ADDR. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] p_data Pointer to data. + * @param[in] length Number of bytes to receive. + * + * @retval NRFX_SUCCESS If initialization was successful. + * @retval NRFX_ERROR_BUSY If the driver is already receiving + * (and the secondary buffer has already been set + * in non-blocking mode). + * @retval NRFX_ERROR_FORBIDDEN If the transfer was aborted from a different context + * (blocking mode only, also see @ref nrf_drv_uart_rx_disable). + * @retval NRFX_ERROR_INTERNAL If UART peripheral reported an error. + * @retval NRFX_ERROR_INVALID_ADDR If p_data does not point to RAM buffer (UARTE only). + */ +__STATIC_INLINE +ret_code_t nrf_drv_uart_rx(nrf_drv_uart_t const * p_instance, + uint8_t * p_data, + uint8_t length); + + + +/** + * @brief Function for testing the receiver state in blocking mode. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @retval true If the receiver has at least one byte of data to get. + * @retval false If the receiver is empty. + */ +__STATIC_INLINE +bool nrf_drv_uart_rx_ready(nrf_drv_uart_t const * p_instance); + +/** + * @brief Function for enabling the receiver. + * + * UART has a 6-byte-long RX FIFO and it is used to store incoming data. If a user does not call the + * UART receive function before the FIFO is filled, an overrun error will appear. Enabling the receiver + * without specifying an RX buffer is supported only in UART mode (without Easy DMA). The receiver must be + * explicitly closed by the user @sa nrf_drv_uart_rx_disable. This function asserts if the mode is wrong. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +__STATIC_INLINE +void nrf_drv_uart_rx_enable(nrf_drv_uart_t const * p_instance); + +/** + * @brief Function for disabling the receiver. + * + * This function must be called to close the receiver after it has been explicitly enabled by + * @sa nrf_drv_uart_rx_enable. The feature is supported only in UART mode (without Easy DMA). The function + * asserts if mode is wrong. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +__STATIC_INLINE +void nrf_drv_uart_rx_disable(nrf_drv_uart_t const * p_instance); + +/** + * @brief Function for aborting any ongoing reception. + * @note @ref NRF_DRV_UART_EVT_RX_DONE event will be generated in non-blocking mode. The event will + * contain the number of bytes received until abort was called. The event is called from UART interrupt + * context. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +__STATIC_INLINE +void nrf_drv_uart_rx_abort(nrf_drv_uart_t const * p_instance); + +/** + * @brief Function for reading error source mask. Mask contains values from @ref nrf_uart_error_mask_t. + * @note Function should be used in blocking mode only. In case of non-blocking mode, an error event is + * generated. Function clears error sources after reading. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @retval Mask of reported errors. + */ +__STATIC_INLINE +uint32_t nrf_drv_uart_errorsrc_get(nrf_drv_uart_t const * p_instance); + + +#ifndef SUPPRESS_INLINE_IMPLEMENTATION + +#if defined(NRF_DRV_UART_WITH_UARTE) && defined(NRF_DRV_UART_WITH_UART) + #define NRF_DRV_UART_USE_UARTE (nrf_drv_uart_use_easy_dma[p_instance->inst_idx]) +#elif defined(NRF_DRV_UART_WITH_UARTE) + #define NRF_DRV_UART_USE_UARTE true +#else + #define NRF_DRV_UART_USE_UARTE false +#endif +#define NRF_DRV_UART_USE_UART (!NRF_DRV_UART_USE_UARTE) + +__STATIC_INLINE +void nrf_drv_uart_uninit(nrf_drv_uart_t const * p_instance) +{ + if (NRF_DRV_UART_USE_UARTE) + { + nrfx_uarte_uninit(&p_instance->uarte); + } + else if (NRF_DRV_UART_USE_UART) + { + nrfx_uart_uninit(&p_instance->uart); + } +} + +__STATIC_INLINE +uint32_t nrf_drv_uart_task_address_get(nrf_drv_uart_t const * p_instance, + nrf_uart_task_t task) +{ + uint32_t result = 0; + if (NRF_DRV_UART_USE_UARTE) + { + result = nrfx_uarte_task_address_get(&p_instance->uarte, + (nrf_uarte_task_t)task); + } + else if (NRF_DRV_UART_USE_UART) + { + result = nrfx_uart_task_address_get(&p_instance->uart, task); + } + return result; +} + +__STATIC_INLINE +uint32_t nrf_drv_uart_event_address_get(nrf_drv_uart_t const * p_instance, + nrf_uart_event_t event) +{ + uint32_t result = 0; + if (NRF_DRV_UART_USE_UARTE) + { + result = nrfx_uarte_event_address_get(&p_instance->uarte, + (nrf_uarte_event_t)event); + } + else if (NRF_DRV_UART_USE_UART) + { + result = nrfx_uart_event_address_get(&p_instance->uart, event); + } + return result; +} + +__STATIC_INLINE +ret_code_t nrf_drv_uart_tx(nrf_drv_uart_t const * p_instance, + uint8_t const * p_data, + uint8_t length) +{ + uint32_t result = 0; + if (NRF_DRV_UART_USE_UARTE) + { + result = nrfx_uarte_tx(&p_instance->uarte, + p_data, + length); + } + else if (NRF_DRV_UART_USE_UART) + { + result = nrfx_uart_tx(&p_instance->uart, + p_data, + length); + } + return result; +} + +__STATIC_INLINE +bool nrf_drv_uart_tx_in_progress(nrf_drv_uart_t const * p_instance) +{ + bool result = 0; + if (NRF_DRV_UART_USE_UARTE) + { + result = nrfx_uarte_tx_in_progress(&p_instance->uarte); + } + else if (NRF_DRV_UART_USE_UART) + { + result = nrfx_uart_tx_in_progress(&p_instance->uart); + } + return result; +} + +__STATIC_INLINE +void nrf_drv_uart_tx_abort(nrf_drv_uart_t const * p_instance) +{ + if (NRF_DRV_UART_USE_UARTE) + { + nrfx_uarte_tx_abort(&p_instance->uarte); + } + else if (NRF_DRV_UART_USE_UART) + { + nrfx_uart_tx_abort(&p_instance->uart); + } +} + +__STATIC_INLINE +ret_code_t nrf_drv_uart_rx(nrf_drv_uart_t const * p_instance, + uint8_t * p_data, + uint8_t length) +{ + uint32_t result = 0; + if (NRF_DRV_UART_USE_UARTE) + { + result = nrfx_uarte_rx(&p_instance->uarte, + p_data, + length); + } + else if (NRF_DRV_UART_USE_UART) + { + result = nrfx_uart_rx(&p_instance->uart, + p_data, + length); + } + return result; +} + +__STATIC_INLINE +bool nrf_drv_uart_rx_ready(nrf_drv_uart_t const * p_instance) +{ + bool result = 0; + if (NRF_DRV_UART_USE_UARTE) + { + result = nrfx_uarte_rx_ready(&p_instance->uarte); + } + else if (NRF_DRV_UART_USE_UART) + { + result = nrfx_uart_rx_ready(&p_instance->uart); + } + return result; +} + +__STATIC_INLINE +void nrf_drv_uart_rx_enable(nrf_drv_uart_t const * p_instance) +{ + if (NRF_DRV_UART_USE_UARTE) + { + NRFX_ASSERT(false); // not supported + } + else if (NRF_DRV_UART_USE_UART) + { + nrfx_uart_rx_enable(&p_instance->uart); + } +} + +__STATIC_INLINE +void nrf_drv_uart_rx_disable(nrf_drv_uart_t const * p_instance) +{ + if (NRF_DRV_UART_USE_UARTE) + { + NRFX_ASSERT(false); // not supported + } + else if (NRF_DRV_UART_USE_UART) + { + nrfx_uart_rx_disable(&p_instance->uart); + } +} + +__STATIC_INLINE +void nrf_drv_uart_rx_abort(nrf_drv_uart_t const * p_instance) +{ + if (NRF_DRV_UART_USE_UARTE) + { + nrfx_uarte_rx_abort(&p_instance->uarte); + } + else if (NRF_DRV_UART_USE_UART) + { + nrfx_uart_rx_abort(&p_instance->uart); + } +} + +__STATIC_INLINE +uint32_t nrf_drv_uart_errorsrc_get(nrf_drv_uart_t const * p_instance) +{ + uint32_t result = 0; + if (NRF_DRV_UART_USE_UARTE) + { + result = nrfx_uarte_errorsrc_get(&p_instance->uarte); + } + else if (NRF_DRV_UART_USE_UART) + { + nrf_uart_event_clear(p_instance->uart.p_reg, NRF_UART_EVENT_ERROR); + result = nrfx_uart_errorsrc_get(&p_instance->uart); + } + return result; +} + +#endif // SUPPRESS_INLINE_IMPLEMENTATION + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NRF_DRV_UART_H__ diff --git a/libraries/nfc/src/util/nrf_fprintf.c b/libraries/nfc/src/util/nrf_fprintf.c new file mode 100644 index 000000000..0aeddf276 --- /dev/null +++ b/libraries/nfc/src/util/nrf_fprintf.c @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_FPRINTF) +#include + +#include "nrf_assert.h" +#include "nrf_fprintf_format.h" + +void nrf_fprintf_buffer_flush(nrf_fprintf_ctx_t * const p_ctx) +{ + ASSERT(p_ctx != NULL); + + if (p_ctx->io_buffer_cnt == 0) + { + return; + } + + p_ctx->fwrite(p_ctx->p_user_ctx, + p_ctx->p_io_buffer, + p_ctx->io_buffer_cnt); + p_ctx->io_buffer_cnt = 0; +} + +void nrf_fprintf(nrf_fprintf_ctx_t * const p_ctx, + char const * p_fmt, + ...) +{ + ASSERT(p_ctx != NULL); + ASSERT(p_ctx->fwrite != NULL); + ASSERT(p_ctx->p_io_buffer != NULL); + ASSERT(p_ctx->io_buffer_size > 0); + + if (p_fmt == NULL) + { + return; + } + + va_list args = {0}; + va_start(args, p_fmt); + + nrf_fprintf_fmt(p_ctx, p_fmt, &args); + + va_end(args); +} + +#endif // NRF_MODULE_ENABLED(NRF_FPRINTF) + diff --git a/libraries/nfc/src/util/nrf_fprintf.h b/libraries/nfc/src/util/nrf_fprintf.h new file mode 100644 index 000000000..b585d339a --- /dev/null +++ b/libraries/nfc/src/util/nrf_fprintf.h @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_FPRINTF_H__ +#define NRF_FPRINTF_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (* nrf_fprintf_fwrite)(void const * p_user_ctx, char const * p_str, size_t length); + +/** + * @brief fprintf context + */ +typedef struct nrf_fprintf_ctx +{ + char * const p_io_buffer; ///< Pointer to IO buffer. + size_t const io_buffer_size; ///< IO buffer size. + size_t io_buffer_cnt; ///< IO buffer usage. + bool auto_flush; ///< Auto flush configurator. + + void const * const p_user_ctx; ///< Pointer to user data to be passed to the fwrite funciton. + + nrf_fprintf_fwrite fwrite; ///< Pointer to function sending data stream. +} nrf_fprintf_ctx_t; + + +/** + * @brief Macro for defining nrf_fprintf instance. + * + * @param name Instance name. + * @param _p_user_ctx Pointer to user data. + * @param _p_io_buffer Pointer to IO buffer + * @param _io_buffer_size Size of IO buffer. + * @param _auto_flush Indicator if IO buffer shall be automatically flush. + * @param _fwrite Pointer to function sending data stream. + * */ +#define NRF_FPRINTF_DEF(name, _p_user_ctx, _p_io_buffer, _io_buffer_size, _auto_flush, _fwrite) \ + static nrf_fprintf_ctx_t name = \ + { \ + .p_io_buffer = _p_io_buffer, \ + .io_buffer_size = _io_buffer_size, \ + .io_buffer_cnt = 0, \ + .auto_flush = _auto_flush, \ + .p_user_ctx = _p_user_ctx, \ + .fwrite = _fwrite \ + } + +/** + * @brief fprintf like function which send formated data stream to output specified by user + * @ref nrf_fprintf_ctx_t + * + * @param p_ctx fprintf context. + * @param p_fmt Format string. + * @param ... List of parameters to print. + * */ +void nrf_fprintf(nrf_fprintf_ctx_t * const p_ctx, + char const * p_fmt, + ...); + +/** + * @brief function flushing data stored in io_buffer @ref nrf_fprintf_ctx_t + * + * @param p_ctx fprintf context + */ +void nrf_fprintf_buffer_flush(nrf_fprintf_ctx_t * const p_ctx); + + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_FPRINTF_H__ */ + diff --git a/libraries/nfc/src/util/nrf_fprintf_format.c b/libraries/nfc/src/util/nrf_fprintf_format.c new file mode 100644 index 000000000..05b4e7878 --- /dev/null +++ b/libraries/nfc/src/util/nrf_fprintf_format.c @@ -0,0 +1,810 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2014 - 2017 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER RTT * Real Time Transfer for embedded targets * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* RTT version: 6.14d * +* * +*********************************************************************/ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_FPRINTF) + +#include + +#include "nrf_assert.h" +#include "nrf_fprintf.h" +#include "nrf_fprintf_format.h" + +#define NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY (1u << 0) +#define NRF_CLI_FORMAT_FLAG_PAD_ZERO (1u << 1) +#define NRF_CLI_FORMAT_FLAG_PRINT_SIGN (1u << 2) + +#define NRF_CLI_FORMAT_DOUBLE_DEF_PRECISION 6 + +#define NRF_CLI_FORMAT_DOUBLE_SIGN_POSITION 63U +#define NRF_CLI_FORMAT_DOUBLE_SIGN_MASK 1ULL +#define NRF_CLI_FORMAT_DOUBLE_SIGN (NRF_CLI_FORMAT_DOUBLE_SIGN_MASK << NRF_CLI_FORMAT_DOUBLE_SIGN_POSITION) +#define NRF_CLI_FORMAT_DOUBLE_EXP_POSITION 52U +#define NRF_CLI_FORMAT_DOUBLE_EXP_MASK 0x7FFULL +#define NRF_CLI_FORMAT_DOUBLE_EXP (NRF_CLI_FORMAT_DOUBLE_EXP_MASK << NRF_CLI_FORMAT_DOUBLE_EXP_POSITION) +#define NRF_CLI_FORMAT_DOUBLE_MANT_POSITION 0U +#define NRF_CLI_FORMAT_DOUBLE_MANT_MASK 0xFFFFFFFFFFFFF +#define NRF_CLI_FORMAT_DOUBLE_MANT (NRF_CLI_FORMAT_DOUBLE_MANT_MASK << NRF_CLI_FORMAT_DOUBLE_MANT_POSITION) + +#define NRF_CLI_FORMAT_DOUBLE_SIGN_GET(v) (!!((v) & NRF_CLI_FORMAT_DOUBLE_SIGN)) +#define NRF_CLI_FORMAT_DOUBLE_EXP_GET(v) (((v) & NRF_CLI_FORMAT_DOUBLE_EXP) >> NRF_CLI_FORMAT_DOUBLE_EXP_POSITION) +#define NRF_CLI_FORMAT_DOUBLE_MANT_GET(v) (((v) & NRF_CLI_FORMAT_DOUBLE_MANT) >> NRF_CLI_FORMAT_DOUBLE_MANT_POSITION) +#define NRF_CLI_FORMAT_REQ_SIGN_SPACE(s, f) ((s) | (!!((f) & NRF_CLI_FORMAT_FLAG_PRINT_SIGN))) + +#define HIGH_32(v) ((v) >> 32) +#define LOW_32(v) (((1ULL << 32) - 1) & v) + + +static void buffer_add(nrf_fprintf_ctx_t * const p_ctx, char c) +{ +#if NRF_MODULE_ENABLED(NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF) + if (c == '\n') + { + buffer_add(p_ctx, '\r'); + } +#endif + p_ctx->p_io_buffer[p_ctx->io_buffer_cnt++] = c; + + if (p_ctx->io_buffer_cnt >= p_ctx->io_buffer_size) + { + nrf_fprintf_buffer_flush(p_ctx); + } +} + +static void string_print(nrf_fprintf_ctx_t * const p_ctx, + char const * p_str, + uint32_t FieldWidth, + uint32_t FormatFlags) +{ + uint32_t Width = 0; + char c; + + if ((FormatFlags & NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY) == NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY) + { + while ((c = *p_str) != '\0') + { + p_str++; + Width++; + buffer_add(p_ctx, c); + } + + while ((FieldWidth > Width) && (FieldWidth > 0)) + { + FieldWidth--; + buffer_add(p_ctx, ' '); + } + } + else + { + if (p_str != 0) + { + Width = strlen(p_str); + } + + while ((FieldWidth > Width) && (FieldWidth > 0)) + { + FieldWidth--; + buffer_add(p_ctx, ' '); + } + + while ((c = *p_str) != '\0') + { + p_str++; + Width++; + buffer_add(p_ctx, c); + } + } +} + +static void unsigned_print(nrf_fprintf_ctx_t * const p_ctx, + uint32_t v, + uint32_t Base, + uint32_t NumDigits, + uint32_t FieldWidth, + uint32_t FormatFlags) +{ + static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' }; + uint32_t Div; + uint32_t Value; + uint32_t Width; + char c; + + Value = v; + // + // Get actual field width + // + Width = 1u; + while (Value >= Base) + { + Value = (Value / Base); + Width++; + } + if (NumDigits > Width) + { + Width = NumDigits; + } + // + // Print leading chars if necessary + // + if ((FormatFlags & NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY) == 0u) + { + if (FieldWidth != 0u) + { + if (((FormatFlags & NRF_CLI_FORMAT_FLAG_PAD_ZERO) == NRF_CLI_FORMAT_FLAG_PAD_ZERO) && + (NumDigits == 0u)) + { + c = '0'; + } + else + { + c = ' '; + } + while ((FieldWidth != 0u) && (Width < FieldWidth)) + { + FieldWidth--; + buffer_add(p_ctx, c); + } + } + } + + Value = 1; + /* + * Compute Digit. + * Loop until Digit has the value of the highest digit required. + * Example: If the output is 345 (Base 10), loop 2 times until Digit is 100. + */ + while (1) + { + /* User specified a min number of digits to print? => Make sure we loop at least that + * often, before checking anything else (> 1 check avoids problems with NumDigits + * being signed / unsigned) + */ + if (NumDigits > 1u) + { + NumDigits--; + } + else + { + Div = v / Value; + // Is our divider big enough to extract the highest digit from value? => Done + if (Div < Base) + { + break; + } + } + Value *= Base; + } + // + // Output digits + // + do + { + Div = v / Value; + v -= Div * Value; + buffer_add(p_ctx, _aV2C[Div]); + Value /= Base; + } while (Value); + // + // Print trailing spaces if necessary + // + if ((FormatFlags & NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY) == NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY) + { + if (FieldWidth != 0u) + { + while ((FieldWidth != 0u) && (Width < FieldWidth)) + { + FieldWidth--; + buffer_add(p_ctx, ' '); + } + } + } +} + +static void int_print(nrf_fprintf_ctx_t * const p_ctx, + int32_t v, + uint32_t Base, + uint32_t NumDigits, + uint32_t FieldWidth, + uint32_t FormatFlags) +{ + uint32_t Width; + int32_t Number; + + Number = (v < 0) ? -v : v; + + // + // Get actual field width + // + Width = 1u; + while (Number >= (int32_t)Base) + { + Number = (Number / (int32_t)Base); + Width++; + } + if (NumDigits > Width) + { + Width = NumDigits; + } + if ((FieldWidth > 0u) && ((v < 0) || + ((FormatFlags & NRF_CLI_FORMAT_FLAG_PRINT_SIGN) == NRF_CLI_FORMAT_FLAG_PRINT_SIGN))) + { + FieldWidth--; + } + // + // Print leading spaces if necessary + // + if ((((FormatFlags & NRF_CLI_FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && + ((FormatFlags & NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) + { + if (FieldWidth != 0u) + { + while ((FieldWidth != 0u) && (Width < FieldWidth)) + { + FieldWidth--; + buffer_add(p_ctx, ' '); + } + } + } + // + // Print sign if necessary + // + if (v < 0) + { + v = -v; + buffer_add(p_ctx, '-'); + } + else if ((FormatFlags & NRF_CLI_FORMAT_FLAG_PRINT_SIGN) == NRF_CLI_FORMAT_FLAG_PRINT_SIGN) + { + buffer_add(p_ctx, '+'); + } + else + { + /* do nothing */ + } + // + // Print leading zeros if necessary + // + if (((FormatFlags & NRF_CLI_FORMAT_FLAG_PAD_ZERO) == NRF_CLI_FORMAT_FLAG_PAD_ZERO) && + ((FormatFlags & NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) + { + if (FieldWidth != 0u) + { + while ((FieldWidth != 0u) && (Width < FieldWidth)) + { + FieldWidth--; + buffer_add(p_ctx, '0'); + } + } + } + // + // Print number without sign + // + unsigned_print(p_ctx, (uint32_t)v, Base, NumDigits, FieldWidth, FormatFlags); +} + +#if NRF_MODULE_ENABLED(NRF_FPRINTF_DOUBLE) + +static void fill_space(nrf_fprintf_ctx_t * const p_ctx, + uint8_t len, + bool zeros) +{ + for (; len > 0; len--) + { + if (zeros) + { + buffer_add(p_ctx, '0'); + } + else + { + buffer_add(p_ctx, ' '); + } + } +} + +static void float_print(nrf_fprintf_ctx_t * const p_ctx, + double v, + uint32_t digits, + uint32_t width, + uint32_t format, + bool uppercase) +{ + bool sign, transform = false; + uint64_t num, mant, lead, low, base, res, carry, x, s0, s1, s2, s3, fr; + int32_t exp; + uint8_t highest, offset, lead_len = 0, skipped = 0; + uint8_t precision = digits ? digits + 1 : NRF_CLI_FORMAT_DOUBLE_DEF_PRECISION + 1; + /* Default digits should be -1, because 0 could be a requirement, not the default. + * This should be changed for the whole library. + */ + + if ((v > 0.0) && (v < 1.0)) + { + v += 1.0; + transform = true; + } + else if ((v > -1.0) && (v < 0.0)) + { + v -= 1.0; + transform = true; + } + + memcpy(&num, &v, sizeof(num)); + sign = NRF_CLI_FORMAT_DOUBLE_SIGN_GET(num); + exp = NRF_CLI_FORMAT_DOUBLE_EXP_GET(num); + mant = NRF_CLI_FORMAT_DOUBLE_MANT_GET(num); + + /* Special cases */ + if (exp == NRF_CLI_FORMAT_DOUBLE_EXP_MASK) + { + if (width && (!(format & NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY))) + { + fill_space(p_ctx, width - 3 - NRF_CLI_FORMAT_REQ_SIGN_SPACE(sign, format), false); + } + + if (sign) + { + buffer_add(p_ctx, '-'); + } + else if (format & NRF_CLI_FORMAT_FLAG_PRINT_SIGN) + { + buffer_add(p_ctx, '+'); + } + + if (mant != 0) + { + if(uppercase) + { + buffer_add(p_ctx, 'N'); + buffer_add(p_ctx, 'A'); + buffer_add(p_ctx, 'N'); + } + else + { + buffer_add(p_ctx, 'n'); + buffer_add(p_ctx, 'a'); + buffer_add(p_ctx, 'n'); + } + } + else + { + if(uppercase) + { + buffer_add(p_ctx, 'I'); + buffer_add(p_ctx, 'N'); + buffer_add(p_ctx, 'F'); + } + else + { + buffer_add(p_ctx, 'i'); + buffer_add(p_ctx, 'n'); + buffer_add(p_ctx, 'f'); + } + } + return; + } + + /* Add leading 1 to mantissa (except 0.0) */ + if ((mant != 0) || (exp != 0)) + { + mant |= (1ULL << 52); + } + + /* Convert the exponent */ + exp = exp - 1023; + + /* Whole numbers */ + offset = 52 - exp; + + if (offset > 64) + { + /* Float fraction offset overflow */ + return; + } + + lead = (mant >> (offset)); + + /* Fraction */ + low = mant & (~(lead << offset)); + + while (((low & 0x1) == 0) && low > 0) + { + low = low >> 1U; + skipped++; + } + + highest = (offset - skipped); + base = 1; + + for(uint8_t i = 0; i < precision; i++) + { + base *= 10; + } + + /* Handle multiplication with possible overflow */ + x = LOW_32(low) * LOW_32(base); + s0 = LOW_32(x); + + x = HIGH_32(low) * LOW_32(base) + HIGH_32(x); + s1 = LOW_32(x); + s2 = HIGH_32(x); + + x = s1 + LOW_32(low) * HIGH_32(base); + s1 = LOW_32(x); + + x = s2 + HIGH_32(low) * HIGH_32(base) + HIGH_32(x); + s2 = LOW_32(x); + s3 = HIGH_32(x); + + res = s1 << 32 | s0; + carry = s3 << 32 | s2; + + /* Divide and combine */ + carry = carry << (64 - highest); + res = res >> highest; + fr = res | carry; + + /* Roundup */ + if (fr%10 >= 5) + { + fr /= 10; + fr++; + } + else + { + fr /= 10; + } + precision--; + + if (transform && (lead == 1)) + { + lead = 0; + } + + /* Maximum precision handled by int_print() is 10 */ + if (precision > 10) + { + for (uint8_t delta = precision - 10; delta > 0; delta--) + { + fr /= 10; + } + precision = 10; + } + + res = lead; + while (res > 0) + { + res /= 10; + lead_len++; + } + + if ((lead == 0) && (fr == 0)) + { + lead_len = 1; + } + + if(lead_len == 0) + { + lead_len = 1; + } + + if (width && (!(format & NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY))) + { + int32_t space = width - lead_len - precision - NRF_CLI_FORMAT_REQ_SIGN_SPACE(sign, format) - 1; + if (space > 0) + { + fill_space(p_ctx, space, format & NRF_CLI_FORMAT_FLAG_PAD_ZERO); + } + } + + if (sign) + { + buffer_add(p_ctx, '-'); + } + else if (format & NRF_CLI_FORMAT_FLAG_PRINT_SIGN) + { + buffer_add(p_ctx, '+'); + } + + int_print(p_ctx, + lead, + 10u, + 0, + 0, + 0); + buffer_add(p_ctx, '.'); + int_print(p_ctx, + fr, + 10u, + precision, + 0, + 0); + + if (width && (format & NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY)) + { + int32_t space = width - lead_len - precision - NRF_CLI_FORMAT_REQ_SIGN_SPACE(sign, format) - 1; + if (space > 0) + { + fill_space(p_ctx, space, false); + } + } +} + +#endif + +void nrf_fprintf_fmt(nrf_fprintf_ctx_t * const p_ctx, + char const * p_fmt, + va_list * p_args) +{ + ASSERT(p_ctx != NULL); + + ASSERT(p_ctx->fwrite != NULL); + ASSERT(p_ctx->p_io_buffer != NULL); + ASSERT(p_ctx->io_buffer_size > 0); + + if (p_fmt == NULL) + { + return; + } + + char c; + int32_t v; + uint32_t NumDigits; + uint32_t FormatFlags; + uint32_t FieldWidth; + + do + { + c = *p_fmt; + p_fmt++; + + if (c == 0u) + { + break; + } + if (c == '%') + { + // + // Filter out flags + // + FormatFlags = 0u; + v = 1; + + do + { + c = *p_fmt; + switch (c) + { + case '-': + FormatFlags |= NRF_CLI_FORMAT_FLAG_LEFT_JUSTIFY; + p_fmt++; + break; + case '0': + FormatFlags |= NRF_CLI_FORMAT_FLAG_PAD_ZERO; + p_fmt++; + break; + case '+': + FormatFlags |= NRF_CLI_FORMAT_FLAG_PRINT_SIGN; + p_fmt++; + break; + default: + v = 0; + break; + } + } while (v); + + // + // filter out field width + // + FieldWidth = 0u; + do + { + if (c == '*') + { + /*lint -save -e64 -e56*/ + FieldWidth += va_arg(*p_args, unsigned); + /*lint -restore*/ + p_fmt++; + break; + } + c = *p_fmt; + if ((c < '0') || (c > '9')) + { + break; + } + p_fmt++; + FieldWidth = (FieldWidth * 10u) + (c - '0'); + } while (1); + + // + // Filter out precision (number of digits to display) + // + NumDigits = 0u; + c = *p_fmt; + if (c == '.') + { + p_fmt++; + do + { + c = *p_fmt; + if ((c < '0') || (c > '9')) + { + break; + } + p_fmt++; + NumDigits = NumDigits * 10u + (c - '0'); + } while (1); + } + // + // Filter out length modifier + // + c = *p_fmt; + do + { + if ((c == 'l') || (c == 'h')) + { + p_fmt++; + c = *p_fmt; + } + else + { + break; + } + } while (1); + // + // Handle specifiers + // + /*lint -save -e64*/ + switch (c) + { + case 'c': + { + char c0; + v = va_arg(*p_args, int32_t); + c0 = (char)v; + buffer_add(p_ctx, c0); + break; + } + case 'd': + case 'i': + v = va_arg(*p_args, int32_t); + int_print(p_ctx, + v, + 10u, + NumDigits, + FieldWidth, + FormatFlags); + break; + case 'u': + v = va_arg(*p_args, int32_t); + unsigned_print(p_ctx, + (uint32_t)v, + 10u, + NumDigits, + FieldWidth, + FormatFlags); + break; + case 'x': + case 'X': + v = va_arg(*p_args, int32_t); + unsigned_print(p_ctx, + (uint32_t)v, + 16u, + NumDigits, + FieldWidth, + FormatFlags); + break; + case 's': + { + char const * p_s = va_arg(*p_args, const char *); + string_print(p_ctx, p_s, FieldWidth, FormatFlags); + break; + } + case 'p': + v = va_arg(*p_args, int32_t); + buffer_add(p_ctx, '0'); + buffer_add(p_ctx, 'x'); + unsigned_print(p_ctx, (uint32_t)v, 16u, 8u, 8u, 0); + break; + case '%': + buffer_add(p_ctx, '%'); + break; +#if NRF_MODULE_ENABLED(NRF_FPRINTF_DOUBLE) + case 'f': + { + double dbl = va_arg(*p_args, double); + float_print(p_ctx, + dbl, + NumDigits, + FieldWidth, + FormatFlags, + false); + break; + } + case 'F': + { + double dbl = va_arg(*p_args, double); + float_print(p_ctx, + dbl, + NumDigits, + FieldWidth, + FormatFlags, + true); + break; + } +#endif + default: + break; + } + /*lint -restore*/ + p_fmt++; + } + else + { + buffer_add(p_ctx, c); + } + } while (*p_fmt != '\0'); + + if (p_ctx->auto_flush) + { + nrf_fprintf_buffer_flush(p_ctx); + } +} + +#endif // NRF_MODULE_ENABLED(NRF_FPRINTF) + diff --git a/libraries/nfc/src/util/nrf_fprintf_format.h b/libraries/nfc/src/util/nrf_fprintf_format.h new file mode 100644 index 000000000..920e68598 --- /dev/null +++ b/libraries/nfc/src/util/nrf_fprintf_format.h @@ -0,0 +1,86 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH & Co. KG * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2014 - 2017 SEGGER Microcontroller GmbH & Co. KG * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER RTT * Real Time Transfer for embedded targets * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* o Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* o Neither the name of SEGGER Microcontroller GmbH & Co. KG * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +* * +* RTT version: 6.14d * +* * +*********************************************************************/ + +#ifndef NRF_FPRINTF_FORMAT_H__ +#define NRF_FPRINTF_FORMAT_H__ + +#include +#include "nrf_fprintf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Printf like function which sends formated data stream to output. + * + * @param nrf_fprintf_ctx_t Print context. + * @param p_fmt Format string. + * @param p_args List of parameters to print. + * */ +void nrf_fprintf_fmt(nrf_fprintf_ctx_t * const p_ctx, + char const * p_fmt, + va_list * p_args); + + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_FPRINTF_FORMAT_H__ */ + diff --git a/libraries/nfc/src/util/nrf_log.h b/libraries/nfc/src/util/nrf_log.h new file mode 100644 index 000000000..cafcd60b1 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log.h @@ -0,0 +1,296 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup nrf_log Logger module + * @{ + * @ingroup app_common + * + * @brief The nrf_log module interface. + */ + +#ifndef NRF_LOG_H_ +#define NRF_LOG_H_ + +#include "sdk_common.h" +#include "nrf_section.h" +#if NRF_MODULE_ENABLED(NRF_LOG) +#include "nrf_strerror.h" +#define NRF_LOG_ERROR_STRING_GET(code) nrf_strerror_get(code) +#else +#define NRF_LOG_ERROR_STRING_GET(code) "" +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Severity level for the module. + * + * The severity level can be defined in a module to override the default. + */ +#ifndef NRF_LOG_LEVEL + #define NRF_LOG_LEVEL NRF_LOG_DEFAULT_LEVEL +#endif + +/** @brief Initial severity if filtering is enabled. + */ +#ifndef NRF_LOG_INITIAL_LEVEL + #define NRF_LOG_INITIAL_LEVEL NRF_LOG_LEVEL +#endif + + +#include "nrf_log_internal.h" + +/** @def NRF_LOG_ERROR + * @brief Macro for logging error messages. It takes a printf-like, formatted + * string with up to seven arguments. + * + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + */ + +/** @def NRF_LOG_WARNING + * @brief Macro for logging error messages. It takes a printf-like, formatted + * string with up to seven arguments. + * + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes warning logs. + */ + +/** @def NRF_LOG_INFO + * @brief Macro for logging error messages. It takes a printf-like, formatted + * string with up to seven arguments. + * + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes info logs. + */ + +/** @def NRF_LOG_DEBUG + * @brief Macro for logging error messages. It takes a printf-like, formatted + * string with up to seven arguments. + * + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes debug logs. + */ + +#define NRF_LOG_ERROR(...) NRF_LOG_INTERNAL_ERROR(__VA_ARGS__) +#define NRF_LOG_WARNING(...) NRF_LOG_INTERNAL_WARNING( __VA_ARGS__) +#define NRF_LOG_INFO(...) NRF_LOG_INTERNAL_INFO( __VA_ARGS__) +#define NRF_LOG_DEBUG(...) NRF_LOG_INTERNAL_DEBUG( __VA_ARGS__) + +/** @def NRF_LOG_INST_ERROR + * @brief Macro for logging error messages for a given module instance. It takes a printf-like, formatted + * string with up to seven arguments. + * + * @param p_inst Pointer to the instance with logging support. + * + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + */ + +/** @def NRF_LOG_INST_WARNING + * @brief Macro for logging error messages for a given module instance. It takes a printf-like, formatted + * string with up to seven arguments. + * + * @param p_inst Pointer to the instance with logging support. + * + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + */ + +/** @def NRF_LOG_INST_INFO + * @brief Macro for logging error messages for a given module instance. It takes a printf-like, formatted + * string with up to seven arguments. + * + * @param p_inst Pointer to the instance with logging support. + * + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + */ + +/** @def NRF_LOG_INST_DEBUG + * @brief Macro for logging error messages for given module instance. It takes a printf-like, formatted + * string with up to seven arguments. + * + * @param p_inst Pointer to the instance with logging support. + * + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + */ +#define NRF_LOG_INST_ERROR(p_inst,...) NRF_LOG_INTERNAL_INST_ERROR(p_inst,__VA_ARGS__) +#define NRF_LOG_INST_WARNING(p_inst,...) NRF_LOG_INTERNAL_INST_WARNING(p_inst,__VA_ARGS__) +#define NRF_LOG_INST_INFO(p_inst,...) NRF_LOG_INTERNAL_INST_INFO(p_inst, __VA_ARGS__) +#define NRF_LOG_INST_DEBUG(p_inst,...) NRF_LOG_INTERNAL_INST_DEBUG(p_inst, __VA_ARGS__) + +/** + * @brief Macro for logging a formatted string without any prefix or timestamp. + */ +#define NRF_LOG_RAW_INFO(...) NRF_LOG_INTERNAL_RAW_INFO( __VA_ARGS__) + +/** @def NRF_LOG_HEXDUMP_ERROR + * @brief Macro for logging raw bytes. + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + * + * @param p_data Pointer to data. + * @param len Data length in bytes. + */ +/** @def NRF_LOG_HEXDUMP_WARNING + * @brief Macro for logging raw bytes. + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes warning logs. + * + * @param p_data Pointer to data. + * @param len Data length in bytes. + */ +/** @def NRF_LOG_HEXDUMP_INFO + * @brief Macro for logging raw bytes. + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes info logs. + * + * @param p_data Pointer to data. + * @param len Data length in bytes. + */ +/** @def NRF_LOG_HEXDUMP_DEBUG + * @brief Macro for logging raw bytes. + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes debug logs. + * + * @param p_data Pointer to data. + * @param len Data length in bytes. + */ +#define NRF_LOG_HEXDUMP_ERROR(p_data, len) NRF_LOG_INTERNAL_HEXDUMP_ERROR(p_data, len) +#define NRF_LOG_HEXDUMP_WARNING(p_data, len) NRF_LOG_INTERNAL_HEXDUMP_WARNING(p_data, len) +#define NRF_LOG_HEXDUMP_INFO(p_data, len) NRF_LOG_INTERNAL_HEXDUMP_INFO(p_data, len) +#define NRF_LOG_HEXDUMP_DEBUG(p_data, len) NRF_LOG_INTERNAL_HEXDUMP_DEBUG(p_data, len) + +/** @def NRF_LOG_HEXDUMP_INST_ERROR + * @brief Macro for logging raw bytes for a specific module instance. + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + * + * @param p_inst Pointer to the instance with logging support. + * @param p_data Pointer to data. + * @param len Data length in bytes. + */ +/** @def NRF_LOG_HEXDUMP_INST_WARNING + * @brief Macro for logging raw bytes for a specific module instance. + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + * + * @param p_inst Pointer to the instance with logging support. + * @param p_data Pointer to data. + * @param len Data length in bytes. + */ +/** @def NRF_LOG_HEXDUMP_INST_INFO + * @brief Macro for logging raw bytes for a specific module instance. + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + * + * @param p_inst Pointer to the instance with logging support. + * @param p_data Pointer to data. + * @param len Data length in bytes. + */ +/** @def NRF_LOG_HEXDUMP_INST_DEBUG + * @brief Macro for logging raw bytes for a specific module instance. + * @details This macro is compiled only if @ref NRF_LOG_LEVEL includes error logs. + * + * @param p_inst Pointer to the instance with logging support. + * @param p_data Pointer to data. + * @param len Data length in bytes. + */ +#define NRF_LOG_HEXDUMP_INST_ERROR(p_inst, p_data, len) NRF_LOG_INTERNAL_HEXDUMP_INST_ERROR(p_inst, p_data, len) +#define NRF_LOG_HEXDUMP_INST_WARNING(p_inst, p_data, len) NRF_LOG_INTERNAL_HEXDUMP_INST_WARNING(p_inst, p_data, len) +#define NRF_LOG_HEXDUMP_INST_INFO(p_inst, p_data, len) NRF_LOG_INTERNAL_HEXDUMP_INST_INFO(p_inst, p_data, len) +#define NRF_LOG_HEXDUMP_INST_DEBUG(p_inst, p_data, len) NRF_LOG_INTERNAL_HEXDUMP_INST_DEBUG(p_inst, p_data, len) + +/** + * @brief Macro for logging hexdump without any prefix or timestamp. + */ +#define NRF_LOG_RAW_HEXDUMP_INFO(p_data, len) NRF_LOG_INTERNAL_RAW_HEXDUMP_INFO(p_data, len) + + +/** + * @brief Macro for copying a string to internal logger buffer if logs are deferred. + * + * @param _str String. + */ +#define NRF_LOG_PUSH(_str) NRF_LOG_INTERNAL_LOG_PUSH(_str) + +/** + * @brief Function for copying a string to the internal logger buffer if logs are deferred. + * + * Use this function to store a string that is volatile (for example allocated + * on stack) or that may change before the deferred logs are processed. Such string is copied + * into the internal logger buffer (see @ref NRF_LOG_STR_PUSH_BUFFER_SIZE). + * + * @note String storing is not reliable. It means that string is copied to the buffer but there is + * no indication when it was used and could be freed. String may be overwritten by another + * @ref nrf_log_push call before being processed. For reliable data dumping use + * hexdump macros (e.g. @ref NRF_LOG_HEXDUMP_INFO). + * + * @note If the logs are not deferred, then this function returns the input parameter. + * + * @param p_str Pointer to the user string. + * + * @return Address to the location where the string is stored in the internal logger buffer. + */ +char const * nrf_log_push(char * const p_str); + +/** + * @brief Macro to be used in a formatted string to a pass float number to the log. + * + * Use this macro in a formatted string instead of the %f specifier together with + * @ref NRF_LOG_FLOAT macro. + * Example: NRF_LOG_INFO("My float number" NRF_LOG_FLOAT_MARKER "\r\n", NRF_LOG_FLOAT(f))) + */ +#define NRF_LOG_FLOAT_MARKER "%s%d.%02d" + +/** + * @brief Macro for dissecting a float number into two numbers (integer and residuum). + */ +#define NRF_LOG_FLOAT(val) (uint32_t)(((val) < 0 && (val) > -1.0) ? "-" : ""), \ + (int32_t)(val), \ + (int32_t)((((val) > 0) ? (val) - (int32_t)(val) \ + : (int32_t)(val) - (val))*100) + + +/** + * @brief Macro for registering an independent module. + * + * Registration creates set of dynamic (RAM) and constant variables associated with the module. + */ +#define NRF_LOG_MODULE_REGISTER() NRF_LOG_INTERNAL_MODULE_REGISTER() + + +#ifdef __cplusplus +} +#endif + +#endif // NRF_LOG_H_ + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_log_backend_flash.c b/libraries/nfc/src/util/nrf_log_backend_flash.c new file mode 100644 index 000000000..1f5424763 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_flash.c @@ -0,0 +1,740 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_FLASH) +#include "nrf_log_backend_flash.h" +#include "nrf_log_str_formatter.h" +#include "nrf_fstorage_nvmc.h" +#include "nrf_log.h" +#include "nrf_atomic.h" +#include "nrf_queue.h" +#include "app_error.h" +#include + +#if (NRF_LOG_BACKEND_FLASHLOG_ENABLED == 0) && (NRF_LOG_BACKEND_CRASHLOG_ENABLED == 0) +#error "No flash backend enabled." +#endif + +/** @brief Maximum logger message payload (arguments or data in hexdump) which can be stored. */ +#define FLASH_LOG_MAX_PAYLOAD_SIZE (NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE - sizeof(nrf_log_header_t)) + +/** @brief Size of serialization buffer in words. */ +#define FLASH_LOG_SER_BUFFER_WORDS (NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE/sizeof(uint32_t)) + +/** @brief Length of logger header. */ +#define LOG_HEADER_LEN (sizeof(nrf_log_header_t)) + +/** @brief Length of logger header given in 32 bit words. */ +#define LOG_HEADER_LEN_WORDS (LOG_HEADER_LEN/sizeof(uint32_t)) + +/** @brief Maximum possible length of standard log message. */ +#define STD_LOG_MSG_MAX_LEN (LOG_HEADER_LEN + NRF_LOG_MAX_NUM_OF_ARGS*sizeof(uint32_t)) + +/* Buffer must be multiple of 4. */ +STATIC_ASSERT((NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE % sizeof(uint32_t)) == 0); + +/* Buffer must fit standard log message. */ +STATIC_ASSERT(NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE >= STD_LOG_MSG_MAX_LEN); + +/** @brief Flash page size in bytes. */ +#define CODE_PAGE_SIZE 4096 + +/** @brief Start address of the area dedicated for flash log. */ +#define FLASH_LOG_START_ADDR (NRF_LOG_BACKEND_FLASH_START_PAGE * CODE_PAGE_SIZE) + +/** @brief End address of the area dedicated for flash log. */ +#define FLASH_LOG_END_ADDR (FLASH_LOG_START_ADDR + (NRF_LOG_BACKEND_PAGES * CODE_PAGE_SIZE) - 1) + +/** @brief Size of the area dedicated for flash log. */ +#define FLASH_LOG_SIZE (NRF_LOG_BACKEND_PAGES * CODE_PAGE_SIZE) + +/** @brief Start address determined in runtime. + * + * If configuration indicates that flash log should be placed after application. + * */ +#if defined ( __CC_ARM ) +#define RUNTIME_START_ADDR \ + _Pragma("diag_suppress 170") \ + ((NRF_LOG_BACKEND_FLASH_START_PAGE == 0) ? \ + (CODE_PAGE_SIZE*CEIL_DIV((uint32_t)CODE_END, CODE_PAGE_SIZE)) : FLASH_LOG_START_ADDR) \ + _Pragma("diag_default 170") +#else +#define RUNTIME_START_ADDR ((NRF_LOG_BACKEND_FLASH_START_PAGE == 0) ? \ + (CODE_PAGE_SIZE*CEIL_DIV((uint32_t)CODE_END, CODE_PAGE_SIZE)) : FLASH_LOG_START_ADDR) +#endif +static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt); + +/** @brief Message queue for run time flash log. */ +#if NRF_LOG_BACKEND_FLASHLOG_ENABLED +NRF_QUEUE_DEF(nrf_log_entry_t *, + m_flashlog_queue, + NRF_LOG_BACKEND_FLASHLOG_QUEUE_SIZE, + NRF_QUEUE_MODE_NO_OVERFLOW); +static const nrf_queue_t * mp_flashlog_queue = &m_flashlog_queue; +#else +static const nrf_queue_t * mp_flashlog_queue = NULL; +#endif + + +/** @brief Message FIFO for crash log. */ +#if NRF_LOG_BACKEND_CRASHLOG_ENABLED +NRF_QUEUE_DEF(nrf_log_entry_t *, + m_crashlog_queue, + NRF_LOG_BACKEND_CRASHLOG_FIFO_SIZE, + NRF_QUEUE_MODE_NO_OVERFLOW); +static const nrf_queue_t * mp_crashlog_queue = &m_crashlog_queue; +#else +static const nrf_queue_t * mp_crashlog_queue = NULL; +#endif + + +/** @brief Fstorage instance used for flash log. */ +NRF_FSTORAGE_DEF(nrf_fstorage_t m_log_flash_fstorage) = +{ + /* Set a handler for fstorage events. */ + .evt_handler = fstorage_evt_handler, + .start_addr = FLASH_LOG_START_ADDR, + .end_addr = FLASH_LOG_END_ADDR, +}; + +/** @brief Flash log state. */ +typedef enum +{ + LOG_BACKEND_FLASH_ACTIVE, /**< Flash backend is active. */ + LOG_BACKEND_FLASH_INACTIVE, /**< Flash backend is inactive. All incoming requests are skipped. */ + LOG_BACKEND_FLASH_IN_PANIC, /**< Flash backend is in panic mode. Incoming messages are written to flash in synchronous mode. */ +} log_backend_flash_state_t; + +static log_backend_flash_state_t m_state; /**< Flash logger backend state. */ +static nrf_atomic_flag_t m_busy_flag; /**< Flag indicating if module performs flash writing. */ +static uint32_t m_flash_buf[FLASH_LOG_SER_BUFFER_WORDS]; /**< Buffer used for serializing messages. */ +static uint32_t m_curr_addr; /**< Address of free spot in the storage area. */ +static size_t m_curr_len; /**< Length of current message being written. */ +static uint32_t m_dropped; /**< Number of dropped messages. */ + +/** @brief Log message string injected when entering panic mode. */ +static const char crashlog_str[] = "-----------CRASHLOG------------\r\n"; + +/** @brief Function saturates input to maximum possible length and rounds up value to be multiple + * of word size. + * + * @param length Length value. + * + * @return Modified input length. + */ +static uint32_t saturate_align_length(uint32_t length) +{ + length = (length > FLASH_LOG_MAX_PAYLOAD_SIZE) ? FLASH_LOG_MAX_PAYLOAD_SIZE : length; //saturate + length = CEIL_DIV(length, sizeof(uint32_t))*sizeof(uint32_t); + return length; +} + + +/** + * @brief Function for copying logger message to the buffer. + * + * @param[in] p_msg Logger message. + * @param[out] p_buf Output buffer where serialized message is placed. + * @param[in,out] p_len Buffer size as input, length of prepared data as output. + * + * @return True if message fits into the buffer, false otherwise + */ +static bool msg_to_buf(nrf_log_entry_t * p_msg, uint8_t * p_buf, size_t * p_len) +{ + uint32_t data_len; + nrf_log_header_t header = {0}; + size_t memobj_offset = HEADER_SIZE*sizeof(uint32_t); + + nrf_memobj_read(p_msg, &header, HEADER_SIZE*sizeof(uint32_t), 0); + + memcpy(p_buf, &header, sizeof(nrf_log_header_t)); + p_buf += sizeof(nrf_log_header_t); + + switch (header.base.generic.type) + { + case HEADER_TYPE_STD: + { + data_len = header.base.std.nargs * sizeof(uint32_t); + break; + } + case HEADER_TYPE_HEXDUMP: + { + data_len = saturate_align_length(header.base.hexdump.len); + break; + } + default: + *p_len = 0; + return false; + } + nrf_memobj_read(p_msg, p_buf, data_len, memobj_offset); + + if (*p_len >= sizeof(nrf_log_header_t) + data_len) + { + *p_len = sizeof(nrf_log_header_t) + data_len; + return true; + } + else + { + return false; + } +} + +/** + * @brief Function for getting logger message stored in flash. + * + * @param[in] p_buf Pointer to the location where message is stored. + * @param[out] pp_header Pointer to the log message header. + * @param[out] pp_data Pointer to the log message data (arguments or data in case of hexdump). + * + * @return True if message was successfully fetched, false otherwise. + */ +static bool msg_from_buf(uint32_t * p_buf, + nrf_log_header_t * * pp_header, + uint8_t * * pp_data, + uint32_t * p_len) +{ + *pp_header = (nrf_log_header_t *)p_buf; + *pp_data = (uint8_t *)&p_buf[LOG_HEADER_LEN_WORDS]; + + uint32_t data_len; + + switch ((*pp_header)->base.generic.type) + { + case HEADER_TYPE_STD: + { + data_len = ((*pp_header)->base.std.nargs)*sizeof(uint32_t); + break; + } + case HEADER_TYPE_HEXDUMP: + { + + data_len = saturate_align_length((*pp_header)->base.hexdump.len); + break; + } + default: + return false; + } + + *p_len = LOG_HEADER_LEN + data_len; + return true; +} + +/** + * @brief Function for processing log message queue. + * + * If writing to flash is synchronous then function drains the queue and writes all messages to flash. + * If writing to flash is asynchronous then function starts single write operation. In asynchronous mode + * function is called when new message is put into the queue from from flash operation callback. + * + * Function detects the situation that flash module reports attempt to write outside dedicated area. + * In that case flash backend stops writing any new messages. + * + * @param p_queue Queue will log messages + * @param fstorage_blocking If true it indicates that flash operations are blocking, event handler is not used. + */ +static void log_msg_queue_process(nrf_queue_t const * p_queue, bool fstorage_blocking) +{ + nrf_log_entry_t * p_msg; + bool busy = false; + while (nrf_queue_pop(p_queue, &p_msg) == NRF_SUCCESS) + { + ret_code_t err_code; + + m_curr_len = sizeof(m_flash_buf); + if (!msg_to_buf(p_msg, (uint8_t *)m_flash_buf, &m_curr_len)) + { + nrf_memobj_put(p_msg); + continue; + } + + err_code = nrf_fstorage_write(&m_log_flash_fstorage, m_curr_addr, m_flash_buf, m_curr_len, p_msg); + + if (err_code == NRF_SUCCESS) + { + if (fstorage_blocking) + { + m_curr_addr += m_curr_len; + + nrf_memobj_put(p_msg); + } + else + { + busy = true; + break; + } + } + else if (!fstorage_blocking && (err_code == NRF_ERROR_NO_MEM)) + { + // fstorage queue got full. Drop entry. + nrf_memobj_put(p_msg); + m_dropped++; + break; + } + else if (err_code == NRF_ERROR_INVALID_ADDR) + { + // Trying to write outside the area, flash log is full. Skip any new writes. + nrf_memobj_put(p_msg); + m_state = LOG_BACKEND_FLASH_INACTIVE; + } + else + { + ASSERT(false); + } + } + + if (!busy) + { + UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&m_busy_flag)); + } +} + +static void queue_element_drop(nrf_queue_t const * p_queue) +{ + nrf_log_entry_t * p_msg; + if (nrf_queue_pop(p_queue, &p_msg) == NRF_SUCCESS) + { + m_dropped++; + nrf_memobj_put(p_msg); + } +} + +static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt) +{ + if (m_state == LOG_BACKEND_FLASH_ACTIVE) + { + switch (p_evt->id) + { + case NRF_FSTORAGE_EVT_WRITE_RESULT: + { + if (p_evt->result == NRF_SUCCESS) + { + m_curr_addr += m_curr_len; + m_curr_len = 0; + log_msg_queue_process(mp_flashlog_queue, false); + } + else + { + m_dropped++; + } + + if (p_evt->p_param) + { + nrf_memobj_put((nrf_log_entry_t *)p_evt->p_param); + } + break; + } + default: + break; + } + } + else if ((m_state == LOG_BACKEND_FLASH_INACTIVE) && + (p_evt->id == NRF_FSTORAGE_EVT_ERASE_RESULT) && + (p_evt->addr == RUNTIME_START_ADDR)) + { + m_state = LOG_BACKEND_FLASH_ACTIVE; + } +} + +/** + * @brief Function for enqueueing new message. + * + * If queue is full then the oldest message is freed. + * + * @param p_queue Queue. + * @param p_msg Message. + * + * @return Number of dropped messages + */ +static uint32_t message_enqueue(nrf_queue_t const * p_queue, nrf_log_entry_t * p_msg) +{ + uint32_t dropped = 0; + + //flag was set, busy so enqueue message + while (nrf_queue_push(p_queue, &p_msg) != NRF_SUCCESS) + { + + nrf_log_entry_t * p_old_msg; + if (nrf_queue_pop(p_queue, &p_old_msg) == NRF_SUCCESS) + { + nrf_memobj_put(p_old_msg); + dropped++; + } + } + + return dropped; +} + + +void nrf_log_backend_flashlog_put(nrf_log_backend_t const * p_backend, + nrf_log_entry_t * p_msg) +{ + if (m_state == LOG_BACKEND_FLASH_ACTIVE) + { + nrf_memobj_get(p_msg); + + m_dropped += message_enqueue(mp_flashlog_queue, p_msg); + + if (nrf_atomic_flag_set_fetch(&m_busy_flag) == 0) + { + log_msg_queue_process(mp_flashlog_queue, false); + } + } +} + + +void nrf_log_backend_crashlog_put(nrf_log_backend_t const * p_backend, + nrf_log_entry_t * p_msg) +{ + if (m_state != LOG_BACKEND_FLASH_INACTIVE) + { + nrf_memobj_get(p_msg); + + UNUSED_RETURN_VALUE(message_enqueue(mp_crashlog_queue, p_msg)); + } + + if (m_state == LOG_BACKEND_FLASH_IN_PANIC) + { + log_msg_queue_process(mp_crashlog_queue, true); + } +} + +void nrf_log_backend_flashlog_flush(nrf_log_backend_t const * p_backend) +{ + queue_element_drop(mp_flashlog_queue); +} + +void nrf_log_backend_crashlog_flush(nrf_log_backend_t const * p_backend) +{ + queue_element_drop(mp_crashlog_queue); +} + +void nrf_log_backend_flashlog_panic_set(nrf_log_backend_t const * p_backend) +{ + /* Empty */ +} + +/** + * @brief Function for injecting log message which will indicate start of crash log. + */ +static void crashlog_marker_inject(void) +{ + nrf_log_header_t crashlog_marker_hdr = { + .base = { + .std = { + .type = HEADER_TYPE_STD, + .severity = NRF_LOG_SEVERITY_INFO_RAW, + .nargs = 0, + .addr = (uint32_t)crashlog_str & STD_ADDR_MASK + } + }, + .module_id = 0, + .timestamp = 0, + }; + m_flash_buf[0] = crashlog_marker_hdr.base.raw; + m_flash_buf[1] = crashlog_marker_hdr.module_id; + m_flash_buf[2] = crashlog_marker_hdr.timestamp; + (void)nrf_fstorage_write(&m_log_flash_fstorage, m_curr_addr, m_flash_buf, LOG_HEADER_LEN, NULL); + m_curr_addr += LOG_HEADER_LEN; +} + + +void nrf_log_backend_crashlog_panic_set(nrf_log_backend_t const * p_backend) +{ + if (nrf_fstorage_init(&m_log_flash_fstorage, &nrf_fstorage_nvmc, NULL) == NRF_SUCCESS) + { + m_state = LOG_BACKEND_FLASH_IN_PANIC; + + /* In case of Softdevice MWU may protect access to NVMC. */ + NVIC_DisableIRQ(MWU_IRQn); + + log_msg_queue_process(mp_flashlog_queue, true); + + crashlog_marker_inject(); + + log_msg_queue_process(mp_crashlog_queue, true); + } + else + { + m_state = LOG_BACKEND_FLASH_INACTIVE; + } +} + +/** + * @brief Function for determining first empty location in area dedicated for flash logger backend. + */ +static uint32_t empty_addr_get(void) +{ + uint32_t token = 0; + nrf_log_header_t * p_dummy_header; + uint8_t * p_dummy_data; + + while(nrf_log_backend_flash_next_entry_get(&token, &p_dummy_header, &p_dummy_data) == NRF_SUCCESS) + { + + } + + return token; +} + + +ret_code_t nrf_log_backend_flash_init(nrf_fstorage_api_t const * p_fs_api) +{ + ret_code_t err_code; + + + uint32_t start_addr = RUNTIME_START_ADDR; + uint32_t end_addr = start_addr + FLASH_LOG_SIZE - 1; + + m_log_flash_fstorage.start_addr = start_addr; + m_log_flash_fstorage.end_addr = end_addr; + + err_code = nrf_fstorage_init(&m_log_flash_fstorage, p_fs_api, NULL); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_curr_addr = empty_addr_get(); + m_state = LOG_BACKEND_FLASH_ACTIVE; + + return err_code; +} + + +ret_code_t nrf_log_backend_flash_next_entry_get(uint32_t * p_token, + nrf_log_header_t * * pp_header, + uint8_t * * pp_data) +{ + uint32_t * p_addr = p_token; + uint32_t len; + + *p_addr = (*p_addr == 0) ? RUNTIME_START_ADDR : *p_addr; + + if (nrf_fstorage_rmap(&m_log_flash_fstorage, *p_addr) == NULL) + { + //Supports only memories which can be mapped for reading. + return NRF_ERROR_NOT_SUPPORTED; + } + + if (msg_from_buf((uint32_t *)*p_addr, pp_header, pp_data, &len)) + { + *p_addr += len; + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_NOT_FOUND; + } +} + + +ret_code_t nrf_log_backend_flash_erase(void) +{ + ret_code_t err_code; + + m_state = LOG_BACKEND_FLASH_INACTIVE; + err_code = nrf_fstorage_erase(&m_log_flash_fstorage, RUNTIME_START_ADDR, NRF_LOG_BACKEND_PAGES, NULL); + + m_curr_addr = RUNTIME_START_ADDR; + + return err_code; +} + +#if NRF_LOG_BACKEND_FLASHLOG_ENABLED +const nrf_log_backend_api_t nrf_log_backend_flashlog_api = { + .put = nrf_log_backend_flashlog_put, + .flush = nrf_log_backend_flashlog_flush, + .panic_set = nrf_log_backend_flashlog_panic_set, +}; +#endif + +#if NRF_LOG_BACKEND_CRASHLOG_ENABLED +const nrf_log_backend_api_t nrf_log_backend_crashlog_api = { + .put = nrf_log_backend_crashlog_put, + .flush = nrf_log_backend_crashlog_flush, + .panic_set = nrf_log_backend_crashlog_panic_set, +}; +#endif + +#if NRF_LOG_BACKEND_FLASH_CLI_CMDS +#include "nrf_cli.h" + +static uint8_t m_buffer[64]; +static nrf_cli_t const * mp_cli; + +static void cli_tx(void const * p_context, char const * p_buffer, size_t len); + +static nrf_fprintf_ctx_t m_fprintf_ctx = +{ + .p_io_buffer = (char *)m_buffer, + .io_buffer_size = sizeof(m_buffer)-1, + .io_buffer_cnt = 0, + .auto_flush = true, + .p_user_ctx = &mp_cli, + .fwrite = cli_tx +}; + + +static void flashlog_clear_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv) +{ + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + } + + UNUSED_RETURN_VALUE(nrf_log_backend_flash_erase()); +} + +#include "nrf_delay.h" +static void cli_tx(void const * p_context, char const * p_buffer, size_t len) +{ + nrf_cli_t * * pp_cli = (nrf_cli_t * *)p_context; + char * p_strbuf = (char *)&p_buffer[len]; + *p_strbuf = '\0'; + nrf_cli_fprintf((nrf_cli_t const *)*pp_cli, NRF_CLI_DEFAULT, p_buffer); + // nrf_delay_ms(10); +} + + +static void entry_process(nrf_cli_t const * p_cli, nrf_log_header_t * p_header, uint8_t * p_data) +{ + mp_cli = p_cli; + + nrf_log_str_formatter_entry_params_t params = + { + .timestamp = p_header->timestamp, + .module_id = p_header->module_id, + .use_colors = 0, + }; + + switch (p_header->base.generic.type) + { + case HEADER_TYPE_STD: + { + params.severity = (nrf_log_severity_t)p_header->base.std.severity; + nrf_log_std_entry_process((const char *)((uint32_t)p_header->base.std.addr), + (uint32_t *)p_data, + p_header->base.std.nargs, + ¶ms, + &m_fprintf_ctx); + break; + } + case HEADER_TYPE_HEXDUMP: + { + params.severity = (nrf_log_severity_t)p_header->base.hexdump.severity; + + nrf_log_hexdump_entry_process(p_data, + p_header->base.hexdump.len, + ¶ms, + &m_fprintf_ctx); + break; + } + default: + ASSERT(0); + } + +} + + +static void flashlog_read_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv) +{ + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + } + + uint32_t token = 0; + uint8_t * p_data = NULL; + bool empty = true; + nrf_log_header_t * p_header; + + while (1) + { + if (nrf_log_backend_flash_next_entry_get(&token, &p_header, &p_data) == NRF_SUCCESS) + { + entry_process(p_cli, p_header, p_data); + empty = false; + } + else + { + break; + } + } + + if (empty) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Flash log empty\r\n"); + } +} + + +static void flashlog_status_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv) +{ + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + } + + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Flash log status:\r\n"); + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Location (address: 0x%08X, length: %d)\r\n", + RUNTIME_START_ADDR, FLASH_LOG_SIZE); + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Current usage:%d%% (%d of %d bytes used)\r\n", + 100ul * (m_curr_addr - RUNTIME_START_ADDR)/FLASH_LOG_SIZE, + m_curr_addr - RUNTIME_START_ADDR, + FLASH_LOG_SIZE); + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Dropped logs: %d\r\n", m_dropped); + + +} + + +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_flashlog_cmd) +{ + NRF_CLI_CMD(clear, NULL, "Remove logs", flashlog_clear_cmd), + NRF_CLI_CMD(read, NULL, "Read stored logs", flashlog_read_cmd), + NRF_CLI_CMD(status, NULL, "Flash log status", flashlog_status_cmd), + NRF_CLI_SUBCMD_SET_END +}; + +NRF_CLI_CMD_REGISTER(flashlog, &m_flashlog_cmd, "Commands for reading logs stored in non-volatile memory", NULL); + +#endif //NRF_LOG_BACKEND_FLASH_CLI_CMDS + +#endif //NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_FLASH) diff --git a/libraries/nfc/src/util/nrf_log_backend_flash.h b/libraries/nfc/src/util/nrf_log_backend_flash.h new file mode 100644 index 000000000..a2704c361 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_flash.h @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2018 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + /**@file + * + * @defgroup nrf_log_backend_flash Flash logger backend + * @{ + * @ingroup nrf_log + * @brief Flash logger backend. + */ + +#ifndef NRF_LOG_BACKEND_FLASH_H +#define NRF_LOG_BACKEND_FLASH_H + +#include "nrf_log_backend_interface.h" +#include "nrf_fstorage.h" +#include "nrf_log_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Flashlog logger backend API. */ +extern const nrf_log_backend_api_t nrf_log_backend_flashlog_api; + +/** @brief Crashlog logger backend API. */ +extern const nrf_log_backend_api_t nrf_log_backend_crashlog_api; + +/** @brief Flashlog logger backend structure. */ +typedef struct { + nrf_log_backend_t backend; +} nrf_log_backend_flashlog_t; + +/** @brief Crashlog logger backend structure. */ +typedef struct { + nrf_log_backend_t backend; +} nrf_log_backend_crashlog_t; + +/** @brief Macro for creating an instance of the flashlog logger backend. */ +#define NRF_LOG_BACKEND_FLASHLOG_DEF(_name) \ + NRF_LOG_BACKEND_DEF(_name, nrf_log_backend_flashlog_api, NULL) + +/** @brief Macro for creating an instance of the crashlog logger backend. */ +#define NRF_LOG_BACKEND_CRASHLOG_DEF(_name) \ + NRF_LOG_BACKEND_DEF(_name, nrf_log_backend_crashlog_api, NULL) + +/** + * @brief Function for initializing the flash logger backend. + * + * Flash logger backend consists of two logical backends: flashlog and crashlog. Since both + * backends write to the same flash area, the initialization is common. + * + * @param p_fs_api fstorage API to be used. + * + * @return NRF_SUCCESS or error code returned by @ref nrf_fstorage_init. + */ +ret_code_t nrf_log_backend_flash_init(nrf_fstorage_api_t const * p_fs_api); + +/** + * @brief Function for getting a log entry stored in flash. + * + * Log messages stored in flash can be read one by one starting from the oldest one. + * + * @param[in, out] p_token Token reused between consecutive readings of log entries. + * Token must be set to 0 to read the first entry. + * @param[out] pp_header Pointer to the entry header. + * @param[out] pp_data Pointer to the data part of the entry (arguments or data in case of hexdump). + * + * @retval NRF_SUCCESS Entry was successfully read. + * @retval NRF_ERROR_NOT_SUPPORTED fstorage API does not support direct reading. + * @retval NRF_ERROR_NOT_FOUND Entry not found. Last entry was already reached or area is empty. + */ +ret_code_t nrf_log_backend_flash_next_entry_get(uint32_t * p_token, + nrf_log_header_t * * pp_header, + uint8_t * * pp_data); + +/** + * @brief Function for erasing flash area dedicated for the flash logger backend. + */ +ret_code_t nrf_log_backend_flash_erase(void); + +#ifdef __cplusplus +} +#endif + +#endif //NRF_LOG_BACKEND_UART_H + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_log_backend_interface.h b/libraries/nfc/src/util/nrf_log_backend_interface.h new file mode 100644 index 000000000..843a4c7e4 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_interface.h @@ -0,0 +1,271 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_BACKEND_INTERFACE_H +#define NRF_LOG_BACKEND_INTERFACE_H + +/**@file + * @addtogroup nrf_log Logger module + * @ingroup app_common + * + * @defgroup nrf_log_backend_interface Logger backend interface + * @{ + * @ingroup nrf_log + * @brief The nrf_log backend interface. + */ + +#if NRF_MODULE_ENABLED(NRF_LOG) +#include "nrf_memobj.h" +#endif + +#include "nrf_section.h" +#include "nordic_common.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief nrf_log entry. + */ +#if NRF_LOG_ENABLED +typedef nrf_memobj_t nrf_log_entry_t; +#else +typedef void nrf_log_entry_t; +#endif + +/* Forward declaration of the nrf_log_backend_t type. */ +typedef struct nrf_log_backend_s nrf_log_backend_t; + +/** + * @brief Logger backend API. + */ +typedef struct +{ + /** + * @brief @ref nrf_log_backend_put + */ + void (*put)(nrf_log_backend_t const * p_backend, nrf_log_entry_t * p_entry); + + /** + * @brief @ref nrf_log_backend_panic_set + */ + void (*panic_set)(nrf_log_backend_t const * p_backend); + + /** + * @brief @ref nrf_log_backend_flush + */ + void (*flush)(nrf_log_backend_t const * p_backend); +} nrf_log_backend_api_t; + +/** + * @brief Logger backend control block. + */ +typedef struct +{ + nrf_log_backend_t const * p_next; //!< Pointer to the next backend in the logger. + uint8_t id; //!< ID of the backend. + bool enabled; //!< Enable flag. +} nrf_log_backend_cb_t; + +/** + * @brief Logger backend structure. + */ +struct nrf_log_backend_s +{ + nrf_log_backend_api_t const * p_api; //!< Pointer to interface. + void * p_ctx; //!< User context. + char * p_name; //!< Name of the backend. + nrf_log_backend_cb_t * p_cb; //!< Pointer to the backend control block. +}; + + +#define NRF_LOG_BACKEND_SECTION_NAME log_backends + +#define NRF_LOG_BACKEND_SUBSECTION_NAME(_name) NRF_LOG_BACKEND_SECTION_NAME + +/** @brief Invalid ID value indicating that logger backend is not attached to the logger frontend.*/ +#define NRF_LOG_BACKEND_INVALID_ID 0xFF + +/** @brief Memory section where backends are located. */ +NRF_SECTION_DEF(NRF_LOG_BACKEND_SECTION_NAME, nrf_log_backend_t); + +/** + * @brief Macro for creating a logger backend instance. + * + * @param _name Name of the backend instance. + * @param _api Logger backend API. + * @param _p_ctx Pointer to the user context. + */ +#define NRF_LOG_BACKEND_DEF(_name, _api, _p_ctx) \ + static nrf_log_backend_cb_t CONCAT_2(log_backend_cb_, _name) = { \ + .enabled = false, \ + .id = NRF_LOG_BACKEND_INVALID_ID, \ + .p_next = NULL \ + }; \ + NRF_SECTION_ITEM_REGISTER(NRF_LOG_BACKEND_SUBSECTION_NAME(_name), \ + static const nrf_log_backend_t _name) = { \ + .p_api = &_api, \ + .p_ctx = _p_ctx, \ + .p_cb = &CONCAT_2(log_backend_cb_, _name), \ + .p_name = (char *)STRINGIFY(_name) \ + } + + +/** + * @brief Function for putting message with log entry to the backend. + * + * @param[in] p_backend Pointer to the backend instance. + * @param[in] p_msg Pointer to message with log entry. + */ +__STATIC_INLINE void nrf_log_backend_put(nrf_log_backend_t const * const p_backend, + nrf_log_entry_t * p_msg); + +/** + * @brief Function for reconfiguring backend to panic mode. + * + * @param[in] p_backend Pointer to the backend instance. + */ +__STATIC_INLINE void nrf_log_backend_panic_set(nrf_log_backend_t const * const p_backend); + +/** + * @brief Function for flushing backend. + * + * On flushing request backend should release log message(s). + * + * @param[in] p_backend Pointer to the backend instance. + */ +__STATIC_INLINE void nrf_log_backend_flush(nrf_log_backend_t const * const p_backend); + + +/** + * @brief Function for setting backend id. + * + * @note It is used internally by the logger. + * + * @param[in] p_backend Pointer to the backend instance. + * @param[in] id Id. + */ +__STATIC_INLINE void nrf_log_backend_id_set(nrf_log_backend_t const * const p_backend, uint8_t id); + +/** + * @brief Function for getting backend id. + * + * @note It is used internally by the logger. + * + * @param[in] p_backend Pointer to the backend instance. + * @return Id. + */ +__STATIC_INLINE uint8_t nrf_log_backend_id_get(nrf_log_backend_t const * const p_backend); + +/** + * @brief Function for enabling backend. + * + * @param[in] p_backend Pointer to the backend instance. + */ +__STATIC_INLINE void nrf_log_backend_enable(nrf_log_backend_t const * const p_backend); + +/** + * @brief Function for disabling backend. + * + * @param[in] p_backend Pointer to the backend instance. + */ +__STATIC_INLINE void nrf_log_backend_disable(nrf_log_backend_t const * const p_backend); + +/** + * @brief Function for checking state of the backend. + * + * @param[in] p_backend Pointer to the backend instance. + * + * @return True if backend is enabled, false otherwise. + */ +__STATIC_INLINE bool nrf_log_backend_is_enabled(nrf_log_backend_t const * const p_backend); + +#ifndef SUPPRESS_INLINE_IMPLEMENTATION +__STATIC_INLINE void nrf_log_backend_put(nrf_log_backend_t const * const p_backend, + nrf_log_entry_t * p_msg) +{ + p_backend->p_api->put(p_backend, p_msg); +} + +__STATIC_INLINE void nrf_log_backend_panic_set(nrf_log_backend_t const * const p_backend) +{ + p_backend->p_api->panic_set(p_backend); +} + +__STATIC_INLINE void nrf_log_backend_flush(nrf_log_backend_t const * const p_backend) +{ + p_backend->p_api->flush(p_backend); +} + +__STATIC_INLINE void nrf_log_backend_id_set(nrf_log_backend_t const * const p_backend, uint8_t id) +{ + p_backend->p_cb->id = id; +} + +__STATIC_INLINE uint8_t nrf_log_backend_id_get(nrf_log_backend_t const * const p_backend) +{ + return p_backend->p_cb->id; +} + +__STATIC_INLINE void nrf_log_backend_enable(nrf_log_backend_t const * const p_backend) +{ + p_backend->p_cb->enabled = true; +} + +__STATIC_INLINE void nrf_log_backend_disable(nrf_log_backend_t const * const p_backend) +{ + p_backend->p_cb->enabled = false; +} + +__STATIC_INLINE bool nrf_log_backend_is_enabled(nrf_log_backend_t const * const p_backend) +{ + return p_backend->p_cb->enabled; +} + +#endif // SUPPRESS_INLINE_IMPLEMENTATION + +#ifdef __cplusplus +} +#endif + +#endif //NRF_LOG_BACKEND_INTERFACE_H + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_log_backend_rtt.c b/libraries/nfc/src/util/nrf_log_backend_rtt.c new file mode 100644 index 000000000..8120cd8e5 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_rtt.c @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_RTT) +#include "nrf_log_backend_rtt.h" +#include "nrf_log_backend_serial.h" +#include "nrf_log_str_formatter.h" +#include "nrf_log_internal.h" +#include "nrf_delay.h" +#include +#include + +static bool m_host_present; + +static uint8_t m_string_buff[NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE]; + +void nrf_log_backend_rtt_init(void) +{ + SEGGER_RTT_Init(); +} + +static void serial_tx(void const * p_context, char const * buffer, size_t len) +{ + if (len) + { + uint32_t idx = 0; + uint32_t processed; + uint32_t watchdog_counter = NRF_LOG_BACKEND_RTT_TX_RETRY_CNT; + do + { + processed = SEGGER_RTT_WriteNoLock(0, &buffer[idx], len); + idx += processed; + len -= processed; + if (processed == 0) + { + /* There are two possible reasons for not writing any data to RTT: + * - The host is not connected and not reading the data. + * - The buffer got full and will be read by the host. + * These two situations are distinguished using the following algorithm. + * At the begining, the module assumes that the host is active, + * so when no data is read, it busy waits and retries. + * If, after retrying, the host reads the data, the module assumes that the host is active. + * If it fails, the module assumes that the host is inactive and stores that information. On next + * call, only one attempt takes place. The host is marked as active if the attempt is successful. + */ + if (!m_host_present) + { + break; + } + else + { + nrf_delay_ms(NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS); + watchdog_counter--; + if (watchdog_counter == 0) + { + m_host_present = false; + break; + } + } + } + m_host_present = true; + } while (len); + } +} +static void nrf_log_backend_rtt_put(nrf_log_backend_t const * p_backend, + nrf_log_entry_t * p_msg) +{ + nrf_log_backend_serial_put(p_backend, p_msg, m_string_buff, NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE, serial_tx); +} + +static void nrf_log_backend_rtt_flush(nrf_log_backend_t const * p_backend) +{ + +} + +static void nrf_log_backend_rtt_panic_set(nrf_log_backend_t const * p_backend) +{ + +} + +const nrf_log_backend_api_t nrf_log_backend_rtt_api = { + .put = nrf_log_backend_rtt_put, + .flush = nrf_log_backend_rtt_flush, + .panic_set = nrf_log_backend_rtt_panic_set, +}; +#endif //NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_RTT) diff --git a/libraries/nfc/src/util/nrf_log_backend_rtt.h b/libraries/nfc/src/util/nrf_log_backend_rtt.h new file mode 100644 index 000000000..9f549cf22 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_rtt.h @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + /**@file + * + * @defgroup nrf_log_backend_rtt Log RTT backend + * @{ + * @ingroup nrf_log + * @brief Log RTT backend. + */ + +#ifndef NRF_LOG_BACKEND_RTT_H +#define NRF_LOG_BACKEND_RTT_H + +#include "nrf_log_backend_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const nrf_log_backend_api_t nrf_log_backend_rtt_api; + +typedef struct { + nrf_log_backend_t backend; +} nrf_log_backend_rtt_t; + +/** + * @brief RTT backend definition + * + * @param _name Name of the instance. + */ +#define NRF_LOG_BACKEND_RTT_DEF(_name) \ + NRF_LOG_BACKEND_DEF(_name, nrf_log_backend_rtt_api, NULL) + +void nrf_log_backend_rtt_init(void); + +#ifdef __cplusplus +} +#endif + +#endif //NRF_LOG_BACKEND_RTT_H + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_log_backend_serial.c b/libraries/nfc/src/util/nrf_log_backend_serial.c new file mode 100644 index 000000000..c5849d01a --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_serial.c @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_LOG) +#include "nrf_log_backend_serial.h" +#include "nrf_log_str_formatter.h" +#include "nrf_log_internal.h" + +void nrf_log_backend_serial_put(nrf_log_backend_t const * p_backend, + nrf_log_entry_t * p_msg, + uint8_t * p_buffer, + uint32_t length, + nrf_fprintf_fwrite tx_func) +{ + nrf_memobj_get(p_msg); + + nrf_fprintf_ctx_t fprintf_ctx = { + .p_io_buffer = (char *)p_buffer, + .io_buffer_size = length, + .io_buffer_cnt = 0, + .auto_flush = false, + .p_user_ctx = NULL, + .fwrite = tx_func + }; + + nrf_log_str_formatter_entry_params_t params; + + nrf_log_header_t header; + size_t memobj_offset = 0; + + nrf_memobj_read(p_msg, &header, HEADER_SIZE*sizeof(uint32_t), memobj_offset); + memobj_offset = HEADER_SIZE*sizeof(uint32_t); + + params.timestamp = header.timestamp; + params.module_id = header.module_id; + params.dropped = header.dropped; + params.use_colors = NRF_LOG_USES_COLORS; + + /*lint -save -e438*/ + if (header.base.generic.type == HEADER_TYPE_STD) + { + char const * p_log_str = (char const *)((uint32_t)header.base.std.addr); + params.severity = (nrf_log_severity_t)header.base.std.severity; + uint32_t nargs = header.base.std.nargs; + uint32_t args[NRF_LOG_MAX_NUM_OF_ARGS]; + + nrf_memobj_read(p_msg, args, nargs*sizeof(uint32_t), memobj_offset); + memobj_offset += (nargs*sizeof(uint32_t)); + + nrf_log_std_entry_process(p_log_str, + args, + nargs, + ¶ms, + &fprintf_ctx); + + } + else if (header.base.generic.type == HEADER_TYPE_HEXDUMP) + { + uint32_t data_len = header.base.hexdump.len; + params.severity = (nrf_log_severity_t)header.base.hexdump.severity; + uint8_t data_buf[8]; + uint32_t chunk_len; + do + { + chunk_len = sizeof(data_buf) > data_len ? data_len : sizeof(data_buf); + nrf_memobj_read(p_msg, data_buf, chunk_len, memobj_offset); + memobj_offset += chunk_len; + data_len -= chunk_len; + + nrf_log_hexdump_entry_process(data_buf, + chunk_len, + ¶ms, + &fprintf_ctx); + } while (data_len > 0); + } + nrf_memobj_put(p_msg); + /*lint -restore*/ +} +#endif //NRF_LOG_ENABLED diff --git a/libraries/nfc/src/util/nrf_log_backend_serial.h b/libraries/nfc/src/util/nrf_log_backend_serial.h new file mode 100644 index 000000000..0b2874ac3 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_serial.h @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_BACKEND_SERIAL_H +#define NRF_LOG_BACKEND_SERIAL_H +/**@file + * @addtogroup nrf_log Logger module + * @ingroup app_common + * + * @defgroup nrf_log_backend_serial Common part of serial backends + * @{ + * @ingroup nrf_log + * @brief The nrf_log serial backend common put function. + */ + + +#include "nrf_log_backend_interface.h" +#include "nrf_fprintf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief A function for processing logger entry with simple serial interface as output. + * + * + */ +void nrf_log_backend_serial_put(nrf_log_backend_t const * p_backend, + nrf_log_entry_t * p_msg, + uint8_t * p_buffer, + uint32_t length, + nrf_fprintf_fwrite tx_func); + +#endif //NRF_LOG_BACKEND_SERIAL_H + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_log_backend_uart.c b/libraries/nfc/src/util/nrf_log_backend_uart.c new file mode 100644 index 000000000..db374cb30 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_uart.c @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_UART) +#include "nrf_log_backend_uart.h" +#include "nrf_log_backend_serial.h" +#include "nrf_log_internal.h" +#include "util/nrf_drv_uart.h" +#include "app_error.h" + +nrf_drv_uart_t m_uart = NRF_DRV_UART_INSTANCE(0); + +static uint8_t m_string_buff[NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE]; +static volatile bool m_xfer_done; +static bool m_async_mode; +static void uart_evt_handler(nrf_drv_uart_event_t * p_event, void * p_context) +{ + m_xfer_done = true; +} + +static void uart_init(bool async_mode) +{ + nrf_drv_uart_config_t config = NRF_DRV_UART_DEFAULT_CONFIG; + config.pseltxd = NRF_LOG_BACKEND_UART_TX_PIN; + config.pselrxd = NRF_UART_PSEL_DISCONNECTED; + config.pselcts = NRF_UART_PSEL_DISCONNECTED; + config.pselrts = NRF_UART_PSEL_DISCONNECTED; + config.baudrate = (nrf_uart_baudrate_t)NRF_LOG_BACKEND_UART_BAUDRATE; + ret_code_t err_code = nrf_drv_uart_init(&m_uart, &config, async_mode ? uart_evt_handler : NULL); + APP_ERROR_CHECK(err_code); + + m_async_mode = async_mode; +} + +void nrf_log_backend_uart_init(void) +{ + bool async_mode = NRF_LOG_DEFERRED ? true : false; + uart_init(async_mode); +} + +static void serial_tx(void const * p_context, char const * p_buffer, size_t len) +{ + uint8_t len8 = (uint8_t)(len & 0x000000FF); + m_xfer_done = false; + ret_code_t err_code = nrf_drv_uart_tx(&m_uart, (uint8_t *)p_buffer, len8); + APP_ERROR_CHECK(err_code); + /* wait for completion since buffer is reused*/ + while (m_async_mode && (m_xfer_done == false)) + { + + } + +} + +static void nrf_log_backend_uart_put(nrf_log_backend_t const * p_backend, + nrf_log_entry_t * p_msg) +{ + nrf_log_backend_serial_put(p_backend, p_msg, m_string_buff, + NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE, serial_tx); +} + +static void nrf_log_backend_uart_flush(nrf_log_backend_t const * p_backend) +{ + +} + +static void nrf_log_backend_uart_panic_set(nrf_log_backend_t const * p_backend) +{ + nrf_drv_uart_uninit(&m_uart); + + uart_init(false); +} + +const nrf_log_backend_api_t nrf_log_backend_uart_api = { + .put = nrf_log_backend_uart_put, + .flush = nrf_log_backend_uart_flush, + .panic_set = nrf_log_backend_uart_panic_set, +}; +#endif //NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_UART) diff --git a/libraries/nfc/src/util/nrf_log_backend_uart.h b/libraries/nfc/src/util/nrf_log_backend_uart.h new file mode 100644 index 000000000..20efa33bf --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_backend_uart.h @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + /**@file + * + * @defgroup nrf_log_backend_uart Log UART backend + * @{ + * @ingroup nrf_log + * @brief Log UART backend. + */ + +#ifndef NRF_LOG_BACKEND_UART_H +#define NRF_LOG_BACKEND_UART_H + +#include "nrf_log_backend_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const nrf_log_backend_api_t nrf_log_backend_uart_api; + +typedef struct { + nrf_log_backend_t backend; +} nrf_log_backend_uart_t; + +#define NRF_LOG_BACKEND_UART_DEF(_name) \ + NRF_LOG_BACKEND_DEF(_name, nrf_log_backend_uart_api, NULL) + +void nrf_log_backend_uart_init(void); + +#ifdef __cplusplus +} +#endif + +#endif //NRF_LOG_BACKEND_UART_H + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_log_ctrl.h b/libraries/nfc/src/util/nrf_log_ctrl.h new file mode 100644 index 000000000..1ff117ca9 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_ctrl.h @@ -0,0 +1,243 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_CTRL_H +#define NRF_LOG_CTRL_H + +/**@file + * @addtogroup nrf_log Logger module + * @ingroup app_common + * + * @defgroup nrf_log_ctrl Functions for controlling nrf_log + * @{ + * @ingroup nrf_log + * @brief The nrf_log control interface. + */ + +#include "sdk_config.h" +#include "sdk_errors.h" +#include +#include +#include "nrf_log_types.h" +#include "nrf_log_ctrl_internal.h" +#include "nrf_log_backend_interface.h" +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Timestamp function prototype. + * + * @return Timestamp value. + */ +typedef uint32_t (*nrf_log_timestamp_func_t)(void); + + +/**@brief Macro for initializing the logs. + * + * Macro has one or two parameters. First parameter (obligatory) is the timestamp function (@ref nrf_log_timestamp_func_t). + * Additionally, as the second parameter timestamp frequency in Hz can be provided. If not provided then default + * frequency is used (@ref NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY). Frequency is used to format timestamp prefix if + * @ref NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED is set. + * + * @return NRF_SUCCESS after successful initialization, otherwise an error code. + */ +#define NRF_LOG_INIT(...) NRF_LOG_INTERNAL_INIT(__VA_ARGS__) + + +/**@brief Macro for processing a single log entry from a queue of deferred logs. + * + * You can call this macro from the main context or from the error handler to process + * log entries one by one. + * + * @note If logs are not deferred, this call has no use and is defined as 'false'. + * + * @retval true There are more logs to process in the buffer. + * @retval false No more logs in the buffer. + */ +#define NRF_LOG_PROCESS() NRF_LOG_INTERNAL_PROCESS() + +/** @brief Macro for processing all log entries from the buffer. + * It blocks until all buffered entries are processed by the backend. + * + * @note If logs are not deferred, this call has no use and is empty. + */ +#define NRF_LOG_FLUSH() NRF_LOG_INTERNAL_FLUSH() + +/** @brief Macro for flushing log data before reset. + * + * @note If logs are not deferred, this call has no use and is empty. + * + * @note If RTT is used, then a breakpoint is hit once flushed. + */ +#define NRF_LOG_FINAL_FLUSH() NRF_LOG_INTERNAL_FINAL_FLUSH() + +/** + * @brief Function for initializing the frontend and the default backend. + * + * @ref NRF_LOG_INIT calls this function to initialize the frontend and the backend. + * If custom backend is used, then @ref NRF_LOG_INIT should not be called. + * Instead, frontend and user backend should be verbosely initialized. + * + * @param timestamp_func Function for getting a 32-bit timestamp. + * @param timestamp_freq Frequency of the timestamp. + * + * @return Error status. + * + */ +ret_code_t nrf_log_init(nrf_log_timestamp_func_t timestamp_func, uint32_t timestamp_freq); + +/** + * @brief Function for adding new backend interface to the logger. + * + * @param p_backend Pointer to the backend interface. + * @param severity Initial value of severity level for each module forwarded to the backend. This + * option is only applicable if @ref NRF_LOG_FILTERS_ENABLED is set. + * @return -1 if backend cannot be added or positive number (backend ID). + */ +int32_t nrf_log_backend_add(nrf_log_backend_t const * p_backend, nrf_log_severity_t severity); + +/** + * @brief Function for removing backend from the logger. + * + * @param p_backend Pointer to the backend interface. + * + */ +void nrf_log_backend_remove(nrf_log_backend_t const * p_backend); + +/** + * @brief Function for setting logger backends into panic mode. + * + * When this function is called all attached backends are informed about panic state of the system. + * It is up to the backend to react properly (hold or process logs in blocking mode, etc.) + */ +void nrf_log_panic(void); + +/** + * @brief Function for handling a single log entry. + * + * Use this function only if the logs are buffered. It takes a single entry from the + * buffer and attempts to process it. + * + * @retval true If there are more entries to process. + * @retval false If there are no more entries to process. + */ +bool nrf_log_frontend_dequeue(void); + +/** + * @brief Function for getting number of independent log modules registered into the logger. + * + * @return Number of registered modules. + */ +uint32_t nrf_log_module_cnt_get(void); + +/** + * @brief Function for getting module name. + * + * @param module_id Module ID. + * @param is_ordered_idx Module ID is given is index in alphabetically sorted list of modules. + * @return Pointer to string with module name. + */ +const char * nrf_log_module_name_get(uint32_t module_id, bool is_ordered_idx); + +/** + * @brief Function for getting coloring of specific logs. + * + * @param module_id Module ID. + * @param severity Log severity. + * + * @return ID of the color. + */ +uint8_t nrf_log_color_id_get(uint32_t module_id, nrf_log_severity_t severity); + +/** + * @brief Function for configuring filtering ofs logs in the module. + * + * Filtering of logs in modules is independent for each backend. + * + * @param backend_id Backend ID which want to chenge its configuration. + * @param module_id Module ID which logs will be reconfigured. + * @param severity New severity filter. + */ +void nrf_log_module_filter_set(uint32_t backend_id, + uint32_t module_id, + nrf_log_severity_t severity); + +/** + * @brief Function for getting module severity level. + * + * @param backend_id Backend ID. + * @param module_id Module ID. + * @param is_ordered_idx Module ID is given is index in alphabetically sorted list of modules. + * @param dynamic It true current filter for given backend is returned. If false then + * compiled-in level is returned (maximum available). If this parameter is + * false then backend_id parameter is not used. + * + * @return Severity. + */ +nrf_log_severity_t nrf_log_module_filter_get(uint32_t backend_id, + uint32_t module_id, + bool is_ordered_idx, + bool dynamic); + +/** + * @brief Function stores current filtering configuration into non-volatile memory using @ref fds module. + * + * @return NRF_SUCCESS or @ref fds error code. + */ +ret_code_t nrf_log_config_store(void); + +/** + * @brief Function loads configuration from non-volatile memory using @ref fds module. + * + * @retval NRF_SUCCESS On successful loading. + * @retval NRF_ERROR_NOT_FOUND Configuration file not found. + * @retval NRF_ERROR_INTERNAL Other @ref fds error on reading configuration file. + */ +ret_code_t nrf_log_config_load(void); + +#ifdef __cplusplus +} +#endif + +#endif // NRF_LOG_CTRL_H + +/** + *@} + **/ diff --git a/libraries/nfc/src/util/nrf_log_ctrl_internal.h b/libraries/nfc/src/util/nrf_log_ctrl_internal.h new file mode 100644 index 000000000..90c4cd18b --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_ctrl_internal.h @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_CTRL_INTERNAL_H +#define NRF_LOG_CTRL_INTERNAL_H +/** + * @cond (NODOX) + * @defgroup nrf_log_ctrl_internal Auxiliary internal types declarations + * @{ + * @internal + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_LOG) + +#define NRF_LOG_LFCLK_FREQ 32768 + +#ifdef APP_TIMER_CONFIG_RTC_FREQUENCY +#define LOG_TIMESTAMP_DEFAULT_FREQUENCY ((NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY == 0) ? \ + (NRF_LOG_LFCLK_FREQ/(APP_TIMER_CONFIG_RTC_FREQUENCY + 1)) : \ + NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY) +#else +#define LOG_TIMESTAMP_DEFAULT_FREQUENCY NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY +#endif + +#define NRF_LOG_INTERNAL_INIT(...) \ + nrf_log_init(GET_VA_ARG_1(__VA_ARGS__), \ + GET_VA_ARG_1(GET_ARGS_AFTER_1(__VA_ARGS__, LOG_TIMESTAMP_DEFAULT_FREQUENCY))) + +#define NRF_LOG_INTERNAL_PROCESS() nrf_log_frontend_dequeue() +#define NRF_LOG_INTERNAL_FLUSH() \ + do { \ + while (NRF_LOG_INTERNAL_PROCESS()); \ + } while (0) + +#define NRF_LOG_INTERNAL_FINAL_FLUSH() \ + do { \ + nrf_log_panic(); \ + NRF_LOG_INTERNAL_FLUSH(); \ + } while (0) + + +#else // NRF_MODULE_ENABLED(NRF_LOG) +#define NRF_LOG_INTERNAL_PROCESS() false +#define NRF_LOG_INTERNAL_FLUSH() +#define NRF_LOG_INTERNAL_INIT(...) NRF_SUCCESS +#define NRF_LOG_INTERNAL_HANDLERS_SET(default_handler, bytes_handler) \ + UNUSED_PARAMETER(default_handler); UNUSED_PARAMETER(bytes_handler) +#define NRF_LOG_INTERNAL_FINAL_FLUSH() +#endif // NRF_MODULE_ENABLED(NRF_LOG) + +/** @} + * @endcond + */ +#endif // NRF_LOG_CTRL_INTERNAL_H diff --git a/libraries/nfc/src/util/nrf_log_default_backends.c b/libraries/nfc/src/util/nrf_log_default_backends.c new file mode 100644 index 000000000..a5634b503 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_default_backends.c @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_LOG) +#include "nrf_log_default_backends.h" +#include "nrf_log_ctrl.h" +#include "nrf_log_internal.h" +#include "nrf_assert.h" + +#if defined(NRF_LOG_BACKEND_RTT_ENABLED) && NRF_LOG_BACKEND_RTT_ENABLED +#include "nrf_log_backend_rtt.h" +NRF_LOG_BACKEND_RTT_DEF(rtt_log_backend); +#endif + +#if defined(NRF_LOG_BACKEND_UART_ENABLED) && NRF_LOG_BACKEND_UART_ENABLED +#include "nrf_log_backend_uart.h" +NRF_LOG_BACKEND_UART_DEF(uart_log_backend); +#endif + +void nrf_log_default_backends_init(void) +{ + int32_t backend_id = -1; + (void)backend_id; +#if defined(NRF_LOG_BACKEND_RTT_ENABLED) && NRF_LOG_BACKEND_RTT_ENABLED + nrf_log_backend_rtt_init(); + backend_id = nrf_log_backend_add(&rtt_log_backend, NRF_LOG_SEVERITY_DEBUG); + ASSERT(backend_id >= 0); + nrf_log_backend_enable(&rtt_log_backend); +#endif + +#if defined(NRF_LOG_BACKEND_UART_ENABLED) && NRF_LOG_BACKEND_UART_ENABLED + nrf_log_backend_uart_init(); + backend_id = nrf_log_backend_add(&uart_log_backend, NRF_LOG_SEVERITY_DEBUG); + ASSERT(backend_id >= 0); + nrf_log_backend_enable(&uart_log_backend); +#endif +} +#endif diff --git a/libraries/nfc/src/util/nrf_log_default_backends.h b/libraries/nfc/src/util/nrf_log_default_backends.h new file mode 100644 index 000000000..055205f1f --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_default_backends.h @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_DEFAULT_BACKENDS_H__ +#define NRF_LOG_DEFAULT_BACKENDS_H__ + +/**@file + * @addtogroup nrf_log Logger module + * @ingroup app_common + * + * @defgroup nrf_log_default_backends Functions for initializing and adding default backends + * @{ + * @ingroup nrf_log + * @brief The nrf_log default backends. + */ + +#include "sdk_config.h" +#include "sdk_errors.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @def NRF_LOG_DEFAULT_BACKENDS_INIT + * @brief Macro for initializing default backends. + * + * Each backend enabled in configuration is initialized and added as a backend to the logger. + */ +#if NRF_LOG_ENABLED +#define NRF_LOG_DEFAULT_BACKENDS_INIT() nrf_log_default_backends_init() +#else +#define NRF_LOG_DEFAULT_BACKENDS_INIT() +#endif + +void nrf_log_default_backends_init(void); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif // NRF_LOG_DEFAULT_BACKENDS_H__ diff --git a/libraries/nfc/src/util/nrf_log_frontend.c b/libraries/nfc/src/util/nrf_log_frontend.c new file mode 100644 index 000000000..698bb5db3 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_frontend.c @@ -0,0 +1,1583 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_LOG) +#include "app_util.h" +#include "app_util_platform.h" +#include "nrf_log.h" +#include "nrf_log_internal.h" +#include "nrf_log_ctrl.h" +#include "nrf_log_backend_interface.h" +#include "nrf_log_str_formatter.h" +#include "nrf_section.h" +#include "nrf_ringbuf.h" +#include "nrf_memobj.h" +#include "nrf_atomic.h" +#include + +STATIC_ASSERT((NRF_LOG_BUFSIZE % 4) == 0); +STATIC_ASSERT(IS_POWER_OF_TWO(NRF_LOG_BUFSIZE)); + +#define NRF_LOG_BUF_WORDS (NRF_LOG_BUFSIZE/4) + +#if NRF_MODULE_ENABLED(FDS) && NRF_LOG_FILTERS_ENABLED +#define LOG_CONFIG_LOAD_STORE_ENABLED 1 +#else +#define LOG_CONFIG_LOAD_STORE_ENABLED 0 +#endif + +#if NRF_LOG_BUF_WORDS < 32 +#warning "NRF_LOG_BUFSIZE too small, significant number of logs may be lost." +#endif + +NRF_MEMOBJ_POOL_DEF(log_mempool, NRF_LOG_MSGPOOL_ELEMENT_SIZE, NRF_LOG_MSGPOOL_ELEMENT_COUNT); +NRF_RINGBUF_DEF(m_log_push_ringbuf, NRF_LOG_STR_PUSH_BUFFER_SIZE); + +#define NRF_LOG_BACKENDS_FULL 0xFF +#define NRF_LOG_FILTER_BITS_PER_BACKEND 3 +#define NRF_LOG_MAX_BACKENDS (32/NRF_LOG_FILTER_BITS_PER_BACKEND) +#define NRF_LOG_MAX_HEXDUMP (NRF_LOG_MSGPOOL_ELEMENT_SIZE*NRF_LOG_MSGPOOL_ELEMENT_COUNT/2) +#define NRF_LOG_INVALID_BACKEND_U32 0xFFFFFFFF + +/* Mask is extracted from the structure log_data_t to allow compile time initialization which + * is needed to allow usage of the logger (logging) before logger is initialized. + * It cannot be part of the log_data_t because some compilers would put whole log_data_t structure + * into flash (including buffer) just because one field is initilized. + */ +static uint32_t m_buffer_mask = NRF_LOG_BUF_WORDS - 1; // Size of buffer (must be power of 2) presented as mask + +/** + * brief An internal control block of the logger + * + * @note Circular buffer is using never cleared indexes and a mask. It means + * that logger may break when indexes overflows. However, it is quite unlikely. + * With rate of 1000 log entries with 2 parameters per second such situation + * would happen after 12 days. + */ +typedef struct +{ + bool autoflush; + uint32_t wr_idx; // Current write index (never reset) + uint32_t rd_idx; // Current read index (never_reset) + uint32_t buffer[NRF_LOG_BUF_WORDS]; + nrf_log_timestamp_func_t timestamp_func; // A pointer to function that returns timestamp + nrf_log_backend_t const * p_backend_head; + nrf_atomic_flag_t log_skipping; + nrf_atomic_flag_t log_skipped; + nrf_atomic_u32_t log_dropped_cnt; +} log_data_t; + +static log_data_t m_log_data; + +NRF_LOG_MODULE_REGISTER(); + +// Helper macros for section variables. +#define NRF_LOG_DYNAMIC_SECTION_VARS_GET(i) NRF_SECTION_ITEM_GET(log_dynamic_data, nrf_log_module_dynamic_data_t, (i)) +#define NRF_LOG_FILTER_SECTION_VARS_GET(i) NRF_SECTION_ITEM_GET(log_filter_data, nrf_log_module_filter_data_t, (i)) + +#define NRF_LOG_CONST_SECTION_VARS_GET(i) NRF_SECTION_ITEM_GET(log_const_data, nrf_log_module_const_data_t, (i)) +#define NRF_LOG_CONST_SECTION_VARS_COUNT NRF_SECTION_ITEM_COUNT(log_const_data, nrf_log_module_const_data_t) + +ret_code_t nrf_log_init(nrf_log_timestamp_func_t timestamp_func, uint32_t timestamp_freq) +{ + (void)NRF_LOG_ITEM_DATA_CONST(app); + + if (NRF_LOG_USES_TIMESTAMP && (timestamp_func == NULL)) + { + return NRF_ERROR_INVALID_PARAM; + } + + m_log_data.autoflush = NRF_LOG_DEFERRED ? false : true; + + if (NRF_LOG_USES_TIMESTAMP) + { + nrf_log_str_formatter_timestamp_freq_set(timestamp_freq); + m_log_data.timestamp_func = timestamp_func; + } + +#ifdef UNIT_TEST + m_buffer_mask = NRF_LOG_BUF_WORDS - 1; +#endif + + ret_code_t err_code = nrf_memobj_pool_init(&log_mempool); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + nrf_ringbuf_init(&m_log_push_ringbuf); + + uint32_t modules_cnt = NRF_LOG_CONST_SECTION_VARS_COUNT; + uint32_t i; + if (NRF_LOG_FILTERS_ENABLED) + { + uint32_t j; + //sort modules by name + for (i = 0; i < modules_cnt; i++) + { + uint32_t idx = 0; + + for (j = 0; j < modules_cnt; j++) + { + if (i != j) + { + char const * p_name0 = NRF_LOG_CONST_SECTION_VARS_GET(i)->p_module_name; + char const * p_name1 = NRF_LOG_CONST_SECTION_VARS_GET(j)->p_module_name; + if (strncmp(p_name0, p_name1, 20) > 0) + { + idx++; + } + } + + } + nrf_log_module_dynamic_data_t * p_module_ddata = NRF_LOG_DYNAMIC_SECTION_VARS_GET(i); + p_module_ddata->order_idx = idx; + } + + /* Initialize filters */ + for (i = 0; i < modules_cnt; i++) + { + nrf_log_module_dynamic_data_t * p_module_ddata = NRF_LOG_DYNAMIC_SECTION_VARS_GET(i); + nrf_log_module_filter_data_t * p_module_filter = NRF_LOG_FILTER_SECTION_VARS_GET(i); + p_module_ddata->filter = 0; + p_module_filter->filter_lvls = 0; + } + } + + return NRF_SUCCESS; +} + +uint32_t nrf_log_module_cnt_get(void) +{ + return NRF_LOG_CONST_SECTION_VARS_COUNT; +} + +static ret_code_t module_idx_get(uint32_t * p_idx, bool ordered_idx) +{ + if (ordered_idx) + { + uint32_t module_cnt = nrf_log_module_cnt_get(); + uint32_t i; + for (i = 0; i < module_cnt; i++) + { + nrf_log_module_dynamic_data_t * p_module_data = NRF_LOG_DYNAMIC_SECTION_VARS_GET(i); + if (p_module_data->order_idx == *p_idx) + { + *p_idx = i; + return NRF_SUCCESS; + } + } + return NRF_ERROR_NOT_FOUND; + } + else + { + return NRF_SUCCESS; + } +} +const char * nrf_log_module_name_get(uint32_t module_id, bool ordered_idx) +{ + if (module_idx_get(&module_id, ordered_idx) == NRF_SUCCESS) + { + nrf_log_module_const_data_t * p_module_data = NRF_LOG_CONST_SECTION_VARS_GET(module_id); + return p_module_data->p_module_name; + } + else + { + return NULL; + } +} + +uint8_t nrf_log_color_id_get(uint32_t module_id, nrf_log_severity_t severity) +{ + nrf_log_module_const_data_t * p_module_data = NRF_LOG_CONST_SECTION_VARS_GET(module_id); + uint8_t color_id; + switch (severity) + { + case NRF_LOG_SEVERITY_ERROR: + color_id = NRF_LOG_ERROR_COLOR; + break; + case NRF_LOG_SEVERITY_WARNING: + color_id = NRF_LOG_WARNING_COLOR; + break; + case NRF_LOG_SEVERITY_INFO: + color_id = p_module_data->info_color_id; + break; + case NRF_LOG_SEVERITY_DEBUG: + color_id = p_module_data->debug_color_id; + break; + default: + color_id = 0; + break; + } + return color_id; +} + +static uint16_t higher_lvl_get(uint32_t lvls) +{ + uint16_t top_lvl = 0; + uint16_t tmp_lvl; + uint32_t i; + + //Find highest level enabled by backends + for (i = 0; i < (32/NRF_LOG_LEVEL_BITS); i+=NRF_LOG_LEVEL_BITS) + { + tmp_lvl = (uint16_t)BF_GET(lvls,NRF_LOG_LEVEL_BITS, i); + if (tmp_lvl > top_lvl) + { + top_lvl = tmp_lvl; + } + } + return top_lvl; +} + +void nrf_log_module_filter_set(uint32_t backend_id, uint32_t module_id, nrf_log_severity_t severity) +{ + if (NRF_LOG_FILTERS_ENABLED) + { + nrf_log_module_dynamic_data_t * p_module_data = NRF_LOG_DYNAMIC_SECTION_VARS_GET(module_id); + nrf_log_module_filter_data_t * p_filter = NRF_LOG_FILTER_SECTION_VARS_GET(module_id); + + p_filter->filter_lvls &= ~BF_MASK(NRF_LOG_LEVEL_BITS, (NRF_LOG_LEVEL_BITS * backend_id)); + p_filter->filter_lvls |= BF_VAL(severity, NRF_LOG_LEVEL_BITS, (NRF_LOG_LEVEL_BITS * backend_id)); + + p_module_data->filter = higher_lvl_get(p_filter->filter_lvls); + } +} + +static nrf_log_severity_t nrf_log_module_init_filter_get(uint32_t module_id) +{ + nrf_log_module_const_data_t * p_module_data = + NRF_LOG_CONST_SECTION_VARS_GET(module_id); + return NRF_LOG_FILTERS_ENABLED ? p_module_data->initial_lvl : p_module_data->compiled_lvl; +} + +nrf_log_severity_t nrf_log_module_filter_get(uint32_t backend_id, + uint32_t module_id, + bool ordered_idx, + bool dynamic) +{ + nrf_log_severity_t severity = NRF_LOG_SEVERITY_NONE; + if (NRF_LOG_FILTERS_ENABLED && dynamic) + { + if (module_idx_get(&module_id, ordered_idx) == NRF_SUCCESS) + { + nrf_log_module_filter_data_t * p_filter = NRF_LOG_FILTER_SECTION_VARS_GET(module_id); + severity = (nrf_log_severity_t)BF_GET(p_filter->filter_lvls, + NRF_LOG_LEVEL_BITS, + (backend_id*NRF_LOG_LEVEL_BITS)); + } + } + else if (!dynamic) + { + if (module_idx_get(&module_id, ordered_idx) == NRF_SUCCESS) + { + nrf_log_module_const_data_t * p_module_data = + NRF_LOG_CONST_SECTION_VARS_GET(module_id); + severity = (nrf_log_severity_t)p_module_data->compiled_lvl; + } + } + return severity; +} +/** + * Function examines current header and omits packets which are in progress. + */ +static bool invalid_packets_omit(nrf_log_header_t const * p_header, uint32_t * p_rd_idx) +{ + bool ret = false; + if (p_header->base.generic.in_progress == 1) + { + switch (p_header->base.generic.type) + { + case HEADER_TYPE_STD: + *p_rd_idx += (HEADER_SIZE + p_header->base.std.nargs); + break; + case HEADER_TYPE_HEXDUMP: + *p_rd_idx += (HEADER_SIZE + p_header->base.hexdump.len); + break; + default: + break; + } + ret = true; + } + return ret; +} +/** + * @brief Skips the oldest, not processed logs to make space for new logs. + * @details This function moves forward read index to prepare space for new logs. + */ + +static uint32_t log_skip(void) +{ + uint16_t dropped = 0; + + (void)nrf_atomic_flag_set(&m_log_data.log_skipped); + (void)nrf_atomic_flag_set(&m_log_data.log_skipping); + + uint32_t rd_idx = m_log_data.rd_idx; + uint32_t mask = m_buffer_mask; + nrf_log_header_t * p_header = (nrf_log_header_t *)&m_log_data.buffer[rd_idx & mask]; + nrf_log_header_t header; + + // Skip packets that may be invalid (interrupted while being in progress) + do { + if (invalid_packets_omit(p_header, &rd_idx)) + { + //something was omitted. Point to new header and try again. + p_header = (nrf_log_header_t *)&m_log_data.buffer[rd_idx & mask]; + } + else + { + break; + } + } while (true); + + uint32_t i; + for (i = 0; i < HEADER_SIZE; i++) + { + ((uint32_t*)&header)[i] = m_log_data.buffer[rd_idx++ & mask]; + } + + switch (header.base.generic.type) + { + case HEADER_TYPE_HEXDUMP: + dropped = header.dropped; + rd_idx += CEIL_DIV(header.base.hexdump.len, sizeof(uint32_t)); + break; + case HEADER_TYPE_STD: + dropped = header.dropped; + rd_idx += header.base.std.nargs; + break; + default: + ASSERT(false); + break; + } + + uint32_t log_skipping_tmp = nrf_atomic_flag_clear_fetch(&m_log_data.log_skipping); + //update read index only if log_skip was not interrupted by another log skip + if (log_skipping_tmp) + { + m_log_data.rd_idx = rd_idx; + } + + return (uint32_t)dropped; +} + +/** + * @brief Function for getting number of dropped logs. Dropped counter is reset after reading. + * + * @return Number of dropped logs saturated to 16 bits. + */ +static inline uint32_t dropped_sat16_get(void) +{ + uint32_t dropped = nrf_atomic_u32_fetch_store(&m_log_data.log_dropped_cnt, 0); + return __USAT(dropped, 16); //Saturate to 16 bits +} + + +static inline void std_header_set(uint32_t severity_mid, + char const * const p_str, + uint32_t nargs, + uint32_t wr_idx, + uint32_t mask) +{ + + + //Prepare header - in reverse order to ensure that packet type is validated (set to STD as last action) + uint32_t module_id = severity_mid >> NRF_LOG_MODULE_ID_POS; + uint32_t dropped = dropped_sat16_get(); + ASSERT(module_id < nrf_log_module_cnt_get()); + m_log_data.buffer[(wr_idx + 1) & mask] = module_id | (dropped << 16); + + if (NRF_LOG_USES_TIMESTAMP) + { + m_log_data.buffer[(wr_idx + 2) & mask] = m_log_data.timestamp_func(); + } + + nrf_log_header_t * p_header = (nrf_log_header_t *)&m_log_data.buffer[wr_idx & mask]; + p_header->base.std.severity = severity_mid & NRF_LOG_LEVEL_MASK; + p_header->base.std.nargs = nargs; + p_header->base.std.addr = ((uint32_t)(p_str) & STD_ADDR_MASK); + p_header->base.std.type = HEADER_TYPE_STD; + p_header->base.std.in_progress = 0; +} + +/** + * @brief Allocates chunk in a buffer for one entry and injects overflow if + * there is no room for requested entry. + * + * @param content_len Number of 32bit arguments. In case of allocating for hex dump it + * is the size of the buffer in 32bit words (ceiled). + * @param p_wr_idx Pointer to write index. + * + * @return True if successful allocation, false otherwise. + * + */ +static inline bool buf_prealloc(uint32_t content_len, uint32_t * p_wr_idx, bool std) +{ + uint32_t req_len = content_len + HEADER_SIZE; + bool ret = true; + CRITICAL_REGION_ENTER(); + *p_wr_idx = m_log_data.wr_idx; + uint32_t available_words = (m_buffer_mask + 1) - (m_log_data.wr_idx - m_log_data.rd_idx); + while (req_len > available_words) + { + UNUSED_RETURN_VALUE(nrf_atomic_u32_add(&m_log_data.log_dropped_cnt, 1)); + if (NRF_LOG_ALLOW_OVERFLOW) + { + uint32_t dropped_in_skip = log_skip(); + UNUSED_RETURN_VALUE(nrf_atomic_u32_add(&m_log_data.log_dropped_cnt, dropped_in_skip)); + available_words = (m_buffer_mask + 1) - (m_log_data.wr_idx - m_log_data.rd_idx); + } + else + { + ret = false; + break; + } + } + + if (ret) + { + nrf_log_main_header_t invalid_header; + invalid_header.raw = 0; + + if (std) + { + invalid_header.std.type = HEADER_TYPE_STD; + invalid_header.std.in_progress = 1; + invalid_header.std.nargs = content_len; + } + else + { + invalid_header.hexdump.type = HEADER_TYPE_HEXDUMP; + invalid_header.hexdump.in_progress = 1; + invalid_header.hexdump.len = content_len; + } + + nrf_log_main_header_t * p_header = + (nrf_log_main_header_t *)&m_log_data.buffer[m_log_data.wr_idx & m_buffer_mask]; + + p_header->raw = invalid_header.raw; + + m_log_data.wr_idx += req_len; + } + + CRITICAL_REGION_EXIT(); + return ret; +} + +char const * nrf_log_push(char * const p_str) +{ + if ((m_log_data.autoflush) || (p_str == NULL)) + { + return p_str; + } + + size_t ssize = strlen(p_str) + 1; // + 1 for null termination + uint8_t * p_dst; + // Allocate space in the ring buffer. It may be smaller than the requested string in case of buffer wrapping or when the ring buffer size is too small. + // Once the string is copied into the buffer, the space is immediately freed. The string is kept in the buffer but can be overwritten. + // It is done that way because there is no other place where space could be freed since string processing happens in + // the logger backends, often by modules which are generic and not aware of internals of the logger. + if (nrf_ringbuf_alloc(&m_log_push_ringbuf, &p_dst, &ssize, true) == NRF_SUCCESS) + { + ret_code_t err; + + memcpy(p_dst, p_str, ssize); + + //Terminate in case string was partial. + p_dst[ssize - 1] = '\0'; + + err = nrf_ringbuf_put(&m_log_push_ringbuf, ssize); + ASSERT(err == NRF_SUCCESS); + + //Immediately free the space where string was put. + err = nrf_ringbuf_free(&m_log_push_ringbuf, ssize); + ASSERT(err == NRF_SUCCESS); + + return (char const *)p_dst; + } + else + { + return NULL; + } +} + +static inline void std_n(uint32_t severity_mid, + char const * const p_str, + uint32_t const * args, + uint32_t nargs) +{ + uint32_t mask = m_buffer_mask; + uint32_t wr_idx; + + if (buf_prealloc(nargs, &wr_idx, true)) + { + // Proceed only if buffer was successfully preallocated. + + uint32_t data_idx = wr_idx + HEADER_SIZE; + uint32_t i; + for (i = 0; i < nargs; i++) + { + m_log_data.buffer[data_idx++ & mask] =args[i]; + } + std_header_set(severity_mid, p_str, nargs, wr_idx, mask); + } + if (m_log_data.autoflush) + { +#if NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED + CRITICAL_REGION_ENTER(); +#endif // NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED + + NRF_LOG_FLUSH(); + +#if NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED + CRITICAL_REGION_EXIT(); +#endif // NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED + } + +} + +void nrf_log_frontend_std_0(uint32_t severity_mid, char const * const p_str) +{ + std_n(severity_mid, p_str, NULL, 0); +} + + +void nrf_log_frontend_std_1(uint32_t severity_mid, + char const * const p_str, + uint32_t val0) +{ + uint32_t args[] = {val0}; + std_n(severity_mid, p_str, args, ARRAY_SIZE(args)); +} + + +void nrf_log_frontend_std_2(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1) +{ + uint32_t args[] = {val0, val1}; + std_n(severity_mid, p_str, args, ARRAY_SIZE(args)); +} + + +void nrf_log_frontend_std_3(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1, + uint32_t val2) +{ + uint32_t args[] = {val0, val1, val2}; + std_n(severity_mid, p_str, args, ARRAY_SIZE(args)); +} + + +void nrf_log_frontend_std_4(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1, + uint32_t val2, + uint32_t val3) +{ + uint32_t args[] = {val0, val1, val2, val3}; + std_n(severity_mid, p_str, args, ARRAY_SIZE(args)); +} + + +void nrf_log_frontend_std_5(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1, + uint32_t val2, + uint32_t val3, + uint32_t val4) +{ + uint32_t args[] = {val0, val1, val2, val3, val4}; + std_n(severity_mid, p_str, args, ARRAY_SIZE(args)); +} + + +void nrf_log_frontend_std_6(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1, + uint32_t val2, + uint32_t val3, + uint32_t val4, + uint32_t val5) +{ + uint32_t args[] = {val0, val1, val2, val3, val4, val5}; + std_n(severity_mid, p_str, args, ARRAY_SIZE(args)); +} + + +void nrf_log_frontend_hexdump(uint32_t severity_mid, + const void * const p_data, + uint16_t length) +{ + uint32_t mask = m_buffer_mask; + + uint32_t wr_idx; + if (buf_prealloc(CEIL_DIV(length, sizeof(uint32_t)), &wr_idx, false)) + { + uint32_t header_wr_idx = wr_idx; + wr_idx += HEADER_SIZE; + + uint32_t space0 = sizeof(uint32_t) * (m_buffer_mask + 1 - (wr_idx & mask)); + if (length <= space0) + { + memcpy(&m_log_data.buffer[wr_idx & mask], p_data, length); + } + else + { + memcpy(&m_log_data.buffer[wr_idx & mask], p_data, space0); + memcpy(&m_log_data.buffer[0], &((uint8_t *)p_data)[space0], length - space0); + } + + //Prepare header - in reverse order to ensure that packet type is validated (set to HEXDUMP as last action) + if (NRF_LOG_USES_TIMESTAMP) + { + m_log_data.buffer[(header_wr_idx + 2) & mask] = m_log_data.timestamp_func(); + } + + uint32_t module_id = severity_mid >> NRF_LOG_MODULE_ID_POS; + uint32_t dropped = dropped_sat16_get(); + m_log_data.buffer[(header_wr_idx + 1) & mask] = module_id | (dropped << 16); + //Header prepare + nrf_log_header_t * p_header = (nrf_log_header_t *)&m_log_data.buffer[header_wr_idx & mask]; + p_header->base.hexdump.severity = severity_mid & NRF_LOG_LEVEL_MASK; + p_header->base.hexdump.offset = 0; + p_header->base.hexdump.len = length; + p_header->base.hexdump.type = HEADER_TYPE_HEXDUMP; + p_header->base.hexdump.in_progress = 0; + + + + } + + if (m_log_data.autoflush) + { +#if NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED + CRITICAL_REGION_ENTER(); +#endif // NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED + + NRF_LOG_FLUSH(); + +#if NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED + CRITICAL_REGION_EXIT(); +#endif // NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED + } +} + + +bool buffer_is_empty(void) +{ + return (m_log_data.rd_idx == m_log_data.wr_idx); +} + +bool nrf_log_frontend_dequeue(void) +{ + + if (buffer_is_empty()) + { + return false; + } + m_log_data.log_skipped = 0; + //It has to be ensured that reading rd_idx occurs after skipped flag is cleared. + __DSB(); + uint32_t rd_idx = m_log_data.rd_idx; + uint32_t mask = m_buffer_mask; + nrf_log_header_t * p_header = (nrf_log_header_t *)&m_log_data.buffer[rd_idx & mask]; + nrf_log_header_t header; + nrf_memobj_t * p_msg_buf = NULL; + size_t memobj_offset = 0; + uint32_t severity = 0; + + // Skip any in progress packets. + do { + if (invalid_packets_omit(p_header, &rd_idx) && (m_log_data.log_skipped == 0)) + { + //Check if end of data is not reached. + if (rd_idx >= m_log_data.wr_idx) + { + m_log_data.rd_idx = m_log_data.wr_idx; + return false; + } + //something was omitted. Point to new header and try again. + p_header = (nrf_log_header_t *)&m_log_data.buffer[rd_idx & mask]; + } + else + { + break; + } + } while (true); + + uint32_t i; + for (i = 0; i < HEADER_SIZE; i++) + { + ((uint32_t*)&header)[i] = m_log_data.buffer[rd_idx++ & mask]; + } + + if (header.base.generic.type == HEADER_TYPE_HEXDUMP) + { + uint32_t orig_data_len = header.base.hexdump.len; + uint32_t data_len = MIN(header.base.hexdump.len, NRF_LOG_MAX_HEXDUMP); //limit the data + header.base.hexdump.len = data_len; + uint32_t msg_buf_size8 = sizeof(uint32_t)*HEADER_SIZE + data_len; + severity = header.base.hexdump.severity; + p_msg_buf = nrf_memobj_alloc(&log_mempool, msg_buf_size8); + + if (p_msg_buf) + { + nrf_memobj_get(p_msg_buf); + nrf_memobj_write(p_msg_buf, &header, HEADER_SIZE*sizeof(uint32_t), memobj_offset); + memobj_offset += HEADER_SIZE*sizeof(uint32_t); + + uint32_t space0 = sizeof(uint32_t) * (mask + 1 - (rd_idx & mask)); + if (data_len > space0) + { + uint8_t * ptr0 = space0 ? + (uint8_t *)&m_log_data.buffer[rd_idx & mask] : + (uint8_t *)&m_log_data.buffer[0]; + uint8_t len0 = space0 ? space0 : data_len; + uint8_t * ptr1 = space0 ? + (uint8_t *)&m_log_data.buffer[0] : NULL; + uint8_t len1 = space0 ? data_len - space0 : 0; + + nrf_memobj_write(p_msg_buf, ptr0, len0, memobj_offset); + memobj_offset += len0; + if (ptr1) + { + nrf_memobj_write(p_msg_buf, ptr1, len1, memobj_offset); + } + } + else + { + uint8_t * p_data = (uint8_t *)&m_log_data.buffer[rd_idx & mask]; + nrf_memobj_write(p_msg_buf, p_data, data_len, memobj_offset); + } + rd_idx += CEIL_DIV(orig_data_len, 4); + } + } + else if (header.base.generic.type == HEADER_TYPE_STD) // standard entry + { + header.base.std.nargs = MIN(header.base.std.nargs, NRF_LOG_MAX_NUM_OF_ARGS); + uint32_t msg_buf_size32 = HEADER_SIZE + header.base.std.nargs; + severity = header.base.std.severity; + + p_msg_buf = nrf_memobj_alloc(&log_mempool, msg_buf_size32*sizeof(uint32_t)); + + if (p_msg_buf) + { + nrf_memobj_get(p_msg_buf); + nrf_memobj_write(p_msg_buf, &header, HEADER_SIZE*sizeof(uint32_t), memobj_offset); + memobj_offset += HEADER_SIZE*sizeof(uint32_t); + + for (i = 0; i < header.base.std.nargs; i++) + { + nrf_memobj_write(p_msg_buf, &m_log_data.buffer[rd_idx++ & mask], + sizeof(uint32_t), memobj_offset); + memobj_offset += sizeof(uint32_t); + } + } + } + else + { + //Do nothing. In case of log overflow buffer can contain corrupted data. + } + + if (p_msg_buf) + { + nrf_log_backend_t const * p_backend = m_log_data.p_backend_head; + if (NRF_LOG_ALLOW_OVERFLOW && m_log_data.log_skipped) + { + // Check if any log was skipped during log processing. Do not forward log if skipping + // occured because data may be invalid. + nrf_memobj_put(p_msg_buf); + } + else + { + while (p_backend) + { + bool entry_accepted = false; + if (nrf_log_backend_is_enabled(p_backend) == true) + { + if (NRF_LOG_FILTERS_ENABLED) + { + uint8_t backend_id = nrf_log_backend_id_get(p_backend); + nrf_log_module_filter_data_t * p_module_filter = + NRF_LOG_FILTER_SECTION_VARS_GET(header.module_id); + uint32_t backend_lvl = BF_GET(p_module_filter->filter_lvls, + NRF_LOG_LEVEL_BITS, + (backend_id*NRF_LOG_LEVEL_BITS)); + + //Degrade INFO_RAW level to INFO. + severity = (severity == NRF_LOG_SEVERITY_INFO_RAW) ? + NRF_LOG_SEVERITY_INFO : severity; + if (backend_lvl >= severity) + { + entry_accepted = true; + } + } + else + { + (void)severity; + entry_accepted = true; + } + } + if (entry_accepted) + { + nrf_log_backend_put(p_backend, p_msg_buf); + } + p_backend = p_backend->p_cb->p_next; + } + + nrf_memobj_put(p_msg_buf); + + if (NRF_LOG_ALLOW_OVERFLOW) + { + // Read index can be moved forward only if dequeueing process was not interrupt by + // skipping procedure. If NRF_LOG_ALLOW_OVERFLOW is set then in case of buffer gets full + // and new logger entry occurs, oldest entry is removed. In that case read index is + // changed and updating it here would corrupt the internal circular buffer. + CRITICAL_REGION_ENTER(); + if (m_log_data.log_skipped == 0) + { + m_log_data.rd_idx = rd_idx; + } + CRITICAL_REGION_EXIT(); + } + else + { + m_log_data.rd_idx = rd_idx; + } + } + } + else + { + //Could not allocate memobj - backends are not freeing them on time. + nrf_log_backend_t const * p_backend = m_log_data.p_backend_head; + //Flush all backends + while (p_backend) + { + nrf_log_backend_flush(p_backend); + p_backend = p_backend->p_cb->p_next; + } + NRF_LOG_WARNING("Backends flushed"); + } + + return buffer_is_empty() ? false : true; +} + +static int32_t backend_id_assign(void) +{ + int32_t candidate_id; + nrf_log_backend_t const * p_backend; + bool id_available; + for (candidate_id = 0; candidate_id < NRF_LOG_MAX_BACKENDS; candidate_id++) + { + p_backend = m_log_data.p_backend_head; + id_available = true; + while (p_backend) + { + if (nrf_log_backend_id_get(p_backend) == candidate_id) + { + id_available = false; + break; + } + p_backend = p_backend->p_cb->p_next; + } + if (id_available) + { + return candidate_id; + } + } + return -1; +} + +int32_t nrf_log_backend_add(nrf_log_backend_t const * p_backend, nrf_log_severity_t severity) +{ + int32_t id = backend_id_assign(); + if (id == -1) + { + return id; + } + + nrf_log_backend_id_set(p_backend, id); + //add to list + if (m_log_data.p_backend_head == NULL) + { + m_log_data.p_backend_head = p_backend; + p_backend->p_cb->p_next = NULL; + } + else + { + p_backend->p_cb->p_next = m_log_data.p_backend_head->p_cb->p_next; + m_log_data.p_backend_head->p_cb->p_next = p_backend; + } + + if (NRF_LOG_FILTERS_ENABLED) + { + uint32_t i; + for (i = 0; i < nrf_log_module_cnt_get(); i++) + { + nrf_log_severity_t buildin_lvl = nrf_log_module_init_filter_get(i); + nrf_log_severity_t actual_severity = MIN(buildin_lvl, severity); + nrf_log_module_filter_set(nrf_log_backend_id_get(p_backend), i, actual_severity); + } + } + + return id; +} + +void nrf_log_backend_remove(nrf_log_backend_t const * p_backend) +{ + nrf_log_backend_t const * p_curr = m_log_data.p_backend_head; + nrf_log_backend_t const * p_prev = NULL; + while (p_curr != p_backend) + { + p_prev = p_curr; + p_curr = p_curr->p_cb->p_next; + } + + if (p_prev) + { + p_prev->p_cb->p_next = p_backend->p_cb->p_next; + } + else + { + m_log_data.p_backend_head = NULL; + } + + p_backend->p_cb->id = NRF_LOG_BACKEND_INVALID_ID; +} + +void nrf_log_panic(void) +{ + nrf_log_backend_t const * p_backend = m_log_data.p_backend_head; + m_log_data.autoflush = true; + while (p_backend) + { + nrf_log_backend_enable(p_backend); + nrf_log_backend_panic_set(p_backend); + p_backend = p_backend->p_cb->p_next; + } +} + +#if NRF_MODULE_ENABLED(LOG_CONFIG_LOAD_STORE) +#include "fds.h" +#define LOG_CONFIG_FILE_ID 0x106E +#define LOG_CONFIG_RECORD_ID 0x3427 + +ret_code_t nrf_log_config_store(void) +{ + fds_record_desc_t desc = {0}; + fds_find_token_t token = {0}; + fds_record_t record = { + .file_id = LOG_CONFIG_FILE_ID, + .key = LOG_CONFIG_RECORD_ID, + .data = { + .p_data = NRF_LOG_FILTER_SECTION_VARS_GET(0), + .length_words = NRF_SECTION_LENGTH(log_filter_data)/sizeof(uint32_t) + } + }; + ret_code_t ret = fds_record_find(LOG_CONFIG_FILE_ID, LOG_CONFIG_RECORD_ID, &desc, &token); + if (ret == NRF_SUCCESS) + { + ret = fds_record_update(&desc, &record); + NRF_LOG_INFO("Logger configuration file updated with result:%d", ret); + } + else if (ret == FDS_ERR_NOT_FOUND) + { + ret = fds_record_write(&desc, &record); + NRF_LOG_INFO("Logger configuration file written with result:%d", ret); + } + else + { + ret = NRF_ERROR_INTERNAL; + } + return ret; +} + +ret_code_t nrf_log_config_load(void) +{ + fds_record_desc_t desc = {0}; + fds_find_token_t token = {0}; + + ret_code_t ret = fds_record_find(LOG_CONFIG_FILE_ID, LOG_CONFIG_RECORD_ID, &desc, &token); + if (ret == NRF_SUCCESS) + { + fds_flash_record_t record = {0}; + ret = fds_record_open(&desc, &record); + if (ret == NRF_SUCCESS) + { + void * p_dest = (void *)NRF_LOG_FILTER_SECTION_VARS_GET(0); + uint32_t length = NRF_SECTION_LENGTH(log_filter_data); + memcpy(p_dest, record.p_data, length); + ret = fds_record_close(&desc); + } + } + else if (ret == FDS_ERR_NOT_FOUND) + { + NRF_LOG_WARNING("Logger configuration file not found."); + ret = NRF_ERROR_NOT_FOUND; + } + else + { + ret = NRF_ERROR_INTERNAL; + } + + return ret; +} +#endif //LOG_CONFIG_LOAD_STORE_ENABLED + +#if NRF_LOG_CLI_CMDS && NRF_CLI_ENABLED +#include "nrf_cli.h" + +typedef void (*nrf_log_cli_backend_cmd_t)(nrf_cli_t const * p_cli, + nrf_log_backend_t const * p_backend, + size_t argc, + char * * argv); + +static const char * m_severity_lvls[] = { + "none", + "error", + "warning", + "info", + "debug", +}; + +static const char * m_severity_lvls_sorted[] = { + "debug", + "error", + "info", + "none", + "warning", +}; + +/** + * @brief Function for finding backend instance with given name. + * + * @param p_name Name of the backend instance. + * + * @return Pointer to the instance or NULL. + * + */ +static nrf_log_backend_t const * backend_find(char const * p_name) +{ + size_t num_of_backends; + nrf_log_backend_t const * p_backend; + + num_of_backends = NRF_SECTION_ITEM_COUNT(NRF_LOG_BACKEND_SECTION_NAME, nrf_log_backend_t); + for (size_t i = 0; i < num_of_backends; i++) + { + p_backend = NRF_SECTION_ITEM_GET(NRF_LOG_BACKEND_SECTION_NAME, nrf_log_backend_t, i); + if (strcmp(p_name, p_backend->p_name) == 0) + { + return p_backend; + } + } + return NULL; +} + +/** + * @brief Function for executing command on given backend. + */ +static void nrf_cli_backend_cmd_execute(nrf_cli_t const * p_cli, + size_t argc, + char * * argv, + nrf_log_cli_backend_cmd_t func) +{ + //Based on the structure of backend commands, name of the backend can be found at -1 (log backend command). + char const * p_backend_name = argv[-1]; + + nrf_log_backend_t const * p_backend = backend_find(p_backend_name); + + if (p_backend) + { + func(p_cli, p_backend, argc, argv); + } + else + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Invalid backend: %s\r\n", p_backend_name); + } +} + + +static void log_status(nrf_cli_t const * p_cli, + nrf_log_backend_t const * p_backend, + size_t argc, + char * * argv) +{ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + + uint32_t modules_cnt = nrf_log_module_cnt_get(); + uint32_t i; + + + if (!nrf_log_backend_is_enabled(p_backend)) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Logs are halted!\r\n"); + } + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%-40s | current | built-in \r\n", "module_name"); + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "----------------------------------------------------------\r\n"); + for (i = 0; i < modules_cnt; i++) + { + uint32_t backend_id = p_backend->p_cb->id; + nrf_log_severity_t module_dynamic_lvl = + nrf_log_module_filter_get(backend_id, i, true, true); + nrf_log_severity_t module_compiled_lvl = + nrf_log_module_filter_get(backend_id, i, true, false); + nrf_log_severity_t actual_compiled_lvl = + MIN(module_compiled_lvl, (nrf_log_severity_t)NRF_LOG_DEFAULT_LEVEL); + + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%-40s | %-7s | %s%s\r\n", + nrf_log_module_name_get(i, true), + m_severity_lvls[module_dynamic_lvl], + m_severity_lvls[actual_compiled_lvl], + actual_compiled_lvl < module_compiled_lvl ? "*" : ""); + } +} + + +static void log_self_status(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + log_status(p_cli, p_cli->p_log_backend, argc, argv); +} + + +static void log_backend_status(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + nrf_cli_backend_cmd_execute(p_cli, argc, argv, log_status); +} + + +static bool module_id_get(const char * p_name, uint32_t * p_id) +{ + uint32_t modules_cnt = nrf_log_module_cnt_get(); + const char * p_tmp_name; + uint32_t j; + for (j = 0; j < modules_cnt; j++) + { + p_tmp_name = nrf_log_module_name_get(j, false); + if (strncmp(p_tmp_name, p_name, 32) == 0) + { + *p_id = j; + break; + } + } + return (j != modules_cnt); +} + + +static bool module_id_filter_set(uint32_t backend_id, + uint32_t module_id, + nrf_log_severity_t lvl) +{ + nrf_log_severity_t buildin_lvl = nrf_log_module_filter_get(backend_id, module_id, false, false); + if (lvl > buildin_lvl) + { + return false; + } + else + { + nrf_log_module_filter_set(backend_id, module_id, lvl); + return true; + } +} + + +static void log_ctrl(nrf_cli_t const * p_cli, + nrf_log_backend_t const * p_backend, + size_t argc, + char * * argv) +{ + nrf_log_severity_t lvl; + uint32_t first_m_name_idx; + uint32_t i; + bool all_modules = false; + uint32_t backend_id = p_backend->p_cb->id; + + if (argc > 0) + { + if (strncmp(argv[0], "enable", 7) == 0) + { + if (argc == 1) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Bad parameter count.\r\n"); + return; + } + + if (argc == 2) + { + all_modules = true; + } + + for (i = 0; i < ARRAY_SIZE(m_severity_lvls); i++) + { + if (strncmp(argv[1], m_severity_lvls[i], 10) == 0) + { + break; + } + } + + if (i == ARRAY_SIZE(m_severity_lvls)) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Unknown severity level: %s\r\n", argv[1]); + return; + } + + lvl = (nrf_log_severity_t)i; + first_m_name_idx = 2; + + } + else if (strncmp(argv[0], "disable", 8) == 0) + { + if (argc == 1) + { + all_modules = true; + } + lvl = NRF_LOG_SEVERITY_NONE; + first_m_name_idx = 1; + } + else + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Unknown option: %s\r\n", argv[0]); + return; + } + + if (all_modules) + { + for (i = 0; i < nrf_log_module_cnt_get(); i++) + { + if (module_id_filter_set(backend_id, i, lvl) == false) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, + "Level unavailable for module: %s\r\n", + nrf_log_module_name_get(i, false)); + } + } + } + else + { + for (i = first_m_name_idx; i < argc; i++) + { + uint32_t module_id = 0; + if (module_id_get(argv[i], &module_id) == false) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Unknown module:%s\r\n", argv[i]); + } + else if (module_id_filter_set(backend_id, module_id, lvl) == false) + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, + "Level unavailable for module: %s\r\n", + nrf_log_module_name_get(module_id, false)); + } + else + { + /* empty */ + } + } + } + } +} + + +static void log_self_ctrl(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + log_ctrl(p_cli, p_cli->p_log_backend, argc, argv); +} + + +static void log_backend_ctrl(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + nrf_cli_backend_cmd_execute(p_cli, argc, argv, log_ctrl); +} + +static void module_name_get(size_t idx, nrf_cli_static_entry_t * p_static); + +NRF_CLI_CREATE_DYNAMIC_CMD(m_module_name, module_name_get); + +static void module_name_get(size_t idx, nrf_cli_static_entry_t * p_static) +{ + p_static->handler = NULL; + p_static->p_help = NULL; + p_static->p_subcmd = &m_module_name; + p_static->p_syntax = nrf_log_module_name_get(idx, true); +} + + +static void severity_lvl_get(size_t idx, nrf_cli_static_entry_t * p_static) +{ + p_static->handler = NULL; + p_static->p_help = NULL; + p_static->p_subcmd = &m_module_name; + p_static->p_syntax = (idx < ARRAY_SIZE(m_severity_lvls_sorted)) ? + m_severity_lvls_sorted[idx] : NULL; +} + +NRF_CLI_CREATE_DYNAMIC_CMD(m_severity_lvl, severity_lvl_get); + + +static void log_halt(nrf_cli_t const * p_cli, + nrf_log_backend_t const * p_backend, + size_t argc, + char * * argv) +{ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + + nrf_log_backend_disable(p_backend); +} + + +static void log_backend_halt(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + nrf_cli_backend_cmd_execute(p_cli, argc, argv, log_halt); +} + + +static void log_self_halt(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + log_halt(p_cli, p_cli->p_log_backend, argc, argv); +} + + +static void log_go(nrf_cli_t const * p_cli, + nrf_log_backend_t const * p_backend, + size_t argc, + char * * argv) +{ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + + nrf_log_backend_enable(p_backend); +} + + +static void log_backend_go(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + nrf_cli_backend_cmd_execute(p_cli, argc, argv, log_go); +} + + +static void log_self_go(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + log_go(p_cli, p_cli->p_log_backend, argc, argv); +} + + +static void log_cmd_backends_list(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + size_t num_of_backends; + + num_of_backends = NRF_SECTION_ITEM_COUNT(NRF_LOG_BACKEND_SECTION_NAME, nrf_log_backend_t); + for (size_t i = 0; i < num_of_backends; i++) + { + nrf_log_backend_t const * p_backend = + NRF_SECTION_ITEM_GET(NRF_LOG_BACKEND_SECTION_NAME, nrf_log_backend_t, i); + + if (p_backend->p_cb->id == NRF_LOG_BACKEND_INVALID_ID) + { + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, + "%s\r\n" + "\t- Status: deactivated\r\n\r\n", + p_backend->p_name); + } + else + { + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, + "%s\r\n" + "\t- Status: %s\r\n" + "\t- ID: %d\r\n\r\n", + p_backend->p_name, + p_backend->p_cb->enabled ? "enabled" : "disabled", + p_backend->p_cb->id); + } + } +} + + +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_log_backend) +{ + NRF_CLI_CMD(disable, &m_module_name, + "'log disable .. ' disables logs in specified " + "modules (all if no modules specified).", + log_backend_ctrl), + NRF_CLI_CMD(enable, &m_severity_lvl, + "'log enable ... ' enables logs up to given level in " + "specified modules (all if no modules specified).", + log_backend_ctrl), + NRF_CLI_CMD(go, NULL, "Resume logging", log_backend_go), + NRF_CLI_CMD(halt, NULL, "Halt logging", log_backend_halt), + NRF_CLI_CMD(status, NULL, "Logger status", log_backend_status), + NRF_CLI_SUBCMD_SET_END +}; + +static void backend_name_get(size_t idx, nrf_cli_static_entry_t * p_static) +{ + p_static->handler = NULL; + p_static->p_help = NULL; + p_static->p_subcmd = &m_sub_log_backend; + p_static->p_syntax = NULL; + + nrf_log_backend_t const * p_backend; + size_t active_idx = 0; + uint32_t i; + + for (i = 0; i < NRF_SECTION_ITEM_COUNT(NRF_LOG_BACKEND_SECTION_NAME, nrf_log_backend_t); i++) + { + p_backend = NRF_SECTION_ITEM_GET(NRF_LOG_BACKEND_SECTION_NAME, nrf_log_backend_t, i); + if (p_backend->p_cb->id != NRF_LOG_BACKEND_INVALID_ID) + { + if (idx == active_idx) + { + p_static->p_syntax = p_backend->p_name; + break; + } + else + { + active_idx++; + } + } + } +} + +NRF_CLI_CREATE_DYNAMIC_CMD(m_backend_name_dynamic, backend_name_get); + +static void log_config_load_cmd(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + +#if LOG_CONFIG_LOAD_STORE_ENABLED + if (nrf_log_config_load() == NRF_SUCCESS) + { + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Configuration loaded.\r\n"); + } + else + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Failed to load the configuration.\r\n"); + } +#else + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Not supported.\r\n"); +#endif +} + +static void log_config_store_cmd(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + + if (nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + +#if LOG_CONFIG_LOAD_STORE_ENABLED + if (nrf_log_config_store() == NRF_SUCCESS) + { + nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Configuration stored.\r\n"); + } + else + { + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Failed to store the configuration.\r\n"); + } +#else + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Not supported.\r\n"); +#endif +} + +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_log_config) +{ + NRF_CLI_CMD(load, NULL, "Load configuration stored in non-volatile memory.", log_config_load_cmd), + NRF_CLI_CMD(store, NULL, "Store current configuration in non-volatile memory.", log_config_store_cmd), + NRF_CLI_SUBCMD_SET_END +}; + +NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_log_stat) +{ + NRF_CLI_CMD(backend, &m_backend_name_dynamic, "Logger backends commands.", NULL), + NRF_CLI_CMD(config, &m_sub_log_config, "Manage logger configuration", NULL), + NRF_CLI_CMD(disable, &m_module_name, + "'log disable .. ' disables logs in specified " + "modules (all if no modules specified).", + log_self_ctrl), + NRF_CLI_CMD(enable, &m_severity_lvl, + "'log enable ... ' enables logs up to given level in " + "specified modules (all if no modules specified).", + log_self_ctrl), + NRF_CLI_CMD(go, NULL, "Resume logging", log_self_go), + NRF_CLI_CMD(halt, NULL, "Halt logging", log_self_halt), + NRF_CLI_CMD(list_backends, NULL, "Lists logger backends.", log_cmd_backends_list), + NRF_CLI_CMD(status, NULL, "Logger status", log_self_status), + NRF_CLI_SUBCMD_SET_END +}; + +static void log_cmd(nrf_cli_t const * p_cli, size_t argc, char **argv) +{ + if ((argc == 1) || nrf_cli_help_requested(p_cli)) + { + nrf_cli_help_print(p_cli, NULL, 0); + return; + } + + nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "%s:%s%s\r\n", argv[0], " unknown parameter: ", argv[1]); +} + +NRF_CLI_CMD_REGISTER(log, &m_sub_log_stat, "Commands for controlling logger", log_cmd); + +#endif //NRF_LOG_CLI_CMDS + +#endif // NRF_MODULE_ENABLED(NRF_LOG) diff --git a/libraries/nfc/src/util/nrf_log_instance.h b/libraries/nfc/src/util/nrf_log_instance.h new file mode 100644 index 000000000..6838974d4 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_instance.h @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2018 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_INSTANCE_H +#define NRF_LOG_INSTANCE_H + +#include "sdk_config.h" +#include "nrf_section.h" +#include "nrf_log_types.h" +#include + + +/* + * For GCC, sections are sorted in the group by the linker. For IAR and KEIL, it is assumed that linker will sort + * dynamic and const section in the same order (but in different locations). Proper message formatting + * is based on that assumption. + */ +#if defined(__GNUC__) +#define NRF_LOG_DYNAMIC_SECTION_NAME(_module_name) CONCAT_2(log_dynamic_data_,_module_name) +#define NRF_LOG_FILTER_SECTION_NAME(_module_name) CONCAT_2(log_filter_data_,_module_name) +#define NRF_LOG_CONST_SECTION_NAME(_module_name) CONCAT_2(log_const_data_,_module_name) +#else +#define NRF_LOG_DYNAMIC_SECTION_NAME(_module_name) log_dynamic_data +#define NRF_LOG_FILTER_SECTION_NAME(_module_name) log_filter_data +#define NRF_LOG_CONST_SECTION_NAME(_module_name) log_const_data +#endif + +#define NRF_LOG_ITEM_DATA(_name) CONCAT_3(m_nrf_log_,_name,_logs_data) +#define NRF_LOG_ITEM_DATA_DYNAMIC(_name) CONCAT_2(NRF_LOG_ITEM_DATA(_name),_dynamic) +#define NRF_LOG_ITEM_DATA_FILTER(_name) CONCAT_2(NRF_LOG_ITEM_DATA(_name),_filter) +#define NRF_LOG_ITEM_DATA_CONST(_name) CONCAT_2(NRF_LOG_ITEM_DATA(_name),_const) + +#ifdef UNIT_TEST +#define _CONST +#else +#define _CONST const +#endif + +/*lint -save -esym(526,log_const_data*) -esym(526,log_dynamic_data*)*/ +NRF_SECTION_DEF(log_dynamic_data, nrf_log_module_dynamic_data_t); +NRF_SECTION_DEF(log_filter_data, nrf_log_module_filter_data_t); +NRF_SECTION_DEF(log_const_data, nrf_log_module_const_data_t); +/*lint -restore*/ + +#define NRF_LOG_INTERNAL_CONST_ITEM_REGISTER( \ + _name, _str_name, _info_color, _debug_color, _initial_lvl, _compiled_lvl) \ + NRF_SECTION_ITEM_REGISTER(NRF_LOG_CONST_SECTION_NAME(_name), \ + _CONST nrf_log_module_const_data_t NRF_LOG_ITEM_DATA_CONST(_name)) = { \ + .p_module_name = _str_name, \ + .info_color_id = (_info_color), \ + .debug_color_id = (_debug_color), \ + .compiled_lvl = (nrf_log_severity_t)(_compiled_lvl), \ + .initial_lvl = (nrf_log_severity_t)(_initial_lvl), \ + } + +#if NRF_LOG_FILTERS_ENABLED +#define NRF_LOG_INTERNAL_ITEM_REGISTER( \ + _name, _str_name, _info_color, _debug_color, _initial_lvl, _compiled_lvl) \ + NRF_LOG_INTERNAL_CONST_ITEM_REGISTER(_name, \ + _str_name, \ + _info_color, \ + _debug_color, \ + _initial_lvl, \ + _compiled_lvl); \ + NRF_SECTION_ITEM_REGISTER(NRF_LOG_DYNAMIC_SECTION_NAME(_name), \ + nrf_log_module_dynamic_data_t NRF_LOG_ITEM_DATA_DYNAMIC(_name)); \ + NRF_SECTION_ITEM_REGISTER(NRF_LOG_FILTER_SECTION_NAME(_name), \ + nrf_log_module_filter_data_t NRF_LOG_ITEM_DATA_FILTER(_name)) + +#else +#define NRF_LOG_INTERNAL_ITEM_REGISTER( \ + _name, _str_name, _info_color, _debug_color, _initial_lvl, _compiled_lvl) \ + NRF_LOG_INTERNAL_CONST_ITEM_REGISTER(_name, \ + _str_name, \ + _info_color, \ + _debug_color, \ + _initial_lvl, \ + _compiled_lvl) + +#endif +/**@file + * + * @defgroup nrf_log_instance Macros for logging on instance level + * @{ + * @ingroup nrf_log + * + * @brief Macros for logging on instance level + */ + +/** @def NRF_LOG_INSTANCE_PTR_DECLARE + * @brief Macro for declaring a logger instance pointer in the module stucture. + */ + +/** @def NRF_LOG_INSTANCE_REGISTER + * @brief Macro for creating an independent module instance. + * + * Module instance provides filtering of logs on instance level instead of module level. + */ + +/** @def NRF_LOG_INSTANCE_PTR_INIT + * @brief Macro for initializing a pointer to the logger instance. + */ + + + /** @} */ +#if NRF_LOG_ENABLED && NRF_LOG_FILTERS_ENABLED +#define NRF_LOG_INSTANCE_PTR_DECLARE(_p_name) nrf_log_module_dynamic_data_t * _p_name; + +#define NRF_LOG_INSTANCE_REGISTER( \ + _module_name, _inst_name, _info_color, _debug_color, _initial_lvl, _compiled_lvl) \ + NRF_LOG_INTERNAL_ITEM_REGISTER(CONCAT_3(_module_name,_,_inst_name), \ + STRINGIFY(_module_name._inst_name), \ + _info_color, \ + _debug_color, \ + _initial_lvl, \ + _compiled_lvl) + +#define NRF_LOG_INSTANCE_PTR_INIT(_p_name, _module_name, _inst_name) \ + ._p_name = &NRF_LOG_ITEM_DATA_DYNAMIC(CONCAT_3(_module_name,_,_inst_name)), + +#else +#define NRF_LOG_INSTANCE_PTR_DECLARE(_p_name) +#define NRF_LOG_INSTANCE_REGISTER(_module_name, _inst_name, info_color, debug_color, _initial_lvl, compiled_lvl) +#define NRF_LOG_INSTANCE_PTR_INIT(_p_name, _module_name, _inst_name) +#endif + +#endif //NRF_LOG_INSTANCE_H diff --git a/libraries/nfc/src/util/nrf_log_internal.h b/libraries/nfc/src/util/nrf_log_internal.h new file mode 100644 index 000000000..2ca00c055 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_internal.h @@ -0,0 +1,516 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_INTERNAL_H__ +#define NRF_LOG_INTERNAL_H__ +#include "sdk_common.h" +#include "nrf.h" +#include "nrf_error.h" +#include "app_util.h" +#include +#include +#include "nrf_log_instance.h" +#include "nrf_log_types.h" + +#ifndef NRF_LOG_ERROR_COLOR + #define NRF_LOG_ERROR_COLOR NRF_LOG_COLOR_DEFAULT +#endif + +#ifndef NRF_LOG_WARNING_COLOR + #define NRF_LOG_WARNING_COLOR NRF_LOG_COLOR_DEFAULT +#endif + +#ifndef NRF_LOG_INFO_COLOR + #define NRF_LOG_INFO_COLOR NRF_LOG_COLOR_DEFAULT +#endif + +#ifndef NRF_LOG_DEBUG_COLOR + #define NRF_LOG_DEBUG_COLOR NRF_LOG_COLOR_DEFAULT +#endif + + +#ifndef NRF_LOG_COLOR_DEFAULT +#define NRF_LOG_COLOR_DEFAULT 0 +#endif + +#ifndef NRF_LOG_DEFAULT_LEVEL +#define NRF_LOG_DEFAULT_LEVEL 0 +#endif + +#ifndef NRF_LOG_USES_COLORS +#define NRF_LOG_USES_COLORS 0 +#endif + +#ifndef NRF_LOG_USES_TIMESTAMP +#define NRF_LOG_USES_TIMESTAMP 0 +#endif + +#ifndef NRF_LOG_FILTERS_ENABLED +#define NRF_LOG_FILTERS_ENABLED 0 +#endif + +#ifndef NRF_LOG_MODULE_NAME + #define NRF_LOG_MODULE_NAME app +#endif + +#define NRF_LOG_LEVEL_BITS 3 +#define NRF_LOG_LEVEL_MASK ((1UL << NRF_LOG_LEVEL_BITS) - 1) +#define NRF_LOG_MODULE_ID_BITS 16 +#define NRF_LOG_MODULE_ID_POS 16 + + +#define NRF_LOG_MAX_NUM_OF_ARGS 6 + + +#if NRF_LOG_FILTERS_ENABLED && NRF_LOG_ENABLED + #define NRF_LOG_FILTER NRF_LOG_ITEM_DATA_DYNAMIC(NRF_LOG_MODULE_NAME).filter + #define NRF_LOG_INST_FILTER(p_inst) (p_inst)->filter +#else + #undef NRF_LOG_FILTER + #define NRF_LOG_FILTER NRF_LOG_SEVERITY_DEBUG + #define NRF_LOG_INST_FILTER(p_inst) NRF_LOG_SEVERITY_DEBUG +#endif + +/** + * @brief Macro for calculating module id based on address and section start address + */ +#define NRF_LOG_MODULE_ID_GET_CONST(addr) (((uint32_t)(addr) - \ + (uint32_t)NRF_SECTION_START_ADDR(log_const_data)) / \ + sizeof(nrf_log_module_const_data_t)) +/** + * @brief Macro for calculating module id based on address and section start address + */ +#define NRF_LOG_MODULE_ID_GET_DYNAMIC(addr) (((uint32_t)(addr) - \ + (uint32_t)NRF_SECTION_START_ADDR(log_dynamic_data)) / \ + sizeof(nrf_log_module_dynamic_data_t)) + + +#if NRF_LOG_ENABLED +#define NRF_LOG_MODULE_ID NRF_LOG_MODULE_ID_GET_CONST(&NRF_LOG_ITEM_DATA_CONST(NRF_LOG_MODULE_NAME)) +#if NRF_LOG_FILTERS_ENABLED +#define NRF_LOG_INST_ID(p_inst) NRF_LOG_MODULE_ID_GET_DYNAMIC(p_inst) +#else +#define NRF_LOG_INST_ID(p_inst) NRF_LOG_MODULE_ID +#endif +#else +#define NRF_LOG_MODULE_ID 0 +#define NRF_LOG_INST_ID(p_inst) 0 +#endif + + +#define LOG_INTERNAL_X(N, ...) CONCAT_2(LOG_INTERNAL_, N) (__VA_ARGS__) +#define LOG_INTERNAL(type, ...) LOG_INTERNAL_X(NUM_VA_ARGS_LESS_1( \ + __VA_ARGS__), type, __VA_ARGS__) +#if NRF_LOG_ENABLED +#define NRF_LOG_INTERNAL_LOG_PUSH(_str) nrf_log_push(_str) +#define LOG_INTERNAL_0(type, str) \ + nrf_log_frontend_std_0(type, str) +#define LOG_INTERNAL_1(type, str, arg0) \ + /*lint -save -e571*/nrf_log_frontend_std_1(type, str, (uint32_t)(arg0))/*lint -restore*/ +#define LOG_INTERNAL_2(type, str, arg0, arg1) \ + /*lint -save -e571*/nrf_log_frontend_std_2(type, str, (uint32_t)(arg0), \ + (uint32_t)(arg1))/*lint -restore*/ +#define LOG_INTERNAL_3(type, str, arg0, arg1, arg2) \ + /*lint -save -e571*/nrf_log_frontend_std_3(type, str, (uint32_t)(arg0), \ + (uint32_t)(arg1), (uint32_t)(arg2))/*lint -restore*/ +#define LOG_INTERNAL_4(type, str, arg0, arg1, arg2, arg3) \ + /*lint -save -e571*/nrf_log_frontend_std_4(type, str, (uint32_t)(arg0), \ + (uint32_t)(arg1), (uint32_t)(arg2), (uint32_t)(arg3))/*lint -restore*/ +#define LOG_INTERNAL_5(type, str, arg0, arg1, arg2, arg3, arg4) \ + /*lint -save -e571*/nrf_log_frontend_std_5(type, str, (uint32_t)(arg0), \ + (uint32_t)(arg1), (uint32_t)(arg2), (uint32_t)(arg3), (uint32_t)(arg4))/*lint -restore*/ +#define LOG_INTERNAL_6(type, str, arg0, arg1, arg2, arg3, arg4, arg5) \ + /*lint -save -e571*/nrf_log_frontend_std_6(type, str, (uint32_t)(arg0), \ + (uint32_t)(arg1), (uint32_t)(arg2), (uint32_t)(arg3), (uint32_t)(arg4), (uint32_t)(arg5))/*lint -restore*/ + + +#else //NRF_LOG_ENABLED +#define NRF_LOG_INTERNAL_LOG_PUSH(_str) (void)(_str) +#define LOG_INTERNAL_0(_type, _str) \ + (void)(_type); (void)(_str) +#define LOG_INTERNAL_1(_type, _str, _arg0) \ + (void)(_type); (void)(_str); (void)(_arg0) +#define LOG_INTERNAL_2(_type, _str, _arg0, _arg1) \ + (void)(_type); (void)(_str); (void)(_arg0); (void)(_arg1) +#define LOG_INTERNAL_3(_type, _str, _arg0, _arg1, _arg2) \ + (void)(_type); (void)(_str); (void)(_arg0); (void)(_arg1); (void)(_arg2) +#define LOG_INTERNAL_4(_type, _str, _arg0, _arg1, _arg2, _arg3) \ + (void)(_type); (void)(_str); (void)(_arg0); (void)(_arg1); (void)(_arg2); (void)(_arg3) +#define LOG_INTERNAL_5(_type, _str, _arg0, _arg1, _arg2, _arg3, _arg4) \ + (void)(_type); (void)(_str); (void)(_arg0); (void)(_arg1); (void)(_arg2); (void)(_arg3); (void)(_arg4) +#define LOG_INTERNAL_6(_type, _str, _arg0, _arg1, _arg2, _arg3, _arg4, _arg5) \ + (void)(_type); (void)(_str); (void)(_arg0); (void)(_arg1); (void)(_arg2); (void)(_arg3); (void)(_arg4); (void)(_arg5) +#endif //NRF_LOG_ENABLED + +#define LOG_SEVERITY_MOD_ID(severity) ((severity) | NRF_LOG_MODULE_ID << NRF_LOG_MODULE_ID_POS) +#define LOG_SEVERITY_INST_ID(severity,p_inst) ((severity) | NRF_LOG_INST_ID(p_inst) << NRF_LOG_MODULE_ID_POS) + +#if NRF_LOG_ENABLED +#define LOG_HEXDUMP(_severity, _p_data, _length) \ + nrf_log_frontend_hexdump((_severity), (_p_data), (_length)) +#else +#define LOG_HEXDUMP(_severity, _p_data, _length) \ + (void)(_severity); (void)(_p_data); (void)_length +#endif + +#define NRF_LOG_INTERNAL_INST(level, level_id, p_inst, ...) \ + if (NRF_LOG_ENABLED && (NRF_LOG_LEVEL >= level) && \ + (level <= NRF_LOG_DEFAULT_LEVEL)) \ + { \ + if (NRF_LOG_INST_FILTER(p_inst) >= level) \ + { \ + LOG_INTERNAL(LOG_SEVERITY_INST_ID(level_id, p_inst), __VA_ARGS__); \ + } \ + } + +#define NRF_LOG_INTERNAL_MODULE(level, level_id, ...) \ + if (NRF_LOG_ENABLED && (NRF_LOG_LEVEL >= level) && \ + (level <= NRF_LOG_DEFAULT_LEVEL)) \ + { \ + if (NRF_LOG_FILTER >= level) \ + { \ + LOG_INTERNAL(LOG_SEVERITY_MOD_ID(level_id), __VA_ARGS__); \ + } \ + } + +#define NRF_LOG_INTERNAL_HEXDUMP_INST(level, level_id, p_inst, p_data, len) \ + if (NRF_LOG_ENABLED && (NRF_LOG_LEVEL >= level) && \ + (level <= NRF_LOG_DEFAULT_LEVEL)) \ + { \ + if (NRF_LOG_INST_FILTER(p_inst) >= level) \ + { \ + LOG_HEXDUMP(LOG_SEVERITY_INST_ID(level_id, p_inst), \ + (p_data), (len)); \ + } \ + } + +#define NRF_LOG_INTERNAL_HEXDUMP_MODULE(level, level_id, p_data, len) \ + if (NRF_LOG_ENABLED && (NRF_LOG_LEVEL >= level) && \ + (level <= NRF_LOG_DEFAULT_LEVEL)) \ + { \ + if (NRF_LOG_FILTER >= level) \ + { \ + LOG_HEXDUMP(LOG_SEVERITY_MOD_ID(level_id), \ + (p_data), (len)); \ + } \ + } + +#define NRF_LOG_INTERNAL_INST_ERROR(p_inst, ...) \ + NRF_LOG_INTERNAL_INST(NRF_LOG_SEVERITY_ERROR, NRF_LOG_SEVERITY_ERROR, p_inst, __VA_ARGS__) + +#define NRF_LOG_INTERNAL_ERROR(...) \ + NRF_LOG_INTERNAL_MODULE(NRF_LOG_SEVERITY_ERROR, NRF_LOG_SEVERITY_ERROR,__VA_ARGS__) + +#define NRF_LOG_INTERNAL_HEXDUMP_INST_ERROR(p_inst, p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_INST(NRF_LOG_SEVERITY_ERROR, NRF_LOG_SEVERITY_ERROR, p_inst, p_data, len) + +#define NRF_LOG_INTERNAL_HEXDUMP_ERROR(p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_MODULE(NRF_LOG_SEVERITY_ERROR, NRF_LOG_SEVERITY_ERROR, p_data, len) + +#define NRF_LOG_INTERNAL_INST_WARNING(p_inst, ...) \ + NRF_LOG_INTERNAL_INST(NRF_LOG_SEVERITY_WARNING, NRF_LOG_SEVERITY_WARNING, p_inst, __VA_ARGS__) + +#define NRF_LOG_INTERNAL_WARNING(...) \ + NRF_LOG_INTERNAL_MODULE(NRF_LOG_SEVERITY_WARNING, NRF_LOG_SEVERITY_WARNING,__VA_ARGS__) + +#define NRF_LOG_INTERNAL_HEXDUMP_INST_WARNING(p_inst, p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_INST(NRF_LOG_SEVERITY_WARNING, NRF_LOG_SEVERITY_WARNING, p_inst, p_data, len) + +#define NRF_LOG_INTERNAL_HEXDUMP_WARNING(p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_MODULE(NRF_LOG_SEVERITY_WARNING, NRF_LOG_SEVERITY_WARNING, p_data, len) + +#define NRF_LOG_INTERNAL_INST_INFO(p_inst, ...) \ + NRF_LOG_INTERNAL_INST(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO, p_inst, __VA_ARGS__) + +#define NRF_LOG_INTERNAL_INFO(...) \ + NRF_LOG_INTERNAL_MODULE(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO, __VA_ARGS__) + +#define NRF_LOG_INTERNAL_HEXDUMP_INST_INFO(p_inst, p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_INST(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO, p_inst, p_data, len) + +#define NRF_LOG_INTERNAL_HEXDUMP_INFO(p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_MODULE(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO, p_data, len) + +#define NRF_LOG_INTERNAL_RAW_INFO(...) \ + NRF_LOG_INTERNAL_MODULE(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO_RAW, __VA_ARGS__) + +#define NRF_LOG_INTERNAL_RAW_HEXDUMP_INFO(p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_MODULE(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO_RAW, p_data, len) + +#define NRF_LOG_INTERNAL_INST_DEBUG(p_inst, ...) \ + NRF_LOG_INTERNAL_INST(NRF_LOG_SEVERITY_DEBUG, NRF_LOG_SEVERITY_DEBUG, p_inst, __VA_ARGS__) + +#define NRF_LOG_INTERNAL_DEBUG(...) \ + NRF_LOG_INTERNAL_MODULE(NRF_LOG_SEVERITY_DEBUG, NRF_LOG_SEVERITY_DEBUG, __VA_ARGS__) + +#define NRF_LOG_INTERNAL_HEXDUMP_INST_DEBUG(p_inst, p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_INST(NRF_LOG_SEVERITY_DEBUG, NRF_LOG_SEVERITY_DEBUG, p_inst, p_data, len) + +#define NRF_LOG_INTERNAL_HEXDUMP_DEBUG(p_data, len) \ + NRF_LOG_INTERNAL_HEXDUMP_MODULE(NRF_LOG_SEVERITY_DEBUG, NRF_LOG_SEVERITY_DEBUG, p_data, len) + + +#if NRF_LOG_ENABLED + +#ifdef UNIT_TEST +#define COMPILED_LOG_LEVEL 4 +#else +#define COMPILED_LOG_LEVEL NRF_LOG_LEVEL +#endif + + +#define NRF_LOG_INTERNAL_MODULE_REGISTER() \ + NRF_LOG_INTERNAL_ITEM_REGISTER(NRF_LOG_MODULE_NAME, \ + STRINGIFY(NRF_LOG_MODULE_NAME), \ + NRF_LOG_INFO_COLOR, \ + NRF_LOG_DEBUG_COLOR, \ + NRF_LOG_INITIAL_LEVEL, \ + COMPILED_LOG_LEVEL) + +#else +#define NRF_LOG_INTERNAL_MODULE_REGISTER() /*lint -save -e19*/ /*lint -restore*/ +#endif + +extern nrf_log_module_dynamic_data_t NRF_LOG_ITEM_DATA_DYNAMIC(NRF_LOG_MODULE_NAME); +extern _CONST nrf_log_module_const_data_t NRF_LOG_ITEM_DATA_CONST(NRF_LOG_MODULE_NAME); + +/** + * Set of macros for encoding and decoding header for log entries. + * There are 2 types of entries: + * 1. Standard entry (STD) + * An entry consists of header, pointer to string and values. Header contains + * severity leveland determines number of arguments and thus size of the entry. + * Since flash address space starts from 0x00000000 and is limited to kB rather + * than MB 22 bits are used to store the address (4MB). It is used that way to + * save one RAM memory. + * + * -------------------------------- + * |TYPE|SEVERITY|NARGS| P_STR | + * |------------------------------| + * | Module_ID (optional) | + * |------------------------------| + * | TIMESTAMP (optional) | + * |------------------------------| + * | ARG0 | + * |------------------------------| + * | .... | + * |------------------------------| + * | ARG(nargs-1) | + * -------------------------------- + * + * 2. Hexdump entry (HEXDUMP) is used for dumping raw data. An entry consists of + * header, optional timestamp, pointer to string and data. A header contains + * length (10bit) and offset which is updated after backend processes part of + * data. + * + * -------------------------------- + * |TYPE|SEVERITY|NARGS|OFFSET|LEN| + * |------------------------------| + * | Module_ID (optional) | + * |------------------------------| + * | TIMESTAMP (optional) | + * |------------------------------| + * | P_STR | + * |------------------------------| + * | data | + * |------------------------------| + * | data | dummy | + * -------------------------------- + * + */ + +#define STD_ADDR_MASK ((uint32_t)(1U << 22) - 1U) +#define HEADER_TYPE_STD 1U +#define HEADER_TYPE_HEXDUMP 2U +#define HEADER_TYPE_INVALID 3U + +typedef struct +{ + uint32_t type : 2; + uint32_t in_progress: 1; + uint32_t data : 29; +} nrf_log_generic_header_t; + +typedef struct +{ + uint32_t type : 2; + uint32_t in_progress: 1; + uint32_t severity : 3; + uint32_t nargs : 4; + uint32_t addr : 22; +} nrf_log_std_header_t; + +typedef struct +{ + uint32_t type : 2; + uint32_t in_progress: 1; + uint32_t severity : 3; + uint32_t offset : 10; + uint32_t reserved : 6; + uint32_t len : 10; +} nrf_log_hexdump_header_t; + +typedef union +{ + nrf_log_generic_header_t generic; + nrf_log_std_header_t std; + nrf_log_hexdump_header_t hexdump; + uint32_t raw; +} nrf_log_main_header_t; + +typedef struct +{ + nrf_log_main_header_t base; + uint16_t module_id; + uint16_t dropped; + uint32_t timestamp; +} nrf_log_header_t; + +#define HEADER_SIZE (sizeof(nrf_log_header_t)/sizeof(uint32_t) - \ + (NRF_LOG_USES_TIMESTAMP ? 0 : 1)) + +/** + * @brief A function for logging raw string. + * + * @param severity_mid Severity. + * @param p_str A pointer to a string. + */ +void nrf_log_frontend_std_0(uint32_t severity_mid, char const * const p_str); + +/** + * @brief A function for logging a formatted string with one argument. + * + * @param severity_mid Severity. + * @param p_str A pointer to a formatted string. + * @param val0 An argument. + */ +void nrf_log_frontend_std_1(uint32_t severity_mid, + char const * const p_str, + uint32_t val0); + +/** + * @brief A function for logging a formatted string with 2 arguments. + * + * @param severity_mid Severity. + * @param p_str A pointer to a formatted string. + * @param val0, val1 Arguments for formatting string. + */ +void nrf_log_frontend_std_2(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1); + +/** + * @brief A function for logging a formatted string with 3 arguments. + * + * @param severity_mid Severity. + * @param p_str A pointer to a formatted string. + * @param val0, val1, val2 Arguments for formatting string. + */ +void nrf_log_frontend_std_3(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1, + uint32_t val2); + +/** + * @brief A function for logging a formatted string with 4 arguments. + * + * @param severity_mid Severity. + * @param p_str A pointer to a formatted string. + * @param val0, val1, val2, val3 Arguments for formatting string. + */ +void nrf_log_frontend_std_4(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1, + uint32_t val2, + uint32_t val3); + +/** + * @brief A function for logging a formatted string with 5 arguments. + * + * @param severity_mid Severity. + * @param p_str A pointer to a formatted string. + * @param val0, val1, val2, val3, val4 Arguments for formatting string. + */ +void nrf_log_frontend_std_5(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1, + uint32_t val2, + uint32_t val3, + uint32_t val4); + +/** + * @brief A function for logging a formatted string with 6 arguments. + * + * @param severity_mid Severity. + * @param p_str A pointer to a formatted string. + * @param val0, val1, val2, val3, val4, val5 Arguments for formatting string. + */ +void nrf_log_frontend_std_6(uint32_t severity_mid, + char const * const p_str, + uint32_t val0, + uint32_t val1, + uint32_t val2, + uint32_t val3, + uint32_t val4, + uint32_t val5); + +/** + * @brief A function for logging raw data. + * + * @param severity_mid Severity. + * @param p_str A pointer to a string which is prefixing the data. + * @param p_data A pointer to data to be dumped. + * @param length Length of data (in bytes). + * + */ +void nrf_log_frontend_hexdump(uint32_t severity_mid, + const void * const p_data, + uint16_t length); + +/** + * @brief A function for reading a byte from log backend. + * + * @return Byte. + */ +uint8_t nrf_log_getchar(void); +#endif // NRF_LOG_INTERNAL_H__ diff --git a/libraries/nfc/src/util/nrf_log_str_formatter.c b/libraries/nfc/src/util/nrf_log_str_formatter.c new file mode 100644 index 000000000..c65ff8c93 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_str_formatter.c @@ -0,0 +1,258 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_LOG) +#include "nrf_log_str_formatter.h" +#include "nrf_log_internal.h" +#include "nrf_log_ctrl.h" +#include "nrf_fprintf.h" +#include + +#define NRF_LOG_COLOR_CODE_DEFAULT "\x1B[0m" +#define NRF_LOG_COLOR_CODE_BLACK "\x1B[1;30m" +#define NRF_LOG_COLOR_CODE_RED "\x1B[1;31m" +#define NRF_LOG_COLOR_CODE_GREEN "\x1B[1;32m" +#define NRF_LOG_COLOR_CODE_YELLOW "\x1B[1;33m" +#define NRF_LOG_COLOR_CODE_BLUE "\x1B[1;34m" +#define NRF_LOG_COLOR_CODE_MAGENTA "\x1B[1;35m" +#define NRF_LOG_COLOR_CODE_CYAN "\x1B[1;36m" +#define NRF_LOG_COLOR_CODE_WHITE "\x1B[1;37m" + +#define NRF_LOG_CHAR_CODE_MAX 0x7E + +static const char * severity_names[] = { + NULL, + "error", + "warning", + "info", + "debug" +}; + +static const char * m_colors[] = { + NRF_LOG_COLOR_CODE_DEFAULT, + NRF_LOG_COLOR_CODE_BLACK, + NRF_LOG_COLOR_CODE_RED, + NRF_LOG_COLOR_CODE_GREEN, + NRF_LOG_COLOR_CODE_YELLOW, + NRF_LOG_COLOR_CODE_BLUE, + NRF_LOG_COLOR_CODE_MAGENTA, + NRF_LOG_COLOR_CODE_CYAN, + NRF_LOG_COLOR_CODE_WHITE, +}; + +static uint32_t m_freq; +static uint32_t m_timestamp_div; + +static void timestamp_print(nrf_fprintf_ctx_t * p_ctx, uint32_t timestamp) +{ + if (NRF_LOG_USES_TIMESTAMP) + { + if (NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED) + { + timestamp /= m_timestamp_div; + uint32_t seconds = timestamp/m_freq; + uint32_t hours = seconds/3600; + seconds -= hours * 3600; + uint32_t mins = seconds/60; + seconds -= mins * 60; + + uint32_t reminder = timestamp % m_freq; + uint32_t ms = (reminder * 1000)/m_freq; + uint32_t us = (1000*(1000*reminder - (ms * m_freq)))/m_freq; + + nrf_fprintf(p_ctx, "[%02d:%02d:%02d.%03d,%03d] ", hours, mins, seconds, ms, us); + } + else + { + nrf_fprintf(p_ctx, "[%08lu] ", timestamp); + } + } +} +static void prefix_process(nrf_log_str_formatter_entry_params_t * p_params, + nrf_fprintf_ctx_t * p_ctx) +{ + if (p_params->dropped) + { + nrf_fprintf(p_ctx, + "%sLogs dropped (%d)%s\r\n", + NRF_LOG_COLOR_CODE_RED, + p_params->dropped, + NRF_LOG_COLOR_CODE_DEFAULT); + } + + if (!(p_params->severity == NRF_LOG_SEVERITY_INFO_RAW)) + { + if (p_params->use_colors) + { + nrf_fprintf(p_ctx, "%s", + m_colors[nrf_log_color_id_get( p_params->module_id, p_params->severity)]); + } + timestamp_print(p_ctx, p_params->timestamp); + + nrf_fprintf(p_ctx, "<%s> %s: ", + severity_names[p_params->severity], nrf_log_module_name_get(p_params->module_id, false)); + } +} + +static void postfix_process(nrf_log_str_formatter_entry_params_t * p_params, + nrf_fprintf_ctx_t * p_ctx, + bool newline) +{ + if (!(p_params->severity == NRF_LOG_SEVERITY_INFO_RAW)) + { + if (p_params->use_colors) + { + nrf_fprintf(p_ctx, "%s", m_colors[0]); + } + nrf_fprintf(p_ctx, "\r\n"); + } + else if (newline) + { + nrf_fprintf(p_ctx, "\r\n"); + } + nrf_fprintf_buffer_flush(p_ctx); +} + +void nrf_log_std_entry_process(char const * p_str, + uint32_t const * p_args, + uint32_t nargs, + nrf_log_str_formatter_entry_params_t * p_params, + nrf_fprintf_ctx_t * p_ctx) +{ + bool auto_flush = p_ctx->auto_flush; + p_ctx->auto_flush = false; + + prefix_process(p_params, p_ctx); + + switch (nargs) + { + case 0: + nrf_fprintf(p_ctx, p_str); + break; + case 1: + nrf_fprintf(p_ctx, p_str, p_args[0]); + break; + case 2: + nrf_fprintf(p_ctx, p_str, p_args[0], p_args[1]); + break; + case 3: + nrf_fprintf(p_ctx, p_str, p_args[0], p_args[1], p_args[2]); + break; + case 4: + nrf_fprintf(p_ctx, p_str, p_args[0], p_args[1], p_args[2], p_args[3]); + break; + case 5: + nrf_fprintf(p_ctx, p_str, p_args[0], p_args[1], p_args[2], p_args[3], p_args[4]); + break; + case 6: + nrf_fprintf(p_ctx, p_str, p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5]); + break; + + default: + break; + } + + postfix_process(p_params, p_ctx, false); + p_ctx->auto_flush = auto_flush; +} + +#define HEXDUMP_BYTES_IN_LINE 8 + +void nrf_log_hexdump_entry_process(uint8_t * p_data, + uint32_t data_len, + nrf_log_str_formatter_entry_params_t * p_params, + nrf_fprintf_ctx_t * p_ctx) +{ + if (data_len > HEXDUMP_BYTES_IN_LINE) + { + return; + } + bool auto_flush = p_ctx->auto_flush; + p_ctx->auto_flush = false; + + prefix_process(p_params, p_ctx); + + uint32_t i; + + for (i = 0; i < HEXDUMP_BYTES_IN_LINE; i++) + { + if (i < data_len) + { + nrf_fprintf(p_ctx, " %02x", p_data[i]); + } + else + { + nrf_fprintf(p_ctx, " "); + } + } + nrf_fprintf(p_ctx, "|"); + + for (i = 0; i < HEXDUMP_BYTES_IN_LINE; i++) + { + if (i < data_len) + { + char c = (char)p_data[i]; + nrf_fprintf(p_ctx, "%c", ((c <= NRF_LOG_CHAR_CODE_MAX) && isprint((int)c)) ? c :'.'); + } + else + { + nrf_fprintf(p_ctx, " "); + } + } + + postfix_process(p_params, p_ctx, true); + + p_ctx->auto_flush = auto_flush; +} + +void nrf_log_str_formatter_timestamp_freq_set(uint32_t freq) +{ + m_timestamp_div = 1; + /* There is no point to have frequency higher than 1MHz (ns are not printed) and too high + * frequency leads to overflows in calculations. + */ + while (freq > 1000000) + { + freq /= 2; + m_timestamp_div *= 2; + } + m_freq = freq; +} +#endif //NRF_LOG_ENABLED diff --git a/libraries/nfc/src/util/nrf_log_str_formatter.h b/libraries/nfc/src/util/nrf_log_str_formatter.h new file mode 100644 index 000000000..55b36d759 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_str_formatter.h @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/**@file + * + * @defgroup nrf_log_str_formatter String formatter for the logger messages + * @{ + * @ingroup nrf_log + */ + +#ifndef NRF_LOG_STR_FORMATTER_H +#define NRF_LOG_STR_FORMATTER_H + +#include +#include "nrf_fprintf.h" +#include "nrf_log_ctrl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + uint32_t timestamp; + uint16_t module_id; + uint16_t dropped; + nrf_log_severity_t severity; + uint8_t use_colors; +} nrf_log_str_formatter_entry_params_t; + + +void nrf_log_std_entry_process(char const * p_str, + uint32_t const * p_args, + uint32_t nargs, + nrf_log_str_formatter_entry_params_t * p_params, + nrf_fprintf_ctx_t * p_ctx); + +void nrf_log_hexdump_entry_process(uint8_t * p_data, + uint32_t data_len, + nrf_log_str_formatter_entry_params_t * p_params, + nrf_fprintf_ctx_t * p_ctx); + +void nrf_log_str_formatter_timestamp_freq_set(uint32_t freq); +#ifdef __cplusplus +} +#endif + +#endif //NRF_LOG_STR_FORMATTER_H +/** @} */ diff --git a/libraries/nfc/src/util/nrf_log_types.h b/libraries/nfc/src/util/nrf_log_types.h new file mode 100644 index 000000000..e0b707807 --- /dev/null +++ b/libraries/nfc/src/util/nrf_log_types.h @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2018 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_LOG_TYPES_H +#define NRF_LOG_TYPES_H + +#include + +/** + * @brief Logger severity levels. + */ +typedef enum +{ + NRF_LOG_SEVERITY_NONE, + NRF_LOG_SEVERITY_ERROR, + NRF_LOG_SEVERITY_WARNING, + NRF_LOG_SEVERITY_INFO, + NRF_LOG_SEVERITY_DEBUG, + NRF_LOG_SEVERITY_INFO_RAW, /* Artificial level to pass information about skipping string postprocessing.*/ +} nrf_log_severity_t; + +/** + * @brief Structure holding dynamic data associated with a module. + * + * See @ref NRF_LOG_MODULE_REGISTER and @ref NRF_LOG_INSTANCE_REGISTER. + */ +typedef struct +{ + uint16_t order_idx; ///< Ordered index of the module (used for auto-completion). + uint16_t filter; ///< Current highest severity level accepted (redundant to @ref nrf_log_module_filter_data_t::filter_lvls, used for optimization) +} nrf_log_module_dynamic_data_t; + +/** + * @brief Structure holding dynamic filters associated with a module or instance if filtering is enabled (@ref NRF_LOG_FILTERS_ENABLED). + * + * @note Backend filters logically are part of @ref nrf_log_module_dynamic_data_t but they are kept separated to enable storing them in non-volatile memory. + */ +typedef struct +{ + uint32_t filter_lvls; ///< Current severity levels for each backend (3 bits per backend). +} nrf_log_module_filter_data_t; + +/** + * @brief Structure holding constant data associated with a module or instance. + * + * See @ref NRF_LOG_MODULE_REGISTER and @ref NRF_LOG_INSTANCE_REGISTER. + */ +typedef struct +{ + const char * p_module_name; ///< Module or instance name. + uint8_t info_color_id; ///< Color code of info messages. + uint8_t debug_color_id; ///< Color code of debug messages. + nrf_log_severity_t compiled_lvl; ///< Compiled highest severity level. + nrf_log_severity_t initial_lvl; ///< Severity level for given module or instance set on backend initialization. +} nrf_log_module_const_data_t; + +#endif //NRF_LOG_TYPES_H diff --git a/libraries/nfc/src/util/nrf_memobj.c b/libraries/nfc/src/util/nrf_memobj.c new file mode 100644 index 000000000..c5bb7d592 --- /dev/null +++ b/libraries/nfc/src/util/nrf_memobj.c @@ -0,0 +1,258 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nrf_memobj.h" +#include "nrf_atomic.h" +#include "nrf_assert.h" + +typedef struct memobj_elem_s memobj_elem_t; + +/** @brief Standard chunk header. */ +typedef struct +{ + memobj_elem_t * p_next; ///< Pointer to the next element. +} memobj_header_t; + +/** @brief Head header extension fields. */ +typedef struct +{ + uint8_t user_cnt; ///< User counter (see @ref nrf_memobj_get and @ref nrf_memobj_put). + uint8_t chunk_cnt; ///< Number of chunks in the object. + uint16_t chunk_size; ///< Single chunk size +} memobj_head_header_fields_t; + +/** @brief Head header extension. */ +typedef struct +{ + union + { + nrf_atomic_u32_t atomic_user_cnt; + memobj_head_header_fields_t fields; + } data; +} memobj_head_header_t; + +/** @brief Head chunk structure. */ +typedef struct +{ + memobj_header_t header; ///< Standard header. + memobj_head_header_t head_header; ///< Head-specific header part. + uint8_t data[1]; ///< Data. +} memobj_head_t; + +STATIC_ASSERT(sizeof(memobj_header_t) == NRF_MEMOBJ_STD_HEADER_SIZE); + +/** @brief Standard chunk structure. */ +struct memobj_elem_s +{ + memobj_header_t header; ///< Standard header. + uint8_t data[1]; ///< Data. +}; + +ret_code_t nrf_memobj_pool_init(nrf_memobj_pool_t const * p_pool) +{ + return nrf_balloc_init((nrf_balloc_t const *)p_pool); +} + +nrf_memobj_t * nrf_memobj_alloc(nrf_memobj_pool_t const * p_pool, + size_t size) +{ + uint32_t bsize = (uint32_t)NRF_BALLOC_ELEMENT_SIZE((nrf_balloc_t const *)p_pool) - sizeof(memobj_header_t); + uint8_t num_of_chunks = (uint8_t)CEIL_DIV(size + sizeof(memobj_head_header_t), bsize); + + memobj_head_t * p_head = nrf_balloc_alloc((nrf_balloc_t const *)p_pool); + if (p_head == NULL) + { + return NULL; + } + p_head->head_header.data.fields.user_cnt = 0; + p_head->head_header.data.fields.chunk_cnt = 1; + p_head->head_header.data.fields.chunk_size = bsize; + + memobj_header_t * p_prev = (memobj_header_t *)p_head; + memobj_header_t * p_curr; + uint32_t i; + uint32_t chunk_less1 = (uint32_t)num_of_chunks - 1; + + p_prev->p_next = (memobj_elem_t *)p_pool; + for (i = 0; i < chunk_less1; i++) + { + p_curr = (memobj_header_t *)nrf_balloc_alloc((nrf_balloc_t const *)p_pool); + if (p_curr) + { + (p_head->head_header.data.fields.chunk_cnt)++; + p_prev->p_next = (memobj_elem_t *)p_curr; + p_curr->p_next = (memobj_elem_t *)p_pool; + p_prev = p_curr; + } + else + { + //Could not allocate all requested buffers + nrf_memobj_free((nrf_memobj_t *)p_head); + return NULL; + } + } + return (nrf_memobj_t *)p_head; +} + +void nrf_memobj_free(nrf_memobj_t * p_obj) +{ + memobj_head_t * p_head = (memobj_head_t *)p_obj; + uint8_t chunk_cnt = p_head->head_header.data.fields.chunk_cnt; + uint32_t i; + memobj_header_t * p_curr = (memobj_header_t *)p_obj; + memobj_header_t * p_next; + uint32_t chunk_less1 = (uint32_t)chunk_cnt - 1; + + for (i = 0; i < chunk_less1; i++) + { + p_curr = (memobj_header_t *)p_curr->p_next; + } + nrf_balloc_t const * p_pool2 = (nrf_balloc_t const *)p_curr->p_next; + + p_curr = (memobj_header_t *)p_obj; + for (i = 0; i < chunk_cnt; i++) + { + p_next = (memobj_header_t *)p_curr->p_next; + nrf_balloc_free(p_pool2, p_curr); + p_curr = p_next; + } +} + +void nrf_memobj_get(nrf_memobj_t const * p_obj) +{ + memobj_head_t * p_head = (memobj_head_t *)p_obj; + (void)nrf_atomic_u32_add(&p_head->head_header.data.atomic_user_cnt, 1); +} + +void nrf_memobj_put(nrf_memobj_t * p_obj) +{ + memobj_head_t * p_head = (memobj_head_t *)p_obj; + uint32_t user_cnt = nrf_atomic_u32_sub(&p_head->head_header.data.atomic_user_cnt, 1); + memobj_head_header_fields_t * p_fields = (memobj_head_header_fields_t *)&user_cnt; + if (p_fields->user_cnt == 0) + { + nrf_memobj_free(p_obj); + } +} + +static void memobj_op(nrf_memobj_t * p_obj, + void * p_data, + size_t * p_len, + size_t offset, + bool read) +{ + + ASSERT(p_obj); + + memobj_head_t * p_head = (memobj_head_t *)p_obj; + memobj_elem_t * p_curr_chunk = (memobj_elem_t *)p_obj; + size_t obj_capacity; + size_t chunk_size; + size_t chunk_idx; + size_t chunk_offset; + size_t len; + + obj_capacity = (p_head->head_header.data.fields.chunk_size * + p_head->head_header.data.fields.chunk_cnt) - + sizeof(memobj_head_header_fields_t); + + ASSERT(offset < obj_capacity); + + chunk_size = p_head->head_header.data.fields.chunk_size; + chunk_idx = (offset + sizeof(memobj_head_header_fields_t)) / chunk_size; + chunk_offset = (offset + sizeof(memobj_head_header_fields_t)) % chunk_size; + len = ((*p_len + offset) > obj_capacity) ? obj_capacity - offset : *p_len; + + //Return number of available bytes + *p_len = len; + + //Move to the first chunk to be used + while (chunk_idx > 0) + { + p_curr_chunk = p_curr_chunk->header.p_next; + chunk_idx--; + } + + size_t user_mem_offset = 0; + size_t curr_cpy_size = chunk_size - chunk_offset; + curr_cpy_size = curr_cpy_size > len ? len : curr_cpy_size; + + while (len) + { + void * p_user_mem = &((uint8_t *)p_data)[user_mem_offset]; + void * p_obj_mem = &p_curr_chunk->data[chunk_offset]; + if (read) + { + memcpy(p_user_mem, p_obj_mem, curr_cpy_size); + } + else + { + memcpy(p_obj_mem, p_user_mem, curr_cpy_size); + } + + chunk_offset = 0; + p_curr_chunk = p_curr_chunk->header.p_next; + len -= curr_cpy_size; + user_mem_offset += curr_cpy_size; + curr_cpy_size = (chunk_size > len) ? len : chunk_size; + } +} + +void nrf_memobj_write(nrf_memobj_t * p_obj, + void * p_data, + size_t len, + size_t offset) +{ + + size_t op_len = len; + memobj_op(p_obj, p_data, &op_len, offset, false); + ASSERT(op_len == len); +} + +void nrf_memobj_read(nrf_memobj_t * p_obj, + void * p_data, + size_t len, + size_t offset) +{ + size_t op_len = len; + memobj_op(p_obj, p_data, &op_len, offset, true); + ASSERT(op_len == len); + +} diff --git a/libraries/nfc/src/util/nrf_memobj.h b/libraries/nfc/src/util/nrf_memobj.h new file mode 100644 index 000000000..66760456e --- /dev/null +++ b/libraries/nfc/src/util/nrf_memobj.h @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NRF_MEMOBJ_H +#define NRF_MEMOBJ_H + +/** +* @defgroup nrf_memobj Memory Object module +* @{ +* @ingroup app_common +* @brief Functions for controlling a memory object. +*/ +#include +#include +#include "sdk_errors.h" +#include "nrf_balloc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A memory object can consist of multiple chunks with the same size. Each object has a header part + * and a data part. The first element in a memory object is memory object head which has a special header. + * The remaining objects have a header of the same size. + * +@verbatim + _____________________ _____________________ _____________________ +| | | | | | +|4 B head header | --> |4 B p_next |------->|4 B p_memobj_pool | +|_____________________| | |_____________________| |_____________________| +| | | | | | | +|4 B p_next |--| | | | | +|_____________________| | | .... | | +| | | data | | data | +| | | | | | +| data | | | | | +| | | | | | +|_____________________| |_____________________| |_____________________| + head mid_element last_element +@endverbatim + * + */ +#define NRF_MEMOBJ_STD_HEADER_SIZE sizeof(uint32_t) + +/** + * @brief Macro for creating an nrf_memobj pool. + * + * This macro declares an nrf_balloc object. The element in the pool contains a user-defined data part and + * a memory object header. + * + * @param _name Name of the instance. + * @param _chunk_size Size of a single chunk. + * @param _pool_size Number of chunks in the pool. + */ +#define NRF_MEMOBJ_POOL_DEF(_name, _chunk_size, _pool_size) \ + STATIC_ASSERT((_chunk_size) > sizeof(uint32_t)); \ + NRF_BALLOC_DEF(_name, ((_chunk_size)+NRF_MEMOBJ_STD_HEADER_SIZE), (_pool_size)) + +/** + * @brief Pool of memory objects. + */ +typedef nrf_balloc_t nrf_memobj_pool_t; + +/** + * @brief Memory object handle. + */ +typedef void * nrf_memobj_t; + +/** + * @brief Function for initializing the memobj pool instance. + * + * This function initializes the pool. + * + * @param[in] p_pool Pointer to the memobj pool instance structure. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +ret_code_t nrf_memobj_pool_init(nrf_memobj_pool_t const * p_pool); + +/** + * @brief Function for allocating a memobj with a requested size. + * + * Fixed length elements in the pool are linked together to provide the amount of memory requested by + * the user. If a memory object is successfully allocated, then the users can use the memory. + * However, it is fragmented into multiple objects so it must be accessed through the API: + * @ref nrf_memobj_write and @ref nrf_memobj_read. + * + * @param[in] p_pool Pointer to the memobj pool instance structure. + * @param[in] size Data size of requested object. + * + * @return Pointer to a memory object or NULL if the requested size cannot be allocated. + */ +nrf_memobj_t * nrf_memobj_alloc(nrf_memobj_pool_t const * p_pool, + size_t size); + +/** + * @brief Function for indicating that a memory object is used and cannot be freed. + * + * Memory object can be shared and reused between multiple modules and this mechanism ensures that + * object is freed when no longer used by any module. Memory object has a counter which is incremented + * whenever this function is called. @ref nrf_memobj_put function decrements the counter. + * + * @param[in] p_obj Pointer to memory object. + */ +void nrf_memobj_get(nrf_memobj_t const * p_obj); + + +/** + * @brief Function for indicated that memory object is no longer used by the module and can be freed + * if no other module is using it. + * + * Memory object is returned to the pool if internal counter reaches 0 after decrementing. It means + * that no other module is needing it anymore. + * + * @note Memory object holds pointer to the pool which was used to allocate it so it does not have + * to be provided explicitly to this function. + * + * @param[in] p_obj Pointer to memory object. + */ +void nrf_memobj_put(nrf_memobj_t * p_obj); + + +/** + * @brief Function for forcing freeing of the memory object. + * + * @note This function should be use with caution because it can lead to undefined behavior of the + * modules since modules using the memory object are not aware that it has been freed. + * + * @param[in] p_obj Pointer to memory object. + */ +void nrf_memobj_free(nrf_memobj_t * p_obj); + +/** + * @brief Function for writing data to the memory object. + * + * @param[in] p_obj Pointer to memory object. + * @param[in] p_data Pointer to data to be written to the memory object. + * @param[in] len Amount of data to be written to the memory object. + * @param[in] offset Offset. + */ +void nrf_memobj_write(nrf_memobj_t * p_obj, + void * p_data, + size_t len, + size_t offset); + +/** + * @brief Function for reading data from the memory object. + * + * @param[in] p_obj Pointer to memory object. + * @param[in] p_data Pointer to the destination buffer. + * @param[in] len Amount of data to be read from the memory object. + * @param[in] offset Offset. + */ +void nrf_memobj_read(nrf_memobj_t * p_obj, + void * p_data, + size_t len, + size_t offset); + +#ifdef __cplusplus +} +#endif + +#endif //NRF_MEMOBJ_H + +/** @} */ diff --git a/libraries/nfc/src/util/nrf_ringbuf.c b/libraries/nfc/src/util/nrf_ringbuf.c new file mode 100644 index 000000000..24cca7913 --- /dev/null +++ b/libraries/nfc/src/util/nrf_ringbuf.c @@ -0,0 +1,237 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nrf_ringbuf.h" +#include "app_util_platform.h" +#include "nrf_assert.h" + +#define WR_OFFSET 0 +#define RD_OFFSET 1 + +void nrf_ringbuf_init(nrf_ringbuf_t const * p_ringbuf) +{ + p_ringbuf->p_cb->wr_idx = 0; + p_ringbuf->p_cb->rd_idx = 0; + p_ringbuf->p_cb->tmp_rd_idx = 0; + p_ringbuf->p_cb->tmp_wr_idx = 0; + p_ringbuf->p_cb->rd_flag = 0; + p_ringbuf->p_cb->wr_flag = 0; +} + +ret_code_t nrf_ringbuf_alloc(nrf_ringbuf_t const * p_ringbuf, uint8_t * * pp_data, size_t * p_length, bool start) +{ + ASSERT(pp_data); + ASSERT(p_length); + + if (start) + { + if (nrf_atomic_flag_set_fetch(&p_ringbuf->p_cb->wr_flag)) + { + return NRF_ERROR_BUSY; + } + } + + if (p_ringbuf->p_cb->tmp_wr_idx - p_ringbuf->p_cb->rd_idx == p_ringbuf->bufsize_mask + 1) + { + *p_length = 0; + if (start) + { + UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&p_ringbuf->p_cb->wr_flag)); + } + return NRF_SUCCESS; + } + + uint32_t wr_idx = p_ringbuf->p_cb->tmp_wr_idx & p_ringbuf->bufsize_mask; + uint32_t rd_idx = p_ringbuf->p_cb->rd_idx & p_ringbuf->bufsize_mask; + uint32_t available = (wr_idx >= rd_idx) ? p_ringbuf->bufsize_mask + 1 - wr_idx : + p_ringbuf->p_cb->rd_idx - (p_ringbuf->p_cb->tmp_wr_idx - (p_ringbuf->bufsize_mask + 1)); + *p_length = *p_length < available ? *p_length : available; + *pp_data = &p_ringbuf->p_buffer[wr_idx]; + p_ringbuf->p_cb->tmp_wr_idx += *p_length; + + return NRF_SUCCESS; +} + +ret_code_t nrf_ringbuf_put(nrf_ringbuf_t const * p_ringbuf, size_t length) +{ + uint32_t available = p_ringbuf->bufsize_mask + 1 - + (p_ringbuf->p_cb->wr_idx - p_ringbuf->p_cb->rd_idx); + if (length > available) + { + return NRF_ERROR_NO_MEM; + } + + p_ringbuf->p_cb->wr_idx += length; + p_ringbuf->p_cb->tmp_wr_idx = p_ringbuf->p_cb->wr_idx; + if (nrf_atomic_flag_clear_fetch(&p_ringbuf->p_cb->wr_flag) == 0) + { + /* Flag was already cleared. Suggests misuse. */ + return NRF_ERROR_INVALID_STATE; + } + return NRF_SUCCESS; +} + +ret_code_t nrf_ringbuf_cpy_put(nrf_ringbuf_t const * p_ringbuf, + uint8_t const * p_data, + size_t * p_length) +{ + ASSERT(p_data); + ASSERT(p_length); + + if (nrf_atomic_flag_set_fetch(&p_ringbuf->p_cb->wr_flag)) + { + return NRF_ERROR_BUSY; + } + + uint32_t available = p_ringbuf->bufsize_mask + 1 - + (p_ringbuf->p_cb->wr_idx - p_ringbuf->p_cb->rd_idx); + *p_length = available > *p_length ? *p_length : available; + size_t length = *p_length; + uint32_t masked_wr_idx = (p_ringbuf->p_cb->wr_idx & p_ringbuf->bufsize_mask); + uint32_t trail = p_ringbuf->bufsize_mask + 1 - masked_wr_idx; + + if (length > trail) + { + memcpy(&p_ringbuf->p_buffer[masked_wr_idx], p_data, trail); + length -= trail; + masked_wr_idx = 0; + p_data += trail; + } + memcpy(&p_ringbuf->p_buffer[masked_wr_idx], p_data, length); + p_ringbuf->p_cb->wr_idx += *p_length; + + UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&p_ringbuf->p_cb->wr_flag)); + + return NRF_SUCCESS; +} + +ret_code_t nrf_ringbuf_get(nrf_ringbuf_t const * p_ringbuf, uint8_t * * pp_data, size_t * p_length, bool start) +{ + ASSERT(pp_data); + ASSERT(p_length); + + if (start) + { + if (nrf_atomic_flag_set_fetch(&p_ringbuf->p_cb->rd_flag)) + { + return NRF_ERROR_BUSY; + } + } + + uint32_t available = p_ringbuf->p_cb->wr_idx - p_ringbuf->p_cb->tmp_rd_idx; + if (available == 0) + { + *p_length = 0; + if (start) + { + UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&p_ringbuf->p_cb->rd_flag)); + } + return NRF_SUCCESS; + } + + uint32_t masked_tmp_rd_idx = p_ringbuf->p_cb->tmp_rd_idx & p_ringbuf->bufsize_mask; + uint32_t masked_wr_idx = p_ringbuf->p_cb->wr_idx & p_ringbuf->bufsize_mask; + + if ((masked_wr_idx > masked_tmp_rd_idx) && (available < *p_length)) + { + *p_length = available; + } + else if (masked_wr_idx <= masked_tmp_rd_idx) + { + uint32_t trail = p_ringbuf->bufsize_mask + 1 - masked_tmp_rd_idx; + if (*p_length > trail) + { + *p_length = trail; + } + } + *pp_data = &p_ringbuf->p_buffer[masked_tmp_rd_idx]; + p_ringbuf->p_cb->tmp_rd_idx += *p_length; + + return NRF_SUCCESS; +} + +ret_code_t nrf_ringbuf_cpy_get(nrf_ringbuf_t const * p_ringbuf, + uint8_t * p_data, + size_t * p_length) +{ + ASSERT(p_data); + ASSERT(p_length); + + if (nrf_atomic_flag_set_fetch(&p_ringbuf->p_cb->rd_flag)) + { + return NRF_ERROR_BUSY; + } + + uint32_t available = p_ringbuf->p_cb->wr_idx - p_ringbuf->p_cb->rd_idx; + *p_length = available > *p_length ? *p_length : available; + size_t length = *p_length; + uint32_t masked_rd_idx = (p_ringbuf->p_cb->rd_idx & p_ringbuf->bufsize_mask); + uint32_t masked_wr_idx = (p_ringbuf->p_cb->wr_idx & p_ringbuf->bufsize_mask); + uint32_t trail = (masked_wr_idx > masked_rd_idx) ? masked_wr_idx - masked_rd_idx : + p_ringbuf->bufsize_mask + 1 - masked_rd_idx; + + if (length > trail) + { + memcpy(p_data, &p_ringbuf->p_buffer[masked_rd_idx], trail); + length -= trail; + masked_rd_idx = 0; + p_data += trail; + } + memcpy(p_data, &p_ringbuf->p_buffer[masked_rd_idx], length); + p_ringbuf->p_cb->rd_idx += *p_length; + + UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&p_ringbuf->p_cb->rd_flag)); + + return NRF_SUCCESS; +} + +ret_code_t nrf_ringbuf_free(nrf_ringbuf_t const * p_ringbuf, size_t length) +{ + uint32_t available = (p_ringbuf->p_cb->wr_idx - p_ringbuf->p_cb->rd_idx); + if (length > available) + { + return NRF_ERROR_NO_MEM; + } + + p_ringbuf->p_cb->rd_idx += length; + p_ringbuf->p_cb->tmp_rd_idx = p_ringbuf->p_cb->rd_idx; + UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&p_ringbuf->p_cb->rd_flag)); + + return NRF_SUCCESS; +} diff --git a/libraries/nfc/src/util/nrf_ringbuf.h b/libraries/nfc/src/util/nrf_ringbuf.h new file mode 100644 index 000000000..3ad80b2c3 --- /dev/null +++ b/libraries/nfc/src/util/nrf_ringbuf.h @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_RINGBUF_H +#define NRF_RINGBUF_H + +/** +* @defgroup nrf_ringbuf Ring buffer +* @{ +* @ingroup app_common +* @brief Functions for controlling the ring buffer. +*/ + +#include +#include "nrf_atomic.h" +#include "sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @brief Ring buffer instance control block. + * */ +typedef struct +{ + nrf_atomic_flag_t wr_flag; //!< Protection flag. + nrf_atomic_flag_t rd_flag; //!< Protection flag. + uint32_t wr_idx; //!< Write index (updated when putting). + uint32_t tmp_wr_idx; //!< Temporary write index (updated when allocating). + uint32_t rd_idx; //!< Read index (updated when freeing). + uint32_t tmp_rd_idx; //!< Temporary read index (updated when getting). +} nrf_ringbuf_cb_t; + +/** + * @brief Ring buffer instance structure. + * */ +typedef struct +{ + uint8_t * p_buffer; //!< Pointer to the memory used by the ring buffer. + uint32_t bufsize_mask; //!< Buffer size mask (buffer size must be a power of 2). + nrf_ringbuf_cb_t * p_cb; //!< Pointer to the instance control block. +} nrf_ringbuf_t; + +/** + * @brief Macro for defining a ring buffer instance. + * + * @param _name Instance name. + * @param _size Size of the ring buffer (must be a power of 2). + * */ +#define NRF_RINGBUF_DEF(_name, _size) \ + STATIC_ASSERT(IS_POWER_OF_TWO(_size)); \ + static uint8_t CONCAT_2(_name,_buf)[_size]; \ + static nrf_ringbuf_cb_t CONCAT_2(_name,_cb); \ + static const nrf_ringbuf_t _name = { \ + .p_buffer = CONCAT_2(_name,_buf), \ + .bufsize_mask = _size - 1, \ + .p_cb = &CONCAT_2(_name,_cb), \ + } + +/** + * @brief Function for initializing a ring buffer instance. + * + * @param p_ringbuf Pointer to the ring buffer instance. + * + * */ +void nrf_ringbuf_init(nrf_ringbuf_t const * p_ringbuf); + +/** + * @brief Function for allocating memory from a ring buffer. + * + * This function attempts to allocate the amount of memory requested by the user, or a smaller amount if + * the requested amount is not available. If a start flag is set, then exclusive access to allocation + * is established. @ref nrf_ringbuf_put frees access to allocation. If a start flag is not set, then + * exclusive access check is omitted. + * + * @param[in] p_ringbuf Pointer to the ring buffer instance. + * @param[in] pp_data Pointer to the pointer to the allocated buffer. + * @param[in, out] p_length Pointer to length. Length is set to the requested amount and filled + * by the function with actually allocated amount. + * @param[in] start Set to true if exclusive access should be controlled. + * + * @retval NRF_SUCCESS Successful allocation (can be smaller amount than requested). + * NRF_ERROR_BUSY Ring buffer allocation process (alloc-put) is ongoing. + * + * */ +ret_code_t nrf_ringbuf_alloc(nrf_ringbuf_t const * p_ringbuf, uint8_t * * pp_data, size_t * p_length, bool start); + +/** + * @brief Function for commiting data to a ring buffer. + * + * When an allocated buffer (see @ref nrf_ringbuf_alloc) has been filled with data, it must be committed + * to make it available for @ref nrf_ringbuf_get and @ref nrf_ringbuf_cpy_get. This function commits + * the data (can be smaller amount than allocated). + * + * @param[in] p_ringbuf Pointer to the ring buffer instance. + * @param[in] length Amount of bytes to put. + + * @return NRF_SUCCESS on successful put or error. + * */ +ret_code_t nrf_ringbuf_put(nrf_ringbuf_t const * p_ringbuf, size_t length); + +/** + * @brief Function for copying data directly into the ring buffer. + * + * This function copies a user buffer to the ring buffer. + * + * @param[in] p_ringbuf Pointer to the ring buffer instance. + * @param[in] p_data Pointer to the input buffer. + * @param[in, out] p_length Amount of bytes to copy. Amount of bytes copied. + + * @return NRF_SUCCESS on successful put or error. + * */ +ret_code_t nrf_ringbuf_cpy_put(nrf_ringbuf_t const * p_ringbuf, + uint8_t const* p_data, + size_t * p_length); + + +/** + * Function for getting data from the ring buffer. + * + * This function attempts to get the requested amount of data from the ring buffer. If a start flag is set, then + * exclusive access to getting data from the ring buffer is established. @ref nrf_ringbuf_free frees + * access to getting data from the ring buffer. If a start flag is not set, then + * exclusive access check is omitted. + * + * @param[in] p_ringbuf Pointer to the ring buffer instance. + * @param[in] pp_data Pointer to the pointer to the buffer with data. + * @param[in, out] p_length Pointer to length. Length is set to the requested amount and filled + * by the function with the actual amount. + * @param[in] start Set to true if exclusive access should be controlled. + * + * @retval NRF_SUCCESS Successful getting (can be smaller amount than requested). + * NRF_ERROR_BUSY Ring buffer getting process (get-free) is ongoing. + */ +ret_code_t nrf_ringbuf_get(nrf_ringbuf_t const * p_ringbuf, uint8_t * * pp_data, size_t * p_length, bool start); + +/** + * @brief Function for freeing a buffer back to the ring buffer. + * + * When a buffer with data taken from the ring buffer (see @ref nrf_ringbuf_get) has been processed, it + * must be freed to make it available for further use. This function frees the buffer (can be smaller + * amount than get). + * + * @param[in] p_ringbuf Pointer to the ring buffer instance. + * @param[in] length Amount of bytes to free. + + * @return NRF_SUCCESS on successful put or error. + * */ +ret_code_t nrf_ringbuf_free(nrf_ringbuf_t const * p_ringbuf, size_t length); + +/** + * @brief Function for copying data directly out of the ring buffer. + * + * This function copies available data from the ring buffer to a user buffer. + * + * @param[in] p_ringbuf Pointer to the ring buffer instance. + * @param[in] p_data Pointer to the input buffer. + * @param[in, out] p_length Amount of bytes to copy. Amount of bytes copied. + + * @return NRF_SUCCESS on successful put or error. + * */ +ret_code_t nrf_ringbuf_cpy_get(nrf_ringbuf_t const * p_ringbuf, + uint8_t * p_data, + size_t * p_length); +#ifdef __cplusplus +} +#endif + +#endif //NRF_RINGBUF_H +/** @} */ diff --git a/libraries/nfc/src/util/nrf_section.h b/libraries/nfc/src/util/nrf_section.h new file mode 100644 index 000000000..1a38ecde7 --- /dev/null +++ b/libraries/nfc/src/util/nrf_section.h @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_SECTION_H__ +#define NRF_SECTION_H__ + +#include "nordic_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup section_vars Section variables + * @ingroup app_common + * @{ + * + * @brief Section variables. + */ + +//lint -save -e27 -esym(526,*) + +#if defined(__ICCARM__) +// Enable IAR language extensions +#pragma language=extended +#endif + +/**@brief Macro for obtaining the address of the beginning of a section. + * + * param[in] section_name Name of the section. + * @hideinitializer + */ +#if defined(__CC_ARM) +#define NRF_SECTION_START_ADDR(section_name) &CONCAT_2(section_name, $$Base) + +#elif defined(__GNUC__) +#define NRF_SECTION_START_ADDR(section_name) &CONCAT_2(__start_, section_name) + +#elif defined(__ICCARM__) +#define NRF_SECTION_START_ADDR(section_name) __section_begin(STRINGIFY(section_name)) +#endif + + +/**@brief Macro for obtaining the address of the end of a section. + * + * @param[in] section_name Name of the section. + * @hideinitializer + */ +#if defined(__CC_ARM) +#define NRF_SECTION_END_ADDR(section_name) &CONCAT_2(section_name, $$Limit) + +#elif defined(__GNUC__) +#define NRF_SECTION_END_ADDR(section_name) &CONCAT_2(__stop_, section_name) + +#elif defined(__ICCARM__) +#define NRF_SECTION_END_ADDR(section_name) __section_end(STRINGIFY(section_name)) +#endif + + +/**@brief Macro for retrieving the length of a given section, in bytes. + * + * @param[in] section_name Name of the section. + * @hideinitializer + */ +#define NRF_SECTION_LENGTH(section_name) \ + ((size_t)NRF_SECTION_END_ADDR(section_name) - \ + (size_t)NRF_SECTION_START_ADDR(section_name)) + + +/**@brief Macro for creating a section. + * + * @param[in] section_name Name of the section. + * @param[in] data_type Data type of the variables to be registered in the section. + * + * @warning Data type must be word aligned to prevent padding. + * @hideinitializer + */ +#if defined(__CC_ARM) +#define NRF_SECTION_DEF(section_name, data_type) \ + extern data_type * CONCAT_2(section_name, $$Base); \ + extern void * CONCAT_2(section_name, $$Limit) + +#elif defined(__GNUC__) +#define NRF_SECTION_DEF(section_name, data_type) \ + extern data_type * CONCAT_2(__start_, section_name); \ + extern void * CONCAT_2(__stop_, section_name) + +#elif defined(__ICCARM__) +#define NRF_SECTION_DEF(section_name, data_type) \ + _Pragma(STRINGIFY(section = STRINGIFY(section_name))); + +#endif + + +/**@brief Macro for declaring a variable and registering it in a section. + * + * @details Declares a variable and registers it in a named section. This macro ensures that the + * variable is not stripped away when using optimizations. + * + * @note The order in which variables are placed in a section is dependent on the order in + * which the linker script encounters the variables during linking. + * + * @param[in] section_name Name of the section. + * @param[in] section_var Variable to register in the given section. + * @hideinitializer + */ +#if defined(__CC_ARM) +#define NRF_SECTION_ITEM_REGISTER(section_name, section_var) \ + section_var __attribute__ ((section(STRINGIFY(section_name)))) __attribute__((used)) + +#elif defined(__GNUC__) +#define NRF_SECTION_ITEM_REGISTER(section_name, section_var) \ + section_var __attribute__ ((section("." STRINGIFY(section_name)))) __attribute__((used)) + +#elif defined(__ICCARM__) +#define NRF_SECTION_ITEM_REGISTER(section_name, section_var) \ + __root section_var @ STRINGIFY(section_name) +#endif + + +/**@brief Macro for retrieving a variable from a section. + * + * @warning The stored symbol can only be resolved using this macro if the + * type of the data is word aligned. The operation of acquiring + * the stored symbol relies on the size of the stored type. No + * padding can exist in the named section in between individual + * stored items or this macro will fail. + * + * @param[in] section_name Name of the section. + * @param[in] data_type Data type of the variable. + * @param[in] i Index of the variable in section. + * @hideinitializer + */ +#define NRF_SECTION_ITEM_GET(section_name, data_type, i) \ + ((data_type*)NRF_SECTION_START_ADDR(section_name) + (i)) + + +/**@brief Macro for getting the number of variables in a section. + * + * @param[in] section_name Name of the section. + * @param[in] data_type Data type of the variables in the section. + * @hideinitializer + */ +#define NRF_SECTION_ITEM_COUNT(section_name, data_type) \ + NRF_SECTION_LENGTH(section_name) / sizeof(data_type) + +/** @} */ + +//lint -restore + +#ifdef __cplusplus +} +#endif + +#endif // NRF_SECTION_H__ diff --git a/libraries/nfc/src/util/nrf_section_iter.c b/libraries/nfc/src/util/nrf_section_iter.c new file mode 100644 index 000000000..2d64763ca --- /dev/null +++ b/libraries/nfc/src/util/nrf_section_iter.c @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" + +#if NRF_MODULE_ENABLED(NRF_SECTION_ITER) + +#include "nrf_section_iter.h" + + +#if !defined(__GNUC__) +static void nrf_section_iter_item_set(nrf_section_iter_t * p_iter) +{ + ASSERT(p_iter != NULL); + ASSERT(p_iter->p_set != NULL); + ASSERT(p_iter->p_section != NULL); + + while (true) + { + if (p_iter->p_section == p_iter->p_set->p_last) + { + // End of the section set. + p_iter->p_item = NULL; + return; + } + + if (p_iter->p_section->p_start != p_iter->p_section->p_end) + { + // Not empty section. + p_iter->p_item = p_iter->p_section->p_start; + return; + } + + // Next section. + p_iter->p_section++; + } +} +#endif + + +void nrf_section_iter_init(nrf_section_iter_t * p_iter, nrf_section_set_t const * p_set) +{ + ASSERT(p_iter != NULL); + ASSERT(p_set != NULL); + + p_iter->p_set = p_set; + +#if defined(__GNUC__) + p_iter->p_item = p_iter->p_set->section.p_start; + if (p_iter->p_item == p_iter->p_set->section.p_end) + { + p_iter->p_item = NULL; + } +#else + p_iter->p_section = p_set->p_first; + nrf_section_iter_item_set(p_iter); +#endif +} + +void nrf_section_iter_next(nrf_section_iter_t * p_iter) +{ + ASSERT(p_iter != NULL); + ASSERT(p_iter->p_set != NULL); + + if (p_iter->p_item == NULL) + { + return; + } + + p_iter->p_item = (void *)((size_t)(p_iter->p_item) + p_iter->p_set->item_size); + +#if defined(__GNUC__) + if (p_iter->p_item == p_iter->p_set->section.p_end) + { + p_iter->p_item = NULL; + } +#else + ASSERT(p_iter->p_section != NULL); + // End of current section reached? + if (p_iter->p_item == p_iter->p_section->p_end) + { + p_iter->p_section++; + nrf_section_iter_item_set(p_iter); + } +#endif +} + +#endif // NRF_MODULE_ENABLED(NRF_SECTION_ITER) diff --git a/libraries/nfc/src/util/nrf_section_iter.h b/libraries/nfc/src/util/nrf_section_iter.h new file mode 100644 index 000000000..874f0ac35 --- /dev/null +++ b/libraries/nfc/src/util/nrf_section_iter.h @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NRF_SECTION_ITER_H__ +#define NRF_SECTION_ITER_H__ + +#include +#include "nrf_section.h" +#include "nrf_assert.h" +#include "app_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup nrf_section_iter Section variables iterator + * @ingroup app_common + * @{ + */ + +/**@brief Single section description structure. */ +typedef struct +{ + void * p_start; //!< Pointer to the start of section. + void * p_end; //!< Pointer to the end of section. +} nrf_section_t; + + +/**@brief Set of the sections description structure. */ +typedef struct +{ +#if defined(__GNUC__) + nrf_section_t section; //!< Description of the set of sections. + /**< + * In case of GCC all sections in the set are sorted and + * placed in contiguous area, because they are treated as + * one section. + */ +#else + nrf_section_t const * p_first; //!< Pointer to the first section in the set. + nrf_section_t const * p_last; //!< Pointer to the last section in the set. +#endif + size_t item_size; //!< Size of the single item in the section. +} nrf_section_set_t; + + +/**@brief Section iterator structure. */ +typedef struct +{ + nrf_section_set_t const * p_set; //!< Pointer to the appropriate section set. +#if !defined(__GNUC__) + nrf_section_t const * p_section; //!< Pointer to the selected section. + /**< + * In case of GCC all sections in the set are sorted and + * placed in contiguous area, because they are treated + * as one section. + */ +#endif + void * p_item; //!< Pointer to the selected item in the section. +} nrf_section_iter_t; + + +/**@brief Create a set of sections. + * + * @note This macro reserves memory for the given set of sections. + * + * @details A set of sections, is an ordered collections of sections. + * + * @param[in] _name Name of the set. + * @param[in] _type Type of the elements stored in the sections. + * @param[in] _count Number of the sections in the set. This parameter is ignored in case of GCC. + * @hideinitializer + */ +#if defined(__GNUC__) + +#define NRF_SECTION_SET_DEF(_name, _type, _count) \ + \ + NRF_SECTION_DEF(_name, _type); \ + static nrf_section_set_t const _name = \ + { \ + .section = \ + { \ + .p_start = NRF_SECTION_START_ADDR(_name), \ + .p_end = NRF_SECTION_END_ADDR(_name), \ + }, \ + .item_size = sizeof(_type), \ + } + +#else + +#define NRF_SECTION_SET_DEF(_name, _type, _count) \ +/*lint -save -emacro(14, MACRO_REPEAT_FOR*) */ \ +MACRO_REPEAT_FOR(_count, NRF_SECTION_DEF_, _name, _type) \ +static nrf_section_t const CONCAT_2(_name, _array)[] = \ +{ \ + MACRO_REPEAT_FOR(_count, NRF_SECTION_SET_DEF_, _name) \ +}; \ +/*lint -restore */ \ +static nrf_section_set_t const _name = \ +{ \ + .p_first = CONCAT_2(_name, _array), \ + .p_last = CONCAT_2(_name, _array) + ARRAY_SIZE(CONCAT_2(_name, _array)), \ + .item_size = sizeof(_type), \ +} + +#ifndef DOXYGEN +#define NRF_SECTION_DEF_(_priority, _name, _type) \ +NRF_SECTION_DEF(CONCAT_2(_name, _priority), _type); + +#define NRF_SECTION_SET_DEF_(_priority, _name) \ +{ \ + .p_start = NRF_SECTION_START_ADDR(CONCAT_2(_name, _priority)), \ + .p_end = NRF_SECTION_END_ADDR(CONCAT_2(_name, _priority)), \ +}, +#endif // DOXYGEN +#endif // __GNUC__ + + +/**@brief Macro to declare a variable and register it in the section set. + * + * @note The order of the section in the set is based on the priority. The order with which + * variables are placed in a section is dependant on the order with which the linker + * encouters the variables during linking. + * + * @param[in] _name Name of the section set. + * @param[in] _priority Priority of the desired section. + * @param[in] _var The variable to register in the given section. + * @hideinitializer + */ +#define NRF_SECTION_SET_ITEM_REGISTER(_name, _priority, _var) \ + NRF_SECTION_ITEM_REGISTER(CONCAT_2(_name, _priority), _var) + + +/**@brief Function for initializing the section set iterator. + * + * @param[in] p_iter Pointer to the iterator. + * @param[in] p_set Pointer to the sections set. + */ +void nrf_section_iter_init(nrf_section_iter_t * p_iter, nrf_section_set_t const * p_set); + + +/**@brief Function for incrementing iterator. + * + * @param[in] p_iter Pointer to the iterator. + */ +void nrf_section_iter_next(nrf_section_iter_t * p_iter); + + +/**@brief Function for getting the element pointed to by the iterator. + * + * @param[in] p_iter Pointer to the iterator. + * + * @retval Pointer to the element or NULL if iterator points end of the set. + */ +static inline void * nrf_section_iter_get(nrf_section_iter_t const * p_iter) +{ + ASSERT(p_iter); + return p_iter->p_item; +} + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NRF_SECTION_ITER_H__ diff --git a/libraries/nfc/src/util/nrf_strerror.c b/libraries/nfc/src/util/nrf_strerror.c new file mode 100644 index 000000000..53746fa86 --- /dev/null +++ b/libraries/nfc/src/util/nrf_strerror.c @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2011 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(NRF_STRERROR) +#include "nrf_strerror.h" + +/** + * @brief Macro for adding an entity to the description array. + * + * Macro that helps to create a single entity in the description array. + */ +#define NRF_STRERROR_ENTITY(mnemonic) {.code = mnemonic, .name = #mnemonic} + +/** + * @brief Array entity element that describes an error. + */ +typedef struct +{ + ret_code_t code; /**< Error code. */ + char const * name; /**< Descriptive name (the same as the internal error mnemonic). */ +}nrf_strerror_desc_t; + +/** + * @brief Unknown error code. + * + * The constant string used by @ref nrf_strerror_get when the error description was not found. + */ +static char const m_unknown_str[] = "Unknown error code"; + +/** + * @brief Array with error codes. + * + * Array that describes error codes. + * + * @note It is required for this array to have error codes placed in ascending order. + * This condition is checked in automatic unit test before the release. + */ +static nrf_strerror_desc_t const nrf_strerror_array[] = +{ + NRF_STRERROR_ENTITY(NRF_SUCCESS), + NRF_STRERROR_ENTITY(NRF_ERROR_SVC_HANDLER_MISSING), + NRF_STRERROR_ENTITY(NRF_ERROR_SOFTDEVICE_NOT_ENABLED), + NRF_STRERROR_ENTITY(NRF_ERROR_INTERNAL), + NRF_STRERROR_ENTITY(NRF_ERROR_NO_MEM), + NRF_STRERROR_ENTITY(NRF_ERROR_NOT_FOUND), + NRF_STRERROR_ENTITY(NRF_ERROR_NOT_SUPPORTED), + NRF_STRERROR_ENTITY(NRF_ERROR_INVALID_PARAM), + NRF_STRERROR_ENTITY(NRF_ERROR_INVALID_STATE), + NRF_STRERROR_ENTITY(NRF_ERROR_INVALID_LENGTH), + NRF_STRERROR_ENTITY(NRF_ERROR_INVALID_FLAGS), + NRF_STRERROR_ENTITY(NRF_ERROR_INVALID_DATA), + NRF_STRERROR_ENTITY(NRF_ERROR_DATA_SIZE), + NRF_STRERROR_ENTITY(NRF_ERROR_TIMEOUT), + NRF_STRERROR_ENTITY(NRF_ERROR_NULL), + NRF_STRERROR_ENTITY(NRF_ERROR_FORBIDDEN), + NRF_STRERROR_ENTITY(NRF_ERROR_INVALID_ADDR), + NRF_STRERROR_ENTITY(NRF_ERROR_BUSY), +#ifdef NRF_ERROR_CONN_COUNT + NRF_STRERROR_ENTITY(NRF_ERROR_CONN_COUNT), +#endif +#ifdef NRF_ERROR_RESOURCES + NRF_STRERROR_ENTITY(NRF_ERROR_RESOURCES), +#endif + + /* SDK Common errors */ + NRF_STRERROR_ENTITY(NRF_ERROR_MODULE_NOT_INITIALIZED), + NRF_STRERROR_ENTITY(NRF_ERROR_MUTEX_INIT_FAILED), + NRF_STRERROR_ENTITY(NRF_ERROR_MUTEX_LOCK_FAILED), + NRF_STRERROR_ENTITY(NRF_ERROR_MUTEX_UNLOCK_FAILED), + NRF_STRERROR_ENTITY(NRF_ERROR_MUTEX_COND_INIT_FAILED), + NRF_STRERROR_ENTITY(NRF_ERROR_MODULE_ALREADY_INITIALIZED), + NRF_STRERROR_ENTITY(NRF_ERROR_STORAGE_FULL), + NRF_STRERROR_ENTITY(NRF_ERROR_API_NOT_IMPLEMENTED), + NRF_STRERROR_ENTITY(NRF_ERROR_FEATURE_NOT_ENABLED), + NRF_STRERROR_ENTITY(NRF_ERROR_IO_PENDING), + + /* TWI error codes */ + NRF_STRERROR_ENTITY(NRF_ERROR_DRV_TWI_ERR_OVERRUN), + NRF_STRERROR_ENTITY(NRF_ERROR_DRV_TWI_ERR_ANACK), + NRF_STRERROR_ENTITY(NRF_ERROR_DRV_TWI_ERR_DNACK), + + /* IPSP error codes */ + NRF_STRERROR_ENTITY(NRF_ERROR_BLE_IPSP_RX_PKT_TRUNCATED), + NRF_STRERROR_ENTITY(NRF_ERROR_BLE_IPSP_CHANNEL_ALREADY_EXISTS), + NRF_STRERROR_ENTITY(NRF_ERROR_BLE_IPSP_LINK_DISCONNECTED), + NRF_STRERROR_ENTITY(NRF_ERROR_BLE_IPSP_PEER_REJECTED) +}; + + +char const * nrf_strerror_get(ret_code_t code) +{ + char const * p_ret = nrf_strerror_find(code); + return (p_ret == NULL) ? m_unknown_str : p_ret; +} + +char const * nrf_strerror_find(ret_code_t code) +{ + nrf_strerror_desc_t const * p_start; + nrf_strerror_desc_t const * p_end; + p_start = nrf_strerror_array; + p_end = nrf_strerror_array + ARRAY_SIZE(nrf_strerror_array); + + while (p_start < p_end) + { + nrf_strerror_desc_t const * p_mid = p_start + ((p_end - p_start) / 2); + ret_code_t mid_c = p_mid->code; + if (mid_c > code) + { + p_end = p_mid; + } + else if (mid_c < code) + { + p_start = p_mid + 1; + } + else + { + return p_mid->name; + } + } + return NULL; +} + +#endif /* NRF_STRERROR enabled */ diff --git a/libraries/nfc/src/util/nrf_strerror.h b/libraries/nfc/src/util/nrf_strerror.h new file mode 100644 index 000000000..6c89a5e22 --- /dev/null +++ b/libraries/nfc/src/util/nrf_strerror.h @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @defgroup nrf_strerror Error code to string converter + * @ingroup app_common + * + * @brief Module for converting error code into a printable string. + * @{ + */ +#ifndef NRF_STRERROR_H__ +#define NRF_STRERROR_H__ + +#include "sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for getting a printable error string. + * + * @param code Error code to convert. + * + * @note This function cannot fail. + * For the function that may fail with error translation, see @ref nrf_strerror_find. + * + * @return Pointer to the printable string. + * If the string is not found, + * it returns a simple string that says that the error is unknown. + */ +char const * nrf_strerror_get(ret_code_t code); + +/** + * @brief Function for finding a printable error string. + * + * This function gets the error string in the same way as @ref nrf_strerror_get, + * but if the string is not found, it returns NULL. + * + * @param code Error code to convert. + * @return Pointer to the printable string. + * If the string is not found, NULL is returned. + */ +char const * nrf_strerror_find(ret_code_t code); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_STRERROR_H__ */ diff --git a/libraries/nfc/src/util/nrfx_clock.c b/libraries/nfc/src/util/nrfx_clock.c new file mode 100644 index 000000000..1b1a436cc --- /dev/null +++ b/libraries/nfc/src/util/nrfx_clock.c @@ -0,0 +1,403 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +//#if NRFX_CHECK(NRFX_CLOCK_ENABLED) + +#include "nrfx_clock.h" + +#define NRFX_LOG_MODULE CLOCK +#include + +#if NRFX_CHECK(NRFX_POWER_ENABLED) +extern bool nrfx_power_irq_enabled; +#endif + +#define EVT_TO_STR(event) \ + (event == NRF_CLOCK_EVENT_HFCLKSTARTED ? "NRF_CLOCK_EVENT_HFCLKSTARTED" : \ + (event == NRF_CLOCK_EVENT_LFCLKSTARTED ? "NRF_CLOCK_EVENT_LFCLKSTARTED" : \ + (event == NRF_CLOCK_EVENT_DONE ? "NRF_CLOCK_EVENT_DONE" : \ + (event == NRF_CLOCK_EVENT_CTTO ? "NRF_CLOCK_EVENT_CTTO" : \ + "UNKNOWN EVENT")))) + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + #if (NRF_CLOCK_HAS_CALIBRATION == 0) + #error "Calibration is not available in the SoC that is used." + #endif + #if (NRFX_CLOCK_CONFIG_LF_SRC != CLOCK_LFCLKSRC_SRC_RC) + #error "Calibration can be performed only for the RC Oscillator." + #endif +#endif + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_132) && \ + (defined(NRF52832_XXAA) || defined(NRF52832_XXAB)) + // ANOMALY 132 - LFCLK needs to avoid frame from 66us to 138us after LFCLK stop. This solution + // applies delay of 138us before starting LFCLK. + #define USE_WORKAROUND_FOR_ANOMALY_132 1 + + // Convert time to cycles (nRF52832 is clocked with 64 MHz, use delay of 138 us). + #define ANOMALY_132_DELAY_CYCLES (64UL * 138) +#endif + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_192) && \ + (defined(NRF52810_XXAA) || \ + defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \ + defined(NRF52840_XXAA)) + // Enable workaround for nRF52 anomaly 192 (LFRC oscillator frequency is wrong + // after calibration, exceeding 500 ppm). + #define USE_WORKAROUND_FOR_ANOMALY_192 1 +#endif + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_201) && \ + (defined(NRF52810_XXAA) || \ + defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \ + defined(NRF52840_XXAA)) + // Enable workaround for nRF52 anomaly 201 (EVENTS_HFCLKSTARTED might be generated twice). + #define USE_WORKAROUND_FOR_ANOMALY_201 1 +#endif + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) +typedef enum +{ + CAL_STATE_IDLE, + CAL_STATE_CAL +} nrfx_clock_cal_state_t; +#endif + +/**@brief CLOCK control block. */ +typedef struct +{ + nrfx_clock_event_handler_t event_handler; + bool module_initialized; /*< Indicate the state of module */ +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + bool hfclk_started; /*< Anomaly 201 workaround. */ +#endif + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + volatile nrfx_clock_cal_state_t cal_state; +#endif +} nrfx_clock_cb_t; + +static nrfx_clock_cb_t m_clock_cb; + +/** + * This variable is used to check whether common POWER_CLOCK common interrupt + * should be disabled or not if @ref nrfx_power tries to disable the interrupt. + */ +#if NRFX_CHECK(NRFX_POWER_ENABLED) +bool nrfx_clock_irq_enabled; +#endif + +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) +/** + * @brief Function for applying delay of 138us before starting LFCLK. + */ +static void nrfx_clock_anomaly_132(void) +{ + uint32_t cyccnt_inital; + uint32_t core_debug; + uint32_t dwt_ctrl; + + // Preserve DEMCR register to do not influence into its configuration. Enable the trace and + // debug blocks. It is required to read and write data to DWT block. + core_debug = CoreDebug->DEMCR; + CoreDebug->DEMCR = core_debug | CoreDebug_DEMCR_TRCENA_Msk; + + // Preserve CTRL register in DWT block to do not influence into its configuration. Make sure + // that cycle counter is enabled. + dwt_ctrl = DWT->CTRL; + DWT->CTRL = dwt_ctrl | DWT_CTRL_CYCCNTENA_Msk; + + // Store start value of cycle counter. + cyccnt_inital = DWT->CYCCNT; + + // Delay required time. + while ((DWT->CYCCNT - cyccnt_inital) < ANOMALY_132_DELAY_CYCLES) + {} + + // Restore preserved registers. + DWT->CTRL = dwt_ctrl; + CoreDebug->DEMCR = core_debug; +} +#endif // NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) + +nrfx_err_t nrfx_clock_init(nrfx_clock_event_handler_t event_handler) +{ + NRFX_ASSERT(event_handler); + + nrfx_err_t err_code = NRFX_SUCCESS; + if (m_clock_cb.module_initialized) + { + err_code = NRFX_ERROR_ALREADY_INITIALIZED; + } + else + { +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + m_clock_cb.cal_state = CAL_STATE_IDLE; +#endif + m_clock_cb.event_handler = event_handler; + m_clock_cb.module_initialized = true; +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + m_clock_cb.hfclk_started = false; +#endif + } + + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} + +void nrfx_clock_enable(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + nrfx_power_clock_irq_init(); + nrf_clock_lf_src_set(NRF_CLOCK,(nrf_clock_lfclk_t)NRFX_CLOCK_CONFIG_LF_SRC); + +#if NRFX_CHECK(NRFX_POWER_ENABLED) + nrfx_clock_irq_enabled = true; +#endif + + NRFX_LOG_INFO("Module enabled."); +} + +void nrfx_clock_disable(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); +#if NRFX_CHECK(NRFX_POWER_ENABLED) + NRFX_ASSERT(nrfx_clock_irq_enabled); + if (!nrfx_power_irq_enabled) +#endif + { + NRFX_IRQ_DISABLE(nrfx_get_irq_number(NRF_CLOCK)); + } + nrf_clock_int_disable(NRF_CLOCK,CLOCK_INTENSET_HFCLKSTARTED_Msk | + CLOCK_INTENSET_LFCLKSTARTED_Msk | +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + CLOCK_INTENSET_DONE_Msk | + CLOCK_INTENSET_CTTO_Msk | +#endif + 0); +#if NRFX_CHECK(NRFX_POWER_ENABLED) + nrfx_clock_irq_enabled = false; +#endif + NRFX_LOG_INFO("Module disabled."); +} + +void nrfx_clock_uninit(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + nrfx_clock_lfclk_stop(); + nrfx_clock_hfclk_stop(); + m_clock_cb.module_initialized = false; + NRFX_LOG_INFO("Uninitialized."); +} + +void nrfx_clock_lfclk_start(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_LFCLKSTARTED); + nrf_clock_int_enable(NRF_CLOCK,NRF_CLOCK_INT_LF_STARTED_MASK); + +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) + nrfx_clock_anomaly_132(); +#endif + + nrf_clock_task_trigger(NRF_CLOCK,NRF_CLOCK_TASK_LFCLKSTART); +} + +void nrfx_clock_lfclk_stop(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + + nrf_clock_int_disable(NRF_CLOCK,NRF_CLOCK_INT_LF_STARTED_MASK); + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_LFCLKSTARTED); + nrf_clock_task_trigger(NRF_CLOCK,NRF_CLOCK_TASK_LFCLKSTOP); + while (nrf_clock_lf_is_running(NRF_CLOCK)) + {} +} + +void nrfx_clock_hfclk_start(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_HFCLKSTARTED); + nrf_clock_int_enable(NRF_CLOCK,NRF_CLOCK_INT_HF_STARTED_MASK); + nrf_clock_task_trigger(NRF_CLOCK,NRF_CLOCK_TASK_HFCLKSTART); +} + +void nrfx_clock_hfclk_stop(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + + nrf_clock_int_disable(NRF_CLOCK,NRF_CLOCK_INT_HF_STARTED_MASK); + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_HFCLKSTARTED); + nrf_clock_task_trigger(NRF_CLOCK,NRF_CLOCK_TASK_HFCLKSTOP); + while (nrf_clock_hf_is_running(NRF_CLOCK,NRF_CLOCK_HFCLK_HIGH_ACCURACY)) + {} +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + m_clock_cb.hfclk_started = false; +#endif +} + +nrfx_err_t nrfx_clock_calibration_start(void) +{ + nrfx_err_t err_code = NRFX_SUCCESS; + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + if (nrfx_clock_hfclk_is_running() == false) + { + return NRFX_ERROR_INVALID_STATE; + } + + if (nrfx_clock_lfclk_is_running() == false) + { + return NRFX_ERROR_INVALID_STATE; + } + + if (m_clock_cb.cal_state == CAL_STATE_IDLE) + { + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_DONE); + nrf_clock_int_enable(NRF_CLOCK,NRF_CLOCK_INT_DONE_MASK); + m_clock_cb.cal_state = CAL_STATE_CAL; +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192) + *(volatile uint32_t *)0x40000C34 = 0x00000002; +#endif + nrf_clock_task_trigger(NRF_CLOCK,NRF_CLOCK_TASK_CAL); + } + else + { + err_code = NRFX_ERROR_BUSY; + } +#endif // NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + + NRFX_LOG_WARNING("Function: %s, error code: %s.", + __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} + +nrfx_err_t nrfx_clock_is_calibrating(void) +{ +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + if (m_clock_cb.cal_state == CAL_STATE_CAL) + { + return NRFX_ERROR_BUSY; + } +#endif + return NRFX_SUCCESS; +} + +void nrfx_clock_calibration_timer_start(uint8_t interval) +{ +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + nrf_clock_cal_timer_timeout_set(interval); + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_CTTO); + nrf_clock_int_enable(NRF_CLOCK,NRF_CLOCK_INT_CTTO_MASK); + nrf_clock_task_trigger(NRF_CLOCK,NRF_CLOCK_TASK_CTSTART); +#endif +} + +void nrfx_clock_calibration_timer_stop(void) +{ +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + nrf_clock_int_disable(NRF_CLOCK,NRF_CLOCK_INT_CTTO_MASK); + nrf_clock_task_trigger(NRF_CLOCK,NRF_CLOCK_TASK_CTSTOP); +#endif +} + +void nrfx_clock_irq_handler(void) +{ + if (nrf_clock_event_check(NRF_CLOCK,NRF_CLOCK_EVENT_HFCLKSTARTED)) + { + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_HFCLKSTARTED); + NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_HFCLKSTARTED)); + nrf_clock_int_disable(NRF_CLOCK,NRF_CLOCK_INT_HF_STARTED_MASK); + +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + if (!m_clock_cb.hfclk_started) + { + m_clock_cb.hfclk_started = true; + if(m_clock_cb.event_handler != NULL){ + m_clock_cb.event_handler(NRFX_CLOCK_EVT_HFCLK_STARTED); + } + } +#else + if(m_clock_cb.event_handler != NULL){ + m_clock_cb.event_handler(NRFX_CLOCK_EVT_HFCLK_STARTED); + } +#endif + } + if (nrf_clock_event_check(NRF_CLOCK,NRF_CLOCK_EVENT_LFCLKSTARTED)) + { + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_LFCLKSTARTED); + NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_LFCLKSTARTED)); + nrf_clock_int_disable(NRF_CLOCK,NRF_CLOCK_INT_LF_STARTED_MASK); + if(m_clock_cb.event_handler != NULL){ + m_clock_cb.event_handler(NRFX_CLOCK_EVT_LFCLK_STARTED); + } + } + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + if (nrf_clock_event_check(NRF_CLOCK,NRF_CLOCK_EVENT_CTTO)) + { + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_CTTO); + NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_CTTO)); + nrf_clock_int_disable(NRF_CLOCK,NRF_CLOCK_INT_CTTO_MASK); + + if(m_clock_cb.event_handler != NULL){ + m_clock_cb.event_handler(NRFX_CLOCK_EVT_CTTO); + } + } + + if (nrf_clock_event_check(NRF_CLOCK,NRF_CLOCK_EVENT_DONE)) + { +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192) + *(volatile uint32_t *)0x40000C34 = 0x00000000; +#endif + nrf_clock_event_clear(NRF_CLOCK,NRF_CLOCK_EVENT_DONE); + NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_CLOCK_EVENT_DONE)); + nrf_clock_int_disable(NRF_CLOCK,NRF_CLOCK_INT_DONE_MASK); + m_clock_cb.cal_state = CAL_STATE_IDLE; + if(m_clock_cb.event_handler != NULL){ + m_clock_cb.event_handler(NRFX_CLOCK_EVT_CAL_DONE); + } + } +#endif // NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) +} + +//#endif // NRFX_CHECK(NRFX_CLOCK_ENABLED) diff --git a/libraries/nfc/src/util/nrfx_clock.h b/libraries/nfc/src/util/nrfx_clock.h new file mode 100644 index 000000000..988744196 --- /dev/null +++ b/libraries/nfc/src/util/nrfx_clock.h @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NRFX_CLOCK_H__ +#define NRFX_CLOCK_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrfx_clock CLOCK driver + * @{ + * @ingroup nrf_clock + * @brief CLOCK peripheral driver. + */ + +/** @brief Clock events. */ +typedef enum +{ + NRFX_CLOCK_EVT_HFCLK_STARTED, ///< HFCLK has been started. + NRFX_CLOCK_EVT_LFCLK_STARTED, ///< LFCLK has been started. + NRFX_CLOCK_EVT_CTTO, ///< Calibration timeout. + NRFX_CLOCK_EVT_CAL_DONE ///< Calibration has been done. +} nrfx_clock_evt_type_t; + +/** + * @brief Clock event handler. + * + * @param[in] event Event. + */ +typedef void (*nrfx_clock_event_handler_t)(nrfx_clock_evt_type_t event); + +/** + * @brief Function for initializing internal structures in the nrfx_clock module. + * + * After initialization, the module is in power off state (clocks are not started). + * + * @param[in] event_handler Event handler provided by the user. + * Must not be NULL. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_ALREADY_INITIALIZED The driver is already initialized. + */ +nrfx_err_t nrfx_clock_init(nrfx_clock_event_handler_t event_handler); + +/** @brief Function for enabling interrupts in the clock module. */ +void nrfx_clock_enable(void); + +/** @brief Function for disabling interrupts in the clock module. */ +void nrfx_clock_disable(void); + +/** @brief Function for uninitializing the clock module. */ +void nrfx_clock_uninit(void); + +/** @brief Function for starting the LFCLK. */ +void nrfx_clock_lfclk_start(void); + +/** @brief Function for stopping the LFCLK. */ +void nrfx_clock_lfclk_stop(void); + +/** + * @brief Function for checking the LFCLK state. + * + * @retval true The LFCLK is running. + * @retval false The LFCLK is not running. + */ +__STATIC_INLINE bool nrfx_clock_lfclk_is_running(void); + +/** @brief Function for starting the high-accuracy source HFCLK. */ +void nrfx_clock_hfclk_start(void); + +/** @brief Function for stopping the external high-accuracy source HFCLK. */ +void nrfx_clock_hfclk_stop(void); + +/** + * @brief Function for checking the HFCLK state. + * + * @retval true The HFCLK is running (XTAL source). + * @retval false The HFCLK is not running. + */ +__STATIC_INLINE bool nrfx_clock_hfclk_is_running(void); + +/** + * @brief Function for starting the calibration of internal LFCLK. + * + * This function starts the calibration process. The process cannot be aborted. LFCLK and HFCLK + * must be running before this function is called. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_INVALID_STATE The low-frequency of high-frequency clock is off. + * @retval NRFX_ERROR_BUSY Clock is in the calibration phase. + */ +nrfx_err_t nrfx_clock_calibration_start(void); + +/** + * @brief Function for checking if calibration is in progress. + * + * This function indicates that the system is in calibration phase. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_BUSY Clock is in the calibration phase. + */ +nrfx_err_t nrfx_clock_is_calibrating(void); + +/** + * @brief Function for starting calibration timer. + * + * @param[in] interval Time after which the CTTO event and interrupt will be generated (in 0.25 s units). + */ +void nrfx_clock_calibration_timer_start(uint8_t interval); + +/** @brief Function for stopping the calibration timer. */ +void nrfx_clock_calibration_timer_stop(void); + +/**@brief Function for returning a requested task address for the clock driver module. + * + * @param[in] task One of the peripheral tasks. + * + * @return Task address. + */ +__STATIC_INLINE uint32_t nrfx_clock_ppi_task_addr(nrf_clock_task_t task); + +/**@brief Function for returning a requested event address for the clock driver module. + * + * @param[in] event One of the peripheral events. + * + * @return Event address. + */ +__STATIC_INLINE uint32_t nrfx_clock_ppi_event_addr(nrf_clock_event_t event); + + +#ifndef SUPPRESS_INLINE_IMPLEMENTATION +__STATIC_INLINE uint32_t nrfx_clock_ppi_task_addr(nrf_clock_task_t task) +{ + return nrf_clock_task_address_get(NRF_CLOCK,task); +} + +__STATIC_INLINE uint32_t nrfx_clock_ppi_event_addr(nrf_clock_event_t event) +{ + return nrf_clock_event_address_get(NRF_CLOCK,event); +} + +__STATIC_INLINE bool nrfx_clock_hfclk_is_running(void) +{ + return nrf_clock_hf_is_running(NRF_CLOCK,NRF_CLOCK_HFCLK_HIGH_ACCURACY); +} + +__STATIC_INLINE bool nrfx_clock_lfclk_is_running(void) +{ + return nrf_clock_lf_is_running(NRF_CLOCK); +} +#endif //SUPPRESS_INLINE_IMPLEMENTATION + +/** @} */ + + +void nrfx_clock_irq_handler(void); + + +#ifdef __cplusplus +} +#endif + +#endif // NRFX_CLOCK_H__ diff --git a/libraries/nfc/src/util/nrfx_glue.h b/libraries/nfc/src/util/nrfx_glue.h new file mode 100644 index 000000000..80e308cb6 --- /dev/null +++ b/libraries/nfc/src/util/nrfx_glue.h @@ -0,0 +1,338 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NRFX_GLUE_H__ +#define NRFX_GLUE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrfx_glue nrfx_glue.h + * @{ + * @ingroup nrfx + * + * @brief This file contains macros that should be implemented according to + * the needs of the host environment into which @em nrfx is integrated. + */ + +#include + +#include + +//------------------------------------------------------------------------------ + +#include +/** + * @brief Macro for placing a runtime assertion. + * + * @param expression Expression to evaluate. + */ +#define NRFX_ASSERT(expression) ASSERT(expression) + +#include +/** + * @brief Macro for placing a compile time assertion. + * + * @param expression Expression to evaluate. + */ +#define NRFX_STATIC_ASSERT(expression) STATIC_ASSERT(expression) + +//------------------------------------------------------------------------------ + +#ifdef NRF51 +#ifdef SOFTDEVICE_PRESENT +#define INTERRUPT_PRIORITY_IS_VALID(pri) (((pri) == 1) || ((pri) == 3)) +#else +#define INTERRUPT_PRIORITY_IS_VALID(pri) ((pri) < 4) +#endif //SOFTDEVICE_PRESENT +#else +#ifdef SOFTDEVICE_PRESENT +#define INTERRUPT_PRIORITY_IS_VALID(pri) ((((pri) > 1) && ((pri) < 4)) || \ + (((pri) > 4) && ((pri) < 8))) +#else +#define INTERRUPT_PRIORITY_IS_VALID(pri) ((pri) < 8) +#endif //SOFTDEVICE_PRESENT +#endif //NRF52 + +/** + * @brief Macro for setting the priority of a specific IRQ. + * + * @param irq_number IRQ number. + * @param priority Priority to set. + */ +#define NRFX_IRQ_PRIORITY_SET(irq_number, priority) \ + _NRFX_IRQ_PRIORITY_SET(irq_number, priority) +static inline void _NRFX_IRQ_PRIORITY_SET(IRQn_Type irq_number, + uint8_t priority) +{ + ASSERT(INTERRUPT_PRIORITY_IS_VALID(priority)); + NVIC_SetPriority(irq_number, priority); +} + +/** + * @brief Macro for enabling a specific IRQ. + * + * @param irq_number IRQ number. + */ +#define NRFX_IRQ_ENABLE(irq_number) _NRFX_IRQ_ENABLE(irq_number) +static inline void _NRFX_IRQ_ENABLE(IRQn_Type irq_number) +{ + NVIC_EnableIRQ(irq_number); +} + +/** + * @brief Macro for checking if a specific IRQ is enabled. + * + * @param irq_number IRQ number. + * + * @retval true If the IRQ is enabled. + * @retval false Otherwise. + */ +#define NRFX_IRQ_IS_ENABLED(irq_number) _NRFX_IRQ_IS_ENABLED(irq_number) +static inline bool _NRFX_IRQ_IS_ENABLED(IRQn_Type irq_number) +{ + return 0 != (NVIC->ISER[irq_number / 32] & (1UL << (irq_number % 32))); +} + +/** + * @brief Macro for disabling a specific IRQ. + * + * @param irq_number IRQ number. + */ +#define NRFX_IRQ_DISABLE(irq_number) _NRFX_IRQ_DISABLE(irq_number) +static inline void _NRFX_IRQ_DISABLE(IRQn_Type irq_number) +{ + NVIC_DisableIRQ(irq_number); +} + +/** + * @brief Macro for setting a specific IRQ as pending. + * + * @param irq_number IRQ number. + */ +#define NRFX_IRQ_PENDING_SET(irq_number) _NRFX_IRQ_PENDING_SET(irq_number) +static inline void _NRFX_IRQ_PENDING_SET(IRQn_Type irq_number) +{ + NVIC_SetPendingIRQ(irq_number); +} + +/** + * @brief Macro for clearing the pending status of a specific IRQ. + * + * @param irq_number IRQ number. + */ +#define NRFX_IRQ_PENDING_CLEAR(irq_number) _NRFX_IRQ_PENDING_CLEAR(irq_number) +static inline void _NRFX_IRQ_PENDING_CLEAR(IRQn_Type irq_number) +{ + NVIC_ClearPendingIRQ(irq_number); +} + +/** + * @brief Macro for checking the pending status of a specific IRQ. + * + * @retval true If the IRQ is pending. + * @retval false Otherwise. + */ +#define NRFX_IRQ_IS_PENDING(irq_number) _NRFX_IRQ_IS_PENDING(irq_number) +static inline bool _NRFX_IRQ_IS_PENDING(IRQn_Type irq_number) +{ + return (NVIC_GetPendingIRQ(irq_number) == 1); +} + +#include +#include +/** + * @brief Macro for entering into a critical section. + */ +#define NRFX_CRITICAL_SECTION_ENTER() CRITICAL_REGION_ENTER() + +/** + * @brief Macro for exiting from a critical section. + */ +#define NRFX_CRITICAL_SECTION_EXIT() CRITICAL_REGION_EXIT() + +//------------------------------------------------------------------------------ + +/** + * @brief When set to a non-zero value, this macro specifies that + * @ref nrfx_coredep_delay_us uses a precise DWT-based solution. + * A compilation error is generated if the DWT unit is not present + * in the SoC used. + */ +#define NRFX_DELAY_DWT_BASED 0 + +#include + +#define NRFX_DELAY_US(us_time) nrfx_coredep_delay_us(us_time) + +//------------------------------------------------------------------------------ + +#include + +/** + * @brief Atomic 32 bit unsigned type. + */ +#define nrfx_atomic_t nrfx_atomic_u32_t + +/** + * @brief Stores value to an atomic object and returns previously stored value. + * + * @param[in] p_data Atomic memory pointer. + * @param[in] value Value to store. + * + * @return Old value stored into atomic object. + */ +#define NRFX_ATOMIC_FETCH_STORE(p_data, value) nrfx_atomic_u32_fetch_store(p_data, value) + +/** + * @brief Performs logical OR operation on an atomic object and returns previously stored value. + * + * @param[in] p_data Atomic memory pointer. + * @param[in] value Value of second operand of OR operation. + * + * @return Old value stored into atomic object. + */ +#define NRFX_ATOMIC_FETCH_OR(p_data, value) nrfx_atomic_u32_fetch_or(p_data, value) + +/** + * @brief Performs logical AND operation on an atomic object and returns previously stored value. + * + * @param[in] p_data Atomic memory pointer. + * @param[in] value Value of second operand of AND operation. + * + * @return Old value stored into atomic object. + */ +#define NRFX_ATOMIC_FETCH_AND(p_data, value) nrfx_atomic_u32_fetch_and(p_data, value) + +/** + * @brief Performs logical XOR operation on an atomic object and returns previously stored value. + * + * @param[in] p_data Atomic memory pointer. + * @param[in] value Value of second operand of XOR operation. + * + * @return Old value stored into atomic object. + */ +#define NRFX_ATOMIC_FETCH_XOR(p_data, value) nrfx_atomic_u32_fetch_xor(p_data, value) + +/** + * @brief Performs logical ADD operation on an atomic object and returns previously stored value. + * + * @param[in] p_data Atomic memory pointer. + * @param[in] value Value of second operand of ADD operation. + * + * @return Old value stored into atomic object. + */ +#define NRFX_ATOMIC_FETCH_ADD(p_data, value) nrfx_atomic_u32_fetch_add(p_data, value) + +/** + * @brief Performs logical SUB operation on an atomic object and returns previously stored value. + * + * @param[in] p_data Atomic memory pointer. + * @param[in] value Value of second operand of SUB operation. + * + * @return Old value stored into atomic object. + */ +#define NRFX_ATOMIC_FETCH_SUB(p_data, value) nrfx_atomic_u32_fetch_sub(p_data, value) + +//------------------------------------------------------------------------------ +#ifndef NRFX_CUSTOM_ERROR_CODES + +#include +/** + * @brief When set to a non-zero value, this macro specifies that the + * @ref nrfx_error_codes and the @ref ret_code_t type itself are defined + * in a customized way and the default definitions from @c + * should not be used. + */ +#define NRFX_CUSTOM_ERROR_CODES 1 + +typedef ret_code_t nrfx_err_t; + +#define NRFX_SUCCESS NRF_SUCCESS +#define NRFX_ERROR_INTERNAL NRF_ERROR_INTERNAL +#define NRFX_ERROR_NO_MEM NRF_ERROR_NO_MEM +#define NRFX_ERROR_NOT_SUPPORTED NRF_ERROR_NOT_SUPPORTED +#define NRFX_ERROR_INVALID_PARAM NRF_ERROR_INVALID_PARAM +#define NRFX_ERROR_INVALID_STATE NRF_ERROR_INVALID_STATE +#define NRFX_ERROR_INVALID_LENGTH NRF_ERROR_INVALID_LENGTH +#define NRFX_ERROR_TIMEOUT NRF_ERROR_TIMEOUT +#define NRFX_ERROR_FORBIDDEN NRF_ERROR_FORBIDDEN +#define NRFX_ERROR_NULL NRF_ERROR_NULL +#define NRFX_ERROR_INVALID_ADDR NRF_ERROR_INVALID_ADDR +#define NRFX_ERROR_BUSY NRF_ERROR_BUSY +#define NRFX_ERROR_ALREADY_INITIALIZED NRF_ERROR_MODULE_ALREADY_INITIALIZED + +#define NRFX_ERROR_DRV_TWI_ERR_OVERRUN NRF_ERROR_DRV_TWI_ERR_OVERRUN +#define NRFX_ERROR_DRV_TWI_ERR_ANACK NRF_ERROR_DRV_TWI_ERR_ANACK +#define NRFX_ERROR_DRV_TWI_ERR_DNACK NRF_ERROR_DRV_TWI_ERR_DNACK + +#endif // NRFX_CUSTOM_ERROR_CODES +//------------------------------------------------------------------------------ + +#include +/** + * @brief Bitmask defining PPI channels reserved to be used outside of nrfx. + */ +#define NRFX_PPI_CHANNELS_USED NRF_PPI_CHANNELS_USED + +/** + * @brief Bitmask defining PPI groups reserved to be used outside of nrfx. + */ +#define NRFX_PPI_GROUPS_USED NRF_PPI_GROUPS_USED + +/** + * @brief Bitmask defining SWI instances reserved to be used outside of nrfx. + */ +#define NRFX_SWI_USED NRF_SWI_USED + +/** + * @brief Bitmask defining TIMER instances reserved to be used outside of nrfx. + */ +#define NRFX_TIMERS_USED NRF_TIMERS_USED + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NRFX_GLUE_H__ diff --git a/libraries/nfc/src/util/sdk_alloca.h b/libraries/nfc/src/util/sdk_alloca.h new file mode 100644 index 000000000..4811e2dbf --- /dev/null +++ b/libraries/nfc/src/util/sdk_alloca.h @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file sdk_alloca.h + * + * @brief Defines alloca() function. + * + * @details This file defines alloca() function. This can be done directly or by including system + * header files. Not all platforms support alloca(). In this case no error will be shown, but + * SDK_ALLOCA_DEFINED will be set to 0. + */ + +#ifndef SDK_ALLOCA_H__ +#define SDK_ALLOCA_H__ + + +#if defined(__SDK_DOXYGEN__) + /** @brief Set to one it alloca() function is available on this platform and it is correctly defined + * by this header file. + */ + #define SDK_ALLOCA_DEFINED 1 +#elif defined(__GNUC__) + #if defined(__SES_ARM) + // SES does not have definition of alloca(), but it have working GCC's __builtin_alloca(). + #if !defined(alloca) + #define alloca(size) __builtin_alloca((size)) + #endif + #else + // alloca() can be defined in on some platforms, but if not then try standard header file. + #include + #if !defined(alloca) + #include + #endif + #endif + #define SDK_ALLOCA_DEFINED 1 +#elif defined(__IAR_SYSTEMS_ICC__) + // IAR does not support alloca() function. + #define SDK_ALLOCA_DEFINED 0 +#else + // All other supported compilers have alloca() definition in header file. + #include + #define SDK_ALLOCA_DEFINED 1 +#endif + + +/*lint -"d__builtin_alloca=(void*)" */ + + +#endif // NRF_ALLOCA_H__ diff --git a/libraries/nfc/src/util/sdk_common.h b/libraries/nfc/src/util/sdk_common.h new file mode 100644 index 000000000..ef3049f2a --- /dev/null +++ b/libraries/nfc/src/util/sdk_common.h @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2013 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @cond */ +/**@file + * + * @ingroup experimental_api + * @defgroup sdk_common SDK Common Header + * @brief All common headers needed for SDK examples will be included here so that application + * developer does not have to include headers on him/herself. + * @{ + */ + +#ifndef SDK_COMMON_H__ +#define SDK_COMMON_H__ + +#include +#include +#include +#include "sdk_config.h" +#include "nordic_common.h" +#include "compiler_abstraction.h" +#include "sdk_os.h" +#include "sdk_errors.h" +#include "app_util.h" +#include "sdk_macros.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @} */ +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif // SDK_COMMON_H__ + diff --git a/libraries/nfc/src/util/sdk_config.h b/libraries/nfc/src/util/sdk_config.h new file mode 100644 index 000000000..84f766958 --- /dev/null +++ b/libraries/nfc/src/util/sdk_config.h @@ -0,0 +1,4231 @@ +/** + * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + + +#ifndef SDK_CONFIG_H +#define SDK_CONFIG_H +// <<< Use Configuration Wizard in Context Menu >>>\n +#ifdef USE_APP_CONFIG +#include "app_config.h" +#endif +// nRF_Drivers + +//========================================================== +// NRFX_CLOCK_ENABLED - nrfx_clock - CLOCK peripheral driver +//========================================================== +#ifndef NRFX_CLOCK_ENABLED +#define NRFX_CLOCK_ENABLED 1 +#endif +// NRFX_CLOCK_CONFIG_LF_SRC - LF Clock Source + +// <0=> RC +// <1=> XTAL +// <2=> Synth +// <131073=> External Low Swing +// <196609=> External Full Swing + +#ifndef NRFX_CLOCK_CONFIG_LF_SRC +#define NRFX_CLOCK_CONFIG_LF_SRC 1 +#endif + +// NRFX_CLOCK_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_CLOCK_CONFIG_IRQ_PRIORITY +#define NRFX_CLOCK_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_CLOCK_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_CLOCK_CONFIG_LOG_ENABLED +#define NRFX_CLOCK_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_CLOCK_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_CLOCK_CONFIG_LOG_LEVEL +#define NRFX_CLOCK_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_CLOCK_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_CLOCK_CONFIG_INFO_COLOR +#define NRFX_CLOCK_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_CLOCK_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_CLOCK_CONFIG_DEBUG_COLOR +#define NRFX_CLOCK_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRFX_NFCT_ENABLED - nrfx_nfct - NFCT peripheral driver +//========================================================== +#ifndef NRFX_NFCT_ENABLED +#define NRFX_NFCT_ENABLED 1 +#endif +// NRFX_NFCT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_NFCT_CONFIG_IRQ_PRIORITY +#define NRFX_NFCT_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_NFCT_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_NFCT_CONFIG_LOG_ENABLED +#define NRFX_NFCT_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_NFCT_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_NFCT_CONFIG_LOG_LEVEL +#define NRFX_NFCT_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_NFCT_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_NFCT_CONFIG_INFO_COLOR +#define NRFX_NFCT_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_NFCT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_NFCT_CONFIG_DEBUG_COLOR +#define NRFX_NFCT_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRFX_PRS_ENABLED - nrfx_prs - Peripheral Resource Sharing module +//========================================================== +#ifndef NRFX_PRS_ENABLED +#define NRFX_PRS_ENABLED 1 +#endif +// NRFX_PRS_BOX_0_ENABLED - Enables box 0 in the module. + + +#ifndef NRFX_PRS_BOX_0_ENABLED +#define NRFX_PRS_BOX_0_ENABLED 0 +#endif + +// NRFX_PRS_BOX_1_ENABLED - Enables box 1 in the module. + + +#ifndef NRFX_PRS_BOX_1_ENABLED +#define NRFX_PRS_BOX_1_ENABLED 0 +#endif + +// NRFX_PRS_BOX_2_ENABLED - Enables box 2 in the module. + + +#ifndef NRFX_PRS_BOX_2_ENABLED +#define NRFX_PRS_BOX_2_ENABLED 0 +#endif + +// NRFX_PRS_BOX_3_ENABLED - Enables box 3 in the module. + + +#ifndef NRFX_PRS_BOX_3_ENABLED +#define NRFX_PRS_BOX_3_ENABLED 0 +#endif + +// NRFX_PRS_BOX_4_ENABLED - Enables box 4 in the module. + + +#ifndef NRFX_PRS_BOX_4_ENABLED +#define NRFX_PRS_BOX_4_ENABLED 1 +#endif + +// NRFX_PRS_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_PRS_CONFIG_LOG_ENABLED +#define NRFX_PRS_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_PRS_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_PRS_CONFIG_LOG_LEVEL +#define NRFX_PRS_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_PRS_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_PRS_CONFIG_INFO_COLOR +#define NRFX_PRS_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_PRS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_PRS_CONFIG_DEBUG_COLOR +#define NRFX_PRS_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRFX_TIMER_ENABLED - nrfx_timer - TIMER periperal driver +//========================================================== +#ifndef NRFX_TIMER_ENABLED +#define NRFX_TIMER_ENABLED 1 +#endif +// NRFX_TIMER0_ENABLED - Enable TIMER0 instance + + +#ifndef NRFX_TIMER0_ENABLED +#define NRFX_TIMER0_ENABLED 0 +#endif + +// NRFX_TIMER1_ENABLED - Enable TIMER1 instance + + +#ifndef NRFX_TIMER1_ENABLED +#define NRFX_TIMER1_ENABLED 0 +#endif + +// NRFX_TIMER2_ENABLED - Enable TIMER2 instance + + +#ifndef NRFX_TIMER2_ENABLED +#define NRFX_TIMER2_ENABLED 0 +#endif + +// NRFX_TIMER3_ENABLED - Enable TIMER3 instance + + +#ifndef NRFX_TIMER3_ENABLED +#define NRFX_TIMER3_ENABLED 0 +#endif + +// NRFX_TIMER4_ENABLED - Enable TIMER4 instance + + +#ifndef NRFX_TIMER4_ENABLED +#define NRFX_TIMER4_ENABLED 1 +#endif + +// NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY - Timer frequency if in Timer mode + +// <0=> 16 MHz +// <1=> 8 MHz +// <2=> 4 MHz +// <3=> 2 MHz +// <4=> 1 MHz +// <5=> 500 kHz +// <6=> 250 kHz +// <7=> 125 kHz +// <8=> 62.5 kHz +// <9=> 31.25 kHz + +#ifndef NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY +#define NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY 0 +#endif + +// NRFX_TIMER_DEFAULT_CONFIG_MODE - Timer mode or operation + +// <0=> Timer +// <1=> Counter + +#ifndef NRFX_TIMER_DEFAULT_CONFIG_MODE +#define NRFX_TIMER_DEFAULT_CONFIG_MODE 0 +#endif + +// NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH - Timer counter bit width + +// <0=> 16 bit +// <1=> 8 bit +// <2=> 24 bit +// <3=> 32 bit + +#ifndef NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH +#define NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH 0 +#endif + +// NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY +#define NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_TIMER_CONFIG_LOG_ENABLED +#define NRFX_TIMER_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_TIMER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_TIMER_CONFIG_LOG_LEVEL +#define NRFX_TIMER_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_TIMER_CONFIG_INFO_COLOR +#define NRFX_TIMER_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_TIMER_CONFIG_DEBUG_COLOR +#define NRFX_TIMER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRFX_UARTE_ENABLED - nrfx_uarte - UARTE peripheral driver +//========================================================== +#ifndef NRFX_UARTE_ENABLED +#define NRFX_UARTE_ENABLED 0//1 +#endif +// NRFX_UARTE0_ENABLED - Enable UARTE0 instance +#ifndef NRFX_UARTE0_ENABLED +#define NRFX_UARTE0_ENABLED 0 +#endif + +// NRFX_UARTE1_ENABLED - Enable UARTE1 instance +#ifndef NRFX_UARTE1_ENABLED +#define NRFX_UARTE1_ENABLED 0 +#endif + +// NRFX_UARTE_DEFAULT_CONFIG_HWFC - Hardware Flow Control + +// <0=> Disabled +// <1=> Enabled + +#ifndef NRFX_UARTE_DEFAULT_CONFIG_HWFC +#define NRFX_UARTE_DEFAULT_CONFIG_HWFC 0 +#endif + +// NRFX_UARTE_DEFAULT_CONFIG_PARITY - Parity + +// <0=> Excluded +// <14=> Included + +#ifndef NRFX_UARTE_DEFAULT_CONFIG_PARITY +#define NRFX_UARTE_DEFAULT_CONFIG_PARITY 0 +#endif + +// NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3862528=> 14400 baud +// <5152768=> 19200 baud +// <7716864=> 28800 baud +// <8388608=> 31250 baud +// <10289152=> 38400 baud +// <15007744=> 56000 baud +// <15400960=> 57600 baud +// <20615168=> 76800 baud +// <30801920=> 115200 baud +// <61865984=> 230400 baud +// <67108864=> 250000 baud +// <121634816=> 460800 baud +// <251658240=> 921600 baud +// <268435456=> 1000000 baud + +#ifndef NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE +#define NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE 30801920 +#endif + +// NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY +#define NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_UARTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_UARTE_CONFIG_LOG_ENABLED +#define NRFX_UARTE_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_UARTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_UARTE_CONFIG_LOG_LEVEL +#define NRFX_UARTE_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_UARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_UARTE_CONFIG_INFO_COLOR +#define NRFX_UARTE_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_UARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_UARTE_CONFIG_DEBUG_COLOR +#define NRFX_UARTE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRFX_UART_ENABLED - nrfx_uart - UART peripheral driver +//========================================================== +#ifndef NRFX_UART_ENABLED +#define NRFX_UART_ENABLED 0//1 +#endif +// NRFX_UART0_ENABLED - Enable UART0 instance +#ifndef NRFX_UART0_ENABLED +#define NRFX_UART0_ENABLED 0 +#endif + +// NRFX_UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control + +// <0=> Disabled +// <1=> Enabled + +#ifndef NRFX_UART_DEFAULT_CONFIG_HWFC +#define NRFX_UART_DEFAULT_CONFIG_HWFC 0 +#endif + +// NRFX_UART_DEFAULT_CONFIG_PARITY - Parity + +// <0=> Excluded +// <14=> Included + +#ifndef NRFX_UART_DEFAULT_CONFIG_PARITY +#define NRFX_UART_DEFAULT_CONFIG_PARITY 0 +#endif + +// NRFX_UART_DEFAULT_CONFIG_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3866624=> 14400 baud +// <5152768=> 19200 baud +// <7729152=> 28800 baud +// <8388608=> 31250 baud +// <10309632=> 38400 baud +// <15007744=> 56000 baud +// <15462400=> 57600 baud +// <20615168=> 76800 baud +// <30924800=> 115200 baud +// <61845504=> 230400 baud +// <67108864=> 250000 baud +// <123695104=> 460800 baud +// <247386112=> 921600 baud +// <268435456=> 1000000 baud + +#ifndef NRFX_UART_DEFAULT_CONFIG_BAUDRATE +#define NRFX_UART_DEFAULT_CONFIG_BAUDRATE 30924800 +#endif + +// NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY +#define NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// NRFX_UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRFX_UART_CONFIG_LOG_ENABLED +#define NRFX_UART_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_UART_CONFIG_LOG_LEVEL +#define NRFX_UART_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_UART_CONFIG_INFO_COLOR +#define NRFX_UART_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_UART_CONFIG_DEBUG_COLOR +#define NRFX_UART_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// + +// NRF_CLOCK_ENABLED - nrf_drv_clock - CLOCK peripheral driver - legacy layer +//========================================================== +#ifndef NRF_CLOCK_ENABLED +#define NRF_CLOCK_ENABLED 1 +#endif +// CLOCK_CONFIG_LF_SRC - LF Clock Source + +// <0=> RC +// <1=> XTAL +// <2=> Synth +// <131073=> External Low Swing +// <196609=> External Full Swing + +#ifndef CLOCK_CONFIG_LF_SRC +#define CLOCK_CONFIG_LF_SRC 1 +#endif + +// CLOCK_CONFIG_LF_CAL_ENABLED - Calibration enable for LF Clock Source + + +#ifndef CLOCK_CONFIG_LF_CAL_ENABLED +#define CLOCK_CONFIG_LF_CAL_ENABLED 0 +#endif + +// CLOCK_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef CLOCK_CONFIG_IRQ_PRIORITY +#define CLOCK_CONFIG_IRQ_PRIORITY 6 +#endif + +// + +// UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver - legacy layer +//========================================================== +#ifndef UART_ENABLED +#define UART_ENABLED 0//1 +#endif +// UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control + +// <0=> Disabled +// <1=> Enabled + +#ifndef UART_DEFAULT_CONFIG_HWFC +#define UART_DEFAULT_CONFIG_HWFC 0 +#endif + +// UART_DEFAULT_CONFIG_PARITY - Parity + +// <0=> Excluded +// <14=> Included + +#ifndef UART_DEFAULT_CONFIG_PARITY +#define UART_DEFAULT_CONFIG_PARITY 0 +#endif + +// UART_DEFAULT_CONFIG_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3862528=> 14400 baud +// <5152768=> 19200 baud +// <7716864=> 28800 baud +// <10289152=> 38400 baud +// <15400960=> 57600 baud +// <20615168=> 76800 baud +// <30801920=> 115200 baud +// <61865984=> 230400 baud +// <67108864=> 250000 baud +// <121634816=> 460800 baud +// <251658240=> 921600 baud +// <268435456=> 1000000 baud + +#ifndef UART_DEFAULT_CONFIG_BAUDRATE +#define UART_DEFAULT_CONFIG_BAUDRATE 30801920 +#endif + +// UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef UART_DEFAULT_CONFIG_IRQ_PRIORITY +#define UART_DEFAULT_CONFIG_IRQ_PRIORITY 6 +#endif + +// UART_EASY_DMA_SUPPORT - Driver supporting EasyDMA + + +#ifndef UART_EASY_DMA_SUPPORT +#define UART_EASY_DMA_SUPPORT 1 +#endif + +// UART_LEGACY_SUPPORT - Driver supporting Legacy mode + + +#ifndef UART_LEGACY_SUPPORT +#define UART_LEGACY_SUPPORT 0//1 +#endif + +// UART0_ENABLED - Enable UART0 instance +//========================================================== +#ifndef UART0_ENABLED +#define UART0_ENABLED 0//1 +#endif +// UART0_CONFIG_USE_EASY_DMA - Default setting for using EasyDMA + + +#ifndef UART0_CONFIG_USE_EASY_DMA +#define UART0_CONFIG_USE_EASY_DMA 0//1 +#endif + +// + +// UART1_ENABLED - Enable UART1 instance +//========================================================== +#ifndef UART1_ENABLED +#define UART1_ENABLED 0 +#endif +// + +// + +// +//========================================================== + +// nRF_Libraries + +//========================================================== +// APP_FIFO_ENABLED - app_fifo - Software FIFO implementation + + +#ifndef APP_FIFO_ENABLED +#define APP_FIFO_ENABLED 1 +#endif + +// APP_UART_ENABLED - app_uart - UART driver +//========================================================== +#ifndef APP_UART_ENABLED +#define APP_UART_ENABLED 0//1 +#endif +// APP_UART_DRIVER_INSTANCE - UART instance used + +// <0=> 0 + +#ifndef APP_UART_DRIVER_INSTANCE +#define APP_UART_DRIVER_INSTANCE 0 +#endif + +// + +// HARDFAULT_HANDLER_ENABLED - hardfault_default - HardFault default handler for debugging and release + + +#ifndef HARDFAULT_HANDLER_ENABLED +#define HARDFAULT_HANDLER_ENABLED 1 +#endif + +// NRF_BALLOC_ENABLED - nrf_balloc - Block allocator module +//========================================================== +#ifndef NRF_BALLOC_ENABLED +#define NRF_BALLOC_ENABLED 1 +#endif +// NRF_BALLOC_CONFIG_DEBUG_ENABLED - Enables debug mode in the module. +//========================================================== +#ifndef NRF_BALLOC_CONFIG_DEBUG_ENABLED +#define NRF_BALLOC_CONFIG_DEBUG_ENABLED 0 +#endif +// NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS - Number of words used as head guard. <0-255> + + +#ifndef NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS +#define NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS 1 +#endif + +// NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS - Number of words used as tail guard. <0-255> + + +#ifndef NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS +#define NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS 1 +#endif + +// NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED - Enables basic checks in this module. + + +#ifndef NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED +#define NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED 0 +#endif + +// NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED - Enables double memory free check in this module. + + +#ifndef NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED +#define NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED 0 +#endif + +// NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED - Enables free memory corruption check in this module. + + +#ifndef NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED +#define NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED 0 +#endif + +// NRF_BALLOC_CLI_CMDS - Enable CLI commands specific to the module + + +#ifndef NRF_BALLOC_CLI_CMDS +#define NRF_BALLOC_CLI_CMDS 0 +#endif + +// + +// + +// NRF_MEMOBJ_ENABLED - nrf_memobj - Linked memory allocator module + + +#ifndef NRF_MEMOBJ_ENABLED +#define NRF_MEMOBJ_ENABLED 1 +#endif + +// NRF_STRERROR_ENABLED - nrf_strerror - Library for converting error code to string. + + +#ifndef NRF_STRERROR_ENABLED +#define NRF_STRERROR_ENABLED 1 +#endif + +// nrf_fprintf - fprintf function. + +//========================================================== +// NRF_FPRINTF_ENABLED - Enable/disable fprintf module. + + +#ifndef NRF_FPRINTF_ENABLED +#define NRF_FPRINTF_ENABLED 0//1 +#endif + +// NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED - For each printed LF, function will add CR. + + +#ifndef NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED +#define NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED 0//1 +#endif + +// NRF_FPRINTF_DOUBLE_ENABLED - Enable IEEE-754 double precision formatting. + + +#ifndef NRF_FPRINTF_DOUBLE_ENABLED +#define NRF_FPRINTF_DOUBLE_ENABLED 0 +#endif + +// +//========================================================== + +// +//========================================================== + +// nRF_Log + +//========================================================== +// NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend +//========================================================== +#ifndef NRF_LOG_BACKEND_RTT_ENABLED +#define NRF_LOG_BACKEND_RTT_ENABLED 0 +#endif +// NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE - Size of buffer for partially processed strings. +// Size of the buffer is a trade-off between RAM usage and processing. +// if buffer is smaller then strings will often be fragmented. +// It is recommended to use size which will fit typical log and only the +// longer one will be fragmented. + +#ifndef NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE +#define NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE 64 +#endif + +// NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS - Period before retrying writing to RTT +#ifndef NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS +#define NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS 1 +#endif + +// NRF_LOG_BACKEND_RTT_TX_RETRY_CNT - Writing to RTT retries. +// If RTT fails to accept any new data after retries +// module assumes that host is not active and on next +// request it will perform only one write attempt. +// On successful writing, module assumes that host is active +// and scheme with retry is applied again. + +#ifndef NRF_LOG_BACKEND_RTT_TX_RETRY_CNT +#define NRF_LOG_BACKEND_RTT_TX_RETRY_CNT 3 +#endif + +// + +// NRF_LOG_BACKEND_UART_ENABLED - nrf_log_backend_uart - Log UART backend +//========================================================== +#ifndef NRF_LOG_BACKEND_UART_ENABLED +#define NRF_LOG_BACKEND_UART_ENABLED 0//1 +#endif +// NRF_LOG_BACKEND_UART_TX_PIN - UART TX pin +#ifndef NRF_LOG_BACKEND_UART_TX_PIN +#define NRF_LOG_BACKEND_UART_TX_PIN 6 +#endif + +// NRF_LOG_BACKEND_UART_BAUDRATE - Default Baudrate + +// <323584=> 1200 baud +// <643072=> 2400 baud +// <1290240=> 4800 baud +// <2576384=> 9600 baud +// <3862528=> 14400 baud +// <5152768=> 19200 baud +// <7716864=> 28800 baud +// <10289152=> 38400 baud +// <15400960=> 57600 baud +// <20615168=> 76800 baud +// <30801920=> 115200 baud +// <61865984=> 230400 baud +// <67108864=> 250000 baud +// <121634816=> 460800 baud +// <251658240=> 921600 baud +// <268435456=> 1000000 baud + +#ifndef NRF_LOG_BACKEND_UART_BAUDRATE +#define NRF_LOG_BACKEND_UART_BAUDRATE 30801920 +#endif + +// NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE - Size of buffer for partially processed strings. +// Size of the buffer is a trade-off between RAM usage and processing. +// if buffer is smaller then strings will often be fragmented. +// It is recommended to use size which will fit typical log and only the +// longer one will be fragmented. + +#ifndef NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE +#define NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE 64 +#endif + +// + +// NRF_LOG_ENABLED - nrf_log - Logger +//========================================================== +#ifndef NRF_LOG_ENABLED +#define NRF_LOG_ENABLED 0//1 +#endif +// Log message pool - Configuration of log message pool + +//========================================================== +// NRF_LOG_MSGPOOL_ELEMENT_SIZE - Size of a single element in the pool of memory objects. +// If a small value is set, then performance of logs processing +// is degraded because data is fragmented. Bigger value impacts +// RAM memory utilization. The size is set to fit a message with +// a timestamp and up to 2 arguments in a single memory object. + +#ifndef NRF_LOG_MSGPOOL_ELEMENT_SIZE +#define NRF_LOG_MSGPOOL_ELEMENT_SIZE 20 +#endif + +// NRF_LOG_MSGPOOL_ELEMENT_COUNT - Number of elements in the pool of memory objects +// If a small value is set, then it may lead to a deadlock +// in certain cases if backend has high latency and holds +// multiple messages for long time. Bigger value impacts +// RAM memory usage. + +#ifndef NRF_LOG_MSGPOOL_ELEMENT_COUNT +#define NRF_LOG_MSGPOOL_ELEMENT_COUNT 8 +#endif + +// +//========================================================== + +// NRF_LOG_ALLOW_OVERFLOW - Configures behavior when circular buffer is full. + + +// If set then oldest logs are overwritten. Otherwise a +// marker is injected informing about overflow. + +#ifndef NRF_LOG_ALLOW_OVERFLOW +#define NRF_LOG_ALLOW_OVERFLOW 1 +#endif + +// NRF_LOG_BUFSIZE - Size of the buffer for storing logs (in bytes). + + +// Must be power of 2 and multiple of 4. +// If NRF_LOG_DEFERRED = 0 then buffer size can be reduced to minimum. +// <128=> 128 +// <256=> 256 +// <512=> 512 +// <1024=> 1024 +// <2048=> 2048 +// <4096=> 4096 +// <8192=> 8192 +// <16384=> 16384 + +#ifndef NRF_LOG_BUFSIZE +#define NRF_LOG_BUFSIZE 1024 +#endif + +// NRF_LOG_CLI_CMDS - Enable CLI commands for the module. + + +#ifndef NRF_LOG_CLI_CMDS +#define NRF_LOG_CLI_CMDS 0 +#endif + +// NRF_LOG_DEFAULT_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_LOG_DEFAULT_LEVEL +#define NRF_LOG_DEFAULT_LEVEL 3 +#endif + +// NRF_LOG_DEFERRED - Enable deffered logger. + + +// Log data is buffered and can be processed in idle. + +#ifndef NRF_LOG_DEFERRED +#define NRF_LOG_DEFERRED 0//1 +#endif + +// NRF_LOG_FILTERS_ENABLED - Enable dynamic filtering of logs. + + +#ifndef NRF_LOG_FILTERS_ENABLED +#define NRF_LOG_FILTERS_ENABLED 0 +#endif + +// NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED - Enable use of critical region for non deffered mode when flushing logs. + + +// When enabled NRF_LOG_FLUSH is called from critical section when non deffered mode is used. +// Log output will never be corrupted as access to the log backend is exclusive +// but system will spend significant amount of time in critical section + +#ifndef NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED +#define NRF_LOG_NON_DEFFERED_CRITICAL_REGION_ENABLED 0 +#endif + +// NRF_LOG_STR_PUSH_BUFFER_SIZE - Size of the buffer dedicated for strings stored using @ref NRF_LOG_PUSH. + +// <16=> 16 +// <32=> 32 +// <64=> 64 +// <128=> 128 +// <256=> 256 +// <512=> 512 +// <1024=> 1024 + +#ifndef NRF_LOG_STR_PUSH_BUFFER_SIZE +#define NRF_LOG_STR_PUSH_BUFFER_SIZE 128 +#endif + +// NRF_LOG_STR_PUSH_BUFFER_SIZE - Size of the buffer dedicated for strings stored using @ref NRF_LOG_PUSH. + +// <16=> 16 +// <32=> 32 +// <64=> 64 +// <128=> 128 +// <256=> 256 +// <512=> 512 +// <1024=> 1024 + +#ifndef NRF_LOG_STR_PUSH_BUFFER_SIZE +#define NRF_LOG_STR_PUSH_BUFFER_SIZE 128 +#endif + +// NRF_LOG_USES_COLORS - If enabled then ANSI escape code for colors is prefixed to every string +//========================================================== +#ifndef NRF_LOG_USES_COLORS +#define NRF_LOG_USES_COLORS 0 +#endif +// NRF_LOG_COLOR_DEFAULT - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_COLOR_DEFAULT +#define NRF_LOG_COLOR_DEFAULT 0 +#endif + +// NRF_LOG_ERROR_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_ERROR_COLOR +#define NRF_LOG_ERROR_COLOR 2 +#endif + +// NRF_LOG_WARNING_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_WARNING_COLOR +#define NRF_LOG_WARNING_COLOR 4 +#endif + +// + +// NRF_LOG_USES_TIMESTAMP - Enable timestamping + +// Function for getting the timestamp is provided by the user +//========================================================== +#ifndef NRF_LOG_USES_TIMESTAMP +#define NRF_LOG_USES_TIMESTAMP 0 +#endif +// NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY - Default frequency of the timestamp (in Hz) or 0 to use app_timer frequency. +#ifndef NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY +#define NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY 0 +#endif + +// + +// nrf_log module configuration + +//========================================================== +// nrf_log in nRF_Core + +//========================================================== +// NRF_MPU_LIB_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_MPU_LIB_CONFIG_LOG_ENABLED +#define NRF_MPU_LIB_CONFIG_LOG_ENABLED 0 +#endif +// NRF_MPU_LIB_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_MPU_LIB_CONFIG_LOG_LEVEL +#define NRF_MPU_LIB_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_MPU_LIB_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_MPU_LIB_CONFIG_INFO_COLOR +#define NRF_MPU_LIB_CONFIG_INFO_COLOR 0 +#endif + +// NRF_MPU_LIB_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_MPU_LIB_CONFIG_DEBUG_COLOR +#define NRF_MPU_LIB_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_STACK_GUARD_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_STACK_GUARD_CONFIG_LOG_ENABLED +#define NRF_STACK_GUARD_CONFIG_LOG_ENABLED 0 +#endif +// NRF_STACK_GUARD_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_STACK_GUARD_CONFIG_LOG_LEVEL +#define NRF_STACK_GUARD_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_STACK_GUARD_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_STACK_GUARD_CONFIG_INFO_COLOR +#define NRF_STACK_GUARD_CONFIG_INFO_COLOR 0 +#endif + +// NRF_STACK_GUARD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_STACK_GUARD_CONFIG_DEBUG_COLOR +#define NRF_STACK_GUARD_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// TASK_MANAGER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TASK_MANAGER_CONFIG_LOG_ENABLED +#define TASK_MANAGER_CONFIG_LOG_ENABLED 0 +#endif +// TASK_MANAGER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TASK_MANAGER_CONFIG_LOG_LEVEL +#define TASK_MANAGER_CONFIG_LOG_LEVEL 3 +#endif + +// TASK_MANAGER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TASK_MANAGER_CONFIG_INFO_COLOR +#define TASK_MANAGER_CONFIG_INFO_COLOR 0 +#endif + +// TASK_MANAGER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TASK_MANAGER_CONFIG_DEBUG_COLOR +#define TASK_MANAGER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// +//========================================================== + +// nrf_log in nRF_Drivers + +//========================================================== +// CLOCK_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef CLOCK_CONFIG_LOG_ENABLED +#define CLOCK_CONFIG_LOG_ENABLED 0 +#endif +// CLOCK_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef CLOCK_CONFIG_LOG_LEVEL +#define CLOCK_CONFIG_LOG_LEVEL 3 +#endif + +// CLOCK_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef CLOCK_CONFIG_INFO_COLOR +#define CLOCK_CONFIG_INFO_COLOR 0 +#endif + +// CLOCK_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef CLOCK_CONFIG_DEBUG_COLOR +#define CLOCK_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// COMP_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef COMP_CONFIG_LOG_ENABLED +#define COMP_CONFIG_LOG_ENABLED 0 +#endif +// COMP_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef COMP_CONFIG_LOG_LEVEL +#define COMP_CONFIG_LOG_LEVEL 3 +#endif + +// COMP_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef COMP_CONFIG_INFO_COLOR +#define COMP_CONFIG_INFO_COLOR 0 +#endif + +// COMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef COMP_CONFIG_DEBUG_COLOR +#define COMP_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// GPIOTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef GPIOTE_CONFIG_LOG_ENABLED +#define GPIOTE_CONFIG_LOG_ENABLED 0 +#endif +// GPIOTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef GPIOTE_CONFIG_LOG_LEVEL +#define GPIOTE_CONFIG_LOG_LEVEL 3 +#endif + +// GPIOTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef GPIOTE_CONFIG_INFO_COLOR +#define GPIOTE_CONFIG_INFO_COLOR 0 +#endif + +// GPIOTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef GPIOTE_CONFIG_DEBUG_COLOR +#define GPIOTE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// LPCOMP_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef LPCOMP_CONFIG_LOG_ENABLED +#define LPCOMP_CONFIG_LOG_ENABLED 0 +#endif +// LPCOMP_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef LPCOMP_CONFIG_LOG_LEVEL +#define LPCOMP_CONFIG_LOG_LEVEL 3 +#endif + +// LPCOMP_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef LPCOMP_CONFIG_INFO_COLOR +#define LPCOMP_CONFIG_INFO_COLOR 0 +#endif + +// LPCOMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef LPCOMP_CONFIG_DEBUG_COLOR +#define LPCOMP_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// MAX3421E_HOST_CONFIG_LOG_ENABLED - Enable logging in the module +//========================================================== +#ifndef MAX3421E_HOST_CONFIG_LOG_ENABLED +#define MAX3421E_HOST_CONFIG_LOG_ENABLED 0 +#endif +// MAX3421E_HOST_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef MAX3421E_HOST_CONFIG_LOG_LEVEL +#define MAX3421E_HOST_CONFIG_LOG_LEVEL 3 +#endif + +// MAX3421E_HOST_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef MAX3421E_HOST_CONFIG_INFO_COLOR +#define MAX3421E_HOST_CONFIG_INFO_COLOR 0 +#endif + +// MAX3421E_HOST_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef MAX3421E_HOST_CONFIG_DEBUG_COLOR +#define MAX3421E_HOST_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRFX_USBD_CONFIG_LOG_ENABLED - Enable logging in the module +//========================================================== +#ifndef NRFX_USBD_CONFIG_LOG_ENABLED +#define NRFX_USBD_CONFIG_LOG_ENABLED 0 +#endif +// NRFX_USBD_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRFX_USBD_CONFIG_LOG_LEVEL +#define NRFX_USBD_CONFIG_LOG_LEVEL 3 +#endif + +// NRFX_USBD_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_USBD_CONFIG_INFO_COLOR +#define NRFX_USBD_CONFIG_INFO_COLOR 0 +#endif + +// NRFX_USBD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRFX_USBD_CONFIG_DEBUG_COLOR +#define NRFX_USBD_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// PDM_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef PDM_CONFIG_LOG_ENABLED +#define PDM_CONFIG_LOG_ENABLED 0 +#endif +// PDM_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PDM_CONFIG_LOG_LEVEL +#define PDM_CONFIG_LOG_LEVEL 3 +#endif + +// PDM_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PDM_CONFIG_INFO_COLOR +#define PDM_CONFIG_INFO_COLOR 0 +#endif + +// PDM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PDM_CONFIG_DEBUG_COLOR +#define PDM_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// PPI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef PPI_CONFIG_LOG_ENABLED +#define PPI_CONFIG_LOG_ENABLED 0 +#endif +// PPI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PPI_CONFIG_LOG_LEVEL +#define PPI_CONFIG_LOG_LEVEL 3 +#endif + +// PPI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PPI_CONFIG_INFO_COLOR +#define PPI_CONFIG_INFO_COLOR 0 +#endif + +// PPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PPI_CONFIG_DEBUG_COLOR +#define PPI_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// PWM_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef PWM_CONFIG_LOG_ENABLED +#define PWM_CONFIG_LOG_ENABLED 0 +#endif +// PWM_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PWM_CONFIG_LOG_LEVEL +#define PWM_CONFIG_LOG_LEVEL 3 +#endif + +// PWM_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PWM_CONFIG_INFO_COLOR +#define PWM_CONFIG_INFO_COLOR 0 +#endif + +// PWM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PWM_CONFIG_DEBUG_COLOR +#define PWM_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// QDEC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef QDEC_CONFIG_LOG_ENABLED +#define QDEC_CONFIG_LOG_ENABLED 0 +#endif +// QDEC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef QDEC_CONFIG_LOG_LEVEL +#define QDEC_CONFIG_LOG_LEVEL 3 +#endif + +// QDEC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef QDEC_CONFIG_INFO_COLOR +#define QDEC_CONFIG_INFO_COLOR 0 +#endif + +// QDEC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef QDEC_CONFIG_DEBUG_COLOR +#define QDEC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// RNG_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef RNG_CONFIG_LOG_ENABLED +#define RNG_CONFIG_LOG_ENABLED 0 +#endif +// RNG_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef RNG_CONFIG_LOG_LEVEL +#define RNG_CONFIG_LOG_LEVEL 3 +#endif + +// RNG_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RNG_CONFIG_INFO_COLOR +#define RNG_CONFIG_INFO_COLOR 0 +#endif + +// RNG_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RNG_CONFIG_DEBUG_COLOR +#define RNG_CONFIG_DEBUG_COLOR 0 +#endif + +// RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED - Enables logging of random numbers. + + +#ifndef RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED +#define RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED 0 +#endif + +// + +// RTC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef RTC_CONFIG_LOG_ENABLED +#define RTC_CONFIG_LOG_ENABLED 0 +#endif +// RTC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef RTC_CONFIG_LOG_LEVEL +#define RTC_CONFIG_LOG_LEVEL 3 +#endif + +// RTC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RTC_CONFIG_INFO_COLOR +#define RTC_CONFIG_INFO_COLOR 0 +#endif + +// RTC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RTC_CONFIG_DEBUG_COLOR +#define RTC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// SAADC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SAADC_CONFIG_LOG_ENABLED +#define SAADC_CONFIG_LOG_ENABLED 0 +#endif +// SAADC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SAADC_CONFIG_LOG_LEVEL +#define SAADC_CONFIG_LOG_LEVEL 3 +#endif + +// SAADC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SAADC_CONFIG_INFO_COLOR +#define SAADC_CONFIG_INFO_COLOR 0 +#endif + +// SAADC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SAADC_CONFIG_DEBUG_COLOR +#define SAADC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// SPIS_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SPIS_CONFIG_LOG_ENABLED +#define SPIS_CONFIG_LOG_ENABLED 0 +#endif +// SPIS_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SPIS_CONFIG_LOG_LEVEL +#define SPIS_CONFIG_LOG_LEVEL 3 +#endif + +// SPIS_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPIS_CONFIG_INFO_COLOR +#define SPIS_CONFIG_INFO_COLOR 0 +#endif + +// SPIS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPIS_CONFIG_DEBUG_COLOR +#define SPIS_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// SPI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SPI_CONFIG_LOG_ENABLED +#define SPI_CONFIG_LOG_ENABLED 0 +#endif +// SPI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SPI_CONFIG_LOG_LEVEL +#define SPI_CONFIG_LOG_LEVEL 3 +#endif + +// SPI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPI_CONFIG_INFO_COLOR +#define SPI_CONFIG_INFO_COLOR 0 +#endif + +// SPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SPI_CONFIG_DEBUG_COLOR +#define SPI_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TIMER_CONFIG_LOG_ENABLED +#define TIMER_CONFIG_LOG_ENABLED 0 +#endif +// TIMER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TIMER_CONFIG_LOG_LEVEL +#define TIMER_CONFIG_LOG_LEVEL 3 +#endif + +// TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TIMER_CONFIG_INFO_COLOR +#define TIMER_CONFIG_INFO_COLOR 0 +#endif + +// TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TIMER_CONFIG_DEBUG_COLOR +#define TIMER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// TWIS_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TWIS_CONFIG_LOG_ENABLED +#define TWIS_CONFIG_LOG_ENABLED 0 +#endif +// TWIS_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TWIS_CONFIG_LOG_LEVEL +#define TWIS_CONFIG_LOG_LEVEL 3 +#endif + +// TWIS_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TWIS_CONFIG_INFO_COLOR +#define TWIS_CONFIG_INFO_COLOR 0 +#endif + +// TWIS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TWIS_CONFIG_DEBUG_COLOR +#define TWIS_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// TWI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef TWI_CONFIG_LOG_ENABLED +#define TWI_CONFIG_LOG_ENABLED 0 +#endif +// TWI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef TWI_CONFIG_LOG_LEVEL +#define TWI_CONFIG_LOG_LEVEL 3 +#endif + +// TWI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TWI_CONFIG_INFO_COLOR +#define TWI_CONFIG_INFO_COLOR 0 +#endif + +// TWI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef TWI_CONFIG_DEBUG_COLOR +#define TWI_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef UART_CONFIG_LOG_ENABLED +#define UART_CONFIG_LOG_ENABLED 0 +#endif +// UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef UART_CONFIG_LOG_LEVEL +#define UART_CONFIG_LOG_LEVEL 3 +#endif + +// UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef UART_CONFIG_INFO_COLOR +#define UART_CONFIG_INFO_COLOR 0 +#endif + +// UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef UART_CONFIG_DEBUG_COLOR +#define UART_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// USBD_CONFIG_LOG_ENABLED - Enable logging in the module +//========================================================== +#ifndef USBD_CONFIG_LOG_ENABLED +#define USBD_CONFIG_LOG_ENABLED 0 +#endif +// USBD_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef USBD_CONFIG_LOG_LEVEL +#define USBD_CONFIG_LOG_LEVEL 3 +#endif + +// USBD_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef USBD_CONFIG_INFO_COLOR +#define USBD_CONFIG_INFO_COLOR 0 +#endif + +// USBD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef USBD_CONFIG_DEBUG_COLOR +#define USBD_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// WDT_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef WDT_CONFIG_LOG_ENABLED +#define WDT_CONFIG_LOG_ENABLED 0 +#endif +// WDT_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef WDT_CONFIG_LOG_LEVEL +#define WDT_CONFIG_LOG_LEVEL 3 +#endif + +// WDT_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef WDT_CONFIG_INFO_COLOR +#define WDT_CONFIG_INFO_COLOR 0 +#endif + +// WDT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef WDT_CONFIG_DEBUG_COLOR +#define WDT_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// +//========================================================== + +// nrf_log in nRF_Libraries + +//========================================================== +// APP_BUTTON_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_BUTTON_CONFIG_LOG_ENABLED +#define APP_BUTTON_CONFIG_LOG_ENABLED 0 +#endif +// APP_BUTTON_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_BUTTON_CONFIG_LOG_LEVEL +#define APP_BUTTON_CONFIG_LOG_LEVEL 3 +#endif + +// APP_BUTTON_CONFIG_INITIAL_LOG_LEVEL - Initial severity level if dynamic filtering is enabled. + + +// If module generates a lot of logs, initial log level can +// be decreased to prevent flooding. Severity level can be +// increased on instance basis. +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_BUTTON_CONFIG_INITIAL_LOG_LEVEL +#define APP_BUTTON_CONFIG_INITIAL_LOG_LEVEL 3 +#endif + +// APP_BUTTON_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_BUTTON_CONFIG_INFO_COLOR +#define APP_BUTTON_CONFIG_INFO_COLOR 0 +#endif + +// APP_BUTTON_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_BUTTON_CONFIG_DEBUG_COLOR +#define APP_BUTTON_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_TIMER_CONFIG_LOG_ENABLED +#define APP_TIMER_CONFIG_LOG_ENABLED 0 +#endif +// APP_TIMER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_TIMER_CONFIG_LOG_LEVEL +#define APP_TIMER_CONFIG_LOG_LEVEL 3 +#endif + +// APP_TIMER_CONFIG_INITIAL_LOG_LEVEL - Initial severity level if dynamic filtering is enabled. + + +// If module generates a lot of logs, initial log level can +// be decreased to prevent flooding. Severity level can be +// increased on instance basis. +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_TIMER_CONFIG_INITIAL_LOG_LEVEL +#define APP_TIMER_CONFIG_INITIAL_LOG_LEVEL 3 +#endif + +// APP_TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_TIMER_CONFIG_INFO_COLOR +#define APP_TIMER_CONFIG_INFO_COLOR 0 +#endif + +// APP_TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_TIMER_CONFIG_DEBUG_COLOR +#define APP_TIMER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED +#define APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL +#define APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_CDC_ACM_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_CDC_ACM_CONFIG_INFO_COLOR +#define APP_USBD_CDC_ACM_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR +#define APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_CONFIG_LOG_ENABLED - Enable logging in the module. +//========================================================== +#ifndef APP_USBD_CONFIG_LOG_ENABLED +#define APP_USBD_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_CONFIG_LOG_LEVEL +#define APP_USBD_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_CONFIG_INFO_COLOR +#define APP_USBD_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_CONFIG_DEBUG_COLOR +#define APP_USBD_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_DUMMY_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_USBD_DUMMY_CONFIG_LOG_ENABLED +#define APP_USBD_DUMMY_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_DUMMY_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_DUMMY_CONFIG_LOG_LEVEL +#define APP_USBD_DUMMY_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_DUMMY_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_DUMMY_CONFIG_INFO_COLOR +#define APP_USBD_DUMMY_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_DUMMY_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_DUMMY_CONFIG_DEBUG_COLOR +#define APP_USBD_DUMMY_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_MSC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_USBD_MSC_CONFIG_LOG_ENABLED +#define APP_USBD_MSC_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_MSC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_MSC_CONFIG_LOG_LEVEL +#define APP_USBD_MSC_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_MSC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_MSC_CONFIG_INFO_COLOR +#define APP_USBD_MSC_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_MSC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_MSC_CONFIG_DEBUG_COLOR +#define APP_USBD_MSC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED +#define APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED 0 +#endif +// APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL +#define APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL 3 +#endif + +// APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR +#define APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR 0 +#endif + +// APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR +#define APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_ATFIFO_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_ATFIFO_CONFIG_LOG_ENABLED +#define NRF_ATFIFO_CONFIG_LOG_ENABLED 0 +#endif +// NRF_ATFIFO_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_ATFIFO_CONFIG_LOG_LEVEL +#define NRF_ATFIFO_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_ATFIFO_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_ATFIFO_CONFIG_INFO_COLOR +#define NRF_ATFIFO_CONFIG_INFO_COLOR 0 +#endif + +// NRF_ATFIFO_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_ATFIFO_CONFIG_DEBUG_COLOR +#define NRF_ATFIFO_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_BALLOC_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_BALLOC_CONFIG_LOG_ENABLED +#define NRF_BALLOC_CONFIG_LOG_ENABLED 0 +#endif +// NRF_BALLOC_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BALLOC_CONFIG_LOG_LEVEL +#define NRF_BALLOC_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL - Initial severity level if dynamic filtering is enabled. + + +// If module generates a lot of logs, initial log level can +// be decreased to prevent flooding. Severity level can be +// increased on instance basis. +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL +#define NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL 3 +#endif + +// NRF_BALLOC_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BALLOC_CONFIG_INFO_COLOR +#define NRF_BALLOC_CONFIG_INFO_COLOR 0 +#endif + +// NRF_BALLOC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BALLOC_CONFIG_DEBUG_COLOR +#define NRF_BALLOC_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED +#define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED 0 +#endif +// NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL +#define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR +#define NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR 0 +#endif + +// NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR +#define NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED +#define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED 0 +#endif +// NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL +#define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR +#define NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR 0 +#endif + +// NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR +#define NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED +#define NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED 0 +#endif +// NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL +#define NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR +#define NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR 0 +#endif + +// NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR +#define NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED +#define NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED 0 +#endif +// NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL +#define NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_CLI_BLE_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_BLE_UART_CONFIG_INFO_COLOR +#define NRF_CLI_BLE_UART_CONFIG_INFO_COLOR 0 +#endif + +// NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR +#define NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED +#define NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED 0 +#endif +// NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL +#define NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR +#define NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR 0 +#endif + +// NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR +#define NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_CLI_UART_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_CLI_UART_CONFIG_LOG_ENABLED +#define NRF_CLI_UART_CONFIG_LOG_ENABLED 0 +#endif +// NRF_CLI_UART_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_CLI_UART_CONFIG_LOG_LEVEL +#define NRF_CLI_UART_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_CLI_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_UART_CONFIG_INFO_COLOR +#define NRF_CLI_UART_CONFIG_INFO_COLOR 0 +#endif + +// NRF_CLI_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_CLI_UART_CONFIG_DEBUG_COLOR +#define NRF_CLI_UART_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_LIBUARTE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_LIBUARTE_CONFIG_LOG_ENABLED +#define NRF_LIBUARTE_CONFIG_LOG_ENABLED 0 +#endif +// NRF_LIBUARTE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_LIBUARTE_CONFIG_LOG_LEVEL +#define NRF_LIBUARTE_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_LIBUARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LIBUARTE_CONFIG_INFO_COLOR +#define NRF_LIBUARTE_CONFIG_INFO_COLOR 0 +#endif + +// NRF_LIBUARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LIBUARTE_CONFIG_DEBUG_COLOR +#define NRF_LIBUARTE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_MEMOBJ_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_MEMOBJ_CONFIG_LOG_ENABLED +#define NRF_MEMOBJ_CONFIG_LOG_ENABLED 0 +#endif +// NRF_MEMOBJ_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_MEMOBJ_CONFIG_LOG_LEVEL +#define NRF_MEMOBJ_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_MEMOBJ_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_MEMOBJ_CONFIG_INFO_COLOR +#define NRF_MEMOBJ_CONFIG_INFO_COLOR 0 +#endif + +// NRF_MEMOBJ_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_MEMOBJ_CONFIG_DEBUG_COLOR +#define NRF_MEMOBJ_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_PWR_MGMT_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_PWR_MGMT_CONFIG_LOG_ENABLED +#define NRF_PWR_MGMT_CONFIG_LOG_ENABLED 0 +#endif +// NRF_PWR_MGMT_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_PWR_MGMT_CONFIG_LOG_LEVEL +#define NRF_PWR_MGMT_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_PWR_MGMT_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_PWR_MGMT_CONFIG_INFO_COLOR +#define NRF_PWR_MGMT_CONFIG_INFO_COLOR 0 +#endif + +// NRF_PWR_MGMT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_PWR_MGMT_CONFIG_DEBUG_COLOR +#define NRF_PWR_MGMT_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_QUEUE_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_QUEUE_CONFIG_LOG_ENABLED +#define NRF_QUEUE_CONFIG_LOG_ENABLED 0 +#endif +// NRF_QUEUE_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_QUEUE_CONFIG_LOG_LEVEL +#define NRF_QUEUE_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic filtering is enabled + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL +#define NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL 3 +#endif + +// NRF_QUEUE_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_QUEUE_CONFIG_INFO_COLOR +#define NRF_QUEUE_CONFIG_INFO_COLOR 0 +#endif + +// NRF_QUEUE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_QUEUE_CONFIG_DEBUG_COLOR +#define NRF_QUEUE_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_SDH_ANT_LOG_ENABLED - Enable logging in SoftDevice handler (ANT) module. +//========================================================== +#ifndef NRF_SDH_ANT_LOG_ENABLED +#define NRF_SDH_ANT_LOG_ENABLED 0 +#endif +// NRF_SDH_ANT_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SDH_ANT_LOG_LEVEL +#define NRF_SDH_ANT_LOG_LEVEL 3 +#endif + +// NRF_SDH_ANT_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_ANT_INFO_COLOR +#define NRF_SDH_ANT_INFO_COLOR 0 +#endif + +// NRF_SDH_ANT_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_ANT_DEBUG_COLOR +#define NRF_SDH_ANT_DEBUG_COLOR 0 +#endif + +// + +// NRF_SDH_BLE_LOG_ENABLED - Enable logging in SoftDevice handler (BLE) module. +//========================================================== +#ifndef NRF_SDH_BLE_LOG_ENABLED +#define NRF_SDH_BLE_LOG_ENABLED 0 +#endif +// NRF_SDH_BLE_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SDH_BLE_LOG_LEVEL +#define NRF_SDH_BLE_LOG_LEVEL 3 +#endif + +// NRF_SDH_BLE_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_BLE_INFO_COLOR +#define NRF_SDH_BLE_INFO_COLOR 0 +#endif + +// NRF_SDH_BLE_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_BLE_DEBUG_COLOR +#define NRF_SDH_BLE_DEBUG_COLOR 0 +#endif + +// + +// NRF_SDH_LOG_ENABLED - Enable logging in SoftDevice handler module. +//========================================================== +#ifndef NRF_SDH_LOG_ENABLED +#define NRF_SDH_LOG_ENABLED 0 +#endif +// NRF_SDH_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SDH_LOG_LEVEL +#define NRF_SDH_LOG_LEVEL 3 +#endif + +// NRF_SDH_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_INFO_COLOR +#define NRF_SDH_INFO_COLOR 0 +#endif + +// NRF_SDH_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_DEBUG_COLOR +#define NRF_SDH_DEBUG_COLOR 0 +#endif + +// + +// NRF_SDH_SOC_LOG_ENABLED - Enable logging in SoftDevice handler (SoC) module. +//========================================================== +#ifndef NRF_SDH_SOC_LOG_ENABLED +#define NRF_SDH_SOC_LOG_ENABLED 0 +#endif +// NRF_SDH_SOC_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SDH_SOC_LOG_LEVEL +#define NRF_SDH_SOC_LOG_LEVEL 3 +#endif + +// NRF_SDH_SOC_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_SOC_INFO_COLOR +#define NRF_SDH_SOC_INFO_COLOR 0 +#endif + +// NRF_SDH_SOC_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SDH_SOC_DEBUG_COLOR +#define NRF_SDH_SOC_DEBUG_COLOR 0 +#endif + +// + +// NRF_SORTLIST_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_SORTLIST_CONFIG_LOG_ENABLED +#define NRF_SORTLIST_CONFIG_LOG_ENABLED 0 +#endif +// NRF_SORTLIST_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_SORTLIST_CONFIG_LOG_LEVEL +#define NRF_SORTLIST_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_SORTLIST_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SORTLIST_CONFIG_INFO_COLOR +#define NRF_SORTLIST_CONFIG_INFO_COLOR 0 +#endif + +// NRF_SORTLIST_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_SORTLIST_CONFIG_DEBUG_COLOR +#define NRF_SORTLIST_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// NRF_TWI_SENSOR_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NRF_TWI_SENSOR_CONFIG_LOG_ENABLED +#define NRF_TWI_SENSOR_CONFIG_LOG_ENABLED 0 +#endif +// NRF_TWI_SENSOR_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_TWI_SENSOR_CONFIG_LOG_LEVEL +#define NRF_TWI_SENSOR_CONFIG_LOG_LEVEL 3 +#endif + +// NRF_TWI_SENSOR_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_TWI_SENSOR_CONFIG_INFO_COLOR +#define NRF_TWI_SENSOR_CONFIG_INFO_COLOR 0 +#endif + +// NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR +#define NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// PM_LOG_ENABLED - Enable logging in Peer Manager and its submodules. +//========================================================== +#ifndef PM_LOG_ENABLED +#define PM_LOG_ENABLED 1 +#endif +// PM_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef PM_LOG_LEVEL +#define PM_LOG_LEVEL 3 +#endif + +// PM_LOG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PM_LOG_INFO_COLOR +#define PM_LOG_INFO_COLOR 0 +#endif + +// PM_LOG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef PM_LOG_DEBUG_COLOR +#define PM_LOG_DEBUG_COLOR 0 +#endif + +// + +// +//========================================================== + +// nrf_log in nRF_Serialization + +//========================================================== +// SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED +#define SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED 0 +#endif +// SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL +#define SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL 3 +#endif + +// SER_HAL_TRANSPORT_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SER_HAL_TRANSPORT_CONFIG_INFO_COLOR +#define SER_HAL_TRANSPORT_CONFIG_INFO_COLOR 0 +#endif + +// SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR +#define SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR 0 +#endif + +// + +// +//========================================================== + +// +//========================================================== + +// + +// NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED - nrf_log_str_formatter - Log string formatter + + +#ifndef NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED +#define NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED 1 +#endif + +// +//========================================================== + +// nRF_NFC + +#ifndef NFC_NDEF_LAUNCHAPP_MSG_ENABLED +#define NFC_NDEF_LAUNCHAPP_MSG_ENABLED 1 +#endif + +// NFC_NDEF_LAUNCHAPP_REC_ENABLED - nfc_launchapp_rec - Encoding data for NDEF Application Launching record for NFC Tag + + +#ifndef NFC_NDEF_LAUNCHAPP_REC_ENABLED +#define NFC_NDEF_LAUNCHAPP_REC_ENABLED 1 +#endif +//========================================================== +// NFC_NDEF_MSG_ENABLED - nfc_ndef_msg - NFC NDEF Message generator module +//========================================================== +#ifndef NFC_NDEF_MSG_ENABLED +#define NFC_NDEF_MSG_ENABLED 1 +#endif +// NFC_NDEF_MSG_TAG_TYPE - NFC Tag Type + +// <2=> Type 2 Tag +// <4=> Type 4 Tag + +#ifndef NFC_NDEF_MSG_TAG_TYPE +#define NFC_NDEF_MSG_TAG_TYPE 2 +#endif + +// + +// NFC_NDEF_RECORD_ENABLED - nfc_ndef_record - NFC NDEF Record generator module + + +#ifndef NFC_NDEF_RECORD_ENABLED +#define NFC_NDEF_RECORD_ENABLED 1 +#endif + +// NFC_NDEF_URI_MSG_ENABLED - nfc_uri_msg - Encoding data for NDEF message with URI record for NFC Tag + + +#ifndef NFC_NDEF_URI_MSG_ENABLED +#define NFC_NDEF_URI_MSG_ENABLED 1 +#endif + +// NFC_NDEF_URI_REC_ENABLED - nfc_uri_rec - Encoding data for a URI record for NFC Tag + + +#ifndef NFC_NDEF_URI_REC_ENABLED +#define NFC_NDEF_URI_REC_ENABLED 1 +#endif + +// NFC_NDEF_TEXT_RECORD_ENABLED - nfc_text_rec - Encoding data for a text record for NFC Tag + + +#ifndef NFC_NDEF_TEXT_RECORD_ENABLED +#define NFC_NDEF_TEXT_RECORD_ENABLED 1 +#endif +// NFC_PLATFORM_ENABLED - nfc_platform - NFC platform module for Clock control. +//========================================================== +#ifndef NFC_PLATFORM_ENABLED +#define NFC_PLATFORM_ENABLED 1 +#endif +// NFC_PLATFORM_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef NFC_PLATFORM_LOG_ENABLED +#define NFC_PLATFORM_LOG_ENABLED 0 +#endif +// NFC_PLATFORM_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NFC_PLATFORM_LOG_LEVEL +#define NFC_PLATFORM_LOG_LEVEL 3 +#endif + +// NFC_PLATFORM_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NFC_PLATFORM_INFO_COLOR +#define NFC_PLATFORM_INFO_COLOR 0 +#endif + +// NFC_PLATFORM_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NFC_PLATFORM_DEBUG_COLOR +#define NFC_PLATFORM_DEBUG_COLOR 0 +#endif + +// + +// + +// +//========================================================== + +// nRF_Segger_RTT + +//========================================================== +// segger_rtt - SEGGER RTT + +//========================================================== +// SEGGER_RTT_CONFIG_BUFFER_SIZE_UP - Size of upstream buffer. +// Note that either @ref NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE +// or this value is actually used. It depends on which one is bigger. + +#ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_UP +#define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 512 +#endif + +// SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS - Maximum number of upstream buffers. +#ifndef SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS +#define SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS 2 +#endif + +// SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN - Size of downstream buffer. +#ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN +#define SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN 16 +#endif + +// SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS - Maximum number of downstream buffers. +#ifndef SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS +#define SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS 2 +#endif + +// SEGGER_RTT_CONFIG_DEFAULT_MODE - RTT behavior if the buffer is full. + + +// The following modes are supported: +// - SKIP - Do not block, output nothing. +// - TRIM - Do not block, output as much as fits. +// - BLOCK - Wait until there is space in the buffer. +// <0=> SKIP +// <1=> TRIM +// <2=> BLOCK_IF_FIFO_FULL + +#ifndef SEGGER_RTT_CONFIG_DEFAULT_MODE +#define SEGGER_RTT_CONFIG_DEFAULT_MODE 0 +#endif + +// +//========================================================== + +// +//========================================================== + + +// + +// NRF_SDH_SOC_ENABLED - nrf_sdh_soc - SoftDevice SoC event handler +//========================================================== +#ifndef NRF_SDH_SOC_ENABLED +#define NRF_SDH_SOC_ENABLED 1//0 +#endif +// SoC Observers - Observers and priority levels + +//========================================================== +// NRF_SDH_SOC_OBSERVER_PRIO_LEVELS - Total number of priority levels for SoC observers. +// This setting configures the number of priority levels available for the SoC event handlers. +// The priority level of a handler determines the order in which it receives events, with respect to other handlers. + +#ifndef NRF_SDH_SOC_OBSERVER_PRIO_LEVELS +#define NRF_SDH_SOC_OBSERVER_PRIO_LEVELS 2 +#endif + +// SoC Observers priorities - Invididual priorities + +//========================================================== +// BLE_DFU_SOC_OBSERVER_PRIO +// Priority with which BLE events are dispatched to the DFU Service. + +#ifndef BLE_DFU_SOC_OBSERVER_PRIO +#define BLE_DFU_SOC_OBSERVER_PRIO 1 +#endif + +// CLOCK_CONFIG_SOC_OBSERVER_PRIO +// Priority with which SoC events are dispatched to the Clock driver. + +#ifndef CLOCK_CONFIG_SOC_OBSERVER_PRIO +#define CLOCK_CONFIG_SOC_OBSERVER_PRIO 0 +#endif + +// POWER_CONFIG_SOC_OBSERVER_PRIO +// Priority with which SoC events are dispatched to the Power driver. + +#ifndef POWER_CONFIG_SOC_OBSERVER_PRIO +#define POWER_CONFIG_SOC_OBSERVER_PRIO 0 +#endif +// +//========================================================== + +// +//========================================================== + +// <<< end of configuration section >>> +#endif //SDK_CONFIG_H + diff --git a/libraries/nfc/src/util/sdk_errors.h b/libraries/nfc/src/util/sdk_errors.h new file mode 100644 index 000000000..21eb0b271 --- /dev/null +++ b/libraries/nfc/src/util/sdk_errors.h @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2013 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup sdk_error SDK Error codes + * @{ + * @ingroup app_common + * @{ + * @details Error codes are 32-bit unsigned integers with the most significant 16-bit reserved for + * identifying the module where the error occurred while the least least significant LSB + * are used to provide the cause or nature of error. Each module is assigned a 16-bit + * unsigned integer. Which it will use to identify all errors that occurred in it. 16-bit + * LSB range is with module id as the MSB in the 32-bit error code is reserved for the + * module. As an example, if 0x8800 identifies a certain SDK module, all values from + * 0x88000000 - 0x8800FFFF are reserved for this module. + * It should be noted that common error reasons have been assigned values to make it + * possible to decode error reason easily. As an example, lets module uninitialized has + * been assigned an error code 0x000A0. Then, if application encounters an error code + * 0xZZZZ00A0, it knows that it accessing a certain module without initializing it. + * Apart from this, each module is allowed to define error codes that are not covered by + * the common ones, however, these values are defined in a range that does not conflict + * with common error values. For module, specific error however, it is possible that the + * same error value is used by two different modules to indicated errors of very different + * nature. If error is already defined by the NRF common error codes, these are reused. + * A range is reserved for application as well, it can use this range for defining + * application specific errors. + * + * @note Success code, NRF_SUCCESS, does not include any module identifier. + + */ + +#ifndef SDK_ERRORS_H__ +#define SDK_ERRORS_H__ + +#include +#include "nrf_error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup sdk_err_base Base defined for SDK Modules + * @{ + */ +#define NRF_ERROR_SDK_ERROR_BASE (NRF_ERROR_BASE_NUM + 0x8000) /**< Base value defined for SDK module identifiers. */ +#define NRF_ERROR_SDK_COMMON_ERROR_BASE (NRF_ERROR_BASE_NUM + 0x0080) /**< Base error value to be used for SDK error values. */ +/** @} */ + +/** + * @defgroup sdk_module_codes Codes reserved as identification for module where the error occurred. + * @{ + */ +#define NRF_ERROR_MEMORY_MANAGER_ERR_BASE (0x8100) /**< Base address for Memory Manager related errors. */ +#define NRF_ERROR_PERIPH_DRIVERS_ERR_BASE (0x8200) /**< Base address for Peripheral drivers related errors. */ +#define NRF_ERROR_GAZELLE_ERR_BASE (0x8300) /**< Base address for Gazelle related errors. */ +#define NRF_ERROR_BLE_IPSP_ERR_BASE (0x8400) /**< Base address for BLE IPSP related errors. */ +#define NRF_ERROR_CRYPTO_ERR_BASE (0x8500) /**< Base address for crypto related errors. */ +#define NRF_ERROR_FDS_ERR_BASE (0x8600) /**< Base address for FDS related errors. */ +/** @} */ + + +/** + * @defgroup sdk_iot_errors Codes reserved as identification for IoT errors. + * @{ + */ +#define NRF_ERROR_IOT_ERR_BASE_START (0xA000) +#define NRF_ERROR_IOT_ERR_BASE_STOP (0xAFFF) +/** @} */ + + +/** + * @defgroup sdk_common_errors Codes reserved as identification for common errors. + * @{ + */ +#define NRF_ERROR_MODULE_NOT_INITIALIZED (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0000) ///< Module not initialized +#define NRF_ERROR_MUTEX_INIT_FAILED (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0001) ///< Mutex initialization failed +#define NRF_ERROR_MUTEX_LOCK_FAILED (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0002) ///< Mutex lock failed +#define NRF_ERROR_MUTEX_UNLOCK_FAILED (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0003) ///< Mutex unlock failed +#define NRF_ERROR_MUTEX_COND_INIT_FAILED (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0004) ///< Mutex conditional initialization failed +#define NRF_ERROR_MODULE_ALREADY_INITIALIZED (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0005) ///< Module already initialized +#define NRF_ERROR_STORAGE_FULL (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0006) ///< Storage full +#define NRF_ERROR_API_NOT_IMPLEMENTED (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0010) ///< API not implemented +#define NRF_ERROR_FEATURE_NOT_ENABLED (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0011) ///< Feature not enabled +#define NRF_ERROR_IO_PENDING (NRF_ERROR_SDK_COMMON_ERROR_BASE + 0x0012) ///< Input/Output pending +/** @} */ + + +/** + * @defgroup drv_specific_errors Error / status codes specific to drivers. + * @{ + */ +#define NRF_ERROR_DRV_TWI_ERR_OVERRUN (NRF_ERROR_PERIPH_DRIVERS_ERR_BASE + 0x0000) +#define NRF_ERROR_DRV_TWI_ERR_ANACK (NRF_ERROR_PERIPH_DRIVERS_ERR_BASE + 0x0001) +#define NRF_ERROR_DRV_TWI_ERR_DNACK (NRF_ERROR_PERIPH_DRIVERS_ERR_BASE + 0x0002) +/** @} */ + + +/** + * @defgroup ble_ipsp_errors IPSP codes + * @brief Error and status codes specific to IPSP. + * @{ + */ +#define NRF_ERROR_BLE_IPSP_RX_PKT_TRUNCATED (NRF_ERROR_BLE_IPSP_ERR_BASE + 0x0000) +#define NRF_ERROR_BLE_IPSP_CHANNEL_ALREADY_EXISTS (NRF_ERROR_BLE_IPSP_ERR_BASE + 0x0001) +#define NRF_ERROR_BLE_IPSP_LINK_DISCONNECTED (NRF_ERROR_BLE_IPSP_ERR_BASE + 0x0002) +#define NRF_ERROR_BLE_IPSP_PEER_REJECTED (NRF_ERROR_BLE_IPSP_ERR_BASE + 0x0003) +/* @} */ + + +/** + * @brief API Result. + * + * @details Indicates success or failure of an API procedure. In case of failure, a comprehensive + * error code indicating cause or reason for failure is provided. + * + * Though called an API result, it could used in Asynchronous notifications callback along + * with asynchronous callback as event result. This mechanism is employed when an event + * marks the end of procedure initiated using API. API result, in this case, will only be + * an indicative of whether the procedure has been requested successfully. + */ +typedef uint32_t ret_code_t; + +/** @} */ +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // SDK_ERRORS_H__ diff --git a/libraries/nfc/src/util/sdk_macros.h b/libraries/nfc/src/util/sdk_macros.h new file mode 100644 index 000000000..e2f800687 --- /dev/null +++ b/libraries/nfc/src/util/sdk_macros.h @@ -0,0 +1,224 @@ +/** + * Copyright (c) 2013 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + + * @defgroup sdk_common_macros SDK Common Header + * @ingroup app_common + * @brief Macros for parameter checking and similar tasks + * @{ + */ + +#ifndef SDK_MACROS_H__ +#define SDK_MACROS_H__ + +#include "nrf_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/**@brief Macro for parameter checking. + * + * If @p _cond evaluates to true, does nothing. Otherwise, + * if @p _module ## _PARAM_CHECK_DISABLED is @e not set (default), prints an error message + * if @p _printfn is provided, and returns from the calling function context with code @p _err. + * If @p _module ## _PARAM_CHECK_DISABLED is set, behaves like the ASSERT macro. + * + * Parameter checking implemented using this macro can be optionally turned off for release code. + * Only disable runtime parameter checks if size if a major concern. + * + * @param _module The module name. + * @param _cond The condition to be evaluated. + * @param _err The error to be returned. + * @param _printfn A printf-compatible function used to log the error. + * Leave empty if no logging is needed. + * + * @hideinitializer + */ +/*lint -esym(666, NRF_PARAM_CHECK*) : Expression with side effects passed to macro */ +#define NRF_PARAM_CHECK(_module, _cond, _err, _printfn) \ + do \ + { \ + if ((_cond)) \ + { \ + /* Do nothing. */ \ + } \ + else if (!(_module ## _PARAM_CHECK_DISABLED)) \ + { \ + _printfn("%s check failed in %s() with value 0x%x.", #_cond, __func__, _err); \ + return (_err); \ + } \ + else \ + { \ + ASSERT((_cond)); \ + } \ + } while (0); + + +/**@brief Macro for verifying statement to be true. It will cause the exterior function to return + * err_code if the statement is not true. + * + * @param[in] statement Statement to test. + * @param[in] err_code Error value to return if test was invalid. + * + * @retval nothing, but will cause the exterior function to return @p err_code if @p statement + * is false. + */ +#define VERIFY_TRUE(statement, err_code) \ +do \ +{ \ + if (!(statement)) \ + { \ + return err_code; \ + } \ +} while (0) + + +/**@brief Macro for verifying statement to be true. It will cause the exterior function to return + * if the statement is not true. + * + * @param[in] statement Statement to test. + */ +#define VERIFY_TRUE_VOID(statement) VERIFY_TRUE((statement), ) + + +/**@brief Macro for verifying statement to be false. It will cause the exterior function to return + * err_code if the statement is not false. + * + * @param[in] statement Statement to test. + * @param[in] err_code Error value to return if test was invalid. + * + * @retval nothing, but will cause the exterior function to return @p err_code if @p statement + * is true. + */ +#define VERIFY_FALSE(statement, err_code) \ +do \ +{ \ + if ((statement)) \ + { \ + return err_code; \ + } \ +} while (0) + + +/**@brief Macro for verifying statement to be false. It will cause the exterior function to return + * if the statement is not false. + * + * @param[in] statement Statement to test. + */ +#define VERIFY_FALSE_VOID(statement) VERIFY_FALSE((statement), ) + + +/**@brief Macro for verifying that a function returned NRF_SUCCESS. It will cause the exterior + * function to return error code of statement if it is not @ref NRF_SUCCESS. + * + * @param[in] statement Statement to check against NRF_SUCCESS. + */ +#define VERIFY_SUCCESS(statement) \ +do \ +{ \ + uint32_t _err_code = (uint32_t) (statement); \ + if (_err_code != NRF_SUCCESS) \ + { \ + return _err_code; \ + } \ +} while(0) + + +/**@brief Macro for verifying that a function returned NRF_SUCCESS. It will cause the exterior + * function to return if the err_code is not @ref NRF_SUCCESS. + * + * @param[in] err_code The error code to check. + */ +#define VERIFY_SUCCESS_VOID(err_code) VERIFY_TRUE_VOID((err_code) == NRF_SUCCESS) + + +/**@brief Macro for verifying that the module is initialized. It will cause the exterior function to + * return @ref NRF_ERROR_INVALID_STATE if not. + * + * @note MODULE_INITIALIZED must be defined in each module using this macro. MODULE_INITIALIZED + * should be true if the module is initialized, false if not. + */ +#define VERIFY_MODULE_INITIALIZED() VERIFY_TRUE((MODULE_INITIALIZED), NRF_ERROR_INVALID_STATE) + + +/**@brief Macro for verifying that the module is initialized. It will cause the exterior function to + * return if not. + * + * @note MODULE_INITIALIZED must be defined in each module using this macro. MODULE_INITIALIZED + * should be true if the module is initialized, false if not. + */ +#define VERIFY_MODULE_INITIALIZED_VOID() VERIFY_TRUE_VOID((MODULE_INITIALIZED)) + + +/**@brief Macro for verifying that the module is initialized. It will cause the exterior function to + * return false if not. + * + * @note MODULE_INITIALIZED must be defined in each module using this macro. MODULE_INITIALIZED + * should be true if the module is initialized, false if not. + */ +#define VERIFY_MODULE_INITIALIZED_BOOL() VERIFY_TRUE((MODULE_INITIALIZED), false) + + +/**@brief Macro for verifying that the module is initialized. It will cause the exterior function to + * return if not. + * + * @param[in] param The variable to check if is NULL. + */ +#define VERIFY_PARAM_NOT_NULL(param) VERIFY_FALSE(((param) == NULL), NRF_ERROR_NULL) + + +/**@brief Macro for verifying that the module is initialized. It will cause the exterior function to + * return if not. + * + * @param[in] param The variable to check if is NULL. + */ +#define VERIFY_PARAM_NOT_NULL_VOID(param) VERIFY_FALSE_VOID(((param) == NULL)) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // SDK_MACROS_H__ + diff --git a/libraries/nfc/src/util/sdk_mapped_flags.c b/libraries/nfc/src/util/sdk_mapped_flags.c new file mode 100644 index 000000000..de510b4ac --- /dev/null +++ b/libraries/nfc/src/util/sdk_mapped_flags.c @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_mapped_flags.h" +#include +#include +#include +#include "compiler_abstraction.h" + + +// Test whether the flag collection type is large enough to hold all the flags. If this fails, +// reduce SDK_MAPPED_FLAGS_N_KEYS or increase the size of sdk_mapped_flags_t. +STATIC_ASSERT((sizeof(sdk_mapped_flags_t) * SDK_MAPPED_FLAGS_N_KEYS_PER_BYTE) >= SDK_MAPPED_FLAGS_N_KEYS); + + +/**@brief Function for setting the state of a flag to true. + * + * @note This function does not check whether the index is valid. + * + * @param[in] p_flags The collection of flags to modify. + * @param[in] index The index of the flag to modify. + */ +static __INLINE void sdk_mapped_flags_set_by_index(sdk_mapped_flags_t * p_flags, uint16_t index) +{ + *p_flags |= (1U << index); +} + + +/**@brief Function for setting the state of a flag to false. + * + * @note This function does not check whether the index is valid. + * + * @param[in] p_flags The collection of flags to modify. + * @param[in] index The index of the flag to modify. + */ +static __INLINE void sdk_mapped_flags_clear_by_index(sdk_mapped_flags_t * p_flags, uint16_t index) +{ + *p_flags &= ~(1U << index); +} + + +/**@brief Function for getting the state of a flag. + * + * @note This function does not check whether the index is valid. + * + * @param[in] p_flags The collection of flags to read. + * @param[in] index The index of the flag to get. + */ +static __INLINE bool sdk_mapped_flags_get_by_index(sdk_mapped_flags_t flags, uint16_t index) +{ + return ((flags & (1 << index)) != 0); +} + + + +uint16_t sdk_mapped_flags_first_key_index_get(sdk_mapped_flags_t flags) +{ + for (uint16_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (sdk_mapped_flags_get_by_index(flags, i)) + { + return i; + } + } + return SDK_MAPPED_FLAGS_INVALID_INDEX; +} + + +void sdk_mapped_flags_update_by_key(uint16_t * p_keys, + sdk_mapped_flags_t * p_flags, + uint16_t key, + bool value) +{ + sdk_mapped_flags_bulk_update_by_key(p_keys, p_flags, 1, key, value); +} + + +void sdk_mapped_flags_bulk_update_by_key(uint16_t * p_keys, + sdk_mapped_flags_t * p_flags, + uint32_t n_flag_collections, + uint16_t key, + bool value) +{ + if ((p_keys != NULL) && (p_flags != NULL) && (n_flag_collections > 0)) + { + for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (p_keys[i] == key) + { + for (uint32_t j = 0; j < n_flag_collections; j++) + { + if (value) + { + sdk_mapped_flags_set_by_index(&p_flags[j], i); + } + else + { + sdk_mapped_flags_clear_by_index(&p_flags[j], i); + } + } + return; + } + } + } +} + + +bool sdk_mapped_flags_get_by_key_w_idx(uint16_t * p_keys, + sdk_mapped_flags_t flags, + uint16_t key, + uint8_t * p_index) +{ + if (p_keys != NULL) + { + for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (p_keys[i] == key) + { + if (p_index != NULL) + { + *p_index = i; + } + return sdk_mapped_flags_get_by_index(flags, i); + } + } + } + if (p_index != NULL) + { + *p_index = SDK_MAPPED_FLAGS_N_KEYS; + } + return false; +} + + +bool sdk_mapped_flags_get_by_key(uint16_t * p_keys, sdk_mapped_flags_t flags, uint16_t key) +{ + if (p_keys != NULL) + { + for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (p_keys[i] == key) + { + return sdk_mapped_flags_get_by_index(flags, i); + } + } + } + return false; +} + + +sdk_mapped_flags_key_list_t sdk_mapped_flags_key_list_get(uint16_t * p_keys, + sdk_mapped_flags_t flags) +{ + sdk_mapped_flags_key_list_t key_list; + key_list.len = 0; + + if (p_keys != NULL) + { + for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (sdk_mapped_flags_get_by_index(flags, i)) + { + key_list.flag_keys[key_list.len++] = p_keys[i]; + } + } + } + + return key_list; +} + + +uint32_t sdk_mapped_flags_n_flags_set(sdk_mapped_flags_t flags) +{ + uint32_t n_flags_set = 0; + + for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++) + { + if (sdk_mapped_flags_get_by_index(flags, i)) + { + n_flags_set += 1; + } + } + return n_flags_set; +} diff --git a/libraries/nfc/src/util/sdk_mapped_flags.h b/libraries/nfc/src/util/sdk_mapped_flags.h new file mode 100644 index 000000000..93075e6e7 --- /dev/null +++ b/libraries/nfc/src/util/sdk_mapped_flags.h @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef SDK_MAPPED_FLAGS_H__ +#define SDK_MAPPED_FLAGS_H__ + +#include +#include +#include "app_util.h" +#include "compiler_abstraction.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * @defgroup sdk_mapped_flags Mapped flags + * @ingroup app_common + * @{ + * @brief Module for writing and reading flags that are associated + * with keys. + * + * @details The flags are represented as bits in a bitmap called a flag collection. The keys + * are uint16_t. Each flag collection contains all flags of the same type, one flag for + * each key. + * + * The mapped flags module does not keep the flag states, nor the list of keys. These are + * provided in the API calls. A key's index in the key list determines which bit in the + * flag collection is associated with it. This module does not ever edit the key list, and + * does not edit flags except in function calls that take the flag collection as a pointer. + * + */ + +#define SDK_MAPPED_FLAGS_N_KEYS 32 /**< The number of keys to keep flags for. This is also the number of flags in a flag collection. If changing this value, you might also need change the width of the sdk_mapped_flags_t type. */ +#define SDK_MAPPED_FLAGS_N_KEYS_PER_BYTE 8 /**< The number of flags that fit in one byte. */ +#define SDK_MAPPED_FLAGS_INVALID_INDEX 0xFFFF /**< A flag index guaranteed to be invalid. */ + +typedef uint32_t sdk_mapped_flags_t; /**< The bitmap to hold flags. Each flag is one bit, and each bit represents the flag state associated with one key. */ + + +/**@brief Type used to present a subset of the registered keys. + */ +typedef struct +{ + uint32_t len; /**< The length of the list. */ + uint16_t flag_keys[SDK_MAPPED_FLAGS_N_KEYS]; /**< The list of keys. */ +} sdk_mapped_flags_key_list_t; + + +/**@brief Function for getting the first index at which the flag is true in the provided + * collection. + * + * @param[in] flags The flag collection to search for a flag set to true. + * + * @return The first index that has its flag set to true. If none were found, the + * function returns @ref SDK_MAPPED_FLAGS_INVALID_INDEX. + */ +uint16_t sdk_mapped_flags_first_key_index_get(sdk_mapped_flags_t flags); + + +/**@brief Function for updating the state of a flag. + * + * @param[in] p_keys The list of associated keys (assumed to have a length of + * @ref SDK_MAPPED_FLAGS_N_KEYS). + * @param[out] p_flags The flag collection to modify. + * @param[in] key The key to modify the flag of. + * @param[in] value The state to set the flag to. + */ +void sdk_mapped_flags_update_by_key(uint16_t * p_keys, + sdk_mapped_flags_t * p_flags, + uint16_t key, + bool value); + + +/**@brief Function for updating the state of the same flag in multiple flag collections. + * + * @details The key and value are the same for all flag collections in the p_flags array. + * + * @param[in] p_keys The list of associated keys (assumed to have a length of + * @ref SDK_MAPPED_FLAGS_N_KEYS). + * @param[out] p_flags The flag collections to modify. + * @param[out] n_flag_collections The number of flag collections in p_flags. + * @param[in] key The key to modify the flag of. + * @param[in] value The state to set the flag to. + */ +void sdk_mapped_flags_bulk_update_by_key(uint16_t * p_keys, + sdk_mapped_flags_t * p_flags, + uint32_t n_flag_collections, + uint16_t key, + bool value); + + +/**@brief Function for getting the state of a specific flag. + * + * @param[in] p_keys The list of associated keys (assumed to have a length of + * @ref SDK_MAPPED_FLAGS_N_KEYS). + * @param[in] flags The flag collection to read from. + * @param[in] key The key to get the flag for. + * + * @return The state of the flag. + */ +bool sdk_mapped_flags_get_by_key(uint16_t * p_keys, sdk_mapped_flags_t flags, uint16_t key); + + +/**@brief Function for getting the state of a specific flag. + * + * @param[in] p_keys The list of associated keys (assumed to have a length of + * @ref SDK_MAPPED_FLAGS_N_KEYS). + * @param[in] flags The flag collection from which to read. + * @param[in] key The key for which to get the flag. + * @param[out] p_index If not NULL, the index of the key. + * + * @return The state of the flag. + */ +bool sdk_mapped_flags_get_by_key_w_idx(uint16_t * p_keys, + sdk_mapped_flags_t flags, + uint16_t key, + uint8_t * p_index); + + +/**@brief Function for getting a list of all keys that have a specific flag set to true. + * + * @param[in] p_keys The list of associated keys (assumed to have a length of + * @ref SDK_MAPPED_FLAGS_N_KEYS). + * @param[in] flags The flag collection to search. + * + * @return The list of keys. + */ +sdk_mapped_flags_key_list_t sdk_mapped_flags_key_list_get(uint16_t * p_keys, + sdk_mapped_flags_t flags); + + +/**@brief Function for getting the number of keys that have a specific flag set to true. + * + * @param[in] flags The flag collection to search. + * + * @return The number of keys. + */ +uint32_t sdk_mapped_flags_n_flags_set(sdk_mapped_flags_t flags); + + +/**@brief Function for querying whether any flags in the collection are set. + * + * @param[in] flags The flag collection to query. + * + * @retval true If one or more flags are set to true. + * @retval false Otherwise. + */ +static __INLINE bool sdk_mapped_flags_any_set(sdk_mapped_flags_t flags) +{ + return (flags != 0); +} + + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* SDK_MAPPED_FLAGS_H__ */ diff --git a/libraries/nfc/src/util/sdk_os.h b/libraries/nfc/src/util/sdk_os.h new file mode 100644 index 000000000..3cc9c9db1 --- /dev/null +++ b/libraries/nfc/src/util/sdk_os.h @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2013 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @cond */ +/**@file + * + * @defgroup sdk_os SDK OS Abstraction + * @ingroup experimental_api + * @details In order to made SDK modules independent of use of an embedded OS, and permit + * application with varied task architecture, SDK abstracts the OS specific + * elements here in order to make all other modules agnostic to the OS or task + * architecture. + * @{ + */ + +#ifndef SDK_OS_H__ +#define SDK_OS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SDK_MUTEX_DEFINE(X) +#define SDK_MUTEX_INIT(X) +#define SDK_MUTEX_LOCK(X) +#define SDK_MUTEX_UNLOCK(X) + +/** + * @defgroup os_data_type Data types. + */ + +/** @} */ +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif // SDK_OS_H__ + diff --git a/libraries/nfc/src/util/sdk_resources.h b/libraries/nfc/src/util/sdk_resources.h new file mode 100644 index 000000000..2d1f7379d --- /dev/null +++ b/libraries/nfc/src/util/sdk_resources.h @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * @brief Definition file for resource usage by SoftDevice, ESB and Gazell. + */ + +#ifndef SDK_RESOURCES_H__ +#define SDK_RESOURCES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (BLE_STACK_SUPPORT_REQD) || defined (ANT_STACK_SUPPORT_REQD) // defined(SOFTDEVICE_PRESENT) || + #include "nrf_sd_def.h" +#else + #define SD_PPI_RESTRICTED 0uL /**< 1 if PPI peripheral is restricted, 0 otherwise. */ + #define SD_PPI_CHANNELS_USED 0uL /**< PPI channels utilized by SotfDevice (not available to th spplication). */ + #define SD_PPI_GROUPS_USED 0uL /**< PPI groups utilized by SotfDevice (not available to th spplication). */ + #define SD_TIMERS_USED 0uL /**< Timers used by SoftDevice. */ + #define SD_SWI_USED 0uL /**< Software interrupts used by SoftDevice. */ +#endif + +#ifdef GAZELL_PRESENT + #include "nrf_gzll_resources.h" +#else + #define GZLL_PPI_CHANNELS_USED 0uL /**< PPI channels utilized by Gazell (not available to th spplication). */ + #define GZLL_TIMERS_USED 0uL /**< Timers used by Gazell. */ + #define GZLL_SWI_USED 0uL /**< Software interrupts used by Gazell */ +#endif + +#ifdef ESB_PRESENT + #include "nrf_esb_resources.h" +#else + #define ESB_PPI_CHANNELS_USED 0uL /**< PPI channels utilized by ESB (not available to th spplication). */ + #define ESB_TIMERS_USED 0uL /**< Timers used by ESB. */ + #define ESB_SWI_USED 0uL /**< Software interrupts used by ESB */ +#endif + +#define NRF_PPI_CHANNELS_USED (SD_PPI_CHANNELS_USED | GZLL_PPI_CHANNELS_USED | ESB_PPI_CHANNELS_USED) +#define NRF_PPI_GROUPS_USED (SD_PPI_GROUPS_USED) +#define NRF_SWI_USED (SD_SWI_USED | GZLL_SWI_USED | ESB_SWI_USED) +#define NRF_TIMERS_USED (SD_TIMERS_USED | GZLL_TIMERS_USED | ESB_TIMERS_USED) + +#ifdef __cplusplus +} +#endif + +#endif // SDK_RESOURCES_H__