From 51b98a5b33c3d1f4b367f0b14b7d5d69252f783e Mon Sep 17 00:00:00 2001 From: Orly Natan Date: Thu, 19 Apr 2018 13:31:31 +1000 Subject: [PATCH] cleaning up for public release --- source/FirmataClient/.DS_Store | Bin 0 -> 6148 bytes .../FirmataClient.xcodeproj/project.pbxproj | 312 +++++ .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 25372 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 55 + .../xcschemes/FirmataClient.xcscheme | 80 ++ .../xcschemes/xcschememanagement.plist | 22 + source/FirmataClient/firmatalib.c | 622 ++++++++++ source/FirmataClient/firmatalib.h | 146 +++ source/FirmataClient/maxlib.c | 259 ++++ source/FirmataClient/maxlib.h | 63 + source/FirmataClient/serial.c | 345 ++++++ source/FirmataClient/serial.h | 54 + source/Info.plist | 28 + source/cr.boardin/.DS_Store | Bin 0 -> 6148 bytes source/cr.boardin/cr.boardin.c | 902 ++++++++++++++ .../cr.boardin.xcodeproj/project.pbxproj | 233 ++++ .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 224284 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 143 +++ .../xcschemes/max-external.xcscheme | 80 ++ .../xcschemes/xcschememanagement.plist | 22 + source/cr.boardout/.DS_Store | Bin 0 -> 6148 bytes source/cr.boardout/cr.boardout.c | 1054 +++++++++++++++++ .../cr.boardout.xcodeproj/project.pbxproj | 236 ++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 48395 bytes .../WorkspaceSettings.xcsettings | 16 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 5 + .../xcschemes/max-external.xcscheme | 80 ++ .../xcschemes/xcschememanagement.plist | 24 + source/maxmspsdk.xcconfig | 70 ++ source/readme.md | 22 + 34 files changed, 4902 insertions(+) create mode 100644 source/FirmataClient/.DS_Store create mode 100644 source/FirmataClient/FirmataClient.xcodeproj/project.pbxproj create mode 100644 source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/FirmataClient.xcscheme create mode 100644 source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100755 source/FirmataClient/firmatalib.c create mode 100755 source/FirmataClient/firmatalib.h create mode 100644 source/FirmataClient/maxlib.c create mode 100644 source/FirmataClient/maxlib.h create mode 100755 source/FirmataClient/serial.c create mode 100755 source/FirmataClient/serial.h create mode 100755 source/Info.plist create mode 100644 source/cr.boardin/.DS_Store create mode 100755 source/cr.boardin/cr.boardin.c create mode 100755 source/cr.boardin/cr.boardin.xcodeproj/project.pbxproj create mode 100644 source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme create mode 100644 source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 source/cr.boardout/.DS_Store create mode 100755 source/cr.boardout/cr.boardout.c create mode 100755 source/cr.boardout/cr.boardout.xcodeproj/project.pbxproj create mode 100644 source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/WorkspaceSettings.xcsettings create mode 100644 source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme create mode 100644 source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100755 source/maxmspsdk.xcconfig create mode 100755 source/readme.md diff --git a/source/FirmataClient/.DS_Store b/source/FirmataClient/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..11d2d6b11c977e2840593bc3be9dad203f6047e0 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8zOq>i@0Z1N%F(jFwA}k>Dz-A;elrSVRI5XrhWJ2Xe zslgorptuufaAU}1C}PNEK#spuhCGH620exfhGd3(h7^WWh608nhJ1#soOHwB`Z5l44+FU;uXk7#SEq%}B65MsPoX z0VD^~3Zg+;K{QA!10#q9HUq4cff1^e5!?*{=>v61Kr~o810&dG5Ff0afe~yo0|O&O zI|C!sW=3cag%P5iff1sefe~yw*lDB0Xb6mk08$7rL)ZeK`rnm-0ayPYqH2^J4S~@R z7?vTx$l?<0;smagvHK5H*MjQP1gJEq4hB`njG%fMAqK9BnIHp-5>R1Kbq~@CqQO-$ WBLf3S>u5s&7C@u)Xb22I2mk + + + + diff --git a/source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate b/source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..8635d2da5164da88c4916dff5212ac36e2343206 GIT binary patch literal 25372 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 + + + + + + + + + + + + + + + + diff --git a/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/FirmataClient.xcscheme b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/FirmataClient.xcscheme new file mode 100644 index 0000000..24eb3e7 --- /dev/null +++ b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/FirmataClient.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..41d4ae5 --- /dev/null +++ b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + FirmataClient.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 0C7CC0C81F904CDA0011BFAE + + primary + + + + + diff --git a/source/FirmataClient/firmatalib.c b/source/FirmataClient/firmatalib.c new file mode 100755 index 0000000..7515098 --- /dev/null +++ b/source/FirmataClient/firmatalib.c @@ -0,0 +1,622 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// +// adapted from firmata_test +// https://github.com/firmata/firmata_test +// with permission to redistribute and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +#include "firmatalib.h" +#include +#include + +// Internals +void firmataclient_domessage(board_t *board); +void firmataclient_parse(board_t *board, const uint8_t *buf, long len); +int firmataclient_initdata(board_t *board); +void *firmataclient_looplistener(void* data); + + + +// Blink the on-board LED 3 times. +int firmataclient_blink(board_t *board) { + + if(board == NULL) return -1; + + // store current mode (in order to reverse it afterwards) + int current_mode = board->pin_info[LED_BUILTIN].mode; + firmataclient_changemode(board, LED_BUILTIN, MODE_OUTPUT); + + // blink 3 times + for (int i=0; i<3; ++i) { + // turn the LED on + firmataclient_senddigitalvalue(board, LED_BUILTIN, 1); + //sleep(1); // in seconds + for(long j=0; j<50000000; ++j); + // turn the LED off + firmataclient_senddigitalvalue(board, LED_BUILTIN, 0); + //sleep(1); // in seconds + for(long j=0; j<50000000; ++j); + } + + // reverse the mode change + firmataclient_changemode(board, LED_BUILTIN, current_mode); + + return 0; +} + +// Returns the Firmata version currently on the board. +char *firmataclient_getfirmware(board_t *board) { + if (board == NULL) return ""; + return board->firmata_name; +} + +// Opens a communiction port to the board. Returns 0 on success, -1 otherwise. +int firmataclient_initport(board_t *board, char *name, int baud) +{ + if (board == NULL || name == NULL) return -1; + + serial_close(board->port); + board->isBoardReady = false; + firmataclient_initdata(board); + serial_open(board->port, name); + + if(serial_open(board->port, name)) { + char *msg = board->port? board->port->error_msg : ""; + sprintf(board->err_msg, "firmataclient_initport: error opening port %s.\n%s", name, msg); + return -1; + } + + if (serial_setbaud(board->port, baud)) { + sprintf(board->err_msg, "firmataclient_initport: error setting baud rate for port %s.", name); + return -1; + } + + /* + The startup strategy is to open the port and immediately + send the REPORT_FIRMWARE message. When we receive the + firmware name reply, then we know the board is ready to + communicate. + + For boards like Arduino which use DTR to reset, they may + reboot the moment the port opens. They will not hear this + REPORT_FIRMWARE message, but when they finish booting up + they will send the firmware message. + + For boards that do not reboot when the port opens, they + will hear this REPORT_FIRMWARE request and send the + response. If this REPORT_FIRMWARE request isn't sent, + these boards will not automatically send this info. + + Arduino boards that reboot on DTR will act like a board + that does not reboot, if DTR is not raised when the + port opens. This program attempts to avoid raising + DTR on windows. (is this possible on Linux and Mac OS-X?) + + Either way, when we hear the REPORT_FIRMWARE reply, we + know the board is alive and ready to communicate. + + */ + + firmataclient_requestfirmware(board); + + sprintf(board->err_msg, ""); + return 0; +} + + +// Sends a firmware query to the board connected. +void firmataclient_requestfirmware(board_t *board) { + + if (board == NULL) return; + + uint8_t buf[3]; + buf[0] = START_SYSEX; + buf[1] = REPORT_FIRMWARE; // read firmata name & version + buf[2] = END_SYSEX; + long r = serial_write(board->port, buf, 3); + if(r > 0){ + board->tx_count += 3; + } + else { + sprintf(board->err_msg, "firmataclient_requestfirmware: error writing to board\n"); + return; + } +} + +// sends a new value update to the board. +void firmataclient_sendnewvalue(board_t *board, int pin, int newValue) +{ + if (board == NULL) return; + if (pin < 0 || pin >= TOTAL_PINS) return; + + board->pin_info[pin].value = newValue; + + if (pin <= 15 && newValue <= 16383) { + uint8_t buf[3]; + buf[0] = 0xE0 | pin; + buf[1] = newValue & 0x7F; + buf[2] = (newValue >> 7) & 0x7F; + serial_write(board->port, buf, 3); + board->tx_count += 3; + } else { + uint8_t buf[12]; + int len=4; + buf[0] = 0xF0; + buf[1] = 0x6F; + buf[2] = pin; + buf[3] = newValue & 0x7F; + if (newValue > 0x00000080) buf[len++] = (newValue >> 7) & 0x7F; + if (newValue > 0x00004000) buf[len++] = (newValue >> 14) & 0x7F; + if (newValue > 0x00200000) buf[len++] = (newValue >> 21) & 0x7F; + if (newValue > 0x10000000) buf[len++] = (newValue >> 28) & 0x7F; + buf[len++] = 0xF7; + serial_write(board->port, buf, len); + board->tx_count += len; + } +} + +// sends a new mode update to the board. +void firmataclient_changemode(board_t *board, int pin, int newMode) +{ + if (board == NULL) return; + if (pin < 0 || pin > TOTAL_PINS-1) return; + + //printf("Mode Change, pin=%d, %d\n", pin, newMode); + if (newMode != board->pin_info[pin].mode) { + // send the mode change message + uint8_t buf[4]; + buf[0] = 0xF4; + buf[1] = pin; + buf[2] = newMode; + serial_write(board->port, buf, 3); + board->tx_count += 3; + board->pin_info[pin].mode = newMode; + board->pin_info[pin].value = 0; + //firmataclient_updatestatus(board); + } +} + +// Sends a new digital value pin update to the board. +void firmataclient_senddigitalvalue(board_t *board, int pin, int newValue) +{ + if (board == NULL) return; + if (pin < 0 || pin >= TOTAL_PINS) return; + + board->pin_info[pin].value = newValue; + int port_num = pin / 8; + int port_val = 0; + for (int i=0; i<8; i++) { + int p = port_num * 8 + i; + if (board->pin_info[p].mode == MODE_OUTPUT || board->pin_info[p].mode == MODE_PULLUP) { + if (board->pin_info[p].value) port_val |= (1<> 7) & 0x7F; + serial_write(board->port, buf, 3); + board->tx_count += 3; +} + +/* +void firmataclient_setdigitalvalue(board_t *board, int pin, int newValue) +{ + if (board == NULL) return; + if (pin < 0 || pin >= TOTAL_PINS) return; + + board->pin_info[pin].value = newValue; + int port_num = pin / 8; + int port_val = 0; + for (int i=0; i<8; i++) { + int p = port_num * 8 + i; + if (board->pin_info[p].mode == MODE_OUTPUT || board->pin_info[p].mode == MODE_INPUT || board->pin_info[p].mode == MODE_PULLUP) { + if (board->pin_info[p].value) { + port_val |= (1<> 7) & 0x7F; + serial_write(board->port, buf, 3); + board->tx_count += 3; + //firmataclient_updatestatus(board); + +} +*/ + +// Returns true if the board is ready for communication. +bool firmataclient_isboardready(board_t *board) +{ + if (board == NULL) return false; + return board->isBoardReady; +} + +// Waits (blocks) until the board is ready for communication. +int firmataclient_waitForBoardReady(board_t *board) { + if (board == NULL) return -1; + + while(!board->isBoardReady) { + if (! board->isOK) return -1; + firmataclient_onidle(board); + //sleep(100); + } + return 0; +} + + +// Handles message received from board. +void firmataclient_domessage(board_t *board) +{ + if (board == NULL) return; + uint8_t cmd = (board->parse_buf[0] & 0xF0); + + // analog I/O message + if (cmd == 0xE0 && board->parse_count == 3) { //ANALOG_MESSAGE + int analog_ch = (board->parse_buf[0] & 0x0F); + int analog_val = board->parse_buf[1] | (board->parse_buf[2] << 7); + for (int pin=0; pin<128; pin++) { + if (board->pin_info[pin].analog_channel == analog_ch) { + board->pin_info[pin].value = analog_val; + //printf("pin %d is A%d = %d\n", pin, analog_ch, analog_val); //debugging + return; + } + } + return; + } + + // digital I/O message + if (cmd == 0x90 && board->parse_count == 3) { // DIGITAL_MESSAGE + int port_num = (board->parse_buf[0] & 0x0F); + int port_val = board->parse_buf[1] | (board->parse_buf[2] << 7); + int pin = port_num * 8; + //printf("pin= %d port_num = %d, port_val = %d\n", pin, port_num, port_val); //debugging + for (int mask=1; mask & 0xFF; mask <<= 1, pin++) { + if (board->pin_info[pin].mode == MODE_INPUT || board->pin_info[pin].mode == MODE_PULLUP) { + uint32_t val = (port_val & mask) ? 1 : 0; + if (board->pin_info[pin].value != val) { + //printf("pin %d is %d\n", pin, val); //debugging + board->pin_info[pin].value = val; + } + } + } + return; + } + + // Sysex message + if (board->parse_buf[0] == START_SYSEX && board->parse_buf[board->parse_count-1] == END_SYSEX) { + // Sysex message + if (board->parse_buf[1] == REPORT_FIRMWARE) { + int len=0; + for (int i=4; i < board->parse_count-2; i+=2) { + board->firmata_name[len++] = (board->parse_buf[i] & 0x7F) + | ((board->parse_buf[i+1] & 0x7F) << 7); + } + board->firmata_name[len++] = '-'; + board->firmata_name[len++] = board->parse_buf[2] + '0'; + board->firmata_name[len++] = '.'; + board->firmata_name[len++] = board->parse_buf[3] + '0'; + board->firmata_name[len++] = 0; + //printf("firmata name is %s\n", firmata_name); + + // query the board's capabilities only after hearing the + // REPORT_FIRMWARE message. For boards that reset when + // the port open (eg, Arduino with reset=DTR), they are + // not ready to communicate for some time, so the only + // way to reliably query their capabilities is to wait + // until the REPORT_FIRMWARE message is heard. + + uint8_t buf[80]; + len=0; + buf[len++] = START_SYSEX; + buf[len++] = ANALOG_MAPPING_QUERY; // read analog to pin # info + buf[len++] = END_SYSEX; + buf[len++] = START_SYSEX; + buf[len++] = CAPABILITY_QUERY; // read capabilities + buf[len++] = END_SYSEX; + for (int i=0; i<16; i++) { + buf[len++] = 0xC0 | i; // report analog REPORT_ANALOG + buf[len++] = 1; + buf[len++] = 0xD0 | i; // report digital REPORT_DIGITAL + buf[len++] = 1; + } + serial_write(board->port, buf, len); + board->tx_count += len; + board->tx_count += 3; + + } else if (board->parse_buf[1] == CAPABILITY_RESPONSE) { + int pin, i, n; + for (pin=0; pin < 128; pin++) { + board->pin_info[pin].supported_modes = 0; + } + for (i=2, n=0, pin=0; iparse_count; i++) { + if (board->parse_buf[i] == 127) { // default mode (off) + pin++; + n = 0; + continue; + } + if (n == 0) { + // first byte is supported mode + board->pin_info[pin].supported_modes |= (1<parse_buf[i]); + } + n = n ^ 1; + } + + /* commented out the state query so that the pin's initial values on board will be discarded: + // send a state query for every pin with any modes + for (pin=0; pin < 128; pin++) { + uint8_t buf[512]; + int len=0; + if (pin_info[pin].supported_modes) { + buf[len++] = START_SYSEX; + buf[len++] = PIN_STATE_QUERY; + buf[len++] = pin; + buf[len++] = END_SYSEX; + } + serial_write(buf, len); + tx_count += len; + } end commenting out */ + + // note that the board is now ready for communication + // after verfying board's pins capabilities + board->isBoardReady = true; + + } else if (board->parse_buf[1] == ANALOG_MAPPING_RESPONSE) { + int pin=0; + for (int i=2; iparse_count-1; i++) { + board->pin_info[pin].analog_channel = board->parse_buf[i]; + pin++; + } + return; + + } else if (board->parse_buf[1] == PIN_STATE_RESPONSE && board->parse_count >= 6) { + int pin = board->parse_buf[2]; + board->pin_info[pin].mode = board->parse_buf[3]; + board->pin_info[pin].value = board->parse_buf[4]; + if (board->parse_count > 6) board->pin_info[pin].value |= (board->parse_buf[5] << 7); + if (board->parse_count > 7) board->pin_info[pin].value |= (board->parse_buf[6] << 14); + //printf("pin: %d value: %u", pin, pin_info[pin].value); + //printSupportedModes(pin); + + } else if (board->parse_buf[1] == PULSE_IN && board->parse_count >= 5) { + int pin = board->parse_buf[2]; + int val = board->parse_buf[3]; + if (board->parse_count > 5) val |= (board->parse_buf[4] << 7); + if (board->parse_count > 6) val |= (board->parse_buf[5] << 14); + if (board->parse_count > 7) val |= (board->parse_buf[6] << 21); + if (board->parse_count > 8) val |= (board->parse_buf[7] << 28); + //todo? + //board->pin_info[pin].mode = PULSE_IN; + board->pin_info[pin].value = val; + } + + return; + } +} + + +void delayMicroseconds(int t); +long pulseIn(int Echo_pin, bool opp_observable_cond); + +void delayMicroseconds(int t) { } +long pulseIn(int Echo_pin, bool opp_observable_cond) { + return 0; +} + +long firmataclient_pulse_duration(board_t *board, int Trig_pin, int Echo_pin, bool observe) { // Ultrasonic::Timing() + + bool observable_cond = observe; //LOW + bool opp_observable_cond = !observe; //HIGH + + firmataclient_senddigitalvalue(board, Trig_pin, observable_cond); //LOW + delayMicroseconds(2); + firmataclient_senddigitalvalue(board, Trig_pin, opp_observable_cond); //HIGH + delayMicroseconds(10); + firmataclient_senddigitalvalue(board, Trig_pin, observable_cond); //LOW + long duration = pulseIn(Echo_pin, opp_observable_cond); //HIGH + return duration; +} + +// Parses the bytes received from serial. +void firmataclient_parse(board_t *board, const uint8_t *buf, long len) +{ + if (board == NULL) return; + + const uint8_t *p, *end; + + p = buf; + end = p + len; + for (p = buf; p < end; p++) { + uint8_t msn = *p & 0xF0; //0xF0 START_SYSEX; + if (msn == 0xE0 || msn == 0x90|| *p == 0xF9) {// 0xE0 ANALOG_MESSAGE; 0x90 DIGITAL_MESSAGE; 0xF9 REPORT_VERSION + board->parse_command_len = 3; + board->parse_count = 0; + } else if (msn == 0xC0 || msn == 0xD0) { //0xC0 REPORT_ANALOG; 0xD0 REPORT_DIGITAL + board->parse_command_len = 2; + board->parse_count = 0; + } else if (*p == START_SYSEX) { + board->parse_count = 0; + board->parse_command_len = sizeof(board->parse_buf); + } else if (*p == END_SYSEX) { + board->parse_command_len = board->parse_count + 1; + } else if (*p & 0x80) { + board->parse_command_len = 1; + board->parse_count = 0; + } + if (board->parse_count < (int)sizeof(board->parse_buf)) { + board->parse_buf[board->parse_count++] = *p; + } + if (board->parse_count == board->parse_command_len) { + firmataclient_domessage(board); + board->parse_count = board->parse_command_len = 0; + } + } +} + +// Listen for incoming bytes from serial. +void firmataclient_onidle(board_t *board) +{ + if (board == NULL) return; + + uint8_t buf[1024]; + long r; + + r = serial_inputwait(board->port, 40); + if (r > 0) { + r = serial_read(board->port, buf, sizeof(buf)); + if (r < 0) { + // error + board->isOK = false; + return; + } + if (r > 0) { + // parse + board->rx_count += r; + firmataclient_parse(board, buf, r); + //firmataclient_updatestatus(board); + } + } else if (r < 0) { + board->isOK = false; + } +} + +// Resets all fields. +int firmataclient_initdata(board_t *board) +{ + if (board == NULL) return -1; + + board->port = (serial_t *)malloc(sizeof(serial_t)); + if(board->port == NULL) { + sprintf(board->err_msg, "firmataclient_initdata: couldn't allocate new memory"); + return -1; + } + + for (int i=0; i < 128; i++) { + board->pin_info[i].mode = 255; + board->pin_info[i].analog_channel = 127; + board->pin_info[i].supported_modes = 0; + board->pin_info[i].value = 0; + } + + board->tx_count = board->rx_count = 0; + board->parse_count = 0; + board->parse_command_len = 0; + board->firmata_name[0] = '\0'; + board->isOK = true; + board->isBoardReady = false; + board->threadlistening = false; + board->err_msg[0] = '\0'; + + return 0; + +} + +int firmataclient_closeport(board_t *board) { + + if (board == NULL) return 0; + + if(firmataclient_destroylisteningthread(board)) { + char * msg = board->port? board->port->error_msg : ""; + sprintf(board->err_msg, "firmataclient_destroylisteningthread: %s", msg); + return -1; + } + + serial_close(board->port); + //free(board->port); //causing NULL pointer ERROR!!! do not free port. + // firmataclient_initdata(board); + return 0; +} + +// Reads pin values from the board. +void firmataclient_getinputvalues(board_t *board, long *indexArray, long *valuesArray, int pinsCount) { + if (board == NULL) return; + if(pinsCount >= TOTAL_PINS) return; + + for(int i=0; ipin_info[indexArray[i]].value; + } +} + +// Starts an infinite listening thread to recieve board messages. +int firmataclient_launchtlisteninghread(board_t *board) +{ + if (board == NULL) return false; + + // Create the thread using POSIX routines. + pthread_t posixThreadID; + int returnVal; + + returnVal = pthread_attr_init(&(board->attr)); + if(returnVal) { + strcpy(board->err_msg, "err in pthread_attr_init"); + return returnVal; + } + returnVal = pthread_attr_setdetachstate(&(board->attr), PTHREAD_CREATE_DETACHED); + if(returnVal) { + strcpy(board->err_msg, "err in pthread_attr_setdetachstate"); + return returnVal; + } + + // caution: listening thread expects a pointer to struct board_t as input + int threadError = pthread_create(&posixThreadID, &(board->attr), &firmataclient_looplistener, board); + if (threadError) + { + sprintf(board->err_msg, "err in pthread_create"); + return threadError; + } + + board->threadlistening = true; + return 0; + +} + +// Stops listening for incoming board messages (kill the listening thread). +int firmataclient_destroylisteningthread(board_t *board) +{ + if (board == NULL) return 0; + if( ! board->threadlistening) return 0; + + int returnVal = pthread_attr_destroy(&(board->attr)); + if (returnVal != 0) + { + sprintf(board->err_msg, "error killing listening thread"); + printf("error killing listening thread\n"); + return -1; + } + + board->threadlistening = false; + return 0; +} + +// continuously listen to incoming board messages +// and update the pin_info representation of the board pins +// caution: this thread expects a pointer to struct board_t as input +void* firmataclient_looplistener(void* data) +{ + if (data == NULL) return NULL; + + while(true) { + firmataclient_onidle((board_t *)data); + } +} + + + diff --git a/source/FirmataClient/firmatalib.h b/source/FirmataClient/firmatalib.h new file mode 100755 index 0000000..e26437c --- /dev/null +++ b/source/FirmataClient/firmatalib.h @@ -0,0 +1,146 @@ + +#ifndef firmatalib_h +#define firmatalib_h + +#include "serial.h" + +// Constants from the FIRMATA PROTOCOL: +// https://github.com/firmata/arduino + +#define MODE_INPUT 0x00 +#define MODE_OUTPUT 0x01 +#define MODE_ANALOG 0x02 +#define MODE_PWM 0x03 +#define MODE_SERVO 0x04 +#define MODE_SHIFT 0x05 +#define MODE_I2C 0x06 +#define MODE_ONEWIRE 0x07 +#define MODE_STEPPER 0x08 +#define MODE_ENCODER 0x09 +#define MODE_SERIAL 0x0A +#define MODE_PULLUP 0x0B +#define MODE_IGNORE 0x7F + +#define START_SYSEX 0xF0 // start a MIDI Sysex message +#define END_SYSEX 0xF7 // end a MIDI Sysex message +#define PIN_MODE_QUERY 0x72 // ask for current and supported pin modes +#define PIN_MODE_RESPONSE 0x73 // reply with current and supported pin modes +#define DIGITAL_MESSAGE 0x90 // send data for a digital port +#define ANALOG_MESSAGE 0xE0 // send data for an analog pin (or PWM) +#define REPORT_ANALOG 0xC0 // enable analog input by pin # +#define REPORT_DIGITAL 0xD0 // enable digital input by port +#define SET_PIN_MODE 0xF4 // set a pin to INPUT/OUTPUT/PWM/etc +#define REPORT_VERSION 0xF9 // report firmware version +#define SYSTEM_RESET 0xFF // reset from MIDI + +#define SERVO_CONFIG 0x70 // set max angle, minPulse, maxPulse, freq +#define STRING_DATA 0x71 // a string message with 14-bits per char +#define PULSE_IN 0x74 // send a pulse in command (34 bits) +#define SHIFT_DATA 0x75 // a bitstream to/from a shift register +#define I2C_REQUEST 0x76 // send an I2C read/write request +#define I2C_REPLY 0x77 // a reply to an I2C read request +#define I2C_CONFIG 0x78 // config I2C settings such as delay times and power pins +#define EXTENDED_ANALOG 0x6F // analog write (PWM, Servo, etc) to any pin +#define PIN_STATE_QUERY 0x6D // ask for a pin's current mode and value +#define PIN_STATE_RESPONSE 0x6E // reply with pin's current mode and value +#define CAPABILITY_QUERY 0x6B // ask for supported modes and resolution of all pins +#define CAPABILITY_RESPONSE 0x6C // reply with supported modes and resolution +#define ANALOG_MAPPING_QUERY 0x69 // ask for mapping of analog to pin numbers +#define ANALOG_MAPPING_RESPONSE 0x6A // reply with mapping info +#define REPORT_FIRMWARE 0x79 // report name and version of the firmware +#define SAMPLING_INTERVAL 0x7A // set the poll rate of the main loop +#define SYSEX_NON_REALTIME 0x7E // MIDI Reserved for non-realtime messages +#define SYSEX_REALTIME 0x7F // MIDI Reserved for realtime messages + +// Constants for the client +#define TOTAL_PINS 128 +#define BUF_SIZE 4096 +#define LED_BUILTIN 13 +#define MODE_OFF 255 + + +// this struct follows the firmata transmitted data protocol +typedef struct { + uint8_t mode; // current pin mode + uint8_t analog_channel; // corresponding analog channel on board + uint64_t supported_modes; // all possible modes for the pin + uint32_t value; // pin value +} pin_t; + + +// this struct supports the client library +typedef struct { + + // serial communication with the board + serial_t *port; + + // board pins information + pin_t pin_info[TOTAL_PINS]; + + // firmware name + char firmata_name[140]; + + // read and write counters (in bytes) + unsigned int rx_count, tx_count; + + // parser utilities + int parse_count; + int parse_command_len; + uint8_t parse_buf[BUF_SIZE]; + + // board state (for serial communication) + bool isOK; + bool isBoardReady; + + // POSIX thread - infinite listener + pthread_attr_t attr; + bool threadlistening; + + // latest error message + char err_msg[4000]; + +} board_t; + +// Blink the on-board LED 3 times. +int firmataclient_blink(board_t *board); + +// Sends a firmware query to the board connected. +void firmataclient_requestfirmware(board_t *board); + +// Opens a communiction port to the board. Returns 0 on success, -1 otherwise. +int firmataclient_initport(board_t *board, char *name, int baud); + +// Sends a new value pin update to the board. +void firmataclient_sendnewvalue(board_t *board, int pin, int newValue); + +// Sends a new mode pin update to the board. +void firmataclient_changemode(board_t *board, int pin, int newMode); + +// Sends a new digital value pin update to the board. +void firmataclient_senddigitalvalue(board_t *board, int pin, int newValue); + +// Returns true if the board is ready for communication. +bool firmataclient_isboardready(board_t *board); + +// Disconnects the board. +int firmataclient_closeport(board_t *board); + +// Waits (blocks) until the board is ready for communication. +int firmataclient_waitForBoardReady(board_t *board); + +// Reads pin values from the board. +void firmataclient_getinputvalues(board_t *board, long *indexArray, long *valuesArray, int pinsCount); + +// Returns the Firmata version uploaded to the board. +char *firmataclient_getfirmware(board_t *board); + +// Starts listening for incoming board messages (launch an infinite listening thread). +int firmataclient_launchtlisteninghread(board_t *board); + +// Listen for incoming bytes from serial. +void firmataclient_onidle(board_t *board); + +// Stops listening for incoming board messages (kill the listening thread). +int firmataclient_destroylisteningthread(board_t *board); + +#endif /* firmataclient_h */ diff --git a/source/FirmataClient/maxlib.c b/source/FirmataClient/maxlib.c new file mode 100644 index 0000000..a7562a0 --- /dev/null +++ b/source/FirmataClient/maxlib.c @@ -0,0 +1,259 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + + +#include "maxlib.h" + +void maxclient_setconfig(int *config); +void maxclient_listen(board_t *board); + +// restart listening to board after pausing (unchanged configuration) +void maxclient_restart(); + +// stop communication with the board via the serial port and disconnect +void maxclient_stop(board_t *board); + +// pause (ignore) board updates. +void maxclient_pause(); + +void maxclient_toggleListener(board_t *board); +void maxclient_listenerOn(void); +void maxclient_listenerOff(void); +void maxclient_configurePins(board_t *board, int len_config, int *pins, int *modes); + + +bool listenOn = false; + +// continuously listen to board and update info_pins values +void maxclient_listen(board_t *board) { + listenOn = true; + while(listenOn) { + firmataclient_onidle(board); + } +} + +// connect to board via a serial port, init board to requested configuration +// and start listening to updates from the board +board_t* maxclient_newboard(char *name, int baud, char *err) { + + if( !name || !err) { + err = "invalid arguments"; + return NULL; + } + + // allocate memory for new board + board_t *board = (board_t*)malloc(sizeof(board_t)); + if(board == NULL) { + strcpy(err, "firmataclient_initport: couldn't allocate new memory"); + return NULL; + } + + // open connection to the board, and request REPORT_FIRMATA + int result = firmataclient_initport(board, name, baud); + if (result) { + sprintf(err, "firmataclient_initport: couldn't open port: %s", board->err_msg); + return board; + } + + return board; +} + +// connect to board via a serial port, init board to requested configuration +// and start listening to updates from the board +int maxclient_connect(board_t *board, int len_config, int *pins, int *modes, int func) { + + if (board == NULL) return -1; + + // request firmware report + firmataclient_requestfirmware(board); + + // wait for board to be ready for communication + firmataclient_waitForBoardReady(board); + + // if it's a READ (boardin) connection, then + // launch infine listener thread + if(func == IN_BOARD) { + // start new thread for (continuous) listening to port updates + // should be *before* requesting FIRMATA_REPORT + int err = firmataclient_launchtlisteninghread(board); + if(err) { + sprintf(board->err_msg, "firmataclient_launchtlisteninghread: %s", board->err_msg); + return err; + } + } + + // now that the board is ready for communication + // set the correct pin configuration as requested (if supported), + // or default is MODE_OFF (255) + maxclient_configurePins(board, len_config, pins, modes); + + // now the max object is ready for work. + return 0; +} + +int maxclient_disconnect(board_t *board) { + + if (board == NULL) return -1; + + // for a READ (boardin) connection, kill listener thread + if(firmataclient_destroylisteningthread(board) == -1) { + sprintf(board->err_msg, "maxclient_disconnect: %s", board->err_msg); + return -1; + } + else + return 0; +} + +// update board with requested configuration +void maxclient_configurePins(board_t *board, int len_config, int *pins, int *modes) { + + // note: assumes that the board is ready for communication. + + // set the correct pin configuration as requested (if supported), + // or default is MODE_OFF (255) + if(len_config > 128) len_config = 128; + for(int i=0; i TOTAL_IN_PINS) return; + if(newVal > 180) newVal = 180; + if(newVal < 0) newVal = 0; + // update the board + firmataclient_sendnewvalue(board, pin, newVal); +} + +void maxclient_movePwm(board_t *board, int pin, int newVal) { + // check pin index + if (pin < 0 || pin > TOTAL_IN_PINS) return; + if(newVal > 255) newVal = 255; + if(newVal < 0) newVal = 0; + // update the board + firmataclient_sendnewvalue(board, pin, newVal); +} + +void maxclient_toggleDigital(board_t *board, int pin, int newVal) { + // check pin index + if (pin < 0 || pin > TOTAL_IN_PINS) return; + // update the board + firmataclient_senddigitalvalue(board, pin, newVal); +} + +void maxclient_changeMode(board_t *board, int pin, int mode) { + + // todo: check the mode is supported.. + + // update the board + firmataclient_changemode(board, pin, mode); +} + +void maxclient_toggleListener(board_t *board) { + // listenOn = !listenOn; + if(listenOn) { + //pause + listenOn = false; + } + else { + listenOn = true; + maxclient_listen(board); + } +} + +void maxclient_listenerOn(void) { + listenOn = true; +} + +void maxclient_listenerOff(void) { + listenOn = false; +} + +void maxclient_getInputValues(board_t *board, long *indexArray, long *valuesArray, int pinsCount) { + firmataclient_getinputvalues(board, indexArray, valuesArray, pinsCount); +} + +int maxclient_shutdown(board_t *board) { + return firmataclient_closeport(board); +} + +int maxclient_verifyconfig(board_t *board, int len, int *pins, int *modes) { + + if(board == NULL || pins== NULL || modes == NULL) return -1; + + // compare pins modes between the current (connected) board, and the board that wants to open a connection to the same port (reader vs. writer) + for (int i=0; ipin_info[pins[i]].mode; + if(board_mode != 255 && board_mode != modes[i]) { + sprintf(board->err_msg, "pin %d conflict between connected boards", pins[i]); + return -1; + } + } + + // no conflicts found. + return 0; +} + + +void maxclient_freemgr(board_mgr_t *mgr) { + if (!mgr) return; + mgr->board = NULL; + mgr->board_in = NULL; + mgr->board_out = NULL; + free(mgr); +} + + + + + + + + + + + + + + + + + + + + diff --git a/source/FirmataClient/maxlib.h b/source/FirmataClient/maxlib.h new file mode 100644 index 0000000..316c131 --- /dev/null +++ b/source/FirmataClient/maxlib.h @@ -0,0 +1,63 @@ + +#ifndef maxlib_h +#define maxlib_h + +#include "firmatalib.h" + +#define TOTAL_IN_PINS 20 +#define TOTAL_OUT_PINS 12 + +#define DEFAUTLT_BAUD 57600 +#define MAX_PORTS 20 + +#define IN_BOARD 0 +#define OUT_BOARD 1 + + +typedef struct { + + // ARDUINO Uno board + board_t *board; + + // serial port full name + char *name; + + // double links to the Max interface objects + void *board_in, *board_out; + +} board_mgr_t; + +// connect to board via a serial port, init board to desired configuration +// and start listening to updates from the board. +int maxclient_connect(board_t *board, int len_config, int *pins, int *modes, int funct); + +// close communication with the board, +int maxclient_disconnect(board_t *board); + +// +int maxclient_shutdown(board_t *board); + +// reads input pin values from the board. +void maxclient_getInputValues(board_t *board, long *indexArray, long *valuesArray, int pinsCount); + +// +int maxclient_verifyconfig(board_t *board, int len, int *pins, int *modes); + +// toggle the digital pin value. +void maxclient_toggleDigital(board_t *board, int pin, int newVal); + +// update servo pin value. +// caps the values to range [0-180] degree angles. +void maxclient_moveServo(board_t *board, int pin, int newVal); + +// update pwm pin value. +void maxclient_movePwm(board_t *board, int pin, int newVal); + +// +board_t* maxclient_newboard(char *name, int baud, char *err); + +// Release resources allocated. +void maxclient_freemgr(board_mgr_t *mgr); + +#endif /* maxlib_h */ + diff --git a/source/FirmataClient/serial.c b/source/FirmataClient/serial.c new file mode 100755 index 0000000..d589a91 --- /dev/null +++ b/source/FirmataClient/serial.c @@ -0,0 +1,345 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// +// +// adapted from firmata_test project +// https://github.com/firmata/firmata_test +// with permission to redistribute and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + + +#include "serial.h" + +#include // required for: ioctl function +#include // required for: select function +#include // required for: kIOCalloutDeviceKey, kCFAllocatorDefault consts + +// Internals +void serial_inputdiscard(serial_t *port); +void serial_outputflush(serial_t *port); +int serial_setcontrol(serial_t *port, int dtr, int rts); +int macos_ports(io_iterator_t *PortIterator, char **list, int max_ports, int index); + + +int macos_ports(io_iterator_t *PortIterator, char **list, int max_ports, int index) +{ + io_object_t modemService; + CFTypeRef nameCFstring; + char s[MAXPATHLEN]; + int numPorts = index; + + if( ! PortIterator || ! list) return -1; + + + while ((modemService = IOIteratorNext(*PortIterator)) && numPorts < max_ports) { + nameCFstring = IORegistryEntryCreateCFProperty(modemService, + CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + if (nameCFstring) { + if (CFStringGetCString((const struct __CFString *)nameCFstring, + s, sizeof(s), kCFStringEncodingASCII)) { + list[numPorts] = (char*) malloc((MAXPATHLEN) * sizeof(char)); + strcpy(list[numPorts], s); + numPorts++; + } + CFRelease(nameCFstring); + } + IOObjectRelease(modemService); + } + return numPorts; +} + +// Return a list of all serial ports +int serial_portlist(char **list, int max_ports) +{ + int numPorts = 0; + + // adapted from SerialPortSample.c, by Apple + // http://developer.apple.com/samplecode/SerialPortSample/listing2.html + // and also testserial.c, by Keyspan + // http://www.keyspan.com/downloads-files/developer/macosx/KesypanTestSerial.c + // www.rxtx.org, src/SerialImp.c seems to be based on Keyspan's testserial.c + // neither keyspan nor rxtx properly release memory allocated. + // more documentation at: + // http://developer.apple.com/documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/chapter_2_section_6.html + + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + io_iterator_t serialPortIterator; + + if (IOMasterPort(0, &masterPort) != KERN_SUCCESS) return 0; + + // a usb-serial adaptor is usually considered a "modem", + // especially when it implements the CDC class spec + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (!classesToMatch) return 0; + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDModemType)); + if (IOServiceGetMatchingServices(masterPort, classesToMatch, + &serialPortIterator) != KERN_SUCCESS) return 0; + numPorts += macos_ports(&serialPortIterator, list, max_ports, numPorts); + IOObjectRelease(serialPortIterator); + + /* + // but it might be considered a "rs232 port", so repeat this + // search for rs232 ports + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (!classesToMatch) return 0; + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDRS232Type)); + if (IOServiceGetMatchingServices(masterPort, classesToMatch, + &serialPortIterator) != KERN_SUCCESS) return 0; + numPorts += macos_ports(&serialPortIterator, list, max_ports, numPorts); + IOObjectRelease(serialPortIterator); + */ + + return numPorts; +} + +int serial_isopen(serial_t *port) +{ + if(port == NULL) return 0; + return port->port_is_open; +} + +// Open a port, by name. Return 0 on success, non-zero for error +int serial_open(serial_t *port, char *name) +{ + if(port == NULL) return -1; + + if (name == NULL) { + sprintf(port->error_msg, "serial_open: No port name provided"); + return -1; + } + + serial_close(port); + + int bits = 0; + int port_fd = open(name, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (port_fd < 0) { + sprintf(port->error_msg, "serial_open: Unable to open %s, %s\n", name, strerror(errno)); + //printf("Unable to open %s, %s\n", name, strerror(errno)); + return -1; + } + if (ioctl(port_fd, TIOCEXCL) == -1) { + close(port_fd); + sprintf(port->error_msg, "serial_open: Unable to get exclussive access to port %s\n", name); + //printf("Unable to get exclussive access to port %s\n", name); + return -1; + } + + if (ioctl(port_fd, TIOCMGET, &bits) < 0) { + close(port_fd); + sprintf(port->error_msg, "serial_open: Unable to query serial port signals on %s\n", name); + //printf("Unable to query serial port signals on %s\n", name); + return -1; + } + + bits &= ~(TIOCM_DTR | TIOCM_RTS); + if (ioctl(port_fd, TIOCMSET, &bits) < 0) { + close(port_fd); + sprintf(port->error_msg, "serial_open: Unable to control serial port signals on %s\n", name); + //printf("Unable to control serial port signals on %s\n", name); + return -1; + } + if (tcgetattr(port_fd, &(port->settings_orig)) < 0) { + close(port_fd); + sprintf(port->error_msg, "serial_open: Unable to access baud rate on port %s\n", name); + //printf("Unable to access baud rate on port %s\n", name); + return -1; + } + + port->port_fd = port_fd; + memset(&(port->settings), 0, sizeof(port->settings)); + port->settings.c_cflag = CS8 | CLOCAL | CREAD | HUPCL; + port->settings.c_iflag = IGNBRK | IGNPAR; + serial_setbaud(port, port->baud_rate); + tcflush(port->port_fd, TCIFLUSH); + + strncpy(port->port_name, name, MAXPATHLEN); + port->port_is_open = 1; + + sprintf(port->error_msg, ""); + return 0; +} + +const char *serial_getname(serial_t *port) +{ + if ( ! port || ! port->port_is_open) return ""; + return port->port_name; +} + +// set the baud rate +int serial_setbaud(serial_t *port, int baud) +{ + if (port == NULL || baud <= 0) return -1; + if (!port->port_is_open) return -1; + + if (-1 == cfsetospeed(&(port->settings), (speed_t)baud)) { + sprintf(port->error_msg, "serial_setbaud: Couldn't set BAUD rate"); + return -1; + } + if (-1 == cfsetispeed(&(port->settings), (speed_t)baud)) { + sprintf(port->error_msg, "serial_setbaud: Couldn't set BAUD rate"); + return -1; + } + + if (-1 == tcsetattr(port->port_fd, TCSANOW, &(port->settings))) { + sprintf(port->error_msg, "serial_setbaud: Couldn't set BAUD rate"); + return -1; + } + + port->baud_rate = baud; + sprintf(port->error_msg, ""); + return 0; +} + +// Read from the serial port. Returns only the bytes that are +// already received, up to count. This always returns without delay, +// returning 0 if nothing has been received +long serial_read(serial_t *port, void *ptr, int count) +{ + if (port == NULL || !port->port_is_open) return -1; + if (count <= 0 || ptr == NULL) return 0; + + long n; + n = read(port->port_fd, ptr, count); + //printf("serial_read %ld bytes\n", n); + if (n < 0 && (errno == EAGAIN || errno == EINTR)) return 0; + return n; +} + +// Write to the serial port. This blocks until the data is +// sent (or in a buffer to be sent). All bytes are written, +// unless there is some sort of error. +long serial_write(serial_t *port, const void *ptr, int len) +{ + //printf("Write %d\n", len); + if (port == NULL || !port->port_is_open) return -1; + if (len == 0 || ptr == NULL) return 0; + + long n, written=0; + fd_set wfds; + struct timeval tv; + while (written < len) { + n = write(port->port_fd, (const char *)ptr + written, len - written); + if (n < 0 && (errno == EAGAIN || errno == EINTR)) n = 0; + //printf("Write, n = %d\n", n); + if (n < 0) return -1; + if (n > 0) { + written += n; + } else { + tv.tv_sec = 10; + tv.tv_usec = 0; + FD_ZERO(&wfds); + FD_SET(port->port_fd, &wfds); + n = select(port->port_fd+1, NULL, &wfds, NULL, &tv); + if (n < 0 && errno == EINTR) n = 1; + if (n <= 0) return -1; + } + } + return written; +} + +// Wait up to msec for data to become available for reading. +// return 0 if timeout, or non-zero if one or more bytes are +// received and can be read. -1 if an error occurs +int serial_inputwait(serial_t *port, int msec) +{ + if (port == NULL || !port->port_is_open) return -1; + + fd_set rfds; + struct timeval tv; + tv.tv_sec = msec / 1000; + tv.tv_usec = (msec % 1000) * 1000; + FD_ZERO(&rfds); + FD_SET(port->port_fd, &rfds); + return select(port->port_fd+1, &rfds, NULL, NULL, &tv); +} + +// Discard all received data that hasn't been read +void serial_inputdiscard(serial_t *port) +{ + if (port == NULL || !port->port_is_open) return; + tcflush(port->port_fd, TCIFLUSH); +} + +// Wait for all transmitted data with Write to actually leave the serial port +void serial_outputflush(serial_t *port) +{ + if (port == NULL || !port->port_is_open) return; + tcdrain(port->port_fd); +} + +// set DTR and RTS, 0 = low, 1=high, -1=unchanged +int serial_setcontrol(serial_t *port, int dtr, int rts) +{ + if (port == NULL || !port->port_is_open) return -1; + + // on Mac OS X, "man 4 tty" + int bits; + if (ioctl(port->port_fd, TIOCMGET, &bits) < 0) return -1; + if (dtr == 1) { + bits |= TIOCM_DTR; + } else if (dtr == 0) { + bits &= ~TIOCM_DTR; + } + if (rts == 1) { + bits |= TIOCM_RTS; + } else if (rts == 0) { + bits &= ~TIOCM_RTS; + } + if (ioctl(port->port_fd, TIOCMSET, &bits) < 0) return -1;; + + return 0; +} + +// Close the port +void serial_close(serial_t *port) +{ + if ( ! port || !port->port_is_open) return; + + serial_outputflush(port); + serial_inputdiscard(port); + tcsetattr(port->port_fd, TCSANOW, &(port->settings_orig)); + close(port->port_fd); + + port->port_is_open = 0; + port->baud_rate = 0; + sprintf(port->port_name, ""); + sprintf(port->error_msg, ""); + port->port_fd = 0; +} + + + + + + + + + + + + + + + + + + + + diff --git a/source/FirmataClient/serial.h b/source/FirmataClient/serial.h new file mode 100755 index 0000000..8986839 --- /dev/null +++ b/source/FirmataClient/serial.h @@ -0,0 +1,54 @@ +#ifndef serial_h +#define serial_h + +#include // required for: termios struct +#include // required for: MAXPATHLEN const +#include // required for: io_iterator_t + +#define DEBUG 0 + +typedef struct { + int port_is_open; + int baud_rate; + int port_fd; + char port_name[MAXPATHLEN]; + char error_msg[MAXPATHLEN]; + struct termios settings_orig; + struct termios settings; +} serial_t; + +// Returns 1 if port is open, 0 otherwise. +int serial_isopen(serial_t *port); + +// Opens a port, by name. Returns 0 on success, non-zero for error. +int serial_open(serial_t *port, char *name); + +// Sets the baud rate. Returns 0 on success, -1 otherwise. +int serial_setbaud(serial_t *port, int baud); + +// Reads from the serial port. Returns only the bytes that are +// already received, up to count. This always returns without delay, +// returning 0 if nothing has been received. +long serial_read(serial_t *port, void *ptr, int count); + +// Writes to the serial port. This blocks until the data is +// sent (or in a buffer to be sent). All bytes are written, +// unless there is some sort of error. +// Returns number of bytes written, or -1 otherwise. +long serial_write(serial_t *port, const void *ptr, int len); + +// Waits up to msec for data to become available for reading. +// return 0 if timeout, or non-zero if one or more bytes are +// received and can be read. -1 if an error occurs. +int serial_inputwait(serial_t *port, int msec); + +// Closes the port. +void serial_close(serial_t *port); + +// Return a list of all available serial ports. +int serial_portlist(char **list, int max_ports); + +// Returns the port name, or en empty string if err. +const char *serial_getname(serial_t *port); + +#endif /* serial_h */ diff --git a/source/Info.plist b/source/Info.plist new file mode 100755 index 0000000..c40cc19 --- /dev/null +++ b/source/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${PRODUCT_NAME} + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + ${PRODUCT_VERSION} + CFBundleLongVersionString + ${PRODUCT_NAME} ${PRODUCT_VERSION}, Copyright 2014 Cycling '74 + CFBundlePackageType + iLaX + CFBundleShortVersionString + ${PRODUCT_VERSION} + CFBundleSignature + max2 + CFBundleVersion + ${PRODUCT_VERSION} + CSResourcesFileMapped + + + diff --git a/source/cr.boardin/.DS_Store b/source/cr.boardin/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d3e6ed46f09e7d46f05b51924494f5d274235991 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8zj35RBCIAV8Fop~hRD=a&9@vb0hD3%Uh7^WOhCHa; zV5dNC7hypinsArray[index]; + char *str; + + if(pin<14) str = ""; + else { + str = "A"; + pin -= 14; + // analog pins are numbered as: + // Arduino: A0-A5 + // Firmata: (#14-#19) + } + + if (m == ASSIST_OUTLET) { // outlet messages + + switch(x->modesArray[index]) { + case MODE_INPUT: + sprintf(s, "pin %s%d mode DIGITAL values {0,1}", str, pin); + break; + case MODE_ANALOG: + sprintf(s, "pin A%d mode ANALOG range [0-1023]", pin); + break; + default: + sprintf(s, "pin %s%d is INVALID", str, pin); + } + + } + else { // inlet messages + sprintf(s, "msg inlet"); + } + +} + +void boardin_free(t_boardin *x) { + char err_msg[4000]; + boardin_disconnect(x, err_msg); + + // free allocated outlets + if (x->outlets) { + free(x->outlets); + } + + if (DEBUG) post("boardin object deleted."); //debug +} + +void *boardin_new(t_symbol *s, long argc, t_atom *argv) +{ + t_boardin *x = (t_boardin *)object_alloc(boardin_class); + + // verify memory allocated successfully + if (x == NULL ) { + post( "Max is out of memory, could not create new boardin object."); + return NULL; + } + + boardin_init(x); // todo - remove, for debug + + x->board_ob = NULL; + x->port_index = -1; + + // parse input arguments to set up the board configuration + if (boardin_parseArgs(x, argc, argv) == -1) { + //Error - Invalid arguments + object_error((t_object *)x, "Invalid args, could not create new boardin object."); + //object_free(x); + return (x); + } + + // debug: + //if (DEBUG) + // if(x->att_portname && x->att_baudrate) { + // object_post((t_object *)x, "port name: %s\t rate: %d", x->att_portname->s_name, atoi(x->att_baudrate->s_name)); + // } + + // create int outlets from right to left (highest-right to 1-left) + x->outlets = (void **)malloc(x->pinsCount*sizeof(void *)); + // verify memory allocated successfully + if (x->outlets == NULL ) { + object_error((t_object *)x, "Max is out of memory, could not create new boardin object."); + //boardin_free(x); + return (x); + } + for (int i = x->pinsCount; i > 0;) { + --i; + x->outlets[i] = intout(x); + } + + // boardin object is now all set up + return (x); +} + +int boardin_parseArgs(t_boardin *x, long argc, t_atom *argv) { + + x->port_index = -1; + + // set baud rate to default + char str[20]; + sprintf(str, "%d", DEFAUTLT_BAUD); + x->att_baudrate = gensym(str); + + // parse input arguments + int i = 0; + + // check if the first argument is the port index + if(argv->a_type == A_SYM){ + char *portCh = atom_getsym(argv)->s_name; + //object_post((t_object *)x, "port char: %c", portCh[0]); + int index = portCh[0]-'a'; + //object_post((t_object *)x, "port index: %d", index); + + if (index >= 0 && index <= 'z'-'a') { + x->port_index = index; + i = 1; + } + // check if the baud rate was entered as input + if((i)< argc && (argv+i)->a_type == A_LONG) { + long val = atom_getlong(argv+i); + sprintf(str, "%ld", val); + x->att_baudrate = gensym(str); + i = 2; + } + + } + + // Parse DIGITAL and ANALOG pins lists + bool analogFound = false, digitalFound = false, rangeFound = false; + x->pinsCount = 0; + x->analogPinsCount = 0; + x->digitalPinsCount = 0; + + for (; i < argc; i++) { + + if((argv+i)->a_type == A_SYM) { + + t_symbol *s = atom_getsym(argv+i); + + // if the symbol is digital + if( ! strcmp_case_insensitive(s->s_name, "@digital")) { + //post("Found DIGITAL"); + digitalFound = true; + analogFound = false; + rangeFound = false; + } + // if symbol is analog + else if( ! strcmp_case_insensitive(s->s_name, "@analog")) { + //post("Found ANALOG"); + analogFound = true; + digitalFound = false; + rangeFound = false; + } + // if symbol is range + else if( ! strcmp_case_insensitive(s->s_name, "@range")) { + //post("Found RANGE"); + rangeFound = true; + analogFound = false; + digitalFound = false; + } + else if( ! strcmp_case_insensitive(s->s_name, "A0")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 14; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A1")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 15; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A2")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 16; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A3")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 17; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A4")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 18; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A5")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 19; + argv[i] = a[0]; + --i; + } + else { + // otherwise, unknown attribute symbol + digitalFound = false; + analogFound = false; + rangeFound = false; + // error + object_error((t_object *)x, "forbidden argument %s", s->s_name); + //return -1; + } + } + + else if((argv+i)->a_type == A_LONG) { + + int pin = atom_getlong(argv+i); + + if (analogFound) { + if (!boardin_pinAnalogRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, analog pins range [A0-A5]", pin); + return 0; + } + } + + if (digitalFound && !boardin_pinDigitalRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, digital pins range [0-13, A0-A5]", pin); + return 0; + } + + if((analogFound || digitalFound) && x->pinsCount < TOTAL_IN_PINS) { + + // first check if the requested pin already exists + if(boardin_pinNumAlreadyExists(x, pin)) { + // error + object_error((t_object *)x, "forbidden argument, pin %d already exists", pin); + } + + // we verified the pin does not exist, add it now + else { + x->pinsArray[x->pinsCount] = pin; + x->valuesArray[x->pinsCount] = 0; + if(analogFound) { + x->modesArray[x->pinsCount] = MODE_ANALOG; + x->att_analogPins[x->analogPinsCount] = pin; + x->analogPinsCount++; + } + else if(digitalFound) { + x->modesArray[x->pinsCount] = MODE_INPUT; + x->att_digitalPins[x->digitalPinsCount] = pin; + x->digitalPinsCount++; + } + x->pinsCount++; + } + } + else { + // error + if(x->pinsCount >= TOTAL_IN_PINS) { + object_error((t_object *)x, "forbidden argument, too many pins requested"); + //return -1; + } + object_error((t_object *)x, "forbidden argument, number %d is out of context", pin); + //return -1; + } + } + + else { + // error + object_error((t_object *)x, "forbidden argument, expected format:"); + object_error((t_object *)x, "[port] [@digital pins_list] [@analog pins_list]"); + //return -1; + + } + } + + return 0; +} + +void boardin_bang(t_boardin *x){ + + // update input pin values + maxclient_getInputValues(x->board_ob, x->pinsArray, x->valuesArray, x->pinsCount); + + /* + // todo - remove (for debug only) + object_post((t_object *)x, "bang!"); + for(int i=0; ipinsCount; ++i) { + //x->valuesArray[i] = (i+1); + object_post((t_object *)x, "outlet %d=pin%d=%d", i, x->pinsArray[i], x->valuesArray[i]); + }*/ + + t_atom val; + for(int i=0; ipinsCount; ++i) { + atom_setlong(&val, x->valuesArray[i]); + outlet_int(x->outlets[i], atom_getlong(&val)); + } +} + +void boardin_ports(t_boardin *x){ + boardin_printAvailPorts(x); +} + +void boardin_config(t_boardin *x) { + + char str[4000], str_pin[10]; + long count, *arr; + + str[0] = '\0'; + + // Digital pins + count = x->digitalPinsCount; + arr = x->att_digitalPins; + if(count) { + str[0] = '\0'; + strcat(str, "Digital pins: "); + for (long i=0; ianalogPinsCount; + arr = x->att_analogPins; + if(count) { + str[0] = '\0'; + strcat(str, "Analog pins: "); + for (long i=0; iboard_ob) { + object_post((t_object *)x, "test: board is not connected."); + return; + } + + if ( ! x->board_ob->isBoardReady) { + object_post((t_object *)x, "test: board is not ready for communication."); + return; + } + + if( ! x->isopen) { + object_post((t_object *)x, "test: board is not connected."); + return; + } + object_post((t_object *)x, "test: board is connected."); + + char *firmware = firmataclient_getfirmware(x->board_ob); + object_post((t_object *)x, "test: firmware version %s.", firmware); + object_post((t_object *)x, "test: board is ready for communication."); + + object_post((t_object *)x, "test: onboard LED will now blink 3 times."); + firmataclient_blink(x->board_ob); + + object_post((t_object *)x, "test: Done."); +} + +void boardin_open(t_boardin *x){ + if (x == NULL) return; + + if(x->isopen) { + object_post((t_object *)x, "board is already connected."); + return; + } + + if ( ! x->att_baudrate) { + // set baud rate to default + char str[20]; + sprintf(str, "%d", DEFAUTLT_BAUD); + x->att_baudrate = gensym(str); + } + if(DEBUG) object_post((t_object *)x, "baud rate %s.", x->att_baudrate->s_name); + + // get updated list of available ports + char *ports_avail[MAX_PORTS]; + int num_ports = serial_portlist(ports_avail, MAX_PORTS); + + if (x->port_index == -1) { + object_error((t_object *)x, "port not selected."); + return; + } + if(DEBUG) object_post((t_object *)x, "index of port selected %d.", x->port_index); + + if (x->port_index < 0 || x->port_index >= num_ports) { + object_error((t_object *)x, "port selected is out of bounds of available ports."); + if(DEBUG) object_error((t_object *)x, "index of port selected %d.", x->port_index); + return; + } + + // select the port + x->att_portname = gensym(ports_avail[x->port_index]); + if(x->att_portname == NULL) { + if(DEBUG) object_error((t_object *)x, "port can't be found."); + return; + } + if(DEBUG) object_post((t_object *)x, "port selected %s to open", x->att_portname->s_name); // debug + + // check for other connections + if(x->att_portname->s_thing == NULL) { + + // the port is not in use by other boardin/out objects + if(DEBUG) object_post((t_object *)x, "port has no other connections, %s", x->att_portname->s_name); // debug + board_mgr_t *mgr = (board_mgr_t*)malloc(sizeof(board_mgr_t)); + if(mgr == NULL) { + object_error((t_object *)x, "port can't be opened, Max can't allocate memory."); + return; + } + mgr->board_out = NULL; + mgr->board_in = NULL; + mgr->board = NULL; + x->att_portname->s_thing = (t_object*)mgr; + } + else { + // the port is in use by another boardin/out object + if(DEBUG) object_post((t_object *)x, "OK, port has another connection, %s", x->att_portname->s_name); // debug + } + + + // setup pins configuration as requested + int len_config = x->digitalPinsCount + x->analogPinsCount; + int pins[len_config], modes[len_config]; + + int len = 0; + // digital pins + for(int i=0; idigitalPinsCount; ++i) { + pins[len] = x->att_digitalPins[i]; + modes[len] = MODE_INPUT; + len++; + } + // analog pins + for(int i=0; ianalogPinsCount; ++i) { + pins[len] = x->att_analogPins[i]; // + 14; // analog pins are numbered 14-19 corresponding to A0-A5 + modes[len] = MODE_ANALOG; + len++; + } + + //if(DEBUG) object_post((t_object *)x, "%d pins allocated", len); // debug + //if (DEBUG) { + // for(int i=0; iisopen) { + sprintf(msg, "close: board was not connected."); + return; + } + + if ( ! x->att_portname) { + sprintf(msg, "close: no port selected."); + return; + } + + if(x->att_portname->s_thing == NULL) { + sprintf(msg, "close: board was not connected."); + return; + } + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if(mgr->board_in == NULL) { + sprintf(msg, "close: board was not connected."); + return; + } + + int err = maxclient_disconnect(x->board_ob); // kill listener + if(err == -1) { + sprintf(msg, "close: error disconnecting"); + if (x->board_ob) asprintf(&msg, ": %s", x->board_ob->err_msg); + return; + } + + x->isopen = 0; + mgr->board_in = NULL; + object_post((t_object *)x, "close: diconnected from board."); + + if(mgr->board_out == NULL) { + + // no writer board is connected, release the port altogether + err = maxclient_shutdown(x->board_ob); // close port + if(err == -1) { + sprintf(msg, "close: error shutting serial port down"); + if (x->board_ob) asprintf(&msg, ": %s", x->board_ob->err_msg); + return; + } + + if (DEBUG) object_post((t_object *)x, "close: port %s was shut down", x->att_portname->s_name); + + x->att_portname->s_thing = NULL; + maxclient_freemgr(mgr); + } + + sprintf(msg, "close: boardin is closed."); + return; +} + +void boardin_close_orig(t_boardin *x){ + + if (x == NULL) return; + + if( ! x->isopen) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + if ( ! x->att_portname) { + object_post((t_object *)x, "close: no port selected."); + return; + } + + if(x->att_portname->s_thing == NULL) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if(mgr->board_in == NULL) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + int err = maxclient_disconnect(x->board_ob); // kill listener + if(err == -1) { + object_error((t_object *)x, "close: error disconnecting"); + if (x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + x->isopen = 0; + mgr->board_in = NULL; + object_post((t_object *)x, "close: diconnected from board."); + + if(mgr->board_out == NULL) { + + // no writer board is connected, release the port altogether + err = maxclient_shutdown(x->board_ob); // close port + if(err == -1) { + object_error((t_object *)x, "close: error shutting serial port down"); + if (x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + if (DEBUG) object_post((t_object *)x, "close: port %s was shut down", x->att_portname->s_name); + + x->att_portname->s_thing = NULL; + maxclient_freemgr(mgr); + } + + return; +} + +void boardin_printAvailPorts(t_boardin *x) { + + char *ports_avail[MAX_PORTS]; + + int num_ports = serial_portlist(ports_avail, MAX_PORTS); + + for(int i=0; idigitalPinsCount; ++i) { + if(x->att_digitalPins[i] == pin) + return true; + } + + for (int i = 0; ianalogPinsCount; ++i) { + if(x->att_analogPins[i] == pin) + return true; + } + + return false; +} + +void boardin_init(t_boardin *x) { + + if(x == NULL) + return; + + x->outlets = NULL; + x->pinsCount = 0; + x->att_portname = NULL; + x->att_baudrate = NULL; + x->digitalPinsCount = 0; + x->analogPinsCount = 0; + x->isopen = 0; +} + +void boardin_connectBoard(t_boardin *x, int len, int *pins, int *modes) { + + if(!x || !x->att_portname || !x->att_portname->s_thing) return; + if( !pins || !modes) return; + + + // search symbol port_name. if found, then board is already connected to by a boardout + // object (writer object), then get the pointer to that board object. Otherwise, create + // a new board object and initialise it with the requested baud rate and the rest of + // the pins (read) configuration. + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if (mgr == NULL) { + if (DEBUG) object_error((t_object *)x, "can't connect to port, internal error"); + return; + } + + if(mgr->board_in != NULL) { + // error - there is already another boardin object connected + object_error((t_object *)x, "open: port %s is already occupied with another boardin (reader) object!", x->att_portname->s_name); + return; + } + + else if(mgr->board_out != NULL) { + + if (DEBUG) object_post((t_object *)x, "open: port %s is connected to a Writer (okay)", x->att_portname->s_name); // debug + + if(mgr->board == NULL) { + object_error((t_object *)x, "open: internal error. board can't be reached on %s.", x->att_portname->s_name); + return; + } + + // check if there is any configuration conflict + if(mgr->board == NULL) { + object_error((t_object *)x, "open: can't connect the board. internal error"); + return; + } + + // check baud rate conflict (between existing and the new board) + //if(x->att_baudrate) { + // long current_rate = atol(x->att_baudrate->s_name); + // if(mgr->board->port) { + // long port_rate = mgr->board->port->baud_rate; + // if(port_rate != current_rate) { + // object_error((t_object *)x, "open: can't connect the board, baud rate conflict. "); + // object_error((t_object *)x, "port baud rate %ld, but baud %ld requested. ", mgr->board->port->baud_rate, current_rate); + // return; + // } + // } + //} + + // check if there is any configuration conflict + if (maxclient_verifyconfig(mgr->board, len, pins, modes)) { + object_error((t_object *)x, "open: can't connect the board. "); + object_error((t_object *)x, "%s", mgr->board->err_msg); + return; + } + + x->board_ob = mgr->board; + mgr->board_in = x; + } + + else { + + // requested port is not connected to by any other board object, then + // create a new Arduino board object, initialise and connect to (serial) port + + if (DEBUG) object_post((t_object *)x, "open: port is not connected to any other board yet, establishing a new connection."); // debug + + char err_return[4000]; + long rate = x->att_baudrate? atol(x->att_baudrate->s_name) : DEFAUTLT_BAUD; + x->board_ob = maxclient_newboard(x->att_portname->s_name, rate, err_return); + if(x->board_ob == NULL) { + // error - there is already another boardin object connected + object_error((t_object *)x, "open: Max could not create new boardin object."); + object_error((t_object *)x, "%s", err_return); + return; + } + // debug + //else { + //if (DEBUG) post("maxclient_newboard succeedded"); + //} + + mgr->board = x->board_ob; + mgr->board_in = x; + } + + // start a new listener thread and + // set board configuration (i.e., pin modes) as requested + int err = maxclient_connect(x->board_ob, len, pins, modes, IN_BOARD); + if(err) { + object_error((t_object *)x, "open: Boardin couldn't connect to board."); + if(x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + // debug + //if (DEBUG) object_post((t_object *)x, "Listening thread started."); + + + x->isopen = 1; + object_post((t_object *)x, "open: boardin is now connected."); +} + +int boardin_pinDigitalRange(int pin) { + return ((pin < 20 && pin >=0)? 1 : 0); +} + +int boardin_pinAnalogRange(int pin) { + // Analog pins range: 14 [A0] .. 19 [A5] + return ((pin < 20 && pin >13)? 1 : 0); +} + +void boardin_pinNumConvert(int pin, char *str) { + if( ! str) { + str = ""; + return; + } + if(pin < 14) sprintf(str, "%d", pin); + else sprintf(str, "A%d", pin-14); + return; +} + + +// +// The Arduino board contains a 6 channel, 10-bit analog to digital converter. +// This means that it will map input voltages between 0 and 5 volts into integer values between 0 and 1023. +// 10 bits, pow(2, 10)=1024. +// + + + + + + + + diff --git a/source/cr.boardin/cr.boardin.xcodeproj/project.pbxproj b/source/cr.boardin/cr.boardin.xcodeproj/project.pbxproj new file mode 100755 index 0000000..c1849b1 --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/project.pbxproj @@ -0,0 +1,233 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0C3533DA1F87027000671127 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C3533D91F87027000671127 /* IOKit.framework */; }; + 0C3533DC1F87027E00671127 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C3533DB1F87027E00671127 /* CoreFoundation.framework */; }; + 0CDDA8BF2022D13700358057 /* firmatalib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8B92022D13700358057 /* firmatalib.h */; }; + 0CDDA8C02022D13700358057 /* firmatalib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8BA2022D13700358057 /* firmatalib.c */; }; + 0CDDA8C12022D13700358057 /* maxlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8BB2022D13700358057 /* maxlib.h */; }; + 0CDDA8C22022D13700358057 /* serial.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8BC2022D13700358057 /* serial.c */; }; + 0CDDA8C32022D13700358057 /* serial.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8BD2022D13700358057 /* serial.h */; }; + 0CDDA8C42022D13700358057 /* maxlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8BE2022D13700358057 /* maxlib.c */; }; + 22CF11AE0EE9A8840054F513 /* cr.boardin.c in Sources */ = {isa = PBXBuildFile; fileRef = 22CF11AD0EE9A8840054F513 /* cr.boardin.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0C3533D91F87027000671127 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 0C3533DB1F87027E00671127 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 0CDDA8B92022D13700358057 /* firmatalib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firmatalib.h; path = ../FirmataClient/firmatalib.h; sourceTree = ""; }; + 0CDDA8BA2022D13700358057 /* firmatalib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = firmatalib.c; path = ../FirmataClient/firmatalib.c; sourceTree = ""; }; + 0CDDA8BB2022D13700358057 /* maxlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = maxlib.h; path = ../FirmataClient/maxlib.h; sourceTree = ""; }; + 0CDDA8BC2022D13700358057 /* serial.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = serial.c; path = ../FirmataClient/serial.c; sourceTree = ""; }; + 0CDDA8BD2022D13700358057 /* serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = serial.h; path = ../FirmataClient/serial.h; sourceTree = ""; }; + 0CDDA8BE2022D13700358057 /* maxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = maxlib.c; path = ../FirmataClient/maxlib.c; sourceTree = ""; }; + 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = maxmspsdk.xcconfig; path = ../maxmspsdk.xcconfig; sourceTree = SOURCE_ROOT; }; + 22CF11AD0EE9A8840054F513 /* cr.boardin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cr.boardin.c; sourceTree = ""; }; + 2FBBEAE508F335360078DB84 /* cr.boardin.mxo */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cr.boardin.mxo; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2FBBEADC08F335360078DB84 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C3533DC1F87027E00671127 /* CoreFoundation.framework in Frameworks */, + 0C3533DA1F87027000671127 /* IOKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* iterator */ = { + isa = PBXGroup; + children = ( + 0CDDA8BD2022D13700358057 /* serial.h */, + 0CDDA8BC2022D13700358057 /* serial.c */, + 0CDDA8B92022D13700358057 /* firmatalib.h */, + 0CDDA8BA2022D13700358057 /* firmatalib.c */, + 0CDDA8BB2022D13700358057 /* maxlib.h */, + 0CDDA8BE2022D13700358057 /* maxlib.c */, + 22CF11AD0EE9A8840054F513 /* cr.boardin.c */, + 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */, + 19C28FB4FE9D528D11CA2CBB /* Products */, + 0C3533D81F87027000671127 /* Frameworks */, + ); + name = iterator; + sourceTree = ""; + }; + 0C3533D81F87027000671127 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0C3533DB1F87027E00671127 /* CoreFoundation.framework */, + 0C3533D91F87027000671127 /* IOKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 19C28FB4FE9D528D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 2FBBEAE508F335360078DB84 /* cr.boardin.mxo */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2FBBEAD708F335360078DB84 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CDDA8BF2022D13700358057 /* firmatalib.h in Headers */, + 0CDDA8C32022D13700358057 /* serial.h in Headers */, + 0CDDA8C12022D13700358057 /* maxlib.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 2FBBEAD608F335360078DB84 /* max-external */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2FBBEAE008F335360078DB84 /* Build configuration list for PBXNativeTarget "max-external" */; + buildPhases = ( + 2FBBEAD708F335360078DB84 /* Headers */, + 2FBBEAD808F335360078DB84 /* Resources */, + 2FBBEADA08F335360078DB84 /* Sources */, + 2FBBEADC08F335360078DB84 /* Frameworks */, + 2FBBEADF08F335360078DB84 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "max-external"; + productName = iterator; + productReference = 2FBBEAE508F335360078DB84 /* cr.boardin.mxo */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + }; + buildConfigurationList = 2FBBEACF08F335010078DB84 /* Build configuration list for PBXProject "cr.boardin" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 089C166AFE841209C02AAC07 /* iterator */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2FBBEAD608F335360078DB84 /* max-external */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2FBBEAD808F335360078DB84 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + 2FBBEADF08F335360078DB84 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2FBBEADA08F335360078DB84 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CDDA8C22022D13700358057 /* serial.c in Sources */, + 0CDDA8C02022D13700358057 /* firmatalib.c in Sources */, + 0CDDA8C42022D13700358057 /* maxlib.c in Sources */, + 22CF11AE0EE9A8840054F513 /* cr.boardin.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 2FBBEAD008F335010078DB84 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Development; + }; + 2FBBEAD108F335010078DB84 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Deployment; + }; + 2FBBEAE108F335360078DB84 /* Development */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_LDFLAGS = "$(C74_SYM_LINKER_FLAGS)"; + PRODUCT_NAME = cr.boardin; + }; + name = Development; + }; + 2FBBEAE208F335360078DB84 /* Deployment */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = YES; + OTHER_LDFLAGS = "$(C74_SYM_LINKER_FLAGS)"; + PRODUCT_NAME = cr.boardin; + }; + name = Deployment; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2FBBEACF08F335010078DB84 /* Build configuration list for PBXProject "cr.boardin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2FBBEAD008F335010078DB84 /* Development */, + 2FBBEAD108F335010078DB84 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2FBBEAE008F335360078DB84 /* Build configuration list for PBXNativeTarget "max-external" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2FBBEAE108F335360078DB84 /* Development */, + 2FBBEAE208F335360078DB84 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..299f9e0 --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate b/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..d6e3079adfc5f8ccd3a5e0151597bc03ce92e6a5 GIT binary patch literal 224284 zcmYc)$jK}&F)+Boz{tSFz|6qHz{jHRWz=TW zXEbKCWVB**VsvJ7WAtY9V+>>rVvJynWQ=8uW2|7TWUOMWW~^bXWvpYYXKY|>WNcz= zW^7|@XY69^WfEahU{YjKVp3*OVbWx>WU^wiXL4ckUUV4A`-gK0j~0;a`GOPH22Eo0imw4G@m(|)ESOedJGFx_Cf!*rMF0naQMW(Q_R zW+!Gh<{;)U=2+%9=6L2L=49p+=2Yf1<|5__=6dD^=0@gr<__jg=84Rcm?tw&XI{*_ zoOvDddgcwxo0+#TZ)M)byq)u{>1!` zg@uKag^Puog`Y)$MUX{|MVv)~MTSL#MTfdBE%jpa8h11lpd6DtQRCo2~#AFC9r9IFbeDytf+ zCaV^!HmeS+E~_Q09jhy=8>=sCAZsjZB5N9JI%^hdHfs)R1#2y96KgYT2Wt=O4AwcU zi&>YjE@xfAx{`Gj>o(RstcO?+vmR$X!+L@B4(kKf=d3SSU$eepearfe^%olh8#@~Z z8!wv>n;4rCn;M%An=YFHn<1MKn=zX`n=_jyn-`lOTNGOyTQXY;TRK|?TP9l;TNzsw zTOC_HTMOF+wkd2g*=DiLWt+#giftX+X0|PC+u3%o?PNR3c9QKp+Xc2OY!BF;u)Sn^ z#rBr%9osLqf9y=`%oWwbga}nn<&gGn|I9GG7;oQNwm-8^^5zdpGXE|?j-sODA`H1r==QGadoS!+r zbN=Q0$N8U&nTwxGm`jpNic6YHj!T|PflHA~iA#^mh|7Y@lFN$Aj?0V7k1K>Llq-xY zk}HZUnk$AYmMfDhiz}b2l&g%ZimRHdhO2?Ahid}Y6t1aU)3~N{&Ei_dwVG=a*JiGr zT)Vh-bM5Ck&2^sZ3fEPxYg{+Eo^rk7de8NN>kHRcu5VnwxY@Y5xcRvGxdpg|x#hW4 zxV5;oxplY=xDC0DxQ)3@xGlI{xIMUixP7_(xc#{UxP!Ulxl_0^xHGwPx%0S-xa+u^ zxZAkfxjVSKxu5zjiFO*}hzcJl1v+0CXM{ycc+{@LuJ;#(SOj2Jao-7rbwHKk$C!{lxp3_Y3cLK4v}+ zK0ZEvK4Cr)J_$Y*J`Fw{K3zUNK0`iRJ|{j8K2JVhK0m%7zBs-lzBImcz6`!>zH+{5 zzDB+#zGl7_zE-|YzNvgO`R4J>=Uc$HkZ%#+QofCRoA|c#?d99YcZly8-zC0le7E`T z@ZINo!1s{v5#Kw$_k3UYe)0Y0`_Iq7&&bcpFUT*-FUc>(FU>El9F z`Iqpo;$O|bhJOS9M*dCw+xQRhALBpEe~$k=|0Vv*{8#v|@?YbB#Q&WC4gXvIcl@9D zKl6X#{~^F4z#+gRz$?HfASfUtAS@svAS$3JpeCRrpetY~U@G7!;40uP;3MEK5FijJ z5F`*M5HFA-kSUNQkSCBYP#{nu&>+wv&@Ip-&@0d4j3%d(@2zv{M3&#j23a1KZ3YQ933fBtP z2{#J&3QrWCCOlnuhVV?`xxy=j*9vbE-YmRDc&qRZ;UmIFg-;2e6Fx6|LHLUBRpD#G zw}hVwzY=~Y{9gEj@MqyK!e52I34a%17GV+L6yX;U5D^p+6%i8=7m*fG7tt2c7cmeq z6fqGo6)_Vr7qJj=5%Cc55%CoX7Ks;07ReCF6v+|E70DB+5~&ku5@{A`6KNNjEHYhW zw#Xcj`63HM7K$tqStqhtWQWL3k-Z`ZMb3&`6uByLP2{G?Es@(IcSP=rycBsS@>%4I z$PbY}q8y?;q5`6Vq9UTAqGF;-qUxeLqPn7nqNbvbqOPKzqF$oDqJEKF;+1)F?KNyF%dCI zF?lfsF-0*|F*PxDF>Nt(F>5h`S1u~e}%v2?L) zv2w9$v3juvu|}~Lu~xA*v39Wzu_RqQVLS4 zQd&~lQaVxwQif7SQf5+4Qf^XSQr=QNQvOl_Qh`!IQo&O3QYliIQdv^jQu$H^QiW2b zQjJorQk_y=Qr%L0QvFgBq$WyDlA0&ASZbxzDyh{{>!mhGZIs$7bx`W4)JdsRQm3WP zNu8IvAazmdlGHt^$5Jn(UP`@^dMEW>>Vwo5X$EN)X%1;lX)bAAX+CLwX#r_LX<2C{ zX?1B0X-#QeX+3FuX=7=7X=iD7X%A^nX&-4{X+LRy=>X{%=|t%?>2&D~=^W`?={)Ho z={o5q={D(h=?>{`=^p7`=|1Ux>Dkf?q?bxBlU^mgPI{O0KIuc!hoz56AD2EMeMS1F z^gZeO(hsDcNI#W+CjCnKoAfW~f71VD7-X1bSY%jb*ksscgk>aTWM$-Jlw{OojAYDY ztYoZZ>}2d^9AtcC0%gKv!et_4Vq{`v;$)I!@??r+%4EuADrBlSXF=x@9KF zOqH1?GhJr3%p94yG7Dwa%50R`Dzi;yyUZ?`-7t$PI+hp5iyJdT1du1oe&Xrv#yHs|W z>~h&vva4m+$gY)LC%a2_zwBYzBeF+jPs*N>JuQ1)_O|SO*~hX^WS`2ukbNopO7^wv z8`&SSf906unB`dHIOI6xxa9ccq~zq}l;o7BnmQ{<=1&yb%fKTCeL{Brp<@*Ctg%5Rn5 zDSuS{r2JX=bMhDEFUen)zasxY{;B*c`PcIAa(yR+ykLRbiUK zbcNXpa}?$(EL2#luu);F!ZwBN3cD0`E9_C&tFTYul)`z1D+*T?ZYtbWc&YGK;iJMQ zg|7QLIp`QLI&LP;6A}Rh+0eRdJf)OvPD>vlZtku25X5xK?qa;wHt- zidz(SDIQZirFc&9yy6ALi;7niA1FRie4+SK@s;9R#h;3Ql^B(nl$e#+l!TPTm1LA; zl@yhfl$4d!m5i0lm28x3mF$!pm3)y$PrZC2W%v|Z`2(g~%rO6Qa=DP2~&uJl;xxzcN;H%f1n zJ}CWBW>)4@=2GTU7FHHlmQa>dmQ~hN)>k%GHc_@vwo$fKwo`Ug_Eip2j!=$Nj#G|T zPFBuWE>^Bku2im4u2t?-?o*zuJVkki@=WEq$}5%EDsNKWth_~eyYgY>Q_AO+FDPG8 zzM*_a`L6Oq<+sY8mA@PredyQ zp<=0GrQ)FCsN$*;suHFWt`eaVsS>3UtrDk_tdgRVs*qEgD-t5T*?qtc|( zrP8g^qcTNhs>(E#c`EZ&7N~4f*{-rfWv9w6mE9_PRQ9UuQ`xU_ROOh;8I`jt*Hvz) z+*G-xa!=)h%14z?DxX!psC-rVrt)3ohboIIt16o+yDEn&rz)2!x2mYBn5wj@BGW8Y zB~>+5Emdt*eN_Wh6IBaUOI3SSCsj99chz9k5Y!uZq**uUe$@J(^Y4v&Q)Eix=eMs>Uz};svA{zsvcH7sd`TJlIk_p>#Da^ zAE-W2eWChB^@AFV8mk(c8oL^Y8mAhU8n+scnv9yPnw*-vnu405nv$A^nvR;TnxUGx znzfpZnwy%tnunUFnwOflnvYtMTA5n8TD4k@T7z1nTB}-{T9;b4TEE%^wJB;-)n=;A zQk$nXUv074615d-E7jJjty9~iwpnev+77inYJ1fVsvS~0rgmKIvf353t7_NOuB+Wp zyQg+v?TOkOwYO^T)ZVLoQ2U|wQ|+%hvpS19t2(bbpE|#~fV!Z%n7Wj@n!38WhPtM@ zmb!tup}Lv6ow~icgSw--r@EKApL(!*h+)zxo9A8R|3DXQ|IqU#z}FeX06-^$qG9)iXUlN%d3e zr`6A>Ur@iSeoy_O`V;l%>aWz_s((=bto}{?mj;Ihrv{e>w+4>}uLhrnh=!g&K=A7HcfgSgNs1W4Xo(jWrsZG&XDO&^VxRP~(usVU5!o zXEe@gT-3O#aZBU2#vP4k8qYOeXuQ;TtMOOkpT>Vp22DmyCQW87c`YR^RV@uIZ7n@5 zLoE|6b1f?^TP+7IXDv4^Pc0uUf2|;`P^}29XstM{M6DF9bge9{T&)7FVy!Z*O062L zdaWj{R;>=LZmmA8iCR;%rfbd8nya-yYq8cct(979wAO2F(%P!ELu zozgn1bwTU0)-|o0T6eVWYdz9>s`WzawbnbWk6K@}zH9x``m4>L&8*F)&8f|!&95z_ zEvhY{Ev+r5t*EV{t*))5t*dRIZLDpkZK-XeZLjU5?W*me?XB&n9jG0m9j+av9jl$7 zovfXvovEFpov&S_U8-H7U9DZG-KgE7-LBoG-K#x8d$RU4?U~whwC8It(q5{)LVLCL zI_-_xTeP=p@6z6@eL(xL_A%{~+Gn)SYhTj7s(nNIw)Q>khuTlHpKHI;eyjaK`?K~p z?Vs9zwEycc>9Fc>=y2=s=?LnG=!ok`>B#CR=qT%`>1gWc=;-Sh>6q$R=veF6={V}R z=(y{6>G7?pp=w$2U=@jae=#=YJ>D1~p=rrrJ>2&J!==AGM z(wV9=Luam1TKs&hi;w9Yx5i#k_yuIt>= zxvTR)=dsQ+otHXqbl&TH()p_ML+7{7KV3#$7F~8-E?r(-0bOBTFi2;FGiINe0u6y0>)EZtn)Lfu;3 zI^8DScHIu$PTh&RlXMs9F4kS5yHt0X?sDA~x+`_p>u%89s=HTrpYDF$1G*=4PwJl1 zJ*Rs~_qy&4-J7}(bsyT`Fh2Aje1Rb&3f&6J$k)* zeR?zWX6nt-o2R! z`t14~`keZ_`a=4``Xc&r`tte;`YQS+`WE`u`fmE3`ab&p`a$}k`Vsok`f>VM`q}z9 z`nmdf`X&0M`c?W(`px<+`mOrC`hEKS`jho%=+D%jrN2mjvHlYM75ZEBx9V@x->$zy zf1mz-{UiEk^v~*_(?73&UH^vuP5rz2kMtkwKhb}q|5pE<{wD(l14aWT17-sj16Bhz z10Dlj10e$`18D;p16c!A12qFJ1APMn149FA0~-Td13LpJgCK)ogAjvIgD`_|g9w8d zgLs21gIt3GgJOd+gGz%MgL;D|gEoUn29ph@7)&*oW-!NKuE8RMRR*gK))=fcSZA=! zV7tK%gFOZZ4GtL`HaKl?#^9{MMT7eW4-6g}JT-V_@Y>*w!8e2N20sk`81fhj7z!K8 z87dm87^)j;8R{Au7#bUz8M+y|8+sUe8hROe8~Paf8U`DN7)BZ<8YUSg8>SfM80H%0 z85S9q8CDzC7}grL8nzj>8+IE$H+*gQ#_)sTN5ij%-wb~l{x1 z#BU^QBw{3CBxxjTBxj^#q->;aq+z6Eq-$hoWMpJ!WNu__WMkxDx|YLZ8q9sw8Lnp(O#o{MyHHU8=WyaYjn=& zywL@ti$-^i?it-TdSLX>=#kN5qbEimj6NEDGWu-v#ptWiH>2;y%*HIntj27{?8Y3% zoW@+nV#eae62_9oQpVE8GRCsTn#Nkj+QvG@y2g6O`o;#vHpaHbcExyE_M`NjptHO95Zb;k9^-Nrq}y~cgUvy5jO&oQ2BywZ4; z@oM8W#ygC68t*dRZM?^Lukk+P{l=$_&lsOIK4*O1_=538<4eZ(jPDyiFn(zK$oR4G z6XU1GAB{g5e>VPa!eGK^!eqj1!eYW|B4{FHB5WdWqF|zEqGY0HqHkhgVrXJyVr*h! zVrt@O;$-4%;$q@z;%4G*5^NG;5^54=5^j=gl46o-l4g=_l3|i*l4Vk6Qf^XVQfX3U zQf*RWQfty_(q+lOrYJKJT-Y{^4#Qw$rqEaCf`iHoBS~O zY4XeDw<)_RhbgBim#LVkxT%Dxq^YW@nyI>}hN+3Esi~Q%xv7PzrKy#vwW+(QhpDHj zmuZ-3xM_rGq-m;YnrXUev1y5EscD&MlWDVQi)pLrMAJ#8lTD|XE-+nay2x~~=}ObJ zrt3_%m~J)QX}aI^fayWgW2R?JFPL65y=!{U^uFl>(`Tm7O<$P)G5u@$&-A|;gBhb4 zlNqxaiy5mKn;E+qhZ(1tx|z0_j+ud(p_!?fnVFTDwVAz{gPDt&tC^>nuUVj3kXe{n zxLLGWj9G$NqFJU{mRYu0j#;i*sacs>omstEyIF_X1hW}t3(QuSZ7|ztw$p5v*&(x2 zW|z$FnB6tIXLjH0x!FgvPiCLZzLrN1_1`a29`$7`wfcm0xGVqMs5~v2Ijg3E=Fd$CMK4Sx=sde2D&D$PA(RP z1_lNu&TjDnBAza;;rT_`#RZAUsV@1+rManjB`}-C7~~k#mNSSmNH9n;NHIt=$S}w@ zur{zYus3ita5iu?a4%<&XHZ~JWZ-3xU{GODZQyC(ZREVyz|_FjAl~5Epc)4^)#nrzkCABCu1@3MXbBf~ybdW6x2V0M@Cp0g!ur$>(B{i=k zGc7Zxg@hJ6}yS?0+JvD!RCbI=jS9P7I_wjWfo^9<)p?7NFd}Albleb z#Hy=}Om!4YYjqT=EzJyc6bubajcRLQ!E3-^$)L7^!H~g-!I;5>!IZ&_!JNUOfvnh78W zLCnugEsphcafLVpTn@2L2VhsT!wiJ z^BEQ}EM!>JVAx>PVBBERVA^1|jA04GQU+B9RfZJ|D;vxk>>C^#92=Y(oIybgDwka= z3KH{DQd7VwJESNz6_VB9!GI#~l$w@bljJ zP^wCSl%+wb$*FlIu#z8L4XFHvmUKa>$tCdu{2&vd@d2^}=G9FMTNs2EGHhl4mQ|;p=v-BxI!rhm0S@4MfoYE$tA@w-Derj!yR*>!J$DBCUKeJ3WM5$ z1r4ek@d6^%)do5W2B5Op$kE+;f{aQ;M$-HGx{mR zbCATd2DgP^SE^zy#NIG`0oAB)8Qw9xXZXPIk>L}==LU}k&jzms?*^X+-v+H-t2VHiR{VH$*fDuws;8lmu0#j4};T4Pp(+4XNO&Gz}E0nw~DM z@G=J;u%H6Sv81FZGpV#BH3jA)F=VyApn@v6q!QE=0JS?X4GBsu$Vp62#WV+$$KlFB zR)tg+fQn3H<58UAo1c;zFMwjbXL??KQL1xdacaB(s33-$2iFKIJ$>?%6G3%TF{m_1 z^-it4%&5k|&8W_(!Klfo72y{Q@m`EyFx;iZ4bcrr4Y8LPxEXaAbs6;{{etz1!O_|f z6E7eIZhpommXs95C#Ix;>4L!@LVYFtnVYFr7X0&6pXLMk64D$;P&a6s}@C#0J z%SlWx4)Y84OUzAGc2EP?pA3Rr0u2cbiSYtLdX5DJIjO;w#U-h^p`IYaAntNubcGte zh|z^18zSh==n*d<33n_!44|>Tn9-Bbiy<4Il6V0jq_BhPy95qoA4biFl!hc&Kn66# z1PHLed>+gg3i5eKL;6C-u!amgMJHnvV>G1vB2)vy%#LTwW>8zrn82`*F^MsmF@-Ue zF^w^uF@rIaF{>fF!L%W#A-5r~A-|!Zp|GK-p}3)BHG?W+E`thVK4Sr6A!89^A*eiJ zENpOWC~XjLC~GKhsA#Bar~sEs6`-60YX7I_Kx)Bo-8>rnqJ1fa-T~ zP?Hf_J%VyY4y2h?94~;P8e{-e3~FRhXtO6$&`=3URo#p|p?<-^sU?dTyFn?9i-DW5kFj&TKnr6(;{?Wu zjFT8AGfrWg$~cX2I^zt+nT)d-XEV-WoXa?maX#Y$#)XWF7#A}xVO+|%jBz>R3dWU; zs~A@^u3=ouxQ=l>;|9i!jGGuYGj3tr%D9bjJL3+uNYr5zF~aJ_>S>C;|Io%jGq`k zGk#(G%J_}(JL3<=pNzj4e>47J{LA={@jnv-6C)E76EhPF6Dt!N6FUdy4f`6-H9Tqf+{nn;$SBsxsM5%2)X3=C$Qabfn9#_W z-^f_k$k^S;IJc2;MI+;`M#htkj5it?pEojoZDe9@WD;y-l5b?vZDg`;Wb$Za3Tn%u~=u#ss?Bh$e~ri+bCPa2uNG%~X{GK)7dYc(?4HZprPGDkErXE!oe zG%~j~GEZ$}Ued_CxsmyBBlD$3=BJI!KN?xs8d<~|SyUTYj2cSxOsOS{hmA zH?r(%WI5i*aekEgGN@{Mpo}e)`&*d>_*nQM%IaqtVPpL zX=J_L$oi_0^=~7aL?fGaBb#F*TTvt1>_)bAjcf-S*{(FQJ#S?D)yU4<$ga@HuG`4& z(8%uF$ez&1Ufjsu*vLMmk$qt!`<6!b7?U`Y1e0V#O+#%%T|@m6CTS)aCRrxAhK7dLhVF)5P`(#TfsPiyE0)OO(xgmq z13aXpvLH1&tu!acH?_DpF+CNWGniBv*%mRWF{w8+HncPZ++xyV(q__O(q+>`k}?CMaBC0 zMLCuFuts*VzFTHdZemHIb53SzUWtBgVnt47l3p^n$Y8Q&vWXWEcL@s#Nv(jl%VA-> zn8}vO4pa@{QV=g7>=G6Nvk9gRcThW`26aP|fJH-d0|O+!Tp1!4GPyOh!CP8Po=kpN z(}zD(0Fx|JP(yn|S3`G0PlGDNfKW!Zg-l@$9k?Ppk|~NQ3hIIwCUd43rnrVq0o#UV zfxZR?K@qEX0do?t<*q;b4nB#&|a})DQGLsd+-NSeRgMh^1;>^5sg~XzC z@PL;>NxnisB4~uFB(+FEy$sT80d>386*5vm{n!-GyyX1cg2WQgppOE$2bo%=07~Y? z3I$Np)N}Il(iODSQBBKF11nQdFU`y=F*b@X0jbbP%uZBLFU-JW&x8120>O1vO_usZ#SwiuGMm%Tjal3sQ^p19B2e((;RPi}igIll_Au z^a`K?NNR#zykXLY26~1@dc`T(`lZE1`k8sjIi)G7`o$$FNQrbIIFZH+h^iM9=NFZz zE2N|*=Oh-TrhtRme?E%u|3G zY!6Fc%bC_-4e7N^>zLLvZD^R=u&`l8!%A34Zw95WElgV*<~1z971}$Pb~5dRhW2hI zbEe%)dmH8pST!^Yn1Rw)KDE;q$i9Qn^o6j`4w1f&g3{NqhDC_Q5^tBXZ|6oa1_voIko-n-xh452o>tQvl_3(=6 zHPaiWw+(9=HaF~P*o(dO04cFQg3{V2rq2y)8#dqy`EN|$nZ85Q+E1omOh1`^H>?wo z1m)~$4eJFhSH}yaW6jyVi4}S%`8p`IIKQ+gIki~d+1y0GATc>RF+H^yR3YmYr)2Az z>w)GDAxiX*>I}iG|cP4Ag_ls9K+@H zaOMc+2&mVin4_7am}44_3kZX}uGervP{(+{yq*9~d$FN;$%!SY>G?&ORjDcQ0;&N; zsb!h@rNs)Fxdl0y$(be4${##Zr;u5Uy$N9KoS&DL1Da~eO9wYDixtxHixkwMX)eAX z6Vxqo%qdDuOsRCO$Sf`?R)^O8%<0VFwk3KQ&78}e$DGex&~U!tX2YX~$FLADW@KB; zT*6$+T-I=*;c~+@Tp?e{T*X`k4fz`8TIL$&x`vAa8V$_?n;R|(%6if_;JFXVn0&!n zBr`WLS1~s;w}6Ud<{IWUutTp1XbUhmG&fuwG_LGo?q=>`?uELtpSgy4Lc?_d2>~eq zriSK*8-lWy1K?ChCY}Py#8Vq?Au{m{=DAoi@jT}F%nO(oHr#Hw-|(Q}AuLHP0cGN) z4R>%Qr4`I8nO8!S(rV^4%&VE#Hry33Z)g_KYq%%KrWr5Lh_!a7X1g7;$)1{AT9m3E zUtE%snXi`ts`tT(3=$?AvF2`B*p!AsT2X$kLT+MmQNDtDaY0dLUP+pIynwJzvv{MT-kklR*mRmF9t`(-qWn z6O;3c<3W9!Vo<)0rQr=Cab9P>gEbiLGT&pq&-|d_ZNrC# zj}4z-iSseY%TF5K;qvlx<`>K_pk98#WX}A8`E|p40Zvf6U!aG6c`{z0$}>*^lnoT1 zoyrtQBGX6d%2L0z5deJW)z~#WLgNNr}t!!9$kSiM&KCmNyHZ(W<8Z<5xLau07M3`$>L>vAz zG&lSel(iZFM}l)PizGwjLKdlp|L|PQBFmzLEg7*Wv#7ABvZysOGBh%>G%~U_GO|Gv z5{o7y+d>wtMn*c^W7=rZ+MQ;_`GB=6F&gqY$WK86*QrEcxIhh-;uSUV!qENtP0p zN>I3>_fA-Vkd z1O==hQ#WnPxct%*eOR*wl8w7SX{o!BQ5un!`dB7o_45>#sVvi2rZ+OmG&0IJGAcAO zD#H9c3*_h7jf}Fm{5%iQOkr8TWX`gHWl80Ft4mdz|%ShhAYsx~reHZmGCG8)2S zZU?9U+{v=5kx{LYQ3F@>?Pb}=vJcYAWI4cO&T@d|P$Q$dfMP?lfGKD=-H6&n;9iym zun{|`g(n&r)xmj$RT2`3r$8z9bR(k{BITZAxs28SS6Hqx$+BE;WYlhC)B}}*`Y`X` z0wvtrjf^_De14C?8w5ceUzP_<<}43b9yKz8CRXP(FbImKlI?HEgdodPXiC&jhfWqI zgQsBQOAAsGOHx7OfP|)Bd_(f{71C16;r*{-&~zWXr;?nQlcS>mnq165>Y_nLX4DnR z6N?q{@=Fw8eY+G`N_x%m0ThxL<=#h@Pb{BVzBDo#H!@l^GCDUhy1;_+J18anVENg| zXwt}Njw?L>FrQ&Q14&8Dwk-dcZCU;|GMa)KIc9o%*J3WORZ?6{|EO+d@{EMn+p4vBWCR+{@exi6vG=RwY(NR^>)UyM|@~twu(B zLDuX6h$o2u)xr4%%aD0VJan*;P=2ut&CAZqFVBOFVt_`?71W`c^Gi!GhR2~&nR)84 z;c-?yR%1|rqnEO*Cak7RvaIHfjBbsLUZ7GY5EkB6jBJZotyygv8QmKhJ#huPJ*xw& z12oW`n9Nz7SX~+!Jp>FJngya77z8;x$S!QK&xy0zvwE<4qFCqyvQS1q6I{@8LUu2( z`hn7%e^ z!(`4Z!dWDIF! z48s*Tbu0&14nP8j3A`?awXu;g6dX9bhe#XMf-UaBIzYf$$8vzR6~iI{2ar{epzH*t zhOS1&2t;b=Wu1i8+ml(RFv+q`Yh;XUWQ+l&hFF-dXM$40tVYHtTppgwI*)Z8#2u^) zn9Nxhur6w3j27^1Xcm|Ys(4S6ogyF=FDtAGNb=GfjIxKGD{)v?vu?l|)Eikhv2JGF z(#V+D$e7m1nA^yh2Mg=%jBJZpcd+hc-POpL)X139$e4;N)c3M_uzEm4eLs^q>weaQ zjf}|+%>tS zFfX!R!x~!GS#L1OvfgTB%xGlH1|`fKSYX`+CCqz`jG4HC=ppMP)<@7FdctJR`h@ja zBV(3;T0^tIEKtG}qBLQ?gcem=1lELst2@YQFpMM_Ma<$bumPaeUpN-;0e>5@{G%}VpGS)OQ*22Q}Hz*?mbvoW$Uu`xkHnT>_XoQ;Kzt&y=v0MubDYG4rLE~R2p1Y5=hNm|I3seqCe*2D9vYD`1Vhva;HfttX zHrqzV`bNekP{4M<0@eW(u#Sz44Y&f<1+ip-&5g;N&5g~Yk+HF%S&)gNfkDuIBH1CE z3`ri|pyc7x$k>cX9{y}0SbY)77RDCN7SYJq(#Y7}$k@@y*a`DSG{_qU`f*9QtL+V*xZ#^l%<7$O(3HL^7|GEQh@oQ6mut!!ObecjF0!!nJnuaR+LBjXhC zz}!@ruP1_hJ*klq*E(Fbsptc9Y%>}eCxd(~5F0O$2b$3Xtr`anlx4yeq*7-V59Du@ z$ObJq=VIVyn+;7^4fGp@!{QbzODkwy$2OmB3D$65%C?MU8rzCS#_5fWv%ujFTLR6t z8Wiqp8X0HcN^R>A;m+#8WX|frwyBYECMetmnfU1)@X#E+6&mo30~zocRz{#EksteHh>ELcT-nIDrr}j1<62nx^&8~bKaGs5a7EXDq*5EYR21OYp%_J~HE@V$^WIPCOVpVr8D1{BkRynm^g2q&|ige598 zi?DXp*(bA}WMhIPLNxV|B)AZi1Q#_jUO*(lrR=M)hSX~IHB7SX>lzs^HZooTCBdt( zfZ7O3f}0u{FX0NFt?b*_w?Tr3eFu{{`wsS9jf|HC)Ek-wrh^h;5T%K5@6bquhuKeH z4ce3Jr`S)kpJ`;g*~oaWk@0CG<1<*;o(Cnu3+xx!FEujWYGl0A$aoi5C|^a)QnO!Y zGH1WeezTGBc0;qkvIYi0{%h1shF4kkv2TMUD-^46m4#PX_Q8fb(bPkd;S*3YeA>u( zACU}Su)oC`R`1x~Gs&`lY-D`U$oLqP44=S)>I*0ter;rYh%118FgG(dLy{q6RGs~I zBjY1*E)`u#X%hT5B$6Np3kN5*VCCTA;O5}r;B91l(a89wk@0gQ;}>YSatJW8E#eU5 z5Nc$6*~s`BN8oaZBIalyQOqIH$oL8zxcslEnD#&x!RBbtECQz`4ta*ig&YcvjBnv- zi9?w~1FNSsIkcE$IdmEs-!(FR1f?a|x?>J~Mz)0<291pGae3Dm+YHSIkasCeNE{Z> zgfz6KW;pCPu(tC!TsT~rWI5a$8NW3${%+`QWMYD)D^N3!!<)m0!?%&~dn4n|M#f*b zLOTF`W`-lUk?{v8v<11OsF}C|(C1~~7GbSlIY16WnU{g9ha|>0P-2X4Wc-6jj7c2n zSQBFgM<$ajM|LCQ-$o_|P(U%l0xA!b81ow$|KSRrB5boV|3Se+X<{sgCPorkcku$W zpMT+~;b_Dfh)o>LOtKuUjZ7?!Oq`%V6o3U{2P4~Jj!up)j_yV#)K12HNi~$uz*?vN^NT!nRsvo&ju{hE=;_j;Gs0N!A9T)TWZ_IfpwgX z;{eA&CRvWdjZ8v~OyZ!_CI<_|W1!S_oZ|$?$wnsOMkdilCNW&Wc?P`;%W=MuNdy#} zf;@fHOl=^GP&%-1i?FA*Gt6gTeGN#57p@+X+HQeT+wDds2}EkU$MG0zYJ0-*lu4H3 zc_Wi#Ba;j$pk!eI^$L{QUNF2Sh}xF;DakDO>LhCY1#IJ<1f}g{KxU1NtTnb zkx8MENfi``I?zDmWMO1m%*o2h#>w8uq}a%$+{mPYBRDy^(0i(!yp2ps;MB&)NX66! zvIv%XAU#jGMOafCNIhy#6|Nqf+BhW`A{TN>HZrNfQyZrYry{n1;#A^PW|HMpZDdk! zWYPi!lr}7&G#J?ya%wg*;acg-se`4b$^>2yMqz5>G#s3%jnjj=c@J1#RL~4uT#Jb0XGl4UaNtQFYk;$}?$pREmmau>V?J?m@ zZ)7sV6+Bs3I;2eIpx~i2wZZm=46>S(vxF18p#*)#inEHdnn{+kwvoxEk;wrRi0-gJ zY+z(t#M#K%)W~Gp$YhTzBwNvYpPU_yOyJ{Z1o_`lGoiI2RtQ3RpKyyH38x=aOigHH zazrGY$(%E==CGNZvzTN#=QJ`oH8Qz^{Oktv^L$V-wV;v78JBk#W9fY|xlkkFtbi6% zB=E*-pJ(N$P@(fcreW4piLf}w;P!PaQXQjw$u~|as{Qu(BmPJ znqF{X9c<)$$N8S~1Lwy^rm#k)s79vbMy3>4sDeftIlneCh2sj6Q35_C0y!LHz=;!m z3$GN`Qz8oTi%Zm_13z6n2FzgioyiAE%p{p9h`)Cqef205inLOFDR`&VEzxsnveuT@)dGHr;#g^WMmdAs8^$; zpBQ-RQLlxq>*I>!N@h@7!W9oWBcze340=ciR|;1uR~lD(BU5=JQ*|R#O(Rn+o`XWT zvKiSHapiF3HZoN-GF9OU&jLhM$5q5+&Q-)!(#Ql_KKrzRK~PeSiZvZq0ivcuv8sWA zPR$0iHR6zTTMJ6Jb&X7Qh;-Y?1)3(t5(DjA9bBDUU5!ljjZ96AOwEl$x^IGIa>JgIZSMpz}Y4=$C!5 zb_u!GaBYQc&4jve2grqjpm-A0yF=>kC{yU}C=;x~z6X@__BJy0B9h(#uA|t){utMB zu1>C#jZA%wOcTLLZxSr&odG4ivyDvsxPtuxqReD&W-@1P=DOU-Gy&`-<}9*uct*T{ zqB>%!TXKG0UTQM>Rx(Kbx{i{zZgJhm?!O0I54j$3J#J*0(#SNUk!fZl(=3?(o`KTV zbFLSSOj8?~rsMMDYm9>IT_e*puqP!|$?_!j9fMr2F^VymMFUxkeFvqbAB{}25ozf+ zHv`tN=4Rw(;_BpPX=Iwy$TT0EmKH$6nwy=GZ6P;DBhy?Q!OYE#tr(jJ3TE=V31k#w z;Izdph@7^#MYtug`&Wuvnp=ijwvlO3Bh$)8rd5qht6~0CU}Rgwt;ns^$h5eTX&Ek0 zt0K}Cw>q~5w>r0GBh!+GW`ReIOiKkNUXqu*lpXh9xpE_xxfE+r#H|ZTXUwNSyTiEE zx%DBIE(bN7S5Ro*0H#xJQ-;We+-8kTYvAdW+mhQ3d%W3mJ8(O4J2f(`ZDiWm$h4`E z3ASXE+ZB{f-5QzJ;fgX(p_C8^}+n@E9W_#bT=gxC2oVa0quK_JD}u zj^^s*j%{Sx+Q_sMoMCst5^w@20Vi@NH8O2$WZHo%G*XchFq1hXErHjKl9!aQr`S|b ziiK3~-0Eny&}Cc*QYz$wl2}0_({4l(E9S1i90vw_ck(}X=oN$(8zRFP;5D6#SAn+@NOVO9kqZY z{=+Eg;28I5>>fYEeU|$i_xVPqON~r78<}o3GTnxG{1T`{xy*f~k?C?H(=}Yazs}6S z%mB^nH@RuHW zk?C$D)5AulM~zIdQ-8UCf)eDfMy7kXqU0~4na}FM{h!r?hoO<_J}6io2r_e!R?x$? zf`IZSI4WvOivq`UN$nl zYGis1O^ZCDjBE>e#2T5N;qWt$Bq9^?Nb|_>Nb|@xGCc<+!~9037lK9=l%fg*HZuKbWcu64^bZyS0gP;mcmjEX8kxQ|GX2DrEJBgn zJ3Qgs>O2vROy5CS;fJ8a1JberY(oerEBrJ6yHx>evBVPtYW%>O>pXD~D}RBk{7s>K z1K3>WNdcwX)J7)6v7bB{Jh|9oEsrOkr+}xhk(r^9nWd4LwUHTi?k7(PDBYGeGUGb; zlcxgFmg1@6sphHTscB?pYG@W{Z)9c`6bT_a-GWYqz5O0&l>S20_I#@%>Dd1WCkH2|f3!vSz3!4rJJ2NYpsmZbcg z6zrLW7t|ExRcT~4g?C4I)p@ZuMR|33^?3Dp4H}uv8kwycnXMa{ZD7$3YKrojG&18l z<&xJNaegANC9f5)C9icOvjr%#m^U(83P$$D3)Ij*v-l<^gD;>3XBS8^0A&|&coEJn zyeJJ=UKd{M4Om_;UT>~WUf)J$yGCXw(6AD-Gc4Rd4Orel-k?Ti`$lF*T-hZQxd96r zO5=@aWCou=Cn#}&y!Isa)E)|IwnFkHWXuk35uKWy;9`guv4d1o&+Z#7r1CHBXE2<$%Wr+hRB7ya~he05b10_ zFX&_&Ea_}1?=r4V-W83^!Hvve;B*!aOJ|^?ZFtu-GKb&_?e)loACozx@B<%KNsns` zu-|ZrDEe@ec#s4M*?$9V-16?=1>IPKC3N=j?&m$gd$5r?vXMEVkvXxE8MZ2n_XsFK z9_2mO$Q;$kjB9Ba?@6Qtd7AeO?`ht%jm*)Y7Qu%`<`_XoC;Ha5)%`AOGqESoi%1Fd zH1B1IOXEN-g7^V&BlTMZH$j==RwHvVB2(PueT2Ondd&NT_bKnQM&^`8=8Q(>%tq!c zSf+T%$hMI8RU@6hcFckA;zK5g#ibTO)IBBXd5EvV@Nl(H7w2W-{mF=HqQ- z1}%Ae0a~J^LdDhq$ST-kWE86!=(R)(TmEId=bA z@L6(o@>w@BS2i-&g8f?u^RFE!Iob0$G%{B;GS}crPR>Zl$(6~R&y~-;k-55|S>P)u zIX$6fa&ksAF8PqHqH}Wc2PLO~M&^1%ath{)z#hDjd{JDTd@+s84UNpr;NXQV@#BjJ zC8vZ&=0;p0n~W_vH4RX5%0x*{IedlK{aeIW%vZu!+Q{72$lTS)+}+3wThqr^0rGEU zBXc`0|JEQTocQYa>iO#U8XB29Ks~%Yjm({b;hX7NJKnPFu)As*FQA2EvIenSEibhk z(s0C9tn;;jQcHUyGh$U9Ul(6L_RyQaH<51=-{eN-zDDLrjm(o9nWw-q<1|p{O>bnz zwJMKq7Gh+ROO$U8mnh%dM&=2i+Hj&E(?znfA!x8077V1$9P=$kNifU!Rx_wA;ad*5 zHoB2{T0?C^T|@m6zBPPn`PT8RZ)Bd{$UM7|d0`{-Vmzym`8I>fq%C|~8<}S`GS9-5 zl6D}KNxPWL`F8Q`X=I)WY7440FbK-4lNB1+*YEM|Kq{1w?SkGc#djDK|3?~`=O9Xp z<9uhZ2gX^xbA0RgE;KUFZDd{m4va;x_`eK_|0|8m^Kkk3I(lircdLM&@OW%&Qui zS2r@R!I$bjfKuH@zE6$J%Nv1e3DJ@QIF;}-F+?upXKrL(3s36&Z2a6|QFSKXY<>{RGAxS4rn`H=IC;{{}WQY$i( z6LS>uixdKiQVWXmlT(X}^NS!GpCBP=2}(Xzjm(IJcKo*dPS}Ifncs!qmEWzAc~2wr zfkx(ojm(E&$;T5EoL-I0xE9*+`yzsqDT?2pDT+U!k$E4erra;c)-)hn$Y6mg=?l3) z7`!!10X&}yooyj52*beTG%PVj^2af#E#Z%XRk}x@l`elge*%9Ze^Mj!(MINzjm+m8 znJ?lg{`k`v*%tAq^Jg?NA8TYjfh(0}qZfbtd5z4+LAg>;N|cI)AAdG_;Rm+}l8#D2 z>8Pxc`4plUs^qW39uoEZ4g87xO^wW_8=236Ljty9kG~a^j@lZT&*1WKC$?hfEXc#; z7ef#q^ns%ht&An3graYC%RhyGCiYOC#Xp;W4*%Rn=F5%Dw;GurH!{Nx*yCRSN?Qy0 z7d0|pX=J{RE2x(;uV!8inO5Lm&cA|xIseK==BuEtfl4FuH9^n4bS(lO@<`9P#)Wlo zkAE%mYE}ox-Shm*`PV~SdIMCr+#CQmLXy*FP=?sj$b1`-A-41H!5&q6`SZZ=DYm&`0w*SXk>oY$o#gE`CB9N zcUb5@24$2d{7)O1pEokU!j(~8AQ!^?ulT<6ziwoH0ZM)^1*Me9D}|LE_dnz*VZZ$q zdy0P#Dvlv5)%d>ie}q{18f4`g3hjeL)K^gI{np6*4v~6)^8dr0djAVB2rvpTH8Q_% zWd7X9{H2lkD>VEBSQ*(C3a~XYf4~uR0-VUjxd1nKiwE;ZkUu_=U!KFm4(t~Je(Z^k zUrYPQXk6bgU6(wiU1uu;ot_uy17k-^juQYDKUx!~E?8N_NhTEDX53?1o5o z0v=4}0v-ZhjVz1}%>wg5cVE3AKj}eIoG&=Vk~xi>x!^ z1@sBr`IecNl3L+dRFqhW_2M^3Ch7-gB3SV^NnkoC04BrIrZ_BZ&JdU>FiT)|Ba1{M zi%cVnY$J;tzO*?XR3t7CSlGxS*~lV|D{U^pmNun8X>*_x<`QfP6Y4UWC(QMrgt?)S zMIKQ+Zx+~rJzjPS>=KwIu&0qlp^-%y94{)cgt;G-Fb_1cDB=qK!`Kq05+wNTU`<)9 z7kv0u1{CFIr6!jY>$_zZIEt}B6GjVyY&0{ySRKY@RcK!+5Bf{cwU`U1`%e+jaFh!>zZ(UIKP1P6d1 zD{`t6n4%ud=mqF+nqIdz}(7e*()D#8Rypqh4N~EDlK?R1$MS_Zg zN{uX*jVuoEBp|3NsD(Wkv;}npbp`brS*#jaY#UkZ8d>aN!C(jq2BSt6Yh1x#ikQh0 zG-omwG#9jNWU*;z7GQ5+5b%!|a3ZhRfo^&Q6+OxE0wJW_(1WumLpIaO!YW<>`^|-r z6l8~zf*b|ium_#HpogHYpjRV{VS>hX6 z67aM#1k*ubmeI(9>yC86Y;3vC3zX|9&T^1=FJOpVELbR5Bv{kqnq$Fqt#I5bSJZ z@dtTHpqqXb7sU5H;8c$*XT%Fs;Yd+5xE>Pu{5~r~1K4GZf)fR&VUL69f-?l`1ZOp} zgfy~*gX16q76)@daWJouB@|ayT!@GRR#ql+R#w3!jVxi{ILIbDVdW-PfXXk(HBo}g zp+OsnBM8ZBmRT7Z!b4`YAn0yzEU9y&;3mO3!7Ys}QH?CI;E;)fh0JzP$n0ojiN+N& zyAdISD5PRQAw!iy3X<#&K*NSM1s3+Yl28o`E830;f{qaoJPzwXB|$q-f@cKJ3Z4@@ z-^h~O$dc8_lHJIXgRf}2%*eJ#@QUEoMwXOD7F=f>3f@5OKndQ0bna6dnj2Zt;svr` zbt!eTTXK;ecu5d)j#GBr|7Y&Q_Gy|}yZM6mz`ZF(&^{slQ-Tj5&ddNgGjm{^N!?a7 z+Hevikv?OHTqyXwktG*VAG{KL4=Rf=)5ZtEkAj~BKR2@EHL?^nvJ^M6z>c{T{07Q# z-y2zQEqN9Eh1`h~`~w+XC;)|RA;m=zJX)zU)*!?L&bZL5BE%}hg*7;YxP^Fxc!l^H zS;`t&sv23U8(C_g1(1*+BikY&AtB*LmhwiHN*tw-kQj2dfQ@-oKuV!pn&%2c2GPU5 zt_tKn%waF6`)ED7Af(6;xll-{k);-%FojfwKuvwjLQ6K?7 zLT*CtLLQAQZH+9QjVyB;S?1v>xrKZf*%k`` zu~3*$xKKnROIIVy#736sh`5dxO2F>TM4=?1WTBKsmhMKDzDAb*MwSV%xCXVeg)$mh zdT_;cHlo@`G)j9RUJ8I^IHV>^0c`XrH?cxDu_&c9GcR8^6SU|iEipM&zc|0NC^=Oh zk&%#?&}+Yi3c!(vyX~1wxZy;RE07wQLZw2WVPh;wr&_2+C|Rhkk!4aN%T#cfPJ@Lh zXxLb&sgY$et}tyygejttIRz4?6y_gDGURHT_ejDMDcc92(RpIwy2q=t3jQibj^z zjVx;#Szxz@3xU=j3tees!L|BT=sF_FG0kE!XPU*ewvlBOMF9cr?Ip(x1Q0H$T=J7k zb5rw5iuG}hR>91(GRHqsC3Fv*>tSuMheFRlITSN3Jr{Z*bYAFHBg^_mmQCQ`f*mL+ z^cIwE-Ziq|x`SNkBOYzu|i8(DB&NiNKV2p2@j1wC4l3MChq2n!&GkFcY(_FhnjCHf&@$1TWTvO@uA6hqslmwXn3XZ6nL! zMwX-C@P^%VF6;nGkB*HjM{oqS&_`@7bZ9;$wSf&uB%UaV#78&~yQ71^i6pd<k~o(2l^^hTC5h(OO0 z&d2Wl0^veoY2o5Vma~m4=fQyvTUjey1`71@MiyLGp9_7&HVFm|bn>RJgzHg4y-Bzo zyQ@2dJB7Q1yBk?9HL_f7WVzPJ0=v3hxDVv&{zjI|xB`3v_F zS2F68U8*Bm1B#(}*?IZpc?#fF8=ymt6x1{G<1H1(f|y1r z%g08RPk5S>!pA^4_qgzhMwUm7EKhNT{0Ph_>u5q;U|qOuNqn2 zHL|>KWcdI~yU#&s_eCQMu8YQnUn7_D!fzo1gl|Bed`ouHg|;>`;Nc7L(noN@LK_aH zcN}EBr$kbO?d)Pgt`50!#M)g#U{$h%h#?d~Ia;)5yx&$jXK%*^96;vMmx} z6Jc*;`PRsS>y%9qE<~~y;Su2#;Su3$WcdzSJbs{&<%ggxD~-~ds^at-H>`}Y00|hGC z=>%3EK?1M^8i3RvDu7lVA{`>V*u$kyq+cXPWMU($R3ob_I9y;?oQq5Wh0D}NR$QxL zMP?wv1wK5$Dgy}@Dhv-m!e%ZsYzE=nxyWJ>(5bjsvhYfgRU)fJ)-cdjodQkPdL1beit8yc&Dy}5A1<_v=;bAft;St&1$f^PgT0vVq8f0YbHSQKf7ZlAE zkhs~$5V=rfe_ktISD;k)TI5Y5t5G8>t~H+`?~xmQ;8gdik<}O!&VsfIbW3&b!R@>j(Bcbds)M-# zlInhfQr)jcRx?DZ`zy+XHAqC6MOj2niLy1anm4jqfrG>vn(9P38QB(!ay7DA;K+lb zyvR*4=zK7%B`7qglj=l;phcTLiQ6J*(!UTD7nQ*thO(k^qVl2&jjVQ!tWJ%ro{g+t zurO3+WLqSvBC6WRYTw9;>%db{4fMiHRJ)NCa^R_LKE09^$Q7_f>X2jwcLg|EiGuc+ zi5fSuI>VEdsF^5ej~Qn9E@~rcD{@NIzLC|Xk<}fXtUO>r0@`CH>fFfciYr;UVJpm_ zOXbO&q(W&;h`uqU%{(Fl=KqEU^kK8>sa;4p<9$tfBON@j7Q@r|s$ zjjaB-0yYV~^b$>NWc35(S3%oEx+SwD^wJCN3P>``VTfEPn%l@4h)8AyqNUh_q)fD2 z4 zh}64AbR+iEyGeAj=oZnfjjTzHtZ9v`>5Z%zu++N)lzMkIvL@q7y?c;TujoF=Fh~k0 z&{HW+zVPsccfINKMul2GF4CCwfx!9QLq2FM2`rqUfbY)|^Jx;zrh*M%G$b zSYHLD@@t~k8(DK3SqpFl_$@>#7ri5TSM-kPy++nN(0Jd4M%H{m`yN`Qtv#2M}Uh-AriWnjF_yL zjF?;_Ya=M3pKD}o614ZGRodEdah4a>gf6CpNa$iRVk!_PwSdw?MOaMMLB zMl1#5r0JkEFoTwkfuxEIP(sgaWSxab=s9AblXJ0@GDTv=VkKgwjjXd9S?4#hE@)(3 z2n&4B$+=>cjjVHUg?kN3La&1)^tm8U&ZAC32Zb-hOOWfzhDM3rDb|NQq4$eT5Su7A zsgZSQBkP(*)~$`K+hAcm6_n7YiA`^0UDn8oYq6u)ETn`!M{KUx9I<(gtjj?O{d6Pi z3PF2QTBWVammB%9Cv>DXy4W1CB@idA0;Pe~v~&z4Re;*)Vk;Y25hs6&tr5fCMi<*8 zwpnb8*w#kYb&af>8d*0tvTlI|KB$c@wzH9SJ+6d~(nc49w$V3$Jh_oN2^|!^5HAg( zHoDkJG3;%0vGZaVL{5oaYGmEf$hrsIn?DQ-Yfu|q?3&p1M%JB;th;dqI8qy3Yz~vT z*c`FDjjX#sU3x)#O&X*t>}mTJxU~Wvo<(y7B(9!-%Gsxlta}mV>*9 zIFmT&`ZmnEN}NNSQ=ChjyOH&1BkP$))~k)I*PzKt9CUq~IKQ|+BkQq7){{5_Qd}6R z{EQJ76^juUYh*nR%Eu=J?cHdQu0S1d9q!WjaI6VTTne1PpsfUP8HjsMf!uSNmW}~O zy0`*E2A2zZ+ZDf7c$od=>_@GemsFTn^;S2Fn)euVP zjpA+C3zc^94)IR$u140^jjW# z81bo%tZzZ7>Ybo{3at`WN$=VX*i-pTL@F1H5uXil&j*luKGM=LkR$=R%2<3sBkN~G zDqk#)ebI*aO7T_VtHswevVLh~{n5z!vyt@|EaX8~8H;adWc`XO&^M!`@~x0m{te{G z@6<`vXS*)BO7ZYn^+^8I4rC|*C&gg6+hR= z`oEEl30Ht$Le_H=$zpbfSINBpw* zZHSAQ8=4#0SZL`ONTRsU5V=VFf%wBlHnv7KK15=FBK{J4G4o3NwfGzHw~cJ^KSfMlzd>cL3=oE$WjMO{@@bPXLsYR*K(3X&w0NvMtSz1V_N~lSwOK3E*Nj9=6 zG_q+nvS~pRtAsWq+ad`a3Ef6EsYW(gTtRMtR42+v7>UV97&o#>gHo)Fp#4@_1~=BS zLc$!WPLz?bgt$iz3 zwMI7eMm7yt$b-&*lkjU~Q^pnOfruhnphF@UTqm-rfIO*6ojMVo$0Z=AwPB>g!8o-g z5hsz1y-Z1wNR>#FNN;4*X=F2NWV3H%bAW|)79-nYiEN1+iQGmu-9|QpMmA$yp`kWayEX63a+i7)qLWYC91?POH?ddKaedK4EmLR@453L#pJ6zhN-1Ic`?pv>3S$Yzene4P^5N5&=k zB_>Erl$g}WX3@xI)5vDq$Yuvi5K}>w%(O-}OI*odCUPAuF&k1xTY)@jJ!tA^M5xRM zS4X7P(eVPpZuy|2d=-LAiZb)kAxDJ93#hv!mLw`97NvtvAW+E5FM-rFNtFs|5cS1i z^{`rJnFQ!IJS?@&8i}m=4UvN<)fc{Z{YG_t`ica_)#s&zI?Y-wb3Ze(-A6$jgy zBbXx~Sy5uA#4d@Q61yAOTpF4MS{vD11w{>MmGrN#y<~|!Gwx@OV6Kul0LqLKJ0%W5 zT;whw3Q89qv~&(6@gHM|Tq1E?;)KLWiBpYiUX5&_jcl=vY#E5Wa8}|H_QLV9#1)CF z64x5pyc^kq8rgyy*+O7>;U*|DZb{s3WbJ=gku4lYZjod} zF0mw;AthD>C^#YqO^F4{S>W)KWCw>IX(bl4#F6BY6vP^Ql0uTgk|L6#jcjp^Y^jZG zX^m{@(BPAlU}Re)DJdz{$QIwomV_$|Ww{@4KY%9kos#krJ0%qw*%Clum?$Vpi+v;D zp=MC1U`^tZD%=lP6`6Y=<&C5o#68I%_t0!03gj3_6lsB@2qTH>2c_obmw|dPpxYMG z)D<$*6f#SS6_WGwQZh?2^Yaukixm=c$`dP#6-tUqVZ9SceMuASv1Tf1CTT8d(a4tB z$d=p4mee4$=~His7N}BMCa1Ll|RYpJb3^uw;m2Xd_#3BU@D?+ssC` zS+LLt9nB#bDH+wsR?^5;jw=dc5w)~rykvr8ykue{TWLeHKwBeQnV_fzt>WQD{G%%D zxh54+OH0N}rbAp*0ZJ5=v~&z4*JLw9E|$!Z%$3Y*WUFpuYinfdgICXzg_331^FX;| zg=D2AZqN-=fsgkp>$I@)cIg)cF z=QXmmH?nm%vh_5w^}=FlA*iHU)X3I>D}t7yRL{#H)pI8(IJyQ+^$dzkNGe_p4nOj$ z=M9ouu?OEa$?cLmBzHEl^*6FjYh;_=$TkBOe0xBNe6QrbMz#r!Y`9KHlst&2o+aZY z4@<^N9%*Eo2nxeVf}%9*e1bv+dqHyoQ9Vn>OP+$bX9~zYQ)!>X&w-Hs$aVl0){2a5i=>pKlpEPLG_q~R72s-!gf1o{r6DFGrP;`~5ftE?1ntk!C}HVJ zFK_#THI+;0B2u}SjFdjaJzGHTq18+P*fEeKVayP@P|Bo{Z96=bOPNdAU=Mj)DLW~9 zDThY39gS>z8rk+Xvh9O~yfY}3yEL-x#1-i7D5=~NlFE01Jh_`HsT|2mzC$RLhe%;R zbVVvkDq1Q=Dz=gBP$S#PMz%|hY?onS4Z5~aDp4w_k?n9J+c8`Lo{C82qIaayMej&u zG_oB51^7`x2RmA&a^)k6kFclm97HM?y(5(eaSv!!AKM99ItG%;i$JNoxRDKU>$g;y z6zFal%nqDXja02vom71z+v!HO^Nnm58rd$wLLPLtj8tho?S`O3J+0D~qtNx^*c191L_(KZ zCbbUYq+6ggaGREnfuxE}poG4;k?k&`INv6<8++jIk=iS@PilW7+r37%M~!Tc8`)qN zuS*>Q73YT=+3w>C_hTpt{RAYTKLC01A$1ZuD10GaIx~cd^UG2OkLPh`D;Wf7n6~C3vmx=jBtC*t`KLy<4~SI$Rq7Y^kpC_9N9wQC zzecu?jci{V*}gTh!EQ~LW@KbrD9zN!_6bLzOS2-Ua%px*D*p@$^e@y&<)H8ddr6vm z=%jLKA!%`}VJ$5oEh#M}E#1iWyOEu#k)6Adod=rArR5me7D>xXD>SnGX=KB7+Mu*D zQYxP#ttvK0TCI`oFDStO3EIQ@OMjQYxP#tpj!sJLvR7c1BvIa*$&n zNdk0=wX|U)J2O0$OPff8PO-*pv`AY?TT9zW+cvVZG_rFvvU4`FbHPF$bc(gKVcq6-XBfCr^ zyDTi^>p^9CLnFHcu0U@_N#(7OR4xhfq!e{hIl@bwLnxI`ke-S?tfxs&7da(8vyok~ zkzEzE6^>m87S?k>W%*p`d5!E!jqED80(>Fjlr8ZPCUfx+=_QTq%Aoylf)2FU>y15a zFJyvW1&!tkNb*|^N`7k^+0_uqZ@u&u>_M_sdYi~8=^c&i>W%DL;2_b41<7tu^4rtM zu7N8w_G39^i(L~G8Wg8Kh)0fq6J5Lj$){}5WP70WNonl6PNmOFUy!~ieW{UMuaVuf zk=?G59d`M#G-%hU^fl@0jqLi3>_)gk5_#9DjPxBb8R@%??4ad)?1q94&NM1L4V|BD z-;F(mJw%kAVlvW?A?`5-6)YySbPOccpM#R`i$-?D*@M!rr9WU#z8|GONq?69(#US! z$Zp-pZqvwa3roJ=LCN<=BRj6M2c>_bl%9VfrKcsxlU5X`U(mcKIDElgl3@fV;{;!2qza-ob$BfA^C^psJT(ZQPh zWOQZpL{7;VG_t!lvU`Dp#2XeQ#*AzWWlS2`J#dAFIkwW%lX|764CE%m!BTq4ILcsO z|1RS$<00cIXO z$d5fx&_RP%1=cafG&bxhED}+AiigNVL);S#Dp*2j=@>}zjRz&)ghuu-MDk6R$-tg` zGi9=5vSo4_*~1&zqZ`>{8rfrEA)gOQz6Fi!5x4@q7`gP6DTS1tkswb-QK$3-g)hWQ zl|!iXY>;Wi9@cF#?J^xQosH~?jqDkX?1hc&MX<2$0hOM;GJTEgNsa8OxB`44(l~;Q z%w#bcnJJCzpml!iDS{4;G)h>;ZNDVWVNc~V5UE^DMrIboJ!zl>kWNd-K$65fP%59_ z$exKv<%?vNV-NWiGAm_P$*gW<&uV1PYh=%FWG{e){5nu7U*E`{jVsVMp``LHkW`)n z@?l@h{U}1e4l*-S@oNZ(; zZDhx_Qc&gsaw-S45kM{BGDxjRpKIPgPJnGPg)|W0PJl$zO;Fmq)yQ6nNPBl>9$^oU z$1+buPRTrLWUp#uuLU;{>R{pV5|s8{HL_RZN_%gS(;oEj74{lPP*CF#W0_Cj6i52u zD;Vuq8myI&`5}XSU5d;dZe;InWbbcepU}uY5f<`ljBE>K z)f?GyT?Q?yg`AjWbsz<4FUXU9R4GWo;S2GS0XSJ>B+|h+i!5s{i+x>+tgWn_$SGNe zM)s+V>@&dy#{yVbJA+cWi>zxS`?N;(8Msop2XZP04OM|s`E*F>L7#$D)&rcd!KYY3 z#;M>=fJ9UvDD4F`vLj9llns@Q!k+e`Wn)B6$;LIZ&u(O&2M&+%9hEN%T_e9FKT38*~q@BksWq@ zuWU6aP1VTOHnJ~nWXH9vPqqP(revFBn`N71TN>GyfL114X=Gn2=uk_e^2+pPfM-7T z!m|UM#=r+z$u`M$L7W6yX~@2UmX3iW;66|S?r&sYg(y5H$xg?ffM>|gl$|9zyODi$ zBm4SB_6?2f8(|4}9w-6NZ)9JCD*-P;PQbEDAcf~zP~o|bDupLFd?8+1F@y@w^|IIp zwPm-;Zj;?EyQ7hPYa{!fM)sqP?6CW9Wp{%T`X1T6jqKYR*>SDxlRbb)=wdRmhs0#0 zG8);pg93a9jZ&4_(~b=%u_y6kh$Jp1BYOhk7|=39c3Lf820I3l9?pQ0_}NBw#7Tp) z7i6zuPvX~Pugl($z1hgVuaW&wBm3b-_9L()eg~Aq?>4gII%!b$0ZI~o1WDotKp}pR zDoGs4OV5T-5`QcE342(7mi;38RrXsW`-w*O^Ns8`8rg5c!ulsDiT{%Q-N=5jksa5v zKG}bWBrf?vjzRK;9AhIp=mbFa(}E7+G)m>>7s|eV#hS|HSP`jQ@`W5b#66&8hU~Oj zz6^E@IF-wBGejdM~&>S8`}Oko_So9Ro?|-k^l;)5!i9kUKGPL+fX4qu3u zQio7N&ymA^7`j}MT(MkG;H!Z62Wy-Lp@)krYmwX}D25}E)nIZcZTBUN3V<1VQ z3zW*c8`-}hQhA@;WbCPYiriGWX>!vW*}pfk|88Xe)5!i87V@(|seE=L`wv{Hd>%?F zUjRwvKS9O$FY2UngqM~Kp;W$FZUgqP-YB<8$ioB&CCM?q=tSR)4;BJG`& z!@h1r?!4RukyCP)8adb-Ik>>#!3|4$S3zm-S|bOp0|Mo4BBwp*pausgBq*pcs3CU` zoZ?6y9j3{khTLPh7ueI(OSxBaujSq}a_}{Bh&FP_HFCgKF3PT zT$KBQNK@@(?lSOZ_4N1j)nPoBS#L%fkgx{*Vskps4P zQC^6VZK1qyBL}X1%PY%kU=M3ec`cDs z@;Z$iij5rL#fuy|(85AqpOI~myn(!7BZpEW2d>47@+OGoH*(*Y%;mnxn>TVm7BAWx z(IAmyP1_(>z?R>jxdM{>>_ExSzL5j5cv0R--W_|8c*uK-oRar$ z_X8zA|3(g6ix=gCuq?mfpyJ|1dB}N*gJt=Re2jb|_CQRMPnJ)SPi^GTYveF(&j6*XO!=%v4*fU)bqE!pbL1Cd59>wp zi{+QdFKy&-Y~*lll34rr@`6;`K$8S4=jEI$4*y1u zU~q#z3YNq`2bRb`l7HOD5zxqiYbm4rGejK?jo+7z9D$%#y`bG>x}~ybhz$m4u7ISn z51>@`v5_ML-p7&uBL5S6D*Gk>TjZ4d-$stmMve$@kVHa*M1g^kZJ`2VBS#pH?w$e* zwmLc-6dDwFufZNs;Dn~K!Lh+Wfloo0L2ZcwKa)6<1e0VVM@&O)LtR7t5(NNW^WLu;lqafSJ5!c9(fGczr z5XG5-5|g=tl7dPjM?9$Bp47k~XiAIg0VGej;_&}rmIf+sfx zeFe}#fmlMyRKZL^Ou?d&Be{_y4IEOi3y~E-2L&qFG;*Zi3J`m2g<2{VlbZ`TxnUGh zgJRi$f|r6n_QV&U5C}QvOpa5Dgj3ptrDYPqeD0DV*ls0nIG;*{yaxC80D4cEN=xpTZ#uea5>xCgr6@|-<99^I? zThNXcD}b;UR~Ha97@8{}adit+T-|Qu=tU&IdkT-S7gtXdo+=zyc;3j-*T^vu93-&y ztqQL|$?tU|M?bF6c!#Y9n*a(8ic=rNBcH+L-5{;Oek%OK9;E*j859{6nHo8!HFC^q zSB7AdkRvNv)}Z{(PXBU}}^5H%PieieBeIc9*uRnTr54N5PpsSM-_ z*w7i8E5MPXD8dlAP*JpzV>UdMDM~2HVhs{SIYoJe zMb$=*xwt|@16!py4-^{INo9(<;8aFNt%{K_Y0&#qG*+}=P+OvC0&Ar&X@IoS6)hF5 z6s;9)8ab9Wa;#|N*we_d7f&l)5p;-!qGKZmuH}}BF3hKxPeD!^=Rd(@&VNEsx{+f! zB%J-zGILU`^!4>ai&Kk=_4A8zD)kGB^0QKtON#Y#6DxERi&9E6^YV2w^GZ^S(h`$X z^^5aMi;`3I-7-NJIwU&hWTxho=ohCJWhUn6Wk6DpCqv|7MK48fMW05Fm5m%58acKh z(vrU-XrTZWe}^iDDTXUXG;*wJ&tXa#9t1e8PMcEc6T%{PIgOlT+gbl05SiK%uLU z3Ay_!MIjAzeHG~;om`}sl%H6Xl9{KEVCp4XSy(|^e~OukdDxRuzG8u5xMEQw$HqpE zE#M&C3JcOwQ1&cq*DYBuJ^1Y;qGTax#-($)*k(q6IVv zIXykJ6brN3%FqxVj!lZ~*u$|yu~RWzvAdCDdn3m#a5(OUg<~Hm9QzwNcHj!fNr-S{ zV`4IAV`4XNYtr;5)-y^GY&H zDq;7%$U;TaO7oII;|vPwxrxd7#qkCCMJ2_so7@!VDlP)W!aOEPCJ83-Mvnar^$m3m zwM!HiD=tx7s<^C?<3JS%k!5A-q z-}Bgm3{ui;1r?{;6t^qxXyiE7$Z@uj<1(T+-K}^4duSb0JfwJ7@kk@b@kWkQjU1;N zInKb+>~T=YoM_}Yfh%NABhoC>EGBcNSzK!yIZjeHWS|)^IbI-9k>eq}@Kn-J(#0O$dP@3A{7Qz6 z9FH0~o`S<0cKWK42_xG=CDTTZ$GC#p0?~VBI>=d#{#R{@9#tTrZW`P!O z8Kh5cL4v~`CFMCOffh$#mU>DaN}fvmO5TkeFB&=CfJ5RfEamxuQl7t3KqJS?Mvm9G zf+HBw>4n6(QdlF$D{yew7tt-{1tUg+(Of~@g=UbjjRPgM_(qO*h@_UJl#V^AWhiAT z@hfFFa=dTk_yi8%&#(Z_10}WmMvf1-!nO#}g@z`z(ngMt;IL&Aq(xFAd4>xT9+0Cq zpf#*gjZ!0n+7hK&SOf1Xw1KD8q|~g`qSV^R@vV{LS0l&oMvgyt8hA>bp!Cf8S zMb1}Rf;}3RDlJoLQCiW+@vo7S5flxaOt2KW8k8c}G;-iNrc`OYK$}1tq$3VZk=Biz z44@QAi{wb^loup5Zbd2ib|~$|9t``G_A9k09c<)eY2@Sp2LmT87>Izzvd{;o}>}n$?7b2bAP`ZmffbS{Y zS87pu*vQG<$jJu|V18HtKLMq)r;VIExWe`YmXeQ?7ZkR%NM~dgd+$&Z+D9eORg74Q zz3)mtlzuAxYUC7b

k@lxXCXgoVRjMz$qN|CIhKGbl4QatbwaiZpVHHgbyL2o_}) zW*KG~NcK}^V=`A}Q|4&o6mDo1_}0K6sKi6BL9w9M3Eg_6pGR0Z{%{JeB^g{0CF@RjNC zi)S+uVG2s(6HD@Q<00xa60;K_dKAC{n%M8L1s5vHQVfv`m8BawrQiu)Sx#9QYr(D@jww z+wWIKX#p!cDT7+TnAua=L)lZQMcKQNQ>Bqp16)jL!eR^50#^1{4rt_5ZRAwP6;Z*= zRm@co_bZ1onJb4Xhc|MnH8czCY+w)+X`*_8g?-*fIheUhIU2(m0!EM&@)Zn44OXTBMMl23Dq^o|#vo zprsDU4hf*_kl4tng~$#m%9+@+LzZ&3Qj2nKBd2yFrye+f^Az{s{xxv-H_2UpmZ zATj}O8Iw70nR0m}r!L4#f+Ak=0<;++AgkG-T#Z~CDAy@BV-JlM7!R->u0LwJciW~@d8v% zYO_E|ZFVCk;+}TpdCH5i2ksK(rAjTz%Nsc@8ab`OfolT`T+nc_^6ExTOI+c)j%zK~ zT8NjRNo`{zrxhezqiB`X$ZL5hqcm}qcPL|T;wtY`-mmmi`CucbT_dLpxaHvr3yvd< zY)h1nDj!omu6&}A)4q|@v60iMk<%Gh=$vNR$FdI+I?x*YTqCCgICPYF>DKN5x#9py zy94G5>{ZKY<{maC#z3TYiV)P*q^if?=zugJ$Y^<}YeLG?d!R&qzmXHMB3t>9@^kEo z{DtyMWi{p3jhya{oIc>@1#E@7@;gxT;=S?*<&TY=9*vw{jhx=NBJ>NQWJT1{(6!lv zx_R*hTk`_!aO`o4&!hO87vQK-VL~p~RajNH7}S=iu&J=CaHw!La{4!N1~hU8HgX0n zQQ>A#RpC|PYvc@WaBSoZZR89G4R45ey0|*$=j0?76sM+?q~;cf6s4xd3#fX!xH=W3 zCT4@{V!y<)%=E;P{35r^oRZWc|2)^c#H5_mlz0JtPZ!tV)ST4hlGGHC&NxpO*RafD zP&FQunp^@MbWssi5oZuu$QaU)zEDM?ku#)05hf$8BEz7zU_pZ_Oh8^m5w29JkuwaT zR8>U{qBLGWq`KNbN5P<0N1@up#8OAW($u`R7G{E$iasb1v{iIebXD{kIU^f6qZ&D* z8#!Z^s2DJ)GN`H;H*&@{ID u0a*e$?*bmo-VF#iJ3X6DIuxFCB+awXXd5DJuM0H zbaF;&ZYri6dVrv~)yUWs&8^ldwhTfGRcsnL;}@#fHF732C_(}?EHS4vwb&6H9WakM zsW?Ld04Ct3;>)16OvPQrL&a0YOT}Bor;#(Mku$lGGo_I;wUINeku!Z6gQ|)@*w;ZS z;F!;V_&l?bGY6Ca6g*vAee%=cQS6jo0`XOFNn%N=cWPz4fJibdfq15*=9Pf@G)2Yn z0-#_(*6Ci9Us~W)3D%;FW@=Dsa%vui!NRG~_A`pPN@#kK(i6;0khC8IPWupcJVWFn zl?0W&{099{gh!SIv z)MaFWNL}eFIiS>)p^~Yht&-iynb*jf4^CqROBhrcR8;a+3K|?6#2Yya8##+W0W1qj zEgAXc{&_i-o}h&+sl_FkdFjrOPEfpn1d@DEX&OOB*>W8ab=s zEo7Bul{N;UMGP?vu??~fa*I^jRXQ3u%NjY$K}k};)5R5@L>!A!6O|ou8yG+UmKJ+e zCZK4as4}UMv$B!1szEW<)5X;_C9@>I2%@uX0Z1bQOy_i!StvSZtITQStZC$|g;^Y0 zl9`iPl9^fz(O%$yqJ5#tY6i8HDvMMWt1MAjsKQ&d4u!NlhwEPfrE=AYMQW?jzs)l+^g(lEl1}#G(|CM?s1qo{bj}2}>;k z#TkSjT$+@dpOP8{;ej;@2z$D?x}>HhmgbazZ2suo(k zpo_bw=I5rC6jeg<|7}LLg(`O%IVUy*z?^npJznBj=<> z&dH6OQyMv^HgZmD3-zp+P?J0{jJ~NjaIxdda$wMR721|5W+MAhbl~ zm&$LIKPrD4IcGO=&S~VF+sHX@iOPRf1_o7Crbf>BjhqWWNq!+HZX^PVGIJA)Dq+Tl zWag$8mn7yE#0#jPNkPkE=ZwUn#N?9HqM*dQ^we07%#>8eoE*>M;!;rjsiJ9uH4spB zDLWpJfByCudw3+ME;ll>G^vfKHa0f`)$3PPc~p5BxK;U7`Beo}1yzMqg&C~k1q5I& zgzXiILj+DhVo3&AMQ&nd9zqyYP35JeR`{pE^djUMIafDwE@|Xk*T}iJK`dSXIz$C8 z>Y$P4k`FRcFFd~}ySN}RIW@SXs5H5xv?vvv@>Inc*%qlvs7f|+E^Xvo*${A9RYp~o zfm`LZs=TT~6vDPpPmnvn32J#G=ZY9a-J_R$i6NaKLseN-C0;GGr4_1GQgOoxw^~p^=G6N3w~JCEmAcGc?FvkBs5G_&Ef?#&?5s8G^jakk*Yb!IHJ|Z z3#dT`Bt2bRy)#SnAo&^QNWEb2I#cKTl+??rRt(&#)=cKAwyHYee!+T4iN&eWe!+T0 zsX2)ynPsUB42_%{8#&iNT7P8#%W%a&B+r+_6N}Pt~8XKsB(Db7v#x zzJ~Hf&I1jq5k9GD;L<-bC^J0+%!-1P?2!6J6x6tY)ezt+160F6iwe~U)mV^ik*ZOu z(W)_xoVyx1cQmHHRRPE?57idcb`(t=s-2CT#~L}0 zHz*>aL)oF|0gA3Z)qa##yy_&?DIlLsZsa_*P<3h}=V`FlT*5-&RXeEafNYCYoeA>5 zEY;bKoM#$2&m%lAPjw-RTNbG9Z0+Ep@fYdk`QjMIqK>6u%Bj?>~s=HKoGjOZ!QQfP$kI7th ztLj0y%sd-@qiAAY-CDF-{rZgzE#0w~-Kr2^VB}H_85opyoxFH3y z>sBM@okq?p$W_h})uWIqr;+n!Bj;6E4FSo`r$FWXY1K20oYxvTucMXs@Zud-3SWkl z!Z(O2g;Bh$dWXqe^&o>clR1NTBj;_9Q|~}`ld6K2{HbnlSsQ1^6k zh1XIciAkU!FUrr!Ni9N+nMJD4L1iVe${{wqRDBgM0P5BevSE?xYmn*0D1`**TSm5p z3=$1&@RpnEN7Y{pYAaMfseV@dqWV?!o9cJfAF4kaIUh7~K5XQC)X4d`k@HC-=hH^c zXN{cCS1_ok{!#s_`cL)08iN|68WX7Z^`eolypgY}k*}kXZ)+ppjYhtkpll_A7}O|s zOfJbRODq8mV@N?8+>rDLZq*gL1m8AxQJB;xHA`os+X`TzX9|ulG@dC0a z2DpJ{CxSDpQlVzV3mEt$7MFl}aXG0asVRs?H^f+{#NyNxu)&!nl|iYX70K}e{IJR> zJTosPzZ}#Zg!G~zj)cjA+z2i8)OgiI7}S=j@u~5v38)FG38@J;a=vWjeAUSLx{>ou zBj?*j&UeeyMAgL9#MLC!B-NxEIo~&Oes1La+sMn;$d}g0R{#nmb$m6WPkuVg>UaT5 zd@2!js4i&pfi76BaR6k4N4$VJn6C#WVWPpM1t8C(*TyJnh_1OoO+Odcz|zd*Y@hu6 zY)G&{jgA*E0~@MGsQL%RSz=LUaef{|KP;$~)l?bO7OJTz zi7AlHHN`2}y5@Sudd9F#HA#uZnaRca#hJMUIjOmc6$q9d*aM&u64?G6y@I3)5Fh4J zTQx@pwZ&?7YW8Xljhx>bIe#>A{#>l)q~@&V(#ZL%k@I&WCwMs(aeV_KJ09@@IJ+)k zpiYHBEx2!CXl7L;O{%ho7XyjsQk^5~U_t!=qfkqyaMjr1*p5R8FtVW*3MxGgsJVzRN?lkgzMvd=M zXng;V7bu3sH_Dv5Z(;@NjC)Wjq_bG8?`&?OUyzubotU1ABRwP+vh<+Cn$~G+bvLM zu*M4%(Z(5wj+;JSmvlm1!cJBMKvD(L7B3_wcAxh_?clHGxGDXJ7hW3A;R$jrL+kVq>PAQVt33MsAELq1#)TQ7-(ofiUjPA*a&rmc)UOu z%n?W#lnRXjcj>3D%`n7?UIR^SWz15hW((yU&F#eZ^94s3re z_Ec~b>J)jJ)su+!BG^$#&S2nV;9}rbJE3-#L2ZfJNwrgIr`66haw#@)DK&B_H*%>g zQ9Gx0UhRU~#YQgGMlQ8RE`vrcD^L#vwXXnac?T4wmSyIb7DGD_5JBgh#Ny(30S$0t z9@dM1Na;c9G@s1k61`&35Lj7eYI(eX4i=@b0cu#s1~x`n950}aMR`1mm}g#EKE%d& z0dX8Mur}vSwcDWf`>jSU^@VD88o4w;o)yGC)T8!*A##!0L$ybZT$+tsdhnqhwWn$? z7=#w7JyUz$$fecDrM*b)rP`}TE}cd$-3G;2m#`4Tm@{ZVJS+q>+~%8@o|jotnvxnX zpoj<L7h>ZDY6(mDwvs{*T`kk$YtEfWz@)JNwZ9XNSSaJB6TV|?)O`i z?d-H08aL`}>YNN}i`3cGIU2c48@bFDsdK4wH*%Rba#=K}hC@?b7$l}6;RfQ4bMUZ< zx{$hXynrl9vOrH|i`7NcML|O*_?2NFMu)jxTwMaCtEeumuE3zSL|sN*R$WeAzLCqO zk;}G`%dU~jeu=uGx{|uGx=JILLnD_*BbR3*S0E@MQkaWi88jrZC_S|V5>U|mNn)mh z49%lvXK1EFHXP=6ZFOA+wT0?Bja-fk)%6;=oIswzk*m~=7$O&`8>^c%ayd70xx;gn zy1BX)gU}*%3w6szE|*3w*G1~qpj_ofy<7!zu9Lbes!d3lDPBMbl2(xY3C>vR-i&Mu z)qNVdypS@My1#mWdZ0Q}BbRR@mro;?H#j@dtOKU(xc`yWslTgZU|tDRj|Alz4)use zF0)21zeVa%>d}o{{*7D#4XR-fKVbDz4DxCWm}!aXNvLT!RXv+QZK-;idb)asdZv0- zBUf-ES4bmQXd_oxBUkuR^&ItF^*r@_^@2vOh(@l+My}*Wu9QZudQbwOa~(px)Q`>K z(A4i#nwgUloLT}}Kmr|C$6Wxyg0)h;8k9Y%8o8nts@F7fMT0^ISN3QCWsgSnrbe!q zMy^Ce_Gne_043Ts_4Y=t*ha3nMe3dEU5#Avja&&-t8`LP7IvslRG)%sCBs4WX`o6c z2~_Dcy#UWe#0yA5G7OfWf#jRnpnNl@kt-EB-^^EEpuP~4Z!#LW(i^$b!1;#W0}0BG z`~S>sl3TkN7E{aAS23t9R_9QUP+tkjP>o!fi`7@FuTfvy$d%Q|mEFh%UbLlz)6YJz zQ#A1f3`*{UgwkeEC~axv%7u@@t8Z7|$Dp=MeTVu^^? zY~(6x}%qptd;hM0H(3B~~MkXmM5dQDA0kiSc) zb4a|OqpvGO+ZBe$73x>juc=>GzoC9p{g(P|^*fDR<&9hwja-$DTvd%+)s0*=ja;>j zTy+SOAAn4jWOPX_$jPtFP0cH*sqyp+4srDHi4SlL@qjttF-VyTin8F4AkTn!sA5-_ zc&LkDI-Y@ah%@?R=4FE&>>uLc8Wiv2;^yP%9t>0T5~N59*hSm<5=!sEszBXPU6<6f%)Hc;ni^+wli<*R0RNy6m|34ds+2HP#Ro_F#`}2s zdAkP1qqy-aNWCnodZ=IH!(4-cJ^lS)S?s&|57av5w+17qj`^ehSN)&*e+`C4uEs{L zrbe#jMy{4duGXa*Od8A@EE=pDY>ixPja==GT>Xt)6B@ZzgVHy)q8e5aKwFF`?Vv%^ z!ouco3hEdQehonewS^i2ja(fIHG~?uIzgd>qmI!KV~AX&A+90O$ko-z)d#O*G^91; z7=#vS$Y{tma&MLF_zglV7K`RvvTUezsb+LxMhJ%J< zBiFP>uIY_jGeFUWT>7E5G03b`G~7V}<7BiGzUu6d1I^OrFeXoP?&6^(F6rLq80sVst2D#-4{QA!WGngxGF2uW6n z43P^pk{Y=d!i#>5G*H2Yr%>f$;MT}sh+LtOsgb3Tt&yXVtC6RXuTjv*wYZUMNh8kx7+cTMBE z#tl#{ceIh~NF&!_a4kp1cmrez5^0hViHST$cR(=9Ms4Xv$vAiAJuIpm;*ABXHDZNK0bzr4LA8fKDyec-_c#3Q?22)A$Oi z$=+*x(DUQ3+QqxM)TGK|;R?|+?UeiI-QPWA&S<^++ zRntw=UDHF;Q`1Y+Thm9=SJO|^Uo${6P%}s~STjU3R5MI7Tr)y5QZq_3S~Es7Rx?gB zUNb>6Q8P(1Su;g5RWnU9T{A;7Q!`65TQf&9S2IsDU$a27P_sz0ShGa4RI^O8T(d&6 zQnO04TC+y8R zlQpMkPSu>IIbCyx=1k35nzJ?MXwKD~r#WA9f#yQZMVgB>muN24T&B5PbA{$g%~hJK zHP>ja)m*2!UUP%yM$Ju{n>Dv+Zq?kTxm|OI=1$FBn!7dkXztbAr@3GAfaXEXLz;&* zk7yp%Jf?YE^MvL}%~P7EHP2|C)jX$pUh{(HMa@f^mo=|wUe&y&d0q2{=1t98nzuFY zXx`Plr+HuVf#yTaN1Bf{pJ+bSe5Uza^M&S1%~zVQHQ#8y)qJPrx}v5GJB?g-8@cW^a@}v_deF%Au#xLgBiG|bt|yILPaC-ZgT)Z{+&W$n~+2>r*4w=SHqCja*+FxxO`WeQ)IY(a80) zk?U6@*Y8HIKaE^}8@c{9a{X`QW@zMQY~*HYP$Y~;>r3z;zsTzjoeEc zxtBF^FK^^t(a62Bk$Y7m_v%LOHI3YB8@bmta<6aX-q6Usv5|XIBlqS;?k$bnTN}Bz zHF9rnk^2?AsjsC7YRJkng4aWrq=I*@6xY-^y100Tc>4P}`ossjc*BN!v{XQfMKKfy zIeWmBsDqSj$wuFx2yP@U1SxHz>0}8tfk$eZ1)vj|LGj}g?id*y4+-($a8I|8 z_}~ynKNrU!mv|pfry$25Pej=HfsBwtH^M2@)5j$~%rVH*(JurZ&VeAc(&%d4ot$j=$Zmxf$Qn#>=WwZ8t>ui z=;9g_9Pbw7?;8&ajS!G|prL4#2uJ{#riX5tV?cn9r!&Nht`Q-wexSkAc*g+Gcu?Mj zIX@Ys*9cv2u(P9IJZM-NlE%Tq!@;0{iibK49vk3s4Fz=l;Oq!%UDe zSri+b{R1N713VmqUE{&?G%%%eKuSp&a?_d*(jkFjIM~Ga;E+f})wBqt3NR( zJJx|TpeE^X#}H=^e0ny5^e8cUq$Z}M78TdjfI}TtLI#EU#fSQNy1BTz!ThlWq+SJw zdZ=1(x`mfz+d(>{8NE}B@=|jYeDhOEb5d(+eEnTQeO#e)f8b$Ctz966GB^|lhd9FL zzqIy(RN^u+IK&@blpO#ml*3^pbmj}@v%?^@NCUHwhD*GUKd82VDLMvHq>7jlbcyuw zbczR6-i{%jkSYwl!aNDmB9EaZG$;sEETK8#3`nsol44g!7jPX1$pZmMh0%GCQgwt< z=)|9kXK;WIsEy$3=;!X^>H-d8cw~TQVRaCi0)kwFU4z10<6Ynufl7ejcu-?3#1*MU zc@<FNTS&~)c75*YwD_7zBp2touJm?w4Tuj2@(cqt7C_COAV0X;KOnW{2(>;g@qwZKA+E5QXvZM;P*4pFY3YEn zil>ijP`pc!XBeU-@gHP}3c`>eS9lt53v%>z4fhZ7MoG!qOduUfSatY-8^V#O>RAb^ z2dx%>c}trEq#h{=`?$mhh5CV%#0R^A`exw81#souAmvCQhNwCseVzP$;$2-lVU1yJ zK9E|Zn1#gva`bBpf)q(2{1@!%90E;FNXrkjML^1soEho|54d2k%_x~iTO6bu$<%Pi zAU{wu8g)^FwiHMil5c!m;*nBT0CI}fmIbL*MP2{_Y9mL+2Zj2$f_sACDh0F@0v=cj zAT4^xTAYJiK}ieLfdIEW;z8c^^!EexB%M6{9D`sD3TeiFx8qM)v`ELgL;VY&abu(NU1imQiQ8rBLYALC#X~5 z5+Cdu65{CxUn!!k57MZJtPvij0p9K)Lr}ui2&5dvNr;?)T5D*Vf|R0!3CJ`^K!O4u z=1U8ZI+SonsDl(Q0YR?NX0Jc$8WL@5kX~hE*CXi-4G9Ph0rg~j;axs$JCFt(mZ4Y! zjz92N3p{~1g7o6B3=$jAUL4G4E+7pkApl7YzK*Dg)g7b`B~>7Nfp8SeR4uq;dXzi|Yr;cYEudpTLOk6(;U%ng7)XmevK25b5UaqYCCojMAjO2{m9=9)s#H*n zgfDxF_s5nd;z2r40yH4h$;Z>Xp&d!|FC{7J%0*$fIcpa*e`jI(X%R4Vq%t2=EXI zXdKQtG$d~m3fi)RqHB0>x^X@gfZpqm68(ICwv@UjOTG&coD28X!%#^VoL z@InYp>^kuTF?eN!44SskVAvRuuOqA{p$%Rgfu2af$pVk@;3X0YjKP`dpi}EWohujD z_+U?Wzj#-_5YG@FS8%%smg#yx@hyW*H6%B~N5{1LK`NyXDqUQ|Tz&ilK&HiqxH|g6 zOq>K#Cy!7EDdRyyW)N4PG&rV$6l)+92L$s~{dcIOOLJ?lJgy`nd+jI|j#t z^}0EF`h*6-M}V{!g7j-KhNM=M=z`CTP;d-o@3~2j=5FAT0{GwLn?`FxC4Bs16QwLrh(0 z9|Eby>U2;thmGn!Y>>(RM1s$Oi2M9O{ox^n3q|S zS(RE;Q{(L8=;t2q=;9LO8XO!S>9_1nT+gWP=l!(mQ24Kmvv z!)(w2Tng@~d8tLv`V~)oAfJz9MVdqFPEW*MpJ`7yugJ)QRLY+b5`|vz)5oDMR zu`Yv-_=AfeXa6wQAV+t2I=lih$&|23ph*c(6Tmqn-qFX?5ov%{`#Q)72jYx?mkQv} za)(4esILb1+AWZ2P9&NJE|_3m1Lp*|d3QnPnGk25OQfHpuctF)Xu}y6bKs@!MEVa> zdIiTj27%^Fkh|_M)4(g@iS!?6lqU$>j|WYbxCc4<#)Hx)XfhG*Kk#~aA`&Sm0-*`P z+cn6~)dw_m46DFkj(Z6TEG@!;1-jZd-Y3K#rVqT%-k7jHkRyUzL3KSSEg^N&z)SGW zh%>;?A2jyo>+cuu3F!xymr52)Q9KEki;}h@W?;ikji4F@$KM^?z61JdZ zf-QVuu3-n6h9zg07MJAbDuB*N%FIbEQgF*jOfRmffwT{tk=q73Tp$y$gbZF20(?S) zK^Vm(UXV#x(l2(CU;{v?t`Pv4f$1Ng#Ju#<#Prmf8c+l|dpHKgdj`jc`hl9Xt}gK) z0a%C#gG?~Qh#yoFpoM^Auy4G#Yh<{8kPA$^7)U#2^rC8ajtAAFp^on0bOxRThv}CD z>DM7(y>oax$m-xozYs^*+6EmNkWO6!Iz9cs;~kzpu1KT5I`SZ`Rs^*Ac=~xmMw($o zJfus9=uqh>fy}|osHmQYhFwUIr+biN0DSd{jw(nqW}%K%vzw2!KEU4-G(`#> zH+6RO@quR)4Pwmm^aBmx1%oOYSLg_@3$#xkAMD{6gy@*-XoJi%ClE*hkU4u#AJ=$D zV-IQ=EPC}oh7gKgtU3)rItf`14O>wAF2vI>-YwJ*rOl&b0y2b9_JA4^;27)-T0;~9 z3IuqlnuAQhtldy!DcHk5C?ww1FBHD6N5=}J&z3;wfl4d*st{;L96APo6cM%{qcBSx zm=hFq6`ZxTVJ%AMhzQ)7%>iT*?#znCBuGMo#ilbzKkj@3(+?_hpn>Wf5D@Pg;p`fK z+!WPu1DS(6T5y;X6cPgKYw37`Oo+l6yb8L|`Ag75DX8oLt>*9#kN5NU1NEpveLw{w zWR(ecsyaR(dvIq`OnadIh7Q%iTdc4U^aq(|NrJmk%!8-OAdoTEBp3s6 zPjI|rXox?kV1v0Q6l9PyK7;&|vQk0kOQtF~LuwPKi~OCuoUuf01jsKZArq8JFN zwLvZd&44+BW^O_}ePKNfooJA0c0@Z5Vj8jo9fM#^a-BGkS(uGp{4PT_E8Nx58y0Sf zAj2?Ia6nONK~a7(=z;^#xCvCZUod#hT@XqhNCBCEnU}De0BZRfY5A>aWnm?J>rTCPDs{y}ihm4OWN#t1v8Va1@)g`6CP9B8u)*^|y5 zuFl@^!Qq~um5sq6jv**xjXISegE7Y{h%ngE$v-p%v}h&B)!7lW><6QEtO1$rLxQ6r z`3n>Z@h<+(;7%r_H{|T^3mVge2S+`~WSnu0B{+~wcJ&PiiHvvhcY)0$>NJ5&BgLJd z)DCu~n}28!xV7u(44Pzj_6Kb@M=Yk)X$6^#nF)yq-Cz$#7x<`;P6x;wSMoyA$=}}x zG@t|>n1hut-5?`9$ukn>Pb|jvfs7?9p_-Y*hxo@E8^wdNq_e*tntLaL3??%OKrJUM zZk+-$(uKSr05y{_9XTCjA}P5EDP4N{f)%f~Rx* z;oaxCAX9@$FcqHmL91yz9eq4O4Tk%FE68M0ssp(H!HxuVR2_rhjlvxu z(@04PNZt+(4G!>h_Vf=8j)&)G%&fc{WJ4l}X${gu@bm+_HpCOOT>=&&pu_-g!C*EX z_JOR6CC@rop#-bk!SgeqwXmN4;AUq$YCGj1$SPkFtpZo2AU{CbD&SEEP+brZxjxhnQC7KtyD0FKa~xzSX8#0t5{IXpP(N=!|8T$ffFMVAUq`rsr$7d} zkq`h#i6_)A$kox=1H3Q^G%*6pIA=j-lIcirYZ(@KFas}u3?wVRg!+LE^l@}@h0W6G zTn3p&N;QfUhG5e`YpZ=-L*Nr=I@drZQsTZaN1sr5e!dAZjgsI4Z;^3=FA&$c12Pb^ z(~gvAkOoY^i96mk!ZRcu)bosYaSaXuO`Rb%?(c)l!|PqJdGKHZjWUFJ28Dz=f+kF0 z#=#=@5y&*cu7jKA;pyVy>K6|)5SCWpDf%hMJj}5GJibLL1bzL(;PLPRWDwr0f$B|2 zt>@_n-t7|a@8%Zl8shI3@9XFs1e@g1c?~iXvv-Qean2DD1`&w0KX7lp1DS-k4#hIC z0FGS9N*GAz*)!NNJ~S9Hm#Om+WGvqHFfL=`T|z+(LeOeRU)K;1e;3d+416b`&KHou zcpGlG3N#)C%JJzRr5;UWGDWC8I( z15dDyK0f~8pq*0wz5zb2kWFAA{?7jJ0_iWvV6uY7#T7KF0PfAgLkZkp26sbYF4tuM zS%9}HLJ51g%b~+8!LB}#X>?!ErVMDl)nx`5inpPR-Ox}!(2@!8_Cc7zFeBMOM&d2` zP>e*Xiom`H?J0_P_Hl$av~)Q^21ekk-H;4aNX$!7a13&W4IP6s3)o=S2vExQ42cJC zf(rKV3_xz*>+*nX38mN;coGkC1udqC4|er|H70cVsbfWuYXE4uvI}TXD&F5M9%%?q zR|sSUv8fPiSb^r$rMFm~xk$vz@qLBGT zkP3g_08bxR@&&SF8ni(dzN1T58f1VC=nkOF zWCh2(#GJ~i)FRMG6o^@zSqvF+tE{O3?H+=pEztOrr*picpQBG?6uiFIl>?dNPlQQW zO#zLmgIoqP7QAm7-ow=e@9MUt$Z%MG0`K{D!88iI@Fp`^0dlV+EWN{g3EolzYR`cg z!6E*Lg=)IsE#gijo9OH6>mL*e+L`U>4lbWyE(Gr;w;Vr4 z|AC^opdcp|w5AR*{{f9sNHype?Cb{a7{J^G-i98C=`LbS1Z~*#@r;6Q33*I!3C3V7M4PM&o!b^F0st5bqF(f$B4>lyO3*Jlb zjK{46&2w=D)&6h;!MpCU6dc4m5YnHHhqkaloltl!58kVfFBcGWEqG-aV%3W-cq_jL zCH{x@zdXYn;d2AJ;NAVMl$Z*t8NsI~z|to85CDoC3axXU{X_lWxd?n>fG-}02B#*M z7G;)HVw9l4pkYhTkVw!{Gsh6n`W>*rh#CZZtbiv)Mti$PI(s;J`hoWF!@>c4`alFl z#(MgJ9q$TSn;Yr}9^r6xfedQGYylrv5KJ{&++9Ng!sFdt;b%JNf=@6Apqd4~-k^o} zuodyT;4=;=i&o@73UY)`KI(#xKfss238xrjqk}_&oC9FfKDyx367ZFrq!}LO<{#t@ zJ_JoSn;~)m{IpixJl%ZAadFVY*>sC^%W>a0pj)Y11-Ws6`&}dVd+3b=xDLD5t!HFg zsN2xU{Q>FlIo)R67Ts2Lrbh11johCaxj%vrpQFv?Q{a>CcP9V&1M^&$ZZCt{BGA#? zjgXV>b^Fw#8@a!LPrAoAEhA##!KCf&`AJPeIIEQmC| zO?M}Q&?4RKx;q+q7#n$*7U}L%?`q^>ruy-Xx(9WSpxVT6Q1@7aO(PF0`1nQx=wbI* zG7Kz*p4L4B$&ZlCdLER6E;RCRBIlsXx>t0ssxvk6@HFypH}Y_Sa}b?xR8w}`|G4|t zW4<_;!*A){1!XP7#Vfk^bniFv@HX=BHK>L`E@UHk%Yp6_-KP-m!;F2Q`;I|vDKy{B z(0z-P>wY!z2rkuqulqswqwXi&&y74njXc7QJmQT!5{*2@poD@gx8Xatlu&B?q5BJw z8hWMJ2 zEz}cjs7&UsPtEesK}z9af-JGNpCW(ibql z%IhhEj{f9OkI++s6v2%=vWxXp^i=iK8hPXzdE^^;6hKi&#L=Jl!Upqh1U(&4KK+h6%_$On5 zo^3;UBaa67?9cK>9xZft4*W6JgwFmX=BQF|!K3HK5V=s#y^%)~egd5yxPyRayDb+3 zx1KLU7wHTG$e5E4%H0Th;H|qTyRs3x zt&w&n=z%xu;_*NDP!T=wK3xd{$5`uuH|8o~r~(}&0y~icc|1%HyepTeW32TG^a@dm zP`whpD$v~sIPc%lt5(m|tJSMhFKFa31>L{HW7f!H4!wT|SCNP>^FdNj3n;&}Hu6{? z7fc;`oqAoMg2}p($EuOX5?nCR|ME*^$NhiiChW*{fR#)AdXr)0(j-{9WV2XriuxM8 zX^lL#pgUW5?9lT&jw6W)2My>l8@)M=Jobp9XujSuVyFw6rqqkRYUn7rmBacfX zk82~3TO*HqBacTTk7px~7s6!lE=9uG7Q8``l%ufpz$Dp`W<4=#Q2W=Z5U-t@X5_HYo^UV{M z^*-u-0W};Ew~y(4)l+Tc32Nk#2W2=co`mWArOyB=*l=F@qR*(otk0~^qQTb469T&Q zg(tL;Ck%S&3)uymJ{Kd~LVfN=o^Yhl)#uaa*B1bVZd4;rWFt=mICKZ*4qI^HcE4uJ zJZL!Si|R{43pafUSm74ESYJxRL0_hkC#I2WdLvIPD6A=Lbb%9}z9J}ilp1;B;DwvM zs=f~Bkaa@Wh3M;n3O9X2NZ}R_Dclm_g`2)9L*znzvqqi-coC!z-f4m-b8|6p>)S9y zuF$vDx6`-RchGm#chYy(cWLBFYUD|7$R z0n2Rp`h^T?3-t>cc?uTl7d7$}f;@v`#6rJ}A##y^xqd|>Pf;UJ8NA=9U#(xqAhbxo zM!&X^r?`=)WRZTo+N(yMQmS8Lso$pGiE0zWLH%yfh($SQ#G)Ys_lN}~w@(1&_KA%= zRmg?m6#c3C(?Eq`Z6i-jBTqHBFr@Dt+RBdm<2Tp%3fRKpWw!o2)M`e5zW#znp1MXJ z@I8}Qsu`3%B+Qtl`pZyS*ZM2<*MXAuD*e^^YxLJP@-#K_G&l0JH1f1A(O<8>L4TwE zrbeE&MxOpgo(YXS(?O|Ws3z^5`ny3%dsib*`$GLajXWJ7&)`bh2S7>tp#Gsop3X*| zUPRJ9s(%8M;Ew4ZZ{+D}`|5g8+{&)Q!jXX0Od1f{8%x>hF)5tS-ss1nh z-}-;_|LXs1i3K}0hf^>*Xuxj3$)L8-fTNLT z!9oMBMxKSB(7};C4EPu#7a8yy2sH96YUEi4&mIQC24W0Ciwr~zL>qY)H}Wi5WFQV2 zt6oa=v1$W314UGuNEvoCP-kRYXrR%^vl6M?H_$fFG0+8Njx~)us~dS%fy;dw4?8Af zA9nmR`{i9G7HGyXFfuTMwtx*lmx4EP`8D#aU2I@(U}0d{$g{4IXMH2j22gZS*aAkW zGQbt2fjuao92$8x!kfbe&IVo#YRe2<3|tM|4BQPo3_KfoHZ}5WZsgh0$g{PPXImrB z_GOF(20ozXFk^v10BG282dFs=V(db9=P(*}G>Bw~Txby0$g>mP5H|pCUB%PH(a5vAk!MdM&)!C!eT_W(8+i^i@*HgBIfO77eAqm} zCcXjqq)gfX|o5<9~1y-vE59JfSAO0r)t1;+yye;G^V; zYT_F-8?>NQDhBNaJq&6~3_1)t4Y~}v8+nd4@*HdAIo`-~Vu?YoL7zdt!GuPhlZ`x= z8+ooY^4tcc>LJ=NGMH{K6IAlgXyiGy&|p?0&uNfnaFzV?Kqdctg9VK|XBv4fB1-`9P&jqU8&20g0^Dw}Q#q|c8P_1M*Xs`v;Lb?QM zAvMhatHwGl4k_Dqg37jCjXYP8D}lWR`waGjDuEk~Jl7j}u7N9o?6l0BR4aXb@OdGb z$>4LqiuHXHEA$cz3iO@x^GZ_lN{aP^QXyB$73({jo9GvSZ(&X?)(2fPU!0PyYp!Rk zXRHrVqMuxpqYoZmf{@7f&Fzn0mSt<)5A)^`gX5^pCW8|OCmVTgHuBs8MHG?ECWCVZ z=TTGmC4(Dy=Oztq>V7b|ZEy!PH+dI4H+ip-=RS09lI-y(gGZoHf85CP06Em389X<5 z0SfiUjXaMUc^-m8y_klfo|y-V0qj$LlYa_L_J)Q2TZ50Vy7vQY^y$fBgHL*@245O^ zo`R?Ro`K?l!nzldB!7Ye=T{@ob3}Ff*N}ynSw2HHNOfz-1*&CVz^dC<&^ca1UWUkp zhJ1}YFX0u4A$S!vo}!tHf!k1oA##PGsG*plxS@ohq@k3dw4qER&+A5>H;p`R8+qO} z^1N^4`OwJov61H!!esE-(*&y~LuJs~WKyaoL-2vq;*5B23pWIxGflW^G6WwojmQ7s zs>u+1qBNnZ$q;;yH1SoFp()7gL{&|O=7x3*YRe2Q3@r_<46O}q3~d{EzBKZDZRGjZ z$n(9C=SL&Y&t-=8h7N{~hE9gghAxdfzZ!XdH}bMH^0GGavNiJZg3>3p{=&d?bcV#* z9Ya6E00y;%hW?E_e-;`BHuC%h1tN}G(J+)Da*<(}VR$3YzeZjrc&%s{Wf;pKw8${p zFs6~`e4C@7OSBB7%&M@Dw08&AK ziz35fMz)29C5^o7NaeI)xnYH2r8-k1FIOWkXCp5MxSY5W4P9Eo#A@J4Tc*HH>tcc++w)ZaGT+F!yS#h{EfVVjl9B*yrPY~;*GqL zjl9y0yt0kF@{PQTjl9Z@ysC}7>W#dbjl9~8yt<9N`i;DXjl9NjjlABCyuOXR{*AnWjl98)yrGS};f=hJjhq`Ad7~S7 zV;gzn8+j8Od6Plj+hw@haF5|$!+nPP4G$O|G(2Q@*zkzqQNv?~#|=*yo-{mVc-ruc z;aS6ThUX107+y5IWO&)|is4nmYlhbiZy4S*yk&UX@Q&eK!+VDJ4IdajG<;heGaGs5Hu5fPYxt^xp^tpU>*p8cRO-9rCzs}eTXJrhMY)M3iOxBhsd**(#i>P^i8*>1$VR<~ z8YL7jPy#aww5^IttwVE@;?jbG{Gt+Y>oGGgIj1xwwOBv3q9i^)DYXLCEnlE+5s4Rw zgSiFCTLne=pmU%g4bjA+l+w(+eBI2vlGLKK#NEe`Z{nFwheQ3_mFD^;Jh+lE2iP_1Vmd2&O$A0te{RXph>Zck{0ZtRvD39FF=bI6jNQGrkcbHq{5;GrFfwzS-?kQ zV1+em=y^gdH=}WZisEBGsHGP10{$=`V@r&n;U%24I%>I%Ex!drt+Aq>HPHA&ZZ8={ zK&`Qf7og=@|NPPteQ33q3~s!kMr1716?X9gE?C0UCBHl`CqFR-(p172v%_C_Btgw~ zh!-fV(5(Q~cDe=dLnUcDdWe#6(xDDzAY!+@)k7m7A`TmVRVy0)#!F3Z&4#}F~~C| z1L~RUn~k1t5{a!gG%+wW&`~h5Fg6Ey$imV-pVCLuZ`X?s2aU%8+(Tbts!a{dEp!x&OpJ{{{xLK% z(NQpk`{#$zUyy%(8vQc*ZS<#+x2}=5zLB?~k+*S)(LbaA464SAjl4~byv-p0v<#?! z1T*?#%l<=Bo4F+@attj@brj6ZV4gANHWpw|TVl*(%xlbN%-_h{*2vr5$lKA#+quM8 z&{&8;)mWsFx2uu28|0au0riaR$JAXOK{3_FCdP(33P#3;pxk3(U}31EU=H_>oUt;< zKk~*3#)`&Djl6x0y#0;56B>CZE-_XyR%K8%R&V5;)W|y-7V_75>6en%8jiy zG&eN_<{i8dQ}X4+v)1 z^y+9tRT~-WC>R+S8r4QrTbP1UG(4Ofj9o#7V#}1qZpQ8ms>Ys;yt5j4XM+r#12$Bu zJaq zsJAqx+St<26jXqi7}rKsn;L);sS(29IOAlH!STik#)-yBjl7E*c^5bGE@|Xly2Loe zIF&)wIK7c~StIXqkijbkGExsn&A73^f+4CJRHz$Sm>AZES6iAO2U4MN87Sbf)m6sj z#uW^z##N2Hs~UM%gZ#1v6iE9YvR62L=ZdK|H8nB<1(LC0ZB(_HxtWoUf(g8sHEu9& z#c6PxaXW*mac3j%x<=mhAcHqR3|9Rio%k=N+T6m_SVzIo!qlubs@l>R6#B*pgZquA z;52xu@iYci;~9;-n;LmHgACpRFS!ZM?qdXXne@{u8JjlMs0-Uk)5hMkZ#6GVY7< zPmobxjlUUxH~!Jcd$p1GS|jiEM&27sjDH#bh8T6Tk@ptVsCa<^Ys1EGzPw$y2kZqy z3v;*^OxR4gK?Mc2I>m&?gcst4JB_?|p4wnpidRzHa1w)5!a_k@wva6B`p-NNB%rX>+#_%f(1QFBnUH}O`pYvlad$oZp@_fsS1_a!ENCjJbnCV`E-pBs6< zfFk5;gDPsT3$z7>WjpgRC9uIpMwXVf5!EK39EwPi5hk&qK#DYpGKn^cY2^Lh$or#_ z_h%#TuO%jNCh-iaCW(!_zZ-e~fNTISLK?7K<+tSY-6wirzZip>f)Uk*7Rb#jlWda$ zkY934a!v9~@*8>oH}Www@-a5@F)cAEG$~?GH7RN2V{YVQX>e@hV;#`sE4AI?Qrr=+ zUyMvm4MA14fhkfm-K5T>8RVCGlLnJUlcq*K_C`LAMn29)KCUGuEheoDswVA?eB6zE zJRrY-SJ(|$yu>d%9-#gb>=#1|GmF~rYC|KW?x4v;lW8EYOfs2lGR0(SBOiYwpFks@ zU?ZQ<5|imBGZ<7&W;OB&H}Z*qydpZF(IUjPZHiiWOtq1@0jR@aVqyTwF-C?+wY13+ zla-)C99#R;WR=Nk233=_jeO#bd=env$hmaAdri=nNYfwzxY_bCs zNLx&{nrt)K-pD82$S2dtC)>y;x5Q+p$u4kV#V6m$r!eq^)#IKg(o+2p-xyg~!0LLF zqb8?7UN~lQ+~kDG$woemUP0m2Ppw`HzKJZ@fTcQ$Z25Dm$nw!C! zD<;=X?tr{-!{nyPEtA`ge434XT8(_#jeI&wOzxW8gLpx=kxy^nz3^x56VEp?kXW!Z zu!P6LOOtmXFT65&ZSuzCZ6lvSBcEX-pHU;9@e-5wCLb77O+Gd9nKbg5f|94%K<2jn zeYExcsG1rz@_97!d4kOK8qjEcWVL1T(M(9R8k$>xI{3y&1*oZwsUyxHax!&h zP&IXJbCWv;YnL7+YF`dK(6Y=Ah0XB9OdI0~pknsQgm- zt@20ZZzJdIM$S2noO2sF=PfY}G!0@r@#&=NkBXqsV~3-UO& z_K9hpX+DFhX<;K@cq3l~$a#@q=Sj^m>07uhrrO*HG?-yzW@%I#U2S0ws*wy0;3HtB z<)$^b+KHyMrgaRerVWjJ(T#jDAd_Q3Chzz2PJaBP3*ty@y;0LP({7M!v9&-N4UK$R z3r#mQ@@0eaoFr^o12%2qlMmgl1zy!|x{Z--q3QNUzMO`DD@+EayO^{XxJ`F6SefoM z-4`z)<`NbXSeluf?USFM9gvuw>Q|bZlv>otm)9WH$d}v5mrt3~LF1lTKhnL6FEOMu zWSAZ@Jsd9}gKQ_ds~4LdF+IwVO+Zz=fUrwg2)bTaI2|`V2@0nZjeLa*O;0uQ6{E(= zd1$N@QGYfNGWTVA$rLnQgtZT4dc*W4gR1H6M!u3pzEV(vD+49C{XU#*P3J_StBpaE z9EQdwprJI>u^Q8drjHqf7Meb4VfxDSwdotvx2Eq*-1UUF~lt~ zH#4U)UO*J05+dlEpOTv6oS%}4VWV??UUE@tNosU%Vsd_Qg`t6-p;5emQc77#3fN7$ zIr+(nIl9UDxdo*qsYO;`Zbp7_Nmzx2S-hD^K$LG{a&Bf`rkRcrGz87K%p@4p)|zpf z@tE2H@}f@Nh9B~M!pq| ze5)GyRyXpkY2;hm$hWSMZ$l&BrbfQajeJ{BlChZ?J=8O2~TmV$QCwQN99 zU2ax|p}GdFdO1jSYXXYu2D4@i)vaLFD?zH;AP4%uT+(UQgIhIB(L}Q;7>eKyfGL`3 zHU~oy++LWXg=R|#D_Uu`21C(0un*RQe9)(W;@?eXTQO8`2dmx)Qr!mGkqC3ZZnJ$D zsxe%7*z6c#MW@ZqVJN}~l*?w<8Ppb=T`{|AcCC?bTO;50M!p@3&2E_8G`rQvx3iJ& zS|i_ePxg@hJv!pU86|^A>bR$$wYDs2(UT|qHXn_u-wGo_}4&FKzFCc-T z1$t>zynq;nR7hq?PAa%*YW9F3a;e!vvqxr+&7PP&ZRFe4$ak=j?`R|6$wt0&@GYfg zFU;P6oc_}6mD%e?zTJ&{dm8!nE;f5>_Rj2mBj3J8zWt4S;F&`;49iel8c>v(54sQ} zUO*eaLZ`&!?BLXb#G=HK{GxaPP5i1Mc7!G7l%^^>aDY<;Lo$PcDiec36k?4hEZTpV z{lVs$zh?i;{x|X+YUDfI$akbcRoMYz5JR9ss8eZXP6|}NIjcD*7X9X2=G^8yjeN%% z`Hna8odD^Fq&9{iD+O%LLUSQ=F>L0Dn@gBWHu9ZnmUZhGQ zsEt>aS`?yEl3yUiz}U86EjtIlfRwb1imJAuiFv3>UTIFwWlk<`9$vl(zhG#vswC$m z78l3(1v{3M6lErrmZTOpFg5TrFh>TLCMTz+rlh7^78K$V7Lx?IC_A;XILa?r4{GZo z5zz*g2G&L55)Et(oDJMze!*_}c_ra~!Ora{(6qS_2{DOlst5PHUg45h` z64Q&r{DMKPYGnsCNGu5Q3N)}caKsCU>46sXr3P0Pm!#%~dV&md%Fju;q^7Q+83l1_ zUSe))1caFiG61`Gns4P2Mh z)QyZ`j$CA$P;ipLA^`vrTa zR;H$a+uLQSMHg-DLW=V9OBxs%I2Z&Oq!^SLbQz2o%o$u6ycvQRq8XAH@)-&liWy27 z>KWP?`WR+1%wd?vuz+DP!%~Lj3@aHnFzjGB$Z&$;BEt=ahYU{`o-w>&WMpJx6l9cQ zRAf|P)MYebv|;pQ^kMX43}6gpjAcw>}Q!60Gu)xfKJqxwW|sD9x8- z=B6q;9*}?jx^Ks?M!qYMkg_wkhXhLMB6B;2?6{Epl*CGf#1aKVQ!672fmbMn0=PIf zcVUQJ$iUmc1Ul7%i-FtRgCTO2$~$u}b8mAWb6;~mbAR&y^FZ?;(3WbxTaA2o8u{)u z@;zwed(_DHq>=AgBj1ZgzE=$@NR34EaA;HKHqBdA<}pyK?ozLvh^2vSo(Q$(KGm&3 zUOsJ}2DRqlAXw3h=+KyFL!I@QdcKD@YmuxefLimECdmdRpi7}vJ*T>lP^_tfTJv(S zY!)`Jhq~xBbvI36sb$QY&09i27lx%afQoof8%dIZfkBCZfkB&rfx(o4fx(J_0aWbE zNi#6WDKap~=`k?KnK3ZPIWjQFc`z`@`7$ub`72kUPr2Aa{j_c1Uio@8K9yu`quc%Okm@d*Qi;#&p=#a|2z zivJlHlo%Npl$aS9lsFg|l(-lelz12zl=v7Jl*AbrloS{kl=K-Gl*||ylJLlqN7RC{1EuP@2NPpfrtv zL1`regVJgS2BozO3`*-67?d_LFeq(iU{Kn|z@W5~fk9~x1B23j1_q^r3=B$77#NgU z7#Nh*85oqU85oqK85oos7#Ni2F)%3aVPH_c&A_1if`LK#D+7b_cLoOKp9~DjzZn>m z|1vNr|7T!OVPs%X;bUM>5ny0Y5n^Ca5n*6Z5o2Ibkzim@kz!y_kzrs^F=k*;Nn&77 zX=GqfS<1kmvW0;`P`S^*pz@J{LFFF51_spw z3=FD=85mTLGBBtfXJAmh!oZ+!~dkhS!4;UC!-!d?${$gNI<78k^ z<7Qw`<7Hq_<7Z${6J%ge6J}sg6J=mf6K7yhQ)6II(_mmw(_&yy(_vsx(_>&zGhkp) zGh$#+Ghtv*Gh<*-i)CO?s|Vcxz`&q(l7T_(83Thl7XyR3ECYkO8Uus6Is=2cCIf@I z5d(v|IRk^b0|SG)BLjoFGXsNq00V=1JOhJzA_IeZG6REpDg%RhIs=1xCIf?dHUoot zE(3#l4FiLE9Rq`U0|SG469a>K3j>3C8v}!S2Lpq87XyQO4+Dex4h9DGiwq1J3=9k! z;tUKL<_ruP0SpWpjSLJLeGCj5(-{~vmM}1AEMs8MSi!)cv5J8~V>1JT#$E;njr|M^ z8h02NG@ddrXnbd2(D=u|pvlg_pee$@pefG4pef0~pee(^pee_|psB#XpsCKlplQLt zplQ#*py|QDpy|)Rpc%oypc&1;pc%`+pc&7=pjpDepjpenpxMH}pxMd5pgDDEgc31ElUOlEh`2FEo%k_En5Z#tyl&IttXn$s4(Eh`~pv%s{pv%d?pv%p`pv%j^pv%v|pexA0pexM4pexG2 zpexS6pex0|pexJ3psT>ZpsUQlpsU8fpsUHipsT~cpsUZopligypliy&pliXvpli** zplip#pzFxMpzFfGpzF@SpzFoJpzF)Ppc}xzpc~AIgYFLo2HoEb z47&dq81xt!81z^e81&c~81%Rp81#4<81w`f81#f081%##81y6=81!Tq81&>B81$4F z81z&b81yt481%Fm81(cQ81xJo81zgS81&2;81$?d81!rz81x(%81$SO81&p281y_D z81#G?81(!Z81#Y|81zCJ81y0-81$kU81&*881xbu81zyY81&K^81%9j81!-(81xDl z81#x681%{*81yO`81!lw81(8H81$ML81z~h81y81!Z_Fz792V9;C2 zz@WFBfkAI21B2db1_r$?3=DeP7#Q?+Ffi!tVqnnQ!@!`okAXq&5(9(Y9R>!yhYSpQ zZy6Z$zA-T9{bpd$`^&(f_n(15pOJw=`1_L1m1_NmZ1_M(D1_K)g z27@pL27??127{>#3n3=D?S3=D>{3=D?y3=D>f3=D=U3=D>G3=D?p3=D=D3=D=@3=D>q z3=D>A7#NI%85oQd7#NH+85oST85oRo85oT885oQV85oSL7#NIf7#NJ~7#NHk7#NJ4 z7#NIP7#NIv85oR$7#NH~7#NJg7#NHq7#NHi85oS385oRO85oS(85oQ@85oSZ85oTE z7#NIBFfbT>WMDA*%)nsum4U(NI|GB!PX-2KCI$v$Rt5%Rb_NDxP6h^JF$M->c?Je! zMFs|AWd;UgRR#uQbp{4w0|o|TBL)Uz69xujGX@4@3kC*bD+UH*X9fmiHwFe{4+aKf zF9rtVVg?4|QU(U&as~$DN(Khw8U_aAItB*gMg|5GW(Ecm9tH*zUIqpeeg*~;bp{3# zGX@5eVg?421_lO`P6h^(ZUzREUIqq}eg+1Ui3|)Tvltjm<}fgr%wu3MS-`+xvWS7f zWC;U<$yx>mlT8c^CR-R7OtvvFnCxI+FnP$pVDgxO!Q?3egUNFS29uWz3?{D`7);(V zFqmpEFqj51Fqj52FqnoiFqnojFqlR%FqkGWFqoz?Fqoz@FqmdCFqoDxFqqaeFqk$n zFqk$oFqpP7FqpP8FqlqYU@)D;z+gIsfx&bd1B2-d1_skv3=F1=85m5LF))~}U|=v^ z#lT>CnSsIdDg%S*bp{60n+yzQ(F_b`DGUr|sSFHe=?n~J^$ZMVeGCj{rx_T`ZZI&I zJ!D`od(6OK_LPCa>^TF2*-Hinvrh~RW?vW>%)T)&nEha2F#E;8VD^WB!CZiW!CaDo z!Ca1k!CZlX!CZ-f!CZxb!Q7XD!Q7vL!90+G!919O!90wC!90S2!91FQ!Tcx#gZViI z2J`O>4CX%>7|ee&Fqr>kU@-sBz+l11z+jQhz+h3rz+h3!z+ln9z+lnDz+lnBz+f?v zfx%)H1B1mJ1_q0H3=9?v7#J)TF)&!HW?-;b%fMiFj#XjFjxySFj$K+Fj$K-Fjz}6 zFjz}7Fj%WFFj%WGFj#9aFj#9bFj(s_Fj(s`Fj$*2Fj!kLFj(6#Fj(6$Fj%KEFj!|Y zFj!|ZFj(g@FjyBbFjyBcFj$u|Fj(JbV6c9{z+nB6fx-F{1B3M!1_m1@1_m1~1_m2T z1_qma1_qlN1_ql}1_qmU1_ql>1_qmM1_qm61_qmH3=B3i7#M73F)-N7VPLSC$G~8- zfPukgB?E)a1_lP3O$-b+TNoH@wlOf+>|kK9dC0(E^O%9b<|zY%&2t6@o0kj>Hm?~N zY~C?2*hVlg*d{VC*mf~6*!D0m*!D3n*iK+zu${!fU^|6@!S){mgB?2qgPj2bgPjor zgPjQjgPj=zgPjEfgPj!vgPjcngPlDCgWXC72D?oR40gL280_{jFxVYnV6Z#Hz+iWt zfx+$u1B2Zy1_rx33=DSn7#QpxFfiD?WMHs+&A?#ymVv?UJp+T?M+OGF&kPK9e;63- z{xdMxGcqvPGcz#Q>o73b>oG9c8!#}~8!<50n=mlgn=vrhTQV@%?`2@HKgPgdf1ZKC z{sIGo{Urtl`v(jRpwW5a1_p;^3=9q{85kT^GcY);Wngf)#=zikgMq=}76XIB z9R>!6dkhQ?AbTD&FgQvwFgRK=FgW@%FgQjqFgQjtFgV6CFgV6DFgWHgFgTVnFgTVo zFgR8+FgR8-FgVsSFgUg`FgSKFFgSKGFgW%wFgW%xFgQ+NU~rttz~DHCfx&Se1B2rN z1_sBI3=EE^85kVTGB7xvXJBx=$iU!unSsH{mVv=(7XyRSB?bnk+YAg&cNiF)?lCYp zy=Gu=`oh5A^o@bR=?4RY(=P@Fr#}n~&a4a!&g={f&YTPk&O8hZ&io7v&VmdK&Qc5v z&Wa2S&dLl7&Z-Oy&gu*d&YBDi&OrU~rXV zU~pArU~pAtU~pAsU~n~JU~si&U~si%U~si(U~qM0U~qM2U~u(eU~u(kU~mm$U~mm( zU~r9KU~r9MU~o-lU~o-iU~tW3U~tW5U~rwnz~DNafx&eq1B2@v1_n0;1_n1x1_n1H z1_n1{1_n1%1_n201_rle1_rlE1_rm?3=D237#Q3xGBCJZW?*o;%D~`uoq@saCIf@p zBL)Vyrwj~kFBllyUNJDZy=7oQ7XyQbG6RE$8Uure1_OhK76XIF8U_ZBtqcqv+ZY%;b}%q_Twq}E zX{J_BA`ICXc^A7`q=YIwU zFGdCiFFpnaFL4G2FG&UlFKGq_FIff#FL?$AFAW9;FKq?}FFghZFGB_fFB1j^FEa)P zFM9?CFDC{DFINTzFLwq8uL1@JuVMxUuTlmEuW|+kuSy06uWAMcucr(QUhfzfyxub~ zcztAG@aAM-@NQ*b@Se@U;JurH!TSUQgZD)S2Jg!Z4Bl567`(4DFnHf&VDNs#z~KFq zfx-I)1B3T#1_tkU3=G~M7#O^NGB9}mV_@)MWMJ@NVPNoKV_@*nXJGI#VqowwVPNnv zV_@*HU|{gEVqoyGWnl0rXJGJIz`)?Mn1R7(DFcJgas~#URSXP1n;95||i@ z+0DS;PZlk!RHkNgU?$A2A}r~ z3_d>?7<_&+F!=mqVDM#NVDQysVDQypVDQyrVDL3yVDL3!VDL3%VDQanVDPPAVDPPE zVDPPGVDMeWz~H-;fx(ZJfx%CRfx%Cjfx%Cffx%Cnfx%Cafx%Cifx%CQfx%Cofx*v+ zfx*v|fx*v$fx*v;fx*w2fx*v%fx*w4fx*v@fx$0;fx)kmfx)kafx)kifx)klfx)kZ zfx+(`1B2gp1_plx1_pm+1_pms1_pn11_pmk1_pl}1_u9W3=IDB85sOmFfjP9W?=AN z$H3sffq}t)F9U=BF$M^|1JZA{{sdF|Hljr z{!bYg{NFJ!_hJhi-j)5V_fq@~&iGd-=n}H$7kAWd5 zkbxm6n1LZEjDaC2f`K6@k%1v7g@GX`oq-`JlYt><5(7ifR0f8i=?n}(GZ`3y<}fe> zU1MMfy3fE6^nigO=n(@$FcSkqur&iium=M}a3BLia4-Wya3}*qa5w`)a3ljma1sMU za4G{sa0UZIa5e)&a2^9gZ~+5Da3upna2*3fa3cdla0>%Na2o?d@OlP@;7tq+!CM#@ zg10d+1n*>E2!7AN5F)|A5F*XM5F*RK5F*dO5TeMy5TeDv5TeV#5Msc<5Mso@5Ms~3 z5aPkW5aPwa5aPqY5aP$c5E8(^5E9A25E8?{5E9S85R%Bi5R$^c5R%5g5R%Wp5K_dz z5K_v(5K_*-5VC-QA!IQFL&#DFhLGh93?VBS7(!MvFodjQUcH5eA0P;|vU;rx+MQ z&oVHCUSMDdy~Mx}dYgeE^brF?=u-xU&=(90p|2SjLf=+m#92giPoER7) zTo@Q4d>I%b0vH$~f*BYh!WbALA{iJWq8S(>QWzK_vKbg6@)#H*3K14E1!14E2414E1n14E1% z14E1-14B$M14GOb28Ni83=A>57#L#qFfhdIV_=9mz`zi5h=C#IGy_A-IR=K9iwq1g zR~Q&#t}`&i++<*gdBngF^OS)h<^=;o%qs?lSYZZ+STP2MSP2G(SSbdE*cJwc*j@&P z*hvfwu~QisVrMWg#Li-1h+WRW5W9haA$Ai3L+lm?hS+Tk46!>H7-A1HFvK2VV2C}= zzz};U)t^!fghIgnJAO2@e<;5?(SeB)nl@NO;e{knoX#AyJBfAyJlrAyJ-zAyJWm zAyJuuAyJKiAu)!5Au*MKAu)r2Au*eQAu*SMA+d^qA+ec(A+eQ#A+eo-A+eKzA+ei* zA#n-=L*jG>hQwJ642g3Y7!nsSFeENwU`Slez>v6(fgy1t14H6w28P5t3=E0)85j~D zGB6}QW?)Es#=wvy!@!WF%D|AM#=wxI!N8Cd&A^b9!N8DI$iR?P%)pRT%D|9R&cKjV z$-t1*#K4f$%D|A+!N8Ez#lVo%%fOJ-&%ls0gMlGwHUmS_JO+lO1q=*HCm9%$&M+_} zonv4~y1>AY%*Mcwtj55Qtk1xZY{I~hY|g-tY{kHkY{S5i?8(589K^to9Kyhm9LB(q z9Kpbl9L2zpoXo(GoW{VAoXNnDoXxL&{zThLrCN45=;*45_{h45@w$45K`45@hx45{S|45?KN45_sY45vCtfg$xI14HT=28Psg3=F9k7#LD7F)*ZFWnf6-W?)D&VPHrzXJAOPWMD|MW?)E5 zVPHs0XJAOnWMD|kW?)FmWnf4vV_-d_U`T(@z>xltfg$}f14H^(28Q&13=A2J3=A183=A3U3=A1u3=A1O3=A2f3=A1E z3=A3a3=A1c3=A2n3=A3S3=A0o3=A2;3=A2e3=A3J3=A1j3=A0)7#K2kGB9N9VPMGE z$H0(rfPo?7B?Cjo8wQ4qcMJ>}9~c-ieljp*{9$0o_|L$Q$;iNv$;ZHuDbB!Bzv4>B7K}>Bhj2 zna{wGS;WAQS;D}OS;oMSS;4@NS;fGRS6WK}RQWK}URWHmD|WVJCcWOXtyWc4sGWc4#JWKCpX z$eP8#khPG3A!`W(L)LNzhOAW#3|VU!7_zQ1Fl60eV92_~z>sx^fg$T214GtB28L{L z28L`028L{B28L``28L{R28Qec28Qfn28QfX28Qf%28Qeg28Qfr28Qf528Qeo28QgZ z3=G-x7#Ok_Ffe2~9PV**_T=vVSu$WdCJg$YEe$$gyHz$Z=+1$Z=s{$Z=y}$SG!E z$Z29=$hptJkn@ItA?GUtL(X>whMb=a3^~6U7;^qHFyyi^FywMFFy!(uFy!(xFysm` zFyx9bFyzWIFytyRFyyK-Fyv}5Fyv}6Fysa^Fyw|YFyuxsFyuxtFyzKEFyu~VV94FW zz>vG2fg$%G14Hg%28P@-3=FyF85nXeF)-v_VPMF8$iR^Mih&{b4Ff~&I|hc_4-5>s zpBNZ&e={)T{$pUsV`N~+V`gB;V`E^*<6vOO6J%h>6JcP;6K7z^lVo7Xb7Ek~b7f%2 zb7x@4^JHMiYi3}`>tSHX>t$fb>t|rdJH)_{caec1--3Z5-<5$O-;aSIKY)QDKZt=L zKZJoHKa7DPKc0ahKZ$`MKb3(YKb?UgKZ}7OKZk)Kzm$O?zlMP!zn+01zlnh%zmV-{H+WO`P&&7@^>*XrD9B@AC@5!OD5zv$D5z#&D5zy%D5z&(DCl5d zDClNjDClEgD44*&P%xQ+pe#NFcdvyU?_Udz)Wnd_FU|=YAVqhqCVPGh(V_+z5 zXJ9DqU|=ZjVqhrV&cIM2!oX0X$iPsd#lTRa!@y9Y$G}ixz`#&q#K2Hu&A?D%$G}kH z$iPtI%)n6M#=ubG!N5=w$iPq%!N5=w&A?C+$G}jM$iPsN%)n66!@y9|&%jVJk%6IP zG6O@&GzNx}GYkwRUl|xmelReU{9<4z`NP0as>;Ans=>fes>Q%is>8rgYRbS+YQexz zYR$kczlN8qB~@8p^;>8qUB_8p*&=8qL5^n!>`Ud(oGBurEeG*O20BNlzwAiDE+~} zP^QYjP}ao2P}a}DP&R{sp==fdL)jb#hO&7K3}p)#7|K>MFqExfU?^M9z)-f4fuU>* z14G$128Ocz3=Cz*7#PY;&tPCEpT)pXzLI2n`APc28Qwv3=9>L3=9=A3=9==3=9?3=9=13=9=%3=9>M85k-+ z>i#k?RI)KJRB|vdRB|yeRPrz|RPr$}REje&R7x>0RLU|iRLV0jR4OqrRH`sARO&J? zRGKg_RGKp|R9Z1GRN68yRN6BzRAw+RRAw_UROT`;ROT}_IhN?&ghN>6_hN?IQ zhN^Z3hN>P0hN?aWhN=?`3{@8y7^*HYFjQS(V5nwfV5p8`V5rV!V5lx(V5lx* zV5qKOV5qKQV5qKPV5n|oV5sh3V5sh9V5sh8V5pwJz)(GjfuVXf14H!!28Qaz3=GxF z7#ONoFfdeKWMHTUnR$(Yq51{`L-lP2h8jTzh8imdh8kN2h8lYYh8jl(h8iyhh8kZ6 zhME8dhMFJ-hMIT=hMEiphMFt}hMF7(hMGJEhMEEfhMGzShMF1%hMIZ?hMGnOhME=z zhMG18hMIl`hMGwX3^h|37;2_7Fw`7iV5m9Fz)*9PfuZI&14At%14Atr14Au014At@ z14FF?14Hd{28P-#3=Fk<85nB!GceR1WMHU0%)n54l!2l490Nn`MFxi2D+~;^*BBUT zZ!$2{-ezE^eZs&{`;vj7_6-9=?Ry4>+K&tjby5rrb+QZ$b@B`hbxI5jbt()Db?OWZ zb+HT#b!`j`b)5_hb=?dMb-fG>b^Qzsbu$Si-A)XigHs9V6mP`8$Wp>7)kL){Jr zhPqu040U@L80z*hFw`AqV5mFAz)*LVfuZg^14G><28OyT3=DO585rswFfi0TW?-m$ z%D_<1$G}i8$iPr9%)n4D%D_hd}Uy0 zWMW`wv})M8+0G-hCE zG-F_Bv}9mtv}RyvOkrSXOlM$d%w%9_%w}L{%wu3^EM#D4e9XYm_=bU@5oE_V28PBT z3=BB3=B=K3=B;{3=B=t3=B=N3=B>23=B<)3=B=l3=B;<3=B>A3=B<0 z3=B=B3=B;b3=BhKV#mPHV$Zf_z;?Ka)lFY!+lFz`&|1L2&|1vE z&|1pC&|1#G(0Y%7q4g~TL+dvNhSr}946T0{7+U`^Ftl+qFtmv z3=Hk13=Hk%3=Hj+3=Hix3=Hjc3=Hj!3=HkB85lZ57#KRl85lYw85laG85lZb85lYg z7#KPt85lY;7#KPV85lZB7#KRr85lY$85la+7#KSG85lYyF)(yYVPNQ(#=y`qgMp!A zAp=9l5(b8j67Ca&>6|V(3!@-(3#J`&{@R5&{@L3&{@X7&{@I2 z(Amhq(AmPk(Amzw(AmYn(Amqt(Am$x&^d#Fp>r++L+1hphR($d44q3E7&^}}FmzsI zVCcNez|eV>fuZvT149=d14EZN14EY;14EY$14EY`14GwR28OOJ3=Ca+85p__Ffeo- zW?<+#%D~WdiGiW(HUmS~JqCuZ2Mi2dj~E!bo-il*_@*G~q9 zuHOs{-7E|Y-Ruku-CPU|-8>8o-KGo--4+ZC-Bt_?-8Kvi-DM06-76Uwx;HT}bZ=&0 z=-$e}(0z!3q5CugL-#obhVBat4BeL)7`m@8Fm&H#VCa6pz|j4efuZ{u14H*q28Qm} z3=G|07#O;LGB9-iVPNR~&%n^b$iUE}#lX;`%fQg1&%n@Q$iUEJ!objD&cM)<%fQf6 z#=y`spMjxg5d%Zd5(b8zL7hTf+P41Mwp41F3541LiI41HM)41L8641FaG z41Hw`41LWE41GNe41N6!41E(B82TnNF!W7jVCb92z|gmlfuU~+14G|(28O;>3=Dm1 z7#RAtGBEV*Vqoao%fQfgfPta!5CcQsYX*kCcMJ@D9~c<=J~1%#YcMeM2Qo19hcGbo zhcPhpM=&t-?`L4>Kf%Dzf0}`z{~QBD{{;qy{<{ne{m&Q}`d>0I^uK0c=zq(=(Epx+ zq5lU1L;r6EhW>vH3={3Fid#Oz%bz*1H*)m3=9*#FfdH`#=tP)KLf)=CI*IytPBhj*%=rn8Za@zcVmQ{Kdd9@h=0zBnAeCNz4oklUNxTCh;*aOcG>Z zm?Xl$FiDJoVUj%q!z3pLhDk0A43pd#7$$i#Fii4cV3^d#z%Z$wfnm}*28Ky{7#JoU zWnh?ejDca&2?mBqR~Z;4-D6;w^pJsJ(qjgONlzIVCOv0hnDl{xVbW&?hDqNT7$*H> zV3_oWfnm}=28PM(3=ETb7#Jq=GcZgRVqlmo!oV=umVsfi0|UckCkBSet_%#5Js21! zdowUhZe?JY+{eH$c{>Bc+@!{j3j43qycFic@*V3^{-z%a$1 zfniDn1H+VP28Jnd3=C5e7#ODHGB8XjV_=w4$-pqBnt@?TEd#@pdIp9m9SjUpx)~Uz z^f53@naIE}WeNkslxYkMQ|2=;Oj*RhFl8wN!<6L=3{x&JFig43z%b=11H+UX3=C6l zGcZgQWMG&o#lSGtl7V5W2Lr>@Kn8}XK@1F2Ll_vQCNeNg&0=7fn#;g2HJ^cDY9Rx| z)M5sPsWl7?Q|lQRrZzD!Ol@UgnA*X>Ftv+;Vd`WChN;sS7^co-V3<0afnn+q28OA} z85pLXWMG(jhJj(~c?O25mlznP@h~t<6J=nS=EA@*Etr8}S_}ijw0H)FX-NzW(^421 zrWGt!?b1whG{(v4Ac4<7^Y2PV3;G z4AY|+7^WvPFicNjV3?l9z%ad-fnj8ltRrmtmSn7)C5VfrQphUvE%7^dH2V3_`ZfnoY% z28QX+7#L>AFfhzeWnh@$$G|Wnkbz-FFayJkFb0MhJq!#prZOlYVWuSm!%S-ihM6u53^Uys7-o7gFwFF2 zV3--ez%Vn2fnjDe1H;TX28Nl53=A`q85m~vFfh#QXJD8)k%3|6WCn(r(-;_L&SYSi zd6|J><{buxS^NwPv*Z{UW~nnU%+g?Bn5D(QFw2~QVU_~}!z^b8hFPu*471!B7-o4g zFw6>KV3-xkz%VO%T46}_H7-pL?FwAyh zV3_UAz%bj7fnjz41H{Sd5v)3>%%wEsHFnbdN!|XQ<470y7Fw7BVV3?!G zz%WOPfnkm|1H&9$28KCS3=DIe85ricF)+;WU|^Wz#lSGfhk;>EC~240F~n zFw9xcz%b_}1H+t83=DIn85riuF)++kU|^W5%)l@=mVsez8Uw@JMGOpcH!?8H-NnE# zcP|6O+ye{@a}O~v%stP*F!u%n!`#~p40G=?FwDKrz%chA1H;@`3=DJMGBC{jz`!u~ zGXuliZww4`e=so2V`N~M$HKrckDY;G9w!6CJR=5%d8P~u^UN6-=2E?6S7Tt9ugSnLUx$HVz8(X^d~*hd`8Es;^X(ZJ<~uPk%y(g6n4inQ zFu#C-VSW(FKi;podEI!G=u=q3s!{RRt42!=rFf9Jbz_9o? z1H5umi96*EbV7tSbBzmVd-rKhNVv!7?wU~ zU|9N!fnn(z28N|S85ou^F)%D+Wnfsw&cLvYlYwCwHv_{m5e9~3;tUMSq!<{M$ucl3 zQ($0Nro_OoOq+pWnH~efGD8N2WyTB)%VHQ9mc=tLEK6iySeDGdu&11yqhLs)+3@Zy67*>`rFsv+NU|3nnz_9WM1H&pt28LA<3=FH( z85mX>Ffgn#WMEij%)qe9fq`L_Cj-MO9|ne1ehdt&0vH%p1u-zJie_L~701A^Dv^O< zRSE;cs&od1RhbM7tBM#HR+Td_tg2#QSXIlwu&SPcVbux-hE=N>7*?%iU|6-Dfnn7~ z28LBz7#LQ)VqjP;%)qc(jDcab1OvlrDF%ksjSLK{yBHW&_cAc7p1{DcdJ+S}>iG-| zt5+~EtX|E)uzD>6!|L@6468RXFs$Ciz_5BR1H*1lt4So@uUVeKylhP8hf7}ow{ zU|7e%z_5;!fngmF1H(Fg28MM)3=Hc;85q`yGcc@EU|?9M%)qctje%jE1_Q&oKn8|& zAq))b!WbCVMKCa|o5aAd?kEGpx^oN+>&`PUth>m-updA5*84CptoLVNSih5jVf`TnhV}0m7}oz|VA#OUz_5XffnftL1H%S> z28InX3=A7o85lNbFfeS;Vqn;y!@#gXkAY!>IRnE6D+Y!QwhRm#92gijI5RM8aAjcF z;K#tQA&`M#LkI)IhA;+(4fPBR8=4pxHncD>Y-nd-*wDqmu;Bm$!-kU#3>#iBFl_k7 zz_8&z1H(oJ28NAH3=A9j85lN7FfeSCW?&91Fl?N`z_9Tg1H;De3=A88F)(cW!@#id9|OZCb_Rw`Tnr4Gco`Tr@iQ=Nl3`%j zq{_gsNrQo5lNJNRCLIQbO?nIro6H#)Hd!$+Y_erw*yO;#u*sQ$VUsHZ!zMolhE0JC z44Xn27&e75Fl?%4VA#~ez_6)>fnie{1H-0H28K*gTzqVe3rz_8^L1H+cD3=CU-FfeTS#lWzY zoq=Ji00YBTVFre+q6`dM#Tgj3N-{8PRbpV+s>;ByRfB1H;xA3=CUeF)(cX z&cLvZfq`KgGXujmRtAP`>VOu-{!?r{QhHWVf4BOHf7`9zxVAytxfnhrb1H*O= z28Qj13=G>%7#OyjGcatoWMJ6t#=x-MpMha}5Cg;Z5C(?rVGIo0BN!OACo(W>Phnu# zp3cCqJ&S>1doBaR_Iw70?G+3R+p8HEw%0K*Y;Ry-*uIj1Vfz{ehVAPZ7`AU@VA#He zfnkRl1H%q|28JEp3=BK685nkyFfi;WWnkD*&cLvvg@Iv5F9XAl2@DK7CNVJVn8LuY zV;Td)j`<7>I~Fl8>{!acuww-S!;aMq3_I2`Fzh(Uz_8;K1H+E<3=BIiF)-}7%D}MW zIs?N_1_p+m%nS@WSs561vNJI3A=9SGn0W~XAT3y&O8Q&odpaGJ1ZF&cGfU3 z?5t;C*xAUyu(OYWVdr!PhMlt*7o z1H&#m28La}3=F#(7#MavXJFX%g@Iw$Uj~NV3=9mrnHU&$voJ91W@BL3&CkHFTZn;S zwyUyJZ;|cB?Tk?AB&r*saIFu-lM{-jeuxC93!=8-{410DlFzngOz_8~41H+!f3=DgY zF)-{o!N9QRA_K#oD+~;Kt}`&~xyit=mx+O4FDnDXUUmkCy_^gTdwCcb_VP0@>=j~Q z*xSp%uy+~*!`}G}40{(aFzj8#z_52C1H<0q3=Dg(F)-{CU|`rM%fPTtje%jGCIiDh zZ3c#Yx(p2a^cfiTSuil{vu0q}XUD*>&yj&)p9=%SJ~sx2ef|s#`$8BP_JuPr?2BSx z*cZdVu&L$jh%zu75NBXGpvJ&(Ad-RMKn4TDf%6Ou2ktO19C*sWaNq?4 z!+}=}37#I$nW?(pM#K3Uao`K=82Lr=lZw7|Lz6=b9 z{TUbz2Qn}mj$&Xq9LvCPIDvuTa54kK;WP$@!x;<=hie!Z4!1He9PVOZINZy?aCia( z!{JE`42O3!FdW{;z;O5g1H<7%3=D^lGB6y`XJ9xI!N71Nnt|a+ECa)lcm{?eYZ(}h z>|tOy0+K($z;NU=1H+NC3=BtZF)$o?%)oHu83V(S7YqzXUNJBndBebP^3=BuT85oW>GB6z7%)oH;5Cg-}(+mtp&oD3?J;%Us^fm*-(I*THN1rn=9DT{a zaP&0;!_l`43`f5)FdY5Kz;N^r1H;k(3=GGZ7#NPRFfbgGU|=|=$iQ$+je+5qCIiDU z9R`MDdJGK5!WkHjMKLfOi(z0m7RSJF>;wbDv5yQ4$G$Ky9Q($=aO?*I!*MqThU1|O z49DXb7>*}0FdR=|U^t$}z;L{nf#G-!1H?g&U^sr4f#HN4 z1H*{`28I)n3=Ait7#L2(Ffg3RWMDW^#K3T(l!4(yIRnFqN(P1#)eH=s zaPlz&!^t-c3@6_+Fr56zz;N<21H;L$3=Aj#F)*BBWMDYO!oYBfoq^#L7X!m79tMU} zq6`eDBp4V@Ni#5O$LV3&lng^zh_`L{fU9$^cMz()87~vPXAzF zIK#-maE66};S4(i!x=6HhBLej3}^Tm7|uvAFr1NQU^pYkz;H%^f#HlN1H%~~28J_! z3=C%i7#Pk3F)*A7VPH5jlY!yP2L^_-91IL+1sNF53NbL86=7gFtH{7`R*Qk*tS$q? zS$zhEvxW=|XN?&c&e||AoV90QIP1i~aMqQ9;j9M(!&xr|hO@y83}?d_7|upAFr1BM zU^v^tz;L#mf#GZ?1H;*F28Od27#PlJFfg1mWMDXF#K3UQgn{9lBLl-ZF9wElz6=cK z{23U|1u`(43ua(A7sJ4CE}ntmToMDrxl{&*a~TW_=du_W&ebt6oNH%bIM>C%aITku z;aooh!?_&{4Ci(;Fr3@Vz;JFq1H*X+28Q#T3=HQ57#PkAGccSNV_-Nh$-r=4nt|cG z1_Q%+Lk5QPCJYSc%@`QYTQD%3w_;#8@65n(-i?9bye9+0c^?Lb^ZpDB=K~oS&POpY zoR4K-IG@14a6XBF;e0m(!}&f2hVv5`7|u^(U^qX8f#Li=28Ijl3=9`I7#J>aF)&|kKHu#17=!X5^O3;P%tE*xiIxNwSr;lf!4h6@)M7%p69 zV7PFVf#JeE28Ig{85k}+VPLrMjDg`IKLf)>AqIwvA`A={#TXbaN-!{7jAUTAn8d(v zF`0qkVk!f}#hnZc7mqP8Ts+UfaPblY!^JBM3>U94FkHOBz;N*)1H;893=9{aGca6y z#lUd!Ed#^F_Y4dde=smy{LR2{@gD=jB?bnDOPUM}mvk5yF6l8aTryx_xKzr(aH)=g z;Zi*V!=*+BhD$dY7%shMV7ScBz;IcHf#I?$1H)wv28PR83=Efb7#J?=F)&;czltHJE|nY8V5<)kp@0 zt1%1=SK}ENt|l@tT+L!&xSGqraJ7Je;c5{B!`0ag3|HqdFkD^0z;Jaj1H;uP3=CJ_ zGB8||XJELd$G~vSoPpt*1p~u1D+Y#Z?hFjq0vH&s1v4;Q3uR!q7S6zMEs}xZS`q`p zwNwU%YZ(j-*RmNHuH`W>Tq|H;xK_!)aIKDk;aVdD!?hL$hHGsM4A<5(FkIWjz;JB~ z1H-j#3=G%R7#ObmGca5aVqmx)!oYAnjDg|$5(b9rn;96c?_prLzMp~N`XL5}>qi(E zu3uzexPFU);rd+$hU@nk7_L8LV7UI6f#Lca28QeJ85pjAVqm!bm4V^<4+e(ozZe*9 zFf%aRU}Ipo!O6gIgPVcjh6w}14RZ#D8?>LZqzU^+*rrJaAPk6!;NDM3^$H5 zFx)uFz;NRl1H+B`3=B6OF)-YC!oYCj83V(O7YqzHJ~A-e_`<+&<2wVx4UqYN85nN- zXJEJ~%)oF{hJoRxJOjf`B?g9@Dhv!aeHj>T1~4$(3}Rro8N$GDb1DPF&3OzAHS!_9RJ3^zA0Fx=eBz;N>z1H;Xe3=B65+LR%T$ht;4`@ zTc3g9wh;rvZBqt@+ZGHAx2+f$ZaXtD+;(GNxb4ZnaNC=K;dTiF!|ieghTD}447aNp z7;f)jV7Pshf#LQs28P=w7#QxzFfiQFWMH^s#K3UJl!4)nIRnETO9qBJ)(i}HTo@Sc zxHB-^@nT@O;Hv_}nzYGlb>=+pC#W674D`8-`SIfX~uZ4l(UONNBy)FiZd%X+{_xc$a z?#*FfxVMyn;ob@chI^|R81AiMV7RxAf#KeE28MgP7#QyDWnj2>fPvxOVFreKM;RFI zonv6QcaeeN-W3Lhd)F8k?)_(AxX;AEaG!;N;XWG!!+m!KhWoP^818RkV7R}Vf#Loh z28R3l7#QxKW?;B~g@NJzbq0p}HyIf2-)3OAf0u#b{xb%K`!5+7?!RGRxc{Dk;r=HE zhWlR_81DaNV0gg5!0>>Xf#Crw1H%J728IWQ3=9v985kazGB7+SV_oV0g&R!0?cZ zf#D$!1H(g628M?c3=9vY85kbQGB7-JV_F9XBFNem1RUokK|{LH}c z@CyUO!*2`>kC+)49`P_RJmP0ycqGWc@JN_};gKi28PFO3=EG$85kbNF)%z%WMFul!ocu2je+5DF$2Tn8U}{P^$ZM; z8yOfLH#0CiZe?J2+{eK1cp?MC<0%XbkEb&*Jf6kC@OTab!{en443AeZFg#w(!0>o2 z1H{;c;dsr@Wh{i;YlC^!;>fmh9~h13{R367@njuFg!_T zV0bctf#Jzy28Jh785o{SXJB|Ti-FTrDljlSRbpUx>dnCL)Q^GTX#fMm(;x


zVOPx}}cp7t{^Je|nE z@N^yn!_(yq3{O`vFg#tu!0>b(1H;n|3=B_qGB7;d!@%%#KLf+lLktX0k1{YkJgBJiWod@Qjgx;Ta19!!tGphG!fM49~b37@qksFgz<@V0c!~!0@bt zf#F#<1H-dE28L%77#N<-Wng%=jDg|VN(P2!s~H%ctz}?%ww{6E*$xJVXS*2~p6z2` zcy^G1;n@)ehG)kZ7@nPHV0dE%E0iPoq^#wCj-NC zF9wF^a~T+(Z)IS3zK?<7`F;k5=LZ=Wo}Xi2cn;EYi-FZ{*r;= z`5Oj?=kFO9o_}Isc>a}v;rVw4h8Nrn3@=0&7+y#+FuagqV0aSKV0gWVf#LOP28P#H7#Ln(XJB}JlY!y&9R`Ls`V0(j zEEpKxI5IH2abaM1H%SZ(Z&DZ--lQ=wyvbl-cvHy0@TP=; z;Y~RM!<#AwhBvhg3~%Zg7~XU+FudtzV0hEV!0=`Q1H+r03=D7fFfhE?$H4IBAOpi& zCI*JL+zbqFMHm>~iZU>~6=z_0tH!|aHiCiSZ8`(P+pP=?Zx1mrygkjp@b(-7!`llC z3~w(nFuc9O!0`4i1H;<~3=D4{Gcde;#=!9QB?H6T*9;7Azc4Vo{m#Jf_7?-g+dm8p z?^GEW-f1u}ywhS}c&Ed_@Gh5u;awR6!@F7rhIb7N4DXs57~ZupFudzvV0br`f#Kae z28MSF85rIzW?*=?l!4*hat4NX8yFbgZDwG2w~c|}-A)FEcY7EZ-tA*xcz2wE;oT_) zhIeNf7~Y*{V0ia~f#Kb628MTk85rLEXJB~G$iVPEgn{9GECa*)I0lCI2@DMHD;OBw z-(_HU|B8X({bvS-_um*8-v3}=c>jxm;r$;5h7YU^3?Dcc7(Q?_Fnr)+VE7=&!0Uk%8gkE(V5=yBQch?qy*3 zB+9_>Nt=P;lQ#pyrw9gyPl*f+pHdhYKBX}*e9B;8_>{%K@Tr)A;Zqp{!>39HhEFvN z44>*57(O*JFnsD_VEEL}!0>4j1H-4O3=E&9GcbHQz`*e7FayJo^7 zS(bs}vl|1$XHN!(&)y6SpM4n^K8GE_#(!@@I{`1;fn$T!xtq6hA)N;3|~?h7`_xUFnl@4!0_c9 z1H+f=3=CgxF))0&!@%(69s|Rd2Mi2fUNSIzdBec)GBAAOU|{&h#lY}Qn1SJ&7z4vMNd|^*(hLmWTo@R>xic_)^JHN7 z=FPzHt%rf(+f)XIZ_^kUzRh4@_;!kc;kyI_!*^8%hVOa|4BrhI7`_`bFnl*{19ef_#wu?@I!)u;fE9h!w+Q!h97DS3_mm(7=CCo zF#ND!VEEz4!0^L`f#HW61H%sw28JJA3=BVl85n+qF);jyWMKFa!@%$(o`K;R~G}ruU-a*UlSM@eobOv_%)w_;nxZVhF_~07=Eo~ zVEDD3f#KIi28Lg|7#M!-WnlPqfPvxHVFrd@#~2uXonT=2b&-ML*A)haU)LEJe%)kX z_|3$?@SBx^;Ws-2!*5OohTmQc48MaJ7=DK^F#HZQ-ZhmnEd4+{gsA9e&L+GH-LfRZ!81D-!uk>znKgSf3q1F{^l|;{LN=z z_*=oi@VAk}1H<3x3=Dq{FfjZ*%)s#X zC z!@sKx4F9e(F#Nm8!0_)j1H->33=IFCGcf#n#lZ0IEd#^94-5?dJ~1%-`^~`cpMinl zKQjZve>Mh&{~Qbq{|y-!{+lo`{5NA@_;1O;@ZW}k;lDit!~aPP4F6{{F#JEl!0`V# z1H=E53=IFzFfcO6GcYn}FfcM0GB7fjFfcNhGcYn(GB7f@F)%XtGcYm)F)%WOFfcNN zF)%VjFfcMCGB7fvFfcNtGcYn_F)%XZGB7gaGcYn#FfcMyGcYpLF)%VTFfcN#WME`i z!@$U}j)9S3BLgGDCk94_-wce5EDVf{tPG5d>4h)PeeGH5&vl$p! zmM}1~EN5V3S;@f2vYLUBWi10E%Qgl^mYocYEPEIjS@tt9vK(SyWI4jX$a0o}k>wHt zBg<6=MwS~4j4Zbp7+IMa7+KjE7+E85lXYF)(uOXJF(!z`)3Ph=GywA_F5AGXo=+5CbDu0s|vgE(0T1 z83Q9%B?BW@H3K78EdwK0Jp&_G2LmHlHv=PA9|I%TL zBkw^5M&2U~jC|q@jC=|VjC`65jC?u_jC}eGjC_U+ zjC^(sjC}44jC@`UjC?)}jC_6!jC=tMjC_#{jC?T+jC}D7jC@H9jC`pKjC|<~jC=(Q zjC{omjC^GbjC>UgjC>0j82OekF!C*9VB}lLz{vNCfsyYs10&xb21dTW42*pL85sHb z7#R5-7#R8e85sHJGBEP5VqoOo%)rRMje(JW2LmJjE(S*aJq(QeM;RFTPcSg@pJrg> zKgYnxf02Qa|1tw3{~ZQK{)Y^V{7)Dd`JXc|^1ozY6cA!y6cA-#6cA@%6p&(I6p&?L z6i{Gb6ewn36sTcf6j;i@D6oQoQD7AVqrh4QMnMJ!MnO&nMnNG4MnO>qMnMS%MnNeC zMnQE3MnMAxMnPi+MnO{sMnQ81MnOvkMnNYAMnP8wMnMk-MnP`|MnOLYM!^6EM!`r1 zM!^^cM!|RnM!`e|M!_xyM!{YNM!|jtM!`u8jDnXL7zOVzFbY0pU=)1Dz$o~Ffl=@~ z1EY{81EY`y1EWwI1EbJn21cPd42(ka85o5YGB64)W?&Rr%D^bJj)75VBLkz*76wM4 z?F@`UyBHXS_AoFC9c5q?I>o>!be4fp=mG&q2COQLjM>Tg&7zag_#)`h1nPw zg(Ddlg_9TU1Ea)321bd+42%*>85kv2FfdB2W?+=~$G|Ab z&cG-sz`!Ue$iOHm%)ltA#K0&S#lR?;$-pQ%i-A#cIRm5Q1_nmS%?yl^TNxN7w=*zG z?qpz;JjB2#d6a=s@&p5;hk{1{lC2uk?O5S5&lzhm*DEWkeQSuoBqZB^_ zqm&Q>qm&2(qm(!Uqm&c_qf{;fqf{9Kqttu`MyW*%j8aP&7^RjoFiQPmV3cNNV3g)z zV3g)(V3ZbOV3ZbNV3by5V3gKkV3gKnV3gKpV3amwV3amyV3f9DV3f9JV3c-ZV3c-c zV3hV?V3hV^V3ZDKV3ZDHV3dwzV3dw#V3clQV3clWV3h7;V3h7*V3a<~z$krNC z1Ecf<21e;e42;qr85m`h85m`Z7#L+57#L-G85m`zF)+%^WMGt;&A=!#mw{1cJ_Do7 z3I;}*)eMX>>lheiHZm~EY++!O*~Y*qv!8)c<`@H`%t;1DnKKNGGUpi>WiB!>%KTzr zl=;iRDD$6zQI?5;QI?f~Q8t2sQ8tl*QMQYLQMQ+XQMR9fQFbB&qwEw0M%n2MjIys7 z7-c^*Fv`g=Fv`g@Fv=-1Fv_VgFv^uPFv>MBFv@i@Fv|5XFv|5aFv?A2V3eE3z$mwz zfl+Q11Ebs;21dDc42*Ic7#QVtGBC>RVPKTo&%h{mh=Eb=CD(EsWD(EvXDp)WuDp)fxD%deFDmXGQD!4E( zD!4H)D)=)nDuggFDugpIDnv0bD#S1_Dzq{%Ds(U~Ds(Y0D)cZgD)cijDokQvRCvn3 zsPK+~QBj1Eb(%s%BtRTEf7nw48xaX(a=r(i#RvrT+|!${Y-g%7P4x%0di`$|4Mm%E}Ck%FYao z$^i_F%8?9=$}tR#%5e;g$_WgN%1I22%GnHz%6SZo%7qM!$|Vep%H<4<%9RX^%1sQ6 z%B>8H${h@h%3TbM%9|M&mA5f4D(_%mRNl?NsAA8+sN%uEs1nG)s1n4$s1m}!sFK9M zsM5s1sM62Cs4|0rQDrs*qsm+cMwR&tj4BHm7*$p=FsiI&U{u+_z^JmBfl*}}1Eb0g z21b>G42&wr7#LMfGBB!~VPI4_$H1uaoqwp$v?waSV*A z?F@{nT?~w>Jq(Ph{S1t%j~EzLUo$YOeqmr#{mQ_o`kjGMjg5g(&4PhZ&6R;s&5wam zEs%jxEtr8(EtG*#Eu4W-ErEeiEt!E)EscRuEt7#!Er)?oEsudwt(<{Tt%`wBt(JjN zt)78VZ3P3P+G++ywY3b4Y8x0B)tMO>)p-~g)kPT?)x{VX)g>4h)zujo)#Di$)$Vp z1Ecy^21fN?42C-76YSZE(4=x0Ry9E5d))UH3Oq& z0|TRGGXtY$8v~)&>Sft&I$fTALXdwGJ^bYCU3L)OydrsP%(^QR_DY zqt;&rMy>x0jM|J0jM`ibjM}^mjM@SWjM~BsjM`!hjM@?mjM|C}jM^#;jN0l9jM|zE zjM_mAjM||LjN0K0jM`BQjN11Y7`2}>Flv8bVATG|z^MJ1fl-H%fl)`1fl)_~fl zRWUH?EoNZUTgJesw}OFDZ#4s>-Z}Mv$s)L+KHsK1hdQGYc9qy7#CM*V{fjQU3y81;`aFzTORVAMawz^H$j zfl>b&1Ec;;21flm42=5s85s2+GBE1DVqnyN%fP7rfq_x~69c1xGy|i790Q|)0t2Ig zG6SPQ3In4-E(4=M83Ut1IRm3XB?F^D2Lq$QE(S(}lMIZ8;tY(2Dh!N$lIFdAK8U^KeSz-V-hfzjv&1EVn`1EVnu1EVn;1EVn~1EaAQ z1EX;;1EX;a1EX;)1EX;~1EX;s1EcW*21euc42;Hq85m8t7#K~285m8(7#K|?7#K~Y z7#K}t7#K}d85m787#K~o85m9U7#K|q85m8B85m7$7#K|)85m7m7#L0585m7G85m89 z7#K}T85m8<85m8f7#K}z85m72Fff|jWMDL9Vqi37WneUAXJ9ntVqi3lWneT-V_-DR zXJ9lfVqi2aWneTdXJ9mKVPG`vWneU&z`$raiGk5{3In6*GzLb~`3#Jvix?P9mohM# zu3%s^UCqE~x|V^_bQ=Ss=}rbl(>)A~ru!HeO`kI`n!aLSG=0OsX!@Rk(aeB>(ae&8 z(aec~(af2F(ae>B(JX|4(X5Su(QGOMquFByMzaqLjAlO>7|s4LFq-{iU^Hi7U^Hi9 zU^M4uU^M4rU^EwGU^EwDU^EwJU^JIxU^G`^U^G`}U^LfaU^LfdU^LffU^I_lU^I_r zU^I_qU^Gu)U^Gu=U^Gu-U^L&!z-WGmfzkXm1Ecvn21fG_42o& zsm;J>sl&i%smH)*Y0JQ9na{vzS;xR=d4_?}@+JeLl$G~X6k%7^EGXtajRt84K^@ zA7fy&Kgqyoe};k4{u~3N{dERL`#TJb_V*bW?H@5P+CO1nbl_!RbP!-*bP!@-bP#1= zbdX?RbjW64bSPn9bePM)=&*o+(P0q-qr*}LMu#5^jE;;9jE-CkjE=kvjE({fjE+JK zjE?dQjE))%jE>q2jE=etjE?#YjE;s3jE+_ejE=SpjE)WrjE>F>jE-&$jE)`*jE;c} zjE*4;jE><9jE<2EjE+qVjE=1gjE?OLjE-FljE<)n7#*)LFgo64V0664!07mZfzk0j z1EZ571EZ4x1EW(N1EW(n1EbRv21cjp42(`Q85o^rGcY>MWngq##=z*bl7Z1_4FjXo zdIm}qTNoIf_A)R!9b#Z~I?BN4bb^7==@bK_(`N=or*905PCpnJoqjPeI{jr} zbPix(bgpDzbgp4wbgpAybZ%f^bnawebnanbbna(hbe_n-=sb^s(Rn!oqw^{TM&~sQ zjLz#A7@apTFgovKV07NY!05c6fzkO81Ecd%21e)O42;ef7#N)|GcY<|V_HzP>dnCD>c_z78obrS=l>um-`*Lw_%t`8U(T^}YbRx>YkU zy45i-x-~K|y0tJcy0tMdy7e!7~Q2A7~N$U7~SO<7~ORl7~SI;7~S(27~S_UFuI>)V06F4!03LJfzkas1Ec#* z21fVW42ry4hBXK zLk30<69z^PGX_Qv3kF6HYX(M-J_bgQ?F@__yBHWf_AoGd>|MvuP?jGhb(jGoL4jGn9v zjGlZ9jGlrFjGiJ4jGkf)jGp!kjGj&mjGitGjGk@`jGpZbjGhx17(FL4FnUgAVDvo3 z!008y!04sO!04sL!04sR!04sV!02Vj!02Vn!02Ve!02Vq!06?~!06@5!06?{!06@0 zz~~jsz~~jhz~~jtz~~jn!0460!06SuToeYfLdl(qK_cJhhA7WtiKElB0eU^dI`vL=__hkk~@2d=q z-v1aFeHa-SeV7>-eb^WnecTxseF7L5eIgkceWDl`ePS3GeX?=qt*==qt{^=qt&<=&QuQ=&Q=W=&QlN=&Q}Z=&Q%T=xe~h=xfQq z=xfKo=fzfXT1Eb$621dWl42*u;7#RI_GBEn>W?=Mt!ocYF zoPp8rB?F`18wN&yT?R&fGX_R~dj>{-2L?ueCk96UKn6ztW(G$8Neqns4;UEz-!d@z ze`8?u|H;7U|C@o)|1Sfh|9=L?01gJm0B#1x06qrB06_-E01*bp05Jx}0C@(+02Kzt z0Cfh&04)Z_038O#fKUd;fCvW0fG7sWfEWhGfb|TF0XG;J18y@g2Ha&}47kt081RCD zG2k@=W57EG#()nDi~)Zc7z5cD7y~&O7z4Q(7z2437z6nk7y~637z3pl7z5=P7y}g< z7z0%p7z5Q97z6bg7z2$M7z0fi7z5227y}a+7z2|T7z0xo7z5K87z0-@Fa~a9U<};E zz!o5FoxV_U<}n{U<@^5U<|cqU<`F)U<`F(U<`F*U<~zOUw&tPB-pT)o! zK8JxZd?^EC_zDKb@YM{A;p-R}!#6T8hHqwI4Bx}R7=Dm}G5iPvWB73f#_*F2jNzXc z7{k9ZFou6;U=07oz!?6QfiZ%CfiWVOfiWV6fia?$fia?kfia?sfia?&fidC%17pNX z2F8d_42%(985kpeFfc~^VqlD9XJCvJU|@_CW?+mIWnhdHXJCw!WMGU`VqlC^Wnhfd zU|@{YW?+oeV_=LlU|@{2WMGW6VPK53XJCwUWMGWUVqlESWnhfVXJCvhVqlEi%)l7A zhk-HjC<9~UF$TuS6AX-z*BKb2I2jnDBp4W@(ij+{iWwNAY8V)!>KPcL8W|X)ni&|Q zS{WFl`WP6aCNeNaO<`b+n$Ex&HH(2UY7PTq)KUhVPK5$Wnhd6U|@_1VqlC3Wnhe%#=saepMf!E1p{Nu zN(RQ5)eMX=yBHW_-Y_u6{AOT`^<-d-4P#)8jb~ttO=4h-O<`b+O=Dn;&0t`REo5Mf zEn#4cEoWeitzuw|tz}@0t!H42?O^=s@*nW^lW86Ll#<+tF zjB$q<7~?K5Fvi_vV2r!Nz!-Orfidm@17qAH2FAG842*H_7#QO|GBCz{VPK5=&cGP= zlYudwiGeYmm4PvygMl%gi-9rTn1M0gjDa!Uf`KvKnt?IChJi7@m4Pw7kAX41pMf!c zA_HUm0tUwTOAL(h4;dH}^cffvY#105Tp1V>JQx@gycif0d>9xL{1_M$!WkG7q8Jzx zVi_0{5*Qd0k{K8iQW+Q%@)#HsiWwLa$`}|EDj66Psu>s)mM}0TEN5U$SjoVcu!ey# zVLbz5!X^gBL{mUiM0*Ct#7PW{iL)6P6PGYBCN5`SOkBml zn7D?4F>xmYW8xtO#>ArxjETn?7!yx2FeaX6U`)Kiz?gWQfidwG17qS{2FAn(42+47 z7#I^@GcYE;V_;1D$iSHRnSn7$hJi6jo`Eq*k%2Kug@G|Cm4PuSkAX3%oPjZ^f`Kuq zih(hylYud5Hv?nRDF()52?oYwRR+dnJqE^PLk7lVV+O`#QwGLla|Xs_2L{GuX9mV( zHwMOJPX@+h9|p!`KL*C+a0bTY7zW1Vcm~GgBnHOh6b8oRUIxbG2@H(MlNcD2r!p`m z&tPCoe$Bv`{DpxrMV5gvMS+1aMTvniMU{aurGkMmrICR#rHg?vrI&#*WdZ|Z$|MHH zl=%#dDJvKlQ&uxDrmSUPOj*ysn6i<9F=ZD6W6E9z#*_mLj46j17*mchFs7VfU`)Bl zz?gD{fidMe17pff2F6q-2F6rY2F6r&2F6q_2F6rx2FBD72FBD_2FBDl2FBC`2FBEU z2FBEd42-E87#LIkF)*faGccx!FfgWxGccw}GBBn|GccyfGBBp8F)*fSGBBp;FfgX+ zGccwZF)*f?FfgXsGBBn&F)*gNGBBojFfgWhF)*eTGccx=F)*f8FfgW7F)*f`WnfJE z#=w~NlYueqHv?nZUk1i>HU`FYP6ozw9tOsAJ_g2gX$Ho06$Zw1bq2Xl7u{Xk%c^=wM*Xn8?7GF^z#SVJ(6a!;sECXX^JOg895(8uARtCn*eGH75 z#~BziPcblNo@HRnJkP+Gd53{9^C<&k<_iYK%vTJInQs^vGv6^VW`1X2%>2c`nE981 zF^hqLF^idjF^iRfF^i9ZF-wqvF-wGjF-werG0UETG0Ta8G0TO4G0UBSF{^`tF>4|N zW7aGN#;n;4j9GIT7_-(eFlOCjV9a{Uz?f~#z?kj9z?dD#z?dDvz?dDzz?dDuz?dDy zz?hxPz?hxJz?hxMz?hxGz?hxSz?fagz?fadz?fanz?j{{z?j|2z?j|6z?i*(fiZhC z17r492FC2|42;>k7#MSS85ncS7#MRb85nb{85nbH85nci7#MRr85nbX7#MT>7#MS+ z85na?7#MTX85nai85nc285nbN85ncQ7#MRZ85nbF7#MTv85nb#7#MR}7#MSU85naW zFfis!W?;;j%D|YjkAX4gAOmC0VFt#WqYR9>Yz&OK{0xk_0t}3~LJW+#t_+O1lNlIu z7cem9u4G`$UBkebyN-b|cLM`s?j{Dt+}#X}x%(Ixa}P2w<{n{S%stM)n0u0eG4~P! zWA1eZ#@t&BjJbCi7<2D4Fy?VEFy?VHFy`?xFy;v`Fy;v}Fy@IdFy>`4Fy<98Fy_r> zV9cAxz?ipyfiZ6}17qGd2FASq42=0442=2Q42=1F42<~#42=1*42=0|42=1j42=2O z42=1@42=2u42<~}42=2K42=1942=1X42=0M42=1142=2y42=0f42=1q42=2V42<~= z42=2B42=1$42=0542=0F85r{~F)-%eW?;;}!@!t-kAX4&EdyhLJOg8a9s^@R4Fh9A zCj(=_BnHNUsSJz-(-{~GW->4q%w}LLSi-i#qFc!aMU@ZQ?z*zi=fwA~M17isX z17is{17is<17it417nFG17nF417nFS17nE-17nFY17nFA17nE>17nFH17nE^17nFf z17nFL17k@N17k@l17k@#17k@h17pc52F8-j42&gP7#K^oF))_AWMC{cXJ9OKVPGuv zWne4~U|=i_Vqh!{VPGr`V_+!wih2M;RDPPcSf+o?>7uJiQzpR>#0t*1*76*2%zF z*2BP9*3ZCLHi?0;Y$^j|*>ncRvIPu`Ws4aY%a$=PmaSl5EW60SSayYhvFsWHW7!P` z#&Ssp#&RVF#&Tr_#&T5##&Qz|#_}`<#_}x;jO7Oz7|TyFFqWTXU@Sk+z*v5ffwBBD z17rCe2FCLH42+-id77Z6`L6tE4DE( zR_tV8tk})KSn-5`vEn%cW5r7b#){Vrj1^xP7%RRqFjjnLV64<)V5~G}V61dtV61dz zV61dyV61d!V660HV5|&cV5|&fV62Q_V62R0V62Q|V604FV64n!V64nzV5}@;V5}@* zV5}@-V62?az*xD6fw6K417qcK2FA)&42+e385pbB7#OSc85paK7#OQe7#OR}85pa& z7#OQ2GcZ=oVPLG9&%juJT?~v>dl?w3_A@Y69b{mvI?TXWb%uel z>O2Eu)g=bTs;dl)RW}$It8OtcRy}55ta`@4SoM;DvFbGgW3>naW3@N~W3?m$W3>ze zV|6S8V|5w>V|6|QV|4)oV|5V&V|60~WA$bR#_B^1j5Pubj5V?hj5TTuj5V4Jj5XQ} zj5WFpj5YcUj5QVvj5XE_j5T%)j5Ur7j5RI{j5Tfyj5YoYj5Q$)j5Xm5j5SdVj5RR~ zj5Vzcj5Qq$j5S>hj5R$Bj5YlXj5UuK7;Cv17;E_$7;6O>7;A+X7;B{&7;EJi7;6<7 z7;BXo7;6m}7;7yV7;9}97;Ehq7;7CE7;Bvv7;C*57;F6)7;6I=7;8fq7;D2B7;7UL z7;BRl7;94*7;7^a7;Cc_7;7gpFxF0EV62_Nz*swrfwA^B17qzI2FBW_42-qU85nE7 zF)-F?Ffi7YF)-FOGceZmFfi8jGceXoWMHhD%)nSTm4UHt9s^_DLI%dVB@B#p%NZE! zRxvQvtzlrS+seRLw~K+XZZ894-2n#1x)tUi)_q`Ltoy{kSof8IvEGA$ zvA&RjvA%?XvA&FfvA%+VvA&UkvA%_YvA&&wvA&amv3?o@WBq&v#`;AJjP*+x80(iY zFxIbNV65NBz*xV9fw6u&17rOz2FCim42<>r85rwNFfi7iW?-y8$G}*Bfq}99Cj(>s z9|p$ye+-Nb3=E77jtqd}d&5_{PB4 z@PmP|;THpA!yg95Mpg#KMh*tXMs5biMm`3{MnML~MqviVMs)_pMgs=MMq>uXMl%M+ zMhgbU#zY3j#uNs|#xw@T#ta6=#$61IjfWW+8&5GXHlAf*Y`nn0*m#+NvGFPcW8)(R z#>UqSjE(OY7#lw@FgAW-U~K%tz}Wbgfw75!fw761fw75=fw75`fw763fw4)1fw4)P zfw4)7fw4)3fw9Szfw9Shfw9Spfw9Slfw5^m17p()2F9k942(^y85o-mGcY!5FfcZo zGB7sVF)%heGB7qfGcY#0GB7r~GcYy>FfcX;GcY!XF)%hqGB7sBFfca9F)%i#GcY!1 zF)%jgGB7shGcY#KU|?*X&A`|^mw~Z)J_BR(M+U~`Ukr@RzZn>t|1vPP6fiKh)G{!( zv@tNYbTTlubTcrv^fEBE^fNHF%wS+_na#l1GLM0=Wg!D&%Mu30mSqf#E$bN=TQ)H; zwrpi!Y}wAh*z$mZvE?xXW6M(p#+K&{jIFi|jIC}AjIHhrjIEvwjI9S47+bF~Ft&bU zU~FS%U~J=IU~J=OU~Cg)U~Cg+U~Cg*U~H3PU~E%lU~E%iU~E%oU~JQ3U~JQ2U~F?> zU~KbbU~KbYU~KbeU~CIyU~H>mU~H>pU~H>rU~Fq-U~Ie0z}WVgfw7&Ffw5hPfw5hZ zfw5hNfw5hlfw5hQfw5hMfwA3?fwA3+fwA3|fwA45fwA3@fwA40fwA3(fwA46fw4V^ zfw4W5fw4V;fw4V`fw4WAfw4V}fw4W4fw4WCfw6rG17rJi2FCW842zGB9=&F)(%%Gcb0PGB9>DF)(&?Gcb1aF)(&aU|{T+#K71wg@LhSE(2r70tUv8 z#SDxc%NQ6tRx&VltY%>B*uucrv7LdjV;2Ks#~ud8j;9Qa9WNLdJ6|D&i*tv#*v2!g0W9NDX#?CzqjGadr7&}ieFm|3|VC+1@z}R_?fwA*C17qhc2FA|2 z42+!*7#KSrGca~OWnk=l$H3V6k%6)E3j<^4HwMNoc?QNVB?iVW6$ZvGH3r768V1I$ zrwoi;?-&@n-ZLgZOg#eoyNe}oyox1oz1}5oyWk~UBJND zeVBo<`watQ_j?A$?vD(N-Jcm4dmI@Udjc33dmV7#Mr!GcfipWMJ%F#lYCRo`JD< z69Z%KRtCo2?F@{)4;UDGA2TraK4oC+ea^tx`-*|F&z6C)FO7k*FOz|>FPnj}FPDL_ zZ#M&D-w_7JzT*sxeWw^0`_3>h_T6M)?0dw(*!PryvF|wpW8X^##=h4KjD24i82i36 zF!udoVC?(Lz}U~gz}U~kz}U~tz}U~nz}PRyz}PR$z}RoYz}Rokz}WA|z}WB1z}WA` zz}P>7fwBKI17rUc2FCuY42=EP85sMYFfjJNWnk?8z`)r5iGi{I3j<^SHwMQ3{|t;1 zm>3u*ure@C;9y{!z|FuoftP`Cf+7Rs1T6-}3Hl6-6O0%bCzvuYPB3R+oRGl4I3byV zaY8BsI{sNA{iJb zB{48gN@if3l*+(3sfdAbQZ)nPq&f!1Nev8)lbRS9C$%szPU>Y~oHT)fanfW4#!1r{ z7$?nSV4O6YfpO9j2F6J%85k$6VPKrJo`G@FMh3=7w-^{F-DO~$bf1B7(nAKuNlzFU zCz~@cPEKH8oSe+SI60MpadJ8XfpLl@1LG8H2F5A242)Cy85pM=Vqlzdnt^f383x8F=NK5L+-6{$@`QnL z%5w(BDK8lqr@Urhobr}|amqIa#wkA;7^i^D{m;NSm5G6IDhmVSR9*(gsbUO_QzaP~ zr^+xePL*e1oT|jYI8}v#ajGu^6PH z)7CLCPTS7FIBgdLPXErpIQ=IB;|wJR#u=&%j5E|37-wiQFwXE~V4RW2 zz&Im|fpJDQ1LKTb2F4jx42(0H85n1@F)+^PU|^il#lSeDhkSOE(7Bn4F<+Jh762zOc)sFm@_cWv1DMJdN44~4P{`Q8^^#nH<5vH zZZZSo+*Ag}x#$180SuAV4ORR zfpP9k2FAH_7#QcyV_=+noPlxfDF(*5XBZggo?~E~dy#>0o(cowJYNRJc>xTJ^MV){ z=Y=pZ&Z}i$oY%m>IIoF;ab61pR{gn@CsIRoQ-O9saI)(njEZ5bHnyD>1%_hew4@58`2-=BeTeh>rW z{167l`LPU)^HUfY=ch9;&d*|CoS)0UI6t3(asCVj#`&`u80XJrV4OdnfpLKb1LJ}~ z2F3*;42%oH7#J5sFfcA?WMEv-!oawoje&7N2Lt1R6%33EHZm|S*u=oNU<(7|f`bf< z3r;aGE;!4;xZnZ<(YFfO>yz_{QM1LJ}x42%n&GcYcA#lX1W4FlssW(LNE z+6;^fZ5bFB`YT7nU$EE-YnWTv*P) zxUhwRabY_H zMGF}i7cFLBT(p6KanW7|#zhAh7#AI8U|e*JfpO7E2F68a7#J6wXJA}(hkMpvjEk2rFfLxp zz_@rF1LNWi42+9+GB7UQ$H2JwAOqv#BMgj-k1;SVKF7eggn@x^i8uq}5)}r+1~4!#4Pszin!v!gG?RgGX&D3K(rO0ArL_!0<`QrQaABm;PX2T>6`VaVf~X{}~vUF)}bNV_{%i#?HXF zjEjMB87~9lGJXceWl9W;%d8m~mw7TUE=yowT$atixU7+ZaoG|E#$`Jg7?&MpU|e>A zfpOVo2F7LA7#Nq`WMEu&hkcJ zY++zrIg^2LjH|mD7*}s&U|hYMfpPUS2F5k~42)}J7#P>6Ffgt$U|?Kh z#K5@5l!0-L1q0(6M+U|9zlgfcL$iC|z{6V1T5CYFJ5O(_H8nkELuHQfx1 zYx)=%*Gyz!Tr-7%am{oF#x)BV7}qRjU|h3|fpN_W2F5k385q~BWnf&hje&8^0S3l3 zrx_U6JZ4~A^MQeJ%})l#HGdcw*ZgN-T+773xR#ZHaV;MM<5~d*#P;fpKja1LN9$2FA5>7#P>iXJA~rh=FnKQU=Dg%NZEgu4G_byO)7+ z?I{MvwPzU^*Pdr!TziRuaqU$G#C$?aWF8h<7Qx7$H&09PJn@Nog@R}Iwc0ib@~j9>r5CJ*O@UeuCriZ zTxZL`xXyurah)>*<2qLc#&!M-jO!v87}upTFs@5uU|g5Zz_>1xfpJ|K1LL|%2F7(Y z42m?Z&*DEnFt~X|2T<^iaxIT%2aeXZV@DGZDorZX^Zn90Dn zVGaZ1hItH(8@xM3{=cXEHEuoWsDlaXtg%#zhQ_8<#RLZd}2@ zxN$WD zS`3Vv(-;^xFJNHYe3F52^HT=KEpiNuTeKM%x9Bl2ZZTwF++xDOxW$}-af>Ab;}$mt z#w{KUj9a`J7`ON_Fm7pPVB9i+fpN=32F5Lu7#O!qVPM>{kb!Z_5(dUC%NZEAtYTo? zvX+5y%X$XJEqfRkx9nqJ+;WhCamx_~#;v*xj9aZ37`HkyFm82WVBG4?z_`_mfpKdf z1LM{Z2F9)742)Z&7#O$4FfeXyVPM?4nt^faF$Tu1XBil`UT0w3`hbCP>stoKZ9)u; z+e8@{w@ENCZj)wU+$P7sxJ{9PahnPQ<2H2$#%)>*jN5b>7`GWPFm5wuVBBWLz_`tl zfpMD+1LHP(2F7hp42;`c85p-^F)(h+WnkP^z`(ezn1OLy83W_CN(RPlH4Kc~>KPcf zH8C)5Yh_^E*1^EIt($>yTOR}Cwuuak+omuuZkx`)xNQ~#gBjN9fjFm7AJz_@K4 z1LL-h42;{hFfeY{Vqn~E&cL|cl7Vr%4FltLdj`hsvltk+FK1xfzLJ4)`x*wu?dusB zcPKJ2?$Bdk++o1LxWkx%afcZLC1LKYZ42(Oa85nnJFfi`a zVqn~<%fPtPfPrym2Lt2IsSJ!er!z3_oW;Pnb1nnpEV+@RYPcks>GiPAj=fc3a z&y9g`pC<$3J|70geTNtr_nl{8+;@?Iao-gN#(mcr825WJFzydyVB8q&fpLE- z1LOV-2FCr_42=6r7#R1LGcfM2Vqn}~%fPt5fq`*<69eP^O$?0t?=mnR;9+1qV8y_A zAcTSOKq3R`~4JkZR*c%X-Y@xVj|#sgCr7!OQmU_3CB zf$_jH2F3%c85j?&V_-b6k%95RHU`E6I~f=c>|tO$u#bW9z!?U{1NRvi4>B__9^_$Q zJSfS)cu9$&A@mtkAd-EAp_&V=M0PoKQJ&J{L8?2h>3ym5Gw=YAr1z{ zL%a-(hXfcH4+%3c9uj3>JS5A&cu0kT@sJ(^;~@_Q#zTP&jE4#t7!Or2Fdmx0z<6jK z1LL9H42*~NFfbn4%fNVOKLg{TGYpJ}t}-wly1~GB=r#l6p?eIBhaNI89(u*Vc<3zy zjE6omFdq8Gz{gWU_7#&f$_*L2F4?M85oZoU|>8d z&A@n6g@N&?Ap_%4GX}<^mJEzXZ5SAjIx;XGbzxvU>dwG;)RTenXaED_(NqS;qeTpi zN9!3Fk2Wwc9&Kb`Jlf2_cys~-#$zWL7>`|J zU_5q{f$`WK2F7Fe85oZ}VqiS>gn{wc4+h5Lj0}v&*%%m)b22a<=V4$x&d0!bT$+LL zxB>&?ab*U^<7y0y$F&(4kLxio9yerQJZ{Xuc-)bJ@puRW`e8 zU_3sJf${iE2FBy_7#NQ)U|>AHn1S*5QU=E3>lhf1pJHG|EtU_AbVf$>BZ1LKKe2F4Q&42&n*85mD=F)*I!Wnerpfr0VF zbOy!~vltjp%w=Fav4DZ`#3Ba96UP}CPdsK|Jn?~n@x)IC#uI-S7*G6XU_8mlz<836 zf$^j?1LH|K2F8<$42&mL7#L5gGccaiVqiR}%fNWjih=Q@4Flszdj`gnjtq<^of#NU zx-l@G^k85->BYc!GK+!nWE%tH$+ZlOCoeNFp8UzccuJ0e@sv6P<0&l$##6csjHmP& z7*AO+FrKnvU_2Gdz<4T-f$>y61LLV82F6pR42-8L7#L4gGcca2V_-bh$iR52g@N%@ zI|JjXP6ozPlNcCJO<`a>wSj^0)J_J*Q@a@$Pd#E_JoTD^@zh%e#?$N!jHd+{7*7i^ zFrL5x1LK8a2F43D42&1*85l1#F)&_eVPL$_%fNV{ zpMmkhDh9?2n;94{9ARL*aGZhh!YKyE3uhS^FI-??yl|O;@xnC*#tSzY7%$vmV7zdT zf$_p~2F43785l3JGB94`V_>`}z`%IXoPqJ80|VnlCkDogF$|0sQyCa9rZX^JY++!$ z*vr6pv7dqQ;wlEli<=o3FK%UEym*0u@#0Me#*4QZ7%wp}Fka$hV7$c5z<5cUf$@?B z1LGx22F6QP42+j-7#J^kGB93>U|_ry&A@mmj)Cz~A_L>46b8mi=?sjQvKSaIx75ny1vBFw;eMT~** ziX;Q$6=?>>EBXwKS1cG9uh=p$UU6VxyyDEjc*T{0@k$T_TO1LKu$2F5Gv85pnZVPL#+kb&{a5eCL9#~B!}oMB+Ra*l!V z%0&joE0-A@#+)?#;Xe%7_Y8iV7$7Tf${1(2F9x!85pnbU|_tu zn}PA_J_g3C2N@Wz9%f*?`htP+>SqSVYy1q1*CZGiuSqj7UXx>Byr#gwcukjq@tPF_ z<273b#%m4?jMtnQ7_Yf8FkbUyV7%tTz<4c^f$>@l1LL)L2F7cN42;*385pmnGB93C zXJEXR$-sE6pMmk(Y6iw@mlznY{a|3cF2TTf-Ijszx*G%I%^C*AoAnHgH=7t3Z?-Zp z-t1styxGmbc(adz@#aJZ#+y?Z7;jEzV7xhtf$`>C2F9BU7#MFZW?;OzjDhjyN(RQ8 zYZw@Bu4iDpxru@C=2iyAn>!d7Z|-Jbyt$8o@#aAW#+yeN7;heDV7z&Xf$`>92F9Bg z7#MF}W?;N|je+szO$NrBcNiFN-e+LE`G|q>=2Hg7n=cp`Z@y+=y!noS@#aSc#+zRl z7;k=OV7&Q@f$`>F2F6%p1LLh&2F6t>)iE&MYGh!%)xyAdtDS-IRu==~ ztzHJkTN4-{U~TOSx0Z+&K9y!DNN@zzfU z##?_F7;pV&V7$%5z<8ULf$=s61LJLO2FBZb42-u085nPiFfiU0XJEW7#lU!5mVxoM z0t4f1Wd_FEY7C6GH5nLh>o73h)@NY6ZN$KM+mwOvwgm&@ZEFU`+jb0$w;dT6Z@Vxs z-gakTyzRxnc-xnO@pb?Mn_b@Qt?q^`U zJ&A$w_EZMO+cOv#Z_j36ygiSB@%BOn#@kC67;i6UV7$GGf${cQ2FBYP7#MGFW?;O% zje+s@P6o!?dl(pR?`L4VeTaea_E84L+b0+pZ=YshynT*=@%BXq#@kmI7;j%^V7z^c zf${cT2FBYD7#MFqW?;PijDhj?O9sZZ~tasy#0@X z@eU&c;~f?T#yji`jCZ&g81L{hFy0YhV7w#Dz<5WDf$@$c1LGYT2F5$`42*Y_7#Q!U zGBDoJU|_tX&A@m^kAd-yAp_$b69&dR<_wH?tQZ*Y*fKEQabRG)fy1LK_<2F5$}42*Z07#Q!gGBDohU|_t{&A@o4kAd;dLX?<{6uyt9mf@y<#H#ye{m81JlSV7#-5f$`2(2F5!(7#Q#D zW?;OtkAd;dK?cSbCQAa&LsxMJ69MO?|frmyz`%d@h&3+<6TJx#=A-k zjCWNS81LFKFy3`%V7%+ez<4)@f$?rB1LNIt2FAN{85r*_V_>|yf`RewF$TuF=NTC9 zUSwdr`R6q-b-U(yqC|wc(0Iw@!nDf#(V1+81HRh zV7zyaf$`pR2F80Y85r-gGBDofV_>{5z`%H4hk^0FDFfqua|Xuy@eGXjGZ+}}XE89| zU&O$8e>DT+{k05?_s=mf-oMVkc>g8?W4!1%y|f$>2s z1LK1<2F3>&42%yZF)%)u&A|9zE(7C(2Mml4UNSH~c+J50P=QXd(mSqgf1$kLEBiKHAT~_~--! zd|n7@w#yFh0>|V0>c8 z!1%Qg1LKnj2F52942(}285o~5GcZ0`#lZMvGXvw3tqhD$E-)}YxyiuzEc1LKPd2F4eS42&dl?vC9AjX7ae{&I#YYCl7a%kKFfhIpVPJeI%fR?jo`LbD83W@>dj`grjtq=1 z6Brm@W->6o%w}MG*~-B9vX6oBIDPit5*z+ulX1lUyCy^zLsQQd~L7#Lq4U|@WGhk^0+QwGM@&lwosa4;~w5oBO|Bh0|~#+rfgjSBI0t4gQOa{ic*$j+tyBQeYPGMktJB@+y?PdnXw|f{E-|k~z zeEXh(@hwRIF9ybU3Ji?zG#MD*X)`dsi(z1Vm&(BSE}en#T`vRUyJ-xJ?`AMCzT3*c z_--EqY zD+A;Eb_T}x>lql|?_gkjzl(wK{T&9z_fHuZ-#=$y{J_M(_<@^&@dGad;|CK4#t*g( zj34Y77(Zk%Fn%axVEj_;EG^`^1LL<22F7m_85qA!W?=lbje+soeg?*G z2N@W@Jz`+|_L_n5+gk?4?}7}B-=!EBzsoQ%e$QoK{9eYu_`QOG@%t_Y#_xw27{4E7 zVEn<(!1zOef$@hB1LKcC2F4#z42(Zw7#M#{VPO0*mx1xedy7=H;dF#eKeVEiS^!1ybk zf$>)c1LLnO2F73085n;pU|{^Uh=K9f4F<+v4;dJLJ!WA1EyKY0Ta|(Fw>ksk?<5As z-`Na|zjGNFe{W=9{Jo2T@%J7E#^3K47=M3fVEp})f$@(C1LGfC2F5@342*y37#RPw zGcf+?WMKTWkAd;eaR$aeCm9(3@-Q&|6=h)jE6%|9*N=hmZ#V(JVEiY?!1zy{f$^Uv1LMC~2F8DB42=IW7#RP}WnlcbjDhjr z3I@i1*BKc9Jz!w`_lSY|DzZf|0gpr{!e9K{6C$6@&5t_#{Y{L z82>+KVEq4qf${$*1||k`1||jv1||k41}26Y1}2791}27f1}27W3``9B8JHLjGB7dx zW?*7uVPIlpV_;&mWnf}-V_;(RU|?b_Wnf~gV_;%zU|?cAz`(?Kl7WfwGy@Y87XuTM zFar~lC<7Cd7XuShFar})C<7DIBnBp?*$hlfa~YVJt}rk$-DO~6y3fGGY{S6B?8?Bz z?9RZ%+{3`cJe7foc{&3V^JNAm<~s~b%=Z|WSY#NOSX3F9SkxJqSaKPdSjrffSSlEp zSavfou^eGwVmZdZ#LCXV#45nR#45zV#2UiD#2U-M#2U}Q#5#?EiFG~$6YD|-Cf0il zOsvltm{?yjFtKScFtHgjFtHgkFtN2WFtJTwU}BrZz{GZyfr;%N0~6Z=1}1iK1}1g| z1}1hT1}63t1}64g1}64=1}65s3{33D7?{{kFfegQGB9x{F)(qcFfefxGca+~Ffeh{ zF)(o)WMJYr#lXaIhJlGwnt_Q^g@K7vje&_XnSqHjhk=PRkAaDE8v_&Peg-DagA7bu zybMfSVhl`N5)4dSX$(wU`3y{4g$ztwI~ka`4lyus9bsVN=4N2x7GYrG7Gq%IPG?}^ zE?{8dE@EKf-pjzmeT;#L`ve0Mj~oLNk2(Vrk0t{XPay*nPZa|bPYnYT&oKrjp7RV$ zJQo?5cr_T9cnuktc#RpDc$*lQc)J;xczYR`cyBT=@jhZ;;(fxv#HYc)#AnFB#AnRF z#Mi{Y#MjNh#MjHf#CL&#iSH%@6W?tHCVnFZCVp!MCVpE6CjNyCO#G`DnE2N)FbVK5 zFbRk-fk}{;fk{w|fk{w;fk`ltfk`ln zfk`ljfk|)^1C!ux1}4G13`{}{3`|0t3`|1Y3`{~n3`|1N3`|0?3`{}`7?^}sGB63P zW?&Nf$iO7@i-Ae#4+E31BLkDL7Xy>94+E2MKLeBS3zA`XL+b}Rm zyD~6IyE8CJ_b@O?Pi0_|p3cA|eTjid`ZfcT^j!uf85IU58C?b@8GQyOnPvtinH~lv znLY+4na2!FGH)1|WZp3_$(l1T$vQAF$vQDG$<{G2$+j~v$#ya@$=+gMl6}m;B>R+s zNluG_NzRyoNzRmkNv@iKNv?%~Nv@57N$v&%liWiFCb`E9O!8U`O!CGIO!B4-O!5s3 zO!A!!O!D0fO!Ai*nB?y;Fv;IzU{cUzU{Ww*U{Ww)U{YvdU{dI1U{dI3U{bivz@+em zfl1*R1Cyd71Cyc`1Cyc;1C!z+1}4ST3`~k^8JLt97?_ke8JLu~8JLu!8JLt(7?_mO z7?_lHFfb_{WMEP{%)q29!oZ{~%fO^8$H1hlz`&%e%fO_p$H1hl&%mT?$iSp*$H1iQ z&cLMX#lWQO%fO@@z`&#&%)q1^!@#5*&%mUd#K5GS%D|+Y!N8=P#lWOo&%mVI!N8>4 z&A_DG$H1gKfq_YRAp?{05(Xyabfk|yP1C!bY1}3$=3`}bK7?{-d zGcc(gWMEP|#lWO?mVrs_0t1uUB?cz7#|%tr&ls50UNSJLyiZa& z)DJK)sh?$FQoq5#q<))$N&OxJllnsjCiN!_OzO`WnAATpFsXlKU{e3Vz@+}0fl2)z z1Cs_L1Cs_b1Cxd^1Cxd?1CvG+1CvH91CvGq1CvHF1CvG?1CvGu1CvG}1CvG>1CvH2 z1CvG#1CvG_1CvHK1Cz!C1}2Tk3``o+7??C>Gcak)V_?!)$iSqrn1M-S0|S%BRt6@G z9SlqwyBU}?4lpoj9AaS7ILg4Jah!ok<1qu1#v2AEjrR;p8lM=LG`=t}X|ggfX>u?y zX>v0#Y4R~JX$mqhX|^#iY4$TPY0hC_(p=2Iq`8cNNpmFwlja%*Ce6(ZOq$ymm^61X zFlp{#VA9;jz@+(@fl2c}1CtgH1Cy301Cy2n1Cy3C1Cy341Cy2(1Cy3H1Cy2&1Cy35 z1Cy2m1Cy3B1Cy2;1Cy2~1Cv%51Cv$+1Cv%X1Cv%P1Cv%f1Cv%F1Cv$?1Cv%71Cv%a z1C!Qr1}3d@3`|;I7?`w07?`xJ7?`x38JM))7?`v@8JM)a8JM&~7?`xf7?`vR8JM)I z7?`xX8JM*D7?`vtGB9aRVPMjp&cLKSi-AddE(4SH0tP1S#SBc^OBtB7*D)|@Z(v~3 zKEuGIeU*Vp`#J-Y_CE$D9d-sL9Zm)&9c2b49UTTH9X$pn9d8CEoe%~loiGL_ok9jC zohk+bQUo%=^STZ(z(FEq;rXZN#_#-lg@tzCS3*wCS67bCS7I* zCS3srCS7R;CS5rOCS64aCS4T zCS5lMCfx)ECfynaCf!yBCfyDOCf#lZCf!~JCf#`qOu7phm~@vgFzGI5VA5U1z@#U~ zz@(?nz@%rwz@%r*z@%r#z@+EMz@+EGz@+ERz@+ELz@!(*z@!(#z@!((z@%5rz@#^w zfk|%x1C!oj1}42_3`}|}7?||7GBD{KVqnrc%D|*|f`LizGy{{~IR+-ZiwsP9R~VS| z9x^cLJ!W9id&a<|_kw{*?-c`+-dhGHz4r`EdLJ2>^nNig>HTG3(q~{`(r02|(&uJi z(&uGh($`>M(l=ya(syED(syNG()VCs()VUy()VLv(hp={(hp%^(hp}~(vM78NOs-GW^28WC$|*7Xy>wUj`;41_mZ0W(FoB zJ_aTuK?Wux5e6nBaRw$MDF!AZSq3H}c?KpUO9m#RPzENWI0h!8Lf(%T?5)4en(hN+-atutyiVRG~8VpRv z+6+v_dJIg)h73%`#tclx5e!VmB@9f)wG2$g4Gc`i%?wP&tqe@YlNgwc=QA)FFJfRa zUdq5^yn=zrcr^o)@j3=3hZ3ZS&69y(zGX^Hp zXa*+J6b2^KGzKQqHU=is$qY=Ua~POR=QA*wE@EIZUCO{@x`KhpbRz?k=@tei)9nmQ zrn?xJO!qP{neJy`GJVCsWG2bLWah!ZWLC()WLC$(WY);QWY)sKWY*5WWY)#NWY)vL zWVV2T$!sM9li4~3CbNwUOlDgcn9Q~@Fqs`@U@|+wz+`rsfywL~1C!Zh1}3v>3`}M> z8JNs&GccLGWMDG;!@y*&z`$g#%fMuA#K2^3%D`l9!N6p0%fMuA&%k8v#K2_k!oXzi z%fMuw#K2^p&A?=y$G~J>$iQS?%)n&c!oXzS&cI~e#lU3V%fMtlfq}__fq}__lYz-X zjDg8Qnt{nej)BQSk%7rVje*HRlYz-Xhk?mLkAca;nSse7fPu*(mVwD4j)BP{o`K0C zk%7q~kAcaeoPo)rih;?ZmVwElfq}`QnSsfohk?nWpMl9@5(AUPR0bxC84OGovly5x zjxsP=d|_a+RAykZ^kHDKjAUT4jA3B1jAvl7Ok!ZNOl4rQOlM%StY%=cY++!s>||iF z>|tQC>}O!IoXEgrIgf$Kaxnvw1C!NT1}3Wy3`|y^7?`Zt8JMg^7?`XT8JMh<7?`Y;8JMh98JMh%7?`YW8JMgc z7?`Y`8JMiy7?`X*8JMhn7?`ZX8JMi27?`YM8JMgS7?`Y+8JMio7?`Xx7?`YQFfdsk zW?-`Z#K2^u$iQS1$iQTi&A?<+!N6ow&A?<+$G~LM$iQUN!oXzH#=vAVi-E~zF$0s$ z3I-;d)eKBF>lm19HZU;R>}6oGImEzZbCiL}<^%(i%~=K}n+ps~HkTQgY_2je**s=o zviZiqWGltMWUJ1=WUIr#WUJ4>WNXC0WNXgAWNXR5WNX8~WNXL3WE;i6WSh*uWShpo zWShyrWSh;vWLv|)WLwX`WZT5RWZTNXWZS{OWc!za$&QVI$xfJo$xe!a$xfDm$xeZR z$xfMp$xe%b$xfGn$A&mlfw%JCI^r`pBb1O`52fS#Tl3!B^j6;?HHIG-5Ho1JsFrBGZ~m1 z%NUp(%Ndv)D;Ss@s~DIZ+ZmV~ConKMPG(?ooW{W9IFo_NaSj8M<5C7D#}y1rj;k4% z9M>^0Ic{WNa@@?oY$*G!w$*GQk$*F;X z$!Q7$lha%VCZ|OVOioJ~n4DHHFgdMaU~<~dz~r=tfyrq<1C!Gs1}3ND3`|a^7?_;S zGB7!vXJB%=%fRIHfq}_cgn`LfmVwDxi-E~mpMlBQh=Ix3l!3|Fih;@5hJnf1o`K2P zk%7tCi-E~Go`K0ZgMrC8n}NwWkAca#fPu-mk%7s%g@MVroq@@@i-F0xmx0OoHv^Lk z3j>pjAOn+&7z2}wBm39SkT%Iv7xqN3}a{0x;$#oh7lj}?dCf7L(Os?}8m|T}JFuAT|U~*llv8bHZd@{ zZDn9`+s?q`c7TD&?GOW#+dT#*x91E@ZZ8>_+yxkz+$9;9+@%?q+)Wvn-0c{c+#ML0 z++!J-+|wAC+%p)M+?yGg+r zCJ!G5CJ%oGCXYY{CXYr2CXY1?Odhuxm^?Wdm^>{Qm^>31m^`Z)m^?cem^`}~m^}L! zm^>#kFnLa4VDg;Cz~s4#fyr|-1C!?t1}4wl3{0N;7??Z{Ffe(ZWnl8W#K7cvm4V6g z1_P7lT?Qu42MkP}j~SRepE59cer90uVq;+P(qdrpGG}1&vSDEIvS(oOa$;cea%W)j z@?>E0@?l`|@?&7~3TI&Q%3)ygs$^jDs$yXBs%BvFs%2pEYGYvX>SSQ@>S18=>SJK? zn$N)GwTOYqYbgVh*9rzEuhk4p-m(l#-f9d?-o^|}-WCi@-qs9E-gXR3-p&k6-fj#` z-kuCh-rfvM-U$p$-nk4+-c<}t-qj3D-ZczN-gOL2-rWpL-p?4Ayk9afdB0&`@_x_2 zChy-2Oy2((n0y!+n0#0mn0(k7n0&Yxn0$B{n0y2nn0$m8n0&+- zn0zD|n0#ayn0(|Jn0%BNn0!|Nl z( zF!|qLVDf*;z~uj&fyw_B1C#$-2BrWb2BrXe2BrW<2BrWP2BrXa2Bv`J3`_xA7?=XK zF)#(}WMB%|!@v}1$-oro#=sQl!N3&g&A=4s$G{Z0kAW%hGy_xMSq7%S3k*zwml>FX zA{m&1(ioV6G8mYGvKg3y@)($cmM|~{ZDe2y+RVTdw2gr&XeR?x(0c}^pg#;uLH`(- zf*Bc@f>{`tg0mTzf-4x9f~y#qf@>L=f*Tl^f)6n;1)pbN3ckp|6nuq&Dfl`AQ-~`A zQ%Dd4Q%DE{Q%E=iQ%Dp8Q^*DerjWf1Od2By#-3`}9H3`}9{3`}8M3`}9X3`}A13`}7; z3`}8p3`}8#3`}7q3`}9i7?{E?Gcbi+Wnc=s!N3%Dn}I3Zhk+?Pl7T5ant>@ij)5sW zk%1|E1p`y~RtBcxeyBL_l_cAa=7&0(L*fB6gI503pI5RLsxG^wAOkrS(SjfN> zv6z7=Vi^Nd#7YLHNI?dsNI3?kNCgI_NM#15NHqqg$ZQ6t$O;Cg$SMY=$XW)b$OZrFfhe(Ffhf6GBCx8Gcd(UF)+o-GBCyFGcd*0Ffhf|F)+n8GBCxqFfhd)WnhZE z#K07Lg@GydIs;SeEe58zKnA9`I0mM;1O}$KWCo_VGzO-)?F>wDM;MsmjxjLBon&B& zJHx;fZ^Xb9Z_mIK@5sOu@4~Q{Cx(d_(u#(@lP3;;$JW@#lL1? zivPmE6#t!pDgGA&Q~VzWruhF1ObLt(ObId!ObN~mObLMuObL|?ObJa4ObI;f>XE87(&ShXqoX^0NxQl@)@gW0K;(G?BByk3&Bn1YhBwYrkBy$F) zBo_vzBwq%mqyPq{q+kZ7q%a1iq(}y)q$CEWq*Ml`qzne8q$~!eq+AB3qT(rN&6X?lAbd#C4FFEO8UjXl+41wlq|@=lq|)-lq}1@l&rwOl&s9al&r?Ul&sId zlx)Pnlx)htlx)txlx)Solx)MmlpM~$lw8Nal-$F>l)Qz3DS0mgQ}QVWrsOLOOvw)! zn37*HFeSfbU`qbLz?A%%fhqYL15@&U2Bs7y2Bs8N2Bs8t2Bs7)2Bs7q2Bs8s2Bs80 z2Bwr42Bwr22Bwr=2BwtR3`{A@8JJSGFfgU;WnfAS>r z(mpUSrTt`JN(0&XpMfczk%1|lg@GxZje#khpMfb|hJh(voq;Lcgn=pDmVqhVfq^OA znSm+Yje#lMlYuEch=D0Rlz}Ndf`KVLih(ITmVqffo`ESngMlf%h=D1+hJh))lYuFH z5(88ER0gK>84OJ6vl*Du=P@v)FK1v%U&X+bzLtS0eLVwH`X&aZ^eqfb>3bQN(vLGR zrC(rRO25m%l>Ur?Dg7k_Q~DbQru6p=OzEE(n9_eUFs1)vV9H=*V9H=-V9H=)V9MZN zV9HQtV9M}fV9JPKV9IDJIlx4=il;y<0l;zLBloi3iloiduloiLol$FT9l$FB3 zl$FcClvTjMlvT{YlvT>WlvTmNlvTyRl-10@l-0|?lr@WiDQhtUQ`RyDrmU3=Oj&Cf zn6lP0FlFsvV9MIfz?8L*fhp?%15?&v2Bxf|3`|)M7?`rz8JMy~8JMzd8JM!&7?`pH z7?`qS8JM!u7?`p%8JMzj7?`s28JMz*7?`rF8JM!`7?`pf8JM!08JM!$7?`p<7?`pr zGB9P&W?;%*!N8Qgk%1|D3j^lrh+0Pi5vOh2|W&dSh%3)(*%Hd>S%Hd&P%Hd~V$`N8<%8_PZ%8_GW%28xs z%28%u%28ur%F$q8%5i33%E@A2%Bf&r%2~j`l(Uk7DQ7bSQ_emHrkv9ZOgUE=m~yT& zFy-80V9L46z?AcVfhp%D15?f$2Bw_%3`{v68JKduFfiqOV_?b^WMIm*U|`C1V_?e7 zV_?cHXJE>0WMIneV_?di&cKvAi-9S3E(25U0tTks#SBcjYZ#bv*E2BXZen1{-NL|> zyPbh4cP9f=?r{dD+zSj$xwjaYavw7=<$hpb%Kgc}l>3K)Dfd4EQyvooQywb=Qyw1! zQ=T9LQ=SL|Q=S+DQ=TLPQ=T*fQ=Sgu{yGMx{EZAu`I{M-^3O3a<$q;hDqv<{D$r(NDllPSDzIZ&DyU&#DyU;%DwxT@RB(WS zso)#~Q^7X|rh@+rOocoQOogHhOod7eOoiGEOoe(3OofIFOob*4OoiqQOoa{%Ooh%2 zOoeU?Oobi{OoiSIOohG-Ood?#Ooa&yOoiDDOobH;OofdMOoc5BOoiWU@Dx)z*M-9fvIpY15@EX2ByNN3`~We8JLQs8JLPx7?_Im7?_Hz z8JLP(7?_IO8JLQ^7?_HD8JLO!7?_G88JLP<7?_IU8JLO^8JLPv7?_IE7?_G08JLPz zFfbKuV_+(}$G}wdoPnw6BLh>>KL(~^b_S+mE(WGzUIwOO0S2aGVFspR83v|ec?PCp zB?hKq6$Ykabq1zlO$Mf77Y3%{YzC&{N(QFlg$zu^s~DJyw=ggjA7o%EKE=RPe3pT! z_yPk{@nr_4;%f{{#rGMQiXSmB6+dNQDt^wuRQ!s8srU^8Qwci*Q;8k}Q;7`&Q%MQ~ zQ%NoZQ%N}kQ%OAoQ%Mg4Q^`~Yrji*9OeM1!m`dg`FqJH1U@BR~z*Mr9fvIEz15?Q+ z2Bwm&3``~48JJ2AFff&zWne0~&cIaign_B#Edx`@2L`5+&kRf@-x!!meljqXGBGff zvNAB0axgHJaxpNK@-i@$@-r}%N-!{$sxdH?8Z$7JIxsMmdNMGT`Y69W-~CAmM}1tHZU-ib~7-Q&R}3FUC6*xx`ctL zbU6c4=_&@M(zOgsrP~;oN_R3amF{6+D&5DxRCrZPVUrm`>wrm|!Prm`Fcrm}nnrm`Xirm|87rm_kKrm{u`rm_|Wrm}Vhrm{{3 zrm`Lerm{W;rn2b_Ol3~2Bxx03`}LW8JNmI zc0OcaDtpYpRQ8O4sq6&Oyx}sOy#W%Oy%tiOyv_8n9AoeFqN-lU@G6nz*N4UfvNlu z15^1?2Bz{83{2&x8JNniFff&0XJ9J7#lTd4hk>d5J_A$vLk6buR}4($Ul^Fm|1vOD za4|4d2s1EMh%qo#NHQ>0$S^Qf$TKiiXfQBUXfrTX=rJ%=7%(tZ7&9)H5(uG%+w$v@$SNv@MVrb;6Qrb<%=rb=@LrpjOjrpg!wrpkB*rphD+rpgos zrpi(Vrpi7Brpk#7OqG)vm@20+FjdZEV5+>%z*PBwfvNH(15*_T15*`015=eA15=d? z15;HX15;H915;Hv15;HL15;Hj15;Hz15;HF15;HV15;HY15;HA15?#e2BxY@3`|wG z8JMc>F)&pe|&Szk%E@EJ+E@fb< zu3%uQu4Z7Wu47=TZe(DpZed`mZf9Vsp25IWJ)424dM*P~^?U}V>O~Ap)k_(es#h>D zRj+1Xs$R>$RK1>osd^^^Q}q!Brs}&4Ox0f)m})o~m}=x0m}<-zm}>kPm};UKm}(jr zm}=S?m})v0m}(|7Fx4zzV5-^8z*Mu3fvM&o15?cr2BwaH^|)!k%Zs=LF$RCk|&sos!*sosu(sosHssot4^sosr&slJARslJne zslJOifh`Oii^6Oic|8Oiir}OidjOOikSkOijHEOii;Gn40D@Ff}b=U}{>*z|^#g zfvIUN15?um2BxM>3`|YC8JL<*FfcWpWngN$z`)dWnSrV41_M*mZ3d>MdkjoX4;Yx5 zUNSH>ePdv1`pv-9^pAn5nUR61nT>&|nUjI3nTLU?nU8^~S(<^VS%HD6S($;US&f0I zS(|~WS&xCK*^q&$*_eT;*@1zn*_DB**@JIe>wwIhcW|IgEj+If8+yIhBE_ zIfsF%IiG>4xrl+Oxs-vaxrTwMxt@Wkxru?Pxs`#bxt)Qjc^3my^Hm0>=64KC&EFZA zT9_D^T38vFS~wV(TDTdQTKE{4S_B!GT7(&xS`-+VTGSbsTC^CLT67thS_~MNTFe=k zTC5nDT5K7ZS{xXdTAUb|TCy3KT4pjZwH#$&YB|Ti)N+-9spSC!Q_EWhrj{=ZOf4XL zelak${AFNjWno}yWoKY&nK&0t_^EoES8tzlqlt!H3r zZDL?*ZDn9;?O|YQ?Pp+Woy5S@I+cN`bvgr6>jDO**5wRLts5AaT6Zuob;vR>bto_} zb!aj$b(k|Sb=Wa5b+|Gxbp$amb;L6;btEw`b)+&db!0Fwb!0Ozb>uNHbrdo%b<{C1 zbu=bS|k)XB`i)TztB)M>`R)M>}S z)alN^)ak{*)EUgc)EULV)EUpf)S1V?)LFp5)LF~G)Y-)?VCvklv83 zHZd@DvoJ7q3o>Uqh) z)SJe@)LYEJ)LY8H)LX&8)LYHK)O(VFsrNPmQ||)?rru`^V8FrzrWnjyAvBa^>U{wK DpXFjT literal 0 HcmV?d00001 diff --git a/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..e9cc5e8 --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme new file mode 100644 index 0000000..c7baf67 --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..858be9e --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + max-external.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 2FBBEAD608F335360078DB84 + + primary + + + + + diff --git a/source/cr.boardout/.DS_Store b/source/cr.boardout/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4592977f7e926c844dd01f09cd3cd53d4df1cd94 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8zj35RBCIAV8Fop~hRD=a&9@vb0hD3%Uh7^W;hEk~9 zV5dNC7iCChC}PlKNMb;Ce+h#gLj^-JLq0@C^;GeqaiRN zLV%IQCD_FYTq$GsFQ~2s)u#ziX;2*usg6MvGioD%2{NE40aXgB?m=2XG`K2eWMBYm R9c>7}LTHp84FURx0025WL8Jfx literal 0 HcmV?d00001 diff --git a/source/cr.boardout/cr.boardout.c b/source/cr.boardout/cr.boardout.c new file mode 100755 index 0000000..33df304 --- /dev/null +++ b/source/cr.boardout/cr.boardout.c @@ -0,0 +1,1054 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + + +// Firmata-for-Max Library headers +#include "maxlib.h" + +// required Max core headers +#include "ext.h" // standard Max include, always required +#include "ext_obex.h" // required for new style Max object + + +////////////////////////// object struct +typedef struct boardout +{ + // the Max object itself (must be first) + t_object ob; + + // Arduino board connection + board_t *board_ob; + + // selected pins to write + long pinsCount; + long pinsArray[TOTAL_OUT_PINS]; + long valuesArray[TOTAL_OUT_PINS]; + long modesArray[TOTAL_OUT_PINS]; + int port_index; + + // boardin attributes: + t_symbol *att_portname; + t_symbol *att_baudrate; // Sets the data rate in bits per second (baud) for serial data transmission. + // For communicating with the computer, one of these rates: 300, 600, 1200, + // 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200. + long att_digitalPins[TOTAL_OUT_PINS], digitalPinsCount; + long att_servoPins[TOTAL_OUT_PINS], servoPinsCount; + long att_pwmPins[TOTAL_OUT_PINS], pwmPinsCount; + + int isopen; + + + // hardware attributes: + //t_symbol *att_pin_modes[TOTAL_OUT_PINS]; + +} t_boardout; + +///////////////////////// function prototypes +//// standard set +void *boardout_new(t_symbol *s, long argc, t_atom *argv); +void boardout_free(t_boardout *x); +void boardout_assist(t_boardout *x, void *b, long m, long a, char *s); +void boardout_ports(t_boardout *x); +void boardout_pins(t_boardout *x); +void boardout_config(t_boardout *x); +void boardout_test(t_boardout *x); +void boardout_open(t_boardout *x); +void boardout_close(t_boardout *x); + +//privates +void boardout_printAvailPorts(t_boardout *x); +void boardout_updatePin(t_boardout *x, int index, int val); +void boardout_updateServo(t_boardout *x, int index, int val); +void boardout_updateDigital(t_boardout *x, int index, int val); +void boardout_updatePwm(t_boardout *x, int index, int val); +int boardout_parseArgs(t_boardout *x, long argc, t_atom *argv); +int boardout_getBaudRate(t_boardout *x); +bool boardout_pinNumAlreadyExists(t_boardout *x, int pin); +int boardout_pinDigitalRange(int pin); +int boardout_pinServoRange(int pin); +int boardout_pinPwmRange(int pin); +int boardout_getBoard(t_boardout *x, int len, int *pins, int *modes); +void boardout_init(t_boardout *x); +void boardout_pinNumConvert(int pin, char *str); +void boardout_disconnect(t_boardout *x, char *msg); + +void boardout_in1(t_boardout *x, long n); +void boardout_in2(t_boardout *x, long n); +void boardout_in3(t_boardout *x, long n); +void boardout_in4(t_boardout *x, long n); +void boardout_in5(t_boardout *x, long n); +void boardout_in6(t_boardout *x, long n); +void boardout_in7(t_boardout *x, long n); +void boardout_in8(t_boardout *x, long n); +void boardout_in9(t_boardout *x, long n); +void boardout_in10(t_boardout *x, long n); +void boardout_in11(t_boardout *x, long n); +void boardout_in12(t_boardout *x, long n); + +//////////////////////// global class pointer variable +void *boardout_class; + +void ext_main(void *r) +{ + t_class *c; + + c = class_new("cr.boardout", (method)boardout_new, (method)boardout_free, + (long)sizeof(t_boardout), + 0L /* leave NULL!! */, A_GIMME, 0); + + /* you CAN'T call this from the patcher */ + class_addmethod(c, (method)boardout_assist, "assist", A_CANT, 0); + class_addmethod(c, (method)boardout_ports, "ports", 0); + class_addmethod(c, (method)boardout_pins, "pins", 0); + class_addmethod(c, (method)boardout_config, "config", 0); + class_addmethod(c, (method)boardout_test, "test", 0); + class_addmethod(c, (method)boardout_open, "open", 0); + class_addmethod(c, (method)boardout_close, "close", 0); + + class_addmethod(c, (method)boardout_in1, "in1", A_LONG, 0); + class_addmethod(c, (method)boardout_in2, "in2", A_LONG, 0); + class_addmethod(c, (method)boardout_in3, "in3", A_LONG, 0); + class_addmethod(c, (method)boardout_in4, "in4", A_LONG, 0); + class_addmethod(c, (method)boardout_in5, "in5", A_LONG, 0); + class_addmethod(c, (method)boardout_in6, "in6", A_LONG, 0); + class_addmethod(c, (method)boardout_in7, "in7", A_LONG, 0); + class_addmethod(c, (method)boardout_in8, "in8", A_LONG, 0); + class_addmethod(c, (method)boardout_in9, "in9", A_LONG, 0); + class_addmethod(c, (method)boardout_in10, "in10", A_LONG, 0); + class_addmethod(c, (method)boardout_in11, "in11", A_LONG, 0); + class_addmethod(c, (method)boardout_in12, "in12", A_LONG, 0); + + class_register(CLASS_BOX, c); /* CLASS_NOBOX */ + boardout_class = c; + + post("Arduino UNO boardout object created (Writer)."); + +} + +void boardout_assist(t_boardout *x, void *b, long m, long a, char *s) +{ + if (m == ASSIST_INLET) { // inlet + + if(a == 0) { + sprintf(s, "inlet %ld - no pins", a); + } + else { + + char *str; + + // pin index is inlet-1 + int index = a-1; + int pin = x->pinsArray[index]; + + if(pin<14) str = ""; + else { + str = "A"; + pin -= 14; + } + + switch(x->modesArray[index]) { + + case MODE_OUTPUT: + sprintf(s, "pin %s%d mode DIGITAL values {0,1}", str, pin); + break; + case MODE_SERVO: + sprintf(s, "pin %s%d mode SERVO range [0-180] degrees", str, pin); + break; + case MODE_PWM: + sprintf(s, "pin D%d mode PWM range [0-255]", pin); + break; + default: + sprintf(s, "pin %s%d mode UNKNOWN", str, pin); + } + } + } + else { // outlet + sprintf(s, "I am outlet %ld", a); + } +} + +void boardout_free(t_boardout *x) +{ + char msg[4000]; + boardout_disconnect(x, msg); + if (DEBUG) post("boardout object deleted"); //debug + +} + +void *boardout_new(t_symbol *s, long argc, t_atom *argv) +{ + t_boardout *x = (t_boardout *)object_alloc(boardout_class); + + // verify memory allocated successfully + if(x == NULL) { + post( "Max is out of memory, could not create new boardout object."); + return NULL; + } + + boardout_init(x); // todo - remove, for debug + + x->board_ob = NULL; + x->port_index = -1; + + // parse input arguments to set up object + if (boardout_parseArgs(x, argc, argv) == -1) { + //Error - Invalid arguments + object_error((t_object *)x, "Boardout args are invalid, could not create new boardout object."); + //object_free(x); + return (x); + } + + + /* init all pin modes to DIGITAL MODE at first + for (int i = 0; i < TOTAL_OUT_PINS; ++i) { + x->att_pin_modes[i] = gensym("DIGITAL"); + }*/ + + // debug: + //if(x->att_portname && x->att_baudrate) { + // object_post((t_object *)x, "port name: %s\t rate: %d", x->att_portname->s_name, atoi(x->att_baudrate->s_name)); + //} + + // create pin integer inlets + for (int i = x->pinsCount; i > 0; --i) { + // create int inlets from right to left (highest-right to 1-left): + intin(x, i); + } + + // boardout object is now all set up + return (x); +} + +void boardout_ports(t_boardout *x){ + boardout_printAvailPorts(x); +} + +void boardout_config(t_boardout *x) { + + char str[4000], str_pin[10]; + long count, *arr; + + str[0] = '\0'; + + // Digital pins + count = x->digitalPinsCount; + arr = x->att_digitalPins; + if(count) { + str[0] = '\0'; + strcat(str, "Digital pins: "); + for (long i=0; iservoPinsCount; + arr = x->att_servoPins; + if(count) { + str[0] = '\0'; + strcat(str, "Servo pins: "); + for (long i=0; ipwmPinsCount; + arr = x->att_pwmPins; + if(count) { + str[0] = '\0'; + strcat(str, "PWM pins: "); + for (long i=0; iboard_ob) { + object_post((t_object *)x, "test: board is not connected."); + return; + } + + if ( ! x->board_ob->isBoardReady) { + object_post((t_object *)x, "test: board is not ready for communication."); + return; + } + + if( ! x->isopen) { + object_post((t_object *)x, "test: board is not connected."); + return; + } + object_post((t_object *)x, "test: board is connected."); + + char *firmware = firmataclient_getfirmware(x->board_ob); + object_post((t_object *)x, "test: firmware version %s.", firmware); + object_post((t_object *)x, "test: board is ready for communication."); + + object_post((t_object *)x, "test: onboard LED will now blink 3 times."); + firmataclient_blink(x->board_ob); + + object_post((t_object *)x, "test: Done."); +} + +void boardout_open(t_boardout *x){ + if (x == NULL) return; + + if(x->isopen) { + object_post((t_object *)x, "board is already connected."); + return; + } + + if ( ! x->att_baudrate) { + // set baud rate to default + char str[20]; + sprintf(str, "%d", DEFAUTLT_BAUD); + x->att_baudrate = gensym(str); + } + if(DEBUG) object_post((t_object *)x, "baud rate %s.", x->att_baudrate->s_name); + + // get updated list of available ports + char *ports_avail[MAX_PORTS]; + int num_ports = serial_portlist(ports_avail, MAX_PORTS); + + if (x->port_index == -1) { + object_error((t_object *)x, "port not selected."); + return; + } + if(DEBUG) object_post((t_object *)x, "index of port selected %d.", x->port_index); + + if (x->port_index < 0 || x->port_index >= num_ports) { + object_error((t_object *)x, "port selected is out of bounds of available ports."); + if(DEBUG) object_error((t_object *)x, "index of port selected %d.", x->port_index); + return; + } + + // select the port + x->att_portname = gensym(ports_avail[x->port_index]); + if(x->att_portname == NULL) { + if(DEBUG) object_error((t_object *)x, "port can't be found, internal error."); + return; + } + if(DEBUG) object_post((t_object *)x, "port selected %s to open", x->att_portname->s_name); // debug + + // check for other connections + if(x->att_portname->s_thing == NULL) { + + // the port is not in use by other boardin/out objects + if(DEBUG) object_post((t_object *)x, "port has no other connections, %s", x->att_portname->s_name); // debug + board_mgr_t *mgr = (board_mgr_t*)malloc(sizeof(board_mgr_t)); + if(mgr == NULL) { + object_error((t_object *)x, "port can't be opened, Max can't allocate memory."); + return; + } + mgr->board_out = NULL; + mgr->board_in = NULL; + mgr->board = NULL; + x->att_portname->s_thing = (t_object*)mgr; + } + else { + // the port is in use by another boardin/out object + if(DEBUG) object_post((t_object *)x, "OK, port has another connection, %s", x->att_portname->s_name); // debug + } + + + // set board configuration + int len_config = x->digitalPinsCount + x->servoPinsCount + x->pwmPinsCount; + int pins[len_config]; + int modes[len_config]; + + int len = 0; + // digital pins + for(int i=0; idigitalPinsCount; ++i) { + pins[len] = x->att_digitalPins[i]; + modes[len] = MODE_OUTPUT; + len++; + } + // servo pins + for(int i=0; iservoPinsCount; ++i) { + pins[len] = x->att_servoPins[i]; + modes[len] = MODE_SERVO; + len++; + } + // pwm pins + for(int i=0; ipwmPinsCount; ++i) { + pins[len] = x->att_pwmPins[i]; + modes[len] = MODE_PWM; + len++; + } + + //if(DEBUG) object_post((t_object *)x, "%d pins allocated", len); // debug + //if (DEBUG) { + // for(int i=0; iboard_ob, len_config, pins, modes, OUT_BOARD); + if( err ) { + object_error((t_object *)x, "open: Boardout couldn't connect to board."); + if(x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + x->isopen = 1; + object_post((t_object *)x, "open: boardout is now connected."); +} + +void boardout_close_orig(t_boardout *x) { + + if(x == NULL) return; + + if( ! x->isopen) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + if ( ! x->att_portname) { + object_post((t_object *)x, "close: no port selected."); + return; + } + + if(x->att_portname->s_thing == NULL) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if(mgr->board_out == NULL) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + x->isopen = 0; + mgr->board_out = NULL; + object_post((t_object *)x, "close: diconnected from board."); + + + if(mgr->board_in == NULL) { + + // no reader board is connected, release the port altogether + int err = maxclient_shutdown(x->board_ob); // close port + if(err == -1) { + object_error((t_object *)x, "close: error shutting serial port down"); + if (x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + if (DEBUG) object_post((t_object *)x, "close: port %s was shut down", x->att_portname->s_name); + + x->att_portname->s_thing = NULL; + maxclient_freemgr(mgr); + } + + return; +} + +void boardout_close(t_boardout *x) { + char msg[4000]; + boardout_disconnect(x, msg); + object_post((t_object *)x, "%s", msg); +} + +void boardout_disconnect(t_boardout *x, char *msg) { + + if( !x || ! x->isopen) { + sprintf(msg, "close: board was not connected."); + return; + } + + if ( ! x->att_portname) { + sprintf(msg, "close: no port selected."); + return; + } + + if(x->att_portname->s_thing == NULL) { + sprintf(msg, "close: board was not connected."); + return; + } + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if(mgr->board_out == NULL) { + sprintf(msg, "close: board was not connected."); + return; + } + + x->isopen = 0; + mgr->board_out = NULL; + + + if(mgr->board_in == NULL) { + + // no reader board is connected, release the port altogether + int err = maxclient_shutdown(x->board_ob); // close port + if(err == -1) { + sprintf(msg, "close: error shutting serial port down"); + if (x->board_ob) asprintf(&msg, "%s", x->board_ob->err_msg); + return; + } + + if (DEBUG) object_post((t_object *)x, "close: port %s was shut down", x->att_portname->s_name); + + x->att_portname->s_thing = NULL; + maxclient_freemgr(mgr); + } + + sprintf(msg, "close: boardout is closed."); + return; +} + + +void boardout_updatePin(t_boardout *x, int inlet, int val) { + + char *str; + + // pin index is inlet-1 + int index = inlet-1; + + switch(x->modesArray[index]) { + case MODE_OUTPUT: + boardout_updateDigital(x, index, val); + str = "DIGITAL"; + break; + case MODE_SERVO: + boardout_updateServo(x, index, val); + str = "SERVO"; + break; + case MODE_PWM: + boardout_updatePwm(x, index, val); + str = "PWM"; + break; + default: + str = "UNKNOWN"; + post("unidentified mode selected"); // error + } + + if (DEBUG) object_post((t_object *)x, "%s pin %ld: %ld", str, x->pinsArray[index], x->valuesArray[index]); //debug + +} + +void boardout_updateDigital(t_boardout *x, int index, int val) { + val = (val==0? 0 : 1); + x->valuesArray[index] = val; + int pin = x->pinsArray[index]; + maxclient_toggleDigital(x->board_ob, pin, val); +} + +void boardout_updateServo(t_boardout *x, int index, int val) { + x->valuesArray[index] = val; + int pin = x->pinsArray[index]; + maxclient_moveServo(x->board_ob, pin, val); +} + +void boardout_updatePwm(t_boardout *x, int index, int val) { + x->valuesArray[index] = val; + int pin = x->pinsArray[index]; + maxclient_movePwm(x->board_ob, pin, val); +} + + +/* + PRIVATE INTERNAL FUNCTIONS + */ + +int boardout_parseArgs(t_boardout *x, long argc, t_atom *argv) +{ + + x->port_index = -1; + + // set baud rate to default + char str[20]; + sprintf(str, "%d", DEFAUTLT_BAUD); + x->att_baudrate = gensym(str); + + // parse input arguments + int i = 0; + + // check if the first argument is the port index + if(argv->a_type == A_SYM){ + char *portCh = atom_getsym(argv)->s_name; + //object_post((t_object *)x, "port char: %c", portCh[0]); //debug + int index = portCh[0]-'a'; + //object_post((t_object *)x, "port index: %d", index); //debug + + if (index >= 0 && index <= 'z'-'a') { + x->port_index = index; + i = 1; + } + } + + // Parse DIGITAL SERVO PWM pins lists + bool servoFound = false, digitalFound = false, pwmFound = false; + x->pinsCount = 0; + x->pwmPinsCount = 0; + x->servoPinsCount = 0; + x->digitalPinsCount = 0; + + for (; i < argc; i++) { + + if((argv+i)->a_type == A_SYM) { + + t_symbol *s = atom_getsym(argv+i); + + // if the symbol is digital + if( ! strcmp_case_insensitive(s->s_name, "@digital")) { + //post("Found DIGITAL"); + digitalFound = true; + servoFound = false; + pwmFound = false; + } + // if symbol is servo + else if( ! strcmp_case_insensitive(s->s_name, "@servo")) { + //post("Found SERVO"); + servoFound = true; + digitalFound = false; + pwmFound = false; + } + // if symbol is pwm + else if( ! strcmp_case_insensitive(s->s_name, "@pwm")) { + //post("Found PWM"); + pwmFound = true; + servoFound = false; + digitalFound = false; + } + // if symbol is baud + else if( ! strcmp_case_insensitive(s->s_name, "@baud")) { + //post("Found BAUD"); + digitalFound = false; + servoFound = false; + pwmFound = false; + // check if the baud rate was entered as input + if((argv+i+1)->a_type == A_LONG) { + // move to the next input atom for the baud rate + i++; + //x->baudrate = atom_getlong(argv+i); + } + } + else if( ! strcmp_case_insensitive(s->s_name, "A0")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 14; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A1")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 15; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A2")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 16; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A3")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 17; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A4")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 18; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A5")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 19; + argv[i] = a[0]; + --i; + } + else { + // otherwise, unknown attribute symbol + digitalFound = false; + servoFound = false; + pwmFound = false; + // error + object_error((t_object *)x, "forbidden argument %s", s->s_name); + //return -1; + } + } + + else if((argv+i)->a_type == A_LONG) { + + int pin = atom_getlong(argv+i); + + if((servoFound || digitalFound || pwmFound) && x->pinsCount < TOTAL_OUT_PINS) { + + if(servoFound) { + if( ! boardout_pinServoRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, servo pins range [2-13, A0-A5]", pin); + return 0; + } + } + + if(pwmFound && !boardout_pinPwmRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, pwm pins range [3,5,6,9,10,11]", pin); + return 0; + } + + if(digitalFound && !boardout_pinDigitalRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, digital pins range [0-13, A0-A5]", pin); + return 0; + } + + // also check if the requested pin already exists + if(boardout_pinNumAlreadyExists(x, pin)) { + // error + object_error((t_object *)x, "forbidden argument, pin %d already exists", pin); + } + + // we verified the pin does not exist, add it now + else { + x->pinsArray[x->pinsCount] = pin; + x->valuesArray[x->pinsCount] = 0; + if(servoFound) { + x->modesArray[x->pinsCount] = MODE_SERVO; + x->att_servoPins[x->servoPinsCount] = pin; + x->servoPinsCount++; + } + else if(digitalFound) { + x->modesArray[x->pinsCount] = MODE_OUTPUT; + x->att_digitalPins[x->digitalPinsCount] = pin; + x->digitalPinsCount++; + } + else if(pwmFound) { + x->modesArray[x->pinsCount] = MODE_PWM; + x->att_pwmPins[x->pwmPinsCount] = pin; + x->pwmPinsCount++; + } + + x->pinsCount++; + } + } + + else { + // error + if(x->pinsCount >= TOTAL_OUT_PINS) { + object_error((t_object *)x, "forbidden argument, too many pins requested"); + //return -1; + } + object_error((t_object *)x, "forbidden argument, number %d is out of context", pin); + //return -1; + } + } + + else { + // error + object_error((t_object *)x, "forbidden argument, expected format:"); + object_error((t_object *)x, "[port] [@digital pins_list] [@servo pins_list] [@pwm pins_list]"); + //return -1; + + } + } + return 0; +} + +void boardout_printAvailPorts(t_boardout *x) { + + char *ports_avail[MAX_PORTS]; + + int num_ports = serial_portlist(ports_avail, MAX_PORTS); + + for(int i=0; idigitalPinsCount; ++i) { + if(x->att_digitalPins[i] == pin) + return true; + } + + for (int i = 0; iservoPinsCount; ++i) { + if(x->att_servoPins[i] == pin) + return true; + } + + for (int i = 0; ipwmPinsCount; ++i) { + if(x->att_pwmPins[i] == pin) + return true; + } + + return false; +} + +int boardout_getBoard(t_boardout *x, int len, int *pins, int *modes) { + + if(!x || !x->att_portname || !x->att_portname->s_thing) return -1; + if( !pins || !modes) return -1; + + // search symbol port_name. if found, then board is already connected to by a boardin + // object (reader object), then get the pointer to that board object. Otherwise, create + // a new board object and initialise it with the requested baud rate and the rest of + // the pins (write) configuration. + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if (mgr == NULL) { + if (DEBUG) object_error((t_object *)x, "can't connect to port, internal error"); + return -1; + } + + if(mgr->board_out != NULL) { + // error - there is already another boardout object connected + object_error((t_object *)x, "open: port %s is already occupied with another boardout (writer) object!", x->att_portname->s_name); + return -1; + } + + else if(mgr->board_in != NULL) { + + if (DEBUG) object_post((t_object *)x, "open: port %s has another connection to boardin (okay)", x->att_portname->s_name); // debug + + if(mgr->board == NULL) { + object_error((t_object *)x, "open: can't connect the board. internal error."); + return -1; + } + + // check baud rate conflict (between existing and the new board) + //if(x->att_baudrate) { + // long current_rate = atol(x->att_baudrate->s_name); + // if(mgr->board->port) { + // long port_rate = mgr->board->port->baud_rate; + // if(port_rate != current_rate) { + // object_error((t_object *)x, "open: can't connect the board, baud rate conflict. "); + // object_error((t_object *)x, "port baud rate %ld, but baud %ld requested. ", mgr->board->port->baud_rate, current_rate); + // return -1; + // } + // } + // } + + // check if there is any configuration conflict + if (maxclient_verifyconfig(mgr->board, len, pins, modes)) { + object_error((t_object *)x, "open: can't connect the board. "); + object_error((t_object *)x, "%s", mgr->board->err_msg); + return -1; + } + + x->board_ob = mgr->board; + mgr->board_out = x; + } + + else { + + // requested port is not connected to by any other board object, then + // create a new Arduino board object, initialise and connect to (serial) port + + if (DEBUG) object_post((t_object *)x, "open: port is not connected to by any other board object yet, establishing a new connection."); // debug + + char err_return[4000]; + long rate = x->att_baudrate? atol(x->att_baudrate->s_name) : DEFAUTLT_BAUD; + x->board_ob = maxclient_newboard(x->att_portname->s_name, rate, err_return); + if(x->board_ob == NULL) { + // error there is already another boardout object connected + object_error((t_object *)x, "Max could not create new boardout object."); + object_error((t_object *)x, "%s", err_return); + return -1; + } + + //board_mgr_t *mgr = (board_mgr_t*)malloc(sizeof(board_mgr_t)); + mgr->board = x->board_ob; + mgr->board_out = x; + } + + return 0; +} + +int boardout_pinDigitalRange(int pin) { + return ((pin < 20 && pin >=0)? 1 : 0); +} + +int boardout_pinServoRange(int pin) { + return ((pin < 20 && pin >=2)? 1 : 0); +} + +int boardout_pinPwmRange(int pin) { + // range = {3, 5, 6, 9, 10, 11}; + return ((pin==3 || pin==5 || pin==6 || pin==9 || pin==10 || pin==11)? 1 : 0); +} + +void boardout_init(t_boardout *x) { + + if(x == NULL) + return; + + x->pinsCount = 0; + x->att_portname = NULL; + x->att_baudrate = NULL; + x->digitalPinsCount = 0; + x->pwmPinsCount = 0; + x->servoPinsCount = 0; + x->isopen = 0; +} + +/* + INTERNALS + */ + +t_max_err boardout_digitalPins_get(t_boardout *x, t_object *attr, long *argc, t_atom **argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_digitalPins_set(t_boardout *x, t_object *attr, long argc, t_atom *argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_servoPins_get(t_boardout *x, t_object *attr, long *argc, t_atom **argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_servoPins_set(t_boardout *x, t_object *attr, long argc, t_atom *argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_pwmPins_get(t_boardout *x, t_object *attr, long argc, t_atom *argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_pwmPins_set(t_boardout *x, t_object *attr, long argc, t_atom *argv) { + return MAX_ERR_NONE; +} + +void boardout_in1(t_boardout *x, long n) { + boardout_updatePin(x, 1, n); +} + +void boardout_in2(t_boardout *x, long n) { + boardout_updatePin(x, 2, n); +} + +void boardout_in3(t_boardout *x, long n) { + boardout_updatePin(x, 3, n); +} + +void boardout_in4(t_boardout *x, long n) { + boardout_updatePin(x, 4, n); +} + +void boardout_in5(t_boardout *x, long n) { + boardout_updatePin(x, 5, n); +} + +void boardout_in6(t_boardout *x, long n) { + boardout_updatePin(x, 6, n); +} + +void boardout_in7(t_boardout *x, long n) { + boardout_updatePin(x, 7, n); +} + +void boardout_in8(t_boardout *x, long n) { + boardout_updatePin(x, 8, n); +} + +void boardout_in9(t_boardout *x, long n) { + boardout_updatePin(x, 9, n); +} + +void boardout_in10(t_boardout *x, long n) { + boardout_updatePin(x, 10, n); +} + +void boardout_in11(t_boardout *x, long n) { + boardout_updatePin(x, 11, n); +} + +void boardout_in12(t_boardout *x, long n) { + boardout_updatePin(x, 12, n); +} + +void boardout_pinNumConvert(int pin, char *str) { + if( ! str) { + str = ""; + return; + } + if(pin < 14) sprintf(str, "%d", pin); + else sprintf(str, "A%d", pin-14); + return; +} + + +// Servo.Write() writes a value to the servo, controlling the shaft accordingly. On a standard servo, this will set the angle of the shaft (in degrees), moving the shaft to that orientation. On a continuous rotation servo, this will set the speed of the servo (with 0 being full-speed in one direction, 180 being full speed in the other, and a value near 90 being no movement). + +// PWM: call analogWrite(pin, dutyCycle), where dutyCycle is a value from 0 to 255. diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.pbxproj b/source/cr.boardout/cr.boardout.xcodeproj/project.pbxproj new file mode 100755 index 0000000..a304117 --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/project.pbxproj @@ -0,0 +1,236 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0C91F5A91F73957300EF94C5 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C91F5A81F73957200EF94C5 /* CoreFoundation.framework */; }; + 0C91F5AB1F73958100EF94C5 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C91F5AA1F73958100EF94C5 /* IOKit.framework */; }; + 0CDDA8CB2022D8B500358057 /* firmatalib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8C52022D8B500358057 /* firmatalib.h */; }; + 0CDDA8CC2022D8B500358057 /* firmatalib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8C62022D8B500358057 /* firmatalib.c */; }; + 0CDDA8CD2022D8B500358057 /* maxlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8C72022D8B500358057 /* maxlib.h */; }; + 0CDDA8CE2022D8B500358057 /* serial.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8C82022D8B500358057 /* serial.c */; }; + 0CDDA8CF2022D8B500358057 /* serial.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8C92022D8B500358057 /* serial.h */; }; + 0CDDA8D02022D8B500358057 /* maxlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8CA2022D8B500358057 /* maxlib.c */; }; + 22CF11AE0EE9A8840054F513 /* cr.boardout.c in Sources */ = {isa = PBXBuildFile; fileRef = 22CF11AD0EE9A8840054F513 /* cr.boardout.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0C91F5A81F73957200EF94C5 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 0C91F5AA1F73958100EF94C5 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 0CDDA8C52022D8B500358057 /* firmatalib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firmatalib.h; path = ../FirmataClient/firmatalib.h; sourceTree = ""; }; + 0CDDA8C62022D8B500358057 /* firmatalib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = firmatalib.c; path = ../FirmataClient/firmatalib.c; sourceTree = ""; }; + 0CDDA8C72022D8B500358057 /* maxlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = maxlib.h; path = ../FirmataClient/maxlib.h; sourceTree = ""; }; + 0CDDA8C82022D8B500358057 /* serial.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = serial.c; path = ../FirmataClient/serial.c; sourceTree = ""; }; + 0CDDA8C92022D8B500358057 /* serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = serial.h; path = ../FirmataClient/serial.h; sourceTree = ""; }; + 0CDDA8CA2022D8B500358057 /* maxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = maxlib.c; path = ../FirmataClient/maxlib.c; sourceTree = ""; }; + 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = maxmspsdk.xcconfig; path = ../maxmspsdk.xcconfig; sourceTree = SOURCE_ROOT; }; + 22CF11AD0EE9A8840054F513 /* cr.boardout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cr.boardout.c; sourceTree = ""; }; + 2FBBEAE508F335360078DB84 /* cr.boardout.mxo */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cr.boardout.mxo; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2FBBEADC08F335360078DB84 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C91F5AB1F73958100EF94C5 /* IOKit.framework in Frameworks */, + 0C91F5A91F73957300EF94C5 /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* iterator */ = { + isa = PBXGroup; + children = ( + 0CDDA8C52022D8B500358057 /* firmatalib.h */, + 0CDDA8C62022D8B500358057 /* firmatalib.c */, + 0CDDA8C72022D8B500358057 /* maxlib.h */, + 0CDDA8C82022D8B500358057 /* serial.c */, + 0CDDA8C92022D8B500358057 /* serial.h */, + 0CDDA8CA2022D8B500358057 /* maxlib.c */, + 22CF11AD0EE9A8840054F513 /* cr.boardout.c */, + 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */, + 19C28FB4FE9D528D11CA2CBB /* Products */, + 0C91F5A71F73957200EF94C5 /* Frameworks */, + ); + name = iterator; + sourceTree = ""; + }; + 0C91F5A71F73957200EF94C5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0C91F5AA1F73958100EF94C5 /* IOKit.framework */, + 0C91F5A81F73957200EF94C5 /* CoreFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 19C28FB4FE9D528D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 2FBBEAE508F335360078DB84 /* cr.boardout.mxo */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2FBBEAD708F335360078DB84 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CDDA8CB2022D8B500358057 /* firmatalib.h in Headers */, + 0CDDA8CF2022D8B500358057 /* serial.h in Headers */, + 0CDDA8CD2022D8B500358057 /* maxlib.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 2FBBEAD608F335360078DB84 /* max-external */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2FBBEAE008F335360078DB84 /* Build configuration list for PBXNativeTarget "max-external" */; + buildPhases = ( + 2FBBEAD708F335360078DB84 /* Headers */, + 2FBBEAD808F335360078DB84 /* Resources */, + 2FBBEADA08F335360078DB84 /* Sources */, + 2FBBEADC08F335360078DB84 /* Frameworks */, + 2FBBEADF08F335360078DB84 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "max-external"; + productName = iterator; + productReference = 2FBBEAE508F335360078DB84 /* cr.boardout.mxo */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + }; + buildConfigurationList = 2FBBEACF08F335010078DB84 /* Build configuration list for PBXProject "cr.boardout" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 089C166AFE841209C02AAC07 /* iterator */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2FBBEAD608F335360078DB84 /* max-external */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2FBBEAD808F335360078DB84 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + 2FBBEADF08F335360078DB84 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2FBBEADA08F335360078DB84 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CDDA8CE2022D8B500358057 /* serial.c in Sources */, + 0CDDA8CC2022D8B500358057 /* firmatalib.c in Sources */, + 0CDDA8D02022D8B500358057 /* maxlib.c in Sources */, + 22CF11AE0EE9A8840054F513 /* cr.boardout.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 2FBBEAD008F335010078DB84 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Development; + }; + 2FBBEAD108F335010078DB84 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Deployment; + }; + 2FBBEAE108F335360078DB84 /* Development */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_LDFLAGS = "$(C74_SYM_LINKER_FLAGS)"; + PRODUCT_BUNDLE_IDENTIFIER = boardout; + PRODUCT_NAME = cr.boardout; + }; + name = Development; + }; + 2FBBEAE208F335360078DB84 /* Deployment */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = YES; + OTHER_LDFLAGS = "$(C74_SYM_LINKER_FLAGS)"; + "OTHER_LDFLAGS[arch=*]" = "$(C74_SYM_LINKER_FLAGS)"; + PRODUCT_BUNDLE_IDENTIFIER = boardout; + PRODUCT_NAME = cr.boardout; + }; + name = Deployment; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2FBBEACF08F335010078DB84 /* Build configuration list for PBXProject "cr.boardout" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2FBBEAD008F335010078DB84 /* Development */, + 2FBBEAD108F335010078DB84 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2FBBEAE008F335360078DB84 /* Build configuration list for PBXNativeTarget "max-external" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2FBBEAE108F335360078DB84 /* Development */, + 2FBBEAE208F335360078DB84 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ea47bfd --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..54782e3 --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..23adeed508256cd50cf703457d3c880f401dedf9 GIT binary patch literal 48395 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 + + + + BuildLocationStyle + UseAppPreferences + CustomBuildLocationType + RelativeToDerivedData + DerivedDataLocationStyle + Default + IssueFilterStyle + ShowActiveSchemeOnly + LiveSourceIssuesEnabled + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..fe2b454 --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme new file mode 100644 index 0000000..c6b50ab --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..d70c3e6 --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SchemeUserState + + max-external.xcscheme + + isShown + + orderHint + 0 + + + SuppressBuildableAutocreation + + 2FBBEAD608F335360078DB84 + + primary + + + + + diff --git a/source/maxmspsdk.xcconfig b/source/maxmspsdk.xcconfig new file mode 100755 index 0000000..0c126a9 --- /dev/null +++ b/source/maxmspsdk.xcconfig @@ -0,0 +1,70 @@ +// Xcode target configuration settings for the Max 6 SDK +// Used as the basis for Xcode projects to build Max externals. +// +// Changes to the settings in this file will be applied to all SDK examples +// To change settings for only one of the examples, override the settings using +// Xcode's target inspector. +// +// by Timothy Place +// Copyright © 2012, Cycling '74 + + +// Name & Version +PRODUCT_NAME = $(PROJECT_NAME) +PRODUCT_VERSION = 7.0.1 +ARCHS = i386 x86_64 + +// Paths +C74SUPPORT = /Applications/Max.app/Contents/Resources/C74/packages/max-sdk-7.3.3/source/c74support +HEADER_SEARCH_PATHS = "$(C74SUPPORT)/max-includes" "$(C74SUPPORT)/msp-includes" "$(C74SUPPORT)/jit-includes" +FRAMEWORK_SEARCH_PATHS = "$(C74SUPPORT)/max-includes" "$(C74SUPPORT)/msp-includes" "$(C74SUPPORT)/jit-includes" +DSTROOT = $(SRCROOT)/../../cr/externals +// (This next path is relative to DSTROOT) +INSTALL_PATH = / + + +// Special Files +GCC_PREFIX_HEADER = $(C74SUPPORT)/max-includes/macho-prefix.pch +INFOPLIST_FILE = $(SRCROOT)/../Info.plist + + +// Architecture and Deployment +ARCHS = i386 x86_64 + +// The following section sets the Mac SDK version to be used. +// For most projects this has little to no impact because there are no direct dependencies on OS function calls. +// In those projects with OS function calls, it should be okay to use the most recent SDK version because the +// MACOSX_DEPLOYMENT_TARGET will disable functionality that is unavailable in the older target OS. +// For this reason, the SDKROOT variable is commented out, telling Xcode to use the default (which is the most recent SDK). +// +// SDKROOT = macosx10.6 + +MACOSX_DEPLOYMENT_TARGET = 10.7 + + +// Compiler Version -- leave them all commented out to get the default version provided by Xcode +// GCC_VERSION = com.apple.compilers.llvmgcc42 +// GCC_VERSION = com.apple.compilers.llvm.clang.1_0 + + +// Preprocessor Defines +GCC_PREPROCESSOR_DEFINITIONS = "DENORM_WANT_FIX = 1" "NO_TRANSLATION_SUPPORT = 1" + + +// Static Configuration (don't change these) +WRAPPER_EXTENSION = mxo; +WARNING_CFLAGS = -Wmost -Wno-four-char-constants -Wno-unknown-pragmas +DEPLOYMENT_LOCATION = YES +GENERATE_PKGINFO_FILE = YES + + +// Flags to enforce some build-time checks for the symbols used while not actually performing a hard link +C74_SYM_LINKER_FLAGS = @$(C74SUPPORT)/max-includes/c74_linker_flags.txt + + +// hide all symbols by default +// mark a function to be exported with the C74_EXPORT macro +// most likely this will only apply to the ext_main() function which we've already prototyped for you +OTHER_CFLAGS = -fvisibility=hidden + +OTHER_LDFLAGS = -framework MaxAudioAPI -framework JitterAPI $(C74_SYM_LINKER_FLAGS) diff --git a/source/readme.md b/source/readme.md new file mode 100755 index 0000000..200287a --- /dev/null +++ b/source/readme.md @@ -0,0 +1,22 @@ +© Copyright The University of New South Wales 2018 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see http://www.gnu.org/licenses/. / + + + +The folder “c74support” was borrowed from the Max/MSP SDK by Cycling ’74, and it contains necessary header files. The project looks for these header files by relative location, therefore “c74support” must be in the same directory as the project folder. + +The compiled external (.mxo) will be created in the cr package “externals” folder. Make sure the “cr\externals” folder is in the Max Search Path. + + -- GitLab