From 27cf53e41f8b379f606c1c27620ee08e79bac57e Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 6 Jan 2024 22:05:16 +0000 Subject: [PATCH 01/25] refactor: rename string_wrapper > unit_struct --- src/app_data/container_state.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 247d6ff..0f81b9e 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -49,7 +49,7 @@ impl PartialOrd for ContainerId { /// TODO - use string_wrapper for ContainerId? /// ContainerName and ContainerImage are simple structs, used so can implement custom fmt functions to them -macro_rules! string_wrapper { +macro_rules! unit_struct { ($name:ident) => { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct $name(String); @@ -90,8 +90,8 @@ macro_rules! string_wrapper { }; } -string_wrapper!(ContainerName); -string_wrapper!(ContainerImage); +unit_struct!(ContainerName); +unit_struct!(ContainerImage); #[derive(Debug, Clone)] pub struct StatefulList { From 53b4bafbe53312fe41608ddf33e865d474222aaa Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 6 Jan 2024 22:05:32 +0000 Subject: [PATCH 02/25] chore: dependencies updated --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac77486..7efd076 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -881,9 +881,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.75" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -992,18 +992,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.194" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.194" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", From 40ddcb727d2c1758d6dd26a58507b85b219f51e2 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 6 Jan 2024 22:32:09 +0000 Subject: [PATCH 03/25] fix: header arrow on left --- src/ui/draw_blocks.rs | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 8bbc176..708f197 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -364,22 +364,22 @@ pub fn heading_bar( // Generate a block for the header, if the header is currently being used to sort a column, then highlight it white let header_block = |x: &Header| { let mut color = Color::Black; - let mut suffix = ""; - let mut suffix_margin = 0; + let mut prefix = ""; + let mut prefix_margin = 0; if let Some((a, b)) = data.sorted_by.as_ref() { if x == a { match b { - SortedOrder::Asc => suffix = " ▲", - SortedOrder::Desc => suffix = " ▼", + SortedOrder::Asc => prefix = "▲ ", + SortedOrder::Desc => prefix = "▼ ", } - suffix_margin = 2; + prefix_margin = 2; color = Color::White; }; }; ( Block::default().style(Style::default().bg(Color::Magenta).fg(color)), - suffix, - suffix_margin, + prefix, + prefix_margin, ) }; @@ -390,30 +390,26 @@ pub fn heading_bar( // Yes this is a mess, needs documenting correctly let text = match header { Header::State => format!( - " {:>width$}{ic}", - header, - ic = block.1, - width = width - block.2, + " {x:>width$}", + x = format!("{ic}{header}", ic = block.1), + width = width ), Header::Name => format!( - " {:>width$}{ic}", - header, - ic = block.1, - width = width - block.2, + " {x:>width$}", + x = format!("{ic}{header}", ic = block.1), + width = width ), Header::Status => format!( - "{} {:>width$}{ic}", + "{} {x:>width$}", MARGIN, - header, - ic = block.1, - width = width - block.2 + x = format!("{ic}{header}", ic = block.1), + width = width ), _ => format!( - "{}{:>width$}{ic}", + "{}{x:>width$}", MARGIN, - header, - ic = block.1, - width = width - block.2 + x = format!("{ic}{header}", ic = block.1), + width = width ), }; let count = u16::try_from(text.chars().count()).unwrap_or_default(); From 58ef151600e362048a607c8ae61a5edfe80ab1dd Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:54:08 +0000 Subject: [PATCH 04/25] chore: dependencies updated --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7efd076..b806ca6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" dependencies = [ "anstyle", "anstyle-parse", @@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -237,9 +237,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.13" +version = "4.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642" +checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" dependencies = [ "clap_builder", "clap_derive", @@ -247,9 +247,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.12" +version = "4.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" dependencies = [ "anstream", "anstyle", @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -443,9 +443,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" dependencies = [ "bytes", "fnv", @@ -658,9 +658,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libredox" From 1806165c3e266876b2d1806f7b662d09705f3aad Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:54:15 +0000 Subject: [PATCH 05/25] chore: Cargo.toml formatted --- Cargo.toml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1f5d3fd..a1cd0ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ unsafe_code = "forbid" [lints.clippy] expect_used = "warn" nursery = "warn" -pedantic ="warn" +pedantic = "warn" todo = "warn" unused_async = "warn" unwrap_used = "warn" @@ -29,15 +29,15 @@ similar_names = "allow" anyhow = "1.0" bollard = "0.15" cansi = "2.2" -clap = { version = "4.4", features = ["derive", "unicode", "color"] } +clap = { version = "4.4", features = ["color", "derive", "unicode"] } crossterm = "0.27" futures-util = "0.3" -parking_lot = { version= "0.12" } +parking_lot = { version = "0.12" } tokio = { version = "1.35", features = ["full"] } tracing = "0.1" tracing-subscriber = "0.3" ratatui = "0.25" -uuid = { version = "1.6", features = ["v4", "fast-rng"] } +uuid = { version = "1.6", features = ["fast-rng", "v4"] } directories = "5.0" [dev-dependencies] @@ -46,6 +46,5 @@ directories = "5.0" lto = true codegen-units = 1 panic = 'abort' -strip=true +strip = true debug = false - From c9049dacd95de4f14a09260c3539a778edf141fb Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:17:01 +0000 Subject: [PATCH 06/25] docs: changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfeb98b..bac167f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +### Chores ++ dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd] ++ files formatted, [1806165c3e266876b2d1806f7b662d09705f3aad] + +### Fixes ++ sort arrow now on left of header, [40ddcb727d2c1758d6dd26a58507b85b219f51e2] + +### Refactos ++ rename string_wrapper > unit_struct, [27cf53e41f8b379f606c1c27620ee08e79bac57e] + # v0.5.0 ### 2024-01-05 From d0b27211928f93f8455e1ee5a6a6485c6a21d382 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 13 Jan 2024 13:31:53 +0000 Subject: [PATCH 07/25] chore: create_release check for unused lint --- create_release.sh | 144 +++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 67 deletions(-) diff --git a/create_release.sh b/create_release.sh index a66b1c1..f3c90c2 100755 --- a/create_release.sh +++ b/create_release.sh @@ -1,7 +1,7 @@ #!/bin/bash # rust create_release -# v0.3.1 +# v0.4.1 STAR_LINE='****************************************' CWD=$(pwd) @@ -12,15 +12,14 @@ YELLOW='\033[0;33m' PURPLE='\033[0;35m' RESET='\033[0m' - # $1 string - error message error_close() { - echo -e "\n${RED}ERROR - EXITED: ${YELLOW}$1${RESET}\n"; + echo -e "\n${RED}ERROR - EXITED: ${YELLOW}$1${RESET}\n" exit 1 } # $1 string - question to ask -ask_yn () { +ask_yn() { printf "%b%s? [y/N]:%b " "${GREEN}" "$1" "${RESET}" } @@ -30,19 +29,19 @@ user_input() { echo "$data" } -update_major () { +update_major() { local bumped_major bumped_major=$((MAJOR + 1)) echo "${bumped_major}.0.0" } -update_minor () { +update_minor() { local bumped_minor bumped_minor=$((MINOR + 1)) echo "${MAJOR}.${bumped_minor}.0" } -update_patch () { +update_patch() { local bumped_patch bumped_patch=$((PATCH + 1)) echo "${MAJOR}.${MINOR}.${bumped_patch}" @@ -56,8 +55,7 @@ get_git_remote_url() { # Check that git status is clean check_git_clean() { GIT_CLEAN=$(git status --porcelain) - if [[ -n $GIT_CLEAN ]] - then + if [[ -n $GIT_CLEAN ]]; then error_close "git dirty" fi } @@ -66,8 +64,7 @@ check_git_clean() { check_git() { CURRENT_GIT_BRANCH=$(git branch --show-current) check_git_clean - if [[ ! "$CURRENT_GIT_BRANCH" =~ ^dev$ ]] - then + if [[ ! "$CURRENT_GIT_BRANCH" =~ ^dev$ ]]; then error_close "not on dev branch" fi } @@ -79,8 +76,7 @@ ask_changelog_update() { printf "%s" "$RELEASE_BODY_TEXT" printf "\n%s\n" "${STAR_LINE}" ask_yn "accept release body" - if [[ "$(user_input)" =~ ^y$ ]] - then + if [[ "$(user_input)" =~ ^y$ ]]; then update_release_body_and_changelog "$RELEASE_BODY_TEXT" else exit @@ -89,17 +85,17 @@ ask_changelog_update() { # Edit the release-body to include new lines from changelog # add commit urls to changelog -# $1 RELEASE_BODY -update_release_body_and_changelog () { +# $1 RELEASE_BODY +update_release_body_and_changelog() { echo -e DATE_SUBHEADING="### $(date +'%Y-%m-%d')\n\n" RELEASE_BODY_ADDITION="${DATE_SUBHEADING}$1" # Put new changelog entries into release-body, add link to changelog - echo -e "${RELEASE_BODY_ADDITION}\n\nsee CHANGELOG.md for more details" > .github/release-body.md + echo -e "${RELEASE_BODY_ADDITION}\n\nsee CHANGELOG.md for more details" >.github/release-body.md # Add subheading with release version and date of release - echo -e "# ${NEW_TAG_WITH_V}\n${DATE_SUBHEADING}${CHANGELOG_ADDITION}$(cat CHANGELOG.md)" > CHANGELOG.md + echo -e "# ${NEW_TAG_WITH_V}\n${DATE_SUBHEADING}${CHANGELOG_ADDITION}$(cat CHANGELOG.md)" >CHANGELOG.md # Update changelog to add links to commits [hex:8](url_with_full_commit) # "[aaaaaaaaaabbbbbbbbbbccccccccccddddddddd]" -> "[aaaaaaaa](https:/www.../commit/aaaaaaaaaabbbbbbbbbbccccccccccddddddddd)" @@ -111,20 +107,19 @@ update_release_body_and_changelog () { } # update version in cargo.toml, to match selected current version -update_version_number_in_files () { +update_version_number_in_files() { sed -i "s|^version = .*|version = \"${MAJOR}.${MINOR}.${PATCH}\"|" Cargo.toml } # Work out the current version, based on git tags # create new semver version based on user input # Set MAJOR MINOR PATCH -check_tag () { +check_tag() { LATEST_TAG=$(git describe --tags "$(git rev-list --tags --max-count=1)") echo -e "\nCurrent tag: ${PURPLE}${LATEST_TAG}${RESET}\n" echo -e "${YELLOW}Choose new tag version:${RESET}\n" - if [[ $LATEST_TAG =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$ ]] - then - IFS="." read -r MAJOR MINOR PATCH <<< "${LATEST_TAG:1}" + if [[ $LATEST_TAG =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$ ]]; then + IFS="." read -r MAJOR MINOR PATCH <<<"${LATEST_TAG:1}" else MAJOR="0" MINOR="0" @@ -134,51 +129,53 @@ check_tag () { OP_MINOR="minor___v$(update_minor)" OP_PATCH="patch___v$(update_patch)" OPTIONS=("$OP_MAJOR" "$OP_MINOR" "$OP_PATCH") - select choice in "${OPTIONS[@]}" - do + select choice in "${OPTIONS[@]}"; do case $choice in - "$OP_MAJOR" ) - MAJOR=$((MAJOR + 1)) - MINOR=0 - PATCH=0 - break;; - "$OP_MINOR") - MINOR=$((MINOR + 1)) - PATCH=0 - break;; - "$OP_PATCH") - PATCH=$((PATCH + 1)) - break;; - *) - error_close "invalid option $REPLY" + "$OP_MAJOR") + MAJOR=$((MAJOR + 1)) + MINOR=0 + PATCH=0 + break + ;; + "$OP_MINOR") + MINOR=$((MINOR + 1)) + PATCH=0 + break + ;; + "$OP_PATCH") + PATCH=$((PATCH + 1)) + break + ;; + *) + error_close "invalid option $REPLY" + ;; esac done } # ask continue, or quit -ask_continue () { +ask_continue() { ask_yn "continue" - if [[ ! "$(user_input)" =~ ^y$ ]] - then + if [[ ! "$(user_input)" =~ ^y$ ]]; then exit fi } # run all tests -cargo_test () { +cargo_test() { cargo test -- --test-threads=1 ask_continue } # Simulate publishing to crates.io -cargo_publish () { +cargo_publish() { cargo publish --dry-run ask_continue } # Build all releases that GitHub workflow would # This will download GB's of docker images -cargo_build () { +cargo_build() { cargo install cross cargo_clean echo -e "${YELLOW}cross build --target x86_64-unknown-linux-musl --release${RESET}" @@ -199,9 +196,8 @@ cargo_build () { cargo_clean } - # $1 text to colourise -release_continue () { +release_continue() { echo -e "\n${PURPLE}$1${RESET}" ask_continue } @@ -212,14 +208,26 @@ cargo_clean() { cargo clean } # Check repository for typos -check_typos () { +check_typos() { echo -e "\n${PURPLE}check typos${RESET}" typos ask_continue } +# Make sure the unused lint isn't used +check_allow_unsued() { + matches_any=$(find . -type d \( -name .git -o -name target \) -prune -o -type f -exec grep -lE '^#!\[allow\(unused\)\]$' {} +) + matches_cargo=$(grep "^unused = \"allow\"" ./Cargo.toml) + if [ -n "$matches_any" ]; then + error_close "\"#[allow(unused)]\" in ${matches_any}" + elif [ -n "$matches_cargo" ]; then + error_close "\"unsed = \"allow\"\" in Cargo.toml" + fi +} + # Full flow to create a new release release_flow() { + check_allow_unsued check_typos check_git @@ -231,20 +239,20 @@ release_flow() { cd "${CWD}" || error_close "Can't find ${CWD}" check_tag - + NEW_TAG_WITH_V="v${MAJOR}.${MINOR}.${PATCH}" printf "\nnew tag chosen: %s\n\n" "${NEW_TAG_WITH_V}" RELEASE_BRANCH=release-$NEW_TAG_WITH_V echo -e ask_changelog_update - + release_continue "checkout ${RELEASE_BRANCH}" git checkout -b "$RELEASE_BRANCH" release_continue "update_version_number_in_files" update_version_number_in_files - + echo -e "\ncargo fmt" cargo fmt echo -e "\n${PURPLE}cargo check${RESET}\n" @@ -283,7 +291,6 @@ release_flow() { git branch -d "$RELEASE_BRANCH" } - main() { cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16) options=( @@ -297,24 +304,27 @@ main() { if [ $exitStatus -ne 0 ]; then exit fi - for choice in $choices - do + for choice in $choices; do case $choice in - 0) - exit;; - 1) - cargo_test - main - break;; - 2) - release_flow - break;; - 3) - cargo_build - main - break;; + 0) + exit + ;; + 1) + cargo_test + main + break + ;; + 2) + release_flow + break + ;; + 3) + cargo_build + main + break + ;; esac done } -main \ No newline at end of file +main From b9df4e446a4ede7f33932f0d6970a70f3e2a231e Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 13 Jan 2024 17:17:26 +0000 Subject: [PATCH 08/25] tests: AppData tests --- src/app_data/container_state.rs | 17 +- src/app_data/mod.rs | 1411 ++++++++++++++++++++++++++++--- src/docker_data/mod.rs | 2 +- src/input_handler/mod.rs | 10 +- 4 files changed, 1300 insertions(+), 140 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 0f81b9e..9844b30 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -60,6 +60,13 @@ macro_rules! unit_struct { } } + #[cfg(test)] + impl From<&str> for $name { + fn from(value: &str) -> Self { + Self(value.to_owned()) + } + } + impl$name { pub fn get(&self) -> &str { self.0.as_str() @@ -93,7 +100,7 @@ macro_rules! unit_struct { unit_struct!(ContainerName); unit_struct!(ContainerImage); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct StatefulList { pub state: ListState, pub items: Vec, @@ -234,7 +241,7 @@ impl fmt::Display for State { } /// Items for the container control list -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DockerControls { Pause, Restart, @@ -416,7 +423,7 @@ impl fmt::Display for LogsTz { /// Store the logs alongside a HashSet, each log *should* generate a unique timestamp, /// so if we store the timestamp separately in a HashSet, we can then check if we should insert a log line into the /// stateful list dependent on whethere the timestamp is in the HashSet or not -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Logs { logs: StatefulList>, tz: HashSet, @@ -475,7 +482,7 @@ impl Logs { } /// Info for each container -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ContainerItem { pub created: u64, pub cpu_stats: VecDeque, @@ -594,7 +601,7 @@ impl ContainerItem { } /// Container information panel headings + widths, for nice pretty formatting -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Columns { pub name: (Header, u8), pub state: (Header, u8), diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index a9fcadc..d508310 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -17,27 +17,6 @@ use crate::{ }; pub use container_state::*; -#[cfg(not(debug_assertions))] -/// Global app_state, stored in an Arc -#[derive(Debug, Clone)] -pub struct AppData { - containers: StatefulList, - error: Option, - sorted_by: Option<(Header, SortedOrder)>, - pub args: CliArgs, -} - -#[cfg(debug_assertions)] -/// Global app_state, stored in an Arc -#[derive(Debug, Clone)] -pub struct AppData { - containers: StatefulList, - error: Option, - sorted_by: Option<(Header, SortedOrder)>, - debug_string: String, - pub args: CliArgs, -} - #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum SortedOrder { Asc, @@ -75,6 +54,27 @@ impl fmt::Display for Header { } } +#[cfg(not(debug_assertions))] +/// Global app_state, stored in an Arc +#[derive(Debug, Clone)] +pub struct AppData { + containers: StatefulList, + error: Option, + sorted_by: Option<(Header, SortedOrder)>, + pub args: CliArgs, +} + +#[cfg(debug_assertions)] +/// Global app_state, stored in an Arc +#[derive(Debug, Clone)] +pub struct AppData { + containers: StatefulList, + error: Option, + sorted_by: Option<(Header, SortedOrder)>, + debug_string: String, + pub args: CliArgs, +} + impl AppData { #[cfg(debug_assertions)] pub fn get_debug_string(&self) -> &str { @@ -87,27 +87,6 @@ impl AppData { self.debug_string.push_str(x); } - /// Change the sorted order, also set the selected container state to match new order - fn set_sorted(&mut self, x: Option<(Header, SortedOrder)>) { - self.sorted_by = x; - self.sort_containers(); - self.containers - .state - .select(self.containers.items.iter().position(|i| { - self.get_selected_container_id() - .map_or(false, |id| i.id == id) - })); - } - - /// Current time as unix timestamp - #[allow(clippy::expect_used)] - fn get_systemtime() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("In our known reality, this error should never occur") - .as_secs() - } - /// Generate a default app_state #[cfg(not(debug_assertions))] pub fn default(args: CliArgs) -> Self { @@ -131,8 +110,29 @@ impl AppData { } } + /// Current time as unix timestamp + #[allow(clippy::expect_used)] + fn get_systemtime() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("In our known reality, this error should never occur") + .as_secs() + } + /// Container sort related methods + /// Change the sorted order, also set the selected container state to match new order + fn set_sorted(&mut self, x: Option<(Header, SortedOrder)>) { + self.sorted_by = x; + self.sort_containers(); + self.containers + .state + .select(self.containers.items.iter().position(|i| { + self.get_selected_container_id() + .map_or(false, |id| i.id == id) + })); + } + /// Remove the sorted header & order, and sort by default - created datetime pub fn reset_sorted(&mut self) { self.set_sorted(None); @@ -237,6 +237,11 @@ impl AppData { self.containers.items.len() } + /// Get all the ContainerItems + pub const fn get_container_items(&self) -> &Vec { + &self.containers.items + } + /// Get title for containers section pub fn container_title(&self) -> String { self.containers.get_state_title() @@ -262,9 +267,9 @@ impl AppData { self.containers.previous(); } - /// Get Container items - pub const fn get_container_items(&self) -> &Vec { - &self.containers.items + /// Get ListState of containers + pub fn get_container_state(&mut self) -> &mut ListState { + &mut self.containers.state } /// Get Option of the current selected container @@ -283,16 +288,37 @@ impl AppData { .and_then(|i| self.containers.items.get_mut(i)) } - /// Get ListState of containers - pub fn get_container_state(&mut self) -> &mut ListState { - &mut self.containers.state + /// return a mutable container by given id + fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> { + self.containers.items.iter_mut().find(|i| &i.id == id) } + /// Get the ContainerName of by ID + pub fn get_container_name_by_id(&mut self, id: &ContainerId) -> Option { + self.containers + .items + .iter_mut() + .find(|i| &i.id == id) + .map(|i| i.name.clone()) + } + + /// Find the id of the currently selected container. + /// If any containers on system, will always return a ContainerId + /// Only returns None when no containers found. + pub fn get_selected_container_id(&self) -> Option { + self.get_selected_container().map(|i| i.id.clone()) + } + + /// Get the Id and State for the currently selected container - used by the exec check method + pub fn get_selected_container_id_state_name(&self) -> Option<(ContainerId, State, String)> { + self.get_selected_container() + .map(|i| (i.id.clone(), i.state, i.name.get().to_owned())) + } /// Selected DockerCommand methods /// Get the current selected docker command /// So know which command to execute - pub fn selected_docker_command(&self) -> Option { + pub fn selected_docker_controls(&self) -> Option { self.get_selected_container().and_then(|i| { i.docker_controls.state.selected().and_then(|x| { i.docker_controls @@ -302,6 +328,35 @@ impl AppData { }) }) } + + /// Change selected choice of docker commands of selected container + pub fn docker_controls_next(&mut self) { + if let Some(i) = self.get_mut_selected_container() { + i.docker_controls.next(); + } + } + + /// Change selected choice of docker commands of selected container + pub fn docker_controls_previous(&mut self) { + if let Some(i) = self.get_mut_selected_container() { + i.docker_controls.previous(); + } + } + + /// Change selected choice of docker commands of selected container + pub fn docker_controls_start(&mut self) { + if let Some(i) = self.get_mut_selected_container() { + i.docker_controls.start(); + } + } + + /// Change selected choice of docker commands of selected container + pub fn docker_controls_end(&mut self) { + if let Some(i) = self.get_mut_selected_container() { + i.docker_controls.end(); + } + } + /// Get mutable Option of the currently selected container DockerControls state pub fn get_control_state(&mut self) -> Option<&mut ListState> { self.get_mut_selected_container() @@ -314,34 +369,6 @@ impl AppData { .map(|i| &mut i.docker_controls.items) } - /// Change selected choice of docker commands of selected container - pub fn docker_command_next(&mut self) { - if let Some(i) = self.get_mut_selected_container() { - i.docker_controls.next(); - } - } - - /// Change selected choice of docker commands of selected container - pub fn docker_command_previous(&mut self) { - if let Some(i) = self.get_mut_selected_container() { - i.docker_controls.previous(); - } - } - - /// Change selected choice of docker commands of selected container - pub fn docker_command_start(&mut self) { - if let Some(i) = self.get_mut_selected_container() { - i.docker_controls.start(); - } - } - - /// Change selected choice of docker commands of selected container - pub fn docker_command_end(&mut self) { - if let Some(i) = self.get_mut_selected_container() { - i.docker_controls.end(); - } - } - /// Logs related methods /// Get the title for log panel for selected container, will be either @@ -349,16 +376,16 @@ impl AppData { /// 2) "logs - container_name" when no logs found, again 32 chars max /// 3) "" no container currently selected - aka no containers on system pub fn get_log_title(&self) -> String { - self.get_selected_container().map_or_else(String::new, |y| { - let logs_len = y.logs.get_state_title(); - // let mut name = y.name.clone(); - // name.truncate(32); - if logs_len.is_empty() { - format!("- {} ", y.name) - } else { - format!("{logs_len} - {}", y.name.get()) - } - }) + self.get_selected_container() + .map_or_else(String::new, |ci| { + let logs_len = ci.logs.get_state_title(); + let prefix = if logs_len.is_empty() { + String::new() + } else { + format!("{logs_len} ") + }; + format!("{}- {}", prefix, ci.name.get()) + }) } /// select next selected log line @@ -389,19 +416,6 @@ impl AppData { } } - /// Chart data related methods - - /// Get mutable Option of the currently selected container chart data - pub fn get_chart_data(&mut self) -> Option<(CpuTuple, MemTuple)> { - self.containers - .state - .selected() - .and_then(|i| self.containers.items.get_mut(i)) - .map(|i| i.get_chart_data()) - } - - /// Logs related methods - /// Get mutable Vec of current containers logs pub fn get_logs(&mut self) -> Vec> { self.containers @@ -420,6 +434,17 @@ impl AppData { .map(|i| i.logs.state()) } + /// Chart data related methods + + /// Get mutable Option of the currently selected container chart data + pub fn get_chart_data(&mut self) -> Option<(CpuTuple, MemTuple)> { + self.containers + .state + .selected() + .and_then(|i| self.containers.items.get_mut(i)) + .map(|i| i.get_chart_data()) + } + /// Error related methods /// return single app_state error @@ -485,36 +510,9 @@ impl AppData { /// Update related methods - /// return a mutable container by given id - fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> { - self.containers.items.iter_mut().find(|i| &i.id == id) - } - - /// Get the ContainerName of by ID - pub fn get_container_name_by_id(&mut self, id: &ContainerId) -> Option { - self.containers - .items - .iter_mut() - .find(|i| &i.id == id) - .map(|i| i.name.clone()) - } - - /// Find the id of the currently selected container. - /// If any containers on system, will always return a ContainerId - /// Only returns None when no containers found. - pub fn get_selected_container_id(&self) -> Option { - self.get_selected_container().map(|i| i.id.clone()) - } - - /// Get the Id and State for the currently selected container - used by the exec check method - pub fn get_selected_container_id_state_name(&self) -> Option<(ContainerId, State, String)> { - self.get_selected_container() - .map(|i| (i.id.clone(), i.state, i.name.get().to_owned())) - } - /// Update container mem, cpu, & network stats, in single function so only need to call .lock() once /// Will also, if a sort is set, sort the containers - pub fn update_stats( + pub fn update_stats_by_id( &mut self, id: &ContainerId, cpu_stat: Option, @@ -660,7 +658,6 @@ impl AppData { for mut i in logs { let tz = LogsTz::from(i.as_str()); - // Strip the timestamp if `-t` flag set if !timestamp { i = i.replace(&tz.to_string(), ""); } @@ -685,3 +682,1159 @@ impl AppData { } } } + +#[cfg(test)] +#[allow(clippy::unwrap_used, clippy::many_single_char_names, unused)] +mod tests { + + use std::collections::VecDeque; + + use super::*; + + const fn gen_args() -> CliArgs { + CliArgs { + color: false, + docker_interval: 1000, + gui: true, + host: None, + in_container: false, + save_dir: None, + raw: false, + show_self: false, + timestamp: false, + use_cli: false, + } + } + + fn gen_item(id: &ContainerId, index: usize) -> ContainerItem { + ContainerItem::new( + u64::try_from(index).unwrap(), + id.clone(), + format!("image_{index}"), + false, + format!("container_{index}"), + State::Running, + format!("Up {index} hour"), + ) + } + + fn gen_appdata(containers: &[ContainerItem]) -> AppData { + AppData { + containers: StatefulList::new(containers.to_vec()), + error: None, + sorted_by: None, + debug_string: String::new(), + args: gen_args(), + } + } + + fn gen_containers() -> (Vec, Vec) { + let ids = (1..=3) + .map(|i| ContainerId::from(format!("{i}").as_str())) + .collect::>(); + let containers = ids + .iter() + .enumerate() + .map(|(index, id)| gen_item(id, index + 1)) + .collect::>(); + (ids, containers) + } + + // ******** // + // Sort by // + // ******** // + + #[test] + /// Sort by header: name + fn test_app_data_set_sort_by_header_name() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + // descending + app_data.set_sorted(Some((Header::Name, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("3")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("1")); + + // ascending + app_data.set_sorted(Some((Header::Name, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("1")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("3")); + } + + #[test] + /// Sort by header: state + fn test_app_data_set_sort_by_header_state() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { + i.state = State::Exited; + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { + i.state = State::Running; + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { + i.state = State::Paused; + } + + // descending + app_data.set_sorted(Some((Header::State, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("1")); + assert_eq!(b.id, ContainerId::from("3")); + assert_eq!(c.id, ContainerId::from("2")); + + // ascending + app_data.set_sorted(Some((Header::State, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("2")); + assert_eq!(b.id, ContainerId::from("3")); + assert_eq!(c.id, ContainerId::from("1")); + } + + #[test] + /// Sort by header: status + fn test_app_data_set_sort_by_header_status() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { + i.status = "Exited (0) 10 minutes ago".to_owned(); + } + + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { + i.status = "Up 2 hours (Paused)".to_owned(); + } + + // Sort by status + // descending + app_data.set_sorted(Some((Header::Status, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("3")); + assert_eq!(b.id, ContainerId::from("1")); + assert_eq!(c.id, ContainerId::from("2")); + + // ascending + app_data.set_sorted(Some((Header::Status, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("2")); + assert_eq!(b.id, ContainerId::from("1")); + assert_eq!(c.id, ContainerId::from("3")); + } + + #[test] + /// Sort by header: cpu + fn test_app_data_set_sort_by_header_cpu() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { + i.cpu_stats = VecDeque::from([CpuStats::new(10.1)]); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { + i.cpu_stats = VecDeque::from([CpuStats::new(8.1)]); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { + i.cpu_stats = VecDeque::from([CpuStats::new(20.3)]); + } + + // descending + app_data.set_sorted(Some((Header::Cpu, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("3")); + assert_eq!(b.id, ContainerId::from("1")); + assert_eq!(c.id, ContainerId::from("2")); + + // ascending + app_data.set_sorted(Some((Header::Cpu, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("2")); + assert_eq!(b.id, ContainerId::from("1")); + assert_eq!(c.id, ContainerId::from("3")); + } + + #[test] + /// Sort by header: memory + fn test_app_data_set_sort_by_header_mem() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { + i.mem_stats = VecDeque::from([ByteStats::new(40)]); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { + i.mem_stats = VecDeque::from([ByteStats::new(80)]); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { + i.mem_stats = VecDeque::from([ByteStats::new(2)]); + } + + // descending + app_data.set_sorted(Some((Header::Memory, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("2")); + assert_eq!(b.id, ContainerId::from("1")); + assert_eq!(c.id, ContainerId::from("3")); + + // ascending + app_data.set_sorted(Some((Header::Memory, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("3")); + assert_eq!(b.id, ContainerId::from("1")); + assert_eq!(c.id, ContainerId::from("2")); + } + + #[test] + /// Sort by header: id + fn test_app_data_set_sort_by_header_id() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + // descending + app_data.set_sorted(Some((Header::Id, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("3")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("1")); + + // ascending + app_data.set_sorted(Some((Header::Id, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("1")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("3")); + } + + #[test] + /// Sort by header: image + fn test_app_data_set_sort_by_header_image() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + // descending + app_data.set_sorted(Some((Header::Image, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("3")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("1")); + + // ascending + app_data.set_sorted(Some((Header::Image, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("1")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("3")); + } + + #[test] + /// Sort by header: rx + fn test_app_data_set_sort_by_header_rx() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { + i.rx = ByteStats::new(40); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { + i.rx = ByteStats::new(80); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { + i.rx = ByteStats::new(2); + } + + // descending + app_data.set_sorted(Some((Header::Rx, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("2")); + assert_eq!(b.id, ContainerId::from("1")); + assert_eq!(c.id, ContainerId::from("3")); + + // ascending + app_data.set_sorted(Some((Header::Rx, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("3")); + assert_eq!(b.id, ContainerId::from("1")); + assert_eq!(c.id, ContainerId::from("2")); + } + + #[test] + /// Sort by header: tx + fn test_app_data_set_sort_by_header_tx() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { + i.rx = ByteStats::new(400); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { + i.rx = ByteStats::new(80); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { + i.rx = ByteStats::new(83); + } + + // descending + app_data.set_sorted(Some((Header::Rx, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("1")); + assert_eq!(b.id, ContainerId::from("3")); + assert_eq!(c.id, ContainerId::from("2")); + + // ascending + app_data.set_sorted(Some((Header::Rx, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("2")); + assert_eq!(b.id, ContainerId::from("3")); + assert_eq!(c.id, ContainerId::from("1")); + } + + #[test] + /// Sort by header when selected headers match + fn test_app_data_set_sort_by_header_match() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + // descending + app_data.set_sorted(Some((Header::Rx, SortedOrder::Desc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("3")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("1")); + + // ascending + app_data.set_sorted(Some((Header::Rx, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("1")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("3")); + } + + #[test] + /// reset sorted + fn test_app_data_reset_sorted() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result, &containers); + + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) { + i.rx = ByteStats::new(400); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) { + i.rx = ByteStats::new(80); + } + if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) { + i.rx = ByteStats::new(83); + } + + app_data.set_sorted(Some((Header::Rx, SortedOrder::Asc))); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("2")); + assert_eq!(b.id, ContainerId::from("3")); + assert_eq!(c.id, ContainerId::from("1")); + + app_data.set_sorted(None); + let result = app_data.get_container_items(); + let (a, b, c) = (&result[0], &result[1], &result[2]); + assert_eq!(a.id, ContainerId::from("1")); + assert_eq!(b.id, ContainerId::from("2")); + assert_eq!(c.id, ContainerId::from("3")); + } + + // **************** // + // Container state // + // **************** // + + #[test] + /// Get len of current containers vec + fn test_app_data_get_container_len() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + assert_eq!(app_data.get_container_len(), 3); + } + + #[test] + /// Select the first container + fn test_app_data_containers_start() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + /// No container selected + let result = app_data.get_container_state(); + assert_eq!(result.selected(), None); + assert_eq!(result.offset(), 0); + + // First container selected + app_data.containers_start(); + let result = app_data.get_container_state(); + assert_eq!(result.selected(), Some(0)); + assert_eq!(result.offset(), 0); + + let result = app_data.get_selected_container_id(); + assert_eq!(result, Some(ContainerId::from("1"))); + let result = app_data.get_selected_container_id_state_name(); + assert_eq!( + result, + Some(( + ContainerId::from("1"), + State::Running, + "container_1".to_owned() + )) + ); + + // Calling previous when at start has no effect + app_data.containers_previous(); + let result = app_data.get_selected_container_id(); + assert_eq!(result, Some(ContainerId::from("1"))); + let result = app_data.get_selected_container_id_state_name(); + assert_eq!( + result, + Some(( + ContainerId::from("1"), + State::Running, + "container_1".to_owned() + )) + ); + } + + #[test] + /// advance container list state by one + /// get get_selected_container_id() & get_selected_container_id_state_name() return valid Some data + fn test_app_data_containers_next() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + // Advance list state by 1 + app_data.containers_start(); + app_data.containers_next(); + + let result = app_data.get_container_state(); + assert_eq!(result.selected(), Some(1)); + assert_eq!(result.offset(), 0); + + let result = app_data.get_selected_container_id(); + assert_eq!(result, Some(ContainerId::from("2"))); + let result = app_data.get_selected_container_id_state_name(); + assert_eq!( + result, + Some(( + ContainerId::from("2"), + State::Running, + "container_2".to_owned() + )) + ); + } + + #[test] + /// advance container list state to the end + /// get get_selected_container_id() & get_selected_container_id_state_name() return valid Some data + fn test_app_data_containers_end() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + app_data.containers_end(); + let result = app_data.get_container_state(); + assert_eq!(result.selected(), Some(2)); + assert_eq!(result.offset(), 0); + + let result = app_data.get_selected_container_id(); + assert_eq!(result, Some(ContainerId::from("3"))); + let result = app_data.get_selected_container_id_state_name(); + assert_eq!( + result, + Some(( + ContainerId::from("3"), + State::Running, + "container_3".to_owned() + )) + ); + + // Calling previous when at end has no effect + app_data.containers_next(); + let result = app_data.get_selected_container_id(); + assert_eq!(result, Some(ContainerId::from("3"))); + let result = app_data.get_selected_container_id_state_name(); + assert_eq!( + result, + Some(( + ContainerId::from("3"), + State::Running, + "container_3".to_owned() + )) + ); + } + + #[test] + /// go to previous container + fn test_app_data_containers_prev() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + app_data.containers_end(); + app_data.containers_previous(); + let result = app_data.get_container_state(); + assert_eq!(result.selected(), Some(1)); + assert_eq!(result.offset(), 0); + } + + #[test] + // Get the currently selected container + fn test_app_data_get_selected_container() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_selected_container(); + assert_eq!(result, None); + + app_data.containers.start(); + app_data.containers.next(); + + let result = app_data.get_selected_container(); + assert_eq!(result, Some(&containers[1])); + + /// As above, but now as mut + let result = app_data.get_mut_selected_container(); + assert_eq!(result, Some(&mut containers[1])); + } + + #[test] + // Get mut container by id + fn test_app_data_get_container_by_id() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_by_id(&ContainerId::from("2")); + assert_eq!(result, Some(&mut containers[1])); + } + + #[test] + // Get just the containers name by id + fn test_app_data_get_container_name_by_id() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_name_by_id(&ContainerId::from("2")); + assert_eq!(result, Some(ContainerName::from("container_2"))); + } + + #[test] + // Get the id of the currently selected container + fn test_app_data_get_selected_container_id() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + app_data.containers_end(); + + let result = app_data.get_selected_container_id(); + assert_eq!(result, Some(ContainerId::from("3"))); + } + + #[test] + fn test_app_data_get_selected_container_id_state_name() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + app_data.containers_end(); + + let result = app_data.get_selected_container_id_state_name(); + assert_eq!( + result, + Some(( + ContainerId::from("3"), + State::Running, + "container_3".to_owned() + )) + ); + } + + // ************** // + // DockerControls // + // ************** // + + #[test] + /// Docker commands returned correctly + fn test_app_data_selected_docker_command() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + // No commands when no container selected + let result = app_data.selected_docker_controls(); + assert!(result.is_none()); + + // Correct commands returned + app_data.containers_start(); + app_data.docker_controls_start(); + + let result = app_data.selected_docker_controls(); + assert_eq!(result, Some(DockerControls::Pause)); + } + + #[test] + /// Docker command next works + fn test_app_data_selected_docker_command_next() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + app_data.containers_start(); + app_data.docker_controls_start(); + app_data.docker_controls_next(); + + let result = app_data.selected_docker_controls(); + assert_eq!(result, Some(DockerControls::Restart)); + } + + #[test] + /// Dockercommand end works, and next has no effect when at end + fn test_app_data_selected_docker_command_end() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + app_data.containers_start(); + app_data.docker_controls_end(); + + let result = app_data.selected_docker_controls(); + assert_eq!(result, Some(DockerControls::Delete)); + + /// Next has no effect when at end + app_data.docker_controls_next(); + let result = app_data.selected_docker_controls(); + assert_eq!(result, Some(DockerControls::Delete)); + } + + #[test] + /// Docker commands previous works, and has no effect when at start + fn test_app_data_selected_docker_command_previous() { + let (ids, mut containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + app_data.containers_start(); + app_data.docker_controls_end(); + app_data.docker_controls_previous(); + + let result = app_data.selected_docker_controls(); + assert_eq!(result, Some(DockerControls::Stop)); + + /// previous has no effect when at start + app_data.docker_controls_start(); + app_data.docker_controls_previous(); + let result = app_data.selected_docker_controls(); + assert_eq!(result, Some(DockerControls::Pause)); + } + + #[test] + /// DockerCommands get correct controls dependant on container state + fn test_app_data_get_control_items() { + let test_state = |state: State, expected: &mut Vec| { + let gen_item_state = |state: State| { + ContainerItem::new( + 1, + ContainerId::from("1"), + "image_1".to_owned(), + false, + "container_1".to_owned(), + state, + "Up 1 hour".to_owned(), + ) + }; + let mut app_data = gen_appdata(&vec![gen_item_state(state)]); + app_data.containers_start(); + app_data.docker_controls_start(); + + let result = app_data.get_control_items(); + assert_eq!(result, Some(expected)); + }; + + test_state( + State::Dead, + &mut vec![ + DockerControls::Start, + DockerControls::Restart, + DockerControls::Delete, + ], + ); + test_state( + State::Exited, + &mut vec![ + DockerControls::Start, + DockerControls::Restart, + DockerControls::Delete, + ], + ); + test_state( + State::Paused, + &mut vec![ + DockerControls::Unpause, + DockerControls::Stop, + DockerControls::Delete, + ], + ); + test_state(State::Removing, &mut vec![DockerControls::Delete]); + test_state( + State::Restarting, + &mut vec![DockerControls::Stop, DockerControls::Delete], + ); + test_state( + State::Running, + &mut vec![ + DockerControls::Pause, + DockerControls::Restart, + DockerControls::Stop, + DockerControls::Delete, + ], + ); + test_state(State::Unknown, &mut vec![DockerControls::Delete]); + } + + // **** // + // Logs // + // **** // + + #[test] + /// log title string generated correctly + fn test_app_data_get_log_title() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + // No container selected select + let result = app_data.get_log_title(); + assert_eq!(result, ""); + + // No logs + app_data.containers.start(); + let result = app_data.get_log_title(); + assert_eq!(result, "- container_1"); + + // On last line of logs + let logs = (1..=3).map(|i| format!("{i}")).collect::>(); + app_data.update_log_by_id(logs, &ids[0]); + let result = app_data.get_log_title(); + assert_eq!(result, "3/3 - container_1"); + + // Change log state to no longer be at the end + app_data.log_previous(); + let result = app_data.get_log_title(); + assert_eq!(result, "2/3 - container_1"); + } + + #[test] + /// log title string generated correctly after container change + fn test_app_data_get_log_title_after_container_change() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + // No container selected select + let result = app_data.get_log_title(); + assert_eq!(result, ""); + + app_data.containers_start(); + + let result = app_data.get_log_title(); + assert_eq!(result, "- container_1"); + + // change container + app_data.containers_next(); + let result = app_data.get_log_title(); + assert_eq!(result, "- container_2"); + + // On last line of logs + let logs = (1..=3).map(|i| format!("{i}")).collect::>(); + app_data.update_log_by_id(logs, &ids[1]); + let result = app_data.get_log_title(); + assert_eq!(result, "3/3 - container_2"); + + // Change log state to no longer be at the end + app_data.log_previous(); + let result = app_data.get_log_title(); + assert_eq!(result, "2/3 - container_2"); + } + + #[test] + /// update logs by id works + fn test_app_data_update_log_by_id() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + // No container selected select + let result = app_data.get_log_title(); + assert_eq!(result, ""); + + app_data.containers_start(); + let logs = (1..=3).map(|i| format!("{i} {i}")).collect::>(); + + app_data.update_log_by_id(logs, &ids[0]); + // app_data.log_start(); + + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(2)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_logs(); + assert_eq!(result.len(), 3); + + let result = app_data.get_log_title(); + assert_eq!(result, "3/3 - container_1"); + } + + #[test] + /// logs state reset to start + fn test_app_data_logs_start() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + let logs = (1..=3).map(|i| format!("{i} {i}")).collect::>(); + app_data.containers_start(); + app_data.update_log_by_id(logs, &ids[0]); + + app_data.log_start(); + + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(0)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_log_title(); + assert_eq!(result, "1/3 - container_1"); + } + + #[test] + /// logs state end goes to the end of the logs list + fn test_app_data_logs_end() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + let logs = (1..=3).map(|i| format!("{i} {i}")).collect::>(); + app_data.containers_start(); + app_data.update_log_by_id(logs, &ids[0]); + + app_data.log_start(); + + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(0)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_log_title(); + assert_eq!(result, "1/3 - container_1"); + + app_data.log_end(); + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(2)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_log_title(); + assert_eq!(result, "3/3 - container_1"); + } + + #[test] + /// logs state next works + /// At end has no effect + fn test_app_data_logs_next() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + let logs = (1..=3).map(|i| format!("{i} {i}")).collect::>(); + app_data.containers_start(); + app_data.update_log_by_id(logs, &ids[0]); + + app_data.log_start(); + + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(0)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_log_title(); + assert_eq!(result, "1/3 - container_1"); + + app_data.log_next(); + + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(1)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_log_title(); + assert_eq!(result, "2/3 - container_1"); + + app_data.log_next(); + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(2)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_log_title(); + assert_eq!(result, "3/3 - container_1"); + app_data.log_next(); + + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(2)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_log_title(); + assert_eq!(result, "3/3 - container_1"); + } + + #[test] + /// logs state previous works + /// previous at start has no effect + fn test_app_data_logs_previous() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + let logs = (1..=3).map(|i| format!("{i} {i}")).collect::>(); + app_data.containers_start(); + app_data.update_log_by_id(logs, &ids[0]); + + app_data.log_end(); + + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(2)); + assert_eq!(result.unwrap().offset(), 0); + + let result = app_data.get_log_title(); + assert_eq!(result, "3/3 - container_1"); + + app_data.log_previous(); + + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(1)); + assert_eq!(result.unwrap().offset(), 0); + let result = app_data.get_log_title(); + assert_eq!(result, "2/3 - container_1"); + + app_data.log_previous(); + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(0)); + assert_eq!(result.unwrap().offset(), 0); + let result = app_data.get_log_title(); + assert_eq!(result, "1/3 - container_1"); + + app_data.log_previous(); + let result = app_data.get_log_state(); + assert!(result.is_some()); + assert_eq!(result.as_ref().unwrap().selected(), Some(0)); + assert_eq!(result.unwrap().offset(), 0); + let result = app_data.get_log_title(); + assert_eq!(result, "1/3 - container_1"); + } + + // ********** // + // Chart data // + // ********** // + + #[test] + /// Chart data returned correctly + fn test_app_data_get_chart_data() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_chart_data(); + assert!(result.is_none()); + + app_data.containers_start(); + + if let Some(item) = app_data.get_container_by_id(&ContainerId::from("1")) { + item.cpu_stats = VecDeque::from([CpuStats::new(1.1), CpuStats::new(1.2)]); + item.mem_stats = VecDeque::from([ByteStats::new(1), ByteStats::new(2)]); + } + + let result = app_data.get_chart_data(); + assert_eq!( + result, + Some(( + ( + vec![(0.0, 1.1), (1.0, 1.2)], + CpuStats::new(1.2), + State::Running + ), + ( + vec![(0.0, 1.0), (1.0, 2.0)], + ByteStats::new(2), + State::Running + ) + )) + ); + } + + // ********** // + // Chart data // + // ********** // + + #[test] + /// Header widths return correctly + fn test_app_data_get_width() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_width(); + let expected = Columns { + name: (Header::Name, 11), + state: (Header::State, 11), + status: (Header::Status, 16), + cpu: (Header::Cpu, 7), + mem: (Header::Memory, 7, 7), + id: (Header::Id, 8), + image: (Header::Image, 7), + net_rx: (Header::Rx, 7), + net_tx: (Header::Tx, 7), + }; + assert_eq!(result, expected); + } + + // ************** // + // Update mtehods // + // ************** // + + #[test] + /// Update stats functioning + fn test_app_data_update_stats() { + let (ids, containers) = gen_containers(); + + let mut app_data = gen_appdata(&containers); + + let result = app_data.get_container_items(); + assert_eq!(result[0], containers[0]); + + app_data.update_stats_by_id(&ids[0], Some(10.0), Some(10), 10, 10, 10); + + let result = app_data.get_container_items(); + assert_ne!(result[0], containers[0]); + assert_eq!(result[0].cpu_stats, VecDeque::from([CpuStats::new(10.0)])); + assert_eq!(result[0].mem_stats, VecDeque::from([ByteStats::new(10)])); + assert_eq!(result[0].mem_limit, ByteStats::new(10)); + assert_eq!(result[0].rx, ByteStats::new(10)); + assert_eq!(result[0].tx, ByteStats::new(10)); + } + + #[test] + /// Update stats functioning + fn test_app_data_update_containers() { + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + let result_pre = app_data.get_container_items().clone(); + + let mut input = vec![ + ContainerSummary { + id: Some("1".to_owned()), + names: Some(vec!["container_1".to_owned()]), + image: Some("image_1".to_owned()), + image_id: Some("1".to_owned()), + command: None, + created: Some(1), + ports: None, + size_rw: None, + size_root_fs: None, + labels: None, + state: Some("paused".to_owned()), + status: Some("Up 1 hour".to_owned()), + host_config: None, + network_settings: None, + mounts: None, + }, + ContainerSummary { + id: Some("2".to_owned()), + names: Some(vec!["container_2".to_owned()]), + image: Some("image_2".to_owned()), + image_id: Some("2".to_owned()), + command: None, + created: Some(2), + ports: None, + size_rw: None, + size_root_fs: None, + labels: None, + state: Some("dead".to_owned()), + status: Some("Up 2 hour".to_owned()), + host_config: None, + network_settings: None, + mounts: None, + }, + ]; + + app_data.update_containers(&mut input); + let result_post = app_data.get_container_items(); + assert_ne!(&result_pre, result_post); + assert_eq!(result_post[0].state, State::Paused); + assert_eq!(result_post[1].state, State::Dead); + + } + + #[test] + /// Update logs don't work if container is_oxker: true + fn test_app_data_update_log_by_id_is_oxker() { + let (ids, mut containers) = gen_containers(); + containers[0].is_oxker = true; + let mut app_data = gen_appdata(&containers); + let logs = (1..=3).map(|i| format!("{i} {i}")).collect::>(); + + app_data.update_log_by_id(logs, &ids[0]); + app_data.log_start(); + + let result = app_data.get_log_state(); + assert!(result.is_none()); + } +} diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 8964ebf..1279cbe 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -150,7 +150,7 @@ impl DockerData { app_data .lock() - .update_stats(&id, cpu_stats, mem_stat, mem_limit, rx, tx); + .update_stats_by_id(&id, cpu_stats, mem_stat, mem_limit, rx, tx); } } spawns.lock().remove(&spawn_id); diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 1f7fad0..d6736f3 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -268,7 +268,7 @@ impl InputHandler { // This isn't great, just means you can't send docker commands before full initialization of the program let panel = self.gui_state.lock().get_selected_panel(); if panel == SelectablePanel::Commands { - let option_command = self.app_data.lock().selected_docker_command(); + let option_command = self.app_data.lock().selected_docker_controls(); if let Some(command) = option_command { // Poor way of disallowing commands to be sent to a containerised okxer @@ -337,7 +337,7 @@ impl InputHandler { match selected_panel { SelectablePanel::Containers => locked_data.containers_start(), SelectablePanel::Logs => locked_data.log_start(), - SelectablePanel::Commands => locked_data.docker_command_start(), + SelectablePanel::Commands => locked_data.docker_controls_start(), } } @@ -348,7 +348,7 @@ impl InputHandler { match selected_panel { SelectablePanel::Containers => locked_data.containers_end(), SelectablePanel::Logs => locked_data.log_end(), - SelectablePanel::Commands => locked_data.docker_command_end(), + SelectablePanel::Commands => locked_data.docker_controls_end(), } } @@ -481,7 +481,7 @@ impl InputHandler { match selected_panel { SelectablePanel::Containers => locked_data.containers_next(), SelectablePanel::Logs => locked_data.log_next(), - SelectablePanel::Commands => locked_data.docker_command_next(), + SelectablePanel::Commands => locked_data.docker_controls_next(), }; } @@ -492,7 +492,7 @@ impl InputHandler { match selected_panel { SelectablePanel::Containers => locked_data.containers_previous(), SelectablePanel::Logs => locked_data.log_previous(), - SelectablePanel::Commands => locked_data.docker_command_previous(), + SelectablePanel::Commands => locked_data.docker_controls_previous(), } } } From 52d1610ac6bad7c17b1c4384230df639f8dace8d Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 13 Jan 2024 22:50:37 +0000 Subject: [PATCH 09/25] fix: testing dockerfile change name --- create_release.sh | 6 +++--- docker-compose.yml | 14 +++++++------- postgres.Dockerfile | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 postgres.Dockerfile diff --git a/create_release.sh b/create_release.sh index f3c90c2..13e8a24 100755 --- a/create_release.sh +++ b/create_release.sh @@ -215,19 +215,19 @@ check_typos() { } # Make sure the unused lint isn't used -check_allow_unsued() { +check_allow_unused() { matches_any=$(find . -type d \( -name .git -o -name target \) -prune -o -type f -exec grep -lE '^#!\[allow\(unused\)\]$' {} +) matches_cargo=$(grep "^unused = \"allow\"" ./Cargo.toml) if [ -n "$matches_any" ]; then error_close "\"#[allow(unused)]\" in ${matches_any}" elif [ -n "$matches_cargo" ]; then - error_close "\"unsed = \"allow\"\" in Cargo.toml" + error_close "\"unused = \"allow\"\" in Cargo.toml" fi } # Full flow to create a new release release_flow() { - check_allow_unsued + check_allow_unused check_typos check_git diff --git a/docker-compose.yml b/docker-compose.yml index b2547f0..7553ceb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,16 +3,18 @@ networks: oxker-example-net: name: oxker-examaple-net services: - postgres: - image: postgres:alpine3.19 - container_name: postgres + postgres_but_with_a_longer_container_name: + container_name: postgres_but_with_a_longer_container_name + build: + dockerfile: ./postgres.Dockerfile + context: "." environment: - - POSTGRES_PASSWORD=never_use_this_password_in_production + - POSTGRES_PASSWORD=never_use_this_password_in_production ipc: private restart: always shm_size: 256MB networks: - - oxker-example-net + - oxker-example-net deploy: resources: limits: @@ -39,5 +41,3 @@ services: resources: limits: memory: 512M - - diff --git a/postgres.Dockerfile b/postgres.Dockerfile new file mode 100644 index 0000000..7c27a09 --- /dev/null +++ b/postgres.Dockerfile @@ -0,0 +1 @@ +FROM postgres:16-alpine3.19 \ No newline at end of file From a34c046dee8d5f4b3eeb9c14d42b8f31047e01a5 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sat, 13 Jan 2024 23:01:22 +0000 Subject: [PATCH 10/25] fix: is_oxker_in_container() Check is both is_oxker and running in a container, so that oxker container commands will only be ignored if both are true --- src/app_data/mod.rs | 15 ++++++++++----- src/input_handler/mod.rs | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index d508310..485eee2 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -470,6 +470,12 @@ impl AppData { self.get_selected_container().map_or(false, |i| i.is_oxker) } + /// Check if selected container is oxker and also that oxker is being run in a container + pub fn is_oxker_in_container(&self) -> bool { + self.get_selected_container() + .map_or(false, |i| i.is_oxker && self.args.in_container) + } + /// Find the widths for the strings in the containers panel. /// So can display nicely and evenly pub fn get_width(&self) -> Columns { @@ -1815,12 +1821,11 @@ mod tests { }, ]; - app_data.update_containers(&mut input); - let result_post = app_data.get_container_items(); - assert_ne!(&result_pre, result_post); + app_data.update_containers(&mut input); + let result_post = app_data.get_container_items(); + assert_ne!(&result_pre, result_post); assert_eq!(result_post[0].state, State::Paused); - assert_eq!(result_post[1].state, State::Dead); - + assert_eq!(result_post[1].state, State::Dead); } #[test] diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index d6736f3..ef630b9 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -272,7 +272,7 @@ impl InputHandler { if let Some(command) = option_command { // Poor way of disallowing commands to be sent to a containerised okxer - if self.app_data.lock().is_oxker() { + if self.app_data.lock().is_oxker_in_container() { return; }; let option_id = self.app_data.lock().get_selected_container_id(); From a68794f78d38fa653257d3a462bf61bd22ce2a94 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 14 Jan 2024 00:22:26 +0000 Subject: [PATCH 11/25] wip: terminal tests --- src/ui/draw_blocks.rs | 80 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 708f197..abb34ee 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -851,8 +851,7 @@ pub fn delete_confirm(f: &mut Frame, gui_state: &Arc>, name: &Co .update_region_map(Region::Delete(DeleteButton::Yes), yes_area); } -/// Draw an error popup over whole screen -pub fn error(f: &mut Frame, error: AppError, seconds: Option) { +fn gen_error<'f>(f: &mut Frame, error: AppError, seconds: Option) -> (Paragraph<'f>, Rect) { let block = Block::default() .title(" Error ") .border_type(BorderType::Rounded) @@ -889,6 +888,13 @@ pub fn error(f: &mut Frame, error: AppError, seconds: Option) { .alignment(Alignment::Center); let area = popup(lines, max_line_width, f.size(), BoxLocation::MiddleCentre); + (paragraph, area) + // area +} + +/// Draw an error popup over whole screen +pub fn error(f: &mut Frame, error: AppError, seconds: Option) { + let (paragraph, area) = gen_error(f, error, seconds); f.render_widget(Clear, area); f.render_widget(paragraph, area); } @@ -969,3 +975,73 @@ pub fn debug_bar(area: Rect, f: &mut Frame, debug_string: &str) { // .borders(Borders::NONE); // f.render_widget(block, whole_layout[0]); // } + +#[cfg(test)] +#[allow(clippy::unwrap_used, clippy::many_single_char_names, unused)] +mod tests { + + use std::collections::VecDeque; + + use ratatui::{backend::TestBackend, buffer::Buffer, Terminal}; + + use super::*; + + #[test] + // Test that the error popup is centered, red background, white border, white text, and displays the correct text + fn test_draw_blocks_error() { + let backend = TestBackend::new(46, 9); + let mut terminal = Terminal::new(backend).unwrap(); + + terminal + .draw(|f| { + let block = super::gen_error(f, AppError::DockerConnect, Some(4)); + f.render_widget(block.0, block.1); + }) + .unwrap(); + + let mut expected = vec![ + " ".to_owned(), + " ╭───────────────── Error ──────────────────╮ ".to_owned(), + " │ │ ".to_owned(), + " │ Unable to access docker daemon │ ".to_owned(), + " │ │ ".to_owned(), + format!(" │ oxker::v{VERSION} closing in 04 seconds │ "), + " │ │ ".to_owned(), + " ╰──────────────────────────────────────────╯ ".to_owned(), + " ".to_owned(), + ]; + + for (row_index, row) in expected.iter().enumerate() { + for (char_index, char) in row.chars().enumerate() { + let index = row_index * 46 + char_index; + let result_char = &terminal.backend().buffer().content[index]; + assert_eq!(char.to_string(), result_char.symbol()); + if (1..=7).contains(&row_index) && (1..=44).contains(&char_index) { + assert_eq!(result_char.bg, Color::Red); + } + if result_char.symbol().chars().next().unwrap().is_alphanumeric() { + assert_eq!(result_char.fg, Color::White); + } + } + } + } + // let result = &terminal.backend().buffer().content.iter().map(|i|i.symbol().to_owned()).collect::>(); + // println!("{expected:?}"); + // println!("{:?}", terminal.backend().buffer().content); + // // let mut expected = Buffer::with_lines(vec![ + // // "┌Title─┐ ", + // // "│ │ ", + // // "│ │ ", + // // "│ │ ", + // // "│ │ ", + // // "│ │ ", + // // "│ │ ", + // // "└──────┘ ", + // // " ", + // // " ", + // // ]); + // // for x in 1..=5 { + // // expected.get_mut(x, 0).set_fg(Color::LightBlue); + // // } + // terminal.backend().assert_buffer(&expected); +} From 8e9243d884dc73e515ac5fad9a0a1d906b689bd1 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 14 Jan 2024 00:31:38 +0000 Subject: [PATCH 12/25] test: terminal wip --- src/ui/draw_blocks.rs | 69 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index abb34ee..600d353 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -988,8 +988,9 @@ mod tests { #[test] // Test that the error popup is centered, red background, white border, white text, and displays the correct text - fn test_draw_blocks_error() { - let backend = TestBackend::new(46, 9); + fn test_draw_blocks_docker_connect_error() { + let (w,h) = (46,9); + let backend = TestBackend::new(w, h); let mut terminal = Terminal::new(backend).unwrap(); terminal @@ -1013,15 +1014,63 @@ mod tests { for (row_index, row) in expected.iter().enumerate() { for (char_index, char) in row.chars().enumerate() { - let index = row_index * 46 + char_index; - let result_char = &terminal.backend().buffer().content[index]; - assert_eq!(char.to_string(), result_char.symbol()); - if (1..=7).contains(&row_index) && (1..=44).contains(&char_index) { - assert_eq!(result_char.bg, Color::Red); + let index = row_index * usize::from(w) + char_index; + let result_cell = &terminal.backend().buffer().content[index]; + assert_eq!(char.to_string(), result_cell.symbol()); + if (1..=usize::from(h)-2).contains(&row_index) && (1..=usize::from(w)-2).contains(&char_index) { + assert_eq!(result_cell.bg, Color::Red); } - if result_char.symbol().chars().next().unwrap().is_alphanumeric() { - assert_eq!(result_char.fg, Color::White); - } + if result_cell + .symbol() + .chars() + .next() + .unwrap() + .is_alphanumeric() + { + assert_eq!(result_cell.fg, Color::White); + } + } + } + } + + #[test] + // Test that the clearable error popup is centered, red background, white border, white text, and displays the correct text + fn test_draw_blocks_clearable_error() { + let (w,h) = (39, 10); + let backend = TestBackend::new(w, h); + let mut terminal = Terminal::new(backend).unwrap(); + + terminal + .draw(|f| { + let block = super::gen_error(f, AppError::DockerExec, Some(4)); + f.render_widget(block.0, block.1); + }) + .unwrap(); + + let mut expected = [ + " ", + " ╭────────────── Error ──────────────╮ ", + " │ │ ", + " │ Unable to exec into container │ ", + " │ │ ", + " │ ( c ) clear error │ ", + " │ ( q ) quit oxker │ ", + " │ │ ", + " ╰───────────────────────────────────╯ ", + " ", + ]; + + for (row_index, row) in expected.iter().enumerate() { + for (char_index, char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &terminal.backend().buffer().content[index]; + assert_eq!(char.to_string(), result_cell.symbol()); + if (1..=usize::from(h)-2).contains(&row_index) && (1..=usize::from(w)-2).contains(&char_index) { + assert_eq!(result_cell.bg, Color::Red); + } + if result_cell.symbol().chars().next().unwrap().is_alphanumeric() || ["(", ")"].contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::White); + } } } } From 53543a1b728187416c7ea52c4ec65bff074b7413 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Sun, 14 Jan 2024 10:06:41 +0000 Subject: [PATCH 13/25] tests: gui tests --- README.md | 3 +- src/app_data/container_state.rs | 97 +- src/app_data/mod.rs | 242 ++-- src/docker_data/mod.rs | 148 ++- src/main.rs | 79 ++ src/ui/color_match.rs | 76 ++ src/ui/draw_blocks.rs | 1871 +++++++++++++++++++++++++++++-- src/ui/mod.rs | 35 +- 8 files changed, 2235 insertions(+), 316 deletions(-) diff --git a/README.md b/README.md index e1d47d5..a1a6f2c 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,8 @@ see ( .update_region_map(Region::Panel(panel), area); let mut title = match panel { SelectablePanel::Containers => { - format!("{} {}", panel.title(), app_data.lock().container_title()) + format!("{}{}", panel.title(), app_data.lock().container_title()) } SelectablePanel::Logs => { - format!("{} {}", panel.title(), app_data.lock().get_log_title()) + format!("{}{}", panel.title(), app_data.lock().get_log_title()) } SelectablePanel::Commands => String::new(), }; @@ -95,6 +95,7 @@ pub fn commands( gui_state: &Arc>, ) { let block = || generate_block(app_data, area, fd, gui_state, SelectablePanel::Commands); + // let block = block(); let items = app_data.lock().get_control_items().map_or(vec![], |i| { i.iter() .map(|c| { @@ -115,6 +116,7 @@ pub fn commands( if let Some(i) = app_data.lock().get_control_state() { f.render_stateful_widget(items, area, i); } else { + let block = || generate_block(app_data, area, fd, gui_state, SelectablePanel::Commands); let paragraph = Paragraph::new("") .block(block()) .alignment(Alignment::Center); @@ -207,7 +209,6 @@ pub fn containers( f: &mut Frame, fd: &FrameData, gui_state: &Arc>, - widths: &Columns, ) { let block = generate_block(app_data, area, fd, gui_state, SelectablePanel::Containers); @@ -215,7 +216,7 @@ pub fn containers( .lock() .get_container_items() .iter() - .map(|i| ListItem::new(format_containers(i, widths))) + .map(|i| ListItem::new(format_containers(i, &fd.columns))) .collect::>(); if items.is_empty() { @@ -734,7 +735,7 @@ pub fn help_box(f: &mut Frame) { .title(title) .borders(Borders::ALL) .border_type(BorderType::Rounded) - .border_style(Style::default().fg(Color::Black)); + .border_style(Style::default().fg(Color::Black).bg(Color::Magenta)); // Order is important here f.render_widget(Clear, area); @@ -759,7 +760,10 @@ pub fn delete_confirm(f: &mut Frame, gui_state: &Arc>, name: &Co Span::from("Are you sure you want to delete container: "), Span::styled( name.to_string(), - Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), + Style::default() + .fg(Color::Red) + .bg(Color::White) + .add_modifier(Modifier::BOLD), ), ]); @@ -776,19 +780,22 @@ pub fn delete_confirm(f: &mut Frame, gui_state: &Arc>, name: &Co Block::default() .border_type(BorderType::Rounded) .borders(Borders::ALL) + .style(Style::default().bg(Color::White)) }; let yes_para = Paragraph::new(yes_text) .alignment(Alignment::Center) .block(button_block()); + // Need to add some padding for the borders - let yes_chars = u16::try_from(yes_text.chars().count() + 2).unwrap_or(9); + let _yes_chars = u16::try_from(yes_text.chars().count() + 2).unwrap_or(9); let no_para = Paragraph::new(no_text) .alignment(Alignment::Center) .block(button_block()); + // Need to add some padding for the borders - let no_chars = u16::try_from(no_text.chars().count() + 2).unwrap_or(8); + // let no_chars = u16::try_from(no_text.chars().count() + 2).unwrap_or(8); let area = popup( lines, @@ -811,23 +818,15 @@ pub fn delete_confirm(f: &mut Frame, gui_state: &Arc>, name: &Co ) .split(area); - // Should maybe have a differenet button_space IF the f.width() is within 2 chars of no_chars + yes_chars? - let button_spacing = (max_line_width - no_chars - yes_chars) / 3; - - let button_spacing = if (button_spacing + max_line_width) > f.size().width { - 1 - } else { - button_spacing - }; let split_buttons = Layout::default() .direction(Direction::Horizontal) .constraints( [ - Constraint::Max(button_spacing), - Constraint::Min(no_chars), - Constraint::Max(button_spacing), - Constraint::Min(yes_chars), - Constraint::Max(button_spacing), + Constraint::Percentage(10), + Constraint::Percentage(35), + Constraint::Percentage(10), + Constraint::Percentage(35), + Constraint::Percentage(10), ] .as_ref(), ) @@ -851,7 +850,8 @@ pub fn delete_confirm(f: &mut Frame, gui_state: &Arc>, name: &Co .update_region_map(Region::Delete(DeleteButton::Yes), yes_area); } -fn gen_error<'f>(f: &mut Frame, error: AppError, seconds: Option) -> (Paragraph<'f>, Rect) { +/// Draw an error popup over whole screen +pub fn error(f: &mut Frame, error: AppError, seconds: Option) { let block = Block::default() .title(" Error ") .border_type(BorderType::Rounded) @@ -888,13 +888,8 @@ fn gen_error<'f>(f: &mut Frame, error: AppError, seconds: Option) -> (Paragr .alignment(Alignment::Center); let area = popup(lines, max_line_width, f.size(), BoxLocation::MiddleCentre); - (paragraph, area) - // area -} -/// Draw an error popup over whole screen -pub fn error(f: &mut Frame, error: AppError, seconds: Option) { - let (paragraph, area) = gen_error(f, error, seconds); + // let (paragraph, area) = gen_error(f, error, seconds); f.render_widget(Clear, area); f.render_widget(paragraph, area); } @@ -954,53 +949,1655 @@ fn popup(text_lines: usize, text_width: usize, r: Rect, box_location: BoxLocatio .split(popup_layout[indexes.0])[indexes.1] } -#[cfg(debug_assertions)] -// Single row at the top of the screen for debugging -pub fn debug_bar(area: Rect, f: &mut Frame, debug_string: &str) { - let block = Block::default().style(Style::default().bg(Color::Red)); - let paragraph = Paragraph::new(debug_string) - .style(Style::default().fg(Color::White)) - .block(block); - f.render_widget(paragraph, area); -} - -// Draw nothing, as in a blank screen -// pub fn nothing(f: &mut Frame) { -// let whole_layout = Layout::default() -// .direction(Direction::Vertical) -// .constraints([Constraint::Min(100)].as_ref()) -// .split(f.size()); - -// let block = Block::default() -// .borders(Borders::NONE); -// f.render_widget(block, whole_layout[0]); -// } - #[cfg(test)] -#[allow(clippy::unwrap_used, clippy::many_single_char_names, unused)] +#[allow(clippy::unwrap_used, clippy::many_single_char_names)] mod tests { - use std::collections::VecDeque; + use std::{ops::RangeInclusive, sync::Arc}; - use ratatui::{backend::TestBackend, buffer::Buffer, Terminal}; + use parking_lot::Mutex; + use ratatui::{ + backend::TestBackend, + layout::Rect, + style::{Color, Modifier}, + Terminal, + }; + use uuid::Uuid; - use super::*; + use crate::{ + app_data::{ + AppData, ContainerId, ContainerImage, ContainerName, Header, SortedOrder, State, + StatefulList, + }, + app_error::AppError, + tests::{gen_appdata, gen_container_summary, gen_containers}, + ui::{draw_frame, GuiState}, + }; + + use super::{FrameData, ORANGE, VERSION}; + + struct TuiTestSetup { + app_data: Arc>, + gui_state: Arc>, + fd: FrameData, + area: Rect, + terminal: Terminal, + ids: Vec, + } + + const BORDER_CHARS: [&str; 6] = ["╭", "╮", "─", "│", "╰", "╯"]; + + /// Generate state to be used in *most* gui tests + fn test_setup(w: u16, h: u16, control_start: bool, container_start: bool) -> TuiTestSetup { + let backend = TestBackend::new(w, h); + let terminal = Terminal::new(backend).unwrap(); + + let (ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + if control_start { + app_data.docker_controls_start(); + } + if container_start { + app_data.containers_start(); + } + + let gui_state = GuiState::default(); + + let app_data = Arc::new(Mutex::new(app_data)); + let gui_state = Arc::new(Mutex::new(gui_state)); + + let fd = FrameData::from((app_data.lock(), gui_state.lock())); + let area = Rect::new(0, 0, w, h); + TuiTestSetup { + app_data, + gui_state, + fd, + area, + terminal, + ids, + } + } + + /// Insert some logs into the first container + fn insert_logs(setup: &TuiTestSetup) { + let logs = (1..=3).map(|i| format!("{i} line {i}")).collect::>(); + setup.app_data.lock().update_log_by_id(logs, &setup.ids[0]); + } + + // ******************** // + // DockerControls panel // + // ******************** // + + #[test] + // Test that when DockerCommands are available, they are drawn correctly, dependant on container state + fn test_draw_blocks_commands_none() { + let (w, h) = (12, 6); + let mut setup = test_setup(w, h, false, false); + + setup + .terminal + .draw(|f| { + super::commands(&setup.app_data, setup.area, f, &setup.fd, &setup.gui_state); + }) + .unwrap(); + + let expected = [ + "╭──────────╮", + "│ │", + "│ │", + "│ │", + "│ │", + "╰──────────╯", + ]; + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.fg, Color::Reset); + } + } + } + + #[test] + // Test that when DockerCommands are available, they are drawn correctly, dependant on container state + fn test_draw_blocks_commands_some() { + let (w, h) = (12, 6); + let mut setup = test_setup(w, h, true, true); + + setup + .terminal + .draw(|f| { + super::commands(&setup.app_data, setup.area, f, &setup.fd, &setup.gui_state); + }) + .unwrap(); + + let expected = [ + "╭──────────╮", + "│▶ pause │", + "│ restart │", + "│ stop │", + "│ delete │", + "╰──────────╯", + ]; + let result = &setup.terminal.backend().buffer().content; + + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + // Check the text color is correct + match index { + // pause + 15..=19 => { + assert_eq!(result_cell.fg, Color::Yellow); + } + // restart + 27..=33 => { + assert_eq!(result_cell.fg, Color::Magenta); + } + // stop + 39..=42 => { + assert_eq!(result_cell.fg, Color::Red); + } + // delete + 51..=56 => { + assert_eq!(result_cell.fg, Color::Gray); + } + // no text + _ => { + assert_eq!(result_cell.fg, Color::Reset); + } + } + if result_cell.symbol().starts_with('▶') { + assert_eq!(result_cell.fg, Color::Reset); + } + } + } + + // Change the controls state + setup + .app_data + .lock() + .update_containers(&mut vec![gen_container_summary(1, "paused")]); + setup.app_data.lock().docker_controls_next(); + + let expected = [ + "╭──────────╮", + "│ resume │", + "│▶ stop │", + "│ delete │", + "│ │", + "╰──────────╯", + ]; + + setup + .terminal + .draw(|f| { + super::commands(&setup.app_data, setup.area, f, &setup.fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + // Chceck the text color is correct + match index { + // resume + 15..=20 => { + assert_eq!(result_cell.fg, Color::Blue); + } + // stop + 27..=30 => { + assert_eq!(result_cell.fg, Color::Red); + } + // delete + 39..=44 => { + assert_eq!(result_cell.fg, Color::Gray); + } + // no text + _ => { + assert_eq!(result_cell.fg, Color::Reset); + } + } + if result_cell.symbol().starts_with('▶') { + assert_eq!(result_cell.fg, Color::Reset); + } + } + } + } + + #[test] + // When control panel is selected, the border is blue, if not then white, selected text is highlighted + fn test_draw_blocks_commands_panel_selected_color() { + let (w, h) = (12, 6); + let mut setup = test_setup(w, h, true, true); + let expected = [ + "╭──────────╮", + "│▶ pause │", + "│ restart │", + "│ stop │", + "│ delete │", + "╰──────────╯", + ]; + + // Unselected, has a grey border + setup + .terminal + .draw(|f| { + super::commands(&setup.app_data, setup.area, f, &setup.fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + if BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::Reset); + } + } + } + + // Control panel now selected, should have a blue border + setup.gui_state.lock().next_panel(); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + setup + .terminal + .draw(|f| { + super::commands(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + if BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::LightCyan); + } + // Make sure that the selected line has bold text + match index { + // pause + 13..=22 => { + assert_eq!(result_cell.modifier, Modifier::BOLD); + } + _ => { + assert!(result_cell.modifier.is_empty()); + } + } + } + } + } + + // *********************** // + // Container summary panel // + // *********************** // + + // Check that the correct solor is applied to the state/status/cpu/memory section + fn check_expected(expected: [&str; 6], w: u16, _h: u16, setup: &TuiTestSetup, color: Color) { + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + if (145..=207).contains(&index) { + assert_eq!(result_cell.fg, color); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } + } + } + } + + #[test] + // No containers, panel unselected, then selected, border color changes correctly + fn test_draw_blocks_containers_none() { + let (w, h) = (25, 6); + let mut setup = test_setup(w, h, true, true); + setup.app_data.lock().containers = StatefulList::new(vec![]); + + let expected = [ + "╭ Containers ───────────╮", + "│ no containers running │", + "│ │", + "│ │", + "│ │", + "╰───────────────────────╯", + ]; + + setup.gui_state.lock().next_panel(); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.fg, Color::Reset); + } + } + + setup.gui_state.lock().previous_panel(); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + if BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::LightCyan); + } + } + } + } + + #[test] + // Containers panel drawn, selected line is bold, border is blue + fn test_draw_blocks_containers_some() { + let (w, h) = (130, 6); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &setup.fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + // result matches expected + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + // Selected container is bold + match index { + 131 | 133..=258 => assert_eq!(result_cell.modifier, Modifier::BOLD), + _ => { + assert!(result_cell.modifier.is_empty()); + } + } + + // Border is blue + if BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::LightCyan); + } + } + } + + // Change selected panel, border is now no longer blue + setup.gui_state.lock().next_panel(); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + // Border is gray + if BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::Reset); + } + } + } + } + + #[test] + /// ALl columns on all rows are coloured correctly + fn test_draw_blocks_containers_colors() { + let (w, h) = (130, 6); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ container_1 ✓ running Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let index_blue = [ + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + ]; + let index_blue = index_blue + .iter() + .flat_map(|&x| vec![x, x + 130, x + 260]) + .collect::>(); + let index_green = [ + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + ]; + let index_green = index_green + .iter() + .flat_map(|&x| vec![x, x + 130, x + 260]) + .collect::>(); + + let index_rx = [229, 230, 231, 232, 233, 234, 235, 236, 237, 238]; + let index_rx = index_rx + .iter() + .flat_map(|&x| vec![x, x + 130, x + 260]) + .collect::>(); + + let index_tx = [239, 240, 241, 242, 243, 244, 245, 246, 247, 248]; + let index_tx = index_tx + .iter() + .flat_map(|&x| vec![x, x + 130, x + 260]) + .collect::>(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + + let result_cell = &result[index]; + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + match index { + _x if index_blue.contains(&index) => { + assert_eq!(result_cell.fg, Color::Blue); + } + _x if index_green.contains(&index) => { + assert_eq!(result_cell.fg, Color::Green); + } + _x if index_rx.contains(&index) => { + assert_eq!(result_cell.fg, Color::Rgb(255, 233, 193)); + } + _x if index_tx.contains(&index) => { + assert_eq!(result_cell.fg, Color::Rgb(205, 140, 140)); + } + (0..=130) | (259..=260) | (389..=390) | (519..=520) | (649..=779) => { + assert_eq!(result_cell.fg, Color::LightCyan); + } + _ => { + assert_eq!(result_cell.fg, Color::Reset); + } + } + } + } + } + + #[test] + /// When long container/image name, it is truncated correctly + fn test_draw_blocks_containers_long_name_image() { + let (w, h) = (170, 6); + let mut setup = test_setup(w, h, true, true); + setup.app_data.lock().containers.items[0].name = + ContainerName::from("a_long_container_name_for_the_purposes_of_this_test"); + setup.app_data.lock().containers.items[0].image = + ContainerImage::from("a_long_image_name_for_the_purposes_of_this_test"); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ a_long_container_name_for_the… ॥ paused Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 a_long_image_name_for_the_pur… 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + setup.app_data.lock().containers.items[0].state = State::Paused; + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + } + } + + // THis char: … + } + + #[test] + /// When container is paused, correct colors displayed + fn test_draw_blocks_containers_paused() { + let (w, h) = (130, 6); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ container_1 ॥ paused Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + setup.app_data.lock().containers.items[0].state = State::Paused; + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + check_expected(expected, w, h, &setup, Color::Yellow); + } + + #[test] + /// When container is dead, correct colors displayed + fn test_draw_blocks_containers_dead() { + let (w, h) = (130, 6); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ container_1 ✖ dead Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + setup.app_data.lock().containers.items[0].state = State::Dead; + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + check_expected(expected, w, h, &setup, Color::Red); + } + + #[test] + /// When container is exited, correct colors displayed + fn test_draw_blocks_containers_exited() { + let (w, h) = (130, 6); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ container_1 ✖ exited Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + setup.app_data.lock().containers.items[0].state = State::Exited; + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + check_expected(expected, w, h, &setup, Color::Red); + } + #[test] + /// When container is paused, correct colors displayed + fn test_draw_blocks_containers_removing() { + let (w, h) = (130, 6); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ container_1 removing Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + setup.app_data.lock().containers.items[0].state = State::Removing; + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + check_expected(expected, w, h, &setup, Color::LightRed); + } + #[test] + /// When container state is restarting, correct colors displayed + fn test_draw_blocks_containers_restarting() { + let (w, h) = (130, 6); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ container_1 ↻ restarting Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + setup.app_data.lock().containers.items[0].state = State::Restarting; + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + check_expected(expected, w, h, &setup, Color::LightGreen); + } + #[test] + /// When container state is unknown, correct colors displayed + fn test_draw_blocks_containers_unknown() { + let (w, h) = (130, 6); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + "╭ Containers 1/3 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│⚪ container_1 ? unknown Up 1 hour 00.00% 0.00 kB / 0.00 kB 1 image_1 0.00 kB 0.00 kB │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB │", + "│ │", + "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + ]; + setup.app_data.lock().containers.items[0].state = State::Unknown; + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::containers(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + check_expected(expected, w, h, &setup, Color::Red); + } + // ********** // + // Logs panel // + // ********** // + + #[test] + // No logs, panel unselected, then selected, border color changes correctly + fn test_draw_blocks_logs_none() { + let (w, h) = (25, 6); + let mut setup = test_setup(w, h, true, true); + setup.app_data.lock().containers = StatefulList::new(vec![]); + + let expected = [ + "╭ Logs ─────────────────╮", + "│ no logs found │", + "│ │", + "│ │", + "│ │", + "╰───────────────────────╯", + ]; + + let _fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::logs(&setup.app_data, setup.area, f, &setup.fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.fg, Color::Reset); + } + } + + setup.gui_state.lock().next_panel(); + setup.gui_state.lock().next_panel(); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + // When selected, has a blue border + setup + .terminal + .draw(|f| { + super::logs(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + if BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::LightCyan); + } + } + } + } + + #[test] + // Parsing logs, spinner visible, and then animates by one frame + fn test_draw_blocks_logs_parsing() { + let (w, h) = (25, 6); + let mut setup = test_setup(w, h, true, true); + let uuid = Uuid::new_v4(); + setup.gui_state.lock().next_loading(uuid); + + let expected = [ + "╭ Logs - container_1 ───╮", + "│ parsing logs ⠙ │", + "│ │", + "│ │", + "│ │", + "╰───────────────────────╯", + ]; + + let mut fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + fd.init = true; + + setup + .terminal + .draw(|f| { + super::logs(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let test = |terminal: &Terminal, expected: [&str; 6]| { + let result = &terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.fg, Color::Reset); + } + } + }; + + test(&setup.terminal, expected); + + // animation moved by one frame + setup.gui_state.lock().next_loading(uuid); + + let expected = [ + "╭ Logs - container_1 ───╮", + "│ parsing logs ⠹ │", + "│ │", + "│ │", + "│ │", + "╰───────────────────────╯", + ]; + + let mut fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + fd.init = true; + setup + .terminal + .draw(|f| { + super::logs(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + test(&setup.terminal, expected); + } + + #[test] + // Logs correct displayed, changing log state also draws correctly + fn test_draw_blocks_logs_some() { + let (w, h) = (25, 6); + let mut setup = test_setup(w, h, true, true); + + insert_logs(&setup); + + let test = |terminal: &Terminal, + expected: [&str; 6], + range: RangeInclusive| { + let result = &terminal.backend().buffer().content; + + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.fg, Color::Reset); + + if range.contains(&index) { + assert_eq!(result_cell.modifier, Modifier::BOLD); + } else { + assert!(result_cell.modifier.is_empty()); + } + } + } + }; + + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + setup + .terminal + .draw(|f| { + super::logs(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + let expected = [ + "╭ Logs 3/3 - container_1╮", + "│ line 1 │", + "│ line 2 │", + "│▶ line 3 │", + "│ │", + "╰───────────────────────╯", + ]; + test(&setup.terminal, expected, 76..=98); + + // Change selected log line + setup.app_data.lock().log_previous(); + let _fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::logs(&setup.app_data, setup.area, f, &setup.fd, &setup.gui_state); + }) + .unwrap(); + + let expected = [ + "╭ Logs 2/3 - container_1╮", + "│ line 1 │", + "│▶ line 2 │", + "│ line 3 │", + "│ │", + "╰───────────────────────╯", + ]; + test(&setup.terminal, expected, 51..=73); + } + + #[test] + // Full (long) name displayed in logs border + fn test_draw_blocks_logs_long_name() { + let (w, h) = (80, 6); + let mut setup = test_setup(w, h, true, true); + setup.app_data.lock().containers.items[0].name = + ContainerName::from("a_long_container_name_for_the_purposes_of_this_test"); + setup.app_data.lock().containers.items[0].image = + ContainerImage::from("a_long_image_name_for_the_purposes_of_this_test"); + + insert_logs(&setup); + + let expected = [ + "╭ Logs 3/3 - a_long_container_name_for_the_purposes_of_this_test ──────────────╮", + "│ line 1 │", + "│ line 2 │", + "│▶ line 3 │", + "│ │", + "╰──────────────────────────────────────────────────────────────────────────────╯", + ]; + + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + setup + .terminal + .draw(|f| { + super::logs(&setup.app_data, setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + } + } + } + + // ************ // + // Charts panel // + // ************ // + + const EXPECTED: [&str; 10] = [ + "╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮", + "│10.00%│ • ││100.00 kB│ •• │", + "│ │ •• ││ │ •• │", + "│ │ ••• ││ │ • • │", + "│ │ • • ││ │ • • │", + "│ │ • •• ││ │•• •• │", + "│ │• • ││ │• • │", + "│ │• • ││ │• • │", + "│ │ ││ │ │", + "╰──────────────────────────────────────╯╰──────────────────────────────────────╯", + ]; + const MEMORY_INDEX: [usize; 16] = [ + 134, 135, 214, 215, 293, 295, 372, 375, 451, 452, 455, 456, 531, 535, 611, 615, + ]; + + const CPU_INDEX: [usize; 15] = [ + 92, 171, 172, 250, 251, 252, 330, 332, 409, 413, 414, 488, 493, 568, 573, + ]; + + #[allow(clippy::cast_precision_loss)] + // Add fixed data to the cpu & mem vecdeques, that match the above data + fn insert_chart_data(setup: &TuiTestSetup) { + for i in 1..=10 { + setup.app_data.lock().update_stats_by_id( + &setup.ids[0], + Some(i as f64), + Some(i * 10000), + i * 10000, + i, + i, + ); + } + for i in 1..=3 { + setup.app_data.lock().update_stats_by_id( + &setup.ids[0], + Some(i as f64), + Some(i * 10000), + i * 10000, + i, + i, + ); + } + } + #[test] + // When status is Running, but not data, charts drawn without dots etc + fn test_draw_blocks_charts_running_none() { + let (w, h) = (80, 10); + let mut setup = test_setup(w, h, true, true); + + setup + .terminal + .draw(|f| { + super::chart(f, setup.area, &setup.app_data); + }) + .unwrap(); + + let expected = [ + "╭───────────── cpu 00.00% ─────────────╮╭─────────── memory 0.00 kB ───────────╮", + "│00.00%│ ││0.00 kB│ │", + "│ │ ││ │ │", + "│ │ ││ │ │", + "│ │ ││ │ │", + "│ │ ││ │ │", + "│ │ ││ │ │", + "│ │ ││ │ │", + "│ │ ││ │ │", + "╰──────────────────────────────────────╯╰──────────────────────────────────────╯", + ]; + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + match index { + // chart tiles - cpu 03.00% && memory 30.00 kB - are green + 14..=25 | 52..=67 => { + assert_eq!(result_cell.fg, Color::Green); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } + // Cpu & Memory max are orange and bold + 81..=86 | 121..=127 => { + assert_eq!(result_cell.fg, ORANGE); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } + // All others + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert!(result_cell.modifier.is_empty()); + } + } + } + } + } + + #[test] + // When status is Running, charts correctly drawn + fn test_draw_blocks_charts_running_some() { + let (w, h) = (80, 10); + let mut setup = test_setup(w, h, true, true); + + insert_chart_data(&setup); + + setup + .terminal + .draw(|f| { + super::chart(f, setup.area, &setup.app_data); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in EXPECTED.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + match index { + // chart tiles - cpu 03.00% && memory 30.00 kB - are green + 14..=25 | 51..=67 => { + assert_eq!(result_cell.fg, Color::Green); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } + // Cpu & Memory max are orange and bold + 81..=86 | 121..=129 => { + assert_eq!(result_cell.fg, ORANGE); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } + // cpu dots are magenta + _x if CPU_INDEX.contains(&index) => { + assert_eq!(result_cell.fg, Color::Magenta); + assert!(result_cell.modifier.is_empty()); + } + // memory dots are cyan + _x if MEMORY_INDEX.contains(&index) => { + assert_eq!(result_cell.fg, Color::Cyan); + assert!(result_cell.modifier.is_empty()); + } + // All others + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert!(result_cell.modifier.is_empty()); + } + } + } + } + } + + #[test] + // Whens status paused, some text is now Yellow + fn test_draw_blocks_charts_paused() { + let (w, h) = (80, 10); + let mut setup = test_setup(w, h, true, true); + + insert_chart_data(&setup); + setup.app_data.lock().containers.items[0].state = State::Paused; + + setup + .terminal + .draw(|f| { + super::chart(f, setup.area, &setup.app_data); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in EXPECTED.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + match index { + // Titles and y axis are yellow + 14..=25 | 51..=67 | 81..=86 | 121..=129 => { + assert_eq!(result_cell.fg, Color::Yellow); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } + _x if CPU_INDEX.contains(&index) => { + assert_eq!(result_cell.fg, Color::Magenta); + assert!(result_cell.modifier.is_empty()); + } + // memory dots are cyan + _x if MEMORY_INDEX.contains(&index) => { + assert_eq!(result_cell.fg, Color::Cyan); + assert!(result_cell.modifier.is_empty()); + } + // All others + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert!(result_cell.modifier.is_empty()); + } + } + } + } + } + + #[test] + // When dead, text is read + fn test_draw_blocks_charts_dead() { + let (w, h) = (80, 10); + let mut setup = test_setup(w, h, true, true); + insert_chart_data(&setup); + setup.app_data.lock().containers.items[0].state = State::Dead; + + setup + .terminal + .draw(|f| { + super::chart(f, setup.area, &setup.app_data); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in EXPECTED.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + match index { + // Titles and y axis are red + 14..=25 | 51..=67 | 81..=86 | 121..=129 => { + assert_eq!(result_cell.fg, Color::Red); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } + // cpu dots are magenta + _x if CPU_INDEX.contains(&index) => { + assert_eq!(result_cell.fg, Color::Magenta); + assert!(result_cell.modifier.is_empty()); + } + // memory dots are cyan + _x if MEMORY_INDEX.contains(&index) => { + assert_eq!(result_cell.fg, Color::Cyan); + assert!(result_cell.modifier.is_empty()); + } + // All others + _ => { + assert_eq!(result_cell.fg, Color::Reset); + assert!(result_cell.modifier.is_empty()); + } + } + } + } + } + + // ******* // + // Headers // + // ******* // + + #[test] + /// Heading back only has show/exit help when no containers, correctly coloured + fn test_draw_blocks_headers_no_containers() { + let (w, h) = (140, 1); + let mut setup = test_setup(w, h, true, true); + setup.app_data.lock().containers = StatefulList::new(vec![]); + + let mut fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + let expected = " ( h ) show help "; + + setup + .terminal + .draw(|f| { + super::heading_bar(setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (index, expected_char) in expected.chars().enumerate() { + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.bg, Color::Magenta); + assert_eq!(result_cell.fg, Color::White); + } + + fd.help_visible = true; + let expected = " ( h ) exit help "; + setup + .terminal + .draw(|f| { + super::heading_bar(setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (index, expected_char) in expected.chars().enumerate() { + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.bg, Color::Magenta); + assert_eq!(result_cell.fg, Color::Black); + } + } + + #[test] + /// Show all headings when containers present, colors valid + fn test_draw_blocks_headers_some_containers() { + let (w, h) = (140, 1); + let mut setup = test_setup(w, h, true, true); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + let expected = " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "; + setup + .terminal + .draw(|f| { + super::heading_bar(setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (index, expected_char) in expected.chars().enumerate() { + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.bg, Color::Magenta); + assert_eq!( + result_cell.fg, + match index { + (2..=122) => Color::Black, + _ => Color::White, + } + ); + } + } + + #[test] + /// Test all combination of headers & sort by + fn test_draw_blocks_headers_sort_containers() { + let (w, h) = (140, 1); + let mut setup = test_setup(w, h, true, true); + let mut fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + let mut test = |expected: &str, range: RangeInclusive, x: (Header, SortedOrder)| { + fd.sorted_by = Some(x); + + setup + .terminal + .draw(|f| { + super::heading_bar(setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (index, expected_char) in expected.chars().enumerate() { + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.bg, Color::Magenta); + assert_eq!( + result_cell.fg, + match index { + 0 | 1 => Color::White, + // given range | help section + x if range.contains(&x) || (123..=139).contains(&x) => Color::White, + _ => Color::Black, + } + ); + } + }; + + // Name + test(" ▲ name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", 1..=14, (Header::Name, SortedOrder::Asc)); + test(" ▼ name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", 1..=14, (Header::Name, SortedOrder::Desc)); + + // state + test(" name ▲ state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", 15..=26, (Header::State, SortedOrder::Asc)); + test(" name ▼ state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", 15..=26, (Header::State, SortedOrder::Desc)); + + // status + test(" name state ▲ status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", 27..=47, (Header::Status, SortedOrder::Asc)); + test(" name state ▼ status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", 27..=47, (Header::Status, SortedOrder::Desc)); + + // cpu + test(" name state status ▲ cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", 48..=57, (Header::Cpu, SortedOrder::Asc)); + test(" name state status ▼ cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", 48..=57, (Header::Cpu, SortedOrder::Desc)); + + // mem + test(" name state status cpu ▲ memory/limit id image ↓ rx ↑ tx ( h ) show help ", 58..=77, (Header::Memory, SortedOrder::Asc)); + test(" name state status cpu ▼ memory/limit id image ↓ rx ↑ tx ( h ) show help ", 58..=77, (Header::Memory, SortedOrder::Desc)); + + // id + test(" name state status cpu memory/limit ▲ id image ↓ rx ↑ tx ( h ) show help ", 78..=88, (Header::Id, SortedOrder::Asc)); + test(" name state status cpu memory/limit ▼ id image ↓ rx ↑ tx ( h ) show help ", 78..=88, (Header::Id, SortedOrder::Desc)); + + // image + test(" name state status cpu memory/limit id ▲ image ↓ rx ↑ tx ( h ) show help ", 89..=98, (Header::Image, SortedOrder::Asc)); + test(" name state status cpu memory/limit id ▼ image ↓ rx ↑ tx ( h ) show help ", 89..=98, (Header::Image, SortedOrder::Desc)); + + // rx + test(" name state status cpu memory/limit id image ▲ ↓ rx ↑ tx ( h ) show help ", 99..=108, (Header::Rx, SortedOrder::Asc)); + test(" name state status cpu memory/limit id image ▼ ↓ rx ↑ tx ( h ) show help ", 99..=108, (Header::Rx, SortedOrder::Desc)); + + // tx + test(" name state status cpu memory/limit id image ↓ rx ▲ ↑ tx ( h ) show help ", 109..=122, (Header::Tx, SortedOrder::Asc)); + test(" name state status cpu memory/limit id image ↓ rx ▼ ↑ tx ( h ) show help ", 109..=122, (Header::Tx, SortedOrder::Desc)); + } + + #[test] + /// Show animation + fn test_draw_blocks_headers_animation() { + let (w, h) = (140, 1); + let mut setup = test_setup(w, h, true, true); + let uuid = Uuid::new_v4(); + setup.gui_state.lock().next_loading(uuid); + let fd = FrameData::from((setup.app_data.lock(), setup.gui_state.lock())); + + setup + .terminal + .draw(|f| { + super::heading_bar(setup.area, f, &fd, &setup.gui_state); + }) + .unwrap(); + + let expected = " ⠙ name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help "; + + let result = &setup.terminal.backend().buffer().content; + for (index, expected_char) in expected.chars().enumerate() { + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + assert_eq!(result_cell.bg, Color::Magenta); + assert_eq!( + result_cell.fg, + match index { + (2..=122) => Color::Black, + _ => Color::White, + } + ); + } + } + + // ********** // + // Help popup // + // ********** // + #[test] + // This will cause issues onces the version has more than the current 5 chars (0.5.0) + // Help popup is drawn correctly + fn test_draw_blocks_help() { + let (w, h) = (87, 30); + let mut setup = test_setup(w, h, true, true); + + setup + .terminal + .draw(|f| { + super::help_box(f); + }) + .unwrap(); + + let expected = [ + " ".to_owned(), + format!(" ╭ {VERSION} ────────────────────────────────────────────────────────────────────────────╮ "), + " │ │ ".to_owned(), + " │ 88 │ ".to_owned(), + " │ 88 │ ".to_owned(), + " │ 88 │ ".to_owned(), + " │ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │ ".to_owned(), + r#" │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ "#.to_owned(), + r#" │ 8b d8 )888( 8888[ 8PP""""""" 88 │ "#.to_owned(), + r#" │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ "#.to_owned(), + r#" │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ "#.to_owned(), + " │ │ ".to_owned(), + " │ A simple tui to view & control docker containers │ ".to_owned(), + " │ │ ".to_owned(), + " │ ( tab ) or ( shift+tab ) change panels │ ".to_owned(), + " │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ ".to_owned(), + " │ ( enter ) send docker container command │ ".to_owned(), + " │ ( e ) exec into a container │ ".to_owned(), + " │ ( h ) toggle this help information │ ".to_owned(), + " │ ( s ) save logs to file │ ".to_owned(), + " │ ( m ) toggle mouse capture - if disabled, text on screen can be selected & copied │ ".to_owned(), + " │ ( 0 ) stop sort │ ".to_owned(), + " │ ( 1 - 9 ) sort by header - or click header │ ".to_owned(), + " │ ( q ) quit at any time │ ".to_owned(), + " │ │ ".to_owned(), + " │ currently an early work in progress, all and any input appreciated │ ".to_owned(), + " │ https://github.com/mrjackwills/oxker │ ".to_owned(), + " │ │ ".to_owned(), + " ╰───────────────────────────────────────────────────────────────────────────────────╯ ".to_owned(), + " ".to_owned(), + ]; + + for (row_index, row) in expected.iter().enumerate() { + let mut bracket_key = vec![]; + let mut push_bracket_key = false; + + let result = &setup.terminal.backend().buffer().content; + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + let result_str = result_cell.symbol(); + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + // First and last row, and first char and last char in each row, is empty + if row_index == 0 + || row_index == usize::from(h - 1) + || char_index == 0 + || char_index == usize::from(w - 1) + { + assert_eq!(result_cell.fg, Color::Reset); + assert_eq!(result_cell.bg, Color::Reset); + // Borders + } else if BORDER_CHARS.contains(&result_str) { + assert_eq!(result_cell.fg, Color::Black); + assert_eq!(result_cell.bg, Color::Magenta); + // everything else has a magenta background + } else { + assert_eq!(result_cell.bg, Color::Magenta); + } + + // check that ( [key] ) is white + if result_str == "(" { + push_bracket_key = true; + bracket_key.push(result_cell); + } + if push_bracket_key { + bracket_key.push(result_cell); + if result_str == ")" { + push_bracket_key = false; + for i in &bracket_key { + assert_eq!(i.fg, Color::White); + } + bracket_key.clear(); + } + } + // TODO should really be testing every color of every str here + } + } + } + + // ************ // + // Delete popup // + // ************ // + + #[test] + // Delete container popup is drawn correctly + fn test_draw_blocks_delete() { + let (w, h) = (82, 10); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + " ", + " ╭──────────────────────── Confirm Delete ────────────────────────╮ ", + " │ │ ", + " │ Are you sure you want to delete container: container_1 │ ", + " │ │ ", + " │ ╭─────────────────────╮ ╭─────────────────────╮ │ ", + " │ │ (N)o │ │ (Y)es │ │ ", + " │ ╰─────────────────────╯ ╰─────────────────────╯ │ ", + " ╰────────────────────────────────────────────────────────────────╯ ", + " ", + ]; + + setup + .terminal + .draw(|f| { + super::delete_confirm(f, &setup.gui_state, &ContainerName::from("container_1")); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + if row_index == 0 + || row_index == usize::from(h - 1) + || char_index < 8 + || char_index > usize::from(w - 9) + { + assert_eq!(result_cell.fg, Color::Reset); + assert_eq!(result_cell.bg, Color::Reset); + } else { + assert_eq!(result_cell.bg, Color::White); + } + + // Borders are black + if BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::Black); + // Container name is red + } else if row_index == 3 && (57..=67).contains(&char_index) { + assert_eq!(result_cell.fg, Color::Red); + // All other text is black + } else if !row_index == 0 + && !row_index == usize::from(h - 1) + && !char_index < 8 + && !char_index > usize::from(w - 9) + { + assert_eq!(result_cell.fg, Color::Black); + } + } + } + } + + // ***** // + // popup // + // ***** // + + #[test] + /// Info box drawn in bottom right + fn test_draw_blocks_info() { + let (w, h) = (45, 9); + let mut setup = test_setup(w, h, true, true); + + let expected = [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " test ", + " ", + ]; + + setup + .terminal + .draw(|f| { + super::info(f, "test", std::time::Instant::now(), &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(expected_char.to_string(), result_cell.symbol()); + + let (fg, bg) = if row_index >= 6 && char_index >= 32 { + (Color::White, Color::Blue) + } else { + (Color::Reset, Color::Reset) + }; + + assert_eq!(result_cell.fg, fg); + assert_eq!(result_cell.bg, bg); + } + } + } + + // *********** // + // Error popup // + // *********** // #[test] // Test that the error popup is centered, red background, white border, white text, and displays the correct text fn test_draw_blocks_docker_connect_error() { - let (w,h) = (46,9); - let backend = TestBackend::new(w, h); - let mut terminal = Terminal::new(backend).unwrap(); + let (w, h) = (46, 9); + let mut setup = test_setup(w, h, true, true); - terminal + setup + .terminal .draw(|f| { - let block = super::gen_error(f, AppError::DockerConnect, Some(4)); - f.render_widget(block.0, block.1); + super::error(f, AppError::DockerConnect, Some(4)); }) .unwrap(); - let mut expected = vec![ + let expected = vec![ " ".to_owned(), " ╭───────────────── Error ──────────────────╮ ".to_owned(), " │ │ ".to_owned(), @@ -1012,12 +2609,17 @@ mod tests { " ".to_owned(), ]; + let result = &setup.terminal.backend().buffer().content; for (row_index, row) in expected.iter().enumerate() { - for (char_index, char) in row.chars().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { let index = row_index * usize::from(w) + char_index; - let result_cell = &terminal.backend().buffer().content[index]; - assert_eq!(char.to_string(), result_cell.symbol()); - if (1..=usize::from(h)-2).contains(&row_index) && (1..=usize::from(w)-2).contains(&char_index) { + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + + if (1..=usize::from(h) - 2).contains(&row_index) + && (1..=usize::from(w) - 2).contains(&char_index) + { assert_eq!(result_cell.bg, Color::Red); } if result_cell @@ -1036,61 +2638,114 @@ mod tests { #[test] // Test that the clearable error popup is centered, red background, white border, white text, and displays the correct text fn test_draw_blocks_clearable_error() { - let (w,h) = (39, 10); - let backend = TestBackend::new(w, h); - let mut terminal = Terminal::new(backend).unwrap(); + let (w, h) = (39, 10); + let mut setup = test_setup(w, h, true, true); - terminal + setup + .terminal .draw(|f| { - let block = super::gen_error(f, AppError::DockerExec, Some(4)); - f.render_widget(block.0, block.1); + super::error(f, AppError::DockerExec, Some(4)); }) .unwrap(); - let mut expected = [ - " ", - " ╭────────────── Error ──────────────╮ ", - " │ │ ", - " │ Unable to exec into container │ ", - " │ │ ", - " │ ( c ) clear error │ ", - " │ ( q ) quit oxker │ ", - " │ │ ", - " ╰───────────────────────────────────╯ ", - " ", + let expected = [ + " ", + " ╭────────────── Error ──────────────╮ ", + " │ │ ", + " │ Unable to exec into container │ ", + " │ │ ", + " │ ( c ) clear error │ ", + " │ ( q ) quit oxker │ ", + " │ │ ", + " ╰───────────────────────────────────╯ ", + " ", ]; + let result = &setup.terminal.backend().buffer().content; for (row_index, row) in expected.iter().enumerate() { - for (char_index, char) in row.chars().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { let index = row_index * usize::from(w) + char_index; - let result_cell = &terminal.backend().buffer().content[index]; - assert_eq!(char.to_string(), result_cell.symbol()); - if (1..=usize::from(h)-2).contains(&row_index) && (1..=usize::from(w)-2).contains(&char_index) { + let result_cell = &result[index]; + + assert_eq!(result_cell.symbol(), expected_char.to_string()); + if (1..=usize::from(h) - 2).contains(&row_index) + && (1..=usize::from(w) - 2).contains(&char_index) + { assert_eq!(result_cell.bg, Color::Red); } - if result_cell.symbol().chars().next().unwrap().is_alphanumeric() || ["(", ")"].contains(&result_cell.symbol()) { - assert_eq!(result_cell.fg, Color::White); - } + if result_cell + .symbol() + .chars() + .next() + .unwrap() + .is_alphanumeric() + || ["(", ")"].contains(&result_cell.symbol()) + { + assert_eq!(result_cell.fg, Color::White); + } + } + } + } + + // *************** // + // The whole layout // + // **************** // + #[test] + // Check that the whole layout is drawn correctly + fn test_draw_blocks_the_whole_layout() { + let (w, h) = (160, 30); + let mut setup = test_setup(w, h, true, true); + + insert_chart_data(&setup); + insert_logs(&setup); + + let expected = [ + " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", + "╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮", + "│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │", + "│ ││ delete │", + "│ ││ │", + "│ ││ │", + "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯", + "╭ Logs 3/3 - container_1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│ line 1 │", + "│ line 2 │", + "│▶ line 3 │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + "╭───────────────────────────────── cpu 03.00% ─────────────────────────────────╮╭────────────────────────────── memory 30.00 kB ───────────────────────────────╮", + "│10.00%│ •••••• ││100.00 kB│ •••••• │", + "│ │••••• •••• ││ │••••• ••• │", + "│ │ ││ │ │", + "╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯", + ]; + setup + .terminal + .draw(|f| { + draw_frame(f, &setup.app_data, &setup.gui_state); + }) + .unwrap(); + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(expected_char.to_string(), result_cell.symbol()); } } } - // let result = &terminal.backend().buffer().content.iter().map(|i|i.symbol().to_owned()).collect::>(); - // println!("{expected:?}"); - // println!("{:?}", terminal.backend().buffer().content); - // // let mut expected = Buffer::with_lines(vec![ - // // "┌Title─┐ ", - // // "│ │ ", - // // "│ │ ", - // // "│ │ ", - // // "│ │ ", - // // "│ │ ", - // // "│ │ ", - // // "└──────┘ ", - // // " ", - // // " ", - // // ]); - // // for x in 1..=5 { - // // expected.get_mut(x, 0).set_fg(Color::LightBlue); - // // } - // terminal.backend().assert_buffer(&expected); } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 1b22599..eb2586d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -217,22 +217,6 @@ impl Ui { } } -#[cfg(not(debug_assertions))] -fn get_wholelayout(f: &Frame) -> std::rc::Rc<[ratatui::layout::Rect]> { - Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Min(1), Constraint::Min(100)].as_ref()) - .split(f.size()) -} - -#[cfg(debug_assertions)] -fn get_wholelayout(f: &Frame) -> std::rc::Rc<[ratatui::layout::Rect]> { - Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Min(1), Constraint::Min(1), Constraint::Min(100)].as_ref()) - .split(f.size()) -} - /// Frequent data required by multiple framde drawing functions, can reduce mutex reads by placing it all in here #[derive(Debug)] pub struct FrameData { @@ -279,21 +263,16 @@ impl From<(MutexGuard<'_, AppData>, MutexGuard<'_, GuiState>)> for FrameData { fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc>) { let fd = FrameData::from((app_data.lock(), gui_state.lock())); - let whole_layout = get_wholelayout(f); - #[cfg(debug_assertions)] - draw_blocks::debug_bar(whole_layout[0], f, app_data.lock().get_debug_string()); - - #[cfg(debug_assertions)] - let whole_layout_split = (1, 2); - - #[cfg(not(debug_assertions))] - let whole_layout_split = (0, 1); + let whole_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(1), Constraint::Min(100)].as_ref()) + .split(f.size()); // Split into 3, containers+controls, logs, then graphs let upper_main = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Max(fd.height), Constraint::Percentage(50)].as_ref()) - .split(whole_layout[whole_layout_split.1]); + .split(whole_layout[1]); let top_split = if fd.has_containers { vec![Constraint::Percentage(90), Constraint::Percentage(10)] @@ -318,11 +297,11 @@ fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc Date: Wed, 17 Jan 2024 16:15:09 +0000 Subject: [PATCH 14/25] docs: changelog --- CHANGELOG.md | 4 ++++ src/ui/draw_blocks.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bac167f..cd68634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### Chores + dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd] + files formatted, [1806165c3e266876b2d1806f7b662d09705f3aad] ++ create_release.sh check for unused lint, [d0b27211928f93f8455e1ee5a6a6485c6a21d382] ### Fixes + sort arrow now on left of header, [40ddcb727d2c1758d6dd26a58507b85b219f51e2] @@ -8,6 +9,9 @@ ### Refactos + rename string_wrapper > unit_struct, [27cf53e41f8b379f606c1c27620ee08e79bac57e] +### Tests ++ Finally have tests, currently for layout and associated methods. Running the tests will not interfere with any running Docker containers, [4bcf77db776a36e0a8151ecfbda722a66c4ba46c] + # v0.5.0 ### 2024-01-05 diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 7ab880e..024ffcd 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -2369,7 +2369,7 @@ mod tests { // Help popup // // ********** // #[test] - // This will cause issues onces the version has more than the current 5 chars (0.5.0) + // This will cause issues once the version has more than the current 5 chars (0.5.0) // Help popup is drawn correctly fn test_draw_blocks_help() { let (w, h) = (87, 30); From b6fd35022a99ec0e982ddb154b0450d49c4840e9 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:15:29 +0000 Subject: [PATCH 15/25] chore: dependencies updated --- Cargo.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b806ca6..e2eb72f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +checksum = "628a8f9bd1e24b4e0db2b4bc2d000b001e7dd032d54afa60a68836aeec5aa54a" dependencies = [ "anstyle", "anstyle-parse", @@ -139,9 +139,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bollard" @@ -237,9 +237,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.16" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -247,9 +247,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.16" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", @@ -295,7 +295,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "crossterm_winapi", "libc", "mio", @@ -643,9 +643,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] @@ -668,7 +668,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "libc", "redox_syscall", ] @@ -933,7 +933,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cassowary", "crossterm", "indoc", @@ -1110,9 +1110,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" [[package]] name = "socket2" @@ -1469,9 +1469,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1479,9 +1479,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", @@ -1494,9 +1494,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1504,9 +1504,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", @@ -1517,9 +1517,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "winapi" From 65a1afcb0605604ede350a5630c775f94ebb74ee Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 18 Jan 2024 00:25:09 +0000 Subject: [PATCH 16/25] feat: Added ports section, closes #21 --- src/app_data/container_state.rs | 62 +++++- src/app_data/mod.rs | 137 ++++++++++++- src/docker_data/message.rs | 2 +- src/docker_data/mod.rs | 4 +- src/input_handler/mod.rs | 4 +- src/main.rs | 16 +- src/ui/draw_blocks.rs | 339 ++++++++++++++++++++++++++++---- src/ui/mod.rs | 15 +- 8 files changed, 518 insertions(+), 61 deletions(-) diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 2b6fddb..53976d6 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -4,6 +4,7 @@ use std::{ fmt, }; +use bollard::service::Port; use ratatui::{ style::Color, widgets::{ListItem, ListState}, @@ -100,6 +101,47 @@ macro_rules! unit_struct { unit_struct!(ContainerName); unit_struct!(ContainerImage); +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ContainerPorts { + pub ip: Option, + pub private: u16, + pub public: Option, +} + +impl From<&Port> for ContainerPorts { + fn from(value: &Port) -> Self { + Self { + ip: value.ip.clone(), + private: value.private_port, + public: value.public_port, + } + } +} + +impl ContainerPorts { + pub fn len_ip(&self) -> usize { + self.ip.as_ref().unwrap_or(&String::new()).chars().count() + } + pub fn len_private(&self) -> usize { + format!("{}", self.private).chars().count() + } + pub fn len_public(&self) -> usize { + format!("{}", self.public.unwrap_or_default()) + .chars() + .count() + } + + pub fn print(&self) -> (String, String, String) { + ( + self.ip + .as_ref() + .map_or(String::new(), std::borrow::ToOwned::to_owned), + format!("{}", self.private), + self.public.map_or(String::new(), |s| s.to_string()), + ) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct StatefulList { pub state: ListState, @@ -247,7 +289,7 @@ pub enum DockerControls { Restart, Start, Stop, - Unpause, + Resume, Delete, } @@ -259,7 +301,7 @@ impl DockerControls { Self::Start => Color::Green, Self::Stop => Color::Red, Self::Delete => Color::Gray, - Self::Unpause => Color::Blue, + Self::Resume => Color::Blue, } } @@ -267,7 +309,7 @@ impl DockerControls { pub fn gen_vec(state: State) -> Vec { match state { State::Dead | State::Exited => vec![Self::Start, Self::Restart, Self::Delete], - State::Paused => vec![Self::Unpause, Self::Stop, Self::Delete], + State::Paused => vec![Self::Resume, Self::Stop, Self::Delete], State::Restarting => vec![Self::Stop, Self::Delete], State::Running => vec![Self::Pause, Self::Restart, Self::Stop, Self::Delete], _ => vec![Self::Delete], @@ -283,7 +325,7 @@ impl fmt::Display for DockerControls { Self::Restart => "restart", Self::Start => "start", Self::Stop => "stop", - Self::Unpause => "resume", + Self::Resume => "resume", }; write!(f, "{disp}") } @@ -484,21 +526,23 @@ impl Logs { /// Info for each container #[derive(Debug, Clone, PartialEq, Eq)] pub struct ContainerItem { - pub created: u64, pub cpu_stats: VecDeque, + pub created: u64, pub docker_controls: StatefulList, pub id: ContainerId, pub image: ContainerImage, + pub is_oxker: bool, pub last_updated: u64, pub logs: Logs, pub mem_limit: ByteStats, pub mem_stats: VecDeque, pub name: ContainerName, + // todo remove option, can be empty vec + pub ports: Vec, pub rx: ByteStats, pub state: State, pub status: String, pub tx: ByteStats, - pub is_oxker: bool, } /// Basic display information, for when running in debug mode @@ -516,6 +560,7 @@ impl fmt::Display for ContainerItem { } impl ContainerItem { + #[allow(clippy::too_many_arguments)] /// Create a new container item pub fn new( created: u64, @@ -523,14 +568,16 @@ impl ContainerItem { image: String, is_oxker: bool, name: String, + ports: Vec, state: State, status: String, ) -> Self { let mut docker_controls = StatefulList::new(DockerControls::gen_vec(state)); docker_controls.start(); + Self { - created, cpu_stats: VecDeque::with_capacity(60), + created, docker_controls, id, image: image.into(), @@ -540,6 +587,7 @@ impl ContainerItem { mem_limit: ByteStats::default(), mem_stats: VecDeque::with_capacity(60), name: name.into(), + ports, rx: ByteStats::default(), state, status, diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index feb88cc..7250f78 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -254,6 +254,51 @@ impl AppData { .and_then(|i| self.containers.items.get(i)) } + /// Find the longest port when it's transformed into a string, defaults are header lens (ip, private, public) + pub fn get_longest_port(&self) -> (usize, usize, usize) { + let mut longest_ip = 5; + let mut longest_private = 10; + let mut longest_public = 9; + + for item in &self.containers.items { + // if let Some(ports) = item.ports.as_ref() { + longest_ip = longest_ip.max( + item.ports + .iter() + .map(ContainerPorts::len_ip) + .max() + .unwrap_or(3), + ); + longest_private = longest_private.max( + item.ports + .iter() + .map(ContainerPorts::len_private) + .max() + .unwrap_or(8), + ); + longest_public = longest_public.max( + item.ports + .iter() + .map(ContainerPorts::len_public) + .max() + .unwrap_or(6), + ); + } + // } + + (longest_ip, longest_private, longest_public) + // ) + } + /// Get Option of the current selected container's ports, sorted by private port + pub fn get_selected_ports(&mut self) -> Option<(Vec, State)> { + if let Some(item) = self.get_mut_selected_container() { + let mut ports = item.ports.clone(); + ports.sort_by(|a, b| a.private.cmp(&b.private)); + return Some((ports, item.state)); + } + None + } + /// Get mutable Option of the current selected container fn get_mut_selected_container(&mut self) -> Option<&mut ContainerItem> { self.containers @@ -571,6 +616,10 @@ impl AppData { }) }); + let ports = i.ports.as_ref().map_or(vec![], |i| { + i.iter().map(ContainerPorts::from).collect::>() + }); + let id = ContainerId::from(id.as_str()); let is_oxker = i @@ -611,13 +660,17 @@ impl AppData { }; item.state = state; }; + + item.ports = ports; + if item.image.get() != image { item.image.set(image); }; } else { // container not known, so make new ContainerItem and push into containers Vec - let container = - ContainerItem::new(created, id, image, is_oxker, name, state, status); + let container = ContainerItem::new( + created, id, image, is_oxker, name, ports, state, status, + ); self.containers.items.push(container); } } @@ -1325,6 +1378,7 @@ mod tests { "image_1".to_owned(), false, "container_1".to_owned(), + vec![], state, "Up 1 hour".to_owned(), ) @@ -1356,7 +1410,7 @@ mod tests { test_state( State::Paused, &mut vec![ - DockerControls::Unpause, + DockerControls::Resume, DockerControls::Stop, DockerControls::Delete, ], @@ -1652,9 +1706,9 @@ mod tests { ); } - // ********** // - // Chart data // - // ********** // + // ************* // + // Header Widths // + // ************* // #[test] /// Header widths return correctly @@ -1677,6 +1731,77 @@ mod tests { assert_eq!(result, expected); } + // ************* // + // Header Widths // + // ************* // + + #[test] + /// Returns selected containers ports ordered by private ip + fn test_app_data_get_selected_ports() { + let (_ids, containers) = gen_containers(); + let mut app_data = gen_appdata(&containers); + + app_data.containers.items[0].ports.push(ContainerPorts { + ip: None, + private: 10, + public: Some(1), + }); + app_data.containers.items[0].ports.push(ContainerPorts { + ip: None, + private: 11, + public: Some(3), + }); + app_data.containers.items[0].ports.push(ContainerPorts { + ip: None, + private: 4, + public: Some(2), + }); + + // No containers selected + let result = app_data.get_selected_ports(); + assert!(result.is_none()); + + // Selected container & ports + app_data.containers_start(); + let result = app_data.get_selected_ports(); + + assert_eq!( + result, + Some(( + vec![ + ContainerPorts { + ip: None, + private: 4, + public: Some(2) + }, + ContainerPorts { + ip: None, + private: 10, + public: Some(1) + }, + ContainerPorts { + ip: None, + private: 11, + public: Some(3) + }, + ContainerPorts { + ip: None, + private: 8001, + public: None + } + ], + State::Running + )) + ); + + // Selected container & no ports + app_data.containers_start(); + app_data.containers.items[0].ports = vec![]; + let result = app_data.get_selected_ports(); + + assert_eq!(result, Some((vec![], State::Running))); + } + // ************** // // Update mtehods // // ************** // diff --git a/src/docker_data/message.rs b/src/docker_data/message.rs index e1066c1..866aada 100644 --- a/src/docker_data/message.rs +++ b/src/docker_data/message.rs @@ -14,6 +14,6 @@ pub enum DockerMessage { Restart(ContainerId), Start(ContainerId), Stop(ContainerId), - Unpause(ContainerId), + Resume(ContainerId), Update, } diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index caa5a18..be8f1f2 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -387,11 +387,11 @@ impl DockerData { }); self.update_everything().await; } - DockerMessage::Unpause(id) => { + DockerMessage::Resume(id) => { tokio::spawn(async move { let handle = GuiState::start_loading_animation(&gui_state, uuid); if docker.unpause_container(id.get()).await.is_err() { - Self::set_error(&app_data, DockerControls::Unpause, &gui_state); + Self::set_error(&app_data, DockerControls::Resume, &gui_state); } gui_state.lock().stop_loading_animation(&handle, uuid); }); diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index ef630b9..8f9cc36 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -286,8 +286,8 @@ impl InputHandler { DockerControls::Pause => { self.docker_tx.send(DockerMessage::Pause(id)).await.ok() } - DockerControls::Unpause => { - self.docker_tx.send(DockerMessage::Unpause(id)).await.ok() + DockerControls::Resume => { + self.docker_tx.send(DockerMessage::Resume(id)).await.ok() } DockerControls::Start => { self.docker_tx.send(DockerMessage::Start(id)).await.ok() diff --git a/src/main.rs b/src/main.rs index a6d1b6d..7eb6851 100644 --- a/src/main.rs +++ b/src/main.rs @@ -168,10 +168,10 @@ async fn main() { #[cfg(test)] #[allow(clippy::unwrap_used, clippy::many_single_char_names, unused)] mod tests { - use bollard::service::ContainerSummary; + use bollard::service::{ContainerSummary, Port}; use crate::{ - app_data::{AppData, ContainerId, ContainerItem, State, StatefulList}, + app_data::{AppData, ContainerId, ContainerItem, ContainerPorts, State, StatefulList}, parse_args::CliArgs, }; @@ -197,6 +197,11 @@ mod tests { format!("image_{index}"), false, format!("container_{index}"), + vec![ContainerPorts { + ip: None, + private: u16::try_from(index).unwrap_or(1) + 8000, + public: None, + }], State::Running, format!("Up {index} hour"), ) @@ -231,7 +236,12 @@ mod tests { image_id: Some(format!("{index}")), command: None, created: Some(i64::try_from(index).unwrap()), - ports: None, + ports: Some(vec![Port { + ip: None, + private_port: u16::try_from(index).unwrap_or(1) + 8000, + public_port: None, + typ: None, + }]), size_rw: None, size_root_fs: None, labels: None, diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 024ffcd..370114e 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -1,7 +1,7 @@ use parking_lot::Mutex; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, - style::{Color, Modifier, Style}, + style::{Color, Modifier, Style, Stylize}, symbols, text::{Line, Span}, widgets::{ @@ -269,6 +269,61 @@ pub fn logs( } } +// Display the ports in a formatted list +pub fn ports( + f: &mut Frame, + area: Rect, + app_data: &Arc>, + max_lens: (usize, usize, usize), +) { + if let Some(ports) = app_data.lock().get_selected_ports() { + let block = Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .title_alignment(Alignment::Center) + .title(Span::styled( + " ports ", + Style::default() + .fg(ports.1.get_color()) + .add_modifier(Modifier::BOLD), + )); + + let (ip, private, public) = max_lens; + + if ports.0.is_empty() { + let paragraph = Paragraph::new(Span::from("no ports").add_modifier(Modifier::BOLD)) + .alignment(Alignment::Center) + .block(block); + f.render_widget(paragraph, area); + } else { + let mut output = vec![Line::from( + Span::from(format!( + "{:>ip$}{:>private$}{:>public$}", + "ip", "private", "public" + )) + .fg(Color::Yellow), + )]; + for (index, item) in ports.0.iter().enumerate() { + let fg = if index % 2 == 0 { + Color::White + } else { + Color::Magenta + }; + let strings = item.print(); + + let line = vec![ + Span::from(format!("{:>ip$}", strings.0)).fg(fg), + Span::from(format!("{:>private$}", strings.1)).fg(fg), + Span::from(format!("{:>public$}", strings.2)).fg(fg), + ]; + output.push(Line::from(line)); + } + let paragraph = Paragraph::new(output).block(block); + f.render_widget(paragraph, area); + } + } +} + /// Draw the cpu + mem charts pub fn chart(f: &mut Frame, area: Rect, app_data: &Arc>) { if let Some((cpu, mem)) = app_data.lock().get_chart_data() { @@ -307,10 +362,7 @@ fn make_chart<'a, T: Stats + Display>( current: &'a T, max: &'a T, ) -> Chart<'a> { - let title_color = match state { - State::Running => Color::Green, - _ => state.get_color(), - }; + let title_color = state.get_color(); let label_color = match state { State::Running => ORANGE, _ => state.get_color(), @@ -966,8 +1018,8 @@ mod tests { use crate::{ app_data::{ - AppData, ContainerId, ContainerImage, ContainerName, Header, SortedOrder, State, - StatefulList, + AppData, ContainerId, ContainerImage, ContainerName, ContainerPorts, Header, + SortedOrder, State, StatefulList, }, app_error::AppError, tests::{gen_appdata, gen_container_summary, gen_containers}, @@ -2687,49 +2739,260 @@ mod tests { } } + #[test] + // Port section when container has no ports + fn test_draw_blocks_ports_no_ports() { + let (w, h) = (30, 8); + let mut setup = test_setup(w, h, true, true); + setup.app_data.lock().containers.items[0].ports = vec![]; + + let max_lens = setup.app_data.lock().get_longest_port(); + setup + .terminal + .draw(|f| { + super::ports(f, setup.area, &setup.app_data, max_lens); + }) + .unwrap(); + + let expected = [ + "╭────────── ports ───────────╮", + "│ no ports │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "╰────────────────────────────╯", + ]; + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(expected_char.to_string(), result_cell.symbol()); + if row_index == 0 && !BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::Green); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } else { + assert_eq!(result_cell.fg, Color::Reset); + } + } + } + } + + #[test] + // Port section when container has multiple ports + fn test_draw_blocks_ports_multiple_ports() { + let (w, h) = (32, 8); + let mut setup = test_setup(w, h, true, true); + setup.app_data.lock().containers.items[0] + .ports + .push(ContainerPorts { + ip: None, + private: 8002, + public: None, + }); + setup.app_data.lock().containers.items[0] + .ports + .push(ContainerPorts { + ip: Some("127.0.0.1".to_owned()), + private: 8003, + public: Some(8003), + }); + + let max_lens = setup.app_data.lock().get_longest_port(); + + setup + .terminal + .draw(|f| { + super::ports(f, setup.area, &setup.app_data, max_lens); + }) + .unwrap(); + + let expected = [ + "╭─────────── ports ────────────╮", + "│ ip private public │", + "│ 8001 │", + "│ 8002 │", + "│127.0.0.1 8003 8003 │", + "│ │", + "│ │", + "╰──────────────────────────────╯", + ]; + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(expected_char.to_string(), result_cell.symbol()); + + let result_cell_as_char = result_cell + .symbol() + .chars() + .next() + .unwrap() + .is_ascii_alphanumeric(); + if row_index == 0 && result_cell_as_char { + assert_eq!(result_cell.fg, Color::Green); + } + if row_index == 1 && result_cell_as_char { + assert_eq!(result_cell.fg, Color::Yellow); + } + if row_index == 2 && result_cell_as_char { + assert_eq!(result_cell.fg, Color::White); + } + if row_index == 3 && result_cell_as_char { + assert_eq!(result_cell.fg, Color::Magenta); + } + if row_index == 4 && result_cell_as_char { + assert_eq!(result_cell.fg, Color::White); + } + } + } + } + + #[test] + // Port section title color correct dependant on state + fn test_draw_blocks_ports_container_state() { + let (w, h) = (32, 8); + let mut setup = test_setup(w, h, true, true); + let max_lens = setup.app_data.lock().get_longest_port(); + + setup.app_data.lock().containers.items[0].state = State::Paused; + setup + .terminal + .draw(|f| { + super::ports(f, setup.area, &setup.app_data, max_lens); + }) + .unwrap(); + + let expected = [ + "╭─────────── ports ────────────╮", + "│ ip private public │", + "│ 8001 │", + "│ │", + "│ │", + "│ │", + "│ │", + "╰──────────────────────────────╯", + ]; + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(expected_char.to_string(), result_cell.symbol()); + + if row_index == 0 + && result_cell + .symbol() + .chars() + .next() + .unwrap() + .is_ascii_alphanumeric() + { + assert_eq!(result_cell.fg, Color::Yellow); + } + } + } + + setup.app_data.lock().containers.items[0].state = State::Dead; + setup + .terminal + .draw(|f| { + super::ports(f, setup.area, &setup.app_data, max_lens); + }) + .unwrap(); + + let expected = [ + "╭─────────── ports ────────────╮", + "│ ip private public │", + "│ 8001 │", + "│ │", + "│ │", + "│ │", + "│ │", + "╰──────────────────────────────╯", + ]; + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(expected_char.to_string(), result_cell.symbol()); + + if row_index == 0 + && result_cell + .symbol() + .chars() + .next() + .unwrap() + .is_ascii_alphanumeric() + { + assert_eq!(result_cell.fg, Color::Red); + } + } + } + } + // *************** // // The whole layout // // **************** // #[test] // Check that the whole layout is drawn correctly - fn test_draw_blocks_the_whole_layout() { + fn test_draw_blocks_whole_layout() { let (w, h) = (160, 30); let mut setup = test_setup(w, h, true, true); insert_chart_data(&setup); insert_logs(&setup); + setup.app_data.lock().containers.items[0] + .ports + .push(ContainerPorts { + ip: Some("127.0.0.1".to_owned()), + private: 8003, + public: Some(8003), + }); let expected = [ " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show help ", - "╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮", - "│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │", - "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │", - "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │", - "│ ││ delete │", - "│ ││ │", - "│ ││ │", - "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯", - "╭ Logs 3/3 - container_1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", - "│ line 1 │", - "│ line 2 │", - "│▶ line 3 │", - "│ │", - "│ │", - "│ │", - "│ │", - "│ │", - "│ │", - "│ │", - "│ │", - "│ │", - "│ │", - "│ │", - "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", - "╭───────────────────────────────── cpu 03.00% ─────────────────────────────────╮╭────────────────────────────── memory 30.00 kB ───────────────────────────────╮", - "│10.00%│ •••••• ││100.00 kB│ •••••• │", - "│ │••••• •••• ││ │••••• ••• │", - "│ │ ││ │ │", - "╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯", + "╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮", + "│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │", + "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │", + "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │", + "│ ││ delete │", + "│ ││ │", + "│ ││ │", + "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯", + "╭ Logs 3/3 - container_1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮", + "│ line 1 │", + "│ line 2 │", + "│▶ line 3 │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯", + "╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮", + "│10.00%│ •••• ││100.00 kB│ ••• ││ ip private public│", + "│ │ ••• • ││ │ ••• • ││ 8001 │", + "│ │•• ••• ││ │•• ••• ││127.0.0.1 8003 8003│", + "│ │ ││ │ ││ │", + "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯", ]; setup .terminal @@ -2744,7 +3007,7 @@ mod tests { let index = row_index * usize::from(w) + char_index; let result_cell = &result[index]; - assert_eq!(expected_char.to_string(), result_cell.symbol()); + assert_eq!(result_cell.symbol(), expected_char.to_string(),); } } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index eb2586d..79c3cb9 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -286,7 +286,7 @@ fn draw_frame(f: &mut Frame, app_data: &Arc>, gui_state: &Arc>, gui_state: &Arc Date: Thu, 18 Jan 2024 16:46:51 +0000 Subject: [PATCH 17/25] docs: screenshot updated --- .github/screenshot_01.png | Bin 56540 -> 97870 bytes README.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/screenshot_01.png b/.github/screenshot_01.png index 27a7932a4ae1bae1205d99c7e7296de8770ecd72..bf60694b7327982edf8bb5dc44defa355ff31c64 100644 GIT binary patch literal 97870 zcmeFYWl&t();0=Zn?!j&+=j?s< ze(zUx>#e%qztdH8tu^Nu&z$3#V~jQDSWS?EoCGQo9ugE36snY@s1g(u95WQus~ZG3 zNDGv9jVt8)hpUPPSjoVN*v1ZI0<;7WgI#O@!~kcY2^5s`f|bG|Y8NreqiJyXnmRr! znv2wO=H~3@f=}7$-L`W!a|{o=9}WKyXV(q29PYN0R|!fxGU#p}u!;SarzlI7FZmg-#tgY;!QIMW zejP1Dm1iH%!>CDcl;z>7uK&d#J1wdsNizrPL>h?`doR>-Dy|7DJvE7%_T2k1Wisdj zHe*xkLtoO_BNP<0KTt$OK}tmAPn#f?qO)UsiuCmqg0e7E=cqO#rI|jbWsTrvd5f8cg?mht#;(Cq7Q0WnpO=o)`Q-b3YEJ4musr&f4@Q`K3$S?Xi_Zde z$GwPaUU20#@$E|RI|5P_wKZetWA&&yI`wQWuN#)2{nNqyk@)@c(m!^+Z&`ol(=jzE{{;23-)>pO;hjIk`y7D~FVm2f z;V}YPF&G$w3;_(zRyM!95Q=(qpFhl|NMs`3OFc4%-{0q~-5aa;n zCnbgS6aP6sD;rtazu>Ly|IGq~4@PGL8%AaZCPphO#=mRWgT)*nApf4w|53wU1rn@` zN&tJ1gPjpT%n@J>Ci^>tvC&`pHV$@{zs)f=Vgy(MtRSNHkWrccF{Ff)tioR!zbG&T zTG{;8f{^_WNifjlKVDi170QB5!Ovdz_OaN{!6GKCQp&{$vAmr_U z5U(__{QInaK^a4!04&UgCdNkW^hQjEZ1ij_?8fxmoE#kVOa^SM+{OmnY(_?GzoCqc zc*H?=RtAve1X>xG0vK(qO@DX%BAiD^L5iQ0g@NfmEee(fU=xS}BnE)i#vmvA|8%JU ztpLhkgI|0yb272Bb91n=vav9+b2GF5M@S7|XAkkkU#QGX46Gc#dwwkp55yb@wFbXD z6$0?P9byfSh#kNH46;)Jfh_q+e@%+`m*!vHvXFo>HUJxl8h`;1P$m{O9wufUW-b+G z4jwjk9#$4wCMF)HzsZA)fhI2hU(&zAhnVkQi!KSYhm7y?yXjvsr3|qB*WJHvErGv- ziJ16zSnwDa{mTS<14n@I?|DM7{?%nHJ^({Cge#FOC3#{_i0FD}Mh=*MI5yUor5%68^7u{g1B4_Zp;!x^Lz z9xNp*2EPaW_6Yu>)6|C9`EbSU4Q2cbO)e0TRWTCd@L^_xv(~^{ixC$)VK*SO7CRlx{c0LkTL%c44S_%5|UOjV?_UjXQea>@M$#h-vxyE zbOoNE+&6JT3nBGK(nf*}|1~LF+@zAtd)HIm$BNF2m(Xmk4U6Zu1J~G(?TZGVaQF-E7s&~y zQ!uC}fPwDq>LHHCbF3p})B*<8%^0eevzsKVYU(4o7$Uz14;-YTbRze1XbMpriGSJM zpXADxH@ZB>*3gKw9Y1|fQ~iUJtsQ+_ZykXFsJDeVQd^vgVB0wOduaKw&a69gY3{3f zuByu+4zKp7&!9(fm-PS-9)h<8R+JLEa@{S2^fxKT1iLfn86Vk5@Rmf_z&HmY1zCh; z<)sk18q5=irQF;1T|Mcn2-Xouw%oCW5$qlK4kMBqx+>I<>k_|cH*l|EiMw&>J{qh5 znI_@O*9^+?UE;om_>#?bNoxpIE2kro095CmkKBA~y@uB~ahIkkRAQ|@cifn2Xho7; z6WD4N=3nJofXHDjl#-{s$Cc1L6D9mW5^%#eu`*R!a1TTaUEu*YnS;Q*?PK$Sa5Ux4 zvk z8}Tv@#VC!BQhI|4^(Dm^IijaBSA}SWqfQuit5PLhNLpO6pI4QT&OesG+I$z%m0d4x z?PjP>Z3%a}F_|Mv=fWvwhDI}oN5Q3>zf!0oKQFl2S$Ie*Vkxwp;mXXVEwGzv_(@}{ zAhL2{^;kP$m+g?m8abAA{9OUn93HcYbYdlN)2sy-+D%!1t?Mh&W7_oR0g@t`#0RpB zO|{qbP$?UfXVwP8OuHUNclZjl`DeY~3idQv3H!mWtQDtv!j2D6d`ywr(IOwubo5>y z>AB%sbaG#QJm1L*Q)#^0F#D0)y(twh78)D+b_0ob71?fwrR+1#>P;zc`)h*cD5qG^ zrE2Rp2CUtZw0$sL21VIl13Y>#*JC4Vso~1!uXH)34)? zBzL9sg>P|^thwlv)ah{6 zxmB#a(_eMnc7v&Yk=eR27#+VYWggo(_)wS1)e{!gYAHun%^eh%lChhC1HFgS(jcT+ ze^akdhp(OPC3>k5gAD~gp|`f2`sz`)MkUE(???lYFxoTwI#$)9LU4vJ0{W5j+*2+6 zq@SJ8oaHjcZ1vu={4!m#M*Kj(?Bv<1r?sla4Ax7YZo9)GJ*T4hM4(!zIT3aO0TdU* zjeP&0h~Xq5DBO{*t{^rdmUPd;h--4L7I{62IkU7%-rjwGzqx}P2jt=P&C9+Fwm83n zRVF(wk+tQMlb=0d0OLTBo8p_gSJ1vo6E79pCBwRKyc!# z`LX~FPt$+D#kirCVLkrVN!7s+hqF6I5bz=Ged9l`swsFZk}spK4C1220Umq-+qCK1QIk9Kr1*@?!fmTl_iXoD>JrrG|iv2 zz-Lk?6k1oA<>F#-B_J~=tRr}M>ZLyk+cIrNZ{e^LZ50m~Zh77nyw7mbg$ZC^Pm}|# zr8OAoaGs?soE}TIg=lI*+r?o2^5)}ryCqE|xjzwJ1g==bJToq8^*oJZ!Vt46!d;_CCEvw zRa>?%LoCSNa>F7}Kerje);ZKs7pfo%pRgiDb~ST0nkAee2+#AFC4W3f8f>p8{NB8S znnZPKp`jP~$2AYLUC1->;!C>WJ#(z-}7~y>K*GPZg zL~-8JmWO}ZmS|SwRm(H8M7zvSv=KBiTDhlzpW%O%O$-vyzog5M*3;a!>9y1wz1?XP z7#;6}82Y7?*_XPtz3+NWmoMruahkyS-~M?Di#LT=IK(~w_>GN$>h?qRI*Igy`D%qk zcG+y(0_L<>-VFAMD>g!eI)m;gAeX=CRV^Zds3CBfGLmUEH`320~a8|f6?mz4V z=4F=-CaK&XanB+YgiYXL?nUvMQa`yj^KJ%wZJ}<(d5)#b@7}abj+cWkL#kf+Jc$q; z($kl2HTX&23EzC7Yt(nsLz+_~ZLaDzvE=xJ^m9>UZ#)5S?Gu$I2O1;Gp>%(0hW^7# zvG`6H-=sIRLS55W8hi!#snxEJ_Xn(9ZpBpcG(41U!V)zSQOZNlaRqdJ!-Et6dEA3H zubXP4#f%x;$Z&Wr1`b(1u467eh7?x6Uf+SioNtg@j(U8FT=$YbhGF}jDHqekw)Upe zLSWJnN4ov1tYwK4eb8vXajE)N<1FkYdY#D@Q-8$^KvrbDE*H}>Gv_pL0SyJp%L!fz zXPly99g@BB4wZr?W^_f~p+$l1&cb=oH2;}iPD;Uqsn6epEp!m}q!D^kue0ti^kc|T`hdPI;H0$h!VxzGfd~h6^ z(^PI}u-Ld4kitkc#$?>{rD@ol{d2=lmT#5PA&UepSS%S%Xfit%eK>IUx_xFDQKE%b zuMeG{Z%YT3r0rbrDU0bA3G0|cTP8LelAXTsRF_ju>LmGVYrav9))m%K+rBas z5KC`+Ifn2)SEBa{sH#k(uXZIqigb}y+&;tlbL=#`#yIgn^dz*YptM zM+*D1V4O`}QvQ(eTwWu8WozIg0%}~_W!AN>d0-}Q7fsnJvz$nIh(o@$AvLB-qZG4S z&V3_iy4U}}9JZx@&EdNdkpo9FiRUP~bx0RC>p1>-Nl~bW{)8(6N@CS9mK-Y^FOvd6 zZ~>Uba_&ojkpozam%gI-=y!==iFa*SQc|^$@u*BfU^-J4HXf^pGZW}a`%2%6yH8+A z{s4|2Nz4~8P)NaaKuaZSG54gHtkGd$EQ|6*;borgHY2Q@F8uB>!`Y%werfK7-+EkQ z^^^16oPA3v(eT&3TLh));p+uVYlf&lEDlte*hzd{T^5!kDSgeJAWIWmZI&zShgUn) z=QZ6t#$OMBd*86(>}`j=?Lm;S)$M&NX%|u#dFnOFeJGwdEOECvhn_Dg6N7>&tWyig zlE|n!ogP8*7h74aq4w%Anhi00$KaJS1?i_jIm6eZ#wml~6*OKaxlZPed65?bszNsUc?`W>ZA=7J9)oQ)qq1qf$+7-1b5lt*N_VMa6B^d#SjI# z{jZ0g*=bN>E9y+mYb+P-B+8JOY?}PAfz9S6&!#iIW<`oPCe?&Kgi~4xO&DhJymDWZ ztQO~*r%=UI9?Wt1`YISoDP1R?b{y@d|DgHAQC+E`ek#Nnv^7D)Lo;ov`bdg+lDiQp zkTd=%E2262YjBgzRi5pm6nnww{O#j9)E^H^(#9jEkIL%UGeuPz(X<~bd>bZ4oWd3Y zCdsAn+0wXYIlobqR7u&kX>r#Q4W=ctyQjTVZ!W8133~eJ&j__A?f9+IL>Jv&_3dGJ z8CiEdwh+gbU&F=RVfy=}b#b25#~`bBY*a?ADc|(`I+^_qJiRK}O)=a;>n94L#8{jF z$A-Qyks3xQg(hwXbSsQRUFuIqtKJ6X!P}GFq7?qi zl99rLnPaAA8I`d_(x}nsWoOR}_v8#)dn?Ls3dzkaEwn2*BS%`(3MGd&6T__91Xx`s zLE^d(C4+${4d&23Rw2WwGIU)>C3&|5*0t+kURt?4Zq>r(=9~$2g;aGJTe)`!Z%{wA zdMi81p=&S1UpLWU;+gWe%udz%;MEz?vf=UYk5_h_vgE$AukkZQZZbEG;;;^+1nGc2 zX?@1@7sHb9JSROU9|_go4MAku-uuCynLjxu*>r)eIn#Sx0E2%Kk+qv^Q~t0y^kt4F z*vsPeE?c9nNAZ|F@iF2GqwR+wR3&+#moMrXdtN6kX-`cZOPM4M;0iZJC8l}mKqb%M=~!OGWvDlqb$v}QY!|6V?>zhZ=HQMVx5v~ zKEOl4@8Vb^c?P#7H(BHlhz*__$+M8$8tV#7NsCxq2P=JtV-K?S?bc9p5>%9dlQWpOLm$e|B8j1;5r+TO4;3_p`=* zQIp%Yj|g!xcHDF3Cjrv*p)(i}I}Z_=?D~8J>uf%DycIeoKo|OH;G<=BLr{W#WVt602LG zX2J_&ThTXsd3C~o88@NhD+#5Sst7{e)$Qq{`=J3&j5SZejDY!-D9~`Lo&A7YDvEm= zkbSb4CJm02-55r`;5mlSgNQ^}d|uBEyQy8;GYFZxmMy;Jx$De>INEnrd-RA|2pPsU z$9KxfQeVAOv#is~jB8exNUjzde@Y=vS#p54Zum5|-R8AK4Gf|uoBttU8Swp&AC&uP zO*3m4+;GBf%}QyCGH50XhoQbV))Ef>;rP5x2y+v4?ne)=ct#IJujc#HP(K#>YU;JLf%-Or!tF*LNK?eVPR;XI*d;#stL#48sxffSNH z0!HJf0s9WAgIl(}(A%5#89keOoHw(Xf7d!#O$L>m-PWdc!MOlRuoIqUQx zg)3j*J#H@UaAjr!pu}K}qF^lelzUOduBo=Aa~M_HO_`SoD%f#H>MPQ(0#1!#P@kY- zYo7=mDA%@3eRlRd4h;oT$0=kb=memLG*qnM37hzYllpObY$+{}=E8^|7);jkaE9=| z6y&~W>o%H*3wSZmHCoy#%I?d)<^_L-N(~;a9*mXSX)(Oe)>JO9wHm};9MuCFJ3J5U zQ~WM0ie4tkl>uCr)En*udM++yy4DX#l=~4$eAzLBKl{U9Z=~J1xh#=Gy@pao3wqZk z6YlA4k@u^trv+>83g`A55ATxT#7w#=4?owqoD|HbiMb!_&Jeku`y2hQFI$1*>{gpe zoc=}4rR=__|JiL2GkFf1+9uOy4gT;yDx}@p`LQ4VSu_>3_>hLvN#i3P5ik0W9$P6X z%3fG!J>C>X&!6e_gs3gWlR_Bh_!0asRF@Qu(pmqM?$@dEYkdg!3g@M`CY$Pu8_3oP zKY9pHNU*fJM$A>SuyMHlgJr?pgttMQ`Fy-NX^b&7yd01!Gv6}{Vg#t4`z44qo_Je~ znOJXju;&dUN&Kz+OXB;jhg@^+p||m6>)aC$DcY%;Qa3^gqx-wJ(AM)K+hjWraMp;M znrPdHozwQ`Zc0*H%O|^r^mh6uKlB5i$LC8twDABb&M=Y**0j-+l>SjbzIglQ+cQie z1_JSsf{1f-SxRx`PV~RmoJlOFH^`V@S$ibFKNQVBb*QY&X=pV+ZHnJG+`cXdDZs1F z3iP4Y+(oSxsi%5mQ_zf+Ggs4$*?5Qk&X4T#jH%t3nQgV_X3xgv+IZLrbaKdZxxX!I zWMt&Tp6y3@;ZDVr&HM8h&Kc=j-R|etSYCqO22Q+ZmlwUfM7@Fz^a71u7jwxICVu~{ zWzQA&@G@Hy5CpM!$oim#_VU%8xlxFjG2x(4O_}2d-&~Hf+TXR#O;8DVo;dNXdjuX| z8AnFoDdecVm%O_WGAX0!5VEDO@VX@(PfmscX$>!=K-p>Dc5l-4v%DHJxWbR;Bk~F9 z`1meUt%N(R&3`f9L+gD4YYz8Fsjq>Ie%F?3hBih9t2lqXuW9cehUK@-@x#eoO>~2& z7jA310&Uc9B^-DJhb4(ko@K#`FnjaYHy$mGWk_)*Y>W)9$wC63nH22l_=^r9PjhL< zT8)*)Klla;Z7AQi>ynrT|J|hmrOU;zZyZ&YK4BC682)+r_2JqXj6N%&Qu*N1oWue^ zs$af`h9`Kq*c+^8n;Rv`WK4UD#RM3@%$G^}F{8sC)pY`uxzG|e?hBv9XSOmPKACMlIfzSP=HoslRszmvMOoL7Bl6XlwS# z>3rh5MMDtLJ+8D-o0L~1QGEBTXGKft{h-SpM_T6DJr3J*o!715d7~$oYou|}#mXaV z+bQmNXK`5qhpTP$@k*@l+Jk5+R(JvGzQx%^K2`fIEQ)Rv`oPlfE`u$T5D z@+>?x)BfcA<20V5tu+r1kZ`Suit??Oo;dyvMS#74qN1YR4J76Lxs>CdVq;@9rgz*( zAV$=HR`uR0&((%%E(eWQ8cCRpq)JIjB7}#BL%1G4@ZsWVf+7#|+{tXfdaceC=kTrL zW>XA6HI)v&Q5TMXn$fhe3EK$YO*a%4B;uPoSU9G39C-uZ^YN=kssfjPrStj5!ZDJB zeDRc+b0*i}d${QOXLzU_7k=dYvWlk<7WdVqh%eYN`6Ga16@Ko0>Z3aYbbZT7c4})P z`RY<2I)^7i1wVhl?PG@N&x=je8{DfSv#$d;mjmjMgtY69MW;b*TK1=55EIKJRaM^= z6&1BS-7M~bXKMM=m#yi1TL%-T({L_#hP%ecqli53ysxgnC2dA*l#%LSjAa;cD8c$* zQCGVTonDuMv!3_Wnyu}Hj!z-+N}AxWTU-7~QHhZ~&?6^_%nF*|XB&YuTcQj%u+iAJ za~(lXocfkKTz<0BQ#Lc1gP*f=?9bS}qPp-?+8CmjP%#fcu9j#0T>?J?SuSiAiy1m@ zBftMl*Nj|8R~Hcp$#AMrvE}*aA>Y&0q?*U+k>h4n6^oRN!7Pi3DrK)UW2-2{DtUqX zT-m5%j??w%5EIE-7fuN~+Wk$<-e_{^BMg&s#kva3&Kq{=#QS?KxWzG`jX-!fQlWXn zadJU9`45{&*g@9hcO!luK79D0N&A=okciS&x!qc~J>6(yG3pJ4Ft+_z1hso~Jf$pH z)8&S0YWIF0@M{e~!R`EceVBq-6n6Uj1D&)1bZ)VZWhC!v3m)Erw+81~0!WnrFa zXc!k4cX+6l?2L^Q@3ziR?9}u=gc@$I`<#;@b$cE7*Hzq3idV-;e4(MnB|^h zoF6VnUAAZT7Ob8?x*uh}c#|Q%lr8TRCUu8-cyQNUU-r%pd}G1?Pn?j5F4lNHx%#6K zxQ;(1PyV(|x%Yc;6i!rB6cr|tTsq%;?hl-+l0^2w@2eTjDFM4rApsV3DOX9Bt7-7x zv$jX8IxkrXJ%7^9%!CqCORfG0IJK<3ccyr&aemBzCGbLJF)Ls`uU3z-aw zSVzMkwzzYtH`d(ZyeKmhp5$UeODzZr2&;s;6Ko0uaKgec?e$ETy0{7=pSYI^!GCSe z@wYZIsk+He!a>PY69Mip#J8X4Oz$ft)%vgAgzZEXU25`P52-;O3M*Dj66A2a_9sL6(C1;bo{bHD3Y-LDXB`aoITN}2{6)_D>Ff1|_lf_J_ zi9Gby@8>QaO~{1Qu(`W+O<*CFoGFD(OT#@PI_5jS5P=vUiIDeCGOCP(kPjv{cD+Xa zAHEXfD`U<&rPEp3n4Wkrh=|m~5y)crGX3V?bn|dVzn-S|0uLu;wC!5@|B~@r|6HVin{UN8KrY)RsQ}=5IxNNKW^gwv3c<|i~IA1@n5=S z(SN?b{?qNURiot~yW(%-Q;~Y^TkA_(r(RRl$g_jn>X1F@5c|mS9$i22zvp$4t8Vz` zZvoW*0E#tt^9h?~9%I>$IEIbl!^u1ZF`z?7OUlv@jkLKl+d!SN=h!wM`w3@(`!#FA z??P{LP;G2Z2R31ZaYKdQiXe>t%%~)SS$ZFs16Nx@8986<)rE7K zF5@t2pr=JrlsJ0Nw?q~?+Zy7+&E7sfsvWJh9a?fQq1H*};l||c;~wq$GnlcR(O+!M z>joygGO}Xw^1d3;WqIbpB2-_W!lKFr0 z^=5OE&)2j`Jv!aFN@_Q#}yi&>n4h!=<#2-b}2i#Z4n-UsFG4 z^kQ?9Vv7E5khpU7r1{qus1{@v=f)-C`)Rm~5^BDjm0Ww@UGBw_E5zJVeRE|Ex#QFC zUUwQTP4`Nw&c3C~88Hc1pi66yE~U}&?nUCwS}$Y}K+;RgYx5~$nr{kbovvU2+a3-0q^G@_vZ#?^su zHXs?;fcb_Em~>s$1=97oO^tNsu0~*Nv~uaI;`htDbuQJ;JWVHfdZ}qY!ZW|BR*hHF#oy^n$pa<3 zYx6Ac29%&oq_Jx-eNaoM;_?!i?jw`9j0%XsY@k5eTf)t#*6-#_q-sXGklsJ)+gz9H zCePpk!S^0|1hCGurVhiut}2;|tAxo(EON92ugWVs*{HnleQ}u=s1nM$+1c!9MP%8k zBE4bsq*53w0ih7NH0aD+4(e_3SrL>{kJiQB$MdNPo|+NfmV5?Sl2VTt$lTsSeV?9> zfQNba;Slwgqtx-Wnvn&BaCT_H+aV)KG6?v^a0CK<;ODn_IJhOH9xGqTNqW`>J(>p( zbb>El7qH#2Hu{fc`B>$Eba;pIZ#Qq(b!sDg^oMqhlgYA_cP7hBePtg~$89B1(B|LI zJzOi}Kdw`6yg0PW65!DEaFG%wh3WShqE+1Er;P;cNyKMQPvqVO(&mJlu)TcxNh6&! zvA-~KiqvL7BD8K}x#|9P=HyjIWoY6|jT}nE7-x`+BT3Y(=mv)K$sYRX5Atq4CM;E3 zzWzfsJ1umf_Ue(eI>}?s)KdYs_;FA2(uW=GI&4?w^SFC==KlVZ^|?chnX_|b^10Ed zq1dhug!G+xhfL`nrqo_T77w$P+z}(6eVv%KXUYK)ACiAdW!$I3H9NXwG%%TE7(zlwM?@l(Ml!dZo}TvAO4YBCERp#`1pX`ccGI zwS2sAIVXkSbwIKW{<06Fg)1LXim+nsTgZP*=v{;Ko1bLh{d0T}bwA&7TUc9s zGHqr}H4G__l)tFDYwzZNRj>i89X znigxKN2XzdA@k96DXE{N6EF9?fRQNhAy6Bir`KIPBZaJgl`mt8%YzIRZ~P7gF^g{B z9<5woQgPwagxWBr`s~>fFhrb>H|Y7K?|#1rc1WWpJi_P_3M9T_@3Y)hIa|q)Tdlv` zJ{))dc)o#@?&=A>Nx|1WV9aVDQf?~z-a7kW;nv!<(WKII+w1;fPO*(QHozNA$KLAO zZ|zb*`BWu8L%7$`eHwQAQ|t@ABeB^>qNYJMl?|3X&R4zRJ_2cYj9z5DngT?ug)ZBy z?vY8A>;|wI1*N?H(?_hr7u!mZG7;W7dShUrZ4?(*9(wx+Z|m2qp#d0)<1uI3m=~9H z4=#P~;@yQGhvMKAikPwcs-7{W-TxB|5rOs+>-fokFNDfNMwH{O7zd*R8jpZoYy-pG+Oz>@PXfYms~z0`+{!gSpC9$RiP%! z<1V?~xqT``vz7qop@DO;$)EtHmDIVYnGvV><948yS1RDVPt&NXWuGuFZ0$Pzc&_n* z_!Nf@^=-v1ftU)AFJ=Mir~^C1tp$`a}Rm>i_r8tUfxW# zofw@pxyTuG2VOE;uM;CR*?{4m3yVa3eLe8#?(0XKldM-Nh125EXh#6oqAbUZ1b1Z3 zZ-Uj1P0@Xw;%2<5TWx@%ini}DX z&#%1uNYOsd6JVgNoSvezqkW(uMge1vJ-E2*k*dq29-Dm~@!^w~Z&HtyTXFkL?1)9O zqTXr*uAG7k4f0$25#FcIQCUb!1U6RUuCjZ@6x?lV(ekF$_YN*Ddc63v!5?lYIgdWt zT&KuJEQ+rke9kP!o02mRMyuGVn8r@GD{i{j$e}zq-Q2ikvR=VU)$QoO50yVPg{B&O zCiKv=KK8^e;jKBv3S@qmP4=HhXbFJNlpgG$C5F?OKjM-mIKG%3UkGTE?!NmR{F;so zaOe-0(Rkk~b=CT$sESwEt0X6%S7CBWvzRY_=)NouzLcRD<$jfMW3EtS3m%I)s|e^9 z)HJz1t@6c?4<}8QTQUC_HqiflJ>!J9WaUS8NZ#drhPu_@?bo?R%ivlWm+5(@g+^4# z0TQzsg$6f=V~!Z^_z4+)g65m1y~fF8S)|*7pWWhmp|yPSqYWb&HCsAOP9cUo7S6A8 zmOCL)KC;7c$KdJe#-b}{wLL8nfBxi>Pe;QJWiGL_V3Tu~XziIO_Z(3{L*H(DPz!BL z!0>^OI1v%wBrWb1VDTHE=%vB;at4O;twvRn-|7k34c{nKPn@_yk&RHG^rp9$%6V*t zf2+!vcF8MBRynS35B#vB?xu~_4vFT*)2sNlW#I2$a49LBuM>m!>+$BamE(!-B;Lsf zSU8*dMyayT=FFa9oxyb7Ek-s7IGm zK;%kt$r+1!R~`0fq+e^SfgBZu6Q&y(lfM0Zpj9QCd~fDGSFM}CB6fLxVCq7E(d~C9 z#P@mR+s(Xlb!Mczs~I8&4k*l=v#rPDwsRPF4g&_ORVnjrW~9wnFNdi>CmLDlAP-ag z_p7*l=a(`P2UyHWaw|M&?krKqArw`|t+q#Z=G%b%?_6YJ867Q80rX@n!Vl--?FS2spCMuRvnafpv}|4D zpxtWWhp+7W`_Z6YP*ugz3lRo~)dvEui&;ks%U?a`sCeRKLGqB@Qm<>;#%vld-NVt6 z#0-GYZ!B4-|9*QvKXdQZWeL0)KGmc)p0uv+t9{dS^MvTf|MnC|5%PFIARVz3!t&t+Cx!#v23 z5ehleCVMWBKaLmC3b`Q0gI8Mp1i^Bmcdm0+%|~&kXoLnRr_RZ!yiU_w4<;uoG2N%G zi}{@_s>Zg9`NJ}Etv>zD^|p)UT`CS}J3w zm6pD!%j+Y(=CCXTJ9UBrKq#UH*8yPW!ab#AtP1lN1#ozyA z@Fd}h=JYg~6p$nMx`%@o9)dxY*0PU-C)YP-lbU+K!^EKGWTO!Jy%8pLf*d*%(H*D0 zm_C)SRAT?gT-iN0eCFqYnO>_~mhG>%Vt1*da)6gKg40l=K~iZhxAv6~qcIKlt0 zrPV;Vh~0;u1UU0yoJV0UXHpctA_w{U8d8f^kC(Gw+n9*H+(6G_=(eT+fPl3lIf?f? zrlr63o91%@lW{J$IxR3c+6B;|sEUzo_NOkl=-Fa;-*|ENBiFjeNTyN3m@5mueT8v3-Zv3Iv9vByN8pH=gc1Cf}`_$#Ju5T3lf{)$Cd_P#HE4- ze7thsP4j?I2(Kp3MPXf?qiC1p9{%$)}rM zQ?|q7&Yf=4&Z8o@0mRIkTH#IQ$k}a_QwZY7fD2Or1^5sF@DJLvwWlJMu*^b;o;Fo}p!kE1Pw)6kRNhr1 z_uJ(R+OagcYG6jTZ~JVyxXux^i+^5M*1d zrB6U_@$@OYfHJCO{UO93_@lq`3tR%l5Q@6$TX|O<_hZngV5CoszH@aab5v;M+gO^# zLDr9FR_}3crcihshDfZsjE*_&FG$<_#X1jOZiXH{O5LsV zPH3~?yAdE`u6NzfE-EAP%ftImv~q%aJtWkY+V3wKqquS(yQ4u$6<;2!_}t`LIlf%I zkjKns|2naK5i!+k-BlAFz&rh}P;=UY%xdz@wHLX?YxKAgCC@=saArcC# zs`|A{l#3ZQ(f+|i<_~9USDoQ|haIaqY}(@Q!R*b`D$2ma#$EK@#bwpUHhanpoUqUv zgte8Er~3$rSMmirx^+F4jIb6Ltxa5UUPPyT$F1;HBPw#%BZ!VBfa;dc8@S^|ce13V zX$*M>-jOR#{GwYI_abjkEzZs=X^Yi6Cx;?&)51gJbFhC^sz0hnkUx?QxL3a@Gj42QDBFY68T zK>1`vKPusg(}(_@|4i0(g`H@YUEYq3d$R31W;#SskSR=&l`|7+z|N>&YG!lxe873} zXy`{^KwFWo3;p|WkMQk|2J(nCG13sidx;z}CYd~V=k101`(X2?ZWPm#7KyvFYRRQj z=M={3#T(Yeq$u>)yXxy3%T?=hyBZ9k4KHX5C1{mfi%((U;V)cQ8oW>kF7(Yr{E%GCI zS|So&26BtFf;d`;^dB0vGdh31jYhsWe7*=jcUwX<9K{ponQtcZpL}Qbiub&*YWwF( zY1pW$hZ;^Xw^?cNlSOIAw7P0Mc4*&9hcIQ*GZb>sZft9Gz^&U@~Q>X zW23aK7kySY`JcuMOlQKG<~36LS6N`lmKq`9NNY@NNAYMVx5;gm{WIG#)bt)rTCnYnU z?aBn<+mj_h(3RM&0AaHqc00H43V*crsbc-|?k>w%&8_J{?edQ$Anx_<@fH+EIi(@w z9|U;f#kiY27bS`;UZ3D$IFGju(#ThVm07lr-E4FNTd?UF+Jl7$FJBHzLYJ_%yPIH< ze)g$M3g#z=Qk3sr_AaBLv@QNv)K`m=dKY7wbZ;%!b*S7!4O>*Sv(O&<2kgtT!muY$ z(307%2e<(v=8_IAkIml*kCnD7L=!%l$GLfMVAKx3eLxnQ%b3mfL^*Wp5w3q4gfH86 z1=8`ec;!+HqDn`j084BnCa4P2^bCatIAz1o5#Z{$TZL{Z!N5$bK1!(Dba3{OwKTB` zrHlrr;fmfIkD`*mT5?+$=(>HQ5NLE|eP#%^d#mGjAD5~=EK67(IeQ02t?cOu-Nzd` z+h9vCPdYHm48XD%;yE0#-#pq}_d5go{9gEM!beQgCVQoGl}Q6TXAjcrmfr#qNx!*4a z2Ge=EMGZ%u(_ZP4$+EccsPI3HKIrBNhfF}D08m$daH`@IiiZgUmIZE#c@z6{>Et84 zx)J!0y>@MeLk_IHwTi1Y%fhQ6n+;O}Ss|!oYvxp*rlxh)G&|A-Ye>PMG_Q2(Ip=6v z3zfoXoLqkQQh*aWZ1`%guo1ew!-tyZ+c(W>)KpEuF4H9s=f<6HE?qkuy7@-cgE$!c zfgP~gjl0Owqsume^2PE;I}&)|d{ZcdUD*LYmG2e>6B|F7+H=+658`dF((TCQSG(?r zbbG0ZU6H*sZ&V1`x{!wk;`|_9W;hwD$<>tk!nt!q;O?N7lRuG%A^-YB3b=&THo!TD zA5Py_CBEHH&-V}(Eq1%qyX!vM@Whr-9x zI3uvM9}PAW27Q-4)KW%tsg^>naS?9~(jRTw~gV?6myei=Tak@3_=_?BkuG6TQUSh{|BQ6&XI6BoNN1 zbB%@45-alXo!KFl#E}|^{*}c>HG_7*0^^q?c2Kih(;e%Qx__`P>ixCV2x752@2tN= z-LHdf(bOK&CvNBlo(JZ6c`4Vrla{dKdU*43Q_Q&*6G^*r^wp30gsCb{(s2&%BZ-Su1g3OoIr301b250!QI`R#@!`AaCdiicXw;tT^o0I zhUa-dnOXDeuBy7Hs&226ea_z1MYfmj^y_mG8|n?*%82PNw}%iGA%?>wf6gWZu0t%w z+Q+UQ$~`P3PBc=L4V&nAvRQ9;JPz-^PC{)@oC6V${4ADX8{=0NzIju* z;p_9*E|w+dpKSFyRdxXz%UuV_m`fmiEGoEMdc?{6wB6~JDEk+osEgiYRR@DN@s32p zn6NKdvyj`he%D&_w`c<7=Gj7hN2)yd>!492>gJ1trTjw36(j^PRryx;_;4@mJv!JaWsV=Dd6q|_iK|JXXa!*0II1or+oAV|PoVEn6YNNh{_ zNx7|{*S@El;hssSnpdV-bLxH>N@?z@3cXe$C0J~4&*L`kt$=7aQIkry>4$+yKdu0{ z-Ns`F3<_llB-)Zmu`||N^nJr1K2Z8}%B52_)Wc%gERxVN9o!>)U7l03h^-xV@+rc5*23<}|-ZMeaK z!N4Gb_nD$`>a!`&ZykaaEORf{JzvwDchFox`rJ^MP5Igd77#<|JkagG`N7lQo0Sfm zY=E0?9Ij_M@tU)#B-roiaVn{iHKi>wqfVxi=v2CStSH5dV;*(B>P`}7Ze#$-MTjL# z`BjiY^UoRiL!C0fWoq3Osm4w^A|CC`nX-@$`&#KtH7FI`F1lga3~H(PUhI*o2P{yC z0}+gE3$Po0rfI7WAhd^0oU)@Y!g(0u~3>Dt!MaE8>JTMo_IQ0!ST9TxUY-h>Irz<`>{ zj4y(p-HZF;Wlc3UV|nuUCTdS*GbB_qQT7Ew0-@E>n^z0C$KXSD#y5&1rIb`yLo@<} z+(a4V()^(_XKs2vPkOV_6KB~cq+&-!K>o0olB{3OtUem}ZbWp^u+x-Y@h@9ie$AB4 z8-L7cuY%n$RO7iOn4CSbpQkkT6d#Q_iE$y&&dGrikA5014M0a+qfNj}qRL34D&R`dTLv$N-;R`bE3PF7a%#vBZJCw~SlfsJb~pYMboI>BzOe zKbtNbc!beVD?oHiN*s3}Zhm8#y3FcC<5?jXkc#nuhnh-9P5Tg;`|F@!lkI0C2?QiF9N~&$85IJ9P2L=gFLBszLTa zC#^fOp`5j-jY);z8l@)I|9$L(8s4S0L&vlHN&B8+QW?FBG8|X(P#XT|NG}B>6OwuR zF>#>L`2*0f zml@wZ9cOuJcW74ace)HF%CHYk({$vZ*IAO`uEn`dq&+_);~=$0wTI&{M|+Q~W%dpr zfs76F{s5#Tex8Z`1v@x{oFhx5ah2zA?`h*Ig-*fhAk0a})63(K8t>9qD)d%={+iRN zmWL9w0ArNw9tF6(1((A&Do~l`@li$abXXJ$ADhy>f)|FbP!6QKQRZ_xti1a2YXhW7571z!Mizh zNbZVKq;Wznu6d-pIZ6qk%CS3Nv5vjL@8{h!WBdFT|Jv{aT7r^_UtAnSku2Bcbh^7m zpLdagwJJo_y21X7nz0p{$BKsANDC>YrUP ztT7yd=i@D$h}aL)3t(R<%{|P!+az3iU*uPcuT;Uf8o8iEl~9w^lvP%0tHoMSl8Wjo zjiOD-y2Xlt+t_icdU8TzaH3>rff^j$*RK?Cp|$?D3l9%N0!S2N`tsinZg_Y`^(VM* zCOl2|+kmk_CHZ!m*YT}ize^Ao_V#0_23aXl@l;?SX;cB(%25*Fv}vC>6*8@vTIaHZj-u;P0P_=y+~Oe`d4B86{MubXL}B z;8EGk*0-g$UpRq3!6hZqIcd%eNL^VjMBYoYvKmU;~%int;YVqUXWn(3B zT{DXPm@{&pv^YbKojsgX7_VtgD+rv9&M1?A^YDNpR)o-uBJ%NX)!$9mQ8+YN=b4*( zJ!V+1amZY~vGZf&PMhz=4kXU;1kC6-&?Dl?v$r@1twbKF=5K6X#(r9)6Qp9V8Ojc= zI{rPgg!5`Y1a^(f4I)wJ=UsuM4b*9gv5#ae3k4;avv{0NF=&_eaVBi+tvp13?7SKm zsePt(xnvxM!nFW=@=k<9A=hIIpe83o^%%5({#`!$bMvR{e}Q znd78WQk)9QX#Z>2%U3D-w4W zEERwgG1`A52-~hgmLB@02ef~%#v>2=#rGHCr}5`ws`r}!!oMk;;mA|})8R&ZO();M zAG)OfDVNi&Z5;opsZ2!m`yUJ1NLqrDVMo?#b)^BPh_43!sY47YG*9cF+6Ym!gv$ek z%Aw_v|4Es-rLT~O1o(aVo(uP%;(y=&ucir6SVV|RDnTf9ke6Yu4Mt-6!p5Zx5FX%i7Q?mFv$r&esVP{a7s zamGuaC{grJ6fNuY7Y#RCG9E@1S}H+XVM`d_UrR0je&<)nq@{Qam@#efZ8RXG++Vh}zbh1K^Hv60ATB?t(jjtERCKMCxt!qRg-(g(65~R``5u<<& zx_`@~YO7Bf5h)48fl)0yk!J@UM-%|>iXM_G^T|>R-G#@ zPwy09x3xq&3)}>$q-Se)?}PXPj`8~!(+5gd`<%D+*2o4Xi`ZD_cev5o@0Qxcb!HhY zBV&pwq`DI6Y6V6Bh`~M5X_Vm5i@_1kkO~5xuHS`CO{owBJ+D~I#0MI#*=fAdn(?GH zF8;tE#)vG*3!gPVn?(338kg5VP0`hzqX8yC_j&CgG!sp1ezlM;9d%RfMIpKPgo7fg z!xfp3^fE&4G~fqqP1DeTlPh?On*5p8qSk8JPmUH@a$7T(dQ9m&PS}gsgk-gg12?Bk z+0`_z-F*`hb~pAQADtDwwrol{6oC@~^?q@|20+Kt8!wMY&_ZhW>+WXq zc-*(lBu{HvF5u1!{{eZqbZn9YPVdl35ULcJ#kqG>RQ0&JO{HhpJ0H~Pn;s**C6kUq z<}0x8^w}Ffj18St6I-#>2~?1ian*cylkU$5A8x|4_{H(nGrm7WH`-~P155apwnx7;h8DQK;mpr8^ELQ!6o(>VOy{mT=%!gKU zarktE(e}kMg_M|nlP;v4l`2qpbd7n;cKO6ZI~|zXva+wQCCkLzkc0-43zGbT<;)pf zZ9*v~Q5#!SW4o$Et!q8QFwtc!rFw%&#c`xNdI~k}gBeNumRs^HV|atp2JC-4m+|~O z{3*Yvkf~|*!ebO0K)GIzx9(^RA(Cp%L||3R)Y+Cew~o5nsL9a8d_5y-KA2N3un!8& z`U~mtfyJ;03|%(o2~+O3td9+ifvqcWpRdaUwfuUtL~Ht6B1g({`sVL%{M#?*g5;&n z<{av0!r-U2mAdC0jwz0ib3IhCIv@yBEP~Huq9iFnex0S?rna{0hwpFE6$7366uXT2 zQ(f|_ELO7Xw}|%JlF*{pvP#(0+TQ;17!A04O7)gOAS_KiUFjy)KGzIz2tK}^-Td*^ zDdi#x=a94Hn9*}tw1CQ3S|XOSiWTj&f~4ukg*WSJSx#}r>P)E@N+Gi-XPV^)KnyHz zyt?X;k0pAot7~g(@GL&Nt<SK6QI!lz-=`*N!TSnFvCTp(biJpr z0;UEMdAlNElCi*Z!ifa2yKB=Y0)|WcG(=~xecodjzB!<&%hjI~{B9NC`fSD(6iKG~ zM7dC(=id}q2f7hf+$)G)nPMbKo{G*y$h1tuWeuf7IL4PU6Qi*5vUHF*`6I^#Svf#lmt=W2nvu!tjKPM131Gi9H%_4l2AXPue|+M|UJ;Ao+X$ligmi z#j7$K4Z)b)^`--1`h(Xju4iIRLU)f!9;Nqh6J*R5YkjOQ=dNu(7!!fK`p2}{?A zvQulz9KTV-E9F~=f5ICb9F8QyE0=|Wz=tggISBih{={`e7ZImpYP3Cb2ZN$PvH8k# zm7oy>q{Qy8X-)Gw8@-Np8~5{x5i3}Y-Tf!>6Z!oVkHc+$MY)gY#hGn=V=^nb7WnDv z^s*ABurZ*W6=yzl&@Ib-++ain0ImI5;!Iw7zZSoybP!er;)US9&}E@5d`of3znB{^ z%rnxeooiPeIzFtcL8Y&Xp~iEYb}&Bol90T`>-*e2;?ESTkFrBVpL;msg=TqNY&mD> zQ%f&cgmrk|a<+>5gBVp>ks2AYvw$2vP<=n*IP27V{MJ>SF$1r%`)8uE4a>bmgHOcZ zryU}~Q%iojhZNwXRDBNQ{7%$F5_A_?RwXmU3oEL;j}ES&la@}9n|Zb04NL;fi232U z1k`~&&EH$aUK~xZ**Z1JnhihMA_)PsIKK>sa-pP$wxli^LnU4OrW{*W%$@EQwvqAe`&cVk=`9B`*A0Qe;K(`T4U7BGs8Q zHu@QMTEf{2zzU4&TjesI;3%>!2Y2C4U2Yr>(T`A)V&ZhMZ|;VYpAh4Y+)euX2zu9m zff&qsIXCPPGytZ`@+E=H@*o14kjI%^*LY=LHLGpTGG#IDYSQA-QockG0G8=-#TYIM zKbqAZRF$XyA}iS9lo|?E#npLp_b_kq=+*G%Qp6Uw^cWZ8zv-!6!`xhQ9Xqmr%BlPN zC8+1vm52OkSJ^d_h#gbIsrF=~cAG|1=k@R8sM9v7MVFrE$96bjNS0Y8^(&$g`H#K4 zxJK| zq{G`q2?1U%`T`Fn)A*I|JDakEd)thzW&(xDf0Z9>*>JTW_R;KgZIqyiF@ELE%f+{5 zc1$Zb=}Sr=bICqq5a=QnvlJ(^ka%7(nAS<)$>b?Z6}^-*HtX6-WvES7R-Hak6!iS% z=Y#3G-l~5I##)I2OPPJWzlUHGT(sfSJ_khf90rxi_>&Yx$AN-O|3Eh^3~#eO*pfkf z|BJ@s&Dir3CmfnMwl>+12C?#bpUO?gLP}kKCj^YwgEFru39g<=BmT8UXs3j=ab($} zH02Q6+!2~o#He1wl~sEHXg3HKVDUc=l|z7_txkPNjuP*N`4eg5bG$&Fi%aI2ccFPghl?2E^ktOZ!M{VVM- zXH->t=@R>StwgCT8akQ&xI(R7mb6MCvSM^5r2bhsPhhD5uA`N!3{%~?9l1S%$?Kg2 z3x)Uu)AWR{uL?Dzev99b%JOIjoeWvA=V3#B89k@pS#R4#Zakk(qgcBNwVi83ETtp@ z(%9>eChe^wt?p0m8Q?~Y#4iEh?&S=hVu#U2Oy}-b{5SOPWb*U}8q*@^F&n7%F+#&yXc_ie$!_S8F<&_9 zWp?C9iQCQcLU&qJSSs&4BD0$Ofa}Wqt8vx^F61pbz6_#cyS(#K$TsBfS_=#?eg`DX zIvg>{)|Jg_9(w%PNm8Umz2-?F2E<|-Q|#3f8Sd)X3)0d%I8U{BizX7e6JY1X@+v&` zbM1AnO!(qv@#bsBjLs<`_t5ws>Smo}D<=}!@X3OUSN_wK5y>=ctAzA!ygpS;N0aBg zz48bBs*nukj{RJE+;a(-e{A+7dTro;vtbDo2uws79{3z6y}oI!omfWHL9wko@@jW{ zRf5w4OLmyl`|Up?ei*e#)jQz?oNz+c7J9_TAKv`B$wK4H7(S>Qv zBRb84sImB5Py>UWnixWVmlyXlz|97O9iWpL_j;8RF9;yLg*}AochLL*yG=<+w>?oi zaegHdQPk#(NE3wf7dHa6DB0 z<3qF`3W|M4QpFH{%6j#Vh1xjn479@&RM27FT>uRJHpKy;;C@59QB;Z1*!Vc6y73A3 z!W)&;J4ofsBK{a=KGki*gwUxca3L4=q0u|ox!bdAHGoo)-hQ61#u5 zp+CMC1b9tr8&P?o1ok;Rc^!#4)20lR(9rja^5|BvK%c<$ci<0H*@)V;rxO3By=Fu5 z!Wx(uIH3en@To26$X-&W@O7uJ^-dqoG(O?H_$~yP!E^3vOFsS0+nMtQb{U#3EGI_y z!jIQCQ9!)>c8;mGen%?^_BQTHZiYRz+51O~+QWYI(4opN6ErG0L4_W*PCZ-Hl>+`o zE2U=8k7~I;U2?Z-GVfd&llIEm&r?thn&0f53lMn^ZcwpX17ekfF71tl-F7%8JD8P~ z@gO1I8eTbu?$b31SnQw~tF;i9ZR5M*_PFr>27r2=rm#(j5M(@)jf7@KT^EVz#pxZ~ z69PXQGct?dAYXp(>Kgtfru!nt&8Cp+k{_TuGiL7P@&Q={r@ro187|{>@~{!LQJVi* zgqAG6T>Q^F8!?4c;0U7sPHGCBO1KH*`OTWcV{@N}Lx-80@ zfYA00?ZQ2yHYVqD1*W_t_?Ji=?Dg>wT@1FYHc?O0WI#kJa7l7(mwRb+aay7?Ql-Y$ zeEyD<%&7jES~c@XK6^spYCr^SLvg%Z+gS5SSC1$jrhL@M8%c!&rI^nc%*@pDRS3ZEC~}4lKeOT0?uY zSMBYa8erY7H8~R80Sh8CxlX@`UfaWK2rXIZ;Au0*Y$ipL+-J1xsS$_R@{{=h)$cF63%hfj09qe#`+YV}f;L;cUN7bY>8C!g4NAs38arx^V5Q>#Sn80Y&M-njGNPfsL1@Iq zV2`zERQ|=f-f|Ri^vkcnRa-5$vOw424ZNIKvTJ9)N6Nf)vC_jCy0X1Gxbk@3&ZADCUTo2|?%p1VbtQ;H2uzM+BZh4(8StQX=TU&E^hCbt9E^VKJ# zgzVm@87Q7s2cEc31HCmPlhYU6uuVcTt};Bjx;!^ns;AM)jxQbpJ(whKEr?;Be(iIq z30}KLaahy4J34m`iUK?(FqWFi75xr$+mPlL_jfY0-_Y2;lKy%H?T>7wHygG!(D2Yx00NIr~^%uN559mSj6b>);#N z!ZW=PB#=yv;K_HIbu-nqA0e^wrX_AZ3F}Wao=xaU4Ajh{V-A|hUnFB=TNoQz3Q0oH zI1OxDG-{{kqzDSPhPlLSq6I~W93r$sxDyE3m)kHgb$@`S{>A@VbSt;9Zh(XUB(soB zJu61T-^+YZB75#t!(AF2GxRu%E;w1LN7hig&wXQ6ny#lhXwRJ(Ol*=gOQz;*caM|$ zdeW-b!r>iKD#YKDs6WpYF_DK#Bc%VlS(a*(e|CutXpCXt1Ui>b2B)YL-p^&h)k;qTuWnSb%}h-cs?oAK zk*d5cbL%sF4-)6-r!?y22Tm-26#nBpJgPe&ar0{zYBwM0J0^U-RUy-g#0q$v!Z@vn zd-M2rOR63v6QRUOTeeSxcc3|Y_dWpWRKIDObFqNQZUqO!wnWdsmhdK~pDMkmBJnoW z>aRG3^<{+&<>QFv-m3xMX(~hc3w-1floJSQ3x64#$&fQHMpd6AYa_{zE?<}UG}rbO z`mQ^=HLd+O(SvDdQy4u3K@sIW_EHv?T4iOToQ^+($`m}6mDy! zZH#{rgMpW+ILTWO_M9@8|AVgN@^*D%bBb7 z^3t+H_=OUzhRSO%Sk#-`v0$82iAhq_ml(Gb9adl5QP+D(^vT?O7RkPid?sjD#j3G1 ze9FlTMcR5cj+(+(j$F;xlMhR6a%y*yVZU8M57N7%0X0@~dfvl9?J~ z`^wPeT?A0u9}=eC@bb_Z23%HnyK>K_307vCVrI?#?lmm5hw%pcO*!f;5erxM^sCr) zEX0nXJ@W=tL&_2L?hO}euWEwt=Zui+JQ-+|Vc@HD@RB_v8lnbZeJSIw=-S@kw)#9n zE>7e`<|x$*`l~sW?ePlyTAr+gL9rta2@2y9lkJpOod!qI*UlZ%>9e%8ch9Fp1?Lj^Ka27)0o-4hOuUxAsIpLSy7RtVKd(;3fpYeixnmpdyoBiiG#Gbeh z_Be8I!F%v~GK|@-1Yb{>tg7of5g5Xv*hJ3sdu@Na9g>Y24Io-#+$9;%3_IlzlY=Yo+zh&U>O z2m6-}5+71rS>DOvV9xT!#ORES{tZQf>w+Bb=VU*+@N19o;>L>0DKo0ZuXtsmDNu-0 z_>~<*xQwa}s(C_$J-e7wj67NXSsRm$O=p@~C%+Yz(R&8-zp>yObFzV~J|m`A>kY5< za#Z|CL(V)hg043@q;Kb}Hw#o~*vZWvjh`W7WmVyLba4{W5p;f0G!nJ}wMR)j?D-UYe*S1)fS!w5Nyj#FH3u9>pye9J)*5-J~MP?xV%p|jSWvzfH5i8V(+9%Am zo_KwZ1p2KzaaSwa|FTawQY&d^7FphVtMRBD| zn2~;boh3S=UBjfkQi$I@(b+SA8X4|4)&MxY|slfuPTNi)A&HOdjk>xn~joI@W!>IYy$l1XIZr`#}rQ5D=6Qe$WdK&{chm zD_`C=iG1%d`SC-u!Q1j7*(@Z?dZ|q};=UCxejg)m%8fI9gUF^&4os!*s5FvQavgC& zuB}_iHnwg=l-hd&Z+SdM8j;z^i&zzPrX>0CgO@(O55vFOAuY5QjXowL=V$i8aG6GA z(8n9nq)7U~P}9MUL3vFNfUer<=(o4zEDDaDrvK>f#}@VNJtg6Nnw3+bm6^^$uV>_N z%IPSuNZSaxok_M@Xk%cR=+E&%*sk5Go88^d&~+|m>)QR)n7a7%FH>$aRf+gzk@+QI z;o-EWz$w(3y5q=_fh>Ac;Er&(dV8>s=MR6=?8w~ke0S}7TFkvzDN2tzxXY7ozLU#} zuR?6g0dljc2dIJ=1eVf!?H;OuPD-CNp1KrP1?yOI4M|*ly3xzza)%{(W-*0E1`c1RMegLGlZFjbYd4xbonOxmj~aEsS6cvI*QR$gz14k4QNaw1&h0O{D2 zD=H7-C~W;4;T+MF%-Lxv!&;`9bX}fpHh<1gx3d`Q51nm=_VJInBV*I8^^1LW&wb5c z4STKLj6Op}uqUY(rKF_pbrfjb6ypi51lH5M}{HO$pKt#&a*a#5BbPvkSe!sAs|^f z33|r7%v5x=j#i`xTlK|(x3~Nepp;LpE^ht{%uUkIkKA2-guV-Y$jp``THWV!`>xg4i_ZGWyv)oeL+E8)FUC=UMw(4Abo$|XxfGufm%(K800>;A9fm$^7cy zk-iV_pyfB#*UnqgLSal6j&ahn0^j4to8uQm{UgDSktbs)dLh-WT-Sw52BjkJgExZk zh|#fc-ZC5`{2ngk><(F0GK_w0vTYdi>|Tys42zvNIfX26m_C`OixIsLt52tv>j~xH z42v{Vf`L2|s@fQu29s=u+V=%Ugm=d>&ij;_9^mdavV;)6TN>R|7o-it!-%D*eB&=m zIT!utK2lmULsE*Kf#v(Sf-I(9CF$(iRAO#10>|q^^gmfw7;7avKJlLG>xB6hVHK^85{^3Wy)Voa z^11U%_&ES!rr3>9L{JiW)F&tS%T^iwW1$5@WkOk%hn!+mB08xyRN;OaI^^bEL`S>4 zM2y{_Fkz8=SvCCCekzm+*c)5Zc;J#{-a#4P%&7C_ib zyLMX1vrT|V)}rvT*viRem;}DBuRj00ZsBO@(_taH!Q{v=BqaoH1X)))3N#ZlU5IVk z#nU_cb1ng6V-PQxY4xSByz*lG{6lf*6)llIU7X`tmwg}$b8(Vio2ew`dee-fTL;T( z=!*bt6dil4X?;aDL7*S5hx#-+wRq=e48ydsosQ8Vq9%|9qf?`NJMel-=b;26FbKer z&`k=SL`PiVSubEBLXUZ0<(pKxJTI@w%dL<(4^OI-X;|^NoMs694|%bb<`p@=d6w`c zVR=j$A-JCDx}-JZq$x7N&7$;6hQL52{Yld-;5I@+W>5iS9s1m)dSlgE=H|h#=uyRnt{; zBE}5SH;4X^JeO29ts^Z^Hv9naQH<~S;kS=ACEs`ScF%knhJ0Swy(OCEvoy(I2b(NY zX8V$I`ebK^)jMLzy|OBR6AJJGFyvSVsDo)4jugS;P95Gz>XksoKVw;%4?@_~1q zz8<1SSrOj+NbdbSs_#|eF|>UP%M$H;*-r|(wmliPKn8A&WMUu**=%*(M6P0by{=Qe z+YCPjz=mNmzCEOKbzv@)64wOFXHc3jiovHMrZGJz9l1GRp6MjXg=6-5qNf-KAS}Ef&USf$=%{9);{xHW{9GDgun@K zcsqE~xJK1tG1a1mx;uU6W8awm_9wW6F&S=WsiwEGTW!zE=#?eD@*{b;Vltu!A>PEA z`0Wqo8T2^m-5;;Ah9jUjkBv`~jI{_!0<@tF5BK6nQ>lWAD2{5^lMv4oZc(W0pzRC1 zVRdZpLQ_`pmA5%Dp;fad6ZV zy{Pz*h!Xb<`n|PiZmqRvn6^&&)Key8Fn@cJ1foM2uTOu0x1jJQ$v8Z-M1NRHvn|Pl z((Qp#5gkTKyLNl3nQ;Wz#5{i}4<^y=OnDSm{KSTeVUYVt75E6Jrm!pYY;b7mEW;$U zGdCTfA4-lHQcWt#%l{Dl+;{1#UC})`X1i7G*hJMp%;W#MdXae~wDNTgHrVn}Cr2If zrB_;8E6oc-e?OhKzj=nBwf%}q?thmZnLV%`y>a^~i^oq&f_4SMb4R_f z*wk5>Y-mogOMcIc#ut{UG1Ym$?SN7w-aGo8F-Mu9R~_~Y5c#`^Z;VORJ5A%mMI!@U zFj6Z+?#-zrm?CFPfHc+wEbw7a@e;)9FZ_gVS#LXAwYA7%uY++DhUa_tnCCPusTgrB zA1tP5yOxt9>c2)OFJ?iRE&v-gqB556S;0T8-&Wk0p(6K`Jx%CBa1&?T=J3A2EL&oQ zaVFg+Pwbfv%hR!*VqT~*_pVNqzPP>tJCKIMRl}&#X`S*CfDyX^U>+|MKB_tD+>VDunHA(V-jEkI ziF~o;671YRn%Zwv^*d&?wv=RGA&+C=?ZvB%(9{vJUtP140mKCQ9iv+3wbWZn`wi$1 z`dWJDx28uq%=TP%m|-dxZ!_e0)MN8M0JP<#qS0>wFFsh4UUlSAQ43>MIfT#BeW!%dciNpImGe{rc zfn8v>s)uH)@9HYtykXO3t9B}$&h#IWZ=Dj}Do8g^HN3zseX6omnFcSF>Lhg7uqDc& zS~52V`upazc^zVgFN5uBjfHL~^LAu{yE>-KDnX-KraC)cE~&)4y}cV2#2(Jq)tha9 z)|pO{{rtI|>PnfQ3Ax|<*ouyz4T*}KEP0KSP{;@Bow+onboxdy+@};PFeJ`cpSW0e zQfBc$)b%;%c$UQ{SN+FHZduWS{b<|DFo!xn>4BIfdGB~RGb|EvW$9T9hQrWPKxy7* z$e&O(!~qu7$8h^+b0`?_gaU zk64(+_wOfLD2|j#BISwn)NBrL^Wg(cE1U)LA1&Ws*`PH0>cE@j1LiYh%&pYl-4hgMD zY7*ubnhH-28mL}2XMV^~mP_uXk&b6e9S!^0f~#PdGA5ZevF(+rJX&jC<PYLom=nuCFH)sc+`cBsAJ>75GFg z8%)@X>oUqJ3{6I#Ub#AZUr1L~4Orl>sWmrqa3fkplnhdpFaM}Vs~w;|m6ucTT4e$V z0%Nf?@Wnz-u%G!`5(W~nu9nF{{x)#!&?B082G?viPHh?*gw0wsX%NAA2>+KtRG8Qg zYmuU4lTW(!7|8mDq{*icb7ZbpXC~ttjo9P#=d0d-sZi@ZxmEvFrV7mdYoa1Zq|(>$ zhE#Mf9vsI8^_%T~Dix5d`L}1Ea#mhA`G5NO|0!BiiHU-L{zp&y|CD8`+p9;$!670V zUvBlRInGMEy1B)qrjAy1y^m)3yo*~~R~9=7)QO;-M=i<`gzh3MwyzUW2@#^aDSaw! z1vx~a^Q_V&-(3Q7nLfLdjZ^;|WP64sBEDY~Z$f~MPV{e+*~_!}0tf^I1cfpgqs?}^ zW{Z{D0|#C&_r|?{VevR@{~+_fMicnF%FnfDFQamKpiX$}g}`lJobd<4efuiJkNy>M z5yk2P*Vn_8JT%vP`w%TiIG~+qFJYw~vw{II_zr0^DCOp;V^4%o3*!@fp`>1CK3Pb`* zIMQa*x@@jJD^>XCMVj@3`F*rUVh+{?zqWS4n|Hv4F(%D2;*MxIS8f0*g>Y7;@+=|PG@s(yt5%9IBIjIdj`QJV(Chbl3J*W6jF_v|gYSyoCNtwKos z6?JHGy(t990u8L5Pk_+-QtAB@YJR7tw2Cur4Ue3uKLg2<_l@J}8uq0UX>3j&tdJ48 z?C<{Wy9jFV?VK*vCcGO(2e|d;1b%foVe?vIIx}>UR*wfDY-aBA{PD}LTDKZB#sNl_ z9!aMVn@X?i^B2012p3TstB4J7qoI>AEjN8sfbM<0`wnBAPF}$&F5jF-yrM>W&}mO8 zxQA+&&$PK7hXmmT7yQ1K4O^<|gMR(dk;1I+C^ogHSLS#zsu=YxjHkCR-b<3;6*FQbdZiwETV*@SHfD$`WGCLF)aWq~VN&PS)Su*G1WDPml3179* zi4MSB39>jTTDh!r%;ZNXD2$cKdngzm4w5_K|7nzrN1pm^YrxOCn|7jMnG#8x1lO1GJj^Nh} zba{}vLxM-T$<}>BeTbvrmvCd~?=sf)$M!Jpx~fR84#KBML*J&IBP9VoIgQu4CfZ-}CJaOXr^f(fP{o5&{Q5iHNe!F04i142S0LnW=-&V_LSIdf zpc%$~WH9XuTb8y{02La}OafZd{cgf)(j~U)d+}0@;14~6$%SJ2VOw9c&vQ(Qt*BV2 zzuU}L>;K?Ye5qzI8qVWcp3U=or4!uECoVoP)4AK|AG1IcD-k6W6d<=g+m9?Ng@NdM zxLIC4bx&CS7O&yc`}_A4_?eQgEkzVvk2WmqRn2nz@;L?C*=PHxGZC8h_tlTH`2B2z zzlc$^WD+;sxvw|5-KaJRsqzA*Nd&^}b_eZNt(rK`^&Ff{M&l_hH`-G^rw|9rpRzT< zUu=3bYoT>7%!j{U8Nc@{#NYP`O9+xw{>+_?6V4JyA% zpY8k42rHf&O=Y_fM8Z}l&oU$}O(H0lKJQ{M!6hKY%kVV^IQQ~)N95+)h-rP0;k?l>IyEV0}KOBSU6eCIp0#M&6@C-A% zUp!H9cwL2V3H{F^DHAjA*PB-s-n;eFHC9zosGU|yCt8xDIGdJ38XwjJOp#scb=Y@! zw_WTzD}BIzuiuOCxF7yulKFVVRCUJ`QEXJze(U}AWH&6Hd-5x&h@1uS>3Lx0r+9 zMTiuI+)ql9uT5LT)(8`TQ*#J>{GX+U6Vn~EHDA&za0Oe^K8Fn z4XlqbTYqCCtL++{fU(&2*1>*M@lSuDIzkc=TEaU~Z?goCJ)i9Ab$M_SJ(&%-+GQz^ zqO*u?pW`!U0XbCdolSm^cf4-AIG-|KE6bPtIFsIZ7w)4)=I!C{y`%6X!un_roWw}b zX|a=9j`87jX3ya&QOP*lBruk9bH7-!AyRB}`}0HtFs&$FgJ#O%`4e-uD#~-E3Z9uE zQfHuqwbBi~hUxmZ1)D5r4BgR?{I{<#Vt1!~605@?Zb6R@TZ{GiT6OYfJ4>WHr>2ZiH72AoHJ=l03FS$?5TAMG+{ykrik_OWk8EzAm0RMVNAl>WL`|A+$SI|M7gg`lxi<}p zpW(~(OzfG|s$fo=&wN%5vklNX*z0Eq_vB3NQ|+Nj(SsyoO5|E&xRRlFe(shi*`~eh zN2hR&O}8PmlWNYw*bxaP_&AT4Ts?LZE zkGoUF{5E(vTTzfAa3HVba>Y#0=SLJMpT2fRe5WBC@$7VRL3>!2x40DaKJ>|_zDk5v zGAUqCzA99b_DaoM)`}>xpGT)BSBO3H^_kRU&cyBKb$QoTcf7WFc&F(>o{u=RB0%0% zd85jS6cA0x##19dM(m8*}{k6M?pvTIrkUk$6d_x3V(WbKN>=SETY<&A7CK1%`s;JfoUk=~F2GJqu9(-n^|^4d{_Nh~W^M zP_Q(SPtv4R4<2t(8aInq9*Voy6tJ$hT1$;DIINb6FY6FC@&{%!=+YV&(0^Vws2wpd z`1+*f>-0o|?}3}E(ZrLL)tROKlM~pykt_7bqtI$GkbjI@K4y@RM`e?cCXykc|Iigy zH(ak&2&guOaARTIZm#lowi&0PwNwHs;@C{SDsv|7z?Wm4 zbuy*Bz~%GCnZPiuCzEF>`SDx7nqkhgFb(Dy6V)#5(6Y^=0E*r{!AG7mAISK}?_sY# zjuxdq?w3=0xfSPd`!l=*M=+5RUTs>{-lLhJ{0U+ry6`F_yhnD7DUzUS(t&4HK>EcC z8eP}tDLT)#aE-jH{5X*OYRJiT3pT{Fj<}{RVwd(er(xtxhQo*DJWJr+m}K-ow@lm@ zCp|UgS+33d#+8xXafDLMW#*rE(=az`f8GMJ@^x1%Iip{M-*Quobn0@W6ozlOJ7L8q z4;`F;Fl(*bdb*_i-u^SDmGSJXnbUH)c~2D5+g7;rC@=b9!%ASYx2fo-Cd!=Zxbp)D zP#Lj1p4X1VwsrS~^&5}{!x?_u9NT!p!>_R%u3dCao86JRv*m#JCHbzlaOd#n`|Mj% z+6O)q4c7C`2X3ui-ZGZ2$6|6G7|7u{w(y#K_1A9QO^u@J`WuO^ zF86x#Gp38G&t`sGmbQ88KtfUXP+_vNX3^ubSJMg>6?H23Ao`Sl;BZ;`Ca z^2P!3=~FEd_oq;AMAPF$bxNoVeSJc1v?j(j*Il3PigfYY&^sMAL0nB@h|d2yA-mgT z`tIHxcfiH#Xtu|XKai4i`G@Gnvyn2&m~3yanmKh{F4*@&9zMOcwW+c&HUwcp(%T!x zM7M(qT?x@mA~)Ek9YQdgw6n(z?GOis{rl>dM~pGfQ|d17o-}lew|5vND+fC=klWv0 zq87g9R|;Azgv6y-8^&ZM2`>1AYfA~dIAo-d3rr|`_L(ayP|s;Z(&vrXyO{XqAWn9> z$9e2-qoPM9aK_6&DBt#cB;z-UG^W@kH?= z!(7tIDyysUH}FNH;QYQvK(Ych8w!dX;>V_Iu1U{qfLdApehAXy0q< zjS1U}(YsoHrgTe&ibF!cnfB77-O>v_AX(e@fbY*dO+4NKIdY%5&96iV4TnVasXGYh zJUMKg^>kcd(GaHO(mIEkqQf*Bs*NBYs-FEh8?-1nY)Fl~VpQ+t=E>EU)XTfs#{W9% zl7t)h>6vIrikYutslQ6ui!fU9?=J?)ME$>aFaB;k0?W};5j*F&TMZa;#&+apJ_I{@ zMqsIFxa5*8w_n_8z+aJ=zEP{ZNp9DGwE3sT(h_ipI=XuzFZLn#tTCD5F}0`%-4_z@ zAS`#F+*7b87ch>~QW3;U#9mWX*X3{H?Wvn}5v+iKB|8uTqPVV8>#dLm=KlN*IdGNG zU$B27=pZ6|mHkH5tWzHNvW^7-|CST6W=QLR zw&2#!R?JC+y{$`YR$Gqj$I`U&$h+cZed6gVLyXRT-(Rh6L}$3N+q2zTWd4fZh-)r% zA*3x}7o@Ci+;gdE3^GeVlD||>FYgx^wQ=^n?w2CpnSAt1fJ1e?SRlytt?5+PLTLD@4m4jIa<)%si8iojX(PjLD#C z*|kKiy>DdmcIo3vX``Yx0JIxeJ6Q|hd$4=+7I$j{Mr{pTe2 z^jP&zJNO+N^;;a%4p=B{i{St!%nl6BCwgcPb*o%pI}E_}|ss?R-(7pR@|A zcRO;8i+=y!THccPcK6v3-@T5qzHbtBtGCo6IYVf#iY-dGH5O|*rtrr zjg9HtZNkaP=$Xu9NJ!VWO(NSFak6pyqy&0ST-a9UNcgV)gk+0-r6_de_G4v*_Nx(Z zNIncJ5#+6n6+ByJ#`>mUatmc;A5zQ_M&uYdZRKOG{M=lF>rcPy#lKWkRP=u)!o7cH z!x8v>Gr(XyS!AfsAxw#yh%HZn>z1xuWAImaH zhpnt$YWg?Op-}&~1 zc1LDxlJJGi5`x!ZA9*#rV-#;vZ{5sO$6~aF4_Nwc8%_V-myA|2dyU|^W&^I%dPhzQ zGjm|!`MOQS@Mkj$`t+GL@}q{jF__u&^VM*aN%BX2E1vahcp<|kn`n&$O=`x+FOC*Q zzoN^rHPu7jOgwsMFFbPn;mYRoH-%Si*hc*obg&b`M5~Z^+Kyt?MaryfbUeb@Bfu=%$wChNo)$~)s(q2kZ zV_}9OAstX*eW>nvJAKq5tKY}+pTv!cLa5fj@@(?LL1G$RuO1ZawwF10lWzq|nk;_=G zzCuYE5ACT+JcfDxGxVMTwo{#{n`W{cgs_@zvXp{5Z6u;3Zr(PBVQ31`@RuXL%Go3b zhC`Sw0wni=;g5(y@Tl%C1gXj7z zXMqi>PNyv8`O6AR7xh+S@exO52Hn<%SiEbiWk9kHU~Bm-@nYc+ZMuH@Rl3qM*{O%| zUVl_gvYgSvnY!v4N@lFp0iEqbgr;I62z%pr2$lYH5o^hQE|F`Q5DD<{hCzGftvf$Y zdgX1^|2Q?$)ZV*+tIS&^Yi_rXfM*Svy=PY{ql^1f=)I6G@5e)tjPcJs5Q$rTR zV^O#3_3n3MFYxH1jQKAVUEhuQUYxqDrS;9mR}>d>_xNEDPi#6@5!VJr+a%2OT4Pi` z_xZXl$A;avOZj~AoyeQ$P+PD8ZM##=nA~VI2>w%YjC}oS|nc;naT`SLK=r#1k zA5Zo^Uv(+L26?hMy;{{_rXD1ZxabQ%dg(VGdT|$|)OPryNF{b2dwo~mJk+Im!lavy z_-O-ip@~>cS7Wo*IVE=(VDg;J{4O1N&d!CSV4l4)%0;tRdWir86N*Q~Rz`M(8?Ukl zTlW_i?PH3%pLBN)n+loej1Mk_m=HgwaIKKM@3tA|Ow9ROcW&FY8c*mU0Anu2QrXxw zZ;O1#%|<}IxN(qGvwn~jbHPhNn#ZcR7r$tqb76iW45q+9m6#D1p9}rFZHnvwp8W+H zqq&ZI=I=1l!eU470ZHn4*9e-@B})9gs@ctNLy-dnHFBWG65WGhxjJnCrnTYNEeauK z$xI<#oF_v%0n=K#UmYj6-F<0zWKpGYT-Y!}a3Ce=paIkZ?GOR`PuLV%Al0;IofMh@CiFc5k5nnyE13}|$Bfer*BKrZXPPjjgMIOV^ zvW2Vb@~`a=+-Jh=-cOeBLyb~vgN}*{S|<->z%NagsFkzEt(d`jXVtOiDwi!|h}F=~ zku~QSCV*+cN)xQuapsTSmChwXzC-b4U7hhYiaQuOeAFOgvyB-4j|-v53bN9s!c{B* zOFDO#ql^nOV+rqt!jl6pXy1tsJ>nb`q7WJ$9yWUYdIGY&HCc_X-`@GTY2tv@L^u(b zMFELYRG}7S7Qbt!@mk66^#K%6`#)fp z3P1@qem`>ReHgQ)WqB`2&qEG>WgYJa(hvse!v@Ax2E%fpw6b04kIl0Qc zwg6h@LaR_em5H#QLVUS-!4C+)RbmR@yr48`KTN44%XvHk8F&v+5j5DKD|Kj0?d^FM{@1l-^@i@(JhVqGO~xr)rrfy5zSWRP1N@?78fGP&-fDf1dE%VmN2?F3awAJZ1T#2Nt|dU_ z!k_L*7<}XxRU@*^(vG+KCFR<*>GIyyO^*wfG{tnwYCuv72>a8k`Cth+Wx4epks2B{ zs(X8F{=`=VTp(cDY(V2WQ+nGo5zZu}H~_va8N*%@*F3JElsR};faYYklXa{$B&4pz z(98^)#A||V{$o9NDDND;XK0j^!Pz)(tN-wSOQ9Cz&1Wy;YD|E(^d9*q^?s9<$YWBD=|ux6Bgi~60){39LwFtVE_p-4P2dNT z2;KxXAADV%=JyQfAwE{d!8iD=G^0>a?w7xkHr3?>u;N+QVXCLE`QTzg!NzmRZ`GI3 zXOe(1lK;J1-~_cK{-t;~^v{)tNr84LhfVP48+q@;vj#Hd$*(9eZgIhG>#Q1Ay3{#t zgxMk0LWOYbn<)(ec*5zivlM6N+oSRvV4&Tqz$7DcC%co#;)$nVnys;N2hV1fh1YLa zvwMoR*!0qa@_s>|#&u~h^N1;jDea&pOOEu)Yvw7G3c#+-_Er^If=lzv3T+sk7qn#K z-d|^RlBRytJ3b=%P(B3pF@U_JBO7AndRiZxBimfQ6~e#~E~~^2l;)Y|;|{@b?$ufr zz->@GCof75WVY7>vWTKHwH!vZdl;Aam^qb-B7brJ|CQ`UJ{5zaqmUzHRSqw>@%D1= zmrinC&j{LSUL(NghWK9=t#Qi_V-j=@!ww7@<~C2w7HGGmeU^gHHr2s!x#Wdkh8M-Y zN|2`mlu7Dy5luA(!pMj81WO$9Rlrz)Cg4;cT8c880a zas2wg0;C#@W+fK%S6)`JYK%ZssB#($P##HW?=xQi>5H||ky-jC!t&&OZ@$}sf9_`8 z2gTcBNa_XiJ}WYVrCy~SLDb!Dgy?GAriQ|-CpEG_3y3V}Dj~-u1am4Q`<`S?%<@T> zP8}8{c~~};^Ddk(RTQl^uhOhoVnzYWf0pSQ#!Qu~CfV@3V!F&E)sz zZvFIPFcjF+ttsq?Dd;K!o4!x;D z5#PQEoo8*g5-puDa4_*5@gQS~PFnpkYaugIUg~K5N5>uEjAZDGkW{|A2E9_cT=n*) zSIt3CYWDvWRi(lKi1jUfwUui;Qz@jBfTcFGT?EFA4!ZQQ_9AHfqWv0V#Pw1!935tN zOA9lef=FTeeBjfc$M`5IH%6>K(4Xa!bn08mt*bwo9Z5Td z*dBnjT^I~x0wF~&O?-pT*r>U*^k2f+gJaX zkTTQw-Lhw{Y0PKG8i9jCOg|RFZAjU95ckMCe%C|9dWL_-W>9OX+|GF1b8b%ls(q}u z`{KvMNH4V=Pl$79em{-L#E+f_#|T{2k%xebmCbDV27bfRCllrAQrcTWbyFF(-6W5; zeDt`rg3tM)jS^tzV$dBkN4d$Et7ETi{0BA(t<}7u?+z6J7C9RR$Hmsd=2=lHmvc)z z18RnoEZj}OLo*(Lt=)b(qxzqd5bA&r{+A1ALW@4i-Z1EKry7I$p6(;aW6YF#OR}W0 z`SM_>is21^M#1WP@8kSgExM zY){q#toCwbZMz-c10dNG$d4)YMo&Ww^bn%z#u$J3Y*#^DSfgOYtNJaf#NysDEA-Ud zdw}_a;+LLZ{dEb7evQ=+9*DeDxYECBN9o-JGYdD`F|1NVXnkk*{qO}>t5Rg@O|GH} zMZV7$Z+$T~Sc}5#!Xtdn)t|IITdFvP4HU8;9zRwt?GJ-s{dmWMd}eaoR7Ur6MklX>Z{aTrEhBU1{BxTdB}xSW^rVzo+f-os#U{EPaI}*$ z#|*rprH=PFr2%c;yz&W2e(ccK!?bet^;ftG+QgvKl1}uN+XVG;Pu@SLsi@LzF$A0S$JLrKhZ?~|IVI;O>K0!F_1&Uu zkt!J1XTihw05GF~hZe3S<!sVTeq9kk$uTjmR?VDq*eX-d$Z~5L#Q=2zhea-SB*M1( z71T$<{-1((MCxYbG+gKY_Ate9W}aH~(2CPYQt=njT0dY-$!(R=t+{NL5 zq|*R^@a4~EkZl?7Q195Jt81OvXF$LURpI(w@CTrhG-m{q$|#c;RGqV^@t``dAS#!&Q3 zP)uqIMp<{x-QDRjKJbXh$U8-|Rd$gq{wsOn&OOF_pAVuE#Jc?4<1gC_ z`l-x14eSDU;1T=mIUm%N>xYd1TPEmynJl`0TD$?aOQkB8l#$H2TWu6vT%&34F!`z( z?~8qBUo5%w8rHA%6DLreX8Mm*Nqc(KsBGLDfEz;c z^Fc(MRqAeFmAoZMFi^rW`x_HJxrAwPfRKoc$GAeHCzoMk4q-}+T)E_9fK+TPLjc^s z${dPrv&^ibH6pc}xegl8*d(6ZubZLnO5KM<_&m_8;(0sqlKc||X@&=r9S}I2S{Pkv zC|cQH;(5ItXg(;&Wj3DbX8LjdL2%MO-gDrB2lgu}&}{V?*kPT4)|uP5xY|W;;w5ZvJQ!>H2Lrqw^XM^aWHqAZ;9=n9jN z`Bf(p5d?pD{F?e0P?OyA9$|sdz&vmk3*XDQE*X*Vu=H^!M|k1jgmr|*24t9dmMbJ@ zzdC)bQ024<9_EzkB%ivtflwH_^#!XKU*_H)bSv89#vQFalh*^1%10Y{-}^hTjkN@! zDLi|(F;anK`1wRyiE3Ycq;eJ*{`E+HSEWbK+0Va|A)2S_M z+RYvl($1~}z>z#q65 z{6xmPN^tdw>1f4R+AXc^bPT7={y)LrhV6alfq z=Vu5u zudK}7LX$JWEf1raO8S*$4u`jvsVrq%MN9!WT?`#v)cAjXRM${WHBKio)egnkx^jgI zN&{nb_)L6n@tel%e(8-+n?~)q=7G`wRLzeohS|5f$ksJ}_#*jJ%L3&kwU+Ak_HF)h zZ(2c`h&>NC_i9O6G^2!$$h%&>*_NQ#KzFBV(Baw`ig7&hocjpbSloWa>pbjB?BEcFXo@b{^xswRk`e}t|JGiya1 zULKr1iW+c)ju=^N`IAq*uyn`B=I?NI8`oI2HoX{Y&agDe?xz|Mj4kbL6$_SCTD;E& zpp&FfY)Weg$PH}kgL3nlfT8@(Z5FttYRefkH3dNigz zp0TSRlGSqlHjQnmJVY~15YQzt9jMi5YC)6_1h#(m)O0!<-`S3LUXPot-(&p~cy-9S zt~>j^3^zd+7tCDVt1>*g8KMbZ_~x8e_JmT-ibv|!tgnaWH!yg*J*)Sx}8|f zm~$g!?7ST{xMg#A_(ItUvlcy4<5j-pBnN!wR8buRoX{CLi%ss?xKpnxV!4d+DnA_Q z0NW$8%h&c;%{Ek{k8wEU&cwC}@1af2K%KFCc{YVs!f2jPg@$+Y7NE0>TS-^nXk6|h z6TJ9@PV=0b0o!)i8+>V5J~0^RFX!o1qr&N(NYoz|)iH@{QF1a%y#)OxzZmU)s!nT$ zdePj6RqTPUFqc~mSJl=*4pjxuK7H!4v~=X+cguf7f8X1Fdg8v0$XRIxN~65|F|ECL zj~TGQiWFdK&8RwOV{A-Ae#5=Pvw&i~Mc_2Onp?@q2sRR^#lIZ7Fw(geQv9M}&aNjJ zL^|?@+U;NjU!LN5*?x73IKZ^Kp5mWgFqnzMR<&x@|Inh!ByKLPEgEVGJ^EZ+XSfX;ruaZ=h}CbBmf_8K*9`CC+(qu~uyvRdVC?bC)>_}H8 ztp^8~u7~BYX*C;?63h0?s5J=JF0jVi`{DTa`I6|Y0ZCjdTnN% zXorOqig7!y5<*~n8smsvm)X9!_#{U=Z=3pLY()lna>Hbjh@z`zG&>hRzyCj& zJpKBA1x@j3Jbw#(8&??dYm+_mM)A%1Ju`udk>bdfo5x?B-mUHZ{=<>82|UZSBt#tw z>OA~!YnB631>y7a^M&_M+wF^t<5ez=Qg7o@YABx4{Ll5FSnRq;_Lx;?16}V* zVBecVkG|=epi^elCacf-9QZ7w@Y^F1h4~jJH0mNqq{1#^_s~f* zu5G7o!_Tftvx@8W6Ip3Zv{6NSt9p9=c2%N?l!AUd&+zQNJrd4_dCrOX=S7AB!N8ns zN5fq4RrojJ;5&&usYFXU^Kav+#qtJWNw4wlOEp;fc3ye8w`^up~K#=L$1>oBCKg%tuj$rsJPuGE)$lK1vY zCZgqTfBTQfk=9{d8D`Z!-KzCB!#)Id?3}S&ZA`R30TOxGO7L>-q3F2#UkbBh`@v?F z)rVg6jIGW!Xl3laNV=}mY7}W4gt8v|9f%!`|BmuJ58nTFPG9eb>ztsO0rN3%=)FGHM^!x*XHjEr*k zI--YWg39Eaoo+=$M6ju6NrUs>{i`tlNWfkAB<1B>+eM=o3!2YfXBme*GBq{j;o{O$ zRJ1UX?j#Rl0Lekw!^6W&tF2yWsxfv<=m0(t&#o!zf8dbs_;1A>#)q`DMfwFQ zpw;1`a){mZqfJ~VTlZ*@CS7>kCtcaYL>;N62SIz?oKv+o-lfCSe z-E3WRpYz-|fAJ{@SSz@4Z=FG))NeBAYCsPYy_Y%qFMv9PSl<1;qc4Bho)oTuwoz@@yUORL1K(Ls6%i>!KN-5y&D3$=UGB$j}x9=l|9wnlq!` zr?eq*q+glY#k5wZ;H3ZrH?^hMGRs(d*%Ci&L!mDcMI$$`* z5uE}{qYbf(r5$WhB7Me~{Sn(KxdwHTI{i8_lj`jdB3;2pHe>2Ec|yy z*%WJXT}ax(J4NySCaE)a9$k+c&j|flrIyFqIQF$8@<+S zY`{Ckymr_HrVli&Z}O$R_#K4xSW?}}p*&OKYRurDFVYY<u+0B)EdFZC*wct(cTHqyjyaph#$)Hp>r(R5`tTHSS>F{MwZkErgM{4bC z!X_f?fR3H>0_z5>F&+?$NzyxXil;Ec2?g$q&z#bkhFL_h*W=6>e+RNlqRY`<4B3*Zx?)y@j2}r}>Va zvlS%Y%Ef4L@)>(Gr`3o9`0K8T{+`6)Y~_-(v3C1)a?Z@sd;ssdyaczJHC<-jFMzye zJWu>AxbX#bQYq>e+{_MU8*4ERx96Ed`b{ zvMHdfyYoKn+Wi~u9e?FayhMQC#nAkx+dpkgcZ)3RUKHjU>LkVHD3fB2VxEy7i$y29 z<{Bk76?Nl}nuJdYpEW(?CS7}4bbnU1u}M+X^(D{j-AR1!*SOZ2P2Ec+(3<^E0GcngJSlkKr{Y^Z5P6)~!2Da%9N?a&MQQM|%qVHd z|34oejRalG@Gy~DFGKHs8)hN?9t-J@3o!{v8?_4B3MkKOz41jen4E>*lX#ZBr1h86 zosEEYCZILyO+R;}W5nL=piZ}co?XHl_I9he_V=!MaM z%2bVe^VkE{qM(T5Qd`~N5f2VK-Ix?6+9$+r(4 z-zFFGpOyyJjsd2QVi{jp?x22N=EAQ5j$gH~nLZ6O0u22_R&o6(fxYUb3~PuPfHuAW z`Vugy$`S}KpD54I$u=I+)rHMwj;(nu`_h*AUZ$c~>E7x)13(D_(pDlGVze1a$ER^U z)&qh`wf5T_Icc_}!ZeH*lcw!=M=0gABbDx0wT$zW2l3Pr`97mJuLx~AN-%rYX|Noz z*p~_Vm+x|;(c%qMT?|1fPY1T39}B-Y-#A!%%1X|?(W_4>6os@pS zN$SvHNS(?oJ5j9^g>GIgL}>?W>nC)M5oO2VIdIX4u5)&4e11hP#IO~^*?n9wz|1pt)F}v1uG-(OlnBo4$NMA|XzcAK zL>vd^WyOk?$xeJ6GC zEDg#@0Z3*sXP83Nk540G#1_A)qmxaj+GUXimCIgak`=~n@>nbf*(r$3fgU-#q6)I! z@dR&iZJPSVMVN>My2LQC<-Ba%2ru$6(L6m#<0zS>^5G5`1FM?1&Z+TLhQ-wX@5j(G zk&Z39<~;FSe5`(Elx6bA8(346&0iU21RU}T*s3Yj54ND_)*sso_Jg|qebK3otopFY z49cyNDbBN&WrZt|t7eA*J1TYp4HkYm61U~fI=$m;O#t2_2V;KOw;t9c>X!QApj6Ft+v_dgi_e=Z9xD=X>{V)`aHA!R)0ff1)uLzUt9Y8I#EC@LSaZ+!}8Dg|z3 zm5Iz37$qd+vp$rAm<02IWmwgPGdfnc8p4vA++vd1#*n4ct z^|!UIn!6B69~e67NdWsU*G!_sg~}#<9H|nf9VH6R>}rUQR$;1|Hia*? zHqIIh-#HC8?5;gD2LHC<^I!DB$@zWte`N|tPs;~v%NF_2U=ia-%}bYNCE8Rh;~DB= zna%%8ybrUX_gQ{dc(Ih5>duyqoEy}>JH$-oKR%Q{zSkEhVD1SIF9@tud7x-kEy<4};Di$h6jg=d&UZv>C z%9((n@3{8d8`myA+JJ|x8#@MAD>ZLvPpw*F8M`{0FQk#uy_(v9s+?MoJiW&^oqbJr z-W^+#rTd7rAn4Em6qVSy(qbid#(%+HsR3-cL?plS9{+yVdhNBH0q*LHi{ieKR*v5* zZ-yQD&C*H@2eL|>SbSBo7&cR6&Sp||U;7;1tmXl5^iTgU3;o#seT(Mpgr|w)46nfk z#I>enTIwGLLPE~+089Sv{XVn0dX4ze<1g}DL+MZ&d%0W*5vNg$Hn8_RS(ezThHNz` zZZ43myHoTvCJSp4Zm_)GyoZz>!kPK8fhL&u4+lTbO`WzU5JcA=;vUT+rG?`MOc!)b zUd?gHN_vJOyPmLLaCwe~r@shnBJit?AK7_=vBLIdAvqwz!0;{Ug9QtelR^>}>rtCo zMT*yZ>I99crC<`vF+T2j;#Fr{fsVS@P_d>Ll2Y@eUyLVOq8}{p&zD9B+t658a~dI??x(0Py}t;6(z`2@OKZ+Q6QX>~Vn$Wpe8_ z@IbZCO4XdK*xPv~3l^(FSGrc53wFb6uFd~w(*AIx+VcS8eoIx$bFSgrn!~MO?r&{k zHJ|;Z%;dHn5Kg;_?VCV8>0! zaZ1np)NdW1PQ7^aNZ5dmn#MKR9_*-G{o~`%Ud`_;XXd`|p#6hbca9*beFlCtp0Rq6 zr`K40KuwALM-JRkgetaVrIYh8gos$RN7~DeDKA{I@n7mD4D=X2T>FWg%8r#e1J^h= zEWE~Oxfch=jE6)W0G@%SxpjNawm2hmj1sC1Ff&IV*Q?oNnM2Z-ErbmW58@s|Rft() zR+alMW1HK2W1I0jp5l%+9%)~~lVJ6CIz4cr2A-)Z{~G^KEcT#}J4$4h*NOLf4wn*+ z*ZUH`Tp4BPWS~u&g#}xOiTNq&76}Hc#XoSV)qpdm#HW??UEgB=hg1?n3k&d zK-tuFyFxke2R{p&N!6T17!xx^?mO@u7zy& z-q*gaeV)g89LKpYIk)1GN0Cr}vubfzP&A7&X}ATK)YQs>?5d}BpBib~v1ty&h+Rxh zGiLNS)_v&|En0u3>B!<%pP*)V49{?^>1ycdQXHHgb9s|k_Eweyq;z_-+ zRM{~dmj9978CYrCquPH_Q!hWf@kC21Ws^+1?i8bN{E(!E(1KhD2xfTw!(_Xczb7h) z$@x@9VMpeaEPpTL-(Ev(AIWoelA(r(vWd!&owC5?D;N8utL&1H^pgH~XGR5MdBn)n z-8j#{{TM?GyQy$n-y&>~wut^6&e8To-JH+N>>KYWGQ;gV+x^Mk9tB_MlX;})LBs4; z?N!E(hym-PeprBkCUWUGwE%utbagc_8JnBzTE#EeVLENfRYo^geh?yZJ$}uEU1B9; zI(Tsqr*hRY?l(6F&2fYm{-Qszd6gsZ++cs*u)tnREsi?$`fW$5@1a7@y&pGD#w$=R zlu`jy-C{6%Qlk_&tX+kJI%9c|+Tap$@w3Nv^NSvhqVbf)H4}^7-m7^}lm%V{>tw!e z=Ez07U0qa$)F62Qxo_-(olapfiJ>MQ)zT zvXw}dv(7TFKUIhwm%Iu#d*q$@i-9OBOhS@&Lu|ajBUbW0SjMI3Ol?GM_rJkH)!v^+ z+14b93J-N0m!$LR-nB&>(y8&xn3Qe%-h|x?<^y*`Zo`@T$1096OEb=7x`$P zQ{WSa@4xX{GDF95yap#5I~T%u+}P#4YI~Vqs0U`oO|3NT(2D)NW^SoE<#QT0QXjb< zwSSV{3|y=N3LVGdHw#-NpH<%|kA06|+~#<^DiYxg6Fv%m5L?ErEyf3*^)MuI4GqJ$ zyX-qnJ{7ji&85+llshS@arBO5K1M+R;I_(>ZEc=Cz){`(e#v#_3^b$Q&HgW{!zOU2 zi$=Bozk-giNCUCg-uK3T-O{c1LjM%N|L=68i=9JCiPv??bkG3ajInbbyMCHR+l#MO zSs{@ZAq%H}{3Rqg5__;!-^X2&J8aDT#5DNp<3vv$isjpU`rt2>=|3=gR#niC#KW8> zW25>vMg``N3Tlu$bu*;EZI(csY z&jK4}_}H;z?RAAVdi2|LpG0UlsutIl`6&T9H>*MzxsWVDWVkKQZtTkS<_8Q4*OBdo zYwAu$V&SjOec^i7R$BdJ2S{MV8o2s$--0Gn#Id$q1=s#KW9BDg-OS*h0E%rXxidR^ zTZ0l=Q~PYrG(r6&+)!uq6p(KWP$k0*1*qdb*)fLx;*o(TWj?zsEweU@hC9&DGu6M} zwWMf`=GVH!Tid}K6MXzPdKSAM1nkv|xgVXh)h;Q=nza^UGdGEx4H@)8NRISb&ccyo=eXj`=O&~yA1oiQEGmJs;nl>f|^yYsYfeeL0Hn z6+0FMShK}`HGJaF5@5W~ioCW~Q>yDIh~jz&4@SKQDq}4hb~9?KL%*#BO=mTX=!XQ|)&r8UzKN>1-8dLp2ep&W(pdyRr z^|j!JR;SUOe+4#hwK1=14y}R&xhb=U~vRctJxtH3cK8bPY z{l-Yk^<`b_Dv}*ub}hF)@g*0pG>l*g@|xxRc$}x#f$146yZnOWYS@otWDo0V z#o`rV5_fW}_^#?`*R1nTE4NB;H*2ky5TP=oRJF?TOc#~DX{&4l&E7(-ui@ba@Z4Q!lkMjqZM zIG7Dm=fW{~4s#UC%Y%z<#}~u4mf|kcdRcZ*1Y?=A=mwK8sZ*^9z!X-t@}y?gTL^(1 z)+5+mlY*Z66pcmOm7U$pt{P`!CbQvn_9r3J4_jo`k2C(r2RP-p1^BIc6AC(gp;l{Q zNczmS-;%ZMzCY_o;H7uMH8alkemMQ*L@mZor!w*=iqD@ie2IvyuP?brf0NUYlEMxYLGcYg1o;M9BFQ))_nwIJLHM%N$DsRl`mQ8bVT)@X z##~E+R96reVOBA}9WuiHRr)nmo^y(44U#iy>zF}r>|GjO;vfRoLhq)OEC@$WvCJC# z%v7WDbiX5e71vYr=hc=>x~5T=ty@Zo4t9~N3mz28YK>VV+12>WsV%5SDjwR@q8hWP ztFaj#qQ|6)?<>FoHPty9RP&L#&ZzYg)(|I9PEF5{Rn0W_?E+&}eBCBrlAwOU!pBvu zB531~1@s>gd{cPgiayG7*f`)Y6?O(v_FXRN(K@%&ziu5XHUW>l441cbiorB^X|*Mt z2hoD#O^!-Et_z@tQ>{ZsUj1G?nP3sANA#Zvmz(TgimMjq*MCX%yuLg#gb7Cxftnr# z22#_?fCf!!wR{PRnR-IrYOBj8d+>UCYP`wO5WB@-&B)Et+nq*rT{p>7(sdAh)Hh(T zYrK$IQ8?*;R79hek>7OxFs#7?N8}w|$>XwmMIo?rVpMe0hIxz6W{D8WL&fchjvCXn zq1qpl1J&Tk#v2>>1$8=Bnrk)nE&X1nygEU1}hlrK&t9T-tRlQVh&&39? z>3FfEl$@N1oOrCBix>z+x=JVAUWg-qG}-dTsGcGk7Sv(Wh0q=%KFL3DEAyhpP6O$+ zzT+AF*p9r-ZO%X83PE58WN3cOq7PeiG#z3DgPGBRR$k&S@qBk$!_)0{LzBM0a}{s4 zqhESXHZDLb_eoD!TfOHxAz@G9i11q`&%wmL#l@?ONL7=@#g2FZw2|E*tsjfGJ7-Cn zT92|?-_pXAD}PNmW`ks7tH`OTsmp9r7$Z97|o$OYt`weZwIQNo-=n{ zIyKWU(N7t$bBF?swgUZl^as6y3#*YOf(C^VNeC_nO-7~3ynQ4g18p>p3wQ+d9^?qG zPS17n<~^dYzAcnqv*0r^^(R@2mrS0o|4#=Jz4iz4`gX}KB5!1mArGxFjqTrkaX!g_ znLl2Q5R2_}WSdz_YOfa%Z{hyp58ruKps{_ou)ltmuNLg}`}`uy^$t;DIZED=B0;>Q zD~C6~xMt?y!_P)h(_o5Ub>8Q2{H|F^~2=eHm?r99`Z48vSb`i2ItY}53`;1rhUqwG`Sw@dX z*+~G`n)EClnVg3I>aO&0gtMam@cgx^Wva=)avjr3vc(UNz15x>YvdDn{;NF{v;afj zg_SKm^x{G2OnexAWhNAzB+`^YD%A4{EUnFA4@Yp1T-`$P#qzg^|ENPB76?6DSXNSE zEShE+2&?AhppQd!+Ib7?HUyx+T3ekK*4A+u8GRDRdz?w3k+no-{NN&vSeE&fyaLba z(j-OGeyKlrp5+OYt5!TMOlrjs|~IfG}NlPB))l$uW_xQqVF>d z>-i}8YTE+^J$U32v$y}~Zj*S&cW(z3onVUZ3&_h3ztLk~ zEEbAiOdr@?HjpWiW=PuH&TP#AZsbW?(XUDtuqXA+Fm~qh86$81@s*L*fI2eJlC#Rn z8+-_F651cRx@=a8+lLf61htww*|6r<@82c+EawTK-%-o|yrc_$gSi`=c!qw|5F2Be zQik!a>iJ1Pyk-aFECegMFrrAY>@0gdxp+?-WN^0sC}n_YaP}N{`ctML3-d#cMg#}z z>^CUnuv}YWjWi|eyf&+7zs0W>2r;?PO~@Tt!^k85sF9LP?c+eLKP1smPc4)_6KzYa zW7oYtE`^dgjdr-L0f$5mPp-!lLG)5mt`h?~$v$~S$q(ZURKXB4gA#XS9meDPs z* z1un)+Zs-GFxUtcPW}c1MZEcTMP_j+cU6ln4s3DGWG)h?EHhoJH(@j(RT^ji~w8(L? zhm8`pzh*DSgjUsoPa!AwHeqbvd!41Ee(mERBJ*r$9!_ts>8E4UTBLn3ebYKmlltGrQ#(J&m$kz3A4`=8o;B(5*$>%1`^9W>S&p zLnA6ybm4I(g#eiw#FWY{q3c zOHhzIIduXPvm%PoJ`=6iIi1c40aLAq*^{+M@FJ^tsOdBe+-w0xT|ml_nCI)(z%rEa zJHe&3xowT=#14BTT(qsp|Newy@Vm4Vmh}1+ z5tT@`W}0<6Ge%ZgAG*8|mDuX@7y$1$+t|nD^$A9>2U`j$a}H{jAp%AR+x@L$iB_(# zt*ESwp@nX>?Y582FaVg9&-KeH`7A~y9&HwJah#nEJ>kG?6a{G6XX7OZnSb(-(^w!S}q54G=qQBue-vb^K|Og;7q@D?$M=fJ>?84bi0DPPdJp!FG@Zs)~tU9;oVF1y5rQ!wgOKjXgYmFKyrQ!6Qis4-N?zJk3~z% za-XU_6%%XPejn2I*(WFby{B$SfJQoef7$5$nrmIv@`FbyU|r37?};?Ie=VWAdYm1u zPWYzlnV3>ih@|h5&n_2=%WqRd#e;pVgcILlX_eFf>AyNcSIvO7d#y+^AJlWM10oJ$ z=O6bUc;~fC@Ug@R2yocK>L$JC<+jQZEu>+loxBXOgdh?wGOet*Gt9_#&wGmS!CUx? z@KYKMcvUT=5fM#$pm!Z%d*U~qoavtd7qNTVog?~Jnhvl6(liHI6!{OrxR&IlEBws* zKv}4&*&iPH@1yof)w|bzb$-MLJ!W8BHr*a#J>&NDai~87Ivw?Q09*9O+GOlGjHllD z(J-A>6U&i+M+1BtY#!b)$D7gxVWV5iyUqR6;*Kh79n$*CO5+~U`?UJ4qpsVs>{hrv zZ`Z5}h!fAb4XN>-Sk5cNL}$2ipbxurO%MEut&-%<`OVVW=UG?(ODEHw;jMVn5_o&OLP^F1aGhZI;;>B3BVwt3uO)bWk7EaN>Us>9Qc)1(JRCk<{7s;QWkrzcF zFuP?cCy3RQ_iyuG&!)VKUdvxiYv64@P{_<&S#`1;DaJez7y3Y8?DEd+K3QX!x4FGG znUb&Y_GW9@XnAyD;UCBz+E`xTdTR}B0^I(K68+B6M^Fo&F*8Bh;w6aReKKrM@i$24 zhe&fiKm7wlK-NKsk?lM3x0;vOx59b(PW8>05R&^(13A5FB%Sq37HW~!yvvK>o?3RY zC`bm?*~F9(4Hy8{Tw;=z`zz%66}!>v=Bx1+>?a){*QC)VQI&Kry4F*VTsI=O_+iHX5q7B`f>3La z7(+%6w_Ps|yH@nQiJP5YWvDSnE}uGFbK*}3+S(Neq4M&eJCH>l^{$!y9ks8xcvV#| zcpgtm)|BWWhN2N?>V?_~K08W|r{`3Seu!+v0RGEgifsv@Ym~>$dnXf!;LnUP4s-}~ zY)zxMT(KCQO1GaMdji`XXzuFf%lAqg&r`)=!fq2J=6E&okd~(*laJiq_*)50db-9I zFw233$qbDywqpvW)}6BZkjrl^YkHEEFyGerbq(iMR#t4z4eL&rqOcjpTCPaxCXJPT zY_q8D+rB$ae>^+&;UhQblA zD+{ERWFKA7H1h`zsqxD1>)_E9Cri6??r)?83V`->Kb_Sp$3M==JsS`KkbQvAuEYS@ za9n6;C7<}AjO2^6U9gVSLb9>nnC!rIl5M+J=Ftr2VLEJRHTkTVrv+8eW|t!cV3Yp2 zX9r+-t10yfvr5i$XS}q=)3TA0?Y#LDw~))~>_xB?L4}hoFTx5^&){g9bv`00zo8I) zndO}0Y`*a2%}hHD5jMVfYZ!-hMwe6)IvxIZBv=o*jaH4=U=d-Dl9YXCpjfx-0?GN! z(aKGU$=)Zq#Tlw#Em!{`FrhWKD5K43ke<0<`}%5k$~ILz3vD+fXi}ylwO95S(clIE zzLOF&<*tttC%C664g--{UB4O`s9!k!@@^K~Gi7n)V#KW|;A8N+pC$ZI)JTEAMRUzl zgOqb+38o>|@^wa6%wV#lpj8Vm`i-SQlb5o)b;q(rzWgh0-;JP*%#_ktx`Z?JBJG>f z+vSZ5b+qIGE&-FW-L|it%yUf{alhZE_R#&DXEttkO0v8|o1WpuI~zTb!Bk%H}IJQDVm$Q+_0CE4l6gNl)g_gvits z*EAkMh8ndhS}WqC?Vk3ERvlIS`c-=(VMHhzwvUTP*9;htt|uEFVU9Kneav zDBhDU)fLF}@z5?KFLtH?sURobALAkUV7YyGgmpcG`TdZCs%fM7_LTgGb||wiOw}F@ zCA+P$O1A>9-}QS>j0`;mzc{Ynn+VbbGsuIBv(w9*jv!igos*Cv8r&DF<6&GBq>@2- zF=RDE9HXU|;8+8uwZLuhc6BvEDTS8slGLmXNy+D-Ob5^S9mxQ=Tnt?pT5h4_OG2bb zf2sIbNw&#<*|K(Kn<->yF1afHGN38hp99H;Fjz*v_Mp{tE$`9F>p3i?YgtPKOG6=A zH$c1PaqOC_N`0h$`0nV1xpTtqYL&l6{7~R(;LDe+Z$>YAQ!|%ulM`hOX%=a6XB>H{2=}GcXknu85lx7=&=;EtUI`_2~nPAzM#J+GLm+|bILj)OHJ(eTE#8~H6#P$X2vJ9nN7-supDL%Y-4W@f}n6J}x zL&r%eEVO6v!RmbJAv7T;4!7`h`X&t^Gm$o0JSd=51UTJDeG z;tOn=YT9H6jrP2Jj^ir>Ceuqe&T8H|wy?~KjHV!|l^?@$^+lFVE_;UzY}1<4h1Ah@ zj;EAdBte5b?2A8=ZKdVJY00;We$&Z+4CQY5i8r`gr#_8+RLt{!n5W2s@ipJXoDLaX zqT_mI)tZ>;>}ROsTLG*03M^^*#A2y&B;Ip=*QA5zOvOiW4q|<nA})-D=LhPF1IIW;r;l2vTk&3hiE-Y_fxG^)`#N;R|y2xjRM zDNPrNk~ZG5E`EzK!B?GW*F*>7pNo!ay44GL^2;Av?4i~Qg@mqqe%Tx941Ui% zs3HXT=~mzBbg$DMHSo)k?J~ow|Hy5!MlbTf@_{)v@#k zqwPZf?z@&Y?S`gUF>1AhOJbwKWW6##%Z>c86-f9pF#I~V_w53w#Lt&Vdv-0@lXkl* zp3?F#S$Vrzf70NC(l&vRg1xRr8_9XMM{N6>zUm=@!M>u$qas$P{qm8YXB}|yB333| zi6!AQOkbaxS2i}}l(p$Q3S!rljJ|(=%g)|D;h?l8Bj&px=l65!;_4hpx?^#ZV(>0g z5BTH-enN5!$~2_Y_0@sr_Y*(iF7asjx^6J*HNK96!~&Pc)YUim(l55AQo01o>J@#s z=XaP_U84BWNpkT-a^_-z&j1IO>(CZz&P@`5Scv;fFYI~f=%jvWvJ-xloO7m=a|Qrs z;#qw)a7N~A5$*ZIbvskk?Af{TP0^3l>2<{z3>00nr&N#in)qU*&vhyp*IOnKu<6Mk zo)zQk^Uv1%0mMz^}XFR1j_)#^n8n z!0pB7i;;`NmvlFQ^ghpzz6^4i`>yyk;uCPCHu=W;^kylo%^uU)-!V{VIgMK`vv}47bPY5`$N_=midEbF8 zb^^^%?w(M-f(=iV)cbdXJ&h*G*{0)bQ@m_9H8UIStv_*m=sqyNUxhuahg`|Xb`(!N z5xm8)P4Lx$%352+TJWlftBNCZRY5Q~YrMzWQslzP{`3A>VeROs&qhk8eJt;3 z-r&fFGo>-U3NpXZr1*w`X-z~Y$R;FTDhpaMG4b*}E1lg|Qo{EQYdH7uP|y6UbmH~> zE3N%KR#)1ax0D?}SIK2t9wz#r?ogr#UK!AQpDIqM zHpb2x@`^Ru@|DK!6r9i2neW;2n6&kRgJer4xZ9$by)CiyAWoGO*jEZY)|7D0YtX~n zK{;T}^tu^k2S=&H-Z_GxzoKf7HVyHmyhMxkggi>)zN8p!m=+2gLDVfrkf5{zl z@k=n&p4i#KAA`T@c?+B3Iru+%nwPHo|DB$u^sohvPZ8gRyoXL_HSVdL^BMFPh^Efk zT70GI>Tk;S4d&@rp0wO|FX8Cyn+==DYsYJen^**rXimFs-a+MQNEP1@%}3P7)CY0l zk zPY@H_GHvsSK$7#+`mzp2cgot77Fj6OoV>R-_q(r6B7j??+OyD1$+~?jB0Z)(_bC~U z*M&0_q!YSMhl)!PK#^UDT1yq2ea(`rK^Bd=lYKDZsk^VH*LlFz(o0-aaYm{_h8ov2 zZPAC?$L9R-VP%=|kvU1#EutGY*!GaHFO6LI!x>@#YGu0ADc#W!EL-5Js{QA*#oWf2 z?=DrzP^jQ(=Ph8B1_Zz8xl1YRI&f~2_PbudXo+)ex?hw#^*&Hn^IF%9F83t6a2B#! zI$|i~X6VfP zQ_Jqq|J4lwiN8N7KI$|a5vFZb1&ejc#HKvP-`xTBBeAk^;hy;MNUWLeo!@|X0Ik*L zn2GGs?W?zMCs2^a>;+v!RFWn7oFk_OZ;el^)1sHsT}wY{4ujzvjvBK_dYqx zKZ5I!5Y`T3&k@D!mrCL3lq2P&>jk&*v*u-=kDWDj2SKw86vn!J*Y*L;Dv)pY{y@GS zSAKb6&LHn@cMx%4Q9%5t80{{vWGWE~l$SxCSiZV*isi_=$GOT*wFQ-(ltbxEY3?*R zGb8Lf$@A!{(dnksqD$ScCy)@te1f63jl%Bi-y?c$x|ov9BqcEp@IS@uIkB=FJW>r^ z=S~q1j&Vmw3l8-5G!j>7wTHed4saOIG9<7>$2|UbS^+XH4~fPScSccWRr>ipk))}=4WIZnA~0IAJ8%3w)} z{Q`e6XG*6txrvfhe>`WWi^Z$HXc=JnZGuDc&y3d8LY%fd@cknWwgi?;xKBf6l|Ij~iHtO#g8RF9L)XI9=Pue|}`fTmV1eS~T zkIy}+UQ>!e8UQdVXOlkSZDxxT=_#rc0qW}0YSlRYGHzSCm0gvoZ9cq2dq3wdrO=n6hWaeb8(pv{6w&1moeNxW<1UY?r4NY|`tTg$XU+Ad1$LSzw*pw$BOr5|B{%2;-yvgjR z6xm-QsbPc+UDi&_t@CZ;iDj#`)swe&;h|Z*bV_y?2A(jV>r1e_6RPS|dv-V+3b`fs zs^CCt^T`aSXx2dge3Q*jg@w|*elLf@>VuUass@`u@f$9kiIxC6Cq^zQZXPxHgWyO^ zS?jF$MafEjH_O&S#z=DnP@B3n_+I*E%z!5aO!Ie(Q0^Mic{Ch1@xB=3`nRv7D!?AW z_3C;`8^`EP4vJ55tNX?rVxr~thiz?i{N5Hv_|g!@eoCS(Z&eh3SAUkpYMlC*pE=?I zKX0!CNIJi)v(0wi^Piw?(J6R1NB#uTaQ0(w7u@Y!>AilUB&?4{+$M7WM=7%Wm&N1n zO2qTT-jqSsF@op3l~;{>=1V2C86c(-Q8czNwI{dT-z!|Eoxi(K; z5<@-=ojeG-d`yCvb~Of-T4>H#E#wucd2g0y5Y^r#MTFB2P`(01#(9O+qHPvtZ$(r~ zy-bzJovm#vo*F7nf2_hIkAR!0x|4%AjwIF^p(0v?RT=PHS*2sA?^;F%>I09!YKUdDYv??`F>tE?eow4OP zKHgBSF(6;#KV)?>MOP;S?zXjcUd(E>OX)j$++EAef7{!L9~1?KTaQ%sLiu^U14ID= zX)9ZRSXu{okd8Obwp8<0Ne~r57Y?}DK}^kYT8+f&&L&>t`da}+u@B`5Tdd#@S8w)2 z0~|4v-cAMbx?^DNd+U^CwH^ad?WU0bQ3n$3eH<}PseB7NrQ!2`=U7bIJz++EiPK`ebGIA+V+LVr>2<|Ky2RQI39(#EZnOq5 zaOu=`_5R=AfSq!CT)my{?UI&TDtz~vrVomRG(XMM%!BJC8bTYOfZYo7`cq7u;C!d^ zqt-jWS3h3$eedVJ8L{+KJ-wUwQIrvBdGAR;?P#BKokXecUxTKTINR5k;>Q@}e`HH= zE9A{LtuE2Ml_T<@)bRjQYC(mfv9?$v{qI|}EY9 z6h6WCiu_oP@r9u3P&B=k_89k=D24BVU12pAx0~FZxQ|BEbLh5~D1D~Pz*04DMt)kF(;o&r*n)CU0oQ*-@VXj{h2qFvRJVNN9__<9f)2o%Tw=eeMC;Ud~Qm%W<#~xJo}M`HSqreP=(*j z&qB^dsqQ(W^O$1Cww_qMHb_bQ+IRG>raAQ_eaHVZj{a$5ekxqU4H(A!me0m(w@D7G z;-Ui|nB44r?$m}c(!O|4Zvd)Ih)R_Tjy^$;73|#}%Prg;# zpBPJ5d-md*TQ^SP5BL3f|2rQtUz`g%uy_hF=r|h}Qg`$eIl=E1xAVP3{-_Y>=-j^@ z2x6HyX8*>e3}*j7aVZUzlq@5eAug_9w`*0!mqA;RF>Ek(zzUg4g>6EDB5kt(W# z`vwjufBCuj$FH&$ON$8eFl|bt2`S27-YIL_MAclim#T)(zTKgmBM-5vf97x z-F8LbvCkLSaeb@ycM%nq!(T0Z{67O#v@mWke!% zafB@#l*{|S29$(7rf?&)%)J9w3E49lvd-dv65=P(M41e&CiXG*7-G1&@ZJ+WCw9?j zpaCBLAkGYWPWt(p=P^w`*Uo`E-CD7wZ4+CZIfVYt% zCW)%$oxa>n++O#hQUxryB9=e&N>$csWtB`(rG{Zn^;j1Vs8M?j1+BFF;y>Roh4`%M zd(0umNGg1!XKio=r~2k-J3{+4R!_nTkMnR^&NPa$j&Pjb7V~6@jB)4dGCJ-_%5AE9 zN5ua(Iufpz+y*}e`6%L_ydwAxL23vldIr3!&N;iQUi%_{;t5Z|58C%7A369Znn#!V zuQO+CZCLXx7HOkue0@e=7;=M@J+!oiF~7zOsiAh_>p!0c-OC{o-$97_1@v!!rwxp_ zI%^GI!qTOY!mD)?37!Z#mkSKsOV6zh4;rj+b@i(aB*U>!<6nPkpV_ooQipU>`;U02 zdM(I5FqGZ(#^@X{S)-FKH9)O@#*-8;4{LiG`@i=m<3Cb(HZ5sF%fR%{eCG5ry(>90 zwsZLp-;;a}=a&9+hntF&gJ{^#81wK}Qdce?!(4mYzUj!=kZIkT`>C_9+!LZi1Vq3( zEx)8ids#$RRcLKLKwDfYRIgnPYs9;umPjftvH=}OZlK%u!Q5Z1xLJLVRLEM{YNy#n z$HAWDmI!c>LiQc5{BAN+9D5d6id0-)eDIEnSH1V~24s{8p9-xIG zwe6n=t?VZ&aYAx5tepNC8i^MH94#gLq#_1MZvN?XV7sgCyl$qlW-jrV^0$A{9x(=_ z5NQhE(c5hc-08K{-%(fD{?n~1ev2V{)SJ}<*wmn3&1!YWn16}Uc|v1VOi*jad%q*n zsM?D5M8dTFaWqcQ@S7ZX-_DW!RLI%*HLE`j7ks(z0xx$+A6^b|HoZ|QRN$zw%%&mV zJc-Ok)o2Tin?%Oy&*F$1EKIx5`c+{~RI=!W`B5dcqFs~II!Kqsg+8(zar%SIzi7q# z+jul^w20PpBJ3_Dg+*<})6t0~=bG}Sf2`Ey*+Fif!#}jq?f8$bC3P>Jr+EdmeTpNw zrY4jQnRQ4nc5|sF4P9psxc%LT)_koR-nV4u)plZCGflpg1O58rZ82-~p-2b-77oYb z6k@(N8y6p+_;1@k)hMDfz42SUoK5@(ZXmo=gL{WKul{d-(@)G+WS9HR$Uhbxu>8J! zc%y3mPuzOrF^i1@NZNl))F<+#;?47p;uGz~bs^NCma7&YF3F!v_#p*7FF< zNW5yF3AW4+BSWXj-WTEnV@p*zd1?nq`#lF%?0>y&TDV{0$Qc|S%%xEyPGs3nu?kRj zkicuJfH=*OcMT=Wyl+&ic_tik0oDG}zX4#c+5j|cW$nNqxpyB5>}d%2eYUjcS^<*t zIco|N_$LLn-$DqTMe9tciix~=b;8T4nCEIVS)$<>B!fu&n4qhyo#1FtlXFuaawYG) z%Dc=PHmYk10P?zAMn1l0pKgJ`<>79pDo!aoH_-T@DKfwlKU&l?HZB3ywDWxaT%!yx z!BXpN(_S2u9orBX!2ex(jkbKSr>uwwYV^h!nGntg5{$J-H;7W-9NLk51lnjs{Z zx4IX-hHiY$5@b5V&aXaL&V-tp45urwGJFh=bHH`J?CK;N(lCL~!b`?I*2NK5!S~J0 z8OU+Q8e3a*@$muIpKyLUm-X@J?Sl!5#2s`V~#(d zH}iFQ-coA$pDCl|*gn)k_(xjH9O7EZ8BHx!^`Fggs+s!{gNCzEoHtYVy;~^Yap#lF zu3g^chOw#RX+kie?vPO*-N43N!qM|v0H3CiB}EkYtq<8@R?Yp+iWOmiJ6CZguZMw>9NeWwK_%_b@0 z`yW|y-z9u()or&UT30;#_3v&HSL|7}}Y1dkRC@wrLs zJv%|vy`y}J4B(vjOU;rAH_7U{|KL$hab+dqwx6+fzqB=f{@I;M+CJouccM2M79*dL zd0NMl{uk?tPIbNn7L%fG4UVrRH8lm}NX1ga8OoW54@IxbehsBTpzN70=K_9+`zl)K zo{2hsnE3rP1jIAdd1V;y0F&9^jG=fO|GO#!@J^bi5m3{yDXi<3EHVl^;i<8we+vAS zaKSzjEhTyN6e>{nPHl)$^e!ceTD-|e>^eI%zmau;pfMEo#QbUr>mb?u!*ex~d$vU@ zqN<1Weh_$+V_7YA>z~%SbzZJ|D!=3L>;l&|8x2+=f%P9gi&%3lR}!2Vb%{sd=e%R0 z8MxiK|IBV-svt z@!kd*p`vnW#_qk39pqi7+-$@$?CGVwNasAm`wyxFpiMX-ono-7r<|cI9anzKQ$tzd z#$}E*egU2XKq?NklYFaz>RqZO@x66wqWth=W_~W|{DJJiL9+cxX1lO{3~cbkxkJ>M zBYH69OW-eo3-g87)N?*YCtMi}=Q{iHFoJr4;h|M&IT_F{$Z|TZl=Ilh6zRyL9QqJ+ zY^{?0@9zRIx{Ht&dmoRh`hJK**3_?$uvh^oLg9A6UCE@*MXWEfW-!EH0;fd_0I;wWm5H0T0K# zcv+TZaGw+BaGUYVyV`#B z!YMUhqy-eJgDj$)+uY=3Q(Hk0dW4tQudW-8Aa$)p@La3=wxF(kn~X9?rnm`fR>%B1 zx(d)`Kz{jQ=G);-ca_~4boIIEj@@bYOb6Lu)QL}WEJtVh3;DR+V4&4!rM;xcgT$S62sHH z_DlM&?!8BqA%rEZpOJyAs<+)B> zD)W-az{Px#B3lPNOoMGkh|s8F*F<}}ypJgl_<2=gBXL6FeeKmA!Im~T-HDSIxWDE< zdpoK}ZN*oP0qH1(<@x6-59!(lH++rbea%3uRvx};K43%=i1lRGY(ujt^ub2`MPLu} z@>FbpSaTrXGg0He8Yi3!k5sSre&k`C;!~zTj|_GxHEdzJj$0xVf56RJuNfW9v{4y+ zd@1AIymg$$oe`d1^BMm4@=OFK%yl-cNMA+gZzr!SI~nQ84l{UFjMppLHUnO@IV8}C zb|958+804bFs8o$$4G~45*YG3{d}(|^l^db45w*at@2FP0yu@ z55r)(V#P;WB{ULsKpRCsa{SP86|IWL@qc=RN`^vi-k;MQs<8qo!4N`$K)icglHL2C z6dLcYI7czC0%kgoetiY{NZd5)sS9=wAyXYeBDeE0W!{FuGOU}nm4?6D;P|EN+oDdw zuDbAwrh#^#gIUI-Q*GpQmIrg_qmdWFD;t`sel_6yqW6>33oxvEs!Hphx0FX7GcQGl zL?1gd7o0^n`KLIH8bBLuxd(r@dF?(IJ9yx^8xV7?m@?sti%S%NXSZ-$5Sv%f{cAq8 z9mNUIZXvo8yhgJ<8e)|0pzo~t-VI+HD3t%gh9hj5q*7T)17RKr^rKnA_U=Vfh@2*V z&7tK(HS_KZh2-~gx*}Agp2xO^yySPdYtHZax4Q^<&97g&j_a+j)JU8|>Uyw}8{CE- z@8BL|J*AfnQDgb#kZ z`QLB<`DFKYl3Y@dHLds8f|Ec+|E6SnQ> zafpMP5BDHo&5N?yo+bAy1dUg zc{_;$l>q@~pfS>H@n-beFxU0lv|JK7{3bxP;_np1$}_oDwgPHK5t^F-F+HoFjz&d(Jsj1y+r)+PUQ}%(T=O+~7q%E;dYC8K( zQT$T9#6QDu=max88IaG#3iQB54g!Q>P~Sqh@WQCkU+cTjI^mK|+sDzgbw<6+IstMx zrK){?R(Zxfv?iP}R9x$&1rZ+;%)$j6SjQyMCVxAyUOh5i2px9gMTR_3z4Ag?WwLKw zW>E0azA?+D_!cZ=0%`4R6IklG9M+yAm54!Fdi$9xJ^codkn=TQkTT@p_ONWT7I_zf zxKuAlRMI+dsWB<}-8Qu$vFg4R*E+Q2OMzJH*-ZH<`_j)+>K>>PlVDfxYaJ@G+mRmQ z@!L}0oICE~f^6U&uJ+ufIwC~^ID8Z4Rx(=|xm(vbD0mCA3cyxWT3-vWf1w>zSZ6DX z4j&fHXN8|`!YAtk8|q@CkuW}NKB?p^1w}mp(6emA#e(0SB~n5!0ncal2oOMUDdJyU zq^Ase7a=b%XQzz;;{bK-n?}Tf-%(=t^=xI-Hp9*yjL;+SZtQ;rXkc(LvYJ+D1VZ%r6$0bP9{6FlyS6I_o7d~nk zWkykE#!*DtID$wQkPeB;AOZqPi*%I|Izs3nU>RYgN`TNoq)7`BN~DV@B|st_0wlB$ zNJs(+0TN11ocX?s^E~JLpL28WzFd;RZ|_z1yVhQ7y?w9#5;e$z|2(+Dj;O|-lG1m- znwfJ{!O=Ux{g#?++@PSQr&yhYr?Q? zR=Yp@pe1CqWYf!+dcz*lV^D|U*rYk^WBPx*yF?E!kIzZ1k=M+_zWGz=x-u zEcN>a(u*9YO+CjaVgi zbqlZfb}emye-J=knCM0NmRG{W`^<_-nMB3xnXH5`N$#hfxc-2Sw-&X2)BK+w`WI2P z6nyd2&3^3Ov#7rB>&!6s_>ZOc-evB48Sn40*1d&F_C#js5*+GAAb z*rlp;nW~^&gXpZCz*C5)Pr$K|7UbFM$0WSv2sW74-brh>N|oD}wCA!+x8p6pmzzv{ zd3bn5CwuUE?oMbxe$dn+H_`7)Y`AMJ0Uc`;-$4nPH8A)idzwDMsr8BPhq)OYV+f_K?yg)oE`QhEz-`x)^o>~sLwkA4e?-JY6 zr&Ke;d7aZiB2UwCCXk2w31Xk0z&|Pi;6F|Le(%$_Hy1CI{=}S2`lowb{vXDH1YT_96WcD5wjbZ;5 zd7S8VNSw$P?Ju03h`IUYxL)B=bZL9fcBpa}06Hgj<7sMacoaWDAOH5R#xD=sm2Vu7 zlg-*39dU;sN4*xmvqZI%ff)w^_YY$M0&NuiJebaM;@*$F)JKVS2lIbnvI;5=Qk5cX z#dlD%ds(21J?g0%SgkzAy72RAg9NZ-W=P=q0FW{oL^BnVgB_IF^TOrC&OX)J)e1j0 zt=8K}pu;|FC9a5tlHpmw(Zl=j4SpXx-I?cH7!*=jnck`0@dHKMKBw0O)2`fn#&&-% zEQqlLDh>5F{h+2jh`FZbc=0zZdjK>VzARB1MvZdH99R)Q*M|G62_LcS1*N7mU4b^$ zjn6eF^|%^M8C@BZ=o*ek%0-D{8j z{N^T>?D}ngb{WwdBl4j^qRmUs*6b&@Pn5NsF$q64DE` z&hglwP6gtd5>|sH)(v$}-m9U~H$SG&Y}$yn-bkKop4hu2s{Z!L$=!{N?kEXtBD*xeSK>ZpeNyDw}I)pzPa{VXzvtJGWwK!SGG!)LS z(t6p54@F%`)BF&%vdb^l>%^|@W*tQr|CK_0*F5Fh*;Ck=A{a{>? ze#b=;?Ml6Bs}t+&**$;V}bAL+_?2zV$j-U zz^3EC(<_N*o6q)v?>@a)oFjB6+K%-&j#TT?oYY#DH2<;wnTyhNiEJD_rGeDk+F{)* z{=GWaS@uQ{wbI(-VSCumVoFhE>*(1>Jw@Ybye zN(?)_&GZh*=W2w{UrELl_I%lxw-=Aj-_p3bfat1O{P}8kh1Msi-uvJegmZ5`E9l&eQ7v73f9>}V z6~4a-jO8NYF8KXowR-&9*5x$=K8-XG4j3MZ8Z8Tdv$Mx$N}D0`s9a|+RSq*av+YgLmN-SL4mrZ zR9n}Yh?Px-`{#6OYe&X(7H_||c6lX(CK9vodQQo=y`@m*UIk+zRL5nbskYsVQ=fgE zbRg|l-dgv7a!4_!q7cZhO&IG*p}P|H8JPHXNO$cBUQbh5BR?N~)|4wKo>5@~3$hp} z+p6*I`^YQ+J1#)Fv2_XDtI=6AqguMf zs>_Tn>t!TNj*Konl-*td_))W_X&2>dkZ3lBO5sdRwMJIymx?)rmLq{*mRND+a|J@` zf`escM$BSYS%xTB0AF5PpvU~Q)J8O+A zA?+N%+{P>hsT?4E{`_%xH6>8jNDLBjcvym+*=XO|-P#`-7h2EQ36kBO*c3Su&Z4~f z*>$HoL27#EZ&POfPBH=YW6VZfCJF1R$qTiM-u!B@;scSuv_r6%-Qrl8n(^%LVIW!D zfU2g|-(uJ~xb;=kbOu{fIOIZTX{2|!TQOhF@H^ec`!T(>Lqta>{P-!F_#9fsIJtRs zB@fulnpw`BOxnrc4QpocL8NI)ROvFeWd>XBAES!15YIBEhLH0rR>LE2eVF2%7~A|o!lrBthT%NVfgw7rjnyW*}T!1vxIe9j1>PQ;4%Mn&lGcU}-~cFWiR5X}+M zeotlZh;zZs^cDlw4D=Ad+bO^T{Jr+=CGvpMl43K;1}Pt<&~sB9+Hf>qq-~_GyshCF zY;R`}*j&sZTMX)JdqW9}X^I!DsHJk}r*dAg=mV5y9J$Ugja9(KD9(7*PVXP)WSlMo z@+yVu*xGD*YY?UBU}sldzXMM8v+B&0END;-- zVwSD8gqhV~(Argd;LF?4g8pB?DW--Y23Q`UM!T*~y)mR4Wigr;#hx8ND{RobW9C4g zp&!zB4B@_*w*8%V=e3_a^t*H8Mu}=aUV>oaz2Ki**pWMv*Q2Ka*~=)Hl(WK2T`Sdx zdC({Qcw5U_wj(c=CFrA;A(}lBl&>w3q$yR1F`CgeQ%K4GY5y~+R8nncYicB|FTCdbUXLRuUcaaH5uwOgw-Xzl= zLsfgWmoM62v`fb}XXp0It=q>7=uMD;wr%)I$ebUUtRKlx=rbMa_2{IO9Jce|qN4Vy z@BPdVCr@amkF5j%wPj-<+Y1i2Uv!-zld!rt>Lpe_&OWLnlQ2-)wmlQc*%dtU4IIXM zYUN`Ut-Ckj$Xys^p>{)~p^ho53x!F_;=&)q7vl(L5}_@rIJ(KC!=&ZZlDW z>|nt+%*-Inq?op5qnS0uxkeddxvI*hhRe0y4mLJx#J4nmT^~{Ka-qi{*0jdhj{PCA zP)kK%GMM1(O%NXqn%4Jgx1DUN)vv%hTs0q*9Jekj4zW92ij{{n(CFyyM7y! zoQ7sPjWz=1tQ;nr^N&}htso}uRmu&Z!Lx89QS`yV$}4!8-d>syb4?`w=vTAM**DVz z_ULqK+q+8HgbRw;nW~nUti^bDUTrVT*>+mwk*}vSLMA3C#GKaRUAeh!=j3O6a@=h2 zuh_@aTD&ej{M2+u(?q5zgBz9%lN*YOHCY;3W{KDtp)5LOYCDnp|hx~@rV|yK0CBmZLRI+s;eO( z`HX{!iDYX(NNtl7trkvrDcz)P938@VQH@!V$3}+AR}>_7OHF?pLtJL(5_M}L_&N2^ zkBXy6Oz?+mzQ3N##>QBTXH8u(D?<4oFws` zWunA-l=9W_iYigWEw*dW#J3LVrsh9poTEBv-~~*BySq*#t=-WC8o9@}i~AZ;yBkhO zy3obFUhQQ`lF581M2~21EO29+_bfK;`G6RsLo*^O9^U3Ur1x#5RPC$4kGodQ?zi?B zq>D}b%NTKWIr#!8vLu?2PxB@0=u*2a(R>HD>L0^i*8fP;ii9PzTKyp~1A|KErH}V9 zhjMKFhw($`#OX0T177d{E!+dX05vrGh zcYv_l0~0A3IFoiQ9tQ>^KxBsx&;-=^C;rcT}z#jX$A zU~AFu#ES{nSdvGMILGMsi(^8BKM{6z!x%$q=djq1lse9Ug;Pf7>`GW}4w<~C8CCx? z&DWu2p)gTKl*r=BcjSqX@YmV#- z9C@}Fr2Fq2i~ zNMG1?t#?2CL_I5CnV2;#kB(Uh|Jq^2e2vTJZ>^dFZQ#OkXF$6gG2G281)U~Ea<^h- z`&@N9tQlo8SQ<3j)>a=})eIY)ahjPl;Ex?$n<|cutf7r*hk~E zI5fv*`J_yi4Q2>)er7k&BZ^Q=Yw{SgvWkqFFE5cU)-D^iI2` zuuF_r^4)1f5NW)3Ii3p4ERIDE#t?!`C0w*`FgW{@K?{E|CH%nk$9Y_*Y-^XaqY0SM zs=2vFaDBH$^BI?a}dH%I9scIB^*J-AfIW&O8I-QnNTUZ}p zjYE29Zay|5wl0MSRZWK_jWwl4e$l-1Xi~jQ(qLt804@o=|3^{{8c2_Xb~=r-Qc#o22?bb{IS6N_pUojW}xz~SnQ_> zdgY|?Ae)JLsmX4HE3%T?FY{D5U)o?j`+6{(dh>h%7WEx`#T0~CPUth&q9X4&q);#E zZH`2FK5>s`Cv^@1vG(~=%`p>%wQfsHx;JRA-Qp7Sd4KV!M!+3F^rlljaIfp#{*oVP zJJRXOa@s*#Du)%vPAr5j0O=?JfFKMuYvDfu5Jjg6}h{suo$fUb5ab{W8{dE-p#GHm9*wE)#7?s7g&hzRmj zi?4mt?`Q?O<1_uGl9n4-7X*wV?eB2QVp8OVhiV_xbR-S!+Xs72JZbPH0k*Un$PsB4F2501Xg z5>N)}vlUa8MP0bBivOXAXnXw|vy1T3;$p^v9iqobN0_*WFzY{RFo5#l)EmQ7Wt=k% z>?JUrN}lF&RjOO+7XU8p`9tOa4_~e(0PeHmJNTxeaA*>#B@@8t`h!hx0tZBfPt>B` zc?2o7&_Cy0Zw46UsVXug>s5id9GY{j&-#a)YL_N00T(TOVl#`rzlx0+NUY5(G`7+0 zpu47c_jP29N0@dmi*=ZFrjNww)5w#RRp|#E6~(eOHO4mMN83k?pgKK~t;m7~aOZ>G z<=Lua%to1!Dx{}pPN`n4brcs;?wFR=iyVr9EfuOlH4A0_8OPnGk|A9809<$jKuhqT zEjOvu80&N<@zxbH-N&bDx59OgQ?LUIu4(0rU30iwVd@ntpvQdZ4owm^Xr~>0Z~W|x zkIVkR{y=qaSgXFSRismE6J@iCWr1EVl*a0p}YtQySC$*dsQd_+~}GW2G)%>1O6gTq&D{ zq)_}j(IJC_O9|$rVTPo!Z%S&tlYrslw5 zy!eY(mNP8-NXtp&Jv}nsQMFNF$2ls}W6&Y3f_!hl%Sss|&US@RP7qhW>RYE`;!9xD zFF7v4v)|bD5gbaE0fYuEU#_0bcBp4(pH@8WKg4T(H0hvFDr72KU)5qV%|N>V^aY%t z(T8;1dS;mMgfbs(-~zDAIS5e3Y@;QGqF6dMp0*uz@B&MAZu6s@Z(iT$JKEGjOgUJO zxTM-Tk`_&f60Pv^zw!?sQ-H~Y7pN$Q?Q97WUFF`T=CVtM%yp8Uotzz7nL*b_tFxit%1uAODK}8mxH9syn z@UqntY%xnYi zQYmLj$JjI(8JaD$pq-2wEZ`Q4%{+~Q=f^!NO`p!T+~d4~AZcw$W0`aRGGP#^Pn~27tLsdE;Crk%SF3LZ`mF9FuQfQ)M7+#JJ-?~;}( zGf6VU1BQ4s8kbeU`67}wy7j4%GZ|;Qo}n_4Z%M(YZChKXOZ&C@8_2r1)YnH#+z&Iy z=IY@6{$VzmH$(n>xu;bruYi?;&w?uL%_8J~a;7cVbz%f$Br@2i53d-jE@lm|3hLZB zLr!7ap1M18IiHHSbP0Oh5xK#@bj9#}K@Jso%Zv$Z}$pIhms za5!db49VqODa_AlnTX&_+L?KIdQPp>+N%W+jv4uXH~R3j-S)VPuz|AWseXY{7x$o% zh8f6+MyCU3SDmxr4zw8I%u}cgo$JE+!De(o6Kh8qms>}2-Y@kpM+Z+9NLA=ln6u%- z3)5Wb-JhDb=#r&Z5>#1#S!6_gy(2rr;Yw?|eX{|R7#(Wz!VOJj)hN~6oC_p+ty6}8 zB^S*zt?(IN0+FdE8W`v`pBnfMyt-HnA- z$LHeLw^JZ}K*!AT!?hRH`8?jK;nlh`XY3}mlyS9*+ifO9m=TEamS*St~E zmfJ$#=XiNNiez1A6T-HMAVVjYAWPRax2D4{C z1%4<(c=I24zR@%G{}spI{+EUF?Hhie@%?r4@=yPAqCdO+>wid*UrBxW=dB~BjQ`zAM82Q_TE76ZxNs969noSNp$H1!rGGy4!2z=)np$KeQ7Fvd-7+Q(#0p6g9+-616?Uo|zmtqPy)&1J zAS(5hg$zah%bvDsTX^)i6&ryjeOj_GW3f81^`ij05n_he6Di|4UdJ_ql<+XF_4eq} zKmXunKVGvM$BbhPEIM@BGl^T*zD!jIEw847gvS2!3A6^%E@MKtfVxSf+5X3tKA_pd z|FQ(%{N?{g0B4On{YRF zP9~UxqhsMz5W>-jw;8wm7D8-t(iP|ou#D{taD%(8r7#9#dkoO&)7$Eu`}HpFUw2$o zwT?{r9rOtG#Wwqd{GcoS*QrmG zd4AjgGXImLaReJZqE*xId+|GuFNjGSM2o_}sK|}OH-chLxl(qz(f4gNQl{2?ByVuP ziG?$HZiF?S`?Dk8u5{z{)vs6}O^q(Gb~nRTe-1VI`{;i3S-=$^OX%mmt%|!7n*4kJ zm;gMpHF`-vdOGV2WYVLsTL?RU<)jB}iDk!ORJqt(o&NYF1TQQD^FCSf4jDB-%!ooo zxqu&i+xoJD+IS}%G+z$P`=$4c8EwI6g)=DEJV<>2a=lQ-+bD5gSgelpwTcx7Wl{pK zjK)l5`T9nynj!<#KOA{BX=r%xcB_J(7dBp?0T8!?moYli@uNNEZQB_NoolaX7{=an zgON_8g+^lymYG|?VDodsq^Vo7RfD0dt|3w`8IaFDy;mAQOJ;q=YDkIKh>qyQ7@%um zRt1=}Lcdu$SRgOdJNDkCyzPlh)C$>{^IsK}%Va*QU=` zHxpvz5V^2p8odA3*JOk+3r|YGhuMQh3dU0u2l3OQ>(!dSTt2zcQq{WKEfTr)jn>)4 zeocy4VKUt2)(wJI)qBeo`ddl|>Tw%I4)N96bb^8wV{NN{LRA?U=@7lg$yD2V$r$KM zqr$5(FRD7S)up3ssSfIZgE$#uQ|FdW*72aBg?u)sRE=?f4Hc18U?W_=LPExR1)>t#jiiXZ&T1 zd!pA|o!Hg5&!UXJ=aPE$RLZN83V%cU3*yb)QFRJeROva`6TBgpKV-kgHaiFhajlaE zKLx%_lj0Ae)-y&0PJh0k-=ufESE(Q30@H~Jo?dGe_Z$-=aQ?8>XFZ+3&25H?iYr#J zsHZ!dNyJ=xgxipsqKImR9BKT(1X)hpxFu^!9{)rd&kwPkNC#U{d$uZO1J~ZRyN75D zI;EW#QpR?~ok?CjHlGwQ!$wTnrx}mPlS!z=ff*%{PSXAZ#POg$bxG>}fd+Z`7NY<@ zEPM}-MCQHmeYokil7DEWOEXi`IO=S~w?yskmSc$_riFv2!*~|hD#M)7#du@}kJ7%muIB&n@(eq0_^r)`W;d$jcyj%h0h z(GMFv?iC=O3KJ5|%am-_-5WFOE1MuLzp6#5edJ5Y$&ejeZTC?wpP1qRx4~6Y^7h8s z)r~EA+1Mkl-SIN4L6RnT8s~h^GNVPPAU!^{GyuF+t}ASU&nX<>KjI%$1d{kz*ED+H z6Yr2#5_49ErHoccmooMlrL+a@d=AD+34p&XWH=>U4=^*FJT?#?C(N*w6x{18EJx|A z^(AoTLL3a<^mq~bN5T48A&|`VP##?^+61%*XHtMeU-k|GXD0J zH_Xq@$X-7tIe+Ua$^rC5Ecn|k5xwvagyT(FngPznq*yVPN^18oZ@HpghmqYXrP~=s zmro&N^mv(Csi6K?>Wm1+SQbzM)x9X}33Pc#Ad06;%sHPjm*2HyahFLkT?w=C7^2!v z;-?{M0ik65A5Mw^q?bCZ3o<1S4O3hK4Gdo1I=$6bX;Z~)B_8;vG(A51Q_MqFiDLg4+Gj6BUJ=AUjdsr)|&1^^qNxUhN ztXA;SJx+}9ogA<1N9{2NM{+StW}lf8tNXa>{@`=3yr!$lFltd^xC{BVkzt69bHrAw z9Xj0PPJ!cO^Fvg)`Gm5*&Yqw}rIBRb^7L58-lueFuYd!yoWpkV=?!<0?1L1*@5U%@ zwzikUZ0p{;HV>*;;QZ<@US3g3B%3QEwM}wYlJ{=rL`R!W0GbYnZKTDe)(%d~AIeeW zX*jj={hA;^LjkIC5GR>)D~?eJ$e`9gwu7D&w7@twM%{-@wOT7z)T{H3|e{j zDB3f6S0E^wIG%Hlsw>3EU@^JP7Nlub0VVS49UC3gAE&W<3(4fq*ikF4^;r``KT|&Z zaH(xhUT$N>L1(XNf0>Hm&wU#<6eiUKz`P>U!OsaV=-}pBiX19$qyQl1vZO zjQPDkA>uF>IE0^`LqEnhc%4-I=IZC?7r|#LXOWa+wB>E9udM|PUS1G_ID_K5jOddg z34b%&NP{fMx9of%F-rIE_?!`>XU!$hXk)&OS45d_9A^RBVLdXj?^dw4KeAQ93Cxi& z*BGSjw`M?G1_9kdea~86rs?HUhI5bqT^>qRKdZa*H}cb4Y2~@j-=`BaZ6X)wqNrsq zYOCf_A+Uorm0qf0K+wPLBRRvx3+->ksk)8KOYx`R#Sv#N&rgvSZ%*p>&p2e3)NsBD zO2piSXQGfQ4xi)w_>41&H}v(Sx-AY_V-gy2F~MEE%-aV^c0sTm(2uzZFE~Y<$D24t zZR6F^Tr$bBO?9uC4?Ny)Rfy%Zq~Y$)_47|B7Hc0(H)Y{wO!2WStSIl}1E%+U2FN)( zK0banuvgr!!SjK=z5SNfMr>1E1xv~Sffoz*aIysiI^Zt|5tv<(2dFZ>tK)WilctV( z<#&eGPK*bv|0Q^75?HIZ|KyI#Q(t@ju#x_$#O4yc&Pbj`3ydc05>1=Ur}3Z zFc#56DxDuEO@nv&t9HA;qO^~OBVPm)B9MJj& z)O9tzHBDbPI9A|xhRqq1h&f8b5V2!_#dKm%vV%S9=P*i<>JFU>o%WB4aGjft8=26P z%^0^?b2xb7{Be!64%z*OpH&Q8j%)1?XbBvW1Dsq4f z*lCX947g~-{op)Gi4z^&93GZ6jhN6l1Q&EZA)R&K&L&xa|+RP zx9Er5{eXiJ<7sfXUu?d{RhWDj&(_ia81!WFpj~q%BaZB5f&3g^qrHru?lgu{|4Ij4 zJx;lEHR3YqN}PX$c~7i+el6^aj0GRuk4f7V3eyim5ssZ@y?)nfk$5JNnpuPSH2iXO zqCw9X?7~=IAQ)R}hw}U+n(XnnOPa4`N#;miAo|qwZ>>Z`Kw(zes$Nd}6xQHd->g=x z&iolp!g1iiLZX(xC&n`4u-MA-?}av255QkaI`$$XyjNEh#tqgNT(2tyI%hqQ6wi;R z7^rP_v{d`ezBw9VFxs;CdQi9i+@F8{w-(@_i-tLy9c(FAuFMB!#g+){t9=sDXQ)gs z)@$oFHQc!7Nh{=;&CAhLo9PeVw2p5hq4Z`M za(elwG0)4GDGLvO(^%byT<_f-tM_4nN|wyP^)qddG#mj-TCh_ z^*&3D`khVZ*WdEOLIJ^TH4)K>7@yuF#q>oXaOV|)kfAfWF-^phQA<|wGAXVzx5rDq zHQylAEzmjQBP9AEWa=jo_E%zw_g0o-Ev%niQ=zlCD)#jD93o}6J4IKmJH>R8Sld?* z+IKKUXW%tTJlp8o3I~rPG>{MT4=gUWjxq(>82kLgGJ@;RX=O*aZ(E%Ne3$y+^Y9w= z?53~nP-kc2M;&ovOwi39r@f*E4Yq~hP(t<|2xwMV`HKi-dTk9f14s0bN7}Ae=tp@@ zYV(K9)Wo#OCW{)6l(!yR>Pa%Sv$I1e*u4Wk*WbqMFffb`^jqo=>RL7-2Ce47#-u8M zq~F6jmE%S!6mHoxw{4@lwMlASnb%fF?Y8whnGE=y?+5?X2`$!78ujn51lxxAmDeNB z$QSgzU=`3FWJ^t7=MDR=wq&?KsgMYg4ajTD*69lFAWcenC2X?&O$bDiFLGOV-W8sX zdYlc36c^LfjlOUsat1DKYH4eWsB`Vj;d{-7jggu9$c0L&11lR%?}2>}S5sVp{5I7q z%;O+FX=W%D?~n;z2&r#M7rBA-A9%u#SHB60=~q%iAIw14dQXq04kc{o21t*q9Z$Z< zKlt-&A9Ha0$%(2Yn}ZQFDu!#jO-mKm4R&YJS@hcoTlwAwevd8v5>WYfkF_E2}9! zb_giQ1Rd^nREIaP{(SfVcC(V=ZDpq~*I3Rrmecf@L!Do{Yg0)%JM^JgXwGu-ohUdX z`25L$^`#yU_wG0$BB{m2z`&r)EEkcW+%Z+Ed9#+H*}=;75(NcE-!y>(w54K#h?}ye zOlC3KebdZm6x|fns+wyvCS+1bS>FoNw*ds*h$u!&Gs%ZUVy8X*j*MTLIY&WwqP250f9$BOIGCz=O3rrSu=!v5ri*_;L9BB>Q z#QR1iJ$vW(fT6g^s4OT*mnDVJyqx&qQ9ZY}N9f2#&jX5Hr7l#5SEcV46)UEF`DO3; zRl7p>9biLmEtQ=kx??MO>}EiIrwcA5Tc7jm4*^mCq~!>|Z3DSef@1knigwIVhWnZs zHTcw#n_WMtJi9%eUe5eHCvJBzwdm`ajyiti8`OD!A!QWpZD@PAXR34ji1XJRK|_Ax zNv%(HPJcu z|8_080kwv3duI2DuplZa!&$eTVqrl{S9T_7GVq4KZS+^cm7T|BT-quvkoz+ouV!4J zTaOsW<97OOz#tNi!Yy?%L`B;`qYvg|^THW;8m67~cYM?)FMvVlF0P~>q{M7Hsy4s* z;quKN-#&lxv$RCI>CyA|FF!inYo+XW%c8V=@qB=Q!_fpog%TUBucAhqu{CchZ&v+Y zbLza-sYAgZd!PP!?6zFyk5U(d@(0}+M9FibW7!FO9HhC ziqEW!n)$Rdx5B)~8w24bPCoJ>xF!rmRR-t2<1VnZ9E*v>eZQ9e7Iaau~I znA?33*pmjc=V}OJv$V**`?o<>G*2Wt8mLb)GFHF2Czy60RzIbz&GkZJrS9C1dvZJnVw%^& zF;nC?l=?R--RZm}QM3M}^UluSBSkcl)OCJ#HRq-iq;DYZj@KIQ+;Zw=j+qVSeMo_< zaZKKfGf28wI-7~ejW-W|kD3?5TN~-4Et1EJUoJ}7745j@Q&g_qVd-3wJ9Jw8oYnGi z)RF5fnewY!Uy!Lib$rTnmNM6S#WeqW2eAsKW34_cITb4oc**H&z?#n0@!ZPMEOU(D!+DWq-npwqH6%&XUgV)qk!4 zPpmU;xQ9MwcM3PzI#rN0+3bS1Ez%;WQ;PbBF@W;}_fwqBhr~tYEt*|S4;PEk{A%AD z&s$dB6b3NYD%jHaUHjLO1c&zt^lRVZI30Y{tV#S;+(d8pIG5@wP1&1&z-}wPuNiJk}{ym?Z># z;&WB0!_KNqwm!vsZTr4%qEx2881`2vzl5R5_tRak;Mib0Q1RDBu-l!bri1W<;#iI9 z=&+p9VUdy5)S7@!O?RG%eIqbd$a5F0?kU|KW>xH>;6d0K&O5=trk zMaASzDUCF;;j9F!@2N5b+nFD4_~G@P_+&eQpMLoC@rbW-p7yf5sT><)?A~#x@qXAz zy2=r6)fl;5R>^tvb^DnFccQ$iMN|jn!tNkhI^!KerVPG( zKgCpRr0M$EShyHpj-|wOnc@2`+T)kI>>4qLBBEbd93I zvZePNoJ6LKd}yv)5#NN?SU0U6c1(|OY|6b}M);p;?sr%^m~q~s7Nmz_5TFCyk5Vbp zDS6!Bj-owO&f(|vr}#A0xpH*R*S@8|Rpp)RmMG_&!r_i^L6{ElffEwx$6JyqYw=GR zU)8nP?_i}rv%MET!QB(oZ|Sd1PCI6=@s&?{ULJ*7-n^SaYQDj~T8OGz*;0o6X3g8J zJpH2QM7lm!w!cV-o-Q#Lw79=zK|XQAy+o#=@005q`jvmX&Xu>q=pXz<$lo850HAFx z_dP?{)J&bNDf=5JjX?C&2v&v(fM0Sgoc2uNk>)>AvSrPsp^lZ+g0vLZW~HJ0)kz?B z)BG;-?#b=A%)fMAG~-61PAr>4Z02y9?-yYRrHk|lo7b_)Vrt{E!$nyA&kjEZDX|?l zucUB$Tcl2G9m2njAVieU`)n?U!+5`xY7|?2tP>GXO}rI5dJ$Qm79$N3=m*sqLCc6lIm{<> z9?mZxmIx5y>cJ#DO*GF6=I;pRMdDBXA^V?-<-vcgyu@`fCg)!VQVQKY9n|zR zYmnPwgNY-%Z6-BYlwq5C1smV^2@A<>O8=0#_@L@%!gd%yh>{v?s@ZP`gGVpk?9XS6 z%;erCN1YaK4eq^T+8Sig`>Vdp4{VCH7gGw25{|=ba=gn+=4H`ol@SGRmrCv*TN)te zwYY9>(&Q$;RMbTVK4Ilq{XG_~i9bP_zG&lwi>cXKi#HCult%it$G)pD*4X(@AS=>4 z-av)7wOa7HNXim@6yY!Kl@B>);5|<(F%R8ao}!d=wP;%Ce?(b3`mVjM3)D%cjS?zP zPVPLb1+ew>{?G`%-lq0GF5ZAP-KqKhfm;07PFL}zL(7WTtw8GiCKt$YDV1+QhP@u| z18$ECTLxFgN}Z9q_K#36EUiQCu1yp+jQ$-x%$^sApVB0>W|TXQafXQD?XnhV+%~ELFu)vn^;!k3wHi z0t6y=o6X9ejNUV=gXK{{IC$jqlJkk4Pyve{SC3hwPnfG$DFbvreEr5CYp&ak z{bKxNmbr9MU+nb))m;#Le-S_9!&NcXXyg2-p1{EwO{lty8wms0Fx4Jcb_QkQD6HCWDfBBqk;DJ>d zNi;O%w#<&by0W-6yB>b9nl8JpDKEEmAd~DhmdL1tpHXsBeRr6)SwGIAo-eITm%h?p zQ#bjwBH&#;_pL7xyK!|n|49{I{LNXu6CnMX374YHIP4K)>dWp~t%#3fYR*?ChEOQb z#l}OUIyU|)Z}{Cg0VQU|AIjx6>Ca&e6M#E~0krT(`PpaS3Q(pTI zQ#_L6(~J;7AZyP-qJ_fT4*BGAnsk3fD)9A#uT3Uq^2jmDl$J5#EJGLOdQwIdb*g0j zSqPRrtF9OP6ihq4+V4lOpUEGVF^SXQrbk;;E9rQ6$CboDwQP;(jWZBKoVlly-=#eY$P_LWGAA z#w;WuWUR#!-4wBsooA10=>$>mNg0=jL`Ahp%R=M2mZD56zRW`)u)-P-@0g;ZuJ(5& zt^iokTV$qNaP_oDK;4$J|9u7tK^ZEay_C0VnR3gY-^p~g<}8cSh&r&Ke5#6;KFg?B zY;&)ieK_%dw+Z{f&442Jz3uSRqPFB*?OQ{l-rn!>D0uL#X$Lq5-Rq)$Y z*PCjAn;VF;~~k?*I; zWj>C;)rM3(X|Di+<^W#znVT6x2)O}i-r=+Dbb)c;M5(#HMRm)MHK8A@9UsN2q58Llugh^}}aP=O6GeJ&A(mKC-vuPZKfl`Aj2bZ+_YgSL^RRsLire=_+*ng1G?f;xIu zF=4*@8;%odNxzX94K--fvoZC;N{ZHqpVqNH(6u-Xjg>XtJ>ue=0)J)V7yYUVSU2 zZCxkxb~4^4w0fbvk_oKychac#h?bs>EVw8t9Qi=vTjiNX$@}JQ3ijt`jz_vdUPYnO zv(~?5h|d)r9}iIMIKdAwE*Wb<`W$4wQtApDKr5rK@z>RFNn(9#i9$Xy#wey)dlPW$ zNN4FZKW#ne{s4bn+nK@d8S@Xid-#mtvL*yA5HZ)EgX%Qw9)`mjuNeb76@pJ!8&KtO zA77Mcyr!SCVrX8-^ues(p4$w5eF}RY;1`t`s<8ULJwc};cjj{c81l@az48eENp4a- z8cur;j(v+_?h9H0TW3p|j&TK6Z^zNStzV0jSqBu|E=%Qzt^WNSncfscRy^a{|JB-a zMm4!?8_@$Q&4yA$EEFlip@C&;G2uKI%oe)}3LKhHeiZl~K2@sSP z657Qilu+_M;HdYUckf+iy?5uwx4xCk?Afzt&u-a!5)8vL+l-HgHGJgN9>kSawOkWR ziucW1jLqfg7BF=zepjj;(;Hb5LZQ*ul#y+99ouQHZIA!bH)yNCjsgnZuj9)|?R3|D zhC#KD>$Pm>hHtu|!B<&UlPgwO`2z2m?x43!lGQ0?L|h4u0i1HS#O5DI^joqqm29^a z=OZ{76o>0%V3r+$3N*&Ayk-96gxuyMORM}^5f<4fOMzU;fadCw+FxR@3 zjecu`cA=2aGc54}!BrTTVl`db7*AJ8y2g%=>ik!~*)_GDpu17a@r4R(O%0H`cy~p> zuIJX>I{!VoX_g$K4Cy3A7{$o)7+D!C5}aG#i3FD+v8giDhB6-;Z|1~UlnjiAQ8MOI z?Mtu@j1VPaWb*w_=_|;|6odKP@o+GXgxMY7iE&H=`cZg5xd59B^rDnn~_{&T&qCZI)oc z-n1FRAs)fa`=5GZ5a??b;2R$brDsY!UOmJBCls1QCd`!(J{MNteS=eG5z|oFojE0S z;0CH~D$W#Nn1~A-mYz`=_c<;pd52bXHWmdlZXh~K8pdG}nKw4!3PyLgJ_EdEDSK{ER6i)KX)t%xQ_a(n za`rj(CkZU=+dj#NZ~gT1mdjU)VrZIajGEkJ6d}9vrtlLaE&(MrGm8MnrLBKxn8p{!02i6bzUF0=2GTm zljfgKD9|t_bs*BL35x^;Rm+uHqT#jE(*eDr&QM6u6HB2|m4&Lq)w{tGMZ3$di-x`j zuFQoAqJu7Tf9nH&t^o~v-y;ypB-tC?R-wH)x-yX6DUm2DmUF?=LDS1}Tu(W0t^Xw= z$D~!%T|2l2j4)1hqwl60B4diM8LWg1F|jKb{c>~Z%^t^}54h3`0Yefe(q{)>3phF` z>3DE;IxbnH;1CTO>W7u6eJl!ducr%`b(zGwHKGT?;&PWHo0kv~_TleBAZD=7JfC#3 zb9bxOvR`U4On5)jLhp75tiYN~w4(dp4%$1&W4qYUgh#nBwz}J0D1%j59Rc>M6+=OCWGt0rO2R?ee6Rg-8>j-o4zf>R9r&mt1h2brcnS2j1SsKT;z#AUc+R{O=b|S z8*0HSfg>xfXH*Ze_d%da=wUND_8ghTb@8Y%Re-Vw&Fpf-&v%yy&V!^n;Hyfm_=7ZU z;Qf8F${!Imkb|!-Sd{p&N}V(`WzsO0h)nL940NARaviVJS`o-9T|hb|*M|%_o7T9D zI#QfUVS>VrH}Ic2_^U&vm3C}qtz|-NYQ4}!Dky#9Ak*N<2O0>DY`H}JYIvRn zRsx=Vs0p5R{vLpTsckl}9`~i8Bqn!9l0NbWE=E5WE@WC2`dg5I(n zJLlY*=TBaG%CWG^A}VIn8QkEt%ft7QBWG1#+dRLCmtSAhuBUkS2Dbi`B1A=6To&d^ zQn5A{wtXxZJz0tp<#t~4>c_>TGj{95S7ba4ZK~ndBMmZT5B1qb0r$@d>G}6PPs`?z zz)C2WT(BM?3Ufvrf}e^|I(`epd=50=Br(-^`pDWj;Uim-%z4opTU9X&YsZWiAfm}Q zjbSIOKtDoy(~i_%SQ=BJf#9VTa$=SXei~pjH^0-f9Z-!$!_TDB_&F}0ZOQ1=(Y%1v z5A-|xF%_-tH#prX9#_JXu>rUHR69|uo^U*|$wl?a)Ol#Yi+jkfSWfDJ50cUM$o=Y* zLm&9FhS&~gBkn*Fa>6`Xf&7xQ+w8LDO&n&0a2^XCSdabDqX2r3cNFQqP;U@w9h@r+ zVJ$fC_22MiRP5t-wEzV|O8kRN6UIalvr-%NyYOyUqN+CPA-78KpAbB7=;o_WW|mx1J$&pq z^SLNmki(mOmns+kRc_y_$W@Me!Y3X4kYoz%+vk4TzjTrp%6cFAIn zZwHyy<-k;IQKh`Wd{@VA>AbJAG6%^=&dQk@?4aYdnfxV>JOgObx_dcXo<70ibt%8p zGHP;;QZr-^9`u>qeZbxl4_xyb`UK4e4M69;sioJ!Cb%6FFLOpnc1Y@xPPENMW=Dqc5Lu|)olpkL;?`s=!*YAJ~&HH3{H2(cr zT2;CPXpj8tYRwx@)S53c{J_Jh!3WdWVEN8jR{&ct& z!kwEQ<;Ssos|CDkNLtZ!j%=2=TgH!gj{p9F)Zp$>{AvYK@bPAyMW}v`>Vo6v$K6Qf zl@^%0cEH0ee>L5dVydOy3F!E3(XxX>6ts7U4&=-GA-Bx+iscJ)NSdnUp)y8>j5p@k z)&BXHvf;i0fz3l^n(Q}R@n@nJ9Y3;eyndNX(df_ge(W<_a(>7AD^tmMz1rA)T;4IE ztNAgxFyq}#xKw&xR!uj&W?nnh%xQc1oKQ%?BUNRRoM_gdNb0wPU9*e4^iwAHa@&5O z?GAVti#DX#g%!DPY9QgppJ_A(f~L8_qLt}+GKh^Hzw5Gu_Q?wqH!bs5%uRO)8y+|{ zfsq3LRGNNr>eg}ff~;dAw6B~}dRSN3E&N-uSpQLTfrAoNV~|-ZlbBFQNvp0m!9)3F zQ=P{8(Z$*s>3J8dnK$>g=AaF@FxX8Lo;0!NwX(&@DcLZB}5{$zxgK1zY^%CI2K#6cD`fXv9rpOgVvsdFAw znE{`NU5opHEC^I{=hS{}^&avgr~ci5`lCOz+IzP=^8}(5zC-Qi?7A{zOWtOOwDUF~ zV28lxh{Kp*0?A^aY&A>a+(KHP+|cV8+N3jvMQxLAL*I9VaH1yHyQ9>b6^(tgWNcW( z7=Nk*kW1IBmhQ@on`hGE(DDZ`iqc#>;$u4cbR(|e=H|@~>S!cpYaQNo{Y`!@WsCsN zqVSn<9lPR9`6@}ihr)S4YzjjBanocE&&2O*<;ZYYi$NvQVPajVWzdRc@x|OS&C|UF z?lmjz5O%=;Lzp3T-`x-Qh{NSr$fxz*&jDtwNKB~A`dsYjLlB7GUG>f_1G^@R9i)Bt zrdpb)fWoBq1ALRU{w=S}v$JYY3<+&o{rz&$c)9-*va^eHiYz~=iY@IVzwm%HCo8Xu z^y;lEOY238HgV*#A{R!``yv|A)rn(N9(I;t5~uA!IvR^DP|K|q;%rI~Wo!0()^PswP*sP$#l*K)s)okw zM!qcl?GA1;aaMs}-mu`)PXxH$fjFrk&{IWl31e0HDyo}@ z{U*wW{Zz4Cy#rTLdIwIAK^*N?n~~koi)3|hYAgh0w);`5@=!c;B_$;(^wC*20&XtR zAobbG)hhUFS034@$fJK^iLCB5Ip|>+IZ5IjS@FBi&W7Y4RDQo>Dpl{7PJf9 z;ENdoXO($=07WaGQ?a8%RruU|5eA70Tv>5$@*&=^^&*YN)m|?lT!n5_v#!uvUBiGxyrul+uym3BIl}(GB&Yuq(m=#ur;~E8m zxzz=`7{$u_84Xu^Z}$pq$*JnKY?uJiI>@^vXDxEsZw;j>+6y(?rZ5`yW6jaAKSkI` zUls!Fxz?ntP9DGV8>eKD6x{wzmRuT}FZ&^{YFP;}z1qTlL_D5}%@$oXor=falZd|8 z&=7dTKd&Gv7sfkX&>}QSf-g%Eg{AXZeu>5rtw-xzy(8p!G}}4;(Qm`Xtze(s-mc9A zqDM&YWUw#Mn7pVDEhSiU6{|=;p>cl40T;)wkBOgpkp9aFs4Ty{pw6WDm1S(RRVYbY z!t6ZFr2{zxJbnL`?EQt&-xSvWVSt!QUL4}S?c&gdM%TcCsV6m(f@n_;8^DsHm3UNKm%lMA1sdUh| zdYQv#Wjg^oHF=vd{ueQn=vX9@(~rb{F%P}~8t|<=br{PjC9V*B`Zg?j-hf_}b;RLc z;qsX&Th^$8A$M3;xM?tC>O{AgKFa_WM{uZ+Wdu1$t5|={I1;SLCmKVU0YciZl=y9G z*G62&t}t=Hdp*7+Z!u3(flLK~P&0lwIjH30=UffZq+iRbX&bEoH=5Y<4-~4Oi)Ov9 z<~&@1`quV*%-{j~d(AMHRY-}PB9qhOK-yq(8r&eDgI2m4kGbh?1x0Tb~*#91hpr(=$lKA(Il2H6oL) z{$qevWfj?n!~_EU&T|h#b@5b}$ha1ecX81Ey-NNMyZmGD|5NGz%^5CV{G#GmWh-x4 zNXIOlcyAGyJx3UDrSvsHX_BUUy0y zssNV)xkSoKOQMAANGD|y5fdjl@+T%uQr6>!foa?dO(Y_*DXubQYSlyX{5EDRxMB4la9ZBqFvrn_=&IQYyXJbJPl3a#% z-=w<*uDLO}){Oa9w@d6>y(bvQ;Vt z&x1=d^mYj+b3e6U`vPT+7ppY+3~yiBXSC zAP1wrI?f(d$&L|Y`$dxS&Wih++PLq$j>+1ZPWkTd(*Uc-A6MZ4nEn^(F?0EdeR4uV zzQ=Uu$)f=1-}lZFD9rA?+K&L<-<(Y^Q#H!!cV@o%i<1DtVW$ZnrU`fEVgX=1jHw({ zFNo_(KBEoC&oD8m#!LC&@7xD^>BtBP2ngI~I0p2Zdk$iq4VY zKq+$s7htnPn=L}S_V5GdBq)He~8tR literal 56540 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%0D|Shb@UJ}1?TY=t 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

=g&ZNuZ+;yj=#tTaeNkThc{Fsn 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

f$xfvd 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 Date: Thu, 18 Jan 2024 18:32:52 +0000 Subject: [PATCH 18/25] docs: screenshot compressed --- .github/screenshot_01.png | Bin 97870 -> 34493 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.github/screenshot_01.png b/.github/screenshot_01.png index bf60694b7327982edf8bb5dc44defa355ff31c64..383a567c2ec76e65282f716d0a2fb02214ce7635 100644 GIT binary patch literal 34493 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 literal 97870 zcmeFYWl&t();0=Zn?!j&+=j?s< ze(zUx>#e%qztdH8tu^Nu&z$3#V~jQDSWS?EoCGQo9ugE36snY@s1g(u95WQus~ZG3 zNDGv9jVt8)hpUPPSjoVN*v1ZI0<;7WgI#O@!~kcY2^5s`f|bG|Y8NreqiJyXnmRr! znv2wO=H~3@f=}7$-L`W!a|{o=9}WKyXV(q29PYN0R|!fxGU#p}u!;SarzlI7FZmg-#tgY;!QIMW zejP1Dm1iH%!>CDcl;z>7uK&d#J1wdsNizrPL>h?`doR>-Dy|7DJvE7%_T2k1Wisdj zHe*xkLtoO_BNP<0KTt$OK}tmAPn#f?qO)UsiuCmqg0e7E=cqO#rI|jbWsTrvd5f8cg?mht#;(Cq7Q0WnpO=o)`Q-b3YEJ4musr&f4@Q`K3$S?Xi_Zde z$GwPaUU20#@$E|RI|5P_wKZetWA&&yI`wQWuN#)2{nNqyk@)@c(m!^+Z&`ol(=jzE{{;23-)>pO;hjIk`y7D~FVm2f z;V}YPF&G$w3;_(zRyM!95Q=(qpFhl|NMs`3OFc4%-{0q~-5aa;n zCnbgS6aP6sD;rtazu>Ly|IGq~4@PGL8%AaZCPphO#=mRWgT)*nApf4w|53wU1rn@` zN&tJ1gPjpT%n@J>Ci^>tvC&`pHV$@{zs)f=Vgy(MtRSNHkWrccF{Ff)tioR!zbG&T zTG{;8f{^_WNifjlKVDi170QB5!Ovdz_OaN{!6GKCQp&{$vAmr_U z5U(__{QInaK^a4!04&UgCdNkW^hQjEZ1ij_?8fxmoE#kVOa^SM+{OmnY(_?GzoCqc zc*H?=RtAve1X>xG0vK(qO@DX%BAiD^L5iQ0g@NfmEee(fU=xS}BnE)i#vmvA|8%JU ztpLhkgI|0yb272Bb91n=vav9+b2GF5M@S7|XAkkkU#QGX46Gc#dwwkp55yb@wFbXD z6$0?P9byfSh#kNH46;)Jfh_q+e@%+`m*!vHvXFo>HUJxl8h`;1P$m{O9wufUW-b+G z4jwjk9#$4wCMF)HzsZA)fhI2hU(&zAhnVkQi!KSYhm7y?yXjvsr3|qB*WJHvErGv- ziJ16zSnwDa{mTS<14n@I?|DM7{?%nHJ^({Cge#FOC3#{_i0FD}Mh=*MI5yUor5%68^7u{g1B4_Zp;!x^Lz z9xNp*2EPaW_6Yu>)6|C9`EbSU4Q2cbO)e0TRWTCd@L^_xv(~^{ixC$)VK*SO7CRlx{c0LkTL%c44S_%5|UOjV?_UjXQea>@M$#h-vxyE zbOoNE+&6JT3nBGK(nf*}|1~LF+@zAtd)HIm$BNF2m(Xmk4U6Zu1J~G(?TZGVaQF-E7s&~y zQ!uC}fPwDq>LHHCbF3p})B*<8%^0eevzsKVYU(4o7$Uz14;-YTbRze1XbMpriGSJM zpXADxH@ZB>*3gKw9Y1|fQ~iUJtsQ+_ZykXFsJDeVQd^vgVB0wOduaKw&a69gY3{3f zuByu+4zKp7&!9(fm-PS-9)h<8R+JLEa@{S2^fxKT1iLfn86Vk5@Rmf_z&HmY1zCh; z<)sk18q5=irQF;1T|Mcn2-Xouw%oCW5$qlK4kMBqx+>I<>k_|cH*l|EiMw&>J{qh5 znI_@O*9^+?UE;om_>#?bNoxpIE2kro095CmkKBA~y@uB~ahIkkRAQ|@cifn2Xho7; z6WD4N=3nJofXHDjl#-{s$Cc1L6D9mW5^%#eu`*R!a1TTaUEu*YnS;Q*?PK$Sa5Ux4 zvk z8}Tv@#VC!BQhI|4^(Dm^IijaBSA}SWqfQuit5PLhNLpO6pI4QT&OesG+I$z%m0d4x z?PjP>Z3%a}F_|Mv=fWvwhDI}oN5Q3>zf!0oKQFl2S$Ie*Vkxwp;mXXVEwGzv_(@}{ zAhL2{^;kP$m+g?m8abAA{9OUn93HcYbYdlN)2sy-+D%!1t?Mh&W7_oR0g@t`#0RpB zO|{qbP$?UfXVwP8OuHUNclZjl`DeY~3idQv3H!mWtQDtv!j2D6d`ywr(IOwubo5>y z>AB%sbaG#QJm1L*Q)#^0F#D0)y(twh78)D+b_0ob71?fwrR+1#>P;zc`)h*cD5qG^ zrE2Rp2CUtZw0$sL21VIl13Y>#*JC4Vso~1!uXH)34)? zBzL9sg>P|^thwlv)ah{6 zxmB#a(_eMnc7v&Yk=eR27#+VYWggo(_)wS1)e{!gYAHun%^eh%lChhC1HFgS(jcT+ ze^akdhp(OPC3>k5gAD~gp|`f2`sz`)MkUE(???lYFxoTwI#$)9LU4vJ0{W5j+*2+6 zq@SJ8oaHjcZ1vu={4!m#M*Kj(?Bv<1r?sla4Ax7YZo9)GJ*T4hM4(!zIT3aO0TdU* zjeP&0h~Xq5DBO{*t{^rdmUPd;h--4L7I{62IkU7%-rjwGzqx}P2jt=P&C9+Fwm83n zRVF(wk+tQMlb=0d0OLTBo8p_gSJ1vo6E79pCBwRKyc!# z`LX~FPt$+D#kirCVLkrVN!7s+hqF6I5bz=Ged9l`swsFZk}spK4C1220Umq-+qCK1QIk9Kr1*@?!fmTl_iXoD>JrrG|iv2 zz-Lk?6k1oA<>F#-B_J~=tRr}M>ZLyk+cIrNZ{e^LZ50m~Zh77nyw7mbg$ZC^Pm}|# zr8OAoaGs?soE}TIg=lI*+r?o2^5)}ryCqE|xjzwJ1g==bJToq8^*oJZ!Vt46!d;_CCEvw zRa>?%LoCSNa>F7}Kerje);ZKs7pfo%pRgiDb~ST0nkAee2+#AFC4W3f8f>p8{NB8S znnZPKp`jP~$2AYLUC1->;!C>WJ#(z-}7~y>K*GPZg zL~-8JmWO}ZmS|SwRm(H8M7zvSv=KBiTDhlzpW%O%O$-vyzog5M*3;a!>9y1wz1?XP z7#;6}82Y7?*_XPtz3+NWmoMruahkyS-~M?Di#LT=IK(~w_>GN$>h?qRI*Igy`D%qk zcG+y(0_L<>-VFAMD>g!eI)m;gAeX=CRV^Zds3CBfGLmUEH`320~a8|f6?mz4V z=4F=-CaK&XanB+YgiYXL?nUvMQa`yj^KJ%wZJ}<(d5)#b@7}abj+cWkL#kf+Jc$q; z($kl2HTX&23EzC7Yt(nsLz+_~ZLaDzvE=xJ^m9>UZ#)5S?Gu$I2O1;Gp>%(0hW^7# zvG`6H-=sIRLS55W8hi!#snxEJ_Xn(9ZpBpcG(41U!V)zSQOZNlaRqdJ!-Et6dEA3H zubXP4#f%x;$Z&Wr1`b(1u467eh7?x6Uf+SioNtg@j(U8FT=$YbhGF}jDHqekw)Upe zLSWJnN4ov1tYwK4eb8vXajE)N<1FkYdY#D@Q-8$^KvrbDE*H}>Gv_pL0SyJp%L!fz zXPly99g@BB4wZr?W^_f~p+$l1&cb=oH2;}iPD;Uqsn6epEp!m}q!D^kue0ti^kc|T`hdPI;H0$h!VxzGfd~h6^ z(^PI}u-Ld4kitkc#$?>{rD@ol{d2=lmT#5PA&UepSS%S%Xfit%eK>IUx_xFDQKE%b zuMeG{Z%YT3r0rbrDU0bA3G0|cTP8LelAXTsRF_ju>LmGVYrav9))m%K+rBas z5KC`+Ifn2)SEBa{sH#k(uXZIqigb}y+&;tlbL=#`#yIgn^dz*YptM zM+*D1V4O`}QvQ(eTwWu8WozIg0%}~_W!AN>d0-}Q7fsnJvz$nIh(o@$AvLB-qZG4S z&V3_iy4U}}9JZx@&EdNdkpo9FiRUP~bx0RC>p1>-Nl~bW{)8(6N@CS9mK-Y^FOvd6 zZ~>Uba_&ojkpozam%gI-=y!==iFa*SQc|^$@u*BfU^-J4HXf^pGZW}a`%2%6yH8+A z{s4|2Nz4~8P)NaaKuaZSG54gHtkGd$EQ|6*;borgHY2Q@F8uB>!`Y%werfK7-+EkQ z^^^16oPA3v(eT&3TLh));p+uVYlf&lEDlte*hzd{T^5!kDSgeJAWIWmZI&zShgUn) z=QZ6t#$OMBd*86(>}`j=?Lm;S)$M&NX%|u#dFnOFeJGwdEOECvhn_Dg6N7>&tWyig zlE|n!ogP8*7h74aq4w%Anhi00$KaJS1?i_jIm6eZ#wml~6*OKaxlZPed65?bszNsUc?`W>ZA=7J9)oQ)qq1qf$+7-1b5lt*N_VMa6B^d#SjI# z{jZ0g*=bN>E9y+mYb+P-B+8JOY?}PAfz9S6&!#iIW<`oPCe?&Kgi~4xO&DhJymDWZ ztQO~*r%=UI9?Wt1`YISoDP1R?b{y@d|DgHAQC+E`ek#Nnv^7D)Lo;ov`bdg+lDiQp zkTd=%E2262YjBgzRi5pm6nnww{O#j9)E^H^(#9jEkIL%UGeuPz(X<~bd>bZ4oWd3Y zCdsAn+0wXYIlobqR7u&kX>r#Q4W=ctyQjTVZ!W8133~eJ&j__A?f9+IL>Jv&_3dGJ z8CiEdwh+gbU&F=RVfy=}b#b25#~`bBY*a?ADc|(`I+^_qJiRK}O)=a;>n94L#8{jF z$A-Qyks3xQg(hwXbSsQRUFuIqtKJ6X!P}GFq7?qi zl99rLnPaAA8I`d_(x}nsWoOR}_v8#)dn?Ls3dzkaEwn2*BS%`(3MGd&6T__91Xx`s zLE^d(C4+${4d&23Rw2WwGIU)>C3&|5*0t+kURt?4Zq>r(=9~$2g;aGJTe)`!Z%{wA zdMi81p=&S1UpLWU;+gWe%udz%;MEz?vf=UYk5_h_vgE$AukkZQZZbEG;;;^+1nGc2 zX?@1@7sHb9JSROU9|_go4MAku-uuCynLjxu*>r)eIn#Sx0E2%Kk+qv^Q~t0y^kt4F z*vsPeE?c9nNAZ|F@iF2GqwR+wR3&+#moMrXdtN6kX-`cZOPM4M;0iZJC8l}mKqb%M=~!OGWvDlqb$v}QY!|6V?>zhZ=HQMVx5v~ zKEOl4@8Vb^c?P#7H(BHlhz*__$+M8$8tV#7NsCxq2P=JtV-K?S?bc9p5>%9dlQWpOLm$e|B8j1;5r+TO4;3_p`=* zQIp%Yj|g!xcHDF3Cjrv*p)(i}I}Z_=?D~8J>uf%DycIeoKo|OH;G<=BLr{W#WVt602LG zX2J_&ThTXsd3C~o88@NhD+#5Sst7{e)$Qq{`=J3&j5SZejDY!-D9~`Lo&A7YDvEm= zkbSb4CJm02-55r`;5mlSgNQ^}d|uBEyQy8;GYFZxmMy;Jx$De>INEnrd-RA|2pPsU z$9KxfQeVAOv#is~jB8exNUjzde@Y=vS#p54Zum5|-R8AK4Gf|uoBttU8Swp&AC&uP zO*3m4+;GBf%}QyCGH50XhoQbV))Ef>;rP5x2y+v4?ne)=ct#IJujc#HP(K#>YU;JLf%-Or!tF*LNK?eVPR;XI*d;#stL#48sxffSNH z0!HJf0s9WAgIl(}(A%5#89keOoHw(Xf7d!#O$L>m-PWdc!MOlRuoIqUQx zg)3j*J#H@UaAjr!pu}K}qF^lelzUOduBo=Aa~M_HO_`SoD%f#H>MPQ(0#1!#P@kY- zYo7=mDA%@3eRlRd4h;oT$0=kb=memLG*qnM37hzYllpObY$+{}=E8^|7);jkaE9=| z6y&~W>o%H*3wSZmHCoy#%I?d)<^_L-N(~;a9*mXSX)(Oe)>JO9wHm};9MuCFJ3J5U zQ~WM0ie4tkl>uCr)En*udM++yy4DX#l=~4$eAzLBKl{U9Z=~J1xh#=Gy@pao3wqZk z6YlA4k@u^trv+>83g`A55ATxT#7w#=4?owqoD|HbiMb!_&Jeku`y2hQFI$1*>{gpe zoc=}4rR=__|JiL2GkFf1+9uOy4gT;yDx}@p`LQ4VSu_>3_>hLvN#i3P5ik0W9$P6X z%3fG!J>C>X&!6e_gs3gWlR_Bh_!0asRF@Qu(pmqM?$@dEYkdg!3g@M`CY$Pu8_3oP zKY9pHNU*fJM$A>SuyMHlgJr?pgttMQ`Fy-NX^b&7yd01!Gv6}{Vg#t4`z44qo_Je~ znOJXju;&dUN&Kz+OXB;jhg@^+p||m6>)aC$DcY%;Qa3^gqx-wJ(AM)K+hjWraMp;M znrPdHozwQ`Zc0*H%O|^r^mh6uKlB5i$LC8twDABb&M=Y**0j-+l>SjbzIglQ+cQie z1_JSsf{1f-SxRx`PV~RmoJlOFH^`V@S$ibFKNQVBb*QY&X=pV+ZHnJG+`cXdDZs1F z3iP4Y+(oSxsi%5mQ_zf+Ggs4$*?5Qk&X4T#jH%t3nQgV_X3xgv+IZLrbaKdZxxX!I zWMt&Tp6y3@;ZDVr&HM8h&Kc=j-R|etSYCqO22Q+ZmlwUfM7@Fz^a71u7jwxICVu~{ zWzQA&@G@Hy5CpM!$oim#_VU%8xlxFjG2x(4O_}2d-&~Hf+TXR#O;8DVo;dNXdjuX| z8AnFoDdecVm%O_WGAX0!5VEDO@VX@(PfmscX$>!=K-p>Dc5l-4v%DHJxWbR;Bk~F9 z`1meUt%N(R&3`f9L+gD4YYz8Fsjq>Ie%F?3hBih9t2lqXuW9cehUK@-@x#eoO>~2& z7jA310&Uc9B^-DJhb4(ko@K#`FnjaYHy$mGWk_)*Y>W)9$wC63nH22l_=^r9PjhL< zT8)*)Klla;Z7AQi>ynrT|J|hmrOU;zZyZ&YK4BC682)+r_2JqXj6N%&Qu*N1oWue^ zs$af`h9`Kq*c+^8n;Rv`WK4UD#RM3@%$G^}F{8sC)pY`uxzG|e?hBv9XSOmPKACMlIfzSP=HoslRszmvMOoL7Bl6XlwS# z>3rh5MMDtLJ+8D-o0L~1QGEBTXGKft{h-SpM_T6DJr3J*o!715d7~$oYou|}#mXaV z+bQmNXK`5qhpTP$@k*@l+Jk5+R(JvGzQx%^K2`fIEQ)Rv`oPlfE`u$T5D z@+>?x)BfcA<20V5tu+r1kZ`Suit??Oo;dyvMS#74qN1YR4J76Lxs>CdVq;@9rgz*( zAV$=HR`uR0&((%%E(eWQ8cCRpq)JIjB7}#BL%1G4@ZsWVf+7#|+{tXfdaceC=kTrL zW>XA6HI)v&Q5TMXn$fhe3EK$YO*a%4B;uPoSU9G39C-uZ^YN=kssfjPrStj5!ZDJB zeDRc+b0*i}d${QOXLzU_7k=dYvWlk<7WdVqh%eYN`6Ga16@Ko0>Z3aYbbZT7c4})P z`RY<2I)^7i1wVhl?PG@N&x=je8{DfSv#$d;mjmjMgtY69MW;b*TK1=55EIKJRaM^= z6&1BS-7M~bXKMM=m#yi1TL%-T({L_#hP%ecqli53ysxgnC2dA*l#%LSjAa;cD8c$* zQCGVTonDuMv!3_Wnyu}Hj!z-+N}AxWTU-7~QHhZ~&?6^_%nF*|XB&YuTcQj%u+iAJ za~(lXocfkKTz<0BQ#Lc1gP*f=?9bS}qPp-?+8CmjP%#fcu9j#0T>?J?SuSiAiy1m@ zBftMl*Nj|8R~Hcp$#AMrvE}*aA>Y&0q?*U+k>h4n6^oRN!7Pi3DrK)UW2-2{DtUqX zT-m5%j??w%5EIE-7fuN~+Wk$<-e_{^BMg&s#kva3&Kq{=#QS?KxWzG`jX-!fQlWXn zadJU9`45{&*g@9hcO!luK79D0N&A=okciS&x!qc~J>6(yG3pJ4Ft+_z1hso~Jf$pH z)8&S0YWIF0@M{e~!R`EceVBq-6n6Uj1D&)1bZ)VZWhC!v3m)Erw+81~0!WnrFa zXc!k4cX+6l?2L^Q@3ziR?9}u=gc@$I`<#;@b$cE7*Hzq3idV-;e4(MnB|^h zoF6VnUAAZT7Ob8?x*uh}c#|Q%lr8TRCUu8-cyQNUU-r%pd}G1?Pn?j5F4lNHx%#6K zxQ;(1PyV(|x%Yc;6i!rB6cr|tTsq%;?hl-+l0^2w@2eTjDFM4rApsV3DOX9Bt7-7x zv$jX8IxkrXJ%7^9%!CqCORfG0IJK<3ccyr&aemBzCGbLJF)Ls`uU3z-aw zSVzMkwzzYtH`d(ZyeKmhp5$UeODzZr2&;s;6Ko0uaKgec?e$ETy0{7=pSYI^!GCSe z@wYZIsk+He!a>PY69Mip#J8X4Oz$ft)%vgAgzZEXU25`P52-;O3M*Dj66A2a_9sL6(C1;bo{bHD3Y-LDXB`aoITN}2{6)_D>Ff1|_lf_J_ zi9Gby@8>QaO~{1Qu(`W+O<*CFoGFD(OT#@PI_5jS5P=vUiIDeCGOCP(kPjv{cD+Xa zAHEXfD`U<&rPEp3n4Wkrh=|m~5y)crGX3V?bn|dVzn-S|0uLu;wC!5@|B~@r|6HVin{UN8KrY)RsQ}=5IxNNKW^gwv3c<|i~IA1@n5=S z(SN?b{?qNURiot~yW(%-Q;~Y^TkA_(r(RRl$g_jn>X1F@5c|mS9$i22zvp$4t8Vz` zZvoW*0E#tt^9h?~9%I>$IEIbl!^u1ZF`z?7OUlv@jkLKl+d!SN=h!wM`w3@(`!#FA z??P{LP;G2Z2R31ZaYKdQiXe>t%%~)SS$ZFs16Nx@8986<)rE7K zF5@t2pr=JrlsJ0Nw?q~?+Zy7+&E7sfsvWJh9a?fQq1H*};l||c;~wq$GnlcR(O+!M z>joygGO}Xw^1d3;WqIbpB2-_W!lKFr0 z^=5OE&)2j`Jv!aFN@_Q#}yi&>n4h!=<#2-b}2i#Z4n-UsFG4 z^kQ?9Vv7E5khpU7r1{qus1{@v=f)-C`)Rm~5^BDjm0Ww@UGBw_E5zJVeRE|Ex#QFC zUUwQTP4`Nw&c3C~88Hc1pi66yE~U}&?nUCwS}$Y}K+;RgYx5~$nr{kbovvU2+a3-0q^G@_vZ#?^su zHXs?;fcb_Em~>s$1=97oO^tNsu0~*Nv~uaI;`htDbuQJ;JWVHfdZ}qY!ZW|BR*hHF#oy^n$pa<3 zYx6Ac29%&oq_Jx-eNaoM;_?!i?jw`9j0%XsY@k5eTf)t#*6-#_q-sXGklsJ)+gz9H zCePpk!S^0|1hCGurVhiut}2;|tAxo(EON92ugWVs*{HnleQ}u=s1nM$+1c!9MP%8k zBE4bsq*53w0ih7NH0aD+4(e_3SrL>{kJiQB$MdNPo|+NfmV5?Sl2VTt$lTsSeV?9> zfQNba;Slwgqtx-Wnvn&BaCT_H+aV)KG6?v^a0CK<;ODn_IJhOH9xGqTNqW`>J(>p( zbb>El7qH#2Hu{fc`B>$Eba;pIZ#Qq(b!sDg^oMqhlgYA_cP7hBePtg~$89B1(B|LI zJzOi}Kdw`6yg0PW65!DEaFG%wh3WShqE+1Er;P;cNyKMQPvqVO(&mJlu)TcxNh6&! zvA-~KiqvL7BD8K}x#|9P=HyjIWoY6|jT}nE7-x`+BT3Y(=mv)K$sYRX5Atq4CM;E3 zzWzfsJ1umf_Ue(eI>}?s)KdYs_;FA2(uW=GI&4?w^SFC==KlVZ^|?chnX_|b^10Ed zq1dhug!G+xhfL`nrqo_T77w$P+z}(6eVv%KXUYK)ACiAdW!$I3H9NXwG%%TE7(zlwM?@l(Ml!dZo}TvAO4YBCERp#`1pX`ccGI zwS2sAIVXkSbwIKW{<06Fg)1LXim+nsTgZP*=v{;Ko1bLh{d0T}bwA&7TUc9s zGHqr}H4G__l)tFDYwzZNRj>i89X znigxKN2XzdA@k96DXE{N6EF9?fRQNhAy6Bir`KIPBZaJgl`mt8%YzIRZ~P7gF^g{B z9<5woQgPwagxWBr`s~>fFhrb>H|Y7K?|#1rc1WWpJi_P_3M9T_@3Y)hIa|q)Tdlv` zJ{))dc)o#@?&=A>Nx|1WV9aVDQf?~z-a7kW;nv!<(WKII+w1;fPO*(QHozNA$KLAO zZ|zb*`BWu8L%7$`eHwQAQ|t@ABeB^>qNYJMl?|3X&R4zRJ_2cYj9z5DngT?ug)ZBy z?vY8A>;|wI1*N?H(?_hr7u!mZG7;W7dShUrZ4?(*9(wx+Z|m2qp#d0)<1uI3m=~9H z4=#P~;@yQGhvMKAikPwcs-7{W-TxB|5rOs+>-fokFNDfNMwH{O7zd*R8jpZoYy-pG+Oz>@PXfYms~z0`+{!gSpC9$RiP%! z<1V?~xqT``vz7qop@DO;$)EtHmDIVYnGvV><948yS1RDVPt&NXWuGuFZ0$Pzc&_n* z_!Nf@^=-v1ftU)AFJ=Mir~^C1tp$`a}Rm>i_r8tUfxW# zofw@pxyTuG2VOE;uM;CR*?{4m3yVa3eLe8#?(0XKldM-Nh125EXh#6oqAbUZ1b1Z3 zZ-Uj1P0@Xw;%2<5TWx@%ini}DX z&#%1uNYOsd6JVgNoSvezqkW(uMge1vJ-E2*k*dq29-Dm~@!^w~Z&HtyTXFkL?1)9O zqTXr*uAG7k4f0$25#FcIQCUb!1U6RUuCjZ@6x?lV(ekF$_YN*Ddc63v!5?lYIgdWt zT&KuJEQ+rke9kP!o02mRMyuGVn8r@GD{i{j$e}zq-Q2ikvR=VU)$QoO50yVPg{B&O zCiKv=KK8^e;jKBv3S@qmP4=HhXbFJNlpgG$C5F?OKjM-mIKG%3UkGTE?!NmR{F;so zaOe-0(Rkk~b=CT$sESwEt0X6%S7CBWvzRY_=)NouzLcRD<$jfMW3EtS3m%I)s|e^9 z)HJz1t@6c?4<}8QTQUC_HqiflJ>!J9WaUS8NZ#drhPu_@?bo?R%ivlWm+5(@g+^4# z0TQzsg$6f=V~!Z^_z4+)g65m1y~fF8S)|*7pWWhmp|yPSqYWb&HCsAOP9cUo7S6A8 zmOCL)KC;7c$KdJe#-b}{wLL8nfBxi>Pe;QJWiGL_V3Tu~XziIO_Z(3{L*H(DPz!BL z!0>^OI1v%wBrWb1VDTHE=%vB;at4O;twvRn-|7k34c{nKPn@_yk&RHG^rp9$%6V*t zf2+!vcF8MBRynS35B#vB?xu~_4vFT*)2sNlW#I2$a49LBuM>m!>+$BamE(!-B;Lsf zSU8*dMyayT=FFa9oxyb7Ek-s7IGm zK;%kt$r+1!R~`0fq+e^SfgBZu6Q&y(lfM0Zpj9QCd~fDGSFM}CB6fLxVCq7E(d~C9 z#P@mR+s(Xlb!Mczs~I8&4k*l=v#rPDwsRPF4g&_ORVnjrW~9wnFNdi>CmLDlAP-ag z_p7*l=a(`P2UyHWaw|M&?krKqArw`|t+q#Z=G%b%?_6YJ867Q80rX@n!Vl--?FS2spCMuRvnafpv}|4D zpxtWWhp+7W`_Z6YP*ugz3lRo~)dvEui&;ks%U?a`sCeRKLGqB@Qm<>;#%vld-NVt6 z#0-GYZ!B4-|9*QvKXdQZWeL0)KGmc)p0uv+t9{dS^MvTf|MnC|5%PFIARVz3!t&t+Cx!#v23 z5ehleCVMWBKaLmC3b`Q0gI8Mp1i^Bmcdm0+%|~&kXoLnRr_RZ!yiU_w4<;uoG2N%G zi}{@_s>Zg9`NJ}Etv>zD^|p)UT`CS}J3w zm6pD!%j+Y(=CCXTJ9UBrKq#UH*8yPW!ab#AtP1lN1#ozyA z@Fd}h=JYg~6p$nMx`%@o9)dxY*0PU-C)YP-lbU+K!^EKGWTO!Jy%8pLf*d*%(H*D0 zm_C)SRAT?gT-iN0eCFqYnO>_~mhG>%Vt1*da)6gKg40l=K~iZhxAv6~qcIKlt0 zrPV;Vh~0;u1UU0yoJV0UXHpctA_w{U8d8f^kC(Gw+n9*H+(6G_=(eT+fPl3lIf?f? zrlr63o91%@lW{J$IxR3c+6B;|sEUzo_NOkl=-Fa;-*|ENBiFjeNTyN3m@5mueT8v3-Zv3Iv9vByN8pH=gc1Cf}`_$#Ju5T3lf{)$Cd_P#HE4- ze7thsP4j?I2(Kp3MPXf?qiC1p9{%$)}rM zQ?|q7&Yf=4&Z8o@0mRIkTH#IQ$k}a_QwZY7fD2Or1^5sF@DJLvwWlJMu*^b;o;Fo}p!kE1Pw)6kRNhr1 z_uJ(R+OagcYG6jTZ~JVyxXux^i+^5M*1d zrB6U_@$@OYfHJCO{UO93_@lq`3tR%l5Q@6$TX|O<_hZngV5CoszH@aab5v;M+gO^# zLDr9FR_}3crcihshDfZsjE*_&FG$<_#X1jOZiXH{O5LsV zPH3~?yAdE`u6NzfE-EAP%ftImv~q%aJtWkY+V3wKqquS(yQ4u$6<;2!_}t`LIlf%I zkjKns|2naK5i!+k-BlAFz&rh}P;=UY%xdz@wHLX?YxKAgCC@=saArcC# zs`|A{l#3ZQ(f+|i<_~9USDoQ|haIaqY}(@Q!R*b`D$2ma#$EK@#bwpUHhanpoUqUv zgte8Er~3$rSMmirx^+F4jIb6Ltxa5UUPPyT$F1;HBPw#%BZ!VBfa;dc8@S^|ce13V zX$*M>-jOR#{GwYI_abjkEzZs=X^Yi6Cx;?&)51gJbFhC^sz0hnkUx?QxL3a@Gj42QDBFY68T zK>1`vKPusg(}(_@|4i0(g`H@YUEYq3d$R31W;#SskSR=&l`|7+z|N>&YG!lxe873} zXy`{^KwFWo3;p|WkMQk|2J(nCG13sidx;z}CYd~V=k101`(X2?ZWPm#7KyvFYRRQj z=M={3#T(Yeq$u>)yXxy3%T?=hyBZ9k4KHX5C1{mfi%((U;V)cQ8oW>kF7(Yr{E%GCI zS|So&26BtFf;d`;^dB0vGdh31jYhsWe7*=jcUwX<9K{ponQtcZpL}Qbiub&*YWwF( zY1pW$hZ;^Xw^?cNlSOIAw7P0Mc4*&9hcIQ*GZb>sZft9Gz^&U@~Q>X zW23aK7kySY`JcuMOlQKG<~36LS6N`lmKq`9NNY@NNAYMVx5;gm{WIG#)bt)rTCnYnU z?aBn<+mj_h(3RM&0AaHqc00H43V*crsbc-|?k>w%&8_J{?edQ$Anx_<@fH+EIi(@w z9|U;f#kiY27bS`;UZ3D$IFGju(#ThVm07lr-E4FNTd?UF+Jl7$FJBHzLYJ_%yPIH< ze)g$M3g#z=Qk3sr_AaBLv@QNv)K`m=dKY7wbZ;%!b*S7!4O>*Sv(O&<2kgtT!muY$ z(307%2e<(v=8_IAkIml*kCnD7L=!%l$GLfMVAKx3eLxnQ%b3mfL^*Wp5w3q4gfH86 z1=8`ec;!+HqDn`j084BnCa4P2^bCatIAz1o5#Z{$TZL{Z!N5$bK1!(Dba3{OwKTB` zrHlrr;fmfIkD`*mT5?+$=(>HQ5NLE|eP#%^d#mGjAD5~=EK67(IeQ02t?cOu-Nzd` z+h9vCPdYHm48XD%;yE0#-#pq}_d5go{9gEM!beQgCVQoGl}Q6TXAjcrmfr#qNx!*4a z2Ge=EMGZ%u(_ZP4$+EccsPI3HKIrBNhfF}D08m$daH`@IiiZgUmIZE#c@z6{>Et84 zx)J!0y>@MeLk_IHwTi1Y%fhQ6n+;O}Ss|!oYvxp*rlxh)G&|A-Ye>PMG_Q2(Ip=6v z3zfoXoLqkQQh*aWZ1`%guo1ew!-tyZ+c(W>)KpEuF4H9s=f<6HE?qkuy7@-cgE$!c zfgP~gjl0Owqsume^2PE;I}&)|d{ZcdUD*LYmG2e>6B|F7+H=+658`dF((TCQSG(?r zbbG0ZU6H*sZ&V1`x{!wk;`|_9W;hwD$<>tk!nt!q;O?N7lRuG%A^-YB3b=&THo!TD zA5Py_CBEHH&-V}(Eq1%qyX!vM@Whr-9x zI3uvM9}PAW27Q-4)KW%tsg^>naS?9~(jRTw~gV?6myei=Tak@3_=_?BkuG6TQUSh{|BQ6&XI6BoNN1 zbB%@45-alXo!KFl#E}|^{*}c>HG_7*0^^q?c2Kih(;e%Qx__`P>ixCV2x752@2tN= z-LHdf(bOK&CvNBlo(JZ6c`4Vrla{dKdU*43Q_Q&*6G^*r^wp30gsCb{(s2&%BZ-Su1g3OoIr301b250!QI`R#@!`AaCdiicXw;tT^o0I zhUa-dnOXDeuBy7Hs&226ea_z1MYfmj^y_mG8|n?*%82PNw}%iGA%?>wf6gWZu0t%w z+Q+UQ$~`P3PBc=L4V&nAvRQ9;JPz-^PC{)@oC6V${4ADX8{=0NzIju* z;p_9*E|w+dpKSFyRdxXz%UuV_m`fmiEGoEMdc?{6wB6~JDEk+osEgiYRR@DN@s32p zn6NKdvyj`he%D&_w`c<7=Gj7hN2)yd>!492>gJ1trTjw36(j^PRryx;_;4@mJv!JaWsV=Dd6q|_iK|JXXa!*0II1or+oAV|PoVEn6YNNh{_ zNx7|{*S@El;hssSnpdV-bLxH>N@?z@3cXe$C0J~4&*L`kt$=7aQIkry>4$+yKdu0{ z-Ns`F3<_llB-)Zmu`||N^nJr1K2Z8}%B52_)Wc%gERxVN9o!>)U7l03h^-xV@+rc5*23<}|-ZMeaK z!N4Gb_nD$`>a!`&ZykaaEORf{JzvwDchFox`rJ^MP5Igd77#<|JkagG`N7lQo0Sfm zY=E0?9Ij_M@tU)#B-roiaVn{iHKi>wqfVxi=v2CStSH5dV;*(B>P`}7Ze#$-MTjL# z`BjiY^UoRiL!C0fWoq3Osm4w^A|CC`nX-@$`&#KtH7FI`F1lga3~H(PUhI*o2P{yC z0}+gE3$Po0rfI7WAhd^0oU)@Y!g(0u~3>Dt!MaE8>JTMo_IQ0!ST9TxUY-h>Irz<`>{ zj4y(p-HZF;Wlc3UV|nuUCTdS*GbB_qQT7Ew0-@E>n^z0C$KXSD#y5&1rIb`yLo@<} z+(a4V()^(_XKs2vPkOV_6KB~cq+&-!K>o0olB{3OtUem}ZbWp^u+x-Y@h@9ie$AB4 z8-L7cuY%n$RO7iOn4CSbpQkkT6d#Q_iE$y&&dGrikA5014M0a+qfNj}qRL34D&R`dTLv$N-;R`bE3PF7a%#vBZJCw~SlfsJb~pYMboI>BzOe zKbtNbc!beVD?oHiN*s3}Zhm8#y3FcC<5?jXkc#nuhnh-9P5Tg;`|F@!lkI0C2?QiF9N~&$85IJ9P2L=gFLBszLTa zC#^fOp`5j-jY);z8l@)I|9$L(8s4S0L&vlHN&B8+QW?FBG8|X(P#XT|NG}B>6OwuR zF>#>L`2*0f zml@wZ9cOuJcW74ace)HF%CHYk({$vZ*IAO`uEn`dq&+_);~=$0wTI&{M|+Q~W%dpr zfs76F{s5#Tex8Z`1v@x{oFhx5ah2zA?`h*Ig-*fhAk0a})63(K8t>9qD)d%={+iRN zmWL9w0ArNw9tF6(1((A&Do~l`@li$abXXJ$ADhy>f)|FbP!6QKQRZ_xti1a2YXhW7571z!Mizh zNbZVKq;Wznu6d-pIZ6qk%CS3Nv5vjL@8{h!WBdFT|Jv{aT7r^_UtAnSku2Bcbh^7m zpLdagwJJo_y21X7nz0p{$BKsANDC>YrUP ztT7yd=i@D$h}aL)3t(R<%{|P!+az3iU*uPcuT;Uf8o8iEl~9w^lvP%0tHoMSl8Wjo zjiOD-y2Xlt+t_icdU8TzaH3>rff^j$*RK?Cp|$?D3l9%N0!S2N`tsinZg_Y`^(VM* zCOl2|+kmk_CHZ!m*YT}ize^Ao_V#0_23aXl@l;?SX;cB(%25*Fv}vC>6*8@vTIaHZj-u;P0P_=y+~Oe`d4B86{MubXL}B z;8EGk*0-g$UpRq3!6hZqIcd%eNL^VjMBYoYvKmU;~%int;YVqUXWn(3B zT{DXPm@{&pv^YbKojsgX7_VtgD+rv9&M1?A^YDNpR)o-uBJ%NX)!$9mQ8+YN=b4*( zJ!V+1amZY~vGZf&PMhz=4kXU;1kC6-&?Dl?v$r@1twbKF=5K6X#(r9)6Qp9V8Ojc= zI{rPgg!5`Y1a^(f4I)wJ=UsuM4b*9gv5#ae3k4;avv{0NF=&_eaVBi+tvp13?7SKm zsePt(xnvxM!nFW=@=k<9A=hIIpe83o^%%5({#`!$bMvR{e}Q znd78WQk)9QX#Z>2%U3D-w4W zEERwgG1`A52-~hgmLB@02ef~%#v>2=#rGHCr}5`ws`r}!!oMk;;mA|})8R&ZO();M zAG)OfDVNi&Z5;opsZ2!m`yUJ1NLqrDVMo?#b)^BPh_43!sY47YG*9cF+6Ym!gv$ek z%Aw_v|4Es-rLT~O1o(aVo(uP%;(y=&ucir6SVV|RDnTf9ke6Yu4Mt-6!p5Zx5FX%i7Q?mFv$r&esVP{a7s zamGuaC{grJ6fNuY7Y#RCG9E@1S}H+XVM`d_UrR0je&<)nq@{Qam@#efZ8RXG++Vh}zbh1K^Hv60ATB?t(jjtERCKMCxt!qRg-(g(65~R``5u<<& zx_`@~YO7Bf5h)48fl)0yk!J@UM-%|>iXM_G^T|>R-G#@ zPwy09x3xq&3)}>$q-Se)?}PXPj`8~!(+5gd`<%D+*2o4Xi`ZD_cev5o@0Qxcb!HhY zBV&pwq`DI6Y6V6Bh`~M5X_Vm5i@_1kkO~5xuHS`CO{owBJ+D~I#0MI#*=fAdn(?GH zF8;tE#)vG*3!gPVn?(338kg5VP0`hzqX8yC_j&CgG!sp1ezlM;9d%RfMIpKPgo7fg z!xfp3^fE&4G~fqqP1DeTlPh?On*5p8qSk8JPmUH@a$7T(dQ9m&PS}gsgk-gg12?Bk z+0`_z-F*`hb~pAQADtDwwrol{6oC@~^?q@|20+Kt8!wMY&_ZhW>+WXq zc-*(lBu{HvF5u1!{{eZqbZn9YPVdl35ULcJ#kqG>RQ0&JO{HhpJ0H~Pn;s**C6kUq z<}0x8^w}Ffj18St6I-#>2~?1ian*cylkU$5A8x|4_{H(nGrm7WH`-~P155apwnx7;h8DQK;mpr8^ELQ!6o(>VOy{mT=%!gKU zarktE(e}kMg_M|nlP;v4l`2qpbd7n;cKO6ZI~|zXva+wQCCkLzkc0-43zGbT<;)pf zZ9*v~Q5#!SW4o$Et!q8QFwtc!rFw%&#c`xNdI~k}gBeNumRs^HV|atp2JC-4m+|~O z{3*Yvkf~|*!ebO0K)GIzx9(^RA(Cp%L||3R)Y+Cew~o5nsL9a8d_5y-KA2N3un!8& z`U~mtfyJ;03|%(o2~+O3td9+ifvqcWpRdaUwfuUtL~Ht6B1g({`sVL%{M#?*g5;&n z<{av0!r-U2mAdC0jwz0ib3IhCIv@yBEP~Huq9iFnex0S?rna{0hwpFE6$7366uXT2 zQ(f|_ELO7Xw}|%JlF*{pvP#(0+TQ;17!A04O7)gOAS_KiUFjy)KGzIz2tK}^-Td*^ zDdi#x=a94Hn9*}tw1CQ3S|XOSiWTj&f~4ukg*WSJSx#}r>P)E@N+Gi-XPV^)KnyHz zyt?X;k0pAot7~g(@GL&Nt<SK6QI!lz-=`*N!TSnFvCTp(biJpr z0;UEMdAlNElCi*Z!ifa2yKB=Y0)|WcG(=~xecodjzB!<&%hjI~{B9NC`fSD(6iKG~ zM7dC(=id}q2f7hf+$)G)nPMbKo{G*y$h1tuWeuf7IL4PU6Qi*5vUHF*`6I^#Svf#lmt=W2nvu!tjKPM131Gi9H%_4l2AXPue|+M|UJ;Ao+X$ligmi z#j7$K4Z)b)^`--1`h(Xju4iIRLU)f!9;Nqh6J*R5YkjOQ=dNu(7!!fK`p2}{?A zvQulz9KTV-E9F~=f5ICb9F8QyE0=|Wz=tggISBih{={`e7ZImpYP3Cb2ZN$PvH8k# zm7oy>q{Qy8X-)Gw8@-Np8~5{x5i3}Y-Tf!>6Z!oVkHc+$MY)gY#hGn=V=^nb7WnDv z^s*ABurZ*W6=yzl&@Ib-++ain0ImI5;!Iw7zZSoybP!er;)US9&}E@5d`of3znB{^ z%rnxeooiPeIzFtcL8Y&Xp~iEYb}&Bol90T`>-*e2;?ESTkFrBVpL;msg=TqNY&mD> zQ%f&cgmrk|a<+>5gBVp>ks2AYvw$2vP<=n*IP27V{MJ>SF$1r%`)8uE4a>bmgHOcZ zryU}~Q%iojhZNwXRDBNQ{7%$F5_A_?RwXmU3oEL;j}ES&la@}9n|Zb04NL;fi232U z1k`~&&EH$aUK~xZ**Z1JnhihMA_)PsIKK>sa-pP$wxli^LnU4OrW{*W%$@EQwvqAe`&cVk=`9B`*A0Qe;K(`T4U7BGs8Q zHu@QMTEf{2zzU4&TjesI;3%>!2Y2C4U2Yr>(T`A)V&ZhMZ|;VYpAh4Y+)euX2zu9m zff&qsIXCPPGytZ`@+E=H@*o14kjI%^*LY=LHLGpTGG#IDYSQA-QockG0G8=-#TYIM zKbqAZRF$XyA}iS9lo|?E#npLp_b_kq=+*G%Qp6Uw^cWZ8zv-!6!`xhQ9Xqmr%BlPN zC8+1vm52OkSJ^d_h#gbIsrF=~cAG|1=k@R8sM9v7MVFrE$96bjNS0Y8^(&$g`H#K4 zxJK| zq{G`q2?1U%`T`Fn)A*I|JDakEd)thzW&(xDf0Z9>*>JTW_R;KgZIqyiF@ELE%f+{5 zc1$Zb=}Sr=bICqq5a=QnvlJ(^ka%7(nAS<)$>b?Z6}^-*HtX6-WvES7R-Hak6!iS% z=Y#3G-l~5I##)I2OPPJWzlUHGT(sfSJ_khf90rxi_>&Yx$AN-O|3Eh^3~#eO*pfkf z|BJ@s&Dir3CmfnMwl>+12C?#bpUO?gLP}kKCj^YwgEFru39g<=BmT8UXs3j=ab($} zH02Q6+!2~o#He1wl~sEHXg3HKVDUc=l|z7_txkPNjuP*N`4eg5bG$&Fi%aI2ccFPghl?2E^ktOZ!M{VVM- zXH->t=@R>StwgCT8akQ&xI(R7mb6MCvSM^5r2bhsPhhD5uA`N!3{%~?9l1S%$?Kg2 z3x)Uu)AWR{uL?Dzev99b%JOIjoeWvA=V3#B89k@pS#R4#Zakk(qgcBNwVi83ETtp@ z(%9>eChe^wt?p0m8Q?~Y#4iEh?&S=hVu#U2Oy}-b{5SOPWb*U}8q*@^F&n7%F+#&yXc_ie$!_S8F<&_9 zWp?C9iQCQcLU&qJSSs&4BD0$Ofa}Wqt8vx^F61pbz6_#cyS(#K$TsBfS_=#?eg`DX zIvg>{)|Jg_9(w%PNm8Umz2-?F2E<|-Q|#3f8Sd)X3)0d%I8U{BizX7e6JY1X@+v&` zbM1AnO!(qv@#bsBjLs<`_t5ws>Smo}D<=}!@X3OUSN_wK5y>=ctAzA!ygpS;N0aBg zz48bBs*nukj{RJE+;a(-e{A+7dTro;vtbDo2uws79{3z6y}oI!omfWHL9wko@@jW{ zRf5w4OLmyl`|Up?ei*e#)jQz?oNz+c7J9_TAKv`B$wK4H7(S>Qv zBRb84sImB5Py>UWnixWVmlyXlz|97O9iWpL_j;8RF9;yLg*}AochLL*yG=<+w>?oi zaegHdQPk#(NE3wf7dHa6DB0 z<3qF`3W|M4QpFH{%6j#Vh1xjn479@&RM27FT>uRJHpKy;;C@59QB;Z1*!Vc6y73A3 z!W)&;J4ofsBK{a=KGki*gwUxca3L4=q0u|ox!bdAHGoo)-hQ61#u5 zp+CMC1b9tr8&P?o1ok;Rc^!#4)20lR(9rja^5|BvK%c<$ci<0H*@)V;rxO3By=Fu5 z!Wx(uIH3en@To26$X-&W@O7uJ^-dqoG(O?H_$~yP!E^3vOFsS0+nMtQb{U#3EGI_y z!jIQCQ9!)>c8;mGen%?^_BQTHZiYRz+51O~+QWYI(4opN6ErG0L4_W*PCZ-Hl>+`o zE2U=8k7~I;U2?Z-GVfd&llIEm&r?thn&0f53lMn^ZcwpX17ekfF71tl-F7%8JD8P~ z@gO1I8eTbu?$b31SnQw~tF;i9ZR5M*_PFr>27r2=rm#(j5M(@)jf7@KT^EVz#pxZ~ z69PXQGct?dAYXp(>Kgtfru!nt&8Cp+k{_TuGiL7P@&Q={r@ro187|{>@~{!LQJVi* zgqAG6T>Q^F8!?4c;0U7sPHGCBO1KH*`OTWcV{@N}Lx-80@ zfYA00?ZQ2yHYVqD1*W_t_?Ji=?Dg>wT@1FYHc?O0WI#kJa7l7(mwRb+aay7?Ql-Y$ zeEyD<%&7jES~c@XK6^spYCr^SLvg%Z+gS5SSC1$jrhL@M8%c!&rI^nc%*@pDRS3ZEC~}4lKeOT0?uY zSMBYa8erY7H8~R80Sh8CxlX@`UfaWK2rXIZ;Au0*Y$ipL+-J1xsS$_R@{{=h)$cF63%hfj09qe#`+YV}f;L;cUN7bY>8C!g4NAs38arx^V5Q>#Sn80Y&M-njGNPfsL1@Iq zV2`zERQ|=f-f|Ri^vkcnRa-5$vOw424ZNIKvTJ9)N6Nf)vC_jCy0X1Gxbk@3&ZADCUTo2|?%p1VbtQ;H2uzM+BZh4(8StQX=TU&E^hCbt9E^VKJ# zgzVm@87Q7s2cEc31HCmPlhYU6uuVcTt};Bjx;!^ns;AM)jxQbpJ(whKEr?;Be(iIq z30}KLaahy4J34m`iUK?(FqWFi75xr$+mPlL_jfY0-_Y2;lKy%H?T>7wHygG!(D2Yx00NIr~^%uN559mSj6b>);#N z!ZW=PB#=yv;K_HIbu-nqA0e^wrX_AZ3F}Wao=xaU4Ajh{V-A|hUnFB=TNoQz3Q0oH zI1OxDG-{{kqzDSPhPlLSq6I~W93r$sxDyE3m)kHgb$@`S{>A@VbSt;9Zh(XUB(soB zJu61T-^+YZB75#t!(AF2GxRu%E;w1LN7hig&wXQ6ny#lhXwRJ(Ol*=gOQz;*caM|$ zdeW-b!r>iKD#YKDs6WpYF_DK#Bc%VlS(a*(e|CutXpCXt1Ui>b2B)YL-p^&h)k;qTuWnSb%}h-cs?oAK zk*d5cbL%sF4-)6-r!?y22Tm-26#nBpJgPe&ar0{zYBwM0J0^U-RUy-g#0q$v!Z@vn zd-M2rOR63v6QRUOTeeSxcc3|Y_dWpWRKIDObFqNQZUqO!wnWdsmhdK~pDMkmBJnoW z>aRG3^<{+&<>QFv-m3xMX(~hc3w-1floJSQ3x64#$&fQHMpd6AYa_{zE?<}UG}rbO z`mQ^=HLd+O(SvDdQy4u3K@sIW_EHv?T4iOToQ^+($`m}6mDy! zZH#{rgMpW+ILTWO_M9@8|AVgN@^*D%bBb7 z^3t+H_=OUzhRSO%Sk#-`v0$82iAhq_ml(Gb9adl5QP+D(^vT?O7RkPid?sjD#j3G1 ze9FlTMcR5cj+(+(j$F;xlMhR6a%y*yVZU8M57N7%0X0@~dfvl9?J~ z`^wPeT?A0u9}=eC@bb_Z23%HnyK>K_307vCVrI?#?lmm5hw%pcO*!f;5erxM^sCr) zEX0nXJ@W=tL&_2L?hO}euWEwt=Zui+JQ-+|Vc@HD@RB_v8lnbZeJSIw=-S@kw)#9n zE>7e`<|x$*`l~sW?ePlyTAr+gL9rta2@2y9lkJpOod!qI*UlZ%>9e%8ch9Fp1?Lj^Ka27)0o-4hOuUxAsIpLSy7RtVKd(;3fpYeixnmpdyoBiiG#Gbeh z_Be8I!F%v~GK|@-1Yb{>tg7of5g5Xv*hJ3sdu@Na9g>Y24Io-#+$9;%3_IlzlY=Yo+zh&U>O z2m6-}5+71rS>DOvV9xT!#ORES{tZQf>w+Bb=VU*+@N19o;>L>0DKo0ZuXtsmDNu-0 z_>~<*xQwa}s(C_$J-e7wj67NXSsRm$O=p@~C%+Yz(R&8-zp>yObFzV~J|m`A>kY5< za#Z|CL(V)hg043@q;Kb}Hw#o~*vZWvjh`W7WmVyLba4{W5p;f0G!nJ}wMR)j?D-UYe*S1)fS!w5Nyj#FH3u9>pye9J)*5-J~MP?xV%p|jSWvzfH5i8V(+9%Am zo_KwZ1p2KzaaSwa|FTawQY&d^7FphVtMRBD| zn2~;boh3S=UBjfkQi$I@(b+SA8X4|4)&MxY|slfuPTNi)A&HOdjk>xn~joI@W!>IYy$l1XIZr`#}rQ5D=6Qe$WdK&{chm zD_`C=iG1%d`SC-u!Q1j7*(@Z?dZ|q};=UCxejg)m%8fI9gUF^&4os!*s5FvQavgC& zuB}_iHnwg=l-hd&Z+SdM8j;z^i&zzPrX>0CgO@(O55vFOAuY5QjXowL=V$i8aG6GA z(8n9nq)7U~P}9MUL3vFNfUer<=(o4zEDDaDrvK>f#}@VNJtg6Nnw3+bm6^^$uV>_N z%IPSuNZSaxok_M@Xk%cR=+E&%*sk5Go88^d&~+|m>)QR)n7a7%FH>$aRf+gzk@+QI z;o-EWz$w(3y5q=_fh>Ac;Er&(dV8>s=MR6=?8w~ke0S}7TFkvzDN2tzxXY7ozLU#} zuR?6g0dljc2dIJ=1eVf!?H;OuPD-CNp1KrP1?yOI4M|*ly3xzza)%{(W-*0E1`c1RMegLGlZFjbYd4xbonOxmj~aEsS6cvI*QR$gz14k4QNaw1&h0O{D2 zD=H7-C~W;4;T+MF%-Lxv!&;`9bX}fpHh<1gx3d`Q51nm=_VJInBV*I8^^1LW&wb5c z4STKLj6Op}uqUY(rKF_pbrfjb6ypi51lH5M}{HO$pKt#&a*a#5BbPvkSe!sAs|^f z33|r7%v5x=j#i`xTlK|(x3~Nepp;LpE^ht{%uUkIkKA2-guV-Y$jp``THWV!`>xg4i_ZGWyv)oeL+E8)FUC=UMw(4Abo$|XxfGufm%(K800>;A9fm$^7cy zk-iV_pyfB#*UnqgLSal6j&ahn0^j4to8uQm{UgDSktbs)dLh-WT-Sw52BjkJgExZk zh|#fc-ZC5`{2ngk><(F0GK_w0vTYdi>|Tys42zvNIfX26m_C`OixIsLt52tv>j~xH z42v{Vf`L2|s@fQu29s=u+V=%Ugm=d>&ij;_9^mdavV;)6TN>R|7o-it!-%D*eB&=m zIT!utK2lmULsE*Kf#v(Sf-I(9CF$(iRAO#10>|q^^gmfw7;7avKJlLG>xB6hVHK^85{^3Wy)Voa z^11U%_&ES!rr3>9L{JiW)F&tS%T^iwW1$5@WkOk%hn!+mB08xyRN;OaI^^bEL`S>4 zM2y{_Fkz8=SvCCCekzm+*c)5Zc;J#{-a#4P%&7C_ib zyLMX1vrT|V)}rvT*viRem;}DBuRj00ZsBO@(_taH!Q{v=BqaoH1X)))3N#ZlU5IVk z#nU_cb1ng6V-PQxY4xSByz*lG{6lf*6)llIU7X`tmwg}$b8(Vio2ew`dee-fTL;T( z=!*bt6dil4X?;aDL7*S5hx#-+wRq=e48ydsosQ8Vq9%|9qf?`NJMel-=b;26FbKer z&`k=SL`PiVSubEBLXUZ0<(pKxJTI@w%dL<(4^OI-X;|^NoMs694|%bb<`p@=d6w`c zVR=j$A-JCDx}-JZq$x7N&7$;6hQL52{Yld-;5I@+W>5iS9s1m)dSlgE=H|h#=uyRnt{; zBE}5SH;4X^JeO29ts^Z^Hv9naQH<~S;kS=ACEs`ScF%knhJ0Swy(OCEvoy(I2b(NY zX8V$I`ebK^)jMLzy|OBR6AJJGFyvSVsDo)4jugS;P95Gz>XksoKVw;%4?@_~1q zz8<1SSrOj+NbdbSs_#|eF|>UP%M$H;*-r|(wmliPKn8A&WMUu**=%*(M6P0by{=Qe z+YCPjz=mNmzCEOKbzv@)64wOFXHc3jiovHMrZGJz9l1GRp6MjXg=6-5qNf-KAS}Ef&USf$=%{9);{xHW{9GDgun@K zcsqE~xJK1tG1a1mx;uU6W8awm_9wW6F&S=WsiwEGTW!zE=#?eD@*{b;Vltu!A>PEA z`0Wqo8T2^m-5;;Ah9jUjkBv`~jI{_!0<@tF5BK6nQ>lWAD2{5^lMv4oZc(W0pzRC1 zVRdZpLQ_`pmA5%Dp;fad6ZV zy{Pz*h!Xb<`n|PiZmqRvn6^&&)Key8Fn@cJ1foM2uTOu0x1jJQ$v8Z-M1NRHvn|Pl z((Qp#5gkTKyLNl3nQ;Wz#5{i}4<^y=OnDSm{KSTeVUYVt75E6Jrm!pYY;b7mEW;$U zGdCTfA4-lHQcWt#%l{Dl+;{1#UC})`X1i7G*hJMp%;W#MdXae~wDNTgHrVn}Cr2If zrB_;8E6oc-e?OhKzj=nBwf%}q?thmZnLV%`y>a^~i^oq&f_4SMb4R_f z*wk5>Y-mogOMcIc#ut{UG1Ym$?SN7w-aGo8F-Mu9R~_~Y5c#`^Z;VORJ5A%mMI!@U zFj6Z+?#-zrm?CFPfHc+wEbw7a@e;)9FZ_gVS#LXAwYA7%uY++DhUa_tnCCPusTgrB zA1tP5yOxt9>c2)OFJ?iRE&v-gqB556S;0T8-&Wk0p(6K`Jx%CBa1&?T=J3A2EL&oQ zaVFg+Pwbfv%hR!*VqT~*_pVNqzPP>tJCKIMRl}&#X`S*CfDyX^U>+|MKB_tD+>VDunHA(V-jEkI ziF~o;671YRn%Zwv^*d&?wv=RGA&+C=?ZvB%(9{vJUtP140mKCQ9iv+3wbWZn`wi$1 z`dWJDx28uq%=TP%m|-dxZ!_e0)MN8M0JP<#qS0>wFFsh4UUlSAQ43>MIfT#BeW!%dciNpImGe{rc zfn8v>s)uH)@9HYtykXO3t9B}$&h#IWZ=Dj}Do8g^HN3zseX6omnFcSF>Lhg7uqDc& zS~52V`upazc^zVgFN5uBjfHL~^LAu{yE>-KDnX-KraC)cE~&)4y}cV2#2(Jq)tha9 z)|pO{{rtI|>PnfQ3Ax|<*ouyz4T*}KEP0KSP{;@Bow+onboxdy+@};PFeJ`cpSW0e zQfBc$)b%;%c$UQ{SN+FHZduWS{b<|DFo!xn>4BIfdGB~RGb|EvW$9T9hQrWPKxy7* z$e&O(!~qu7$8h^+b0`?_gaU zk64(+_wOfLD2|j#BISwn)NBrL^Wg(cE1U)LA1&Ws*`PH0>cE@j1LiYh%&pYl-4hgMD zY7*ubnhH-28mL}2XMV^~mP_uXk&b6e9S!^0f~#PdGA5ZevF(+rJX&jC<PYLom=nuCFH)sc+`cBsAJ>75GFg z8%)@X>oUqJ3{6I#Ub#AZUr1L~4Orl>sWmrqa3fkplnhdpFaM}Vs~w;|m6ucTT4e$V z0%Nf?@Wnz-u%G!`5(W~nu9nF{{x)#!&?B082G?viPHh?*gw0wsX%NAA2>+KtRG8Qg zYmuU4lTW(!7|8mDq{*icb7ZbpXC~ttjo9P#=d0d-sZi@ZxmEvFrV7mdYoa1Zq|(>$ zhE#Mf9vsI8^_%T~Dix5d`L}1Ea#mhA`G5NO|0!BiiHU-L{zp&y|CD8`+p9;$!670V zUvBlRInGMEy1B)qrjAy1y^m)3yo*~~R~9=7)QO;-M=i<`gzh3MwyzUW2@#^aDSaw! z1vx~a^Q_V&-(3Q7nLfLdjZ^;|WP64sBEDY~Z$f~MPV{e+*~_!}0tf^I1cfpgqs?}^ zW{Z{D0|#C&_r|?{VevR@{~+_fMicnF%FnfDFQamKpiX$}g}`lJobd<4efuiJkNy>M z5yk2P*Vn_8JT%vP`w%TiIG~+qFJYw~vw{II_zr0^DCOp;V^4%o3*!@fp`>1CK3Pb`* zIMQa*x@@jJD^>XCMVj@3`F*rUVh+{?zqWS4n|Hv4F(%D2;*MxIS8f0*g>Y7;@+=|PG@s(yt5%9IBIjIdj`QJV(Chbl3J*W6jF_v|gYSyoCNtwKos z6?JHGy(t990u8L5Pk_+-QtAB@YJR7tw2Cur4Ue3uKLg2<_l@J}8uq0UX>3j&tdJ48 z?C<{Wy9jFV?VK*vCcGO(2e|d;1b%foVe?vIIx}>UR*wfDY-aBA{PD}LTDKZB#sNl_ z9!aMVn@X?i^B2012p3TstB4J7qoI>AEjN8sfbM<0`wnBAPF}$&F5jF-yrM>W&}mO8 zxQA+&&$PK7hXmmT7yQ1K4O^<|gMR(dk;1I+C^ogHSLS#zsu=YxjHkCR-b<3;6*FQbdZiwETV*@SHfD$`WGCLF)aWq~VN&PS)Su*G1WDPml3179* zi4MSB39>jTTDh!r%;ZNXD2$cKdngzm4w5_K|7nzrN1pm^YrxOCn|7jMnG#8x1lO1GJj^Nh} zba{}vLxM-T$<}>BeTbvrmvCd~?=sf)$M!Jpx~fR84#KBML*J&IBP9VoIgQu4CfZ-}CJaOXr^f(fP{o5&{Q5iHNe!F04i142S0LnW=-&V_LSIdf zpc%$~WH9XuTb8y{02La}OafZd{cgf)(j~U)d+}0@;14~6$%SJ2VOw9c&vQ(Qt*BV2 zzuU}L>;K?Ye5qzI8qVWcp3U=or4!uECoVoP)4AK|AG1IcD-k6W6d<=g+m9?Ng@NdM zxLIC4bx&CS7O&yc`}_A4_?eQgEkzVvk2WmqRn2nz@;L?C*=PHxGZC8h_tlTH`2B2z zzlc$^WD+;sxvw|5-KaJRsqzA*Nd&^}b_eZNt(rK`^&Ff{M&l_hH`-G^rw|9rpRzT< zUu=3bYoT>7%!j{U8Nc@{#NYP`O9+xw{>+_?6V4JyA% zpY8k42rHf&O=Y_fM8Z}l&oU$}O(H0lKJQ{M!6hKY%kVV^IQQ~)N95+)h-rP0;k?l>IyEV0}KOBSU6eCIp0#M&6@C-A% zUp!H9cwL2V3H{F^DHAjA*PB-s-n;eFHC9zosGU|yCt8xDIGdJ38XwjJOp#scb=Y@! zw_WTzD}BIzuiuOCxF7yulKFVVRCUJ`QEXJze(U}AWH&6Hd-5x&h@1uS>3Lx0r+9 zMTiuI+)ql9uT5LT)(8`TQ*#J>{GX+U6Vn~EHDA&za0Oe^K8Fn z4XlqbTYqCCtL++{fU(&2*1>*M@lSuDIzkc=TEaU~Z?goCJ)i9Ab$M_SJ(&%-+GQz^ zqO*u?pW`!U0XbCdolSm^cf4-AIG-|KE6bPtIFsIZ7w)4)=I!C{y`%6X!un_roWw}b zX|a=9j`87jX3ya&QOP*lBruk9bH7-!AyRB}`}0HtFs&$FgJ#O%`4e-uD#~-E3Z9uE zQfHuqwbBi~hUxmZ1)D5r4BgR?{I{<#Vt1!~605@?Zb6R@TZ{GiT6OYfJ4>WHr>2ZiH72AoHJ=l03FS$?5TAMG+{ykrik_OWk8EzAm0RMVNAl>WL`|A+$SI|M7gg`lxi<}p zpW(~(OzfG|s$fo=&wN%5vklNX*z0Eq_vB3NQ|+Nj(SsyoO5|E&xRRlFe(shi*`~eh zN2hR&O}8PmlWNYw*bxaP_&AT4Ts?LZE zkGoUF{5E(vTTzfAa3HVba>Y#0=SLJMpT2fRe5WBC@$7VRL3>!2x40DaKJ>|_zDk5v zGAUqCzA99b_DaoM)`}>xpGT)BSBO3H^_kRU&cyBKb$QoTcf7WFc&F(>o{u=RB0%0% zd85jS6cA0x##19dM(m8*}{k6M?pvTIrkUk$6d_x3V(WbKN>=SETY<&A7CK1%`s;JfoUk=~F2GJqu9(-n^|^4d{_Nh~W^M zP_Q(SPtv4R4<2t(8aInq9*Voy6tJ$hT1$;DIINb6FY6FC@&{%!=+YV&(0^Vws2wpd z`1+*f>-0o|?}3}E(ZrLL)tROKlM~pykt_7bqtI$GkbjI@K4y@RM`e?cCXykc|Iigy zH(ak&2&guOaARTIZm#lowi&0PwNwHs;@C{SDsv|7z?Wm4 zbuy*Bz~%GCnZPiuCzEF>`SDx7nqkhgFb(Dy6V)#5(6Y^=0E*r{!AG7mAISK}?_sY# zjuxdq?w3=0xfSPd`!l=*M=+5RUTs>{-lLhJ{0U+ry6`F_yhnD7DUzUS(t&4HK>EcC z8eP}tDLT)#aE-jH{5X*OYRJiT3pT{Fj<}{RVwd(er(xtxhQo*DJWJr+m}K-ow@lm@ zCp|UgS+33d#+8xXafDLMW#*rE(=az`f8GMJ@^x1%Iip{M-*Quobn0@W6ozlOJ7L8q z4;`F;Fl(*bdb*_i-u^SDmGSJXnbUH)c~2D5+g7;rC@=b9!%ASYx2fo-Cd!=Zxbp)D zP#Lj1p4X1VwsrS~^&5}{!x?_u9NT!p!>_R%u3dCao86JRv*m#JCHbzlaOd#n`|Mj% z+6O)q4c7C`2X3ui-ZGZ2$6|6G7|7u{w(y#K_1A9QO^u@J`WuO^ zF86x#Gp38G&t`sGmbQ88KtfUXP+_vNX3^ubSJMg>6?H23Ao`Sl;BZ;`Ca z^2P!3=~FEd_oq;AMAPF$bxNoVeSJc1v?j(j*Il3PigfYY&^sMAL0nB@h|d2yA-mgT z`tIHxcfiH#Xtu|XKai4i`G@Gnvyn2&m~3yanmKh{F4*@&9zMOcwW+c&HUwcp(%T!x zM7M(qT?x@mA~)Ek9YQdgw6n(z?GOis{rl>dM~pGfQ|d17o-}lew|5vND+fC=klWv0 zq87g9R|;Azgv6y-8^&ZM2`>1AYfA~dIAo-d3rr|`_L(ayP|s;Z(&vrXyO{XqAWn9> z$9e2-qoPM9aK_6&DBt#cB;z-UG^W@kH?= z!(7tIDyysUH}FNH;QYQvK(Ych8w!dX;>V_Iu1U{qfLdApehAXy0q< zjS1U}(YsoHrgTe&ibF!cnfB77-O>v_AX(e@fbY*dO+4NKIdY%5&96iV4TnVasXGYh zJUMKg^>kcd(GaHO(mIEkqQf*Bs*NBYs-FEh8?-1nY)Fl~VpQ+t=E>EU)XTfs#{W9% zl7t)h>6vIrikYutslQ6ui!fU9?=J?)ME$>aFaB;k0?W};5j*F&TMZa;#&+apJ_I{@ zMqsIFxa5*8w_n_8z+aJ=zEP{ZNp9DGwE3sT(h_ipI=XuzFZLn#tTCD5F}0`%-4_z@ zAS`#F+*7b87ch>~QW3;U#9mWX*X3{H?Wvn}5v+iKB|8uTqPVV8>#dLm=KlN*IdGNG zU$B27=pZ6|mHkH5tWzHNvW^7-|CST6W=QLR zw&2#!R?JC+y{$`YR$Gqj$I`U&$h+cZed6gVLyXRT-(Rh6L}$3N+q2zTWd4fZh-)r% zA*3x}7o@Ci+;gdE3^GeVlD||>FYgx^wQ=^n?w2CpnSAt1fJ1e?SRlytt?5+PLTLD@4m4jIa<)%si8iojX(PjLD#C z*|kKiy>DdmcIo3vX``Yx0JIxeJ6Q|hd$4=+7I$j{Mr{pTe2 z^jP&zJNO+N^;;a%4p=B{i{St!%nl6BCwgcPb*o%pI}E_}|ss?R-(7pR@|A zcRO;8i+=y!THccPcK6v3-@T5qzHbtBtGCo6IYVf#iY-dGH5O|*rtrr zjg9HtZNkaP=$Xu9NJ!VWO(NSFak6pyqy&0ST-a9UNcgV)gk+0-r6_de_G4v*_Nx(Z zNIncJ5#+6n6+ByJ#`>mUatmc;A5zQ_M&uYdZRKOG{M=lF>rcPy#lKWkRP=u)!o7cH z!x8v>Gr(XyS!AfsAxw#yh%HZn>z1xuWAImaH zhpnt$YWg?Op-}&~1 zc1LDxlJJGi5`x!ZA9*#rV-#;vZ{5sO$6~aF4_Nwc8%_V-myA|2dyU|^W&^I%dPhzQ zGjm|!`MOQS@Mkj$`t+GL@}q{jF__u&^VM*aN%BX2E1vahcp<|kn`n&$O=`x+FOC*Q zzoN^rHPu7jOgwsMFFbPn;mYRoH-%Si*hc*obg&b`M5~Z^+Kyt?MaryfbUeb@Bfu=%$wChNo)$~)s(q2kZ zV_}9OAstX*eW>nvJAKq5tKY}+pTv!cLa5fj@@(?LL1G$RuO1ZawwF10lWzq|nk;_=G zzCuYE5ACT+JcfDxGxVMTwo{#{n`W{cgs_@zvXp{5Z6u;3Zr(PBVQ31`@RuXL%Go3b zhC`Sw0wni=;g5(y@Tl%C1gXj7z zXMqi>PNyv8`O6AR7xh+S@exO52Hn<%SiEbiWk9kHU~Bm-@nYc+ZMuH@Rl3qM*{O%| zUVl_gvYgSvnY!v4N@lFp0iEqbgr;I62z%pr2$lYH5o^hQE|F`Q5DD<{hCzGftvf$Y zdgX1^|2Q?$)ZV*+tIS&^Yi_rXfM*Svy=PY{ql^1f=)I6G@5e)tjPcJs5Q$rTR zV^O#3_3n3MFYxH1jQKAVUEhuQUYxqDrS;9mR}>d>_xNEDPi#6@5!VJr+a%2OT4Pi` z_xZXl$A;avOZj~AoyeQ$P+PD8ZM##=nA~VI2>w%YjC}oS|nc;naT`SLK=r#1k zA5Zo^Uv(+L26?hMy;{{_rXD1ZxabQ%dg(VGdT|$|)OPryNF{b2dwo~mJk+Im!lavy z_-O-ip@~>cS7Wo*IVE=(VDg;J{4O1N&d!CSV4l4)%0;tRdWir86N*Q~Rz`M(8?Ukl zTlW_i?PH3%pLBN)n+loej1Mk_m=HgwaIKKM@3tA|Ow9ROcW&FY8c*mU0Anu2QrXxw zZ;O1#%|<}IxN(qGvwn~jbHPhNn#ZcR7r$tqb76iW45q+9m6#D1p9}rFZHnvwp8W+H zqq&ZI=I=1l!eU470ZHn4*9e-@B})9gs@ctNLy-dnHFBWG65WGhxjJnCrnTYNEeauK z$xI<#oF_v%0n=K#UmYj6-F<0zWKpGYT-Y!}a3Ce=paIkZ?GOR`PuLV%Al0;IofMh@CiFc5k5nnyE13}|$Bfer*BKrZXPPjjgMIOV^ zvW2Vb@~`a=+-Jh=-cOeBLyb~vgN}*{S|<->z%NagsFkzEt(d`jXVtOiDwi!|h}F=~ zku~QSCV*+cN)xQuapsTSmChwXzC-b4U7hhYiaQuOeAFOgvyB-4j|-v53bN9s!c{B* zOFDO#ql^nOV+rqt!jl6pXy1tsJ>nb`q7WJ$9yWUYdIGY&HCc_X-`@GTY2tv@L^u(b zMFELYRG}7S7Qbt!@mk66^#K%6`#)fp z3P1@qem`>ReHgQ)WqB`2&qEG>WgYJa(hvse!v@Ax2E%fpw6b04kIl0Qc zwg6h@LaR_em5H#QLVUS-!4C+)RbmR@yr48`KTN44%XvHk8F&v+5j5DKD|Kj0?d^FM{@1l-^@i@(JhVqGO~xr)rrfy5zSWRP1N@?78fGP&-fDf1dE%VmN2?F3awAJZ1T#2Nt|dU_ z!k_L*7<}XxRU@*^(vG+KCFR<*>GIyyO^*wfG{tnwYCuv72>a8k`Cth+Wx4epks2B{ zs(X8F{=`=VTp(cDY(V2WQ+nGo5zZu}H~_va8N*%@*F3JElsR};faYYklXa{$B&4pz z(98^)#A||V{$o9NDDND;XK0j^!Pz)(tN-wSOQ9Cz&1Wy;YD|E(^d9*q^?s9<$YWBD=|ux6Bgi~60){39LwFtVE_p-4P2dNT z2;KxXAADV%=JyQfAwE{d!8iD=G^0>a?w7xkHr3?>u;N+QVXCLE`QTzg!NzmRZ`GI3 zXOe(1lK;J1-~_cK{-t;~^v{)tNr84LhfVP48+q@;vj#Hd$*(9eZgIhG>#Q1Ay3{#t zgxMk0LWOYbn<)(ec*5zivlM6N+oSRvV4&Tqz$7DcC%co#;)$nVnys;N2hV1fh1YLa zvwMoR*!0qa@_s>|#&u~h^N1;jDea&pOOEu)Yvw7G3c#+-_Er^If=lzv3T+sk7qn#K z-d|^RlBRytJ3b=%P(B3pF@U_JBO7AndRiZxBimfQ6~e#~E~~^2l;)Y|;|{@b?$ufr zz->@GCof75WVY7>vWTKHwH!vZdl;Aam^qb-B7brJ|CQ`UJ{5zaqmUzHRSqw>@%D1= zmrinC&j{LSUL(NghWK9=t#Qi_V-j=@!ww7@<~C2w7HGGmeU^gHHr2s!x#Wdkh8M-Y zN|2`mlu7Dy5luA(!pMj81WO$9Rlrz)Cg4;cT8c880a zas2wg0;C#@W+fK%S6)`JYK%ZssB#($P##HW?=xQi>5H||ky-jC!t&&OZ@$}sf9_`8 z2gTcBNa_XiJ}WYVrCy~SLDb!Dgy?GAriQ|-CpEG_3y3V}Dj~-u1am4Q`<`S?%<@T> zP8}8{c~~};^Ddk(RTQl^uhOhoVnzYWf0pSQ#!Qu~CfV@3V!F&E)sz zZvFIPFcjF+ttsq?Dd;K!o4!x;D z5#PQEoo8*g5-puDa4_*5@gQS~PFnpkYaugIUg~K5N5>uEjAZDGkW{|A2E9_cT=n*) zSIt3CYWDvWRi(lKi1jUfwUui;Qz@jBfTcFGT?EFA4!ZQQ_9AHfqWv0V#Pw1!935tN zOA9lef=FTeeBjfc$M`5IH%6>K(4Xa!bn08mt*bwo9Z5Td z*dBnjT^I~x0wF~&O?-pT*r>U*^k2f+gJaX zkTTQw-Lhw{Y0PKG8i9jCOg|RFZAjU95ckMCe%C|9dWL_-W>9OX+|GF1b8b%ls(q}u z`{KvMNH4V=Pl$79em{-L#E+f_#|T{2k%xebmCbDV27bfRCllrAQrcTWbyFF(-6W5; zeDt`rg3tM)jS^tzV$dBkN4d$Et7ETi{0BA(t<}7u?+z6J7C9RR$Hmsd=2=lHmvc)z z18RnoEZj}OLo*(Lt=)b(qxzqd5bA&r{+A1ALW@4i-Z1EKry7I$p6(;aW6YF#OR}W0 z`SM_>is21^M#1WP@8kSgExM zY){q#toCwbZMz-c10dNG$d4)YMo&Ww^bn%z#u$J3Y*#^DSfgOYtNJaf#NysDEA-Ud zdw}_a;+LLZ{dEb7evQ=+9*DeDxYECBN9o-JGYdD`F|1NVXnkk*{qO}>t5Rg@O|GH} zMZV7$Z+$T~Sc}5#!Xtdn)t|IITdFvP4HU8;9zRwt?GJ-s{dmWMd}eaoR7Ur6MklX>Z{aTrEhBU1{BxTdB}xSW^rVzo+f-os#U{EPaI}*$ z#|*rprH=PFr2%c;yz&W2e(ccK!?bet^;ftG+QgvKl1}uN+XVG;Pu@SLsi@LzF$A0S$JLrKhZ?~|IVI;O>K0!F_1&Uu zkt!J1XTihw05GF~hZe3S<!sVTeq9kk$uTjmR?VDq*eX-d$Z~5L#Q=2zhea-SB*M1( z71T$<{-1((MCxYbG+gKY_Ate9W}aH~(2CPYQt=njT0dY-$!(R=t+{NL5 zq|*R^@a4~EkZl?7Q195Jt81OvXF$LURpI(w@CTrhG-m{q$|#c;RGqV^@t``dAS#!&Q3 zP)uqIMp<{x-QDRjKJbXh$U8-|Rd$gq{wsOn&OOF_pAVuE#Jc?4<1gC_ z`l-x14eSDU;1T=mIUm%N>xYd1TPEmynJl`0TD$?aOQkB8l#$H2TWu6vT%&34F!`z( z?~8qBUo5%w8rHA%6DLreX8Mm*Nqc(KsBGLDfEz;c z^Fc(MRqAeFmAoZMFi^rW`x_HJxrAwPfRKoc$GAeHCzoMk4q-}+T)E_9fK+TPLjc^s z${dPrv&^ibH6pc}xegl8*d(6ZubZLnO5KM<_&m_8;(0sqlKc||X@&=r9S}I2S{Pkv zC|cQH;(5ItXg(;&Wj3DbX8LjdL2%MO-gDrB2lgu}&}{V?*kPT4)|uP5xY|W;;w5ZvJQ!>H2Lrqw^XM^aWHqAZ;9=n9jN z`Bf(p5d?pD{F?e0P?OyA9$|sdz&vmk3*XDQE*X*Vu=H^!M|k1jgmr|*24t9dmMbJ@ zzdC)bQ024<9_EzkB%ivtflwH_^#!XKU*_H)bSv89#vQFalh*^1%10Y{-}^hTjkN@! zDLi|(F;anK`1wRyiE3Ycq;eJ*{`E+HSEWbK+0Va|A)2S_M z+RYvl($1~}z>z#q65 z{6xmPN^tdw>1f4R+AXc^bPT7={y)LrhV6alfq z=Vu5u zudK}7LX$JWEf1raO8S*$4u`jvsVrq%MN9!WT?`#v)cAjXRM${WHBKio)egnkx^jgI zN&{nb_)L6n@tel%e(8-+n?~)q=7G`wRLzeohS|5f$ksJ}_#*jJ%L3&kwU+Ak_HF)h zZ(2c`h&>NC_i9O6G^2!$$h%&>*_NQ#KzFBV(Baw`ig7&hocjpbSloWa>pbjB?BEcFXo@b{^xswRk`e}t|JGiya1 zULKr1iW+c)ju=^N`IAq*uyn`B=I?NI8`oI2HoX{Y&agDe?xz|Mj4kbL6$_SCTD;E& zpp&FfY)Weg$PH}kgL3nlfT8@(Z5FttYRefkH3dNigz zp0TSRlGSqlHjQnmJVY~15YQzt9jMi5YC)6_1h#(m)O0!<-`S3LUXPot-(&p~cy-9S zt~>j^3^zd+7tCDVt1>*g8KMbZ_~x8e_JmT-ibv|!tgnaWH!yg*J*)Sx}8|f zm~$g!?7ST{xMg#A_(ItUvlcy4<5j-pBnN!wR8buRoX{CLi%ss?xKpnxV!4d+DnA_Q z0NW$8%h&c;%{Ek{k8wEU&cwC}@1af2K%KFCc{YVs!f2jPg@$+Y7NE0>TS-^nXk6|h z6TJ9@PV=0b0o!)i8+>V5J~0^RFX!o1qr&N(NYoz|)iH@{QF1a%y#)OxzZmU)s!nT$ zdePj6RqTPUFqc~mSJl=*4pjxuK7H!4v~=X+cguf7f8X1Fdg8v0$XRIxN~65|F|ECL zj~TGQiWFdK&8RwOV{A-Ae#5=Pvw&i~Mc_2Onp?@q2sRR^#lIZ7Fw(geQv9M}&aNjJ zL^|?@+U;NjU!LN5*?x73IKZ^Kp5mWgFqnzMR<&x@|Inh!ByKLPEgEVGJ^EZ+XSfX;ruaZ=h}CbBmf_8K*9`CC+(qu~uyvRdVC?bC)>_}H8 ztp^8~u7~BYX*C;?63h0?s5J=JF0jVi`{DTa`I6|Y0ZCjdTnN% zXorOqig7!y5<*~n8smsvm)X9!_#{U=Z=3pLY()lna>Hbjh@z`zG&>hRzyCj& zJpKBA1x@j3Jbw#(8&??dYm+_mM)A%1Ju`udk>bdfo5x?B-mUHZ{=<>82|UZSBt#tw z>OA~!YnB631>y7a^M&_M+wF^t<5ez=Qg7o@YABx4{Ll5FSnRq;_Lx;?16}V* zVBecVkG|=epi^elCacf-9QZ7w@Y^F1h4~jJH0mNqq{1#^_s~f* zu5G7o!_Tftvx@8W6Ip3Zv{6NSt9p9=c2%N?l!AUd&+zQNJrd4_dCrOX=S7AB!N8ns zN5fq4RrojJ;5&&usYFXU^Kav+#qtJWNw4wlOEp;fc3ye8w`^up~K#=L$1>oBCKg%tuj$rsJPuGE)$lK1vY zCZgqTfBTQfk=9{d8D`Z!-KzCB!#)Id?3}S&ZA`R30TOxGO7L>-q3F2#UkbBh`@v?F z)rVg6jIGW!Xl3laNV=}mY7}W4gt8v|9f%!`|BmuJ58nTFPG9eb>ztsO0rN3%=)FGHM^!x*XHjEr*k zI--YWg39Eaoo+=$M6ju6NrUs>{i`tlNWfkAB<1B>+eM=o3!2YfXBme*GBq{j;o{O$ zRJ1UX?j#Rl0Lekw!^6W&tF2yWsxfv<=m0(t&#o!zf8dbs_;1A>#)q`DMfwFQ zpw;1`a){mZqfJ~VTlZ*@CS7>kCtcaYL>;N62SIz?oKv+o-lfCSe z-E3WRpYz-|fAJ{@SSz@4Z=FG))NeBAYCsPYy_Y%qFMv9PSl<1;qc4Bho)oTuwoz@@yUORL1K(Ls6%i>!KN-5y&D3$=UGB$j}x9=l|9wnlq!` zr?eq*q+glY#k5wZ;H3ZrH?^hMGRs(d*%Ci&L!mDcMI$$`* z5uE}{qYbf(r5$WhB7Me~{Sn(KxdwHTI{i8_lj`jdB3;2pHe>2Ec|yy z*%WJXT}ax(J4NySCaE)a9$k+c&j|flrIyFqIQF$8@<+S zY`{Ckymr_HrVli&Z}O$R_#K4xSW?}}p*&OKYRurDFVYY<u+0B)EdFZC*wct(cTHqyjyaph#$)Hp>r(R5`tTHSS>F{MwZkErgM{4bC z!X_f?fR3H>0_z5>F&+?$NzyxXil;Ec2?g$q&z#bkhFL_h*W=6>e+RNlqRY`<4B3*Zx?)y@j2}r}>Va zvlS%Y%Ef4L@)>(Gr`3o9`0K8T{+`6)Y~_-(v3C1)a?Z@sd;ssdyaczJHC<-jFMzye zJWu>AxbX#bQYq>e+{_MU8*4ERx96Ed`b{ zvMHdfyYoKn+Wi~u9e?FayhMQC#nAkx+dpkgcZ)3RUKHjU>LkVHD3fB2VxEy7i$y29 z<{Bk76?Nl}nuJdYpEW(?CS7}4bbnU1u}M+X^(D{j-AR1!*SOZ2P2Ec+(3<^E0GcngJSlkKr{Y^Z5P6)~!2Da%9N?a&MQQM|%qVHd z|34oejRalG@Gy~DFGKHs8)hN?9t-J@3o!{v8?_4B3MkKOz41jen4E>*lX#ZBr1h86 zosEEYCZILyO+R;}W5nL=piZ}co?XHl_I9he_V=!MaM z%2bVe^VkE{qM(T5Qd`~N5f2VK-Ix?6+9$+r(4 z-zFFGpOyyJjsd2QVi{jp?x22N=EAQ5j$gH~nLZ6O0u22_R&o6(fxYUb3~PuPfHuAW z`Vugy$`S}KpD54I$u=I+)rHMwj;(nu`_h*AUZ$c~>E7x)13(D_(pDlGVze1a$ER^U z)&qh`wf5T_Icc_}!ZeH*lcw!=M=0gABbDx0wT$zW2l3Pr`97mJuLx~AN-%rYX|Noz z*p~_Vm+x|;(c%qMT?|1fPY1T39}B-Y-#A!%%1X|?(W_4>6os@pS zN$SvHNS(?oJ5j9^g>GIgL}>?W>nC)M5oO2VIdIX4u5)&4e11hP#IO~^*?n9wz|1pt)F}v1uG-(OlnBo4$NMA|XzcAK zL>vd^WyOk?$xeJ6GC zEDg#@0Z3*sXP83Nk540G#1_A)qmxaj+GUXimCIgak`=~n@>nbf*(r$3fgU-#q6)I! z@dR&iZJPSVMVN>My2LQC<-Ba%2ru$6(L6m#<0zS>^5G5`1FM?1&Z+TLhQ-wX@5j(G zk&Z39<~;FSe5`(Elx6bA8(346&0iU21RU}T*s3Yj54ND_)*sso_Jg|qebK3otopFY z49cyNDbBN&WrZt|t7eA*J1TYp4HkYm61U~fI=$m;O#t2_2V;KOw;t9c>X!QApj6Ft+v_dgi_e=Z9xD=X>{V)`aHA!R)0ff1)uLzUt9Y8I#EC@LSaZ+!}8Dg|z3 zm5Iz37$qd+vp$rAm<02IWmwgPGdfnc8p4vA++vd1#*n4ct z^|!UIn!6B69~e67NdWsU*G!_sg~}#<9H|nf9VH6R>}rUQR$;1|Hia*? zHqIIh-#HC8?5;gD2LHC<^I!DB$@zWte`N|tPs;~v%NF_2U=ia-%}bYNCE8Rh;~DB= zna%%8ybrUX_gQ{dc(Ih5>duyqoEy}>JH$-oKR%Q{zSkEhVD1SIF9@tud7x-kEy<4};Di$h6jg=d&UZv>C z%9((n@3{8d8`myA+JJ|x8#@MAD>ZLvPpw*F8M`{0FQk#uy_(v9s+?MoJiW&^oqbJr z-W^+#rTd7rAn4Em6qVSy(qbid#(%+HsR3-cL?plS9{+yVdhNBH0q*LHi{ieKR*v5* zZ-yQD&C*H@2eL|>SbSBo7&cR6&Sp||U;7;1tmXl5^iTgU3;o#seT(Mpgr|w)46nfk z#I>enTIwGLLPE~+089Sv{XVn0dX4ze<1g}DL+MZ&d%0W*5vNg$Hn8_RS(ezThHNz` zZZ43myHoTvCJSp4Zm_)GyoZz>!kPK8fhL&u4+lTbO`WzU5JcA=;vUT+rG?`MOc!)b zUd?gHN_vJOyPmLLaCwe~r@shnBJit?AK7_=vBLIdAvqwz!0;{Ug9QtelR^>}>rtCo zMT*yZ>I99crC<`vF+T2j;#Fr{fsVS@P_d>Ll2Y@eUyLVOq8}{p&zD9B+t658a~dI??x(0Py}t;6(z`2@OKZ+Q6QX>~Vn$Wpe8_ z@IbZCO4XdK*xPv~3l^(FSGrc53wFb6uFd~w(*AIx+VcS8eoIx$bFSgrn!~MO?r&{k zHJ|;Z%;dHn5Kg;_?VCV8>0! zaZ1np)NdW1PQ7^aNZ5dmn#MKR9_*-G{o~`%Ud`_;XXd`|p#6hbca9*beFlCtp0Rq6 zr`K40KuwALM-JRkgetaVrIYh8gos$RN7~DeDKA{I@n7mD4D=X2T>FWg%8r#e1J^h= zEWE~Oxfch=jE6)W0G@%SxpjNawm2hmj1sC1Ff&IV*Q?oNnM2Z-ErbmW58@s|Rft() zR+alMW1HK2W1I0jp5l%+9%)~~lVJ6CIz4cr2A-)Z{~G^KEcT#}J4$4h*NOLf4wn*+ z*ZUH`Tp4BPWS~u&g#}xOiTNq&76}Hc#XoSV)qpdm#HW??UEgB=hg1?n3k&d zK-tuFyFxke2R{p&N!6T17!xx^?mO@u7zy& z-q*gaeV)g89LKpYIk)1GN0Cr}vubfzP&A7&X}ATK)YQs>?5d}BpBib~v1ty&h+Rxh zGiLNS)_v&|En0u3>B!<%pP*)V49{?^>1ycdQXHHgb9s|k_Eweyq;z_-+ zRM{~dmj9978CYrCquPH_Q!hWf@kC21Ws^+1?i8bN{E(!E(1KhD2xfTw!(_Xczb7h) z$@x@9VMpeaEPpTL-(Ev(AIWoelA(r(vWd!&owC5?D;N8utL&1H^pgH~XGR5MdBn)n z-8j#{{TM?GyQy$n-y&>~wut^6&e8To-JH+N>>KYWGQ;gV+x^Mk9tB_MlX;})LBs4; z?N!E(hym-PeprBkCUWUGwE%utbagc_8JnBzTE#EeVLENfRYo^geh?yZJ$}uEU1B9; zI(Tsqr*hRY?l(6F&2fYm{-Qszd6gsZ++cs*u)tnREsi?$`fW$5@1a7@y&pGD#w$=R zlu`jy-C{6%Qlk_&tX+kJI%9c|+Tap$@w3Nv^NSvhqVbf)H4}^7-m7^}lm%V{>tw!e z=Ez07U0qa$)F62Qxo_-(olapfiJ>MQ)zT zvXw}dv(7TFKUIhwm%Iu#d*q$@i-9OBOhS@&Lu|ajBUbW0SjMI3Ol?GM_rJkH)!v^+ z+14b93J-N0m!$LR-nB&>(y8&xn3Qe%-h|x?<^y*`Zo`@T$1096OEb=7x`$P zQ{WSa@4xX{GDF95yap#5I~T%u+}P#4YI~Vqs0U`oO|3NT(2D)NW^SoE<#QT0QXjb< zwSSV{3|y=N3LVGdHw#-NpH<%|kA06|+~#<^DiYxg6Fv%m5L?ErEyf3*^)MuI4GqJ$ zyX-qnJ{7ji&85+llshS@arBO5K1M+R;I_(>ZEc=Cz){`(e#v#_3^b$Q&HgW{!zOU2 zi$=Bozk-giNCUCg-uK3T-O{c1LjM%N|L=68i=9JCiPv??bkG3ajInbbyMCHR+l#MO zSs{@ZAq%H}{3Rqg5__;!-^X2&J8aDT#5DNp<3vv$isjpU`rt2>=|3=gR#niC#KW8> zW25>vMg``N3Tlu$bu*;EZI(csY z&jK4}_}H;z?RAAVdi2|LpG0UlsutIl`6&T9H>*MzxsWVDWVkKQZtTkS<_8Q4*OBdo zYwAu$V&SjOec^i7R$BdJ2S{MV8o2s$--0Gn#Id$q1=s#KW9BDg-OS*h0E%rXxidR^ zTZ0l=Q~PYrG(r6&+)!uq6p(KWP$k0*1*qdb*)fLx;*o(TWj?zsEweU@hC9&DGu6M} zwWMf`=GVH!Tid}K6MXzPdKSAM1nkv|xgVXh)h;Q=nza^UGdGEx4H@)8NRISb&ccyo=eXj`=O&~yA1oiQEGmJs;nl>f|^yYsYfeeL0Hn z6+0FMShK}`HGJaF5@5W~ioCW~Q>yDIh~jz&4@SKQDq}4hb~9?KL%*#BO=mTX=!XQ|)&r8UzKN>1-8dLp2ep&W(pdyRr z^|j!JR;SUOe+4#hwK1=14y}R&xhb=U~vRctJxtH3cK8bPY z{l-Yk^<`b_Dv}*ub}hF)@g*0pG>l*g@|xxRc$}x#f$146yZnOWYS@otWDo0V z#o`rV5_fW}_^#?`*R1nTE4NB;H*2ky5TP=oRJF?TOc#~DX{&4l&E7(-ui@ba@Z4Q!lkMjqZM zIG7Dm=fW{~4s#UC%Y%z<#}~u4mf|kcdRcZ*1Y?=A=mwK8sZ*^9z!X-t@}y?gTL^(1 z)+5+mlY*Z66pcmOm7U$pt{P`!CbQvn_9r3J4_jo`k2C(r2RP-p1^BIc6AC(gp;l{Q zNczmS-;%ZMzCY_o;H7uMH8alkemMQ*L@mZor!w*=iqD@ie2IvyuP?brf0NUYlEMxYLGcYg1o;M9BFQ))_nwIJLHM%N$DsRl`mQ8bVT)@X z##~E+R96reVOBA}9WuiHRr)nmo^y(44U#iy>zF}r>|GjO;vfRoLhq)OEC@$WvCJC# z%v7WDbiX5e71vYr=hc=>x~5T=ty@Zo4t9~N3mz28YK>VV+12>WsV%5SDjwR@q8hWP ztFaj#qQ|6)?<>FoHPty9RP&L#&ZzYg)(|I9PEF5{Rn0W_?E+&}eBCBrlAwOU!pBvu zB531~1@s>gd{cPgiayG7*f`)Y6?O(v_FXRN(K@%&ziu5XHUW>l441cbiorB^X|*Mt z2hoD#O^!-Et_z@tQ>{ZsUj1G?nP3sANA#Zvmz(TgimMjq*MCX%yuLg#gb7Cxftnr# z22#_?fCf!!wR{PRnR-IrYOBj8d+>UCYP`wO5WB@-&B)Et+nq*rT{p>7(sdAh)Hh(T zYrK$IQ8?*;R79hek>7OxFs#7?N8}w|$>XwmMIo?rVpMe0hIxz6W{D8WL&fchjvCXn zq1qpl1J&Tk#v2>>1$8=Bnrk)nE&X1nygEU1}hlrK&t9T-tRlQVh&&39? z>3FfEl$@N1oOrCBix>z+x=JVAUWg-qG}-dTsGcGk7Sv(Wh0q=%KFL3DEAyhpP6O$+ zzT+AF*p9r-ZO%X83PE58WN3cOq7PeiG#z3DgPGBRR$k&S@qBk$!_)0{LzBM0a}{s4 zqhESXHZDLb_eoD!TfOHxAz@G9i11q`&%wmL#l@?ONL7=@#g2FZw2|E*tsjfGJ7-Cn zT92|?-_pXAD}PNmW`ks7tH`OTsmp9r7$Z97|o$OYt`weZwIQNo-=n{ zIyKWU(N7t$bBF?swgUZl^as6y3#*YOf(C^VNeC_nO-7~3ynQ4g18p>p3wQ+d9^?qG zPS17n<~^dYzAcnqv*0r^^(R@2mrS0o|4#=Jz4iz4`gX}KB5!1mArGxFjqTrkaX!g_ znLl2Q5R2_}WSdz_YOfa%Z{hyp58ruKps{_ou)ltmuNLg}`}`uy^$t;DIZED=B0;>Q zD~C6~xMt?y!_P)h(_o5Ub>8Q2{H|F^~2=eHm?r99`Z48vSb`i2ItY}53`;1rhUqwG`Sw@dX z*+~G`n)EClnVg3I>aO&0gtMam@cgx^Wva=)avjr3vc(UNz15x>YvdDn{;NF{v;afj zg_SKm^x{G2OnexAWhNAzB+`^YD%A4{EUnFA4@Yp1T-`$P#qzg^|ENPB76?6DSXNSE zEShE+2&?AhppQd!+Ib7?HUyx+T3ekK*4A+u8GRDRdz?w3k+no-{NN&vSeE&fyaLba z(j-OGeyKlrp5+OYt5!TMOlrjs|~IfG}NlPB))l$uW_xQqVF>d z>-i}8YTE+^J$U32v$y}~Zj*S&cW(z3onVUZ3&_h3ztLk~ zEEbAiOdr@?HjpWiW=PuH&TP#AZsbW?(XUDtuqXA+Fm~qh86$81@s*L*fI2eJlC#Rn z8+-_F651cRx@=a8+lLf61htww*|6r<@82c+EawTK-%-o|yrc_$gSi`=c!qw|5F2Be zQik!a>iJ1Pyk-aFECegMFrrAY>@0gdxp+?-WN^0sC}n_YaP}N{`ctML3-d#cMg#}z z>^CUnuv}YWjWi|eyf&+7zs0W>2r;?PO~@Tt!^k85sF9LP?c+eLKP1smPc4)_6KzYa zW7oYtE`^dgjdr-L0f$5mPp-!lLG)5mt`h?~$v$~S$q(ZURKXB4gA#XS9meDPs z* z1un)+Zs-GFxUtcPW}c1MZEcTMP_j+cU6ln4s3DGWG)h?EHhoJH(@j(RT^ji~w8(L? zhm8`pzh*DSgjUsoPa!AwHeqbvd!41Ee(mERBJ*r$9!_ts>8E4UTBLn3ebYKmlltGrQ#(J&m$kz3A4`=8o;B(5*$>%1`^9W>S&p zLnA6ybm4I(g#eiw#FWY{q3c zOHhzIIduXPvm%PoJ`=6iIi1c40aLAq*^{+M@FJ^tsOdBe+-w0xT|ml_nCI)(z%rEa zJHe&3xowT=#14BTT(qsp|Newy@Vm4Vmh}1+ z5tT@`W}0<6Ge%ZgAG*8|mDuX@7y$1$+t|nD^$A9>2U`j$a}H{jAp%AR+x@L$iB_(# zt*ESwp@nX>?Y582FaVg9&-KeH`7A~y9&HwJah#nEJ>kG?6a{G6XX7OZnSb(-(^w!S}q54G=qQBue-vb^K|Og;7q@D?$M=fJ>?84bi0DPPdJp!FG@Zs)~tU9;oVF1y5rQ!wgOKjXgYmFKyrQ!6Qis4-N?zJk3~z% za-XU_6%%XPejn2I*(WFby{B$SfJQoef7$5$nrmIv@`FbyU|r37?};?Ie=VWAdYm1u zPWYzlnV3>ih@|h5&n_2=%WqRd#e;pVgcILlX_eFf>AyNcSIvO7d#y+^AJlWM10oJ$ z=O6bUc;~fC@Ug@R2yocK>L$JC<+jQZEu>+loxBXOgdh?wGOet*Gt9_#&wGmS!CUx? z@KYKMcvUT=5fM#$pm!Z%d*U~qoavtd7qNTVog?~Jnhvl6(liHI6!{OrxR&IlEBws* zKv}4&*&iPH@1yof)w|bzb$-MLJ!W8BHr*a#J>&NDai~87Ivw?Q09*9O+GOlGjHllD z(J-A>6U&i+M+1BtY#!b)$D7gxVWV5iyUqR6;*Kh79n$*CO5+~U`?UJ4qpsVs>{hrv zZ`Z5}h!fAb4XN>-Sk5cNL}$2ipbxurO%MEut&-%<`OVVW=UG?(ODEHw;jMVn5_o&OLP^F1aGhZI;;>B3BVwt3uO)bWk7EaN>Us>9Qc)1(JRCk<{7s;QWkrzcF zFuP?cCy3RQ_iyuG&!)VKUdvxiYv64@P{_<&S#`1;DaJez7y3Y8?DEd+K3QX!x4FGG znUb&Y_GW9@XnAyD;UCBz+E`xTdTR}B0^I(K68+B6M^Fo&F*8Bh;w6aReKKrM@i$24 zhe&fiKm7wlK-NKsk?lM3x0;vOx59b(PW8>05R&^(13A5FB%Sq37HW~!yvvK>o?3RY zC`bm?*~F9(4Hy8{Tw;=z`zz%66}!>v=Bx1+>?a){*QC)VQI&Kry4F*VTsI=O_+iHX5q7B`f>3La z7(+%6w_Ps|yH@nQiJP5YWvDSnE}uGFbK*}3+S(Neq4M&eJCH>l^{$!y9ks8xcvV#| zcpgtm)|BWWhN2N?>V?_~K08W|r{`3Seu!+v0RGEgifsv@Ym~>$dnXf!;LnUP4s-}~ zY)zxMT(KCQO1GaMdji`XXzuFf%lAqg&r`)=!fq2J=6E&okd~(*laJiq_*)50db-9I zFw233$qbDywqpvW)}6BZkjrl^YkHEEFyGerbq(iMR#t4z4eL&rqOcjpTCPaxCXJPT zY_q8D+rB$ae>^+&;UhQblA zD+{ERWFKA7H1h`zsqxD1>)_E9Cri6??r)?83V`->Kb_Sp$3M==JsS`KkbQvAuEYS@ za9n6;C7<}AjO2^6U9gVSLb9>nnC!rIl5M+J=Ftr2VLEJRHTkTVrv+8eW|t!cV3Yp2 zX9r+-t10yfvr5i$XS}q=)3TA0?Y#LDw~))~>_xB?L4}hoFTx5^&){g9bv`00zo8I) zndO}0Y`*a2%}hHD5jMVfYZ!-hMwe6)IvxIZBv=o*jaH4=U=d-Dl9YXCpjfx-0?GN! z(aKGU$=)Zq#Tlw#Em!{`FrhWKD5K43ke<0<`}%5k$~ILz3vD+fXi}ylwO95S(clIE zzLOF&<*tttC%C664g--{UB4O`s9!k!@@^K~Gi7n)V#KW|;A8N+pC$ZI)JTEAMRUzl zgOqb+38o>|@^wa6%wV#lpj8Vm`i-SQlb5o)b;q(rzWgh0-;JP*%#_ktx`Z?JBJG>f z+vSZ5b+qIGE&-FW-L|it%yUf{alhZE_R#&DXEttkO0v8|o1WpuI~zTb!Bk%H}IJQDVm$Q+_0CE4l6gNl)g_gvits z*EAkMh8ndhS}WqC?Vk3ERvlIS`c-=(VMHhzwvUTP*9;htt|uEFVU9Kneav zDBhDU)fLF}@z5?KFLtH?sURobALAkUV7YyGgmpcG`TdZCs%fM7_LTgGb||wiOw}F@ zCA+P$O1A>9-}QS>j0`;mzc{Ynn+VbbGsuIBv(w9*jv!igos*Cv8r&DF<6&GBq>@2- zF=RDE9HXU|;8+8uwZLuhc6BvEDTS8slGLmXNy+D-Ob5^S9mxQ=Tnt?pT5h4_OG2bb zf2sIbNw&#<*|K(Kn<->yF1afHGN38hp99H;Fjz*v_Mp{tE$`9F>p3i?YgtPKOG6=A zH$c1PaqOC_N`0h$`0nV1xpTtqYL&l6{7~R(;LDe+Z$>YAQ!|%ulM`hOX%=a6XB>H{2=}GcXknu85lx7=&=;EtUI`_2~nPAzM#J+GLm+|bILj)OHJ(eTE#8~H6#P$X2vJ9nN7-supDL%Y-4W@f}n6J}x zL&r%eEVO6v!RmbJAv7T;4!7`h`X&t^Gm$o0JSd=51UTJDeG z;tOn=YT9H6jrP2Jj^ir>Ceuqe&T8H|wy?~KjHV!|l^?@$^+lFVE_;UzY}1<4h1Ah@ zj;EAdBte5b?2A8=ZKdVJY00;We$&Z+4CQY5i8r`gr#_8+RLt{!n5W2s@ipJXoDLaX zqT_mI)tZ>;>}ROsTLG*03M^^*#A2y&B;Ip=*QA5zOvOiW4q|<nA})-D=LhPF1IIW;r;l2vTk&3hiE-Y_fxG^)`#N;R|y2xjRM zDNPrNk~ZG5E`EzK!B?GW*F*>7pNo!ay44GL^2;Av?4i~Qg@mqqe%Tx941Ui% zs3HXT=~mzBbg$DMHSo)k?J~ow|Hy5!MlbTf@_{)v@#k zqwPZf?z@&Y?S`gUF>1AhOJbwKWW6##%Z>c86-f9pF#I~V_w53w#Lt&Vdv-0@lXkl* zp3?F#S$Vrzf70NC(l&vRg1xRr8_9XMM{N6>zUm=@!M>u$qas$P{qm8YXB}|yB333| zi6!AQOkbaxS2i}}l(p$Q3S!rljJ|(=%g)|D;h?l8Bj&px=l65!;_4hpx?^#ZV(>0g z5BTH-enN5!$~2_Y_0@sr_Y*(iF7asjx^6J*HNK96!~&Pc)YUim(l55AQo01o>J@#s z=XaP_U84BWNpkT-a^_-z&j1IO>(CZz&P@`5Scv;fFYI~f=%jvWvJ-xloO7m=a|Qrs z;#qw)a7N~A5$*ZIbvskk?Af{TP0^3l>2<{z3>00nr&N#in)qU*&vhyp*IOnKu<6Mk zo)zQk^Uv1%0mMz^}XFR1j_)#^n8n z!0pB7i;;`NmvlFQ^ghpzz6^4i`>yyk;uCPCHu=W;^kylo%^uU)-!V{VIgMK`vv}47bPY5`$N_=midEbF8 zb^^^%?w(M-f(=iV)cbdXJ&h*G*{0)bQ@m_9H8UIStv_*m=sqyNUxhuahg`|Xb`(!N z5xm8)P4Lx$%352+TJWlftBNCZRY5Q~YrMzWQslzP{`3A>VeROs&qhk8eJt;3 z-r&fFGo>-U3NpXZr1*w`X-z~Y$R;FTDhpaMG4b*}E1lg|Qo{EQYdH7uP|y6UbmH~> zE3N%KR#)1ax0D?}SIK2t9wz#r?ogr#UK!AQpDIqM zHpb2x@`^Ru@|DK!6r9i2neW;2n6&kRgJer4xZ9$by)CiyAWoGO*jEZY)|7D0YtX~n zK{;T}^tu^k2S=&H-Z_GxzoKf7HVyHmyhMxkggi>)zN8p!m=+2gLDVfrkf5{zl z@k=n&p4i#KAA`T@c?+B3Iru+%nwPHo|DB$u^sohvPZ8gRyoXL_HSVdL^BMFPh^Efk zT70GI>Tk;S4d&@rp0wO|FX8Cyn+==DYsYJen^**rXimFs-a+MQNEP1@%}3P7)CY0l zk zPY@H_GHvsSK$7#+`mzp2cgot77Fj6OoV>R-_q(r6B7j??+OyD1$+~?jB0Z)(_bC~U z*M&0_q!YSMhl)!PK#^UDT1yq2ea(`rK^Bd=lYKDZsk^VH*LlFz(o0-aaYm{_h8ov2 zZPAC?$L9R-VP%=|kvU1#EutGY*!GaHFO6LI!x>@#YGu0ADc#W!EL-5Js{QA*#oWf2 z?=DrzP^jQ(=Ph8B1_Zz8xl1YRI&f~2_PbudXo+)ex?hw#^*&Hn^IF%9F83t6a2B#! zI$|i~X6VfP zQ_Jqq|J4lwiN8N7KI$|a5vFZb1&ejc#HKvP-`xTBBeAk^;hy;MNUWLeo!@|X0Ik*L zn2GGs?W?zMCs2^a>;+v!RFWn7oFk_OZ;el^)1sHsT}wY{4ujzvjvBK_dYqx zKZ5I!5Y`T3&k@D!mrCL3lq2P&>jk&*v*u-=kDWDj2SKw86vn!J*Y*L;Dv)pY{y@GS zSAKb6&LHn@cMx%4Q9%5t80{{vWGWE~l$SxCSiZV*isi_=$GOT*wFQ-(ltbxEY3?*R zGb8Lf$@A!{(dnksqD$ScCy)@te1f63jl%Bi-y?c$x|ov9BqcEp@IS@uIkB=FJW>r^ z=S~q1j&Vmw3l8-5G!j>7wTHed4saOIG9<7>$2|UbS^+XH4~fPScSccWRr>ipk))}=4WIZnA~0IAJ8%3w)} z{Q`e6XG*6txrvfhe>`WWi^Z$HXc=JnZGuDc&y3d8LY%fd@cknWwgi?;xKBf6l|Ij~iHtO#g8RF9L)XI9=Pue|}`fTmV1eS~T zkIy}+UQ>!e8UQdVXOlkSZDxxT=_#rc0qW}0YSlRYGHzSCm0gvoZ9cq2dq3wdrO=n6hWaeb8(pv{6w&1moeNxW<1UY?r4NY|`tTg$XU+Ad1$LSzw*pw$BOr5|B{%2;-yvgjR z6xm-QsbPc+UDi&_t@CZ;iDj#`)swe&;h|Z*bV_y?2A(jV>r1e_6RPS|dv-V+3b`fs zs^CCt^T`aSXx2dge3Q*jg@w|*elLf@>VuUass@`u@f$9kiIxC6Cq^zQZXPxHgWyO^ zS?jF$MafEjH_O&S#z=DnP@B3n_+I*E%z!5aO!Ie(Q0^Mic{Ch1@xB=3`nRv7D!?AW z_3C;`8^`EP4vJ55tNX?rVxr~thiz?i{N5Hv_|g!@eoCS(Z&eh3SAUkpYMlC*pE=?I zKX0!CNIJi)v(0wi^Piw?(J6R1NB#uTaQ0(w7u@Y!>AilUB&?4{+$M7WM=7%Wm&N1n zO2qTT-jqSsF@op3l~;{>=1V2C86c(-Q8czNwI{dT-z!|Eoxi(K; z5<@-=ojeG-d`yCvb~Of-T4>H#E#wucd2g0y5Y^r#MTFB2P`(01#(9O+qHPvtZ$(r~ zy-bzJovm#vo*F7nf2_hIkAR!0x|4%AjwIF^p(0v?RT=PHS*2sA?^;F%>I09!YKUdDYv??`F>tE?eow4OP zKHgBSF(6;#KV)?>MOP;S?zXjcUd(E>OX)j$++EAef7{!L9~1?KTaQ%sLiu^U14ID= zX)9ZRSXu{okd8Obwp8<0Ne~r57Y?}DK}^kYT8+f&&L&>t`da}+u@B`5Tdd#@S8w)2 z0~|4v-cAMbx?^DNd+U^CwH^ad?WU0bQ3n$3eH<}PseB7NrQ!2`=U7bIJz++EiPK`ebGIA+V+LVr>2<|Ky2RQI39(#EZnOq5 zaOu=`_5R=AfSq!CT)my{?UI&TDtz~vrVomRG(XMM%!BJC8bTYOfZYo7`cq7u;C!d^ zqt-jWS3h3$eedVJ8L{+KJ-wUwQIrvBdGAR;?P#BKokXecUxTKTINR5k;>Q@}e`HH= zE9A{LtuE2Ml_T<@)bRjQYC(mfv9?$v{qI|}EY9 z6h6WCiu_oP@r9u3P&B=k_89k=D24BVU12pAx0~FZxQ|BEbLh5~D1D~Pz*04DMt)kF(;o&r*n)CU0oQ*-@VXj{h2qFvRJVNN9__<9f)2o%Tw=eeMC;Ud~Qm%W<#~xJo}M`HSqreP=(*j z&qB^dsqQ(W^O$1Cww_qMHb_bQ+IRG>raAQ_eaHVZj{a$5ekxqU4H(A!me0m(w@D7G z;-Ui|nB44r?$m}c(!O|4Zvd)Ih)R_Tjy^$;73|#}%Prg;# zpBPJ5d-md*TQ^SP5BL3f|2rQtUz`g%uy_hF=r|h}Qg`$eIl=E1xAVP3{-_Y>=-j^@ z2x6HyX8*>e3}*j7aVZUzlq@5eAug_9w`*0!mqA;RF>Ek(zzUg4g>6EDB5kt(W# z`vwjufBCuj$FH&$ON$8eFl|bt2`S27-YIL_MAclim#T)(zTKgmBM-5vf97x z-F8LbvCkLSaeb@ycM%nq!(T0Z{67O#v@mWke!% zafB@#l*{|S29$(7rf?&)%)J9w3E49lvd-dv65=P(M41e&CiXG*7-G1&@ZJ+WCw9?j zpaCBLAkGYWPWt(p=P^w`*Uo`E-CD7wZ4+CZIfVYt% zCW)%$oxa>n++O#hQUxryB9=e&N>$csWtB`(rG{Zn^;j1Vs8M?j1+BFF;y>Roh4`%M zd(0umNGg1!XKio=r~2k-J3{+4R!_nTkMnR^&NPa$j&Pjb7V~6@jB)4dGCJ-_%5AE9 zN5ua(Iufpz+y*}e`6%L_ydwAxL23vldIr3!&N;iQUi%_{;t5Z|58C%7A369Znn#!V zuQO+CZCLXx7HOkue0@e=7;=M@J+!oiF~7zOsiAh_>p!0c-OC{o-$97_1@v!!rwxp_ zI%^GI!qTOY!mD)?37!Z#mkSKsOV6zh4;rj+b@i(aB*U>!<6nPkpV_ooQipU>`;U02 zdM(I5FqGZ(#^@X{S)-FKH9)O@#*-8;4{LiG`@i=m<3Cb(HZ5sF%fR%{eCG5ry(>90 zwsZLp-;;a}=a&9+hntF&gJ{^#81wK}Qdce?!(4mYzUj!=kZIkT`>C_9+!LZi1Vq3( zEx)8ids#$RRcLKLKwDfYRIgnPYs9;umPjftvH=}OZlK%u!Q5Z1xLJLVRLEM{YNy#n z$HAWDmI!c>LiQc5{BAN+9D5d6id0-)eDIEnSH1V~24s{8p9-xIG zwe6n=t?VZ&aYAx5tepNC8i^MH94#gLq#_1MZvN?XV7sgCyl$qlW-jrV^0$A{9x(=_ z5NQhE(c5hc-08K{-%(fD{?n~1ev2V{)SJ}<*wmn3&1!YWn16}Uc|v1VOi*jad%q*n zsM?D5M8dTFaWqcQ@S7ZX-_DW!RLI%*HLE`j7ks(z0xx$+A6^b|HoZ|QRN$zw%%&mV zJc-Ok)o2Tin?%Oy&*F$1EKIx5`c+{~RI=!W`B5dcqFs~II!Kqsg+8(zar%SIzi7q# z+jul^w20PpBJ3_Dg+*<})6t0~=bG}Sf2`Ey*+Fif!#}jq?f8$bC3P>Jr+EdmeTpNw zrY4jQnRQ4nc5|sF4P9psxc%LT)_koR-nV4u)plZCGflpg1O58rZ82-~p-2b-77oYb z6k@(N8y6p+_;1@k)hMDfz42SUoK5@(ZXmo=gL{WKul{d-(@)G+WS9HR$Uhbxu>8J! zc%y3mPuzOrF^i1@NZNl))F<+#;?47p;uGz~bs^NCma7&YF3F!v_#p*7FF< zNW5yF3AW4+BSWXj-WTEnV@p*zd1?nq`#lF%?0>y&TDV{0$Qc|S%%xEyPGs3nu?kRj zkicuJfH=*OcMT=Wyl+&ic_tik0oDG}zX4#c+5j|cW$nNqxpyB5>}d%2eYUjcS^<*t zIco|N_$LLn-$DqTMe9tciix~=b;8T4nCEIVS)$<>B!fu&n4qhyo#1FtlXFuaawYG) z%Dc=PHmYk10P?zAMn1l0pKgJ`<>79pDo!aoH_-T@DKfwlKU&l?HZB3ywDWxaT%!yx z!BXpN(_S2u9orBX!2ex(jkbKSr>uwwYV^h!nGntg5{$J-H;7W-9NLk51lnjs{Z zx4IX-hHiY$5@b5V&aXaL&V-tp45urwGJFh=bHH`J?CK;N(lCL~!b`?I*2NK5!S~J0 z8OU+Q8e3a*@$muIpKyLUm-X@J?Sl!5#2s`V~#(d zH}iFQ-coA$pDCl|*gn)k_(xjH9O7EZ8BHx!^`Fggs+s!{gNCzEoHtYVy;~^Yap#lF zu3g^chOw#RX+kie?vPO*-N43N!qM|v0H3CiB}EkYtq<8@R?Yp+iWOmiJ6CZguZMw>9NeWwK_%_b@0 z`yW|y-z9u()or&UT30;#_3v&HSL|7}}Y1dkRC@wrLs zJv%|vy`y}J4B(vjOU;rAH_7U{|KL$hab+dqwx6+fzqB=f{@I;M+CJouccM2M79*dL zd0NMl{uk?tPIbNn7L%fG4UVrRH8lm}NX1ga8OoW54@IxbehsBTpzN70=K_9+`zl)K zo{2hsnE3rP1jIAdd1V;y0F&9^jG=fO|GO#!@J^bi5m3{yDXi<3EHVl^;i<8we+vAS zaKSzjEhTyN6e>{nPHl)$^e!ceTD-|e>^eI%zmau;pfMEo#QbUr>mb?u!*ex~d$vU@ zqN<1Weh_$+V_7YA>z~%SbzZJ|D!=3L>;l&|8x2+=f%P9gi&%3lR}!2Vb%{sd=e%R0 z8MxiK|IBV-svt z@!kd*p`vnW#_qk39pqi7+-$@$?CGVwNasAm`wyxFpiMX-ono-7r<|cI9anzKQ$tzd z#$}E*egU2XKq?NklYFaz>RqZO@x66wqWth=W_~W|{DJJiL9+cxX1lO{3~cbkxkJ>M zBYH69OW-eo3-g87)N?*YCtMi}=Q{iHFoJr4;h|M&IT_F{$Z|TZl=Ilh6zRyL9QqJ+ zY^{?0@9zRIx{Ht&dmoRh`hJK**3_?$uvh^oLg9A6UCE@*MXWEfW-!EH0;fd_0I;wWm5H0T0K# zcv+TZaGw+BaGUYVyV`#B z!YMUhqy-eJgDj$)+uY=3Q(Hk0dW4tQudW-8Aa$)p@La3=wxF(kn~X9?rnm`fR>%B1 zx(d)`Kz{jQ=G);-ca_~4boIIEj@@bYOb6Lu)QL}WEJtVh3;DR+V4&4!rM;xcgT$S62sHH z_DlM&?!8BqA%rEZpOJyAs<+)B> zD)W-az{Px#B3lPNOoMGkh|s8F*F<}}ypJgl_<2=gBXL6FeeKmA!Im~T-HDSIxWDE< zdpoK}ZN*oP0qH1(<@x6-59!(lH++rbea%3uRvx};K43%=i1lRGY(ujt^ub2`MPLu} z@>FbpSaTrXGg0He8Yi3!k5sSre&k`C;!~zTj|_GxHEdzJj$0xVf56RJuNfW9v{4y+ zd@1AIymg$$oe`d1^BMm4@=OFK%yl-cNMA+gZzr!SI~nQ84l{UFjMppLHUnO@IV8}C zb|958+804bFs8o$$4G~45*YG3{d}(|^l^db45w*at@2FP0yu@ z55r)(V#P;WB{ULsKpRCsa{SP86|IWL@qc=RN`^vi-k;MQs<8qo!4N`$K)icglHL2C z6dLcYI7czC0%kgoetiY{NZd5)sS9=wAyXYeBDeE0W!{FuGOU}nm4?6D;P|EN+oDdw zuDbAwrh#^#gIUI-Q*GpQmIrg_qmdWFD;t`sel_6yqW6>33oxvEs!Hphx0FX7GcQGl zL?1gd7o0^n`KLIH8bBLuxd(r@dF?(IJ9yx^8xV7?m@?sti%S%NXSZ-$5Sv%f{cAq8 z9mNUIZXvo8yhgJ<8e)|0pzo~t-VI+HD3t%gh9hj5q*7T)17RKr^rKnA_U=Vfh@2*V z&7tK(HS_KZh2-~gx*}Agp2xO^yySPdYtHZax4Q^<&97g&j_a+j)JU8|>Uyw}8{CE- z@8BL|J*AfnQDgb#kZ z`QLB<`DFKYl3Y@dHLds8f|Ec+|E6SnQ> zafpMP5BDHo&5N?yo+bAy1dUg zc{_;$l>q@~pfS>H@n-beFxU0lv|JK7{3bxP;_np1$}_oDwgPHK5t^F-F+HoFjz&d(Jsj1y+r)+PUQ}%(T=O+~7q%E;dYC8K( zQT$T9#6QDu=max88IaG#3iQB54g!Q>P~Sqh@WQCkU+cTjI^mK|+sDzgbw<6+IstMx zrK){?R(Zxfv?iP}R9x$&1rZ+;%)$j6SjQyMCVxAyUOh5i2px9gMTR_3z4Ag?WwLKw zW>E0azA?+D_!cZ=0%`4R6IklG9M+yAm54!Fdi$9xJ^codkn=TQkTT@p_ONWT7I_zf zxKuAlRMI+dsWB<}-8Qu$vFg4R*E+Q2OMzJH*-ZH<`_j)+>K>>PlVDfxYaJ@G+mRmQ z@!L}0oICE~f^6U&uJ+ufIwC~^ID8Z4Rx(=|xm(vbD0mCA3cyxWT3-vWf1w>zSZ6DX z4j&fHXN8|`!YAtk8|q@CkuW}NKB?p^1w}mp(6emA#e(0SB~n5!0ncal2oOMUDdJyU zq^Ase7a=b%XQzz;;{bK-n?}Tf-%(=t^=xI-Hp9*yjL;+SZtQ;rXkc(LvYJ+D1VZ%r6$0bP9{6FlyS6I_o7d~nk zWkykE#!*DtID$wQkPeB;AOZqPi*%I|Izs3nU>RYgN`TNoq)7`BN~DV@B|st_0wlB$ zNJs(+0TN11ocX?s^E~JLpL28WzFd;RZ|_z1yVhQ7y?w9#5;e$z|2(+Dj;O|-lG1m- znwfJ{!O=Ux{g#?++@PSQr&yhYr?Q? zR=Yp@pe1CqWYf!+dcz*lV^D|U*rYk^WBPx*yF?E!kIzZ1k=M+_zWGz=x-u zEcN>a(u*9YO+CjaVgi zbqlZfb}emye-J=knCM0NmRG{W`^<_-nMB3xnXH5`N$#hfxc-2Sw-&X2)BK+w`WI2P z6nyd2&3^3Ov#7rB>&!6s_>ZOc-evB48Sn40*1d&F_C#js5*+GAAb z*rlp;nW~^&gXpZCz*C5)Pr$K|7UbFM$0WSv2sW74-brh>N|oD}wCA!+x8p6pmzzv{ zd3bn5CwuUE?oMbxe$dn+H_`7)Y`AMJ0Uc`;-$4nPH8A)idzwDMsr8BPhq)OYV+f_K?yg)oE`QhEz-`x)^o>~sLwkA4e?-JY6 zr&Ke;d7aZiB2UwCCXk2w31Xk0z&|Pi;6F|Le(%$_Hy1CI{=}S2`lowb{vXDH1YT_96WcD5wjbZ;5 zd7S8VNSw$P?Ju03h`IUYxL)B=bZL9fcBpa}06Hgj<7sMacoaWDAOH5R#xD=sm2Vu7 zlg-*39dU;sN4*xmvqZI%ff)w^_YY$M0&NuiJebaM;@*$F)JKVS2lIbnvI;5=Qk5cX z#dlD%ds(21J?g0%SgkzAy72RAg9NZ-W=P=q0FW{oL^BnVgB_IF^TOrC&OX)J)e1j0 zt=8K}pu;|FC9a5tlHpmw(Zl=j4SpXx-I?cH7!*=jnck`0@dHKMKBw0O)2`fn#&&-% zEQqlLDh>5F{h+2jh`FZbc=0zZdjK>VzARB1MvZdH99R)Q*M|G62_LcS1*N7mU4b^$ zjn6eF^|%^M8C@BZ=o*ek%0-D{8j z{N^T>?D}ngb{WwdBl4j^qRmUs*6b&@Pn5NsF$q64DE` z&hglwP6gtd5>|sH)(v$}-m9U~H$SG&Y}$yn-bkKop4hu2s{Z!L$=!{N?kEXtBD*xeSK>ZpeNyDw}I)pzPa{VXzvtJGWwK!SGG!)LS z(t6p54@F%`)BF&%vdb^l>%^|@W*tQr|CK_0*F5Fh*;Ck=A{a{>? ze#b=;?Ml6Bs}t+&**$;V}bAL+_?2zV$j-U zz^3EC(<_N*o6q)v?>@a)oFjB6+K%-&j#TT?oYY#DH2<;wnTyhNiEJD_rGeDk+F{)* z{=GWaS@uQ{wbI(-VSCumVoFhE>*(1>Jw@Ybye zN(?)_&GZh*=W2w{UrELl_I%lxw-=Aj-_p3bfat1O{P}8kh1Msi-uvJegmZ5`E9l&eQ7v73f9>}V z6~4a-jO8NYF8KXowR-&9*5x$=K8-XG4j3MZ8Z8Tdv$Mx$N}D0`s9a|+RSq*av+YgLmN-SL4mrZ zR9n}Yh?Px-`{#6OYe&X(7H_||c6lX(CK9vodQQo=y`@m*UIk+zRL5nbskYsVQ=fgE zbRg|l-dgv7a!4_!q7cZhO&IG*p}P|H8JPHXNO$cBUQbh5BR?N~)|4wKo>5@~3$hp} z+p6*I`^YQ+J1#)Fv2_XDtI=6AqguMf zs>_Tn>t!TNj*Konl-*td_))W_X&2>dkZ3lBO5sdRwMJIymx?)rmLq{*mRND+a|J@` zf`escM$BSYS%xTB0AF5PpvU~Q)J8O+A zA?+N%+{P>hsT?4E{`_%xH6>8jNDLBjcvym+*=XO|-P#`-7h2EQ36kBO*c3Su&Z4~f z*>$HoL27#EZ&POfPBH=YW6VZfCJF1R$qTiM-u!B@;scSuv_r6%-Qrl8n(^%LVIW!D zfU2g|-(uJ~xb;=kbOu{fIOIZTX{2|!TQOhF@H^ec`!T(>Lqta>{P-!F_#9fsIJtRs zB@fulnpw`BOxnrc4QpocL8NI)ROvFeWd>XBAES!15YIBEhLH0rR>LE2eVF2%7~A|o!lrBthT%NVfgw7rjnyW*}T!1vxIe9j1>PQ;4%Mn&lGcU}-~cFWiR5X}+M zeotlZh;zZs^cDlw4D=Ad+bO^T{Jr+=CGvpMl43K;1}Pt<&~sB9+Hf>qq-~_GyshCF zY;R`}*j&sZTMX)JdqW9}X^I!DsHJk}r*dAg=mV5y9J$Ugja9(KD9(7*PVXP)WSlMo z@+yVu*xGD*YY?UBU}sldzXMM8v+B&0END;-- zVwSD8gqhV~(Argd;LF?4g8pB?DW--Y23Q`UM!T*~y)mR4Wigr;#hx8ND{RobW9C4g zp&!zB4B@_*w*8%V=e3_a^t*H8Mu}=aUV>oaz2Ki**pWMv*Q2Ka*~=)Hl(WK2T`Sdx zdC({Qcw5U_wj(c=CFrA;A(}lBl&>w3q$yR1F`CgeQ%K4GY5y~+R8nncYicB|FTCdbUXLRuUcaaH5uwOgw-Xzl= zLsfgWmoM62v`fb}XXp0It=q>7=uMD;wr%)I$ebUUtRKlx=rbMa_2{IO9Jce|qN4Vy z@BPdVCr@amkF5j%wPj-<+Y1i2Uv!-zld!rt>Lpe_&OWLnlQ2-)wmlQc*%dtU4IIXM zYUN`Ut-Ckj$Xys^p>{)~p^ho53x!F_;=&)q7vl(L5}_@rIJ(KC!=&ZZlDW z>|nt+%*-Inq?op5qnS0uxkeddxvI*hhRe0y4mLJx#J4nmT^~{Ka-qi{*0jdhj{PCA zP)kK%GMM1(O%NXqn%4Jgx1DUN)vv%hTs0q*9Jekj4zW92ij{{n(CFyyM7y! zoQ7sPjWz=1tQ;nr^N&}htso}uRmu&Z!Lx89QS`yV$}4!8-d>syb4?`w=vTAM**DVz z_ULqK+q+8HgbRw;nW~nUti^bDUTrVT*>+mwk*}vSLMA3C#GKaRUAeh!=j3O6a@=h2 zuh_@aTD&ej{M2+u(?q5zgBz9%lN*YOHCY;3W{KDtp)5LOYCDnp|hx~@rV|yK0CBmZLRI+s;eO( z`HX{!iDYX(NNtl7trkvrDcz)P938@VQH@!V$3}+AR}>_7OHF?pLtJL(5_M}L_&N2^ zkBXy6Oz?+mzQ3N##>QBTXH8u(D?<4oFws` zWunA-l=9W_iYigWEw*dW#J3LVrsh9poTEBv-~~*BySq*#t=-WC8o9@}i~AZ;yBkhO zy3obFUhQQ`lF581M2~21EO29+_bfK;`G6RsLo*^O9^U3Ur1x#5RPC$4kGodQ?zi?B zq>D}b%NTKWIr#!8vLu?2PxB@0=u*2a(R>HD>L0^i*8fP;ii9PzTKyp~1A|KErH}V9 zhjMKFhw($`#OX0T177d{E!+dX05vrGh zcYv_l0~0A3IFoiQ9tQ>^KxBsx&;-=^C;rcT}z#jX$A zU~AFu#ES{nSdvGMILGMsi(^8BKM{6z!x%$q=djq1lse9Ug;Pf7>`GW}4w<~C8CCx? z&DWu2p)gTKl*r=BcjSqX@YmV#- z9C@}Fr2Fq2i~ zNMG1?t#?2CL_I5CnV2;#kB(Uh|Jq^2e2vTJZ>^dFZQ#OkXF$6gG2G281)U~Ea<^h- z`&@N9tQlo8SQ<3j)>a=})eIY)ahjPl;Ex?$n<|cutf7r*hk~E zI5fv*`J_yi4Q2>)er7k&BZ^Q=Yw{SgvWkqFFE5cU)-D^iI2` zuuF_r^4)1f5NW)3Ii3p4ERIDE#t?!`C0w*`FgW{@K?{E|CH%nk$9Y_*Y-^XaqY0SM zs=2vFaDBH$^BI?a}dH%I9scIB^*J-AfIW&O8I-QnNTUZ}p zjYE29Zay|5wl0MSRZWK_jWwl4e$l-1Xi~jQ(qLt804@o=|3^{{8c2_Xb~=r-Qc#o22?bb{IS6N_pUojW}xz~SnQ_> zdgY|?Ae)JLsmX4HE3%T?FY{D5U)o?j`+6{(dh>h%7WEx`#T0~CPUth&q9X4&q);#E zZH`2FK5>s`Cv^@1vG(~=%`p>%wQfsHx;JRA-Qp7Sd4KV!M!+3F^rlljaIfp#{*oVP zJJRXOa@s*#Du)%vPAr5j0O=?JfFKMuYvDfu5Jjg6}h{suo$fUb5ab{W8{dE-p#GHm9*wE)#7?s7g&hzRmj zi?4mt?`Q?O<1_uGl9n4-7X*wV?eB2QVp8OVhiV_xbR-S!+Xs72JZbPH0k*Un$PsB4F2501Xg z5>N)}vlUa8MP0bBivOXAXnXw|vy1T3;$p^v9iqobN0_*WFzY{RFo5#l)EmQ7Wt=k% z>?JUrN}lF&RjOO+7XU8p`9tOa4_~e(0PeHmJNTxeaA*>#B@@8t`h!hx0tZBfPt>B` zc?2o7&_Cy0Zw46UsVXug>s5id9GY{j&-#a)YL_N00T(TOVl#`rzlx0+NUY5(G`7+0 zpu47c_jP29N0@dmi*=ZFrjNww)5w#RRp|#E6~(eOHO4mMN83k?pgKK~t;m7~aOZ>G z<=Lua%to1!Dx{}pPN`n4brcs;?wFR=iyVr9EfuOlH4A0_8OPnGk|A9809<$jKuhqT zEjOvu80&N<@zxbH-N&bDx59OgQ?LUIu4(0rU30iwVd@ntpvQdZ4owm^Xr~>0Z~W|x zkIVkR{y=qaSgXFSRismE6J@iCWr1EVl*a0p}YtQySC$*dsQd_+~}GW2G)%>1O6gTq&D{ zq)_}j(IJC_O9|$rVTPo!Z%S&tlYrslw5 zy!eY(mNP8-NXtp&Jv}nsQMFNF$2ls}W6&Y3f_!hl%Sss|&US@RP7qhW>RYE`;!9xD zFF7v4v)|bD5gbaE0fYuEU#_0bcBp4(pH@8WKg4T(H0hvFDr72KU)5qV%|N>V^aY%t z(T8;1dS;mMgfbs(-~zDAIS5e3Y@;QGqF6dMp0*uz@B&MAZu6s@Z(iT$JKEGjOgUJO zxTM-Tk`_&f60Pv^zw!?sQ-H~Y7pN$Q?Q97WUFF`T=CVtM%yp8Uotzz7nL*b_tFxit%1uAODK}8mxH9syn z@UqntY%xnYi zQYmLj$JjI(8JaD$pq-2wEZ`Q4%{+~Q=f^!NO`p!T+~d4~AZcw$W0`aRGGP#^Pn~27tLsdE;Crk%SF3LZ`mF9FuQfQ)M7+#JJ-?~;}( zGf6VU1BQ4s8kbeU`67}wy7j4%GZ|;Qo}n_4Z%M(YZChKXOZ&C@8_2r1)YnH#+z&Iy z=IY@6{$VzmH$(n>xu;bruYi?;&w?uL%_8J~a;7cVbz%f$Br@2i53d-jE@lm|3hLZB zLr!7ap1M18IiHHSbP0Oh5xK#@bj9#}K@Jso%Zv$Z}$pIhms za5!db49VqODa_AlnTX&_+L?KIdQPp>+N%W+jv4uXH~R3j-S)VPuz|AWseXY{7x$o% zh8f6+MyCU3SDmxr4zw8I%u}cgo$JE+!De(o6Kh8qms>}2-Y@kpM+Z+9NLA=ln6u%- z3)5Wb-JhDb=#r&Z5>#1#S!6_gy(2rr;Yw?|eX{|R7#(Wz!VOJj)hN~6oC_p+ty6}8 zB^S*zt?(IN0+FdE8W`v`pBnfMyt-HnA- z$LHeLw^JZ}K*!AT!?hRH`8?jK;nlh`XY3}mlyS9*+ifO9m=TEamS*St~E zmfJ$#=XiNNiez1A6T-HMAVVjYAWPRax2D4{C z1%4<(c=I24zR@%G{}spI{+EUF?Hhie@%?r4@=yPAqCdO+>wid*UrBxW=dB~BjQ`zAM82Q_TE76ZxNs969noSNp$H1!rGGy4!2z=)np$KeQ7Fvd-7+Q(#0p6g9+-616?Uo|zmtqPy)&1J zAS(5hg$zah%bvDsTX^)i6&ryjeOj_GW3f81^`ij05n_he6Di|4UdJ_ql<+XF_4eq} zKmXunKVGvM$BbhPEIM@BGl^T*zD!jIEw847gvS2!3A6^%E@MKtfVxSf+5X3tKA_pd z|FQ(%{N?{g0B4On{YRF zP9~UxqhsMz5W>-jw;8wm7D8-t(iP|ou#D{taD%(8r7#9#dkoO&)7$Eu`}HpFUw2$o zwT?{r9rOtG#Wwqd{GcoS*QrmG zd4AjgGXImLaReJZqE*xId+|GuFNjGSM2o_}sK|}OH-chLxl(qz(f4gNQl{2?ByVuP ziG?$HZiF?S`?Dk8u5{z{)vs6}O^q(Gb~nRTe-1VI`{;i3S-=$^OX%mmt%|!7n*4kJ zm;gMpHF`-vdOGV2WYVLsTL?RU<)jB}iDk!ORJqt(o&NYF1TQQD^FCSf4jDB-%!ooo zxqu&i+xoJD+IS}%G+z$P`=$4c8EwI6g)=DEJV<>2a=lQ-+bD5gSgelpwTcx7Wl{pK zjK)l5`T9nynj!<#KOA{BX=r%xcB_J(7dBp?0T8!?moYli@uNNEZQB_NoolaX7{=an zgON_8g+^lymYG|?VDodsq^Vo7RfD0dt|3w`8IaFDy;mAQOJ;q=YDkIKh>qyQ7@%um zRt1=}Lcdu$SRgOdJNDkCyzPlh)C$>{^IsK}%Va*QU=` zHxpvz5V^2p8odA3*JOk+3r|YGhuMQh3dU0u2l3OQ>(!dSTt2zcQq{WKEfTr)jn>)4 zeocy4VKUt2)(wJI)qBeo`ddl|>Tw%I4)N96bb^8wV{NN{LRA?U=@7lg$yD2V$r$KM zqr$5(FRD7S)up3ssSfIZgE$#uQ|FdW*72aBg?u)sRE=?f4Hc18U?W_=LPExR1)>t#jiiXZ&T1 zd!pA|o!Hg5&!UXJ=aPE$RLZN83V%cU3*yb)QFRJeROva`6TBgpKV-kgHaiFhajlaE zKLx%_lj0Ae)-y&0PJh0k-=ufESE(Q30@H~Jo?dGe_Z$-=aQ?8>XFZ+3&25H?iYr#J zsHZ!dNyJ=xgxipsqKImR9BKT(1X)hpxFu^!9{)rd&kwPkNC#U{d$uZO1J~ZRyN75D zI;EW#QpR?~ok?CjHlGwQ!$wTnrx}mPlS!z=ff*%{PSXAZ#POg$bxG>}fd+Z`7NY<@ zEPM}-MCQHmeYokil7DEWOEXi`IO=S~w?yskmSc$_riFv2!*~|hD#M)7#du@}kJ7%muIB&n@(eq0_^r)`W;d$jcyj%h0h z(GMFv?iC=O3KJ5|%am-_-5WFOE1MuLzp6#5edJ5Y$&ejeZTC?wpP1qRx4~6Y^7h8s z)r~EA+1Mkl-SIN4L6RnT8s~h^GNVPPAU!^{GyuF+t}ASU&nX<>KjI%$1d{kz*ED+H z6Yr2#5_49ErHoccmooMlrL+a@d=AD+34p&XWH=>U4=^*FJT?#?C(N*w6x{18EJx|A z^(AoTLL3a<^mq~bN5T48A&|`VP##?^+61%*XHtMeU-k|GXD0J zH_Xq@$X-7tIe+Ua$^rC5Ecn|k5xwvagyT(FngPznq*yVPN^18oZ@HpghmqYXrP~=s zmro&N^mv(Csi6K?>Wm1+SQbzM)x9X}33Pc#Ad06;%sHPjm*2HyahFLkT?w=C7^2!v z;-?{M0ik65A5Mw^q?bCZ3o<1S4O3hK4Gdo1I=$6bX;Z~)B_8;vG(A51Q_MqFiDLg4+Gj6BUJ=AUjdsr)|&1^^qNxUhN ztXA;SJx+}9ogA<1N9{2NM{+StW}lf8tNXa>{@`=3yr!$lFltd^xC{BVkzt69bHrAw z9Xj0PPJ!cO^Fvg)`Gm5*&Yqw}rIBRb^7L58-lueFuYd!yoWpkV=?!<0?1L1*@5U%@ zwzikUZ0p{;HV>*;;QZ<@US3g3B%3QEwM}wYlJ{=rL`R!W0GbYnZKTDe)(%d~AIeeW zX*jj={hA;^LjkIC5GR>)D~?eJ$e`9gwu7D&w7@twM%{-@wOT7z)T{H3|e{j zDB3f6S0E^wIG%Hlsw>3EU@^JP7Nlub0VVS49UC3gAE&W<3(4fq*ikF4^;r``KT|&Z zaH(xhUT$N>L1(XNf0>Hm&wU#<6eiUKz`P>U!OsaV=-}pBiX19$qyQl1vZO zjQPDkA>uF>IE0^`LqEnhc%4-I=IZC?7r|#LXOWa+wB>E9udM|PUS1G_ID_K5jOddg z34b%&NP{fMx9of%F-rIE_?!`>XU!$hXk)&OS45d_9A^RBVLdXj?^dw4KeAQ93Cxi& z*BGSjw`M?G1_9kdea~86rs?HUhI5bqT^>qRKdZa*H}cb4Y2~@j-=`BaZ6X)wqNrsq zYOCf_A+Uorm0qf0K+wPLBRRvx3+->ksk)8KOYx`R#Sv#N&rgvSZ%*p>&p2e3)NsBD zO2piSXQGfQ4xi)w_>41&H}v(Sx-AY_V-gy2F~MEE%-aV^c0sTm(2uzZFE~Y<$D24t zZR6F^Tr$bBO?9uC4?Ny)Rfy%Zq~Y$)_47|B7Hc0(H)Y{wO!2WStSIl}1E%+U2FN)( zK0banuvgr!!SjK=z5SNfMr>1E1xv~Sffoz*aIysiI^Zt|5tv<(2dFZ>tK)WilctV( z<#&eGPK*bv|0Q^75?HIZ|KyI#Q(t@ju#x_$#O4yc&Pbj`3ydc05>1=Ur}3Z zFc#56DxDuEO@nv&t9HA;qO^~OBVPm)B9MJj& z)O9tzHBDbPI9A|xhRqq1h&f8b5V2!_#dKm%vV%S9=P*i<>JFU>o%WB4aGjft8=26P z%^0^?b2xb7{Be!64%z*OpH&Q8j%)1?XbBvW1Dsq4f z*lCX947g~-{op)Gi4z^&93GZ6jhN6l1Q&EZA)R&K&L&xa|+RP zx9Er5{eXiJ<7sfXUu?d{RhWDj&(_ia81!WFpj~q%BaZB5f&3g^qrHru?lgu{|4Ij4 zJx;lEHR3YqN}PX$c~7i+el6^aj0GRuk4f7V3eyim5ssZ@y?)nfk$5JNnpuPSH2iXO zqCw9X?7~=IAQ)R}hw}U+n(XnnOPa4`N#;miAo|qwZ>>Z`Kw(zes$Nd}6xQHd->g=x z&iolp!g1iiLZX(xC&n`4u-MA-?}av255QkaI`$$XyjNEh#tqgNT(2tyI%hqQ6wi;R z7^rP_v{d`ezBw9VFxs;CdQi9i+@F8{w-(@_i-tLy9c(FAuFMB!#g+){t9=sDXQ)gs z)@$oFHQc!7Nh{=;&CAhLo9PeVw2p5hq4Z`M za(elwG0)4GDGLvO(^%byT<_f-tM_4nN|wyP^)qddG#mj-TCh_ z^*&3D`khVZ*WdEOLIJ^TH4)K>7@yuF#q>oXaOV|)kfAfWF-^phQA<|wGAXVzx5rDq zHQylAEzmjQBP9AEWa=jo_E%zw_g0o-Ev%niQ=zlCD)#jD93o}6J4IKmJH>R8Sld?* z+IKKUXW%tTJlp8o3I~rPG>{MT4=gUWjxq(>82kLgGJ@;RX=O*aZ(E%Ne3$y+^Y9w= z?53~nP-kc2M;&ovOwi39r@f*E4Yq~hP(t<|2xwMV`HKi-dTk9f14s0bN7}Ae=tp@@ zYV(K9)Wo#OCW{)6l(!yR>Pa%Sv$I1e*u4Wk*WbqMFffb`^jqo=>RL7-2Ce47#-u8M zq~F6jmE%S!6mHoxw{4@lwMlASnb%fF?Y8whnGE=y?+5?X2`$!78ujn51lxxAmDeNB z$QSgzU=`3FWJ^t7=MDR=wq&?KsgMYg4ajTD*69lFAWcenC2X?&O$bDiFLGOV-W8sX zdYlc36c^LfjlOUsat1DKYH4eWsB`Vj;d{-7jggu9$c0L&11lR%?}2>}S5sVp{5I7q z%;O+FX=W%D?~n;z2&r#M7rBA-A9%u#SHB60=~q%iAIw14dQXq04kc{o21t*q9Z$Z< zKlt-&A9Ha0$%(2Yn}ZQFDu!#jO-mKm4R&YJS@hcoTlwAwevd8v5>WYfkF_E2}9! zb_giQ1Rd^nREIaP{(SfVcC(V=ZDpq~*I3Rrmecf@L!Do{Yg0)%JM^JgXwGu-ohUdX z`25L$^`#yU_wG0$BB{m2z`&r)EEkcW+%Z+Ed9#+H*}=;75(NcE-!y>(w54K#h?}ye zOlC3KebdZm6x|fns+wyvCS+1bS>FoNw*ds*h$u!&Gs%ZUVy8X*j*MTLIY&WwqP250f9$BOIGCz=O3rrSu=!v5ri*_;L9BB>Q z#QR1iJ$vW(fT6g^s4OT*mnDVJyqx&qQ9ZY}N9f2#&jX5Hr7l#5SEcV46)UEF`DO3; zRl7p>9biLmEtQ=kx??MO>}EiIrwcA5Tc7jm4*^mCq~!>|Z3DSef@1knigwIVhWnZs zHTcw#n_WMtJi9%eUe5eHCvJBzwdm`ajyiti8`OD!A!QWpZD@PAXR34ji1XJRK|_Ax zNv%(HPJcu z|8_080kwv3duI2DuplZa!&$eTVqrl{S9T_7GVq4KZS+^cm7T|BT-quvkoz+ouV!4J zTaOsW<97OOz#tNi!Yy?%L`B;`qYvg|^THW;8m67~cYM?)FMvVlF0P~>q{M7Hsy4s* z;quKN-#&lxv$RCI>CyA|FF!inYo+XW%c8V=@qB=Q!_fpog%TUBucAhqu{CchZ&v+Y zbLza-sYAgZd!PP!?6zFyk5U(d@(0}+M9FibW7!FO9HhC ziqEW!n)$Rdx5B)~8w24bPCoJ>xF!rmRR-t2<1VnZ9E*v>eZQ9e7Iaau~I znA?33*pmjc=V}OJv$V**`?o<>G*2Wt8mLb)GFHF2Czy60RzIbz&GkZJrS9C1dvZJnVw%^& zF;nC?l=?R--RZm}QM3M}^UluSBSkcl)OCJ#HRq-iq;DYZj@KIQ+;Zw=j+qVSeMo_< zaZKKfGf28wI-7~ejW-W|kD3?5TN~-4Et1EJUoJ}7745j@Q&g_qVd-3wJ9Jw8oYnGi z)RF5fnewY!Uy!Lib$rTnmNM6S#WeqW2eAsKW34_cITb4oc**H&z?#n0@!ZPMEOU(D!+DWq-npwqH6%&XUgV)qk!4 zPpmU;xQ9MwcM3PzI#rN0+3bS1Ez%;WQ;PbBF@W;}_fwqBhr~tYEt*|S4;PEk{A%AD z&s$dB6b3NYD%jHaUHjLO1c&zt^lRVZI30Y{tV#S;+(d8pIG5@wP1&1&z-}wPuNiJk}{ym?Z># z;&WB0!_KNqwm!vsZTr4%qEx2881`2vzl5R5_tRak;Mib0Q1RDBu-l!bri1W<;#iI9 z=&+p9VUdy5)S7@!O?RG%eIqbd$a5F0?kU|KW>xH>;6d0K&O5=trk zMaASzDUCF;;j9F!@2N5b+nFD4_~G@P_+&eQpMLoC@rbW-p7yf5sT><)?A~#x@qXAz zy2=r6)fl;5R>^tvb^DnFccQ$iMN|jn!tNkhI^!KerVPG( zKgCpRr0M$EShyHpj-|wOnc@2`+T)kI>>4qLBBEbd93I zvZePNoJ6LKd}yv)5#NN?SU0U6c1(|OY|6b}M);p;?sr%^m~q~s7Nmz_5TFCyk5Vbp zDS6!Bj-owO&f(|vr}#A0xpH*R*S@8|Rpp)RmMG_&!r_i^L6{ElffEwx$6JyqYw=GR zU)8nP?_i}rv%MET!QB(oZ|Sd1PCI6=@s&?{ULJ*7-n^SaYQDj~T8OGz*;0o6X3g8J zJpH2QM7lm!w!cV-o-Q#Lw79=zK|XQAy+o#=@005q`jvmX&Xu>q=pXz<$lo850HAFx z_dP?{)J&bNDf=5JjX?C&2v&v(fM0Sgoc2uNk>)>AvSrPsp^lZ+g0vLZW~HJ0)kz?B z)BG;-?#b=A%)fMAG~-61PAr>4Z02y9?-yYRrHk|lo7b_)Vrt{E!$nyA&kjEZDX|?l zucUB$Tcl2G9m2njAVieU`)n?U!+5`xY7|?2tP>GXO}rI5dJ$Qm79$N3=m*sqLCc6lIm{<> z9?mZxmIx5y>cJ#DO*GF6=I;pRMdDBXA^V?-<-vcgyu@`fCg)!VQVQKY9n|zR zYmnPwgNY-%Z6-BYlwq5C1smV^2@A<>O8=0#_@L@%!gd%yh>{v?s@ZP`gGVpk?9XS6 z%;erCN1YaK4eq^T+8Sig`>Vdp4{VCH7gGw25{|=ba=gn+=4H`ol@SGRmrCv*TN)te zwYY9>(&Q$;RMbTVK4Ilq{XG_~i9bP_zG&lwi>cXKi#HCult%it$G)pD*4X(@AS=>4 z-av)7wOa7HNXim@6yY!Kl@B>);5|<(F%R8ao}!d=wP;%Ce?(b3`mVjM3)D%cjS?zP zPVPLb1+ew>{?G`%-lq0GF5ZAP-KqKhfm;07PFL}zL(7WTtw8GiCKt$YDV1+QhP@u| z18$ECTLxFgN}Z9q_K#36EUiQCu1yp+jQ$-x%$^sApVB0>W|TXQafXQD?XnhV+%~ELFu)vn^;!k3wHi z0t6y=o6X9ejNUV=gXK{{IC$jqlJkk4Pyve{SC3hwPnfG$DFbvreEr5CYp&ak z{bKxNmbr9MU+nb))m;#Le-S_9!&NcXXyg2-p1{EwO{lty8wms0Fx4Jcb_QkQD6HCWDfBBqk;DJ>d zNi;O%w#<&by0W-6yB>b9nl8JpDKEEmAd~DhmdL1tpHXsBeRr6)SwGIAo-eITm%h?p zQ#bjwBH&#;_pL7xyK!|n|49{I{LNXu6CnMX374YHIP4K)>dWp~t%#3fYR*?ChEOQb z#l}OUIyU|)Z}{Cg0VQU|AIjx6>Ca&e6M#E~0krT(`PpaS3Q(pTI zQ#_L6(~J;7AZyP-qJ_fT4*BGAnsk3fD)9A#uT3Uq^2jmDl$J5#EJGLOdQwIdb*g0j zSqPRrtF9OP6ihq4+V4lOpUEGVF^SXQrbk;;E9rQ6$CboDwQP;(jWZBKoVlly-=#eY$P_LWGAA z#w;WuWUR#!-4wBsooA10=>$>mNg0=jL`Ahp%R=M2mZD56zRW`)u)-P-@0g;ZuJ(5& zt^iokTV$qNaP_oDK;4$J|9u7tK^ZEay_C0VnR3gY-^p~g<}8cSh&r&Ke5#6;KFg?B zY;&)ieK_%dw+Z{f&442Jz3uSRqPFB*?OQ{l-rn!>D0uL#X$Lq5-Rq)$Y z*PCjAn;VF;~~k?*I; zWj>C;)rM3(X|Di+<^W#znVT6x2)O}i-r=+Dbb)c;M5(#HMRm)MHK8A@9UsN2q58Llugh^}}aP=O6GeJ&A(mKC-vuPZKfl`Aj2bZ+_YgSL^RRsLire=_+*ng1G?f;xIu zF=4*@8;%odNxzX94K--fvoZC;N{ZHqpVqNH(6u-Xjg>XtJ>ue=0)J)V7yYUVSU2 zZCxkxb~4^4w0fbvk_oKychac#h?bs>EVw8t9Qi=vTjiNX$@}JQ3ijt`jz_vdUPYnO zv(~?5h|d)r9}iIMIKdAwE*Wb<`W$4wQtApDKr5rK@z>RFNn(9#i9$Xy#wey)dlPW$ zNN4FZKW#ne{s4bn+nK@d8S@Xid-#mtvL*yA5HZ)EgX%Qw9)`mjuNeb76@pJ!8&KtO zA77Mcyr!SCVrX8-^ues(p4$w5eF}RY;1`t`s<8ULJwc};cjj{c81l@az48eENp4a- z8cur;j(v+_?h9H0TW3p|j&TK6Z^zNStzV0jSqBu|E=%Qzt^WNSncfscRy^a{|JB-a zMm4!?8_@$Q&4yA$EEFlip@C&;G2uKI%oe)}3LKhHeiZl~K2@sSP z657Qilu+_M;HdYUckf+iy?5uwx4xCk?Afzt&u-a!5)8vL+l-HgHGJgN9>kSawOkWR ziucW1jLqfg7BF=zepjj;(;Hb5LZQ*ul#y+99ouQHZIA!bH)yNCjsgnZuj9)|?R3|D zhC#KD>$Pm>hHtu|!B<&UlPgwO`2z2m?x43!lGQ0?L|h4u0i1HS#O5DI^joqqm29^a z=OZ{76o>0%V3r+$3N*&Ayk-96gxuyMORM}^5f<4fOMzU;fadCw+FxR@3 zjecu`cA=2aGc54}!BrTTVl`db7*AJ8y2g%=>ik!~*)_GDpu17a@r4R(O%0H`cy~p> zuIJX>I{!VoX_g$K4Cy3A7{$o)7+D!C5}aG#i3FD+v8giDhB6-;Z|1~UlnjiAQ8MOI z?Mtu@j1VPaWb*w_=_|;|6odKP@o+GXgxMY7iE&H=`cZg5xd59B^rDnn~_{&T&qCZI)oc z-n1FRAs)fa`=5GZ5a??b;2R$brDsY!UOmJBCls1QCd`!(J{MNteS=eG5z|oFojE0S z;0CH~D$W#Nn1~A-mYz`=_c<;pd52bXHWmdlZXh~K8pdG}nKw4!3PyLgJ_EdEDSK{ER6i)KX)t%xQ_a(n za`rj(CkZU=+dj#NZ~gT1mdjU)VrZIajGEkJ6d}9vrtlLaE&(MrGm8MnrLBKxn8p{!02i6bzUF0=2GTm zljfgKD9|t_bs*BL35x^;Rm+uHqT#jE(*eDr&QM6u6HB2|m4&Lq)w{tGMZ3$di-x`j zuFQoAqJu7Tf9nH&t^o~v-y;ypB-tC?R-wH)x-yX6DUm2DmUF?=LDS1}Tu(W0t^Xw= z$D~!%T|2l2j4)1hqwl60B4diM8LWg1F|jKb{c>~Z%^t^}54h3`0Yefe(q{)>3phF` z>3DE;IxbnH;1CTO>W7u6eJl!ducr%`b(zGwHKGT?;&PWHo0kv~_TleBAZD=7JfC#3 zb9bxOvR`U4On5)jLhp75tiYN~w4(dp4%$1&W4qYUgh#nBwz}J0D1%j59Rc>M6+=OCWGt0rO2R?ee6Rg-8>j-o4zf>R9r&mt1h2brcnS2j1SsKT;z#AUc+R{O=b|S z8*0HSfg>xfXH*Ze_d%da=wUND_8ghTb@8Y%Re-Vw&Fpf-&v%yy&V!^n;Hyfm_=7ZU z;Qf8F${!Imkb|!-Sd{p&N}V(`WzsO0h)nL940NARaviVJS`o-9T|hb|*M|%_o7T9D zI#QfUVS>VrH}Ic2_^U&vm3C}qtz|-NYQ4}!Dky#9Ak*N<2O0>DY`H}JYIvRn zRsx=Vs0p5R{vLpTsckl}9`~i8Bqn!9l0NbWE=E5WE@WC2`dg5I(n zJLlY*=TBaG%CWG^A}VIn8QkEt%ft7QBWG1#+dRLCmtSAhuBUkS2Dbi`B1A=6To&d^ zQn5A{wtXxZJz0tp<#t~4>c_>TGj{95S7ba4ZK~ndBMmZT5B1qb0r$@d>G}6PPs`?z zz)C2WT(BM?3Ufvrf}e^|I(`epd=50=Br(-^`pDWj;Uim-%z4opTU9X&YsZWiAfm}Q zjbSIOKtDoy(~i_%SQ=BJf#9VTa$=SXei~pjH^0-f9Z-!$!_TDB_&F}0ZOQ1=(Y%1v z5A-|xF%_-tH#prX9#_JXu>rUHR69|uo^U*|$wl?a)Ol#Yi+jkfSWfDJ50cUM$o=Y* zLm&9FhS&~gBkn*Fa>6`Xf&7xQ+w8LDO&n&0a2^XCSdabDqX2r3cNFQqP;U@w9h@r+ zVJ$fC_22MiRP5t-wEzV|O8kRN6UIalvr-%NyYOyUqN+CPA-78KpAbB7=;o_WW|mx1J$&pq z^SLNmki(mOmns+kRc_y_$W@Me!Y3X4kYoz%+vk4TzjTrp%6cFAIn zZwHyy<-k;IQKh`Wd{@VA>AbJAG6%^=&dQk@?4aYdnfxV>JOgObx_dcXo<70ibt%8p zGHP;;QZr-^9`u>qeZbxl4_xyb`UK4e4M69;sioJ!Cb%6FFLOpnc1Y@xPPENMW=Dqc5Lu|)olpkL;?`s=!*YAJ~&HH3{H2(cr zT2;CPXpj8tYRwx@)S53c{J_Jh!3WdWVEN8jR{&ct& z!kwEQ<;Ssos|CDkNLtZ!j%=2=TgH!gj{p9F)Zp$>{AvYK@bPAyMW}v`>Vo6v$K6Qf zl@^%0cEH0ee>L5dVydOy3F!E3(XxX>6ts7U4&=-GA-Bx+iscJ)NSdnUp)y8>j5p@k z)&BXHvf;i0fz3l^n(Q}R@n@nJ9Y3;eyndNX(df_ge(W<_a(>7AD^tmMz1rA)T;4IE ztNAgxFyq}#xKw&xR!uj&W?nnh%xQc1oKQ%?BUNRRoM_gdNb0wPU9*e4^iwAHa@&5O z?GAVti#DX#g%!DPY9QgppJ_A(f~L8_qLt}+GKh^Hzw5Gu_Q?wqH!bs5%uRO)8y+|{ zfsq3LRGNNr>eg}ff~;dAw6B~}dRSN3E&N-uSpQLTfrAoNV~|-ZlbBFQNvp0m!9)3F zQ=P{8(Z$*s>3J8dnK$>g=AaF@FxX8Lo;0!NwX(&@DcLZB}5{$zxgK1zY^%CI2K#6cD`fXv9rpOgVvsdFAw znE{`NU5opHEC^I{=hS{}^&avgr~ci5`lCOz+IzP=^8}(5zC-Qi?7A{zOWtOOwDUF~ zV28lxh{Kp*0?A^aY&A>a+(KHP+|cV8+N3jvMQxLAL*I9VaH1yHyQ9>b6^(tgWNcW( z7=Nk*kW1IBmhQ@on`hGE(DDZ`iqc#>;$u4cbR(|e=H|@~>S!cpYaQNo{Y`!@WsCsN zqVSn<9lPR9`6@}ihr)S4YzjjBanocE&&2O*<;ZYYi$NvQVPajVWzdRc@x|OS&C|UF z?lmjz5O%=;Lzp3T-`x-Qh{NSr$fxz*&jDtwNKB~A`dsYjLlB7GUG>f_1G^@R9i)Bt zrdpb)fWoBq1ALRU{w=S}v$JYY3<+&o{rz&$c)9-*va^eHiYz~=iY@IVzwm%HCo8Xu z^y;lEOY238HgV*#A{R!``yv|A)rn(N9(I;t5~uA!IvR^DP|K|q;%rI~Wo!0()^PswP*sP$#l*K)s)okw zM!qcl?GA1;aaMs}-mu`)PXxH$fjFrk&{IWl31e0HDyo}@ z{U*wW{Zz4Cy#rTLdIwIAK^*N?n~~koi)3|hYAgh0w);`5@=!c;B_$;(^wC*20&XtR zAobbG)hhUFS034@$fJK^iLCB5Ip|>+IZ5IjS@FBi&W7Y4RDQo>Dpl{7PJf9 z;ENdoXO($=07WaGQ?a8%RruU|5eA70Tv>5$@*&=^^&*YN)m|?lT!n5_v#!uvUBiGxyrul+uym3BIl}(GB&Yuq(m=#ur;~E8m zxzz=`7{$u_84Xu^Z}$pq$*JnKY?uJiI>@^vXDxEsZw;j>+6y(?rZ5`yW6jaAKSkI` zUls!Fxz?ntP9DGV8>eKD6x{wzmRuT}FZ&^{YFP;}z1qTlL_D5}%@$oXor=falZd|8 z&=7dTKd&Gv7sfkX&>}QSf-g%Eg{AXZeu>5rtw-xzy(8p!G}}4;(Qm`Xtze(s-mc9A zqDM&YWUw#Mn7pVDEhSiU6{|=;p>cl40T;)wkBOgpkp9aFs4Ty{pw6WDm1S(RRVYbY z!t6ZFr2{zxJbnL`?EQt&-xSvWVSt!QUL4}S?c&gdM%TcCsV6m(f@n_;8^DsHm3UNKm%lMA1sdUh| zdYQv#Wjg^oHF=vd{ueQn=vX9@(~rb{F%P}~8t|<=br{PjC9V*B`Zg?j-hf_}b;RLc z;qsX&Th^$8A$M3;xM?tC>O{AgKFa_WM{uZ+Wdu1$t5|={I1;SLCmKVU0YciZl=y9G z*G62&t}t=Hdp*7+Z!u3(flLK~P&0lwIjH30=UffZq+iRbX&bEoH=5Y<4-~4Oi)Ov9 z<~&@1`quV*%-{j~d(AMHRY-}PB9qhOK-yq(8r&eDgI2m4kGbh?1x0Tb~*#91hpr(=$lKA(Il2H6oL) z{$qevWfj?n!~_EU&T|h#b@5b}$ha1ecX81Ey-NNMyZmGD|5NGz%^5CV{G#GmWh-x4 zNXIOlcyAGyJx3UDrSvsHX_BUUy0y zssNV)xkSoKOQMAANGD|y5fdjl@+T%uQr6>!foa?dO(Y_*DXubQYSlyX{5EDRxMB4la9ZBqFvrn_=&IQYyXJbJPl3a#% z-=w<*uDLO}){Oa9w@d6>y(bvQ;Vt z&x1=d^mYj+b3e6U`vPT+7ppY+3~yiBXSC zAP1wrI?f(d$&L|Y`$dxS&Wih++PLq$j>+1ZPWkTd(*Uc-A6MZ4nEn^(F?0EdeR4uV zzQ=Uu$)f=1-}lZFD9rA?+K&L<-<(Y^Q#H!!cV@o%i<1DtVW$ZnrU`fEVgX=1jHw({ zFNo_(KBEoC&oD8m#!LC&@7xD^>BtBP2ngI~I0p2Zdk$iq4VY zKq+$s7htnPn=L}S_V5GdBq)He~8tR From e887fd9ffbf862e7c7a50f16528edb8e9bfe3152 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:42:31 +0000 Subject: [PATCH 19/25] docs: changelog --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd68634..4dfa16d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,18 @@ ### Chores -+ dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd] ++ dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd], [b6fd35022a99ec0e982ddb154b0450d49c4840e9] + files formatted, [1806165c3e266876b2d1806f7b662d09705f3aad] + create_release.sh check for unused lint, [d0b27211928f93f8455e1ee5a6a6485c6a21d382] +### Docs += Readme updated, screenshot added, [7561a93415c1e1f596b15edba95e7b32a939cd90], [4069e5572f81cb689dbb9f735db919e4636cdccc] + +### Features ++ Ports section added, closes #21, [65a1afcb0605604ede350a5630c775f94ebb74ee] + ### Fixes + sort arrow now on left of header, [40ddcb727d2c1758d6dd26a58507b85b219f51e2] -### Refactos +### Refactors + rename string_wrapper > unit_struct, [27cf53e41f8b379f606c1c27620ee08e79bac57e] ### Tests From 7a096a65c40924021fe643fe0aa1067095832df9 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:48:33 +0000 Subject: [PATCH 20/25] fix: ports display tweak, plus test --- src/app_data/mod.rs | 6 +++--- src/ui/draw_blocks.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 7250f78..557de6b 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -1731,9 +1731,9 @@ mod tests { assert_eq!(result, expected); } - // ************* // - // Header Widths // - // ************* // + // ***** // + // Ports // + // ***** // #[test] /// Returns selected containers ports ordered by private ip diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 370114e..2dacdf4 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -291,7 +291,11 @@ pub fn ports( let (ip, private, public) = max_lens; if ports.0.is_empty() { - let paragraph = Paragraph::new(Span::from("no ports").add_modifier(Modifier::BOLD)) + let text = match ports.1 { + State::Running | State::Paused | State::Restarting => "no ports", + _ => "", + }; + let paragraph = Paragraph::new(Span::from(text).add_modifier(Modifier::BOLD)) .alignment(Alignment::Center) .block(block); f.render_widget(paragraph, area); @@ -2780,6 +2784,43 @@ mod tests { } } } + + // when state is "State::Running | State::Paused | State::Restarting, won't show "no ports" + setup.app_data.lock().containers.items[0].state = State::Dead; + let max_lens = setup.app_data.lock().get_longest_port(); + setup + .terminal + .draw(|f| { + super::ports(f, setup.area, &setup.app_data, max_lens); + }) + .unwrap(); + + let expected = [ + "╭────────── ports ───────────╮", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "│ │", + "╰────────────────────────────╯", + ]; + + let result = &setup.terminal.backend().buffer().content; + for (row_index, row) in expected.iter().enumerate() { + for (char_index, expected_char) in row.chars().enumerate() { + let index = row_index * usize::from(w) + char_index; + let result_cell = &result[index]; + + assert_eq!(expected_char.to_string(), result_cell.symbol()); + if row_index == 0 && !BORDER_CHARS.contains(&result_cell.symbol()) { + assert_eq!(result_cell.fg, Color::Red); + assert_eq!(result_cell.modifier, Modifier::BOLD); + } else { + assert_eq!(result_cell.fg, Color::Reset); + } + } + } } #[test] From edc6ff82383aff30c0914cf2833cca791aa5468a Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:49:18 +0000 Subject: [PATCH 21/25] docs: changlog --- CHANGELOG.md | 2 +- docker-compose.yml | 16 +++++++++------- postgres.Dockerfile | 1 - 3 files changed, 10 insertions(+), 9 deletions(-) delete mode 100644 postgres.Dockerfile diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dfa16d..07a4ed4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ = Readme updated, screenshot added, [7561a93415c1e1f596b15edba95e7b32a939cd90], [4069e5572f81cb689dbb9f735db919e4636cdccc] ### Features -+ Ports section added, closes #21, [65a1afcb0605604ede350a5630c775f94ebb74ee] ++ Ports section added, closes #21, [65a1afcb0605604ede350a5630c775f94ebb74ee], [7a096a65c40924021fe643fe0aa1067095832df9] ### Fixes + sort arrow now on left of header, [40ddcb727d2c1758d6dd26a58507b85b219f51e2] diff --git a/docker-compose.yml b/docker-compose.yml index 7553ceb..442e95e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,18 +3,18 @@ networks: oxker-example-net: name: oxker-examaple-net services: - postgres_but_with_a_longer_container_name: - container_name: postgres_but_with_a_longer_container_name - build: - dockerfile: ./postgres.Dockerfile - context: "." + postgres: + image: postgres:alpine3.19 + container_name: postgres environment: - - POSTGRES_PASSWORD=never_use_this_password_in_production + - POSTGRES_PASSWORD=never_use_this_password_in_production ipc: private restart: always shm_size: 256MB + ports: + - "127.0.0.1:4040:4040" networks: - - oxker-example-net + - oxker-example-net deploy: resources: limits: @@ -41,3 +41,5 @@ services: resources: limits: memory: 512M + + diff --git a/postgres.Dockerfile b/postgres.Dockerfile deleted file mode 100644 index 7c27a09..0000000 --- a/postgres.Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM postgres:16-alpine3.19 \ No newline at end of file From 0438c108bdd9815d7eae1b89c47c4e6438f358d6 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:27:07 +0000 Subject: [PATCH 22/25] chore: dependencies updated --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2eb72f..8c66081 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628a8f9bd1e24b4e0db2b4bc2d000b001e7dd032d54afa60a68836aeec5aa54a" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -443,9 +443,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -484,9 +484,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "hex" @@ -1110,9 +1110,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" +checksum = "3b187f0231d56fe41bfb12034819dd2bf336422a5866de41bc3fec4b2e3883e8" [[package]] name = "socket2" @@ -1382,9 +1382,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" From bd82534e9b7c57c6c9d8d280969b28d6e542f637 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:29:56 +0000 Subject: [PATCH 23/25] docs: changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07a4ed4..0b1d1d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ ### Chores -+ dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd], [b6fd35022a99ec0e982ddb154b0450d49c4840e9] ++ dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd], [b6fd35022a99ec0e982ddb154b0450d49c4840e9], [0438c108bdd9815d7eae1b89c47c4e6438f358d6] + files formatted, [1806165c3e266876b2d1806f7b662d09705f3aad] + create_release.sh check for unused lint, [d0b27211928f93f8455e1ee5a6a6485c6a21d382] @@ -16,7 +16,7 @@ + rename string_wrapper > unit_struct, [27cf53e41f8b379f606c1c27620ee08e79bac57e] ### Tests -+ Finally have tests, currently for layout and associated methods. Running the tests will not interfere with any running Docker containers, [4bcf77db776a36e0a8151ecfbda722a66c4ba46c] ++ Finally have tests, currently for layout and associated methods. At the moment running the tests will not interfere with any running Docker containers, [4bcf77db776a36e0a8151ecfbda722a66c4ba46c] # v0.5.0 ### 2024-01-05 From abcc2eb983ad37641eb8c55e78f399e24b3a6574 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:30:28 +0000 Subject: [PATCH 24/25] docs: changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b1d1d3..7f323e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ + rename string_wrapper > unit_struct, [27cf53e41f8b379f606c1c27620ee08e79bac57e] ### Tests -+ Finally have tests, currently for layout and associated methods. At the moment running the tests will not interfere with any running Docker containers, [4bcf77db776a36e0a8151ecfbda722a66c4ba46c] ++ Finally have tests, currently for layout and associated methods, at the moment running the tests will not interfere with any running Docker containers, [4bcf77db776a36e0a8151ecfbda722a66c4ba46c] # v0.5.0 ### 2024-01-05 From 858832cfd34239d91017275cc41d527e371e688c Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:56:22 +0000 Subject: [PATCH 25/25] chore: release v0.6.0 --- .github/release-body.md | 25 +++++++++++-------------- CHANGELOG.md | 19 +++++++++++-------- Cargo.lock | 2 +- Cargo.toml | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/release-body.md b/.github/release-body.md index 2c5997b..6e8fc56 100644 --- a/.github/release-body.md +++ b/.github/release-body.md @@ -1,26 +1,23 @@ -### 2024-01-05 +### 2024-01-18 ### Chores -+ .devcontainer updated, [2313618eb1493ce41d70847b888c32b65fdc40ea], [5af6b8bcd31c3c38ff5a5799c76dc1cbe1167763], [9b0b6b10c3a0c1d5095490cfd3cda18d252f38f5] -+ alpine version bump, [061de032dad935c56c6caab419ecb5c9bbac4c7e] -+ dependencies updated, [0890991ff1a239fe2d556a0c4eac6ae05beb9b50], [0a7b266b2a358a4788ae877ca8a97f08eac4eef2], [333621f1a7321c1fdf73fd35dd7f3ab165a9dc64], [3e51889cd8a552b1da463ae6a40d5de6eec188f5], [a179bb6f6a7e076269fa830f56c0d4a31cf8488a] -+ file formatting, [eb5e74ae67d815bf49f241d2baf319e41cf9adf8] -+ Rust 1.75.0 linting, [81be75f27fd32a59ebff57e44c5022ff862df84b] ++ dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd], [b6fd35022a99ec0e982ddb154b0450d49c4840e9], [0438c108bdd9815d7eae1b89c47c4e6438f358d6] ++ files formatted, [1806165c3e266876b2d1806f7b662d09705f3aad] ++ create_release.sh check for unused lint, [d0b27211928f93f8455e1ee5a6a6485c6a21d382] ### Docs -+ screenshot updated, [0231d1bdcda304300d289243a95044ab3bdce85c] -+ comment typo, [0ad1ec9d85d6f0cac743b4421d0ad03432c9d717] += Readme updated, screenshot added, [7561a93415c1e1f596b15edba95e7b32a939cd90], [4069e5572f81cb689dbb9f735db919e4636cdccc] ### Features -+ re-arrange columns, container name is now the first column, added a ContainerName & ContainerImage struct via `string_wrapper` macro, closes #32, [e936bb4b78980d0e34a1ef5e9f6f82a9ed0ddc7f] ++ Ports section added, closes #21, [65a1afcb0605604ede350a5630c775f94ebb74ee], [7a096a65c40924021fe643fe0aa1067095832df9] ### Fixes -+ Docker Commands hidden, [4301e4709f99fc23ee438bf345b0dc698a05dc4e] -+ .gitattributes, [1234ea53897b2ed6ada0eb18cd81b8783a5dc5f5] ++ sort arrow now on left of header, [40ddcb727d2c1758d6dd26a58507b85b219f51e2] ### Refactors -+ GitHub workflow action improved, [04b66af2b60c96cfbece0b13109e30b08ef35cc4] -+ sort_containers, [ccf8b55a7495982f72b4fb3af6e11a9bd7465216] -+ string_wrapper .get() return `&str`, [a722731c6a77e00d1fb13967b51400aa34e72213] ++ rename string_wrapper > unit_struct, [27cf53e41f8b379f606c1c27620ee08e79bac57e] + +### Tests ++ Finally have tests, currently for layout and associated methods, at the moment running the tests will not interfere with any running Docker containers, [4bcf77db776a36e0a8151ecfbda722a66c4ba46c] see CHANGELOG.md for more details diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f323e1..94a81a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,22 +1,25 @@ +# v0.6.0 +### 2024-01-18 + ### Chores -+ dependencies updated, [53b4bafbe53312fe41608ddf33e865d474222aaa], [58ef151600e362048a607c8ae61a5edfe80ab1dd], [b6fd35022a99ec0e982ddb154b0450d49c4840e9], [0438c108bdd9815d7eae1b89c47c4e6438f358d6] -+ files formatted, [1806165c3e266876b2d1806f7b662d09705f3aad] -+ create_release.sh check for unused lint, [d0b27211928f93f8455e1ee5a6a6485c6a21d382] ++ dependencies updated, [53b4bafb](https://github.com/mrjackwills/oxker/commit/53b4bafbe53312fe41608ddf33e865d474222aaa), [58ef1516](https://github.com/mrjackwills/oxker/commit/58ef151600e362048a607c8ae61a5edfe80ab1dd), [b6fd3502](https://github.com/mrjackwills/oxker/commit/b6fd35022a99ec0e982ddb154b0450d49c4840e9), [0438c108](https://github.com/mrjackwills/oxker/commit/0438c108bdd9815d7eae1b89c47c4e6438f358d6) ++ files formatted, [1806165c](https://github.com/mrjackwills/oxker/commit/1806165c3e266876b2d1806f7b662d09705f3aad) ++ create_release.sh check for unused lint, [d0b27211](https://github.com/mrjackwills/oxker/commit/d0b27211928f93f8455e1ee5a6a6485c6a21d382) ### Docs -= Readme updated, screenshot added, [7561a93415c1e1f596b15edba95e7b32a939cd90], [4069e5572f81cb689dbb9f735db919e4636cdccc] += Readme updated, screenshot added, [7561a934](https://github.com/mrjackwills/oxker/commit/7561a93415c1e1f596b15edba95e7b32a939cd90), [4069e557](https://github.com/mrjackwills/oxker/commit/4069e5572f81cb689dbb9f735db919e4636cdccc) ### Features -+ Ports section added, closes #21, [65a1afcb0605604ede350a5630c775f94ebb74ee], [7a096a65c40924021fe643fe0aa1067095832df9] ++ Ports section added, closes [#21](https://github.com/mrjackwills/oxker/issues/21), [65a1afcb](https://github.com/mrjackwills/oxker/commit/65a1afcb0605604ede350a5630c775f94ebb74ee), [7a096a65](https://github.com/mrjackwills/oxker/commit/7a096a65c40924021fe643fe0aa1067095832df9) ### Fixes -+ sort arrow now on left of header, [40ddcb727d2c1758d6dd26a58507b85b219f51e2] ++ sort arrow now on left of header, [40ddcb72](https://github.com/mrjackwills/oxker/commit/40ddcb727d2c1758d6dd26a58507b85b219f51e2) ### Refactors -+ rename string_wrapper > unit_struct, [27cf53e41f8b379f606c1c27620ee08e79bac57e] ++ rename string_wrapper > unit_struct, [27cf53e4](https://github.com/mrjackwills/oxker/commit/27cf53e41f8b379f606c1c27620ee08e79bac57e) ### Tests -+ Finally have tests, currently for layout and associated methods, at the moment running the tests will not interfere with any running Docker containers, [4bcf77db776a36e0a8151ecfbda722a66c4ba46c] ++ Finally have tests, currently for layout and associated methods, at the moment running the tests will not interfere with any running Docker containers, [4bcf77db](https://github.com/mrjackwills/oxker/commit/4bcf77db776a36e0a8151ecfbda722a66c4ba46c) # v0.5.0 ### 2024-01-05 diff --git a/Cargo.lock b/Cargo.lock index 8c66081..c67ea51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -783,7 +783,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "oxker" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "bollard", diff --git a/Cargo.toml b/Cargo.toml index a1cd0ff..5bcbd65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxker" -version = "0.5.0" +version = "0.6.0" edition = "2021" authors = ["Jack Wills "] description = "A simple tui to view & control docker containers"