+
Note that you can't rely on plot-window previews when you're developing your sticker (they lie).
You must inspect the generated PNG file instead.
From d00a06659f1ab5e345571de6b1cc3aa946cb2af7 Mon Sep 17 00:00:00 2001
From: Matt Dray <18232097+matt-dray@users.noreply.github.com>
Date: Fri, 21 Feb 2025 12:09:18 +0000
Subject: [PATCH 03/12] Simplify arg names, bolster checks, tweak roxygen2
---
R/hexagon.R | 409 +++++++++++++++++++++++------------------
README.md | 36 ++--
man/add_border.Rd | 24 ++-
man/add_hex.Rd | 17 +-
man/add_image.Rd | 40 ++--
man/add_text.Rd | 52 +++---
man/close_device.Rd | 23 ++-
man/hexbase-package.Rd | 4 +-
man/open_device.Rd | 20 +-
9 files changed, 354 insertions(+), 271 deletions(-)
diff --git a/R/hexagon.R b/R/hexagon.R
index 355be68..330721b 100644
--- a/R/hexagon.R
+++ b/R/hexagon.R
@@ -1,11 +1,19 @@
-#' Open a Device
+#' Open a PNG Device with Sticker-Standard Dimensions
#'
#' Begin a PNG plot device with dimensions matching
#' [the Stickers Standard](https://sticker.how/#type-hexagon): 4.39 cm wide by
#' 5.08 cm high (2 by 1.73 inches).
#'
-#' @param file_path Character. Full file path to a .png where the output file
-#' will be saved. The containing directory must already exist.
+#' @param file_path Character. File path to a .png where the output file will be
+#' saved. The containing directory must already exist.
+#'
+#' @details
+#'
+#' ## Order
+#'
+#' When building a hex, this function should be called first, followed by
+#' [add_hex]. You can then use [add_text], [add_image] and [add_border] (if
+#' desired) and finally [close_device].
#'
#' @returns Nothing. A graphics device is opened.
#'
@@ -15,20 +23,23 @@
#' temp_path <- tempfile(fileext = ".png")
#' open_device(temp_path)
#' add_hex()
-#' image_path <- system.file("img", "Rlogo.png", package = "png")
-#' image_png <- png::readPNG(image_path)
-#' add_image(image_png)
+#' img_path <- system.file("img", "Rlogo.png", package = "png")
+#' img_png <- png::readPNG(img_path)
+#' add_image(img_png)
#' add_text()
#' add_border()
#' close_device()
open_device <- function(file_path) {
- if (grepl(file_path, "\\.png$")) {
- stop("Argument 'file_path' must end with '.png'.")
+ if (tools::file_ext(file_path) != "png") {
+ stop("Argument 'file_path' must end with '.png'.", call. = FALSE)
}
if (!dir.exists(dirname(file_path))) {
- stop("Argument 'file_path' must resolve to an existing directory.")
+ stop(
+ "Argument 'file_path' must resolve to an existing directory.",
+ call. = FALSE
+ )
}
grDevices::png(
@@ -46,11 +57,17 @@ open_device <- function(file_path) {
#'
#' Add a hexagon 'canvas' to which elements can be added.
#'
-#' @param bg_col Character. Named R colour or hexadecimal code for the interior
+#' @param col Character. Named R colour or hexadecimal code for the interior
#' background.
#'
#' @details
#'
+#' ## Order
+#'
+#' When building a hex, this function should be called after [open_device]. You can then use
+#' [add_text], [add_image] and [add_border] (if desired) and finally
+#' [close_device].
+#'
#' ## Colours
#'
#' Named colour values must be listed in [grDevices::colours()]. Hexadecimal
@@ -65,16 +82,16 @@ open_device <- function(file_path) {
#' temp_path <- tempfile(fileext = ".png")
#' open_device(temp_path)
#' add_hex()
-#' image_path <- system.file("img", "Rlogo.png", package = "png")
-#' image_png <- png::readPNG(image_path)
-#' add_image(image_png)
+#' img_path <- system.file("img", "Rlogo.png", package = "png")
+#' img_png <- png::readPNG(img_path)
+#' add_image(img_png)
#' add_text()
#' add_border()
#' close_device()
-add_hex <- function(bg_col = "grey") {
+add_hex <- function(col = "grey") {
- if (!(bg_col %in% grDevices::colours() | grepl("^#[0-9A-Fa-f]{6,8}$", bg_col))) {
- stop("Argument 'bg_col' must a named R colour or a hex code.")
+ if (!(col %in% grDevices::colours() | grepl("^#[0-9A-Fa-f]{6,8}$", col))) {
+ stop("Argument 'col' must a named R colour or a hex code.", call. = FALSE)
}
hex_coords_outer <- .get_hex_coords(diameter = 1)
@@ -87,11 +104,11 @@ add_hex <- function(bg_col = "grey") {
hex_grob_outer <- grid::polygonGrob(
hex_coords_outer[["x"]],
hex_coords_outer[["y"]],
- gp = grid::gpar(lwd = 0, fill = bg_col),
+ gp = grid::gpar(lwd = 0, fill = col),
default.units = "native"
)
- # Remove anything outside the outer hex boundary
+ # Remove anything outside the outer hex boundary when popped
grid::pushViewport(
grid::viewport(
xscale = x_scale,
@@ -104,21 +121,35 @@ add_hex <- function(bg_col = "grey") {
}
-#' Add a Border to the Edge of the Hexagon
+#' Add Text to the Hexagon
#'
-#' Add a border of certain thickness and colour to the hexagon. Should
-#' be called after [open_device] and [add_hex], in that order. If desired, make
-#' this the last function before [close_device] so that the border overlays all
-#' other elements.
+#' Overlay text on the hexagon. Call this function separately for each string
+#' you want to add.
#'
-#' @param border_width Numeric. Thickness of the border, expressed as the
-#' inverse ratio of the interior of the hex to the full extent of the hex
-#' (must be between `0` and `1`).
-#' @param border_col Character. Named R colour or hexadecimal code for the
-#' border around the hex.
+#' @param string Character. Text to display. `NULL` (or an empty string) if
+#' you don't want to place text.
+#' @param x Numeric. Text location on the hexagon's x-axis.
+#' @param y Numeric. Text location on the hexagon's y-axis.
+#' @param angle Numeric. Rotation of text string in degrees. Positive
+#' values will rotate anticlockwise by the given angle.
+#' @param size Numeric. Text point-size.
+#' @param col Character. Text colour. A named R colour or hexadecimal code.
+#' @param family Character. Name of a font family available on your system.
+#' @param face Character. Font face for the text.
#'
#' @details
#'
+#' ## Order
+#'
+#' When building a hex, this function should be called after [open_device] and
+#' [add_hex]. You can then use further calls to [add_text], [add_image] and
+#' [add_border] (if desired) and finally [close_device].
+#'
+#' ## Coordinates
+#'
+#' Coordinates should be provided within the x- and y-axis ranges, which are
+#' both from 0 to 1, giving the centre as x = 0.5 and y = 0.5.
+#'
#' ## Colours
#'
#' Named colour values must be listed in [grDevices::colours()]. Hexadecimal
@@ -133,89 +164,102 @@ add_hex <- function(bg_col = "grey") {
#' temp_path <- tempfile(fileext = ".png")
#' open_device(temp_path)
#' add_hex()
-#' image_path <- system.file("img", "Rlogo.png", package = "png")
-#' image_png <- png::readPNG(image_path)
-#' add_image(image_png)
+#' img_path <- system.file("img", "Rlogo.png", package = "png")
+#' img_png <- png::readPNG(img_path)
+#' add_image(img_png)
#' add_text()
#' add_border()
#' close_device()
-add_border <- function(
- border_width = 0.05,
- border_col = "black"
+add_text <- function(
+ string = "example",
+ x = 0.5,
+ y = 0.4,
+ angle = 0,
+ size = 20,
+ col = "black",
+ family = "sans",
+ face = c("plain", "bold", "italic", "bold.italic")
) {
- if (!inherits(border_width, "numeric") || border_width >= 1) {
- stop("Argument 'border_width' must be a numeric value below 1.")
+ if (!inherits(string, "character")) {
+ stop("Argument 'string' must be a character string.", call. = FALSE)
}
- if (!(border_col %in% grDevices::colours() | grepl("^#[0-9A-Fa-f]{6,8}$", border_col))) {
- stop("Argument 'border_col' must a named R colour or a hex code.")
+ if (!inherits(x, "numeric") | !inherits(y, "numeric")) {
+ stop("Arguments 'x' and 'y' must be numeric values.", call. = FALSE)
}
- hex_diameter_inner <- 1 - border_width
+ if (!inherits(angle, "numeric")) {
+ stop("Argument 'angle' must be a numeric value.", call. = FALSE)
+ }
- hex_coords_outer <- .get_hex_coords(diameter = 1)
- hex_coords_inner <- .get_hex_coords(diameter = hex_diameter_inner)
+ if (!inherits(size, "numeric")) {
+ stop("Argument 'size' must be a numeric value.", call. = FALSE)
+ }
- x_scale <- c(min(hex_coords_outer[["x"]]), max(hex_coords_outer[["x"]]))
- y_scale <- c(min(hex_coords_outer[["y"]]), max(hex_coords_outer[["y"]]))
+ if (!(col %in% grDevices::colours() | grepl("^#[0-9A-Fa-f]{6,8}$", col))) {
+ stop("Argument 'col' must a named R colour or a hex code.", call. = FALSE)
+ }
- grid::pushViewport(grid::viewport(xscale = x_scale, yscale = y_scale))
+ if (!inherits(family, "character")) {
+ stop(
+ "Argument 'family' must be a character string
+ representing an available font family.",
+ call. = FALSE
+ )
+ }
- hex_grob_outer <- grid::polygonGrob(
- hex_coords_outer[["x"]],
- hex_coords_outer[["y"]],
- default.units = "native"
- )
+ if (!inherits(face, "character")) {
+ stop(
+ "Argument 'face' must be one of the following character strings:
+ 'plain', 'bold', 'italic', 'bold.italic'.",
+ call. = FALSE
+ )
+ }
- # Inner hexagon will be clipped from outer, leaving a border polygon
- hex_grob_inner <- grid::polygonGrob(
- hex_coords_inner[["x"]],
- hex_coords_inner[["y"]],
- default.units = "native"
- )
+ face <- match.arg(face)
- border_grob <- gridGeometry::polyclipGrob(
- A = hex_grob_outer,
- B = hex_grob_inner,
- op = "minus", # removes inner from outer
- gp = grid::gpar(lwd = 0, fill = border_col)
+ grid::pushViewport(grid::viewport(x = x, y = y))
+ grid::grid.text(
+ string,
+ gp = grid::gpar(
+ col = col,
+ fontsize = size,
+ fontfamily = family,
+ fontface = face
+ ),
+ vp = grid::viewport(angle = angle)
)
+ grid::popViewport()
- grid::grid.draw(border_grob)
}
-#' Add Text
+#' Add an Image to the Hexagon
#'
-#' Overlay text on the hexagon. Text outside the hexagon will be clipped. Should
-#' be called after [open_device] and [add_hex], in that order. Call this
-#' function separately for each text item that you want to add.
+#' Overlay an image on the hexagon. Call this function separately for each image
+#' you want to add.
#'
-#' @param text_string Character. Text to display. `NULL` (or an empty string) if
-#' you don't want to place text.
-#' @param text_x Numeric. Text location x-axis.
-#' @param text_y Numeric. Text location y-axis.
-#' @param text_angle Numeric. Rotation of text string in degrees. Positive
-#' values will rotate anticlockwise by the given angle.
-#' @param text_size Numeric. Text point-size.
-#' @param text_col Character. Text colour. A named R colour or hexadecimal code.
-#' @param text_family Character. Name of a font family available on your system.
-#' @param text_face Character. Font face for the text.
+#' @param object Array. A PNG or JPEG file read in by the user, most likely
+#' using packages 'png' or 'jpeg'.
+#' @param x Numeric. Image location on the hexagon's x-axis.
+#' @param y Numeric. Image location on the hexagon's y-axis.
+#' @param angle Numeric. Text rotation in degrees.
+#' @param width Numeric. Image width.
#'
#' @details
#'
+#' ## Order
+#'
+#' When building a hex, this function should be called after [open_device] and
+#' [add_hex]. You can then use further calls to [add_image], [add_text] and
+#' [add_border] (if desired) and finally [close_device].
+#'
#' ## Coordinates
#'
#' Coordinates should be provided within the x- and y-axis ranges, which are
#' both from 0 to 1, giving the centre as x = 0.5 and y = 0.5.
#'
-#' ## Colours
-#'
-#' Named colour values must be listed in [grDevices::colours()]. Hexadecimal
-#' colour values must be provided with length 6 or 8 and must begin with an
-#' octothorpe (`#`).
-#'
#' @returns `NULL`. Adds to an existing graphics device.
#'
#' @export
@@ -224,94 +268,76 @@ add_border <- function(
#' temp_path <- tempfile(fileext = ".png")
#' open_device(temp_path)
#' add_hex()
-#' image_path <- system.file("img", "Rlogo.png", package = "png")
-#' image_png <- png::readPNG(image_path)
-#' add_image(image_png)
+#' img_path <- system.file("img", "Rlogo.png", package = "png")
+#' img_png <- png::readPNG(img_path)
+#' add_image(img_png)
#' add_text()
#' add_border()
#' close_device()
-add_text <- function(
- text_string = "example",
- text_x = 0.5,
- text_y = 0.4,
- text_angle = 0,
- text_size = 20,
- text_col = "black",
- text_family = "sans",
- text_face = c("plain", "bold", "italic", "bold.italic")
+add_image <- function(
+ img,
+ x = 0.5,
+ y = 0.7,
+ angle = 0,
+ width = 0.4
) {
- if (!inherits(text_string, "character")) {
- stop("Argument 'text_string' must be a character string.")
- }
-
- if (!inherits(text_x, "numeric") | !inherits(text_y, "numeric")) {
- stop("Arguments 'text_x' and 'text_y' must be numeric values.")
- }
-
- if (!inherits(text_size, "numeric")) {
- stop("Argument 'text_size' must be a numeric value.")
+ if (!inherits(img, "array")) {
+ stop(
+ "Argument 'img' must be an array (a png or jpeg file).",
+ call. = FALSE)
}
- if (!inherits(text_size, "numeric")) {
- stop("Argument 'text_size' must be a numeric value.")
+ if (!inherits(x, "numeric") | !inherits(y, "numeric")) {
+ stop("Arguments 'x' and 'y' must be numeric values.", call. = FALSE)
}
- if (!(text_col %in% grDevices::colours() | grepl("^#[0-9A-Fa-f]{6,8}$", text_col))) {
- stop("Argument 'text_col' must a named R colour or a hex code.")
+ if (!inherits(angle, "numeric")) {
+ stop("Argument 'angle' must be a numeric value.", call. = FALSE)
}
- if (!inherits(text_family, "character")) {
- stop(
- "Argument 'text_family' must be a character string
- representing an available font family."
- )
+ if (!inherits(width, "numeric")) {
+ stop("Argument 'width' must be a numeric value.", call. = FALSE)
}
- if (!inherits(text_face, "character")) {
- stop(
- "Argument 'text_face' must be one of the following character strings:
- 'plain', 'bold', 'italic', 'bold-italic'."
+ grid::pushViewport(
+ grid::viewport(
+ x = x,
+ y = y,
+ width = width
)
- }
-
- text_face <- match.arg(text_face)
-
- grid::pushViewport(grid::viewport(x = text_x, y = text_y))
- grid::grid.text(
- text_string,
- gp = grid::gpar(
- col = text_col,
- fontsize = text_size,
- fontfamily = text_family,
- fontface = text_face
- ),
- vp = grid::viewport(angle = text_angle)
+ )
+ grid::grid.raster(
+ img,
+ vp = grid::viewport(angle = angle)
)
grid::popViewport()
-
}
-#' Add an Image
+#' Add a Border to the Edge of the Hexagon
#'
-#' Overlay an image on the hexagon. Image content outside the hexagon will be
-#' clipped. Should be called after [open_device] and [add_hex], in that order.
-#' Call this function separately for each image you want to add.
+#' Add a border of given thickness and colour to the inner edges of hexagon.
#'
-#' @param image_object Array. A PNG file read in by the user. `NULL` for no
-#' image.
-#' @param image_x Numeric. Image location x-axis.
-#' @param image_y Numeric. Image location y-axis.
-#' @param image_angle Numeric. Text rotation in degrees.
-#' @param image_width Numeric. Image width.
+#' @param width Numeric. Thickness of the border, expressed as the
+#' inverse ratio of the interior of the hex to the full extent of the hex
+#' (must be between `0` and `1`).
+#' @param col Character. Named R colour or hexadecimal code for the
+#' border around the hex.
#'
#' @details
#'
-#' ## Coordinates
+#' ## Order
#'
-#' Coordinates should be provided within the x- and y-axis ranges, which are
-#' both from 0 to 1, giving the centre as x = 0.5 and y = 0.5.
+#' When building a hex, this function should be called after [open_device],
+#' [add_hex] and any calls to [add_text] and [add_image] (in that order), and
+#' before [close_device].
+#'
+#' ## Colours
+#'
+#' Named colour values must be listed in [grDevices::colours()]. Hexadecimal
+#' colour values must be provided with length 6 or 8 and must begin with an
+#' octothorpe (`#`).
#'
#' @returns `NULL`. Adds to an existing graphics device.
#'
@@ -321,53 +347,75 @@ add_text <- function(
#' temp_path <- tempfile(fileext = ".png")
#' open_device(temp_path)
#' add_hex()
-#' image_path <- system.file("img", "Rlogo.png", package = "png")
-#' image_png <- png::readPNG(image_path)
-#' add_image(image_png)
+#' img_path <- system.file("img", "Rlogo.png", package = "png")
+#' img_png <- png::readPNG(img_path)
+#' add_image(img_png)
#' add_text()
#' add_border()
#' close_device()
-add_image <- function(
- image_object,
- image_x = 0.5,
- image_y = 0.7,
- image_angle = 0,
- image_width = 0.4
+add_border <- function(
+ width = 0.05,
+ col = "black"
) {
- if (!inherits(image_x, "numeric") | !inherits(image_y, "numeric")) {
- stop("Arguments 'image_x' and 'image_y' must be numeric values.")
+ if (!inherits(width, "numeric") || width >= 1) {
+ stop("Argument 'width' must be a numeric value below 1.", call. = FALSE)
}
- if (!inherits(image_angle, "numeric")) {
- stop("Argument 'image_angle' must be a numeric value.")
+ if (!(col %in% grDevices::colours() | grepl("^#[0-9A-Fa-f]{6,8}$", col))) {
+ stop("Argument 'col' must a named R colour or a hex code.", call. = FALSE)
}
- if (!inherits(image_width, "numeric")) {
- stop("Argument 'image_width' must be a numeric value.")
- }
+ hex_diameter_inner <- 1 - width
- grid::pushViewport(
- grid::viewport(
- x = image_x,
- y = image_y,
- width = image_width
- )
+ hex_coords_outer <- .get_hex_coords(diameter = 1)
+ hex_coords_inner <- .get_hex_coords(diameter = hex_diameter_inner)
+
+ x_scale <- c(min(hex_coords_outer[["x"]]), max(hex_coords_outer[["x"]]))
+ y_scale <- c(min(hex_coords_outer[["y"]]), max(hex_coords_outer[["y"]]))
+
+ grid::pushViewport(grid::viewport(xscale = x_scale, yscale = y_scale))
+
+ hex_grob_outer <- grid::polygonGrob(
+ hex_coords_outer[["x"]],
+ hex_coords_outer[["y"]],
+ default.units = "native"
)
- grid::grid.raster(
- image_object,
- vp = grid::viewport(angle = image_angle)
+
+ # Inner hexagon will be clipped from outer, leaving a border polygon
+ hex_grob_inner <- grid::polygonGrob(
+ hex_coords_inner[["x"]],
+ hex_coords_inner[["y"]],
+ default.units = "native"
+ )
+
+ border_grob <- gridGeometry::polyclipGrob(
+ A = hex_grob_outer,
+ B = hex_grob_inner,
+ op = "minus", # removes inner from outer
+ gp = grid::gpar(lwd = 0, fill = col)
)
- grid::popViewport()
+
+ grid::grid.draw(border_grob)
}
-#' Close the Device
+
+#' Close the Device and Write to File
#'
-#' Clip to the area of the outer hexagon and close the PNG plot device, which
-#' writes to the `file_path` specified in [open_device].
+#' Clip to the area of the outer hexagon and shut down the PNG plot device,
+#' which writes to the `file_path` specified in [open_device].
#'
-#' @returns `NULL`. Adds to an existing graphics device.
+#' @details
+#'
+#' ## Order
+#'
+#' When building a hex, this function should be called at the end, after
+#' [open_device], [add_hex] and any calls to [add_text], [add_image] and
+#' [add_border].
+#'
+#' @returns Named numeric. The device name and number where the hex has been
+#' written.
#'
#' @export
#'
@@ -375,21 +423,22 @@ add_image <- function(
#' temp_path <- tempfile(fileext = ".png")
#' open_device(temp_path)
#' add_hex()
-#' image_path <- system.file("img", "Rlogo.png", package = "png")
-#' image_png <- png::readPNG(image_path)
-#' add_image(image_png)
+#' img_path <- system.file("img", "Rlogo.png", package = "png")
+#' img_png <- png::readPNG(img_path)
+#' add_image(img_png)
#' add_text()
#' add_border()
#' close_device()
close_device <- function() {
- grid::popViewport(0) # clip to outer hexagon
+ grid::popViewport(0) # close all open viewports, clip to outer hexagon
grDevices::dev.off()
}
#' Get Coordinates of Hexagon Vertices
#' @param diameter Numeric.
-#' @returns A list of two numeric vectors that represent points of a hexagon.
-#' The elements are named 'x' and 'y'.
+#' @returns A list of two numeric vectors that represent vertex coordinates of a
+#' regular hexagon (point-side down) within a unit-1 square. The elements
+#' are named 'x' and 'y'.
#' @noRd
.get_hex_coords <- function(diameter = 1) {
diff --git a/README.md b/README.md
index 598cab3..cdc5e2e 100644
--- a/README.md
+++ b/README.md
@@ -55,30 +55,30 @@ temp_path <- tempfile(fileext = ".png")
# Build up and write the sticker
hexbase::open_device(file_path = temp_path)
-hexbase::add_hex(bg_col = "#BEBEBE")
+hexbase::add_hex(col = "#BEBEBE")
hexbase::add_image(
- image_object = image_png,
- image_y = 0.6,
- image_angle = 20,
- image_width = 0.5
+ img = image_png,
+ y = 0.6,
+ angle = 20,
+ width = 0.5
)
hexbase::add_text(
- text_string = "example",
- text_y = 0.35,
- text_col = "red",
- text_family = "mono",
- text_face = "bold.italic"
+ string = "example",
+ y = 0.35,
+ col = "red",
+ family = "mono",
+ face = "bold.italic"
)
hexbase::add_text(
- text_string = "visit https://rstats.lol/ ftw",
- text_x = 0.73,
- text_y = 0.17,
- text_angle = 30,
- text_size = 6,
- text_col = "blue",
- text_family = "serif"
+ string = "visit https://rstats.lol/ ftw",
+ x = 0.73,
+ y = 0.17,
+ angle = 30,
+ size = 6,
+ col = "blue",
+ family = "serif"
)
-hexbase::add_border(border_col = "grey20")
+hexbase::add_border(col = "grey20")
hexbase::close_device()
# Optionally, open the image for inspection
diff --git a/man/add_border.Rd b/man/add_border.Rd
index f3859e5..bfc284e 100644
--- a/man/add_border.Rd
+++ b/man/add_border.Rd
@@ -4,26 +4,30 @@
\alias{add_border}
\title{Add a Border to the Edge of the Hexagon}
\usage{
-add_border(border_width = 0.05, border_col = "black")
+add_border(width = 0.05, col = "black")
}
\arguments{
-\item{border_width}{Numeric. Thickness of the border, expressed as the
+\item{width}{Numeric. Thickness of the border, expressed as the
inverse ratio of the interior of the hex to the full extent of the hex
(must be between \code{0} and \code{1}).}
-\item{border_col}{Character. Named R colour or hexadecimal code for the
+\item{col}{Character. Named R colour or hexadecimal code for the
border around the hex.}
}
\value{
\code{NULL}. Adds to an existing graphics device.
}
\description{
-Add a border of certain thickness and colour to the hexagon. Should
-be called after \link{open_device} and \link{add_hex}, in that order. If desired, make
-this the last function before \link{close_device} so that the border overlays all
-other elements.
+Add a border of given thickness and colour to the inner edges of hexagon.
}
\details{
+\subsection{Order}{
+
+When building a hex, this function should be called after \link{open_device},
+\link{add_hex} and any calls to \link{add_text} and \link{add_image} (in that order), and
+before \link{close_device}.
+}
+
\subsection{Colours}{
Named colour values must be listed in \code{\link[grDevices:colors]{grDevices::colours()}}. Hexadecimal
@@ -35,9 +39,9 @@ octothorpe (\verb{#}).
temp_path <- tempfile(fileext = ".png")
open_device(temp_path)
add_hex()
-image_path <- system.file("img", "Rlogo.png", package = "png")
-image_png <- png::readPNG(image_path)
-add_image(image_png)
+img_path <- system.file("img", "Rlogo.png", package = "png")
+img_png <- png::readPNG(img_path)
+add_image(img_png)
add_text()
add_border()
close_device()
diff --git a/man/add_hex.Rd b/man/add_hex.Rd
index b22dc1b..e3a864f 100644
--- a/man/add_hex.Rd
+++ b/man/add_hex.Rd
@@ -4,10 +4,10 @@
\alias{add_hex}
\title{Add a Hexagon}
\usage{
-add_hex(bg_col = "grey")
+add_hex(col = "grey")
}
\arguments{
-\item{bg_col}{Character. Named R colour or hexadecimal code for the interior
+\item{col}{Character. Named R colour or hexadecimal code for the interior
background.}
}
\value{
@@ -17,6 +17,13 @@ background.}
Add a hexagon 'canvas' to which elements can be added.
}
\details{
+\subsection{Order}{
+
+When building a hex, this function should be called after \link{open_device}. You can then use
+\link{add_text}, \link{add_image} and \link{add_border} (if desired) and finally
+\link{close_device}.
+}
+
\subsection{Colours}{
Named colour values must be listed in \code{\link[grDevices:colors]{grDevices::colours()}}. Hexadecimal
@@ -28,9 +35,9 @@ octothorpe (\verb{#}).
temp_path <- tempfile(fileext = ".png")
open_device(temp_path)
add_hex()
-image_path <- system.file("img", "Rlogo.png", package = "png")
-image_png <- png::readPNG(image_path)
-add_image(image_png)
+img_path <- system.file("img", "Rlogo.png", package = "png")
+img_png <- png::readPNG(img_path)
+add_image(img_png)
add_text()
add_border()
close_device()
diff --git a/man/add_image.Rd b/man/add_image.Rd
index 3760b49..66c327b 100644
--- a/man/add_image.Rd
+++ b/man/add_image.Rd
@@ -2,37 +2,37 @@
% Please edit documentation in R/hexagon.R
\name{add_image}
\alias{add_image}
-\title{Add an Image}
+\title{Add an Image to the Hexagon}
\usage{
-add_image(
- image_object,
- image_x = 0.5,
- image_y = 0.7,
- image_angle = 0,
- image_width = 0.4
-)
+add_image(img, x = 0.5, y = 0.7, angle = 0, width = 0.4)
}
\arguments{
-\item{image_object}{Array. A PNG file read in by the user. \code{NULL} for no
-image.}
+\item{x}{Numeric. Image location on the hexagon's x-axis.}
-\item{image_x}{Numeric. Image location x-axis.}
+\item{y}{Numeric. Image location on the hexagon's y-axis.}
-\item{image_y}{Numeric. Image location y-axis.}
+\item{angle}{Numeric. Text rotation in degrees.}
-\item{image_angle}{Numeric. Text rotation in degrees.}
+\item{width}{Numeric. Image width.}
-\item{image_width}{Numeric. Image width.}
+\item{object}{Array. A PNG or JPEG file read in by the user, most likely
+using packages 'png' or 'jpeg'.}
}
\value{
\code{NULL}. Adds to an existing graphics device.
}
\description{
-Overlay an image on the hexagon. Image content outside the hexagon will be
-clipped. Should be called after \link{open_device} and \link{add_hex}, in that order.
-Call this function separately for each image you want to add.
+Overlay an image on the hexagon. Call this function separately for each image
+you want to add.
}
\details{
+\subsection{Order}{
+
+When building a hex, this function should be called after \link{open_device} and
+\link{add_hex}. You can then use further calls to \link{add_image}, \link{add_text} and
+\link{add_border} (if desired) and finally \link{close_device}.
+}
+
\subsection{Coordinates}{
Coordinates should be provided within the x- and y-axis ranges, which are
@@ -43,9 +43,9 @@ both from 0 to 1, giving the centre as x = 0.5 and y = 0.5.
temp_path <- tempfile(fileext = ".png")
open_device(temp_path)
add_hex()
-image_path <- system.file("img", "Rlogo.png", package = "png")
-image_png <- png::readPNG(image_path)
-add_image(image_png)
+img_path <- system.file("img", "Rlogo.png", package = "png")
+img_png <- png::readPNG(img_path)
+add_image(img_png)
add_text()
add_border()
close_device()
diff --git a/man/add_text.Rd b/man/add_text.Rd
index 326a409..8ea0b5b 100644
--- a/man/add_text.Rd
+++ b/man/add_text.Rd
@@ -2,47 +2,53 @@
% Please edit documentation in R/hexagon.R
\name{add_text}
\alias{add_text}
-\title{Add Text}
+\title{Add Text to the Hexagon}
\usage{
add_text(
- text_string = "example",
- text_x = 0.5,
- text_y = 0.4,
- text_angle = 0,
- text_size = 20,
- text_col = "black",
- text_family = "sans",
- text_face = c("plain", "bold", "italic", "bold.italic")
+ string = "example",
+ x = 0.5,
+ y = 0.4,
+ angle = 0,
+ size = 20,
+ col = "black",
+ family = "sans",
+ face = c("plain", "bold", "italic", "bold.italic")
)
}
\arguments{
-\item{text_string}{Character. Text to display. \code{NULL} (or an empty string) if
+\item{string}{Character. Text to display. \code{NULL} (or an empty string) if
you don't want to place text.}
-\item{text_x}{Numeric. Text location x-axis.}
+\item{x}{Numeric. Text location on the hexagon's x-axis.}
-\item{text_y}{Numeric. Text location y-axis.}
+\item{y}{Numeric. Text location on the hexagon's y-axis.}
-\item{text_angle}{Numeric. Rotation of text string in degrees. Positive
+\item{angle}{Numeric. Rotation of text string in degrees. Positive
values will rotate anticlockwise by the given angle.}
-\item{text_size}{Numeric. Text point-size.}
+\item{size}{Numeric. Text point-size.}
-\item{text_col}{Character. Text colour. A named R colour or hexadecimal code.}
+\item{col}{Character. Text colour. A named R colour or hexadecimal code.}
-\item{text_family}{Character. Name of a font family available on your system.}
+\item{family}{Character. Name of a font family available on your system.}
-\item{text_face}{Character. Font face for the text.}
+\item{face}{Character. Font face for the text.}
}
\value{
\code{NULL}. Adds to an existing graphics device.
}
\description{
-Overlay text on the hexagon. Text outside the hexagon will be clipped. Should
-be called after \link{open_device} and \link{add_hex}, in that order. Call this
-function separately for each text item that you want to add.
+Overlay text on the hexagon. Call this function separately for each string
+you want to add.
}
\details{
+\subsection{Order}{
+
+When building a hex, this function should be called after \link{open_device} and
+\link{add_hex}. You can then use further calls to \link{add_text}, \link{add_image} and
+\link{add_border} (if desired) and finally \link{close_device}.
+}
+
\subsection{Coordinates}{
Coordinates should be provided within the x- and y-axis ranges, which are
@@ -60,9 +66,9 @@ octothorpe (\verb{#}).
temp_path <- tempfile(fileext = ".png")
open_device(temp_path)
add_hex()
-image_path <- system.file("img", "Rlogo.png", package = "png")
-image_png <- png::readPNG(image_path)
-add_image(image_png)
+img_path <- system.file("img", "Rlogo.png", package = "png")
+img_png <- png::readPNG(img_path)
+add_image(img_png)
add_text()
add_border()
close_device()
diff --git a/man/close_device.Rd b/man/close_device.Rd
index c220669..a40aa98 100644
--- a/man/close_device.Rd
+++ b/man/close_device.Rd
@@ -2,24 +2,33 @@
% Please edit documentation in R/hexagon.R
\name{close_device}
\alias{close_device}
-\title{Close the Device}
+\title{Close the Device and Write to File}
\usage{
close_device()
}
\value{
-\code{NULL}. Adds to an existing graphics device.
+Named numeric. The device name and number where the hex has been
+written.
}
\description{
-Clip to the area of the outer hexagon and close the PNG plot device, which
-writes to the \code{file_path} specified in \link{open_device}.
+Clip to the area of the outer hexagon and shut down the PNG plot device,
+which writes to the \code{file_path} specified in \link{open_device}.
+}
+\details{
+\subsection{Order}{
+
+When building a hex, this function should be called at the end, after
+\link{open_device}, \link{add_hex} and any calls to \link{add_text}, \link{add_image} and
+\link{add_border}.
+}
}
\examples{
temp_path <- tempfile(fileext = ".png")
open_device(temp_path)
add_hex()
-image_path <- system.file("img", "Rlogo.png", package = "png")
-image_png <- png::readPNG(image_path)
-add_image(image_png)
+img_path <- system.file("img", "Rlogo.png", package = "png")
+img_png <- png::readPNG(img_path)
+add_image(img_png)
add_text()
add_border()
close_device()
diff --git a/man/hexbase-package.Rd b/man/hexbase-package.Rd
index 5372f0a..48bff3f 100644
--- a/man/hexbase-package.Rd
+++ b/man/hexbase-package.Rd
@@ -4,11 +4,11 @@
\name{hexbase-package}
\alias{hexbase}
\alias{hexbase-package}
-\title{hexbase: Create Simple Dependency-Free Hex Logos}
+\title{hexbase: A Dependency-Light Hex-Logo Builder}
\description{
\if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}}
-A simple interface to create hexagon-shaped logos that help promote your R package or other projects. Uses base R only.
+A simple interface to create hexagon-shaped logos that help promote your R package or other projects. Uses the 'grid' system.
}
\seealso{
Useful links:
diff --git a/man/open_device.Rd b/man/open_device.Rd
index 84c028f..f745c20 100644
--- a/man/open_device.Rd
+++ b/man/open_device.Rd
@@ -2,13 +2,13 @@
% Please edit documentation in R/hexagon.R
\name{open_device}
\alias{open_device}
-\title{Open a Device}
+\title{Open a PNG Device with Sticker-Standard Dimensions}
\usage{
open_device(file_path)
}
\arguments{
-\item{file_path}{Character. Full file path to a .png where the output file
-will be saved. The containing directory must already exist.}
+\item{file_path}{Character. File path to a .png where the output file will be
+saved. The containing directory must already exist.}
}
\value{
Nothing. A graphics device is opened.
@@ -18,13 +18,21 @@ Begin a PNG plot device with dimensions matching
\href{https://sticker.how/#type-hexagon}{the Stickers Standard}: 4.39 cm wide by
5.08 cm high (2 by 1.73 inches).
}
+\details{
+\subsection{Order}{
+
+When building a hex, this function should be called first, followed by
+\link{add_hex}. You can then use \link{add_text}, \link{add_image} and \link{add_border} (if
+desired) and finally \link{close_device}.
+}
+}
\examples{
temp_path <- tempfile(fileext = ".png")
open_device(temp_path)
add_hex()
-image_path <- system.file("img", "Rlogo.png", package = "png")
-image_png <- png::readPNG(image_path)
-add_image(image_png)
+img_path <- system.file("img", "Rlogo.png", package = "png")
+img_png <- png::readPNG(img_path)
+add_image(img_png)
add_text()
add_border()
close_device()
From c6b750717fa15d67841608826b79cb23ce445f69 Mon Sep 17 00:00:00 2001
From: Matt Dray <18232097+matt-dray@users.noreply.github.com>
Date: Fri, 21 Feb 2025 15:13:03 +0000
Subject: [PATCH 04/12] Add basic tests
---
R/hexagon.R | 17 ++--
inst/images/hexagon.png | Bin 169206 -> 0 bytes
inst/tinytest/test_hexbase.R | 153 +++++++++++++++++++++++++++++++++++
man/add_image.Rd | 6 +-
tests/tinytest.R | 5 ++
5 files changed, 171 insertions(+), 10 deletions(-)
delete mode 100644 inst/images/hexagon.png
create mode 100644 inst/tinytest/test_hexbase.R
create mode 100644 tests/tinytest.R
diff --git a/R/hexagon.R b/R/hexagon.R
index 330721b..6868f0c 100644
--- a/R/hexagon.R
+++ b/R/hexagon.R
@@ -203,16 +203,16 @@ add_text <- function(
if (!inherits(family, "character")) {
stop(
- "Argument 'family' must be a character string
- representing an available font family.",
+ "Argument 'family' must be a character string representing ",
+ "an available font family.",
call. = FALSE
)
}
if (!inherits(face, "character")) {
stop(
- "Argument 'face' must be one of the following character strings:
- 'plain', 'bold', 'italic', 'bold.italic'.",
+ "Argument 'face' must be one of the following character strings: ",
+ "'plain', 'bold', 'italic', 'bold.italic'.",
call. = FALSE
)
}
@@ -240,7 +240,7 @@ add_text <- function(
#' Overlay an image on the hexagon. Call this function separately for each image
#' you want to add.
#'
-#' @param object Array. A PNG or JPEG file read in by the user, most likely
+#' @param img Array. A PNG or JPEG file read in by the user, most likely
#' using packages 'png' or 'jpeg'.
#' @param x Numeric. Image location on the hexagon's x-axis.
#' @param y Numeric. Image location on the hexagon's y-axis.
@@ -358,8 +358,11 @@ add_border <- function(
col = "black"
) {
- if (!inherits(width, "numeric") || width >= 1) {
- stop("Argument 'width' must be a numeric value below 1.", call. = FALSE)
+ if (!inherits(width, "numeric") || (width < 0 | width > 1)) {
+ stop(
+ "Argument 'width' must be a numeric value between 0 and 1.",
+ call. = FALSE
+ )
}
if (!(col %in% grDevices::colours() | grepl("^#[0-9A-Fa-f]{6,8}$", col))) {
diff --git a/inst/images/hexagon.png b/inst/images/hexagon.png
deleted file mode 100644
index d35ef8065e06a1500edcbc98f6026c067a457f37..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 169206
zcmZr(1z42Z*2Zx_QbG_VM-V9q1tcWKL^>6v6_k|jbVS6Uj;Nqg23?AxbSR;M(x7ys
zfP{#kBK+%{VPMAlpXa&H<;={tzr9zlcdfP0bzN;$23ihU3JMAawG&76DJZBpC@5$w
zsj1+Tz3H{96cp&Kc1MorIvi0w;_T?`X5ezx>b&ZCr}J)hR{E-kDJbM3B2Jpwa-C#V
zeo*vWboa-Qw$#*HTUTUmzi`xVzS8w&<^1!DJ$qvuUmS{lH`SY9w?}Gt`H7vdGV39x
zs`P=FfmM?#!Cm%Q-)@RMdZGIW9U`>U^jq~|3p>qZk<{~&m_moL`kr57@iRf=eeTnJ
zbkT)-P44yyp!Ra)ijB4oJ+B+9u2%4<3T$EsY!VZVyd!dF&nC{{*A-pgW-zsVoj+eK
zuG&oI>Z)$K)*JdMTkbvI(0Pg#wwipiOXx#?-qLiQTcW8x=opU~VtG?QVYDNWaW=p4
z-hcbASsdFdlo`HK-F3f}!QD)W4Ro=)W}!AX!!^2tfKmc?74dvfSV9TvE%f z3YJ|lH#2cVt~Di^UO=YpK9rL+)V}|(*!s^e2r`v$8V-4yL%Ln@ztNL5QuVl*l}Zwd zJ!dWa{$4bxz-91t*6_3OkKgrP5!3t&yP?!+0y#^OLuYPT;Bm&n>+1%K0&G7-h+bb= zH=1z=GAIvR?DA-XY)qxQt?fr`T6s6IBM5?!fFMXPCl5G&aw!yfypyC)64wU7<`XA1 z){Ukw%N0~vS(z9#Th55zQRhuqwLMPsj}7KP^(A~4-B5)_tpS#=0Y?0YcB1{qBzHGBAK5qo} zhFSnA=tO~W=Gpwx=%Vgsq6%kXfJ&UujQiup!enQ^S?D1B2FovDL%?!ylv1Q_q;--O zh)Em+2Uc<2^+1F7-bNz9?S;YEH-?9Ym$tjo78o8lu)ZT)7a+-PeV^zzj%Cls6nw~H z;>#Bw6R!W7jACf$iAfG%$;4*5y5L4bd+E@#8|Pq3e+U#}aAnwN6G%RV+Prylql2B@ zFYWMP)0{C$C}ZGXlzbO)s$~SH8deZr_i5w()P@#d?0=_{#QM5YxO(xDsZY=}5xC21 zpWLepF*}HOv92m4zyafxxXpPbUmo53+3sp=KqMhBL=uTxeFuCuv=>{3#!FM?$t-1# z`LZl?_xoXM;-_na)AC=O4xG8lz`3>4*SwW @p9pKhXl;?!iQPQ*|zC)c)B6DFwsa#TES8~4c3(uLZd)Rsx4b|_TEu9f>P{f zd}9GYnsBwWP?UHH?%o{?oVUzNmghvSBB%SG8kB7^u{!(&;>C)N%Ke%|hS(N@k1coU z4_wUgj6eJ)_VA_+qVqr?GrMD}1xd6RkMHP)wi$mb_7D|EQ??7lxa|p20}Yr(io~|i zUv&C+rUCPYL=Az`u{wX)@Cng}5oEg+0<_%7mhCA|6$Oa2Xu-J1dCAGW0!2~fxS_Yv z{0Hb0EaJPcux UicEuip%KySvyjo9jbnWm zgs`eYrhR>TSSmDRt bU zGgPca(66D#0Q%pAcwDVm#DHOq9~f^`zi4cI1q)IeF8L_EVF@wEAUG2?rcWVKk$ObK za(?Us-Cc3B-_O3-Ta_!cuCKCy3VsH258lq7o({T9h@Ql3u$vw+*DlSs*FyjV$(GwQ zcph@F&f$~lfd#b0q _-g$taS$>uV23&3yrWpJLo%dtv6_#0n|A$nlE&N=ut2ZYiSq`7_Lmx>#B |1d0>gWms-R6~-2-5A5rsGnO(6Zyua(ny(&|1j3}Hn&5|bi^S^K8s zLN5pN+a?id3IPXnSf;x16U6|lhtwV)n3$OKGj9q}5zWM{Hy0jb0x)nUG9uzHqMt{Z zf48qcMS&{<;b}Pe(S2QGLzy7WK +7hpt`8tIqndBpbLcR=SQjIqkuA*&dfSolr;@t-0HzL?kK&Za1^^tQk=} zFuFcCXtEhUS~xIqN$%n74vKXH6)6N=JIi%obu_HJLrfxlVr199qh^Kam}H8Z*~f@V zg8Pb0BW=$Crpq0s%DYDd0rA{b)I-`(KUV|?kJqqU1PolCDc(R2F{{A$Ulvx}L|LO& z)Q0FK2aHZInqGOS@KwM@asxsFxD-DO?(p8=-uhlD=xxM0oBwx=s4|@LJ{1+!M<)mt zE+{^;tkofLkCn^_^6CbK8WZS0UL3mnj=yJY{|22*5uDRrUS?g+U3TNWMTKqiBj?&b z5{fNKxf@ -CLqTlRm+=AxQC2sh7Pk3(` i? z8ZF>x6K6Qq9bs}L3Q9*6Oq)Wo05-AdAd3Xk?^aIB=?|tLMdHkkOC hz7DvCSuQdL&g{^Ua>+dnnH2}&P)W0;l4)^R6E@#=qS*2y@0VwRJW zv&s+Povr20)D3H+yA7#>K7&b&{wSDi+OuuPx{tVoWrOHG^O~Iykq)@~)Y32xRRQ^( zDci1)m-ibiNlLiFOxGT>zK6hL6v|r_+eS50lXnZO`*o%`D6?4_Z)aZ@>lO^q$mV$E zzyr|SAdU&r$jc9@0^!^3YNn~Y+pLt`Nfye1TAO1{&-%Qjt~-L?4Gn*f`=TY2)PUYl zaD5R-IUaU9m0bK=8^Ot|X?OkoI_HGaL5>cwn|H7M94AeCv@*FUCFQ);)VbL?>5ep# z?zspiz&ZP*6cm16hjyL0;| 5a+s)o1FTW)YiYD zesUV}J0A=lw2yd#bOaK$g!L#0B!(_WYmM4iW6E>vA?JZ@76_Chl_Qh6(L#$ghS2P( zlzARBMJw55s1Xy $lQqzb=Gzb=>l2vQRKX=s z{_{p~;0E2#>g($87AZVyZ`bD}k|&$Gx+p&d%4` zVBgBYl3N`d?duJ~(JCO>%Q+d5#206{O$E z@5U_B+eA3o&koHR;>jAIT_HIqC1sQmTJ!y-3|<^2S;-W#3{$^9-(Ce do+Kvn9o^QYFwEj7VFm#We z2y`c|>b9Fu3(qwzl0i|Kkq;!f?UIz2|5fy7x@1NLA5e>Pw4jNe1$zX^>)$&Q(*^>7 zi_CZQ!|>^{>(7$r4NwdhKnE?CixH8L<{YcsDv2}Np6fkJx=6liJi$SXLZaP~=%Ez? z#;ivlYUG_y`fnV|jUUh*MNrszNPQ;VTW=p rh++h63Da(1V`2Evzp*^{I1T+?6larw9=?WQ}h(G+b?OKJ)-UPpij64S?r L9Lwee9?_&s2jsxgSy*{6>6E}G|#mjH$VqSi*ornbxVnx8D zbCKDeb||m@;ayGKuvmcREp7N~pYY05tp||`bJg2cpBjpk{n_WdMB4MgM;mYsn&xJ@ z@uN?U=8fx2Y=tdp&J2kVNqi08M^Vw!)3aP)Tb^=_f~4$%wKT@fLC1MJI76eFl9<-M zLB37gr?eM{cLA&&6#EcBOWJ~-`$XHMDe)BG!8>yw+blSgy?-AxC4K?qP5y#}=rWN8 zK4-Yno@Ra7zTR>0Zwmsg*i+y6F-ncQdw(TCtE}^0=__MIuEKd>;Cj4%^!o#7>$5$; zxPg6QRFLTNj79>HmlyUD`-`R_ZfM@AGIE^AA%qHw>Ih6RfWQ>PiHV8NRLy=0uk(Bp za ~f~yozAB;$)>X(S~4ZU{Av4T Q zj34M^qOj0r5L{4H^vivIXWjF4xj_hy^rEcPzkPecV!7za{%)Jzbq60E1j_Ow?>|~1 zf1Yp0myXf&x%T$<#``A2l-BbT_yadV-D4&m+c*gpAVcBJ&h;K;j2P_6?hK75k$0IG zpwsFLXmJh(Gh1|W$44fzExm$z{cOUH5e5*^#Si|Hn~4unKRFC!VyjOkj!IN6li+!e zOC6AjBYY~e_Jqu~PcHzwSad?Liw_qhC5a5dnt=>Vymn+EO8W;i-iPqi?GBK?V4LxI zFV#l02A~fcl7y(!&))~wU~+EKn(gX3$uD6y!&)4doQTa eu(tnd@fsD^Ir%bCPve zMzCC~Jmd^rNi4XKXUE$u&50U-yN8&&p7#lB9Kv0t0SGSZ!Gi}CJ1O_tpJv{J& zjO@E_ulf_KD-MXELU@o>lIKvecj`yyk*46TCH;*;^uL%X$KJIjdGVXPOQl6sDRGGi zX=(YMTEDwxy)e=K?$NPU=S26}fvS<>$y|+})4e84%oh =Rt?wgp zYnIn#I0`@jpX{(aB4ZiYVEA9Grh+dJ4;dbzBOIRf_&}Uhq_6Fk5I~k1)5oE)?BGsr zZmpj){VI?D^b#_yF;Q^M-p2<944CEO;$}`Ku4De`kq}UMazzkRbjYM&aNwQmo}VZ8 zHM!37G!Vv^2803RIqoy @T#x`n^S9>kb96$xDDU2YiDTg1x_ zPM)TEMrpBjvJ`5coa54$!ia}JXNpLcRH&s-v{9{R&2cOcQkt_=1Ski+ooC_06Cjxf z)fg})71OGtqGWgRVnU~f!LAVK umni<7-P)%{NY)rEA^zEmfA{!K*Sq&wQV`A>WAkrLeR} zy-($VC6Jpj4&yPI^pr{q$kbQ2*9miFj_$5qSLrx&$cp^f2@O!d@d8#`4lXXm3Ycec zpE{?TIeK*Mw+1K>Lrra^g*Iz@!4Tu1 o?+9@YVE+08waAOe_CMu djxlo&jUa95VhXyf`~4U0D$>I^hrcZ zU@y?r+4;&A<{ww#eR+B9Tx(-EQQJ?K2l%?x?zGuWw|%eAkI~}5m4Oh(JqNv}PE3QW z7k{wBcex0?#9m=2=c@bVVTLqD-w=D~PqdleAY8R~cBCTwdvYa!t+o-oKT0K5*uI9* zHY9r@U!*@qw07!wqc(uc#&u_+6x#zAdt*)gzZJZDcqFn(IscY7!qDXVNN>X|VDQR4 zt*uh0KDb7#=Q6X`Aot|ozCNaHXpn3N!?>~M>L>DQl5b|pKYd1!2@jMix2NIvN9<)n zXNvYGq|NsP{rUDTLbfkX^P9&20ZwT mUELZ{h5b_M{}% z;6aa*;l77u>ei~$aoWj6Me@EspC=`hemN$guAXwvrW v6X>Jyq^ssi^XK#PoDp1PA5=EQ0dNJCBga_6kz8bekz#%q<(a zrSVhtT#!4_uC<6mb?+ye{#r|~t~b6!vvjEO58SR)RjlKLC$i9!%GaTEG0z*751z@l zR@?O_$*y&=KRDR!TS2FS_Ssa+Y2;9Ffl>9e0`E><-c?=b8}jW7*-0$dM)N}q%q5n3 zYE9^T>7*eE`WC-q)M3t8a;BM{5CJD1wzMC=Y@jVKAP?DF(vY^Zit#|?!RZrDrE|3- zvx9G<%BKiP1t5Q)dXu^G=1Yo;R|NLNWxv}^B|un&+FBFj!-_6-4Sh*IfHE@o9eqAz z5d*gmnV){JGn`P3=V88P=6>srsL>w820 %MGBQ-D`%(qqcDkMsGNY zc}=Af-tYd=0wDBn?%Z#PMHgHt(ok|SgO4Y4VX;E|Wr32pBByUG;M(XJEP)B#nF`zTE zT4UKgn8kWqyI4}_Q5}IsenUU|k z0m(;HY@X}gFyUtx@c212ny E%!-{yDG{_;T|}b%m12-3XTtti1;wI$r~SyLj Bs5m`dF>No z%k;&!recqv0RXxUL>H{%4GuUmCj0dVYHoAV$W>0K6I;3DB>yrHI)`u*bjxWF_ysAi zm>OrSG_Ch^NJH?IJm-*%BACHsDx|j22slo;2n;S|SIw?vEm6$Z`rU7hD~`**dncF_ ze_K8OVpNsdJ9cJ8O{e0osUw0aztmW4?9!fyoJZ0<>_ P(@_**%j+KN`?mxfO0teR{@UG@j^x;pe3iJo#ZdfzL&f6&G?7L6H)V z_!9etb&WucirpTpUC0s_MZC8#ID(sAJfTr;?bbFaw$r6Pt}wfns7c8Xz^XdX=Rc!$ z{P^)(;OE)W>=F=_2CVj+YQiD}enEUIJqP=m4r>xi$8pdMRYm`hSnQKNWoc 2~D#TO-2h*VVn?7-q_hE+nB^B&|XeF=9W=_u`y+OihlS&G9e-1X+y+UB9= z1Dz1ZK=GFatseDfua7_MnthR`Bql$Z{rr|yr2N$_I6-PIITuV3WD>U!u$yM4h~VVx zcR|cr(F@JKwa7hC*tPLuWD}Oi1upZ=^Ly8fo+nN&D&M2aF0E`A)k;_P4u$Xd(GQDYh~&- z!-4Ph5*A$DhR!$D%=KEY(-Wj8Xpdxpf7REtq(m;yHa$)1hBL{g;)4CFD~mBnD?ifQ zdEO9kXUhW!;A#(Tq7g6wu-G^zPsrclJegvEB3;xQUPF SCoU|k@#fsB{L4(ItU<-AgoK-sX7A~z7<^v4O x=Bx@Noc5Rq%rT$9VF2K>G)>`#Rm#{`%bra zb^UIq-k~@%cysL@8W|QOG&-4eN{My9t&>x3^WLihv)8c8bgf~psb2N@IqY93BHT8s zy9H+g_iw)c`0-;>+gWh~#D(A!Xh;HQBi16awqwxZKnJKkjP4Xq?0=|+$Z7H{s*c;+ zl#n{Ji|;L^y!4w+^P9cP--S*=@BTpVzk;&(J{MMfuy{$`0MPvg3_2tf-Ly7{LvDyz zb>Om|S1C{eH=C1CUoAxLut0XgvFj-y8&4iT=0Ee&@eF^etRUP{&;!*q#AU?131d_l zE0!Q=Zw?wUM)+D#Fc0rWl8+{>Fxc;b`l!n(xID7++~pdX2i27XD{g~=P~x4MV8U(1 z)nAP^hcTWg16AQ3k5(-;aDZkPY4LKfj5G%@YHDhh+V>WF_Sai@E36fDTaasEub$K; zwD7&XhrcQCQ`LY*bH`VC2d#t1Tc8h6?yjKx;FWDWT{Re#lW?}K8nkFvE-m@6tiF~m z`unpxW@!@>Vdy}Y5!VcDI^`D-n0#{IJ$d{gDR4t@8djSePeK%IFOI)R3u47jS`dG{ z>Im4s4bMaz6HKP5xsBbk*#j3-(les&G0V9OVDqi&BCVlY_+G}DhxjdB#8DUi{R_7v z1pisJnbA(pE^c|F>)c+gtE&kYAfu^wA3C`&W1HaI&7^OIy}kC{Qbm4tPq_+)LWkx2 zyn3$3nE!PD^*M_h0F$@Zt?ss-%C 5>JTzLJCva5+%R&tcZDKjH>6l(j zU$ASIi2!Z-qv;~gu$gR6sFZ$GUaeU4K0SG2aoKw&j5lcMoL$t>8N}a#=AC2Kwbk8g zbXbMJV;Z{Z;;56)w*M4-RB7o?!+4| VyfcM~h;lG(3Z-Imd&oe*gRKRcIyzn|c2uv- zUsE}l#Wh5TUl-AC8vJ{WUkLTOH!2Cgvc4RF&7d2(tLyV?G`98OkuZHHJ4#NxroJ`C zOe+cg`SYj38Lsa0fBsQGc};`MQRR1p`yCvt2^%uNfuYffKsVMMNt}5n{U2d2W55K3 z )k-f?m@rTPEJb`gaH##Zk3?YsoD$p&_g#KrROqhWM*cT^Z)+=5 zH@I^f?hpz}4qu@=?8LLkpUHof35=kp44QAaCzTX9@}Fw4RKqT!7GFToGQ6L<%t5%b z@U8B^UC0dsof{xd^7$ofceUv-0*h4k(`v>^?vgohAhpPSoNt$|E2`)o1Kd*z_vOZl zI89OEyC<#qnq(y kpoboJpi>Jx5X34U=0C+_r~!AK$TrTMqz zwvK)nd_ZV;3)^r%X1lnlPs&@&V&Cy(eaa!6PFIPyvqHd<%Z-8;qIaqa@$zyB^(_AN zVBu;&KlElWvPtF`;@NQtOG68EtGby=2$W$Q4f hz~ z{l1FfB|_*L*1^F+3-VW+A)_Z1`q5WzA@s)9&}rV=FHln^sFkE#HY7&DRCpJgos>}i z3ogOX9yG@OM5qdPz@EDrlBDAMeI(DRue3;ZriyZ{*r9HWeRxRCLOFo|mH%S*nJyY% zaq{$BNUGgS74x0f>?q!)NS;p4%@r3rlfN5AF&t+%B~ddKrdDz~m;ML#HCEiBJmABD ztpo{Br1hv_QSRrh7hcw?4v>v<4PCQ+>ZjpdG!}|xYwu!3F4Ps{`5B;e?!dTI?rA)2 zEroDGwi+OT75i@<($e~%t2M=s9Z*2(;Be(9KexQw?wUNUyw;Xo_ Nd=){2U?|#EQ>HU$qQ6szJ301U#t0+)B4T!$A z78Vv{9kZZkpp*T}#Duy;>FnY6Vp*1iupZhiFNdslba(&R2Gb+#wEyX>)xVSFC|NmJ z$32@x3EL?`*mM%^s=Nw<$0(( %`ZYg3 z&vk}y=bhvWEGt)l2<_IF$EyylHi=EGVIA+!0Nvy*%&Z7|>0|=j(8Nmf%wLERLKS1&HFO uKtU?97i!NGY zZJa;(8am*O!BO@jMg$w$fzna=`{Tv+ZGu6 }uj$^o(T11> z;2o_dC69{xSVN=o_qY7L_bxE6WiLD2!A@HR(RdK@b!!n9jlD6mQDD-_InLPc|FD~I zT>v$XX4{@8Foh}YJ#ESZ2f1E3H(b{Wu)4mHk =c{_k `MNfy@8djOu<|rND90R{#Et_=KnNuNhLL;rJA| z0Ggv!ha6ao1D&92zU zvP0I%)EMe7#Sr-9wO0qB(CSAA-u}mL2La00@1yksEmpk Wr}`tI-3KkyyfH!LvIvUczvL&(U; z5QfW7ScAhBqA74^X zR=bm%EW-y<2 QfM1LI>jl_HBDwG}#s z6Ov4a*3JkJ!BG9F3T3=1eWlS%RQ&axo=VE* 1eHbC9!Es4u$~Z19^X6L R2@h4}Y9Q&B? z0RD>;cZOCBuD_85A?g#17TpgM s9-a+{9gKIM)#EA1!dPG7aVLP*B?x7*s )) zA8C-HLa7X8Y9ytQxsWs{l9VDPkunxCy=(95R?c~T?;q#$>3NcC+WR}KwZ3aNG<2Uq zu@I+n`Un^9th#{caDLAoW~{AfO-&eTINvE-)VkJ8x$$9Jq zgbybrL4~Bp*Bhy8^z`%sY|q)GMlemRtPH6|*gK<+94M)P=x#)ts3}(}0lkml$hMD4 zjGao+F}BKSm#v1Tef6t1-;`DTi @K~stS>8dC#VaV{5j>eLBZnfQfnDHMlB^5g0fP$43z%EyS9F;Wf8<5 z%avU^zPupXnU^?t PkTsHI$$) z!QDG${dmRN8b%g?AFr!FAGnz-7;d9=C4vJRtIDON42)mSyVBCqsMUhp0ZNXD$+ns( zrlu+~vFrKy?V4O~l X5S_9FWb1m5||_y;EXj+k9bI{6|h)#QdewWPiF#p z<#>-4Q(^1U5oGl!?ct<#UN}CvKL)d-IqN vE&24s4m4pNrZPq zbzo14w|Yk2m01%aCo>;jT@o89zV{Rj)9GXth+`G>TL*rP3Vr)JkTQb-rqLHKP9`$p zX{lDUe2bICXrF%vqEPIf`e&g_+<(x?yyB%b?g#eo@7I->U^PAHgEV6oAbo%oLcSwW zaNsQzv!mf5I{nrC{&ffs1s>+1dBMWe?k~VfcFVJs&=@wrwM@A!vXLlC4vm?=yLK5_ zYB1oQaF3J4?2RB69)rE}>&N%)bzPIT$_8EZX9|4DX{hYvR5KRS2h!$6I=_4U>ea>) z`OleNg_4XkTv)-x+B)KU+Q$ %M|J3|q;glgGP<;B%Sc^Z#Ycjd5Z3@1XZz&4> z2gw)yx#QKgc{+u2&B74Oi|@T(PBXHn2y*(hYd3E6r7Nj*b!#0IXSQIKiLBt;QdF(y z OYCa~xp&fj2FmDYV=zhXX_QV5&w(V;ho*UQfZEVP z1$4;y(SO3@_jN`eHjUSC>Xq4Dobf uGiCh4Ro>U=Yvs>=e|HQn`5wjxh+M3XY~ zT+YSQk6jbbgG^CMBx4{WOeNCZnF%Xm^+vx$6eH+h%q(9JlSGj{ftSW>&9 z>j-=JcOZGKF%oHR4Qt+jJ0%)-y|IrP=&jkkW|jQt=+Hv(Wi{^S*m2~=4C4rrD6~ <=4Q@LVODC4ILYf|xkD>7(OPyZ`6PzC5M4#U^dJ(+{juA@ zLsU{zFjZvs1g|YH9~!ubUzGIvSG*2${p%z-{g4c1YDXgyjCRDB`i|4(Gd-5LN0Zt5 ziX#5I98nTER^D=$_I;6qhW2pW0y~=*!nLSTleEs(o{8@sHG>gwrRmECjC|&^V}ZZ3 z&EDrshiQ_>$}YHCbIkb*xvJVa*)8g4$BsUbEjHRydrsF8E?_mDgGMsp^XJbY%JXiL z=#tw){7g(yL}>2s*cLE?{Xl0m^VwZavv%irs)x0wH*-czn!o$> *s2d(DDouxwgO?=7c?&rpz zwXQ$b+E<$U{06souA707l$n(Dg1(cvJP1kqjLpr CiMz3CERI>~t-JR&1njX*eFQPB!-(Wz| z3fZugXT`qS4QvF;^#?^17EAkf_iZW0>Pt~e@G-R!&n;0}9Fg+Xr`Kbiz%nKtSP1p% zN@VU@m*bOl$1Cw}GEEA_X82O!HtQbH9ykvbn2A)*)IR?%$%d^XWLlXyug&smj-|0F zXv^mS;oK>9ga)_@$@%DYX n z70woYJVoW2+3=5~Cg sK8fEo0Q$Qh(^% zToMrwKO}+O_=$d$DR31`3Yr(zP9qXqoj`bn*Dlc=q&6Ku;l?G_*6&V>ZZ5mbS`be1 zCBLv~4G|Lp*IOw*)lmBL*mHDQtmC)3=QfS@)0%s?tCT*bV@8rB&A&B5vn3A^=J%_) zAI4KkG9(#qa8`EOA#5{_zBri78|%|u>joreOZV=NJO0jthN_)BIIV6Cfu_|r6~1<3 zuO;6lZ5pR6!I(B`->cGxHfriI&vF6|lsFf5T6oJZvS1n!lR&buTCaW)CpRCsj)Wv% zMWbK)CB_;~Y<* |2qP{cAhAnQ37& z8z`|ElFHtyQs3@J@<(;uBEuMhLUHs(T8^(%uyWXh#+wbnM*EZ5w^1G? U&MXpyAklPDb>vWHO&$~A;KAuRu$yNwvxkq$@G_0x)gh8ud~#lM^4g)v3nHUVUF z9@)wBjZ|5Gvy%nzsqQLYqJbBcDj-P13uwrlbwA3Ce glNTWNuj{pDU6O{hWJzCT{95usNCH724tBEGc zYs0+lOQ`*u><)%V7@_r6uB3BIgaO0328*Aaz17Gv-PK{7hSLNQazmHxIO}v4etVy_ zFbmpe$?JAB|6tHtX &ro3bsqX?hc()BSY=)8_^ziQzu z0i6vuV}ak{JL(DnidaCuQ24C4OB03ZkMjw^^F1DBUy} =WcQhd65D>+dv1eNnTwa4y(R@(*2BS zKD#AMwgnrnN+!>3m(RlN=3F>>=}gQ~>boNv)x<+Tr;9kv^1GW?BK+hWy&b{m=J|v! zXnSkkNli^a=P^>dhK3n@16jdQ+T;D5a WJQi^_Fb7&c $eBEZD#Pn4jc|V-9yariX32r6{mhHzvGM*<9&*4s+iQXEVqNBCRuEWJNPYH~ z(TNaK`s$u`PkQmhiQaE;I6~}`ZM-x?>=3|s#k(=6k;Xl=u)TJ?kNL*7tk${>mN)Gd zV;)Bz0jXP1nE@ix^fB(Cy4TGuR$lK!tj937^4$x`+xeT#$NC27NUu3(pkHJ5kr|A1 zBrP7-+EF^6ezoo~9&L{lzv9*RdT6zDyJc@W#tar75<5F@mn9AtBC)8{ %z?GRz{NKU&eVW)A93X|7Ot5{!2ccX0+Ezb6T8f+cryz T@*GZmlyCO5!d}JJbJmWVH0Vf+QYPEU@gU%n1Tt+g7<-0s zlE)OyxY$yxoD@r%>8ese()oh6zQe9MU{v_^0ghIA9kF+2D^2OHyk6~V(<|N=;eQ2R zVPgco1Dj}9YjFL6S*y4>d{EzsGTv@aS!Q_z;cJz?n_q2TMXeb_LRq zA7E_P_UTar(I<@@IkKQ)!6If|$%uSuYBGer#=aO^fb2#NOQ82NP;~8{BKaUUDW#}6 z R 0h zDsm?A@-Yx3eTyi|EurfoKwfcHDs#_kBr$Nd0vX?cHqgz@s^uHM&;mCknBBYLi%ZoW zA0k*Cp?~@9Jz5LuVuMEWs|L&@sm_cCpJggyo3 !bFcc=< zgpw2o4Q2{rfE!+@>)2C_b9AeaR~>e8`T!J1i)?$X%a@X6p|l*C0Z;=?ySh|{@CL88 z-w;)Jl7y$lUP`4DFS9ESnRW%zYQke0F5fIo-Z;BvJ4r$$;6N8* ?zIyk!e*JGiMIH^D&&R#O5Vl6`je%O2xdR;hlZM86O%Mc9XsH z{cDUndBSm=tx?|loRGWkd@ 6GxrCt>tlJqu2ah^Dr|Eu#?V(7&EmOt?_TMNi3`_UkFowMW-h6%MU?n} zgL*V}G?eMIeu)W+R6W%Hg-TK+5)j>LGiUBcxzACTLJ!#3#oI92!rXk(!p{*eNL?v% znX;MI;dyqiuCJF=xA9|ArMxJl6lMf2sxLY4dA67aMTl!Y^V;w8J8XX_V&FZXJm2=! z7ihIy2cRR`D(U)eEIvC4)>}}TV`ocy$=gJB1m1sfh1|QE7N@F|AJeZZ-QVrCD4r3{ zC1U|hApqU<9q4z%tl7)AZx_1BA0WZzZ+KC@KVk1T+WAe2<5r&s0rYjp&8*pwxhCfy z0#I N+%I?Yp->Ei!gzjQpj;a}NrLuQ4?>J%^gN$mk_^&J&FNAAYl9?pr2@ z@xVVyDtR6`DN-5bz;Mq%WjeAF5{#uFc5fXC7C5sA@Z@9rqX(VycWl)D7{J`(A+aNu z?z%zX6^p671RzBWm`7Gg;1Vb~WwQo*XYiXbM?RU+MUL-^W^)Cais3*Kl^-lzO38C2 zMMR1^x4Fm3Fzf@7uHJeb9nZqRu_epPdJes?V$y=zb`HRR1VTRJQGztXhLM$mmf{ER zy^IXg-urVR09yVUIgx3fJ;l)bCD~+pwDupKwEyyK c=Vx22r=`yP&f`3Q@y yrN|G@GM(B8+P+?A_ zlD0%0WND24FCB14=csr0nJhZ=v6c!|we_}___` zEz`ZKaPj1;|a97Hbh z3I<`HR4=dKKGrIM2~a$MI8v?KqRA~7Sp04Uoxz cUMlHkzg2QUTe$e8zvX(*?cy+oPhiynl5J zNR0OKi5)@^&))K61yM~xTTxK1p2bw4JlQjzmzRg``(71Jl# Y0-|DfJJ-Ijb2hu0Kw_1+1vw12)-&*Sp8xEl0?@fJjil0G>-YZgK0daQAvkTqX zS?_7{1g8;|VC6a6oSlv9OQKXiJ2le!JLDw$$>sJK^#{+&)E_MN9Xs-|2|o8winwly z$m1#3uOue+?+ nxDV3B3yKnFZCdWN z?Qq+Wxji7~;eKjwA2)Ld-D%pm^aa<3wuX-d9DXgy9uTT&Y-?afUqNQts$6M#km26k zzs9S1LGYO)|Ilx-m{dsz*OM9VPkO*a)0D$jcAW->v)D&2faz8eXAq`=%wIs#6lrE| zzW3Xq6XYLbe>f {LdW5y2^J-KdI2GQctz73 zP%c)B^YYmq-6<|~YBl|l%iB0rPk2$OuEK~+A_lG*Hj^iD#t@d%``dEnRdRX-+#b4W zckaS!Q #;m&75VVy5~g6C%sWUeti8VO zBnqTMJ2%mq6VFw%h*-2L7w~bxysUS$F&xHG;kCi@Xpeh5Ei=+VESavm&B(jUKgSXO z@LA#>jaL%Jfj)wS!M=sPWhD>8 m-%m#yB| zVlFSSjs;}g2u<}gi*17y3xDA98q~%sxjg5bjTe|6`OplJ*^OG`IwKWf3D|SU`_v(F zSHbScgr16$1V4HpMI!m(OSv^co5uJxJkT-V!uWewpd&~fYe>G;?&kD#*=I~O!^7{z z9*jbSVjvO~mzR2ECCdi;Qogg!OZyfUQqCh^H+t%=-F#NI^@J0r=HX|GZs!6f-=fRO zlag<8uUnAfLjED!ZUx~OY{Ls=Eq4BXtj&|jKEhO{8yN5qWo>STy3Skc)BY5?Nx3jn z59O~`wUi{V!&CV!BEl_pJPQ)3gD6aWrB?Q@k3UMI)L*P>v9kbFN!MIF )!+PvReGB^ zXeJpx-Btd3q^Y=8f^Aouf0Rgk$N{;$MN=83lr|e4HG3Qm`C;Av={2@;&c3sgy=z?1 zr1`eC=QT&`uoRUoXv+fK7%r=gOe0%OG%9V-sEikNxA_eLXe-h9qiS>Edv?!viN_sY zEjJmw3H$pWLS%H19vU7eahMbN=W`exwV7^;D&oXLri(TWXDuf^?R|(7lmc&~=rfBZ z9brP@%^Z>2(q3t-!_Jg?P~&4k2Deh(&j e)87JlG z$CM2sZL}iM1n;Ms;L~~*7x4;PA`u@tr}UzFVn_0T!@q~E8+DB37gb`qVk3FSf9LMy zox18=2oWyD8$B>EB%gR%(H=3qWd$v_ifq<*fLFK2R~_9TnYHPp-Bf17x_!VXhhp zw7Jsgprk(v$->3a0cKIytlp3<#Yvav-x?R5M=r!({V$eNsVBjd-aFpM4uK)Og6*}V zT!<%%^$xOuqex1|@?SE#g;j|3X1X~dQJjtE$Ej@DBv2(TdcX>2{PRAdBKIF|Afg;6 zmROR+SV;9ozqrqUnz)5lp+Nu-^R|l$9z@kP-<(7tsz*Zku7W)uBimoVF?`bW?mTI{ z0-i!hT=S%Dhc)2*$X$d|&{sKqQz1ZD+x(6E3kuRJNm6|tDPWI^uZlFCUSX(1q8Og) ztI!Zyq49SO+b!;qc>8v{j1Ix6+oje_!A2fJN8>vo*Y1D_p*sd|(6&utjWpfrF4i2# zT}joj<;mII@Ai%27DYVz<}?=Jtb+!?E^(SXeH1qn6F-8;O6DC0M5;S~hOVpFOMr?^ z0L1N{sB%W3M%vE8^lDZ0amT9zC|~2lEUUR#hA~kGzXN@0VY9n=TGn4*pfY2k1`n$> zS262X<24H#;5qX1O--4b+r!%3ykLA%5J?=q@2q&X5)fh*ub1ii@QQbr1w_&Wh;;id z{xbkpv{YmFJyT}+9pQ1+<;4Os24^?zyN ?Ed%eJu}0+p26^`v~_tv?ufzKe9LdD%9KKk@vDi za;D#yZncGx%dFu=#!7l(2c2K2zFpPeGM~%~HYptH_ce(vxeRo0Ys`_#k$iFw`%n@2 z%* zE$VwOw{sFL#ZD6N!pEej3{Oii#j_yeK!L*`W9EdCyN9IBD=Vy5ypW)LCoV< zLqfu;3-i@A{PISJn56EdGf4XD?4Dg%;8<52>Ikq!K}(P;`G%~>s0NBn0#Rml$19`= zCCnrs@^qUTRb;7oU{fz7dI*r7t_y;T;R6zQha!Z;-`l#Q)b@z&NuM#y@yJRCb3Z*g zU50wK6p*LTNh4A~(Wp@=aABeQO!9F+Cpc)#8%~lUr&a=s+vgipqXV57nckN^|G;Gn z{0k)_(!0MN93p$SVAU-^^)g35IZD49Z+pS|CATl-pw%QQtU1n+<(|b2_r@W?kgGw# ziljulrZY3G$7L85(ZXDkVDF36U+;yjaLJ6U9;X7~DmM0b%U?) RrKtzmUL&FEan^PIhD*Ks}-`ce@tM~Ql+3)Pt)x^4})X|;yj9O z&wFcliRqQy;w}T# )aw3p`Uf!SoUto?RLYk9!(7H$!0`OM88?9EfkQYt zXq=AIfdv0g4k0hEISPzhGM$W?-1Z2R0Qh&*4Y7AYyKhE4WNUuI@=oL7k2*DxN7oBq zon@> dDa>o0c#9K3ZyfLppy^d8z)A4=4bPB@VlpqH%o+%ssYKY{?F?ZLLRGL`eLZpV z+$u5<`!=-64puNB*$51!Ifs_BqwRTrPem0d&FZQQ&{bmu*Ow`lM>w8Y4*HAR^_dWj zmlu44y>;tWBIGvxp2VHGAiKxsBi^x2CuJ4nq9)*Wi&AkJuVVpvxH%rDN{p21$-QV# zHe6yoS*)0{I-xTNqmW`>5HJTIK=C29{ezJ%TqqONtRwu?r};QqewJsXiv-%8_vL6s zA*Q3e6n?cksducDAxql|EAByP>4{;SOGLhwaT^HowbgbvQ i}^^a)FN@(+cA6q*Z9>dGX?;{lss1Bj_y2U#zhhRLI*(0KBi1S6~=t={xvB ztHlMeeAW_}y65}ZTbil!%$KfRkk5dO%>pmW%LlQ$*t3i42fJ0elhQCaTa)Kjlgn}I zZgY?$cgB|8gKv}ur5u&O42*sB{;3u$BynRG|8_Rf PIcGR=~5DIYbK>%v_U5}C{XwjzX z08Eq+b?_hT$q1^7eoW|gD&iE2nVZk(ZpNjAZBZ1r+(?q20MHUKrU#=-YtQNM?ls*N~uF{z;Pw)4z?ZqtPfU0iMCX_6&~Z&%2MS$V%V zE!!@Im98N1TbP$uvhBeFCPqQmi8;-f7Y`AL25Lu+-$6=>KpwA*bE4OwH?Xp;3Evu- zz87h *=8+U2OYXc&SHpaMA*EG}k-ZkLQw$9R@%pG)`BGMU=f_H~; zDYb1m1gYZg+FO*}Bt~WR&mtbnWuB#fRWqHyLpOlw^`F<9 MggCFHqUNHW^YKx^huNyC%x_-Bh1npBE~*--`4v3Li|-GHPHH#aC8MHz#ZDf zm0x*^d>Jn-{W@jigF~vi*WKGijENzol?B$sUe5_w6EbMZZmM7`;^UD!N;0ka%#Bxw zlW~P(V`k`+De*y;lkO;KWiqi~v%ev( Y-jfXPHbD1nA+T;%&-g5*;*<6j@ zI3CVVo6xA+2|tgUVls_oyy%D7Qg83R(+~(NDmy$Wq*T- QU`>muS-kqXGFE-yjs4(&JYz!pg=|}Pw@siEd@^g zmm&Jh#7w>IgkUG)D$@ibR6(mqFe)fj=;MAL{GCf^E*MdgmIC&sBx!Ep!W&CS*y~0G z*53$~`2mJP3RMPe=9-jsQoLwMO&Gc(ndO|kGau(pJH}&A`#VmjP~dotnKP{K&B3(h zr@o9VBu#EkH+jiAJZhpo{K||WexBLr(@2?%ki%?=@m5EG2_=Js7!Z};pQ^RoK>~Fk z34O`gtTu5aaSo14dixdfbfrUOR#TVGqO&JT_GDO-xc(7C7>Fu#t4ysKr2~0r3^d|Q z#y6?Xwsf%eu+CDATa1s%r`5=YKl2yWe?;XvpYm{+8G!iji@(NBR<{_c xF6Gp;GcVM*X%rf7-6amqCc zN^qNs<$ot(toHWu!4VfbX|kRE%_;o;T8XSyp)!& ?q3OQty>??z!w+{LJP!)qHMax9pMC<|4BAyIgt*Zf06 z(}g#I1GFoFAOTnvALdjr3{&9R=%k#S90lM}_VCim-R6h*&S`@p$|SO){4#$a7(C(c zM{EB{D%kPivY!C6ahzoT%Om)`8udHpid@zrW&s^=C`lkto=2Cd=dk6p59F0XuYLmB zWSOItBY#TLvP2>aTk2BIGR+k@Fc5vMsr|&J1o^Mt{!dkCQ(|^CcUS7WFfvk77$jqR zg{P1 wE0*mkn{8r;wafo-(5{aj@#h^2Tr`-0f z$f&mw@KhybBe1DSm|q2< kgeB&UfuDLGHIP ?g&%4Er#@N+iUhaYUlJeF$Vjx|F{R!mx?08>eUDaVkK70bX=!~=eErErok zb&4l5@wg+Uzu6WlAKD)QN$X$#X@BalZiwQdR}kK>!T-e!!e*i{o_Ml8=u&QUvPkzQ za?hONF_*7i)hEG+bj#SQp~p9N(C4E&2&^^{SwKEBN=EwvD%3XV^~^C5E~_}+z@lV= zDo5I6Y!L$4ue1Ys&%xVs ;11%|UcNC*U60p^ zdvO+W+q*Z8bj;Wf-Up&InD=Zzi0(?@$^BhRM=yFA?l1|Ag_1TdR^Yy>;|O~Tolj^L zDn0yBDlR8#y%>jf`;gO<@n_U7r2xFEQyBu0`=M@0cwh{IDEk|mI-V#RY-LE0;M|}B z;St99!Xx_pe*Qv?s3g7LTZ0{TJbXgn?tK-JVfCA`_O^&(b|1fs-Osyy$Br^aduM$P zs`mjNxj~2!KLPl#%inSTk8%szH#<8w?|rMu98ujiS)? e;~4s2=Gunpt*Rk?Z)q>7B6_c;HLm# zqKP>%VZkZaf)T&FiyZ8pR|QRc&XTfcNKzW4x!JEMiO@B-AqhE9r-W&&vAPx2Ab95n zpkcoq$nllr#OM3=d(i>JJ)aFv{xEbWZz9@?_fqaR_e(Y3W+NG0q4f21GIFnA>%B?U zfL5%Stk`RAZf<+zuU8>;vLmT!WPm;)5-hf;5$){i9|l|LYh@x`nq|$6)c0h-pG+TW zZ(~dyigLhaCbY0|R@^HOANAmY7QAq^wTN_u`ZhMKvHV({ReO{nLCG0&TUw=vt7`(W z%C6c|iUz3coqKh}rQBW5)k#A)nAVkSXgNgM=P%DMzO1OQ$edU{o9-(`&L;J WU6nRTk58hk$84#2J>To@H}#ti@c%1BYo0ixemd`b`@b*aWp zZdqa>>;B+qxx_1P`Tp$l)z2@9Le+?Xeciu6F&Ioxf!%712%M2`YSTJAhM8WqQZO+$ z4<++#ZotN4v1hNHc*}K&r1$PAFi0U-y~xYWWqXc%wv8^kbqtl6?bG+mui(w%J~_zU z5E(4azg`-x#2X3Au0sek)tHd_u39{*k ^1-5n1iQ8|9`zCKq+V@oYEo(DjH4ql5-5N!w;N+B&=!0|Pe8n><`U9I z2#!pYOZH>8@}r&okzrqq837f(vSSGr`zbV3H{E2zhVulv`<~hUOrkS#u*hLHlC0Vi z5{5KATI@98ON7CMu^%eW(d^4PDU_d8ztc-+Z#5Xw{{5e}F7zX6+Ah9ykz!Z0(kSho zK*aHWQbON5(a5WeiMn($JMlMsR~ImH=bwy$UQ=H-{ c+Vom(ej+wK8r4nT zzuUO~ArPdA=J=$i<0@HGIy<6O)BVGvrd>RIsu<%!;bmY485+e`A(J#5vSVd2!+^XM z3$<4~1lFUkWi6+7fd&*`D2$X;$H>IgaXPG06od=)&JS2(XU0l?W>OBBtOm&0GSSOh z%6FZ;QqrZ?oQsD YI{7by=~$KW-zv8F(3P)q+3|} zBpWKMA#}p=FILBywDy!N#8wY)Hz2MV&Y!*Nft1N$$#?IT@XtNMi%OyawD4_IuZQ)# zZi&-3mb;4oNyI`8Qmvpld6!>l^si^T!wbpgg<7e2suK3zr9icpvQdQI+}irfOkzUj z>5?>{iE3K7(1Sgt0qplYs3K+x3j1i|znRX9c^THcLKKSaH=!&)pba(&p-ca@F0yiA zM?^)GTesGK@jJ@ww?mVG9Ok&9YPR))GsDc$G3ir*tgJ-th?Mo8tR~Fa5b4|BUDd?< zws!7r>2KW65+f$9Vn;j0*4!%mQQzqNn;9vAQ>hvHRW0`XvjZ*hw}{_ zF-)EwceOezd>Hinc7w_X&UrCXA)Pv#SMzvx+dB=(+?C9N;$h(nk?_|LdEom>Fbcm3 zIOYlXKN*w3=pn3vtS?8G`$9xezK+WO`Vnigf5)8U^H5I$do2==RpWv1&p|96WyJ>g z9hokjn93NUeA+-Hp_KQjU3R+u)7!PoqG2xPWK;vuB$agM9~IY-%3aUg EQi9sBN$hRGnfllUNvA2DlHja}5g)f09 zZBMKhj<01t1vs-IuwGi{<_IHZT88`T;9CIO8{(=-AR_?F%OxK_VxDL|B{!?jd)otX z8`v}d+Z}u?4_5Z^Ns*`5@;E6$UiRdegV^1NPaBtN?7Ys*vqMnwCo&cEZ4G(BOfYdD zjB{U*u$w^L fbz>*mGvJx7hB#Q;Z`-8Y|CXh>v4vvwnw z4rp&te|^DGE|1xYsXXz VCM6K zvYX4a7x0IW$w1evTZN(N?CbTWcYvQ5>t0HzwTLqhR}7(Ve7en$2~LYlUdG6Qd4I(Q zH9#DRr&FEapGP5L3)kK+z+9q^)-R$M^K!`4?GD 4roOW`&8V1OK z3MfREDTDSD-m+|Wo>c9RzRAINR1jd(OX=!O>p7KwgxHJcEOw`WNGMyEq)8CcGD>SBe351O2i_O(s zX~7no3rRLLr~{)gxvZFx*)l7eb4irUZj$nKux3KG%ReQUvkwtypeOm^qzo&f^i*;{ zsEhOZ(UBzr5D!^*6E=Q{?yXk;6-pFaGP|1#q>fgTa6wyFDYBf|Al&OX3w+u#?=|eu z?zuIHF{F2JIv}Es{L#i5#K`>d$) )) ^l`wa*LF z7u@i#;Db52h6#oLy3k@)vd_N##1(HxQdXF2GjnD*;SA=_fBp9D0a7gM6X}~t05+(z!m@vOJ0&QaqEcypvy&T}}of|)38nL$;-aQTi$)1fvrLqJx+|7puyA_66Y{(CJK zF!NUiSjg|@ z0Eqf}chBI_w#<^I^HIO_^YV}^_+(nRbGXD&KxZ#Xfa=Oj&ppn|ryW%IZBw_CnTtA1 zOV=X1?f>BvaE^BzA??#TGgIH%4h?Z4(j2x{OZu&l`{<<0puW~~iI1m|q9&eK^J9Pe zBQnb7u*{<~K4$(&_E`R|Eg8ZyFHNq>%xy2x$&q#9fBSKA$(uhWDq0+?)p=yjpNjEz z>^SUxYArYNFqJv_K{5~sB{Crw{vCdr0(wda;{U#gG+_Zmag%H_wy)T%24>XPowsf4 zdb*-*(~!zYHdX6BDPm(+o<93KY?D7=<_hX7L+Z }I8u^m!_(fKEJj$lO7`rUo*oQN&QYU5&}f`{9q&+ub^kbV-J zmJ+Kf5`O5cR4|(kT(WJ*v;xP&`(9E~^A)A}UrKhk jH@~2J1|=5iq(5T3f%JQvMgMwx!QsRQY50gyR-Q0HdeZh=xa@K%CuiU=&ZOvi z+WD `&X?Ovc!rUWaOOiC6#o&Y#^33~@{1td6e_X`xXHJf3x&aS<9U&s~Otg8o(9 z#HSZDDQmI8VHyfi@$^>%qmp@lbK`?YnYt23z##)1xPR=DLANNKmYT8dX3fCSvBLf4 zf8?{OY7WIAq>T>g*)gOIL@2BkB@ qqwzu==n)98?13{O;YFbS00*$kqJB?~yuLoYzQF`V~W&NN$ED2y!kg z=EDgrFcXNj+i>oj$0XiMucH<3dy~V#Yy)$eKdYyh@Dy61BRp%%npl*O3+GLDZ0;Xt zVsh zxV|5k>V7C&zDDI)e(oz4$Y*8 zG*X^osXxe*fmJlLt=H>JPg$5GRI5T?*f;L|nG3%`yb|D18U~5&nzZIum`1*jGA|!G z2-QdZU%5t`+*b!*Q-I!~|M;!bJ21pm2ZMk*?u{?6*uEVMMZxJ<+fR-vkV#tNPh~72 z{pnSTW6{t-+QQ=opzSkmnJuaE&CaSG|F1x# !oaX2K?SN1^Y1&bYu7g^grcjp%{Lfj9lTNl1d*4{;@HDT zxO+7|7!-+fS9#irL0VxyT!c*%ySSW nhd5N3X2 zG4mq>&ys}SCaXmPRr|l*a4f%SekD755+f_(7UBG6!MTXLg!(Zx++n>qW*(hfkv1Tr zU_ZDYMd=VW;o&VAS8?e?yuc6oj<6tu4}-O-X}bwhx=-fS!t3&4luRh#H#|3I8UjQd z+#k)KoE`b+{900_B#tSP9Os7V!1nq@?dg^eY49o{a)JJpK;3+I!KkW4F8eGF)dag~ zd#ZM`95EUkZEj&Pu$m_-VkK)aQ`7bET)H^}#1CYp#B`SYZYHCXKahFxK`c+J>n}!@ zF-r<=Vvj(e8Si|P(%?J`c9op-5-lTtt-8Np#s<#TNY5=lQL4X{d#v+h-f21k&aQF( z!y{-;8FB@LZ_tVu6(P-w8%mOECn^~r1crwH_~iu1rOKSB9QnSOJj-%<6m@g !f{q!HBeS2K3?zn>}Bj#3FIQr%G84kax>B}4%Duht@# zki@zdoh8V*vrh!Z3RH< fXQo&$XWZ76y~y+I0p!TOQ13nk(TASQ4Wie*H-1 zYiBo|o--ww)RV#{{u3erbr>eWj;IeE;OW7EDp@s6>ePvH<$nVaRul1}#p!USPGN&M zd~mMCOq Lz=8l|_HaFw^<~?7x z>?sJ_6BW1iw!o>^?;m(Kd#5%^R%Zb9=pR^k!PDAnU=f8%u5O5iT>Pf)5+Gi&&5@0T z!4NGES@ z3Dl+gPl90R=pCUw_0lpM7WO7LZc|LwcGvom@M z>q>6)qQ2brm^rT#PYG{h3>UU@u3wH`Bq>GCXn-8_+M6Y9LoR|`sKbhhiP;Tcmb3w} z39ITA`C|xR&o&o`{l_maq~*+-ygV0kg_)I}q<>azDxdu&HK*^Jf@64N^$MM?-tNXn zmb6Gzi(+9oCjd$tJva(`9oo2a=<}dA5=q-rZ +8v|J5!lhp)VIKI4av66C<|0 zjTSk-d{*+!Sz(Sk;&QR0s!!>N(UY$4K%rM@X4euFpmR=?u4{Had`2!olyYmyaE8|Y zvp&wV*KUI|hDfT@yW$PoYDv+~5tLlPJ3=)mFwpD3K!4vR$#a)dKCebr*Q&K>e4Yg1 zV?FG3eM16-*IH~p(MFY>z5KEF?|%9I{RB2=(OpBAf0dKNIGKX6GG6u`9`m7vmg{Rd zU7=P$o2pO+m$|z>v~R_Ld5^EfTFYDE&VLPz)2W_qviM_yx$vuanFBr=eMRkFym1PW z A2d$y1eb kcWu_b4K&UAq6<}GdHbB?bt zZhDu|dA#;SY^Oe9Y>(cy%)gL5(ATW$j8l!j&B>3NR;zZ8k$A}n!mL*t=(#;~nlh2x z0mI)b10*Y86v1obGyCq&NKxK lXRO9m)QTL$3xFkNa{bW!3%KHNxAdw&JoAXJ_3%JC7ZN zO>5(!|AZWo6)ebkjzJu<{B9|SxqJw_#%TI{4 4 z@w ISE((VYSIN $*{Zq zMUu`;NAZH^#3GNDnq!W8{mOL5FMjAie73G9B+bwBZ9Y)F!x3NPDFQ)xZD6zo4J|D< z*>55h_cgzt*w9lyxIKm}02x7yA}Eq^_%z^1hwC$$9=KD9Z$5k0rW_ePNo!rj>(^cFj(_`_q%3x0pm1$^9x0|-k6nMBJA{a#rGw$B8jOIc zBkB-SkC#r*nd9G{tdW(R(Ccs3^m79&kfb9VES!?4)&H!Mw^zlWL92ZEFsO7#-Oe6Y z=9prSa|C__wlWu9aq>5!+fzO#{`=+q#gY+6%+1GQzM#2xdTHq^nT$qx`ow1sa5~=Y zS(KHX2FuWaP2UexY)TjCI@9NRK6$~3&ciyrAAUh`n;mwquXlp`jfM36sS)dp4NVZ- zl}^_@ZR0*T-!m8%&fa5NKGTGjY@F0gt0ULDxKFMx+miIKu+Rn?oazFxK!+n25U`6H z@+B5t$6CAJF{h#0$oORcn&gM2%!3t?VUkS LOq>3cl zsE?7{Q`DzQt>ASU_z3u=zxJ_ej@Q< 0}< (6zVcNm};?9+1e;>C+Nru76)P^%poyOmkG2`uNh<_l3CaHs75Vr=Yb@dypz zbkBVQ^Ha_2Zp!uLp1xt%^3gmvQG#p^*%2)0{#CEr--Jkf!ZLXt`b!4CldMSutFyT} z2< *AY`Cg}RyF?2ue{?nH8Zj0ekE z`{v|v3T2c}|NjLbk)p%7AdrN3N8me^=&>)+?Hqv5G$KcP)~vP)+`>R~?dcn(Jd)$g zKId0}q8!khcBgt(l5S}DrRyZM0RyWn=7mn;1r+)TH}jb);7`)?T2=%1EExG7CCgav z*?tC}2$<@kx~snZG@hi-wv1m~cf9VycB0g^d3{Xd%eAEJHxWPczZ=UpDZN+Xp0qx& zR(97@aqv^gFbrK$t+VdWMQBLnToA(Xa+0t{ys$vL;bGJTJ!MKOT;?HB)ntF)vgz}s zuO9UJ)Z&av->j(mAl7y%{!1;LCt#Zx91=^zzyhgt802Y!qt%!hw>*rVCFpz5#W9GT zuxms)NOSpESGAZ?39{P5n0iXAtZ_HZ%k#a9w&uKUqiw1JNk2=P?1lFtt^UH=&AJe& z)Ynt+#WsprZ68^fbbC=zA*F1Q*xRKLMPugg)@2xHmU!B}Z
a`BKD+M?GbT52 bH XlADK`cJvcTw`1(x3 zSn~4nyHULok;UeMKb3HYce`_GXNduHhdCxq$sIbW*gv0N-M*Za)mE&0tkmbVuVP u~oY*#aX28~?lV%#P-ckns{BHaDB!PUlt$(|8IWKD1p~t&`Js zC&~NUK4iJVCM<_1?{9ut6x+9QyZg5{KkA@E7+5fJt!>9&Bk3D5SH(%f6W}wP44=fS zsl$G6Q|1p>&B?Tq`uzFQ>pyU_ti%S{8%}DGHnuw8{W?pVV?Gv4G9I{%q@ H-cJ77pG^&vQn>h{?A3NWfyH6hw~u< zfb2Z{@@3ndJw;T8tS=iJF!KG%2fg``iw^WxiwSQ%G8W }0EC=N_CQe$y4aO||jc zDZPgaCK(LSwJ0n8`sT)^FtFD(H5F$Z!-hO#?otwrRIfV`kG#XrdYHcoP(Y>MUr aytV&dJpQPn+Gx_4h4@vPXznnE8f4MOwqxRJ2Z{AocsjUOw^f0lD z(FqON#~$lyV2gV!C3deBv}%I|07s5=E;*QDaPEJjk==T{M|a$k)Y&lSP#c~i_ijxY z+{mM1vwan_#z;B}bL->Lykk_>iGT?r9J#N@vwy=;h;+GT#En!0r1ud_$r;6^lc{D% zKJKzQOZRE(+(EdiHki2T$=%tVB{pa-nnrGYx74yATJeT`%K=A8zZ+*){~D*nya#%+E( z4HKe{PMR;70W^;Q_Vju4-B1Ze4KzgcH;gxSY;-+|LR>=N=gN1J=DS4uAd}|=2MA|( z$L>pHT9R}z9Md)pc;&ie`^0-MxyQ}?Sro{XKvfE$RLO*=`sfI1-HyRM16fpIwz2l9 z)hlvxPK? =wwPrVAJNFv)z_HCy)5C%_hg6m1BdW>JxWsLne-3}{b zs#hkxksBGafr+#i*=yH5fBSYktRK2tE-*sEKMMgIJik@Cs{-_la>`mvv&*p;iVYoE zSoXC(Xu^WFpt9z>h)GG~dMvh?99-`U&`-zO!lLuQm0UY((R&pN^!<3OAj~Y3z?X0` zNU5OaXzO-r1U_V8%LYqvIu1HsfaM<`8%bafo&pABW=Ynhl63|dF>2M@X}vn35nCE= zn{HD({27g(Wki6GPq59v`w9E5(cwwimUSHTLSxpdZ`;0o|IYDJLoYxVou^ou?|{#Q zMf>NF(_^&!6)>2 PsZ$1mS1`7GZ+`|6 zX@)v`eH`DZtd?~uKK8lYzN)!!pXj%ruGY1&aZ6s)Jy*sq*yA2KCwUVB(2G~Ees&aE z(jsN(#fsUo8!E{##L37xGyJys(`Z;XGkC3rN3f0UdjzEqA&-}(*Uz?XRi1P9qSu?L z8xP RGRvJQm9P}- z{XTR=v=>0r(XcJ~;nn e4vU;p5HPODCH-D;!?!kAF-o`p@34cD*3zxudy-diu z=}WB1a-wQyP_x`&ob>`Kpl(PrUJxL8yYb=Ee}5J9nXck#&@W%Csll8>-I#mS5Epjb zNazElupaE`wDIY#J2`9B#n_E_Uo7`GQ$JL%+At+E%;s_Y^Fd=MWez5E?~f5c9f@*i zOZl1e|0%ovV%1F#j#N9283D<=W@OQ&$4{o^jgl&-vuvMWPid>_n%ClWlKUNT8bE<~ zCSr9}q)*L)kaJ$O0#B_NS
t+;e)~eFi2xkcFzX#MrfR8eO_-^(<77fdr=e z)gRuTz4w38DoSAR;SX^Rm#E|Vulg(WMv?17L|N^Lp)(EOfg|cfof $M0Es!MSK-w(Q-45{8EYHznas}RqPe4;*t>h?j#nO0T+~$w zD_Yt(XJXC;AcLm`M-{qC*Jd7!eO?n(X$$wG68O*!D0y4WR-L`kgl_8ToQ?=_2rgfg zmrFYGMqDknzBS}3%nGg)+`K_)Fz8gi667K%J4w&%;N{hO3 4S=?^8mH@81ueE&R zHhP1zIKDW0?x!izcEt3@Jr~`s-F;qjzPfjd>933RIV&Gq^~N1u#%S#gIXDH=aL; zCUuM>4^y2C-9J}NYx+8?gNSnk eKH% zuL-rVaa?z2d%0@Gt5^N0?MHV~*!uD$4hl3$L1AJ!rRW&`-!6#9RmBA~F+nlGI((k5 zPbXhU`~-^7NaBmTEr~Ds#b0C6&9+}${1j(2_V)q4+OB`ME=J%0qge@ ^smed|)>- MW&NXW%cPI-b#8dP{puw7=xb6)kwGLe|8Ic`kt*)XpJKm zjc;%)3n;Pphx|;i5JrSOO3WxTHKAph3THT)yHI`Ks@Uo|$+I}p!^@4Fk_P8_% z%wbU0cxOE^Hzi^IPby$%^8kOD2u18sM59m2YA?uNvkL@~X{wFhcK6nn^i`F-yLxcS z Y`@i|LDf<98=8x|lx?giCo!t}s4Z2yBkv%|%5kY#vBRkq0*m#z#s& zh1s<4Z&RBGR4P)6Z;@w0Lwv0#;$Xjh`u$_{D4Sw~A>DABpjFEMK8~pvTJFW9upwuQ z#{J~llvfI( wk+iZPr-}RE?Kn7H=zlo6 6qXRhAkoy*6?GSE2!GzDNYtCP(`IckEJHowQ{gC5G z>zV$1w4QksQt6x^_y5>???9^CKYqAQ$jlx|ISnc!6rqgM)UXPfA 4HDg@ zB&%U3tB|cSns!EJDneHF>Um$Eqi*B){{DEL=bz^}_h0TDpL2b#>pfoY*Lz&c{Qp(8 z_L0I2(mh&qS!tSG^BQR92zm3fX92uL*W@@gxoo6azPt^L>Z!16eaN{+C{yjPq+f1y zxbXDvuldAfZ7%+@Bi=MHO11MT`1@(9x|>eQ{TRv`V|bk4@(^wmf{x0+He~jj#2Ege z|KTDiQ;4W8tUZYha2H@dP)O5W+2sq2lKZ2RULTDk8n}4jDrAd&dO0mSe+;p2D|si1 zJ<7NqQE&cDO-(vr)t~jA$3~jY73OdB4Ovc!Ntp%uc7cst^?^G#Z-wF(f;YlVo);io zQv4m5==RNOjmC%L4+?}6vn!II1m^cvPM%3Wxl7lunl2$V)K!289+;OIcsJzwoD;zW z?YnXd)CnS@c~%ZU6Ru%RgaH}pn7uiAUAkIgK+{6qezdQZUyaDGypP5=I{2a^>0P35 z&@74=v05nAfVAU1$2Ns|XAf#6gtfGU$edl568Oh@*5YDoTfClyma2i{=naob7W9#C z(i|@>OcVkW&<%NRH&N MsBu#e_qz|xp{i{-)G80c2Us>Jip&8 z2_c!2eJlq)|9TCR)*ili@q#v|Yd(h!0##211-tCX)Id*{wtnXTR(k2ec0ibKvIV7w z`>GQ?hNfKT*M5&I!zUQ4-wnf0si7DuIPtX~#z0mJB%OToFVYxj)zZ@blCl8Juxz@P zUmdS)3R7>IJUyE{er-M&MuTJ?a`&K{M!YLmj$xMO&}ywW3`~ZuKcBaQV}9_=r=aub zMr#uHsDK(IU^JUG46olm-V>{^Waaz>01S~(#!t1d!?4h6C{r`u8hx^j7i-9m(`)Ih zt*y; P&J;TC8RJQ zNE)O25xA^@X u zoF@8BPU_v>?S*dfzIExR+CG~5zO2s_FnFJ@L=7`BkzoO*ffvXc{+zFKv-8-C5e;a; z+ZRJ={}(8*mDsT(*f)p@V}%1n5viu!tcd!Qn}18!-1}(x0LZjHuRwralf|+5Es9}X zgdx&AOhJrJJHhD%rMt?Gb+QW-aw+Zw8}Q+VnAtbX4vNp9!jz0tv~M}=S0dzQn?mD7 z+5sg0K{JU+OKhLwDoIp~^S}APIX*%;bH&xOE#!<(eyf7Qq_a?81LfZPUwCi9ly#E^ z@!87)BYqIwAuuz>X`A1tkuasFAY> Yi5w=7_VYDx6A(IQny;@@cYH&3E!zUK8YW><+EwQ%)>ZOjut-(iR))l!up~A+HvS z$WIp1EMOJj>#DZ|+)2Fw1Fs8V+~ay-g&B^Cjdrq2^XnZ)jatvvv!*I~76EAobwC_O zohnrFMl)80k}?bSo0A~SN4IbS+&j4?s~Uq Ov7#SkW#Vmw^jD}qItS{$ov7q07J! (+vz{0nOhQ9(Duk;?AoW_6>_EDBcaE=*_?N1JS>Y^ >j~MM_5t8@X1wh8&@*auEf33 zeMm+lzw4ukgfLT`72z5%x-gq?Y{eT=R82(r+cQTap%Z{O#xX;O1FNd=N__kL_k@qc zaRHn>;WnHa?gwNUC;bYzLLw(_H~_m$_ juYIY2*!J_RsK8x{(e5;H20} zNHRM|1qfTH1qB7aK*zt{YT*({d2VOl5w@__;4 zWi!5H&V%snxH_-hb$RW!btwYSgv|z#@#&B2xv*Q>zM`6bL-YLx@QNkLv(#^4B@{_i zf|QMcMDOoTEiHnQU=-dfjJ9~a36>@Kh&50c-jXc21#L@yF&cmP2#9FJM+14-cGsO$ z$20d}Bd$|HcNsgXTx)G@<>BS^fi(SAAHx{*F(_$+q@qSMs)ygSem4Dc{UH~OftmX# zB+qJWC2KalU9$zc;C&w{`4>T<`Pk#{Gg*>LFn!Pyt6^)ohI&B|*ay?>YO<4bq2WAz z*knZPUiwJQW8hYKUENCLZGP52A5hu<6@9I`1LX1dZQPFj=B)-A9hVH|wdB~=d9`Y! zMXImHf#LE)_pv0U6u{)cHc+hUn;A%z2*!HwmQjU~XuPJ75nf+NG+wU rZ`^tEdRPe2m!Z|-}J z)Gi@*!j|o=&vkc2{3n>!Y`BrTni`K~E}YS2=*1No2uF9amfYFX1rQu=Jl9mK*t$^J z;Z+WSxE%~1dvE?_e5ecI8E0VUqa4EbV|#{OPU&>|6jVA8qv<8LZ!ZEGgr4&alu@hM zCNALf(1BJ9Lk^NxaoALmYX-c$hGMID?mY-M$DD&h(3<8ZjDz`%hOALp*qxp4?#M&U zVvH)4?n3MiB4tZd97`_*i^M@9SlHRwD|?EWu=O>;*1dI|-V2z6XvqN8VM35W=7cOl z*l^Bia_V)FAQ&K@f7`aWzP$WMt!gXu@>63tMhk{kMfeEn$HBtV|4aVFo~E5wk42IX zadTW<-cAJ=O?VcXUJ$DV)w;$X$3L;6-+$GPh|0@rzOa2Pl>a6eOXE+-kD&`pQ1uI> zzja5VmyuBhDq3YhaRr4nGqFlhM@Pp=l@QeSL8NC*dbrD5`LQI@vU^w=5=11>=q{Mm zO}hDD{_B2M#=Dlm5Gdgk&%1JP{Unq`p7^`K!(d2qzweQRT?}F;f`D)gE9pb0S;%1Y zp8>0Ll}lL*B@z|-uc^I;bO=4 !m{DxQ z_ZdSYPV(u-OoRXsZw+aP0k&Yw02^NhDKVpMN3$%x?lT4!5`{u_R%C$}eB=n;m0$&l zrDVux^6>FpPjB{sOG X$9_DxO; zKOtQTwT-plwjR`?T5$;piT a^<_-&6zS6#_z83SkNZOg_bW;j`eU8=hf zk3@t}At#cap57NxjY@c+P5v84t`1mD^+06i&eaR3z-<+@C%vg*=WcTHl3e6mS%VM1 z!2uE4?=9x`09F8%A5fkg_toJW5(t=L`J=Ztokm4h?LiA2OUz#4lS}E4o`aePHh?&R zK@UUFUM*N0 vyGmADUlnr%`i>+%b$$1i%8Q!QZxRUUlru zQ-yRRqM`)B+OL}!shGj&!m`I-AXwB5iT~S;x9Ni5c1F(sQWM9le6T`kwCQsem;a0k zXoWVIT(|kj^bTOEv=a?OKTYu>Q%E?AstAg;V8|v*Pzq8j24Nl>7^nGm#9~1b3Og_> zR90{e=0 lF z *O&i!nDTMF z>zQ~srR{c2d|qB&-dZc7Jksx=%@F4X!T7%W2tRtuhxve*QK>ql5u%;1n9bX*895QZ zmw$ *|nIr9>#p%-?dK#oC zalT3rj8`DtGKD%5s0G9<$=JX**$ypkv`2ks;XL@c`w`4d< Bh`g$K|w*AY$5|}W5u^_J^JLtATmdBY~a1LRMJnv;H5i+w2>O<(0-Vl zoUF5_V@?P6f5&ZxSBF|DsH#I=L $|NclpNulK0O@cqvQFrq|M5P~YJ4j*P< zny*}>Jp`mY98nAS4T>k$19{HyS?H^b0m?t&Ye4kQ$;)3_kEYVYm_wl_#?YO4Is5Xa zE##jSv=`*KyUsk^4|xSd`GJPrdmM-j#qZkyhoDw*7BzkGvxAEH!VPU5ot-}2IrD1M z{~QPaQE*3?j?MTjpkv-`V?6?!ZoCGx|NXMewXkPuM4O>Yh*5{ZO)i3XEAoqja+0*P z?TEhsALCukgt~~z&SO)Gj1lm iV+fc0f0b}NdcLv9Sls2zbW-<1d7kP0^cyy73hpjsmw zAn!bkd>7I%sLMbaR-ASo8R2RX01(0EMm>OlgaSkZ1GN&&-kqwhnaF!{@MZ|9`S?HC zz#d7UW;n?l0pltZz`>!`5(rd hUgJ6bQ=( 2Sv+WB$>-4LO!dt`Z-5WL}{b z!5>VuWpgjhA}drNb~oQig_ON*JqJwI_J?AOeaGBT=?UREc!`*_m3PIK;%?gdqY@~n zqD7#Sj0L%v|BO1N`W)1Bast#q!Nbcm`pTZy2%B#*1%W#y=dybv!T18MPC*Y5EYJ GYVG!q@5N%N?nN9U677qRaDrB0tiT& z-L v6EpLFFiz&?hNBZTn)^C10#h aCsJzUvPWfUZ~YJ)*G>cYzuLWE9qf@Tj@m@Y7&%!qSmG+Lmk67s5^Q)Vo4T> zp)VE;n7Z#YOlX7@Btsz$QeIL=2=N?3C@WGoK$VOM(Aj{gVHEQ2MO*}}=5@lb@o}(V zt-G>pc6!4g-gJ|5w;)49nnK%t%#2ECcLZG8iP&) R6}asKG;cG2!V&jI-rOPwXnp $%kJ9^1OJngUKT)|4F75D z*5yOUYrSr{yq?gaMx7EF4iN$UJ4NKR9w`R)lS#LLnE>k`lms)hbka{BN0?eQTw%4T zsj2_=b7(rM088IHL%IN_T^I8o;p#kThXBMq+~>O{rvodrUHF;+pJNGdfe_5(5uJh7 z{V;l0t^VUd_$m$!I8=%AL!kESXLUokeGQ&F;9-_7