From 1fcc7ff6a9242040589c63ac8e0edf2c20941d3e Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Wed, 4 Sep 2024 17:37:03 +0800 Subject: [PATCH 01/19] TEST123 --- src/main/java/{Duke.java => PlopBot.java} | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) rename src/main/java/{Duke.java => PlopBot.java} (74%) diff --git a/src/main/java/Duke.java b/src/main/java/PlopBot.java similarity index 74% rename from src/main/java/Duke.java rename to src/main/java/PlopBot.java index 5d313334c..a8086f860 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/PlopBot.java @@ -1,4 +1,4 @@ -public class Duke { +public class PlopBot { public static void main(String[] args) { String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" @@ -6,5 +6,12 @@ public static void main(String[] args) { + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; System.out.println("Hello from\n" + logo); + + static void welcomeMessage() { + + } + + welcomeMessage(); + } } From 94dd0c42400fd8fc3322b34b854a981a69bf706d Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Wed, 11 Sep 2024 11:26:19 +0800 Subject: [PATCH 02/19] plopBot - Level 01: Echo // Implemented --- src/main/java/PlopBot.java | 47 ++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index a8086f860..7a4d8efae 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -1,17 +1,56 @@ +import java.util.Scanner; + public class PlopBot { + + // Static variables + private static final String name = "plopBot"; + private static final String HORIZONTAL_LINE = "//" + "\u2500".repeat(50); + private static final String ECHO_LINE = " " + "\u2500".repeat(50); + private static Scanner scanner = new Scanner(System.in); + public static void main(String[] args) { + /** String logo = " ____ _ \n" + "| _ \\ _ _| | _____ \n" + "| | | | | | | |/ / _ \\\n" + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; System.out.println("Hello from\n" + logo); + */ + welcomeMessage(); + echo(); + } - static void welcomeMessage() { - - } + /** + * Welcome message when the user runs the program. + */ + static void welcomeMessage() { + System.out.println(HORIZONTAL_LINE); + System.out.println("Hello! I'm " + name + "."); + System.out.println("What can I do for you today?"); + System.out.println(""); + System.out.println(HORIZONTAL_LINE); + System.out.println(""); + System.out.println("Thank you for choosing " + name + ". Have a great day!"); + System.out.println(HORIZONTAL_LINE); + } - welcomeMessage(); + /** + * Echoes the users inputs. + */ + static void echo() { + String userInput; + + while (true) { + userInput = scanner.nextLine().trim(); + if (userInput.equalsIgnoreCase("exit")) { + break; + } + + System.out.println(ECHO_LINE); + System.out.println(" " + userInput); + System.out.println(ECHO_LINE); + } } } From 541db925f50816f79990881464bef47d933e53e0 Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Wed, 11 Sep 2024 11:59:57 +0800 Subject: [PATCH 03/19] plopBot - Level 02: Add, List // Implemented --- src/main/java/PlopBot.java | 39 ++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index 7a4d8efae..e56346279 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -1,13 +1,16 @@ import java.util.Scanner; +import java.util.ArrayList; public class PlopBot { // Static variables private static final String name = "plopBot"; private static final String HORIZONTAL_LINE = "//" + "\u2500".repeat(50); - private static final String ECHO_LINE = " " + "\u2500".repeat(50); + private static final String ECHO_LINE = " " + "\u2500".repeat(48); private static Scanner scanner = new Scanner(System.in); + private static ArrayList storedInputs; + public static void main(String[] args) { /** String logo = " ____ _ \n" @@ -18,6 +21,7 @@ public static void main(String[] args) { System.out.println("Hello from\n" + logo); */ welcomeMessage(); + storedInputs = new ArrayList<>(); echo(); } @@ -29,14 +33,15 @@ static void welcomeMessage() { System.out.println("Hello! I'm " + name + "."); System.out.println("What can I do for you today?"); System.out.println(""); + } + + static void farewellMessage() { System.out.println(HORIZONTAL_LINE); - System.out.println(""); System.out.println("Thank you for choosing " + name + ". Have a great day!"); - System.out.println(HORIZONTAL_LINE); } /** - * Echoes the users inputs. + * Echoes the user's inputs. */ static void echo() { String userInput; @@ -44,13 +49,31 @@ static void echo() { while (true) { userInput = scanner.nextLine().trim(); - if (userInput.equalsIgnoreCase("exit")) { + if (userInput.equalsIgnoreCase("bye") || userInput.equalsIgnoreCase("exit")) { + farewellMessage(); break; } + if (userInput.equalsIgnoreCase("list")) { + list(); + } + else { + storedInputs.add(userInput); + System.out.println(ECHO_LINE); + System.out.println(" Added: " + userInput); + System.out.println(ECHO_LINE); + } + + } + } - System.out.println(ECHO_LINE); - System.out.println(" " + userInput); - System.out.println(ECHO_LINE); + /** + * Lists the user's inputs. + */ + static void list() { + System.out.println(ECHO_LINE); + System.out.println(" Stored Inputs:\n"); + for (int i = 0; i < storedInputs.size(); i++) { + System.out.println(" " + (i + 1) + ". " + storedInputs.get(i)); } } } From db01444e0f1209577e150fef0839184e62d9d0bc Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Fri, 13 Sep 2024 16:54:23 +0800 Subject: [PATCH 04/19] plopBot - Level 03: Mark & Unmark // Implemented --- src/main/java/PlopBot.java | 111 +++++++++++++++++++++++++++---------- src/main/java/Task.java | 34 ++++++++++++ 2 files changed, 116 insertions(+), 29 deletions(-) create mode 100644 src/main/java/Task.java diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index e56346279..ea4eaecbf 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -9,20 +9,13 @@ public class PlopBot { private static final String ECHO_LINE = " " + "\u2500".repeat(48); private static Scanner scanner = new Scanner(System.in); - private static ArrayList storedInputs; + private static ArrayList tasks; + private static String userName; public static void main(String[] args) { - /** - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - */ welcomeMessage(); - storedInputs = new ArrayList<>(); - echo(); + tasks = new ArrayList<>(); + inputReader(); } /** @@ -31,39 +24,70 @@ public static void main(String[] args) { static void welcomeMessage() { System.out.println(HORIZONTAL_LINE); System.out.println("Hello! I'm " + name + "."); - System.out.println("What can I do for you today?"); - System.out.println(""); + System.out.println("What can I do for you today?\n"); } + /** + * Farewell message when the user ends the program. + */ static void farewellMessage() { System.out.println(HORIZONTAL_LINE); - System.out.println("Thank you for choosing " + name + ". Have a great day!"); + System.out.println("Thank you for choosing " + name + ". Have a great day!\n"); } /** - * Echoes the user's inputs. + * Reads the user's inputs and calls processInput() */ - static void echo() { + static void inputReader() { String userInput; while (true) { userInput = scanner.nextLine().trim(); + // processInput(userInput); - if (userInput.equalsIgnoreCase("bye") || userInput.equalsIgnoreCase("exit")) { - farewellMessage(); + if (processInput(userInput)) { break; } - if (userInput.equalsIgnoreCase("list")) { + } + + scanner.close(); + } + + /** + * Processes the user's inputs. + * @param userInput + */ + static boolean processInput(String userInput) { + if (userInput.toLowerCase().startsWith("mark") || userInput.toLowerCase().startsWith("unmark")) { + toggleMark(userInput); + return false; + } + + switch (userInput.toLowerCase()) { + case "list": list(); - } - else { - storedInputs.add(userInput); - System.out.println(ECHO_LINE); - System.out.println(" Added: " + userInput); - System.out.println(ECHO_LINE); - } + break; + case "help": + showHelp(); + break; + case "clear": + tasks.clear(); + System.out.println("All tasks cleared."); + break; + case "mark": + toggleMark(userInput); + break; + case "bye", "exit", "quit": + farewellMessage(); + return true; + default: + tasks.add(new Task(userInput)); + System.out.println(" Added: " + userInput); + break; } + + return false; } /** @@ -71,9 +95,38 @@ static void echo() { */ static void list() { System.out.println(ECHO_LINE); - System.out.println(" Stored Inputs:\n"); - for (int i = 0; i < storedInputs.size(); i++) { - System.out.println(" " + (i + 1) + ". " + storedInputs.get(i)); + System.out.println(" Your List:"); + for (int i = 0; i < tasks.size(); i++) { + Task task = tasks.get(i); + System.out.printf(" %d.%s %s%n", i + 1, task.getStatusIcon(), task.getName()); } + System.out.println(""); + } + + static void toggleMark(String userInput) { + try { + int taskNumber = Integer.parseInt(userInput.split(" ")[1]) - 1; + + if (taskNumber >= 0 && taskNumber < tasks.size()) { + Task task = tasks.get(taskNumber); + task.toggleStatus(); + System.out.println(ECHO_LINE); + System.out.printf(" Successfully updated task %d.\n", taskNumber + 1); + System.out.println(" Your Updated List:"); + + for (int i = 0; i < tasks.size(); i++) { + Task t = tasks.get(i); + System.out.printf(" %d.%s %s%n", i + 1, t.getStatusIcon(), t.getName()); + } + } + } + catch (NumberFormatException | ArrayIndexOutOfBoundsException e){ + System.out.println("Invalid command. Please use 'mark '."); + } + } + + static void showHelp() { + System.out.println(""); } } + diff --git a/src/main/java/Task.java b/src/main/java/Task.java new file mode 100644 index 000000000..4c94f4f97 --- /dev/null +++ b/src/main/java/Task.java @@ -0,0 +1,34 @@ +public class Task { + protected String taskName; + protected boolean isDone; + + /** + * Basic Task constructor + * @param taskName + */ + public Task(String taskName) { + this.taskName = taskName; + this.isDone = false; + } + + /** + * Returns String depicting whether the task is finished or not. + * @return + */ + public String getStatusIcon() { + return (isDone ? "[X]" : "[ ]"); + } + + public String getName() { + return taskName; + } + + public boolean getStatus() { + return isDone; + } + + public void toggleStatus() { + this.isDone = !this.isDone; + } +} + From 65db2a27277390af4f07578e208d10d56b96c574 Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Fri, 13 Sep 2024 19:05:59 +0800 Subject: [PATCH 05/19] plopBot - Level 04: ToDos, Events, Deadlines // Implemented --- src/main/java/PlopBot.java | 220 +++++++++++++++++++++++++++++++++---- src/main/java/Task.java | 109 +++++++++++++++++- 2 files changed, 304 insertions(+), 25 deletions(-) diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index ea4eaecbf..a70621dc8 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -1,14 +1,19 @@ +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAdjusters; import java.util.Scanner; import java.util.ArrayList; public class PlopBot { - // Static variables private static final String name = "plopBot"; private static final String HORIZONTAL_LINE = "//" + "\u2500".repeat(50); private static final String ECHO_LINE = " " + "\u2500".repeat(48); private static Scanner scanner = new Scanner(System.in); - private static ArrayList tasks; private static String userName; @@ -18,25 +23,21 @@ public static void main(String[] args) { inputReader(); } - /** - * Welcome message when the user runs the program. - */ + // Welcome message when the user runs the program. static void welcomeMessage() { System.out.println(HORIZONTAL_LINE); System.out.println("Hello! I'm " + name + "."); System.out.println("What can I do for you today?\n"); } - /** - * Farewell message when the user ends the program. - */ + // Farewell message when the user runs the program. static void farewellMessage() { System.out.println(HORIZONTAL_LINE); System.out.println("Thank you for choosing " + name + ". Have a great day!\n"); } /** - * Reads the user's inputs and calls processInput() + * Reads the user's inputs and calls processInput(). */ static void inputReader() { String userInput; @@ -63,7 +64,20 @@ static boolean processInput(String userInput) { return false; } - switch (userInput.toLowerCase()) { + String[] parts = userInput.split(" ", 2); + String command = parts[0].toLowerCase(); + String details = parts.length > 1 ? parts[1] : ""; + + switch (command) { + case "todo": + addTodo(details); + break; + case "deadline": + addDeadline(details); + break; + case "event": + addEvent(details); + break; case "list": list(); break; @@ -74,22 +88,89 @@ static boolean processInput(String userInput) { tasks.clear(); System.out.println("All tasks cleared."); break; - case "mark": - toggleMark(userInput); - break; case "bye", "exit", "quit": farewellMessage(); return true; - default: - tasks.add(new Task(userInput)); - System.out.println(" Added: " + userInput); + System.out.println(" Unknown command. Type 'help' for a list of commands."); break; } - return false; } + private static void addTodo(String details) { + Task newToDo = new Task(details); + tasks.add(newToDo); + System.out.println(ECHO_LINE); + System.out.println(" Added the To-Do task: \n " + newToDo.toString()); + System.out.printf(" You now have %d tasks in your list.\n", tasks.size()); + System.out.println(ECHO_LINE); + System.out.println(""); + } + + private static void addDeadline(String details) { + try { + String[] parts = details.split(" /by ", 2); + + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid deadline format"); + } + + String description = parts[0].trim(); + String dueDateString = parts[1].trim(); + LocalDate dueDate = parseDateString(dueDateString); + Task newDeadline = new Task(description, dueDate); + tasks.add(newDeadline); + + System.out.println(ECHO_LINE); + System.out.println(" Added the Deadline task: \n " + newDeadline.toString()); + System.out.printf(" You now have %d tasks in your list.\n", tasks.size()); + System.out.println(ECHO_LINE); + } + catch (Exception e) { + } + System.out.println(""); + } + + + private static void addEvent(String details) { + try { + String[] parts = details.split(" /from | /to "); + + if (parts.length != 3) { + throw new IllegalArgumentException("Invalid event format"); + } + + String description = parts[0].trim(); + String startTimeString = parts[1].trim(); + String endTimeString = parts[2].trim(); + LocalDateTime startTime = parseDateTimeString(startTimeString, null); + LocalDateTime endTime; + + if (!endTimeString.contains(" ")) { + LocalDate startDate = startTime.toLocalDate(); + LocalTime endTime2 = parseTimeString(endTimeString); + endTime = LocalDateTime.of(startDate, endTime2); + } + else { + endTime = parseDateTimeString(endTimeString, startTime); + } + + Task newEvent = new Task(description, startTime, endTime); + tasks.add(newEvent); + System.out.println(ECHO_LINE); + System.out.println(" Added the Event task: \n " + newEvent.toString()); + System.out.printf(" You now have %d tasks in your list.\n", tasks.size()); + System.out.println(ECHO_LINE); + System.out.println(""); + } + catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + System.out.println("Use 'event description /from START_TIME /to END_TIME'"); + System.out.println("TIME can be 'Mon 2pm', 'Tuesday 14:00', or 'YYYY-MM-DD HH:MM'"); + } + } + /** * Lists the user's inputs. */ @@ -98,11 +179,15 @@ static void list() { System.out.println(" Your List:"); for (int i = 0; i < tasks.size(); i++) { Task task = tasks.get(i); - System.out.printf(" %d.%s %s%n", i + 1, task.getStatusIcon(), task.getName()); + System.out.println(" " + task.toString()); } System.out.println(""); } + /** + * Toggles the status of the task specified by the user. + * @param userInput + */ static void toggleMark(String userInput) { try { int taskNumber = Integer.parseInt(userInput.split(" ")[1]) - 1; @@ -116,8 +201,10 @@ static void toggleMark(String userInput) { for (int i = 0; i < tasks.size(); i++) { Task t = tasks.get(i); - System.out.printf(" %d.%s %s%n", i + 1, t.getStatusIcon(), t.getName()); + System.out.println(" " + t.toString()); } + + System.out.println(""); } } catch (NumberFormatException | ArrayIndexOutOfBoundsException e){ @@ -125,6 +212,101 @@ static void toggleMark(String userInput) { } } + /** + * Parse helper method to parse date strings for deadline tasks. + * @param dateString + * @return + */ + private static LocalDate parseDateString(String dateString) { + LocalDate now = LocalDate.now(); + try { + return LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE); + } + catch (DateTimeParseException e) { + String[] shortDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + + for (int i = 0; i < shortDays.length; i++) { + if (shortDays[i].equalsIgnoreCase(dateString)) { + return now.with(TemporalAdjusters.next(DayOfWeek.of(i + 1))); + } + } + try { + return now.with(TemporalAdjusters.next(DayOfWeek.valueOf(dateString.toUpperCase()))); + } + catch (IllegalArgumentException ex) { + switch (dateString.toLowerCase()) { + case "today": + return now; + case "tomorrow": + return now.plusDays(1); + default: + throw new IllegalArgumentException("Unable to parse date: " + dateString); + } + } + } + } + + /** + * Parse helper methods to parse date and time strings for event tasks. + * @param dateTimeString + * @return + */ + private static LocalDateTime parseDateTimeString(String dateTimeString) { + return parseDateTimeString(dateTimeString, null); + } + + private static LocalDateTime parseDateTimeString(String dateTimeString, LocalDateTime referenceTime) { + LocalDate now = LocalDate.now(); + try { + return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + } + catch (DateTimeParseException e) { + String[] parts = dateTimeString.split(" ", 2); + + if (parts.length == 2) { + LocalDate date = parseDateString(parts[0]); + LocalTime time = parseTimeString(parts[1]); + return LocalDateTime.of(date, time); + } + else if (parts.length == 1) { + LocalTime time = parseTimeString(parts[0]); + LocalDate date = (referenceTime != null) ? referenceTime.toLocalDate() : now; + return LocalDateTime.of(date, time); + } + } + throw new IllegalArgumentException("Unable to parse date and time: " + dateTimeString); + } + + private static LocalTime parseTimeString(String timeString) { + try { + return LocalTime.parse(timeString, DateTimeFormatter.ofPattern("HH:mm")); + } + catch (DateTimeParseException e) { + try { + return LocalTime.parse(timeString.toUpperCase(), DateTimeFormatter.ofPattern("ha")); + } + catch (DateTimeParseException e2) { + // Handle "2pm" format + String formattedTime = timeString.toLowerCase(); + if (formattedTime.endsWith("am") || formattedTime.endsWith("pm")) { + int hour = Integer.parseInt(formattedTime.substring(0, formattedTime.length() - 2)); + + if (formattedTime.endsWith("pm") && hour < 12) { + hour += 12; + } + else if (formattedTime.endsWith("am") && hour == 12) { + hour = 0; + } + return LocalTime.of(hour, 0); + } + throw new IllegalArgumentException("Unable to parse time: " + timeString); + } + } + } + + /** + * W.I.P Method that displays a 'help menu.' + */ static void showHelp() { System.out.println(""); } diff --git a/src/main/java/Task.java b/src/main/java/Task.java index 4c94f4f97..aa65c858f 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -1,34 +1,131 @@ +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + public class Task { - protected String taskName; - protected boolean isDone; + // Static variables + private String taskName; + private TaskType type; + private boolean isDone; + private LocalDate deadline; + private LocalDateTime startTime; + private LocalDateTime endTime; + + /** + * Enum for different task types + */ + public enum TaskType { + TODO, EVENT, DEADLINE + } /** - * Basic Task constructor + * ToDo Task constructor * @param taskName */ public Task(String taskName) { this.taskName = taskName; - this.isDone = false; + this.type = TaskType.TODO; + + //this.isDone = false; + } + + /** + * Deadline Task constructor + * @param taskName + * @param deadline + */ + public Task(String taskName, LocalDate deadline) { + this.taskName = taskName; + this.type = TaskType.DEADLINE; + this.deadline = deadline; + + //this.isDone = false; + } + + /** + * Event Task constructor + * @param taskName + * @param startTime + * @param endTime + */ + public Task(String taskName, LocalDateTime startTime, LocalDateTime endTime) { + this.taskName = taskName; + this.type = TaskType.EVENT; + this.startTime = startTime; + this.endTime = endTime; + + //this.isDone = false; } /** - * Returns String depicting whether the task is finished or not. - * @return + * Getters and setters for the Tasks. */ public String getStatusIcon() { return (isDone ? "[X]" : "[ ]"); } + public String getTypeIcon() { + switch (type) { + case TODO: + return "T"; + case DEADLINE: + return "D"; + case EVENT: + return "E"; + + default: + return "?"; + } + } + public String getName() { return taskName; } + public LocalDate getDeadline() { + return deadline; + } + + public LocalDateTime getStartTime() { + return startTime; + } + + public LocalDateTime getEndTime() { + return endTime; + } + public boolean getStatus() { return isDone; } + /** + public void markDone() { + isDone = true; + } + + public void markUndone() { + isDone = false; + } + */ + + // Toggles the isDone status of the specified Task. public void toggleStatus() { this.isDone = !this.isDone; } + + // Returns a String of the specified task. + public String toString() { + String base = String.format("[%s][%s] %s", getTypeIcon(), isDone ? "X" : " ", taskName); + + switch (type) { + case DEADLINE: + return String.format("%s (by: %s)", base, deadline.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + case EVENT: + return String.format("%s (from: %s to: %s)", base, + startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), + endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))); + } + return base; + } } From 804b7f65a0c08efedad342f2e4e8b66f02d9cb0f Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Tue, 17 Sep 2024 14:59:51 +0800 Subject: [PATCH 06/19] plopBot - Level 5: Handle Errors // Implemented --- src/main/java/PlopBot.java | 42 ++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index a70621dc8..138a2d1eb 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -59,6 +59,11 @@ static void inputReader() { * @param userInput */ static boolean processInput(String userInput) { + if (userInput.isEmpty()) { + printError("The input cannot be empty."); + return false; + } + if (userInput.toLowerCase().startsWith("mark") || userInput.toLowerCase().startsWith("unmark")) { toggleMark(userInput); return false; @@ -92,13 +97,18 @@ static boolean processInput(String userInput) { farewellMessage(); return true; default: - System.out.println(" Unknown command. Type 'help' for a list of commands."); + printError("Unknown command. Type 'help' for a list of commands."); break; } return false; } private static void addTodo(String details) { + if (details.isEmpty()) { + printError("The description of a To-Do task cannot be empty."); + return; + } + Task newToDo = new Task(details); tasks.add(newToDo); System.out.println(ECHO_LINE); @@ -113,11 +123,18 @@ private static void addDeadline(String details) { String[] parts = details.split(" /by ", 2); if (parts.length != 2) { - throw new IllegalArgumentException("Invalid deadline format"); + throw new IllegalArgumentException("Invalid deadline format."); } String description = parts[0].trim(); String dueDateString = parts[1].trim(); + + if (description.isEmpty() || dueDateString.isEmpty()) { + printError("The descriptions of a Deadline task cannot be empty."); + return; + } + + LocalDate dueDate = parseDateString(dueDateString); Task newDeadline = new Task(description, dueDate); tasks.add(newDeadline); @@ -128,6 +145,8 @@ private static void addDeadline(String details) { System.out.println(ECHO_LINE); } catch (Exception e) { + printError(e.getMessage() + "\n Usage: deadline description /by DATE" + + "\n DATE can be 'Sunday', 'Mon', 'Tuesday', or 'YYYY-MM-DD'"); } System.out.println(""); } @@ -138,12 +157,18 @@ private static void addEvent(String details) { String[] parts = details.split(" /from | /to "); if (parts.length != 3) { - throw new IllegalArgumentException("Invalid event format"); + throw new IllegalArgumentException("Invalid event format."); } String description = parts[0].trim(); String startTimeString = parts[1].trim(); String endTimeString = parts[2].trim(); + + if (description.isEmpty() || startTimeString.isEmpty() || endTimeString.isEmpty()) { + printError("The descriptions of an Event task cannot be empty."); + return; + } + LocalDateTime startTime = parseDateTimeString(startTimeString, null); LocalDateTime endTime; @@ -165,9 +190,8 @@ private static void addEvent(String details) { System.out.println(""); } catch (Exception e) { - System.out.println("Error: " + e.getMessage()); - System.out.println("Use 'event description /from START_TIME /to END_TIME'"); - System.out.println("TIME can be 'Mon 2pm', 'Tuesday 14:00', or 'YYYY-MM-DD HH:MM'"); + printError(e.getMessage() + "\n Usage: event description /from START_TIME /to END_TIME" + + "\n TIME can be 'Mon 2pm', 'Tuesday 14:00', or 'YYYY-MM-DD HH:MM'"); } } @@ -304,6 +328,12 @@ else if (formattedTime.endsWith("am") && hour == 12) { } } + private static void printError (String message) { + System.out.println(ECHO_LINE); + System.out.println(" Oops! " + message); + System.out.println(ECHO_LINE); + } + /** * W.I.P Method that displays a 'help menu.' */ From 221a033e4409a8468e75b0e4f09e298713bd5eac Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Tue, 17 Sep 2024 15:25:24 +0800 Subject: [PATCH 07/19] plopBot - Level 6: Delete // Implemented --- src/main/java/PlopBot.java | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index 138a2d1eb..e7a91a7e3 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -86,6 +86,9 @@ static boolean processInput(String userInput) { case "list": list(); break; + case "delete": + delete(details); + break; case "help": showHelp(); break; @@ -234,6 +237,7 @@ static void toggleMark(String userInput) { catch (NumberFormatException | ArrayIndexOutOfBoundsException e){ System.out.println("Invalid command. Please use 'mark '."); } + System.out.println(ECHO_LINE); } /** @@ -328,6 +332,30 @@ else if (formattedTime.endsWith("am") && hour == 12) { } } + private static void delete (String details) { + try { + int taskNumber = Integer.parseInt(details.trim()); + + if (taskNumber < 1 || taskNumber > tasks.size()) { + throw new IllegalArgumentException("Invalid task number. Please enter a task number between 1 and " + tasks.size() + "."); + } + + Task removedTask = tasks.remove(taskNumber - 1); + System.out.println(ECHO_LINE); + System.out.println(" Understood. Deleted the following task: "); + System.out.println(" " + removedTask); + System.out.println(" You now have " + tasks.size() + " tasks in the list."); + System.out.println(ECHO_LINE); + + } + catch (NumberFormatException e) { + printError("Please provide a valid task number to delete."); + } + catch (IllegalArgumentException e) { + printError(e.getMessage()); + } + } + private static void printError (String message) { System.out.println(ECHO_LINE); System.out.println(" Oops! " + message); From d664dd700ca9d35aaf89017a8e8853484d8d2ba2 Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Wed, 18 Sep 2024 12:55:20 +0800 Subject: [PATCH 08/19] plopBot - Level 07: Save // W.I.P --- src/main/java/PlopBot.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index e7a91a7e3..75d427f45 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -1,3 +1,4 @@ + import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; @@ -16,10 +17,12 @@ public class PlopBot { private static Scanner scanner = new Scanner(System.in); private static ArrayList tasks; private static String userName; + //private static final String DATA_DIR = "./data"; + //private static final String DATA_FILE = DATA_DIR + "/plopBot.txt"; public static void main(String[] args) { welcomeMessage(); - tasks = new ArrayList<>(); + tasks = loadTasks(); inputReader(); } @@ -362,6 +365,16 @@ private static void printError (String message) { System.out.println(ECHO_LINE); } + private static ArrayList loadTasks() { + ArrayList loadedTasks = new ArrayList<>(); + + return loadedTasks; + } + + private static void saveTasks() { + + } + /** * W.I.P Method that displays a 'help menu.' */ From 7cf900b2c5284b0785b0241e61582986276854ae Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Wed, 18 Sep 2024 14:33:52 +0800 Subject: [PATCH 09/19] plopBot - Level 07: Save // W.I.P + .jar --- .../Individual Project.jar | Bin 0 -> 9853 bytes src/main/java/PlopBot.java | 26 ++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 out/artifacts/Individual_Project_jar/Individual Project.jar diff --git a/out/artifacts/Individual_Project_jar/Individual Project.jar b/out/artifacts/Individual_Project_jar/Individual Project.jar new file mode 100644 index 0000000000000000000000000000000000000000..5972a87f0bc1db7a381a5761d451928924ce6262 GIT binary patch literal 9853 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 tasks; private static String userName; - //private static final String DATA_DIR = "./data"; - //private static final String DATA_FILE = DATA_DIR + "/plopBot.txt"; + private static final String DATA_DIR = "./data"; + private static final String DATA_FILE = DATA_DIR + "/plopBot.txt"; public static void main(String[] args) { welcomeMessage(); @@ -367,12 +371,28 @@ private static void printError (String message) { private static ArrayList loadTasks() { ArrayList loadedTasks = new ArrayList<>(); + try { + Files.createDirectories(Paths.get(DATA_DIR)); + } + catch (IOException e) { + printError("An error occurred while loading tasks: " + e.getMessage()); + } return loadedTasks; } private static void saveTasks() { + try { + Files.createDirectories(Paths.get(DATA_DIR)); + BufferedWriter writer = new BufferedWriter(new FileWriter(DATA_FILE)); + for (Task task : tasks) { + + } + } + catch (IOException e ) { + printError("An error occurred while saving tasks: " + e.getMessage()); + } } /** From 7f23fdfeb9aad3690b37ee8ede34f96ef99e45d6 Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Wed, 9 Oct 2024 11:56:59 +0800 Subject: [PATCH 10/19] plopBot - Level 7: Save // Implemented --- src/main/java/META-INF/MANIFEST.MF | 3 + src/main/java/PlopBot.java | 99 +++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 src/main/java/META-INF/MANIFEST.MF diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..49d2c8b51 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: PlopBot + diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index 724d11e72..173cc9079 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -10,6 +10,7 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAdjusters; +import java.util.List; import java.util.Scanner; import java.util.ArrayList; @@ -103,6 +104,9 @@ static boolean processInput(String userInput) { tasks.clear(); System.out.println("All tasks cleared."); break; + case "find": + find(details); + break; case "bye", "exit", "quit": farewellMessage(); return true; @@ -121,6 +125,7 @@ private static void addTodo(String details) { Task newToDo = new Task(details); tasks.add(newToDo); + saveTasks(); System.out.println(ECHO_LINE); System.out.println(" Added the To-Do task: \n " + newToDo.toString()); System.out.printf(" You now have %d tasks in your list.\n", tasks.size()); @@ -148,6 +153,7 @@ private static void addDeadline(String details) { LocalDate dueDate = parseDateString(dueDateString); Task newDeadline = new Task(description, dueDate); tasks.add(newDeadline); + saveTasks(); System.out.println(ECHO_LINE); System.out.println(" Added the Deadline task: \n " + newDeadline.toString()); @@ -193,6 +199,7 @@ private static void addEvent(String details) { Task newEvent = new Task(description, startTime, endTime); tasks.add(newEvent); + saveTasks(); System.out.println(ECHO_LINE); System.out.println(" Added the Event task: \n " + newEvent.toString()); System.out.printf(" You now have %d tasks in your list.\n", tasks.size()); @@ -229,6 +236,7 @@ static void toggleMark(String userInput) { if (taskNumber >= 0 && taskNumber < tasks.size()) { Task task = tasks.get(taskNumber); task.toggleStatus(); + saveTasks(); System.out.println(ECHO_LINE); System.out.printf(" Successfully updated task %d.\n", taskNumber + 1); System.out.println(" Your Updated List:"); @@ -348,6 +356,7 @@ private static void delete (String details) { } Task removedTask = tasks.remove(taskNumber - 1); + saveTasks(); System.out.println(ECHO_LINE); System.out.println(" Understood. Deleted the following task: "); System.out.println(" " + removedTask); @@ -369,14 +378,72 @@ private static void printError (String message) { System.out.println(ECHO_LINE); } + + private static void find(String details) { + if (details == null) { + return; + } + for (int i = 0; i < tasks.size(); i++) { + Task t = i. + if (details.toLowerCase() == i.getName().toLowerCase()) { + + } + } + + System.out.println(ECHO_LINE); + System.out.println(" Found the following tasks in your list: "); + + + System.out.println(ECHO_LINE); + } + private static ArrayList loadTasks() { ArrayList loadedTasks = new ArrayList<>(); try { Files.createDirectories(Paths.get(DATA_DIR)); - - } - catch (IOException e) { - printError("An error occurred while loading tasks: " + e.getMessage()); + if (Files.exists(Paths.get(DATA_FILE))) { + List lines = Files.readAllLines(Paths.get(DATA_FILE)); + for (String line : lines) { + try { + String[] parts = line.split("\\|"); + if (parts.length < 3) continue; + + String type = parts[0].trim(); + boolean isDone = parts[1].trim().equals("1"); + String description = parts[2].trim(); + + Task task; + switch (type) { + case "T": + task = new Task(description); + break; + case "D": + if (parts.length < 4) continue; + LocalDate deadline = LocalDate.parse(parts[3].trim(), DateTimeFormatter.ISO_LOCAL_DATE); + task = new Task(description, deadline); + break; + case "E": + if (parts.length < 5) continue; + LocalDateTime startTime = LocalDateTime.parse(parts[3].trim(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + LocalDateTime endTime = LocalDateTime.parse(parts[4].trim(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + task = new Task(description, startTime, endTime); + break; + default: + continue; + } + + if (isDone) { + task.toggleStatus(); + } + loadedTasks.add(task); + } catch (DateTimeParseException | IndexOutOfBoundsException e) { + System.out.println(" Error parsing task: " + line); + // Continue to next line if there's an error with the current one + } + } + } + } catch (IOException e) { + printError(" An error occurred while loading tasks: " + e.getMessage()); } return loadedTasks; } @@ -387,10 +454,28 @@ private static void saveTasks() { BufferedWriter writer = new BufferedWriter(new FileWriter(DATA_FILE)); for (Task task : tasks) { - + String line; + switch (task.getTypeIcon()) { + case "T": + line = String.format("T | %d | %s", task.getStatus() ? 1 : 0, task.getName()); + break; + case "D": + line = String.format("D | %d | %s | %s", task.getStatus() ? 1 : 0, task.getName(), + task.getDeadline().format(DateTimeFormatter.ISO_LOCAL_DATE)); + break; + case "E": + line = String.format("E | %d | %s | %s | %s", task.getStatus() ? 1 : 0, task.getName(), + task.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), + task.getEndTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + break; + default: + continue; + } + writer.write(line); + writer.newLine(); } - } - catch (IOException e ) { + writer.close(); + } catch (IOException e) { printError("An error occurred while saving tasks: " + e.getMessage()); } } From dcb0c09d3e58020fc41d6cd443b3809d33407d04 Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Wed, 9 Oct 2024 12:10:38 +0800 Subject: [PATCH 11/19] plopBot - Level 9: Find // Implemented --- src/main/java/PlopBot.java | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index 173cc9079..1e1619dee 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -379,21 +379,30 @@ private static void printError (String message) { } - private static void find(String details) { - if (details == null) { + private static void find(String keyword) { + if (keyword == null || keyword.trim().isEmpty()) { + printError("Please provide a keyword to search for."); return; } - for (int i = 0; i < tasks.size(); i++) { - Task t = i. - if (details.toLowerCase() == i.getName().toLowerCase()) { + keyword = keyword.toLowerCase().trim(); + List matchingTasks = new ArrayList<>(); + + for (Task task : tasks) { + if (task.getName().toLowerCase().contains(keyword)) { + matchingTasks.add(task); } } System.out.println(ECHO_LINE); - System.out.println(" Found the following tasks in your list: "); - - + if (matchingTasks.isEmpty()) { + System.out.println(" No matching tasks found."); + } else { + System.out.println(" Here are the matching tasks in your list:"); + for (int i = 0; i < matchingTasks.size(); i++) { + System.out.println(" " + (i + 1) + "." + matchingTasks.get(i).toString()); + } + } System.out.println(ECHO_LINE); } @@ -401,8 +410,10 @@ private static ArrayList loadTasks() { ArrayList loadedTasks = new ArrayList<>(); try { Files.createDirectories(Paths.get(DATA_DIR)); + if (Files.exists(Paths.get(DATA_FILE))) { List lines = Files.readAllLines(Paths.get(DATA_FILE)); + for (String line : lines) { try { String[] parts = line.split("\\|"); @@ -436,13 +447,15 @@ private static ArrayList loadTasks() { task.toggleStatus(); } loadedTasks.add(task); - } catch (DateTimeParseException | IndexOutOfBoundsException e) { + } + catch (DateTimeParseException | IndexOutOfBoundsException e) { System.out.println(" Error parsing task: " + line); // Continue to next line if there's an error with the current one } } } - } catch (IOException e) { + } + catch (IOException e) { printError(" An error occurred while loading tasks: " + e.getMessage()); } return loadedTasks; @@ -475,7 +488,8 @@ private static void saveTasks() { writer.newLine(); } writer.close(); - } catch (IOException e) { + } + catch (IOException e) { printError("An error occurred while saving tasks: " + e.getMessage()); } } From 823e47a62e3de12649798e0946fb27faf2fa8742 Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Fri, 11 Oct 2024 14:37:00 +0800 Subject: [PATCH 12/19] plopBot - A-JavaDoc // Implemented & Tidied Up --- src/main/java/Parser.java | 135 +++++++ src/main/java/PlopBot.java | 579 +++++----------------------- src/main/java/PlopBotException.java | 5 + src/main/java/Storage.java | 111 ++++++ src/main/java/Task.java | 77 ++-- src/main/java/TaskList.java | 66 ++++ src/main/java/Ui.java | 108 ++++++ 7 files changed, 563 insertions(+), 518 deletions(-) create mode 100644 src/main/java/Parser.java create mode 100644 src/main/java/PlopBotException.java create mode 100644 src/main/java/Storage.java create mode 100644 src/main/java/TaskList.java create mode 100644 src/main/java/Ui.java diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java new file mode 100644 index 000000000..51a734dab --- /dev/null +++ b/src/main/java/Parser.java @@ -0,0 +1,135 @@ +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAdjusters; + +public class Parser { + public String[] parseCommand(String userInput) throws PlopBotException { + String[] parts = userInput.split(" ", 2); + if (parts.length == 0) { + throw new PlopBotException("Empty command"); + } + return parts; + } + + /** + * Method that parses tasks into their respective types based on the user's inputs. + * @param commandParts + * @return + * @throws PlopBotException + */ + public Task parseTask(String[] commandParts) throws PlopBotException { + if (commandParts.length < 2) { + throw new PlopBotException("Invalid task format"); + } + String type = commandParts[0]; + String details = commandParts[1]; + + switch (type) { + case "todo": + return new Task(details); + case "deadline": + String[] deadlineParts = details.split(" /by ", 2); + if (deadlineParts.length != 2) { + throw new PlopBotException("Invalid deadline format"); + } + return new Task(deadlineParts[0], parseDateString(deadlineParts[1])); + case "event": + String[] eventParts = details.split(" /from | /to "); + if (eventParts.length != 3) { + throw new PlopBotException("Invalid event format"); + } + LocalDateTime startTime = parseDateTimeString(eventParts[1], null); + LocalDateTime endTime = parseDateTimeString(eventParts[2], startTime); + return new Task(eventParts[0], startTime, endTime); + default: + throw new PlopBotException("Unknown task type: " + type); + } + } + + /** + * Parse helper method to parse date strings for deadline tasks. + * @param dateString + * @return + */ + private static LocalDate parseDateString(String dateString) { + LocalDate now = LocalDate.now(); + try { + return LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE); + } catch (DateTimeParseException e) { + String[] shortDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + + for (int i = 0; i < shortDays.length; i++) { + if (shortDays[i].equalsIgnoreCase(dateString)) { + return now.with(TemporalAdjusters.next(DayOfWeek.of(i + 1))); + } + } + try { + return now.with(TemporalAdjusters.next(DayOfWeek.valueOf(dateString.toUpperCase()))); + } catch (IllegalArgumentException ex) { + switch (dateString.toLowerCase()) { + case "today": + return now; + case "tomorrow": + return now.plusDays(1); + default: + throw new IllegalArgumentException("Unable to parse date: " + dateString); + } + } + } + } + + /** + * Parse helper method to parse date and time strings for event tasks. + * @param dateTimeString + * @return + */ + private static LocalDateTime parseDateTimeString(String dateTimeString, LocalDateTime referenceTime) { + LocalDate now = LocalDate.now(); + try { + return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + } catch (DateTimeParseException e) { + String[] parts = dateTimeString.split(" ", 2); + + if (parts.length == 2) { + LocalDate date = parseDateString(parts[0]); + LocalTime time = parseTimeString(parts[1]); + return LocalDateTime.of(date, time); + } else if (parts.length == 1) { + LocalTime time = parseTimeString(parts[0]); + LocalDate date = (referenceTime != null) ? referenceTime.toLocalDate() : now; + return LocalDateTime.of(date, time); + } + } + throw new IllegalArgumentException("Unable to parse date and time: " + dateTimeString); + } + + /** + * Parse helper method to parse time strings. + * @param timeString + * @return + */ + private static LocalTime parseTimeString(String timeString) { + try { + return LocalTime.parse(timeString, DateTimeFormatter.ofPattern("HH:mm")); + } catch (DateTimeParseException e) { + try { + return LocalTime.parse(timeString.toUpperCase(), DateTimeFormatter.ofPattern("ha")); + } catch (DateTimeParseException e2) { + // Handle "2pm" format + String formattedTime = timeString.toLowerCase(); + if (formattedTime.endsWith("am") || formattedTime.endsWith("pm")) { + int hour = Integer.parseInt(formattedTime.substring(0, formattedTime.length() - 2)); + + if (formattedTime.endsWith("pm") && hour < 12) { + hour += 12; + } else if (formattedTime.endsWith("am") && hour == 12) { + hour = 0; + } + return LocalTime.of(hour, 0); + } + throw new IllegalArgumentException("Unable to parse time: " + timeString); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index 1e1619dee..5edf6a0ea 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -1,504 +1,125 @@ -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.time.temporal.TemporalAdjusters; -import java.util.List; -import java.util.Scanner; import java.util.ArrayList; public class PlopBot { - // Static variables - private static final String name = "plopBot"; - private static final String HORIZONTAL_LINE = "//" + "\u2500".repeat(50); - private static final String ECHO_LINE = " " + "\u2500".repeat(48); - private static Scanner scanner = new Scanner(System.in); - private static ArrayList tasks; - private static String userName; - private static final String DATA_DIR = "./data"; - private static final String DATA_FILE = DATA_DIR + "/plopBot.txt"; - - public static void main(String[] args) { - welcomeMessage(); - tasks = loadTasks(); - inputReader(); - } - - // Welcome message when the user runs the program. - static void welcomeMessage() { - System.out.println(HORIZONTAL_LINE); - System.out.println("Hello! I'm " + name + "."); - System.out.println("What can I do for you today?\n"); - } - - // Farewell message when the user runs the program. - static void farewellMessage() { - System.out.println(HORIZONTAL_LINE); - System.out.println("Thank you for choosing " + name + ". Have a great day!\n"); - } - - /** - * Reads the user's inputs and calls processInput(). - */ - static void inputReader() { - String userInput; - - while (true) { - userInput = scanner.nextLine().trim(); - // processInput(userInput); - - if (processInput(userInput)) { - break; - } - } - - scanner.close(); - } - - /** - * Processes the user's inputs. - * @param userInput - */ - static boolean processInput(String userInput) { - if (userInput.isEmpty()) { - printError("The input cannot be empty."); - return false; - } - - if (userInput.toLowerCase().startsWith("mark") || userInput.toLowerCase().startsWith("unmark")) { - toggleMark(userInput); - return false; - } - - String[] parts = userInput.split(" ", 2); - String command = parts[0].toLowerCase(); - String details = parts.length > 1 ? parts[1] : ""; - - switch (command) { - case "todo": - addTodo(details); - break; - case "deadline": - addDeadline(details); - break; - case "event": - addEvent(details); - break; - case "list": - list(); - break; - case "delete": - delete(details); - break; - case "help": - showHelp(); - break; - case "clear": - tasks.clear(); - System.out.println("All tasks cleared."); - break; - case "find": - find(details); - break; - case "bye", "exit", "quit": - farewellMessage(); - return true; - default: - printError("Unknown command. Type 'help' for a list of commands."); - break; - } - return false; - } - - private static void addTodo(String details) { - if (details.isEmpty()) { - printError("The description of a To-Do task cannot be empty."); - return; - } - - Task newToDo = new Task(details); - tasks.add(newToDo); - saveTasks(); - System.out.println(ECHO_LINE); - System.out.println(" Added the To-Do task: \n " + newToDo.toString()); - System.out.printf(" You now have %d tasks in your list.\n", tasks.size()); - System.out.println(ECHO_LINE); - System.out.println(""); - } - - private static void addDeadline(String details) { + private Storage storage; + private TaskList tasks; + private Ui ui; + private Parser parser; + + public PlopBot(String filePath) { + ui = new Ui(); + storage = new Storage(filePath); try { - String[] parts = details.split(" /by ", 2); - - if (parts.length != 2) { - throw new IllegalArgumentException("Invalid deadline format."); - } - - String description = parts[0].trim(); - String dueDateString = parts[1].trim(); - - if (description.isEmpty() || dueDateString.isEmpty()) { - printError("The descriptions of a Deadline task cannot be empty."); - return; - } - - - LocalDate dueDate = parseDateString(dueDateString); - Task newDeadline = new Task(description, dueDate); - tasks.add(newDeadline); - saveTasks(); - - System.out.println(ECHO_LINE); - System.out.println(" Added the Deadline task: \n " + newDeadline.toString()); - System.out.printf(" You now have %d tasks in your list.\n", tasks.size()); - System.out.println(ECHO_LINE); + tasks = new TaskList(storage.load()); + } catch (PlopBotException e) { + ui.showLoadingError(); + tasks = new TaskList(); } - catch (Exception e) { - printError(e.getMessage() + "\n Usage: deadline description /by DATE" + - "\n DATE can be 'Sunday', 'Mon', 'Tuesday', or 'YYYY-MM-DD'"); - } - System.out.println(""); + parser = new Parser(); } - - private static void addEvent(String details) { - try { - String[] parts = details.split(" /from | /to "); - - if (parts.length != 3) { - throw new IllegalArgumentException("Invalid event format."); - } - - String description = parts[0].trim(); - String startTimeString = parts[1].trim(); - String endTimeString = parts[2].trim(); - - if (description.isEmpty() || startTimeString.isEmpty() || endTimeString.isEmpty()) { - printError("The descriptions of an Event task cannot be empty."); - return; - } - - LocalDateTime startTime = parseDateTimeString(startTimeString, null); - LocalDateTime endTime; - - if (!endTimeString.contains(" ")) { - LocalDate startDate = startTime.toLocalDate(); - LocalTime endTime2 = parseTimeString(endTimeString); - endTime = LocalDateTime.of(startDate, endTime2); - } - else { - endTime = parseDateTimeString(endTimeString, startTime); - } - - Task newEvent = new Task(description, startTime, endTime); - tasks.add(newEvent); - saveTasks(); - System.out.println(ECHO_LINE); - System.out.println(" Added the Event task: \n " + newEvent.toString()); - System.out.printf(" You now have %d tasks in your list.\n", tasks.size()); - System.out.println(ECHO_LINE); - System.out.println(""); - } - catch (Exception e) { - printError(e.getMessage() + "\n Usage: event description /from START_TIME /to END_TIME" + - "\n TIME can be 'Mon 2pm', 'Tuesday 14:00', or 'YYYY-MM-DD HH:MM'"); - } - } - - /** - * Lists the user's inputs. - */ - static void list() { - System.out.println(ECHO_LINE); - System.out.println(" Your List:"); - for (int i = 0; i < tasks.size(); i++) { - Task task = tasks.get(i); - System.out.println(" " + task.toString()); - } - System.out.println(""); - } - - /** - * Toggles the status of the task specified by the user. - * @param userInput - */ - static void toggleMark(String userInput) { - try { - int taskNumber = Integer.parseInt(userInput.split(" ")[1]) - 1; - - if (taskNumber >= 0 && taskNumber < tasks.size()) { - Task task = tasks.get(taskNumber); - task.toggleStatus(); - saveTasks(); - System.out.println(ECHO_LINE); - System.out.printf(" Successfully updated task %d.\n", taskNumber + 1); - System.out.println(" Your Updated List:"); - - for (int i = 0; i < tasks.size(); i++) { - Task t = tasks.get(i); - System.out.println(" " + t.toString()); - } - - System.out.println(""); - } - } - catch (NumberFormatException | ArrayIndexOutOfBoundsException e){ - System.out.println("Invalid command. Please use 'mark '."); - } - System.out.println(ECHO_LINE); - } - - /** - * Parse helper method to parse date strings for deadline tasks. - * @param dateString - * @return - */ - private static LocalDate parseDateString(String dateString) { - LocalDate now = LocalDate.now(); - try { - return LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE); - } - catch (DateTimeParseException e) { - String[] shortDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; - - for (int i = 0; i < shortDays.length; i++) { - if (shortDays[i].equalsIgnoreCase(dateString)) { - return now.with(TemporalAdjusters.next(DayOfWeek.of(i + 1))); - } - } + public void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + String fullCommand = ui.readCommand(); try { - return now.with(TemporalAdjusters.next(DayOfWeek.valueOf(dateString.toUpperCase()))); - } - catch (IllegalArgumentException ex) { - switch (dateString.toLowerCase()) { - case "today": - return now; - case "tomorrow": - return now.plusDays(1); + String[] commandParts = parser.parseCommand(fullCommand); + switch (commandParts[0]) { + case "bye": + case "exit": + case "quit": + isExit = true; + break; + case "list": + ui.showTasks(tasks.getTasks()); + break; + case "todo": + Task newToDo = parser.parseTask(commandParts); + tasks.addTask(newToDo); + storage.save(tasks.getTasks()); + ui.showTaskAdded(newToDo, tasks.size()); + break; + case "deadline": + try { + Task newDeadline = parser.parseTask(commandParts); + tasks.addTask(newDeadline); + storage.save(tasks.getTasks()); + ui.showTaskAdded(newDeadline, tasks.size()); + } catch (PlopBotException e) { + ui.showError(e.getMessage() + "\n Usage: deadline description /by DATE" + + "\n DATE can be 'Sunday', 'Mon', 'Tuesday', or 'YYYY-MM-DD'"); + } + break; + case "event": + try { + Task newEvent = parser.parseTask(commandParts); + tasks.addTask(newEvent); + storage.save(tasks.getTasks()); + ui.showTaskAdded(newEvent, tasks.size()); + } catch (PlopBotException e) { + ui.showError(e.getMessage() + "\n Usage: event description /from START_TIME /to END_TIME" + + "\n TIME can be 'Mon 2pm', 'Tuesday 14:00', or 'YYYY-MM-DD HH:MM'"); + } + break; + case "delete": + int index = Integer.parseInt(commandParts[1]) - 1; + Task removedTask = tasks.removeTask(index); + storage.save(tasks.getTasks()); + ui.showTaskRemoved(removedTask, tasks.size()); + break; + case "find": + String keyword = commandParts[1]; + ArrayList matchingTasks = tasks.findTasks(keyword); + ui.showMatchingTasks(matchingTasks); + break; + case "mark": + handleMarkTask(commandParts); + break; + case "unmark": + handleUnmarkTask(commandParts); + break; default: - throw new IllegalArgumentException("Unable to parse date: " + dateString); + throw new PlopBotException("Unknown command"); } + } catch (PlopBotException e) { + ui.showError(e.getMessage()); } } + ui.showExit(); } - /** - * Parse helper methods to parse date and time strings for event tasks. - * @param dateTimeString - * @return - */ - private static LocalDateTime parseDateTimeString(String dateTimeString) { - return parseDateTimeString(dateTimeString, null); + public static void main(String[] args) { + new PlopBot("data/tasks.txt").run(); } - private static LocalDateTime parseDateTimeString(String dateTimeString, LocalDateTime referenceTime) { - LocalDate now = LocalDate.now(); - try { - return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + private void handleMarkTask(String[] commandParts) throws PlopBotException { + if (commandParts.length != 2) { + throw new PlopBotException("Invalid mark command. Usage: mark "); } - catch (DateTimeParseException e) { - String[] parts = dateTimeString.split(" ", 2); - - if (parts.length == 2) { - LocalDate date = parseDateString(parts[0]); - LocalTime time = parseTimeString(parts[1]); - return LocalDateTime.of(date, time); - } - else if (parts.length == 1) { - LocalTime time = parseTimeString(parts[0]); - LocalDate date = (referenceTime != null) ? referenceTime.toLocalDate() : now; - return LocalDateTime.of(date, time); - } - } - throw new IllegalArgumentException("Unable to parse date and time: " + dateTimeString); - } - - private static LocalTime parseTimeString(String timeString) { try { - return LocalTime.parse(timeString, DateTimeFormatter.ofPattern("HH:mm")); - } - catch (DateTimeParseException e) { - try { - return LocalTime.parse(timeString.toUpperCase(), DateTimeFormatter.ofPattern("ha")); - } - catch (DateTimeParseException e2) { - // Handle "2pm" format - String formattedTime = timeString.toLowerCase(); - if (formattedTime.endsWith("am") || formattedTime.endsWith("pm")) { - int hour = Integer.parseInt(formattedTime.substring(0, formattedTime.length() - 2)); - - if (formattedTime.endsWith("pm") && hour < 12) { - hour += 12; - } - else if (formattedTime.endsWith("am") && hour == 12) { - hour = 0; - } - return LocalTime.of(hour, 0); - } - throw new IllegalArgumentException("Unable to parse time: " + timeString); - } + int taskIndex = Integer.parseInt(commandParts[1]) - 1; + Task task = tasks.getTask(taskIndex); + task.markAsDone(); + storage.save(tasks.getTasks()); + ui.showTaskMarked(task); + } catch (NumberFormatException e) { + throw new PlopBotException("Invalid task number. Please provide a number."); } } - private static void delete (String details) { - try { - int taskNumber = Integer.parseInt(details.trim()); - - if (taskNumber < 1 || taskNumber > tasks.size()) { - throw new IllegalArgumentException("Invalid task number. Please enter a task number between 1 and " + tasks.size() + "."); - } - - Task removedTask = tasks.remove(taskNumber - 1); - saveTasks(); - System.out.println(ECHO_LINE); - System.out.println(" Understood. Deleted the following task: "); - System.out.println(" " + removedTask); - System.out.println(" You now have " + tasks.size() + " tasks in the list."); - System.out.println(ECHO_LINE); - - } - catch (NumberFormatException e) { - printError("Please provide a valid task number to delete."); + private void handleUnmarkTask(String[] commandParts) throws PlopBotException { + if (commandParts.length != 2) { + throw new PlopBotException("Invalid unmark command. Usage: unmark "); } - catch (IllegalArgumentException e) { - printError(e.getMessage()); - } - } - - private static void printError (String message) { - System.out.println(ECHO_LINE); - System.out.println(" Oops! " + message); - System.out.println(ECHO_LINE); - } - - - private static void find(String keyword) { - if (keyword == null || keyword.trim().isEmpty()) { - printError("Please provide a keyword to search for."); - return; - } - - keyword = keyword.toLowerCase().trim(); - List matchingTasks = new ArrayList<>(); - - for (Task task : tasks) { - if (task.getName().toLowerCase().contains(keyword)) { - matchingTasks.add(task); - } - } - - System.out.println(ECHO_LINE); - if (matchingTasks.isEmpty()) { - System.out.println(" No matching tasks found."); - } else { - System.out.println(" Here are the matching tasks in your list:"); - for (int i = 0; i < matchingTasks.size(); i++) { - System.out.println(" " + (i + 1) + "." + matchingTasks.get(i).toString()); - } - } - System.out.println(ECHO_LINE); - } - - private static ArrayList loadTasks() { - ArrayList loadedTasks = new ArrayList<>(); try { - Files.createDirectories(Paths.get(DATA_DIR)); - - if (Files.exists(Paths.get(DATA_FILE))) { - List lines = Files.readAllLines(Paths.get(DATA_FILE)); - - for (String line : lines) { - try { - String[] parts = line.split("\\|"); - if (parts.length < 3) continue; - - String type = parts[0].trim(); - boolean isDone = parts[1].trim().equals("1"); - String description = parts[2].trim(); - - Task task; - switch (type) { - case "T": - task = new Task(description); - break; - case "D": - if (parts.length < 4) continue; - LocalDate deadline = LocalDate.parse(parts[3].trim(), DateTimeFormatter.ISO_LOCAL_DATE); - task = new Task(description, deadline); - break; - case "E": - if (parts.length < 5) continue; - LocalDateTime startTime = LocalDateTime.parse(parts[3].trim(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); - LocalDateTime endTime = LocalDateTime.parse(parts[4].trim(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); - task = new Task(description, startTime, endTime); - break; - default: - continue; - } - - if (isDone) { - task.toggleStatus(); - } - loadedTasks.add(task); - } - catch (DateTimeParseException | IndexOutOfBoundsException e) { - System.out.println(" Error parsing task: " + line); - // Continue to next line if there's an error with the current one - } - } - } - } - catch (IOException e) { - printError(" An error occurred while loading tasks: " + e.getMessage()); + int taskIndex = Integer.parseInt(commandParts[1]) - 1; + Task task = tasks.getTask(taskIndex); + task.markAsUndone(); + storage.save(tasks.getTasks()); + ui.showTaskUnmarked(task); + } catch (NumberFormatException e) { + throw new PlopBotException("Invalid task number. Please provide a number."); } - return loadedTasks; } - - private static void saveTasks() { - try { - Files.createDirectories(Paths.get(DATA_DIR)); - BufferedWriter writer = new BufferedWriter(new FileWriter(DATA_FILE)); - - for (Task task : tasks) { - String line; - switch (task.getTypeIcon()) { - case "T": - line = String.format("T | %d | %s", task.getStatus() ? 1 : 0, task.getName()); - break; - case "D": - line = String.format("D | %d | %s | %s", task.getStatus() ? 1 : 0, task.getName(), - task.getDeadline().format(DateTimeFormatter.ISO_LOCAL_DATE)); - break; - case "E": - line = String.format("E | %d | %s | %s | %s", task.getStatus() ? 1 : 0, task.getName(), - task.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), - task.getEndTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); - break; - default: - continue; - } - writer.write(line); - writer.newLine(); - } - writer.close(); - } - catch (IOException e) { - printError("An error occurred while saving tasks: " + e.getMessage()); - } - } - - /** - * W.I.P Method that displays a 'help menu.' - */ - static void showHelp() { - System.out.println(""); - } -} - +} \ No newline at end of file diff --git a/src/main/java/PlopBotException.java b/src/main/java/PlopBotException.java new file mode 100644 index 000000000..730efe72e --- /dev/null +++ b/src/main/java/PlopBotException.java @@ -0,0 +1,5 @@ +public class PlopBotException extends Exception { + public PlopBotException(String message) { + super(message); + } +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java new file mode 100644 index 000000000..9739e243e --- /dev/null +++ b/src/main/java/Storage.java @@ -0,0 +1,111 @@ +import java.io.*; +import java.nio.file.*; +import java.time.*; +import java.time.format.*; +import java.util.ArrayList; +import java.util.List; + +public class Storage { + private String filePath; + + public Storage(String filePath) { + this.filePath = filePath; + } + + /** + * Method for loading tasks from a file. + * @return + * @throws PlopBotException + */ + public ArrayList load() throws PlopBotException { + ArrayList tasks = new ArrayList<>(); + try { + Files.createDirectories(Paths.get(filePath).getParent()); + if (Files.exists(Paths.get(filePath))) { + List lines = Files.readAllLines(Paths.get(filePath)); + for (String line : lines) { + tasks.add(parseTaskFromString(line)); + } + } + } catch (IOException e) { + throw new PlopBotException("Error loading tasks: " + e.getMessage()); + } + return tasks; + } + + /** + * Method for saving tasks to a file. + * @param tasks + * @throws PlopBotException + */ + public void save(ArrayList tasks) throws PlopBotException { + try { + Files.createDirectories(Paths.get(filePath).getParent()); + BufferedWriter writer = new BufferedWriter(new FileWriter(filePath)); + for (Task task : tasks) { + writer.write(convertTaskToString(task)); + writer.newLine(); + } + writer.close(); + } catch (IOException e) { + throw new PlopBotException("Error saving tasks: " + e.getMessage()); + } + } + + /** + * Parser for loading tasks from strings. + * @param line + * @return + * @throws PlopBotException + */ + private Task parseTaskFromString(String line) throws PlopBotException { + String[] parts = line.split("\\|"); + if (parts.length < 3) { + throw new PlopBotException("Invalid task format: " + line); + } + String type = parts[0].trim(); + boolean isDone = parts[1].trim().equals("1"); + String description = parts[2].trim(); + + Task task; + switch (type) { + case "T": + task = new Task(description); + break; + case "D": + if (parts.length < 4) throw new PlopBotException("Invalid deadline format: " + line); + LocalDate deadline = LocalDate.parse(parts[3].trim(), DateTimeFormatter.ISO_LOCAL_DATE); + task = new Task(description, deadline); + break; + case "E": + if (parts.length < 5) throw new PlopBotException("Invalid event format: " + line); + LocalDateTime startTime = LocalDateTime.parse(parts[3].trim(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + LocalDateTime endTime = LocalDateTime.parse(parts[4].trim(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + task = new Task(description, startTime, endTime); + break; + default: + throw new PlopBotException("Unknown task type: " + type); + } + if (isDone) task.toggleStatus(); + return task; + } + + /** + * Converts tasks to strings for saving. + * @param task + * @return + */ + private String convertTaskToString(Task task) { + String baseString = String.format("%s | %d | %s", + task.getTypeIcon(), task.getStatus() ? 1 : 0, task.getName()); + switch (task.getTypeIcon()) { + case "D": + return baseString + " | " + task.getDeadline().format(DateTimeFormatter.ISO_LOCAL_DATE); + case "E": + return baseString + " | " + task.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + + " | " + task.getEndTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + default: + return baseString; + } + } +} diff --git a/src/main/java/Task.java b/src/main/java/Task.java index aa65c858f..fdbffacaf 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -3,7 +3,6 @@ import java.time.format.DateTimeFormatter; public class Task { - // Static variables private String taskName; private TaskType type; private boolean isDone; @@ -11,26 +10,22 @@ public class Task { private LocalDateTime startTime; private LocalDateTime endTime; - /** - * Enum for different task types - */ public enum TaskType { TODO, EVENT, DEADLINE } /** - * ToDo Task constructor + * ToDo task constructor * @param taskName */ public Task(String taskName) { this.taskName = taskName; this.type = TaskType.TODO; - - //this.isDone = false; + this.isDone = false; } /** - * Deadline Task constructor + * Deadline task constructor * @param taskName * @param deadline */ @@ -38,12 +33,11 @@ public Task(String taskName, LocalDate deadline) { this.taskName = taskName; this.type = TaskType.DEADLINE; this.deadline = deadline; - - //this.isDone = false; + this.isDone = false; } /** - * Event Task constructor + * Event task constructor * @param taskName * @param startTime * @param endTime @@ -53,28 +47,19 @@ public Task(String taskName, LocalDateTime startTime, LocalDateTime endTime) { this.type = TaskType.EVENT; this.startTime = startTime; this.endTime = endTime; - - //this.isDone = false; + this.isDone = false; } /** - * Getters and setters for the Tasks. + * Getter methods for tasks. + * @return */ - public String getStatusIcon() { - return (isDone ? "[X]" : "[ ]"); - } - public String getTypeIcon() { switch (type) { - case TODO: - return "T"; - case DEADLINE: - return "D"; - case EVENT: - return "E"; - - default: - return "?"; + case TODO: return "T"; + case DEADLINE: return "D"; + case EVENT: return "E"; + default: return "?"; } } @@ -98,25 +83,39 @@ public boolean getStatus() { return isDone; } + public void toggleStatus() { + this.isDone = !this.isDone; + } + /** - public void markDone() { + * Marks specified task as done. + * @throws PlopBotException + */ + public void markAsDone() throws PlopBotException { + if (isDone) { + throw new PlopBotException("Task is already marked as done."); + } isDone = true; } - public void markUndone() { - isDone = false; - } + /** + * Unmarks specified task as not-done. + * @throws PlopBotException */ - - // Toggles the isDone status of the specified Task. - public void toggleStatus() { - this.isDone = !this.isDone; + public void markAsUndone() throws PlopBotException { + if (!isDone) { + throw new PlopBotException("Task is not yet done."); + } + isDone = false; } - // Returns a String of the specified task. + /** + * Converts tasks into strings and returns them. + * @return + */ + @Override public String toString() { String base = String.format("[%s][%s] %s", getTypeIcon(), isDone ? "X" : " ", taskName); - switch (type) { case DEADLINE: return String.format("%s (by: %s)", base, deadline.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); @@ -124,8 +123,8 @@ public String toString() { return String.format("%s (from: %s to: %s)", base, startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))); + default: + return base; } - return base; } } - diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java new file mode 100644 index 000000000..0b701c46d --- /dev/null +++ b/src/main/java/TaskList.java @@ -0,0 +1,66 @@ +import java.util.ArrayList; + +public class TaskList { + private ArrayList tasks; + + public TaskList() { + this.tasks = new ArrayList<>(); + } + + public TaskList(ArrayList tasks) { + this.tasks = tasks; + } + + public void addTask(Task task) { + tasks.add(task); + } + + /** + * Removes the user specified task from the list. + * @param index + * @return + * @throws PlopBotException + */ + public Task removeTask(int index) throws PlopBotException { + if (index < 0 || index >= tasks.size()) { + throw new PlopBotException("Invalid task index"); + } + return tasks.remove(index); + } + + /** + * Finds the matching tasks from the user's saved list of tasks. Case insensitive. + * @param keyword + * @return + */ + public ArrayList findTasks(String keyword) { + ArrayList matchingTasks = new ArrayList<>(); + for (Task task : tasks) { + if (task.getName().toLowerCase().contains(keyword.toLowerCase())) { + matchingTasks.add(task); + } + } + return matchingTasks; + } + + public ArrayList getTasks() { + return tasks; + } + + /** + * Getter method that returns the user specified task. + * @param index + * @return + * @throws PlopBotException + */ + public Task getTask(int index) throws PlopBotException { + if (index < 0 || index >= tasks.size()) { + throw new PlopBotException("Task index out of range."); + } + return tasks.get(index); + } + + public int size() { + return tasks.size(); + } +} \ No newline at end of file diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java new file mode 100644 index 000000000..98a18d18e --- /dev/null +++ b/src/main/java/Ui.java @@ -0,0 +1,108 @@ +import java.util.ArrayList; +import java.util.Scanner; + +public class Ui { + private static final String HORIZONTAL_LINE = "//" + "\u2500".repeat(50); + private static final String ECHO_LINE = " " + "\u2500".repeat(48); + private Scanner scanner; + + public Ui() { + scanner = new Scanner(System.in); + } + + /** + * Welcome message that runs upon program starting. + */ + public void showWelcome() { + System.out.println(HORIZONTAL_LINE); + System.out.println("Hello! I'm PlopBot."); + System.out.println("What can I do for you today?\n"); + } + + /** + * Exit message that runs upon program ending. + */ + public void showExit() { + System.out.println(HORIZONTAL_LINE); + System.out.println("Thank you for choosing PlopBot. Have a great day!\n"); + } + + /** + * Reads the user's input and cleans it for processing. + * @return + */ + public String readCommand() { + return scanner.nextLine().trim(); + } + + /** + * Following methods are the program's responses to the user's commands. + * @param tasks + */ + public void showTasks(ArrayList tasks) { + System.out.println(ECHO_LINE); + System.out.println(" Here are the tasks in your list:"); + for (int i = 0; i < tasks.size(); i++) { + System.out.println(" " + (i + 1) + "." + tasks.get(i)); + } + System.out.println(ECHO_LINE); + } + + public void showTaskAdded(Task task, int totalTasks) { + System.out.println(ECHO_LINE); + System.out.println(" Added: " + task); + System.out.println(" Now you have " + totalTasks + " tasks in the list."); + System.out.println(ECHO_LINE); + } + + public void showTaskRemoved(Task task, int totalTasks) { + System.out.println(ECHO_LINE); + System.out.println(" Removed: " + task); + System.out.println(" Now you have " + totalTasks + " tasks in the list."); + System.out.println(ECHO_LINE); + } + + public void showMatchingTasks(ArrayList tasks) { + System.out.println(ECHO_LINE); + if (tasks.isEmpty()) { + System.out.println(" No matching tasks found."); + } else { + System.out.println(" Here are the matching tasks in your list:"); + for (int i = 0; i < tasks.size(); i++) { + System.out.println(" " + (i + 1) + "." + tasks.get(i)); + } + } + System.out.println(ECHO_LINE); + } + + /** + * Following two methods mark and unmark tasks as complete/incomplete. + * @param task + */ + public void showTaskMarked(Task task) { + System.out.println(" I've marked this task as done:"); + System.out.println(" " + task.toString()); + } + + public void showTaskUnmarked(Task task) { + System.out.println(" I've unmarked this task:"); + System.out.println(" " + task.toString()); + } + + /** + * Prints relevant error message. + * @param message + */ + public void showError(String message) { + System.out.println(ECHO_LINE); + System.out.println(" Oops! " + message.replace("\n", "\n ")); + System.out.println(ECHO_LINE); + } + + /** + * File loading error message. + */ + public void showLoadingError() { + showError("Problem loading tasks from file. Starting with an empty task list."); + } +} \ No newline at end of file From d4fd50a45e4cfec7254927ccad3d77b60941d714 Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Fri, 11 Oct 2024 15:11:22 +0800 Subject: [PATCH 13/19] plopBot - A-UserGuide // Implemented & Tidied Up --- docs/README.md | 139 +++++++++++++++++++++++++++++++++++++----- src/main/java/Ui.java | 8 +-- 2 files changed, 129 insertions(+), 18 deletions(-) diff --git a/docs/README.md b/docs/README.md index 47b9f984f..8eedfaa0b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,30 +1,141 @@ -# Duke User Guide +# PlopBot User Guide -// Update the title above to match the actual product name +![PlopBot Screenshot](docs/Screenshot.png) -// Product screenshot goes here +PlopBot is a user-friendly task management application that helps you keep track of your to-dos, deadlines, and events. With its simple command-line interface, you can easily add, view, and manage your tasks. -// Product intro goes here -## Adding deadlines +## Adding Tasks -// Describe the action and its outcome. +### 1. Adding To-Dos -// Give examples of usage +To add a simple to-do task: -Example: `keyword (optional arguments)` + todo + +Example: 'todo Buy Groceries' -// A description of the expected outcome goes here +``` +Expected output: + Added: [T][ ] Buy Groceries + You now have 1 tasks in the list. +``` + +### 2. Adding Deadlines + +To add a task with a deadline: + + deadline /by + +Example: 'deadline Submit Report /by 2024-01-01' + +``` +Expected output: + Added: [D][ ] Submit Report (by: 2024-01-01) + You now have 2 tasks in the list. +``` + +Note: Dates can be in the format 'YYYY-MM-DD', or day names like 'Monday', 'Tue', etc. + +### 3. Adding Events + +To add an event: + + event /from /to + +Example: 'event Team meeting /from 2023-06-10 14:00 /to 2023-06-10 15:30' ``` -expected output +Expected output: + Added: [E][ ] Team meeting (from: 2023-06-10 14:00 to: 2023-06-10 15:30) + You now have 3 tasks in the list. ``` -## Feature ABC -// Feature details +## Viewing Tasks + +To view all your tasks, simply write: + + list + +``` +Expected output: + Here are the tasks in your list: + 1.[T][ ] Buy Groceries + 2.[D][ ] Submit Report (by: 2024-01-01) + 3.[E][ ] Team meeting (from: 2023-06-10 14:00 to: 2023-06-10 15:30) +``` -## Feature XYZ +## Marking Tasks as Complete -// Feature details \ No newline at end of file +To mark a task as 'done': + + mark + +Example: 'mark 1' + +``` +Expected output: + I've marked this task as done: + [T][X] Buy Groceries +``` + + +## Unmarking Tasks + +To unmark a task: + + unmark + +Example: 'unmark 1' + +``` +Expected output: + I've unmarked this task: + [T][ ] Buy Groceries +``` + + +## Deleting Tasks + +To delete a task: + + delete + +Example: 'delete 2' + +``` +Expected output: + Removed: [D][ ] Submit Report (by: 2024-01-01) + You now have 2 tasks in the list. +``` + + +## Finding Tasks + +To find tasks containing a keyword: + + find + +Example: 'find meeting' + +``` +Expected output: + Here are the matching tasks in your list: + 1.[E][ ] Team meeting (from: 2023-06-10 14:00 to: 2023-06-10 15:30) +``` + + +## Exiting PlopBot + +To exit the program, simply write: + + bye + exit + quit + +``` +Expected output: + Thank you for choosing PlopBot. Have a great day! +``` diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 98a18d18e..606769d73 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -51,14 +51,14 @@ public void showTasks(ArrayList tasks) { public void showTaskAdded(Task task, int totalTasks) { System.out.println(ECHO_LINE); System.out.println(" Added: " + task); - System.out.println(" Now you have " + totalTasks + " tasks in the list."); + System.out.println(" You now have " + totalTasks + " tasks in the list."); System.out.println(ECHO_LINE); } public void showTaskRemoved(Task task, int totalTasks) { System.out.println(ECHO_LINE); System.out.println(" Removed: " + task); - System.out.println(" Now you have " + totalTasks + " tasks in the list."); + System.out.println(" You now have " + totalTasks + " tasks in the list."); System.out.println(ECHO_LINE); } @@ -81,12 +81,12 @@ public void showMatchingTasks(ArrayList tasks) { */ public void showTaskMarked(Task task) { System.out.println(" I've marked this task as done:"); - System.out.println(" " + task.toString()); + System.out.println(" " + task.toString()); } public void showTaskUnmarked(Task task) { System.out.println(" I've unmarked this task:"); - System.out.println(" " + task.toString()); + System.out.println(" " + task.toString()); } /** From 34901440420d9099373b91fa2007fc1e52378126 Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Fri, 11 Oct 2024 15:12:04 +0800 Subject: [PATCH 14/19] plopBot - A-JavaDoc // Implemented & Tidied Up --- docs/Screenshot.png | Bin 0 -> 169749 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Screenshot.png diff --git a/docs/Screenshot.png b/docs/Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..d861db98b0572f1bf9a57aa92393481c4d4760b7 GIT binary patch literal 169749 zcmeFZby$>L*FHS70s7xZsn8H#)2k2qDb+_H7mU+qa=A&W;w=cIFU>Tu6*Io{oAeMT&k@B+M#Z@qGRH zt7@2X=hGigT(o<{1Y_aG4SG&zsPS;^DyD#|rgRhjGdfjv_UH9n6lBrx5)aDK+a_>f<9)ky*(eZx_L z!wRoEgY?ZJ-)5lOzn|k<}3X)^=M65m_5rbdj_=+3p zHufz@-y+HJ?EJkcsU-Z83-2#VvG-eMAk$uL33j|URU*1>cDa4wDw_gEkeDiwW%Fr5 z_^F|@3)5|h0u#+vvZtRF76^CT*t`|u$?{=i*YCav4br(ZO^liHjsAXg)Q$73<>l;m!|W-f z`J0agGG6H3#qkekj6IAw@`ASBGhy8|w%i`Z_q|(wlUL&Wef-Z9I6*# zpgt7J@$G>O(eN|olJ5r`(C|5eVC3A9j#P~hioo(+Y($1nF_$cgwV;EC}F9?C`ep) z`S+VdJ(Wp*K)Am`e14>vJdtH;^?I~PjfdZqO0p$2u_@%Q%f&zjL#^CoYZM7^)pZQC zlNRv6cj7#UCA?{Lm4=yiKlWS-Om>ragI1^d<^-)yCh|0e;eI3U-LH?>F1JSBsTql6 z=8n7!BfNrp(cTGb3=i?~(^NmPmSs|)H(|$2t@zZ9^jDkD9wv9oREg`VFrjX=o$JQg z3nr_UJLaaWBJXqkf^>4sA&POL8-ob_;Or*8gm|1{c%@zKGe-wL^7>k1$B(Dk>F*dt zU#gaJ4oBGS?8s?~Y7Ux`TNn9%ix3-lKjl;Q#M^Jb>*&g{*w7QCT?j`rk%|4#ja4x> zGFxodRqTOBiR2DSeXsUpx; z7WBY4Y7*U~k|H6!C1dqW;(n0M)%|C@A7MU`Fecg%nW&ct<;=Q$<<)XW8i=MTG>#xFg+zpBF#@F zGgl<9RMRuhMUzdFMx!{VL4z@0H;=pjS}u8BOfH>DZN@eH~$axg)9hjgvKj)u@%F)wrHC zhb>2iR;*U;0DU1*p`X2yJ>{D8+R{kONXz?L*PC=~bQSl)JDrn}B7&|GOVmp|@WUEK z*A>^|#fCwe^o8t}LAIX!!P+8(d}_5zhYx$dD&K|_hG(i?Nq?a64iGvzgpGzSihApqcfA7VV~h|ZVB^NvbrX5 z4f7TMwd;3NddrvYdJ)wpO(<*#^7CEbj)`~RUb~63v#^gf(R!fe=r);jzw@TDpt0G+ z{Rv0Y`xi~Q+usaKhcgdHnI>3Qcb0Z+*TbAvKP97V?CW8XMJc*^2y6S$DcCtpxcPbSO6bjx(FW4#Ob5fBv+4(s^v=)?05 zcv2QpR8r+q$flwu-eBb?B)2}^(t0xXfTpB+mD_us#eXZPoy#0{vmrrKbY>WoE1ZMA&s4~}$UCkwNstKxmUN2r>-gcg2Y<8)~KQy)E2C@ne!>YrgrFbq; z&dV+_&gw421pJSOKMvezR?kviA{&13T*gpcJi91)gp25sW7qBNYxDebwoMF&ZXsc_ zl&zmIQ-j)qhEesH4Tb7`pNxkx^QMsFlA{DJrM4yy^R&AToaho;@>)731b>rH z?ByBsS^ro)z7w>tmZY4JDt?N%n*Byo#*vbV!^+6&{lkfmEsfZxYWf9w?3@kNUu}D` zz8u_d0vp*Zw!nD1@R7E2dSCAQg57qt_3s3Y;i&SH!6x&ic9Gaxj>)Q^s#{gl%?gt- zqT1F4tOk1?@XyOn_;>l4Z6}J7>-K#T6o<3m(eNm^;oQ`m(hA2wvCe7c)v{@?On-A< zpmwA35Hub%MRvQRI+d+f>PuP5i8zUl_U9+QtJrHAR>V}5o2>NOhm4N+nVo2rjhlEc z?H7wa60LD-UbicBD$X4?ud?;z&|}M88y7c4%!y-ozh0(YuIO9oi(AL>X7*0qP#kwR z^B+RRVufPC@i>UJ=-$w=TEnO<_;v*?1l;(U1>y8W;$z=e`+XLl&Ag<$qZFY<-z!mZ zA~bjKaVGdWzA|AFS@NoAjMK7H`)q7&c1v=@0Cn$()3w!a)n!ta%9Y%s_mXxb+;{61 z4nI*|rljVoF|@OCx0l?lUD}$d{Fu zgQAX1V^wgMj~zw&b^79!E)I<~>JJxo#yt_|5DM%0lU&{=2Wdydqms+DV>RL8gdRgE zmi3{tcZ%Qf4))@bWH_xme!Tc)pQZd# zMe{Eg0z?i@>3i``9UL&C@&s_~86QIb9bBgn}9Uiy^PZvcsjV6dT=_Rqt=U;O7m zF)sAnf0M=Eg8u7MAZZDFF|L1CO#;76ANw0vNLp)YWexBOwi)^da}E4q`TG^T#}M`D z?NCmEKwuDgX(RV>GvVtR8h0<>3~Xb4j(PthtkcwD0%cfGQIqXmQ~S|%@EalpOWkm4ps=fO zJ;Cef$u%1L_o`tpNnj8R>_7el%CdJqg2*!#`Et_z$0ZExBnSqn1NNT}fSas#F|p;S zksM$$=b!<9{2_r((f<#9aKQpHdL}U;|8Y0?7lU;DOt=59cF?CEkYU-C;`WWZMQF>h z5jlXGJU#YjUV19tknyU7_sop>SOdKXB5A!A(<|00Mn%SY-rgC_QGKfrMC9!wf8EUT ztn@T6_ei1E0|g0&51ViX8hpL=OYT25^jgXkupKCPlm3a`csFF|;u)z>XTs{S;(y9Lt>=87V!(sN?(~6TW67u98}*2hDyNzM z!^m7QOYr1G;L8szli{)ajuT_^U8x&2BO02TFfkT)~`30rVll@VL14V|S`VBtShR&_b0+yXgZ_-}~Hz=n{aGt*5?HExt z^rv9g`GFhJF{06@7~T?b{1g#^n@SXQaPng(xQkdq68+}YBts@a^f4k^LCp8aHBs2b zV(Ib1Ic$;~4|GqX4z-1JV8amon2C_ANOWwdW(vI@F1hvjQlb0C1oK>b=O0prsRSNx zG)_+V>~y3(b)m&Pr|}MEhY@A^>51-SgYS4|1g-G*317r`)aB>GDG+)2j6Vf(1?ODk zBkb9S*sMCxScv>WXR`gl=F~u)r%R%!d(n!G`g5t0mW{?!)WF+25o)EKL)r_v@2y^P z7}W`x`Ic}A(Mzr~Oi23pZ64~bZV;aOouGao@ERo#kWCvA4^XIX%frQqGhu?Ofq3*RA#DHaJ|zH8sII(p7_ZS)QVKKlNAB&< z#Tmz9gFP2@ulY}Dpx3&45$7s#(JpiXW?=DN)#(aq*w?FDAIcgqfI40Z@PG?IjzN^B$puBNu z|G-t2{j7$@WX6}KfC?Y4mf7$1YgfL0O)XfyF=*^>sHXbye>veKo9CrTp}H;|{PsZm zaE@*?9CtcYP2o=7z~_1*-mpnEgmp$MkbLDLkq=<<-?x+cPaB1S&1xI~k)Ip9$SnPz z*8=0g_Wn$GcmK>}RStobVr>j(xpGG2pV?snOco}kch0odMhS(f9m1v0pIKU{1qLP) zH`XihGp%VfKro`(iWwE2omnPs0upElRs8Ci*3!1Yur>VXRBxTJ56^VLT)%yI5OJop z7_=GPUU^1(^-rS<4S;OdglX+wlY>-R;W|*u{n%Jd!?<`eZ!&NYtMNlTPe|E z(q~7h&^M@{TNu9XV-0kZfXn^p8!_oWNx@}guz;GIsA|Hc(aFA-Y(xIG zre?(NXzBX#b~~5-aH)BD#nbvz8*`m3a@5f_rD;^5raKbnDnVotc&uW7 z#I_c1YUzfEr2h}cC8vK4yRV8*djM&ek?FjslsT_sLev8Za%&8(O&t<(dE)PLGXApIWxB2S7Cr z0lSpo825DmZ5k??scoxFR-RpCyxJZY`X_*_!KU^Jn5`IfC?l3QfEG)oFS?Ca& zeR4z;xmwmGQL$1~OQE^kd>OiBLUy@k>IseA(e9FiX6A&Xk>?AfE(>gWj@PD-g+|=# zJ%T4T^?IsL>E7yaZ!DLwgFkY`*i0BN;m;jEj2S{4ne*bd+u*ap%83zxiGCE7s{}Bv zZVjiY))#F+`oRAy?p z<{m$}5P5(lh6a!vdhb~rFSFKH^#Y4wkA+9{Uci222w26n-0YN6VCi})#+(&x z2aA$@x4uyBZ+;?PpuNezasK(xk1(kyF4!A(BC$=FHU-BZ`FqwD-e^A<)F+R%rKEVHU$1F zG}dVu`|i2Av6f+~QX}SGDND!~BPd)i)NslDE|~yWG$uJYw1-{r_7g&Rf5lYabT1RF z7m>H5qHYrBh*);d?3old-Rn1G*8XnC$9N~az(tF0L+Dg+AK>47--D^A6Iot|R01#_ z*F_aXfYCQ0(iU2m(7lbx$v&@i_{zY*8<`io{CRVsHI;m}1MKzp%BIIW7m}9-RoTfd zL8-(%-l3iUgK_n^te3gWI-}Jm|#NV zgO|=gbS0gL>qE*6Xo#Tftp+y2qk!c(%2|(cH`T!atwOzOyZMg9g@$tNLw;X}o%xOz zoQ-aTG-(g76Koj|?Rd8g_Fw(PmO=^bu@yh@14tpu=|LUr6{g}*Hrz%x4U$mPv=}Iv z2l}0jyiHUBe5KB0o@ELq(+x1SDNmN}bVOwH#ijAuO52xj?Ozump0gD_QhwUFW5E>1e z#fTzj(;D(WJ)YEi{5>Fb+3QWT?T8-yVz_N2tyFGVm9WJ!pkxBwimuW(u^u32Us?8q6o$6d6ENh=}~$t|}(;q5rcVX?nDoV6vgtY8%I`E=8Fvk7k$ zfU%8bv;lE%x^^nAHh38dPiC8}xP?KgB$^M2*Q;Vd<6%z7OSVSmF-fC<+jBk8Qv9o| za@iT{Y&Jb_G0bDufri%W-ScW;Tg>1zR8V@&=Y$1ytQfXf+IpCEH{t#%SmklIQ-9To z(@?4DIP*VTvSmf+HbkBqr;8jayp-|kZXkA8`okU>%KoD#`o!&CR$AW+SRL!OWGB>d zJYjp56tf;182V*Ci!^S&mo}79OD3s)N7jP$kNmy&OC>J@kCt*MN!X>-J!B6)1d+e- zcn6ay@7SeFe2^~Bfgg{~HPTuu1{j6W!cn(T`?c=d#PD`}2nz|h=JvAV()UcN!b^Ra za)pe2zF@`Ga$el|YwKT$pmD70xu*0wTI#LT3r$XDc!a7TmH#%whVh26A8>5Ph8Xa@ z%&&KGJwHx`$X9R{|BkrQB(Pbp!JL;e<7ylr)RI#*D@FP6wUvUh@I)DDnhCEJ4#C)4 zg~@(K7iTg0Q?(;!xN28<25Vqga(M)|-M+_@98nm{2X}Z)ez)9MsMjJOykL|^!ArgQ zzCqpWLL`~9Rz6`lq)l)rH=L^lIfEnA7o^zqeAUUTuVWqKJIL_FpL7lr<)H~nNMlL{QC%|bMkz8O5DX6wURPlc={ul zc+8{)m0ULiPeyS%zoxp=Bn~fAjiae=bZj`Z5Y!4iJ$N^VSda<$T-?O-JHY9SLmxOF zVk>H6>2cZUW&B7om#N2?gy=K0gDWi2h9@2KW#zuwH)uAh^iKJh4i*zWiB%RQm$gAO z6|Y5AlTtnWv0v;AY73M0aKfLuF*_b2-yo$|PoY$^A0ctX!uaeB9PDIlIVOc$zv1`5 zBZ{0VNJ#TYH(wE)ISgZ~@f69m!6`+rf=s~qyIb>ij>`9wzo6H5Q!kcq) zF*B$TQP>;nSgyLwTWC{6K!HSV_;*z?zpXnxANinr8}oOxyDXnJjmh--V_A2>^~ZKW zVrfS^gwAz{fSfI=qUw2Se>aC}z#lnKkkzyFVags`4Md|Y(FL_UDgG7Fi_r6&M&ZXT z1d%xeq0Jz{!nUZ|q9$L<(9mwX9IEO!0V`09WiM$&t~pX=KifuKheb)X6a22xa;^tH zKOeams3;Z4;CIl(XJQ*a>ksSKVaAt>jtZhJ9@gdy%fsCg+o;>INxoN>M(cMl<$wf$ z(m?R7o~ULwJ| z9a()@N;Gkmom0v{;;PtkdgV6e0RKEj9?V1tcy2%NSJjpMZ4EH6o$=8M9{AbL9M|}4 zfxO42aX;_I9eltdc2zI5Nya4Xk~l8#cWEva{E(|f!E1K6xha=YDx5*UE*_23%=f^~ z8(ArCOnF&oLDF;gssdncBI=R*qg111#sxLYc@+RZ)^G|8t5;ZT(6rcs*=V*qoqAvp z#T)?+s_ZF)pU@0uxcI)#sK-p$xX1;*gHlUv;Ki=XU zW;yp}T_9NmUTReDH5@LwZuPoVHrOJMpc8Of+XJzf7q2Hh3`&IG?oDP*T}^OuJpn_E z=Z%22tW0e^V+?Uzw_pDa0Yrm{D^!!!H7?|+|c7KJ2Pxovy>Gs zzvs4AJ~-ed>Cs{vsm)cDt!q0{>FD@6P$9U^%X8B2c&&Uw6bKOi$uhDiBOt8kcA2o} z5>W5?@g4XWdKA(}ZUY6zZd0Lkq(iSEoXZ zT@|)6pGnoYlEPOUZ>qOr5KQ)FdG;&^pNkK1~l)}I4r^mAOqD7n2jrB{}a}pBO?X$joh=f zb4)NmqF}OyT>af~gvp?Jjm72khv&$y11d%6`u^`01Ot2fJPEY*`X?`JQl^JPrcV=a z1&nH3bH`nKmCmyY%gFqp=~!=r9?bi&GJkjb{<0H+JoqHg8;la!f2u~X`vcoZ!1$>4 zU^o6a3h1ZOLIH!*_N*)ScQx}@hiN?6fGUy|SHb^d|Dmt3yaj9w&$NrpnJYPjXxc}W zIQkFM^;fTLEMV%P2T9bw8@0c#{C7%!uiyW1mC~`807VpA2%y(G>4fJ3oqmN~g8n-2 zgV7!`hyVVte#q8uw^`R zv0FUDJ2!q8gg0~n-^5)jThT>rTVPu6jc_~kJ)Dc%-y1Z(6YFk3#cwf`I!1e8deqz-~FcjO6+kpTThO9}z{K zth+asCA*EZ8jipR4K2MqG(><0WDkgXxAq9}{rLKm?RE#xNnb>HQ!rlb906z5hxu;_ zGqE6&a(88-zLrDh-G@%$1=`9YgVQzOdt^y2Y8U93GRlUsXmeBddm{efm!H?n19EG0 z`pb(KBZ7I!6_bA6q6a^M4c*?#hLwUi*_{|T`{80eVssKUfcDCh4vl@bzM!ePq*0v& zyFi7L6NQh*3y%KBiN|AzK@@PA?V3ZW_5rE%8WZ_T9S1()AQ6GRd8#Kq#yalQLMH(5 zcBK#SfomL#_ii;^#F38<7u%K_^;#($uN*uI?o*5*g9;zczA6Mjt%$$f`1DA-_}NLQ zs!6Kveu<&`czF&lP!x&Q<^5V^z4t^rM#^pD-0xsAd0LY;rjtrGMo~kz9B^?S0LR1V z7;qN#nkT=AX{!KCut#iSeSo(p`nrX|f6T~hiMg_1;7Wc`O`g8rDH5^PDZ1g>e)}m= zxgYXqSXmPJ0PUQPiy-d=Fe&x0P>xjC+oFNt(TMw~z2M4dWwAFBAgGL5E9)<_t*?5I z`v-54(r=2r_!($GsEuPA`R-{V{B|l<7T#O;8bG=m!{?q{0X(;kIH2C24Aj7F-QU$@ z!`H`#1%@A78Z1?1>-XKw%P(p>V8?g67XC{U=4b?VV~~d3a0Y&D?FtRAMW{FkO=U@P z7T0YnAn-C#Kl-rN&@@$L#C|{b@+dOG|AcYv78rWdErlP%{s+PKC0{5yZz1jp2VF%y zms1OnHnhdPKt22X6u5iCLnUU&xR1_QfN3W+#9474PDSlrr|i=%0^mNU9vI}FKgDMMpLVVt*GkbYXN&3>Wn?Gx7~v6^K73tGxk7VPWqqL8~Pp2t$*y2JoQnh z3;;yr2M0j?@ja1*IS-r1RX;T=hjxERrv9pl{E+IsUadFzD4X}dQ)=>Pt!gG57+FCy z8?2x2bWbCe(#Ye=!iVVPvJHlODRYO(hI&nH?NNXiqkQoNJtoza#Wihx$=DveExN|q z7|VY-x2@RmMAOfaWXxv)-x=Aqg2uYRvz^JJgDHUJ{eU`b(_B7s#*ynoDh5)Givst& z1jK8qA;C5Iw9a3ztn(~W2TQ}Vemufu(IQ~IAW|Sk_9M3#_IP~(O&!K)ZgAHRK39@z zFXmRR7+0YxZ`pNXC^fqW7=Qy8ylvwg4 za!03s%_7l$zQP*1Xuv5|Y>bcvQ6ED?$&<}b<(=WobY(-{h<%5 z2o>LFC!k=N>RWM~8_Nu6z;}a2-W?~zq$o&hcB-F^zC(59=;UcCCbRoEJWe#izrM0y zT>_0-*j`k2o~j2v(RZxx&jL8e*dDc|#v?H63CKa?p+-PviRlLyPk{N{4Iav?l`qP` zvp4i_NFWa~YV0I`>|ex#XQ&+x3BffT2kUQO^4Zt{r>#%P4nSz<{2d%QTq4@V=9yM) z|GdhVjCW)ugz|!fQIOQODI83)ugCL=jwf+5v66s9)ibpF7;TI}C7#2Us~Dj-wm6Dd zo!xMVtLIp>bu?h4+hEvW5p8Tdnd4fbs&p<_z;{7X>*(#JAr?W5FxuA!;kkc2c{)+( z4+g&0dOfwET)CTE)z|NFwO@?*>hbl(1#OKz%NI|6}Wh# z{J!n`;D%xTL4z4Rm{qqDzs!IK<)yC4W{B3f7S_ugSrwL9~&6w~TnPNd?dcWaYRj-)n4(>jh>!I;v(EPH#2Q`>qsb zI6t^9ZZH8@n&JH0^;%TQVq4O-{dqC%cbaGo^YyM+$HgEZh#=;EI9LJz^Y0j*K!-=`vkw*tQScp_175??|JFa0#EeS zZkNYfaulAcxXw6;x-YFW9l040!|)AQa$}zBse?IQq z&ass1I_C}|F@sARF^qYsdvwsPb<>#171nvf7wh)+2(==Jyc;q%KS7YZq$Yuoiic^< z{nG`(cs+TUm^UD_&*ASDFbh}>cUi^jr+l4GJ_N;hI^thh&HXE^k{d@~W?5;`9jOnk za9IX?_NUV$+WsjmKflL?r*^kD$}v-pR{OnmS|!cvD~Tn40Z{627!w9*amdd1Ohv=A z+mlwTH{YzYdo2!1!Y}kxE&@Ncs#jg{Qn}Dp)G3BxaHWMirrM!tVO%|<2dA{6dGy840NM0wWKuT)!GgYKDJEi}KS%kP<}vfgDW zThA>SSOdJt~0cv>=nSY zy64>>H7RK-+4ymw@zjXwrb$5JRsD?{^>KBGSikHX+c`k0AZyEh<)w57V1pBFH5Lj0 zl_-Tc%gN*^G8G-Yb1=~gTS}8O;-?6rkJ^pQnMc^HuWwwNImH^MA?eyE*b*iZUAei5qr>NJ)^f8GvN%wr3Y(`8)>1fJ(4y!{JGd+}&ypQ|Ei!Vk!= z2m0}-LP#4y5F?MMTzAPd;E>~c_pZiePSj}pGvAl;?;sm!b;5fC-tKg=;K-r63N18k z-yn_@15`nl|M1eIOZOg*$GkVfh+2he?4vU4v{v5lbc%L~4_V+z2@IKFArdl9O7-JB z7tVPj<%uI7)uY~RFci~oBiVNk$tzIEh5q_nhjdiNR&ih6y7E`sJ$C3z{$#;*73IM)7TeXgpbs{EImK6M%3ErHGhl_#F)D zV7|nb6Bw~#Jnl_J%|77U57P5uIp;j}n6hS_e|fU#hD1!?u5qw2I=;7FjxZj=bM;j$ z$Q9sMGEeU#!D2EkmMrkUvZGdc33@|wH4e$f58GBeKclOb&mSg}9;nmbreK)NH?)mI zY48KHV!|@2aqhKt)GgwJ$Cw7#a$24DV!1x(v%pCAcNF(7XQAqkVUqQL(O|4`|1I7X zb_W^-3CXJ5)$_HQe~tDGlrr;&@9*AO0o=oUGg*(IGxaK`HZS5P@uE%P8&1*JTQc(g zLpCIL`CsfuVP2nr=3L-_k$%~Gx`haRdjirpsw@CAE_XMW+Z)RUL@Q6>h7NtD(l-W& z#2s!gOJXv?@r3Ykp5Y(%lS4zMciQt?()Kp!ic_)WPN;t&j4(y?u)g+Pm7W~%1BTRL z;5ERAUv~rESoAKZ7A*<#?>{?o%MbD1=c_iQ@5Updji*21pA?M$;@Y#7*Fb9)=|OzC zH(S}gm&Dur#=IrjAVYIwJWp(may2H?;R!MrHqig2IdNI!*sH%>8WE$$>|SzS4|Tt0 z=t1J=EdT-P0>0ug`21*&>0y_~@3h&p^p%)Zm^kJFFo{EMyIiFELn=BKPZXXGu`k+= zDdqgSpgpvL2q)e~p=Y&oUhi~b>nIl^fm~$})(%L%Z2wE`zru_c44|cu*29I=4OC8& zq<=$Vr$%qLjfh?*psqI@aA_{241ruU!QiJB+nd=y^k{@cb584UE-}6N6r#O|!Tc5L z$*EotyX}(*05=zCQ)Lcse3-ewa|{~j!bz>$k#<6wp42-_AC@&>F}<#qof(+*{s6dT zf7@W(3(j%zY%sMP)CqB*S#xN~^%Re*3PquHLGPjGftPa3HhGze>T&4OW89#D16k@m zo!}2P^Xxd*1WFvV`$wnSag9SDkg|fCG7zfK9>>*Hb?d^t!i3Ohz}j8(2Jt`MiaUo_ z{}KrC&fnr7K#Sw|!!d@=M!F}yr?CROK|OWgHKj>2JL2m*&VXqB58V14qZP||Jl9jQ zJX8}E@-Wq7I;6<_#Q-^eB(3|HQ4&53|#SF$L zIeOKI+P*-xJSq9j`7br0tq>sXKz&mW>&~%ydTz`oqeg#ft9rpb`n@Rnk$xPNUJtgp z*Ue+2g3nW>BpN>&`Nt!a$x@B|DxrHDl171?<*t1~0>ix>#|X1uk+-y|^B}9L2U!IWzgkW*k1;O!co~@B@lUxm2a6H<$?lcoZi;ve zuALxAc*-zjV^K&!(;myANQnTHV}ng^+)5Ym0`7?cT`o>=bHkQ}qpEfp2(ydKc)516sQ)cC;P(Zd z!(k$eSNZO(%|Xj|TdRR=|44A`;cZbsH;h9{^87oz-Z(|No?4w;;5tC3;nZy1+m2y; zzIR5Bbj39yi)q2%dDGCr+ID zD6)MFWV~5;2?}|9T;@7MLW8Kkbg!W~r$u5`&_HzemkhvW)Iw#sk11*+^g@!HnyD%PN53E@jYY5`b?=5T z9RO#a1pr-`)MA@SpVTj6{_)XyPejQ5+mVovPMWL}Ks)r5ALvWpO%m!+Vk_8#XWkqv z_9>IZD(lLGbtnmr&o*1PO;F6Oq@^007D)m>VRb>f!Z`9sn|`=4f5QLNxA{tI^Bn(A zKBqeXJ?+)%g_JCR1?o^u;<1U!6BJ$rvtn!&r{HkbW8fM5$d&vNBO5`#Mz4nSTudCP zM@)?FD&?Sb5K(Pu157rLZ-NYkN|C{>3DksgM+4YF$5mQ{!+}xv!x7sz_7;9L1uGFJ zu8mf93GD*J_izvioNtQ)gb~`>ql|=uP`mzEhAa_6m`oy;AS-GW1aREO^Z7IjqA)#e zxcSW6$OL`M2j*xNWJme#h3F+&5fX9N{b;!FKe_=^vBTH%gIlY% z<77IAOHCVQH};Q!En(!U!V2{5Lch26hUw|CcIwkS_j1|I%9HKzkQvMFuHu`64D-lK z`#Wr-H?5E>h;LI*8P;(Tnu?|9tQ2t>-+f_^uwNTx6B|fO13wJ+L0@#V7p$M5%I3R- z<3ydp4Wpj9ZV$@wT6gzj+kn>LGD}Lh-R)}B8%}|&WHJ)_LF^v5`o*Da-y#P+@JsEoG>W>viwcni;w?~ z~hh7+?Lj)eEcWx4lrb(5MabVrz@oVY~LpxQ5EdUmS+T_DVJ|6QQ}o}j;u*MCnC&~yLaR&lT|cyczc`*h&h z<`(vO1xtv+g4WV3C!xr*CWh~R;8#p0{*JuD8szh&Xr)-e@+^YHg)uEF^Nwo-1xPQp z;H!$3TBFzxvS?J>6sBs*jjX`joU3V_7|wxuOSSl@A3PqmGd9Q$Fl8BbqfC? zP5yqz@)Y&}gVgplHCx6PE)F@H0bM(~l(#WYGgsNkuefxXIe|kbZ-FmUGvw))Gf07ylLXZ4eJLU+ka?O_;i_dbpAh$grtxi_ zWnO>te6`Gie+o6;?5B^1eY?sXUcJ{(H9qv`8u1bsS(z$Pns?^)+C4oSsIlEDG@!Vv zgrnY!N8=%YATK=&2illP6azA{`x(hihHd7W_;aUJVtrEaJHPSSMo|xzYa)!d^>&r=WWH8? zrXS`{j+xerJ)0IX)J&YnjMy_HC{nBY*db8(Z)t7h`#NNrEmyqm~0VTd&a7&IUjHn?Gm2 z!h@3H7&)7?K`n3g()q%~q31HWOF^lHZ|`=fVWdoQx_x1-m{Vx5HTj>4USRAPQF;Aw zdM$L?;S1dEc6pg!h?}zJ3zy5F;+Hz>RDn$5W;Za^d&T%UHvHIHxO5LRWBTh}4d0U1 zOpoPIQVWx|QR9xg#^3m!qO~Y1&#k?1X1#rU`MzG!1BR|Yvv|>fOfD8Ch7n~%P&i}3 zhDEZ|7iE@S%zWp(gi?jVh24?23APtYH%n@2%MP%Z+84A(r%MWX^dqAmPN?Y@MMv`2 zo|PO9T5=^FlsdI3Y%QDdr&cQ>`nG-M)9?gcf$Wjkup45qe8HOqaSKtYYG#q&ib|G+ zI|^ErbqyWrlATWq)@whA*wvMuJk~3k6-v@Pb0i{f2I_#S!qxX5^xPb5Y+6fp4?hNm zC0XR+ve^6Aetn%WP4?QNa(;Zo(4sV=cef$mu0m+Tj*lQqL%*Y?I{R>MVNOZ4pyV!o zxxp4g%O5$Ge-&vlsiVvJ5|I?Ty&A=@WsJ4;_ePsAnfeRWvFg{$ozn(A8Txg4Tj7Lx zug#6imw9YUn|W*}(wp^)QmC=M2#R)0Z{!s&cht0N*tEwK^zM+J1(Mf5(OgEBjLo|~ zUoE0ok&PJ}Tany#6KRR!JWj=enPPP%AvNpum0V~Gq6ycl|3g8)Eqe(OmO9q^qE&t~ zxZAO6dkj*YLt9Of(kFr1YQO#?hxWE1*2-C6N`?6nPp;4F?F=W|SbrDY;OE!Ay&{p) znq4vjPCGGAJ86=sB_Bw=3pA>KB{=C6SgufhO;vAGC+}`Q8{W&k3U5hXy&|7@o9b=P zEe`EEv4gWnx|Q^q1nfIU|F+q2s=nXdoDvK5Ae_rK0?CeR-o;4+l|=zhs`yv#=I?3W zW2<{`i@;W(Qbv}e52$2&gx+)wl07kwdUNmbNuk~eMv5mx1}2j+Hk-dJhR2^dQRt;1 z=RnJ?#ykdo(4pququFj_H$};>RrX_{frI|b39uKPTvDNV{FI6O`CAn=sfB~Z6%=%@ z^^{oGxi^rZN*2Q{?#6a>m4==trPaAN{3P#n=<$li`1kfz*Rq#siRX`}m9&{V1g0q| zt+3iQ4z_I<>u8w0XtldxICordy!lB{q;o9SA)K=fIK{3 zvKnN?@yw1#`xCejbWayUnTBkQNcU_>}7Eh^7SGMyP z5%AN&iec{l0Wzh&C|dugorOWq1II8*MKStpE%tqK;25%TCHxA8|5G;!BSkxg>9H(~ zXB|UU;Ff6_@%-}ie>#Rt=ST(vUi|p^T>wz|TO6vgXTZcWP2eOJ)IA74! z-kZ=fI*246IEgx*t+Z#(b`%th=6ty{)!DPC1$^j|?I__hGhmVeBOtX2;W?{I3xKHZ z&oR548MRyx7y(lssrgxdSO;CX*`_#uX4H|`U{Dm>q27UzMUQQ z|HZ}n?*gG0=!#7)sEP;G^0gm?T{kAiL0P=X&O#S)G+xV03-Z(PQ*<$W@CoMB&uVdE zKY#3FWO~=wQg8Msh>!;RoH!GLYW6CyLrBP7r$esmZA{ePp8b&i`;LfY`nUR>i9Dh7 zY5?pd*lW)pwUyIgL>aIa-2O?{07h#H><;THczX<*gZV7}?i6*bA3-mUxzJthVyXX6 zQM(E~Z3F{?c!L35s!zC0@Mx0Z-;(x=tB9u(E_Wj6?O$C}!r$I%=LW3W6zGq&E{KKx z%z^j}3UZnJ%sPK6lV9T|=LAWXAK9Kw|3`u00DFTCGAAuTGFLgewyrsX!4XY|ziCr8ZD;#{R5$YP{AtXZfGxnTY!@m8x!(im z!wS9lty{eImgfeP69qaN4(Ai~j^AMhreV_9y$ggd_qC$SgwfT}-F8Bp zX)ttJ97wDL1vU2QLZuG|tPAfze*TESq>qbs+G7IB5s*n{2hwJ9lQ7kyR<@h?+1)6`*K-xz%yRIPx($;DU>-R@0JhtxBNceaZ8n};GUq2q- z@LL!_e{qL^#kY9$xG>JIf`r(=%cVy_x%Bf}!XW%;Na6r$!QDX4EE)W6<~rHzQM)h4 z^<>v5f&f-nURO{?_CM9y9lPeBR`0X~#O$(-%err3gJ2i&D9G2^Hu?r)Ar6!)9kTFYTXK?jY= z)Ivr1-$Po{q=M~0Wb{1+hu#pSv5zUbp4hC9amD#O8Ghs#D9hV#!efXwsQbGY7OHrx zf208wK=;x^`d2St!OoGy99LLdVUT)OIE;^gI%;c>yL&^BYav1&W@y*P-<8U@|1AIm zYb-AfgOtvo==dolq*35!$xxsgkZm+;tffE5tToXwEQLi%ZdyyJ@wjB*@qLEKXTToB zUdDi{4uiT_L(fkjJs#d9B_Y|asnz+D!-xTC{7i2|LRevMnhXR%TB|-NJRXZDrS6r+ zsmk;Q*^L?EUrQ$_L>s%00&4IdHZK|iL z2954iQXre~K*INE2#DSiUF}nDKRQr2;QbI(i>@OPpj^+p*5ZWy8thF8J>R{TQ4=q! zab1YKP@x~OJ<=(#5*=!+I~l>Y^X{#Zhi8J&d3U*VJos)GapZ1q;6`Ij=S#;?kQTa( z`?Y@`osSGE!k#A8=9z!~Cogoy!0?X7#xgzn>qPWH9*Uu0B|1TMOS?IoE|J%`Pz+S; zj)h8&AV6rXsN5JBY5O=sH>VZ&#A;yl+oO{__%wtq2wUzWDCp-79y?X_^UI;_c+KxJ z^LS}T-Atr@tC?mWL=C-UW`FW_d=Jq(N%J8Y1}P5j6GEzK`fwL=+v(3CS&qBTIP$w; zsVEf5fS@EL zN=8uuL9%245fnr~K#(9HIZ2k-phyw~ksNHvNkGX-Q9;R|k~2z_oMY4d&F#_WJf8F3 zTld$kTi^Yv{9~a_@3q&OYtAvp7!yd|rU~(E+UKiJ?GmBe=_Pe>OCxH12_LV zzxmg?udt9etZ%0r2yXt#+N;@%D%PRk?CI~s9oduzl+HNc6@U>++D*S@y^E$I$} zm&>A55U+wk-Qn_0{hkG+| z&G|6`vke7WQ{!vbw?SIsckYARv1qNO%9t4?iyP9D^m;>Y?2uhjO)Y zu`WqWGYKO0it**h@ABp3Iwb%hGE32Ws z2I=c^1$KlkK%G8!iEJ8?NV!X|f0m0Gre~es(f`DX9R&S?ehVFn+UgQ(xH@#84Ytrs z=elRFZH)ri-#lNp)KO=;8lIZRMduKGiy@j~2k*EQUB+<^*I?XLtBrx|d9|UQKtj^M>^9CVi3A zu=2gz9Cg+eM4T08J2a2ILBSJmivf9cQgSj%Lz%4qDv)pj!u9qa@_%nT=5^wnmp|D=P@}4`ZlXN;c zEv;}}&G+O4*Cy=lJ(HGM21;XP!Ex+@RlLJu<#d(LhE=jdkB<3{NHw8_BoV$OO2O=) z!5Pt1!m|Xvf<20xt#nwkb*twEX0zC+^|>v>Yj6)i#6V%jnd#nNm|P%>uuKYwHSlOc zH5c{P*7E&a)p4>{*^dJrL}AmZB@KL)@~_p5Ju462!fN{7L=zaSj>ghAOaMR1DW9um zVWi{L^ww(ds`**63L|6zH{Bk87QFymqt1SF^unhdwf$@%8P^u>LQHP&weywrf-xb| zJ6Uzzv|ONQs6iE9reh&q-APu9i{NdhUE z@B9p|c6|qeu@>4S#6s74@;Nt{vFU~=WJ$>G&*nxYgRcOCI9MGF&Fc9?-^9UU?0FO$ z#yaA%NdlN2bVt7Ei{g?=wX^l;_>pMg;;zy1)XRN$wDwZFq0Pl>{k)2>V*^;W49`wB zmljriNCH+I;<(mw?19az_lGx8xGmJ;tN9*F0|WZLTVx+At41x*h)U*jirnxby&ml4 zrHx}Q(rKg-_&b>9Gs=xLlaQgg;~G|WC?cckegOk4mwp~cZeE!p=O5~xAjPqs%$*t? z;TEXWjnD(nm4e!8-Fq|P>+|Vv*JfJ^qvjYCaLjR)xGeSYXtprcyD?}-rQ)gv^NOzzL1ze0u=$ztwU$ zKEJAacyjkw(5lw*lY4mvghb7I(lgUni1?5F;!pS;22mtOYV$gX*Heta0ed^tf>F!AYGAZFe%FA17S6C&W;U)ik9_qpMcA%1149&JY}p=!&+l zEF3i~V?m8w0nS`3`>5kd;JIl>%#nHGW-tj;T1#rY+244d#UQq9o!jjF5dUAtL=MqnRo^{tx7OSC4o5J@uIIfE_lqF2p zBnwU+q>R3hyC>Z^eI-X>{bHF7doVLTBI-NQvKgAUb0k;!lPZ}9|$k^)}S;P=z8s}M# zPdH}`Nk3ZUokX5jX9i>Q3(fTdUKSj#s$&c+7iY86ehQ98oku|4k(qODU#XL2Hr)Bt zzTb1nYbt%xuj-zU8uqBi?rT`=5^AqcJN<&bhI4^LH(BF@bFtb1iHg&xhqVsbcZ1lY zZ`xx&99E35EnA}67q zr%eL?Ri;g{dB{DGGmFu%u%c#>OtNSV$kE8Edd&h=QTck^#V^ zpAx^U=NRmQ7SxL1^9QLHr+0SKDo{u%@rmi_>%$uRu-}Gl1U)J=@7Se<2|MOfmgtk! zdJt`L@)CVtG9I_>B|YQu&aS^GgT!|PF)}c#hiJc~cL}u!9LR#wN4Wbg+G_C|?&)@? zuw!#(?hoJkOu}8zU&IWCUmfr3+7?ZSZ%OpKW;fgZEct_vkNGssROzCdit2E> z5>+poLTzA?xmM>A^P4L*$2YNmUH3oq1ATT6$$RAQDQtz~1a8eK{f;1fKOu@jqeC3$ z0y4jKGn>0)cy&HfSRr(UFsnIbT*m0sdZDKlg$vaevh&3%YJU~kr5DvW3p%+~p6QJ_ zjDflas_j$C!P6iS>1FoQKmaYWL;Z@$1$+DWc+lF zReXNOJ4;9Z&N7XBT3&7AuEgWAiDh`&V1)T5#e>GbIVZNG5P+YR^= zQ#snY<{zuE3~*X_K{yYUYm;E3t82*jHF5C{zCy?xXVzczRj#Vhu)3UBRqxy5gj=j} zI?Ii5H|I`ALG@ySf#y8-%^!zu{qCK}BYei}-6I5wu6;&?;=mCe>Syv^SM!{V)^>dE zjyN));;c4FmKbg#U4gNVJT79hw23Ssn<3*UWC@9zqnKfNKV5i?7+0X2P|)(ei_4I1E)wsLzM zxY_JdGZ?X3sJF3{Ji4bdyAk*XXR6xjnbT%i)7&I~s9-p;(%fRPmTcmOlq)8(L z%Oz+j62}ZgX55`uW-5KhNuDpt4CcN|GW17T- zbV9l={+F7S1=2Q^X<|}2u($TpB*2ugZT(PW)zFM&_Ce7(_UJv#hC`Uc^R#QR=Y_qt z@6p@==&DTr{TgYxIUB*&vC^?b}zcJE>+gGl>&_eEUz?M9{szm@&CU^y3_sK`KwY-sdcZewy^z(L^WvF zt@FdJK7x+ej*#j8o%VSE(5!(_Z_{qu&-KgzJ3sgBca8SDU;N8z^<1St+_?pibXN8_ z0Eqe;Oc!4C>}G3`((=7~BKkcjsXvTP;L~$Zz$6V8X`gF=%k2S>&aF|Vc6V6-i2 zI*r(h$9jU9GJ?HbNX~IBGw?M6do{2|*j_&duEx!D>9@dFE*}ovkNpsxI5C%8Z-Cn9 zstbTZUIuZAlkyBl+{TM@rOb35}xe{MV( zLDu_Zll1`f?C^dq$41g){Y(WK75LjK z0hg=W!Y{fzlQz7|0z1CNaIT&rpGX(0e!UhP#`_FB7oxW!z&>LbaRG7@{|(ju?gOS$ zz$%2LwPYYJ?%?90Q*JlR?|4eaSizTLJVvqh%yE`Va37q1NG7GY)PeVpZ!Q7foXv3< zAZJ@}cPImhyy7Hk`y1hw#={IP4`2EGD*$uHvtGErJm%aR4tGYcSzAK%>wRbt6oF>I z{AG&|MG1jI4$qyU4umGeIeC$P>7^*x*1D@}{mC34MqZW4WeOHnfZH~Y zP_q00cadbT)ux_YO)j@)1k(iL-K9_UI-pN_34gWwZKz!ua$<8^m#*kfdmF_#2!ym; zp$CYe-%+j&s+&xWo;cpCM;;6T;DPjRwyF@%1nTRcXK^dE2UKbgoQZ~uKzBL*#_J!f zAr6Fa5h`@W7v>{G400*@P9WyFrFc590e+gpyDbidYV2iEfy+bI0?7C}`j|GiLtFpx zYB_Eo9}}(c_s0~qoG(UFh%UI zrH(t$1bD7#FBlHP(|~!e%AuD>U0hxhdwAtj{m*ZSTiG{VGf%7RBlYb*Ko%{YajKrr ziTV`<`_0;uERg`A)Zih`3|M;EM$hf)So%&`liN`Vf(tJ*!!_xDIg?yArMSHFSWL|? z`9VyrOc{X)=z8h7$up$*EQb3s;ril=H$@!O*t|w0i>( z>n035aj^AT0?~h@A2LX%PX4CTbdu8(7q~@ch7>|+ji@-Uw^oArz+Hf03-Us%RF6L+ zia4Hj^FA7ZI`~~wzV593$Hy(S4UBYOgTzhHBpLSzj}t6&OTZUtTRSVZjtSi!MDRx{ z#Nvm9;oOolhd2<^9EKZCh!xN5tW_`Lcj&TlRi%R&A)<^QgcjD&#qRH80EM03#f&_; zbcj?fYLrVZ4t=SGc`9k-HPVO54-y`<5$N=O5ySE`)9r`Md(m(*hVUr;UGasIXV#VJ z)Mu_92~|c*4L|o@-a>R+yg&o=ebv;@zBy0_ zE=K~oIcExyPH}q8)&5EdG{{7dOfpMo68@l%ay&|!Fml0~^~YpKRZ>yS{O6R1-~-IV zcv4L}SzWzv{MtpDy+6ZaG;p-iDXmz)^WQ%hFmH2$rE=^6Ns;!I%>n5rmt8Y2;c@i4 zb7WxpC;x-|{yVG4N^%UDi+Sq(;uD{mTYQB&^v4%Kq1PV&=5P*Tx$I=uVi3Q+YHmpO zhh-{UrvEP*y8j=~eP5aX&v@>SIo?Hq+lo$afxdOJ$4HI4E0oeby3E}nh~GBYaleCH zlu^9rx%Xy2l8a(do9%c*cE+}Y4{4xk5I?^6neF2>zlV%tbw@r*omqeI-SOTLl!Kca z*A2ZRCu?NBu<;z@SseI&V}1I0r;fR+YmpYeQp~*6l1nKUS5s?ECA(+aGq({-7hGCH z+7cTJ-ar1dsmkCft)bIenV1~vDKa5}U1;_O=QFdhcYX^!G^NImPH6EB+7zqUxPbrB zD+DMrDZi{;R)zaA{>6=g{NSH|{H8cr{L=lMN}vQz>^DnnYvrpWoq|)$4`yVK+`$yB z70gqwTI732U%!JWAL%X_%bOB<1yh}+Hn?vj(5Wn-xp}R485(Q}s!}`YZ3Qs)B38#V zTJzjoX0oepai8(y{v}pCC0!iX(J8$%%{X!qYJF!lf$>m*1n3{Pkupn3q#uc<_{aA; zNYQu+9`xA#FR5TiEs$ta6dZ5z#%Nq|Am*EJP4Qj0);f(NtJ}&s}Jjv17hYq>@ z6x=omdpqJo;Dcrc!#A+Nt0%4OHwt8&-zE3s$j2?vpSQT2jbm+N_F8_gb#o8!WrU(3 zn|p)q&xL{S<98CDBVxjLn&ycZ1j>T zw<62gfL#1YW6@mde}0k$0Uo8|5wUB~BMxMJfbR+UVH>EE z2^MT?z|V+-U-XfjQ7y*wgkliWrN$N8{L&u&$J|9h`Y!OWG@#&M?YgOeFE8`QpAdF<#|L77r%`zkyFK2P{_MKdBYd-vOGGQS z&=li`o?E)U5M1hOkPhx8lIWk8y@RkZ+5m*H-xmW458}2L+uv}*FNFvvAkLt{(uIr5 zwJL~|QKx$9`ma5uP7I52fHzptt>2X9Hq{40JjE^yodIHv-~`Wp#qI38Ck5U^d`c7g zQxQq`J`iVmZY`RnJfbezvGKvITv>y*Ts%1Vb&ywTv?-Df(}?#TC~-chN@}`Mm0QMRPS>m z=J@!+CQ4=%%GCsz22(bCi6siK86j7NbXNwP0a!AS6+`uy2y6;5yW%&(a9c)+)^OSH z^eL38|FOKV+PeO}_>p4CXUv>Z>caE^7M%ivCEk2fCBZ$QxWo=1yK!0gDvLXSldTBv%V&Q>+ml7V+O<@G z18MO1)WXD^!OKs#@lLWef@y?x+-2fKm;t2%gO$R#P+(?Gn|jl$<@QM7wU&Q`A-2yU za5$p8dSG?}|6pY@`1RRboA9dug4#7(O+2Lw_Zi^T0;=RH8j3njnv6`tS;!S%tf(Vp z6zdwb(TmiUj|Nl^J-7IF&u$Goj1{i+h~f5zQ>MF>a1=4~iz z9F!S!0SXX52v3!5qjOJ19Di6rf!74#_72SA_`G;Pai9tygux95JYtP&&+Pqw#Kp&4 zw1Vn3>*AR2q4`;OyCbJPH@2GoZ&ud12OdzU_(x`9LNMp^J`6;+GPzwabB^iK9ke7UPz*0>;M*|(OkyixnhB{K+pHy}Vo1tx z2woGIB3N99EYkG=93(06qkIgxM_&{3j22wV|5sd~M&i&rs!z_j`@0*(ojd9afI?Uh zL`IxOrmj`N2%EQ<)oTLa@~Nz;$kOD`5`9O~E#F zYg#8Knu!Q1&B52Rqt>-x1vpt`_%4D{39Br^c{1}Opne4lwkRgxwY@AnxE5ot5&t&G zm*|f_G9;7{GU}g|2Ej6rw2qd~>}9a4%)VJ4VwJgim9&@vO0)W|dp*EqJ+as$!`nuGAzFu-a zEe?1YFN&ww4t%|Nh4aLF_NG+4CSa19v0tK8e#nR$oV3W&HUpzCZRd z=hUnb>)`S7vf_J4SsPMlB1WM(gKpvhM@TPkFL^bt(aoY16Bl=t=#3vP`}z7&csm1F zUoYGVw?JpHxvF@Vb!zbGqCJ*SlJeXiRTqT@Z%TYAQyo1Z*= zq_rjOD0*~9Y@wtxLB}lqqo~6KlMvL2@FOi4^S`6I?7eCJ@lFm>Sopx@m4G{Gx_J*q8VQhg1k+zsrai9_ z!}GeHG84w*z3%>bi2d?E+s#a;L< zR}a6O{r7+TKmYe1Yx%$XT5q)0pqwp!X)rEkRu1+Y*&-mcKzSui| z?4DIiSDce}L3#{RFwDfETTAL9R#QtSW7T`WGD`^hWO4gUY!P=?YQI`M zd6bIj`1T;=2`9|t5~oc){fZfd`2t_^LV%~aEI*q9VqlEaAA@yRXw0`#sY)2xLA1C@ zdpiCuWu3Q*U_T`I1DT{9FNnj;84jHC(ShlzZ8pViW@d3lhwB8k9`nBC!@Oq}FjLOe zayJ8Bev;M%7Vf<^B<(~7!XOEF#yNDkckNT)BzZ{c=5@qGv`+;RK!w(d=*Vg-}M z-O4s_@Smo@-&J{7%fKgxA-Bjx3y0YDh7jpy=+#}FDhHZz{=gDWKJ6eT$ zZl#%#kUm2wJJyRdRdU%lhlTFFzZN>&guJLAs20t-AY7$9GOazb7F)i5rq;s{SAw3# zwzcqjU|2`x_LqV>I)LZ>iY;OEUft0ts7H=mN`U{chAx+Qa=v=L$P9tidE>U*AJ;&J zAMi0C4nOk2tE>~WvtNG1%BZ(0Maxirasc1(3vjifS-<-QdIep0c^3xs6kik}pJ<24 zvk_LhMWoV{dP()`wWLgvmt4qOG|)%$aZ$pFB3_b&7ZR2_hx14HpeM<~Wyj zI&MoXZp-zaSlJRNSx|^>*~2t@Vpq$U8}gt3-k^`gu`lOVw@1G`M`L)#;`ma9gVWD+ zu0F>>xnPoLm76n9@-*Hop{d}sal+`kFg_$SOdn+EWsSEfYH9`2sXj5yX$hElSjdyz zy=}F6gadjXkB%MUgl;q-*+bcaVJvT zT(;<_$k6B9nUiZkzPk&y)S>m+exm0QfVkiIG0*(zfXfoVbeIAx+p2gD>UDF1m%>~d zr{dlrV-)Wn3TBs4eY~sN-ViwR9yYa&-3$zdDxJ4BHEavb@jo| zPc0d&i)fT6(~X{rh`QwBm7FvL8i#J~lFzcPZ2*qj1)k)1q)0P)HR#>gm3JorhzXYd zT(nJlj?aG^{jsoQFJKp4hJ8{s>c4%cu+iy90L_;R5)c~rIkQbIj#7xyemN}9*x1bO zd(Q!!aR()MC+LhaUs?FXB5t8iNE%4$!x7{=OzzNOQofoCtg{mcS`nK>>UTHUrRTLN zZKe|xA)3uJUwkr(YH7BsFXKlteAfyW5nbF~8Hd_jFbJQ|q(Y6}uRfaW+cdN1Op z7Ybj+UnSJ@?L2dR8S4@(tEZ;W`Yy2~4mNAcUIW_AU3PCzr+$sAV}~o%8<#Y<^P^L- ze7>F8Gf)C2{&mf%xJ|fNfcnFCAhlfprkmvBjD&IKT}%sX_c56E$W{FHDp$V>B){eE zV}69+UAbJ6@gpf$V9a7KcWIY;8No;&oTx_&KBBQ-yn^0A!LT2n9V;W-VE){F>b26z zVA?Sie*5Q2{@1Pqi98N%W0!4uldPZ_CcqZ2c<=H9&KUQx6@>(yJNNr~D5qtc1+bkR zE|-kYQgVNNed%~8z1mS9!tjMw--*TO87xQ6OQA!Q_T*+s4ho4fae`9tN7e4te3^=~4tr=CMD(4s;R2|H2G5+3KeXDw zK`eCH0C$peI&Ar-zhx@3;_}XrQ%AQfc};RTqx|hVM9f4iYHQ}(4+_y1*s%iUo5TlnEZW4vMc7un1Y44wa-p5QDnaD+^I>4`|lT)q$u=e_&9?( zQYMvN%oN#Hm*skYI<+$l+KsjZ>jJvlqz>aS3odkcN{b1>UCF@N{$^43+-Paj z#R;BJf#=mhBuc$+26@-1jE-G8HcnglHtlf7$Q#eW?x9X_7rkvtQ=bmGSF4XI<*h!& zs3RXN*Rk+6KR&IcfwnWubEfxMkGLDpd}<@eY^?GT-}mxpEhP9!V?R$yU^_E$W%FGc zs-<#svfjjJV|!7(W)!`uJ$oWZ;2L4{oH3_BNn47q?=WnLbKxv@UXHFw)vi9Tty$P> zxLv=o)En!1v130m)=-x@Zy~k;HM?-8Lsj~#;JTaHuMd&>Q59!b^`rHj-=T#T*?^(d zy{wVbe(Ct%-#$>z7P9xhPh!9eFO>9$2MNc0mFd8tLx3!MWuz%(`JrCQVP2#Ke!uP< zB*1p#{^JF)EP3uP7ZhxCkdr>Bvzd{`EK;oed=)U7Jpb{|!%~zkz8_U=uTEn?&`~@% zjJjLkYJX!`jE$U}tB8mr>JeG8@yG}MH#|nW&f9$TSH)PqwDa%H!-+lN;J`U9&)ZsFIawO$IaG(7&9JZfaT?{M9}87@(e|U0_jPeO zgUof_Q}N~l=E7SizfS|>!hY#gevj#Pe5zSVBez|2ovd3xf?lzx-zKq##UBiU! zCP$-6yy!;rBx`3Ku(_p6%=Q$$wT6bzCrh^MCEzgW=pdo*@b&)TYZczSzYWBY5th4fPM0hAqvS$=A9i~D&J3wy=uqU)&y_J zZL1moz;u4ekP>T9`^IMPK23iKdjr6Jfsl9ym zd_&}DgsG}X#hJ>&w^G+|IVS3C#7m%hngNNpNHmzAW#C0j^aW4{lqO0y)8Yd&F+Y( zMY}(*RjVyt4Ugg7^~|5v?w>By+FQK;xC*Q9?JnIZg=(In@i=L@pM`z0jF}K2;XSt# z*1n|SQ_dby9qV2Ab~iU!rGUBv*#(dndzFqmC@Y?ov3u)*yt$xi260^>dlO`4=H6kx zs{BT1c0I%qmL>4k_;IG)qbE5*D~nMbSMBkZ-R~H9GRLx-8DY|($Kf}c!gd8e#h+G< z#aTZ2!pAd=YO@IIYmgLuhgrHF_ld#2#GOu0M`|r*YwpO7-*l{7OA4NnUZh;@!|(&5 z;9A=ox_@_w)BQI2WebtrOjpQ&iy5_C3u0v+ytsN?iHE)Su+fKhS`8H@`q1)rho@9Q zGCpi-(hoT77Tp5|@7aEJn7%j_;9ns;OwNgo`0_MMV4C3~PteP{RO0fG3haK_txSOq zK6d;750^7(Qp?GT=N5Sic)0BnMk<(TiO!L5zw+f7XLFUsj}%dR3oBpK0QXyc7Qr|n#JB7ZO}+2QCoBS z7=>bF)D%c2zaZ$PR+t6f-9~bF1I{U4H5c8Q`1TnwjZe&|6uqQ%Hs!cGPg`yfL?ZOK zWnQaALXtTOMe?gj$LSxuws$#VCJ zcE^4#Ah}hn7k9{88@nvar~=I60rOoylRxAQ{<6t<_@ruqO;C`PZWG z>8JLav&;F^zib&^Rc7vwrj!XSLl$b!%$(Ghfpoh%8y4a{ojoxLUtF9=b;3 z9gt3;h;bPEV&Xg+|3UPL-{2xDbKwQe$3iu%Q>*`UHFG1vXsyoX#Od)ffQV<`o>}v3 zi{%cD3#_mF&j^t{sx&E!^_gW=M|Z z1VG{KH@7JJ)~}5=F}L)XL{OtITinsm=hI%(##0g!W3(7Vrl!TUQ&t1+r2a_h5t9#6 z!bBh?j8K-G%J}V{B_lnmXpw3b)#Dm6!&1epXgTo2^~Tv}^{jn5{8#>PoO_OT57hthOW_L7>!`?+tiP=s~b5?$$W%H-F_iTh5?_tTjQpHnuOe_-RKVlTEqfc^dpDZoGq)y7#0ub`G z%a8|;OJyg*`|IxL41fvy&&s`s2CYnC2UOLLvf{Z28PdPznOn=7S&J>*S1`mWO`loV zzb#nHfhsCcokJyfB<}1e-90Z?@fs?H5kCBj?Osfztf$}Xk^F-f9mtc&Rp4pJjOX6jgy4|drrhU^5CtJjiT2P_vP z*2B5z-`|wS*Q`FwQ1Sr364H{i6I6Vy%xcXZo~!#Oz$KK%|N4g^$P0 zkp8?LlLPhFx41*n1=msf0!KJ51Hvb?RD^q2Hoz@mIl!vM`0dlJb*$3QErS(-_>UAN zWJb61l^aJ@x3yI1yn1c*)Lm~~TxZvCYexry ziM)h_Vv`RxC&%*C)&uGKi$@=(d;dgco?*T}I)&E+NXO0Cp=1XiMV8>SNC!syULZG! z+@6KTDxb)(vqm*Ntp*n8#&rdpP3_3AZaZca!MAQOLoXM&vHVc<@gpMA5Ee_-ifH^u zxnmdWPvf7a;2_p6)`iZRC#&=_X4S6pIbIV0X2#nN{=)#(Av_cZyT16_jpz$)f}MTH z3Ququ>zbQ|u`02X7U{C%Q*N~?sqcCs{mgTsF@el>TrsZ4{rvSv@1}CTrjI$L{yCUg znXrRfS&h9(L_qV763Rq&a5^jpvc!Rcz4lU7d1YAF?OXWLZZ9u;o9*~@kg7sIt#pzh z*03HeD<~G`sfJaUQ<_5w=t=(!*J`frZv%hE{kC!Ir>1gf!|faOJ0!NXbOpGIwTMkD zPl#a>nR}1vN^LY6&2eWKM1MTFf^s%eI}=pm98zS17X39_(DzeAZ7*)s5=m6Rq{Q*R zK!XR%Rovg5$>Va~k)e#6PeqpSpWqz-LVO}O6Pwbw@(CWL5By+AgLl6s?3u)8JUr!> zE~$?@9OgPZu*CQ_Gw4`q;#6pPPpqD!QuUeBkrH2&K8nW7aG!$0_e%J!Ad4Vzzaq{ zV)ToU6=wZW%LJHd+zIZItG10ui0u<66KklP+VQLc6vqN-L!g$`Cts+J?=%!Zbz4u zJspYTu-3DWINb%aN*~$9(VA&#`yJf|v}rY}pcDI7;XwUCTK7uN((l6En$bRHu2xIm zvfLbj!LQ9}#OxPsk2ZIx8U$RxJd6FVq@=q~gbb2o@R2*E)A(0r%k0LTo_=S7U=xTE zanOVjw<}lgRbn|y#CzmLVhFL(gJXF1YZT&f2Om1<-ulWQI~oT3;g01FI+3HrHhgkd z=v%lV#yp*a{6^2a(=(wk4k*oe$DbF;%R?8n0ShbbXA=v;GI)qR3SuFRy%aUxMx@lB zNUKB`$|?#e)pZ6DNK3ia1ni!5_={hT zy(y;8DaCy^!ck<^9>dd!15MWLanF9dVCjryXnC7u5p3;TL`O<7utsdl5mETjos+E) znU30PZ1aJky0ly@dw32|Ty=byZty4|eva0m-QCF0akcmEk2P5V;C>l zk9S>NU7I_m(>_ioO zVjHrZz4&RU;(7d0B~SjY;(NtLxO-9gRo}~|q3TxWyVm*Ug@1hF)D}Sh zWpXaC*_)cz(4&0HYfRpL-;0mI4nRsoZ=8;SRZa#e@4}DbWzXPjD`~jYQ91x^9+!_f zD@$Am!KISQI$fh-@RAsDH}|;+7OiDr$pr#RHok2Nj&3g#2I--=+=`e-M@U(*y$~)v zvvE3u=W-v;aC%-{H39*wD|JT2f_qD~7zII-)Ivad#m1t5U4p(yz`%mWoB+L}w!e&Q zA`S-WSdVs5U67sje{!bo8@Z$i@QCNM`tgYEk0bPBu7a<3dMx~bZ?oMGirT%vut`xl z(QgaejZfJJZO+f`x<&7rc<2P4X=I>pH`19Zs1@8;!(4e4oPeVitqNNTVwb0kOgwFM zxr8~t6KAkV=Ec9>A=s(x^Gq{#N4(iN@a^Qc>0YX$q+ZmbI~(E9&3Ol@dyHgE5*4}I z)Y|9UU{QOm*jqNJeqm(2#u2WbaDB`k^TsOdADLv%=WN+xdv567Zb-49EsngDQjf^8 z6ArkkZ?O~F0~_x9OGJRIToru6urI@Qg;woGo9xohW9#mwyq-aK>04@9OX!H3?P>3; zEJ|!tub!8C!(>|FHEi+?+WQFS0O1Do{k~j36Fjy3g7nA*)&NT$vH8~hcS<2Fm!3`$a!~jBl<3zpuKI-n zCDm>gQrqORXkJqij*{2kL%0%vWcc;mOwA?E1Yf>7!JQy4KaL+_DMv?x_`bKDqULWa z%*z9w_F=7>ohx@fkSpBBB7{bT&z0W8=1*bF-PiYhP{VM2A=Kjnc|)mnL6oZ7enyUj z63aKANi5=(#8t)s)>>(2hW9ZusQ zqssOQoO`0;hfz_7)`i&VVj^5#DSC33_&>l1s>fCb=To&S_q?{?Ez{EB}l(^fX9`VtZR6JnN>a=U>?5#FDSo3q(=i@&2c#;R5k+0R) z8(z@F&T{vn*5avDEpjV9v8(dM9Bqz~8H+_*lRO7d6Um~oQyU%(9jXBw+j19X7vwBF zUyUbzXRu(3rh2SmIY5%U=1#m7-%*0>j{q z$@jt5^Crn$x$nakv9^~yjhY$7A^&cV)7m}qvjE=1TKD!Dk+lcBCAj(7rnR$v9As}! z^UUD~WP1{E9Kt;p&f#8bc&{)>M4_1KQWAnwtKE20O3dQAx;&KPkf57-ULZfHpvwDCD4o%38ev&$G0>nGvZ-={7v4p6ga3{ZS z^sTS{BCV7O&}d65Z8l0i{iHjL6Ku%#PIz`eP5_f4RTCz|!m4vTK(Rrv+4M9fG?JBT zrc7b`ep;vBy<#(v<)_25Jb?5(GSc$NC=lAAhXK@;sU~#Q)7D=U? zK!5hh?s0+5Ukv_yRbOjl9-PQe_BA+1>Ee8y@!3B65pfqavSotdBSK#j0#M;49XKlz ziit6X{N7_Sl0ah2;am_ab*ZPg5_ev|s>FH$`riM~m@nn${Qxs)U!U!6X}!>Ja+5`B ze_g`~v<;<;2b-KIW_-a^es^oh@L~~gBJ02#j%NyzvzISB|BL;S&dTD)aWv!P9(Tpq z1G5+%v)4kOE0NJ>j$JVY*?&=70J~K?3++znQz6G6pRdY}hsj`Edqv9t`o@FsYY?I6 z+xBpJ#{+|)^<3Ud^?nJ2NC(2VRv)sTj0{V`23a+1V8z&WUCncQX^O8idK`3&VOB`` zobH-vBuT;kQvd+O%6##pL>SfgVF=hD3q#gjJJ!>RR4;G+NpP7)2rhb8V630gBx6S? z@LM`VYHG2&c{|X}ZUe78Q(%P_-<(`4TN3#iG%d1#VBwig)9pRc@UL1V;KjbZtr@$v zny2gD9Hz661_>P(+uhRB6-+_o!MY#~X#--(Pq9IBCa50jlc0W>0#Z=UAB)$;1`Si{ zl#_!LM8`zO4%7kKgSCVrk$(*wR;IEq=&Nx?e10tn@&)x1VlWxrHtV#VxtK606Z|TH z_uC2_L)!9d^I`#~z;Ei9>+K+!$d9;NgpLKI;3}chfW}Zmx0xu1ExQ#OGuA!8f2l9h zHgM0N14F&CYETz1Qc*{%cy$e#Q*a@nE=WU{vAlI^7m$+e<}^rZ!GZ))9e{3868 zndp?nA!y89Lb)WGHrrPH$ULmZX5}0jc$C2mEX0`BfEOSyGPvYE7_Kz{>s2WTR)W{+ ziW}&9M}ZBq^!BvR)MV@}hk&CgSa$h>*PrNxZX3tV0An}4Vie?_);*wFZXPXy1sFjn z_Q8AB4}b4*Um|(fUbgtz8S!Q0(gJZsg0TnoSYQs9U($3v&L zi|;09*w3^!YlU-8Dg|q98@SKV{l4JTJw2d$PeCVLSt9-CqmLaHa|aT5M40cpuq#aN z9Ku%hZm-p*JGN4uw#bJ@h!eTC`(Ss>1wJ0vxXE_!mNekGYcQa_QvVzpDWgw33l@7H zl<-18xt*r=x+5Jj2|3!)j@5pzfC*PT(y;_kQ3qhbS(FYlUP|l{6O+>e&1>-uDF4@B z;Lv09HbzF4Ktn|+`b3mjg5~Mk%oK)0b^L$dD2j5xC#S!9aF5T%2uMG#lMkihA&iPD z(>^i^T0O#WqNgB&f5&t|T9DYq@Mo%JPs=9X=lG9~!m&MZ?8XxoqabBxixa6#J9l3} zUuI;r?CI$}d$6`Eu}^UzQCkibXjcr?y;mSIi>cgX*sla7IW3zgU%=#3oFD5k2{~k( zt@yefLaeQ0RCHuuEGvbIF_B5qqi+@1XRqlb?!Vdrj^KhD8%g$|*Zwn4q1YNNGZJ}% z7T!dm&t10&O#70zFj4n3;>nbJYOf^lsKXO_2NrF<9qO~DeMQd`O&jhatsDcHY( zD+i#Bt6t-|=aYVm)&EErZnr_`w`ICN=2xSc`yVpoJA4_~8pNFONykVf-_7W)9Ikp! z&BoU?JGRpxXz`h44Iw2+q~Gvkt;Z#_;{zKEC8J@>rg&ZS8-C{-Pq*UFvLyZWkK{KZ zULdrIYhJ~(19!6z5>qOPw(v8a9%*!9lmhyu5Ibu&XW_f2SrT&GaY6wi(?pIO3a1F$ z!N~Av9appPfaFg)#W5Ynv5LKEAj^w@D{K-XMYlPQ7c8vPu)`jxH1v|*d7{zHZy5Ae z(KCKCuPOWG@YF-)-zv$WXT}8X`ppFfaZq|?RvM-)WbC&OdZbSB`IF&CWbX**T^M?= z>O2S+ZVPEG+@5V$@e>UjoI6#_f08I>VdgYHgj9D^r;$yr6JdtSXqOESA9lWbMa%gEfFTAN&TY_tmuO2VZg91rU)IJd72rucIMBoOGPPeyQ8Ji8qZSzuRF4 z^MfPOhR=%V6vxTC3K62e#WGyQhVolrtEW955ueTxYloA-BXsePF%S9(Z9JRCEOPUq zD?v`C0nNNX;}#Y~Z+`QFL5ZI#zuF~>qb3yZ1Iw;8gsY&>j=6ml0d&jv820sO)K=b& zhEE?U_P*o1IkWzeu6W4?+y|pow*4z^%++WgxvO_42nJ_=x@a7v(iJzH3-oYTm-QXL zGcMr!&~K}RN5T-SZq#^e0}T{NR}{7d;iJ^j&~IR--cAnfp}4*t;c(yCX<)8OYCsLF zr%58SXvIr-x@feIsfAfCP>9nN)o-x>orwGCLfYBI7Ui%9ng*-5RVtIKH_Uu;%A*0f zvA29ic}gK%15YVRan-H&y&0KUk-W-h9pgT{9r77kW91Vnglq%-$-o?%zwO0&z6Pue zg&qCB5kjoLcS`o)Q-AsDZtk07_gn&^I0sCDQuz7<+s5Y{a}RBdx$7~}JC>Mo@x$0F zZy7)AzlQMjzu5cgu&CCyUquv9L{dt6=VAd*?la zRG?!oH_Da#ZR$w=1Nu9FB@pcn$`x+aJvt0<>u4k5&$0b}F{oquN5om!Bwujo;OOo- z(Z;Q@eKO{#*bm+NvGc+O*nuyL*CC_pRpC&~EyCKF+cUbdPE%zB4Jm2(FeI8u4Z`~E z%dh;|-jIxtWD6-WB2J9cJ$A?T>+tJ<*L#GH@0R9-Xivchz%X-j-Cv5b-eMM_>Fl8LaRz3i+HPNhmB`kk+m&x*(B*6HV9m-1~^Qf)CU{N~!c{ zQcx2*NZSlhh$HPF4MLE?F!04Ne??JY^bxz5Mi#IvK+e(%$lezbp2*k6K_o$>;X+L* z$DqE4Bi$fC>(qEPdC7R2$lG8*K4e~fKE&*iTxeL{a}V! zDSZ1h^c64^LzHMhz_?sMYJT-X;?BDkO+lBX9x*%=E{+_IgMP3qR+=aE5ec0|37~7zS~R~` zyx1zZw{RiUIEV-*!QsuJ0*C1^eJ|r6E?*nd#~8npK1|AieGa8T^Q~-29U+hAn%>W# zyJw{K@VStacVIlMLd-};RR0w_k^U`O?_#hH37Df<)G!us4t9V>zU!<>AuID-ON#%C zm1|LgdE_K6)}7~-0kj(beCN;82Wf;vKeH!17UPOKQ? z)$+6-4KgWq@bN_%gv=C^UOMhm$HXZ0T{X90&DTrCs}PrCWD?{}`H?Zrg1kfW#3$Nz z=f_G>WxG7~*)2wtly@B#YC}B9z8!R3N3%rA^a+@1Y2g~T&?Df*RhFSf>ZQB~)gX2I z>f%L(x(}}F6FvAl&WMzsK(zu%w}VrD(>xK{%W^Klv7fTm6_sXvIsm;V+U6bM4Vdpm z#VkKI!QoXfM`MCdBmAQZzXr%}`oe%i3W0voHmTz1=N`)66_O7**lRK#Yu%-s92fTY z@i;a6F}Z?V(2WEig>w4xpf~v43@&Mab8jn~FYMu}h6OMbtPnOS z#(-*n%&dr~Sr?!!i*R@8DsQyH6`ZWZk2d=Zyh-zqZi8_cCUawIt>Zv*2ZIwEqeb*}imQ*An30-M$z@+htk6TK)HTR6 zJ;mm^DHC(R$g`L%)(A31tE5X@K)0s+A^aG{4Snb(oY8vzT&@J2ZWY*g?dWq`mWPYn zaY?y4+5ocMOz&Zq$>0;m>Z>BD$jrE{{rjwZ!3G{cQIf&Q_gJ6KvG-O-xHfm#NG8|0 z@lbZDWShiE8ANWQ_@ox@fICK+E5`=^2)!QHs$wI8roPPzFhB^->i=%K*-|vCbNq5Yl zVzE;C+Z&wVyk^g=`Nf^?gM?35k=2a1-e^}QzL<4N$7m+of0WhP@}PF&mE){9fEV9g zoy-LQ&?y$aRx@DLys!>tA1~;-$^bI@S-=Y(DSCsE3EpV^D&I0Oh3m~t1p@j?|uz&-E$L~NZbqUp58iJ&}K9?YE|5tTUdoX7)m%Qq3Y zG6mo=H$@%MGzj3YT6|rF;VIQ#EVe!?-6f+}Qgag=Wc93IxAN{_I6u7Zy! zaR~Y?w`P2&RsY-d^t-!i`mwF5ZOTt(XHh6FQMrO(h=9?4rWO-GI9mD4Dlf2yBAm@i z2;Wl2L0yX5b1mN+B2^@koFi^@DSjPKHK0U%tVC%xN*X?{X9>3dcsAbpE*orKeS zNgUoQ;PEQuN9d`^Y#@bOVNZu)(JH~oAIJAijV866FVzV?;nYkpfy4`y<4&RV)g>ym zv9Fv1hZ}_bT1xXv?`v`8&*D_eKp=r= zIpz*YU^{_yF0qXQi)RzBW*3CbxztvrnT@4RdR<#fnSzJdG@sBnW?0 zk;a!E22&#I=W0>~l3oJ!0Nx)ntVd=>v7Dk6v5ImBt@x-`O68$g+y{@=O-uwQeHmBi zyQpVIQGTLl%{S6;I|yICpXmb5xdMI^-I;m;jKKoInc)xfwO>_){+{c(cz{(~c8Ms( zyu0o2cAF5JEXcb6x@$X2mwPL@o}l{uh9_3Kz8*lt5|Vj$sPK%BzgehGe1tj#57xUD ze#QdrjAP>lAnRf9&q;l7>7%xX%p69=S6r$j16^dPeSzh~8mtC|MoEeLXJJ8pLq+OR zheYgRfo<}W*j*ML#pTxP=K{uvufBJ`vvdw*hfqmt^Yl`fC-AR(055Mb5xA77U76ke zBb^Jv$Y98!RRMj^7R+?2cpDBs5HmA-#)K)p0J@c)cymC`tY3rAS7$x&8jw1(b6zw6 zUhUje4UN)Fv~59Me=ugd8(7WQ5c4&$r8*}ovF+@m2!^o;U<&CLthQA#^)cZ&KPXQG zKin;ZYGo;)mCNi<*CPB)rlEKiar3CeqE1d)ptCdiCCC z2Vw=UB4F2$xiHlL^s#Nwm}V!Td0RnCMfjVV^3J;S0~BFN5X%6`u^M5{IdwbzD6s6yUf@|+S*rlQRP$i|=sHpTrId3K@KzgE zf%BYG0Bu7wv$YbdJ*bs&nrq20JgeI+K)D%2-1;Q5`rE|5=_d+YQj3(}(yFDHszXdr zBA5$(P|$8#rL43Eowx|F`*kxXLsJn*1#@t>k8ixnVTeBWp3UY+@LJ^touj{^lv5+J zAZX{M>yN2YnfgN~>t}d`RCmB6Bde@-Y{h4#p;88ec_Xoj@aR{wd%eu+fAO(#RmVr`;G_K17Ap6HcurnxV<4s0pj4VwBC~+HyZ^C z0L#LWj!^2MiuezpDI2bI?=`+qjv;^ultuH(Ej!n-h*2lZQ(RL2Q@FTcm1{snr4I1O z{L%;tvFFtOYJsJ|onu@EI9%ie>RrE>9$|dmAQ0)aCkDupP^8Ho5K{-nuK@#Ihy&*7 z+%;xOXzULQ1=dW0bJ^K}(Xr3pNFyiK&!kzMY54wh^W^?wB$Mo$UP$3*cSMYlmvCQ7 z545GT#!LacKn0*qUkkyynzL!(gMRCt!S|Dh@6gmrr1Q+$Yy@2OGzy1ch;V2vH?MG1 z;f@>?lb)}!@k@p95Ic0xjwv^Ny-@pBt=?tS*ydTnfuumA7$fT;%4vA7T(UQM8cd_x zl={cLq3Mh29xZ!BHJzkaDxCow*<~E$i?ofSc3c=8`lEK1ZDs_+#^7!i;}0rhO3%G7 zI^&$8JBQ_EagA<&!NY=LDZMALGG6V>Dk9ac|K{fI6rDE<{EO|qD*}aS5XCk6o0)x5 z<-5`jA!YeXSM&2K9eEu8}aZL8$%=R(ELE4a9-2`gWF=Kyd z%Cy-v%KZ0DwZWU_;9N_Bc7=EBvM>U7lQto&>K>%k>XO;&C`r8WmboV#Um(Bu)AeK` z*LPA>f2!dd;CDGs=W$U0RLtiquA;yQnDppJvbf98G}KcfkK(`nWi~eG-d2w=_#c=0|1TIf@$oxA zm@tJhjo8+ad;qnRz=2MrF9SJGsiocW^)uD~E~ypd&2awxZTpwtma9x~Uq~1e-2@}Z z1PGg&_<_C&Ky)DsQ1C<+d;`a}IOr4{zE&)?0sn)CGCw9C;20!<^1dbkSX6d$zDn+U z5I6_$P}wK{RkwYs!usmpdu$m6zTnH&LpcR35fTk9z(vpSE*ScSL)Hk#>hWJr7G(Mp zz_ZWx8=!6fQIE^jk$ou!RI?;dJ$8jr2{u4T2GgL*x_P|5y(HudA>4e)S?40qS?5TC zIWF4+K7em_B2WC3Z9kiQFnit^Zb}&s=3;=J zd6l-r=3s7O0XM*L_TxxbdEOdO_9>Bb{&uaZFII>^ z{1PSet+IZ#yCC*T@Zd6%u@%yCUOFegpVs(9sh7@e_&xFV$D-qykk7^sf{jf|UI9%NZ?mu=w-!3NHNGME|N)0)4r8i0e z?>)EinVa{zyCDI0#=QG3C~uR1Sx7rnMLE`1>O+#vhJOgShyND>ZhEYHL<1;CEydwj zOzA#w+Ufuk;ZE>@l7>zGg?wH4t;F6Q&m->r0~*R+hPJ98A-dPc3S~P&XUSuQ@-s_3 z_9#Z%p4V!ocnluJtK=V6m79YYssRD>>1uxSDp_R&(G@yRp5tXRIB*|&@*}O%u#Lf` z=W2!D^s*HAj%?W*J%=n^aey4V)Bl$od;M6ByjlWQ$DZtMU8Wm8ZvL@rqzTy{9HyM zv~&Vf*zw%qZt%;WAX;i%y*#K%-hi>xU6b8GrN56NhacF7eY5)nmw;_<=ct|rqa)t- z{{*>k)c^3hXLc-gTEN89(%FLegEQy^KCWiD3gRx!F$s{I^noL+hjYae2| zA-@|UP1A{A_?wNl^K?5T>;^EGI;+VC(WqmIZ+YCJa3mHP{vFuJs**FKX%@WzaFkmW zw;%)gncch&s>B3aN#?q$K=c22)>NGOAVBDo+iyUq!eTNto^FATS)huDxlxj@>NC!Y zG+c`y?ilofdu*f>FN$0|E{#8?dfOfx(K zSdC%rY7hWuN!z|*bQ@dwOIWLWL9q;cW0j*@-BTA*U)s*S~gsSH8Rz!wqK%JfuODYb$o-o7-ie8uadZfuCZ- zb-6*XF+Ci6pYAnD^r_?+4$`ahx4i(sVcs~-h{v?4$mZBbf~vk_HbdTysiKyu_>=(1 zeUkueyX{g*ENVg)YDd8IgV3E*;HP}K^SuC1u=o)&B^h}7uEyMDjskF*R0Uz93rKD& zRN7}AEz;WA#YtI))cbKvHrqk6_WfR`~~rg*A6#H#i$7CZ+|dYE_vR_k5W7Vya! z@tO{IO;ppps#=-HhP;zKc+~)yP<29Z_ZZFZmbjeO@VIkgu&WJPgF2^9LRn?raxGhp z7=urVCxh_WV!p=UM|UBCptw`+9IqJ}UcWBW)dk>*{ETX3j)pDd@yhdbQvX9!mr=;E z97vm~w}4K++Rzl}cB5{(}p;ga0@-m-;$ zF>(nH0>`FimX%5)D!2b?jI&Lu{L+>xfXFzW`dDEchc8l3WML`=(pA3ZLSbA6UiB{BY{>A95mQy2S=wvF8v`VEO#%S!jC^!Dde)4E=F(~m>i#6XEI~0E zruY=QR69_W>LJkUiJq#ykktu_%>G-n+1r&SA*9RbB4Ida&0%oU_Mj`sY%`#wtr zp%8=~uRnwBN)%Gds@7wKl=}4Oh9IyD$~PEJSxQa=WR2Hj<4RaeFN$@ApG^r5xkMuGIgPQO-KtLxjSJ*=jKLymx5U5 z(_hoR#S#xbk564BLBv;inf+ti69bR(X_B4hs5DS-S%8bF*q43~+C^)HKA~t&bvUha zeakMF6T!GKwV8G3!|jvNX`S=qNdm0_Zp>ITlWIppj67pH51qBt;G4^q!??yrE>wb9 zQu6OY-6|FnEa^d7%YhcD6LV~Iy`NbU(r-^w)(9{mR?!T@f$6+Rk~@U&nJG`ZYdueC z4Cc!>#aAfv)X#p5#astN-$xw_a*CP-WQtTo?**N^>p;C1KZSFS=Q>J+=02Yy1fNAj zQ>R6MToO0c%`=A(eC8pEM~F}?SqjtHLo5pLJ!H*^Ko1@Cs~P<=JuDKLzQ3OKaG6v3 zO-4zt{)<@^ts7d58*V6r{7-d8R*)5&%1D@Crm(PsXd7tffC^Utvc@^!$A8Wl#H1kO zC%`;QJVce->e)TjtS6~x`@SBFV4Ri=l5ht_ZtdU?@;_aNPajlj;PYyRkOo9B1@st) z(KIqVICa@(AbTlGu3nju^NeNoJ57}ptS2|^5NBk?wA5Zcd-ONfX9gyLAJ{iiQl@%i#T$h!3vM#{^N;$a4Z$YxkcyD_CUWYQqBvu$;L2SAmgy!E&>cId+fA*V0@@z zxPPDwHAo<_JJm`lF%zQ~WBHDN2l=eWcq9A`3qFCt+8j-=g3^BD^%yWFT{&mCbDGqm zk1+%mzK=uD2uC-n1g#spT?#Xco^#*mWVqN!oB~F8!&A0XUiTr5#sMRZzv5FH1vWHe11KYxq z>-}|q9#4001GOT$xod%6pREzl!~$<{uN=GDk8|M$r`xbeTn090~n*AjJ^gV zYFRN#^CiI9E)pW%5_5W(CaOz`Qz2xDPXxg@+c1>}j;oY|Y5-aaW+1^wM&E%TwQ$@m zPYbqq2vVyuc1t&Q5{d2m^+rHHzSI}#WD8yN(KjCHT~x)6l3-}K@Z}V zd)LF`Xn)bJWGuVh%W^WU?GvT?%jomHQi)$`u^lE=B#4(Yq+dU}u4DJRy%PaVYE<_D zl@szxay|}fbO*4*yh?%V>lnvnshPPd*|{y=aM_u~=*{*Y?Q`A1rk~elFgdm?@^@_^8x5Uis8S`v zBU-C0WyqyI?`w*~Q`{I2jKLQIKxu#OzSaJ{WG*sDcry8^chh|A`Yw}_Z{?nSkY7D- zjRC(840a5}8FS33FiznRXp`VlG0TaZZqOVl?L|s1?sr9T7M_lH5=3PIS0ZfzkmM*^ zUCTMrCNVuuWje}n2+ zZlv|jy02CO^MTcPAB!dHWaV7&T8m$P%r`+wlq(U%WMe(l+xZJ^TWZR1)DxM^Iu<@^ z^;lcZI~~=e>ji}p8bAFX`%|py1CPTumst#`l+X4u)ZhIUDTGT350p2EX_I(`fJ_i( zN4snJ2C#2dYc@gOAJ(b=)chgcJi?|)`8-m)Hp9gybwW>647BM+ZWAEBrh>97!sB2}LgK+9?T z=`0@m7}Gcp!4EdG62(6OxKCV@&h$UwJ~{F;(dV|Gzs;RNe#VLBhkRT$kXqA=8adjT zWbo(A)Jz;UL3af{wjdmVQeLO~dz|*ouX}UdD?pPIU#||7aE-O+PoueUpsQ;xEbQp&U zzg+Y2I*(&iP^wgxc-oibgWNT;DQf8m^Dh#|L|pyFvq$^`z}6iKOF9S`<8QxrJ^*&_ zvLH`;CB%&e2Jdf5+5qL)Cp5+8yyKU#N(b|YYRvo(OxrtHkjSX2P4A;cZ?2TZB_0(o z9Fsx@X58Ce`{oFdlhrC!{Nse`dp9a%XS&TA*I{y7FE#sT0v#e4R`3tmjandMugEmD zQQ(Sfe}8cg&}T&&V=GY#fzio4#f>GC^>x_T#bhV!&`)HLRZxb2W%EOQ1=_Xb*e$+v zlwrX)NU21Nd8);_k(j4B(hd~GwzWhqlRIM)WLufGKK}Sz7QUO|i+H|dh`7(wv^dr9 zezn_PUK331?uT%Y?L;_q`J>1#h*vLrdo5^mdNS~IJ5{j#{;kdyzcOGUG+Zxj;DCH0 z|FR}GZbmg>-BCjsk={{xLG0F@K%wYg!y%`$WXy-b3Qjd40UM+t!&E@ zjeYL+DOj%P&1Oh=ZqV15J&M=1fSg!pyI6hISIs}Kq=U{o@ketQwdv`krMf?8p~PRb zu>GPUFu}f~h)nzxR5D$&&U*n~pL&d+x-4Li*0A|6ZWnm^Gi_38x5FFAr;*odGP=+E2pM*Bvo3J}bNFVlX=8)M7bUZMr@g2)72*=$ zOP4mpD2-Wv!iVHuO{7AqWjMV8$TMcIkB1^WUYQf(1K6_dgM*EL*r1fMi*#=DT}Cmo z4Yaywq`>6S6zG;$6Pa)LM@XXN4V$9If^%+v>DgF#mK~PO#1WMpZFO$*u>@kACDH$G z6{z*yTyGo{s*t3?DA*i`n%^G=$|9b7-MGS#1~EV^AG}nTLj}~{!Dq*pwhnBCW>6GY zmq2SF0zzL@JazD;3EiD~@sjTbT#mE;w%1=44U1w_*}y<@x-oned={kgz11)pQLc($ zAg3#MfN8r)thl+J=l0dQ0+d=-r;Pok&Bw(t=bdLbx=$QM5T9ub1y6nS@u~D>3U~-~5h|A2b!B6ODQgvOACFZfrWf?pw`+EZ#*D`rWE#Tk0 z1LpP-f}>x1nv7a38{tkG8LcP#n*#fYhxAeya0uiGx*r3=#;b)=KUf`X$yLKh){=@g zT5K?&RCI1GTjTdrcjc60`4pJ$nc4+&4)iqR>n`C{9ibQlD3I@+cq2vB}5~~AzU>Q}bW(SN`z1$wC zl}S#{f#x=+ckqcPJKpJ(UI`YIarwGUy$V<_r z-9lO>;vk%ZCIO%&LMNHagBXn^=+Vps=zq*dd+@<~(OfmO3wew$;(sC* za-!6f&9eLots;CE7|z`KS?Z9{XsRWhKVLBo6bUlh?IQ#5uqDP{*x)0Pj^X!k@P@yk zcjyP%yPzNFqgh1V25<7OSC0QP_Sob8o_6_vdF}uI?!Q;;|An;zex%kNz*>}%6g0f5 z$bb1cGZ)SM)tN!KAk#qNn-0*7*n#_X!R@ve$c|lryqY10<#wLZun|DfH_dwlcHmB+ zgQNZHupBCT8)sbqpTJ;DrBod7Hs^@?ufQOsPHpa=uz_rL@YdTIhOa6>%p-n(rt&kl zg8y}#&F)v+pjaI+2LCkk)8 zr-1bw&(STK&ezvtv62SYdpB^Q>|VqG?hdd8NCcnvBls-*vz2O849GBG9?N#jXT|F{ z+yfSh&jDNdE!~Ox*phDlqa7f7dnN`(r%4Hr$$s}6Fo|#IF{T5?&eHad+z%|EuKcFi ze^@(zSe>*toZLA)3z~wf!r;pt)($cS_*z7Lb=f=^j`g_!`}y6L}_N%;Y(H=s}qC>rIwRI(qDR12gMiYo!U=v3;<4_)RT zT{B{I^0(q?iUvq#|Jf8UrLPA-BVBE+cdNS4op+C`45ctN_7gR_S6k)B_V6IX=B0lb zHWRa=1Fj3%4hw(+VfQ)wXpg04#0waKl-Mf+*_XlkmKM*k;318X<Ms z|JgrB+ybbCGBTW@;DZCxq-FP&=A?{=y1h8TTL(bZP(pV*U^(MwnIm#!Ho*?80asnh z4tGZ^8H546PK~A}CP0K4vjP|@wX^yvc|WimrM;o59H0I)P!@@cy8~Q>niRtK@Wj*4 z!GJ2R_W!1eW4YvvW@{Ni9*g2Q|0RlplrrUw9Xh)W=lGw4?O=4uU{7e*7NoCmk#HCK zh~S`ZdleSQq@^EGf}RPhrKxIuIuo)?rYHKBHqPf0L}l{dYPzrtne8m7f3{;uk(P5> zF^7n*cLbBC0o$y&a1#-EeG4cP%7rj&p~GYlKxlMk>^io6Ju_~ZM~-p&L3QNP=PZgj zyELz&xZU{4N2g&6VwnSn_re_`*No`RE=#xn(wuC0)pUB`d9Ua=g zN4*j_>IwEB9}ekRTx~cIoh$$fKYx1M92WvNz=n>8)Pi*RGURDXgtJXna{FK26HygX zHxkgeF%eC$_SaR@@-8afQY-md1U7jS^}3_>A3RBpwQxxY-|5F40~DRNN&(JEy?NtK z5ChogT#^vxI;0MmU@1xH@3Rge&A>zGYD*pPWgHZ~ahvjGMmb;CPD>49e+<=gAv%^x zd_G4oz;AnO$Tvn^e=a!A0p@vaf$Lk5mt9868D{GFqwal$51R}QDPC)l0o{;|=b&&|g0Qst)CD`6`Y>%{<#B7+RTwi^t z2hht!gnR3jqPtT>R7MYVa4>3J zv3VJgKt$m)^n@7&--@6D0Nqm`VVUteK(uvAiWoCFYUT)mM|?&g3IUIRpd7kdn~~V zaRzo2&u)n=+ST9a-8+rdZTD2h)ffqQ_fAwcf@1Fupha48{girkVK?{E6c`~Ra;{xC zqKqn6yS?fstVp~6wB;r24|D4{;pFUj$R8r4S5Sg;lwud z@kROA#ab#c$XWMh=-EjCq_u7h+Cre@fX0TxWCe2s_iS8z4vbL=Z3LfSTw9m zhTzhKt!iO`O%;k)%Mu#=UwmmfJdM^YVtz9s?qKfX33iNJ!N z%f}AGy$VCKAADY`1ctzgV0JWAm>Vxb8G!7SJjNeax>hNF1PcT(1_;JRuW;LR6n-Ek z;Q4{slx_jiy!oITSF6Hq*Sn9svbThZ2MD82p(3UYBBv8%#3gpU^sP?=W9aeMi~Ak3 z@ce|+XH6y2x*xmBJi<<454>;6&Ll&*X*mJyjJfA8N%VxI1VtFY|L6vOruw~eo{OYi zEVyhsL2GP|4?EKD%RoNqH3h~Otfbfp0+?;)mi)ICE^+pHI2Tu?HSY_lJXRxC_}&IG21Y^1_7#BHK9ax0Wb@?F zb0Wbp45w^6xgv4n1O0HYA)79MUaTBicpL%^X?!VU_`UO=cYgts%`tQFFbP(hCtI#S z6<&F_i^1ds!ift;Ek`EFlstVQrLk&>qEdY%?Wn95a{WzeVXOD+Rxu^y4;E&2uBu1l zpvLQZU4f8q7cw~GPI1k0u~;Zp&;>4~7wd`7SRWQ?i#M7Dl{5qe&Hg~$#fQl>6SIRV zdcEvJS=#T;NcI$zFGYL=YzQm^m3o*yukt%YQ4M{Qx~tOLGKMWt1!$2!;&f_$fwYf* z7jKF{z|HjvK+ZByMymH1TMpxtvKYgT@{nl=2~TQ2^qc)Tl6}p%x;iGJ>XU+Rb6ffY70_`UOzK>egnw;Gr~n2NiN{4 zxEL6kp%Q>_#Z+w6ie-geLk|Kfv3n%_5?;^PgJ#!&?dWQ@#;tK;$=$Ce2S0Uv79$D# z4+XRW)2UD&T@^glV%tV@RC2{#F3)rU*}nOQ!m0=xFf)A>pMG;Z6sRkTRRJ-*eDr?v zlM_lJ7e4mz1z1XvkZ+_c31JvSZDP4^EL-aNq}zI>;{mCT-nR29qmT*mg`lg0p7SmR zg1MFVe{Kd+*C1~HwCY3Am#0)-Sps{iLAUm|UXsEE&#QVfrN#q3f_ZOz@0y_0<_@XGS3sbI62(S>mn$6g13+|_HUAPIg! zlpCRntTB&ktOJ6JnY%-UpH&Tc(^uJ)Yq3$L4)}E)z6!Fx@HtOXLQYs8tBahAIUvW z@W$F?sV#s1a^+QR-lK)&j)TkUPCs1kYkMBLrKJ`cd2h$bVYWZ#y}_{$E0v;p=$BVH zpiPZ~k~EVB{-OIMeKAtGe%W*HOEP56rO$iLAULBkT#>O)XCan=JsYd8OZvKX3M0Ja zWeV^foxO$Yi&ix2&r&U|R{Z+R2P;Cpk~D?MQpLSQdDUg@25vZy`t4Ys+`#XRX(*&S zqsSYjFc0PB&$TO8531AEKwJXPYF;=fx?hMj@dP_w^g(J^qN?v})eFyy9BU1&({uqi zzIQ$jYu#M5edHdzSZIB-%MQWl2;1{Nfy;Q}B)$vLN#NMrH3*puO%*TmKBcb%;H<6m z(NvLMsjZb5sjeP9zBe|?9O(ESDz~}C$fECgxH?f>MPDLu>@UwTpb>f!#H}@6*#iRA zpvz^YzS+DHg&=xu&qe?Xsp;jRr)kT8g*gI`+EHCbl(g4df+4Mml8mjQ?PECf=D0z# zH2ug6r&XIoS7qUb7Ljp9L>^>5c;^07sXcI>zM|`5OQmswO$!#u#*}-?d!g>!s-p^C zftX&64icfVP9UIWgu0l9s7F7YGwdnVgW7?MprK^O1hipCPc~m9KV<3cnfL6wd}B9I zCO|eO@Zy2o2^`|bCvZpoPJq&J@v7+{g)e`9Y&=Z^vAFB)2`zu9@&S2FnlrVrJ9Iu7 zmts>mqRb>WGl)va6hq>7+kv=SWRv$GnoHg(${!yVB6On*H`rP!o9n7<3Q}H^L(+P% zw0DkW(8D3Pw=M6bsRl9(_kEdlFc4}C7GB~yN6D8j8heegwZG27p^sQVS1(o$e0_*S zgTk=H{?|`{6y?)`U~qvNhK4yRyWtIVB~5;jH*1U|MhlHi9&SVrwOoyPMs{B^;M(~vRYV=73ZecSysZGP6C)b zh|Y9H%c93|lq{0iZhs^1Y(pb6OSo7RF*3sTR)O7Ob|dqQ1SE=DKo9$J9a z8ZU9~r^hh1C0UOHTWf(d_7e)eLVqG)Ivj3m^0MaLmYQoJjm| zt(1XtzaWdq-m>3iE|AXcIh{RQT2ak1*GnetP89^qwL6*>LPSN&H$ZMv2N-17$F{Sy zhCqNQ%Js*}s0JhF(=_k=^!9IS1;`Sh=S1gdDQFrg7xxj%i05hFtcO)8F>26})``3! z@ca3=%Dy-xV2p~r7lDBV=mreG0gjGtXPYfl^Gt=T`(U)og?O7XH<5Sl(dCyBWu@iw zStky_upuBt5TkAbcQMq!ZdK?9$U1>2U~1LHTx6_LRpRmTfV*Jxi@Nt{wljCAx@n&H zyS6Dox34<2Z^4=%^h64r@87RXIjO*`anZTnXf;oKaAT1Uf(f%*0taEP*#!rA^ z0UB3?ubEgzbM@6VNsE^|BQj5MyR3dOD0?Z(OxgbVncG+Sp+J0?*Ok>7jf*b5>OP~L;2$l{m zI7B|WF%^lv5zB(e(rp1`=+v5Axv`==p6Srgdp5TKux8Z2gjdAH6}9vuPLV@HX~@sw zN}qE08@8)A8Zvn94wz4h4Kp0?ii{_GHle$$X~0!LI|W#CKB2r_>|g{n*euak*-p-b z7QptwiAq|$qcPC0g&#?3Qa{;KdedSek9%hKG53^1-M+I5f>xfh=P z^q02#XmGKl^dF^P&}3za&(&F11>&Biiwt6EIB~30OG5c+`)VNPBq>T|W^_3d+~waHIyi9Vppf*Dmiu zsdFQWEVsmtaui|b!(-OD+aF;!4sW@qkWJL<(fRH0+VI(+<|+1PjA%Ec$_+K->h>EA zU^v{mxy-xd;pnM=*;jT-#k}bal|4I$H8Jsd7Zsr?=A)wZu}_EUzE6?2Hr7?$D=;G^ za{K$Pl)6OQQx2NDN)wG{HR@o#oN%)>lMmZw|vX!z{Zht>XNVfvD9g;T^nNIS^ZP3(qj zqfM-|ES<6Zxib0e!ROiW0Q2A+?YK;J3;!0XS*I)N8Bt9URz;s_DHHzzSJ9A-*%p?QfM@C z=!(!4-`nLBnNRzbNMy)oF#Z|pzGB2t>s+lN*B806?w=-?PigR$5Y#wneUvpTe z=zJk?SmuE|+EDf%q&))p&_`+R>lmO?y?K204XR17B7sCRnR_><2HWjl5jAid-;Smk&S)Dt0UM zu-7*cv9TT^d7Lq_?g0bSPh~UIOi20JXu2j+L~gRrKy!`g*P5=p}nh zY||}*hlT^sy8K8jlv_Tr|G9=ma1CFQo#RF{*#(-;DKbiOCb|`KZCSPjRcas^gNJ%K zkD3n`9$AuIr>A%I8u;=w1(cxYCAD&;iWsApIf|?i5FGZPrXA?<3&0T9S3PG0fl1 z>5Ii85oK}~>)Czqox{;XE<(bvIt+4B9%so$y;hSls>I8AKl&9J5GHWsGU0^ad}6^ z$G`OJV^Hyd(>b6$VL^iF*S|o2@z;-vtdC`5LITeJ^A~=-Oh}1?4{MxPaZdg(FJ_R+ zGNR-g&-~sWfB!x3y8-9GelYEnC8_pbUc3RWBLi)G>dy!G`&k;FoWNv}75oG`6MJ;d*Iab5}dEt-W-D7$zD`e0m(oFD= zzxL}#SuSu5S|YZ8Ki!`-xKjo0V`@Ij#Mxhs-0vy~dJo1pa7=K~i2d{5el`k>0tVn_ zBzz{^>G-P!`p1$nMk#??h2lKzKmYC5Xa3jM2i!n)gaIEy@t<$-;w3>C*cig-FOR+f zqh>nKOREbqIH=l`Xj06617+Qv$<6pmkpYoMJGRb1J0X_OfA>S}FX|BK53W)yhi+V-A2qiFL%8(olwPVof%t4Y2+bjt+_76F2JK# z)}oSjI_9dG!42I>(jmu>(~9GQQ|!vaZryH@l!N#JM~PZ12HDCdQp z7gV+&kQJ0rlA8OVvQdCYZKb0&c_Z4Si>W}>;O%VQ4$9yJYA}u|s4pt&2JHo1!r0mQ zXVvL!(gAyGx5k1ofuq=-Ee5?wxL5oZ8U9=EC#m(|Z?Sx9nsVTPWA{S&oUtMm@Ry6? z3I4u-aF)-}WiT(-?H#=dg?UN>QrXw7T<4pI?ZVwMPZzcDGTyP{piWwCFfYqO5keb- zs`Ze>V*$#Ptx@j{S7U?{V>>JB4_8$NFFjmJS7;M?Y#YI@+Z4cAY3`^PFR05PX;&k>no(JXHrFXV0uxuh{EGye`BbncRCtf68XkV@q`_)ChyN5~aJv_x*_C;gQ@}2_|cC(oL;Sr*JC2A5336h{R(7HQ)_usZ0Y!`VK$mgE`+YL_7j6AX8 z4Dv@kB{1Z{muaQ-LRwQw?Ng$8A3qhyA!-)ev zbhE`W2#Z>EBgLlgz`tVxcm zU-q^NXOF7=hk*vo$|&9{94$tH(#U+0itX7vdJTvVPCHLpfG4lXEf7x9{Z7XVY~K3` zqOt3zV?fHR$TsgHfY^cK+TSv^YRr_oynmR^~5g$4Xv z0_4!c8hlRvkiYkIE|=^bNa(^<69SCww-%b=M~*fFvNyKZJl7s|UW5P8s6{n0gADGA zTVNnJu==AzxO*6Hy(Z3sQ{{8^%Lt)|r#LnJ6F+uyA+@}}?hcz?kahun_3Z(nT&?yp z4;Nqf5ed;Oa6AHvst!=%8q{6`aI0DcQ7~%=J(Le^N9(regOwZGUFj?Ni0l2MKdoXW z!a`LF{9jz10&5H}M~8}G2}fe)B#LrCCx>$c2$hpAuQnKLu!wGNtNp!=Ko5op9MrER zWq?nm>>Qk*r-UE9nX2~YW6Gjrh72(i1dM$TA7P)SVVctZ6vqCvMF^Dp`U;IMg*D5$diz47McNm6m(r0WiZy4<{Wi`t)76jmPcc3r z4^0T51q)`d&x{$c$gbYeRwuz8?np;_*X|j8$JB1!DtqINNpEjdW#pVaqHsEDvzmf0 zrtBS@hcaz@&4=pRF6sm-#diy7`@}=VZO+#j z=X(3UUb8WxYk$zJJwLeNF*Y`7@zjdT1mUo`y~snmY} z6Rt^)(HB-kwBg1>wA2YrYM1Yo3C;63dWVsBadaiw*W~CzQdP(s{B$$qO*Jo}bDktx z;^nLkusX1f4MF+=F|LeuHfK^j{q?wZQz&3@D2UW!5}?%u#N?BOm2@E$QK-GB6Qtta z0X!SqWUCRN-tbtIo_gKj&%6i4wm)z@5c>j1S`{m<^guZ6J9;I3b%={bXYVr}+zL{M z9jMmR$_r@QJ$%>q+;d)sgkfay%5Jp^w4aRWeD-EKYEJqla%6sxw!2FU?Uv1v@U{CB zMTZ6&N*&p_A4tythxF-y|HIy2Mn$>xf55mRh^T-fAs`_k9ZD%ET|;+=D4hxdQZry6 zAt{|QbSvFsAkrPuO2g1K)XY44JV!mh`#x*^U;N)Z&sz5zm&dba=DPN^uf4zf`>7Rj zilqx#7!tX41k{UXah`!M9p&@;y_ixvMh9+(GQgrXCM1sYmuq$T*jUDv5B2zr1qX#A z=F#Xt+;U_0HOI0I*E*amTKt-h+0C`9FB1m^DCB&?-9q;O#4O#$JGsHc!HYso%Y-Ni z?2g3#X}#dj7!(X!-T#OUpDZ1^HUE)PaWGLFoG%^9)tm3de}7BjW5S{-E!rsLR!bQZ z18Gb2-I@M?%-C9#z}6y0d67@Eo9ajTX2h1p^uBI`VE5wjEDiDC(@B$x;!6>4$`P2o zQoXB#E*}hI53kO>I*GDdc$1D^wJ8}LR40rJlo;Q0i_TDCn&%qEyOvDT|2KIv*P^)V)L0IT- zqb+r@lymlfGOSe!xP*;rGI!@(wrGC*W z>F-&POP3yaH5Jgj_N_9^QdSNLI}|{%%up)rgX}8%lxk_0Iv`DvOD@d_IsL|M@BVp~ zkPp4aPe$(Ogt#iN3XSECT2eut|3H=hm1=JNbBZ-jtlo_q0h`K%?N^G$y)=~1jWX=0P2TH@5`{XNJEg55%+DsKY zL`y37HO2{wxkN+(1A}fP{Ns4qbuoZ$24VzS@LzMQsKPA>ApBM1~(X9{0cmRweGUYd0R;NQA3A zQO$9WjBc-S?;7(wUfK~zKU7iW9*OA(rExyOOCvt?9EsB40Ly*gdtns~uXQHx#4Ho%^**}A@rCjh)Scrj)A zewdwf5n9K00mZ`0YtYfPm*-h%;O}kw;Ih@bA2LLC42$wUXf$KFF>{l-d04OUJ`7F& z+s^S@jYRveE-4aWR&694#Ku;N3f$6mzb{Id)Vz;hxx|SIbB?|h_I0qT<|pr&{q^uy zX1-lL1%cN~YxOFc)EJ@fqDy21gG8|h@M^R-k`IPJHExV{H^E*jrDPKhm93#SV2H!s zSK^k1x-kK8Vxze~ERf!BAo#T)#n&EB z>u#B%jG|ynGx!WQhU^JWg_2|-a1~x67MG>&ZuW$9XfJ;EmH6^$nUVSzQl_E6oT7S& zl0wm_a>F<7R9+&LB^d|B6jE05UIWB21{-m2KY>eY*=CqZo#qpUpVKleT(wTnjT3%_^D4eAlM?+D@^HU!n6s zVAZkAMJPCw(aygNO5T3O-Y^}85C@da)n0;oPg`S@nK=U9RdJ{Xy|{#atXXoIICrZn z-IY&#r~vu4!!uPZF?4QItOCjs3%|e1XjF9w@D~Mgx&HPT2N@6&E0{CJim%)BHmfxl zP_0UN8A2JPq6JY6Om5Bcbe?jtII2m>o#VsGQopW+ls<-A?sGyWZ6fm(z26)S@BYxE zI2lSD;b$hg_6*0ntRDB1fZEs^%PRbEkKK+?0+q7BjGEM}!tvnOF;b8@wt+Z+B~jVV0cZA7KIt(&dNlKJQQ3h-PxErX=>V3qH;+UUKp1R9 zWZa-8oh)m5v`|O++sA`Rw^VLK$pq7~cH0@sJlwufR7N3(J#_J$fdm#Vy{-4yfl^*p zoN+q19%uNb;s~z|=jFZ2=e!;uhXl5g*ENnx8f1?$Q*KCd;#q(W+F0dv0UxogYLd8v z^z>lUSz8LPE9#rcyZ%3fn>`AMch!Cc|>^OMU0(qNJ&pSEDue351TO`x~MlxM&HyPFuhO^S9gt^ZN95A3yyz;+$(OKQvrM3G zNQtWrM8rt&qphD9xyUfub>(f+YxN)wyIh91dD-WTya)=aGy>+pNuV<7A_Tf}HdD=n zk_&erkO-CVLUG_|yA_BM;`w9^a(OcI;A=@`+HvF=_eCzAA{nZr=Ffb+7tOqtwD^mxAl!EfnX`E#3IrJt1VhiY{yJ`Gry&&9!6#j zwsc6!!|>_$pJ^#p)ZvXSSUItBLKGokdFv8?f5d!WD18r;-)c8M(koDAmWIm-k?+xL zI+SUBvt4s|ykJU#dr--Tp_2Wt)sRl-Sc<7Bralu3Xb_y&9wGJQFZvnw3akJ*JdFA^ z%tLYLCh49~zON?s&5C_x_r153W9s9xbaPLsiLNEB3>zBdjysLyl6y3Mc`So};4z4x2D~ZvYV=4xL zk@4L%uIksWovrmTjOtdtHO-Fbl3 zS{SiL_j6xUx*cnc(Xkm*xW4Zlnl}9LHJfWJsS@6bXERGm(5s?U0+9C_OlxG3+P%eq z2O%+_SS_%);hmflbkVdPBySi#=%yQaGIl0#d|kX87~+qk82BkBFZVEH?h9RsrSBJI zB@BMJ0JEN7j3+YeKJuSf{@J9MR=54RG!^-*_Y1DJUx@i4jlaUZN4g;HwP9|R4jh9> z_x92~oXe&_zIgnC`qUm^&p*M0bPn~}8!|&|{ z&%{N5y6l=T60&AaWv(}x3AK06lzK*8!%i3yZ+qD?F1Gg6$dWt1Z4B^ zy7Fq5rw@NB!FFtR!OpoC_8V&eLhg2Ez3s>L5L{h;D?uQI!VoKb53lQn0#ZnBxOkq~gzZz_-($4@Mw_*(p|^nP9 z_MoI@h3kA8W~u^rHfHEZei<&Z(e9S9DPyW|i#4Zi^V5a3GVC<&P>O>v>p8_`!p3`g z-uHFm1#e?)jV!uoGN}y&>0Hcc&j&Q4tp{SD=FN3mEwo*rZZ;Gv%9dm7GqAOS>o?IR zT5Csxke-yE!6C`ufS#EVl#?{5LQw`}8p)w^M-r#5w{Ze(_5k9L0BN)N;r*1{E^_F~VF( zbS^g(BL1SNV+EGGGqSna0&-L# z_3ulFK>zE;qY&r*arKu1%NYiAx?K%#8Laf)!1F|`SC&AoQM&UhMN6Iy%(um5##_xf zQ6;eF!JbOAC=%nHV0GV5A=KUuCCgqoD~Dm-JQi) zW?wGghFGB9JP7ZtxIH_lpV#` z00w_`L3QdO(kdYkwvz2?6g?aPnkn*ku3LcwhUz{6og|9Gzs|joudX1jIvK|xajpYk zK^Mp%O|76sCIL{x4lj4<2J3^jM5m;)@ z$UL{4t$Mp07KM-yJ1aSYKqgG}c6RGN97AQ-5phW)SBEJma2#j3F7IOfMAa_uwWvSM zvrJ^W2+Z4<=porWCAYzS&puTx5#_{^3CW-~kJ^#M(100-8 z>ihjnCC8vn&Lja@`WIYWtIUO2?ay@&)sL=$`7fcMq*Rc>bRMNgPh$GYc|Uo?%%>~O zSmJJlV?gsFn|Ph|6KvaO{$Pi4_x0k^{l53h*Kc%Hpn+vq;rQw+B4nL@nU35P2aOz} zSXFqmdA-z0sdr&89#P7(QEY$;^*&(7M$X8Bk*fKFmxV z8JKGO^2x*aWmwd-3vM5tUOCutMA;Pe@@vl{w>M0c5*Vm7WY!Unr$aU)MxuWADW+GR zj-x?Kv~*~zdQ5y`BYmA9^LDKr)colW0x=y=AfhVosTs>T_J`-qB4AE#dUJnuM8u{i z7Z;LUoOk<;{m$Ow<0z;rO5rLA`>`1;srAv&Lz^pqC9C>lankDxEM#1QI*dpHGhpe= ztY~zYE3ysb5qgD_E7A?yviCu66xV*gsuCc(e%O}NKQWYi0!FvNx!n4;=8J%BE=9@1 zc|KtALjq$P2_CM?nz)AX&{w155*aELO(s*eT;q*g3j0LFmU5pwn6-m8j331CoZJPy zkTA@lpy$X?esw4y+?<#a$6C0>{hwA^y!{*1Nm zdV+^7JK;MzCjMEs(81E2>NXtq&&qAyFg)3aAJ1JvkHmVW%N^El%ZYc?z+R>xZjHHk zOr&mt7U0P0w{pi>jNNMn)}&??G&=wwm=K8+zfNHZ?2U)2iN5UpR^~{{)UukN zCtlf00bHR+4E?KfZEuR-Wpxg(EQuxGamxuCgV3yem*%K+F*uZv&RtsD)jfC#xa#8ru+Z*!|NP z=wB=buMaM2yghr`yMIkj5w=K|!sxleOq|ML7_(4IGXq0Rc>7V-Vvpu4O=4gTX?Lgp zJctIb60G%JmJ`=3=E^6&uF@i8$0owiIQ4@$205zAw>?Ozy<4op7aTJq$=7Za%shrz z+7Q`2iPtpi3HQc4?#uGd&(6X^o8O0D?vMvPA==#QqslT2g7XD|L~|fMHSghf1goB@ zZn(^V#wGV>sdi^6=$>;&gp4j@>Ssw02YXL##VnsV7!2G`XffwxlFK-(gYLd!b(rhF ze-NKbY#OR0iNt3hVr{?oxKEC1KHpk#f5`Y0I?8jV^?sNCJVOz*{=8;sey8gvt)GJl=+PQFLmI~Kn_b~%TYk2ve8nwyUJm_783=7c8k;zH8R zhb`Y07hhhWqQo3CY~v=fR|gkSBiV%V#N!+yhTUiC=>|6e4DzL>(wzhFCQ@T0;dbc- zw_w*?8p4?HAPwPaTN3Lc)33t)1B-r=B&vf}R=FPD{zbd^36ZPaV7&QN?y19Iz&4fr zU_c=A!!H1bC$|}?Vim&ZeRiA4Q9Q-EQPeIB;^)VyRSV1^ctZOkp@2GK7#`(!Ho_nN z0AvS$+9GKtx21^z!!^MLc`Yz?BA6a}xDecrU`Owg3#`dq36SOA{gcOIO( zXpGnXG1^@?&*DPxnOA?f2TSJw%|X2+%2k~L4^n$KpZK5f?q7I&00|4|vyxCA`F#~E5Kbf+2@@n-|#hSIrz(iUO|)w6$-EEu^!L0FU} zYKy?FP76FPUdqqEmfQ3Q1A-zjx-8RcK$>pc0x3S`58QEa7PWxjcBT4@zDFH)Yxc)+ z@0H%lTHtq*fOEb*0r10B?^m0Ehmm*r>GAlqGfv#S1H$x@!D%89SKw5I5&Y(=@FxIN z?ChI;#>%}g7vIlC4cqO+#c0sMzsZ*RL<{%Zj-Z{h0IZ1Biq?V0q#(|8f2Qi79ngrN zcYPT|@r<2ovqy1xf=Lb~cBxN+D!LN|s0kTvL2=hl1)K%O3OOo?TEXIcEJc9|4q`h5*y`0!GMPI5x9&SCX!jLzu2;fJGl%&iZI~ zxsq<7Cq120s{sHP{D9S|v~LO&m)*mBZy8-0KYIb7ZxamhOW7`UbjwlAI_(MffTYXz@M_Cy>TSIW;r2d5Qk(DO)Ofp^c49#!_PSgnkf(EYntiG3-kx2~Wbd~K> zAye2Lf1~jfH1|fky|u*FkMyC@ATtsPspsB9-cU=iCHUzB{q6-BIuN@Zk2okq#_6sz zGaLp{yg~}gnzO{O*zoOZRO(IUvZvfUF1Ko0LmO+1(j|kLJnAImL39}m98{tPzSTl5Y zKmK^dU`F*aL+vH~?fSYp`{EPOFBbuBuf=|)4>CX@eRu#=#mVL;GgR_2?q&bc9UdS# z=2HcnM>liyioO&6i*JA-(G{%8EROoY8_E6vWH^7jqrE#0i1_xGZyWIipg<`0qxwN{ zT$&=6Jo}1lP9D+xT`io1d!DIowXDN_enQ$7mutE+%YdyJwJ$7QE%;coQ3Bd)3qZ|N zBv*9XyzCrH#)*mX_4Ml)wo2m|EQkxXA;{L%GyI}g7{rGTy!q#Kz6xGvCHTqv2*1vT z#d{m?D>?cm7%-cJxZuiB4LA3SN>x&58ITrtIhUJr9+ZL}muOJPC4gtT73pv5XKdpb z0uG-dqz5VXQ@;Ep_x8-FPx;~W4yhjBV8Fx?EIxe4ltw13D`)_ttA!aoTAkdJFkx22 z30E`Ekt7BVabTYZ-d>`A2M9L6^O?+BXhI6&Jo|Z_M0AM}RQu!Kw)q-1pVflu(E(MH z>M_y_I;X0iJD-Bi+w z1+O@{9MfkU6=FO|8ckvU6V6U@za%77AU&jSU_mhcQ4<{!{+|avhrAAM@ciKlF<1q< zfxl@0nXFURBe0H7bWP9yA^=#=wMGf|1Oc1pMZbeNHVM0!c<4H{igXpOP zu9Z35Z_Bu<26Mk*Q?IfDlm(DgQV`3Ri8iw8dnP&rs6O#)LT~q;KNju*rWcpRBD zTIlWuiCR6@K36MhQHCS0+urrn1yT;%At~I}@e{d5Zo5EtXhFXgRq+Jzv!ajdCjXWJ zFsx*)uUk$oKFMn-oHJ4`GkXgdVA@bY*9H(MKlz6{tU9&T$L-6JmhI0o(Xk2rFY_D} zV`TB%Rc7hT^%X#Ocf_A5#1dx)QHw08H`yA?EpcuRHvuTHJ2ZKUP%U@^j%NgeQnD?S9%TQ=AO1p_*6o`X0L+OJU zLIQo*Pv@%y-ygcHddr>vFKGW(2vp-)4>zJZtFEkQVBA~IxRu3M@ljf#5QJ~5|^n8+8;KaWBbJc{T@PU-?t?IZ#j zx}v9P;iP^`GF1cF3;iuW;^#s*e!Aq1T{>gVeSkE_+GD^QR`q0rPbPZ!iOG4^5$;<_ z^NNQwyYiEX23$vqsp>X%hvdyZ0MIG!O8c?znO=8e=%((c$(l+sAf~cT)acg^rl+ya zHk9(!j@F<5@wnXcV7!9+<6=DG#44L9_;yIHia_ZJKqAXKiZN~@2&NAhoIfZqqp!*c zf`07_+r7fFbLnjiJY{|vm=&o9qaGmp00r%P3^*@nzW>&`@5mnI3?MLVhC zmCDxC*m)EH7mn$Ya4d#ZiQ}1C+>Lj($#5N(lY9)YHCHYydReux(zj?leWb_SO-2{< z$RNdCMK`&`C)Ed040K)Wh+E-2%`gP|)}gY_K*g^2*4yjKbx{64V=06i%X7y+J|uBS zT_0Zg)(T?a-NtJ*@cl*a;JC$3zEDWlh_3ca$r;3a7w0Bp{;bFIdq`#6g3X-AYWvn?(1S_583XaX^q~RWcEaQbWF4 zt|<2{*3m(s@5xN}$zOi=g@R0a$73=dAajhZ@(I-i+$7fk6=vBwB(dBzc|P_h9r9)u}wf-Cg=T`yW! zykODA6(0NG=u*SKH`doPb>7Xav0W#+6wWI6$2Z7ZgtG!EM`?9mk*l7T?Z&7-R*^j%1W$M-*aR(d-F}8`q#jPy9m?&1c2v+w^);#uY`J5m!IeBt` zVpWt1vtT%+zp4tHj89{R7rgp}6O|{B*tDZ+9TZG2AVUdyT_Ju5R;ft=6X6B`@0^Mj z<=+R~fV-e3+3vM}Un(jDHk`L~T34psK_S0iTqk;7mlmj+UjS9JoqIE8d=f|9RAkxe zBD&T-{+2q_QmM5-Et-j%d=h@b+QdE^?|I1DAN`=LSc#pt|uB)@Kv6Q^U7)gK7q49@=a(aQTwad^NK4VazX9;=O0>s-%d?_4Vo#E*7e z@<@_|tJN_H3ChX{*aM6eo*~lEeuc^5M&uGrZ0q#_ej@anNnBA3e5k5BAXvNG&&Txf zA$PqB_eU-XsCseMgYvcu6xj~-Ci}1qJcv>BgJ3ze+e$lsMX9bdeHWeDY7GP?D4j=w z4zboOQ5?Bpf!3_72dXRIH-KKxqc!%aeQQ)neght4ct7@)kJ7fS#HSY-ykgTf(ky5{6&q=iv}kS$dgF}J66QtYaZyiCeb4A zr|tp&_ zs|5qDdu{kS_yuGsl0*YtnC7iIQpJl}(z--=G)!>Y^Q>&e-oi<~SxFnk*Nxk`{?a)Y z|I#_PGQ*6Pfdn9J-xE+v!{~gNov=*sz6+IhMJT?h zoTcJ5?dEY{fgH7B1`uSvNL|LZ@uq1h{U@(-I>M8!wX|Mn=gu((AD3|%B%Po?98*+WUy_mTk(1{!|#K)}rK;JA_9&Y`OnsXqg9(Ss8+VR@M5=ptv0N2!+ZUmIFAz} z*B@CR;k777m>#%ZcmZF$Fjp@Y!>D~UiMLiNrp|+0v3+il!C}euazK`DveyCEaxIt& z6fv9GKUA;&-y<{83yIAD(yL+A@uq(h`<04xA)FL}rH_8#VxcT@ND+~udT(y1mSr5R zXw1C_>PIAFPNNj+%_ghrmE<||pZp=n+N%;S9MwXGAR5qXMr325k>nkD|0Jd?L&x<%zaPt(&M-ZLaPJEVUWQ3SP` zy zzNWGu?3QTzff;ZWcLMGv&|4i9YQ++3_Vj>NI73R?x0#r)ohy88hE05ns2s>;#uS79 z(nT9j1IY@)2v5cILi@Z%EW}sG)smQ0GfUdHeJaaia<9}0GO`k-KySw^Rnce|lTd{; zh?V&)GS8wlr%MUAq!xE@Iqa(5g&B9qtO^8K6NgpA$UH1*v@(|y%9Cc<7LuNcLNOGv z@N3j%(|}#Nkmj?VUnmLywRW;5%QmTK4Q^Xqy#T4O7uy$H!IPuMY8T`c^L{damW6R{ zF9RmfG?stbT;!m)t98kwA;IT)$sSIZncA-7GO#zSMMvjeRmrWr_|A~AT>ZK^lp80qCtXS+m;pFgRTq1R9Gxl=OM5C)$G~Gw9K~l^RDT;Lhweej=Sj|} zOBL?qitM&i!>bR0a6~ZVQNPd5rY0aOnwUroZ6Qv>$r2^?*(v6$ zY}9G8i%*F2&lF$3t$uGW#w~YOU1CM<5K5j@I;|mqn~i*8lGbTp-E%Wp6dV+cd16PRp!9jzcTaWn3x!8Xey`vm0N~}2=E-;+*nNT{F;kF} z36JP4e>8BhPM&87zQ7Vn#V1U|8M$}*J?MaVUUKMeq1+B)<}~_g@X$G=)kL``;WC>6 z7Hv281E<1=3pq!+Ho6ajaie)FXujC-WG9wUSdfhO~Mb4@vGJ%RW#_>EMQtHpQ&P})gHt{)0P;+iSt;4C~W4AIeg z)(zYYE2~OY2|P~>SB4Jb?_UwYCd`iSn$85}oaj0y)Wr5d8`Q8qQzUif!X4W{Oy!;_ zJ7K7*DO|_vd*R38h_+?zMQn0q@+(9RyFPxlvS{j_$Vic2mDJy|yi);1pcpHQZhx0f zze;XaDqG}TfO+wWs6qWi{dZxa!oeJ}OH<+Ohzb_|d%N`?O>JxIK zPe{+7eaJzq_+A;SkaTJNsBIpQI)XB6aipNj~RpZcq| z6~$w>--6|LH>mvHUiW}}PP(NV_m~Zgs)stT+h}sfOdn67hKW?jPPkpTF7JlF*s2$YG)I1Q|HP-&g{3`;7Dk8ti%_@@XH0znXb{eJ} z|5Xk7OP&KK(o5c|?MibRgvzOBh}mo@Z$|5d1*+MO=@4IOeW-R{V;p0w1a!+l1skZ9 z_m^(j<7s%)qSu9QIFybdMBRN!h($itAr%&EY{(qZzN;_8_2gdb6Lb^M7Fy}0=ko%b z?miNq8Q^q&8KY0j60A2g2g6=)5$m_xhp=d*(YqfAPOJm4w%e$}SptoTy{bPWottPa z*)K8wpgsCb&OmsnJ?s^&1nZXWv-?zgtW7j6H?G%=KB_9{IkWe-&kG~}2`GWMUoK7% zUEGp+Bk8Io==4B0XNf>$K;b`uI9F;M5GP-r(8?lg+Y-sy#lR$RRVJk1F6Bz#hx?Z{ zci1`XwGKnch4O7#|CA#aZ-Bx2m~~IliOz4g54bxw+>s+Y0gU9=rMzp$ICY&^CWL|$ zViEU4j&pn&!$v283=H>&+y8~kDN+nQ2FRS=%IocP+n^{>@1sVJ5JIqJ^%JpcDPLxx zAz{~i-13@wS*)DkPYDDR%30DiI>e{XRZyLli?&Q9E(lu0ByZZJd9|n=nr6|rOWv=! z-23jXYuLr=^6sr)?makC*m3HWyU$MHi^lkq#i#+clXod~;V$W)O%ivC7RA~8*rc#E z{EJrhtu#1XQ+&#{|J}j(qtgA~=l(Y?zn77}5##?)&6m@U*Z|lA@J{2JKpZ&MnJPBY znJAE}K^OCiVT2UHu+A_c5qfg^#|9xx;^6q>G${s3etewavkt&E2VBx8Q?+?JPb zC+P2gU8&~k#;f&O5S*6k1$ts36HtybVDY#EzD`&|s^-s$@%Ph{+yj?WV54&3cUSG# z&ilX5{cl`;*QfuRFJSlk|LZ;aMzlg#{cYDBl~|I-EAXKE*8Ae;*rCJBxbZK!A9E!` z|Lgq=um{K3=7aE2x3p#YAuGy;K?m>kYqaB{PtA|xmW7zB5^Unk>+6S?o+U=9SCgiy z3n>`g^4>e6Ns?|KaqcLtXsZc7uR*_w2nK zWWZy0?Si)*n_QA@quFloyAY=(GO-1t4ub^JCfE%_weMW%Nko82N8`MiG3{% zqgF?H#+Gqj4iU@rlU7p)>N-8;nJOsc-h2wP-$sH%OJk<_+XOe=!6{mFhjYESk4TD5 zojmFSrJs7ZFf--rT@gYaPbZQDF+?fS;x$Yb@e+;)1^!z5|d?uSeCCNUEE}!L2oMKH4lWz z=*l|^eC4jTu6{3LT;ez}MN7m!ONbwH*n>)%s_*F1?62=Ld1(DQ|B4h#61%2@OS3zM zB20Yr`Mkux)QZo}#oUNY;8{p|k0g~fze^cR=vLo((8Zkbkny_|Q?P6}QJ?5&3Y)j; z2zn1srl&o$^93C<>-`T4_eLIiTg&y`m_6$JRz0Mk*4gB2DDc3!@u;tS%2(irX~`3o zLGC4i3u(b{c}n+0D}7`rT;K@(CP)Ca!Y)0hs{TGWBy7wnjlzSnso!C$fpuYgOmotB zZ=`Xl(yaU-jvX(@Gx%IZ){twwydUGXoe8;nrm#y1Dt{s_@4vZj{JuosKDf(gO$6-c zFZc1sneDWMq3I5z#5#pC^{Fiux`W7reU5F+9}g_u=xV)n^n9nY^(Gqrxd>$R37eTl zyL|2;g%1d!)HQb%Ro*U*`-W3jOCch+TZ+uemloz#^ds1wsU(%N{8(ONRzY>zUJWn5 z9Cfa|2k+Ra`%RzYO{~E!{85KhX!b z{eso4Dy8oI*J^V5+*R!)$+C%8i?qZxiF=ZQS2||Vcx|`De%&#R zVP1HO6>k_a$Qa-QV*9EB&~4f(PT0Bd;fTq3Vjo8uZvj+GODn|)bTqxOLqU9Pcw=A{ zCfktacz2;}JWKq)Ce4ROEE*w_@VUI%eIwh$!%v@GeH7pj7TeAyC^c^71X;ZXBxI}- zbcj*OUB&i`w>mZirWbA`Vjn~-qm5N0gN@ABWp+A)tl*YKn!ByEB7!6e`5l^Mp^VFh zJniOXXh}?Tz!Jrwm~}jX^_=S&=p_bMT`3=9EUMR&_QB zhnOUqr@MVQ?EW{4CJX%b%Zd&7IlLl4+a4<4IA}PpblH}FPl8%xd>!uzzR``g@=T;9 zBHO#8G9m1@uM#AifI@}xm#>FgXLvQ4$u5VME*-Q#+gGrT=It<$8QLsR@lX zU^1wlhU;hI=fj-HO(yo+#R*+=*lUYxJofe!%5;Y5DgI^jIB=uaq`Hvwm^Li0gSdaE z%62&3lXiCT%JOlkEg~!v4Ij0l#F+aD2GWWPY>d(mc&g8CQcFOPMYQrs>xLb}sby)E z@cpWFb0H*O-~Q;{IMPGyUnS!O&rFtB#A-7y; zLK6SQ?WhxuM0&+eU8VSMyQjdLxNBQSbyjLG9o0%535PXD1~Yf2*tSoX5FTgpO+cG$ znf5V{QbtMllP*UH$ve~>RbW#j6l6RoeaZ)9kw;V1m8gA;yIYB4$|wDKEuQn82IB4U z{S*`^f}}L}#YCbvN14Ji0^?c3x-)0vKg$i!p;R5n!;~CD3Vv;8e|GZENs=6R!O3YO zJ;eQuek@5vlwJ4T)CI3CjADN9SEBFs$?nF|TAr+*5ol$ZJ6fV0b!b_y?R_H0A>*AH z?7V=jg+2{ix%z(XTKNRcWDZ+9UD@{n-#*I5%uHVnpWVe9TQ)y#_6;H)dZfG5DUd?R zA*r_H$y*9vLx1<+{?r2+^a1w+vP#92(&|tjE(#AY-JGk1ZawHs+gaU=F%*&eY-$z} zSpNi-_}+)_UD`1`Snpt9^yu=nb$%a_@aR~KK8)q-Cm1S8#Kq|~KXv2rU9s`bXD~T0 zb>|K$r#{D{>9jp~hS#7BRj|ff6w-U7s?mia0{%o@orA8!Z(zN4WZ43tvd+Yk5EO-$rMblo(AI4gCTVH`6d#Ic_B( zuCRuipLWBI9a~`c-!CJBUA;~E7&QBLhHVjRE2u&@XlSkV>XEYSDOj>>#ADihmt>)P z=jFZXph2n|-)w`r=XS4)LQNqZe#vr&%4b(R%Qp{>_lg+inMNNlQbA6i2Jfa_txx?O z>rUf%l{+aF5r}RgO^hfYxc+&TfB%(TjnRN{d6J(7Q)U>y9EQiZV19=&u&An6M7`J!%1C8=gkY6lJk@p5t>;gP(qpSkqr+5&#ammQ{bthw|qggeA?UAHf$K9n$}vrlcT zB-10=CeBdY$4@OtsW4*iU!L+D^Cj-cIS1a)Q&Cs@#B#pM!<%gi+BA|(|ND&rCdtlt zYiyBo;`rzPyYvLqW`5AxvN&M}eYOAm-ygIjfn6QP?ci&Fh^l{HMGH_%e>%{l`Q4%W z{qcZoe_)TN^@!{a{(pVvDf~}3e0D2S@Hde6=PQ*@gR^<|cNW)k|8mbSfFl6Wtb*OY zd}SOSxCiBrkhx3$a{uR;{Uym-BKrU3E4c~qF%qXvox)->Uw_;Cm6yFbNtmKDG>P4s z#_P{I|KKJ2Ii|~U#BViqR-d`l)LC8=|XJPS36-ati)O(oWri@9BJvpGx|x!~a}hMGXt3e=-Sa&>S#haw?B z++hR*_Rl}w5na%^%sBeOAzfJk_-$R1{O2ERlKwAf!ASkzf5jK}I*rGYe8O|;|9%zj zn=di`p#MK#f%|NLs+s?(wi~bur~c!|!GGc_))4;3*I$yn|AvZDvaD?y@6>-@uK%gi zWb6NRL6^SG5uA&3+^#-#>Yw-a^Vh&F`26#p{<@j}pSKVo&(@oxN{a+(43#d^Q#aJp z96`9yqVVj9KmtN63%}n`G5UI_)sC@P#o*e@FT-o%F!vF?T zd<~)$^6SYA@=5wbR%-m@xrYB`lnnp zhOb(EaL1(+F<8SsTaU$qZXh93j4Vd==?m_EQ?%z=sd6I*ap`1SoR*a z#TP5j!}XQ<*CdSPmh@-I85;-8Yylqen0Z`;PowvqYpn#p(hYeeLk@3@V~KC`fa}S^ zFNYkTnQ8I;aDK<1!P8$>cd|g2%b^eclE_Jq340R)&FLH?X4sAzpSR9cV~D1(BwBqfuoClblE(HL#%Hd~+_6MQ143 zhPH)oOIqmBvx;dfF}cKEvd98(>A6aN>DSy-)bIzNt=nCMoh=!ZOmIPgy>3LsV>EY`lxQ6_7M`88e$s3 zRvaif(8;pGy9GoW-^MB(&bSSKs2YRe48^-s#p?YApGls1BM!8`1cWQN{3$2US$%wr z0BY{xEXb9C4{vDnEpe(&pnT|`0@{#gXWmin_}{i{y)+IyBPZ{~#NTz()`4|Rz(1sv zG!_)JTZ%rOk+neqCjN9P@A^$eLF<+{W9P)$7!VOxd#rh8xCt1sC+V!Qv%*mlJun#M8e@W@R zziKxPjM$2S_&Z0>Ldl!d7f_~4nn3hoqx7plGlo~`p4;zG2v6T{$Ot3`)qZ*3rnW@F z(aS9&^awNLSGaCvTKD8V37u%oG{MnDjY2s;5^^P+jU+9LS6eizu=7Ms^fb(@NmBw5 zqYeT$*6IO8&>&rWyWLXhFy@!JO;*j5o5bQY^RqF24dW}^JK7y6^DABk24R5#(8fE1D0mz&8bk3 z?$x{dwqO_N9uu414>7FN=J(dpdQ0kzs)-+3Qa$ZEg91Vs%k$(1;E@r3DtP5XAb$c0 zI3H_#`c`%gn5StzB^>Npj#`n(2-wNP;dRWZHbMs9?|2Uk#RhrUHQ=YdSJ(}>iWvGm zXW^=G9n#gGr+ZUB14vUIXLuHm4p9T2lj=IZB+hJVd%-8LqT=m)Ums%&FKTMtDV0#F zZG4#71fwt*KK^iF)$7n(!lo<93YYp0RD|Azw0Z(QyNyuTr zhyrIEX{cW~%!!j zM;P{xa^208sk@%cq#qgZ$PeJI`|h z2f@#k6PioSt!^Ale|#X< zh|M&E4IU$O$|ukre(2hH4);4N$ND@XPq0;vqy4EyQUrcymv)TPVN=J5-i=oisPu6C zLNU7B9#Xz(=?z3)x;d1OpkU8ym9>!&hN_>o3Y(pGnq~X7CtRvNbvn964x5}#^}Y8& z!cE2SyW5H=p1P!c5y3OBp5cMa<|iMY4;AXyj~p$lCp|Nm=H(>tyHIkYi}2bb|M3#u z8@SLipJMFpeHaKCttXU2zZbP1%=BTh7ww@CnZ?9sz% zbNR$2sc~g{5XUB@WN+~Zq?JBRo`@;;_TQl|tK_Msc!ps_8AUFgHa$e1o>azAp7(w< zEK?^jg!0e(pbZigC)QVtGWED?+h9%>C&24By#SZ!2(IL8f1I4Zf$n}gtznVEh$ z@_Q9}fLn#&U=`x1G>|y?K`LO%{>+Fy1tbLwXDX1!7uSj8Wd}-lk>2iz;}NLj)f}OR zZIhDa@t!?j)*Rb#D!98?k5_3$4@YXNb*2Cuk@v>AH>H7(GDFRXJ!>Z6X+DnIaEUnz zjXneBT7mc!U~X4J5VDV>RIdVdB#Ojc0)09fr!I4!-`sf17}K7kM`WK0e{6Bmw)3l6 zNmRx7Qmo=k98{;f7jA4Eyu;kmVw4KFIb^<`AcQ$)UOOF4>(X$O>ozMF%-2X>7X>(+6X|1OgQn(@JwFI)fq42wOyIOj;|$U|eA%o{?ww-OCs=(i8fkm7fH zlf+;6@@0}P^18T)UGFfC*H#P#q3^2!g!HYGth=t)PDcl-m8{=6AI%O1kug}}@C-GM zrL}DCydCi)TI^8?%Ktk|Qm6VFU8allkf=P5!if|e45Nu2YL{QsymEjG15K~=@dK2j z`kOZ@A{R~t({B#ya*C{h=!Kk*L9x$P_&i}e@nno!BZ!N8mkjeto^?|!*71WM?lx~n zGb|isPGN~-C06#cN+s+M;Z3l;#FDqt87M?uzMTmdD=~>57LBr@7-hImw1WvfI*ir5 zO8Kjf4!Cvx5+mds>>;7z6Kb4g)x;{TJvu{bZCABA8vEmR^>N^M#yY~Gq_$;Ge?-Lp zh*?a;&he~^|HxZP%G%@jvTOSOvgoy;emT{O@k)ny>IfMt%}oXLTV5`tNlV+4zcrEkStxQ#B&(>cx?QK7C*=|ERP4a%Wvp!>iHV z;tY%CIyP^FVGn~Ux!2y~Y6gpOMSjcMm<|lre>dzfO1XQuMxgkq3EQv}KZ{b(@d^KZ z^g03W*a^+eCSC=9*-7<=e0wzMp&5$iUIUI8=e*fAp}dFr&}GU{ac!5-qZj(6B$Qba zoD=F3a^4U5=MiVWC0u=g$Fet%yvKNUZI;29)wX_EZVwgCCb?bth~qRFUAEmbp}1#M zgGZvd#HE5y-?cC#-t}@_&oPMqjIRiv!q)k-<1Renyums$!U*&8qNfc`M?bhWgF$O{ z-WetQRe`eWfeQ4!7VElx_$f$F+P%s9-kb#@u?7DRd+!+))w*>J62t%^iU9=)Dky@0 z62t@rVjwCxXCxLm2Z<_*sBn;sN)V7#*Y=3_sP}!}uSfUS zqkHuI<3Pf$y`TMrwdR_0F0$>|;q)1S$j&bi#<-7!ID6{iH0)${Y3RrgAx<>d{)Bi7 zh+EgYbQF%c(c~9xci?Nw&D0LMmq@B4$tx1a5-dm(R~9ncmf(MR0)i4W7z90ZV_ecv z^qh4jW7WZh(zAU0%gi)h#Cu38r5H)E(y6r5&0Z#mhK;>Y{t;Nr3N%i1X$yoJf zJ!d!dJQ6>xR|WTbN_VVUiPc)Akw{$h@chQ;@^mXtx`w22N|_b&V><(r*y$O>XdLOO z#PII~DCXpDGLA_md#~~5UzJ@TRCMh=PTdr_-~Mi}13U!zjy<`+{{46`R@N&fmYd!t zk7YhUG%j5@a!AF1oZZxDSJT=3!N{y?iQuc1F?iwj4ADCwvYx_D`>eXo(C?-@ZMz_CTFr1OQNNfRqH8JN zEi$MgDav)>Gge>W*)VfqcRdJL|IV8CUWXTSpw#sl7c35%O4q~6Z6fjXFK!R}rba#7 zPNtcuE2^6e0?Vs{R8R6bGqcYrbdX5Q2&6s`w_MmE5@as=88(a~o1^6>M!SScwl(SR z@6_SzHW@hRe(!R&W&Om!65;@R-g!>9Ad*BP{Z>^&WTINT0O!~qpMklL3Da_BUaZeH z%42EI+v7z^X~)#2@WZWZxc*nZxQvXu)#ga3yj#O;I=&?oX7HKWU;>MLD}N`9Dul%g zl#P^IAz6$xa`O1_sCd0c^*>3x*-;VCrJkF4m5Z~aVKvcPY5y8<0Gr@c6|#E1k!&im zd^10YP_ZX+tlDDtyiNy+pC_f1%=`wVuM;8f7#?0{IB(>2ZDNs$X<2nR`Im}35q3Ea~<$<~5SaNld zP&VqeluPpd%oMarD9CYJ>TN5$NY6t@>VjLdPl1{(fOdO9m9SoIL%dn)G0W6Ky|=!) zKe7A4jHd-vRGT$kuVIFfZW-?ep@~L@cB!T#jnGnHDLD6Qcg?&ZE<)qdnH-7dCk-tE z){$*9h&-Fj7ErmaW3Ya#T1F(IuS>D-J63V1}HbT z8eZ;WOCcgk%ytJ3jCx)@!k%=((?^RAiUFE2oA@d1TRFrY{&h+q|lIyYXq36a;>!nSr#2@AsX!@iyybZp*s|RMXH1!6s*4vcE(C zlaTc6?8!xm?#>DqluNNtnUqE)?Sqf1fGS=z=q8v%PHD0bh1IZEaq*pq< zix!fLpAPM(cdLtXhcy1WBOInic;U7+$P*wAO5Ux#P;ja3Qd8RNL+1%Cd#!Xlj&H#R zHdK3A{3Mpb4W4B|NAskl0Yv<`x>uv4`Iuu{)#qamj|N?o_dE+tVDZs7kH6H8%w28o z!3MOi%Zs&lb~%~HBAZ`*cSDuu9*PqwDJcUSc{^oWB~6jDulbmmZ+O|HGRIONZM6>O z+JVFUAmSdpXiJPbZxM;JOjwrKA>PrUGyJH)wEbkkVatmjP%vbI+r>t9kt=hMAXHz0 zoaD6Vov7H-y&sH-~ z9}X(+!86I?vQH}{ezADAPEhcb6E5GRsK?`#8qeqwM*p#0?t|^}dW%yUkwIh_TM-6# zl@AcsYG&FYW=YCDCgV@_+w>kXj+fbVh-9p<3`i|bv=o4I)ycMwUbnX&1@Ty^j9f(K z2b~$hI^Sq>k##JoPXdZm0380J?)BR=`AFxo$tXo3b7u)IhmOc=80TA;r}+!k!PCG- zl+215ujs$S-7e!x^zmK{`{!~*VojmzQzp8)2L*a6X|0&xj(4Di{`Roo^djHma`la) zo}2UOZvHB(^v|zxo$vTOaex0U>xqv&{FL&n5%w=#Tje-Y21FzRJ;i8DBGqotcu9)Z zqVK*8<2O^?psKja($dl|x}35hqtyV$`F2(g`Y5Soe^(;zYMiF0 z=BlS^tF4Y_kL^Z)|0q7y#IQmt!Om zEgPsY+Zki&H$>RHwgp#yJ^-Wq#^^-7RJ(f$`e_i;&gCW-Hr8bwmFFIK8%+91tmHm^ zcC)?=w#VgUv}XG0{+;e+q}_D@4wY081n;_SADXg8rVKVjG((Mq7B_l+Yg zg#a&Mz+}T^1(X;0f1S8FjtM|ldy$fxL(5_B-lt&F0`yUaw_Hw!31O&JSJfN8y?>Zx zPcMmN8#U2KL0p@-M>$>d$l>@N02eWK6U`C$$1dm4U1y>$J6U!C^68id8Z1Es19j|r zbDO!~=t&WI^EISdDSEi9^l}T*pRv-1B7zrMCrtJ7wP%k55j6*Zo((|_&9RpHTyF}B zY}eHpE$a-#gA>jsDobPXtpM!jiLsL(3+YrESaWv9w1cz~Jy_&`wnh>6KyR$9UHead zQyD8&4vYF!^{{wZ4-TwQBA7n>wT$a-c{u^dd*0=lwnO;0L+Rmi1xhRv#_yf@^u+-AWJdd-KXF7!x3q!kSKln?Mv#d1b!>leSZvU0=Kf- z$XV&F#y}xfFy0iU0o4dRggE&Cm9XOo-=MXR0a#`yX{fM23UUw`yh=~T>Ip7EQ(!jU z%thbu$2B?Nn$Hw4H`Q}Z{-SV$EYi5hW5AP$&j+9Lv}qlWwFF@As;J>?>E3NWTyjqQ zv4v>dqM3D%=)LWQ$DwQI4#&kNLF31-(}n3Yt-USE*2eQ4r`3z$^s*Q#afz6Ipnadq zGj`LbE7ia0kcRnRdC8tcLgKt;@Xo+HA2YW`dTTBP!uCX#3D!hGlcHLa;H~n3A>(z0 zo?2?v_QUVCi67XMrHjnB=pT28y*TrB)Vv(OVrSTHehFGIS}R|q;`;tkkF`+tUu7>t zgO|aXZQ_rW3{b!BLEh}JpHY5QQun$RX7l0L!yY72NH+mDAIEkX!NN*o&$R}fqC>oZ zhU9>nx?VX=Rc<6DM@2>7D;{uVByJWwjH%I3E3kZt#=ovaA3SQteqE?6cC+`{)~otE zkPo~C`!EGIftH+AChEL{mMQ;9zaVuT6JLf5w(osh!GS~5o3mL*%ak<8n|wzf;(Sv? zJErqA!ib4p$ko15%_feC+5T47v0n#D(H9w~Wko+yTCK-E$2f}Hp!L(>HzuTelAAm9 zpekCfCa6e|23h3y)-*#O=5})-NvSYyhwe0QbOCsup}W!|G*HkFrx-kx2GK-Mxq#J) zYV>adRllD_L_Ve&60^7lxfoq&$vc@4tStdJE!@wlQ6savZ zIq(@ZW}@(uw<*$d=dhBLfhM6$d1N2Y2&rh=Jm6BxX2reR18Wz+Lnr-p_38wvc!68s#jTd>TWb5?>F~o+& zU|FIGhW;08gRAl4^|& zYoIXw&tj=a_4jcx454R{6MT-ef2E-67`!G*&ek+y3n1j7w|yg>+r-NpP1!3JmS2@z z{dFHrx`s?SGmJbrD{8IocAC|2*-Q$P%a?uqP$n{mN~c2uXb_`iBKeam4o+m81bAT= zX;222Gjt32$9?eM_~CSdrs2 zHvIz%li}Gnd@A~G8)OBOWGXL9FHw`yK0O%cbwg|t>KA!{8T7U#NFgQGhJN>v3U9~H zey56-M{5HLvJ7gCruQ#@OKVj9EgVU@fILh}S{oTFeX!EK9FC?J7uzk1CKDS&Ot*)9 zZ%JZU_xK>EbWRC-V5=;Liq{wLL(6|1Al!G5$D}4A#x@%6CUNp<2sGL{<_0Z_a~v-( zHu7P>SH@CHR^>Ik$Cpniep@_`zb~G8j?*G-*@jZZP&>mw)m2$T1C4lxAb6dciX&*8 zB8x`YOQF=Y0-tGQL8HzNNx%0KL`cmY^Lj=EmV>pH@1~*R-mh>p)TPm%)%BtVESW1q zr+f%ZNvIW^BEUc`B3(?&Jopfo`W?io45L}t#^px5E;Q8e@Mkq@YwDgxrf#W{yw{zt zt+9?EVK3r@1B74zsinq{5-gFXr4k!K!6ojhE>u)?ctOwhITh}F`57{Xs5r<6Vn~JeEEX(Knczz4#ek{w&;MHUf z&Ey<4IzNv<*FCOG<4>J>sM+mu#*)HzdhOtkWpt^uj67>UzhhyV^YMuTgG{&o+7ouREP7O6MK0 z9nubn4PDJshA_2f8`oE71_yJCw972|^97y~V-N73O)Aitxy>UYpFGx5`pWi90lK?6 zA3EGgTgUfAxRqqz__r_QSZfkFYoSsMN!^FPPS~l`=1(!#9v5r}gm&NxN5W>|n9-46 z?}@}amkJ5q0QeX!GW_xxJ$}d`OoC&1y1)Awitw#sO6TtUbvMG=1bM+&dfL*$gf`6| z2Yg{l#99 znB8!>davfwTgds(tFrv(gOIK2{~be@OcU6l+^ODl2;HQvqVar<3u9*SocFAaQQM z8FwgaOzOrT*9_$L{zk?sq^!Czh>P1rYFZa%B!jYK%uM*CWG32{cyoWM zngY6E*xXD8SJZ3%M6p=yX`Ya4-sR40Qvu~=CN7opzLzI{J2ymPjw1J&CNZdbtf|~m zRKDLKByYAj7Tp*`hb3c>Q`hYnvk<7P42y3~>Cebp*@=Vjf@{F;5lVbrUn z-C8)$9cNTJspiZv5?Q3p*AR`ilu$ogRbgtOb!N{b@NX4>5MB{-%XNZdd*|9av=5$m+I<7iDCw;LmSk z2~AL-uw zXJ~u+fHJ1;DC5oWh8%fglFgH9Kf%JS^U|Ag|05w0 z`Ti2i%#PRRcZU`&mC~bmF$CRf$1y8(kY{SV7R^!-6$=sp~1L^CpFPX=s_a+|7?gyq)HUuXXj$ z;2X^f7j9`agdC|Z(41cw`N{eCnWrj-ifobwVX*2RQyasf%nD|h$wq5Z-@d^xwSFds z#AOUm%rVQROYOE3)a{fRui6G8xlLawQRPK-_Kb@u7h9sxVO`YU)-^vVa}h{d+6r)F ztjrHMJ(Wp+kM02&Q>1#&#FA_(XE9xtQ=?_S+^4QFFNf*mCRz<23eZ>MLl=F+vD{eB zPi>6sa@81YQNG-*y67mAmd`ztwS-BNF)FOJ%Bk<0&H3~#PYRp(%{{}iD>4JSU?uM~ z(~QQ9Jrl5ORUONY_l}XVNHs%Wz9Y8QospIo!W_|=zLIxdO1&Q>nN!9=2rN-f` ziiWd&`07kH+L9d%p5J{Nd+=e+PE$Vn5Qasu{>Ku8(L7I!9D20F2M%;sAKizYA3jjG zuB#8a zx=x}@vc7Z{Wl4-%9FB@lG}Q8cenK#}hh4edv&2cdX*}|?Q(j&l2P#vpG_fnRT!-#$ z#9+}#&k1ee^<-XwX$=)ZvrL*=>eK?ynu-E?{W=wKrsQ|Xj>Mai4i>fit>=A-4hmG= z4$p109HGHYU1ZVBdECOilVfL2O#Jyr_Ph9tNF<7!PgDQ;5Xd6)fuv9}iLL(8Zx0CS zM}%k)9M<^zkt7mB3p@hqlJ^Z_eHr zo82E*OWFo$WQeR(@V-AzD_W>R2eI=+{Em%!iLe2)I{ChO+>@>A|9r5(BzV?qE_Fx$ zxEhBj+%{VWt>K^3=^6q7*GrB6aW&ap5M*=2hO(ddV^CQE;$xN5=KaZ4__^aq?ge~j zSKhTFe+(*%96YPvLeRxOuJ)f-ghYw|kG>+>*ieMnYSEXw>tz+v1->DSr@*iw^g;c- z(fLurCw$e!j_7DT5AV5+d&A6|gpt)9<*0XfLaXr%80(sUdiOw2*yAE3bMi=mu4PQA+ z5%6UJgHEB_g}99)ME|(@+j>PhXrR`gn+cYWstBO94plM)EDkN8;oQ5EGZQF2Jy5vO z!*yX0I?ID5-_fg-O8_;2ZopVTKwt&9EWV9wLJz5#g$GukmAp#KK~Ci!aL6}b#&%Ag zW?}QYE>fPeRGcas30w#+&9|AY<36}TPs+B;t(EzGa1tpmK`(m?XoFFpl&ahdYM0~NQ(P3 zHavIc!1#KDI~R0`3Z?Sm5J_qAk-S~L-kE`5;7yt}NYjKUVRu)f2)d_YBXdC_d`!H6R`L1EM@V zb1#kD?ohoP(MGe}>4@Ge^X#`*LT-Jas<6QIWW7d;m6mx!_jKHsL)?f!mvtLTS3~*l z3N%UE@l+u`epSC!w^^?xS}A-lmD#yWXy3bo=^~g4wZD~EP5?PqSKc+%>^pu>83ehP z@G8R>DJ?cCX*@=@f9PC68X@^mP?0$)H9YdbuNPX-f4Go@dxX^j9+my1KI0{O>%jp) z>%Y|KL|m}Z*DX_DL$onX-(I^jgA-8+-gW7#-VR3lK#h#W@jdZJyC+nJ{KV3B?q~6H zB|2B{wjEJO&jeuIZyJkDx!>0umHRZ8z5oryQZcm$(C&(;9Mx|xAa%Zv4E*48Eo3vg_-iS-BPL-CgYzE1i!y@t@0CGm4EIl5kE}K zV|47Pw4kE5cZBND$sIgrL!;`_)N;(1zJB!1DKgQ$Lt%fAr4U$pb~FyNH#^JQ7F$qO z(4I`SOW5YFhoRwNy<^{5!Ii)>?Z z<@?%bNFoigx;x5%RCJ687giI=x>)>>t~28d@)Qz2M(5{=-WZa{i3~5#8TcaQnY^00 zvcj%00=)JXv;^()rreQE{cC*8U;8h!g=laYcLZo*SV|r$V>YUo34yD%W^6jd({Ek` z2^c(54@l|^2x59``y9Ir@2Ww4S}4XW0jpCx>7A~YV`NF+0-;zjepwb_nvbuAdY>Z34muXO)NMj93P3SAhKJoy!xDWzX~OBq z&2O`TVtZ`3j5pPiC>~sq+!grMdC=1}NimgY(DU+i?(;kliWq8r1(ifWM>l<|Dq;Nz z`+*qQ26k%aG@RU#bSSB&wVM=UhdJNWXN2;tZ+1=uq>ClDPFTGl4LTb=tVXxr=I;TA z*%2_hSao~6+ym2XTrumhdU*qcPMgK^TO3LHWQf4RIl8CG~IS2P)qq^QSOx~EDekwAy==TQjh4JMvm`ZiIT$|_&#hTWRUr;F^0Umznpl5;y+ znA^$$BgdIEaA0|}aIHEkt>wrdAFzduz)2rA)TpQh&9T;vRq#blS|#|a4V+q)Zmce=A5etVsyd#;$j!I@BPZ zAcmjIY;*K(u2vX2C zqNoinK#3&U(4UkJA3+)R;)Zp?&mvs*`5meq*TqC7J>~tGcq-z%*tF;B`fJ^Y$Z1Z- zvoe-hk4_wP6VaoWs*c`l|77+g1kUlU^4`z81MjbGHgib7e_*>!{BsGh>aNF^pyaW* znpCy7*0Jv2K0PdqQu~2sc;EOgL9GW(%}3^GvtzMFldg~IW$1I4K|*Gis^c~-fOvyv zJIcr7_rcoPOik5I9S8c9x- zx9o$rI*qU#Q}1oIDH8S4fiPtkCROO!K_Ny{X(l0SPDvStI0SHwV4QDIC>p5c*jw$fk`rB6WvlVV#l=+D zK4a!eJ#YNOmB(keWc^6 zNUiDKE6d#~Z5K`wNF+KIi=|Hx-aI^-7c94b0`%2}HH>okluRx%U+*!^F;p$P-9-k5 zjMZ!~=3(AIyr>3_ft8nfc{b!?<1(&?eKnxwupCJnO5~0DIn$L>PlaN3;9}Hnm+-m) z!09C)s32kB{584_5kyw>$_&&L5#wGBH=JsY)gR*sZ5PKj6by9F@@Ryk=0+(4zct7~*XQL~lu9_F8vD8# zIj%e_$CLDl=ef2g9G{8vH*ISRTF--YvAa+LbF;%jtZ5l1CD*K@Q>Isd5S}no{Eqj1 z>L|_XG__vecy@G>OlP_2#^+LE`%#u%8UYDRpb7D-KimznpvBjUCsux7M&31?$jdHz z$|(|Og%LPtF8~srGp^&Ggu+142c$V&|&U@PEOml}qxdz$ydjbk#Gwr6nWdmC6siEHfDi;3xMyg6B9Q&VNrd`SkbO4^!7B z61F5E7>*@sOEIl$9Bb?NL#~YDG@B6jjwK<&pj!NMjqO%y_ZZNdw0R`$ikXu1ENbLz z8_!pDXPD!aw5s__Givf>+Zx%eO6GluncH>f+z8wGD6_=awu^UE*HAr5k_xWz{K!8E zMMLBLy8d6b7o0q!RA)lxI__B`p!Uhv`Yy1Xi+=!U_Lu zO<0tcg;fJYJzNha8EHM#mRJb!9fizdnn(}gw9whvnYb!HbLI|k9nUr0f4zk8M?hnckf!hrHB9+b z6ex>6RG=>wS{)cAyQ+6fr5@idOGx4VO%1d`M1gTr}A$(L5acw_; zYw9ZDqv&nnwRl9>9x-|_SB~zgj6wPVerl(Vmf6NSedkX!Sc;||g{jk$g`R((N$VLB z_k>ap2F>iCj!aKiqrVez{|77=g^&Wnu@^z{L8ML~uq*7nHps~TOcauO3rMsq=R}1n zlBt@(w>3wSX_;m!X`+Qg+K-BGnqPn_km(k`Q5D3=1FCz0K58=l>AKpfeJrOZmM3i2 znCCjMBBMxw+cG3Y{?n?#coau;tifI4yGiM2t%2i1GYWx(RHJWnq)z;Sl9$;zWW zPqNifXcK#^C%S~?QMv!q0xiCnucXD-{SR@IxIz>2@uo-nmp6wB-w_b6a7{E7aUPo< z$BSDOAC%lQof#-p%c;CpO5Ak0>5dV(Pez+^nXS}`8#*nv@4oyX2t|^3Nl}H}p+Ju6 zz*F}xO@us?kH?Ea2AyL>qVUS>lXd;+H*Z@^7&9zYDb3C99&KtIYpP%qbeO1<0gaN~ zqJMVUhE}_Rijq>{p>yj(!IyI)J&0e0;9Oq4{Q}uFZqHs?0uO?=>=}kZ6rn{IbLvMS zUSt#@_bZg%pbktr9O=1|p>AcKlw7}| zLvGB$wvR=wY^=oN<*<|)Hlz0ff5jbH$|AxACRxrkB!qPJQlg_L@|TOQM6Rr^<1$+4 zMN~IB=~zBGdeZsFJcQTPeKC&JYWFk!#n!5M9pZX-TR4~}U{@AMXIvTohuT0wxAPoMtIWx?wCwU<%G!DGN%Hr`&nr~ana z{SKuMQgXA=7iNiPqoao$zP|AhdU(`U+(;=aN7iu92*nSaSP`5ggThlXVoB=2fWR0S zp)W4?qljy_-JoqHdCT1%y@!P;X@QLgtJF!3FHoLkGA}M<=_=^to&6|iEGf61`B13* zvdw)^bi0DzZe&?qXU7X|c^!|ulM&Ec3?YG9E<>kwgWFf;?-%%Qs7-c9JDhW%ip*B@ zOtOMF*~L33250q^REaO=E2-@1ug(RaF176}ck-AuPDWzJk1oqig-lQQlinmgUOpvs z6ep^o)sIL$h$mOqA-c9Ey)yl##V5udY%2;~JM#y2NMrnMJs6qUv>ZB>EJfO3bQNn6 zBDvA(+nI7{-+emH%dzGi`fp@qPptCa+d<<OAyoaT z=8f`>78;mw3Z#c9t~k)Ji)XkAC271M9@hR*Q4$%24KJvzsKfpth|}YD@VeWTo>M4n z&L9k9gJMv(R50p>WP?5Sf=lk9I`xf&;vtNzyfZq>K`FRI9#qLAYNh!)Etp5PqAH&1SKcU_DUaOARu0Xy1IMtgo z4RmmE_lVOhKWi2uu5iitH{9&IkZ*}>Bc=95TvLjUYJ1`x+EKJWf02Z&8p?;Kl^^5( zfaqgB!cR{zNl0|r}`UyRMmN4is z@RJ)Fd76j+abJJ`)d5Z@N2;D)`Lnce1Nq5EWiC43e-&STeD(joN4l@%)}>~w1_mGt z>E@Gd364zy-Cq!$36tBD9`HELbseECN-o%Xa9C^1)JyVs&Map>`E}p|Jz%O%X1orP zLRuNCzq$2t;TgcGSx=8%#lj=}UNc{50@5x*jRyZC_oSJ!{l>IO?pPkEhZQ`p9lT z_c1j4Sx)C~qSikKr0JA<%v=GDYx*Ea0bLADd$SX?p!rN!tk@2P3T&5#RUWhfMOmSU zk2A+sCqA~~s%TIHzPpdeTNnJq*e^5uwj4hrVO_xM217VlauF*eggl$3xhpW7?Fh?9 zXU>Ue&oTk4p@BO{2kTzp^{=nfF8!aCyqrT)fL%+_dpGw)z?C+kcuuK|sE15L?My6k zxW5nvwZu`-wM3*dBYjLkugs3#fbPDgfD$Wp6%9PdB=wcosQ6k!f7B78s`Q=AcjvqZ z!HO#4b5d#x1)ZiYU?YVXt>Uiqa)N7=pp~|fTP&SkgARLn%raI#2aLh)SYW}O%=8K! z8yTw_|CwK3)!*3v3RE%bo{Yv?Z4#j+YCnRnA|65V%q+W(Be1754OUWcz*=m`nNhU% zOF9*UEFPa0O%c(ck8Y?D4DhQwb z%$xYj_-7V}L70+8u&v}2Hy)3;u4Fa8&oVVmMhma1C2vAFRD>OYheC{2^4gO;PFffLDA*wDk3-#fmbbf4Ce0&#Ob)cAF>7GSI$nl`1$%0x&Kfgvm8%PcY1<)gXaFjM99ztEWUta38 z1UIhLKB7*?DBk5KM)~*WeOF=Z>Y^tNddkcq=fEKwbL7G1?&PC= zJx4~64-3-H8qr#F!5SXycqcKM!){8VL45~z``?IiVrY>~M->f>>ixe6XH<%}gfnd2 z_G}d5{89nx-X@{15RbUfGipH&rHIoB^%36p7zRPh7eX6TIcoi{nW96EQ2jW${;ocW zP{4U8k=eF0F!&TC$rjvY-}FKDmX`_%kb5_XuAT*0piZUZz7v$gf=tNETC=rA}^T|Pf{n)t!Y4Sb!TO& z*HV3a05h-?86fZ~+8|#?T~=qo`(A;9Z&PUSH5u+W!n0z9$Ajx}~(pc;3PF=w&u@MmWim!Qc_d>Qmj)q@H^cZ%tk}aou%`LC z(yDrl(u6GH)<6hBlolL%8kCXN1ulIai0onZ<8y@nQ#6~6B5JJpX5 zCaP&45I12D;S{k2kxW07PNyH!@sCQ&7T!kT?c0}XB)5zf)QW#lS!fEllTzh6Mssfc zCa041{tk@fBHkVOJ^KD>14~|mW$tdN7)&1Mm(HQ1nC?zAMTO-xYg~X@tu(PDzVZgR z+Swvjhy^J_(NPF!X2A_+8@SfkdpTuLDVTzq)d${70PD(VSqCG&Z-{I%Cw~W5Fu+P8 zzCLIyNBno;&WB=^1KW^fm$#bf|C($^Bmt(VU-CG2;TKxmK%w2o!a-OWh$5u|Z%57w zwRt-&!^ft}F}nrxNc_sB>H&{shQOqML9-Zq8)`;O`f`)&79p)HfUskHdO}Ao9e}>G zU`$EZGZ$7%Tn89G+jDc2nV@uD|NYK>nC&@TYjg)L91(KOm5Jc3F2A&R2|FM0#QC>b ziM;M`4z6Z6G_INqJbA`=Ssf&)Q7&UJjo6FgZMKO+7sze>fM^e1{2}yN-yC`dLZ5`- za;Pq>L=A7&fcR98lEj2VC-22hNr02?<#QZtw>WLScCT3rdx^sK06VTbBf6NTgZ4d= zIq8D@c0R*4V+ReBIbs!tE{s3KE@=zaSg@`17+4k=6l7O`q>(3(EA$ypM~sMP@vS-=K6Nb#qxqF9?~R-^@}~uqu3ju95t}(i8TbU^OD6~O z27=8QTR!UjEOkw<ZiZJPq0D`w zZ@!k9IlOdxvipB4?wLf$QiMx!_kY>hkytP=6JFPS?>eSGNQXZ@_u8&s1`PQ%7CnfV zcacB;lSUk`1I&VT+#}jQS(NW!-O+A8>GqGT?(d^fyd3Uese2#WpXA2-$kDc2dgaf% z{ySzrA^9gQbQkhM4~I|vdDnQjgEpO;!9Sfhf3m}955e%9J=^Yi1dSHgnzty0gd_i$`zaZ-{&+i0+QUHU_&r_dC8&vvgQ2e>hrc=K9nrm zU=IJijh#7LJU?37-;-tVUn|VMKcfgEB8Er2Wf8&!Qul^bMi-1Ta{vfrv(Dc<1FH`r zD)WUebrFn;p)oAg&6j7*UtV@FP_hiY~&>~=X&7RpPV28m&W1ALVOhwIxhkjLpa zL|@c$o}0l7d%|wadzsy^ayU1ahX%23^z{o0x95%90#K-8d9LxJ?4>&qtIH$*z zg+O=@Ky1dEVD0*)XCze;r(WK{Z>&0v6@Bt$Fo(d7E$p1R*Rf{{VVXT&OT;C$sH1gT$P@%#LvH@&<`{M4Y@ zqoVDSFbx(BU8yQOg0GrD&N~Injsgg8`Z{dil*M~{!)Y;<=rRMxIKz_CJ#whY5At`~ zik-&JQ;)Sc{<3;mFvyc89cdDd+o8_ZG!w^tzEC2k7;&PpT!skFgt$3@vUBpYh#--; z5=smk*X{cWme=^JjCg24jf ze^lf?@R&$=BkI!vN`EvY;u<;jlk!o3z9w&k87avNOM)>+9+1rjOIL_=%g&zKzft)) z}^R*Ua3J%#)h#NJA>p|v{ilnt#AHcbH(n#E_KOduk z#6t_}e|;mj{@Vc{Q7t&Q$Z6IR>H=qHzx{1nS^e)M^df9X%>A|t=*)bp)?odo;&k#U z`8=emr(BiESm)ns9kl;$ZbVq_))AB$1Y*gbHVRuA z8^LN?1$fa|`g4#wZ)fYU9BbdnlSNlf9>d-}H)=s<(JowvkKsb5)lpE=&zLE2k#>AR5)oCYr4{MI8P-6;|bHUDG263$@( zw9q`n3ZM(JgRs{{9aq3hJl1x$Bl-DDFl)$P8;>d;Jq2d+NNM1siwjNhmPp+LW_>UCAhcHtx04d%xJERG=FHvUdt1&1r z3;4HFidF&R!bS49?iNilrSk#FLee0J+6zNju)=cE#{s67ui8LtKvejCqzzQs;Xge9 zw#|ScLxr@O-!Bo6;-l|50|(TyAMRls@QP%-?8x8b^SX}x2FrV4WReae|RiUWHi=`WHWa$_vgb!56%Hv+2dgXk44yn-O zlkd4u;jj!AZMGZiXL*gAk3#TMYu=Tn=3VQ;`I>2V>?eEg@0c*A4XGo%tpP(I3&g&t z1qFGayep|M!b9#zR^Y4UKvDz4&{22jd(^hm{v*Uhq z#vh4JF!_D;B_rYv4h|Ayk6iCcoH8)}Wtp*1z~Leg3fTZ>#5=8uu5#IkdW8D8FA^&p{Qn3r{*lR+0 zbe?VhM*w_~s}<>d0MP;B=agKuhm18j<(V_3-Bv|~SL1)#0c@|5$SekdRG=}DC2)>s zUmMlV|6}_OR*HcP+7W8-Jx~Ohf*mIllmj7oSBC0#1v=9{{W`IgTolJ4c{tc!EOyoA z=~sOB-^{F-`mtkE|I2kOj5{Wj7Q)B^sEPC=a1u;+ZlUKltlt-uEpstkan1x&Y{wZh zu*5P3Dv-m8x4hh2>H_FXqHsvo>q~C?t%kkQQRkZwKOBTx%8RR?_kmp%3poQqX+Iy{ z4~;pd!Jx<7x}5#8GpVlty6f#jDV~fFPL)Hu_qm8=ckP>Dw{wCcMI*~Vh zcllUfnj8%UCtk_Zp5yWRwBIWcc3vN8B~l_~lMb`1rhscfR>9uxW~?A-c8(Ves?crm ze#KL;m-aUBl$%4z*hFqOxI_t*W^_ivs2Qo%e&sV!SZpK{w~mEkOFp8{sC~vL6J1Dk z=*(ICJDpq;IXUA_<8g=%W_<>raGSTbIMM&)2pC7g`3GlIbzX>cv}7bL(=BfjQqR=lGZt^U5N|9mf^A}VLTLFp4-c%%HP7R;GZka(ZTVUz)--3*D~0*R=XCvxw{Nsk`4l$<@XYL~96VO>nbb$a zN6gyEhpQ+P<>a2+d&#}XxG1#<;1mNApg$E93~p($nFgVH*Y{6TV^C_>X6qK+#eNW;Tt`t>&)*^YYrz z=*9!yZb(bxySCJOAgC(=lc2gmNqn>Fm#63Zpa)mbMZ?*=Zf)uYzp?I2ucMdmcSReI zgELZp<~H9tCSW5|uTl*j%O#kO32dzU2@$g<+YjGQ;~ZV(iyu&a{(=vdi9t)xAm$~^+A;XI4;C}RdyhH&EA%pK}!-#D`XL$8+^M{W)(oQt@ z!f|Zwx{Os?B+G2>aEDL2iUMeJEJtz|Ase=^+CnynGSy|Q-DjiBFfd$g+&7rkIGGpy=iTB~EPC5anmmdy1jpy*flzR35@gk@SoJCtj}^9u8Y6?JzedtzE?>8|2* z^bhv&repyl_>}S>K0a`^5Fvdfy!jDyl77eiY5pl+j6>FJ_Q_1QN)>&5GZ zEk-)1tO?Zl(#tcv!VT;AGq1Uu>Q8J9x2@UAGP4`8W z1f+e_k&vi2?Ya2D_Al;Kj$>r^W=ZX!`H*p0%)w-Xq>8=r#N*5L#a1Tx#qnXSRh%D> zv*9Z#!IDS5pMc1xeL;jv3_Vv-Id{GSC!)+qdfy*W0@b)2Yu?HCfNiwc9QxuoYF@|_ z*>r}&&Sk}~pr1Im6h7i$3&){7^4cxUjE(fg;1n5n`~e&5AD^V+O+)S{l)jK z!0R2gjHqQOq|@y_PTF|bGWaHo&7|13wOXd4sd^d=%ID;}lQlv*%T!rhA2#STJb6+> zwNbGX!;GFLuEcK!VQzi1#z$H*!WUI;tPw7DFSft=%c}P!YGscL!RgJ!1o-wD8=tvf zt#Dh&Fuib`{t%}?Eoy0vE=qWMR6S>VR)Uxee!m(>D#|;+e zWNk~^lTu85Pwgj7-#Pc5m*$FVd<2E>LyHqO+^j;QD7-4&w$S4h$Eg{vRmOkn91)!B zY2Gb9qb)IWJFxx+AFvY6_TG8kOY-iD^v23G`hi5vb=of>Bw>KD+!p&3V0UYi=Gj9! z#&JXAsZ-}zHOaR>gRD?LJ2K(!o7zpN@$)+~+}~5XSUdWex`yiJi4#VC8|U7={Hx~_ z&=oTx3`5m+Yg;dw*fl<_spP%3&Bk{${7#TWS{P*<>AXSq{Yc&n&u7mUecd?T@ek2# z`*s(yFNR|~hi+Nz7&>(L-3~JGw#_4DBe)%XjK(i)8o|EN0Ee$(unGq}Q7~d)P)rR0b9FnQ<0Jtls>*_k2HVpyIWGay`?%3vC)pS7|Vj+k#|m8QTS< zSSmxF)UfA}f6}h4`q&Gk4s(X_D@!j7tI1=|`Lj7z6!o-z~y~w0L zJ^Ss1W@_zeIOFbZ*=|GjVFAX znioBu9J|kE>kyuku4X z(@OxVJ*@4zGrLhrg0JL=v*ocT-wekN!d|xqC&p0)&-x{|hFg~=Suf{u=W-vL)W^-% zpBY{MKiGTAsH(QGe^gFYu-9B`&hX(OK&zTD34M#cTYp1 zH+gA6a3E{y2(`z-OnGSpLq+wO0CdkJCe$PB-euhk(A_izZ_y|6!Vl@whXgzV#EaS( zgFjOy3Q!&x(ac`J6~E+EEuB2$CB6xcq`2TkeE7 zX_Cfm*$iws-rgzWn&54ftioBhnk9Aq9=)3FlR3`0qy3#=%^tTiAs@XqyZL;Ci(0DYpOQI zXzidzH}wEwiqDq))7@XaR4&6w$fdrfaa4tDr0;QSm0QvGU&$nF>bvNgwDL5|&VL^k zs)V>F?3H9*cYUYg6Y*-*=If^ZgE#57Jp{s{8f2|q)$wn1Qc5d{6Fv;O$PDAH27#1T zXhb#FVFvc8g3cz)g$AxX69j2M6Z}wxnDrGs&(`dm*zKH4@@g6e7^RDeR$9r<6w@|+ zZ4-L~<_J$WCNE|R@~-_&19Ec4DBDwPW=6PIcFOa(UB!kZJZ?I(f>Yk%+Rk8=f*l~{ zFTFg((}jKam8#sF38ZW@d^m>hK8B6Sje)Z_tH}d~E%*bFjqTu*u4B|&@Z+G{bqH=3 z>jsCzgYB@MB+a9l@Ww?aN4e-byHqfO*8X z^NNqyciaN%rqAkzaWQkey+g=^rY?oU zO+fjoC*Cj`dBueq_yt`zS!<^&sfkUI?%TErSd z2*?u$Un&lHZ8+bvv$eA^uo&3Qs?n#P2Zhwiraf*-!y-!#nyIxWkM1MSp3R8%GPn}DXnq$j>Tgo`Uf2-yxpr;! zO&fR%oi$}~C%Lz6n04(4=by?RyvgLY(n(!Xk6GeYIi$849rmO^ZBN#!ZU>8mRqwqC zarE>cLdR1nac$Y4Pca42s`(X3U)1J*9*?UuIF0URi58bdqe5Ykt{G$IRN_OGi93%? z64KGL`$oY0I(@UN+1rn$V3=6UDZTQdSj=2EB-QT7Qkn;x)6h!x8QHy26G0}1sCkG- zC|?r22y}a0pmnG-4K?Qh&VKcf6NiFlyUA7h@uzqn&2Fj_%cERN)~YlXar?hwGXt$u z#SFK_x}V=3oEgs2bGjAosd1O_wnQ_!0hC(1Z*2UX4MwD1sOEWO^qS&k@RAZ=-IFnF zWEJh(S}IEt#)$c=X?&=7O^agNZ8)=YO=j~cgQr=OfZYID)l<#1bysXcd8vbUIj7WT zAe`Z@Q7b~wY8V>jmiL&ebT;p6? z9Rv4Y>-!kob=$$6Hyj@MISADF$zh;>MbfuR8}4YGJ6z$LEt?j?YcZAjO?K=%hjdWE zV1HVoh^9cA@TiP7-FH%ii+K&SdU>CNnbqjuPkUqMu}Y0yoyN>0jBb^}Kt>!by;b?- z4jP!UWIoD(%0}Xi^Z1cvJB^zJh8Fr3v}oWlH^OtN-x;U^2eU7Iev_F3C9bfCOtF9$ ztdPp|Q(a9;TzAGH?X@pD^a?vgUKgZ2&GQVLc@EJ6t2_eSrE&Z+2vrO~uUwoPHLOQ5 z>{(J>u=!;_(Tha7il8kj_Pcj6AZ(I=CTd*%dz_AAg1aUgTf{skT`#|GUjT7qC&d!? zAx=9}#KBx$RO|9bem^@(^olQMUE>nm!Fsu0dwJ(lH*_P?KxeP4t8)6@Sf~IpJl@E; z6}z*|y?TTt9sqU6H_9e8SEM`;Mya)$d**wtXNki*J7+z})U*|K6Hx>L1RFo0((`e0 zxUK>OYSfKx(Y9UvZPu|GTGYaxQ?ELUduvB__r9`wWzeQPDuW~@{EB9QUmOOM-cMvm z;Ui@9H^t?{fb11HkiPi#;g4QLZ13s^74wte*!yzmpK9nAu8NLIuqau()=vCLb z6eh`;!{UxZ_UPM*F@2GwRx+o z8DSYekOt}1lo+o0eg4J6B-L=W0`6^8&Mb=LD)=Y0s=a9?(39&@Fi?O(F%OYT`|^n0 zBu7gVxQ_Vppbbi6NDozuZH7vvy)bEB`LCZ=!CHB?URyyx{nHK`-%vx~s zAB$~!+@f#4os)R7sn$SsJ{$+>KjAzVnqqi*z+zUTe{fustWJMb>*JYsxYJEO4;&NX z?D)7c$3RJ`l&IJ^JwBb~{c1GnRMe`A{(=HsiOS?r#nI8v?NyW?cX@t);Kaip{(0|f ze~^uAnx08o?W+awFM6-vblI3HIW!r3vwT#&*BfQ4?%^?;&`I!UEzzog7e`W=Kkb6F zJl3QZ@}Fz3$BkLgzz2P+Yn57wlBCw{x?A%GltLpA1FZ{ST;=z-=w13SE7o9_O=JV= zq9T=ca~v8-JF$OmE7Ob21^(1zH?xS17OzeTjp(COU5k6FdXX8& zz>6-H6uaS(`DS4Z8rBvI1kx!B?uN6k)IUI8PAG9ra7{9rH_cR%<0^QV(rO zQ^ghtvhkdZI9$%=|EwES%}S39-6q~XUh$}vPOSy&wr0c7Tgca|Cx4j#@wBw(?&NWW zqKD>FEX}lvQ=(ugerGs$u^x>3guef1t})CIJ5qTl8U>uNrL%ydW0 zi@E{98Uc0m_>n!n;|la5)6dbZCG^oGi5|bpZvNN00%E;pW&vIWz^iT@r!NaR24;^K zJX&DHiu%K&P*LtcWw z)H+KILKR6?4;~t^QXZC2`l+r5UM43kzOgFdZ{0ZfL$=9SVR@;-RhCO2FsAe5)eun>}4r=-QuB$A)m;39T zmP2gLT6XO-izSAQlZT|WW!IbX*zcV=dUy~$e+nSrqU>#);vZ@55eMYu?PbOLD%MBF z?g#4?wU5Or0Z1_L6B>xz{BUJffFS;Dos*|w2a9(Z9|`N+Cp(gyH>c@spXQpdiJ^g7 z(l0+&FM4nU3luR&IApC(y=<4$S*mFqj{YR!Fp&Jo${oNRj@yQeZN#AuLW~r8?hz#c zKU|ZPjPx25G6H#NYen{oD#4$5FZg&Y3(dRJVyoj5<}`-t$-OY0SyJ$XtxNPHnBA7gRmXR5yzm zmYJe=mY`J_SDcYEZVl>~yYFQS(jtDA`L+%y+4AG1>l;7H8O;iU({`~SkMMsWS1KCY z7pt{*bzLgRe)Q-0_g|cVACQuk=78iyMJaFP(dU1>umd7&Y@Gc&O8)toDx#!P_jtC^ zfDG!m3z$g~${%K~GDk%Fl40~IHT&{NqI*p~Yr-?F(@!Ri9h6>HH;PeJ<3)+gWf{cc znl85)6{BH2Rl(!k>|{}H1mX5y3vcP*{pc#RyxL-*0@a!VSEklKRgQrX-e^LwuuQ?Q zOn#op;yEu2Xjms#?sSjrdYF1N+00T9Z1`G@jDkp+>w@R(_%r;+gpv6Scfg|&bBqk? z!T00ee~y>ZjC_W28(CPrp$DdjiGHD+$|aRYUG+l%`-&)MWpD4`)zwlGv_OO}0bzVP zW2KwD^84_KHBig$gpx$KdsT+4PMOW@7>N(VdV3jhPEgw%Rl0iL7e-K4{0XVwoUa}( zR#||=s9qrrwp1VI*N<&`fOXK(shsd7ZhAw-$o5Hfe{8Nty`E`;$y#X#chV|mJ=UkV z>Ee&P%HwGktg&fnPHj)DPu%hEr#iun;jb&n8olU!$a@C>;?28XB{69fnD6=eNsu;z z;V0-awR{k4{c|f_UL|cnB-{juWcFIC(XT9k-og=(!Qkp5uk!VL|Eb)s_G!cpV z)uL=+DRvmqS12B3oK7fq0B6Q`atpMOP9xf&!Q$I%-7Uep8$nCiW1zZNP%C!0gSuIy zkT`{%JkjFRL@0(8s1#Mk*P*hw;#qivkp@43;Br0VFD4#06W04G$UQu6Fmx90Ev2^F z*C{ops$D3Uvns49FF1&ezq7^gB+&bK~A?x6_~-W%noUuF(h+yb-gUeg3D!I>_8)%*tzt1^1U z*$uzA^iC&p|06s9>V2rxeZIH*-KKH|X_{4wmZNliCF31+JcaM*`S@=7@V0tCuUAU6 z)t5d|wFlqT#U>;V0#*At9(5-sY-o#3Wpgo%ON071&lOjufQ+nWG;M}C-%H}XB8yVZ zmd5PO#!^2)gwQGA0^gH)`6_6xj^mix#|NAdcUpiD(o|^7oGJI^fKQn4`A0~XS;9j! zg`uZX6dz{{WO`Xh`ML``t==!h@)x~YqVvL%CZ9PJ27S${gvfQF zdz=rECk2(aZhYB`W8DF0?{Unr?c2=Q9Na=?gfC$=3pr_Psc?746$GbVMFEZOrqRLs zIh>Oa_xd;5lXTb4oN;(LhAH+DHGwdc5^?K<2XC6m=UUxEa9sBNLPb!r6(lDo4u}3? zo;q6l6dhBU_=g;Gha~o=9am~6?a1Drt}aBq%MUpgM(U~6Y!`69hHhSa)k83_x$D7w zCDonmtf;V_r`J0u1DQ7QYM`w|vEt%1e{!MMDMXpm`}CN$)loX?RDu32T!0rwKHL_} zDQzQ#Q=!#FGO-pMPfYGy{M~J|Ew{r?8)xBh>EWFzEY{?rMM^ap4D06%wnxA`ugI0I5@^w`9n39co8u(*@m1ab;JDZte0c+}difM+G1auK2eA#=3j^c- zd5f?JcW})eQZ?2Nwoa`tsh}FL8~{jxCbF?1b|&q421uxp<2UB6-hKRNK2u_5Wl1a3 z*fxl&XNEUeBAWVEUsiuXZ7sexazNpRhk|rzyqHD`BTP!z9|{fGfL}!hHVj32!6UkEj!rC z;gw<8z}c2|fPYFrr;dTN;%QV?6#|p|##*$sPGO@=-(_g6_K%iC zfsHRD`dO5H0CU6GM*`=WBqU_)CSQLHEHtB6pAVD^8_}dPyO@<)%DF#4{`vUeQhbT@ zH9Xa+Zn1eW?^?5^zyl;In2G=o;6x-(AomfqeLL5>>Sg|y^}44LjPz)K|B*ur9@9mHNx(i3 zdV#Sm1J>YdT?Cdo!CtYQ5El@~R`u!A%AH@!14~o@j}^a#@Y_5#=u%%hR_@Uo}h{HBIVP z6dZgYbb(SG>}zG2M~9Kuy=I2>JOzPKI+q1}_%uG*AP8Ys6dE;t=dO7GR;e3&B%6;Q zTNH1#^rIr;WY@HvB`(h<)%;ap7bxm&YW?BNvS0Ps1q{>kj{G0gZ~43MyCDes&9#jw za6?^xbaC%>48&=fR`W{3D1g%4=IJ$I5Y!hB6&s?eg{n)1_hmoS1!0hC)F}e$+q7SS zRZz?x=$5e?pY8&NVLpeZHv}wEPf#`Br;`prrPvL|`kDaybgA}=QRhCkMe|P5_EM7V z4Sq0-XeY1j412E4;p_kn&*vcv$zP9-1dNgpJUan*njB%?kkoh+WKw!2f;nLcK&Z~b*!oN% zMMA3ad-m(Fju>;G#;*uXEK9K%fpp|hYsv%SJ!<z7-B#Z+_&varI*Ewt&*Y8?GO>mzx zP&1FUxM~e*Jw5IJ+Mn|j8tdg9xl_Ki>UF1qe;S;u`|3^_m%o4{A}a!xNV!I55b3-K zOnZDl^nUY6=?U#a55564vD(1Q;!kpbgA?zmJOw&`?tQ@>{#ZTJ77QWZAv%U9fWx%W zAZnC>V2iF3QY~|aQXtbF^5{%;TNkkh!V%;3Lfk)RYXmdB7qdb}(*`8g&@_G_YaIru zFQI_O+g;+Kxg{7x>iv+q0ZOW9yptkE%EsgS8KL4Hv`(yb_G+QXGnaBC6cz?4Cm>2A z!T(ay4gNXOM=xJg0L_jjK0IfuPZ~edHS|E$dhl>(wXefOOfG;1CTa$1>{2j#PQ4r5 ze-Efeg@E|<`FQ4c5fCl&MUcZVw`-cx2!e3;*l#Yv-n{@rjUa z%>HT&UkOz(t(YU4vMT%>iamkG1o_P`ODn(|-dcU&{!(HAxC8+AUfeiU(s&xFv+?ka0Or#N@qa9odjy`p=TiZ=euYs#=Mt? z_kJ5V!i+kLsQ!_FESMr{PmD62Az2Jy<2{hBrqoXs6`YT#uZh!jwRq?QbWJ_aAlc!& z8PpO*RtL{(z;6wO@V1a+ZZOSavJ}4mj8=B<*XDWI3eMeo^(=L_M}Y*`CM&^|GRdvf ztw;)nbj20R5;Do|V~$cZq`ER6-udib^)=l+HStr1!{&^wFJGvzm@O8XvQ#=t6IM5A zXC^ZZT)|0|1ey8`wscldf_c{MqV(F3HrNy}_hoi+@mJX?_6;E4x1Hb>%B?xbuzLp^ z8S`l_e7GH`(EG1eUPf!b#h!0Gz@xYXprq_f?$;T3e4vrNNN8J7M1?M{Gr9TV1UAY^&Ku=$FjGu=IiJuj0asRsx*- z7_15LcsSy9=GvdozbJxkf>Ll3W%L0Ez`7wz`ewurP+d4ru?{bN5C2Mz&xLlx=@$M# zg{>YxINiqU{G9g)UT%Xe*S@Ct)P(Qz^K*f)WRnC;V)1Qlf5=ZGtpeW;B)MkT&)?*& zX|ybIGlGeSjrZ{+a9KBQ2IYi4fuz_z71A%rudSaRg162OaRWpJuKG#5R2s574KRol z%EiPLnbpw;@3M#8ZuBmB&ff{1Ut)@|9qMDHJiyVMKB{L5JeTIOK5XpbQakW4yn-6J z>2)qrz!S-i6g}{I)JkDS-5KIqyVXJ^h%bZ)|3PU(l8HeG;)FaXV~Zo(RurLHn`uwG zT|Jn19^(=iVcZzus#=S@O&_>mh8>I|yYCrvdA)<^Hiv;#{e}tc-IBX8@6|za1bY=# zZ#Y=5s=GusWPi#qCa;Y7;AvaW2qh3}kaC7^*Eft z6WhLnFW7@+ISyJ&J#@T80LoP@ z1?J?ZWBxl%gR1JI-%jNaL$de8HuvY=H(1k=hl*3WTePf+f(BgIhWM$Jwz)ghR5laKUeGLG(LCRaLC1gXM>#|eA_>}rR;k^3XbON(Tl*}J%9ug z8j(CDAzP(8KnQsh4+D8wuJG3ijOP%@qjbmeBm&##q9r1kBTv2v7LEEKIdq8yV#&)T z#9)+2^82y_&*g<2fl5YhJb0tY4}hh;u3cDX_yNnl=)>UWWvt2biTxoQo> z+Nx-K|dq3z2M}z_TnZjFGP73Bu^X~U4xyM+oGJIzOw;l z8NVYnzoZfggjEI7sjv#>x~sS%J;&gmj~a;TO_u`TY%E?*8@=g48($jY_zoSVzr`fG zrb~29>S&%btv=+jf&2!RvIn>*P_i}Palf_MCEe_rIa0pJJjs<1yw|Z${M2u$B3C6q z;vNY?{S9N9%c@GM2Tlx>o_V7*z_YoCeVrv01Ge8yML#h}>;+R1Xle(J@sa$YH-EYj zW14Xj;oY+^A+U4t}4j!B$Dr7pkmwfjb5i5$pGne z6yrwuEd(;;+(~R9>AWDJGpn1D`)W1KcD&~)K{>FvH)ti(Ruf_3QJYg) z&c_JJKqec30PPWA|2KU@Qm!X=veSzt{ggcDg~@#-!59Wa^$1TBTqaPBVzvhq3YM}T z%aE3d5>l^Y-^Ol{8U~qgJfOFD@NuqRHFc`s29Ss59KJugbkg!l`wW#gybpiKzjoTvdE(BgclR~S>ccv$(Y83ViXauFM?cBYC z@e`9;-boIxCM+BQV{`Zb`F3}ja%IQ!;ExjC`*2-mm#)DycZ$;&)TSk%aG^A9t%sAg z^ByeX2O9+7p(WVvdN4)vTI1VBxT2A*5Eup4Kd-x!A-NK#*|M!+6l&5eZaAxNF==Lj z`wT`RX3K*XJ4-?Mlg|Cnzms&N57vS0@I+6k;dCh<@g87+i)sY+EHex@*O*~CQ$8V@ znVT;=tsZFr#Z`4vS=#Ss2Ecy>=Min%T5D)n%s(rBvTp=ltm-WoPL_TWk$a3Pioxg6 zF-Mr{2A^$C*VBZxG@G?uWK%$oNr)+OwWA%Gyy)dyS0?ba6d6A4Y&4;K26{4jQ? z&PkeSq!;(;#UCmF3KLKo7Dtkb2B1<1MO&2g)`m>n7fHxFW`}c*>`@P`i)BM;CgVUJ zzZ&NdfplN|r&gCfoqq~e&5`SQaT5LK{XhOj9OmgkVR#`!uBy+zmjH7X7X`8lki4G- zcArH_FnTrB(nfanpfj+`bK!DR5^5w$G(6^_9CeOhE)?$%0Su|598PQ+s5q(CVip9f z{3K?-s!ZUmr)3a^KYyE5EVrkU#knp17&LKxm3*8fxF2a!cpmdXG9zIi;EfHCPDoLI4pbk~OAYzGepZpE4JUI2~v1Xu) zUBj_89Ad#a&%3fWpLW}H=Lqx_7W+24DrgE!Qe#}dkcFS6FY~mhG#xpb7mJzs<530r z7ZS4ter3HSu*kCNs8vcV93uG!qlhK|CLR*-pXUvJo&!h2A*@gGp3YG1m5ZuYHvGt4 zv)k{OV-SWBPHw8#kr;bTi9X%hFdJ0R-0v(efxaPHZ)hipLFtjko&5elKR_Rhx1`_7 zQ$C?&iij|{3mAXTul1~IN3_iu?Jf7$aQ@k^Y zltWXe#V0DUFE-Yo1!@_ZCq8CaYPLwCDrvIA)^|a|XtYnezS;xB=TN9i6iWm%K5L=G zy)sn6ca&o*4SPi8vk5a@k4I-Cy?{8quRM9onm{(oc;1tcjH2Q4|2{B`s?NN>nD0B1 z$fL-~EhH}wgoz3u{b7;P*JrxeBY4p9=Jt*9Ahwg@2irNwig$kCoKdKl{&g0vq6Y3a z^40jP5j@fJFMaU7KA2VH?>2tC-}-fMip+eovJU&4U5tJVY{LY8PhBVreabR7KaX57 zWutNqPn0=NHw9w(8^yyjo6YPj62>Zl-ER?eqXJFa@}&{mRbxBjH0ruu>53i&YkW6$ zh7c;6E*o?RZqp4ZdV?eD>s;iM|8W8uiH52R3z`qD!}Kz43KoLaF&nUyLxZW9l7yf< z)JEs-^r^=;j2G&*);eL1$hfPZHyq%}Nuww!%ii zXkO|hh$GZ2gniwb_Br&oB6%x(%$KnM0)F_7LU@t5MD5XSE?q!6ocShkZZq_%VnW9y zD#)Y&T544HJChC;@leqw(o_oPRFagM*Rck~WC07sUmV9rDGaU*g}?64rc_4h2O0am z%-~h!4*4->cLKyIx9hBNC^IamZ=u#7ynjw23q$f9U_2;(6v?=!2fEmAJbWH>1GB*g z_jwS}@hxJxM5kc>_CM8Np3XbnUtNL(%o4mEKHqszb%z-fOWYfb9f)%`$WQ~A1V4XJH zjpaHTfe?FH9NeiOMVpY9 z4Mo(>L}hX5A_07=V*q0?G8>Z|si*Zs*)=-!yL4~)9D)fbpI45W+5N~VMt~5_A{77$ zc$%%TQw*ii+_I@tSKEEmNC?Rd4>44WBwsP^v7X~^G%qUKS)D?3={MWIeZe5lsA%X? zLrb;r_S^54`%Bt$SmfoDIY#c0+`N*YnO=$kLtuz{;V5^ZqF~Yt2-tLs68!vWuj6pX zPS&_l!HB#$4P_~B{Lr%5EZjOJ(>jZ=ymp$77Pw&``8r)k%;&9P46K{jlb0J7)g5%VGzdZ4?FjOKHgbv#2z)` z2o(QL7ab@cdiZx_JiEG$QdXFJJjmC{VYH#@d9%O`#ww%Ye1Mdgo{B0)p{k{0jaGZ} zL_u)gl+7oxS^{Un%PyGpUQ_fT2f7fp*ErPxo~M&8p~C0n>Gk0B&GPRgwu{b`Y6$GJ1C3`vYl1D6R4g)U@rI$1cn8qB+doRf~B)*Bgak zB=c74M;R$UniHLcr#dtt6|^W8Sa)`7$y_{+SGKw6p0qKT23^f+tE#G@t>k(ZJhQY! zTtfZOb6uob2E?b8cg?;J1LNVxKFm@DvL>1{u6ijvR^!+ZH**?7{ONBYz&wh}3p{?+ zBcGT6jTSXV>*;Fb!rNuVfF`Yl?t`Qq2=xHoiE?3Xk+t-@?mv6ooZD^R`;Pph7Y@vT z$ngRkPF}MCd9f{d+4IXHNrV6uC+A-Zu%ihvC>z3GD}ppV%m7#0-}%XbkZ~J4b{e;7Gh1bq$^F#UtI$O+%-O}0fhfCoM z61&U2JdHR)9`HTcYc3$w-y8*Cjs!d@fYJ1TqC^6AWkIm#p0*tMZoc_74m_n7` z3%f1}7MAQrcqIwH4f~^0*)CAua-ekOwi#n3Rplb&OlKYo0hn`0`*rZ{>^rxUFIyGc zQ7xDdp>+uIU$rz-8cYVT#RebTA8aCQ`d~^mybZZUj5h8F;R+h+X@j%*eU20p{tF)l zbXi{-F|2?cX&>A9wik;a*|tqp_X%q%9gf1LzajJfO3swXK8mo6Uj-+=z?C{bM_qJh z?gMKUjuc%3065r#Cz zgfsh}34Q|%a_bBESWrtd@0iCOv99KCe7k+FHw9OCqK^lg1UXU{!l0Unkh#1Ok8BcfzVS`^xuyYR$qKx2xTmHUVe}B=u?zqZR? z7fj%_NC2u_>K-=P|NL0k5DVm|5|#sQ@89eA^Xoop0E#3wYG(fKT>k#T!2o>s!)E*4 z!9Ume*J>t8gJ;nGTJEL){8(IyKX$Z8PU2sS`1_jwzR7(Ua7CVa*dmzHvW6yjmjwJW zYA+D5F$`Q;*G$14eH3%!cTF$zk_9Yf8O-gS-#UmuG6lZ0wFhkg7qtaRZNAE@om$0! z4o(Dh4??gCK<}R+C+FMoNcQoX)AqSf`$NRjW7LWk7l1;MS#CX5GD zYq~*LqS>VTz7B}B=#2-w(}!{;>_6+)rwA>VLY0CEjq7r{DLCdg9*hB`-U<(a+Whgm zmtO@ZDhYoP5Uc_s?LH^~^7rq?It+W$$QaCFcp1EM`GK%(2hbM&#shN$900vAh13iH zhRHYC0*q85Fo=I_wI@7BxC|!P%L7zHBe#FGu=dfT4=a~<7UJ}V{zca$N`kdkhz64J zEG?!y-B^e*`o@r3p|U2#$^-SD<#ykIK=EHp*CX}gLttP^0v~_;8Q=kFhRoa(JdRen zfsa#jT5yjYj|SGPU_Tk`1AI7&AiMBpAd2hZ&m4{*8z;)-4p}%_XtZdt8*l!3?uHC-@ z7P{PPX8SsTxNVpk-G|NG(E!8M?R#w-Aa`2nn}98N7rIv#y?(@cqn*K@IqMg!rb zX`}mK;_^Yk&QB`t)e_$}dL1g16PRSIJPJ)ZYkXpOD9AzIipRUxYV~5NhWkk+qX7>n zIQW5@rh;wHKHZO$4}`E9eydmpPXoWYww>(Pk6Ks%oF%H@^tnf*N{I;&2HW_Dq8FLh zUjhV!$^Mfp83pp|eDNJG2AS#h`)RL^eTQnzMJ$f_IgEj{ub7vAb5;bfqy3HpA1A$mBQ48NTDMC4&k%SG_Rt0^X{{^bL^C^(by^3D|mVo8hg% zJDvKp3F5d&|IKmz@J?s_+dB=0wVLwl*F8^c4PvJcmmuowjUm~gxbIml2Z7*tx&Z-} z$;W^j%vZ}7TQY$B&))r)?kgG$%xpD_bNqSPc$xmVViWHuE>JuPRs3d$LTkr0!_^TlF6G4iSn?neBq{Ht(71*byWP{w`%d z5#ag{>18xmaF+U2vIM-)sl=}Fz(i&dJWXC^b)acC_3!!AXA(~)`6#M!9T3;V1BH5s zq*HKi2mym|{vpVs`sO6I+J;{2yEOH0!pp=^%le1sc_tYOY>MfLn5AP{;OD?%(r~2p zVvbGYY0)Fai>p884Yh>$VMeO%ZdArOZ%QDn)@7-X0 zbZ0y9wnCQbpYxJH37ks;4QHLe&n;9Wg*K%OGEk{PYcuk&&4?L!K3mZf@mx~hQ@EVa zB=oo~ZL(hO2SHkUS4XPce+ZGmk$E-zIaR7mW3(Hu%JXs8VNVyq)kG17fAL-`Y!n+% z0ngi%Sx10?t_bf!V-d3}YU{elLiGLX7{fC3niiQc)|AMV3GiBZw`&ru*SS`RK{*Ia z&MGI}1>KM|2K-l4>{&R&ZYQ^e@6P zjvh?B$Pc`qb6y<2>IBkK3lNR`QBI*u=g$lH6(~{ymMB80~x9TGp)nYGa_;cr|(d$#y~s(-tq zG;Gqfnv=&D9~O~u$p3(Mq8{Ht6o?EoOg|rk{x#d><)I4?)d1m5Y6JBeAjMvU%*8fg zoVBr^vN6syo1eDsR9Z^wPgA~}!KbqZZkKi-27?&c)8Myf5JdeZytNU&K`7In!Rs63 zhT{fu@U6w-=Wn!cOt3>be;uU=^;w?wKNZVMOYqXZ#kjWbnBvf{F!b)%(^rXUI7EQ{ zH&dqjlPLqV*AO3&L_GVdEYK4U8kx^Fk1{<^%`=GBcr4&7Ffn7fNP&m;-GVkWsQB~k zC+2y2!1LBV_+50#4Oq={yC+{vV5R#adCz<1mi#2mq7bbvzgbj4*SPzODpDT0WE6z=OSw09A{o31|7UIQ%rvidIoDBPYJJvCjh!SyIwNrBCmmHe6^W2poK&t-p%ZEHz~rjpsdk=lW^Is|EY{G_swnH8N-;LJ26Mr#h4NG1So% z{0HvsWAh7N!)LBwl1_~G%M6f^(xU7a$XO9L8-YDK6(Hx$x-MIHTc{l3)k;BT$1vIx z#%RJ?AN?d}pM(1ndQaL}QmW}j{L?v|3H5R68A&nh9>k|K26E*Y!R1PxC1v}1eq=ub zxsirO4D;frCr-Vd z^Nj>093o7ld+B!Zi!~tTWOVh!;ti-hp6vkf@LA8G4i6=LiFjB?r52uO9{?!ote0o1BjB80?6>jZVoXlidGk=FbJ?|bWF3LJS;CI z83hp+JB1qpw)6;jyMBA0Z9)R=h?cVLlM=(&mCURP%EnGCTYs^|okr|)9$Y{0Dx@i zQE)>Ru``tRj1b{CVRw@qxt$VrPRx?v=9a1Svh4CP#5(xr6syqM?~@G3Fx7SZuMJ%d zs=skeug}fnA$QGkJ6E9ctDuJwCs z5t-FtaX82I>2HNrGQqL5H<~CFTyGsV-YEeicXqIKzGD!X_SXe7haaM0H6kJ#6*o5x z9H~R}Uo^RFb<>eH5~N^rRn|>eyVbgZyfuIKXRqeI7;4C;_vFF zg4+9?1(sdCnH>6XUG;AN?e5K)#+uIyT({`2`kR5mOcNK&&=PWidK+YuG5Xmm?f{6> z2o6U(m&$CCFjbYN=ApUJ1pXarQ=%>y<_3uOJl1$UGgCv6wW3CAmFvaa&j@`P=x|Qz zA}0ksXm#vH>8?vs!<)WjT>D9=UArv&`Kk$s@*t_HYf(d_*zI~r~Fij<|p4Kq^VcSyP;yT8vI6~ z7imnZvr=`|Wmril{kk7*ElY3{O6MA=_4E|Q#+MEY@W20nzWw1Tq@)1DpIBEjZ%QYV zlLb8c2sAXPYxg{~JQEH?EO1cn6Hh`Uh#Cr2_m-UB07204GlOfFkQYcB$aU{`a#kd} z7fkCQFSLXAqxpXO4q)17Z;jpjRZYBvn$C_4O;+yO3yCZD3V~Gdx?8JFqy$lGZ|1rJ z8dj@fFZVzv5rVOMS%md8Ao%LchEE)<=HO9)Pc7&B2@Wr2W=w?;i3XlahL;I!y?&8! zXftoWj+#d%8-3(A4zltxs%JuuhL^xf1iXAp0@G(zR<)rH=0+)k*B9q@Pmz)O=^l5u zL_@@7VUD)U+a91x{vs5n@Wg#=pWMqA6|`-odh#|mn53caUMk@!fdK4&6P2*f{TJ!B zTvHsqTph80kPnFj@bE?}lq!SW`f`<-yG#L1fk8V#bRQUI37lQ&qjy&ZTKhIrKq+U% zUs;r5Ax2?Zhr``tjv_v*$6|L*2*eiFo*s=Afc(=^TuU|jWS%Ali*$fyTkNJdBNWv`GMitu z-P(W=AtU=DZfi{*5C5e0p0|iJV#8Bcv2VBYeQ0pX+L?sdy?BWfth~UNs=WlCD$R<^ z4mh|t?^Er69qfb-P&H%w)sxj;<<(+yyldD{Zc`&=s)6Ab0keqKvb{HBFDs*I1%_r^i9P+SRL_|e_Wyc_$A>0>CU zCuhQI2AFKbM6>IpZ*=&!2roc6 z_lp5Yfab|0=uK=?_}BvxQq++guW!DX6e8n^ipPgthqwhFEBY)f<|dU;KSPbWQo(-+Cu+|_<_@;Ob`KDcB zS_vMI$4d7t(Rm!y<8@0$zwL*Yv@43jMK$_wXWtzSqwC^^6t04^RJvE2+UojK!$BB~ zNhaq~pk!n$m#L1fmlvPmVe@BxcALo~{u<%E)&ZXpQRfH-nZuSHAA^T$jVEmn#zJu< zBONq!3!CL!EUW&Yh60QLhapT)9$%4s&5HZ_t~9zAnWQe3ocfWPU8p%h6c@##%Osfwg~QR*PkYZ$ zD*0Q<$$stl5?y;f{&mne`pwJqu~_?lNi%^TfSc)nf0RcIYZx{Ex$JYJaBKL}%2Q(Q zd5}p~+N#220}`u?tL&cv(!Io>W1Qo4y}Tmnh=J^eUhQedQu32C;h&@+3$GYf<4$vJ zd8A9p2^1__G)A}R9dN7Yf!I`lMI!-6Yq$--Ivw6%I#?glutjZWT6)3o*-X8+Lzgai z6HCWT&rqSgM+_))AP^f73nNE#kL%7OS80tBkHN3*pJa0W_IRVw+ayGy0yW4#OK$@) zflmIbxP?U->gqMSV}F6i>EP`4jl7-lnpcK<2jqptj%-gA?iT0*IO!=B-CD{ed3Z>m zu;e6J6L9p|(w|ne(8L7=FlFHUaFP{gEj+`{_mp1&{DU`5&16VYGeQ%%9PG+;IVOzT*jIOkRKEl{e zeut~rkNK4MTOP3=q+?uFeKEqa!)`bV;{7znd+dGArNGry6GTtN4n2X(Tdhc8eU;As z3Dp$g%}rk>A;o^+{YrRqR%7Wk55=9q7`v^UM<44PkT&-AvKot&pW>t(PMw|Tv1bpn z(-{OD$nMRC^T=MKjpkampL8qGegF6JxPZ>T{We>x$8Ic9Q5fw2*whuV-pE-1%~_+< zW(ne1iiT-TH%HK_nx4M2ldN{HM zo|+1$y(i|lt@UIiM$}Uico<8Z1NC=Lr=AY$qy#nG_eF|G%BHkNj0e8ewdnh_6Ra-R zPjCtTIiLc=P9m9MeMYH4Pz>ZrS6$D27MB&G;(d`=*ne*NRQhGT5`sN7ymtd|XhOTo z#CIZRxX2r@8k8pu_<+dMUWwqtKZT?{9ccrebZYYc`K3ey71czrnkL48%e*gk7OQ3X z77vZM0X|8jEebU)oUcN*@AK2XE1`i!l6&lhiT9RkfOj&15V>xPd!ubU@l6-ZO;K+$ z>is*hb|{$7;*s6twE%Bt>lsHkk;KK|4T@~pVPI%3)`Ww zT&_y9`F~4qOd;));+PVf6%;n!Xdl`9DasR=Ojy+8zNe615|A4Rq4(RH$UPANOLeAE zHsOlq{EnlziAd-Ur!GyC+uQyCPd)&jjm_9i*gAh|1MOD{*7RGy3s{X?1SD7loPXUY zHHd+__?7KG3}R;>TJZKSTF|j$AgANrN5;MsI6))3@fAxa!wW*z)3wEv5Vl6ffwYGgKFFpuKzzy0;~ zm=7S(zF$+x$^7$D`NMNb_<@`80IseX{F|Nn>x;4r;3IysOMP(rKUuDeV7tF1C@3NQ zb!-1-{mk*fry83RdmH;dF6QTdl7L%uk3rjn^ADQ-=il|wf=^Y}>T-|sKQD<5I8QR9 zDm%1(bqT+&6>5`TibD))_)dma&|g>b`-&gPgQfI)9P{o!SHlgunY2p^?=JnMus}-#54m3ZU=+-kld^vJ-JbJYTJdJ^{#bkTsQlJP+0C3STlykob2sAEUCli)m_v`-=9r$T~7Pc9! z0Zc<(U|xYW5|_#p8ksSh;NWu!j6pBb1VWE4K&o4XFM=oqLGb=~CVY*L#vATHhX!W` zlRQFLrRoIZpdY``BXO<;1ow01)$*SHN z-%XmQ(T4zxlC$|nLFXKj2a}2$9t=0E!6g1*F_vB zeBZQzLg?PZQINy|4Y~a6u`ycr9XxQaTg59Z@BDTa+T4P+4(lVpBIfU*bb*9t5x73G zi^|SSzrl!naVcY6tbh37g38=2G=BNvib4m=-1~<<)n%p!`$5J*fV8ZA=sRkS9baFu z_>6uR7%843sRNsejQ$RrwSt8OX4bsvjA>An5NJ~peNq4RFj)_~{=e+0Q zz5Fh|JARwJaLqC29OHSOe-Qxb2kHOxX?Fd`ry2WSKFx}(Z_j<275^`v=E0TKNFzsHgFV##U#4@4F5`%UNf&-#jZyq1?qVos6NyGq$L-v2@5!HYC4h++&1i`! z8JX)2sulWHNf5kF zL`UIN!{9e6(REWq=@IiJFi|RfYDWX6=fS8-AR8SuHv9MjFoV0nBfM_U{xar;^SNe@ zNE3dJlCiM-;4VR$f-Va27cIoS0(`oS|MbLgXg5{);yUYn47jX?TQ`<$(;R%|?21_- zp&Y9ZcRUo&kyEoYWA%?vju8LFIb}@q3ttIReK0*?n^l~8=6PkX#rGdX^JD(XwN?30 zzj8D7B_2J{LIKm{%hAErh!;6m+96-$&F`P_2K`W<@vbBqpZg*o^FzMKGuP286|R#{ zn1m}}H?kH&9v*t2V*ddwP)nx4oL4*U$|KC)9YBF-jWXtccYIT~F6m}xQ^_~LV92Xz z6ch&fK9&TUW)Z0lb1ed&=OGV%>6Z?He#wy96;Pk=)CTbZhOEw}Q;jPm1m^7%GW?j*`s+P`x;}U>12ma1{vsQWa$$)%LlHy+@{n-qkepB20$i z6At=zt7&bhHx@R{p1oRh$Qsa%G3VY(XO^R0R+JB*2qZU0dReGa$g!5Dh@1l#oll+;~7 zSv_f)G#>yad*4`rAk2C1H(of;e*A|^|MfDcBAAcT@E!(7>l(x^AI+KG(p)ps@%#KW z%d>PPCpQ)lXh$hqe$!}udQoXS$n)6lQj*?>Wyjx%{h|dp3d1=>VVCM{w=f{1% zfoM%zlJR#B7u;HxBjvdb<}I%hJc%+TW8L-RzJ~osU*i-#T2tEX(L21fucV&-n48j# zE1)rT%C$~HkJ}}GZ)1d!26&QjHReoy>y8X$0+0n@2+u2-7@ zw4Z-~gYR-1RMk%3Aj&^C}0++^juQlL2Ux^MBWo3nJ2(G@e352!w_SAQ%W)-eO;=|*AC%VY-xn0{3jWG5l_lgoSey#_Cfe5nuLRDffa_x|f~*c<~(-*FHcWc!3OD zD-%Y6QrFZsEk7X#R@O3GpQr7CMxB$pnJDv8_=9sK$jdGV~?zo<{L@7=6T(!eCm$>qpqOp>+k=xbo$@*E* zHsn4v@u?Lus~_${^X7{0HUE4!u@<#~Iul``c2I;2Cg;i6y|SEvYEk#ojlR&;2Y*Hm zU-zK5V~gTwu4TE@{NoFjk64~f8}!{+YVUWsaQr;| zbUpY2S?uon*;}*NxP)$f`mw(xd(>r+5|kJBBH6JecV|=DG%!c7YP^_*`-}o8j%|ke zGc-&)1_5Y3ratL$XQvU!GW(2giC5vV07o2?3shtayLH^p7#q zG2m?h+G&kOg|8)Ead#40iL56&SJ0HzZ>?XkzTIvwChYkd_0o6V23~kA5E2dReCXqe zItM2WSmFzyk(H_g67LcTfhtWWUaTMHOwjRH0r?#RhZD84WhHVbG}C3%W}E!5i>GBX zjHi*?_k@KxByBBDPqV-sF@4MVTk^1$xvaU_b?Zn_WvFjmGY%<7zTGx^k8PkqLg}C< z;)>(>W|3re9w>w2`D6C~bZ{0>VNTyL4Sc`X9%7QA?;`uXnkt+}v>pdYxuJ4_$?pQ* zb)x41QR@KdE9Yq-(Gj)hubtA9d#?1=$ov~xF`Y3wQqtnd4|_Sci%)L#ADSQDJmT7O zQmOD$*jV{u4pe+ujF04WQl~hAO&ReD<*8?&3YUSpk3|VjVY^|oL)tC$>!mfHLnv^> zEmk1-3OWN>eot|ltmm6wV!u2_1D)sh=DARZ_yYnSHyQor{=$`l2R|R3{F3>ZjmB`e zajN+ylW4a*lk!lExILED)xrN%_?O17IsdoDFIi(E5NIumC4Zrnt=<;`09yH#-N*bc zKH=7_jXNtMK*kuI*53;ttD#dd+&lJX7@ThtK_c<+IGbA>dYHuxqB`i5*S>07~~O4U$u#S8h+rriD&+}EriA+(sqJ+wv0M} zx(jCBGYW#+ZM|Y7x(TE=DZl)KSw=&c<=1~OOUwVlEd4th@GjMehN0>DCZLYR9w(Au zU&ZdW`pUTa*+nP@Y>(wyu?M<3m!|Cr=agTYOZdUVGTm3aGYLfDlRS;jweT04W3ElC zir`L^<=o%F!iM~8%WC!tG%<=RxZdD75cYL6Cuc{uO#kpnm!aOuw$T1(d8+uJM=XKL zZ3$D>?+jujUJM8n;izjOdh$5l(&(#~9LF&gzJVg~@#D~Mz+8!oa1~JX zialS2(_Zam7qTd%bx@Wvo$SX&FyPO^Icu<_>e6{PC-**k*k!4nUt`BNi`2JP;$8g^ zX1RG+F0fV{+2dz9o5A6{S_ECG=@!7Zir*#eft0eA{Osr++i>`-^b#0(oOj-15QCSX zX9$!Qb}zJ+O=lNQprDC4)Y-=vQ?F6!!jA%YNF-M$f%K!LGk#lO*jq6-(AMnrh6G<` zZVVQM;S(s3KU)X98F#GG;bBB3?|tTgHLh`P3`5BGXN|0s*rv2sANRZC#^EqI0?D4-mQe#dZa;}mUpxa$in=HXVf=a~BxA;pq z&4a7Kl9$737Uqt;KP%RdH0?oz(*fc52h<>@K;b90R>JkKx?c!eEU|0!tpd5wWUkh0 z4Anb-_P<<%z{Am>U`z}Zq6&DA9!6VIQt;38;3*Dt2nyf^skdIHdMqpF!wGxak{cJW z33kyt&`{bjx*x({?!SH1S(hm*4MBG39CVKus59^% z5{bT&qMaf{XUucsi+z-~vhkKgLGKo%4@Cd+(b6A~4ICMLI)ETVg(VB>Ki$pjA6+Gh zT1W>aDX@7Bj8x8j`e+79t9Otm$W^Ux0UNhg1hBzPa@$1YJ#C~I?cwCQdp{udQ3xS@ z#@Rp9l@EJhj1ckU`qdoiMvQ9U7v2RzCL&{VQ4KPwUKkIEphq)jjVT20UaB&+(X1*S z=Qn=FkT=lndf@ry8yb3XHvvcVnY9KnTI3^jw~#{zvx%?QlWd2J0!0acQn5TxvT^%^ zsMpUB0E#}SC6)bFpGz{u{+#Z6sOUr{wqPRzYZnmd$^l24xIo9J3RFDtRV0^+^jKe)a zSPFI`*L-8q4_atSK00lzeA;NDsWg1~zKJxB@~tu0j-7+myG)M(oFrd)O@x(=*n2W$ z8=Q7Rs@Lx>eNcSS1>|8EE(q3cuQvM4%Va|An`lPFRAwTyhis}>TudL|-G(BbuJfn% zxJ)?s8cVASKlX$5Qn0e!50vX1Y^inIy90E_88lH3V!b0auE8u_k5{Ww;3^CvfDaZu zxxq%Ot?Y?zf4iS^kM}i^_~yh%4cArg?*VWz%BzOh_Wxb32|Vz?YZMZ<0j@+h?s-41 zO|g8xyEZ;jRkb5#(DbW-vobpVveKU+qmXNO&e!}!;;px+G`$Lg61$D>YN9Fswo4C_R2^TR!nSv`%qF*j&E4pQ}RV}5M z4Jh!JYI6k+1DG-*mTe&{vE|mIYX&6G+1oM-w8D4JtVkr!{K+7=Svw zh5hIRW}iRcE6uF|>N4LI%I8K!ZG#)gYqMa>v3N11>LULUI~ze8s8Z(xIKMMs+3d)4 z+`1vcQlna49X_lAWP&*pExy)5Ki~4k07{ZKa&Z;1joASSB!_WM9Q*@bO~G^$!Q41x z5*LLqh_y}dR~a^L8>GXM zOP|=&n0;=Us&ZY>O+5?Vuoo!p$JD&{x$D7GD?(m$nKv@51tX??cS`F2EaDNgfiw;B za<=y_*?_~&WI6bL?TRDaRXLXlL>{gJXxdDF=cR{7?L^T%k2-F`3(B()VwuRq z(+@~Nl+`Q;2f$v&M)C`z*l%`u@42Swr%6 zq0x8IA@)eln-WH^(`PGv`LPas=34pCZ0OVuiNTyfbIfrFtsPP?CJ}jz3Zd}g@$_eA z$Q{Z~M~grxiK{Pb>+m#3xH)|Sx2np?nY)DU>_VAOj?s+ZHwUUhu~4HbHGoVt#| zO0yMX1EA{6=!^EVEdCl=zFL+WxJ+jDPsCLw+zw*+y7L1)XZJ04W1ecejMxXk#bO{s z&pkpTEu2r6EUwP6ZGy>a98fOwYUBb0*7&sm?USbM377T5m}i$gn_QDdv1<@O(>V2G zjCP<0B)JhKL`?*@OSnl03jH+FI#)8HfH%nF%W?B{NG`_mty>Onm=XRse|$!DsEvI} znKRxua1^cIG2qj?S=YR|vxBM4o{G%Z`3IMEwaRI+&UVrI!cSXj#*}|!6IOf-3vv8KR#z&D4j3lb~yvd54%fHOcUQp z$#(bx?K)?g4bc5Ks{*0pTFEE(Y#r)k?ANq64z3hXxovKleFmr{Whmm*uke?=aoFqa zDV`!;#`g8_Q9!s|a|$YXUV}q*KfYPd2Qb$JdQM(BJp--tqAYn;I6y9!i_r)+jnyL84|R{)zh3CM(z4gMTPn%Q_cF9-%-C?c`UhZLa|8ZjcFov?-19u@ zL8Ghm&x_CXj2%Bn-)ME>(Z3GSaCPaxdq{^D&H1z@`={|fGSXgF|P!xSk` z+I~k2Jz+`TV>Xr4`%nWfSmlP%o7^p2sOGq(kDPrB5tLN!~e zX4xcS?5EL%O{qSt!=w)Pa-I4$&Zicxvuo; z`9(iG%a$jBkf1~HVtiRG>8jsbF`G9zT^pDPFmLYyW`r8*${m{MRYVtk=x+~8m)}jl zOL7i`c~U~a0Iu-XT^sPndeE;b#u5Ts&Z`>oy@l7t#sp0dw8Vdbn_kAZHUA6T{HhAz zx@Bf_o!0*en}tAbiVS47|3()7{Jg{rmT)(MJ8$lP!gXzsLEVkFe}}Grrmx_KT|!W~ zyLH42JN!?W@R!Sv6#1X|{l9;3!vbq3ydb|S@IT?nJD{Rymq`7WRo=glK4EXLHzzt~ zJd6JW;{E#vh#Y8=(ueUs5ynJF_o^f~a98nv{u-i6-s!XO`h6<@8~%hVLGsSbc*|S= zvNHO+9&iS5MKKm7|8hb6d%+(vp|?Y|r{vx5hVSq1K>tS_4z5U^XY5}W`S*(coB#NZCA|Nkc(i>x==Sx47jN#tyh0yr_|<%Y=q2U6}yaFsd)$eatK z2L21nOcnsQqd6u$V+k1fkves8sg|ebNi3g$2D_h6vcL(g5pX1}0BWkyxdC*V9fZtg z?~O6I!Pg(z&nw>q=A<#vh8~pz>}k+c-g*_1^m7BL%=iZ2SA-sqV7=91%0=Rb9ePUG_3z|3~3^NOBX9;NkRFJeQ+6}C8}IN%UJj-#%(3L zJ1tz%>bcoVK=kvWIt85ygQk>_Q%0QKT^0fqFg^VNASiy2@EUalvR56`gW0AzOv*;h zxFHiNeQ$vSP`w?-vFIj(PjO~+Q11F8G3Df57PW^`ow;r!-n2*0Z)zwEq zIW1En9&9%m@qS0HJ(zfRV9zRoIuwuIJLM@8Zag9PdB_RC-iPtt{UC}XWcRn&@ulIj z$n=-4Eu`j4!b2A^4OWFs0o(0}6TpkAd!q?!B=LF0muWH%Vhm;qxlzSGS# z(gUKA%#T87=LVipJNsKs5K`&*A4p}58Y51!$^S6$R7IaXcOz}*6#&fM9n|%()V1^k@a0-@>RsCYH0Bf`vMR##olG4(bQ@{>5C&;{G*R?r;fSxgc+y$; z1kwcuQ>dfvZX2`S#7W^79g~yuXNioXs^&jxoc1RYrjP3ErvMmd#18&kD;B*}H*?bc#~(GgB99IBNe5EHk-)sDvYET00k0}Oll$ZbnP zCjl{xBL=*+Q_~4Y+qZK+KDsXO{nhK%fL!W#@UW<)T$XP0*!>4X5|iw{Q6;AusS!!fuiqcCxLC~NQMye zb_w)xchnfj(KR*n1N+5SyQtO@k4ukBZ@{Uu6SUh&>)_kqMT&UYSqXeoiNZ2hY-d4f zuA!nqOdfU>yY5Om=wGb$$2&_UlP^=f+*Pie^j?#>>;qflP>Y=r>)!{8qySmC5x|!Y;m<#T#Lqdpffl@%4v_wu_|`G7l^mn`E=jH}BmW&Z;ofn( z#2oG$J8Ql`nE9z^>HA>dk^&M5Aav zNlei}aZI+i_P@KL((|S{4zM)Cyna-J-kb2?d00}awQxy#EqWpeF_wOUdb{aL)I1{K)hpke^v)hU4Q?;A+8JmJ1qY2>D z%%&@6^F>waud`Z& z*&<3EN0T@%x(2gW|15GgdJf!mV?I4@9ac*gvyS+t&R~DZ^_I~Qe#n!pPD5n>4_Do{jBPsw|Zxl!{ZHmk3o=QG}IkuYGaKbhQ+4GWhs83hxAH9@R_N`UKJ zTQpZjBo`Wqipb7;roDJLOltpRVlVgp8BUnor}xYgOz+^cJxUEq&oX#5>pNunJ?fEW z^V3XgN@J*eR9NDMA!2O7TY>JS>I#$Eh!0rz~u>_ z9> zCG6Ao%ybxTI#dQMn#!PKpojHOW%K;D@8oDmT%t%KCaCt&WW%|Yak@wg?3nPt$V+fd|T zWG941>{BYg?ggsZWU|*yCwpb$Q^a>LsR-Z=KYByx>pW5>DTUqMLux6k8{6NjflWYT z=6JcxV!YG4$$XCrFk{7PP0Oo)__f9x=3tIS8qhKey zB}XmO*{cz?UC*%7Ga_QxS-OpzWVq@!wx~IVl+#5Vy2Y)X#6=k%9*2M2IsN$|V>-`Gkfu+u-hQu0 zGP$f&>-XPw9LX?g7df55U9&s$I2}krs;xgPi>7%6WJ&UDanHO801acG5Ql+R{H^_Q z!x>-*C!Jl)Z8EMu9x8Br<|0PDwajR^ZsRH8wG7mPA30AXDi#PRc#LoZVY1Sh6 z3vnWhy<1PB%UEs(cD@r1GeM;Q4N=WCm3~K~_VCwld+|y8P<^ zkE~9@u+-7`Q)}l|^(yC~YM`kgYfhDlNo0XU-tEP7*2vhi2M3?IfXCDQ2$?E}#?NV&x#hbeVw-SgvyLz_h-)Y?+Ik z#Pm(03$N~_!cGr9i`>sO73PNp9X@L&0$Wz+ELnfabU{7ok@+N2r@sFDtV8ZGYHOf9 z;@6do-)WpixG4577TlYDIovUw5WUO&&rUX7zYe{S3yX$`e$&LHTl1&Wm4Iek=s#&~ zsMx%h^(>6oKN&+V(zCI#9HFq=-1Vr3D1ey+ZSz?Cz>P6`DhPPtKa13! z)%IO)rIU5lvVX*WK=eZ!VAuj60@dL4WbEwmOBuW<%F6z-rw7bExblST9NMNRWv z!}Q~`RlAlAd-Kg5(q?^wZ;44BQ)Fj~L-yZc8o%#IKd&rxDC2gCZsr>U+;?tzlBU>m_dgV@?Z{H!>%4C7HvI!^-N1 z-JaHnkwv)bX7GRUxat}49bo1C*GqV{hQ14}_$Hy|8k!BP;8_#=Zqglu!Pzg3$H4&8 zhAF7bSD0g0;{kWj(pt5=0Vz`WJH`^hWuB{%hbiS}ZmR|RNMKW6=JgUq7AYVnqE=i6 znVEyU1IjWjSJJle53!Z|n)iC94Hc*{88O1m+rUDa2Rf*9p*`}nLoE@@76 zb+KXpbkLr-)?(0J@$CN4hMDV?Rnu@W{psg(ICQvER)25Uo&W=u<;b;%ANc^ov~(-y z$f8y->ROf~!P0}`K(B(z4`hncw48?`c(>Kl5A{tY-aS4)?Slsrb%JHakUFbH4zV! z=6Ehej}9wyt2@b&Lue?6{6p^A%DtGK`OPaOb&QcUM!VuX);pI8T!Y&J(_)!Nd?wx` zFlUU>qpR0_VCwaUT0JJJzztCX-h&z;rzWXOF0PUBK$Dq&_rw*ryNt(kAB$&r)-*)A zt14b=HtTG_EZ_cK@TI092*&wz_|;UsLHS4iFUNLFjKQo{u~S)8By~({Le61VVGb*r z)vzgPzo!G(t2ldE%Bdxtv2qkY5mXa(HTUO`@SbqPSnI%XkD=4eQG~M`+xtN5$!-Umzq_88kqFw5!A7=Ypp%z8ZucQU}%>B zT>GeSEC$WLGx#BUb}ELS?0x#&2y;<~!Pq9TB#u+{6kkPLX#k?) zXBIMM`%<&^DF1^$POcm$DS2J4c60J+PZzpa7@QYZy4^anC!7Bw01Zfua8idEi5QlYb`@_~{f)P}Q zuoLJSqvBbVN+1(+JKO`*>)RoR2HcO*zve%AZFJH9*z?o4UG-=P@502<_%zZC!8&w1 zq$$rZ(eT(aLx}6xAp?00;X8K~M!-k^MB|QsyfSu5kN?Rf?tfM)l_X=7I2utqo>Yb! zNKEo^`1@$`G?Z2J7StSb*qv!lqW0X7Cd~x~YrO%8;RE-H#Y~~hNJH`Z(7o+>FqJ6o z+hCeL#oNvjvO|6;u|-Mm~ofpov0>Zh`=( zSu07i9FJ8qlI};A*uK3hVtJCgV6q)oMa)%d5OsqzL@GE_0lD3-2rb^rt%S zTLi(U=}e*)&6g(|c8VipYLi6L*;c+092_kxiBb3Bdw=j%;6P1)pjVHh^ga?s7P*sC zxfdSktJGKiy$T1uI+r8l-o;dGVlgt{li*3qdJcKQLN~pwoi(_VX#E1;VybBGi zol1!&H}bhrnS&#Xwu0LPi}&;W7z$9hUjJ)(g>Um`M9{;?j250`HOTMsDNvGDx!8kl zye%QmJ_mdZ=&u&_Mv{vW2q(KfsdB&bM-`5eO zBRt6CynJS)F{^;E8vJ&dUH}hxhnD!MuF!;7nBDAt6*d^LA(cpdXVOLXTTc0=EFKVW z-z4{@c+=ghtJ32oD&kg$I@=gmCD*1_z8dI3-I?Mj`t6oFGB*>Ra!Rn0S$H%;v`-B@ z(`_QCHEA9Z+#n=#2J?9xQTDDz!|1@gMAssKJWcgnnBQ>elx3f+ec{4w)}mRtpy0!WK27 z&Z5(hZqwk=V_Dx}l=D~^3p05!vCn@G1?G&C(5QwsfG)&>B4hfRho2Kc-Fp1_z+&Xx zW~SLzC_IHL%V2^@=w|NQ3urR7HIJy)7&8N6U6XZ09dRM+m^Ey&V^PCEnvg-U+|y49 z%poAHiP@Q$ls-kRVHj36A0{`ZZ8MLb!oShsL~|fM`QQ$HMinsp0?D+~fS+s&46LlH znT5pP-s|c8)YpdQzm2reG43WyCDr1&WuK>k>)G_uyp#E?&TkLcLdD_JNfEMJi{eWY zUdxl;?Qo$Ru#qK@o_&8qrX5dxz{+XSbcn5MHZs(F&jefqA5yj5@ zbjk-sCbCJ=hM3N|;PVwG4u9xo0W6#qDtYvgmc8{1FPxltlbG0h?0c0|KEqbdtO(_W^6tqCaAv)3Ir^gwtB{a7Zs;@0&=KzzB{Ri*XHMJ|L?k;pZ8+dxxnDg{(>&8u% zz%xLCe5j^2b{vQmsO2B-1)D8ViX&XFEWY z7R{~~EF6VL8BXvGIqI>k6=>Me?TmDCdLvfS9V=mZzFSPv7~W7-84Bm=6EQ#T zXOUpME!1#p4v|45%_E?CYA2`~M(5hBR-A?=9=5M(Ef;nsGoF;jJ}nOSQt_w8D%XDG zPaibZwUnrm6@>8$>r3Cug*cJgyf<9_j^O1j^j)IK_Dz>R_zI?Sk7`+d`g|8bU}<+Z zBqI@EvK%zkQc$&$V+L&hndFl*oXGU4x3t~Ccxt$mQ;7Ft^?p__bi1gSnX|L z)@c^0xlXl{OHSv3hjDAB!V>?@IP=@-jT4-=KQGePN)ufq#;pARbl~;A;=0 zmwJ8j_biW&Oc}Km$B|ag`0k=u$CKty;-G`raP8Q7bfZ@*nNxLPbvsF4_B|Ex!vP^g zqpe(nh@Mp5>hJOBP=0wI=$MbxtC3bGH}@AaUwmOw1W~MzOxe00pOxJ&G#gdRrWHmZ=9mXbP!p_zM! zHAMR3!z}m&xe%D;y$DgWPMT^%&EO)koV7v&HQ6XhWX23jxiGIU$$Lt4?}RHvTIWyA zPlFkpWHoQ2zYunWp(t7hG-ADc9YxLWH(ZuAL4oveNyN~w%Q3{^qmOC>x1^8U8}JRg z+iu8d}qO>nUBq z%R?TMs&3+RGi)4A_77(xI6|dgD||aG|2*?jCt4uIq`3;!7$tc&bLVWY2JxLm9QE1r z(%Eem_6K_iG7ln_z)#$P`&9(3Lc-#uFG zaY>Rns@@5AH`_Xu9YAOg8ZKAG&78#f7^YhX7Y8q9a@9p!5OTjwofo7HK9!By{dFrh z@@1xG1K?UVpPGMW=(HH*V*?E^5U0B{S_G&sgn#|4VsFq3Z1@7ENR)?GTcT(SV#Ij_ z`o;%!?JDV4zP8!Qp!?{chgGN&GZK-6m52~Xbi4&GNXFPLmx(?sxel8=&${BRxmMkR zgsg{G7G<*^cUI!AtQAqlu0MDzFeo*n2Th!xwX0I%njfdq+7m?Hb3c4it!=-tQN3~B z#V?x&e&A%)bnC-SEX7ZS!1HFzq*UcA{jT))&^Y6G_xp8l0^X1ytEdg$D3e%2%^#3~ z1{i5Ro<6+(ro9wjo>9;8ywA0NgR*&Nt(8j0Brr#g&mA96&&&VD_inhX!e4d~lP z;O1Z`&T`uAME?fmQMq<3zkRybJL0HG(HaMIfb1mvg6@6^In0xs1uGUR^+^{@R5O`yqg9FL{dzk(_d!@Ldo+<12VrynbV1 z_Mxu#;v0p;3$jx&(ZDQE$C;B%y)F#>_CEhR-WMlxdUUN%DKEM<+kgp@)WB%_+pUIc z96xJ1vN3=C>0kefAl^f*P$6l;>jj*r02TQ0u(LrEhSuINvZ%*}POO90@rA&JFUy;l z;O6yzeJetA!YHNuP@8HjPnd3E1jgTA{r>Lo8vwJgY*0*XKfjLO{~25o_|ZfTtjquZ zxnGO%`&R#dcU|CjAf%W}HB(W+`Hs2h1++@MrP1Qd<-Sy3$oAclIZ2G>_YH>YfvJ^) z3~!$UfbTyZweZ&|_s_P6GsZz$6O$Jjx}LkMjC6+cR>BYt$D!(L8}#YLdd^aOMYKw{imUaSbW2m6|ctwLg#7 z2X0L2kf#i5iaDzQ9Ot=&fz6b?29QxcFo$$*-~Y&G0|cT!KpaZ|>pDOU`M~hnZ{p_E zx1VdchPdD@ECk_au|O&k3vj_6zFF`cx$BR*?N>&qt$Z$^%K~-~>3#R-#wSoH={g8% z*=fFMNLcpi_k~|Tzv~4Wi|Y}ZAmDDN+umaU!rRPRf=JCKDjWdP%VpM|CL=hgp_6ul z#H>5kR4SNKU%=>iciB`j0P>uLY#+QImvWl-Q0z>jkR$T%Ig>43R1)bQ011Nfw@KhE zyOU@CvNN9%Um8@Q$^C@av`a-KZ>%`4K)BJMp{=bg;Dsh;>)tE+s6|N!gr(8T{po>h z8V5UoMsEsQmHaTPiL-*o#QWtVfM1ob4LNWFQJ~#!*CF^9&3TLx1RNe=mS^D{_W)}D zwnkcl@QX515sw;3>D&d{Im&0x+sjJ!PA;zq)__RKQ3ZllAeVoMaIx!zIGD@hWUXEE_{3W z&z2X$59(TM%uee$5W2~B9J&}V@_7g3uW^B4X7G5#c;*dhm`yxT@qJnm1bW!D4@@gY zI6<)O2{0Kp8!NF?_LZD`+fOg_PYeTEI1%uMk^ORE#EiwPPMigKS zoOVlg-rcC4KFcTa4Rl6lA&^acK8xuDbOJHM!^4|))rn=uC&>h-PdV<*Y!o2Qav^DM zgRf~-z8)EP;OqN}8)Me%0gvHJUeuXz+p-yct9H@i~sJ!j&$HCz}Io5qHqRwMFu2fYD-tl<>cg&qaY?RR2O*f2@w|& zOY4zCX~j;Ufg|@`Y5H_t{kK{w<+TEIIB$=qVe48LJY{2o!(;2(>P4Ee;QPlGX(N8U zFO6?X&vT``fd7ndKgk15iHmYB5Ep?cFsS|bfd{zb z=zbKc+r8ED=MaROgI=@UZ~LI`K)4tZg!e~StOJ$-4z?d3DmjDU#rR8S zdS4VE8rZ#J!VLx1u@hyoYmvF9z$#rE&tpD?y?jw1>U{nsu$H~|K5ea_G*L>xLzF<; z8N@E15bNmCujm7sl4&h5#S0e{$45#SWS$Uu#aG_=SqJI^-WB0X&LA#?{4Qg%=FD|j zNAl~{_W+@w8${PU>P3OwtP4<~Ri3xl0;*UUwm%H>@$zyFsSdLTW-(^F1lb(T@J)eHHbLrHB4syb} z#fE1^fA4(}HMsYen?D3W#XOQ^SXcGz{WaedX_IfjD8ts)aW6X8)gVg3D2oR^IGE+A zZV1)H7alBh-ph!khl<;J9o?{>N@Gaav!Rpt%OExEz;7F6VdVA?JwT&bcNtmW<`_;z zJu@3!f118LoEO*4of4DZ;7aAOn@U9rv_3MY&w=_lFki=5#%+$HAbk34-G#oLm%ivH z;Fs)EcrQ-?$liOWIII4lR^bQHJ>b${v2oFM0~;6JmDiN0P@!|vpN8C z0M$+SPUq5eeLd)UoPIJ)-W$<#QZRNIJHB?gTX!=_y=r4#G@@L_=&LaGjIO&0q!a^A zm24uq+8wC=5)r*;8mD{uN1z9_2-FPQ5djj|nH(+OKZQ#SVE(jU%??Yp;!M}(}Z$S*({nK}V>2X5|0l9o5BEGP^1&8)Xz2Y{DD7ppf zujJ3nAF*xF;oMJ%?98MF;I_^s)xEfMYIBm zib6~098jb8+FeJW=w;iz6g3o$`tG}COCPEpAr2dKXTLHPJ4;zdf zhItokt2TxXJoFtdhn1WbRh03p9jx|-)l2Xy$Q9hg#nSFS^8(e>uL{Pq29z%*65m{| z49jCWej5HPRR-vg-@bSYUEpb}1T?Q}TSuhohHCPZX#S`u{`}7XiW+1y)r^OyT;h4I zwoc|s&g76P@5O#Lz~_%!<-6OWDdx@X8pKr6gvpHM8Mf$yezW`C48djVI5Fx5uQgdT ze*$}}uh|y^zMkH7f=2&J6&Prf!KL%XY>QI4$ii zGhoVLHL{q;#%c-D#AgeWQAoKKR6O2B4%(<4-|x(?n8n%wWg$_{?v=w>TAkiHrtl{p zPv}@fByN#UX=Hn#zCoi&DOJ8sp7dg~shntf5EnTc{YG?R|HRb1+YrrP>>j;hB8biK zP-hkYv!HrV>;Fc5wiP&s8DB}gtzy5rC#CGA>iuA;yBgyz6p#Kf*F$PZir5QI;Gh2E zt<2>_9TrD4e~d*kiQ7C9;C~-BI0(X+pKA>+H@*ouSFoKkwxDVVOMMMghXZxjfa>>JZcP*Bovf`(RS zk9%LL&UIRU{o6_gcz68q3Fuc$30xqz_!lS%9JN$*0vs#ep`vwDg-@(<=eV{*=qo0B z${2#8+tC3x@FRKD9wq<9wOKglyE&F6TWsrDL~xPoFVaKAFh7%=w`fpT(iR5_TAw$( zc!Th^TFbq_Vcci*41>DeH8`Fy@`ePqkTz@4myAw{c*s|3+%)Z~G7KEpu8##&r|@y+ z>ETQx(gIDLxR?B6b4ugkv`QG019x!lM)rPjm8z`TTkaX*ZSxXKHmf_LAp?Xr5gP*> z1VW<|AapTl^O6Lu?w%4eR5P66z|ESp`yHKXku?3=-EQ1KWxpP9ZY*z<+1EoACHX#$ zeTWrWD8^357i!PsVvJo$oW$~!Q`r^Wg@{l|*I-GUP5P|19Vv!j0#_**zVz&1!Ce@# zUA$F+8wj`g$Avh?7@hoIoNcJKCY~aL!IHFdbPqM#)~~b$)?bqorI+&pR)A=Efn4Rr{inkpwfFO?FtA+7(Pa4E#K}-+J2xZ8Cm}VNG;FIIlcHn% z18Ja%Y+H_Km=NjwWXdUh9n)OW#OqwPVbO8jN;x>qx1`8uWD1QUMc6bdbJ@& z_cZ?6Gg;b@rfkD!KkHFQGjN3?k6?2GX#O>2^J|*z5d#u5PaIhDKZg+2yOkQU7Ja8` zCiH{_V2%GSxZ1+OO_zGgo0k=))p1KNga0cT$x_}H7)9Cstesl#^pX&y*yceGvsvPc zgQtjA3Gou9B$M*$P2u-_B{x{W3{@wPei|%0AHpmzhBAS?+bcnq@=wP>1eW>3vu(iQ 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 Date: Fri, 11 Oct 2024 15:12:38 +0800 Subject: [PATCH 15/19] plopBot - A-JavaDoc // Implemented & Tidied Up --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 8eedfaa0b..3c0e71410 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # PlopBot User Guide -![PlopBot Screenshot](docs/Screenshot.png) +![PlopBot Screenshot](Screenshot.png) PlopBot is a user-friendly task management application that helps you keep track of your to-dos, deadlines, and events. With its simple command-line interface, you can easily add, view, and manage your tasks. From 0a2d2d0050d360b080a61703650faf9084fe8b9a Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Fri, 11 Oct 2024 15:27:31 +0800 Subject: [PATCH 16/19] plopBot - Final Jar File // Completed --- .../Individual Project.jar | Bin 9853 -> 15592 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/out/artifacts/Individual_Project_jar/Individual Project.jar b/out/artifacts/Individual_Project_jar/Individual Project.jar index 5972a87f0bc1db7a381a5761d451928924ce6262..4f813b3674960f64459f94ee4320b56e6de547ed 100644 GIT binary patch literal 15592 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 GpZ*`B`pA|5 literal 9853 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 Date: Sat, 12 Oct 2024 15:20:21 +0800 Subject: [PATCH 17/19] plopBot - A-JavaDoc // Tidied Up --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 3c0e71410..3d46e4d7b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -129,7 +129,7 @@ Expected output: ## Exiting PlopBot -To exit the program, simply write: +To exit the program, simply write one of the following: bye exit From 1d7f773b7ebc45c8bc3ce327771c068f05e2fa36 Mon Sep 17 00:00:00 2001 From: Philip Chang <132544684+philip1304@users.noreply.github.com> Date: Sat, 12 Oct 2024 22:21:42 +0800 Subject: [PATCH 18/19] ip // Final JAR Release ip // Final JAR Release --- Individual Project.jar | Bin 0 -> 15592 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Individual Project.jar diff --git a/Individual Project.jar b/Individual Project.jar new file mode 100644 index 0000000000000000000000000000000000000000..4f813b3674960f64459f94ee4320b56e6de547ed GIT binary patch literal 15592 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 GpZ*`B`pA|5 literal 0 HcmV?d00001 From 5a8561d868ddec08093a18b2ceb464675083ac5b Mon Sep 17 00:00:00 2001 From: Philip Chang Date: Sat, 26 Oct 2024 15:32:18 +0800 Subject: [PATCH 19/19] PlopBot // Updated Notes: - Added abstraction & documentation // Parser.java - Added abstraction & documentation, fixed errors that would cause the program to exit // PlopBot.java - Added documentation // Storage.java - Added documentation, cleaned up code // Task.java - Added documentation // TaskList.java - Added documentation // Ui.java --- src/main/java/Parser.java | 386 +++++++++++++++++++++++++++--------- src/main/java/PlopBot.java | 289 +++++++++++++++++++++------ src/main/java/Storage.java | 55 +++-- src/main/java/Task.java | 108 +++++++--- src/main/java/TaskList.java | 57 ++++-- src/main/java/Ui.java | 64 ++++-- 6 files changed, 744 insertions(+), 215 deletions(-) diff --git a/src/main/java/Parser.java b/src/main/java/Parser.java index 51a734dab..1b3abe613 100644 --- a/src/main/java/Parser.java +++ b/src/main/java/Parser.java @@ -3,133 +3,335 @@ import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAdjusters; +/** + * Parser class responsible for parsing user input and creating Task objects. + * This class handles the conversion of string commands into structured Task objects, + * including parsing dates and times in various formats. + */ public class Parser { + private static final String DEADLINE_SEPARATOR = " /by "; + private static final String EVENT_TIME_SEPARATOR = " /from | /to "; + + /** + * Parses a user input string into command components. + * Splits the input into a command type and its details. + * + * @param userInput - The raw input string from the user + * @return - String array where index 0 contains the command type and index 1 contains the details + * @throws PlopBotException - If the input is null, empty, or cannot be parsed + */ public String[] parseCommand(String userInput) throws PlopBotException { - String[] parts = userInput.split(" ", 2); - if (parts.length == 0) { + if (userInput == null || userInput.trim().isEmpty()) { throw new PlopBotException("Empty command"); } - return parts; + return userInput.split(" ", 2); } /** - * Method that parses tasks into their respective types based on the user's inputs. - * @param commandParts - * @return - * @throws PlopBotException + * Creates a Task object based on the parsed command components. + * Supports creation of todo, deadline, and event tasks. + * + * @param commandParts - Array containing the command type and details + * @return - A new Task object of the appropriate type + * @throws PlopBotException - If the command format is invalid or task creation fails */ public Task parseTask(String[] commandParts) throws PlopBotException { - if (commandParts.length < 2) { - throw new PlopBotException("Invalid task format"); - } + validateCommandParts(commandParts); String type = commandParts[0]; String details = commandParts[1]; - switch (type) { - case "todo": - return new Task(details); - case "deadline": - String[] deadlineParts = details.split(" /by ", 2); - if (deadlineParts.length != 2) { - throw new PlopBotException("Invalid deadline format"); - } - return new Task(deadlineParts[0], parseDateString(deadlineParts[1])); - case "event": - String[] eventParts = details.split(" /from | /to "); - if (eventParts.length != 3) { - throw new PlopBotException("Invalid event format"); - } - LocalDateTime startTime = parseDateTimeString(eventParts[1], null); - LocalDateTime endTime = parseDateTimeString(eventParts[2], startTime); - return new Task(eventParts[0], startTime, endTime); - default: - throw new PlopBotException("Unknown task type: " + type); - } - } - - /** - * Parse helper method to parse date strings for deadline tasks. - * @param dateString - * @return - */ - private static LocalDate parseDateString(String dateString) { - LocalDate now = LocalDate.now(); + return switch (type) { + case "todo" -> createTodoTask(details); + case "deadline" -> createDeadlineTask(details); + case "event" -> createEventTask(details); + default -> throw new PlopBotException("Unknown task type: " + type); + }; + } + + /** + * Validates that the command parts array contains the required components. + * + * @param commandParts - Array of command components to validate + * @throws PlopBotException - If the array is null or has insufficient components + */ + private void validateCommandParts(String[] commandParts) throws PlopBotException { + if (commandParts == null || commandParts.length < 2) { + throw new PlopBotException("Invalid task format"); + } + } + + /** + * Creates a simple todo task with the given description. + * + * @param details - The description of the todo task + * @return - A new Task object representing a todo task + */ + private Task createTodoTask(String details) { + return new Task(details); + } + + /** + * Creates a deadline task with the given description and deadline date. + * + * @param details - The description and deadline information + * @return - A new Task object representing a deadline task + * @throws PlopBotException - If the deadline format is invalid + */ + private Task createDeadlineTask(String details) throws PlopBotException { try { - return LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE); - } catch (DateTimeParseException e) { - String[] shortDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + String[] deadlineParts = details.split(DEADLINE_SEPARATOR, 2); + if (deadlineParts.length != 2) { + throw new PlopBotException("Invalid deadline format. Usage: deadline description /by DATE"); + } + return new Task(deadlineParts[0], parseDateString(deadlineParts[1])); + } catch (IllegalArgumentException e) { + throw new PlopBotException("Invalid date format: " + e.getMessage()); + } + } - for (int i = 0; i < shortDays.length; i++) { - if (shortDays[i].equalsIgnoreCase(dateString)) { - return now.with(TemporalAdjusters.next(DayOfWeek.of(i + 1))); - } + /** + * Creates an event task with the given description, start time, and end time. + * + * @param details - The description and event timing information + * @return - A new Task object representing an event task + * @throws PlopBotException - If the event format or timing is invalid + */ + private Task createEventTask(String details) throws PlopBotException { + try { + String[] eventParts = details.split(EVENT_TIME_SEPARATOR); + if (eventParts.length != 3) { + throw new PlopBotException("Invalid event format. Usage: event description /from START_TIME /to END_TIME"); } + + validateEventTimeParts(eventParts); + return createEventTaskFromParts(eventParts); + } catch (IllegalArgumentException e) { + throw new PlopBotException("Invalid date/time format: " + e.getMessage()); + } + } + + /** + * Validates that event time parts are not empty. + * + * @param eventParts - Array containing event description and time components + * @throws PlopBotException - If either time component is empty + */ + private void validateEventTimeParts(String[] eventParts) throws PlopBotException { + if (eventParts[1].trim().isEmpty()) { + throw new PlopBotException("Start time cannot be empty. Usage: event description /from START_TIME /to END_TIME"); + } + if (eventParts[2].trim().isEmpty()) { + throw new PlopBotException("End time cannot be empty. Usage: event description /from START_TIME /to END_TIME"); + } + } + + /** + * Creates an event task from validated event parts. + * + * @param eventParts - Array containing event description and time components + * @return - A new Task object representing an event task + * @throws PlopBotException - If the time parsing fails or end time is before start time + */ + private Task createEventTaskFromParts(String[] eventParts) throws PlopBotException { + try { + LocalDateTime startTime = parseDateTimeString(eventParts[1], null); + LocalDateTime endTime = parseDateTimeString(eventParts[2], startTime); + validateEventTimes(startTime, endTime); + return new Task(eventParts[0], startTime, endTime); + } catch (DateTimeParseException e) { + throw new PlopBotException("Invalid date/time format. Time must be in format 'YYYY-MM-DD HH:mm' or 'DAY HHam/pm'"); + } + } + + /** + * Validates that the end time is not before the start time. + * + * @param start - Event start time + * @param end - Event end time + * @throws PlopBotException - If end time is before start time + */ + private void validateEventTimes(LocalDateTime start, LocalDateTime end) throws PlopBotException { + if (end.isBefore(start)) { + throw new PlopBotException("End time cannot be before start time"); + } + } + + /** + * Parses a date string into a LocalDate object. + * Supports standard ISO format (YYYY-MM-DD) and relative dates (today, tomorrow, day names). + * + * @param dateString - The date string to parse + * @return - LocalDate object representing the parsed date + * @throws PlopBotException - If the date string is empty or invalid + */ + private static LocalDate parseDateString(String dateString) throws PlopBotException { + if (dateString == null || dateString.trim().isEmpty()) { + throw new PlopBotException("Date cannot be empty"); + } + + try { + return parseStandardDate(dateString.trim()); + } catch (DateTimeParseException e) { try { - return now.with(TemporalAdjusters.next(DayOfWeek.valueOf(dateString.toUpperCase()))); + return parseRelativeDate(dateString.trim()); } catch (IllegalArgumentException ex) { - switch (dateString.toLowerCase()) { - case "today": - return now; - case "tomorrow": - return now.plusDays(1); - default: - throw new IllegalArgumentException("Unable to parse date: " + dateString); - } + throw new PlopBotException("Invalid date format. Use 'YYYY-MM-DD', 'today', 'tomorrow', or day name"); } } } /** - * Parse helper method to parse date and time strings for event tasks. - * @param dateTimeString - * @return + * Parses a date string in standard ISO format (YYYY-MM-DD). + * + * @param dateString - The date string to parse + * @return - LocalDate object representing the parsed date */ - private static LocalDateTime parseDateTimeString(String dateTimeString, LocalDateTime referenceTime) { + private static LocalDate parseStandardDate(String dateString) { + return LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE); + } + + /** + * Parses a relative date string (today, tomorrow, day names). + * + * @param dateString - The relative date string to parse + * @return - LocalDate object representing the parsed date + */ + private static LocalDate parseRelativeDate(String dateString) { LocalDate now = LocalDate.now(); + String lowercaseDate = dateString.toLowerCase(); + + if (lowercaseDate.equals("today")) return now; + if (lowercaseDate.equals("tomorrow")) return now.plusDays(1); + + return parseDayOfWeek(dateString, now); + } + + /** + * Parses a day of week name into a date. + * + * @param dateString - Day name to parse + * @param now - Reference date for calculating the next occurrence + * @return - LocalDate object representing the next occurrence of the specified day + */ + private static LocalDate parseDayOfWeek(String dateString, LocalDate now) { try { - return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); - } catch (DateTimeParseException e) { - String[] parts = dateTimeString.split(" ", 2); - - if (parts.length == 2) { - LocalDate date = parseDateString(parts[0]); - LocalTime time = parseTimeString(parts[1]); - return LocalDateTime.of(date, time); - } else if (parts.length == 1) { - LocalTime time = parseTimeString(parts[0]); - LocalDate date = (referenceTime != null) ? referenceTime.toLocalDate() : now; - return LocalDateTime.of(date, time); + DayOfWeek day = DayOfWeek.valueOf(dateString.toUpperCase()); + return now.with(TemporalAdjusters.next(day)); + } catch (IllegalArgumentException e) { + return parseAbbreviatedDay(dateString, now); + } + } + + /** + * Parses an abbreviated day name (Mon, Tue, etc.). + * + * @param dateString - Abbreviated day name to parse + * @param now - Reference date for calculating the next occurrence + * @return - LocalDate object representing the next occurrence of the specified day + * @throws IllegalArgumentException - If the abbreviated day name is invalid + */ + private static LocalDate parseAbbreviatedDay(String dateString, LocalDate now) { + String[] shortDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + + for (int i = 0; i < shortDays.length; i++) { + if (shortDays[i].equalsIgnoreCase(dateString)) { + return now.with(TemporalAdjusters.next(DayOfWeek.of(i + 1))); + } + } + throw new IllegalArgumentException("Unable to parse date: " + dateString); + } + + /** + * Parses a date-time string into a LocalDateTime object. + * Supports standard format (YYYY-MM-DD HH:mm) and relative formats. + * + * @param dateTimeString - The date-time string to parse + * @param referenceTime - Optional reference time for relative parsing + * @return - LocalDateTime object representing the parsed date-time + * @throws PlopBotException - If the date-time string is empty or invalid + */ + private static LocalDateTime parseDateTimeString(String dateTimeString, LocalDateTime referenceTime) throws PlopBotException { + if (dateTimeString == null || dateTimeString.trim().isEmpty()) { + throw new PlopBotException("Date/time cannot be empty"); + } + + try { + return LocalDateTime.parse(dateTimeString.trim(), + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + } catch (DateTimeParseException e1) { + try { + return parseRelativeDateTime(dateTimeString.trim(), referenceTime); + } catch (Exception e2) { + throw new PlopBotException("Invalid time format. Use 'YYYY-MM-DD HH:mm' or 'DAY HHam/pm'"); } } - throw new IllegalArgumentException("Unable to parse date and time: " + dateTimeString); } /** - * Parse helper method to parse time strings. - * @param timeString - * @return + * Parses a relative date-time string. + * + * @param dateTimeString - The relative date-time string to parse + * @param referenceTime - Optional reference time for relative parsing + * @return - LocalDateTime object representing the parsed date-time + * @throws PlopBotException - If the parsing fails + */ + private static LocalDateTime parseRelativeDateTime(String dateTimeString, LocalDateTime referenceTime) throws PlopBotException { + String[] parts = dateTimeString.split(" ", 2); + if (parts.length != 2) { + throw new PlopBotException("Invalid date/time format. Use 'YYYY-MM-DD HH:mm' or 'DAY HHam/pm'"); + } + + LocalDate date = parseDateString(parts[0]); + LocalTime time = parseTimeString(parts[1]); + return LocalDateTime.of(date, time); + } + + /** + * Parses a time string in either 24-hour or AM/PM format. + * + * @param timeString - The time string to parse + * @return - LocalTime object representing the parsed time + * @throws PlopBotException - If the time format is invalid */ - private static LocalTime parseTimeString(String timeString) { + private static LocalTime parseTimeString(String timeString) throws PlopBotException { try { return LocalTime.parse(timeString, DateTimeFormatter.ofPattern("HH:mm")); } catch (DateTimeParseException e) { try { - return LocalTime.parse(timeString.toUpperCase(), DateTimeFormatter.ofPattern("ha")); - } catch (DateTimeParseException e2) { - // Handle "2pm" format - String formattedTime = timeString.toLowerCase(); - if (formattedTime.endsWith("am") || formattedTime.endsWith("pm")) { - int hour = Integer.parseInt(formattedTime.substring(0, formattedTime.length() - 2)); - - if (formattedTime.endsWith("pm") && hour < 12) { - hour += 12; - } else if (formattedTime.endsWith("am") && hour == 12) { - hour = 0; - } - return LocalTime.of(hour, 0); - } - throw new IllegalArgumentException("Unable to parse time: " + timeString); + return parseAmPmTime(timeString); + } catch (Exception ex) { + throw new PlopBotException("Invalid time format. Use 'HH:mm' (24-hour) or 'HHam/pm'"); } } } -} \ No newline at end of file + + /** + * Parses a time string in AM/PM format. + * + * @param timeString - The AM/PM time string to parse + * @return - LocalTime object representing the parsed time + * @throws PlopBotException - If the AM/PM time format is invalid + */ + private static LocalTime parseAmPmTime(String timeString) throws PlopBotException { + String formattedTime = timeString.toLowerCase(); + if (!formattedTime.endsWith("am") && !formattedTime.endsWith("pm")) { + throw new PlopBotException("Invalid time format. Must end with 'am' or 'pm'"); + } + + try { + int hour = Integer.parseInt(formattedTime.substring(0, formattedTime.length() - 2)); + boolean isPm = formattedTime.endsWith("pm"); + + if (hour < 1 || hour > 12) { + throw new PlopBotException("Hour must be between 1 and 12"); + } + + if (isPm && hour < 12) hour += 12; + else if (!isPm && hour == 12) hour = 0; + + return LocalTime.of(hour, 0); + } catch (NumberFormatException e) { + throw new PlopBotException("Invalid time format: " + timeString); + } + } +} diff --git a/src/main/java/PlopBot.java b/src/main/java/PlopBot.java index 5edf6a0ea..c70a56583 100644 --- a/src/main/java/PlopBot.java +++ b/src/main/java/PlopBot.java @@ -1,11 +1,32 @@ import java.util.ArrayList; +/** + * Main class for the PlopBot task management application. + * Handles the core functionality of managing tasks, processing commands, + * and coordinating between different components (UI, Storage, Parser). + */ public class PlopBot { private Storage storage; private TaskList tasks; private Ui ui; private Parser parser; + /** + * Main entry point for the PlopBot application. + * Initializes and runs the bot with default storage location. + * + * @param args - Command line arguments (not used) + */ + public static void main(String[] args) { + new PlopBot("data/tasks.txt").run(); + } + + /** + * Constructs a new PlopBot instance with specified storage location. + * Initializes UI, storage, task list, and parser components. + * + * @param filePath - Path to the file where tasks will be stored + */ public PlopBot(String filePath) { ui = new Ui(); storage = new Storage(filePath); @@ -18,70 +39,17 @@ public PlopBot(String filePath) { parser = new Parser(); } + /** + * Starts the main execution loop of PlopBot. + * Continuously reads and processes user commands until exit is requested. + */ public void run() { ui.showWelcome(); boolean isExit = false; while (!isExit) { String fullCommand = ui.readCommand(); try { - String[] commandParts = parser.parseCommand(fullCommand); - switch (commandParts[0]) { - case "bye": - case "exit": - case "quit": - isExit = true; - break; - case "list": - ui.showTasks(tasks.getTasks()); - break; - case "todo": - Task newToDo = parser.parseTask(commandParts); - tasks.addTask(newToDo); - storage.save(tasks.getTasks()); - ui.showTaskAdded(newToDo, tasks.size()); - break; - case "deadline": - try { - Task newDeadline = parser.parseTask(commandParts); - tasks.addTask(newDeadline); - storage.save(tasks.getTasks()); - ui.showTaskAdded(newDeadline, tasks.size()); - } catch (PlopBotException e) { - ui.showError(e.getMessage() + "\n Usage: deadline description /by DATE" + - "\n DATE can be 'Sunday', 'Mon', 'Tuesday', or 'YYYY-MM-DD'"); - } - break; - case "event": - try { - Task newEvent = parser.parseTask(commandParts); - tasks.addTask(newEvent); - storage.save(tasks.getTasks()); - ui.showTaskAdded(newEvent, tasks.size()); - } catch (PlopBotException e) { - ui.showError(e.getMessage() + "\n Usage: event description /from START_TIME /to END_TIME" + - "\n TIME can be 'Mon 2pm', 'Tuesday 14:00', or 'YYYY-MM-DD HH:MM'"); - } - break; - case "delete": - int index = Integer.parseInt(commandParts[1]) - 1; - Task removedTask = tasks.removeTask(index); - storage.save(tasks.getTasks()); - ui.showTaskRemoved(removedTask, tasks.size()); - break; - case "find": - String keyword = commandParts[1]; - ArrayList matchingTasks = tasks.findTasks(keyword); - ui.showMatchingTasks(matchingTasks); - break; - case "mark": - handleMarkTask(commandParts); - break; - case "unmark": - handleUnmarkTask(commandParts); - break; - default: - throw new PlopBotException("Unknown command"); - } + isExit = processCommand(fullCommand); } catch (PlopBotException e) { ui.showError(e.getMessage()); } @@ -89,10 +57,203 @@ public void run() { ui.showExit(); } - public static void main(String[] args) { - new PlopBot("data/tasks.txt").run(); + /** + * Processes a single command from the user's input. + * + * @param fullCommand - The full command string input by the user + * @return - Boolean indicating if the program should exit + * @throws PlopBotException - If the command is invalid or execution fails + */ + private boolean processCommand(String fullCommand) throws PlopBotException { + String[] commandParts = parser.parseCommand(fullCommand); + switch (commandParts[0]) { + case "bye": + case "exit": + case "quit": + return true; + case "todo": + handleAddTask(commandParts); + break; + case "deadline": + handleAddDeadline(commandParts); + break; + case "event": + handleAddEvent(commandParts); + break; + case "delete": + handleDeleteTask(commandParts); + break; + case "find": + handleFindTasks(commandParts); + break; + case "list": + handleListTasks(); + break; + case "mark": + handleMarkTask(commandParts); + break; + case "unmark": + handleUnmarkTask(commandParts); + break; + default: + throw new PlopBotException("Unknown command"); + } + return false; + } + + /** + * Common method to add a task and save to storage. + * + * @param task - The task to add + * @throws PlopBotException - If task creation or saving fails + */ + private void addAndSaveTask(Task task) throws PlopBotException { + tasks.addTask(task); + storage.save(tasks.getTasks()); + ui.showTaskAdded(task, tasks.size()); + } + + /** + * Handles adding a new task and saving it. + * + * @param commandParts - The parsed command parts + * @throws PlopBotException - If task creation or saving fails + */ + private void handleAddTask(String[] commandParts) throws PlopBotException { + Task newTask = parser.parseTask(commandParts); + addAndSaveTask(newTask); + } + + /** + * Handles adding a deadline task with proper error handling. + * + * @param commandParts - The parsed command parts + * @throws PlopBotException - If deadline creation or saving fails + */ + private void handleAddDeadline(String[] commandParts) throws PlopBotException { + try { + Task newDeadline = parser.parseTask(commandParts); + addAndSaveTask(newDeadline); + } catch (PlopBotException e) { + throw new PlopBotException(formatDeadlineError(e.getMessage())); + } + } + + /** + * Formats the deadline error message. + * + * @param baseMessage - The base error message + * @return - Formatted error message + */ + private String formatDeadlineError(String baseMessage) { + return String.format("%s\n Usage: deadline description /by DATE" + + "\n DATE can be 'Sunday', 'Mon', 'Tuesday', or 'YYYY-MM-DD'", + baseMessage); + } + + /** + * Handles adding an event task with proper error handling. + * + * @param commandParts - The parsed command parts + * @throws PlopBotException - If event creation or saving fails + */ + private void handleAddEvent(String[] commandParts) throws PlopBotException { + try { + Task newEvent = parser.parseTask(commandParts); + addAndSaveTask(newEvent); + } catch (PlopBotException e) { + throw new PlopBotException(formatEventError(e.getMessage())); + } + } + + /** + * Formats the event error message. + * + * @param baseMessage - The base error message + * @return - Formatted error message + */ + private String formatEventError(String baseMessage) { + return String.format("%s\n Usage: event description /from START_TIME /to END_TIME" + + "\n TIME can be 'Mon 2pm', 'Tuesday 14:00', or 'YYYY-MM-DD HH:MM'", + baseMessage); + } + + /** + * Handles deleting a task. + *t + * @param commandParts - The parsed command parts + * @throws PlopBotException - If the command format is invalid or task deletion fails + */ + private void handleDeleteTask(String[] commandParts) throws PlopBotException { + if (commandParts.length != 2) { + throw new PlopBotException("Invalid delete command. Usage: delete "); + } + try { + int index = parseTaskIndex(commandParts[1]); + if (index < 0 || index >= tasks.size()) { + throw new PlopBotException("Task number " + (index + 1) + " does not exist. Please use 'list' to see all tasks."); + } + Task removedTask = tasks.removeTask(index); + storage.save(tasks.getTasks()); + ui.showTaskRemoved(removedTask, tasks.size()); + } catch (NumberFormatException e) { + throw new PlopBotException("Invalid task number. Please provide a number."); + } + } + + /** + * Handles finding tasks by keyword. + * + * @param commandParts - The parsed command parts + * @throws PlopBotException - If the find command format is invalid + */ + private void handleFindTasks(String[] commandParts) throws PlopBotException { + validateFindCommand(commandParts); + String keyword = commandParts[1]; + ArrayList matchingTasks = tasks.findTasks(keyword); + ui.showMatchingTasks(matchingTasks); + } + + /** + * Validates the find command format. + * + * @param commandParts - The parsed command parts + * @throws PlopBotException - If the command format is invalid + */ + private void validateFindCommand(String[] commandParts) throws PlopBotException { + if (commandParts.length < 2) { + throw new PlopBotException("The 'find' command requires a keyword.\n Usage: find "); + } + } + + /** + * Handles listing all tasks. + */ + private void handleListTasks() { + ui.showTasks(tasks.getTasks()); + } + + /** + * Parses a task index from string input. + * + * @param indexStr - The index string to parse + * @return - The parsed index (0-based) + * @throws PlopBotException - If the index string is not a valid number + */ + private int parseTaskIndex(String indexStr) throws PlopBotException { + try { + return Integer.parseInt(indexStr) - 1; + } catch (NumberFormatException e) { + throw new PlopBotException("Invalid task number. Please provide a number."); + } } + /** + * Handles marking a task as done. + * + * @param commandParts - The parsed command parts containing the task index + * @throws PlopBotException - If the command format is invalid or task marking fails + */ private void handleMarkTask(String[] commandParts) throws PlopBotException { if (commandParts.length != 2) { throw new PlopBotException("Invalid mark command. Usage: mark "); @@ -108,6 +269,12 @@ private void handleMarkTask(String[] commandParts) throws PlopBotException { } } + /** + * Handles unmarking a task (setting it as not done). + * + * @param commandParts - The parsed command parts containing the task index + * @throws PlopBotException - If the command format is invalid or task unmarking fails + */ private void handleUnmarkTask(String[] commandParts) throws PlopBotException { if (commandParts.length != 2) { throw new PlopBotException("Invalid unmark command. Usage: unmark "); @@ -122,4 +289,4 @@ private void handleUnmarkTask(String[] commandParts) throws PlopBotException { throw new PlopBotException("Invalid task number. Please provide a number."); } } -} \ No newline at end of file +} diff --git a/src/main/java/Storage.java b/src/main/java/Storage.java index 9739e243e..ffaf18a4a 100644 --- a/src/main/java/Storage.java +++ b/src/main/java/Storage.java @@ -5,17 +5,35 @@ import java.util.ArrayList; import java.util.List; +/** + * Handles the persistence of task data to and from storage. + * This class manages reading and writing tasks to a file in a specified format. + * Each task is stored as a single line with fields separated by vertical bars (|). + * + * The file format for each task type is as follows: + * - Todo: T | isDone | description + * - Deadline: D | isDone | description | deadline + * - Event: E | isDone | description | startTime | endTime + */ public class Storage { private String filePath; + /** + * Constructs a Storage object with the specified file path. + * + * @param filePath The path where task data will be stored + */ public Storage(String filePath) { this.filePath = filePath; } /** - * Method for loading tasks from a file. - * @return - * @throws PlopBotException + * Loads tasks from the storage file. + * Creates necessary directories if they don't exist. + * If the file exists, reads and parses each line into a Task object. + * + * @return - ArrayList of Task objects loaded from the file + * @throws PlopBotException - If there is an error reading the file or parsing tasks */ public ArrayList load() throws PlopBotException { ArrayList tasks = new ArrayList<>(); @@ -34,9 +52,12 @@ public ArrayList load() throws PlopBotException { } /** - * Method for saving tasks to a file. - * @param tasks - * @throws PlopBotException + * Saves the provided list of tasks to the storage file. + * Creates necessary directories if they don't exist. + * Each task is converted to a string format before being written. + * + * @param tasks - ArrayList of Task objects to be saved + * @throws PlopBotException - If there is an error writing to the file */ public void save(ArrayList tasks) throws PlopBotException { try { @@ -53,10 +74,13 @@ public void save(ArrayList tasks) throws PlopBotException { } /** - * Parser for loading tasks from strings. - * @param line - * @return - * @throws PlopBotException + * Parses a string representation of a task into a Task object. + * The string should be in the format: type | isDone | description [| additional fields...] + * where type is 'T' for Todo, 'D' for Deadline, or 'E' for Event. + * + * @param line - The string representation of the task + * @return - Task object parsed from the string + * @throws PlopBotException - If the string format is invalid or contains invalid data */ private Task parseTaskFromString(String line) throws PlopBotException { String[] parts = line.split("\\|"); @@ -91,9 +115,14 @@ private Task parseTaskFromString(String line) throws PlopBotException { } /** - * Converts tasks to strings for saving. - * @param task - * @return + * Converts a Task object into its string representation for storage. + * The format varies based on the task type: + * - Todo: T | isDone | description + * - Deadline: D | isDone | description | deadline + * - Event: E | isDone | description | startTime | endTime + * + * @param task - The Task object to convert + * @return - String representation of the task */ private String convertTaskToString(Task task) { String baseString = String.format("%s | %d | %s", diff --git a/src/main/java/Task.java b/src/main/java/Task.java index fdbffacaf..c97976525 100644 --- a/src/main/java/Task.java +++ b/src/main/java/Task.java @@ -2,6 +2,12 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +/** + * Represents a task in the PlopBot task management system. + * Tasks can be of three types: TODO, EVENT, or DEADLINE. + * Each task has a name and completion status, with additional + * temporal attributes based on its type. + */ public class Task { private String taskName; private TaskType type; @@ -10,13 +16,21 @@ public class Task { private LocalDateTime startTime; private LocalDateTime endTime; + /** + * Enumeration of possible task types in the system. + * TODO: A basic task with just a description + * EVENT: A task with start and end times + * DEADLINE: A task with a completion deadline + */ public enum TaskType { TODO, EVENT, DEADLINE } /** - * ToDo task constructor - * @param taskName + * Constructs a new TODO task with the specified name. + * The task is initially marked as not done. + * + * @param taskName - The name or description of the task */ public Task(String taskName) { this.taskName = taskName; @@ -25,9 +39,11 @@ public Task(String taskName) { } /** - * Deadline task constructor - * @param taskName - * @param deadline + * Constructs a new DEADLINE task with the specified name and deadline. + * The task is initially marked as not done. + * + * @param taskName - The name or description of the task + * @param deadline - The date by which the task must be completed */ public Task(String taskName, LocalDate deadline) { this.taskName = taskName; @@ -37,10 +53,12 @@ public Task(String taskName, LocalDate deadline) { } /** - * Event task constructor - * @param taskName - * @param startTime - * @param endTime + * Constructs a new EVENT task with the specified name, start time, and end time. + * The task is initially marked as not done. + * + * @param taskName - The name or description of the task + * @param startTime - The date and time when the event starts + * @param endTime - The date and time when the event ends */ public Task(String taskName, LocalDateTime startTime, LocalDateTime endTime) { this.taskName = taskName; @@ -51,8 +69,10 @@ public Task(String taskName, LocalDateTime startTime, LocalDateTime endTime) { } /** - * Getter methods for tasks. - * @return + * Returns the type identifier of the task. + * 'T' for TODO, 'D' for DEADLINE, 'E' for EVENT. + * + * @return - A single character string representing the task type */ public String getTypeIcon() { switch (type) { @@ -63,33 +83,63 @@ public String getTypeIcon() { } } + /** + * Returns the name or description of the task. + * + * @return - The task's name + */ public String getName() { return taskName; } + /** + * Returns the deadline date for DEADLINE tasks. + * + * @return - The task's deadline date, or null if not a DEADLINE task + */ public LocalDate getDeadline() { return deadline; } + /** + * Returns the start time for EVENT tasks. + * + * @return - The event's start time, or null if not an EVENT task + */ public LocalDateTime getStartTime() { return startTime; } + /** + * Returns the end time for EVENT tasks. + * + * @return - The event's end time, or null if not an EVENT task + */ public LocalDateTime getEndTime() { return endTime; } + /** + * Returns the completion status of the task. + * + * @return - true if the task is marked as done, false otherwise + */ public boolean getStatus() { return isDone; } + /** + * Toggles the completion status of the task. + * If the task was marked as done, it will be marked as not done, and vice versa. + */ public void toggleStatus() { this.isDone = !this.isDone; } /** - * Marks specified task as done. - * @throws PlopBotException + * Marks the task as completed. + * + * @throws PlopBotException - If the task is already marked as done */ public void markAsDone() throws PlopBotException { if (isDone) { @@ -99,8 +149,9 @@ public void markAsDone() throws PlopBotException { } /** - * Unmarks specified task as not-done. - * @throws PlopBotException + * Marks the task as not completed. + * + * @throws PlopBotException - If the task is already marked as not done */ public void markAsUndone() throws PlopBotException { if (!isDone) { @@ -110,21 +161,28 @@ public void markAsUndone() throws PlopBotException { } /** - * Converts tasks into strings and returns them. - * @return + * Returns a string representation of the task. + * The format varies based on the task type: + * - TODO: [T][status] description + * - DEADLINE: [D][status] description (by: YYYY-MM-DD) + * - EVENT: [E][status] description (from: YYYY-MM-DD HH:mm to: YYYY-MM-DD HH:mm) + * where status is 'X' for done tasks and ' ' for not done tasks. + * + * @return - A formatted string representation of the task */ @Override public String toString() { String base = String.format("[%s][%s] %s", getTypeIcon(), isDone ? "X" : " ", taskName); switch (type) { - case DEADLINE: - return String.format("%s (by: %s)", base, deadline.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); - case EVENT: - return String.format("%s (from: %s to: %s)", base, - startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), - endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))); - default: - return base; + case DEADLINE: + return String.format("%s (by: %s)", base, + deadline.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + case EVENT: + return String.format("%s (from: %s to: %s)", base, + startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")), + endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))); + default: + return base; } } } diff --git a/src/main/java/TaskList.java b/src/main/java/TaskList.java index 0b701c46d..9fee44253 100644 --- a/src/main/java/TaskList.java +++ b/src/main/java/TaskList.java @@ -1,25 +1,45 @@ import java.util.ArrayList; +/** + * Manages a collection of tasks for the PlopBot application. + * This class provides methods to add, remove, find, and manage tasks in a list. + * It serves as the primary data structure for task management operations. + */ public class TaskList { private ArrayList tasks; + /** + * Constructs an empty TaskList. + * Initializes a new ArrayList to store Task objects. + */ public TaskList() { this.tasks = new ArrayList<>(); } + /** + * Constructs a TaskList with an existing collection of tasks. + * + * @param tasks - The ArrayList of Task objects to initialize the TaskList with + */ public TaskList(ArrayList tasks) { this.tasks = tasks; } + /** + * Adds a new task to the list. + * + * @param task - The Task object to be added to the list + */ public void addTask(Task task) { tasks.add(task); } /** - * Removes the user specified task from the list. - * @param index - * @return - * @throws PlopBotException + * Removes and returns a task at the specified index. + * + * @param index - The index of the task to remove (0-based) + * @return - The removed Task object + * @throws PlopBotException - If the index is out of bounds or invalid */ public Task removeTask(int index) throws PlopBotException { if (index < 0 || index >= tasks.size()) { @@ -29,9 +49,11 @@ public Task removeTask(int index) throws PlopBotException { } /** - * Finds the matching tasks from the user's saved list of tasks. Case insensitive. - * @param keyword - * @return + * Searches for tasks whose names contain the specified keyword. + * The search is case-insensitive. + * + * @param keyword - The search term to match against task names + * @return - An ArrayList containing all tasks whose names contain the keyword */ public ArrayList findTasks(String keyword) { ArrayList matchingTasks = new ArrayList<>(); @@ -43,15 +65,21 @@ public ArrayList findTasks(String keyword) { return matchingTasks; } + /** + * Returns the complete list of tasks. + * + * @return - An ArrayList containing all tasks in the list + */ public ArrayList getTasks() { return tasks; } /** - * Getter method that returns the user specified task. - * @param index - * @return - * @throws PlopBotException + * Retrieves a task at the specified index. + * + * @param index - The index of the task to retrieve (0-based) + * @return - The Task object at the specified index + * @throws PlopBotException - If the index is out of bounds or invalid */ public Task getTask(int index) throws PlopBotException { if (index < 0 || index >= tasks.size()) { @@ -60,7 +88,12 @@ public Task getTask(int index) throws PlopBotException { return tasks.get(index); } + /** + * Returns the total number of tasks in the list. + * + * @return - The number of tasks currently in the list + */ public int size() { return tasks.size(); } -} \ No newline at end of file +} diff --git a/src/main/java/Ui.java b/src/main/java/Ui.java index 606769d73..6eb28d153 100644 --- a/src/main/java/Ui.java +++ b/src/main/java/Ui.java @@ -1,17 +1,26 @@ import java.util.ArrayList; import java.util.Scanner; +/** + * The UI component of PlopBot that handles all user interactions. + * This class is responsible for displaying messages to the user and + * getting user input from the command line interface. + */ public class Ui { private static final String HORIZONTAL_LINE = "//" + "\u2500".repeat(50); private static final String ECHO_LINE = " " + "\u2500".repeat(48); private Scanner scanner; + /** + * Constructs a new UI instance and initializes the Scanner for reading user input. + */ public Ui() { scanner = new Scanner(System.in); } /** - * Welcome message that runs upon program starting. + * Displays the welcome message when PlopBot starts. + * Includes a greeting and prompt for user input. */ public void showWelcome() { System.out.println(HORIZONTAL_LINE); @@ -20,7 +29,8 @@ public void showWelcome() { } /** - * Exit message that runs upon program ending. + * Displays the exit message when PlopBot terminates. + * Shows a farewell message to the user. */ public void showExit() { System.out.println(HORIZONTAL_LINE); @@ -28,16 +38,19 @@ public void showExit() { } /** - * Reads the user's input and cleans it for processing. - * @return + * Reads a command from the user's input. + * + * @return - A String containing the user's command, with leading and trailing whitespace removed */ public String readCommand() { return scanner.nextLine().trim(); } /** - * Following methods are the program's responses to the user's commands. - * @param tasks + * Displays all tasks in the user's task list. + * Each task is numbered and shown with its complete details. + * + * @param tasks - An ArrayList of Task objects to be displayed */ public void showTasks(ArrayList tasks) { System.out.println(ECHO_LINE); @@ -48,6 +61,12 @@ public void showTasks(ArrayList tasks) { System.out.println(ECHO_LINE); } + /** + * Confirms the addition of a new task and shows the updated task count. + * + * @param task - The Task that was added to the list + * @param totalTasks - The current total number of tasks in the list + */ public void showTaskAdded(Task task, int totalTasks) { System.out.println(ECHO_LINE); System.out.println(" Added: " + task); @@ -55,6 +74,12 @@ public void showTaskAdded(Task task, int totalTasks) { System.out.println(ECHO_LINE); } + /** + * Confirms the removal of a task and shows the updated task count. + * + * @param task - The Task that was removed from the list + * @param totalTasks - The current total number of tasks in the list + */ public void showTaskRemoved(Task task, int totalTasks) { System.out.println(ECHO_LINE); System.out.println(" Removed: " + task); @@ -62,6 +87,12 @@ public void showTaskRemoved(Task task, int totalTasks) { System.out.println(ECHO_LINE); } + /** + * Displays tasks that match a search criterion. + * If no tasks match, displays an appropriate message. + * + * @param tasks - An ArrayList of Task objects that match the search criteria + */ public void showMatchingTasks(ArrayList tasks) { System.out.println(ECHO_LINE); if (tasks.isEmpty()) { @@ -76,22 +107,30 @@ public void showMatchingTasks(ArrayList tasks) { } /** - * Following two methods mark and unmark tasks as complete/incomplete. - * @param task + * Displays confirmation that a task has been marked as complete. + * + * @param task - The Task that was marked as complete */ public void showTaskMarked(Task task) { System.out.println(" I've marked this task as done:"); System.out.println(" " + task.toString()); } + /** + * Displays confirmation that a task has been unmarked (set as incomplete). + * + * @param task - The Task that was unmarked + */ public void showTaskUnmarked(Task task) { System.out.println(" I've unmarked this task:"); System.out.println(" " + task.toString()); } /** - * Prints relevant error message. - * @param message + * Displays an error message to the user. + * The message is formatted with proper indentation and line breaks. + * + * @param message - The error message to display */ public void showError(String message) { System.out.println(ECHO_LINE); @@ -100,9 +139,10 @@ public void showError(String message) { } /** - * File loading error message. + * Displays an error message when there's a problem loading the task file. + * Informs the user that the program will start with an empty task list. */ public void showLoadingError() { showError("Problem loading tasks from file. Starting with an empty task list."); } -} \ No newline at end of file +}