From d2f2eb62becc8e2819e2d37fbbd6f486bde99591 Mon Sep 17 00:00:00 2001 From: ponzischeme89 Date: Mon, 10 Aug 2026 07:11:14 +1200 Subject: [PATCH] Improve playback, preroll and TV experience --- CHANGELOG.md | 3 + CLAUDE.md | 12 + .../java/com/ponzischeme89/memby/MembyApp.kt | 4 + .../memby/data/EmbyRepository.kt | 95 +++++- .../memby/data/model/EmbyModels.kt | 14 + .../memby/data/remote/GatewayApi.kt | 6 + .../ponzischeme89/memby/ui/HomeViewModel.kt | 33 +- .../ponzischeme89/memby/ui/MainActivity.kt | 318 +++++++++++++++--- .../memby/ui/alerts/AlertsPage.kt | 44 ++- .../memby/ui/player/CastPanel.kt | 213 +++++++++++- .../memby/ui/player/PlaybackRecovery.kt | 8 + .../memby/ui/player/PlayerActivity.kt | 293 ++++++++++++++-- .../memby/ui/player/PrerollPreloader.kt | 107 ++++++ app/src/main/res/drawable/ic_deceased.xml | 12 + .../main/res/layout/player_cast_overlay.xml | 12 +- app/src/main/res/raw/emby_preroll.mp4 | Bin 0 -> 203992 bytes app/src/main/res/values/strings.xml | 7 + .../memby/data/CastMetadataTest.kt | 20 ++ .../data/DevicePlaybackCapabilitiesTest.kt | 13 + .../memby/ui/ContinueWatchingResumeTest.kt | 44 +++ .../memby/ui/alerts/AlertsFormatTest.kt | 7 + .../ui/player/CastPanelScreenshotTest.kt | 37 +- .../memby/ui/player/PlaybackRecoveryTest.kt | 9 + .../memby/ui/player/PrerollSequenceTest.kt | 9 + bugs-0908.md | 53 +-- server/internal/api/api.go | 2 + server/internal/api/people.go | 94 ++++++ server/internal/emby/client.go | 12 +- server/internal/emby/client_playback_test.go | 20 ++ server/internal/emby/device_profile.go | 21 ++ 30 files changed, 1380 insertions(+), 142 deletions(-) create mode 100644 app/src/main/java/com/ponzischeme89/memby/ui/player/PrerollPreloader.kt create mode 100644 app/src/main/res/drawable/ic_deceased.xml create mode 100644 app/src/main/res/raw/emby_preroll.mp4 create mode 100644 app/src/test/java/com/ponzischeme89/memby/ui/ContinueWatchingResumeTest.kt create mode 100644 server/internal/api/people.go diff --git a/CHANGELOG.md b/CHANGELOG.md index ce206b8..035ee70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## 0.2.39 — 2026-08-09 +- Improved: HEVC playback performance +- Added: Cast panels now mark deceased performers and open biographies and filmographies. - Added: An optional confirmation before the Back button closes Memby. - Improved: Reviewed D-pad navigation across the Android TV experience and documented follow-up work. +- Fixed: Continue Watching now keeps its saved position and shows Resume instead of Play. ## 0.2.38 — 2026-08-09 - Improved: Ratings are larger and easier to read. diff --git a/CLAUDE.md b/CLAUDE.md index 84975cc..cafffeb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1180,6 +1180,18 @@ pays DNS, TCP, TLS and Emby's file open. Pre-warming that connection is the open opportunity. Two things that look like causes and are not: the subtitle auto-selection costs 20–200 ms, not seconds, and the seek itself is about 900 ms. +**The local Memby preroll is prepared while Home is idle.** `PrerollPreloader` owns one +process-scoped ExoPlayer for `res/raw/emby_preroll.mp4`; `MembyApp` queues its first prepare +on the main queue's idle handler, so decoder construction and the local resource read never +sit in `Application.onCreate` or in front of launcher composition. When a fresh playback +requires the preroll, `PlayerActivity` borrows that player and shows it in the pre-roll video +frame while the requested title negotiates and prepares, paused at zero, behind the overlay. +Three details preserve the performance claim: the clip's actual duration owns the hand-off +rather than a stale configured estimate; the old paused-content-frame path remains the +failure fallback; and the preroll player is stopped and parked at hand-off so it releases its +hardware decoder while HEVC content plays. Returning to Home prepares the same instance again +in the next idle window rather than constructing one per title. + **Next up / auto-advance.** 30 s before an episode ends, `PlayerActivity` slides up `player_next_up_banner.xml` and rolls into the next episode when it reaches zero (Settings → Playback turns it off; `Settings.autoPlayNextEpisode`). Which episode that is comes from diff --git a/app/src/main/java/com/ponzischeme89/memby/MembyApp.kt b/app/src/main/java/com/ponzischeme89/memby/MembyApp.kt index 7117108..1cef329 100644 --- a/app/src/main/java/com/ponzischeme89/memby/MembyApp.kt +++ b/app/src/main/java/com/ponzischeme89/memby/MembyApp.kt @@ -6,6 +6,7 @@ import coil.ImageLoader import coil.disk.DiskCache import coil.memory.MemoryCache import com.ponzischeme89.memby.data.remote.HttpStack +import com.ponzischeme89.memby.ui.player.PrerollPreloader import okhttp3.OkHttpClient import java.util.concurrent.TimeUnit @@ -46,6 +47,9 @@ class MembyApp : Application() { .build(), ) ServiceLocator.init(this) + // Registers an idle callback only: the local four-second clip is prepared after + // the launcher's queued start-up work, never on its critical path. + PrerollPreloader.start(this) } /** diff --git a/app/src/main/java/com/ponzischeme89/memby/data/EmbyRepository.kt b/app/src/main/java/com/ponzischeme89/memby/data/EmbyRepository.kt index b0925ad..022bf74 100644 --- a/app/src/main/java/com/ponzischeme89/memby/data/EmbyRepository.kt +++ b/app/src/main/java/com/ponzischeme89/memby/data/EmbyRepository.kt @@ -19,6 +19,7 @@ import com.ponzischeme89.memby.data.model.HomeRow import com.ponzischeme89.memby.data.model.PlaybackReport import com.ponzischeme89.memby.data.model.PlaybackInfoRequest import com.ponzischeme89.memby.data.model.MediaSourceInfo +import com.ponzischeme89.memby.data.model.h264TranscodeFallback import com.ponzischeme89.memby.data.remote.EmbyApi import com.ponzischeme89.memby.data.remote.EmbyServiceFactory import com.ponzischeme89.memby.data.remote.GatewayApi @@ -983,6 +984,58 @@ class EmbyRepository(private val settings: SettingsStore) { ) } + private val personCache = java.util.concurrent.ConcurrentHashMap() + private val filmographyCache = java.util.concurrent.ConcurrentHashMap>() + + /** Biography and life dates for a cast member. Emby models a person as an item. */ + suspend fun getPersonDetails(personId: String): BaseItem { + require(personId.isNotBlank()) { "Person id is required" } + val cacheKey = "${snapshot.userId.orEmpty()}:$personId" + personCache[cacheKey]?.let { return it } + val person = if (ServerConfig.isGateway) { + requireGateway().person(personId) + } else { + val userId = snapshot.userId ?: error("Not connected") + requireApi().getItem( + userId = userId, + itemId = personId, + fields = "Overview,Genres,PrimaryImageAspectRatio", + ) + } + personCache[cacheKey] = person + return person + } + + /** Films and series associated with a person, newest first. */ + suspend fun getPersonFilmography(personId: String): List { + require(personId.isNotBlank()) { "Person id is required" } + val cacheKey = "${snapshot.userId.orEmpty()}:$personId" + filmographyCache[cacheKey]?.let { return it } + val items = if (ServerConfig.isGateway) { + requireGateway().personFilmography(personId).items + } else { + val userId = snapshot.userId ?: error("Not connected") + requireApi().getItems( + userId, + mapOf( + "PersonIds" to personId, + "IncludeItemTypes" to "Movie,Series", + "Recursive" to "true", + "SortBy" to "ProductionYear,SortName", + "SortOrder" to "Descending", + "Limit" to "60", + "Fields" to "Overview,ProductionYear,PrimaryImageAspectRatio", + "EnableImages" to "true", + "EnableImageTypes" to "Primary", + "ImageTypeLimit" to "1", + "EnableUserData" to "true", + ), + ).items + } + filmographyCache[cacheKey] = items + return items + } + private val ratingsCache = java.util.concurrent.ConcurrentHashMap>() /** Optional ratings that never block essential metadata. The gateway owns a durable @@ -1402,22 +1455,27 @@ class EmbyRepository(private val settings: SettingsStore) { ) /** - * Returns a prefetched stream if one is sitting ready for [itemId], without suspending + * Returns a prefetched stream if one is sitting ready for [request], without suspending * and without starting a request. This is what lets the launcher tell instantly whether * pressing Play can hand the player a URL or must let it resolve one for itself, so * that decision never costs a frame of its own. */ - fun readyPlayableForLaunch(itemId: String): Playable? { + fun readyPlayableForLaunch(request: PlaybackRequest): Playable? { // tryLock rather than a blocking wait, and failing to take it is a legitimate // answer: the lock is held exactly when a resolution is in flight, and the caller's // fallback — letting the player await it — is what should happen then anyway. if (!playableMutex.tryLock()) return null try { - val entry = playableCache[itemId] ?: return null + val entry = playableCache[request.itemId] ?: return null if (!isFreshPlayablePrefetch(entry.resolvedAtMs, System.currentTimeMillis())) return null // Consumed: negotiated session state must never be handed out twice. - playableCache.remove(itemId) - return entry.playable + playableCache.remove(request.itemId) + return entry.playable.copy( + resumePositionMs = launchResumePositionMs( + resolvedPositionMs = entry.playable.resumePositionMs, + requestedPositionMs = request.resumePositionMs, + ), + ) } finally { playableMutex.unlock() } @@ -1444,9 +1502,22 @@ class EmbyRepository(private val settings: SettingsStore) { ?.playable ready to if (ready == null) playableInFlight[request.itemId] else null } - if (cached != null) return cached + if (cached != null) { + return cached.copy( + resumePositionMs = launchResumePositionMs( + resolvedPositionMs = cached.resumePositionMs, + requestedPositionMs = request.resumePositionMs, + ), + ) + } if (inFlight != null) { - return inFlight.await().also { + val resolved = inFlight.await() + return resolved.copy( + resumePositionMs = launchResumePositionMs( + resolvedPositionMs = resolved.resumePositionMs, + requestedPositionMs = request.resumePositionMs, + ), + ).also { playableMutex.withLock { playableCache.remove(request.itemId) } } } @@ -2138,13 +2209,17 @@ class EmbyRepository(private val settings: SettingsStore) { startTimeTicks = millisecondsToTicks(positionMs), enableDirectPlay = !forceTranscode, enableDirectStream = !forceTranscode, + // A compatibility retry must re-encode the failing video codec. + // Leaving stream-copy enabled can return the same HEVC Main 10 + // elementary stream inside HLS and reproduce the decoder failure. + allowVideoStreamCopy = !forceTranscode, subtitleStreamIndex = subtitleStreamIndex, currentPlaySessionId = currentPlaySessionId, deviceProfile = if (forceTranscode) { com.ponzischeme89.memby.data.model.DeviceProfile.embyAndroidTv( capabilities = devicePlaybackCapabilities, ) - .copy(directPlayProfiles = emptyList()) + .h264TranscodeFallback() } else { com.ponzischeme89.memby.data.model.DeviceProfile.embyAndroidTv( capabilities = devicePlaybackCapabilities, @@ -2558,6 +2633,10 @@ internal fun millisecondsToTicks(milliseconds: Long): Long = internal fun isFreshPlayablePrefetch(resolvedAtMs: Long, nowMs: Long): Boolean = resolvedAtMs <= nowMs && nowMs - resolvedAtMs <= PLAYABLE_PREFETCH_MAX_AGE_MS +/** A positive position on the pressed card outranks an older prefetched zero. */ +internal fun launchResumePositionMs(resolvedPositionMs: Long, requestedPositionMs: Long): Long = + if (requestedPositionMs > 0L) requestedPositionMs else resolvedPositionMs.coerceAtLeast(0L) + private val BaseItem.resumePositionMs: Long get() = ((userData?.playbackPositionTicks ?: 0L) / 10_000L).coerceAtLeast(0L) diff --git a/app/src/main/java/com/ponzischeme89/memby/data/model/EmbyModels.kt b/app/src/main/java/com/ponzischeme89/memby/data/model/EmbyModels.kt index afe5d22..4125f30 100644 --- a/app/src/main/java/com/ponzischeme89/memby/data/model/EmbyModels.kt +++ b/app/src/main/java/com/ponzischeme89/memby/data/model/EmbyModels.kt @@ -212,6 +212,17 @@ data class DeviceProfile( } } +/** + * A decoder recovery must change the video format, not merely put the same elementary + * stream into HLS. Emby otherwise sees HEVC in the ordinary transcoding profile and may + * stream-copy the codec that has just failed on this television. + */ +internal fun DeviceProfile.h264TranscodeFallback(): DeviceProfile = copy( + directPlayProfiles = emptyList(), + transcodingProfiles = transcodingProfiles.map { it.copy(videoCodec = "h264") }, + codecProfiles = codecProfiles.filter { it.codec.equals("h264", ignoreCase = true) }, +) + @Serializable data class CodecProfile( @SerialName("Type") val type: String = "Video", @@ -332,6 +343,9 @@ data class BaseItem( // is the one date that distinguishes it from its neighbours in a list, so it is asked // for by name in every episode query — it is not a default field. @SerialName("PremiereDate") val premiereDate: String? = null, + // For a Person, Emby uses PremiereDate for their birth and EndDate for their death. + // They remain wire-named media fields because series use the same properties. + @SerialName("EndDate") val endDate: String? = null, @SerialName("OfficialRating") val officialRating: String? = null, @SerialName("CommunityRating") val communityRating: Double? = null, @SerialName("Studios") val studios: List = emptyList(), diff --git a/app/src/main/java/com/ponzischeme89/memby/data/remote/GatewayApi.kt b/app/src/main/java/com/ponzischeme89/memby/data/remote/GatewayApi.kt index 18b96f2..f11642a 100644 --- a/app/src/main/java/com/ponzischeme89/memby/data/remote/GatewayApi.kt +++ b/app/src/main/java/com/ponzischeme89/memby/data/remote/GatewayApi.kt @@ -184,6 +184,12 @@ interface GatewayApi { @GET("v1/items/{id}") suspend fun item(@Path("id") itemId: String): BaseItem + @GET("v1/people/{id}") + suspend fun person(@Path("id") personId: String): BaseItem + + @GET("v1/people/{id}/filmography") + suspend fun personFilmography(@Path("id") personId: String): GatewayItems + /** Optional, server-filtered external movie ratings. Empty is always a valid result. */ @GET("v1/items/{id}/ratings") suspend fun movieRatings(@Path("id") itemId: String): GatewayMovieRatings diff --git a/app/src/main/java/com/ponzischeme89/memby/ui/HomeViewModel.kt b/app/src/main/java/com/ponzischeme89/memby/ui/HomeViewModel.kt index 4fb2a5e..9f35116 100644 --- a/app/src/main/java/com/ponzischeme89/memby/ui/HomeViewModel.kt +++ b/app/src/main/java/com/ponzischeme89/memby/ui/HomeViewModel.kt @@ -307,12 +307,7 @@ class HomeViewModel(private val repository: EmbyRepository) : ViewModel() { */ fun focusItem(item: BaseItem) { val cached = synchronized(metadataCache) { metadataCache[item.id] } - val focused = (cached ?: item).copy( - membyAiringToday = item.membyAiringToday || cached?.membyAiringToday == true, - membyRecommendationReason = item.membyRecommendationReason - ?: cached?.membyRecommendationReason, - membyCompatibility = item.membyCompatibility ?: cached?.membyCompatibility, - ) + val focused = focusedItemWithMetadata(item, cached) _focusedItem.value = focused metadataJob?.cancel() metadataJob = viewModelScope.launch(Dispatchers.IO) { @@ -322,7 +317,9 @@ class HomeViewModel(private val repository: EmbyRepository) : ViewModel() { // Play click a memory lookup. The repository single-flights requests, // so focus and click can never duplicate the gateway call. if (item.membyPlayable) { - launch { runCatching { repository.prefetchPlayable(cached ?: item) } } + // The row owns live UserData. Prefetching the metadata-cache copy used + // to turn a 5% resume point into zero after details had been visited. + launch { runCatching { repository.prefetchPlayable(focused) } } } // Warm the explanation and franchise siblings while the card is already // focused, so opening Details does not add a reason line a frame later. @@ -334,11 +331,7 @@ class HomeViewModel(private val repository: EmbyRepository) : ViewModel() { val details = runCatching { repository.getItemDetails(item.id) }.getOrNull() ?: return@launch - val taggedDetails = details.copy( - membyAiringToday = focused.membyAiringToday, - membyRecommendationReason = focused.membyRecommendationReason, - membyCompatibility = focused.membyCompatibility, - ) + val taggedDetails = focusedItemWithMetadata(item, details) synchronized(metadataCache) { metadataCache[item.id] = taggedDetails } if (_focusedItem.value?.id == item.id) { _focusedItem.value = taggedDetails @@ -580,6 +573,22 @@ class HomeViewModel(private val repository: EmbyRepository) : ViewModel() { } } +/** + * Adds rich detail metadata without replacing the row's live per-user state. + * + * Continue Watching is the important case: its card knows the current playhead, while a + * cached/full metadata record may carry no UserData at all. Losing that state changes the + * button from Resume to Play and sends a zero resume position into playback. + */ +internal fun focusedItemWithMetadata(item: BaseItem, metadata: BaseItem?): BaseItem = + (metadata ?: item).copy( + userData = item.userData ?: metadata?.userData, + membyAiringToday = item.membyAiringToday || metadata?.membyAiringToday == true, + membyRecommendationReason = item.membyRecommendationReason + ?: metadata?.membyRecommendationReason, + membyCompatibility = item.membyCompatibility ?: metadata?.membyCompatibility, + ) + internal fun HomeSnapshot.withAiringTodayTags(): HomeSnapshot { val airingTodayKeys = rows.airingTodayShowKeys() if (airingTodayKeys.isEmpty()) return this diff --git a/app/src/main/java/com/ponzischeme89/memby/ui/MainActivity.kt b/app/src/main/java/com/ponzischeme89/memby/ui/MainActivity.kt index 61a5bb3..5635aae 100644 --- a/app/src/main/java/com/ponzischeme89/memby/ui/MainActivity.kt +++ b/app/src/main/java/com/ponzischeme89/memby/ui/MainActivity.kt @@ -35,6 +35,7 @@ import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.BasicTextField @@ -136,6 +137,7 @@ import com.ponzischeme89.memby.ui.detail.scheduleSeriesStub import com.ponzischeme89.memby.ui.genre.GenreDiscoveryStrip import com.ponzischeme89.memby.ui.genre.GenreBrowseScreen import com.ponzischeme89.memby.ui.player.PlayerActivity +import com.ponzischeme89.memby.ui.player.PrerollPreloader import com.ponzischeme89.memby.performance.PerformanceMonitor import com.ponzischeme89.memby.ui.search.SearchScreen import com.ponzischeme89.memby.ui.settings.SettingsSheet @@ -211,6 +213,14 @@ class MainActivity : ComponentActivity() { } PerformanceMonitor.start(this) } + + override fun onStart() { + super.onStart() + // Also runs when playback returns to Home. A used preroll player is parked while + // the programme runs so it does not retain a second decoder, then prepared again + // during the launcher's next idle window. + PrerollPreloader.start(this) + } } /** @@ -395,6 +405,11 @@ private fun AppRoot(onCloseSettings: () -> Unit) { recommendationOnboarding?.prompted == true -> { RecommendationOnboardingScreen( onboarding = recommendationOnboarding!!, + onSkip = { + // Skip is for this visit only. Do not mark or save completion: the + // gateway may offer onboarding again on a later launch. + recommendationOnboarding = recommendationOnboarding!!.copy(completed = true) + }, onComplete = { ratings, actors, actresses, directors -> repo.saveRecommendationRatings(ratings, actors, actresses, directors) // Record it locally as well, so this profile's next cold start @@ -459,6 +474,7 @@ private fun AppRoot(onCloseSettings: () -> Unit) { } @Composable +@OptIn(ExperimentalComposeUiApi::class) private fun ExitMembyConfirmation( onStay: () -> Unit, onExit: () -> Unit, @@ -502,13 +518,23 @@ private fun ExitMembyConfirmation( onClick = onStay, modifier = Modifier .focusRequester(stayFocus) - .focusProperties { right = exitFocus }, + .focusProperties { + left = FocusRequester.Cancel + right = exitFocus + up = FocusRequester.Cancel + down = FocusRequester.Cancel + }, ) { Text("Stay in Memby") } Button( onClick = onExit, modifier = Modifier .focusRequester(exitFocus) - .focusProperties { left = stayFocus }, + .focusProperties { + left = stayFocus + right = FocusRequester.Cancel + up = FocusRequester.Cancel + down = FocusRequester.Cancel + }, ) { Text("Close Memby") } } } @@ -882,6 +908,7 @@ private fun ProfileEntryScreen( } @Composable +@OptIn(ExperimentalComposeUiApi::class) private fun ProfileChooser( profiles: List, currentProfileId: String?, @@ -892,13 +919,18 @@ private fun ProfileChooser( onAddProfile: () -> Unit, onClose: (() -> Unit)?, ) { - val firstFocus = remember { FocusRequester() } + val addFocus = remember { FocusRequester() } + val backFocus = remember { FocusRequester() } var pendingRemoval by remember { mutableStateOf(null) } val orderedProfiles = remember(profiles, currentProfileId) { profiles.sortedByDescending { it.id == currentProfileId } } - LaunchedEffect(orderedProfiles.firstOrNull()?.id) { - firstFocus.requestFocus() + val profileIds = orderedProfiles.map(EmbyProfile::id) + val tileFocus = remember(profileIds) { List(orderedProfiles.size) { FocusRequester() } } + val removeFocus = remember(profileIds) { List(orderedProfiles.size) { FocusRequester() } } + LaunchedEffect(profileIds) { + kotlinx.coroutines.delay(16L) + if (orderedProfiles.isEmpty()) addFocus.requestFocus() else tileFocus.first().requestFocus() } Box( modifier = Modifier @@ -929,47 +961,81 @@ private fun ProfileChooser( fontSize = 17.sp, modifier = Modifier.padding(top = 8.dp, bottom = 30.dp), ) - Row( + LazyRow( + modifier = Modifier.fillMaxWidth().focusGroup(), + contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 24.dp), horizontalArrangement = Arrangement.spacedBy(24.dp), verticalAlignment = Alignment.Top, ) { - orderedProfiles.forEachIndexed { index, profile -> - Box(modifier = Modifier.width(154.dp)) { + itemsIndexed(orderedProfiles, key = { _, profile -> profile.id }) { index, profile -> + Column( + modifier = Modifier.width(154.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { ProfileTile( name = profile.username, current = profile.id == currentProfileId, enabled = switchingProfileId == null && removingProfileId == null, onClick = { onSelect(profile) }, - modifier = if (index == 0) Modifier.focusRequester(firstFocus) else Modifier, + modifier = Modifier + .focusRequester(tileFocus[index]) + .focusProperties { + left = if (index > 0) tileFocus[index - 1] else FocusRequester.Cancel + right = if (index < orderedProfiles.lastIndex) { + tileFocus[index + 1] + } else { + addFocus + } + down = removeFocus[index] + }, ) ProfileDeleteButton( profileName = profile.username, enabled = switchingProfileId == null && removingProfileId == null, onClick = { pendingRemoval = profile }, modifier = Modifier - .align(Alignment.TopEnd) - .padding(top = 7.dp, end = 22.dp), + .focusRequester(removeFocus[index]) + .focusProperties { + up = tileFocus[index] + left = if (index > 0) removeFocus[index - 1] else FocusRequester.Cancel + right = if (index < orderedProfiles.lastIndex) { + removeFocus[index + 1] + } else { + FocusRequester.Cancel + } + down = if (onClose != null) backFocus else FocusRequester.Cancel + }, ) } } - ProfileTile( - name = "Add another user", - current = false, - enabled = switchingProfileId == null && removingProfileId == null, - symbol = "+", - onClick = onAddProfile, - modifier = if (orderedProfiles.isEmpty()) { - Modifier.focusRequester(firstFocus) - } else { - Modifier - }, - ) + item(key = "add-profile") { + ProfileTile( + name = "Add another user", + current = false, + enabled = switchingProfileId == null && removingProfileId == null, + symbol = "+", + onClick = onAddProfile, + modifier = Modifier + .focusRequester(addFocus) + .focusProperties { + left = tileFocus.lastOrNull() ?: FocusRequester.Cancel + right = FocusRequester.Cancel + down = if (onClose != null) backFocus else FocusRequester.Cancel + }, + ) + } } if (onClose != null) { Spacer(Modifier.height(30.dp)) Button( onClick = onClose, enabled = switchingProfileId == null && removingProfileId == null, + modifier = Modifier + .focusRequester(backFocus) + .focusProperties { + up = removeFocus.firstOrNull() ?: addFocus + }, ) { Text("Back to Memby") } } } @@ -987,9 +1053,11 @@ private fun ProfileChooser( } @Composable +@OptIn(ExperimentalComposeUiApi::class) private fun RecommendationOnboardingScreen( onboarding: RecommendationOnboarding, onComplete: suspend (Map, List, List, List) -> Unit, + onSkip: () -> Unit = {}, initialStageIndex: Int = 0, previewArtwork: ImageBitmap? = null, ) { @@ -1015,7 +1083,25 @@ private fun RecommendationOnboardingScreen( var stageIndex by rememberSaveable { mutableStateOf(initialStageIndex.coerceIn(0, (stages.size - 1).coerceAtLeast(0))) } var saving by remember { mutableStateOf(false) } var error by remember { mutableStateOf(null) } + var confirmingSkip by rememberSaveable { mutableStateOf(false) } val stage = stages.getOrNull(stageIndex) + val choiceFocus = remember(stageIndex) { FocusRequester() } + val backFocus = remember(stageIndex) { FocusRequester() } + val primaryFocus = remember(stageIndex) { FocusRequester() } + + LaunchedEffect(stageIndex, stage) { + kotlinx.coroutines.delay(16L) + runCatching { + if (stage == null) primaryFocus.requestFocus() else choiceFocus.requestFocus() + } + } + BackHandler { + when { + confirmingSkip -> confirmingSkip = false + stageIndex > 0 -> stageIndex-- + else -> confirmingSkip = true + } + } val finish: () -> Unit = { if (!saving) { @@ -1095,11 +1181,21 @@ private fun RecommendationOnboardingScreen( Spacer(Modifier.weight(1f)) } else { when (stage) { - "Movies" -> OnboardingTitleRow(movies, ratings, previewArtwork) - "TV shows" -> OnboardingTitleRow(shows, ratings, previewArtwork) - "Actors" -> OnboardingPeopleRow(onboarding.actors, selectedActors, previewArtwork) - "Actresses" -> OnboardingPeopleRow(onboarding.actresses, selectedActresses, previewArtwork) - "Directors" -> OnboardingPeopleRow(onboarding.directors, selectedDirectors, previewArtwork) + "Movies" -> OnboardingTitleRow( + movies, ratings, choiceFocus, primaryFocus, previewArtwork, + ) + "TV shows" -> OnboardingTitleRow( + shows, ratings, choiceFocus, primaryFocus, previewArtwork, + ) + "Actors" -> OnboardingPeopleRow( + onboarding.actors, selectedActors, choiceFocus, primaryFocus, previewArtwork, + ) + "Actresses" -> OnboardingPeopleRow( + onboarding.actresses, selectedActresses, choiceFocus, primaryFocus, previewArtwork, + ) + "Directors" -> OnboardingPeopleRow( + onboarding.directors, selectedDirectors, choiceFocus, primaryFocus, previewArtwork, + ) } } Spacer(Modifier.weight(1f)) @@ -1109,15 +1205,114 @@ private fun RecommendationOnboardingScreen( color = Color(0xFF9DA8B0), fontSize = 14.sp, modifier = Modifier.weight(1f), ) error?.let { Text(it, color = Color(0xFFFF7777), fontSize = 14.sp, modifier = Modifier.padding(end = 16.dp)) } - if (stageIndex > 0) Button(onClick = { stageIndex-- }, enabled = !saving) { Text("Back") } + if (stageIndex > 0) { + Button( + onClick = { stageIndex-- }, + enabled = !saving, + modifier = Modifier + .focusRequester(backFocus) + .focusProperties { + up = if (stage == null) FocusRequester.Cancel else choiceFocus + right = primaryFocus + }, + ) { Text("Back") } + } Spacer(Modifier.width(10.dp)) if (stageIndex < stages.lastIndex) { - Button(onClick = { stageIndex++ }, enabled = !saving) { Text("Next") } + Button( + onClick = { stageIndex++ }, + enabled = !saving, + modifier = Modifier + .focusRequester(primaryFocus) + .focusProperties { + up = if (stage == null) FocusRequester.Cancel else choiceFocus + left = if (stageIndex > 0) backFocus else FocusRequester.Cancel + }, + ) { Text("Next") } } else { - Button(onClick = finish, enabled = !saving) { Text(if (saving) "Saving…" else "Start watching") } + Button( + onClick = finish, + enabled = !saving, + modifier = Modifier + .focusRequester(primaryFocus) + .focusProperties { + up = if (stage == null) FocusRequester.Cancel else choiceFocus + left = if (stageIndex > 0) backFocus else FocusRequester.Cancel + }, + ) { Text(if (saving) "Saving…" else "Start watching") } } } } + if (confirmingSkip) { + OnboardingSkipConfirmation( + onKeepChoosing = { confirmingSkip = false }, + onSkip = onSkip, + ) + } + } +} + +@Composable +@OptIn(ExperimentalComposeUiApi::class) +private fun OnboardingSkipConfirmation( + onKeepChoosing: () -> Unit, + onSkip: () -> Unit, +) { + val keepFocus = remember { FocusRequester() } + val skipFocus = remember { FocusRequester() } + LaunchedEffect(Unit) { + kotlinx.coroutines.delay(16L) + runCatching { keepFocus.requestFocus() } + } + Box( + Modifier.fillMaxSize().zIndex(20f).background(Color.Black.copy(alpha = 0.82f)), + contentAlignment = Alignment.Center, + ) { + Column( + modifier = Modifier + .width(480.dp) + .background(Color(0xFF20262B), RoundedCornerShape(18.dp)) + .padding(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(18.dp), + ) { + Text( + "Skip taste setup for now?", + color = Color.White, + fontSize = 24.sp, + fontWeight = FontWeight.SemiBold, + ) + Text( + "You can start watching now. Memby may offer these choices again later.", + color = Color(0xFFBCC4CA), + fontSize = 16.sp, + textAlign = TextAlign.Center, + ) + Row(horizontalArrangement = Arrangement.spacedBy(14.dp)) { + Button( + onClick = onKeepChoosing, + modifier = Modifier + .focusRequester(keepFocus) + .focusProperties { + left = FocusRequester.Cancel + right = skipFocus + up = FocusRequester.Cancel + down = FocusRequester.Cancel + }, + ) { Text("Keep choosing") } + Button( + onClick = onSkip, + modifier = Modifier + .focusRequester(skipFocus) + .focusProperties { + left = keepFocus + right = FocusRequester.Cancel + up = FocusRequester.Cancel + down = FocusRequester.Cancel + }, + ) { Text("Skip for now") } + } + } } } @@ -1125,17 +1320,22 @@ private fun RecommendationOnboardingScreen( private fun OnboardingTitleRow( items: List, ratings: MutableMap, + entryFocus: FocusRequester, + footerFocus: FocusRequester, previewArtwork: ImageBitmap? = null, ) { val repo = ServiceLocator.repository LazyRow(horizontalArrangement = Arrangement.spacedBy(18.dp), modifier = Modifier.fillMaxWidth()) { - items(items, key = { it.id }) { item -> + itemsIndexed(items, key = { _, item -> item.id }) { index, item -> val selected = ratings[item.id] == 5 FocusScaleContainer( onFocused = {}, onClick = { if (selected) ratings.remove(item.id) else ratings[item.id] = 5 }, contentDescription = "${item.name}${if (selected) ", selected" else ""}", - modifier = Modifier.width(148.dp), + modifier = Modifier + .width(148.dp) + .then(if (index == 0) Modifier.focusRequester(entryFocus) else Modifier) + .focusProperties { down = footerFocus }, ) { focused -> Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Box( @@ -1164,17 +1364,22 @@ private fun OnboardingTitleRow( private fun OnboardingPeopleRow( people: List, selectedPeople: MutableMap, + entryFocus: FocusRequester, + footerFocus: FocusRequester, previewArtwork: ImageBitmap? = null, ) { val repo = ServiceLocator.repository LazyRow(horizontalArrangement = Arrangement.spacedBy(18.dp), modifier = Modifier.fillMaxWidth()) { - items(people, key = { it.name }) { person -> + itemsIndexed(people, key = { _, person -> person.name }) { index, person -> val selected = selectedPeople[person.name] == true FocusScaleContainer( onFocused = {}, onClick = { selectedPeople[person.name] = !selected }, contentDescription = "${person.name}${if (selected) ", selected" else ""}", - modifier = Modifier.width(148.dp), + modifier = Modifier + .width(148.dp) + .then(if (index == 0) Modifier.focusRequester(entryFocus) else Modifier) + .focusProperties { down = footerFocus }, ) { focused -> Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(10.dp)) { Box( @@ -1253,11 +1458,12 @@ private fun ProfileDeleteButton( modifier: Modifier = Modifier, ) { var focused by remember { mutableStateOf(false) } - Box( + Row( modifier = modifier - .size(28.dp) + .width(124.dp) + .height(34.dp) .zIndex(2f) - .clip(CircleShape) + .clip(RoundedCornerShape(9.dp)) .background( when { !enabled -> Color(0xFF343A3F) @@ -1268,29 +1474,32 @@ private fun ProfileDeleteButton( .border( width = if (focused) 2.dp else 1.dp, color = if (focused) Color.White else Color.White.copy(alpha = 0.55f), - shape = CircleShape, + shape = RoundedCornerShape(9.dp), ) .onFocusChanged { focused = it.isFocused } .clickable(enabled = enabled, onClick = onClick) .semantics { contentDescription = "Remove $profileName" }, - contentAlignment = Alignment.Center, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, ) { Text( - text = "×", + text = "Remove user", color = if (enabled) Color.White else Color(0xFF899198), - fontSize = 20.sp, - fontWeight = FontWeight.Medium, + fontSize = 12.sp, + fontWeight = FontWeight.SemiBold, ) } } @Composable +@OptIn(ExperimentalComposeUiApi::class) private fun ProfileRemovalConfirmation( profile: EmbyProfile, onCancel: () -> Unit, onConfirm: () -> Unit, ) { val cancelFocus = remember { FocusRequester() } + val removeFocus = remember { FocusRequester() } BackHandler(onBack = onCancel) LaunchedEffect(profile.id) { cancelFocus.requestFocus() } Box( @@ -1321,9 +1530,26 @@ private fun ProfileRemovalConfirmation( Row(horizontalArrangement = Arrangement.spacedBy(14.dp)) { Button( onClick = onCancel, - modifier = Modifier.focusRequester(cancelFocus), + modifier = Modifier + .focusRequester(cancelFocus) + .focusProperties { + left = FocusRequester.Cancel + right = removeFocus + up = FocusRequester.Cancel + down = FocusRequester.Cancel + }, ) { Text("Cancel") } - Button(onClick = onConfirm) { Text("Remove user") } + Button( + onClick = onConfirm, + modifier = Modifier + .focusRequester(removeFocus) + .focusProperties { + left = cancelFocus + right = FocusRequester.Cancel + up = FocusRequester.Cancel + down = FocusRequester.Cancel + }, + ) { Text("Remove user") } } } } @@ -1543,8 +1769,8 @@ private fun HomeScreen( // the activity, the layout and the decoder, none of which needed the answer. A cold // start still resolves first — the pre-roll it opens with needs a stream to run // behind it, and whether there is one to show is part of the same answer. - val ready = repo.readyPlayableForLaunch(item.id) val request = repo.playbackRequest(item) + val ready = repo.readyPlayableForLaunch(request) if (ready == null && request.resumePositionMs > 0L) { playbackLauncher.launch( PlayerActivity.intent( diff --git a/app/src/main/java/com/ponzischeme89/memby/ui/alerts/AlertsPage.kt b/app/src/main/java/com/ponzischeme89/memby/ui/alerts/AlertsPage.kt index a3ded13..4fbd1c3 100644 --- a/app/src/main/java/com/ponzischeme89/memby/ui/alerts/AlertsPage.kt +++ b/app/src/main/java/com/ponzischeme89/memby/ui/alerts/AlertsPage.kt @@ -17,7 +17,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -92,17 +93,35 @@ fun MyAlertsPage( onClose: () -> Unit, modifier: Modifier = Modifier, ) { - val listFocusRequester = remember { FocusRequester() } val actionsFocusRequester = remember { FocusRequester() } + val notificationIds = notifications.map(UserNotification::id) + val rowFocusRequesters = remember(notificationIds) { + List(notificationIds.size) { FocusRequester() } + } + val listState = rememberLazyListState() + var pendingFocusIndex by remember { mutableStateOf(null) } val hasAlerts = notifications.isNotEmpty() LaunchedEffect(hasAlerts) { // One frame for the list to place its first row; an empty page has nothing below // the actions to land on, so the chips take the remote instead. delay(16) runCatching { - if (hasAlerts) listFocusRequester.requestFocus() else actionsFocusRequester.requestFocus() + if (hasAlerts) rowFocusRequesters.first().requestFocus() else actionsFocusRequester.requestFocus() } } + LaunchedEffect(notificationIds) { + val requestedIndex = pendingFocusIndex ?: return@LaunchedEffect + if (notifications.isEmpty()) { + pendingFocusIndex = null + return@LaunchedEffect + } + val targetIndex = alertFocusIndexAfterRemoval(requestedIndex, notifications.size) + ?: return@LaunchedEffect + listState.scrollToItem(targetIndex) + delay(16) + runCatching { rowFocusRequesters[targetIndex].requestFocus() } + pendingFocusIndex = null + } Box(modifier.fillMaxSize().zIndex(9f).background(MembySurface)) { Column( @@ -149,19 +168,20 @@ fun MyAlertsPage( AlertsEmptyState(enabled = preferences.enabled) } else { LazyColumn( + state = listState, modifier = Modifier.fillMaxWidth().weight(1f), contentPadding = PaddingValues(vertical = 6.dp), ) { - items(notifications, key = UserNotification::id) { notification -> + itemsIndexed(notifications, key = { _, notification -> notification.id }) { + index, notification -> AlertRow( notification = notification, - modifier = if (notification.id == notifications.first().id) { - Modifier.focusRequester(listFocusRequester) - } else { - Modifier - }, + modifier = Modifier.focusRequester(rowFocusRequesters[index]), onFocused = { if (notification.unread) onRead(notification) }, - onClick = { onDismiss(notification) }, + onClick = { + pendingFocusIndex = index + onDismiss(notification) + }, ) if (notification.id != notifications.last().id) { Box( @@ -179,6 +199,10 @@ fun MyAlertsPage( } } +/** The row now occupying the removed row's place, or the preceding row at the end. */ +internal fun alertFocusIndexAfterRemoval(removedIndex: Int, remainingCount: Int): Int? = + if (remainingCount <= 0) null else removedIndex.coerceIn(0, remainingCount - 1) + @Composable private fun AlertsHeader(total: Int, unread: Int) { Column( diff --git a/app/src/main/java/com/ponzischeme89/memby/ui/player/CastPanel.kt b/app/src/main/java/com/ponzischeme89/memby/ui/player/CastPanel.kt index b372b0d..82ffb07 100644 --- a/app/src/main/java/com/ponzischeme89/memby/ui/player/CastPanel.kt +++ b/app/src/main/java/com/ponzischeme89/memby/ui/player/CastPanel.kt @@ -8,6 +8,7 @@ import android.view.Gravity import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import android.widget.HorizontalScrollView import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView @@ -19,6 +20,25 @@ data class CastMember( val name: String, val role: String? = null, val imageUrl: String? = null, + val id: String = "", + val deathDate: String? = null, +) + +data class FilmographyCredit( + val title: String, + val year: Int? = null, + val imageUrl: String? = null, +) + +data class CastPersonPanel( + val id: String, + val name: String, + val role: String? = null, + val overview: String? = null, + val birthDate: String? = null, + val deathDate: String? = null, + val filmography: List = emptyList(), + val loaded: Boolean = false, ) /** @@ -32,6 +52,7 @@ data class CastPanelState( val title: String = "", val members: List = emptyList(), val loaded: Boolean = false, + val selectedPerson: CastPersonPanel? = null, ) /** @@ -47,14 +68,20 @@ fun bindCastPanel( overlay: View, state: CastPanelState, loadImage: (ImageView, String) -> Unit = { _, _ -> }, + onMemberClick: (CastMember) -> Unit = {}, ) { val context = overlay.context + val selected = state.selectedPerson + overlay.findViewById(R.id.player_cast_eyebrow).text = context.getString( + if (selected == null) R.string.player_cast_eyebrow else R.string.player_cast_profile_eyebrow, + ) overlay.findViewById(R.id.player_cast_title).apply { - text = state.title - isVisible = state.title.isNotBlank() + text = selected?.name ?: state.title + isVisible = !text.isNullOrBlank() } overlay.findViewById(R.id.player_cast_status).apply { text = when { + selected != null && !selected.loaded -> context.getString(R.string.player_cast_profile_loading) !state.loaded -> context.getString(R.string.player_cast_loading) state.members.isEmpty() -> context.getString(R.string.player_cast_empty) else -> "" @@ -63,14 +90,26 @@ fun bindCastPanel( } val people = overlay.findViewById(R.id.player_cast_people) people.removeAllViews() - state.members.forEach { member -> people.addView(castCard(context, member, loadImage)) } - overlay.findViewById(R.id.player_cast_scroller).isVisible = state.members.isNotEmpty() + state.members.forEach { member -> + people.addView(castCard(context, member, loadImage, onMemberClick)) + } + overlay.findViewById(R.id.player_cast_scroller).isVisible = + selected == null && state.members.isNotEmpty() + overlay.findViewById(R.id.player_cast_profile).apply { + isVisible = selected?.loaded == true + removeAllViews() + selected?.takeIf { it.loaded }?.let { bindPersonProfile(this, it, loadImage) } + } + overlay.findViewById(R.id.player_cast_back_hint).setText( + if (selected == null) R.string.player_back_to_close else R.string.player_cast_back_to_cast, + ) } private fun castCard( context: Context, member: CastMember, loadImage: (ImageView, String) -> Unit, + onClick: (CastMember) -> Unit, ): View { val density = context.resources.displayMetrics.density fun dp(value: Int) = (value * density).toInt() @@ -79,10 +118,7 @@ private fun castCard( orientation = LinearLayout.VERTICAL isFocusable = true isClickable = true - // Nothing happens on a press. The panel is a reference, not a destination — there - // is no person page to open — but the card still has to be focusable, or a D-pad - // cannot scroll the row at all. - setOnClickListener { } + setOnClickListener { onClick(member) } clipChildren = false layoutParams = LinearLayout.LayoutParams(dp(132), ViewGroup.LayoutParams.WRAP_CONTENT).apply { marginEnd = dp(18) @@ -96,17 +132,30 @@ private fun castCard( } addView(castPortrait(context, member, loadImage, ::dp)) - addView( - TextView(context).apply { + addView(LinearLayout(context).apply { + orientation = LinearLayout.HORIZONTAL + gravity = Gravity.CENTER_VERTICAL + layoutParams = LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + ) + setPadding(0, dp(9), 0, 0) + addView(TextView(context).apply { text = member.name setTextColor(Color.WHITE) textSize = 14f typeface = Typeface.create("sans-serif", Typeface.BOLD) maxLines = 1 ellipsize = TextUtils.TruncateAt.END - setPadding(0, dp(9), 0, 0) - }, - ) + }, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)) + if (!member.deathDate.isNullOrBlank()) { + addView(ImageView(context).apply { + setImageResource(R.drawable.ic_deceased) + contentDescription = context.getString(R.string.player_cast_deceased) + setPadding(dp(4), 0, 0, 0) + }, LinearLayout.LayoutParams(dp(18), dp(16))) + } + }) member.role?.takeIf(String::isNotBlank)?.let { role -> addView( TextView(context).apply { @@ -122,6 +171,144 @@ private fun castCard( } } +private fun bindPersonProfile( + container: LinearLayout, + person: CastPersonPanel, + loadImage: (ImageView, String) -> Unit, +) { + val context = container.context + val density = context.resources.displayMetrics.density + fun dp(value: Int) = (value * density).toInt() + val life = personLifeDates(person.birthDate, person.deathDate) + if (life.isNotBlank() || !person.role.isNullOrBlank()) { + container.addView(TextView(context).apply { + text = listOfNotNull( + person.role?.takeIf(String::isNotBlank), + life.takeIf(String::isNotBlank), + ).joinToString(" · ") + setTextColor(Color.rgb(185, 193, 200)) + textSize = 13f + maxLines = 1 + ellipsize = TextUtils.TruncateAt.END + }) + } + container.addView(TextView(context).apply { + text = person.overview?.takeIf(String::isNotBlank) + ?: context.getString(R.string.player_cast_biography_empty) + setTextColor(Color.WHITE) + textSize = 14f + maxLines = 3 + ellipsize = TextUtils.TruncateAt.END + setLineSpacing(0f, 1.08f) + setPadding(0, dp(7), 0, 0) + }) + container.addView(TextView(context).apply { + text = context.getString(R.string.player_cast_filmography) + setTextColor(Color.rgb(82, 181, 75)) + textSize = 11f + typeface = Typeface.create("sans-serif", Typeface.BOLD) + letterSpacing = 0.12f + setPadding(0, dp(13), 0, dp(5)) + }) + if (person.filmography.isEmpty()) { + container.addView(TextView(context).apply { + text = context.getString(R.string.player_cast_filmography_empty) + setTextColor(Color.rgb(185, 193, 200)) + textSize = 13f + }) + return + } + container.addView(HorizontalScrollView(context).apply { + isHorizontalScrollBarEnabled = false + overScrollMode = View.OVER_SCROLL_NEVER + clipChildren = false + addView(LinearLayout(context).apply { + orientation = LinearLayout.HORIZONTAL + clipChildren = false + person.filmography.forEach { credit -> + addView(filmographyCard(context, credit, loadImage, ::dp)) + } + }) + }) +} + +private fun filmographyCard( + context: Context, + credit: FilmographyCredit, + loadImage: (ImageView, String) -> Unit, + dp: (Int) -> Int, +): View = LinearLayout(context).apply { + orientation = LinearLayout.HORIZONTAL + gravity = Gravity.CENTER_VERTICAL + isFocusable = true + isClickable = true + setOnClickListener { } + background = context.getDrawable(R.drawable.player_cast_portrait_background) + foreground = context.getDrawable(R.drawable.player_cast_portrait_frame) + setOnFocusChangeListener { view, focused -> + view.animate() + .scaleX(if (focused) 1.03f else 1f) + .scaleY(if (focused) 1.03f else 1f) + .setDuration(120L) + .start() + } + setPadding(dp(5), dp(5), dp(9), dp(5)) + layoutParams = LinearLayout.LayoutParams(dp(194), dp(68)).apply { marginEnd = dp(10) } + addView(FrameLayout(context).apply { + layoutParams = LinearLayout.LayoutParams(dp(42), dp(58)).apply { marginEnd = dp(9) } + background = context.getDrawable(R.drawable.player_cast_portrait_background) + clipToOutline = true + addView(ImageView(context).apply { + layoutParams = FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ) + scaleType = ImageView.ScaleType.CENTER_CROP + contentDescription = credit.title + credit.imageUrl?.takeIf(String::isNotBlank)?.let { loadImage(this, it) } + }) + }) + addView(LinearLayout(context).apply { + orientation = LinearLayout.VERTICAL + addView(TextView(context).apply { + text = credit.title + setTextColor(Color.WHITE) + textSize = 12f + typeface = Typeface.create("sans-serif", Typeface.BOLD) + maxLines = 2 + ellipsize = TextUtils.TruncateAt.END + }) + credit.year?.let { year -> + addView(TextView(context).apply { + text = year.toString() + setTextColor(Color.rgb(158, 168, 178)) + textSize = 11f + }) + } + }, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)) +} + +internal fun personLifeDates(birthDate: String?, deathDate: String?): String { + val birth = formatPersonDate(birthDate) + val death = formatPersonDate(deathDate) + return when { + birth.isNotBlank() && death.isNotBlank() -> "$birth – $death" + birth.isNotBlank() -> "Born $birth" + death.isNotBlank() -> "Died $death" + else -> "" + } +} + +private fun formatPersonDate(value: String?): String { + val match = Regex("""^(\d{4})-(\d{2})-(\d{2})""").find(value.orEmpty()) ?: return "" + val (year, month, day) = match.destructured + val monthName = listOf( + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", + ).getOrNull(month.toIntOrNull()?.minus(1) ?: -1) ?: return year + return "${day.toIntOrNull() ?: day} $monthName $year" +} + /** * The portrait, with the person's initials underneath it. * diff --git a/app/src/main/java/com/ponzischeme89/memby/ui/player/PlaybackRecovery.kt b/app/src/main/java/com/ponzischeme89/memby/ui/player/PlaybackRecovery.kt index 05543e7..76034ff 100644 --- a/app/src/main/java/com/ponzischeme89/memby/ui/player/PlaybackRecovery.kt +++ b/app/src/main/java/com/ponzischeme89/memby/ui/player/PlaybackRecovery.kt @@ -87,3 +87,11 @@ internal fun automaticRetryDelayMs(attempt: Int): Long? = 2 -> 3_000L else -> null } + +/** One prolonged mid-programme rebuffer gets a lower-risk H.264 stream. */ +internal fun shouldRecoverProlongedRebuffer( + renderedFirstFrame: Boolean, + prerollActive: Boolean, + seekBuffering: Boolean, + recoveryAttempted: Boolean, +): Boolean = renderedFirstFrame && !prerollActive && !seekBuffering && !recoveryAttempted diff --git a/app/src/main/java/com/ponzischeme89/memby/ui/player/PlayerActivity.kt b/app/src/main/java/com/ponzischeme89/memby/ui/player/PlayerActivity.kt index 5f9cd63..ce146a0 100644 --- a/app/src/main/java/com/ponzischeme89/memby/ui/player/PlayerActivity.kt +++ b/app/src/main/java/com/ponzischeme89/memby/ui/player/PlayerActivity.kt @@ -76,6 +76,7 @@ import com.ponzischeme89.memby.data.normalizeSkipIntroMode import com.ponzischeme89.memby.data.resolveCast import com.ponzischeme89.memby.data.selectSubtitleId import com.ponzischeme89.memby.data.model.EmbyPerson +import com.ponzischeme89.memby.data.model.BaseItem import com.ponzischeme89.memby.data.model.GatewayPrerollEntry import com.ponzischeme89.memby.data.model.GatewayPrerollSchedule import com.ponzischeme89.memby.data.model.GatewaySeasonFinale @@ -86,6 +87,9 @@ import com.ponzischeme89.memby.ui.ServiceAlertBanner import com.ponzischeme89.memby.ui.randomWelcomeQuote import com.ponzischeme89.memby.ui.theme.MembyTheme import kotlinx.coroutines.Job +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first @@ -134,6 +138,8 @@ class PlayerActivity : ComponentActivity() { private var streamStatusView: TextView? = null private var retryJob: Job? = null private var stablePlaybackJob: Job? = null + private var prolongedRebufferJob: Job? = null + private var prolongedRebufferRecoveryAttempted = false private var automaticRetryAttempt = 0 private var renderedFirstFrame = false private var requestStartedAtMs = 0L @@ -160,6 +166,7 @@ class PlayerActivity : ComponentActivity() { private var prerollEpisodeCode = "" private var prerollRuntimeMs = 0L private var prerollDurationMs = DEFAULT_PREROLL_DURATION_MS + private var configuredPrerollDurationMs = DEFAULT_PREROLL_DURATION_MS private var pausePosterUrl: String? = null private var pauseOverlay: View? = null private var nowPlayingGroup: View? = null @@ -222,7 +229,13 @@ class PlayerActivity : ComponentActivity() { private var castJob: Job? = null private var castPeople: List = emptyList() private var castLoaded = false + private var castProfiles: Map = emptyMap() + private var selectedCastPerson: CastPersonPanel? = null + private var castPersonJob: Job? = null private var prerollView: View? = null + private var localPrerollPlayer: ExoPlayer? = null + private var localPrerollView: PlayerView? = null + private var localPrerollListener: Player.Listener? = null private var prerollTimerJob: Job? = null private var prerollScheduleJob: Job? = null private var prerollActive = true @@ -391,10 +404,11 @@ class PlayerActivity : ComponentActivity() { val resumePositionMs = intent.getLongExtra(EXTRA_RESUME_POSITION_MS, 0L) initialResumePositionMs = resumePositionMs.coerceAtLeast(0L) val prerollEnabled = intent.getBooleanExtra(EXTRA_PREROLL_ENABLED, true) - prerollDurationMs = intent.getLongExtra( + configuredPrerollDurationMs = intent.getLongExtra( EXTRA_PREROLL_DURATION_MS, DEFAULT_PREROLL_DURATION_MS, ).coerceIn(MIN_PREROLL_DURATION_MS, MAX_PREROLL_DURATION_MS) + prerollDurationMs = configuredPrerollDurationMs val showPreroll = shouldShowPreroll(resumePositionMs, prerollEnabled) val subtitles = decodeSubtitles(intent.getStringExtra(EXTRA_SUBTITLES)) availableSubtitles = subtitles @@ -489,8 +503,11 @@ class PlayerActivity : ComponentActivity() { // with it, so the ceiling falls before the overlay goes up. stepDownCreditsSpeed() if (!prerollActive && !seekBuffering) showPlaybackLoading() + scheduleProlongedRebufferRecovery() } Player.STATE_READY -> { + prolongedRebufferJob?.cancel() + prolongedRebufferJob = null trace.mark(PlaybackTrace.READY) endSeekBuffering() hidePlaybackError() @@ -551,10 +568,10 @@ class PlayerActivity : ComponentActivity() { renderedFirstFrame = true endSeekBuffering() hidePlaybackLoading() - if (!playbackStarted) { + if (!playbackStarted && !prerollActive) { startPlaybackSession(playback) } - if (prerollActive) { + if (prerollActive && localPrerollPlayer == null) { startPrerollCountdown() } endFirstFrameTrace() @@ -724,24 +741,127 @@ class PlayerActivity : ComponentActivity() { useController = false hideController() } - enterPrerollVideoFrame() + if (!attachLocalPreroll()) enterPrerollVideoFrame() bindPrerollNow() bindPrerollSchedule(GatewayPrerollSchedule(), loading = true) prerollScheduleJob = lifecycleScope.launch { val schedule = ServiceLocator.repository.prerollSchedule() if (prerollActive) bindPrerollSchedule(schedule, loading = false) } + bindInitialPrerollCountdown() + } + + private fun attachLocalPreroll(): Boolean = runCatching { + val host = findViewById(R.id.player_preroll_video_host) + val playback = PrerollPreloader.acquire(this) + localPrerollPlayer = playback + val view = PlayerView(this).apply { + useController = false + resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT + player = playback + } + val listener = object : Player.Listener { + override fun onPlaybackStateChanged(playbackState: Int) { + when (playbackState) { + Player.STATE_READY -> { + updatePrerollDuration(playback.duration) + if (playback.playWhenReady) startPrerollCountdown() + } + Player.STATE_ENDED -> completePrerollCountdown() + else -> Unit + } + } + + override fun onRenderedFirstFrame() { + updatePrerollDuration(playback.duration) + startPrerollCountdown() + } + + override fun onPlayerError(error: PlaybackException) { + fallbackFromLocalPreroll(error) + } + } + localPrerollView = view + localPrerollListener = listener + playback.addListener(listener) + updatePrerollDuration(playback.duration) + host.addView( + view, + 0, + FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ), + ) + playback.seekTo(0L) + if (playback.playbackState == Player.STATE_IDLE) playback.prepare() + playback.playWhenReady = lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) + true + }.getOrElse { error -> + Log.w(PLAYBACK_LOG_TAG, "event=preroll_attach_failed", error) + disposeLocalPreroll(reuse = false) + false + } + + private fun updatePrerollDuration(clipDurationMs: Long) { + val effective = effectivePrerollDurationMs(clipDurationMs, configuredPrerollDurationMs) + if (effective == prerollDurationMs) return + prerollDurationMs = effective + if (prerollTimerJob == null) bindInitialPrerollCountdown() + } + + private fun bindInitialPrerollCountdown() { + val seconds = ceil(prerollDurationMs / 1_000.0).toInt().coerceAtLeast(1) findViewById(R.id.player_preroll_countdown).setCountdown( - seconds = ceil(prerollDurationMs / 1_000.0).toInt(), + seconds = seconds, progress = 1f, description = resources.getQuantityString( R.plurals.player_preroll_countdown, - ceil(prerollDurationMs / 1_000.0).toInt(), - ceil(prerollDurationMs / 1_000.0).toInt(), + seconds, + seconds, ), ) } + private fun completePrerollCountdown() { + if (!prerollActive || prerollMinimumElapsed) return + prerollTimerJob?.cancel() + prerollTimerJob = null + prerollMinimumElapsed = true + findViewById(R.id.player_preroll_countdown).setCountdown( + seconds = 0, + progress = 0f, + description = getString(R.string.player_preroll_starting), + ) + beginContentWhenReady() + } + + private fun fallbackFromLocalPreroll(error: PlaybackException) { + if (!prerollActive) return + Log.w( + PLAYBACK_LOG_TAG, + "event=preroll_failed code=${error.errorCode} name=${error.errorCodeName}", + error, + ) + disposeLocalPreroll(reuse = false) + prerollDurationMs = configuredPrerollDurationMs + if (prerollTimerJob == null) bindInitialPrerollCountdown() + enterPrerollVideoFrame() + player?.playWhenReady = lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) + if (player?.playbackState == Player.STATE_READY) startPrerollCountdown() + } + + private fun disposeLocalPreroll(reuse: Boolean) { + val playback = localPrerollPlayer ?: return + localPrerollListener?.let(playback::removeListener) + localPrerollView?.player = null + (localPrerollView?.parent as? ViewGroup)?.removeView(localPrerollView) + localPrerollListener = null + localPrerollView = null + localPrerollPlayer = null + if (reuse) PrerollPreloader.recycle(playback) else PrerollPreloader.discard(playback) + } + /** Resume is a continuation, not a new programme start, so it bypasses the pre-roll. */ private fun startWithoutPreroll() { prerollView = findViewById(R.id.player_preroll).also { @@ -781,7 +901,8 @@ class PlayerActivity : ComponentActivity() { val nowMs = SystemClock.elapsedRealtime() if (prerollCountdownAdvances( lifecycleStarted = lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED), - playbackReady = player?.playbackState == Player.STATE_READY, + playbackReady = localPrerollPlayer?.isPlaying + ?: (player?.playbackState == Player.STATE_READY), ) ) { watchedMs = (watchedMs + nowMs - lastTickMs).coerceAtMost(durationMs) @@ -789,13 +910,7 @@ class PlayerActivity : ComponentActivity() { lastTickMs = nowMs } if (!prerollActive) return@launch - prerollMinimumElapsed = true - countdown.setCountdown( - seconds = 0, - progress = 0f, - description = getString(R.string.player_preroll_starting), - ) - beginContentWhenReady() + completePrerollCountdown() } } @@ -893,6 +1008,7 @@ class PlayerActivity : ComponentActivity() { } private fun finishPrerollHandOff(playback: Player) { + disposeLocalPreroll(reuse = true) restoreFullscreenPlayer() prerollView?.visibility = View.GONE playerView?.apply { @@ -1077,6 +1193,8 @@ class PlayerActivity : ComponentActivity() { playerView?.useController = true } retryJob?.cancel() + prolongedRebufferJob?.cancel() + prolongedRebufferJob = null stablePlaybackJob?.cancel() // The skip is over however it ended; nothing after this may be withheld on its // account, least of all the reconnecting notice or the error screen. @@ -1121,6 +1239,8 @@ class PlayerActivity : ComponentActivity() { private fun retryPlayback(refreshSource: Boolean, forceTranscode: Boolean = false) { retryJob?.cancel() + prolongedRebufferJob?.cancel() + prolongedRebufferJob = null hidePlaybackError() val playback = player ?: return // A launch whose resolution failed has no media item to re-prepare and no playhead @@ -1193,6 +1313,39 @@ class PlayerActivity : ComponentActivity() { } } + /** + * MediaCodec does not always throw when a marginal HEVC decoder wedges; some vendor + * implementations remain in BUFFERING indefinitely. After a frame has already played, + * one sustained, non-seek rebuffer is enough evidence to request the same compatibility + * fallback used for an explicit decoder error. The one-attempt bound prevents a slow + * server from repeatedly restarting a title that is already being transcoded. + */ + private fun scheduleProlongedRebufferRecovery() { + if (!shouldRecoverProlongedRebuffer( + renderedFirstFrame = renderedFirstFrame, + prerollActive = prerollActive, + seekBuffering = seekBuffering, + recoveryAttempted = prolongedRebufferRecoveryAttempted, + ) || prolongedRebufferJob?.isActive == true + ) return + + val bufferingItem = itemId + prolongedRebufferJob = lifecycleScope.launch { + delay(PROLONGED_REBUFFER_RECOVERY_MS) + val playback = player ?: return@launch + if (itemId != bufferingItem || playback.playbackState != Player.STATE_BUFFERING) { + return@launch + } + prolongedRebufferRecoveryAttempted = true + Log.w( + PLAYBACK_LOG_TAG, + "event=prolonged_rebuffer item=${itemId.orEmpty()} " + + "positionMs=${playback.currentPosition} playMethod=$playMethod", + ) + retryPlayback(refreshSource = true, forceTranscode = playMethod != "Transcode") + } + } + private fun showPlaybackError(failure: PlaybackFailure) { hidePlaybackLoading() playerView?.hideController() @@ -2664,6 +2817,9 @@ class PlayerActivity : ComponentActivity() { initialResumePositionMs = next.resumePositionMs.coerceAtLeast(0L) renderedFirstFrame = false automaticRetryAttempt = 0 + prolongedRebufferJob?.cancel() + prolongedRebufferJob = null + prolongedRebufferRecoveryAttempted = false resetTimeRemainingCue() // A skip aimed at the outgoing episode must not land in the incoming one. resetSeekControls() @@ -2746,6 +2902,8 @@ class PlayerActivity : ComponentActivity() { // activity before our UP handler gets a chance to hide the active surface. if (event.action == KeyEvent.ACTION_UP) { when { + castOverlay?.isVisible == true && selectedCastPerson != null -> + showCastList() castOverlay?.isVisible == true -> hideCastOverlay() // The drop-up's download half is a level of its own, so Back leaves it // before it leaves the menu — one press per level, never two at once. @@ -2964,8 +3122,11 @@ class PlayerActivity : ComponentActivity() { private fun loadCast() { castJob?.cancel() + castPersonJob?.cancel() castLoaded = false castPeople = emptyList() + castProfiles = emptyMap() + selectedCastPerson = null val requestedItemId = itemId?.takeIf(String::isNotBlank) ?: run { castLoaded = true bindCastOverlay() @@ -2981,6 +3142,18 @@ class PlayerActivity : ComponentActivity() { castLoaded = true bindCastOverlay() } + val profiles = coroutineScope { + loaded.filter { it.id.isNotBlank() }.map { person -> + async { + runCatching { ServiceLocator.repository.getPersonDetails(person.id) } + .getOrNull() + } + }.awaitAll().filterNotNull().associateBy(BaseItem::id) + } + if (itemId == requestedItemId) { + castProfiles = profiles + bindCastOverlay() + } } } @@ -2992,10 +3165,63 @@ class PlayerActivity : ComponentActivity() { } private fun hideCastOverlay() { + castPersonJob?.cancel() + selectedCastPerson = null castOverlay?.visibility = View.GONE playerView?.showController() } + private fun showCastList() { + castPersonJob?.cancel() + selectedCastPerson = null + bindCastOverlay() + } + + private fun showCastPerson(member: CastMember) { + if (member.id.isBlank()) return + selectedCastPerson = CastPersonPanel( + id = member.id, + name = member.name, + role = member.role, + ) + bindCastOverlay() + castPersonJob?.cancel() + castPersonJob = lifecycleScope.launch { + val personId = member.id + val (profile, filmography) = coroutineScope { + val profileRequest = async { + runCatching { + castProfiles[personId] + ?: ServiceLocator.repository.getPersonDetails(personId) + }.getOrNull() + } + val filmographyRequest = async { + runCatching { ServiceLocator.repository.getPersonFilmography(personId) } + .getOrDefault(emptyList()) + } + profileRequest.await() to filmographyRequest.await() + } + if (selectedCastPerson?.id != personId) return@launch + selectedCastPerson = CastPersonPanel( + id = personId, + name = profile?.name?.ifBlank { member.name } ?: member.name, + role = member.role, + overview = profile?.overview, + birthDate = profile?.premiereDate, + deathDate = profile?.endDate, + filmography = filmography.map { item -> + FilmographyCredit( + title = item.name, + year = item.productionYear, + imageUrl = ServiceLocator.repository.primaryUrl(item, 160), + ) + }, + loaded = true, + ) + bindCastOverlay() + } + } + private fun bindCastOverlay() { val overlay = castOverlay ?: return bindCastPanel( @@ -3003,23 +3229,36 @@ class PlayerActivity : ComponentActivity() { state = CastPanelState( title = playbackTitle, members = castPeople.map { person -> + val profile = castProfiles[person.id] CastMember( name = person.name, role = person.role, imageUrl = ServiceLocator.repository.personImageUrl(person, 320), + id = person.id, + deathDate = profile?.endDate, ) }, loaded = castLoaded, + selectedPerson = selectedCastPerson, ), // Coil is handed in rather than reached for inside the panel, which is what lets // the screenshot test render the same cards without a network. loadImage = { view, url -> view.load(url) }, + onMemberClick = ::showCastPerson, ) if (overlay.isVisible) { - overlay.findViewById(R.id.player_cast_people) - .getChildAt(0) - ?.requestFocus() - ?: overlay.requestFocus() + if (selectedCastPerson == null) { + overlay.findViewById(R.id.player_cast_people) + .getChildAt(0) + ?.requestFocus() + ?: overlay.requestFocus() + } else { + overlay.findViewById(R.id.player_cast_profile) + .getFocusables(View.FOCUS_FORWARD) + .firstOrNull() + ?.requestFocus() + ?: overlay.requestFocus() + } } } @@ -3482,7 +3721,9 @@ class PlayerActivity : ComponentActivity() { override fun onStart() { super.onStart() if (prerollActive) { - player?.playWhenReady = true + // The local clip owns the opening frame. The main title is prepared behind + // the overlay but remains paused at zero until the hand-off. + localPrerollPlayer?.play() ?: run { player?.playWhenReady = true } } beginContentWhenReady() if (stoppedInBackground && playbackStarted) { @@ -3507,6 +3748,7 @@ class PlayerActivity : ComponentActivity() { } } super.onStop() + localPrerollPlayer?.pause() player?.pause() } @@ -3518,6 +3760,7 @@ class PlayerActivity : ComponentActivity() { pendingResolveJob?.cancel() prerollTimerJob?.cancel() prerollScheduleJob?.cancel() + disposeLocalPreroll(reuse = true) playbackStartCueJob?.cancel() timeRemainingHideJob?.cancel() seekCommitJob?.cancel() @@ -3527,12 +3770,14 @@ class PlayerActivity : ComponentActivity() { seasonFinaleHideJob?.cancel() prerollView?.findViewById(R.id.player_preroll_video_host)?.animate()?.cancel() castJob?.cancel() + castPersonJob?.cancel() subtitleSearchJob?.cancel() nextUpJob?.cancel() creditsSpeedJob?.cancel() creditsView?.animate()?.cancel() retryJob?.cancel() stablePlaybackJob?.cancel() + prolongedRebufferJob?.cancel() playbackIdentityHideJob?.cancel() playbackIdentityView?.animate()?.cancel() loadingAnimator?.cancel() @@ -3891,6 +4136,7 @@ class PlayerActivity : ComponentActivity() { private const val NO_TRACE = -1 private const val FRESH_STREAM_RETRY_ATTEMPT = 2 private const val STABLE_PLAYBACK_RESET_MS = 30_000L + private const val PROLONGED_REBUFFER_RECOVERY_MS = 12_000L private const val PLAYBACK_LOG_TAG = "MembyPlayback" private fun playbackStateName(state: Int): String = @@ -3949,10 +4195,15 @@ internal fun prerollCanHandOff( lifecycleStarted: Boolean, ): Boolean = active && minimumElapsed && lifecycleStarted -/** Preroll intentionally pauses on its first frame, so readiness—not isPlaying—drives time. */ +/** The caller supplies either the local clip's playing state or the legacy frame's readiness. */ internal fun prerollCountdownAdvances(lifecycleStarted: Boolean, playbackReady: Boolean): Boolean = lifecycleStarted && playbackReady +/** A valid packaged clip owns the gate; the server duration remains the failure fallback. */ +internal fun effectivePrerollDurationMs(clipDurationMs: Long, configuredDurationMs: Long): Long = + clipDurationMs.takeIf { it > 0L && it != C.TIME_UNSET } + ?: configuredDurationMs.coerceAtLeast(1L) + /** A positive position means the viewer is continuing something already started. */ internal fun shouldShowPreroll(resumePositionMs: Long, enabled: Boolean = true): Boolean = enabled && resumePositionMs <= 0L diff --git a/app/src/main/java/com/ponzischeme89/memby/ui/player/PrerollPreloader.kt b/app/src/main/java/com/ponzischeme89/memby/ui/player/PrerollPreloader.kt new file mode 100644 index 0000000..98980c1 --- /dev/null +++ b/app/src/main/java/com/ponzischeme89/memby/ui/player/PrerollPreloader.kt @@ -0,0 +1,107 @@ +package com.ponzischeme89.memby.ui.player + +import android.content.Context +import android.net.Uri +import android.os.Handler +import android.os.Looper +import androidx.annotation.MainThread +import androidx.media3.common.MediaItem +import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.ExoPlayer +import com.ponzischeme89.memby.R + +/** + * Keeps Memby's short local preroll prepared between playback sessions. + * + * Application start only registers an idle callback. Player construction and local-file + * preparation therefore begin after the launcher's queued start-up work has drained, and + * never block [android.app.Application.onCreate] or a home request. The same player is + * sought back to the beginning and prepared again after use rather than reconstructed for + * every title. + */ +@UnstableApi +internal object PrerollPreloader { + private val mainHandler = Handler(Looper.getMainLooper()) + private var applicationContext: Context? = null + private var preloadScheduled = false + private var cachedPlayer: ExoPlayer? = null + + fun start(context: Context) { + applicationContext = context.applicationContext + mainHandler.post(::scheduleAtMainQueueIdle) + } + + private fun scheduleAtMainQueueIdle() { + if (preloadScheduled || cachedPlayer?.playbackState == ExoPlayer.STATE_READY) return + preloadScheduled = true + Looper.myQueue().addIdleHandler { + preloadScheduled = false + val context = applicationContext + if (context != null) { + val cached = cachedPlayer + if (cached == null) { + cachedPlayer = createPreparedPlayer(context) + } else if (cached.playbackState == ExoPlayer.STATE_IDLE) { + cached.prepare() + } + } + false + } + } + + /** Takes ownership of the prepared player until [recycle] or [discard] is called. */ + @MainThread + fun acquire(context: Context): ExoPlayer { + checkMainThread() + applicationContext = context.applicationContext + val prepared = cachedPlayer + cachedPlayer = null + if (prepared != null && prepared.playerError == null) return prepared + prepared?.release() + return createPreparedPlayer(context.applicationContext) + } + + /** + * Returns a healthy player to the process cache without holding a second decoder while + * the requested programme is playing. [start] prepares it again when Home next opens. + */ + @MainThread + fun recycle(player: ExoPlayer) { + checkMainThread() + if (player.playerError != null) { + discard(player) + return + } + player.playWhenReady = false + player.pause() + player.stop() + player.seekTo(0L) + cachedPlayer?.release() + cachedPlayer = player + } + + /** Drops a failed or otherwise unusable instance; the next Home/start call replaces it. */ + @MainThread + fun discard(player: ExoPlayer) { + checkMainThread() + if (cachedPlayer === player) cachedPlayer = null + player.release() + } + + private fun createPreparedPlayer(context: Context): ExoPlayer = + PlayerEngine.create(context).apply { + setMediaItem( + MediaItem.fromUri( + Uri.parse("android.resource://${context.packageName}/${R.raw.emby_preroll}"), + ), + ) + playWhenReady = false + prepare() + } + + private fun checkMainThread() { + check(Looper.myLooper() == Looper.getMainLooper()) { + "The preroll player must be transferred on the main thread" + } + } +} diff --git a/app/src/main/res/drawable/ic_deceased.xml b/app/src/main/res/drawable/ic_deceased.xml new file mode 100644 index 0000000..2b7ce3c --- /dev/null +++ b/app/src/main/res/drawable/ic_deceased.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/layout/player_cast_overlay.xml b/app/src/main/res/layout/player_cast_overlay.xml index 960d37c..6c2edf7 100644 --- a/app/src/main/res/layout/player_cast_overlay.xml +++ b/app/src/main/res/layout/player_cast_overlay.xml @@ -23,9 +23,10 @@ android:paddingStart="48dp" android:paddingTop="20dp" android:paddingEnd="48dp" - android:paddingBottom="34dp"> + android:paddingBottom="54dp"> + + 69b9&m=v~AnAJ#E{zjcMDqZM&y!+wO1XyzhE{oag?|uk-wSYGp<2 zymnSrX6(3PN5#%n0002r*wM|_%)!PA000L3)qma$`p$;*R<=y^0002e*#75F00^v` zm65(90Dv1n(!vL}_4yE0on|yp!~^2FTa#%iNjcAfGF4kN>;wRS001zGZW*xv0C>#R zmj4ti<;;g(InczdDEa%QY305FiBo6B*!t*xsulo1L~>$QWcfLL3;Oqiz^Q*FP}F}( zkN-k||4LBozY_BKuSDbfD!%cEVUYBNVbX>n%yjd##+*<$cTAw$J5I%VA~}rtscQar$@99SW=F z&r1{RP)t_pS{j`gGCl8oC{sc-)G1zLZT{<41@v?t2Q@$*Dyc{#6kHA#v2GCQ}UTPta7{mal4h;JVyX*J*E^OQda%xiEhfJu8M zH-1B`8YsTghF_)M)1|Raq?B%*ulk^zcd7cv=|u>19fMj>xIm+6&Aw9M{ut<(ciWi8T`U*hc*#pzr?1To%4|Be=ZiYOkw3%nmDlDb(ZeH_yV)>DYhk2P2M znL}!5^6Y#CwE<9kg$$yY1H9Gmbb)?dv2yIgOdqJ4&4Y$A{0B5dP9VTGzog{^L=v>C zUr8k3s-%7CUJbtWq$`*qqnO~3SNfF z8S;(?+-83k)ls#itIKITzlVNNl?Ks4%3eGa&4f{dsX13HJKjfF*pNs}wVJ8Wf}+#s zTK0FG#Jl9^0PIu8Egpz8<;+zaqJSR-63w=D4R|%*ZSaKrsweLNx&Bc>KxN|<;wok| zB~@z5fsnIUkb-X~=>hx&Z_}hOk8W&m?>Wi28u^#E=3L8mwV>d`Yq5FL#D}|z4o&K< zN04*`ZcYY<%@bh={pJJNDZ5UFYFOA)6wOn`k9%!6q+eQotLLNrq_vVA7AOYKPZx_? zWr5`*69@4|n!Zs+*!<$C;C5vgbl4cMH@#fEiWPTpv&p5m55wGnk-(oGaB7k~+?`>F8igTBFK{z864*62B3?wC|h?jSr;EdJy7;^?oN>_C9G2ln1BSME>@Xz2CsUN9q%B zwiCX5a$_~!Xw-jA0&8{mYw8TF#{Qj=a-=Kqqe4PlD#V+jyrM0Vn804+Re^99^>w4i zK`utE{fRY6`A`&sl4L4S{VB9l8Z(-_zg?DV+_UtYf-v=XSj6tVG*5P_z~r;0mE8mM zP%A>bIrc=~c5s33u43F(7X>?2(PHaLbiy=yxCIPXby*O zQ0}djniG+M7lAMqN-` zs)|Z)-@(Ax&eSm%}q={%PsCJ-sHi0i@qMZ4AE! zev=G>D^+zOPdydj(pJg3vS)=Bf4W=B7+PE5MPG+$8B!g_yK7tkn- zI?yow^(<)e$NPa{XZ)&b26;?btpvPAT0g*pe?`JvL*PVEg~fIl!q%!@dAnyOsSO zr69triZhF{U6B}|(FLgcM=z2I&+IWCdgyPak+QFlCe4Lfp!GIB%F6wa(t>k8H(VM0 zyh~|b!ug$=Ec!lY5q$d=>5a$%@1WX7x~x8;mw=C^CKrKgU4?O75-!#@)2A`gO&Z>kmR&kJG=%JJC5PLd8I zv^wG4terR0Xm)Qz1h>d7fRTgxn4{>kArXNtH!*AoW9`rpc2#%!HH6M`jl zDFWnT4+XrtFMIhY3_S!aAoC8@SNJw%!PZWHPJo5+Rq17d>zw9-l`MU}{+KQ*T_7pD zf~%p|Jg?pP@hikVeK=t8vDcVAcHUW{{#{e%88j*hI>8Uu<9E1owf-zI9*G)82Yzd} zV^ogsSOiVNFb|Gk;wf(bHa;3kKZTa(mO=OIdPa03Duycesb$2#{=uQ8M;kF4!{9js zFH@k2TTKhbST}3Hr?$L$49vxg)iAQbDJx3$u1?^cEdXa0Tq-$7fIdOgbYxW!wHOq- zx^0|^?<}4L^qzM3>u{?0fC?Tl$EKM5a8kjQ+Td{5{hgW{p|LuNn&>QEk8}wj#&so5 zJ^6#OmOIMmZWJFJM2tq}qY&IRdSE$3Jods1kr=}K0fWNl;TToGfk*(F=wScp5aD}^ z3&aFhL;}sjbK@`1yylxRJ3uTbCY|A(g?ci$!g#mK!V&l(^t-hh8o(K=wb)1fXpG9XQCP&@ z?Nl&O6SHbT=FcGBk}%p@+=K|rmyLeyvUGm8jy+9xoM*Fv)?t(N!Db}W+Bn_TFeB2z z^U_Xyo8H2kL94o5q7VbJd7wL5{uH9qHblxy*c^OI5`qTsF zYL<1{PLW?)8|J{q7mCDo0?W;b?Me_i5q9_HR%|lvsm-7yVAKP#LQ?H1<;_TSbB!g! zR4Z}!Qh5@GzqgHaPhE^KReyXrAQrA3H0{k@xT)Iy-_#gE0RSSHz$ae6FdPWQT*pUn zLpk#B+6LNNm*uoG_?*jRFSNm6`=5Bos|wFrNer))m}KVqHsu#gh?;V)l}h&HGmW zc$GDNklQhI)%!-i@zdI0?2{)j=(8*n^wPsT$pJ(N7|u71ruc=Dh*CtdmBVWZ(ut2J zbI$MqioZPx6#C6dmgDMvrSWzj^`ObEA|>t`r5*h!ajjszOt}2xHQPQGhnKzvpw#5L zxuVakO+)b2wdfN`8@)7n&Fyb`nrqlwDfR*lgsuCYDyU4=B%zen@Rbg1ESV)>9+Kvl z{EM;e9D9? z4H~S)A^VT0U=%HdcJ?Hrhezm*-S)zsw#Yr2Fp6tQsQOfs^m9@MWQ|MfDTmQ*Zw{Sd zOIpE&3;H_}@HwA1yfjY!abLbgQaWgkPdf||T6J|&U7={KE}~@gXo5`gFUKwUC zcVHG=#f{mcCB+KQ)XpS6A;TegP8oc&DF8%q;3Th^TKrq%lvxN(9w;i-+GHh{u=u*I zS2Z?W_fw00Ut;l2MjIo|{cmFS;<^P^;@Bx0;I8Ye4_c3WYn~%18@%*+%wom)%PddM zJ>NwXnj16Rv(h{scs*&1RGs@WP_I!p(MnH^)v3i-$_#@lLY@nWE`<-%IA@3;9@65V z2NTzn-7*}P*?Az8VH!23l)jC<22^AQ7W#4_4JL zL#aJ~BgMB}NDdFz=|NDgkHfQRZmuN&?)64Q7?BMNFKIE=6{t|iuTKt#?JtbHGDi|U z{7Hz|jR~R<^ba-&0NV_?AqK_KWG|KC+Q<*Ma~!H4;{F?!V}$c@P{S1pG{ygtL&L2{F zj77-Kz=Qjnbik$&3+d`tSEW^YgE)KLO~uA(yn$U&vR^Aw(;T$^d=}Jl`>rJw!#Uwii2HY&olOc?v2!shO`F2#=SBTo*mP($Qy?#f&!+E`@m=(CW z-xs5=5M^+L3?PtX;`wc}aLBD(+|-5g^Zs51BpU&ue{I!G;R7#z6`-rwc6vNc%c&UyN|wQ zh#HGFQ2}-tucspkzpZR5LyH}$PDov+Mb3&6ANAAOoWkPMu8Ai{_!ZnzkesGto9O3r zgkvrEl?^&-5WmH&>j7RJyTrp}p>Pdb-%R>D{_*7L$xix&dz$lD2ri;?O@d z0Q#m?1kI>rV7od#JNlU1P~??$CA1SGOme&HAqa=D->|NpkXYgLDzYG667$2j^V}{_ zkPq)C6m#@xj+~&74Cp9R_{#@mD|0FHm`y zlr$2TcEr(%eQHzMHnQUDW&u7wdDw)nBI+O}5dqYVmDL16c*Qcd&2z<_A9@fo1n`HsxLvh`zeQVY$Ho};S6_~Q}--z_Pr`KYS1Tan8tl+JmNKlcuC!{=3` z-u4(hP7Vh{3Qy8gu4X>uw?OM)IYifa?v*2kC`ID$Dh>p%$uyj9MXN|)<2V&$pi83% zG%cTc70ewXTW~97^ahW4B!vzJkdoT$fyc_+f9a_^g&K^98PiDNL{g>GISM2Xxn00T z|5~o%)d3|3%YZ{wah#25=e-z-{^4`5!;Q-f{D8(x?;zPTkR2l>RO?mjyrL}|J7h{J z_`6V&HxC7` zS=oz#+wq{{v~Z)Re!Kl6FZb-(EkF!iyIdI6f|V()K*lkEhYj)8apryqyi;f0`u8u? zl}zUQys(m^jR==VTc;%8m);*mDkaqyexuXIt4CxT{+mViB*}f*LHZ%^j6daB#L~zQ z$1!39oHM(bq2wPm#b1zrbV!K-c)Ya9Q9caL$uXj+gy_UgLV4q$LE(DxPd|SRXu$1m z%1`I@I9Tb;!ETi_-@y(@_sE_HNFNC?T}m6j+C-nx zvJTp_?~{#5hrL!&kDow0O&oxRKid$8|9c;14WoN?~zCv;kFy+~Q_CgCWbB-GoBK7x)W(9X&?gZ7S# zm3C_5y_0&)vUaK4VoVcMMwQa1Os0?rvit^jo56L&z7p7+_oF&F7vhkva`nx|TTyzUpS+=aqj4$2Vf zHR|yj9Gs_QlP(egup)EmA+pWD!NH8GX1}_l-AgKG7FJcUdDlWD}4d z(lj2SiuunUL&9LeAA4DneaKNk!!?aS*8N5WW%IpnwFy0zI=2t#MO$|(P9U%#;ZUJ|*r^F)-eMS}e=}B*Gn>AZ)kc zvwNaJ4%T3lXPMEOLmePEtIo+eS(bVKcRM718 zd71{!c8EfewJznlbtlwO~cCksae&VrP}?)7}4!< zOO@nY%!9y*e!6lZ8ft+(7a#kZ!ex^@jZZQNuDmbHN~6`yLY>p9_c7bX{3O#N9YsD) z#pG)7vvDj}`DFSWNa|qPYP4)sM!a5}&&pe<7PT$}XyG~))HqniekoCcb0X1LZu)ZI zWU+g_YC~PHc$<32D&FJdjOzBVq}qN=%ot5eB;nq)#7qPfkZV7Y1aS#HOINK?vYVlu zwJ;;<@E7Z<3l@EV39F&_MSm}u)-NIAPssg~(hiyh*IPf3>)U7@1?YI51PCmlEr@^| z>UnrAw;Wz5?Iz1~J1vcQWS)W!zw=jJnu<}buRe4b-Q*DyR(jXpXjD^}H+ngrpq8uh zqk-uv4O^wtUl`WT9GHefdDkIps68HSv8fg^*!_3c>1+{T4LH*&X!RcLw%&0b zo|(&2XnN=!dtAZ??WL1;in!c56E~4#>n#MeBV|f!D!+;Z-UqN<8&uOtDB+1%vLGcybygK2FY#8@c;Erfb z%;jcbd}`j3qH_E^`|ZVBRd7nyeW?Vp?P~4EtX|nn@4GG-2)IoF^rwJm8t@eI%^cce zLOJkh39AF?GmGmMx%xBg@21^TBW-U1UgrA=$JRUekxHL@Vk@L&S-zUWH8a91+3ak(S)R?(e!vSY|{kHEq zO%NG~7NLNFX;klGJfrvB+T5r{Q<`R&^Jx}AcZcJH%2e=$=;?f=*J~xCJmd*Odo5km z1&9|Sh!@T?)i0QUsBE-O{SIL>*sx{hTcH+dy*xQ~UtR`@4FGgj#ly_mhkUd)-Kz!@ z&IG1Ab3aMV;jk&NYXC<-!PY<@syeXIS}10%;LJ4}bofncSC2owD*e5X2c|d3&QxDC zje<7MjI{3?0Ve;(`wHSo2;$!u2w}))LG0ZB;t}xRu_`04212}LHO9FCsDUC)vSJvZ z_`+}8{@lO=Tgk7Zha>5wr(mUY=-AE!GH15Y*8wM*7-G( zM3t1NXoRXGLX<8GT9b7?=QqRQNTr0g7v=d0vV?qfT!fI`>r^_2Q)te)UVloo%X|{LWg}t1n_c6C%Q9cOs1ScZd7;CVxLJUxY-{!aQj#-S|(D zR0$o_R&8mK9C#YuM_ycF`5~!aoB{g)AZC5pkYB|RC@Z#kCtU^9#5!GAkD1#Hg^^?O z1I+0vH!X?lU=+lg6_9P!yasyXW#h@HbAM_|!_k_U;Bi;p`7fI@aGz>VMXOZGv8Bgx z@h9E~NtSbk+WSZ80+beD_LXJHRFH#7=d(yZTo!z#frZYXm zM(Iyh5q&_BhqsLse z%~z_5CFmlKggSYA*g1S>syB>j7M1TWfUk2`@mIsjwrl`k zM*tAC9cKWrKtS(X)XZNz>+$t(iVU!Mo2vP%tvvpZ4*IK&`2Y@owHhBr*Z(=qkIt?? z*YoXCxefTi$&Oq(yag}?lh`{4^@1~Z z{dp!3aN5x*$@!nVf;b}Bl?vMW*!lZ~|7`=h<`&vQ-)?0g8v%eS4ieCbP4+Kp$(71k zIz9%xWqP$w@^=6#j69AJG>*v>v`PC$g^+=!?NZ4oX_? zAzr@1NtEe_Ip)x7KUApFfYacaBk+#@x&8ejC0^1v{kcK|>a9c$UUTXo8X0#OGhHVc z?ZSZTUBpts^xYa%5JqGYrGIq5?E)p2wGcv8IZo#FtBc(VESL!Yq{F&8Q z(k1>hwtRS)t*%{tEW5`#w&)x7<|jLq28|? zzWnsP539#*;!BOIL9_1^y6?L!2=d%5`#M4t&>tq0hHDm6NQ(#>U3xYDh7 zmwTRF(lIBu0&yQs)D=^%*n4(94}&~369DWCHCenm5?H2;>?^=6QKM`w)fMB+*}asY z5AO)Y!9(`-c&4P)C12b+lts9C?OSG<6XFcfeF>^9mmSoS2DHBJQLE;`p7aN%)qsA| zdxwI1ug^Mr*Qy>cU9$-NoiP{4X-cwdpFFlRkC$J)mEOV7%5_7UAE&BiMA{^AFsD7tDiW0uQ)8_}>@gU@X^g+1&dzQTcpuoEWLC66 zd5ByKBX=!|oLDw7KMf+D;o(t3@{Rj*ZsZ+t_K9Fp*)co;k%HTZM@Hc3`4vCv*JG0o zoqm5@D)70(l|f?=j5b|>#0ld6Mi${hksM>u6E8m^B9v6?LG!M|22UFXrK`4Be+duP zj$L?f>yc~IMSMgVe3>yO$|VK2b5*$m6JrHI!D{OrwnxCIK;SJ<>bdGO-@N>d3smOM4Q(^fqrf4{QhAN3Z zd+bo$&VccZxdB%auPb$(oLePyMv;szD+}RbM<)FCnJD8J zQ_P3vIB-KpX5nU#O@4D;66f;3V)ot|a%K3qA+Lw!x(RPE`$O)@C^MEwaN||h$7njV zBs^j13cI#}O2k$^s!;trc}+D*e9-8!(qnlmUaO#Wg)9B!UEtFft;wt_(pLsTS(?_Q ziSe63ew=H|m#%f5`DM8xP{qfjTaeijaptGo&$}*)8Yf|bi);o!Q3kmx#1M}02y8to zOhnEdt(rQJtrru=#dRf7eXQEzSH_baeLwv*L(bmKYF+4AWTXG8KhoEOLj)*=*z|6i z@A@PRuvA}!I(Rma`-w$M;xs2w4Rv*Xe|)#et|fHtyiQi{=gNAEglh5S*?yQ9=6i>K ztTEG-QZ`{J3U|Kz7Gt; z$$rtwY>6<;SDLvEMRheVK3@v?Cw&7#<9C-s&I>9}e$vx&%YC?*=!|_ef;(?Yyt(Sv zkH5th(W^+ZM@3Ax*Bq|bH^2Hw85zLev=by%exwyIM~fMUkm%WW)O+Od@_HbY`txLU9M;z=Xojgay{rzp3?-r>KO661Qsex{ob zMAq0-2hRD-WSVha-H9%m2<-FxdGe~FOm=PUpDwweCPQc`!eajN^%}`0d`OUG& z!A;o*oNv65Z-(!?DpZrW`xGqWS}57|n%RL`68FN0b+>-87ps=EQHQDshpuC9uFC*| z+0kpkq4r*P=|sJ#k50B;W9yVYI-gpM@7t2zf1kzwPU_zueF*?yfE=u^9>9NI|IY%f z#s9DQZ)kWBApXbX|J|;v#lHU?`rq7l`ExQ?pi+pD=C6O^!5=DspRuS-bwPUZ!93co zbTfC+xz$l$|KDS4e#|(q?rbsu_brqG*ykv0xha}(fJc!(T;Lc~5e|EFAKgB_;r;W8 zv7LSYGv5C?l>c6ex&O!H|73FCe+B>dkUvi*0~&!!VMdn!|0o9bJRYqsYlG@-ABrOl znmQTXI3x8YlQ9FJLh^8XH8pI}V!Q5pd$W9=^YOUkC4`3zNzrPSxoH~YCDGJO?d16A z#y;#CsE|6-I4&tx3a|GWiF|etZ>*X+7e%(NN0>n81d77w_+k&m+QA#?Avar8)H+}y z7#qxx<7Mxi^tAnFf_kq0$65Z>3;w)~rEPL+?$slk;V@S^SGt`0cCqz*wI5RaKg<`h22H5X7jg!nKxcYgC| zS&F9$no;HRhDxZW^!lxXqCte(jxRMtZVryMihO)B7bNWLk@J1mgAp`~9Jnb))84zM z{uVX^zy{?tzD@laB1r{-qJiui`+YB-ql=_-gr9L!&FX-W*SvcnYZ{as5N?;){fkWN z&0B72AYY2igDAMv`6PH{%BfKW_cVk9VSSeQ9;L8F5wM=E8Pf`Qt zm)%U!r~jDzpG@xiui*b4@`v~ko&@{h1gc-}f&avWzXe_( zZVhbuRR`ITwjZI-(W9*#WGSzWl64U7l&_vXQNn1}XP7}83g0NAo~l%LoS=VbHA{eW zGj>5AIcNh(H$xkZkEv*a8w?_xhRW2pOg{FH7`x1zR6PYd8ZCkKQydgFnBp*DRh=qg zrb`-b5@-pR4y}3q%7*ux!F0P;biekbrqwE(u{;2o`L-hITV!Kz_tpFJE`T_W+CE zC1b!3NDH>ymtHa9Lwl1$!!_a6S~(t?_8&&bU!Zmi&tk`tfLO>{d{7z?>1k>Tq6Y_4 zm7B9oeh3Ql$9fT;#~DsU1v+9deB+Dp?Y$m+3cG5$g8M;@rmxriHL(_RO!MceFesEr z%nO4GOz*tw)nf&(FgrlN_}~bPrBB{GNgE{QmG)@ld1%acZ(s;k%;CBOnTvN86ZY@h z1B=7%9s~1M7cTCFYB}&BJU;`0UM=RE&9)Xs3F&(@HlHn@Cpd&lwkxbT+ZkJ?jK z;Z_khVL0Il%|eRdh5BdB)_dr|bHrWz8I@9$&GMRAI$)lBxbZcRM}HM{ z@*z9c(bizQf46cmJ1{U#ubmn~U2{R=l=``+rOfg!{TpI^&5=g;-OUzqY;>)l>Y|R& z4289QNsKO;IZ7rv2Q5*i6Q&A4M}Q98)UcjJ`GxKI%}oJMn}sc+`Zj-_{kImRC+>3c zOJ|9I)A-V2;@S8HE2h5w@CWslY7X^T2;P@p?YSc?z-^@2` z#eXCrb~^i1T_+MXTO=s{yY1I`fx6^e3ebIolLQHj6w5D}a4N_FItjoJekDdqli1V; ztM$r-P!HeEs?=iH+dc^8?^3r-$s3XsvmGPu^F8*G^!V+~IQIwi`;ItjM_j*7`H6fU z%6-cvFfkLfZBt!+--gRXGs%2L_nM(iu1wOzre2MX^P26#GoT zC&7`CP2UIE<5AE?YmJ*p=PoMG=(JQsze?(!W8&pd%^i>P8+^gANuo0st*|g3AxH}?#t7Pcg2{&wwhpPo_IsJ@ zL3K_fi>Lj>4M>@z=%h6dD(Ovs_Vjz?`+_aV;?tGcXZ1Fht5gWQCkA32DVI9%T`b$@t6 zJBEvpMx|Fqn<8MtX0f&}dnPED* zj(bv0?J~Rx z=`p3PmF^n!PADOBLnoAbXG_$8yaVITGv&kadP&NMpT)eg^LqaO8Vh+Ixtl7t3a4KbFugUn_0dDv}4BNB6!Nx=v=R>%UYM zD6Gi)pNTB2joB<8A8G5iE_%qdDyq!(5)tem4qbS25C85WKmg&QTz2?WpkM4@Y{Xli zN$`_5`+8`evK*0DRZMCUkREW#w62i%IJoG0qrtKDc>V^q0P%*zlOA26Gy#rs889tK zxb}6ixplTlqbIAow1S12>6vP`et6JH|Xr^yu^@jvyS zf5>J2y=kV`pWiwxK&2`FQ@M}J$4kqeF_;0aPsA8z2$3$!Ha}WF*r4%9*C{`FqO!r$ z5YMI9%^v&5`5Ps=l@4x?&O`c;wmvSVFp_&%lEC~yfQ8hWW7!>;yuPV}ZW!6z__YPk z$?cHmTG)q;s#`fm^i%CIm68dFk+Nm5WqqYy=xM?KY{UMD|Ylfve;nywV{c^BfbLM`8U52eE_{0d)e%Q0IsZw}|J7Un&BbYF0?Y*E%B%ns{6HH{y1Wi80AH@7 zCLqX$%g}!n_E1*%2e=#CH6=q0dDizW?qPK~BS=_>pRIJa2i);b5BScm_nLK__GT)B zU}Vh5zM3TMkEdJ*$YZu-z{1n)It*jb7|H^0Dx><~3;|fh%G0cE008!<3~#a}6iaq< zoH)h}!q&T|=+3J*Mz8_i{HwR7d%6Ax#ujp>E^RpP-l69m|TJiAmrb4W8(e(1N@z{$J+l#t=4(|LuShT$+@Pi_fcCb3f@nz%f zLB4rE)HXkgwG~jq$uRW`2`rPe$HvX2JP(iY0 zOH~e@9%-_BOar$sx87EsizenJc3np6fNh=@iO{V6N+JN2Ywjf%kYQD!MM1`UPg4cT zdZcfb$U9P7g*=PbxOGGiesgEW-Ys%$hhmLO55>#Ji^9(7%78uSiYej`u@?9we(l7s zqJpgzoSz*8MlC&M{S5RuivBC2<+E|p))rwxrujFeOlFOm6%dV33wuecuI?yIpdK)k z6~Xs8cZT3e?PozR)XhSbS>xOVq(0P-RL!cV=42DHrTrSs!#I}jV z1P$r@kOV`ze-0RsZ+w58H>i=L2vb=}u0%Nr2@rISXM(e7O2lNBrJ230=Vbn_;I?}S zC^KYcFdz=j*p8ej4G{`4oq?-d$UJF%_i(;>2JUhv0#}FFqq+K&b zydl3@kq8}f+JqDLq3XoL;WX|aaHN8D@bfCYRKuX-Nxj`VCIpICNzXrxH#v>o#yoeM zieAv>xkc^u^=R59YB(UAeb-~{KW;K|E_R+8!cdD+YmTDRNOmMC@)5hZzJ2ZBn|C3C zGvrTb3-i|Fk5@B`o5%G4OC`9ztQxi5 zG>259BtdFAx*qTnKu72BetmtOSQzA{lA|Jdo?~%GFpP|sLLw>n6-MNfte9rkCZFBD zj0c8OZKc?drcMV{gubH0lhEpe;`4W7LY}ZcJ;B^@hz#}rd44*4^-&6LBYZ>rs4>M=bs}L-7}4>FUY+4PtI{<@fLvl zDv#VJq_m_fS{q$J2o#j8CbSP!i5}KDeR#ANSIG-&3H(f@A<(}%UWruara6CRF@ zx{UXmR41Nx+%|cqdWDZdDRGsSJ8aQo-$L;{SEqSY0jJeE&HS~h)FM%lRuk|uBVLnr z!Gp47+|=>EgYJbWka?nv<`R@tLwd&V|HL0wpwjIBA^vQ|Qhuko3mxEW8NTtu;x52m?w` z3M|XKR%jpKO{e=xtO=L70)VsGcBirAhvLY;MM5eRrOWt63V;G@?d?XIqLph5{t`LaKtKvP}t_=0J z@6U|02m2iO@5APDAi#%-A$X%LWEbiHS{QFU{G5}Xe-1-mwgk`#jg=X3B)~xznBeA* zb5*hEFvjcKhdY}gNnqB3qpkk%apl^xo6~8dAUvGW&3^Q%$NiW|UbrU64I7ip2cYwi zWOh)}seZ9Vd>%R05qibWh~z9FKxx22Px$!ge%3X6Qi;(d`=bKSI; zLOI|jgLiuy8i7oekV)N^lEN@4aX#)2j=HiBLC0N?liGg5_( zi3#=-{Q{MWetK?0zE{FUPoxFvCM&T9KR`<-+(NKfoBxJ z)MAi?hO>IWzJvHw4+!LZ2Wo_*A@nfdYv~CV@G51b3O03DiZz29mj}A2xTgnYve`o) zE86g+Q?na?jOP6K)Im7yX2W%lZsuPl^Ku14_l8)fxJ;7uf)!t+w_-e{!gg8fH~-o? z<@>S69!`h%4zFb#o~rdElEqtR?>DS{wIv@k>fmG zMw$Gd|7IDxiMf;*L)P9-Upvj*T?Usbd7|7c>uEEEiAC`RGS!l~!NDOyzOAnmiJoF8 z#9W_N+@8|Ks3FO??7t@4(FkIY^WYdJW7q_3j7=XH<`B6tFzV%q^Qc^?y)8R|;MbVp z6yuvQhlo`y59|n`fNnsj;8wVFnhLi~T-p`+R<8&-xfqd$TL7nGeby70w>q#KJczE>;>;ItMoq zf`=Z5y$Eu4(qW-7B5`nmfP)8^SeyXScjFlUT*$aX-`!qc{eF%82uKG8u!aOk_ zXXoGw2vS`EG|D;~JJu3q_JqeOvL&gpj@K4h46~sxa@_S6QdB6vhHJ#-FT*A5arC7f zx!S491n+db*O6S<8$}h-MncfRCJc94PvC2@afr5P5(dSsdHS}^CjJP5r3H4UH zW=b*m`__tM^d@eufe&sqx;6%6&#hvK7F5{`tMASOklWgcDhR1Tb99Pv4k&3(NGkv>4M40`m9< zygf&t$C0=Y^A83Z2%|vR&>c0N5+ZS^$gO3~lE+8L*zEgQKf<0^*EAUzgQ3Mm>FE%Y z)LPk=0OQrCC>yt8_;gzFz!mJ24Jn~@5B=a<$SdP5d~MvP4{5eq=xe2f6IFd1LbR%fEv<3hPhK#D3y+!U{bX+aa4%h_wy0Bv>wf z%rJ<5tmUyUj=BOt&_c2*bf?vzs`>?n?el_^89>k+BtSUY5tIrfjtiGJs_!EzsT~(I z#Ueevm=3ST2+Pul_tLVZgD%A@#%7+&8!m_cHU_s1gL6gvO$22?@YoGQ-+uoU_?l)W-g;BN5aqHOEdxJFP}oB9Gx zu(cPmj~Cu*7|CCXzZ!bXsWjtZP7pJDE|iA62?T#Yx%JP+)WewU!jux*Dn&sf&CH7H znnsR;u>CZHYF`?tU<7ftu2RLbLsJfxrpx>g&GN67msjCi5Y2a4Hl>9GXn9; zstMyyDT3!K$v?M!vC4txGQ;;vSiGpmWs+IqzLL?>nOURRag_e} z$^09&c)pXudQ&Lzx24Uzhi#naH!P&xsH=>*II{{momgl)iwMbs?3emu4XOhTx=74e z^~DH6ZaR8?mHOBwEVU7yy#@H?Ls3->LDy39v9E`xDsb#~rm&OVJdu_-aEEy3p?n!v`#H=V0v z@h5STy;-&tBG2l53VvCNYsQmwWS8ub1ocu*;t$oYR>knh*-`t>+Uf>~eh8q|<;<3- z0b@le&?Dxe_rtc9tQ%3~=2x^Ep&9k|wf(x}1^ZK$1gQTXVec56N!)#lK2Pk4ZQHh; zOw5UGb7I@JZD*2+ZBK05#?5=H&Z+ai_q}zi`a^%~uI|mX_HVCBfE+rQYuW3QZw`54 zY%nr)3s?PzG7WI9w#sp|K@0d&fZqKM6}oz%WrelKmPqtVapR_JnltQRVulhrGZ&to z+m3_Dij>o{eCLnsidwe#jOWzZJTDb)u~U9G2mrY9IB{G4q3=x>imsUP-uk$#S0bxE+^~K<{ za*kIN5cfW|8)r3f`Kwsf!{R0Re;UkoB=PHSwgC19CcHP2X z9oZvAzH`8;>uaR?r%n$_3T@(dy+oHo4lFT5@CpUdep{n$R}AE4d5|){7sU?u=GbRFL5DnX_IHM5$>&^|)5hXVWXh&xiy@wb(mreh4F1)pmOfEg zt;5g@j|<@Qu&L3nY~!kKkyc2|6c5&}OgimeAc?%wY#3lxpNEEyJqW8FwKxv>GGqd9_kx=0C-wmhpDs^RyJ^?CJN zih!aZdCkK=a)v$~r&Yaozw^{1xW%*eQIX@S?D!o;*gzbqpW;h7eY4@(98DFRnzXw* zu%h2%(8i;>iD1GzoxXfOUu;AumByH0R)?~P5Y3^aLFQvGnv}64G|ReOJ2Q_)%^o-zEUCDm*x^o-j0+_3c}X1bA(dT+*CQ&ds|U! z05hIc{oqa8PBe;-pFbB+EVx_L93fuyiNnkJWRA3W1nS{qRwGBkLPX;l6w?2B6!qnc zjy@id{&D-M+K`hr^5>v;!18rEJ57*_Tu2RkC=q5SY!Iq)t24n58q?n(sk}IqXGaXD z3&8}SLXLO22I@!_*0X`=V5L#*G$RNw4EbD-50X%RJqA}>hT)s_xgu;q^a6U2j&t>SZ5simT;# z&k}Se*-H$P&v=-$o!GoQPM`&?5ZA5BuC!QJu(8@X_{}HisE+gL|B9)JD8pUEtM-r# zYn>ZF$=QfQyb$j6jRxHoy&&it1EHMrRDIj9cqBOnqhAvppPFH~#s;TVazyB* z%j%6;h&AH{jgq&$KlAzSQ31dTijma+Hs!~(*2F{9oFX=GMBq?r;^SHrhhR`|j@NB; zE+FJ*xicQ1GzjdlYYVN&?Hl8ai1Wu4@v~3o^XrTVqUuPBC+`qnN^wax4y1_>=Gp3TtDSv&mEq; zrYUfG8}${EoTt>OFm*;heM~#a_>5B?gXO=6750E9RPqa zCZ7RO`*-&uaKdO_bS*f3g15%OAZ10tGFZvQpW3PVGs9jj_PP?@+KeCX^V7aRHm1SN zVp?{giS;>?T)_n1sqOO)jFB6a{A4w(Mkw3unb(?mF;((d!*4uCT+E_&)J_^PmrdtCRz){P=fMg=`wjto0*O7NxxpkxE$}Tx)^eY z^$DoSZau!)k%nh^8+ySpmskjDHv8R#fHqw!P=LjdAnOjXW*gb`opIx|OvC;cWLv7JwobnIPj!Ol z^T*4jdI*AG4tCpZyJLgPv--grg{FDgVx@Q&rqoE?DdgDaH;n%Utf2Y-HPjqOZTs-! z`qc;DEYQrqKuQ18S-&^Fs2sULz6@?Tw>H*3YZ(u2O1LdmdldPT4iI^3CcXE+7nZ;V7$ZOrHGi%*Bp2u~(Tx-7%eKH%TLxxAnKNdqEj+ zXPcXNF`0#b8j*r^$rDA1{G|_OFLx|&R+l#pIu5s9dGh|7q2jTAXTRtH3g55KFRP>i zs1bi@Mpg+}+Nd2FM4mF+B3G*(?bqAS}k{QgZSDXDk! zKO14p$l3G8*~2Rf+fSeN%#!FVt^nY9f;r<&VYVM;-ewMC{B$Iu`jy%eR)Uh2oU(F4p4eM0U?u{E5vI>p;TMtKQSRixzzC=hgCe!n>=e z^;^H?WzUQhnt7Vab||Sl>$$#U%1j$I<~UzA1UN{b!kzkUROHPd~!m1 z3?XK$b=NWD6sNZZetphpBrM9(!f*nFRdfC-ZK$|n}7lc=F{;Q&$mIMY?-=WPJS z4Vs8bzvinADBsH%ON&TsjDXc{QXsOb@<%DXvZ*Uynba@63tJBf+h);U8-XNW8;Jtw zYr@mR{p`5sK|@CAPcoBki^2Ud9d;oKb-MGdl9lDjAjTI~@{f(FKbQBR&8-|3(@I^Ce&Spsm~-dNhp4#3?gZ=fH^Bopjk* zP(zR5z&MS@LMwq)K``rd!*20~khQenAG^OAoGBJ#qu{?>(Akov2x8E?{k`u;wBzwb z`k4exER!DbhD77{uwTY52%#kYxkx=l z1Y)F6Bt^B{91kb+*JDoOT-p5l%(y=~In^@Eu$0JJG6|t>Ihm*wuyQMB(kTm7=y}nl zq1H;nA~Yb%Vo*m%hyEdzp2Yro%B-Zow7;LFiYFN!zi#T5M)bo=>&&}p{Q zDTB{J9!X) z%xHXcNdnpERI5`{yGpx_@Ov8|{KPLmqb;GKCIHO3wLtonfH5i;eFf&OiU{1+`IFfX zAIG|%{vj3c$}U%Tf7&kbw)t{q7nk=J62m9fWhZad79vCLU`AO<7D=XFhH^~@#6&sG zk@U0f4|q&Rs)(Kw+uah}!Frs7#T$kv*klu6Sj%2QF=!hxufTM%qb7%nyrnUo~oaVMtjWw+`|CRY4R+ zY99nn5g!u~GDi89`jjv0GI0sNCkirJ9#f5m*qc4hH#%)RNitYd)6DP9D)u#J34hPI z++-}Oj{TAIEjc}_cwyp(83ob3o}G9a<-%rpzGVc07k#rDtiM8IjG$)|{#(y9I{7`WILrok-IdMqFd-Gz?Rr)C&k%_+*&O5d8DnXKrDwCV*-<97sS(0PqrC4P-WE>fGoAv?+&P^ZibpFi^MACXt z)LAy)DC2Yda2&Ft(^ClV>bsqt;pkWHISw0prebn>(9}3pVqBu(5j#ncMons7!yZr; z^6}+6W2QrOn^65bA-Omc!(Kl2;85Mlhk@LKJ+#f{z@;AvS=`-W_@oOeCk@{um!Y!5!19$o3Nx-XJ1Pi5#p z-AI%)zcbTA;l#|+->fyj&a(*NPLBJ6ODD+!OelCDcjeZ7h{n;D^Re2ba>K|iPL`}J z@vm(In1-&DK!yRj^I#{MCp2SBGy#Hf{@g!vrQMf$hy^^9*Q@!7yuS#L8<84HaW�DQ)pUgInZ(!QyzPevw0snF#zK z%!qz4FoT=AkD|w5Nn8fsBNZ6{avk#cn;#gChmt(p&gJat3I$?Z$s~Hz;O1ZM4C2+{ zZ0sVzGlh^?sS->uL||3Su!f2jT>2)=ER7a=>xfTsC!j<1jg9;Hq9M&ZxbzJfV+g5N zpfT&5((%N{k=tB}gPLAGlgmPK`P?J=em~hLHWxeS90wj|`;{ZtXg{D{gGL)O^Fh)3 zRlvnlXdqEe+$VsTec%ajIEV-@zCV#ew~q=E7T+HtNH-KU$W|eeIpz+|fhk7tRr}MD zl&uxF=TY+Hxj19Z-vvQm|pKa5kQ zbhY&CRTi#+TAxmdGKirbd}GihkKvC?_u|oZ@@BOteKuXNX^SUuoI7TbSavwC)P~Lp zNy5;iTa9Slqm-P=r}K7#Z2`9t(u#HVLKA%N6Q-rQbqrjek14UZ&%UYdW8P@P@dC!u z!)hFw`*)v%T=jxQFp}d)Fzhm~J`3a?HR0N&@KB6Vc7i{6-W;cxPerNWo^wl zUhV-UMy{nK%44o%WM^p9J9n?MGP?M9CCyh6H@8Rn260v6XRR4_R`sF+Yi%0An)U za4xF+Yi4}GxlnctLR3V1>UaZ|l~th33ZgD%B0bLo>S9K8JYpHD^-D3TzuJ~Y8aB_h zu`YR$eMgD{(uNiVVvLH$+Hel+tMR<7mNiDLs;3b&T%?%COg+Fgddr?4WX$jh<)^Zi zCc|HYE$M$FWf&yQ#e1t&^(i?#V@i#g(~}m1O9Ur@l27HV1?~2USgz~P-2jWB3an8W z*KrQ6TGkBQy^oL&F=Kn!HJ|t2xP5eb+<$_#J!X1%F9)- z!hCan0UIsvDuF7?u9`mtK@4JD$z?0gB0L4T8WN9~z}%Nmn(DYM$VdX5oNhw_VAsZ# z<7fmlEiQX}ET>Ri1<*CTEq~Mym%SPO%|{ogkzumOfZ4m@gdMhC`#LEcr4vC)M!@sZ z64u~tKKD=>T&ap=>R7GA6>|Pc{6>7bogxwKZl>H#{rMT1?%qeeT#lnVb_^g^hgOlf>S?MERwb_<-|M0Bj_d|4GwgQTq80yhleUVtDQOb^ z8f*pa>gdL<`K=ReapqOi)7eg)p+S5^$|Pi+6|0hAiNLeq%Cap7B8p0jg&A{=ds;~c z)fID}aMWPhFDXtgaBW?k3I}J;DQ{$vGdpW;dx$m>`aT@_A)(Pon&`=KuM`Un75#-8 zX)88eO&NO^)7x3k7#?iRv&m&|Q51O>h7gac5vtY6;mA`>c7YEPT|^zE*qZMdhk~bA zaQY(-2}Lx?WpXB9se>KvvGs=aux5h3eL-(#J@LXiEhnfIqc%~Mvdeh*$b_InWluc%&Y{(>CSGU zO^@~)jLd#)=jJT9pPxkl3iPK=v7Og9&76HWmhW}Q~CJuuds`oQSMliAqhQAhk z3av8dj{z^b3QnoC$3#y!^2t5H5O>Cum3VbE?lNclw@6_nUzDTz`oX>lK^23jC!*;e zY``hhnAr~Deolzh>GB4?xF7qC~2mom% znFDVIl&7Pk_D%hs)Q_)l%7dcL198b4Q+x-9kFicD=e21%7%I&X|p9%44#o^M9~U(w>N8r;WIxwpPf@^uiT z({kHgCK?1{`qU=MdC&Y_k)_*oYYNjps5I$b=S^*R%op>GnbHIO1Uv64SZy}y$*)k@ z5;*o0_laEX%k{_$s>;J{i(Cpd$Q6)V%pB&z7@|0ki>Oyuhy9mdlIyX(=MDqkxFuaq^whN{ETGL#eM z0NjNja2O>l`+Ui7cl2o@l8Up1=6*M~s#93ELPFBF`n~cE#a|WY3nJ$|Gda7ljh@N7 zGbVMdO10hkR!Xa4UO9MmbnBu{4O>-Ho6V&o26OQ@0;A@)^i%vno!s5JiR_8Z5zWw) zL?;;onm^|cZn9vEsy%1A8yG~dqFrn?=|ISZAwQco-dJYf2XU9{{I?N<*js+wR1jB)yj?u$G5#g9kUeu>{3y-1_&Zfa}0>?en_>Z(yy zm#&e(v&9@X8aXYFAX(CqtV!vlQK$Z8fhOV!50iKMsYERy#L&O&tdWZZ3~)}FLWw|s zzr8t&67SM>z8@@Cek}T%B3b-%tXHKA`JF>!+m*gVFj2%D-*awX-&o4bzKSr~4jlcg zS-n98VG96hXv1QCwn6I5`-_SAR;JpVIv%nR4N?=3`16sYmi7} za=-iJRd~h9uN_;7)Itjk(UNKpdVi-8&SU=xP8TRL(z*&QO9xVL`C%FG!Wi_%DMA$o zuICbvm@6dfwLaHc+nTgN*heM=K$X*HAh{-q+yHKs^vR(6BiCx#O5Wg~VzO+a@*b<@ ze5N^Bg`ac$vbVmy8da|&J5*B-9M1djgysKWtQDR?+b_{Y>zDb!E?6eA7M6T`Y8X#i zWtUCFleqX*m91w+)VKoR5l_GM&X9l=t)pB2)144ZI;0KaL2S55({~JzUd^8GgRd5p zznTvX@F;Xs?@PDKZRQ(!cP3l~=6$kC)ulQzg%cmod|xkU=H2`bAob>^6>u`2PzT3S zvO;aayAkL+kC-|$9;jytpH`IK#Cv)Qj+I7jZN!R6lzv$r$aa>}DV!V75h%Iso7Pg- z38|9i)R5phkAY-KCj8@8QaG8H0}4osE$?NR>*X)wyGb2$`-B`CC2 zZaN?Sb;luu-QDcg-a3a=$04yv)muw=G3RUN(T@^e2Ahz&b2$j`)w(?DN$)pfH}IjgCer0w8!N#t=WW_xB_!$ zrX&p@FSMW!|0$`2Z?`WH5M7~IJT61-`mwoc|3wG)ZMWCSMwE<#(4a=V_^h!4+(53T z)jegFJd#4j(b$kv81<$uAU>-xDQ^zjFOPHS{7XP^QsW?!zFcieU3Vp#8IS4G|DXT- zKPODTFHqC4Z%7)%^Xuyi)K-OX#1GjJlv0QKk2cK6eL$>S%MdV>;odX01~ zJPjynVFg|F()P*tazVZ~Pk(NuuqFjAvKN1E7Nf50# zdxU_fUWTGs{h(q8-N?=N&GGeDXB$@pFU2fO=s;RN3=ZhR+b8kGIi)-yt?eduEZiYc zq_~lNTr32v17j#ZK};K8XY7NR5dbNWM@~PP?sh*bnLI=j`Pg8`bY+1>772xlTsRJ6-6lJ@eELV4(bA6DZ< zDu<>i(e)!?dhw~d>T_TQk`DWdOj>)8wEv_ThaxinhVD9bP2EkGI5T#>)*h>|b)h)8 zd_3b{2>rP_=x-2jAtOJl|lj2=Y!vPh=><)RygD z3xlkVQs%9g_CtrZSX7Q{64n8X;{~RFl<|)VLX60efRYIY+=qLJYwL@bc`Kb6`nN{%5+Jn{GPnPx zJ;Ag$6>0W1CkNINvJ_hwdN3JST(RG(;!g*smN6sZ6N9Xu7wQmvb2^|-z0Cys2FEQz zbTV&+b;(BsXLlzwixU$k7KV<^)*;F_7-?v$?t&V1qPRWYMPVA<|^7mntb|iK=%e2(+4b+#YwtEGrA%L95ueuQcpAWSlWS znVFdz-5Ywn8ES9gp_5vA?aY5wqg^xkx9!dAz+EVz;6o;}Y4ni6q1^tBJ~8f;fd9&y zS48ZM3(a`D0Y+H8GsIW1zd1IPksP#0I=2msgOT$$>TZ!&*9_j5O~_2_Q*QPJu_wqh zK~<}slxMR<_U$ZDwW{NB_>$aD6E_I|%T|?wm`KGle9*%X#C>QQB6Q|-mI~ym;f<%Z zw;YVt(>#hyeFJ5?G%bvVA~Kkm813Z9W4{|UVND_beSH;cQ_D)nxakW}Z!{5@DamO1 z@CIA+5ilRj{b4u?pK_OT3U!r7Js^FA^X^&_kB=KuyTK^MMPYS#oS$g)qb@C=Pj zjN=+ZIR-caMlw0BJ%zi{;wOom`q%Sf9TnLYYWt7VvG#7UeEZ(*;&eIuh%8ME>H?)n z^K#l*+WX<`!z>gq_S6?76cZ{KcdgWfR7U?bDkc1%BK}4QpE+Dy$gFklfjiat5e#zh zUmwg@i~RWANCoo#oWw<5D)@xiAZiU=$n&}9x^uffF&%IHZ_yufJ+la?ZmGg?uC+^2 z`Tq3r^vTyHias(C`XK#PQYc9S8w;yEfuKiN+fOF^Zz0lXmIyn%OY1+JolO9jf)V=3 z$lZwW{oGQ)3?i_!DW@=}=-SB#hL1ut_-~4W=}-Or6bflGDsq{l8s6wBfgdE8f1~t8 zL|a8XCR2z4rPc%apnB1w@2&Ov=6_AvPWwP{N90K_yQw_t(#XaFUYYH4Ugl6N40rIN z69;SDuT!X$i>wI)Dz`GGP?_=(sCtwn*ovYcMKR+P>2kR~sw*LU?&T+SU>_dzwg{3A zqY{ehXwqS=f~X`5mi(;cOe%}3^)-+LL2~OelTO#h0(E40JSXM1AYA3+x|NxU*)XNv z*)7O{w;T8+ScY` z#ZO7_jGd7RY_FZI{E#B|QY#V7a#?3ksVCHWM5W+B=n;}pyWR7F6|X{zR%(T^h>JAo^=D8#xy4O;ou1`K(geObM-rAzs+>%wr(v`bb&!7~wm%=h) zi|T8`+H6v3gz#X+5vHVQEiR36g8b@!SohrAC_AN~-i9r7nyG7C{>!M=;bh977V%$~ zbwa{wCD5U0&J$f2op6gN&O?Vk_cV-(;<5`QFn+_*td(hVbW0WrdDDw;eds`5pNe~g zjKc6a#3CWd=`NLbJ9yi8Sz<{{>E(tv01b}$ddM#883qO{S`9zY zh@j$ZeU(4&6Gr2Y-EaC!y|CA+Nr;$a)j zr#T^K(HCC-vUtOLD|{!3SF?+}uY>YYqJ96Q`k zhb6iNh;ziw})RXI}6(bUpJRdF={MzakB$>jtoTXEC46V3zVZp;7oYs}j(Y4*&L{G8a z`76nR)xu5~siQ=AJU->>q!-Ob7M$pucYOvYAJOgzM$vGLC&7e4rcaqFogWLpESr+@NM`Ud)^aN5@9U9qEs@w6dT+UKUpr*%2cw!=jlPvR6 z`Pxg!1j}?;LZ6itlAARv&k$y#iJZ+$i(H--W+$^NA_{5T=kgc7YZ|%+m|&9duzirf z&yl6r9ju!AegKR+3g_}0xSPfXpb7KBVyF0c$lOd+5^N4{u0O z8GI&P8|^C{ABa9O)nFz8UX5jJs75&qnRombBH8ZQsaOeXq*8$MOGEz_F$H3wv9zih zgA%uk_M~0ML_|<{Ew7518B9`Au%XBB4?H&>7c5}og7B&&!3)eAyGJ~Qr(xMwJ} z8G7*vi6sKM;<=lqgOfO3$lus$It%f55fI6r{m;F^j`M-gL@Y=Cepw!40E9L*59&5| z9q4nL*Eo7kg7isAe{$y>aWE8!t@h-XK-bj;>{dLAVV zET?Rx2;R+RRUJe=8Wmk+mFj`aa;b$f!g~D@g_?ibH@_vl2rVjq$Oe1KmA?Q0VE#mB z*d$wb|1*yv$nc03{~R#HBBx z>hU6WQ@r**u;I3Og?dQ~Lh*VNg3`M|_djl}{I>t{U25E+FMo1U4gblj_*J*=xde`b zCNtLB2sfnoT8mTRAtj&0LCvq2?Clh0v>?*{EYqyO$i$w-UX&-I15X=H6&rpK#NR3yl zQo;{j_1FF;QI7a><(Okws7*g@#v&t(qj+DdKZISgWawM6LN)OswR7(5LKFmYnWfR; zB2!#}nPn*gh%FCh1S5wQtKP2&VM${(8QXmiHf6F7FN|TKB%5n#jk}*m7#ImWsV~L~ z4wipOvz?O)!pV=A<$W+1tmgMsgS(^liVsnAOs};JkC{GD-O_BH3HJeXhmSC${_C?y zK8~ljpO=cNi4~EaIa6>zLUv~*E^~VKco7bK{VPoL65v6RZ3R%Mw=upn394hve5Nz3 zpChp_P_I1i1Nv2BQmA&6qL>ZKBRC(omXY=7XJ4 z0I_5%{PlALKPo{^UW~o9Hq3s?@pi=yk%=K)adR!FdPrl^0=szjKk0{~0eBGeLykIb zoD$T<0Mg82-g^XYFW8MRxU++Z6evI%8>v5L@@|vLzOpmAb5lXQtuDY1I(xP&Tq1GO zTV)Bu`eSUuuGNpgJex{9`tr|)F%lM*ApQz=>0{Fn7^*-!>h9U!DWn#sVkgVww7#ti z%xi)~Qs-Bxr&8g*TJpR*qJg1PCTV(SZMM9W1hC6b=ddO^Q&FktNRV&~RAf+J9ld(4 zN&^KZyy?2P7C`;$lByXY+11Yh>~5}5T9b`CUoKKWSHWM<4Qpa?cURX6gjm*RY4S<+ zQNo+njIG1Wv7vkg5?JQq)_MhjsvI6G-+w3SzA`^F5h|s*4X%WJrnY1M^)J|VUsl9m zrDv$O??n@>u8J+vp8zpxcBSML=)i#kVX_Iom zDvn~^l@43)ig21Y`kWEUY)H*IYspwX7pzY7a!p|9$or7bep1kEm1l?zlvaY0=ogD# z#dmKV$wZo)(Vl-9Zb`&ooEjV zH2c|D7qfnX=g`P;i1nHsT4juFELu=!8rS+oCl###w9)sCX8;MQG97b(qHL6;YlpGz z>bs|nsAmTZC7gTYPsJ2L?E~bZ>YTG5IoqCkARuiGR9w>WRt)RL1mSVe@U!XgC(EH< zwVQ0KI>ca>EGFPv1$HD&0_X(EsUX2{?4wa~3_q3K2aJ|$_bCHfHjKKaAW=iy=dbrk zpkyn4q=i6SW0yP+Np3j(3>)eDFx~-U%#QnS85Qh%R{?esS#wArX`U3N_|)LFwNA4h zl0hFpkS=>TF5X&>1taG%>ymB}52_d5xsY{|CL5^MQ|wS(OIa^dVt3qO8hl|h61|^Z zS(@*l_>=~YkHtBRwT7yXAix4S=VkQ*Gc1f?>N&0RN2A1Sw(A-JCLNkl$%wn}qH>9J+s_1#ZtS0%fTDOJRSqT&YJiRaHUz|RmdjI+lC8Wl7-ih}=8f+3OgfIGr#K|<`&FL|P`VM<$wILfS3ry@i7I~cySY-(# zR`o0J(FF7V-6>zwZP<{U#JmVeyOnb-d2>4(KHyfy_JuH>4@%*6jnKuJL@#bw5-HSU z%cl2Rk5mUJJR+!{6+(I$c#i6o9c)@Uq{*L56N`De26e$J*r)a{x}}3OiW{@bBY9A? zcsCEr*@4-7a&fH2FGuS}j&gaGWqx-jpqE4!)FgWQg8#$dIX98!nEx11t%V~BK&$0P zHDUdEFA;rkAaKY0*#-tjUfe&T5??8nyCeu*^F5B!Pj8O?P^3TCW3+_m?UA-K=P!== zNRcdHRxR7|d0mT9YDfabb-O5Qj$s@t++B$6phxP=W6T0w7!@yaI_(lahYrt^=(r78 zbLGdlaxs?&pC`?5>r;BbuXw?H!Y@31VQQ}wxW?KdRLmVh%JCyBWo z+nII-YhJ|rO>DAa7zs{lF}Q@2(p9t{Z>B?uTF2dG_Zw5eyZ3?vZlFu*(Dc~S+CrnE zzJy`6%j2CqkQ z!}e1+hQ$f+#}6vRr&L`X$$i->q;s>&KRPRd=RRj=Q@Q?!*QX;+FojWsbMvK(6Fi=Y zn#dh0c^r8#J4$A+=z&z+Y3z(~mhScKdLFpx`_08|p}JU@skEKt@WT^)y< zk#F5y{go%5*CkJ$FOCpJ_Ztg7-p~BZDQ&iO^7(!7kVAb0{d8LJpBWzQ%~cjM+Sf!G zy$WVA=EF@AL#Ry#k}xIRlQ}o_vbP(CIZ)C-D-F)sP&Lm z1(EO*j>ue2IHS!g_zH7|B`Hw9pM=X=#nms&8UP9Y-GAXAZO(jTGO?;eBHcEzi{g^GyMOOuzxW%)hK z_H>NwBHV0Bp>yY&Rfj^!zHp<)kuByumP<&vP3Xqlz4eRZ>pin?n@O0)^WX;MumWO4 z6{PJcgly`VoSE{8|D4vT!&J1$ii>^cMuf+#Qg~5SYit{KZ3HsYD@!^K*sQ^IrOG*} zye$0^-mnSSArJK7T^VNt|6vA%q7wq}_A)IYkA#RiL(<6^#^CQrHqaKqo2(Cr9_W9C z5v4hN=qxHPabCv6UmgE7IKEn7U#0o=8Fr91$Thos4JPj8#Pm^j_Hpw*PZBapi5Qui ztAcL>0-_2zzlY=D*(2%xtKbJyy+%sS9|$d|1*vPGqCc2{lrvQC%dk|vHI34&dSduv z+c{qbsebZt$CXVB;AfAZL0(UIu^{f zc!6o0k^%~7fR;gjWRJh5XG5r#}_Z8T9+vijTuJk~rC%ph|{j=dP4>V&Z3x_y-Z= zTs%$R&7U(^rR9%k1&R>Go03%J-`d}Q_$|4mNraqDRdg4s?JtU^zCWc{btF(6InL0J z${Y5S&PqcNFt`w{zlhaELUKJ`h%W9$P5J{Jed1pYx+(o3MyhQA{{jg?JIEPy#9B$R z{;S+r@@kL`k_u90dRN4(OBLuyKPWSWr^Xow18RKHh}amFjlI)o{aszJvc%8D^LvqW zZ=_i5nUx&XiKN)5HaH8n`GVAK@mQy+$(SO?Cl>s%E&67U(^v*m0@SzPs&F)2A~gxxYYkBOEY^@nQ~<;x^SS6t z?a^noo(jU!eG*P_UN#`ils5c4_0qSKuR&1z-^wWsPVdzmmsRUD4Z&!;Rcy;1RZa#{yrADoR5`v_P z1)u))fHAiaIhJ4mb<;Xd7?~A!=hh=)C2Bdwx?2J6w;7WYU2zj{h5nxg4Gg#uCaBnK zl1dW>S5EFq#Yf6D35iNk63OaTw|lZ4athV?M4J<`P0;xTO#6! z2xyUNz}fEd2!m?Vu|Oa3#*nyN#xQsNY-Gj&GP(t5BkJq#wJ*^(iWB9O&wWmtDk!mI zzoyW4a6S_czJ-#FM&lWPK?!(E@6zaqGeN>3%@{(IWZK9W75aN|?bCvcB*IXp%B2~sMQf_Yfs&IG3>Dn3ttPF~a zGZa?qyNotTRxdy?SvN@9k_g=>?}O=f)%>xxL^QgBR}CMJhEAm zPkVF5*j3VqzCKZUswv&nrJ7~2;LK-Gq3<9N%hbJr zlA|#?6KLoEOwq4_D&jRI*X&OUHtB&Di~9f`MLyUPww=FoTv32XoH#q$)#}Tk|O$^fl}Dk1j|Jq7l}7xtjDBGCQ5?x1+$g!_pZM8;64W5dNr8V&L2^ zz6u*-B%q0(rPGk>a9PaTx4-`|ra$Dw9SJNR?L~q zgaEbvSr(UI65K>tG(pyTaK`eX1~G{J7PqP*hEyr37Gu?SePX8HjjEV+L6emjFN!Qc zjn%2`|0)Jh%g3;FhR1*pFqcwYd;!s@^8>`!+>5eIM>;`Hd)z|}B&Z8}J_G(?2;>)9 zE)*hw<95aY_swd)gnvR7#?`G2PhLrL5Ak*h)2R*bcsf%YtxZzwCq4(RZL@< zP@+qx4IOX!&UTHz>30RIy4DpY57iylC2a!6rNf9M#RdSLpjjhCT5kJb5!0<;MT-qg z=PZ*huSZ>dZM@9(a6o?O05#N*(t97KsyM?e=!g1j6YNVXTRo1lA5_eUZ~%nBz3`md4Gvy=Wm~IJO;+@@8`z7jJccX!;K%&!a^*asYsLX`UJCsQf0HC zHzr{;BOGG(O)sTy-&j^Vz7}cvze&z2Pq)pMgzVUF8f1>>Mw(dfe;Mrm&Ci1@Y7>kF z4EP6>=3SU#^_HOBf)_`)TJeFWn-3qy-m>U2yA-}SzNz6SR}JyQNi*^vkMK2lNn}N; zr|_4+cjS|sa^1CgsDTE$Jl1JQ$n_Wz3-WO4HwsM7Z;A(pz7kNpy!>6w{o-W}+F$BM z90L)D&ky5-404$W0=}P2+@Hp622)FpsarJfu``(_6>3x*bpew)dUAAraJHBJ<;Z)B z8jo)xTd%0WdAN(!CtCt`6(jVVk{TZNZhW4Pd2>ZedO3yuz=-}aR}7K zs;u=|CQO1}AmxAKrSuDEVHc7LcZtuGLvd2V-!Y*Jao+j4YTNH z%n>UAp∈9}ntv6a-)s?S?@d$bS}%_651{JgQTWVM@HId8G7=Qh~yFMc1_2x$P}m zu>HD~0;2P(wAdL(Z>EIjG5s^YnF_E}bxEeO@aZ^qH9I#gD`4Smda@*c?Q&fv4AKh* z=?l3FY$W6hX{^{mNCWQI2|Wt*2IxqEYx!_?8p4sy3&WV(pg+T3{Xs%yiWkE~rjHu< z`^&a}N6$NstS4Q1l{GlP;?6dji;PSz!?M|Eh0usncO5a`4#CJ^f|*oE3pTRrVNds< zTulH|57?GZ=(f@To2+g;Ulbl4^o7&0m9wwBmo* z?EkK%5x-@2u)}TBzu6TGMb2 zp)$@tX|;rpl)LX!?Xi4I7>uIX&UMTc@GH&*1@y%oG_%F28nC@WLs&+y^;=fr%j&`A z`R0&@I-<2PeHELj^7-2sl+_FkTNU`77CHoErso`R9Ium{&-IKF#?01-l$Vte_2I$f zlL`|`h5c*#6~c^d>u5rpv^$GI!9f7?sh?t+z7}*6qMyXI;N!V>ozk;!LA;FtV`axRWba9RvOYGl2NN_P1&E8~`^-j@|w%yux#kTE^ZQHhO+eyc^-7z{=$F^Dc=6 zy#E^GU(XuvUR71ilh?eyS)xyLr$z4fujjIIPc)hMSZad@ zJ4ra3+(fljG`d)IhAk^9?)M<+9cP_OTAPMg@%)q1eeK{sz5M(w{TJhMB7s z3iXpmD%wPUK9%X%0=Ok@f*xyYqHw>V6{%dPUjzCh`tcFhink|WI-}= z-t)?aNde;pxuOrm1$U#n(1nd{i?sD(1eLlYQPDzN<7e7xUrZ}5{c76s5uOpFtAy9^ zKPws86`y!!_rXoOnBVI>@gE-sn&)0Rs_^5#j1++>w?a&2Uozow{q`EYdaTCkA%4M~ zZJa_qMADwvX`DJcM8uq=%@P)jk^95(NlDvFI@17F)@30QB?h{?Jr zB8*9-Lj2^+{S&m!02duD8O_gmO#wuTz%7D|-C zsZY-U3j%G26YMe?fE9V?dvom*h>CN(4RVk8IrzW@#nOwWGIij+Rej-eV}C$}NFdIb z5s7zmvQz>^5y?Yq0p1$krCKrcSFJyYqlb{bq^=iB3PTSlA|?o3N|j4r%gEa>N#ioqUKqvdY5Y!gAG-{6xQ6(^5EPfXaK3jscbEhxhg<4C)H8YqqA~J`Nh!Z& z$ATU-$R9RZl(nB+5$k^_g=os8Y3^CV5t~+saZL(q!9A^}uxaR^YXK~l?l5>te_LJf zSsy2Yr>9$p^E@6{r0fh-}|6*NbMeubzY-G%%c_{LLy+aBoE-1sU4)uACpj- zZAJm2PU5~O0mYOQY~Q6`e`VH#_3p;`%yIJ|^H>_`B!Ujdx%)OWiml*Cd?A};LV^YD z1um@#pcb78mDRJ!=v5X!~@vQp``Cr=uBt*T9Jev6uNq z`<6>cgRq^pzcV?@tp-Kr<<8Lzr9Y?Si>;w?&o))Jp37CR3W@iNLmu0P+$P{xB$3wFobQ&d z7319lB_D>Rbv9^Z_6jV9;tq^)hXlNo@VNCzyRNzK4db_P0KdK@l&#HPB*tU=@)o~yi~9~uQ(X`f@K zp@}z|creXzT%0ZE3I1d-^F~XTL&-MV?uS3>rtT0SD6184CAfQni5Z*gyle9VvonlL z`cd18Xc5=Z^QN>N9CwF~?5$f+*Uh@(K{`*tpBmCP>1<>U^1ks=0%djfc(TZhCS;y~ z4#SQTeZg!zVTMU1t2vkOs8bMXdv!O2tu9}z!@xbdImqq(QR5N{rE9?}vhtb~(@E;L zAUu!-Bmb2kS}vmz!b|TMMcuhQ`n%@0A{S&gM_evi|6P0LuGoRvt~6hChL24eAeaV+ z*|Yl}Bg!I z5f^=>hXk7|vUD^`&czzn#t+SD8;L^;uQj+Lr~SI;x$qvr=ChDNKsC$OEkBd-lsPl( zowW{VfF#f*)QX*iY0&PO8`LF%vj`IWzlXl~Deoq3rf|e!ZO~d7Wskx7Y_2)KFhowI z4t42tOZAVnHOrAFu}bxla=R43z~RcVnPZ<@6-od>|O6;iR|s z5x4$b*#DpCh2-Kpc1b2191}fo5##mQc}{IGYiV7CxYP)jA;YAoA*!AfIIBwp1DL^tzZHeR zHmBet_z~A!_lYnu5HR_Hn?npe!!)X=(?RH-5#PBoc?W{Uk(8@2ICK~j>aT3d!1fCi zxyK%M_1>7bna|H$H@T(r^X$jy<)nap7Q1}WG5*#Y*WkE$R`EX{I(=5cfPR6&c!@k`Qi`e-T_h#)GLAV-Il z(5H3dUuEA|XCA({V!tZ;&ghz}!;>m7eG|AbhV`5+=_r{AYUFGNd}Na{gI*@Fudhoc zmHnBqUhH>^xgr%J+^1kt*1bTSJ@UMUVHn*P&$4#a#sYH#Mw=G6#N$9N%r^B^Zz zdE;IaG4n&?oW)+8YBaf#Fc*w{8a;`X-q!5lJh)38T`^O04x%#8WKY5kG<+Wh1u7_G z2=g!<`Il1B*enFax>FwuS;V3ugS1TK^rae2;$AQRFBoMvJXQeJMXM2Isax{o<0xdP zg>d!x2GHAGa&oIJx2+ZP<2m<+vgj?nUkH7GusdI9+BeS7iJrV@zy6o|=4)N)g9}eZ z9c54AQgx>OX-G_^r{%TJ$~u~A3@_*5*~6gNnqD2Ur*gv407X={*&6a9INHG&2cRX_qJq z3w?IHB=7ytJ)T9j2GhxjHirfRtNnXeu6!oWZIX@M_-)7IrM-dm2bN-7XOohk zG;5S{daJs`pN#Xv*e9zilK#PsnMS|yW3gb2(~Bj1WI1_%9RJE==Q%2z(?Kn$zmKw2C-!%#nPN1M zOUYuIP6;~6M{w+xUi_HJlJ2Ckj>K;9dA?= zZVGo_L*B|o;pyCMCnbc(G*A-!&AwkQ#&vkPv9}M{kG<#j>(aE5JEK|J2@=Nya^ivZ zQHuN$Ms&Jyz?l7lp@{+^>cEXyBy3X#h2BAGX52DZG3MsRUS01kOPH)OY-0~;S!}^; zCV5(~5G5&wrXZT1s;>T2Iz-6cLd-26Gf0FDd1lD4KulZRv|y=TjxkLJ=@FhobH{H- zeA_VEmp7DNiOi7A-5~8qv)K7x5B`7V{J(zzVwU(`Jq;)R9Zeuu8!2dnD@Gagus9aV z1?160gA_0-_M2qCE*sHw7M2s3} z0SG9UaUc!W-NzglGU)7+TVxkZUW~JEblM74C}aEKWK$_uhH1nLakf574{%Mi=0gfj zrA350vC2^Y%8h{FB@{8EQ-o(bHt7~tay#+l22pRH=%h{U5o0fleE&BKA@8I_4F~+e zj_?+L0A(3Rs^NJ1(|pI#T+ac@UE%3$0jICvOfeo}D1-_S=IIP9WzJ_k=TK-^;t=K8 zEfza%o;dp;`-MtfEQkW^bq>%l&gsl=GL#vj=)`{{5fM#L+ne0KKQ>^Z>|F+B2jnZq!SR3p`G4I%m8 zMUBoY)rH`60O})guK3}c@o6p5p{d7&s~7VyA&7r$ZF}|g1!o(WFA?~777`?Ahjw9s z0RWWggFpsG2iq=5d+3L8 z4C7RxJ2<%n7$l*Gsn)`vTNp>JHmoMc)xY!Q$OwBI5auc^4+yDAe zTbpku_nil12AT1|qoMB&OYhU9YSZ{*5>vX|P>Cz?yYs;j5Br#vyIQT-M|L*J2Kv!B zWr_r*DrMa!=_xQY4NaQUHw%G-F?Fx>2#m_O=eB3k%HPQV>R{sjhWq=-&I?K>kn?j! z27;k=Mtq_XUmZ*%hD`&d7@t@bik9$;f+(udb{I5}FaSaLaK{*oe$LvD(6~3$E)-QG zuRVb&qqCodptDCN^urn{BLwu;v*~cAPbr5RXAG&;$#4(;Z5U3*DN&0sue} zL@$x-|C@pyt6ynlg4jKkuYx++IT?Gn?Fuy)K-h@;AIQw0?a)(An*=34wzlAGro7t= zFk6Gb=p$q7E9ho9(@rdjO^!2jLA1rNAnw#H4``1*=>6y4%=~R-8gqsIc#z0_7@4Y2 z)>*px(FdiJtpJdV-q@YXjyr!6?;pCp>vm_9K0{PYIfJCvk4bF{jd=LsWuoJOaAy!3{GbCAb^x~}`_H@m6#`pyK= zwc1pxnp{4s-G`9K`-0yN|%Rd zUNSkVlFSwJ+3D`Pvf`)`AaWHM&%@CABNzi>#0}4NQl~c)F{+&J#+Hsx5aNEuna%v% z@T?_<@8-|yuLTE~DwBoKxs|T+sUf^2LXQ6QBSjkqyiQRZF#Z|%_Fdr#)xd85gus$P zH(J&PTA+x(xy=F^U*C3TZG!9p8O)Fi_U@y(;N!GGqitTwLF@)*6X-vF^m85qWVvKHL zU^(l13aRpk0Rr8;pn@iHWJ*EHSl~s%GY9O*bnTY!Y~8SiGLk9}_M+GZ6OI%ta$7X2 zE~;x;ZK;U)3jo9hSJd4MO>L){cOkPGOLA6POV6>Wu>aa2alFm983~Ik^g{?*fT2o~ zUPXJWI*`JNg=M?L$o}$SjSUcOhf_9onzGQ>biG;{_m@xFpgfz(m+HOKLpz?TzN_qo zH)W3Go<6>bfCpDt5YBg9NeNj0Nvz8CRMljr7qg&!SEz5%m0?dMpYpy*$z_F@N}JI= zRq5(PPI)&J+*#l3@!Gg1ZRz%nodcq`%zt6Wr@q!A=#gq~@Yke=+=%xn`ohNji9t6? znoZ<90+#!diO2<~MYo(r6d(8S`cnax^}GF}dph^R0B%cPj42Jmye<W1>Gp*(urgYzwFIU&TCQP3huXf zs7QKy_UXLcpD|y5J1pshdR_;}K7FUDZj!Fy5*C;f4!kfp<9tYZ%cra5LHr2@3?8c{ zHmw~e#1tsSn0Ljb{;bwY?S+2t_Z8*MjT9sVmtyQ0(#a?L(b?{s6>jK8RM<;sexw)90nPA9k}O~~vJKk+^>7j_V?nG|^Xjs@yX#za zJZ9VWw9ZKN(Z*J=fo}S>IaoS{L_(!lxjG1&8qkxcrt`MvCgArlmuA;qSR^oCBwrA% zG8A|i5^zdSvXQ53@ls4$B&i4z8=@V|;v86r#C(P)%<^|1mkhAKv0H6e8;$C~a*Yqx zGy>#T_umpkqDvy^xM~`|?@-DnpbfHb`G=agweRCT*Hp%(wTJ+~RIV8^TIC|H=uNIC zDIwo#VUF^^05*GTDSckB3m8jtRz-tcKS9BVW&H2~DQokEdEAU$nf(pBpW^r8L?BF` z;C@jt1nr43k(K2~#Vf*|urEzUVxCOw;8O+;F_QXlG+wfHUa`6L$U@4}^6*7N>RJ7m zqNt0x{P^w%P`8b#Ik&TxB9|D4#b=`7Jcx}VTN^c;6b%XYBA_X#yk%d|f(8J_-PMhC zHRk@CbkPKxfIoVR70K0f4)h|qiC=LSmsz1}nP_#JJ*SGEiRmyRI;ry%3B;^AFeuK0 zk+&Lxz{Cl5mITZsim6bA&LB%*ODp%^ zLi!~7DF2s`Ja23Am^iIDQys-|g7V#4@!SMlY+SR{f%NZwkI4+!(TgD$QegDk3qzlv zMr5|Gj+o(W)ErN_DifHQ36)$cF}qk7Nxn+k`St3o8jR==rWxLG&`(kp?FuFgv+Q1Y zqXvR9KeD0XM~$MiiEoFl2vyxott(Ati+7G%+;<}2otX7Osj`JI*~AfZ3q2iqnZKoF3-dG#Z5! zolds+yOna7h+B_4^j1^Qb|{35V~WDD4HBLw81JF5+}Gm35oWqh_XPX-e7}C62mX^+ zJmnslyL#5!oZb+v=V!6ZB+M{Y~0r`D^?@FSjn3;8_3 zP_AZilC{;KG5QB=}raEOex?tWLAUgXu zDjjgV3H#Gz+$kUM*xtLaf7#Dp55ScF&N-q8s0iN1$*&nOieSI%%_B|};I)~Y*@I~?dty>2OZ{<`VAp|SP zcZ)pDV@U?EqB?W22^PRyt}sRfc+PcIPGDpp(fDIAi!9ttO>Y%ZieEr)Fv_r}t?aE@T;Jt?|E7Vi^laG3bJrl>KpFWt=KiKKvxqoiexmU5>wfM-|}EzOhF}qozdb6+*WZsy+-Mz=A4XTv7>KDE^R^7JM}1+g9tXrZ;obUa8bl@q!C?nqVVyjp>4>GJV-CqI=uZ09&B z5$q);gY(B#7Rax4El34dIh_h$_BP^m@WkY3KoD;xU7Hp*B&|R%Lm$1F!J+0etNeo# z*c(4}#9)QOn;nD_zmkeG3aBq!@0VayR0=p_=M9e3?WK9}az#+S?L0Y zrqzp3eIN(87_^IO@DPYn4x{0nQ*FSMIy+Q;W~TCbMDe}bxYRVdA9QJ0Uesc34B(%n zcjCkd&#C%rPeiEhNGrqy-(Y(s`fB?Z*j$|c12%>8lJ^Cn6>Z%{Hk2M-!uQib4)>G! zVNfykq@_o38Xtg7nT!lg&H_pQMq*1=n_j>BRJF1@Gt6iD{NGQ$hmUi)wS@)sEn-E# zR%{GWZgN+F)L+I%bO_VPAD`YiD7(P!qM383cZ>BiNK4~9=IG)c2g{pY_wk|BgSm2g z7FKj&f~v<{iz9v~a6W$=f$Pl!&*PQvH)_vXajMGyR93lQZb>AD_R&h1KSMlQ(V2)- z+Iqd*3fH*ZtH>t2tZV@n(=9Mz6YlL8x-7G?CwJhVLzv2fx*wxrD_3xlnihJ~@dW01 z%e7Fqa=*lat&#hG3m?QmB&Xl{;@K55Xw)>sb`~k|$uz&@xguM*v>vuO;@TCel)Zp!W||KO~1VngasK>BPj z;R?oX0tma2Ox- zG;&k|E7usH=e||PA)*#G1dic8>W=u|ptf4{PbM@AE(!#L3fPz1ulQ|k;Q$RVhECuh zlXY$f#$5h-dl!DW9I|ZiTcjJmftlYcBj_g;x#s`-wT~4dyx19SAbpMnk(}d z6Jp3Q7j--YP%s30ik(w9$fHV1ZXG1(H$?>YSNfKoTbY!#1QYxjC;A)B!cq4J=+HKE zt}*c<0-yjpj?td?eh@|QK^{v2?#9<+#T`feYa0k8A7uk++|HOg(l70bS)O^0o)&&i zPF$O&URlvr>88k+FA%(=FS`u@TLF5)LO+jp9;~F0_?8h5j4nOY*@TMVDquZ3CTz9dA8ij#>+WyXLm!w8>+wq~&6mo0^ zp|BW8%Co#VmEzVAX}ho&CVj}l8&LqKMA*?b1pHrLShhwID<*jG01BYO065OpR)$*Nav#wc@{

V?laz1lEY z$4_Rgq-f&D%umF!qcOB9mE+sfV%a>dRO*f+q&}M2@|z7)a?pM9N0f)NLpNqKt_RF- zURM3C+bMI(1bCR^YGeX$R4$UHp$a)cNvc+NdBr?1eaekFdfe z-hEA_L)Y!5=C6!C40E%3B?EKLm8F<_`!ub^fW=*p@6t0C(eBv4*k*TUBmmZHysD3# z{nu1s(f)BQu}qul_BgBdVr5(N&g(|&^YONbft-;oyiRnHLALs8^hX0bZrp}j zQVnI^xVQi+7$MHhchvw@w=Rb>XS^)=4=q6DpP^og95y!h*$je+27RO4T|?81qZ4@9 znhS2nuz+7lVV+vX>0Ot~-<;p^JnFh&N#2GUAkwm~Jx; z3fzpDN31yphh!Wk4r^>26D`Z&2h&&S9{HtS@yk(rqTd_ zJi){?v12|F)O$<`Z)OEjXfR4oLJ&|$g<9*gaGXi;SgdZ{aGr%V5?Kz~G9nc#$vO3? zE>o?$#9y3?jg{TFr(nUKbr{qpMRw;D>}zEV37EPJOLXX6+fp3{s|P2A3ch5np2_7E-BOW}QXIpPoGn ztN;N4K|s(PAwTE2>A-2LL=NI(cvzb5h`4*QjzED~lQtE@Xj;e_ix2_@`WV(AQ+C~T zmb2sRe8dog;s_N0QIJce=;xkPZ!mpu9(`uR$`6IBbIX30u71i@)n!H8YP^A;H^{P1 z^NqtQwB^hY8U{@uU%0h~qYch3s%$+2q?7_Mxcr0x$3)n%_*UPUDw=g+ybTQ*7NQmf z&@r6->d;lV5C90Q{!pVw^rH&eKvF*+@g6vI0(G?Hw*QW% zD~3-y4`Uc+m6n+poA{e%xgh7`aeDkzEZ#o@to5m}=+KXdvh3TBsH*;OsFMEsa-(Pb zquF+ZL}sSZ?)gjXTB;$2=1w(e9`(;^oees*?%#FSQ%q28Ss3`{(FA_xm{XyZ2Gix1 z6{~!z&pYj>UorF2kh*yf>Bd*cGX;Sim)Qu+YE=L>)?4bFb9WM#ZYSgiV?TmKI z42D_7o`Ex1fveFX`dfOkym9aWC1O3An2NCo%3nI1RB8@S#DPZ(ni6{G1sfyWOhT#F zTw>%P-sYJ+-aG(OGHJ`tWI%2|YF)3>g^o6HTPl9Ff!9W8a(z-LMju4ArNXv|LLY(} zP62AA3?G4(OZwU9WQDhpTwY%kKEU|tr zcBjzS092)hG)6!hb%urTcR?qN`fTfOoc84ES2bRofARR|NNX`&E+HEjP&FMq;5b)%J`2v{d<~)cC-dc^LZQGQ3gFDeLfhIGv}|+b^Cz5o zRJ1z~pumhFuQ*U z1gdrz5WOElZ>Xfu!~0Iv|NnN||9n=NC4!Dr@q7AbgeOQtqY$wNeMuj2XO=RLFmDX` zFwf`|D-&F;N8zk$|6ot%;f>clr}>h!$*5)B1N1CR$-uvpM0(2U)=Iv_#j5WS&8d^} znT1*>(=pcM?165e3W&53w9VkfU`nAy9tsvLz=rDD1r^?uQzLvJA2HmB1Y^niI3XoK zZ}z*^Lm89|Jpi656X|{6P89+f$RQ!o8>%$b>OIt`7B0zA%twKA=Q?Yx<37+IJKN?g zmXBgvSSLmZyq9_>Rt>fmJ(ng>yn4>nXjgI+!JzT+XTvjBxT09eWN8zKT&bAjb}!KV zB8EB%D#)3nb*PX;a)wrcw<4@LEOd3~&Ylz`@ulB*R_sge$hz0RSehm<5EQS|w9O zvx$VHjNJn_YU>FLb5&z`whHdbhYg z1#k2+$3dLPw14aJ}5PJH+?k z`qvbVT1wych8jQw*l1~^A#1L$q(nJDEBO>WQ>GV1A^SqlYkhfXKAr2)T->BjKl@jp zZu5J5H{yX2hz`&Gg+NemXHAyJ%veaW+x}iAx0QRSJSVxXZXA*#aV&{AQA)(^>I6yC zu(ny*S>kzEGUE41!wRL@du1r96Y<5tKNh1-z`?ejv=*z$}?*KMF-=3Hv zp*W3Vr2;SGxh%%vx*DDg%%Q}U^u}4q!5~8noSII1PfYFvktU|IC5(8DU2K_O&X*h@ zGGji5DB)+}xUgB(_t!Vs51$5`M-hl$akc63vrb$)H}B$dOp}3%bB6lZt(xHq;5oQr z!H%M37QieSG=^sgE*Dk(HZw^4sN5-kR}cA73CnSf6%Hd=xLtRGHZlqhs((`y+hDfD z{QkS~;xNcSp&Gp)xLcA$pmK=L6*rD;WuEKd*M$ZZGk$SO3<}B@e5(5%3UO&vuZNCp zsv8H4U&4IiTgYE;5$AW_-FopKV!Eb-gJVW6SBjgd81^X#((EQdF;a4;z7m?3wSer9 zx%=q$)>^szF#=W!V!n*AehVo}@HkW0Z6JI%i!TwK@NNAGU;~9A{;lf>j@r}q!^*1b zI0pP0y!OGLtH@)r);Eo}Fc->Pk3d%|XenmHqQ<@W(UG9RSzm**PSbSa(?i~m z`vrdFY=i;!M}cTDdC@xZV4?Q;y2%+ap@rG^g0Q)~|H8rV`GW?9Z*7|X2@wn|h%mO( zoaZAUbG4%_Z%2_p1zb7e)XFlvyXHDXUxntTFyI;Egm>f#sosXbk)o^$)X%@J$+iTC zZkENF-DQ(tOx4 z>(yrL^fzosLF*mv(ogcmPsUiGCSkSsVF2ErYs#UEk|Cz4ccw_uSlqx>SPmY;TCu$4 zJA!qZ)N$AU7@9DFH%f)Ut{o#yY}uKiXX6&P%r55@(cF+y$}lE6=9n60 zP7VTy0)=9c8aoUf!eNwt63LBqdCwR8{FHZHwd5xYzVN2axA00?t>bk(CXuz+YZG^g zmJmt=08#wb1QrSgpc!WR(idU4Cx|}nvGqxfJ*Ma^U-)wcX7IIwZS6V-gJ)({~H8F-0PzRKY)6y3|=3 z58i>+u5ZNfd%%0{lC8nA;ZHRvdmP3t0^v0G_NwihqGnDldtWSp$%yqQ6>|D`4bf=)dS$Ey zHN;p^b&U{-d@Nl>e8q+Qti8B*7@1ZO$&2Z)pAHYxhU{2de&uLyGd8UV1c z!1bR_yNvgZQ0P;*kq|kJI%Oo9ZHM$IiM7xpYsaXu>nLJ9x}0`A+e2ZypLIjb18Y$F005awY6xr=yhtAYaL%Ui?S_41HYAdSa(lFoL_29 zO^>=gGpg>%7)p5M4~hqFaW$IuJAJ5MGEEU=dryomZTN{U0EBSEGQsiO7|>eh3U)F$I$5@NY=9_TvM>{*0;zl{Ns(F_0QB&trKm5n5ODYYRtQ-EFRb)evYz^D zUwQ|1DWQd=9I)q~pBbs$P*Ry6f`YlnTDiJG4vmuk^dfrhxrW0K6nFT74dmL?dY{0| z!)!v_gCq!+N|`83L?%Y}o0az|%`78*i@{C*=ZceYP4D*pIyj04`DU|VFlgw<)TKWZ zwyeXUxNrh(IB0FsHltG4#jI#v|;3(JDR8q5vDvMRU~p)LEygWoLMDscR1| zWPjpnSaxJYqfK6n!WfXqOe>XCtki!sK z8fgUzZT=P`KpooSzRA8`#Wcsrgt=>MrpCz_&VNf}Kzz@joUUSztpW2q_WFwJ%I~KF z?_XL?zLK}4@UG=N*JB)X`wpLsOfjz}Bw&I(WIaZ$Xy}Wzs_26UGU{BJ99n`9r}cAY z^YU93Yv?lv-^Nz`6JlsMqG-lM3>rGf(4a*1P#CC1GXtRD%)Bsc@mAhkAD9A$#EWR1 zP0}6By`P8aXqx`Ms6f7Dz<5+6!NL8yV_)5CxZ*p z*zdLC!R8AXD4$!Q_J&T}`9N1_WuU3<$#JMC+e?UJ1xEyY)SflUql@BR^o4qP(8#j- zIjjk0Tzj4ki{*BU^TsVgVN=T~oE^)aD|Qa6<~D`M3G+;VBd3tvWXSn6!#Q&(D{cZY zDQ~<>5hhI_JqxH8&!ULofPyir%bz8MY#5^%ea1o;5ul0{2*sOaRXiDy=K|_x6|ApI)VN-tpUJzwKL&`BHk#0^FGilD6QjvuCX;JMOtK<_ zihn`OvkQy!-~jSx#757QcXfXg=|OUrLfW<{MY;KKFKi@5Ao#abl6`U(pcSOg0^XTs z+nOUJr+!GpVNjC^bP+YucbV3!lyRJ;&E$=Ae+hoQQitI_yrBnkC_0lbX8e-2oEMhF z#W;eeZ{D@9^DU|9zxev06iXK_@hRMp)IY(pHQOjyNOP@sjWhc6`-C6TXc3DCHyfAa z`|@O_ADETGE`ND6IeEZ6!G%b{$&-$i4EDK}{*-{{sN(F8&UoVF3@E&js5qRS)hv%H z#DpRinIvcKVCdpWNk||H|3;3mpPhjk$Q%NNrqr#{O+rm$Gl!0zQ0Q4-#DG;D;hYqG zL5uaOvObvy*`Hn<4kaH~j|xGLjr!$5F3aQDY9mcG>zXSc}4OLnm~dkqa-7OW|0OK zlRIrBP3|Xg7}rLgNhOaMBpSuJPVNxv+`rzDnBAtposL1v<;e4its5`lnN9`b(?}(A zk%iUbsa9cE+P!xh7X4+om7VCqoG|7sYkjkpWY26g4bs|A2FQa7>E z2mI{UjRCPW!UMIKX*N-7(k`bJ7KuoslMH=b{p|NWS6iQDYouj(j$$0csh+(MfOY+} zJPQN&_A)rVVL=q^5BhvAp6VwjBC8dh8shskl&}%937Y}siEl6P_d2NMvB=->7evzB zkGLDBs*r^fHq1f}n5-&|^vp2=7&W?z}WnReArk zTYDoz$DsJfQNCo$4KrAo)#|`Wl^wQ6u8L z#@JnQq`VFj6slNywbY4xW?T<)0PIduSUEMZ?G(ZjGpb*>_9u^_?z&6y@zqr=TJd7B zJUZ0Sa52ORd7Udwj*t$si$Qwh!`hzqd5?2&d@sO@>c@hkfO2qn!Yz8RsjZ&fZHn?0&yZp{Uo{$JISQ!S zfoaC^vAI#Ej_CqPI+XGSg0gu0TPI|ZSZk$@0&^(?6o!^p$7vlIfTJ*ul`;PuSXTjp zsN5iIcN2-C;uHnBexD@I#Ds?0zy3dnsb zT-K2n(;BGSPJ3)Ixu^QOm7-LiWq&yqneTJsyF|N94l=Fx8zy05VddExjin&~d&J{4 zro!01I{~qnhBt!B1l2UMg$WS@Pbr!ru~A~vEnv$NNv7RZ5f`iTgFyU^kD*#_<8&%D zIg7FS^oWItZGwXl?|rTRUGKNXA3eW1B_ypE7e!}r=P$+p3>87Iy9xFfn8d&MvS6e8L zJQNDJd=}==AAW4!X_~j7HX*CLSL&Y&-%<$BUBsyy77nwC*|%j|lV|zppgGG*$Mnl}}n8})&XT`TMCJ0i>x`JD< z*5_r(2RRfhhjpTyJva05MXPKB{OI$cPS-Q%BfQthrJamO*)HATT;F2(_ZjzHRNTn# zz#7zGmc6T_th{PQ-c-mCB#;-vf@X4)WN~?Ocj?@1$f*B?#nkm!ec58zIX(+G{RK^2 zdPXy(3`I6%-FP8;4<5*~EUg%Yb267piK_C(J|2_I%x`JCs0@*~S73>$=)P1lT$e&4TGT-g7I)DJBCl8- zRY|MZW8eg&><6`LWA> zQWF#fjewxgdZA#@>UcDecn$TbJl+g&dxyOohiq!zG=e(piPp|~%}RR9RIu=?mhRkm z6W>@SGs zK`A%>t25yEK3(?h45}iC4LKA^m*9T0SFyNy@3AR?m)Kt1j`a-x)oen^jpYO6SQ%j=FN!wi+R{|q{#QF&% z8fX{rmArA15sY=+&g2H#B_=>0L4f|76}N-0eUr`7nXR0M$ME7eLGX_Q+$P<1w|##U zsAvtU_#`}BbuDCwWHhBpIdDF&WHHK`uK@=SB;XqGraB~gPi$FnikCikaWp0{7nzhd zAS?&N9)(~Q$QA%?%bm-<&yOgHT7c35<8EV-;Y%W!jaL+?PzxiwU+ITaoz>BuSa-o1 zcS$G+ek4PoqMs+q8X4oT*`9o?zA3;iIpmF~F|Q-RZ?V;XM;jc_ieyV(eX~V(c#gNU ze1Q$=X)RW*WpQx+yof|x$on)-^&qD86WOP`o4Wap`Mn!6lly)eZm51(+3TSe(>p*BX|(u#oNF;@HiOuY1)Y0;WTO+yVkyh<*66#qwExu z5*O-F(p-AL{gYJm)~Y4w9S!4!q8O}3{zNtY81xW#q%33Z*4x61Jvqq$(_Y~ zz7j0d3j3BoIs4u?|MV9^i<>Z9VLmlBW!j_X-NR(7r-g$4k}8n~l7o9XLT!SDXxYtyq5B1E3OUI1F{cr>{%r~bBwi{U0L?$<8WBNe&N}d?24~hwVxHL)KDrF z=`{@l(-M7ix2^K{J5ko=omXd!eO)<6p@SyqAD;!)tA*&KXgO-e=bT^eHvRDb2z%$i z$eX26cw*bOZQIGl+1TdBwr$(m*tWB=&5gOi#>tm`o^zga?sxBd?>{{=-PKjq^_!Wl zuBxtb ziHioJiU)3!Cf;4OMPKTX(`taut=SyE%5SJ7e<|GHLz8#dEB)!Osl=t1G;+RpC*xYf zQ2E^+f+_tuI-i1yp<>%Qxw@cU?Q4X=Qb2wg$@D zjw}sVz*6)?2Mqyw3WfEcQl6-x2x?{>V+FJ3f*A7jw4*PA ziaWMo0Re8Ark!ehOBc-wUWE&Zv>$^Z0+Nj*cWK_we$L&ClKNj@{bP{3R4BT6ebxuO z^8&@9<%po>SxJ*3W@WBJ<`>^1J?+OCzq_=vk1IeJ50mem{DgwJ0>Y8CcPeLzbTpge zSPsF-(6HBl)2n{(6u#a3y!iRQxgP;A7KDF{IsFhKB9VfiI%JKVv#P07gH%A7hiyQ# zu)5Sf^8UThwX$BQDSKb#iN8R2eu-)V98G>N*a68n0t`vPZjOq zAOLYyqYE*lJ|hH}ovzMO5QUa=5I~jcib86)O?rlJO{;1V><7BbWSF_zS<&WYzymlq z#=<4^Z-OGc(=zR!FwpFX>o?RHhWfXiPfA()JAfG?V|57#Ts=X4@kH(k6SrSi z@!zGU*+1XN2(u$lbFRF2FJY0f!ZVcwylzM`n<2w%6a{Xd7QKB#phtDm(Js(=$a^ zfR*7_c{B-`Y_J)Jhy`B5VF2vVWnrk9XN5siP#`-F2!SlWB_M*uyDpnguCBDwuY{xw*5fIr)n_%U0wE6^=UG}8HqM(GBS4aW*nU@l zeNnJ&)TP_5CKu1d;)YPd2ATj>C!-k{r2t)@fWideF5#!6Fub!2Kk;wEXXFqvt?Rt@QY?bVT7wM5az7VWWge)A%!<&B3+Z&n(-48duM@1j4US;ZVFxb(03s~ZiBJ`CNuM4Csh@<0 zJ|=!M`tih~;mD*Say5e%@SUF}xwnBu!*S!w1oE_{P+8g|F_tVipuS+}efA=gGWZ95 zwvvBMVZ$sKGn{cGO4mtV?)@8ygeRXc^E|s+b0ulnLQU{%}_6D*z)1|ms%}TgGbk$QQSJYuDX>e|w^g(D1LwZxy;;L{3aO7WJWpI{Z zA(K7>)!2GlY7NJ_kG&AVc{^=9dGb_~;DUOo56BQO+-}nT;wC!Zdy8ZN{I?*}<$ktw z;W7JH{@=5fEH2p`n<@8hcE==5)*pj~W&F`255zq!aF>qQ6JPW|sL7;o##Q(=D5N7} zz%p%wlzytz=r%?*HVI1u6U8dHdW6PSZ4k<)a5}C~`rA$i^Iu z8U?Z?QLs;$daGG1WQV;){=ij1aG!$#T|SQ+L`>R7x^XV90bC#Leo%u7bu{j|98)6_ zuX|AZXmcZ`#P z^wQl+PBE=P4#pL*LGXBUXqQMP_k@h)NvwpC6Zl1R#ujSsr+vzB8N&mTrfj!df9NJ| zH9l#7{w68*^YrBLY01|>%0+c}L~}mZP4pvWZ#t${h|_JE&fL{U`Pe{*G5T`~*OZfQ z{v1eds0hLgn=RBB2wP|duRgg3o9HWgn{od!i&p)0FzXuVhBSuk7bZV{NMXbvB8clI z+ZQgYsa86eB+F+#pI0M7Ukgq!zolm?qV5xS&1pEhJMT4)Mp_d?&IaqGrthD%6Vg)7 z{!sgiWWuYkTlOu|lAM<7e!lOy*L*2&P)^Xa@28o^&Q41et-#~ncziI4P2eB{C1jKh zvkL_DR%nq7abp3RCV=CO#l+@02uxaaQ=uQRvk0#f8)>w*lBrXL?J`?WK|{u8&A8(? zXIBIwY&5#2=W(P}tsKCiw|WG>6c#))Vqj=x*Wu8N8gbWAE_xJ`C?tr7?VHTc=vV!| zKyX-0Mgz2qVVMdf8ZBkT+&gnC&3(*d(>qT+Mm8eyN^|9MizO!Okk*NGA|G=WnPq}IZ9^>Ksc4aW4G3D| zme-9d@RjMd9GB9`j?SZZhW5X9QzCWEFY-Y<0ES$AJe7SKj%NsjH+}R3?H5^sWqATnbV!zh*xZH|uL`gsy^Z(<=;CMgj;- z>(!U0os3nAFGrt26N%}vcLTs+ zqzhF1nu{0rn*{?syy1hK%7f5Swov!yVm@^XL_S;;?PfOD!x1%F=NIeDa;N-oM{rKV zv)}c{8dpS1qk5~=Ktp~EUk)7zy73(NHh?ywzNWx=qoP=3YSRl@xRgPncO?$&Qmg1M zUa>or$nT(>`;9ObfQYn5$7o@9Hp+QSLV)(Gmkb6QdA4+sHcPoNMhx6Tut!Oow3|>} zM2Ap_V2&c?>&UBA!n=vD$N^f^@+5S9g3lPnyiaF!vo%Nds+8i-x<|a z51{v@OB+Zeu;n{f@L#K4hFq~45ZeS7+suc&MWiWREtrXguz{~MP4CJ(BgVr(_m{+3 zvH{)rMGE$@hqZ1TgH`(Co#Q(o8wzX! zcnGqzOB;D@8(unk^7prF=+mD`q!e#al=nkYFGFddn8vx3m=-m|%Z5F&F#A$b zC;`xAfJB`-b1z`CYN#1~;Mh^Y&Q(yu&gO>VJl?z>2&M6RE1mC)Tk@PDlt&K8m>0NR zPe0CFtcX1f~XIp0M2XF`nG5gd^I_0^9v#dXl8xdXoVyf%T%jv z(AjKL?{e!}X8bjbIfO5ux>OUsl@!Wa->2bj`XuQk%HjQ@7 zCEaU;Hc{;0g4@!a9!MHF&?FT>>$L(9Vg`~tiJ#Se=2ix~F@mtsf5l;l!q)H92Rh$a zWWB>zENIr$^9`c6LGMN3OjRG13r95Yv_1O~cifoE8*fr=C1k#V^}4r#k5v~OS;Ed( zUy`_<7%}bf2)D3aA5Ve|eY^BvB9}j&?SPq0ImKCkzkHAk@%R93!C+>p@38Ezbs9Ga zGwJ9o;8)TfyDUcF^bhM@ZKMc?mC(`$-YIE@;I5N`-R_h zY@vg#GVtwgkuVWM@=h*c?nfQSaQmJDO#)8L<3ij$-@{Lctaem*tB)*Vjs+$Sf220h zORRqPtL-j&>u#bF{HS^jq>`x?%!a6_ZguvC11@Ad?LU-(;`$lo+RLYXdBP7oj%WdZ ztucE+7?|i(9CE3;?EK&lQ*7^en{(F;j1!WZyyyb@dF@vZw>u-GBs9$;-epQaPg=-OtVety<&8`{jr&dh?7b)STB+4nF*)?!-zttzJ!~+t zR5@6&4WG?!qJp`CYRj)OdkzvhZr5rqYebG&PDX_hk0&`K_uE}vKMXkm58vm*r`Hw_ zou=!zMIbBGmC*WCjab~y$W2&&MHyiZGsQ0O@oUgyJt#wdXy<1n;t8HsRZXEQP<)q$ zPnr;OQTtnoom*WnWd#-u$eo&!Xn@RuEZdu$Sk+zI8qp2-qvQV3Zj0n>X71#GYM__N z7gJ6$#n^&5DQ14#?GpbRARL(xfIDicX5w5X^a-x;>koLjnDgNmKG)K8~vzvk{ed>JC&==dX zpk5g6Wj={XGKfNR?hcw7|Gya-`}Og@Sl+wa2h-mE{_W8~U(wmhft4gli>2@1D!F7l zk;UU<4gZ^hU4U0cGEEdFVsW%sgID`q-rs8cABG)CVK4>VwQq?k%M0yVW^=dxTBhdU zKf3;3t2#`^8GTketS^b)lPWh1s>f*OEYJW)+=$Q3HK8^BUk~;FKQlQ0|1zjO z`#%p#sQ!!g|M7*c484r`Kp!mo(@`2lY?b@*L0}cg6gMa&3DAsTYpYJtFe(6mp)BSA zKmAOXwmpo=vh|%f)PF*n}bA* zDw&Zu$N6fVTbO|@l&)&+VV_uvLjuLPPz#YA3`xZ*XQ2P8$CcmZI(EW4T5QMM!>t!# zeL#IPemY#kwc=v?a8%;z&S2E-+tbbMEn@!dOXi(dW20O*`3=Uo(%!xne$sWH2;Dpx z@%*%hsuF1?XL?OiIY_XgqB|nHo&5Lp$UikW=Yc8_CjUv5)A%a*8CUjiV-md}2;oap zxe7pCob>2%oTi2-5Z*mLK(AD*0$mFuljXaA9L}nPr9+e$4qEB&kP~@s*ctUEf>tRZ z@1Kesx)@+gp;V+AZl9O6v-?lk$7sf)Wk%j8bw1$A5Br!Af7U1rNV*(~R5@Bc0IPt1 zrXB30_l||1+kX0>eZB80FuRQ}J3ah}F^foxL5@JKMV7D$*R#l%)RrSFH9Q@hw@)!w zuN#`7*1o(*S!V(T7VU>kg9;{`fqwt}Dt`eFDO1G@xjn~{~lCWw`l3brQEMw zKb+B*`Na)RZ!)?T9mgi&lq- z;c(>7r%;q$C8U`Z)N{Tr$nc^UM$RK#?dN~6tm}eZB-Zu=ZZ@$@o08E?gq;P}0&>{| z>K~gR(V{=iB}$}Dz6n7DJr7>M)jZ2(S=J6p&WI{@L2Kq@KcTRne_eD;Hp*1`KKYRm z;}&s10SqX-P1kI{)eEVh)DnKoC1i3HzEGk*0%3>gy116W1ENJs=EGhDz@i6J2s|(fa^E@e>>#f;ni>l8QK84=B@~BB zWAJpZC+8y)IB&`^D)h9Yf$16EevYAljq!CaH;+K%*GoQ4Nsl@vqrz zFI-j5`9ev3W)CBiN$S`7a&M~6Uo>#6mEK00$QCAV$*EHP`P`ldI2rSkKVU|Vk68ru z3Fff+_yUv&6E#sF1Zj7bQ?^ULFMHB|v0~?8j;(pG_zrYeaq^&ze}7{ z3-1*L=XW_0a1Lq=R)j->$`H%Jh*{!fk7^3WaRkM)DV$V_Rp+>}t*ndi;(n9wF^u;t z{qcD4JHd7=k5_`+MsDgl-qpS4s4^FQTHU{k=k66UC7No}WlA9TZA8yVRK48qic zT9RqYt0FkikeVR&A*u_Ndg0KzWut&oLA`1-S(w43xqUG zsxYIq-+m6i&yCR=R@wBN`zcqg>X#))ii^K&AmW8Byrl)zLS0z+NlUK5r7JxU8CJTh z#F7G4IRKB*M4~}6FiIdpdtw#M^-FR30g0=QoIohw0A(H7>*p0Ibj{DAa*n)o%$!y& z#w_le=p$7m?SQ1s1^?D!L6_hq#z{;VqP2EK9VcEhTe!2@5^lA7Whp+FWcA-mWIB} zDr&qy8>vg2=2+2cmFVU3fB77!66OE>XKkjH{?QXioH~Z1@8^rXFX0F)rP7>A82+$4 zoVnHgHn1bInUS4fljUYEp?YwhH21)H$*atPWLJpN&TBW*(sM5Ras>MB$*+$5nUTpJ;EIDI5Z9|^`>TPD!A-} z{o0v^5`RqE*fCT5VmJ~G$i0MM9=ZL2i0d(f&}X<~$*>JMH4je7*&Dk0`=#*0R&&C9 zaYTM7^{^+qLm}Y;Rv{2#ZT1Tn^P=;m>_H|7PM0RMnxjf*NJJvpfX8}nvxGJ};M(?; z)Z!)8`_0#DJ#8;bpD44?L*X%!I9Y}DMyh>AFA&~&Dgi#{BfyFE;?5V z9twJD1xHwyCvD6i)Xfx};c|Hb#;3xq6y*8)D9=d^iEh`dp$qj$ILKDOgU!rpbJfGg zSL0n((e{=3aN0w0J_xv*PkG9(KkXgc@=P7Ayui*8k01!w3ugxeSw*apm2dHlwUiy% z#UARCd5hd{hvWuwOpYyZPE12y?h(F8{{$HRRYvva;&16w_b3;c6hZ#4v9Ded;rnpu)~4s@SeS%`_O!Kez)u3 ztF8+4x;e?dHOLJ~mdv>$ja!yLwz`ZTKR4%WrZ^rrht<(wBt*|c`!9d0Nrz@>%Fjt) zMIs5r9K0xRlotw|?;u4I5)p1(de=46&pk0Z$9_zNW(V&C_GITrzKDX}n_^vF&OTWf znvz@Dx9EyF*v?|pSaue!Y{uwlpvP3$hP4z#~-4KsaPa9W?V&b^G23&?z> z?oS1aPlBdJ38N?@%81g>{H$;jJxcTO@qy!Df(;NB1JF_Fur?Nvi19OmF(kr^>mp2g z;GhuN?;Ip<$Z3>5V#UHotc5SuX?os>o`7{Zhp(1$>Kfd7Kvdntmk3&u74jrlgOsEk zOa~SSm-I7Ul=xXF4|uRuiY+pL*GJr~*yD(0{sXIS#n4{WymugI zi}lxAmoSv1WEczp@3+qC)I#SiYNDtZ1p%NJZOkruwx}NNC_# zLe_!XA)DFzsS68#RaHv}&BP`Ln$Jei>`JT$PX&9X@_0k#x<(K2f*1$CG` zX|Uree~C$uuS7=`GE6Wx^4Cj)#EmM(2vr)S(gB8WEMVe9J=Z>vd;Z252 z@-C`NdEe}fyw2zQp{0{cK%YI*Fc`Kdjw}=S zaV@?y2XC027w|elIa_pEi-!o{(Ek=^%EO39P5p4tf?bYv=1)EMI)b$1*c1oFNz*J* zX)W+&&rSxX0?_C*0FuD7o(7w|qkDvD7)TUbzD>$LVgzHhf0n9hpSyuReWNaa|L!0& z0Y_zBq~DEUbF>H?D_{qs;plQTRH|7&dAS|iu~G7PkX>q|AmubPhgMt58&3f+a8i!f zv_eHm->>I8g>k)M!>|6*jQXv$m^FnC3dD@Wf-@-;pPtPj8HjQ}-zFyPrx0IzSl-ldtflnv9*+uYVo(DxE~5VW0cK&2CK)MDfm1i zJF+3tw?6>6Nd!_8L1jG{mxI`~-msUL%I@u#X$mrK&Oq06s z@LO~|Z@fSP?_-&nr@leK2?;Mc#OuDd`F64Cz3s8S~vbQP)1+yJ5`Iwbj zEh&IDF<*h)Q|XuR*|KQ0cVuL{WJ6z8&6lMkXHd?u^CjnKFl4w~TS3oO#i;ggmFWj* zZ*+BcR&Ic24dl?**M+%};SO&vSr@}LPpILGJT&a#t`A_-`(5`z}svJSE$UL+*72$DIxDTAXyCG_xUAxGA zBR}5c|8iL9IgkhLX>ixqU}=118ezn`17)9&^NnFC#C*W=5pGNoqr#E@U7}MH6a9E0p9YJWL@AF*en`{x3AVu`yRumPh-5G z-@~*joZP)AC9Ote^0#|_YmR0;I~R5vywdg;hP`C0XD{DlG5`l)O|cTr)XWj=W=4*Z zvwUD-YlS&88|Ka)nv!?~1a+*n$M`glP0)hYHd@sK;To9(o)E?7x72`{yQm6VgV+KN+0c44TS0l<)$wy?J};@b30$o69l)(=v));1&H;77E3AA zbw$j^&j{5JuEux(SD=QXSo*`yz`CjiXY=|tc$Hwppm~`NlPIT@K!JPa*NxDxZyj$Z ziQB#g71sv6SwAIjno(bW!xVHj%UHef>-+O6x>ng-H1f0kzKs$9Jzk2N00V%UbVCmb z#S8Xx5bCJ)mur)yK+dlKm$^TY2}mn%Kit_;WSv?ocka9#ZI%*h zX&_}ZtTII`&=|DgY(Q@4bi0OfUDv#BtjHTEU8VuYL9x?El?{8kZ|4TeqS9^QnY#ZD z%BN&@{Zbx=yEM%?zp8V(@eNcbO1MYBH2-R@m}HFM2guL0uF+o)TU=8O9sm>G4kF;R&S}Re zI~3)GQolyUIRdV9&6iy4jbMoBnrIX`Hhi3#Al0d42_EDGxG-a*fqcTaESF&Cki9ZG zEG@t0sHc;~s99>keXA_F`0(SHq0%%Ri6rQK$BbNWetadgPL zgkLGt&~AYQh;8j%8D%EK6bC||RBtVJqGNpoaPas=?H@YB$vdaM6wP6&k&9ER9{`^m z3Kf|-GX+9j3g#soEi0BV(Fh0_NLQ6_6}c5p=pIDUl71b54-_Sm%-?pa;{&OrF+=|D zq&Q>cj{C!4#eud(8a)wTiygJ!^*c^c$s{a<%0n7ZSw=ArozB;joFTpsxRZ?G`1ko7 z1Z}+X0W)UNw6xRpKtG_MU0jgc&O{0Ypj`j}C7$T|``>{!vF6E0w^~Vs3HBS(y9T@t ziDqCdL@)gp=Ok34d`e+*n>$LHecBe|JXEY5`nX znHJ)+H@7%*p8iKjJeN=7I62?{N+-!Pwr?WDAM6ff zL>YtbD$BwDakWUbiuKf`O_+oJ2TIaR@skj4P-^FYJ0GkX{|h3XSb~_*>BL7Q)3% zINiCgr5t2je&va*JTo+D&xr{^b_FA4Ozk8Pz&pe(vt$K@Il@1TU`r}er)xw4p# z2%JT;*&P1(G#fllF8(93K zJ!s?PJ=uTZ`CKxMa2Ch6zwJ^xd{vwGd?g^u(v_;}rk%1URL4{gl92_2-mY6TC?H(W zEhYV#5tZuiMmgTiL2Xdk2M5_XG{gK^QQn6bQV0M}w`>xF2smITbN25dJ3xm|sB`I= zR*rc;bS98}*@1m%1HBl$h8=2A9qs+NbmUQ9gD;@b8!!FyM{2gtO=$ z+zMn#{wX{hiM%c$+3iLwvCUP?G&;E9=W2zUQ>`g(YT5+7TejAz74=;e^#yGCC{c~( zAfj!b7fd=V;BS>ok|YU@I7ZLIh60%d8nTH9(mq1#G&cu78GVQ|XglBYZ~ zXov}%@D=2dA+2*dTC2c%)#>Y^;Fn6E+3(gAs<4h5$J}MH1oA6yKiGby+$QX~AxgY6 zBO!63RLB<&hDH^#o)db|+MQctJ`AhkezoMsfSuS$`Ov;MuDj#mk>hFiw~q?B^3gr; zy#Fv~9IWbRXB9#>L<7cjjJF-8EAZf{s;rFrdg9(N=w-qdB#Y&e%#{o>V>@avtN)iU zAtt2XJk!ck2hlIyzFgZ^k4Ibg^OfSqUrIIEqps1yaz;R zTo{nEv_<_z5|^)BMv!6HUPl>8>z!eT8Bho%#El>XE`Ao-RbnUF2&&VY-nw^H%x|3F zUSLzUHdLSUx*Ho*g!WMFgmu*k?sdhba%(v8prbItt=m3N&Cv0i1!R2nibAwvffuNu zYPSU#%}D3v2lks1TxG5|@Rx z0WU}#tKNCGD90daymb)wmHAe~gPJLp&s(DXs=gY{1-ca~J=Ghqt zKKoDVBy2CUofFq4G}e_N+W<-V4fdrYa^spq8Tc)!1&k|r=&)xP>`XwkxtO6=Zn;{I zhRP%$j*><(!AHey8hEOWJ|#+n!V2b@08v$U`s|gY%1U!<(?C1TJF$t^8m*lyisoGy znyAYK>soPAJm2CAZr|;lPU-gq&ya}K=*&AYi4kszup;;Ecb3n>*@m_@sK%YoKbP0zeF|f{Q1%{VvJ${yu zc*F6ZT+`yG(RG{$1W0sW@DG>MNgfL zWt^ENqM?*B%;}DDdS>2^pBj}=Bt~5ftKi3t51|I+iPOZt-Fv^aVwYP4_IZV#+i2`a zzsQ|x#SS>vswy*G5Sm)~fSH+vaodiagE=Ad|2{?D_o$+1t#o%yG`Mpya398TjD`Jn6PETC z>bN0k+!O-0k!=OE+KTV~ZZpx>j;2Ld-@M;v)SyZ{zV?Gm{E|nOyZ=H&D#?>cidc1P z?{LC4E!vEE^UC*zre3$&2vS>5aaX=6LWI6H?y@Q2P@D2?Q*yVVvk4|?-4ehb4- ze!&7mv6(0_30;I?(g=jLII21lkD6_%t-$^)Z&H>jJJxU=H(FvDY|w-HbF(bYeKwX} zSlkaAK?DUoF!FmUc+onG5Pxac*R{>jkZK42^i5BhJXuR(mc0vtvy$YAUh@yAU1eo? zs93b!6P%~l2!?_wJU8HBu*jX)V8B< z>P3Kr9;(Jk1YYQ!U(yaNvevsN^s)ogP3@0%ZY-0GdG&dS+bip7mY3kWva>@d2sW^b z(9ya;sa2pI(=deKoR6qCbcJLKT^<{z=Tzsa?cf3hN^cTHd@u0CGFznJlkc$I{#gw= zE}7E11d{B(giBxMw6=;i3#V2tk^1;j{E?W~cGj8iSmtpo9(7BSCQy)|v9ZC16A*!{oo~fG}+=pRYp*O5J+?0p|?OW8Bzz0p*#Z!w9OH3__hdopU$5>bj2vOtf z_^T}Z3?^mGxB5C2x{mo)CO@0bE7x&*Hsq;O^y=v!nc! z4eO-a7Y*iQIfMmSWA5D}F&eO(@>~wnIzA2VWrB8hphE1h!HWb`!_;@TJG)#GG*y#K zZJDeN1+d2OWUXD;=JmEt^%9oGAaN~KKZ} zmgP0S*GBqQCE#EW-8Dy+YMDgP!M1NET{Ot5DCo$+oAsgIgmqFjIp`synkZ~%yqTM6E_DvqJt>$*56Xq6cDPF7 zL@sebseq$(zZVAT9>vS&m8C#9gihJP1%6WvPCh?gR{!4J0MhvJ5NpR{uvHBcxh1|}l&cRb_{SCPV7t=Cg%YgF(k*;vgZ z2-$pLCynX*{dMB&%%dj>VqQUBhg+WdmQ7|K{q?B6k2V^O`kyPeIIvkc+GQ@@u=Tq^ zd_@v~$9>~!-DKYvKL8jXxi&~pcgzfXC|aDZP2PrfhY_E{D%^?`xc=~T-^Mdy5U;Fe zz@M{`5P@7h3_tL&gpdjTiCLt0_Z9``R2zG6L<&pB(qN4cCWqEm+GZV`Iand_aRksa z-&T;|&=}6-y|^F#Pbn%T`ys>5kLaI=sms%BljOb4 zoAWazET@(J5K=u|9O<>~Z%9i`RnExgO`n>?Qd0#@YphW+!=8OvON0%1&cVG2#3=4O z)SU~q5N+?GJmfjA(t>ww$t^phxtoOMTA6kCV4sM3%ocLGSE44#hJZS-r$FivCqM9^ z*xTO_r~Vc|sO2YnmGAEfLkt(FzoJvbgWtPSNP^*WK`6DO%l%^`{3sAa*#ZTgk zPM@DXasO#nLMV#-;F_@8eE^Rh&_Nx{ZQ9L5v*Fe@7QLygKX?8)lP`yq z4W<(y93#yefy3cF!xV)5bFu`*o%stsT782UqeIyy+F}f_IP)_%P2JoZX$ARYKhOMc zSS52|&_*IiQA{EzAve%}4%rcxzvzS3^~261lAjTiphr}C9uqY)6wJNX+}!?+A|G!5 zzmDZW2jy=YCTEK~LHOwz|MUwrwf=)Mh=`o@49m1RLkJ&-OMeK)WCFLl9{~=;{!IF&1%bXVtRBCJwyvG}+aI%hH}(D#>)D-T5L`|*A6CpD=g=eFH^ z`_gam#|%=2Ef%2xt=ztwq}RiDFxp^q#f3P~Ux0ATi|SEH?HHp&z}U#7*kr<((qhVI^>}3X+e7SOU@{&|v<%~V%__cb_DzvPf?Mo<3kQtw zF04%Zwp~zN+>d*`>YSiVQ5k8!v}){ni2p&-2hNuh(4Kv z;5}GhYt`#ggF;RH!E|0QNq;F_ZBo582>=LM3Yg#4ieLNq%;4Cbmg+)TEe&hgQIGk- zy8!_EPu^p@Hio64_~Zqbr;}oW0Q9WN;UdsNNs}v;`oJMJ#T{f7W*6q`0?UZAJOE?R zBF44>mAj)P4U1&^n?Q~r5k*eyQ`6NK@0Znhi zDQfD2ZjT5m&mB}Af+Eid`a=}UNdIIqFsJr zV!p1Iq^kT?=<+&HH<*AnsaR5RqyD62NR^!3wwUY9n_V!twKhLke1yt|nnHh)pwb1Y zbUH=hjN!b7ir{zc-vy2C(jHi}PzY4oeIqEnX|InmLXcg*#^%AL!4$&1H%KI}|EfC2 zC00q}^aaR1G0M7jatNWS;!b+7ouG+Le87n2tqkUYvt(X=oHl!VdsiShBCD*=@f^qs zO&hK^lrny^bFRRHcB5k)Z6zC)8+51Fea|kSuv@DND}MxHO*z_v{yFOyG)lR7Ns1wV z_`c%%9?<$PaaQE>-s&^wr_fDq5e-NLGmZf~>3W=%yM-h1ZhZPHwTH9epW5i&*faDs z8|P_7m8Yt~GF((x2Zn3{Lq|SSO2zLb#HLgWYv$6CYbfr{!a^yf0ybv9uf@h*m0~wW z*-q%qc}bsv`>j5!yg4f97vE=spJ3ukI97X`dcb=_yo6ELOzPPgaicq=DZib%TovzP zxzRXxhVkf1kQ0l8Z2dkePohj-$Wsll8gk+|jP%B;>sJt1bN{^}wtzJO_i>YtAHO~r zX~CBIAkzAg{;|ooMRf6Ta`3)G zOL7D~oP$g;1GEVy2d_$E{k`}pBr%*;rHR$J>sv7~c_E2_e6doP*fJ-b^S0RtqO(up zkhlL@yQ#Miqorjyv*$QjY^{XuY8`c&zTd7MTv~iv7wj<2*wtx%OGn9AFN4gs4@smp z7knd^lKEa(mDr_kN^HF7e5ckjGgcsghWrhNYeOz9OXNE_sj1_rnY%bcG?m< zyOSl^>Q;Pj!G@_l9@7S zgkBW|aLXW5;&ORgrlLvRgzdGT1pB_{J4_VcK_ys@g)E{S{FeJrAM#*bNZEfD-T zJ|mhheG%EAN8gL57uR*Dye_FY-`Bn8qpY7=a4k5sKG&4=K0NoSl|o!8%|Y$CZN;ff zk%luAl-gdZ#A6cqo(QXLs|jh2Eoj@lE#9P|pj|~OfMZQ>Te<#_+7|Bs(v(w^$N9mz z#*49+OeQA2ddQLP{LxYuZ@k!FbJ+N!w?K-8QN!L5f?_ukU1;YW;s2xTErTQ3wQj*O zGcz+YGc!Y*nb~c(nVH+n%*<_OX13eR%*^cZIro0=&AgcTF%cCR%92u{RF$MH?X}j9 zfT&+e`(#y?<^gqIt7H%~gQ1H&NMU)e8bDdn71QrS#8|(4@NU1TkO$+d>m7u5AZ<#u zIOsjlr>BAFd{_;~+tL!r0*PekV~kpys7ze(KMm1&p*m&YHDl1n<%nLqp2vAD+p|nI z7VAaY$I2)=mvk_Hzp$-9+|WsYdSgd*itMp#635ZNy2&g;^o9>ZtjIenKPm(z7(gZn7GDVkrATlLg=q}TM0n=r?Su@>g$P5~U&f+eGp|XW^P0%- zZje!Ie))OT*f&>P%mZqD-;FTLK9)yNY0 z>Y(dA&n@;uXtm9ly}c2x2f$?_DVK9|OU_qFs9}|S>^rLyzXoq`3u%M=M)w_usPZVSIAL$ z>M44hF;(zkg_Xv{tveUj&ig?$OGVw~R-F8Jm`$S>4>@GR&~O!f5xg=)Amk_VFt1(I z`~||6xKOdYqwF)~S^&!$9P}VpJP@d|;2%2(Ox2G^@n1tozH8Y7UVn=O{^oQxZ`%ND@i0Cfs{;c7xoXH*y*5L` z;rWA2nO=`ZrzW^Lwgu|bOk(@oHq1g9fPQ>A2txl*n@_qnma|azCV8_oG3dQ*P4g1F zcA^Ij_!_{x4USQX89^xennZfS|I$n0H3%>AUuRGV@V+s0P1k}c7`8Yb-CyC*cWv))sZ<6HU2g% z;vYZYM;m(!NYjr@^oPQmYtRk`@uM05fSGj7Q9i%XAl*N;2@vJ=k536GfWo}RuKz)~ z_KxV-ZEnJPsewHNfC{;J1@&z^;S(uaA1`- zcfXQikteA;1cn2DH~zZ_uzvh%+xEPqaDnv4;-1Fu&_N9*Z*_KBUGxYilkwEQFc`IC zSnLiu@D^^2$W+p;6{Luf_L|TJG?RZ&3`oAtG{AodvrrSPJ6*C$j~vjLK%@67qhr5( z8Eer`5iSm{A6sHPy!3(s;b=Xl`g=A?*X}{Xc9f>GdF|A@Sw83O$B}$m?y$djr@sLE zzmc8YGxEhL(3KIux(-}4XmhbxX~+uwBA1%uJ_Kc{Kf0e9Y0k2%%|)O@|&?l;R6mfD71^CrxiWvstT*DHGt=8W50ZJDNc4iVcQ z2})IZf7K+fsC~qN(<&B`sE4Pf7D}SYVXXWFBl+Q}bUj=b_u5MCt8JtoM@$>guiD3; zV`_at3T$6Q$X&2S`MkVDf(ly`m%5SRbTMZ=wz~u!r#^#=gq6SeT5NnX#AdfOxQ!jk zS}jlc0r-{-3US&=Xsv`+6{F6Pk=mF2MM%xLZ*5+|qnSTS*j#Okw^i6Zwd->~G--Y6PmqcCqF3 zz7q5YMDX$6tRz_hjB45~)erf}`lw^1nEMNrYY4r(1tE84g5K>h=xE72V1-2SETzgN zu?z?1&yJ)s3c4D})YtRQ`fLI8l82|+8v*W1YpWpsKPdgZt`L;ldE^jidueyJoh3KJ zG2Mxw*8wqN>kSI!$OIy67F)kOYoKz6Qa%g@en&8891R!Q7{%i3x$qMR1jMlui_#To z8--p$zKACopRQwhN}?e90(vo)JUWFW=5E#V80v)^(*#<6fqI)*>cse%I_3#5GaV%t zkz^mwo^icV)C3n4y0@6#+v*cc>wrt*=-J`AGH-1n!h`Y-px+^o&$nAC|3oMR5S{jS zoCy}6RXboZKcItMN1!)}6`?JQnWRxmglC|Pni1>j@dJ4qr;Iv?{sakXm6y)}{m}-(?%wVLMGiLB8$M(@F%RU%5XRd6Qy> ze5?owu`$V)eZ7)f1w>sU1zZrX*~^0_fA!YndJP`R{b-{C-sG#|6v)ysquwVeVv=4h zt4WPa-g?>n8KkiG@uOO6=dbGE{fXskj6hT1LTV7}N&8U9FlcHQL{G{{1hT#q{}*46 z#0GLdP(9H5HVATFYDZRT<0Npz%p|$r z@RjCW6}jeJf>WnR#ia}?w8tSfdiHO*)l5+%sE*Gr&u%`UJ_!rwTnKgQHP+yTt~SW% z$0T^LL}1vtxW|@prKNR1%)Rc&%#Ra@ZRq;^e5egLodE@ z)A;YWz#X^d8l7dlk5Ox1e}!Q@2cJzn2;6RUextSF^0MV1!jfFrxqC7YMy=FgCU4Xg zdO>AG{{v|pO(h4xy~Aw9)&L7aglKzBA_^eKskY(UQ_P^o*Pw80-B{}&ue58rh-UNH zMhcAUhVzKL{MlBL_fyM7RDI_vc!`0bg4B-4@ei@OO}|wMgt9dAxm>rEy{`IX*~u`S zvR4OPdAj4Qy;@tv3x|~0R~1gbm-zWy7l(Ttt|wVeoAzalzWD2azO0`n+N&5MZFygZ zAa7HDXHtcZjX6`9Ul_3L?eG?^1BVb!)FkM;Nv?94FfZX3>T{Wp``BLmhwQ%~n&)io7#R;e=*W0v=QXGUF98777ytm;5U8@` zzxLEWEB1dr46Mx_*yiB>+zkLpZUaDGs{Sz#K+4j!iT_*VpVfW!f2aTR98g_RuqR}z zOlYhhK(Bl((acEtB0T*;_7%0Q3FOd#%MTD$^7YkCe&2I=y2j?|($#jVa8?byZS@V! zsW~oQ|AK~ce;gI~>U=V1tlz$v|b+W4=CqM|Obe0ZNN(7-2mGi&tP_{_r2g z&FKLEkS%B(eLsJk51^d^0JuwEXa2t7C(&Y`l6gju93%2k$p}|7AT9ppWFS`DL zYNy#diI)2r|0*b<>yySCA!*ISkC?oaT;NVdJSiJLc({VJ`ZD&ER7oLwEdrWl2xkzm zM;X(s0@1|G_KMxUl$QuxhQHgu=C2}!=11=s@W4(pLk3W&qGhjga~`DHoqQt`laH<2 zPa){9=EC8ERsPitLbP4iR(pzt)u(PQ3gxjUvyQCkqZttax75)&S~u?}_6N7qH%9sU zenB+T7J65C01ZHQCyU~UZ{J`4hw66(*=?F$fLkQCI^5tzMdcg)18@ei66sB&asJW$ zYVZtzq9RZHs2E!=w!``J>mpiG6L6%toHo8+XIi<6<;LL)n)UVYWdM5m>GXMS7+avG z8+9LbRvk&-dGKW#p$_fgS}w6>1-0HMn);?DXQrfSnl;Y-MAq58DOdu3hQfFnr8PvR zVP}=i(5m63xW9cgJ>-B$x0)190?AXL0E~WsAxgZGY-g6_zjRPX@cN)1hI@@YTj==4 zker1Rl-~04&eqxFJ`ap!UyKrwRGz+$mnToVr+>M8oRJoG znL#G8#{-H#s$c&EUCDkutKEe*F-yW>54Om&_oD_P0$@OMLANyAc# zgbELn`*~aSf((7u5kD&Y#u@wPt^*II_pM{rFLn%6Xo9$Gh*N^^KAF5Vmr5f9R_v_> zxL-&&{|Kz>pyA64-cu`=g$?v~w6y-_e-(sufW(Bxa{atn!%qHD%J?Or!4TWpcW%NF z(Ogfa{%B55tdV~xbZkT=oR>T^{(K9kRA6WIV;61P(LzGRtxZ-I?0z_L_pinH{>c-N z)`xSP9jsQAO^v)%_ERM6KyZj*E%Dq7*6Tbt&Y_Z<3oi#Z-Yv?WuulQ^oL5~C=FwW& zrDKXMWl+kHBQ?~u?NdEPrV)0x&-l}n!Q-FSpJ3yvk=Gq(F0xw5K3!KeM-9WLvN|!h zB6`V|@ElYA?1AOGXM2`~)6!jxOK|8B->%O`W9H`+YR0ag`GGzm_nJ+6*Dp}ol$Q!6 zdjI3Mg*oS@d@78n5I^$HOk}S;?`tz>FKWu?$*O~SbQWfgZ318M zxte_dYvu6kEesDX8~IqddU;8Z9^ zW`U{br4>6Yc~H@(lx8&RkC0zLQ?R~i_z*m}PB~-SMy!@wc~97QG^~x{v4ivEb)}mk zZ9|Ms+)5Z}rD}G`xn%k?5VpkGK5a%zomX$kPkpm67Y2PM9L2&eNdUyNNv5z9WZiOX zB4LM5vOv)I&(z;~#jBjfT=bY5eD~MJNf|4>wFD9E&zrm=SLBc}d>#cmhiXF4f3d(^;X@t+X7OKuSqIAr{=az1&U0juJf?+rm^i} zraO%g%^2MJ*yX;&@Dy73G!>Iof}7?+w?(Sgj=g$a*3SlC&)nk+PLE83WdO>2Hn<-n z!1E2s#%ry*0%NO>L#AJ3fL1O&TC7JX>yax0frc+(Ai>K}gBs#kMr!>YJB5kuh5EUVaC_!uHPwnv@8)7Z*WD!1Z z3n*jTVHBYG*jmXn>?{XdUsm?iH9`lb%sGEjOP*Qg!sW5R_lJqMUR*+DAD zHlfU}>Vm$fESEvK&OCO*BVU}kPsT?UM;K-lma#$wHMlITrSsEIJphSD>n4_tg1))u zq*5!pR@K4O`=0XV3MQot$sBe>2l6F>_QQJSN2$91GuFCyM00UuC?|eoSY>mf_uUYb zP?F??q$K2-4pyQNtG~(^J!T8mFK{c+84v>fyaRowZgYwK+leaFH9lXl?lZO{*)of!!%7p1<;Zi;;Wq#0Gg? zvhd6W>w&e5>_4&xkkVD&W2QngIy5Y(R7Aul6N>wl^O3|L-Z7d_{26JAwB|}HmMjfB z4x{?k2fajNXD>-NDC9Bqbyr2~{D(gOw}6TZnU zs!Q!;U*ltQzU8iv4nx|8cbBD?fylD`fj%*H1!Wh`T?C>@Y@Dy&1bgY}+9)@IIRKLE zf)0cH*$gYe&BK{3gP4G@CJT{WoK40~<#oX+s*#zGL_=umG}tY6dv6s|rE0B`@MkIh+(-uXuYfD6Gb0O;ZN z|L2bCKl`fhD^z(!*nczehyFvRfTj#{-b{%%qLWShO_#lwZ->44e|wz66~rtfVGW@O zF__uVg9ZWtk*S~m9P>K&-<{2Ojm(y~ujw$=QU*B<5!+qL$--qW=ZfGkd;xGf8xrs< zAG6x(oZt0@FO;Su(fhc9UE6&PiM0y!WUv)GHlENeJ9z;c)3*Kh z)FYVBi5ZxUu+f};D{WF!gOeGg-<<&hkh$M+M4#Qshp8UdtIt|S(7~Nt&5=3~iXs7- z@pIAfW4xgT%4ym%8Wa5Y6=bsyd^|Xmn%HPrIGh?Z;gI#yrz}Q%0!zPRiJTm)A>~5I z+ThC9US2fv(xd&T5cjf$Ex=lxn3AM|VV3e8WVRI|B(4_WSJFw*evDY~E7h!9C8uF3 zEt*lCbwScr=cTAYtl`;Hv}S4cM)Lf1i>mxZbcT!j zWaHQ1;V~jo>7YzIJi6j|uFsNUTD|H|d7EhsekH{pEo$rbkS7XME(cKJ8v6f8@cxIU z!-TeEeFPjQwjf0D z@AUu84^mG2BM8ft#YNuP*Fmpo5o%^n?)&^P-VEi^H3T06nxo8g9APOi!% z0>NqA*k1b%!_)uE()vFyox%=^=ln`@ZNSxk#w0b=cXE7c>0>EF;m?@e?$UkCpV*F` z6_CSXVyj3KL$lH#L@j7e{vZ6Zf6!ijT>SbF#kyR_-nhJ{tgu9Rr)^Nsam2mjzqWF7 zlmtfcL`=P-W%P$hO#52@Rau^stMddrcl}~2}oX=G+p`I>qcC{yv0yze+ zAtlS3=l6bw)`uVWk8vT*Hx8s2D?Ij3&qDx-+x$=Mgu^?@;uLRZhN#xi_bY5|n;-6J zQqJ0|>-LQh`bR-jNn5O*y)sqgN34Sb{dK(ar?JHxm2kj&D0Zlbu z+5NpoKmKJ1)1+i}boCv+(HIyr`OPbvWPcK~IJ{g1+LMFcnj_etG`Aja{tTXWkXRWN&Oe z1RNSJcaciq)h*gL*(uD=A__C~bF%K%F4yj}NlzXY=LdUa>0lKMOe&<$BRe{%mhNx5 zVyvcoJ6b!DcGPXgKMJ*;5F$ww-qB}wYk9=Oio?v?uN^bhzXFLqGy3oXG6K9a_GDV+ zw_#w&|D6s2Aix(JBSsGg%7U9W20{6Ekge==3^a4pRSmce3zc$+yLK*s&AeUP zAMp_A$zCBy{28p zOZ(UGPr!eLy12o#Zk0yygJ(QN}q-eG3~4_9SEX#5=7rG>7z<-5nPy-Xy;SS0rd>4 zL!)KD-{ef^h^P1Jmj)6V6Nfn)XIBP35_)&0wu`isP+Km44XL;BEA0VQQ^e|2xC^h2 zPBl-hd4cYx8RI0Pl%o!O{Xd-roCyfK{1SWtLR(|!3TTa*%1HG%Q}wYBqB4-aMEv=L8Y$p)^nB=8gjYjO@ZU(Uvfh_ylIghN#2*x@;UsA$ir8l&+G=L zaP7j464GAQ@B;FDc!WG=4*pbl!RT`jH6pF)xfqrG8dptC9;if>*EkxOcIEo6o?WtipXS4N-n(j>0DIlq)>Emo13X2)>k zrXE6pUOUH~ZI=h7?G`XfpHFN5)(c8M*v zuPq%|rdzN~G5|f70WhU3hG@LFhIMi3t^z0(u$9=4Z z!lL&SEi{$<+GlElUa|76IM*U@!u8tHS2;rvp7BydY=EK(MpN8QNZIGVqXQlQF}6XBNih~ zhSGXZ($!mnXu{D{Vg_Kuks+3z1|_;+42aK{{%sCu7*N%VV-q;#_?Ma>S-V`(_E$${ zL>#1T?{R2h7z#nY!9rajh>={Gcwrd{616iU`TP|`rWeUKEp%>3Ghn$c2xl~+BJqPJ#VU2Y)c z%2G^0P^?{VIBZ$qshj$!h>7G!xld-Qwu=X0}kDgwo^f6sXz)F zo2~r??MEUAGedCm;*?Aq6`s2%4n7RLz(@#a0RVVDP!vySruD37BA-|Fb?lA!@a|FV z0hIs%T40)R=(2U)(VY}<))0Wk4*+rjS!jVHz`HS<^Lymq9s~jCYH(F(9JbKr{^uUZ zS^0DIKL+amcuD|1DzZ`M^8YO_^nYajuTuZN5@6=j|6L^hTkXGd@BeoiM-jmgDTp#m z|3yt)4O5zq^&&e5w3~ssZ0B3b`=5|TN!73uLJ_Hbg{ld%Nz~gFMag=#;JG|Il}hAT z!aokKH`tOW9N<}Q667=Oe9zB3b_1M`=xVqxn+ktSa-60CALa&u z$El|>5VNNI5u`|@%?)C@husajy?VQ;jQ+)QgjT)GZQ!)`iTPwsI zs{`BvkXk9TX~4{c#N2ogB+b8V83)lc0NG>tn673~2;Yy%Nfy2&1}RkBV;yrn|B7)J zxwFYmsAu&YS+uf=Di2rF*m;!&1cmC5rIwWJx-~j~kfODi?W4abP|L;#rZ70&+0OdB zkm}`%GRa^bAR4$|LRWUaHMtkHx5KJJ4fwNm?! zg-4nIIIi`*PlbxT9w-<1qk4xB;a#qK&ZIV-?I z*J-WaG1xQjwSVK~lfX|uh91YPWuMY^x)Xl*TF3lDTfFj2{ASvDK*ih5c&;$dKGjZ= zUZPe7*iaP%_KPvCUU@q*2(l1-FS|)xt=d%6zI?V^u&W(_z~fCUlia_AZ)HmC_q6h} z?(s|C8qQ0I9BW+F*wa>iW7nB2oVu7I8}%b{k`&z5w(Ub(LJ2xd13#)JtvLym@n$JU zj&=5Lz^ke1o?*hevIq%RUR+R$GvU){MDPgV*a{5nA7%{*zk*oWx#bE=0W(ceO;lxA zTco5s{O|~xJ}g|%&*VaJDxUXQf;4Slbw2s#I@%zaV3a?nLf@Y;Tn5D zLn=8cA=a+xX)a2^SM-s7M5zj$chw5Qcp^8TdvuaF8wg`M#-Vzql;%(G*mYA?K2a4h zw!J{%5sWr6XbY|Hs>^zVDZj>!l0qZC7IB1nxeE~Nn<7Z$^Ds5Qp3c|ZiOtv|=D4c7 zKmB!oi9B`$v0uVbEG%iHwhbe%SXJEk;zAgM3td(ds>$18zbEHi{}ijm*<#Yq3QK=L zFAnZlceouA%d-7i2$<(IlZ22^0J}Q!Fm6yWLSSDrqN0+nhjqF}(v-sOVnI{>c`6)7 z4EC&p;>-*p%J7NaM-p3vaOw;KR4tx1>59*k?IvyQ2fl9kC@S_C5O)U2F3I+YOke*> z{{tGR?p^xTB5B{qwIC`|OqTXK{>yb|Eppo%i!X7l2Y<&(ODmwV1G;=c9?U-&Tc-7P zvG8GN=0V^JPA`_*oEp25NrfA#wNJd*Nqc~)IGEW*4ll9|C*|0AMRLz^S1F)Ocfqz1D_##CYW33-?;&|y}BycB-HeUkg zm*SKE2_p%!y6PJ4)CuG|o?j5Rn33jYd}$u#hS)Lp{9C8CK&2I~o<@uPG0=)Uw)lQR ze%#lwI;*Srvqa^g4RZ(cw~n0kDnnX%^fW|pLz4%cIh!BRSHQZ8i#XzKNMTsxcMQfM z$9h#rC)!P_Fg?^cP2X@vQHwUu?8I&l+cE~#xd%d|W>qyR9K3393|+=rZwrXM+knL< z#vE{gZ!E{}S%pL5if2Z+NVJj*f&gf@Afh`2O8mU~Tw^$5_Zck~?~KIKqXeG{d=YjH zGirhb1E!5lJqeVhCb_Mjg!{&4SgLwO!%LJLJ=pIe-W_mw6-q8>I?__z&-Ek9hgJTo zDB(4c%45RaYdZc=tqQ=RXND6b8?sD`Z1DPbCy< zpYG6YWYfv`u6v>}2N}FNA0yOfXcozd=+Hh<3D;Ez`XYzmVs>d!LDIDn9%P8fXCi(j zm`lBb*9jbtYL&$!K$%AChM-h1@_8Pha)Va{8VR4t!S^-ZTm5XZ$VsgZ0`rEFQgICA zRhr}sjMr4N1XDvv((S$kcDXq%;FeM`Pg--J=Tg+*J6s8zS^$RCoBNi=q>8VO&@(oB zuwgfQq_npF(WZCu1SV~+>}taqz?<*Cmt@f4<4Du&3lbPj!4PV{&|GI2yrI6$H{QEAH zOOUi*4@X2X5&6ECZcR8yPn z?_hX?Yj=Idm6>GU&(R65V-o3ChS@#0BSZX_S!)+=o@00OO)kp95@a|P+X8(8J?rk? zl97qZp z<#7L&pFG!YGbK-qd_F*SVI#mj>aK6lA45<}qY$cuCY!)|c#4vf-U`MTXOd+?VN6fM za8-B5@_>Z$to-l6FYFmDgS|-;w1eJ>x`}BKpARezDf;HME;ChVRxwrXX=xrz5;HiV zsPXrZl8)@LWoh+l(ID0cPX1;0XbA^Q4xBpyvB1g;U;MnFF>VI~^K-CQ>1WDCVPI60 zCGdU1Hsaa|+T_{bE~?UA{%`zD)vPZsjR{@_r;j4-ricS18IPastdeF-!GK5QQ^XbFD>=Pqn0Z=mJo+4EGC5#$h z5s27(_yu~MGJ`&ji(3fp2N42j>gS||s~8nXqz_e7E6*~ADJf4G>YGMQP7Il~HL92R|JqzB{1kdu7ch?Vr9 zoZMRp^f@X$-k&C&b>#fkn))QhGz4(9MzxXKvwWmIAV3|SZ*YHT_{H;0Vqb=A{yb`S zhovM_VT+^Lb#B=@8CIGvU|ZCnLPwRl-?rI+8|>q|(&zX&bBuF))KAV|uCtgCPNVj) zM@irwDSoNB0#Fm`n?!i!pl^N8vIN1rwrP!p$?W@in?kq045yC=`PY( z8hU`fc)}|bk{?kA6CFDlSTEO(QFqnNG8E*`*(>8X_g|lOF&UPWE;gK=Q=t3_xwpa7f1O0R+3)+XMp1GY8-e z2>W*{vR*!X4M_q-Y#IhuMI;2?{kVbhRHYUT`~rxm6SML&hlL^%6NSr{l5{`|PN^NSaeuR@d&XN*7*UA~Y4B6Ack+xhdlmPaid)^DevbG+0+Y zbdwnM{2JLOUb29^N#@=hDKYz4C^Ac@k;tvYU&<2On8`P-NkMp z2owdB3m|Pfr~x;BM%cKR@V-5~WrXnfstf1gHfUlcAl**UcH6 zn-E5V69bh;fXS(8&4+^)I%$T|_<@RAvEAXI6v_vFW}dELL<~q;F6O8Gl4q8^ORtzD zkI5dsqQsE=P@mc3n$S1vlmEdu8_^gZ@|>PO1Qp6Bf)@Wt8-H?CEkU?a)iN z&`k5BD^dYvaiUb^)8h$r&bp*c!;7{}i?Ntf)-ay|%r3_pnUR8_$R8a_Q4v#yVL{vs zF)fDvcB{IxXUz1BiPFFZI8}s@o5&+Es~R@MxJSdD9TzOX+PjJzHz3YZivBk>`2bQ9 z*GNM@b7PenR_JvQl1K#{_i8%hgrx8qE@r94Iw;Y_l(#IsV$-@qwbf(&GQwUWc zxPZS^DI#T(VVp~*@N3cSlzIX`JMW96Drap>L3UPMcMr^Up8UHwL8mB8F>LowBiiQP z*h)eVo3v_U^}h1`gknNyue95LtJN}crdGJTbh$WRow#y8zI)nbfNUxL?S@rECJ1Nt zmKS#!HdPSfE|ieb?vE~XMKS=yHuS;@HR^iMQVHd~?i_~7+ouYU9IZkpMw=yK|z zvR$<@y$7W^;2>y#K8BE)(~qPPc3bvc9V}sr1xKE5rvYmHeF(vqcm~@ZDXjJ!3iBiK zya4gso@)km5se$~H2^r0N}bl16{^$s;mS6?>{%QtU>2C`m&M-~@_sV`5qWmSP+6dJ zX)JDVg@l?~_jd-BT45KXKl`I<$rrxa_{(zh@kIg^-pd!PUo-SXs$-rPcYgvF>dQU5 z?=kD$pz)!fd0t#~aUQlRu-dpEgP&1TwQQABvv)Dg(_sjKf0w4S9)qUCk0PL#9lr+O zpw6wkH=XK3ny2ZA13*@E;u{?s)z0K=%(BFwdQao#PEy5W3XI!vtjA}D>+pXpUfzv% z^ZILT z%LN(EzjlZRL@+JsSsa?k#E#t3PzDl{b_BgOJ2&*o8{Jgnb1-#Hn>A6pL6)~>+vF!p zCaJR%nqQOM46iXcL)LNopFAA;CH@%docXq##wRj`{Bg8U71H*~<+Y8T1VBk>W~YbmoEyz zpr!$fq@K-I0x@N`v8XFzyrkrmAxOwk8X~N>wbmF22zO&`9k0UplZ^;msHTrQcRxsV zxD+YXW_nSOx|HY53w#B5I&-j7l=2c6DRE>2bmPZQHf#Ng5#PNn(aQ5- zm1p-RLKZpUj(P{CD|&}HDmaXL8y6cGi73cBNvO;>D^TQrv-|O>OAyu=fJL5))K2p( zGaX>I3jR?D()Px~>4_0v!&o%Je)Qci`ym+{$LyFQ;`%a>IWi z2ycUwT>zyA$ni>DaD5kP2W?T3zDy`+`< z88rbz<$PPtFnclUiDs9pIAr18zD|Ao<^VV&bva5Ej5g-tnAWH>kI8~{k!#X20 zz+{yoDoX)aT=E6#uQ_d@LxLFjNgt?62NOJeR75aH3v^tqi;?jfuu%06)sZdU(H z?cNDq$a&TgQohI#(F))~QH2(B!FZ0Z!PZJkk+gJ+(P#hCudlTzpjWcwzuU?#`a65G z0J9sTFuJV9_36q+5l=Y8pVWwB2n&uw_0V!4?4+2jY|DM)bkqa99P*7}u!=T~N}3!q z&ajBG3a7d0IdwBv^Jp@q#13iewL}p-6eeaE2K54-)2(>Q`DI&Q|I?{I>L-Q)r-DN2 zDKg|fM1JKSp!0Q|1fh0i6A>TNTMvUP?vA99l5nTFSr?`e_-Gq){!Ux?w|=?0K6cYU zzm_)~=a%bVG9#V|26oiY;||v;02p0Q=(!aP&VwgNsX`Z=s8o$EIBIDVy*Scc8{b*G zj;?UC6-l2nUKm3>K|HC*H#+icuFCt%*Wpu>!TtR{vXU9@xL*V^cDj@5(bg)AeW6}2 zOD^HnzFI1ajAO&Pj4+Oo-&@c}pm;IIca%21?S$R0R`{EDX}Mr$)I~NFFqOCi`@-?k1?mCPb#_KbIs-)u?@gdA4A#6r+ahcFCxL}Qqd)-CQV?^;;` z&k!<_iq;xi2M6Jb+p$m(Ty{;hNkiGaG@}>fXzhy&6EO133E%mKXFH&=l4=g|tNNrP zkl6V*oikGRxofZQqp=D~A31?6Jh%dB``N{T>_9R{*1*zD6$y7JG8%K1H22kI76{JBXj^e$4N75#*XeL}5n||e)+D9T* z0<6w#?TJEY(hEZlhyE1^aU}(?g*FjhP8x=;I6$f&=&K@qB~Etb7Lg9)UerNZDtQ1Q zDRv-8ayGe8KqWn0sqipjb`R-cBy#2&zxfrRnu1AxrX1UG+*aVsn9S+CN8z2#&W7j? zSm!I?x1O20)>11%%s!6tfru|#VA_YrjJ4D#%;eD{iAu!9S1kgDI3#w$j2J1eS&_Y*8ZuVbG+edh`<__TaYD7Xo%C7_yM%V_NQp~Fe*v1|13?r|e; z?U{kK2a;Wt$xyo5Tl@G(;f^gfOcA`;1_XJkwC683qZpk>pXVj6kSk)s`*17I)06`G zR}xOZp+lsH109_~T;}3PMG58hH8?sn)&D0&(cZM50N%ErR5_2^c(zK@T`c)!ua4*E zjWFFTJ3iJUjOz)*we75h>78M(Jw4rB(z&x**LIH4=#;4<-vIssEt_n4LRobxa(!wo zP3ySP$We;PXME#ht^#-yuqaJ!L=$McPJBF#P_4W>n!hDk6}_u_MFMo_rNU(?Cbto~ zb)a_unwMhw*SX#%E8oK_7TCrTT#kLjT&d zy{vEUc3Vmo9BsD)a;YWJUHO>ZdyVjGtv*@bpWB4%Wqr2Z_atL27N*$CP z^c?2Pz55_Db_U{G)(}#*P$Sfqd;0sxTPe&%QVtXpRG3(Qd6~Kp0OBYMfOJ>11OW4P zIROBH*8r3^MF0@PR}TObzvJ5m0~+~0Ek%v~jd8=nuJJ|d_(jOaneb&C3CLOt4jTm| zbI5q+@L<+1r~c|`ZTN-*2IrIa`7^KOYS_dgLMDb$Dv#TiWrkSUEToeD{xx8il7g?= zBuZDZPgYK`UGZmNaZrvfh4zkB`RzK6;m!0T=H(@J#{@+kE;l~n6gf42Lb-6Y3aY-c zryqkwHi$(gxROzQR1vssHjc7b3RRdS_B88kp>5rvXrz?$h_+Dvh7L-lp&ydC@oxU< zE}&<#Oz^mJE+L{PR7=v?=(GSZq;eE4W|cw(eog@_pjNyoyGkXWu=YG(Q9YTgFXpU@ zsed44KMpeLp90l4WEmz@=CVN=&@wt*K7D%zp`XT^)Trgz_KpOd<9e!zwfU#|*ZI`% zo3^Yu&TNz1e1pTO9bF6CXex2O^`>d3$RX`5D7=kFj+zz*g^Mf~b;x(Yi11-xQdYfv%ZA#Wk^8;ecQcqF<&M?ONcXlkEmkdq;X1U{ zv~;pF^tSiSFyQsqMv;JH(1W8C!p)}kPwv^YhL0svk0)S`Ctzj0DlhQ(Y_mEa@oYTK z$6Wk^@mpbx#qX?FRlhorX%~#rc3zRmxmkc;SwK(-Xm}(Pw9V~2gWB&Motz%=Q|4jE zSLAU+k7)Ml?!Ur@<^Nx6cOiP;GG$EFP88tM$4e=B7w2}RP{=D^6c8327B|orH!w0j zFt<243(1Dm`|MlKdl`E^?-YdkpHSF9tN;9ock_(Cp>d5^QH20d#d>9RQ&9)=Z*^OP z?_CD8IvIlRSH15m|EPQ&EC116s0h$a+aI3gCrKr&*8YEb79dBjz=i!|SGe8$>&e;1 z&Rg}onCAm8xcuR!B;4Yk5*Py{md!_dRM#)Z2dtCge!;_`hg-%jh_Ir9pVi7(>j=3^6k^ zL(I%faU3&^*@>AMVo1!)%*^bVV`j(9v*YK!aG%|K-*12HcTS(4?oz2#l6q89RY|p8 zR^AnY{?7kaVRI5X1vJu?O6eO$-GnBJX@K-g@hfAqDUGk+R$Uc96{t;ym|1|@ z#Aa2(xHu?~@#rL#bS~K?A$@lN4q8(#dfZI%q8LN8t4?_PL!dNo%=WSEyGvOmT_dTG z2v82H4x#*BarhW>BwVi5SnQW;HToCEp0-T3_WW>w+zAAGC=%%Yo-(?A!buW#0e=#q z@)&y)AchPromNX@Ez+}lZNBAH1+D&S9V&3*j2B9xFwL{Yq`!Y(@ZI}ePDA!#s}^N zWS>^RlQNJ(aJv@W!QiT#P~Jc>Sayi#HsJzla*sRzk<3;egi-)z5VASsT`S4~)Mb8f zki&q45#-W^*p*a86U;+ac41#`nvLs~Qc7V}n5c>-0%|ISZJAKOexJQKcK7|{biOWb z;Cq%-_TxI%GQa!cGnjX;g$ewfw1}Rf1M*4;EEBznfK_XI7!)-iq(}A9s)wGtgGY43 zO5u$O-qVQ}fB_+}$C%7?1Z_-dM0$wATLrc#(m=h1a@_|c-8P=5AxgZSPj4$xy)Kpm zjyisx`<~c_VpZfJ2Lyxw0%#rR z{4(!gr6l%icb2V~uyb^P(E&r2l1WN6vVdhhEzA_k^_KUvNi!_cjJ%~P^zjn+h(1Z2 zQx(vM3#i;_=y}gFL;3nSfAIm@LUIAo%S~k`KX8y)}o5 zG*ZXe>fKvb*Wv^mvc+hj5eE z`0T~|H$)b!I~iPsE!((zg~oKa$66R{=8m-|ly{MZwL~re)&pul`PKQnxx)b1uP#_*ktZgY6F5^G*H-sTECUz3UdmJv0~r*pAL=%AWVR)a8@zxDZh% zLs&b`SyE61WthE{KLd0K+sbIz_h&!1qMV(4q~?u_Pis#vZ==P$E00u6E(z3^RvPl^ zt%m>rD!M++$4q#cKa>x362@Vtek%D>!+aGXxyLsc*8{bDK@QbUX@t$>AU53xb$9AP?{4(@nqJJ@d=0rE=*T>sXjKK+KFy&SFEl0%fSFKTV$kuV7%E9a8 z=8Eiq*-C>T1Dx@u08{}#p#T8JF5nS0p?)89`CiVd)u_JKH93Sol;hJKv|3HxjDSPmX8+xDm^`kMXk@5m+G8-S3OC0wZ$*jxp!fu z@5oZQtjxbcq&T&i{g@k%Jr{g<4o!uCg)}4-9rhuE^5l^d2A!o-N%x<0m~dEF)27w+ zudNZU^#34TD`sAj3gY^aeTH6@YqY?(*y75 zJr%nV1VRIBlQc@=(S=a4{H-Bw!H&_wn<&1m==Hb*0K22vp;WN%r^=^*Bts7A$C`lP zb0=a{!x*3Z`T*RE7H`~5vHXHV^?BMi@nK{Ks*Z&?Ybb9nK4%WH!9Ct?JzE~nwA|@| zSmM^S2?rlU0~Y|QzJMP_P?hj_XYI;TS`AA9kb&C0@vPPF#L({2!-`Hfofd#(oIyE+ zVIe?dQs$3F=Y(-%1j?=yRKn@elru0zen&te1#nEy$C=;n_@dYyoZVoqyhP(qk z4-1vNu7pG4>9rcXjsXDVnG1bzM3t|Cn*d|bt9UA4?nZc{2>`hXAWa(t0w{7N1VKv* zV3!C-=xr&GqX+l;blFVNY0SHG2?U&X$?if|j_vpKY@FX-DiQ zAt@qMX1iR*9o1%(8=C+0L(rq`ZOAF35HkOm{exLwN>$(UJn%qX%dwmH01fNosh9kC zye{hHFrje@OP1%vT*>dl@*vbK!gL~YbY>aXN4aSXJP$5n6WD*ui)f1nLQc&JddO=M zD|QgY-(~Sxubd{-9*=fDxItEZiv2qV#I(J^zxDKl#JYtn9cnJAC!Zrc|IvPcmCTK1VrhRc|hi*m^o z(n9h~V^rcX5uEZ$UzGP_yjamVVnR+qj7~*W>0H?rA(bOxA#G}lSw%$cVU}zGAGpOG zeqO z+>u8I(x(4BsN+%J&c8e@5PeqW8R^~y$&Yylq|yL!hUkhH^T5{rbQ4E#CKKua^+Z_uRhS! zvn32OKBhTaF7}WG{rekC;VT!7P@b~6hqcg8-q8e$h-QL6)TF=lD`^t+2e&1p)S|9o z78mEtqN6tLU0ladNdbg^tO3F`hu>T%Kjy$(eB5IVK(OCt!T!~UeJ+&0MXjL-r%_(o zaNQ{|&v$KSvH`4G>|GJy@9^~r^&fj@+)+7UOI1-|HOslga=x3jPwnk_Ag`zLLnQx~ zlgCtP7bD>P%RZ%=aO8PWFHK;$>p?df*h!iuMTv1*q6>TV0PB(lGx zEDUhRgT#HF3C|M%K&2qNq5sDwpD=sQ)Nkyw@3yZn1ai0sY>vuzSIhwD?~I z=yiNtpP==3onkH1aUP=V;ED9mM~+&(eRt8R1H7h9Ls=c16{o!Rml`(yBYKngXM)(( z&>e2FW^gDcaU1Oac=#LiU%YVr%>K_j|04CDiOgO7H~Ie&^;fp{KXLLup{h;(PpSVz z#Mbld@Adn4`5e|p9RKey|BlAB_;h_J6 z{vWY0!zlzMx8nUGuVnP1gY$Z{{8kf$(j=EPt5F zL*h$0);N0karl&UMxq^$<;q@fW^O$LL&@0L`Jb;A>b)i=b5QT(Y_VQsKFKmB%TvV< zo7d>PJ*0@W@x*ly_(@B-!GPKWALWDl8yG~KKtYxpx_CxnXw3JlZcqkhe6c~3T#Vka zBQ$T#O~s3#oICkOm$8xL9X-~bWaNJ_sN~a|)z;3rvh$>O!cL?!PBnnr#$*0xBMWm$ zzH<6Qf`D|Bl)C{BrV0vV0^DJ(8K%4vI4l${&{pkLu(ZV-jMQGxC^<`p#$UwV zsIK^W;v*r6BGSbehEX6yq{9yddr%=y{<>|qdcnnvjo|#W&@&rWt@THurJ#rBm&2WK zFg@!q!k&nTm$5ED8mS0Q=zu4qJcT<&~ERX+GE|-6ofH=$@#PPr&Y2}el z-=;9a$dSK3sFBk~N;OnZ>55eJa6BH8H#>3HQky8jL_ADg0u^q=}JR# zG2FRol4F;r*K}BurOqqEwIVa0stlF+k3+u0gJ8q?cMn_83v=-|VNff%Yy*o>CvYwn zsk61hc1MDfWe_Q%LaccI+95XG3SIRSg^)cgY+G{^YLa@&3uQ;LWOZvX^1aLkuWD~q zqMteZ;7qOwcZwerzfSk zq*4qts<|M0-CC!w*GiKp@miQ3$h(VCHaH@WbCo0-A^a3_?%!{eGYiF!P|Nc&+?l+g zgS@KNA=Pg(!6C4PsCMYL7AC2v)N^8>RvB3M2t1%tvewZq9ilb_ELl>34EfrjGJU`L z!+OYSlVHvPJs*E#XKza!a)E@~Zr;hY4d!`7F_*ep_%i40Jr*Eh&TigQeICd&T*<-t z=D_Lw&aAuQ$>g_?#=8RnO(G*EW?p7>Dj&R?O}^Mavda6s0*Wq7vAJEFtg#i%#%}cyl|Dvt7!B0Ic7if^+lf%$U+OhUHAum5!7NpYqp>5kwCQNzXv81KM!B>Z8YDhWT zf+snr_TH7<8V~jP%nPBuq6Xua?tNm?)PO))DAqL8sYa8=LNeCjko=0ejc|0vPL)nD zwk2V^7LY9P#UG{l)>sgbP5zL+X|Z;V3`O}I5+Zk5Hrjj|pVI=1zaeNCCE zEY0Bgbtpq*+g&%()rsDo5Ke5~SlR<4H?6@Bs+(|SB>)79ev%K6ul9dVKG;6;;l+zN zx;HUmMoRw3FNdKK_)%7cv#_cBK_>(@1dZu^EFTf2@ngL=l6}SPLDMKh;}fGAj_8jm zQbwQ97pQtA7vC%{H!d`>qg`I6{&(d+!+z@XmN{yKrTH$l9{C>Qh-uJ6Cx?Xm14ay^I!U9@Uj4f z`|jK8ZEfmVBpd|Zen<*VjJD|l4XN;ZJXrt7}sY^fQHdGaBy_$a}CwA477LT zJGb6Pm4qPvlJ9h^wSnKFf;9*0U+!U6gPZsXPHgFn*zQUYadHq33hT>`8`aLpsq3=@ z``{qtLG)iDwAg20C$ej*T1o0* zr#un_g=JM`Ka?~V7p6G+b`oCnmkyd{MvnPPSXh+N7R1u?r5J_znjQSQv9HcxdYnqH zAt$1^rLTz@E`p!SBt!GN+A@9TG@+)2!(GZ5oUW9}K}F-bL|Q6v?)=bXEO{cl934kS zgHpYeNX0u)S}T~ohgw=&<;(RQnsvrgD5ME76{K5pIMdF0tcIHU!%NIeaen)!@f*X4 zu0qxdYF3*2yyaYz66P_eiUU|^pMrbczxEOHyXhIJs-N#!U(k9ni(gmyo5%UAjU#%T zVU_=;f+O4ZSL`0ePvrOBQ+Y>@_Fnm-`yiD~oG?nbCDG7N_%LPnjwvNbGB#t+PxxV{}do9rIG#@|B&}jhVqTAi;_d4)|ELNRBRA! z4}u?Of`-HoZ9kt-s`XszIW9SZ^1nn(96lnqy2LYJv+d>RTuHIV=p6ut(`dGry8$! za8Ab0tS57kjTU_+!_b628>9=k`>=ufg+Wm}dEytOMVF-+v!*MZUvyV;=)AZ#zM`2AlYIsQ4IMi9fl1cP^Mi$2i&L(IO( zS^)zRdk!{xX~}1hLWkPP#6Urd=bokk2GZO9^3Og>|Gj10v)rWNXGl-Um%+DaA3>LY zN$UT&X@DVbd%^#@A@}^#%YTExPjGv0RR5dgKRqR){L{bC!PMFpL_|7X;{p`mX7 zn>GS-Gy?p$+0d*X{w@3CW&bv5;!pdEfpXcWTx zfQwK^nHNEHK{P>Go#=a_b@Yj!t~9oJge;7nNm2%pDhxaESC&#*a=ucMkCKE#tzTlT zxb7~s%z1%oCo|q8zr-%P!famF1Y}7qx1@n>d1`gI2aO1n)0{vkEaFYTth{OBd0R%w zmU_fl5%Jx!;(ivjMWo5|JVw`_N?HI9kYm`Rl4^u$`{}n>p3h^lyc@HK46;YviL`Zg zDsGCwWr9m|WvJ`b^>YUcjK;ds?H6h$dvj8a=@l)h8n7W<$W-yWA?at>{9L8HXuDb> z+k=?w$C}=+5z4=(@|V<^AHS^5&L3d*$(;03TU6__FGz|JzvjebA2D8|ZZHzj0uM>x!LH)KJX|K|JxWRt;F%N{cFZN^6rK+qt*jxs0o(ix)PSjit;ejW>126KLP@VvFL zLIufmet%W3Xvwf*gzqkb;&T%u_p90)sEG-p+5jf1-B2A zI0J+$(ytL^svUbt^mFwB{KoJ?XfyMdRuK)&3O<+z*guLbYX{o5n#B)&6xC6P=FhJdjR9DSIsa*0zT9pk`^a;INd2Ta-bJk5`NY02+wLmi2EOP$UW8jaumC^=C<&zpi@r%gN!H*7bYCnO-s9dn@7XV+R*L%Ssv=wKO zfTU~pHpIPo3k&fJ9RHsjo<=Q6Uz*n1~YIsCf4ZytNqr(JwVYf9tX_lCkv8~n29U)}s<*HVB{OR7Q3F*MX z_*xZWAPoSJwQ?T2b$2ZJSYjO_1?_faG{m5fdvGjz7m)?ZLv%(-a0D5ng1 z#H%dY%O!+N!-C-K`q{mE_e=@{55Or62`Nv*tNox6(%BcTUk17Z#u{8t%+f%g5f@K1 z#!t)Ns`rOwkuw5S)nvqKjt|a^zO1Y=TjV9%AaWCjk4b~xpEJfrqot8l@2*0Kld`~f zmQP-Y9l#w+iTp0qG^Ux`gtbQ22DBY=!hE4qYFrT;0j9j%0V zAN&qZ?W}*~`55CFijxrwXj{*^-si1sm95G;OB1H<^eS$8s{q%!a_88jjD>n9~FOws2{1+!KPEXjAtX#G(pThNcmdae57b9Ceo1iB z6k9_i9~3LdRo#)9(X8C4kZ)?_MaIL>ivyvPA6aff2|k3-%bRY|mO&9hJqZszvd>TF zFmeiu;r3lzt;C$BZ3Z5G;v0qyPkwUj84+Mv)EX_O*-cg9jGEluo6|ODQV%?xe`PO- z>fG%JPkQJ>-ACcCiSOzZC;#RHs>9hU=C!)ngiyprV{j#cW&m)g-6a#q7`2(N*MqGu zw6%T|askdSBGHt%MTf-9wWVZ1E3tQ45)y^Uj5D~Qj)Dn%c@UG8D6+lhSUHbnw(`$6 z{o;-JKQp(wh~kB*VX|pZnAZJnb+5#Khn^c_XSW?nx8tP$Ww3#z z^=lS1EFI3=5vt;L(uch-EeebK)4XW5FA`cA?cnyDF5fiBT>9!aFrmr*C+3r1Ey5vp z51Eh2$abU78jTrJ8pYwJMg`^G1+(K+u{H^WodjcK`x>pfRR4%kM!%1?R-VQWo#bDb zJ-*txB5$J@(W3otuV~6(u-d(j9r-Zb*JK20h4CVYfY0C31V66|fgIUJ6kZZr8u9~R zg>ub@mDkXri42d_(hw-w(`*SO%0;zwM-mb6mzvbuP~-1#l7Z=M4+0c6@l+b{G2l*m z%bIwakE7Pi!r*B@-_mn){u$O7pQ57zgWs{U2gd3?hvcI=E5nMY#)bT5mr#p%%I zhzHy=P%t!%6aXln-ZXl2+d)*v`gM5x=~KN~7u5BJe8| z00Kz`Ecz`fQV>+Dy6DA#SQ9ilx|&Ivi7gVT2#GY^nyidDC#Zozpc(ouIgBe^BaIqs ziamRq7c#4W`MBLKeLdpeo_Ej7)}yC*dafEHT6Kp{QU@R|lz@6WT`y>jyOUqIuz7JJ zxrL5}B280CYhH)7 zs@|Rz{k>JC$hjH>=O`*jYw4;y9Nz9g`a{PI`qV{@K#D(2WJpOcGIjB`#U2_kZQYq? zvwB@)>oh5Vnykk*w;QD=TA`0Z#An&rE6wLF_pdysH-6rZA1UX*Lx|wH*j0=HN-u4Q z4Oj(TfV^^L?(W`nMl!bYJlnoW6$(ig2e$r0aAwGqF;EMuw>MO2=ts_4D)QoqnDqtyI)$UwHTARDh8a%ZX8 zzIdKt2%%-c(1;1BPf=u@y<@d%bHMVHRMg(al~_Y)iPTPZ}2jJDB94)2` zH3&&YNceEHHcKkVpkhlc9&RiSty1Fp2>W@)cI4^Pp_}SldztO1j=W3emXXJU6{iC6 zA9Pb*Jy&^(_Y(V=FDw^!yEme$Z9``7wYF~VeakI?kZO6$4sEImqFF48nEOg(ax$>+ ztO~omB}C*7nmCviP2??&E7$#25MBDSe7{cZ*Yh|0wIp0no>IdQy54gw&?QnE(1Rm7 zi2-0|Unxr`^|{(o1`iVrG+=#5jPK@OE*INg`A2mh>%CU+HL;bK1B+{aXbw+UO2()o z@fOqbo{<}Wg`ZY?&08d)y z>QC(5fs9y(T+3XfZ^?!}yi_XmprI*>t2 zuI8J|;>nC&DKb`-E%_~ERHqqT}n3Y9-k%#lg z;@kw*)%J=IJGR)!tnHjPUWW4Z9GRt_USx7iH2qZ#fR-qj7orlzN+yW7#PM~HmUG?3 zFEh$AyYUfn2j2d#>QSp~JhwFUxcpb!d)4C18v0sCD>M!#o8Y0;GX%5^^@L*#K&xM@ zlF#Eu;4syMUfoD|=N$A4BjHpIRU%QEdd)yNxn9mwK zxf^W2!f8^rddv^{*yOE&Y28@)46ymJ9Hv&y`B+Fq!%J?I)tQGuy){QWy&cN4x#3F_ zi3kM1Bc#|boBKb|)LKsX8}hD>q#KX5Or6so0MfjSXywcIeqBLH$lEIOUQa;H)G;qj zZa0r1L3`N3Z4r1!uhNL@mLJg1p9s=6$A_`ZoZh6c*WalZBDWw)z_o}{;UP2iA;&{A z?*DcRF}+iKDq&a%Aq9&u@B+h_4NyzcQf1|mlm0o$=1;Gq>&SSpvzApyz=PA4RlM%2`?We+GN-znRk z*4_ASt0-I6Gq^$$%BvahdH%S#2p4qz1S$C56tHHk-$ra3A3lpo%~huh8SyN>zZ`g0 zFwWXaje0Tvm}^=5is$hoC{Z&rUB4?e}ljA(!^!qZd5YWx+1w05LNFjc&nRolN!}Zk&r6$OHKI!vt5k90PHp z{sFg=^1RYSwvs&BUh;v>wRZvY>6*4+FB2q52aK7KNyT9gKkUsEf*qSpL zi9ajHp=}Eyz2zDoe@AFv2lEw?xBjAxS=%Jsrd(f&kh;k(l%NExiPIME+CnRD$S?dF zS~q3tjK50;f4W=lyIj`d(~Q#QG_nbq@87ARB91j>rkNp&kU762Xxt~)@o@|si}eK2 zG7sgD;TVcDXMYNg7=YWvoMph$&wp>t-v>lrSfWp)1{47SbT0eE4ZSx)#;g7mqp8?P zYP}T=YQlC7e1@BK@HG(;Dh;N?&>CjD*4E4)DX9;u*&u$jGs6scB$Rn3pJlcpx<+3h zhm3uCYx9v9Sk)?hDHX&T_{dp~;KFc22*;7H(97;=mdig4<~~sJ@fzxd8dpRF1&X>0 zl_B?A^^?fYvRe;Lwe*ei`c}UCe1$JW`($OYtFq^7cd+r*WgR!ncj?P2HYE6X1d+!b z7TZG_#z#*A&XJ@&4BnsCb2SqI{)BT)fRHbcTQb&O+hBK{n*#}s1;=^<=`r~P3duY| zu0-0g!P<0@%yf-c!9t?-K`l;P*K%4VRVU{?dCni#C@z^8q8NX*^ggVFNhP0AbfuLX>R@}3g>9ak0GQor3nzz(2W~Nnx?(>q!~um1pznqxpw9F zZd-ZMz^rRN{`(8X7u1LJjJZag_&`+eJ3m>~@V1h~tuArdCb2|Q&OoI)W{v)B2m(Mx z#kZ*Ec?gWn11zuf<~45c z9BTvg?LMBh>0SGJITwtH*yr$h8StP&Eew*KBn!jP^6T+G)wIs(n5NaP{%a6{cz5Rq zk@MPxh2|1xEiip}4K^&m!Ec6UD_e7ymFwXAbsRAMQyt*^jXvUa%C%oFwwH;Z+YZ|! zmdG1S`lEX9N6(%hLIs!Ie~d>*i!^myb6ixE$+`s;SxA2kJy;2nyPa?7s3T?OJ>+jN z2<9f8Swq}&FVH#gE#(Wc`FfQHy7aGd|jaXDW*x#Yh0%&&|>Kw(o_han~nq25CC#53*_@Y z&%Z>Gq*WCtzRn&7Ni>HPot0a5%KRjR9-kGE?_!H5BaTZSgljD(0$ynOKc87qRo+jj zfwC07bi@x`W>`emY@w-D3y0z1P<1(=5>%E#;ejzxLnlGxT{bc5pL%46)3!lHGK;-_ z4OLmc3ScNi`_1A!2BtHUcE=YhrZcsH0Kl04QHcQ@SIjSTJJUzqVNME-5DpET2N{i` zZu6=yLiZ-?sEm__hcK)3QSY)_+?Q9wcUC+6AA9m2yUnM8V5?Z^)sZ&`;5m^403f7X z9lSoCkZTcsg`Hc?zB8ZH7h$^dJb~*CiDr_=uj)0(M}{4HSJ;KOa_lqnK&*#b2AX=& zF+3K)n`$a5Ni2Epc7Q^w zbOL3LVrh1i4E@I+11TuO<_(7ez25nK+T4Mv3I1Q~0*AcbDDXL5tp&NmudRmD34cJy z1YPmZB}~}Q8eQ1}*grpXB0aGjAQ8(x`X_d#!gM=?$E|x50Jo8{IY!U|Zswh2*6CP( z=X%W*u55ydL6Ef-=~L*J%dpHSZfc!XAyKBs#cU8)A-0^31rz`PI-;edh`4CGlWLET zp+XDCmT7-NDUlF>Tm~g0+R1q=7^Pa0SXu&_TXzLB=nJV2DA&wah53^kOe293r1J?I zH{$jp04TRmnsz`257d5UUUGTppaPu)Qytd6CEgj36zsO&`Fam6KHIA$igpUEvyxIO z{8>&-!HEMi7u@lhIU*Y1<@vu7wNMNxyW#sBi;QPNPRI34r9{LDY`^-RZ#|wc!(+HoJRrV_k+7 zVxYh^v-3?nx*jXb#>5~SuB2xvgZzBd(>B5Z!Z+gw@5#@5h{AQou1;1S2Z&xUa%JcJ z^}dbWVtgP6E4hmWvxfr^^=bsl_w?fv*EP$!(#d?bq@NPI1+~ug+iK*;p!h-jhx>i`^4E$G zOU_ySEK~z&v6|d??%E^QJcQYe3Sd9a504%IWLf`l79hnLEiGaswTV?+Pb;w}M2LCB zdoDII`2nuf&km*<+qzvOE7vs6kLx!d%-7=opzI!nUAb<3Ap^$k9Kr_fW4bi-EFW8$ zsX$J24K{wm^Lv&m1({~11qH=d#G=(TftRbCWOugz@C^O0u7+IaHhw;%?nsM6Vbo5r zWKy1bC_~)CgoRV3?`58!yr#Jk)oJf%2`?Aq`jS`JsaofUnrY_kY#K>+l94b%ONZP4 zY|lJGfK9S;hnXkfP(eST;i+0|F@RE}%z3CT;9DqW<1Ul1fY{=p!{L z(GN^2ml?dBDhFsU-AUpZGlnA5Rat{FnV`L>)2cr%xKL}G?+96*LdbS4zhd-w!M^T# zOGt3j)izDZ$)2IT%MW#H5BIb!2+r-c37Y#IxGW~(6WECtumG^{?D#=`zqiTydX6@^tH3T;lxg8mn`uvww{1^YfN$>Np002i1@)$|z}pnB zX(N(-SE}qo1-H@Zehd=WS^P(lqGh`yy2#udV){$x*t!i9^OJd75PIc^!)yNv{?7C3 z*>z__D313&Ah?p*G4NIXWPH}8pUa9}JF_`k-Cn2V;s%q&?; z)J3C3m4CdhkU)5Pyo-B>gua_SB0-cg1GkdOaB6#feD~Y$;W10^bjx~HAb1lsi7wH~ zHZDn)UC6#*zc{g6UQB7khR%2>r$hBKUj>Fvy>6#UCrwL4g>A@W#JY@Hepi*#>Ix00 zxG5@G5&Dq?X>LL7kZH;3Y;ZV&T$cE?WRIk5;RF7B8|g*%n0PZKBSqv7{MI>Coo+hL zp!Wq?if!M|ZgI?*(`^QV;5%^QB#~ukit_&~z38fXu;ZTx+TH1}({LT8`wo zKjegdYwDGNT9XQ0@o_I%blRBjd9>6swq6N>oBcJqW&5<6O=~4T#rg#y$*OO;+Xu4kgl!I-7d~FCd$zCRmV*{X$TUE{CM7ir>yX$Z6vUyi?Nh zz98!IPyFQodr_BU@a@ye6t0NF(|DR|8+=$pRryl_R zUuf(#7rFhom2t{Lqp*byI_FGPP~&jkkidtw38N;2xXh-I5V8P`Xy&Qd)s5EDWQyaC|C?@3@%deVrUL{+Ld>h2c8GzwyqA2_v zz|m(;r&|PmQn#ropm=e)Zn@x)1hwytIb+{Z&-tGU<~mEL93>cqgAK|6oB|cHL*Zsp zssSUWOQi1gmRG^|OQRaj(7)&xP)WUVuu_p_uS?Qh#N#DN6Yp-ALh@Im{_p^Y+C3 zUaX#ER&f7md8K~B%@dL<043)*k^0`r5MciK%LGkr;PEsJrmQ}IqGMF2Oa2edERSKtv z8MG!ueI7IoNho#4zw+h7g$uizLn&yVvuX!Fwshi8{#bYrNR+WqVX}AonugC80E?y7 zSdw4gTOp}!Ql%FX@-$G6c5mpFY` z{`w9J6l}lt8T}{mF?&|lym)4zEp5s1Rq@tZ8<*~=p_7Oo&(eZzrbZP=FV@Dk1k zXa17?c)uwqN;J-^Oa}IA70;qX6VtPI;WjR_I**=tIcI`TPNS`aIp61I2tjN|(X+CX z%fhR#cEc2K!i9SIMpkE2pFnZ|ZIMLmK`VDj*2cJ2<%MtGJh8Wl16_s2HpoTC_G8f^ zHt`u4swOEdgLo6+7do;^qWy3 zl6PVw&K3fO#2ozcp6{fq!9nIfeZUQgRJ*&5y}sG1?>k0SCBf^~dxD+h-Ir|7N5kCpcZu?9!`N5@VFqKQE|sfk-L-p5n>Rw$uKpY~(h-X_ z(GSNK?y~G8p67 z;`Oz{J>6Cs_q?_!6LL`svkmx`k`Qy;RQ>WetpeB+CVrsX<`T!p0o4tyfA!=X<@9U`0C@V#+ZSOCX+ zlrWQ(2P2Sw^ws*w`i$!)Xtxfo=-ok6I@av*@n$~OXUfXf zeD`V}a|iwmi3tnVAPunXrOy{1qg4Lp341$23`JS`LVtq zLphh;g;pW)ZXT}zkCZX>y_~{~afvv;>3Glz5A6@@27&P#h+zWqW|#K(!3+J%ZTYpnMShV)L3x;u&It{wU`nqg7 zk+~sWCa>al{g842WEb_|{pUB9OOE}Ksht57PP=J~@;93b&dqGqRJ@hZ6UY4?R$X}s z0dIMv&-A{7`R#$Wp*`2HL)SyonvtHH*QE_@*+uIfid?BlKMCe0AMxEvm`{RMXLkD7 zBTefGi1zkg6dH$?FAv-H`I6R`;M|2s3E|f(ZF@dYHI;nfZ7-6t&^T1I9i3G4oq*gG zuy!kLiXy_N-scsgZaZ_SYi=1W{c*&3L&-CG0^$3+wH8Uy<-z5&Z`g>XitNVn7Zbr& z2cLI8pFtQk4S z_jx2Z@w|>hI_sWnQWQPM3gJ8UM8Vr|G>|;Y$K{vrz5PvKB;!tOoys0d6LrI>M{#}EQ za9KFSIdT=%0i&-*^LOJU?tlXAB`M;S?5N; zYh(yPRA0-Ibs9KKBo*wC{B+{+DQm%?{UT^6hZioz-I|J|qfh$F332b>6}#xT!>mEr zSV9d01&LAg^~$jE@p>fX5+s?&Xhi-%pyNCZg0$^&XDn^Kwj05Trxkx~9-Dw|E|N&t z-KD@V(v+byv&@URQ*5RXK{eeH)Ng5lwNMUY%yHU9`SUMfy`Lk{%YNx|x0`n`FZ{@) zOdx)n>sB1f}Jn##gh`T9whMy#1H zEKLjt03fX%Oq!mooS{uY8>2cSmCNKRs+yx5#CqkS1&X?xt;-mr$tctkFJCCL>5lhp z{ZaP{0C>rSL2~b(R-%>oiG4Jxehgj7Oefn_H?1tToFpKtue-eY#bO9Q%4btnC5;1D~F4dP$!2+@C7Mfi|NI4%N!j5_K{qziX(vz5a>g8WES2DwzH<2y@AqsHS@ zYYN#YV#_^bLIwmNqE*GRYMA57ME^tc{a=8xd2M)S~4QYQE}wNk=4n} z?Iz#(#M!0^duQ|zGjNEAmv$lELR@Dol&0n7sz0>D`4{}wV@qNawa~1}cGH;B9z&e@)g7Ec7`NiD;~zEY5OuO& zQ4L|M+IM_~#-u1QCWTHM7LAtuV*`-M{)X8Xc-IA|OhKh60p{#yVW}6?cH~Jd6fO9U zJB{cfU3uVa7CDM2#HRQjdo#H)u7aGzI8n~HexIL&j)eoNp$@RM*nd$)=Sl-H09@Dl zl*Eh+t(mrL=1$1OP;xfXxDt~f(_JCc(BPUrPMmvh-Zz59y=T|f_PQplF41AC{kBMo z#3MW%8hM^0(>?(sj!THP9y(i?ZgbGG@A$2m)Kng>R&JbI4fq}@X&l;=$Qx~3C?yF9T?>iE1pHfq&Izov!lN@?7)TLpu%cS5Q!gi4)( zhM`oU7Gdw=uOW8mjp5n*llf9@3$(3;~~Vmbv8`3oaqg@aMpP{MelW6CiMp8$@c z=^PS=2QR06&UKZL$6(4ud=pLX4(3$>SxC%JOAtSgl-xNu6y#2Hv86S*t-R=qV|5-r zl(b?q=f3$#yfN@0DQ&{eu$@)Lq8t=Fym+-uxGJ2Fnee7$k}nr_nCS~{hu>wpmI==R znNU5&=_w_xYX0U#6_W~9xZRZj!xAEAMno%@$Yx{S;v2a8qlBGGY|atq^IHDNJFbAW z8vltqov|%l5_B!H3(C5X*Zw*KlKm#e&)gcHG5$fLe<1kp8ql530Mg#iGsDdv zhlI`l-tok}CwpuvMSbT>_ie~36F4*6ht5LJH{@|@ju*btdZU6SQW18vnRS+XNe==zUV;xKbfW#D& zQ+L7VXSkHL_m3ZxM`<^6H1mKzTy@a|j4#4w$F*M|+Wq zMr8W<$>OM7t6Q*RSxJE3x?I=abSO%--n+WkV?m9J`ssdYwbH-&AL0f`m}scUCRKms=2KsUh&Yc%`XoBeoc;hHfYt0+Pv2*SQahImi?^agJ=J# zbj!z3iZlK}QXeQWepuafmbNzDUR7n*nqU==5@noDv}_ew#w+{NVK^El@i71v8&hJe zj-nO=`!QgBixYHs|M4BMvr+lZwQFuxw1L$;rf4{t#zX9 z%%e~25-BDE5Q6XVTSzO-)h|Cv6f?5VKGok+|rZU8mL;Gp?J>{4R+$-9;7ni+!ID^9$X6eHL2e37HroN z&wkLXvk;5Xat;$!9#L&HGPh|D*a0GZac6i^Q;W`sK*>lf6}a_c_3iDS5hzI-CZb1Q z-NGTNdx1WWhQG=DamkFBvEAOdqyY#p0qx_-YJ%Y2@siYv$owP8gXQvvZu9$Bgd>RzPu(SK@UXkDTkRy9R6(owk z*XhpG=2U^R70C}!gtC2?wo1H#%2IPz6+DRLjUKkGk<671Svo+jHo zt|NY~{O`o;#_l|GU7fpUrz@b~h-Gp8S*5uea|j!p&4*AY$zM90^#wO(dpw<9tqcId zurc(d419i?%N*yR`H3ng7>O;(lLR+>m9#nMerJEel^Kf4GRO@-JR!h_%gr((a*Z{8 zRN)I3xWTWVo}gbDbs}rZ4>v~SS@m|4=>DfUAdy*VyS9-1n=glU)j*1Otmb#Q1mgmx zM%p0@@kzHSC1{S#rw1pqlt&p51U*Ywx zzanFd`wk*wbaER!9s5o@t%ruGpOZESS5Q8cb0I|2b@rnE7AbPcmdjY6LH^M0)Ik2r z@rHuCG&S>44-YSP1ZZ=!5UKRzWIbpd1}3cjTKbJ7)f6#0VefP?Tn&UmrnZ~ zh4~2#vP=20eT{Jw8w>EGpiEQ;28Wr~dT$fE>@Jm(c6e6pP?Ek8MCJirtBLUbn`GTP z23&n59}GXgCc6Io@E=#niW&C4)ko zSm9=$S#9f854D5tZCbxmd>(GrVt?9sR-T-M?$qCSZxW1|&RBKY9L<`ot|jyBHDc^! z|3O8p@(An@>$K7Y@96C@-VebKXn3V*fcY|k{1{TrR?T=& z5vBV6`oO_IhUZ>-MVY4n+qXj<~TSXBv;MZ!vBHroxFN*G_UvS7A{+ z?>uA{R&O7^QI7`Q{1x*}^VszhqK+BE^2z1<)(3uC37RV`{ZE)8usAgE@w(Eg9o)bP z)@*VWd(r$pNP8gt&y(?wp6zvZ023tWpG6|e8;?w_SG(X~Vp0IV@)!f{z_A{?j*veT zypg+bk+Lu=Jqk1w$u8DVUn4L$Cq;DCyIbpr|97Pmc`=M(=3!TG6HK>)Uzl^F+y|NJ zs^AH`Vw08EBBKJ0AcIrmxFEr%Bj6+k1pqQ4XmoK=6{<(tbx+kg22ZUjO)yY`KlKs2 zlnVCWT@LE%mdC4SvvqU_gHRtX;oIIK=IY!9)GKQpdZM*YG&7rOxi>T632}Qh6aArU zy|p|8%vNx!*qBTxF<*N7&W~u_qV#Pw)@h zK@Z{!=&=j|P;wK1@~^dp@rm5;E2L88XmK=>iGg%wgL&42>$Y1iZkWFTOHR=02r+3#G?^8IT3GU{7zfOhHA(5PZb2= zbBH1JvqgKl?><%s=o5#mJK>JYVzuY90TTcmK#Q@ZHEHLVP^dGbn3+X04}1$m|Akqw zy~a6{NRZLZl98i=)3uA{TB#uKelCD{PL`-!NBK#oos{mQ2QX185Q~9NG`h=Y1Y%Gq zLW+iHU~9xn@jMM5BfK2|sr48E#o9sr-QTctgtZ_dacP<5G1ddwKJ1-l;t8Qag?~$y zwdp6R`2nMDS4-YsbhWvD1ab zq`v5IL2wX^c=>7E;&}4Z)Hp_KFGdY4*kTxto+mRjuW()z^3^`xQvuneiDdPu^C*Dm zkDu}h9?Z3`P2{}_C5-fh8-LshfB8s31eMKnIFpbU5#zDjoO>m4jD4x{Kyrb#+yAM2 z7S_Hm4O`uDRnXxL0$U8!)lG%ND+Udr7+cvRcwZ0(L6ci5U>DQb(PK#FGDL^cvAt=& z_S~DEr6s~6^m0m$8T#;*S}TWo>;RAxuca?ktTPU)m7zy#Yf|e{!n8vt8+3muZ`;`k ziRYb2lCVq~G{TbG$?B~rBGiLskUmQ^JfnoFJsU#=TW0v+$%sST&W^rJXdRgBNzWoo z*r>5ADabSQVuF1P`RBbuY1^a4K4D{Kd*gvf?pL6LkC3S`p)=t;adlVJV)y6e|*xTwXU zsH3`>U-3!;IpT6frL-pA5n5@?q7Y;^N-R{?>3DxzrAnF>32h+#6vkoLvwU(xK;1jt zRBG~A=h500wpXGGz%3h;Ewb6CsdYZ#sdCqhWGqu(`gc0-$ED5PD&k2bO$iYzW{oW2 z7PlMD)6_)@hZFwEHyA=50^dID`7W*Di9Ey4-(YbIn#FU%kzbyfW!7v+@` z2amIaRrT{sE2`D(F8z`9nEhfIrz>D!*MzR;j8v6Y`~U^|TSphk6#glHUpg2=y+`#o z1g$TRsD`twT=jor0|4UwOY|M?3oY>ZN$h0Mo-S#m3(FLR3DpKD#%q6cXrWs^4Ud(67Hp<-ahEX? zA%jQ<8A2PIZA`I`{a!%m?u@FCiACd z@mn&4?1N0#OVhKZRO9qK9OWjky6+;`~$W**8Q_-_fjM9Lj$DWTy`gLC=@; zM-odLVlVks0RUi4%4)GBElv;`6$Dr82Wu^EPgLl76w5bb#{v+ zA`o2}rAf`sEhZX^Fpf+o`K>g$P6TAAhfT2b9oRLTsB@L-cD-_gtUJ_l#}Zw6PYJvC zCcKZb?As)pTId+u4-}|TtL7{ib?dIrIq*!o6)EPa@y6%+wML+lXGuqzd6v!cqF)^JTas%E|sSq*>i%Ou3_qc7%Hf0{Fa1$PZ2+7a_AyxaD5)BmGhuyuD z)7DnrceRArM$_x_*byI41pHCL;QQ=>fRy|vV5e# zq{q)fz;f*b6MIfNw0v+!1W_X$lKQK4*pL;rA#P;H!Gt^(ZcG`KLihy9-@BMaSG0U@ zLHftJE-``$1=n7n1Z?6$jw2_A0{~d^lLe~9#g;AcuV19}$!JN$$Q4gPysz>CC^x2^!3D zv3CaSUA6n#SZas$I~@+X3J1}-0rilcFeL8JCh1{|G_c&Y)%ZCyzz2p9^tvu?4C!D- znrM8<(*vXu|8BPo-=U_Fwrx0~WJl~*hOgNQa0OVq|G${tfcw*#Ifn@o>Zd)rwd--U zW1riP-ggQ?)OMAg$nE~RdX&ZK7I3CJ)_H2>6^ucY>8G40f+)Cf_M5tZkm~rMegx<; zq1U?3YQ8^7pQUo|E&RbvdeOC%S>EtqH&qx=W&>un3W!S-&F) zWiL2X=t`xKl1X{ndY0I-LXx|1ZAWLt#d8tz)ip=@Y)~P>yjkO^k9vl~Khe#@DYJ;4 zAa_CcmML*6RTG2LxYugub7cB-=w7(R`*&y?Xx0YXVP+v0iD6C(Ob|-}jU`OrT%%AW zMEdqP6vCaBNZD`=pgCUoQC!E-m#fgZzb>cyrN~0m(J#Ai8z^uoNx`=SDx`QlnOe{| zil%}{NqH}a-e_*m;7Cv<+@iu;j;RE;jkc4v-{%ZS+IR_%_ra`fH`o~EoG{-})!D+j zv!eHC7@N!^61H4dwE{%lm1g}j`_t%7+gz@&Y3gHAdE^tWulnQ(dLr1`*gkz0{$x-< zQI=_RRzcWHn7AuB5rs9QEI|jTn0*_qZNob94&{%5!FXG``fXwNv~SpjhGc@8;DAdc z_`f(3$(gwJ@1WU!x?p3O#8ufCu&&_Vlmi*Mj zBtOFM>K>7Kn_}wUnqQ0I(9-8gNmWZrwX0t0Y{6e#PWsFE4l)-4VV=tya|O{a+$wh{ ziUz=u4t;Z9DZ=zF$6hH6ThfX6vu|0iYBZw9IN)+7te7rZ1(;evr(WFIGHsbtVqN}M zgVvmbNoqZV1@h|8K}ZP3P=#g+U^4xBzE#h1LE9_jOxs+ImT?q*wZekKje@S;boSi0 z#BOR)*aK|bqP$epv=wR$ok%hq845gPY`(K|QLj*33&0G4BRUh@GzoD<-g|h}QNpnc0g$_-&K3P#;553!7DD0hZZRia~Zu`l0|DE>O0jD^sni66YyxX-trC9#ya}`18ll8G9>EEQE$6M&Bhu(EGdX9doCp)p z8^d^ZZBoaYJ`!Yihqp85w1k83{j>s3Zu9}drWZ+C<`txBmtDPXxM8Gjr^3_5(;voS zwQKsPQqA?$zisrbrt&VuN9&N)7T6?&oxf%yx9^{XmU4+4`IdRtSn^A`grdZ+TBG`h zKgZXs=>@7i)=joE=3Y5@EN>^${f=q1Fs%a{6o*VCsaR(|&{CJ%mMpaU!NKHSK}L&) zpTj;|mt0V22je$b{Bf{Q^$!er?@KDxNA(OQ#qi#4;&(Sor&L|EM^f=O1i$qR<_gjj zev{(_hzFD!9UO()?9wgM?z-@*Vv%ET5E2C2yS){lfW9^)oi?G|JpjmVy==z6{bdYSsG{j?MhHXHb3BnC9uwxbt z>^2J;9~bO37Wo$F1%u?4$R~pE@x^;@z}n;g@Lp$20Ej@H02o`#&lRLKi>$|A`QtQ& ze6l(E7VCfZ+!#%8YlTOrG=KiRr#MKe@TT9(}ycaPK4XG&;`BuA1Fc! zsN>`nqJEBj)pB(VLzB|C^#CwY6rw-fNZ+nM{n1yT9KO1l2t`8z0Dxk*#Uu&l#P(_L znw81_gE9j9=UcIiWj=uaY@oJW41fqmB^7I{{AebA%F&(C+S&J4CUo??3+EDpmY+rM{?dz5 zt_xs;eM%DV-}7pI8bF>h_iPh>E@kVX>DBe|pBlLzb5!(Gnld<4WhLAYDTR&X8CBf$ z^1(QPn1t!(sA{DSx!FaUSZCP{VA)-AT|I*2g+#a~1d@RqUQA8M?F-iAbnoA zOQD8?=)rv1+q}nblHyJb4{&Y;ay?gwn0ov#Y6J!9n0%!sG5(03pXj!M4U$HMtUUla zM2cJkD<}X=1M=}$X5NsZ`OM&X7Dq|B`mvgGoedR*>lKgYl{rR%gRbNXusetf25kiez;!L@72N8w80eFvj!m4tlXRAFc!XZkVpMmwd zkK)9m$aw5B$6Dp&?z;9leT_z!$~wegTXwry$xM z0=xx|^rHxBX~oVl5M*}x5pC=ToCqQf#}p@% z0z}q=98j$ui7&LrhvL6BvGkgHMi>qMKP{ApDKT znnFg%NzD}079?gKA|0hrSCmvDDJ*C~2rRLN_pkkvIkscq3h7D%-X$!Lc&-kENeN@3 zChoK!sk&>u@@qLaPY^*`>zao?kuW1^8(ao6$6f$VakCoJ-4QrZ5S%t0`Bv}%GK6>! zdp^~INDLFl0>u6NM6?ak)Ra{6(1F6KndwTrG{OKttClqaq81#Np~Z%C_T7Qlw-!jW z-wugo_c95T{Nt19*7dDYBA}Gw3%BxOf7Ks`;KQi$f#_Z-BREyMi&G(kHKr6&k6G0xa)J*rqMn~UZ^)~cEBzxaNs!Af;I#C>N4@~3>zhW{O_~#nD6n!{F_>^{& z5J<dOx6BXYFFkY4GbANoH3S ziE~-^sI@kl-+qGPp*kevotuP?3$F1UToHrmzaxkisa?Cy?eon`*^4dZU{oG5GQ=-Z zml8W#AB^%bd;uYh`1S0+P4EX@<)0QH6)*J=FHa57CVW)UL4w<84DlRF@Xwva^UA}5 zgzv#64DKMGBd}#OXZ$7L5y882321Z=U5-WO#9LD)nWzw{cu2{oGW%Dr)~I)g#8`3; zMPA+39LFF4zflV4?HfWO_)*h$*Uec>aHGaHt7pyD`)R(7`!Qaxm$DXPr%ib&7m_$+ zc^7Dek^J%r9Pr$QcP%~WS&&RP&MXrQ{?wrpA*pD!Q+EO)RbgL>M)v&GaU6&(N@!IX zG9X4iNVTz2pl7s1I!ajg>LL$sE%lI8PPL)^~-3k{-FYArtZ6m};8vr#{64F*9# zTRIt|?lmKnVdb40@5?`yg=(;FMN5T{MfMQxW1qR*PwrVVt{%Gdtdt-S&MP?=(^ zR_$uoeVwZ93(uxcT@qhvF2~Mc1Fazivarosx*>rWiZhd+QCCpxji;&u%6ytD7sh)L z0V;F#P{FjZGk+XK5#9(^m`nLN2{q=h&kT!&HtrNTueIiak*5T=^$Vj_G%jyFk+6pr zt1$3)+iJg^kskl_~*?@$F!8&RAc&2B_ur#LAU^N99HAc%80`qr~}2Jh?Xu zkH$dPxvZ#;!Tcaakf;B%lwrwFQph_}+jgy4=?IJ*i`NvV)&K z!dC;Rh!HIBC?n)*Q}KAD5f&~?e<5J*K>MZU^R8ypa+~F16O}U}ItVts4&eP1h(hr; zl55^h2I&^~&QdV);x)Q!)3=@+NHQw7;mrqihMDk|2r}N zYIRqXe^FnxzsSGvlT;y11b|FKJMEjpFb${|@D>d(;m75HnL}%p5)R-7yDza5EkVIr zEZ_lTORjTI?A?Uq0lZQ}VjGN)D7dw6wxt@cD|ce6`Yfy*n5#42A;RU%(R0(YnbX}a+>Z%`0?Q%|0$7gU%_;3gyUrt}u(a0M%_0qT)V4RWyNPSJVXQEvEd7gMVwC;K&6uJx zTsX@{IxnRW8R(I_RN+h%0g@}WD`oq-v4K8m-yrqQm@U9GU}<0<_*TZ{tM}?$z^5=G zal~(2uifj_9ZkV8^xGk*53v~01E8=#`Ic)>%`G^9X$we~gU#r#%^|eik^!V>8h#=D zex z=zGQg&2Wx{LP}RXx;?J~iRd`6;naTuW6!8juvYv5A!=R1GhLFzQtv%$Se0YEKGUwgPP22gfv;wCaAHE82-Apuxj;Y~ zAAici5I`Xn!GGNDpv9)f*s*9mRu$r|7p(P=iC8eWZhT%lzf~eJP_iuzFDQn z5az06sm-hMG zdKB-m2(5F10HdPJ@M^rDzkkU3=pY2rY9CCyXDgv#d=k*FQnh$SeEWo*3b{=h-+hkD zt+6CS!X+Hk(o@rey?+Av-SJ|N)q~V?<=O{#35vRzNwNqP-zq&o!vsB^t{b0hn*EDr z_%>3UggjphT%{51fb2v@rYX;vl8UOB(nfLSDkwJ_o7q)!^A7sbS|3r(4-@Cj@Gvd) z320R}GP^}f73l{TMzfk1TB_CGDhSR0DkJO7+ zy?SdJjo?ZarF>5$M4Qu1DT0{7w1aCUVZG`9`VlKAWGQ2hARogZFmr5@Z4cA9MhkXOXIzn?#?CCLI3nHiP@X1|nPTsfAAH(eHQYvP z=|~AzKu;=l#}rn}RNAbn~6Iz(ULuTH)7 zSQ_}Aw>o5Z15G|;r>Kfs#)!1t@s%2Yrl<&noygJf|tn=+Uq zWf`uoHr$h%)?!yh3-1Yjn0BcW_SGBOIkl#An9gB@1wMPCQH3UxNaY-gDdHEgr2*@l z|3hrq_|r<8XS+4sDqGy!N%l>SLq@=#Y{EX%e=xJp0&|U4wZRb2za~aihZ}S{qKl(E zgmpNVvN+dgWJtiEi;bR441L%_9tG^Kwh*f8LP0H9|HtN+%o?fNruD|gk8>8WvKzmv zY`3VQB*5fau8Wzn=(vimB;(KdwKmjGtG{gPp1nnxnc^o2HR~Rhqn^UdMr03`{@bL} z*9H=-gg$XkN~yzQi#UWdS^f`}tX`x36%i%ar+v!%Oy3R%LZs!Eei%DCPB08~8oKt1 z^E@||v32)Of)hLAzIqf~CkP-5>-6J%i>3MOYoD~1cuh_eQY-1W>E+fLmn3Pv!CEY5 zIKrn+U?}`>L8S@>R|`sc5ACDz4J5>vQg-HdJ)!P^4=D4ukA*y{MnFIhswvSQ3!@S9K<6K6l5CDe!Oq892m6vjV{Um+Y z3ms3amv9%yN3w>e!~rv#L2GVCReqXEAPSDV{J5F>G4sv(d&bPlx-To#k!u3?mmJ?R zgN7e-|GCsxNjplvZiBu=OkO<-yXwGfY>O=0fu5j=tVsYmViRUiceS-<&p%pDEZty@ z%B2PVrrVu&Vw-*k#CV)92cE8KnAEbDw=p)d)N-iqygWE+6~@(axcK>`>;;R2_a>~X!78eoDLZN06eQ484DS% z7fv#+NLg|+At8qb`oAKN7Oz6y=1v!zUItKSYRI8Xc|fdhDR^S-7_8bQg1lmytzOEk zOc3o^MX}lUOI}^?Bx*NtwXMtC+aOA2zjT5{6xI$n8*aiVIlK1OUZ{j^KvmOzC5^y2Maon24TM zU$!uop3^`iK~VOv2dHC%nfw#|K3h+|uGB4baa}Dhk`bDyS=j>a(id<&ZSm5+%M0_V zUYo>jEfBRZb0CA>lt?fI!i~n2*&f5nsTP4P5lC8vx>k%|ZIXYN?Owe|{)(fy3Y8>A z$O;Ldg`tCa&4P_q7$Lya5}ZW0+s}H=eV-2?sE339cL@Ydkw(1sYc`w&taJYlyUYqG z@*Q!4g~PX56@jrpg%RyJ#5lMrCQbNRBxKNRvI9O4>B?KmvtS-BwS$mbkfzd}eWJO8 zc%?3n61_p-CnhA7Cj|KZ{y`_(ip9t0&77^%~P23C+=Jw zlmP$~7^^+_uk%UMkAc=N#38Ts01zmVRXy>106FRuNuXV-EcqhemuFvy6;FH{oKefGja_^yCZNPJMOId$b-@^7B4@d>r z3O(MUb@23|D3)!uRjA>>{}cghX>whVFsURsm=w&*?0a%?KraHbs`LdqRhSz#u669!S_tY0xJ>`|Mp$XWE&qHTi z7yzIXBciSMIT3)gfh2e$<^O+E+<#;||35v&p7Z~zsG5Oa2#2>Onv-OsR)W&aM{Owd z819_2AOZxO260#3yiyGGAuEMJ7-EsHr-G%tkgZY$Hk)D{8`L`W+JC|&9d+6+1X>0$ zsHxFVY_1xuoR{ITT4v_v6@kHTLWrwl4ghnYlaIQE|D+U15@B>>^P0Q865dYuy4}^y zcPzxyLla9o6{oaFPCGTE{Z<@Z=KhQFu=!%F#Hf>ZH<^U-4a?eASpBK24%Ep}oS(t# zIw&_{X~a&uG^C9gsW^ccy9*M3ut*M%9wNX15bT%|ha#eK36E`G#~q)Yau!*{CxCMxD>8XndTnqSu&+vq)3c`*vLWf%%YEyPHPpw-|2(E%Bq< zZUf1%Un&6F0SaBD6zho|KLu>n#cjk-ki@~-`yHuLc`QPpH3%h*5hJwCaDYGo2U$->ehkT`Uh_-=JBXTDP?JAMG zMbUfV%X9#25`r1pyeKOdn?zEy3z}>vEZPn~mp-8b3Tdbj1wu%GBv}WZ+PaCpE$@w9yNJf@FB{C$m`=`bcNwm1=O zAX8xnKQEWx3+`?|nvSMnLQDq(s>Cf7SpK_8D^t}#T#VSYfJGB=9wh_rMfMxAy37W4 zCps7dpkW6_7S+!pIf(mbxWgB+28rKj{@bU!VJTQBnZc2yu5=&`zGP+~ohJN_*G|Ul ztk=F^v0aLn@-a0@ps1%pH7I8m(iT;ThmdcB@ExxI9Rlkp%UHZ=1Ru>fPf5gkZMCe; z1&9ff#IH>o<{rn!>UfNdarUNA)pV@UxDX&cP6Zqa-E~eP%5jeBUw= zCCYDaqOVR?{8`ivS7+XP@~ZAu3H+x``l&Q6PJb}d^!As}U|M++H2{D9_q(n%SxmGe z9nHalro0o5wIFCz^hm6xIaiT&&M15|-#9c}Rq+7sD3fjs_7G znF*{gt9jo!Cv&^x_j>GI7`OeB)7F+8UdtK+%pYi^-F>MqMkLcYrKco#r=Et>S6%ns zQmmK}8@COtvX=z8mB+tk+f>{6Z;`l)Xat~&Q2=}-k09+#m)%q^*q}?Xs(6&LLmg&t zE-N;n{${U3Tac2Yk4-z^BGtm5d?T#qSKz4RHJYbbyg`?ticSo}jMZT^r|0{Kl*b)e z8S9xy84Q8#31lUM+N*gAR7;Oe`(WNiva9ij_FP2;7Jz~%Sr7~ARZe4h#c1}quDbQI zp=H{;f#OTjS@-)RpFHk_txks0N1`{O~nnLD~WiuN7j%uAKe#u{3TFc zmrj^HpZU12nyfdCRg__W7gBv^gCpU zGB7HXH?3@lgPdq^}& zLqcA^e)R-oLodvg4q9@HjZr)JUp?0T8JP9I! zdY}i$Mog4T`Dp`@XTQMU$wXFqh9KTomhb*A2Y|E=l!h`RaX2tmJr3}7=r#CC0VX&e z3LRIjGK(X1SaFo;ovixfo5-Z`*fZ29YMOG%9>i(Mfg&xuTm#Osv9H+*EYX?^S+ zcxqcb4sDIM9-+@Mzcb+SYv@x61hiU&O`aJryQ7|XawM8Aq1i62H4xR%tAUpM@8?h~ zVPbUv{Bk4^(!!kwVR=*0M55;P6i%>I>!V;2)&WYvfFu-kX|Fs;Q4!ZN>BIrn-D0$^ zkqajG7EeG>s}S1WJX1k8Q^T^HC728p5}4Ds2$w9P<%UFo6?x^-5*HPs~qPXK)$X05s29iSHf^ycMJit$qEiaepLYoz`D=>Q~^^M?}Fg0-2TyYi+YK}nrZ^dpdo+P zy^MUqivOG`{(AXQMDM8OKQW9O9wTQCx6BJ7y6}0eAnwMIQorU|WTG|0$BIl!S?SHNh0Qaum?6i)o?n zpK&3Q$?fwEBAYQ;1mH5vUOoCk_RQ?#5UG?Mh`gufYamhmZQDw!CiLis_a%>zg+OM1 zxj{=6teco>-EFnsNYLS|7;brCawXCDHxQL;vW!wxlWxL#xgG&ye+~Ej9Sr))8>cL2 zIWewD;DcTTf9pr^tu>LCMIETBe6>QP<5WTg>c zEVELVEUCqp<>ra`#KNoXqaUwBa7A)q7Bg!QDLB9uiKP@$TDSl0$!RF1sP+I}3D0l?vR`sCM3V$bHyWaY;_{Vq`Ag7am8IP8OX zfj7xRE8L|9K9;cMTGynh5-2^YF$}{3T>F^-x*s{ekFhT&t^zb$cQbZ4Q@$)`bG`u_FN|Fvl=K4S5{l!B0V89?INBoM4hXu@1RcoTYQ7E?t zCIlTeb9EXAIjn@KcV|h+l}bNnL3VB)ezpN3`uBm?RzQW=i|lILN<4<2=MQoV0s0RK z)g>BuYGKeIb^n@|TBU%GPrj4adSW(WKqThAm+00ce9r??ri(UHvCE4&AI$G^K~Cvq z4$kk{DAvu?NuYx=-|~NmNSWbR-%-?Pg*(%yH@^#h8Cn7lfOSFt0a`YWhfay6DCc(s z3162^ZT({9T)PBts>LJ_sdcG^t_HZ}2~XuVlg4DtogB-$6O!n=PIayA&RPYI7*sn= z8W2@7SDG#tEnwG%1YH{Ri>;5`t-t*@Si;}WwdoJU`;Q1D&@ooRvfx3 zGo%#1D-YC%vHi2H*WW+WJODWyC)u~}jbiHSrt;ZHTW>z4hox4z^!P-$!6C9!ueZdb zAKJ17DK)pphA$a94XfqO#QBO*o<9AIKzdKxzOGs5f5QG^#km_yIu29QHo-!6HR}n#$91D5wm;ry> z#T+etx#C3cRkVBbj&;7@x$SRgnfMmWB>5ny^2-vZX;^34kfsFiV^^&hDx9c(mYOCx z!KEggX8WlGdN#^cGnj0z-m2*~@SsS!>o+u-0_i_Voj220V-?g|aMd7I6j=-`->uN0 z_*X6XfXw8 z56t@7)_KNsG5Ww2TQzgE?!G(;y70Zlc=<97lAeY&WJQqIi2VDJ&CYwbznqv!=BAAC zVqlp8Ju3?lOAt`f8zs@XKE=9@cP5fV?vg-p=#L1#;OGGLKurks{i}@Uy5SPoQ_dv7 zmKc>rLu_)ycb&EfHVVANbJRVswO>|LC88b$o&lo~0}OyJPicgJKaVo^KKK`Z-z@TO zvXhY(Vpxsl-4tb2dV{aV;|>8sKTa6SNK9Ty{#Q>iZ0R>_L-O%^JIc2qRFcB&y)cg{ z%sDTRiE41gVv8M)>-hEQ5W>{lt5qW0l!|q6gK>D+GU^y;E6zm|(UK7{bzSKU>XIUA zg)&o09fO)|2v6DOB3@L|q2mdM;N!rVuc)sFtPA@O8FhMR^XoXGACBMSoEBNp2!+s_ zoNF=9ZouPBdQ=)SCWQz-7c=(F65`At4MY)Hl*y}^d*My1)T46#+})y$J;;^h6_gbs zgFn2zc>F>Kbl+|}PE5qIfW@Bjrbyk>bUgt0{Yh7D4fy454*=U!mSj+*?j{0&!`R?Y zw-R7lgKJOw!D-Qy{sR_du8fzfZO`z(V>ramm#s0je}^_3zO86lSn_>eW;cV4{Lcme z;Hc@`uvJi0ro>k~D>(G#2apm$OR#?Sd@V{)_`EBqg2mAJ=+Jxp|JZx0;5e6^U3ffZ zW{jCRW{Mp%Gsbqz%rP@FGqW8tGc#k%%*@OX`}f-QeWz-#wa(SQch$LAQ*))B>Gv&3 zk6Kcz)f)aAx@Q2uH{h*0bRCMQS2krI z?u|zbaGD^kvEBvO;NtZ+0xPryM*foETe-N5;t4{2Q9M*fBQRsJ7kSE$mpg?Iw0W?dqDmY`u;{SC(ckLV$^7gB+jlfRAEc8N`Pwr#; z6JS;IO`^l=74cSN<@0-dVi@bTh+hd*`K5wAg(l+eMIhP-uRwpgZ)*g{C}V~KDy0%v z>iQ;lI3dcV9m~L$7z=49Ix=Iut~XDa4KOjgpsl*#zcZbz^N?7*@13;zLAG*2<*OD| z?#;o$`B^^Hxh7v5+j?-gXM4R?&KKZ;2)1TlJB(W+2@DxEI*FU@e8!XMFN9+Q9Hr-C{DWznF@4PPpFr>(A8uGCeC7$r-M< z;w#0ZXvG3&7i-a;;_F3ZFE6J~)~}o)iz<*K1hO@W`xT{4_re2cWnl|=4q4P!yp?g~ z(=!Zrkp~hQ4UP_>?PtENgn)VA@Yl$teh^{tKG&dk03Yc#15TL+U z_a)4JLc^-1N0>|gY;B7CGi}5bZsa?o-w_TZ5lrMO5#}GePd3@BVsvBP_;R! zO7a_ewTvQgVoGnKy;O3-2g3X;^3uW7CI9IQF|Lg4Wr z!p11d&Y73vHs4X%zUxyupczUhtoCVBa6XNEN?0Ol(j+@tX8fX9D$iE40fjc+J#G8$ z&|1uMW3c|bg|Jr|%fB5}xj(J6KG|04U|~LyNINM;PybD7Z`xToR0bFc<3i50DE=AP z^1>9BN0mdCoLFbvZY?X&YZLt~5I6_TW1bU?Uq|^-VFIc^^k(*#L>{+qqjdhxS*n7I z{!tB!3MC}}@$(gweHiL5{ZgkQ!7tiBBhRON#t)I`bkWb!E7QItu;$}a>eDw5M5}d+ z*q)76M_$s41A|iNLN{Jw{JHqArR=A^^w7&JK(Cx|Z}-t2MTiQJ)Sr2U77TASmn4V! zx^jF?$&^fQMSF*IwxK;>m&o$+Oz2=aJE1w6Eyzfax2B^tI-=aC`0R5$ji^k+fRN=l zoY0PB=&>EGkj1Wld=ag->24{r3Yj9LkqY|M5FbqB<*|Gj%B)B{Uu0#|fb{;;Y6|IQ zq`wog=w`ZzVrknCj4AhKHotx>&)mZ*ALL}0SC(%|XW!VLv~+0n)Im8R=Y86~NNscE z=bLo0a2wB3OoAsa!xp|H@G&S^X+0h^!nNb^+m~qy7r#abn2OE){xv_x{febi=W9IV zG_}A$+VCxuNZM~uEZB+$0`(9-bzI}R%}dzkf54HanFo=Ywd_Y{$x=Njs8r^bzo|Dn z>t3-Ox?tGOs$g%A=D_hVi#42!U2X?|TI=Gtw@Fg47QD}oiNJk&-CXN`nPVlF3h9$fA>vkR3+?ToI6Qfz81Db3ZItth( zaA&_bq~)fxKSgsHIvmKvv-1A>3QgTi!)>^W!;dS}wQ#A4mFeZGSVwM!A%f5?7}9a7 zi+SCZ0OFFqmNg-f74uz7Cvt^wIDdkhG&{L?1LNn^@9{^WAL{iD6RX}W{^nK4aNfD} zzZCJxNVmQMpdsOC3)9S<%0}Y$wq3LuF*7BrkjPfURm4#v&>dQL?0*(CI`U&0=Ogv4 znWggk18!&IH7lpZe{FrQt+S}p;Mn+#h)6^%kl+g?3Bz!wt{;XMWFknSAvF5CO&+g8 z4NR;ca3Iep-5&d!wopHKsxJTB`dd*-RL_Gcyis-}?(B&HU8#gQcpJ2V-{r9{&;uQR zhstdDW(i$X>?8wgm@ld;jGiWsip@a}Q(N>0!$7W4XnMvIER#-G)Va?$oCjbYGA020 zW=5|btV2dC+}7P(178LHhu1XT$HM%VRRSXKql72I-uC0@AEddSYF?faG8Kd|Ti}i+ zJlj1H4q*xa_W)}O#;3(CFzW6LzrPLNA{^)T^1b8s)pdVveQtt`>m+7wHk&9?{bT|h znn}~>I2~vDy{WXe{N1aVu|hdzI{O->`<($phUsPNg*4P}*;+nw=YW)eYb<3GoBK6J zy)uFwzdR&qT56zNMuD&327w8md>b`j=!=Py>zNddGCfZIeK9mN@!ii#Bp4{+(F+!@ zDX!07ICzF?hfT>1l_OSL_5AGuaGll7(k%?K1M;|VfOvZMs>Fzqx1e9X|9TkdWPDUP zI8CS#^F~14OBA@5SO6hrDXnqKc&Gb~Pt$zi$4%CreK%AQw}zu{7=!*&c0{(nft10? zucqJ&hv@HERz#c)l3Y=ZgAxIBim=fJC0@{gF8_4&k@u3ALpP>Z&w=?Nw?;n3==^a; z=oa$BZ3Z{j%P%@#nA8MHN7xEt>ygV|M41^KNR5lY~| z=!i_jl_sC6LJ78B4>n}nYndgxNR7U7N4R!m_W0f=T~Sba0}-8DJ0kiLf$rr~YGS5+5rpxg@jm7@KGQhGyYy zwB*Zv!ExujG#dB*R1Ax`tW{_~hBgsbVP0lrp=W}n`2YM8;w9*=i=|A6c_L0FDR`;BWPXk7u^h{vq{wc=Z$B^(Nh zWTHb=3dFYp=NAnlMlM!Z#;^b~%vR1NS3-*QbMl3CpUwfHwKdRIJTl=m$j- zFu2|+(r=w6)63|jGk0i5u^4i?0T(Oo)!XS5rJPi-wkYWpaRPP0zd<*X5#~ZhJ&Mq; z<~2C>V+EUL7{xgf9Z?brRVbDy} zYq)R%!uKGp2eM)z-CIPI@A6S@YQae6=#z=HmY0m;ljQ0!xQryU!*cQ?ZV8wL_#*0x z-hyywbk4Mjw3^-(kJ*C1d~i*|yv%UqPZei<+gZrSmZ;hVD!4OZmwaQb8}M4%9C$z7 zIJum+`A-GYXylDCSjmq|e~Y#@)e%6Vg<%v$*=3NPecMgl2FDQT9T|9|3s&wyl&R<|?8uFzyS}0Yzgmc=7(}Muc3R*pg}6YFlc_X$!)yLP zM4N<;)oB?{)tAI?Fe1SugSDyt$=yomGoGo#0kx-{e&PD>bliN2!ylfD*XAd;3irLF z4^1Kq8$01!;+t+ChQ5y3>-`!g(No^ZqzxuOrvU|KrleOwV}~nH4l& zH(!O2&wKZ7l)VcbWPl#F0wbE$3i1?yMT5VsEgs^V%&IMr<+|HnnbV_53CiWrVy_IF ziQ4VJJ6j8=;Qyp*SaN*enJIp7wtTQ)*{R~ga+B8JwN5P%yf>->Pe`3<%8f$yap_rlW4SMBglxL zn#6LIbjrWgauBJ^-q~s=H2J_zuVV3B-phhOb2H`X(@nB|?B&0Iw&ZC(;&4cELmaMq z`7lkNb5#Rjf)TTVVR^ zmEoBWAN7IUSAq&BLHmG=6`lRgLmu7aZH%L|Ck}v&e!sRODv|-|G+;eLSDAo5*dCe- zS{&V1PPd<%?ezj(I&S6ABhHDYf64~|rR%^V1F;VBeky9R3F7o3y9>nG^LJ3HWp&2u z%-ijjRmr@qZLiNOM~vUyaV1`jIxl?%s^P74|GJ? zs=BdHG!|uGXSVx^Jenj@!yGYIfpr&APYbgcJQx5DGDXAY_D1{SVP3F%%J7hk73N(a zm(r5ghlUeFT9p;;0XWmVt}6kArVtZI%nnlUPI|K9yVABwQ}uIm&CB>oT-sJ3kOSRvr%@yL zF!hNrUZIm0EFURwJBxVy7E)}WMutLx3biVu&Fdq-G?~yoe+{6dSFICU>VR zTegfQyz>|Xb>d7&_eb#V(Xi7%;?Ixu^IO@nN`iewW?P_qpMMJ~srGOu;D%a=Y+m8G zl=s_GPcj@%nxL?IY%)3tcm2Wrwt!{TL59k=HNQ15-M)td%UHC zT!Ehonq78|SysZiQAQw@`M-3IalFYHEZMKs0^GRC zjcJiF?OEBO&LG>k*KqHAAY6A_1mA|r&d^i<7CDj;co|Mk0suCJZt8CAUcS6k%hepR z?Fu4XeU=YjDPD~`LwoVAbGIz5Zhsk{M*pGZSW}90VF``~Yk{wdH&cslF6SBA=hmH% zZ!v=%UT*5HFY6Kj=&WsDmnZ!N7o;K&{l|nZ7sUH>&f9Q5j6HPvKPCa4;Dn~nwQZyl z>qM1Gbprqdyk5s@-#uZ*3g2%n=7#t6BX9WYX31s&fJoO#^sc`_*Pj>nzr-K^r5`{Y z{ekL8miIrx8s%^?b4Gs(emth`Y+zTF!`iPff|7GhS1H7n@F%MYoA^!LaGl1HQ{8f%H@>-z`NhFgOI{R^u08TnlI78R!p&c)OlV zDlAoX+v*vu8F&YhwWUh=yMZhn1T$>l(7vsxbyM;dqmDWsYv?=(DdlhFJZuG6l;s6F z(FgXZkF#CrU>+Gp;=c$J=ez;#*FL-XgL{*9O-6$sY#l=Wa3a9XRbt@$<*5=NRw*9lHn$SD@)Lo`=+ERN{{ZK5Tx``Ciu<76#33p zrq;<%q4`(YIcgT^OGs7&-W$x@{9TCQbjgrP^pGPIvRS(_ z<-)R~{~?GO&WO!-R_DI4Mk>g7qc4?Q9)su!W-4jS6B}#d<`IkY4Wokg`rV+W@wt7Z zwXiMo-ni`hv`{ACs&Hr%pkSAl4)(CK`_h<)A~G(WxZ%F7UY=2DOLz!ypC8gNYpx=U zdOu!q6Jr_%{h>us<6%e48_=L2$F-%8btW&o8@M1esyRsn+1bn_#;047f{ z^?+AR``Vihtk$pS(5W;({E4P0T1PfAGt$!mnP?^&)!O=$50d~)BibzaQ-%u^j6j5nut87z*MiM!b&FNOoheFwi~Yk$ z@8%V$DKTKswK*pG+|TK2~PS_PZ8kJ}Bh9S^%{>F;an6s=7Rn z(mo@60tj30H)NbQJAO8O?zBCQ0#TxE@n8jaVxV0t0jx^rP*O~}J*|3DR_l~=<{C^^ zn$33P7)CUiQ%YB9*8&m%bLRppe@Wy1EtZ~KbZf`>vqu+4LDQ+?M(vChWAZ%(-uZ(m7 za=C>I2wLA@qj;AVRM<#p(Faxlocf-yv17$jPjnj$`A}PLS8c^2vB6TSzfj18E?Ag= zt@7xot}WiqxlySQ=Q?U(mbJYp;AmMS;}XX0h9m?6Da0SKBB5AfWK1qS=ZX}iBQwuM zLj}Y%(3S}qm=C|9hE;>t;M_g4nbNz?~T-Xdf3PVHML z@z9#d%o2U%--+-X&YO0<$is`TJHIX~&IO_8FDb$=k`9<(2J+Q_uAxKcE^^K|~; zNy&)0L&fFYih3$6LysYiWM14Ue=wYEpbMt`@<(UnzkHGJKZ=6mewT-blM)Jvk+Rj| zS}li-!j2OI0QIeYAvG)5h}Abj2l$ zAo$|_W17`Jn$=IljxukjqSe>MFWtDxBKbjvrldyJ07+Fsw^Ec+{}P4|mZ8bBZTsnhRS95z4 zazTa5KXvuX($L^Fd}c4%8X2pkMENfUk?dxiW@vq_d9v~PCc_mAktNzEsOUN;bj-!`uir5<%XTV8jEZ{HgtrABQ}~jH5ZWzA+XKUn_AvyQb49=U zP7X2nN{ZLmw;w7&4pR!HXQ@TN%m&)X{v$>IJb@w&&pW9K?uj#uU&suQxxF3Bt9`T3 zbbQ|}TGws~)sTQ7;<*hLLH|7quH=>bdx=oAP3j!UsQ2jqY`FW;x zJ|KF#VWEZibsgY;Q(U?@ptgd&QCQPry3Kf`2|qr%9r8tFBLe7=sZ+}gp>CJ5Gb@Av z*k3md&zbq3`*ubepeK`URBiQrEBa@xiEgRre=dRtU=8`sbJf{?E^qRe7|nfpR$$rk zdceIZ*8fN&0h&RIvi{u>1$^4;PNvSTkGyrPp`-E>`PG)@f;St6-Qi&k0LbL-7orK0 zQKsV(%ObYF@KqK`Cf~yp-1jcsxc@P}~AZXxPMsjLf zamY??F4KN1O*Q94?+&B{PJufEmwn&PVj?N}8ra*H6rf3n9Xz}sTIlK60&T)G>TQa! z%Z1h8pY42Wvyx`v$UO2P=|cKjHll{Qx$3Gv@=*X4Al+R_jU=lhmE1m zjm=+OEv`-E5;9~b(1#pDBohx$wu_858ARy32lI1AAjXz@I|13FMi6>v;zeYlAV5ES zKXx@xACCdc{JDOO)XIPvno|0)XRzPjcR$O1{@R=iC6=Ab#$SuegC(w%(9gbnIYYlQ zH`&Ge0l@n50e=*tkTH2{Cv+DVs@(o!5D-?^f>LslYdpgOjo%^6vhIUP88>s2u&*0g z&<$|bcihIsR1>EziP}e^(krKPAN_H^#b701pw;h|0&x2VO+;jSI@;ELFsWBcF+*5} zg)U#(+~j|AziMUzC4GqmUco?|aga&X*5(sp^4l*#aX@22XhsRfc8Y4QI_3-IypVRu zd*FhO_mw4%tSDjdyKV}F1m#2vZPXy)m7C=fWydPJy2N4Y0?aXxXu+67*N#bSad{)y zKgzSz1V%iePkGxFtSg;R`4Z6EwfB?KT%l`a2glvBlTP0SD1} zjr-nWGj!p1#3i^iXl?Er1s=;&uXobfgv!ETlzpq~NN23|;vYF;#1nU2VJ_S*cn&1i z#)Wgd`z_aOOpY*zYb!WCcs{F~r=O1nF(=Z=P9&y~YD6Of{x{|-FhxS3;V z0Ff?!wyrgN<>kM${wV09c|RV4_q^BpaS;G?|8Jnc2+4#Me=_+uBY(gKUhZQ6v;|K4 z{dtOF_7#BsantzwF81fbk3IgG9@Ibn1p*ELA%L`4cpqgI5kP2W^$G=mECB@w=2~7H z5EuXeY>jr*ys9Ur_o09{Rls)7Dnvv2k6**8ox>Ll0FaMHfqzBk|1%WeefEbMdz8xr zGsZ*sJ{{v%W0hG>w&TmAbstDCXOz(`Q73ykXaCZWqI2uQMjAI$5(*&V;~Zxv#p{@Cd4` z!Fb+=8hsa9Riu2*X6#Zhq}p>90hAj!t!0?W^j5F_aN<~ZWu-CZbMkY&>771$dCbZY zsVT~&iy#V7u&>VA9K*TFPnuTI+>*1eFAfb>xogEZ17P&~cB!Iu>}`E&*PqZjo7i$- zteVoLDkcWab=LG?V;_DjfMo~BUAP55xhMt>{h>NVmv$nQ%X+C`0-Jt-$XE> zHJb15Zx`kw*wBUF_f{~_@wZ#Zgt72B*=~jWZ3Iy= zi+v$eDwKtw`N6DHxZg3DTw1q$YD!XdG%VqP5Y2%Wwe3?Ap1&Z8%#YliRAw?A!S(cW z_6P=HzrbouOPU7+R*|ZVB4jfZy+LVQ(;f*I;k@5LZ`suNWH?FAltV5^MGy-#lN*;* zX}WgUYkR4^;o5b<9ik6II-XKx`q$ss6g^2!U-$)Ynd_n=`3rGEkD|e{d8_F_0eWGY zLaM%|oz~k6Oj`bc#m6LN5Hd69Ql!Q>J)W&~+vz@7HWxatOIlU&G&8mv?6$ztI^n5+ zDD%6_3F*TJ_*0Mw5O^5$X0~Ee-wRX`dVxDU!M3uar)rp2osZ7UfhT@8u-Z-fj^_G% zax2P1kGW|>$mpd|byBgGq>!pXz62Ffg1gcHZd{J07yW{S#;=NlBSSFBe>qOxkQGUo zW%-Hv8zYZ+y!(eqF$5_p{J)tLxJQg_!atx2dQ9+ ztgleZ`~QM$vyTD1Nxk>?`H-)Ln}8yagZFpBVGj-fKqM3C!(9Cf{J>=7sK)mnN(hh6 z+h*@*0MHu1S0+i5V_Y2ofX&5~LR|6Yz2ee?1Ehg71-h^?FF~jmu>@li0Mz=wJoWz# zlLChKkwJ+wtoIjTj0-1a#fP>Yxc7zy_l8(Z5sMFy-{7$dlU~>SDe%*%o|ALYDS4W4 z!@mITHg>WnZ@_~yRov5ZNjD$n3R~(s8?~6bWeuf6;T`VO_B@8s7n7vIpBs`W^^lQ^ z=%~ZzGCnzR=OCs&tgbQhNHe-bOE!wMqVGt3`F*ui(M`0vSv7UN7gvcW#?DOFE626C zuzB>(a8X+IP&QA~;R=<_H|&Rn8V2W-q<%GJ?COg{dQ|?Y4~KUyXX>urV0on=CcsSm zX9YT}8#w)Ty2C8_z%Vi%80YDuTscJ0-bz}=r0Xzeq> zlVyKg#%6xw$}m7|P5wO@n|aLqmaCv4y$_-Ks&(*^PDi7|9AL1LYW@90^2*cJ(7sL( z=@kF~s1j`-|0(DPR=6O}6fLlcq?r9n)qqoh3v%=l4P=bxU{+Azj|I`A|cgVG?=0R8@5W(`CkdcM>_HkJ6}b03B5EWkx6dM~Px{FeXmmqIn znenQxGLcvee(+(>WV3x`?tay4-uJ#X?B2SP-oJuV)smtr#{JBG$=8p)sL$BGM#!B} zbue{#_zN}XMgVJ`5AG$;t}zn{^;~Q7%s={w2mlVqCisw+EJ*R^KbaqY8fzx%8tea+ z1jUG{U%P72@20?Xw6xRfWUh7W|1d&ocV!Tb+C{&PJ2yP4QO;+p^7$NeAV{FS-?W#l+~Jn;iL?EfI= zudM%1QvSQhary^2e-+k$895(Y(;x&ydw=QhP+v;sD$r2N_OzwSo6NCp^Cryd{{^sN z{5})Mx+F{&Z9n_D1K@moZwdzhmEmn~*d3WVdpPxpa5!jdZqQY+tjspzE6S=1PEU<} z;2;(*=S^;F#}iH$XNVbO-Z;8VkfY%Yam?9#0pE3_wRlqpEf}0~m9lV!+~!T=bogoE z|6`EoISq?}hw@+z{5N^X6h5Z`Zu_nI&Py3M@3avC!h&7Lnkb)eAO^_NZ|Xg-iF=ku zjFh!TiOn}9ueJes5-018WwGWp>G?N#)sH))({NK>Vy+`#NMyTa1J+pAGKL+0b-~QI zbP|~C8Dn&P5`*Bw`SHIV=)ZfJ4*JH^>A*HTcKK z{#Eq3K{U7 z=s^8XI|YBr{xiNIdql89da14FhuFI)>Lcdw&4Kl71m!r`4pt&+MDYDRia-*;{*mZk zW%XZ1k@G)1|2v-lmG+;b{IPtAft1Mp`||bgp!nR_Db2{CFIxmSCP#qd?E3f7{}Ya3 z$p0h0zY6BR44MCtpW;9A`z!1JBb5KL_xKNF{wk&aGGv?q0Mf5yO@u)0w@q1maQZ`4 zOa_OfrIC~e9{ea~kJdU5+3+aOQj4wi8PZf_8yd4!ib{Ydfw#rjV*e4gUB`m=J7zIj zT^9b}9n#73(S2;4syzY4mVZT`r8&;;>A!(f3`)hMx3Dtzah!rVQ_D# zrCcG(N@7$6a|j@@WVfiK+#-*pbz>@PPWq~5v zG!u9c&Zc|dl%dL$_#8+J+8S5M;lzi45ho$d;5dERp1X9?ZAD@KGkr^@uwDzG7>&6Y zYqM&iFWrQ_l??lZyf7i2w^Q9yN0kmnxAVN)X1JMapU#7;WZusb44EWWG$Av(ghzZp zF_{1Qtwe+AleIE-2j0)rEY~p-CPsck+MzZwWdv0O**e6~^G!+}d>+ITS>|c*fw4Nv z@@qnLYCvx#R2NUbzoG3#m5!OE2zC;0$J3%fn(lNo+8j_dqRVIwv}+X7<(NA9hf<}^Zc#KMn6Mb~m&RNkUXu7617h91 zm?CpUj_rN4J&bInPIt4())@z!Ou6ddl146C=Eq{zf^hz9#}rFnPAGM5Tk=>vo9!R!$fJAfhuwVLDsAgx zyFz0{^Q<;4%s~pBVV{`r6#U)rLfNQE@&P!gG{1|aGdj3^KF>T4$ILGqfYDrdbZRL@3 z#<%5W7dx!wHE+^>Px%DHILn%HPMV&1!9L^vaNELd-MWp38SND`y#zdu!25U+_rb-joZsPycx=GY+Vmw??-rWyd%>lg}2VU5Rv zyGlf^OZl%RL9-N$7J7k}xdJ~Jo9Yn71 zE9y*?p1)baQdh_ezN^@p58+VDvxyg2cxAf63!ChBNwUfHA@dwJip`6NAKg%duHd)K z!W&SC#rdfo23VW*M+U6w`E5lpW%lG=yo)|V$W8|m%_<|{{Q7P~ZC#oEo+t)XEaKQ# zPTjnSwyHK4K6yq@`0OEaBLQ+Di>CGdtNUU(62I@8z%BMtAY?+Ljq~bBykr)GcbP;# z59qgrt#un^q5NTgbJzRH+GN!Q(WRRR1wzCeJ0d$c(Rp9ZDd}*lbJswBNL#)UXlgkR z{U7@w*M2LTQiYODElIM3)q~TXE|D{@1!&W$&g);HWiJ#p`FV1a0~U^@kLm@mbr=`j zm$iv-pG>8DVdD@+kvi3)RlICw6)6fQI9DgO7QZRSpDvIhCuMkGog)R*4t3N1#;LvP zU=zqgync0VHaO4z9{tNO+g?HOS*{nv{JqZU_?P$Nv6M|NBjMf!=Q$pg1807|to6Kg zesKX_Au7y7EXX-RDn!G%O!A4b_s;9L)*)!rfNLF8J{JJbtfynQm`_J=er49cDNZc< zP4Lvq;$V5!MmTD7fLKJ7%LLT^r-$aCt5(c;1|z{^DdH!VFoXnD;Y!#+5&_Ka%CN(3uuj`g0^1lD9CUfMJF7P$(0(u$^BeDe+!TKh6)ldxtX6K zfQRsBUS%BN#UPR61P7QH_m!EIE0pmg3$n~1Eiy$&jS5eurm4$)lXPDXobStnt+DYD zSGzCt$0GrvF8W2xb#pKYP8pQ(v#A{174X#T=ziRxZf1$7qk}NY^xTR#I&>k5d5-?? zf%-Adx9UGQ$st1CPxA6%lth`v%}hy)MY)#i?&0?OjXB8P@?BT)Z0F5uUlsuQvRmvF zIVWH;lkuBVGK^?T@+ZPgXbP{o78+Cm0&w9P2sE^MuTX0sby}2_yNF72+m`DNwy`rl zj}+(dr)*h4Xp7}_=Da{j{aP>3)L-s9pcbov9zOlNURL*EIuxZfO)^KEy^SPuAOxND z%ot2me#U!1c~=@$JMX4nESAiD7#8ub)c5om>|>=?2H!8gs-rT&3`griiE?10fCW&C z;ws^6ZNMm~B8-10DXJ3@Q)EjcR%X}OiY#*Rxh+TtO!=WyD=yKwb5q)t&n6JmL5!iO z80EtGIw#>|M~lc7n_iZwo&_PfhI1d*pTwck%^valGK0tTe(zB4Q!~yKZ8yKz$~gYA z=K^Zn#UKw_>j_9yXFksKV`i;%7K`g-fefS+3l@da00pR)UBCGZ2)SQ)L%0o;(>6vS z=CG-$BXhE>i+4YSry}(Cvl_c&s`0BtNVrVx2Rw1hug~0s4@w1adVuybDuyJ+~ z4Mfy7!(qQh+T*ocuj8%pU^h;OnTl)fp~t2&E=EiT2NcCHiUdMe?cR#|S8ThQx=M@t zjhUH>jE;(A{ppA}ZXOWR&LS1c-=yPU>hQfAfA=);T7B{tl01<|`Sk0V>HHQI#ajq% zu(?tgXAUFqD#v>`TwHdR_GhMVnhl#Z7FJ^G%hYS!yfx^j&&~|$-lkyt;gzTR)<)jW zAgx%)&d^X#KK07ld>V0Btcfmi$rLswbk7Jj9EHXx)&#El$9H){DW2tID>wxbq^ zb^3Z5Bp~dS3*}Yux{p}HV{|!NlAIUzUXK2o@|=sSi-%NW-}Mdh?S3_ml7I_tR{>97 z!|G(a``iEncElUYc(=*FpQePQWGa!tP&IjrTNYM=xm-O$abzM3G#Da-6SRIKZn+Ec zVhs>r=ZQ*~7IN$aQ9Euj#n*fxn8^Qs||O+-j7!-2$a8WysSjZ|4pErP<2YGV8NG zD2b%c(-i=|2ca9iGnhOi%Fn_3P0?OVnr*dTx5v&=5=N6xW~l6Ho&fXkE`ciE07tY~v^8Nmc! zCeW-cvNIVz0=h(Jt`aA|8RaXLuqWPNXsWNM*Pyy-&+VWlW=aB;>i|L zVeb0f-a$d1HY+}mub)_)Xg6@|AXGC&irjS5UWmF_etn=LfTZ2;d|aOyJPXsJkgQ|d zl`uqyP`|dQCjCY*Zklu+@c9{G*IfPbEB*^b*ldN@ci)8GefY19H#}wIHmqWG^~I#; zSRB=*!IUa69{T$ZT@^-z+?;SbLGgxs>#0F$YEZ+b+>hj`csE-a=FvaOkyz#vN_Azz zMQlgB(_J;q8?=?-C;=m>9?kS?2d&=>=zETfXRQW1_KaS7Lc$hQtoq5pAa>XYmjVnB zoC#CkwH^U!WaN8v>TX4ZN-6OW@Acn-?^*BGog&v>%S=}71#Wfl1Vxr4IovdwRtJl^ zF||0SsH$y`rEW;gI@@gTt)oC1@}N1w-^;R!^C8ohNiC036w61XZQ=D#x6Hz2+lQZFqa!gjjA7J6nE4iRZquO-;%rh&QLJFTUq$Jd;0gLhKSA;{4X4A z3w0}1$Ohu?Hmed}$K%{bdW(f!4(c0+nJCg{N+8Gu?Y;_`3@L~ps35c%0x1MnvalH_ z7)2IdLbtNUE!|nokOB(UWQoZPrHnG(+ZP*NkM9+`EL()IZZrpch81ovw8oGx{ZOtE zuVX)r+LTl`0%>7b&ZIYDZ}nt`_uR`529{rKL>_x{^8hKOJ&nNAYTm+O>I#B_c3BH? zk_8S$*wwi#EUMW)KzHHm7sPz;z)!@6nYd`Hx?mAZD>S=XKkw*H`acZGz)Z=jCR-HS zZf9*G62#}J6?As;v3+-KMbtBg*bq0|v;sYd3LR)B>z+v&c|pfRa>Mt1+xt^Jg>b9e z%P)qcQ{MKL_YUF8!3<<~9Ui%fh>Xn>ICsS&DSC9QdN$TZKQjkU**Wjp7D{ZL1 z?jBrCAhnEu0KsW|IE5ug)M|42>lGf<Y!Of{x; z8#^e7gRceYJ<&Q1oUfiIa^zb}qbdPF4;$8TNyWO`9_?!h4C60qQa?3zLQ~UO)5|K% z`=m;}?93Y_tDdlc&3y5p`e6l_6dL9fPTW3H!u zN0EebcCAEx(EJS&tZ$gIX(SpwIv(z655_m@W?fBca`?n>t=0}=j2G3QEdfO#PMhb& zb%6U0W=W-DQ^bGJ@ENe`lhbf`U)XCU_MJn zTinT~j|0yGBev?`2{5un&$U7O-w%?Q5W4_$?W{z$+$%YKww$8`{J$m5e5Dn3YsaI- zr%3Wie0qfPbcoJ-atLxANqM(c<4X?MD>`SQrpI|$MTpPyenDZq`oA$E#zZu0CUwIBT z-fe`&JSOr$-ud-i$>Vuz)ho@RVZ!)gwT68D*+Gj@Uvlc#zEr_eyIL5xCcFxiSsbw} z89r5njla5rYiv_%_By@)D?Bho?`iT7OBb|wYY0pA)|uwP0!;{$P15uvo!{0sussYM zRvv>=%l1r<>Px?8)B1G8t89Ao2*M9&CS$b(>cR z3A@U2b5BC_qOOIiJx;D2LJs*uEx@D%TTRYmDlE$#CKZ1E;_`M|^WUBndPL*^`^j z8dfeR*@JcBgd+7(45Z&9L~;IS%CQfjv(!!}mdwXfKt8GebXw&#GHB96PAHv5lK0mz zVsK-uiB6bhKN;UAY!cje*&6{|KgFiGFJelEZuZE~4p@SV2?G^rKR*(K-j0xC;foff z`n=5v43$sUaKItx8Y@v*fzSyuvozFy5tU>rx)PT%X0)o%^O5cKo~9&2Y8@t+a(z#J z8VXym{B%WdI$bINrtNLTpN?Jr?ox2GwUbESk@c)LQzhZH7PLHT_XON5?u!Cihi;aRY5P`geaCUHL-LI9`1kk75$Rw=y|gLs7%{8 z{zZT0JOWl23uu|dG_`tG$OOL)2RYFb!)rCxX?v*^(OScq zvyi6-prsT|#5e6@h~WEvJHN>0Cb>O0up1z(2g~)FEVn0%*C!RZd)N7#$V*E=(Afoc z7bi5Px|&5+$X@p+J@=Tfy>UY^xc-~*bZ>h>oVW<(QQ3s2ZIdG(cMPpwHC4y#kX`4S zlx)*M1x;rWLlStHf_{<^0zqdiJs-7w|*t+LFBeu?cKXe9%z25WdU%>(Vx zJz}8trIuT`I(yeMN7|3$*SWUA?SXc^5Gq<2uP8itA~I(ogtxB5lHcq}*Q5C%iPGA~ zwc$7E+XP1qU+nP2=6{?xO9w63-kL1N(y-8fP0S}n{VE?;t|l?LKl@zl9%~VI8>$&9 zWos7T0UU6e3Kq#T?gQbr@Y2mMl5vq}rz#g3^Z-TPktZ(>+w6tDy&+pnyTX$`3hWeRvIdd zGEeP6?KxF2;Ul&5>yD4Nm#IdKU9$+4i2LAjpjPUo8qO`sDRiO_sb+YM?g=vGj! zeVGw}Lf@IS37CP*BG|2zPWsILNF1>>1GX*gwiAgsCGrKLfBaq|F)8(<4PV{dHv`Bv zzOtyRmdU~$037kd{FSoF)!euyHk}0W8KQovjyeb7J38IR*p9mBAnQuGI==W=JcjJ&4?Y<29t`GKFv_)^2g_PpU z$a*EIVF#9fcLZ+flS35ClJF-W=q&avftI{<-r61H^Cj`1c-hGo8W-qSkcdGvlTezy zHv~M%Oe@L9*xESX&nR|*LPQmk3pw6#Ughir8dPMDTXH;lqkJ1XDxK@ktp?kgl!a$! zz5><+q2+at^YuiW_Ed$}WV3fQP?pAwI=`qs=G?+Ow7z5kmt%&Hv)a53%g_sWQd*k& z8@Je#{0*rGQERh!Kl$q2W1EYH6P z-{So$$iVnChC^;ou!-3q+i2u^k`k3M7Kdc{?SjIP@l28W4F~k6*iP2a0hU5k^`a!c z{%k|oYB`0)LlUCXH~+_Do^G=Pok|J~1hpCcR1T#{0MDqZ>`($j626g{u`{dq*NBw4 z#fA58b9hU_A~#%-MSd4%YUX$}(T!}@y1uaN2Mo;k4;T4jV;BDA(;Z_gQrUtyFpYzT zw5Y;c08bN($$x?p;IY_kX8qN4#~CvPCVaCJpB3Eg1S= zPWP-c>6pO-ymRF_Rrvricg`o_stTR^E~8o~0a!@tZnZ4nO6@Y;8~>qEx`(~2Ty|1h zDf~T)T!`)rb9w-?Wf3_mCr&~+vo%Hg-0;ea=X~(7Q^@uVi5VeqP>-NL%@IlGG0QMK z5b1(-<)@)e&dgI0- z_mGJ9Y_y~4fH0w>r%gK&k?0#YK=2*4ysR_;v^H=GMc13PKwhd||EtUTsF;=1a9=TL zi!=h;$WW_B@%Gtv*|!4dQ-kJhOUeal|8nm>f7_%8#8olF8@iOcO9_-YTU{C=w z4dY(HfV_?(k$x(K&@b|bt9%JPy)&S2>(){ya%z$R>6C;$)7oBJj(+ zE>xPT0fp4e&-SI5Zu%jaVfRkk8MqIM$5xMLS;tDiv3IOX;_CF=q#R9Ao5>k~}EZBt@8WT`2>};SFF~&3i4#mtYx4o(sj7*%W?2GZe7aQ#JGUOwZs|8CEF3boiFRsV&MhTuDO?Kt8T!bk%)!?(TSKGr|B1| z(oY}AdY-O%kk*nLuz};QCMO8uKo*!kR7^F0{Lw+v=iH~#-hi*H9AO$_!d+~Z#NINe zqVOxHV|i>N;`JeJ`U-Yk@`+?YgGjGQmD@^qgr?Lr&?7VRnj)XTkj6wpllBjqf_KQV zMwoxqAuAyvYLIzERIeTM;(TIgWN|)9X!u)ul9WZGPlnW%i}IIOyUlNqS1IP-5bIWZ)`2 zikg2vIlG*$2kvW;@aT;ox23*3!S@zj|GdjWr5Cr@!C8b+dm?nClc)ex>=_+0yLlqS zYre)i)XT_tuiuk&N)7MoV}TPe(9MpJ{--ejwCfRy(Ew{ z8Ro#uIGFRp<*yImD9I8B;G_9%8h6DNWU8iiWZ%wk8OL6fe+iilBrwFIWz zbH>8Na7Q_@fKzK;TnzLKD>q3rfe%ANg>Xz^+n->!OA}yY?X^?hXLehziz3z-eXU+Uu&WfMpYmhu0a8q&VJnMShL$ zc?YJH>^d;?mc-Jr&-U5G8gD{J2=^vA-D%}#LllE8lluq39jZuVvidUcNKe(VJ`FxAB4{@4;B^W_GCMP=~FfO|AmV%c>n|6NO_kL0jQJM1}O#Lcje>*)g` zrl(xgU(#r@w+~`JeXD`jCTcgn+*Y~Q9)lQ&4_rvt;`Nwj;{J%UaZ=;_;)rr}Z<&{5whm zvEO?viI{A|C(e^sm2@d*0}<-Z=!+KXsXh`er@5-EMQ(D4A83TKC;SP>NJV;b^vIW) zN~9aB#rxZnvWs^BS&lQHAGAqrXqccC4rnVb+FMC0MwOL)B=~-!?FA$03+FPH@RK(l zvYW#98XgE6TEOyYBp(9TrXF6)%|@K*5ri@_lY$v1V(}yH!@@fovy?LRjZ=ioCY5!W zV%4o9h*E(C$ii3)!WdUroK{G(xpfK=xm@a@2Y1TcOJ6e`5@%+d{JD-Gf1kpyZ5$Sh z8{wG{MRen9aXq`Pz0guTV^*V?60qC+Pr#ZVt!7>yN2qfzk)~U-JVlnnd>Gyzk#BfquSGw~x(7As?lRZGlxDz+!d(qZ zm0(?56bisYa7J>I!TwR@N|uNFk0N;pf&foPjbD?z7PD$GqN^n1JU7c@;DN#KlisH$ zK(L`8uq>XZ^ly;wKS%)wRc*Yc&r3z~Owj=OROoWJem+;R}N z005I}ots%Wf%(|YUJyt(sbb^k6<}I1H8tH<^<&eQ$vYMeM|hg}RHlt>B*;YT#L@r| z9+>X4-1~AWG>~I?Bxv>z;*&wWAMUD2P|@$W$oYm1#|bkidG++GgRG9}lPzsntNDQj z0`?$!0GrTW=4GKFfySpWAG@w0t$9GBBO>Ld@H^Uthw)?r8G$@{*L3Mv->8#^^U-{= z!D&)i2IyZ@p@_ImAl>2V5JbGgv_cnEhQz-@Na#2HNm>-7R@lEgh;UYbVio*{AD_2r zmoDQhBt+1xpIx@CMoIO511ahj#R+ee^mmAcM>1X3>58E=ZWoXgG;(Zyas3txK?k1+ z0bjtopkB*_U1X!d5F{e-rx3iaYreWL{;!0s~V)g{-#7AChjb9#aJ;joG{dj?iL26+~Fa)pWx9Au>kt@1FJ#Nh7>7$68SRoc-++1>TQ<< zNB5x2HA87@htH&25y{_YEuaWy0_P|>oUak*8FW=}_TQ+j{|dGBue|?vod1Rf{};Oa z{|uD-i?An!B5h*;MF07|Kb&D2Q`1oYs9SNIh7ml@jYiAmXZw#xU@~4wE!8u9F-Inq zVE2};P<=`~bw$h|qjlXP#%kw1-I!;*0Otj^MqWVZV5J%T)2^e#x&m#cvw`j}$7-Jl zw=ff4ueXPjncROOr!_nO6#I{9|2uY?_g}LAnfW2%{-5OjbL#&rlW+Y$7yYkUCglFP zYX6e{x1!H1|I&j0oGrijKXd*?=(pJat<5K^;Qw;hf3E+l?f+Fu=l?awjBD3_miwQp zpL6H_AC&v9MSP32UGsWAlw2qEY%#5n7JMBCf2EMhDn-itF(|_J!yNo{9E9S4%=tJ` zbj08fURg(OF(4=>BeuFvYTG$&Qs~FIi*w4EG-zsoVjw)JDX}7W&&P|RzOR$&qoZcz zkBNneVJ^_#%P!QNJc|Z|dJcK#Tl2JpEC)ts`eWqs013VamL~4DfL^HBz*WE0v^8q1 zTcipkXjaR6=}7q%k)mkmd?2SGM1fqnHV#s&Dzn8EOqt%9qaKc%oCTnszDYL-35S^CRUmMt|E#9mUkPbtC}k2+?@L%xjKi+xatgfp9rC* zp(G~*R?$sxwn8F8HF9KXDP-i}D)J{1Jwlh!9)oxhT;Y`&-kz~kF8!e?pu5e zwZaHmN?SbkkH$q8JE=2vfp)84U|eU#$)l?(QzoC5WbW=Z(7oOQEjGqg<`PhQue}JQ2FF#b)-D!W0m;kMlmajPjQQ3BggS5-K z_judIe<~C{H)^eZ7>N$$M+EaS=@a)wJJN72SHtaQ2o7@FflNOrY~zzh*tA{a7pwQE zQd)uajT^-p@1lR#c-+4X8x8R-0XF>pBDKoM%}jHz%_*9Rb#6qjx%6>NK>7)(e2$Cx z02JjbO%U0$w^2A&@?C|=!TRdKrZrlkE`bX28}OFyk}9Y+mhrI$Ux(9sZ}3@2L=u)uvTFUQk4iez#D zCmJ<$&>zYSZ@d29*VWuTFp-#G!AQysjOkldEoLF5Z6SHXzRYr-!69B=D<8lkR5zwW zcgyc$Sqo<#R1t_AH9`BtN;9ASvGA4(-@FjoQ^+wn%|%1jbrKIMtyXvKGzd65B=F4Q zjcgP_;e^VUsDCyI##s#;Tt59&OxM7Drn+S4ow2)~S_yMd;sR#9kA3MNfI*@tN8u$izYY)f3@c?>S#m9&pNOcI`Ikv zDxjMQGH0ip?)jX0tcy2T034m%dQcug{u({;k!aG!u`Ho=Dp=3pgpan1?QIvF;~p)|-EMqftV{e`RSkZ+Xu(Igo)o|M_%QBi^WW^}Ez2Fs9eh{aoXZt8j-lT>)qz!;B}mPEBaEP~{cgE0s;w_d(_E ziie7hQMMY3ua+@%-)n7Jk{(2vlI}bKi{TLKK`M{|xd=Ud397BSgpN4Q-sLLjk4%Mq zz>)&KmhfVK6=pQC;A(|#Sw+v$1NyW#9|U7YraWE@VAA}nhq3H;^xMr=xv3OUMKM_h z+>$1C3Y)DODOGA#LW#x!Q*ozy#wg+?IKM7Aj$O_TfuFELJSFS(2-#IDS)dV3IQm((Hsjol@( zJ2<^L+g&%Xr${9rAVJrSDoS^N2)qoG3=M@A<6xj`G-5xmy?_}?uj#a((SoGXi6_CW zB@u>`)LpF2t7;|%J3$U0RTT<=4lRq8+N5p;Rri#ri8B3eDMKEEY zPfExf%!scxF?@^l4=G7cGr+O%O%gEmZI*Kj>JyLm-1q-c!ON{qPpp2(K4uu0>$6HQ zU8uDH5Y3Ug40wzm26&YOK8t?QJ6nKpumiIS`zt}6X`;66@Nijz?QVbRj+N~J9ss=D z-H(b)Qb@=Zl7ikLw|rOuQ=q++JZnje zV!l9vc1!_PsqQwr>;LM&fApj8Tkwa4<}=jY^87b|<1S{yN;$zbRCBq1q%!eLY&=i= zlSClr8kCmfiCH@hr&upyki7NZePBU2^&v0Pu3PZC6Xfu~hmOHpX*^-ywV)7O%XK&# zynL-$Hf?QaId6y77n_Be=8SI@sITT!5*yKhiZlB0e}DOvb$IH_6R{xZFo^0ueNCWb zDSyoy>-KF)E%Ph;r~6qTl?%T?o^>&cU69CNE8mn@_%rTMl)GT#V4v~2%c$K`JchQ* zcZOiwc|`Eq?P!aH#!`pXl>2N#Du?3T7YETHLym*=Y&KMvHlF``p`}TWi$+)^)?G@^ zqUkFqqEK)0w1fzKVm`IkM+7$9>~v-3;5ePZMw>XCqn4IL3>-9KR0O|65tv3}8_|QU z5$P7Z&Cs~0^p(yi`v{3%g@6v4PIVocJqF~Pp>gSxlj=xHgAAf)ugR2~p5y;{692dF zwvsK?^9k8bK0?(nO-rxm1`|uhp``c^B$V?(|D$dY%k`QT?g@6qmvDP+8GBTuqz+aF z^zPqv*Jaz$(%)^lc}67B4W|o3(vHLo0M7Fd4DF)|#gfTXO6C8N$Sjdq&Zo%2Oya^% z>llirtEnF zGt1v<76ohVUoMwywpglEsn+<9q*co`7nDTjcqy}NM*A}7eLPwhCjq1^_i{*_7DjjY z5HAS{tn`3L3P9Sddso$z245a#hfEH^f{`fee;MJYNz4}9dDX)rsaf$agg+T^_AOS0 z6XeG*kLiIr6!rpLA`uNhZW!c7fAMf^0cqdbT3jO)C+`6s>OTblz_BlpZRxg-(cms^ zcu^9Qz%blXfa(~Tj06WZUN-|p00a<5>xcG<-+a36_jV!=w)S*p{ptzqgl1ub1Cr7f z6B|qDGRgWuL%&`h_mX4lLat0PJQz;%Kp=6f3n&*SJO7%!sfmtruLGdr^Q<$!3VfOG zesev@NRCYKq29foPI4{ra+FahjhX?c;7O(Omx#OWp0_=pupu=_`n!GwbPs{cuXdGZ z?fAyOyW*#$<0G;U=Gc6b=j&Eh0xSe`E>MgnccJ-du9#}swM{L$5R{INk9i<%pZf^B z2enNe?7{z;Ra;5qCd$s)ro@ulPur#x)ZZhhi~8!=gkRMb2c$KOsr;iSn~Q2#9{b(s zVALD)g9`ywH5(_U-ew@!nuD~GRcT+G8Gg>6$C4f0d+wHsk?4*B;lMph=gudHS3?@> zW5BQdJy^NHN=fN$#SC|A*y-TnSUMVm;|@$?8Ds*>YF!H@-y3#*`}bsE$ehjYuyGO;Hcs`n z3QVJI0FESw8+|*#M1Iks?lLxpi!_elMF#C4By^kQ+WmNeHs{5ib!?BjmL|=E2!b;4q}xg z5Wc-ttQYvI&a&TGY45uwm~L1M(0yKlQqK?lzIc<5A7LqIJ~B+cp%Eq9-A0!)>A$}k z_x6xprdOl&%d|Dq5I`%lOLqT z!y=1&e<4bbfPOs~8OL4%=0vj_OsxbN&*^#4c)1wSO!%@PAQF83;>?6WUs)Fx0StO7 zh4iLEG3o{ao5nfErpMcoECHIUTLP*3E*IF|5!$W&y2ElDPy1;MHq#JoG-*L|zYs99 z2#qxgatL%kUQ)#Bewi_dH)2#jtVdA=B+HyEndwc05BQjAA z*?{Nag3qY##1ESbHMNgHKfc)P?YVEZICK8l?DN9deGldGUILO2b{m2vfC zyd}-7(MN47h<9B*i(Tc`cK7Y{5+e7#nIV(Lok!u}420QgM7w*mXxbrV_SkNW+4<&2 z13OEty_fAyY&9KLr1K)d@0w7AFW8bS>dg!6kPMrBid@!@7u08Y_^r#MMY~p66D(7u z_oF@$)Ht-p+2sf`CYYubIT)*GdfBr3wY_1a*;e3dbWvhCAoOm^8vW>oZCH9S*&(8Fd1lRBxa4D%R@$1Um(t3 zb$KK~{=J)9pY(>>{4xCmV*zO- z0yk=O)PO!GYy>thmD3orEI+kCINQ+AGYmCIA$Fr_7N4@lr7TbgNDpwspI?#d9gJ zy(N!m5K7-1{!ECt9g%`{q@uN#IJO(KNeBem(F;U~3zJC}0mYWA$2r))QZdOzSn@j| z;plFwg4Loj9G#k6L*wS#E~NQHHrl#~0-4F#;<*2s5ZjNkFS%ui1I%D{b(;>!+vK(! z*IQ*KIQW^HvvP4a3QSF}T9)|>SoN({`&G>XWE!vM20Q}~X1ci^g>#n*CO+fy>ufC$ z(mQ+Y8Vvq~*Yy>E7g<%dCjxv1iM*}n9COj&{O6?hz6 zMOAl!lcMSzNa9hymg3y6&q&5lYP4VgtH#B_Q?+#sON|G?D-tD8<7u4?dJiH4RX!aZ zlepO8uRhy4D2TOts1wSL7I;3@v*kdncz#zVkA}Qsv1B1)muHPK=rnzL|XB^2k z0C;1j5s+;k4{TFS=V}K^o7SdUk;NOoy$O8o<1zHx!&~pROs#-jQi}oWEL8sPzR9)x zfyLHvxm=S7W(Ty9)Zcm(K_`m#>twHTMeyp%*R6fFRNRASQ}kwp<91|;f(z%9mgt-S z8!HD6uZ-T1#7!uokVdHxq(lSrY!N==z_ud772g?nt$dy3H&9eH(z=ys$MUS?eCUP-9I3eDy3ozjjU$4a z()Bbp9*CGU5J0reJT@Op6=+A`Emj}l-|Mn5JzgKAwS&tBQNV$#$5v;VRK9!hf;4=j z4cE?UO)$!`9tB0RCuPLRC;XM^5(5Igk59U2>L?8hxBGgij?^4Kg zErJNNCr|T}c7J1U)zna5V+}yMsT2zWdLLdRhe(cY2*Htx7pOf%*@*Mkq8UF(5ZeLC zRXPC(sxv*$r?)PgLH;o0+EI38*lCUdnRLaXd zI~}HViQU3(^rkm?g#gu0MsuJNm5~B6GPF}4?Y4eu&g}hsZ^L={5cLHmHQ1xSSB%2n z1*-q$Gl$4`td}-Noso7N5C2;2dMMhBEd>k=0~NT>Rg}CNO?`5VZl;c}#le;ImbGOH z2amLM&|)f9QU)Y2CG}i#Ukd#QV5?h%!J_uwg+9S~b`=aRSMC&Tt#r%f1BR20bgBI3 z=k$qBY`m7&a*BKgicu;2Wx0#lkV9&EU9HqTr^oC|DQKlA^<98I%;&Ls`g}JYpdDG1 z;<<;jpeKRk=9*Yp!Z#0zG#w!X^m-*Os8lB=keE$+EMwgC+pnILB(XR<8cZX=JouAc z_+@t7vwOI^97Qrf6~eTE1Vii$OL775*$#qrrO|yau7o?VXfaZ*UMZT+=?GF1LUK!} z`fWaILn4*t*08I0&WhG}OP&Df@`z@84f6reG$mRU@BuU`D-iXXi9#E1F>v`jLEi@v zY96QLQECg68C<0*?v$W=D0-exaOtsIR$z?KiI5PhCS~A|;$WgnawWbNMXAt6EBZx@ zI)nnbI75^ry92K0Q{8wK`3s!!a}5y791*5F)80yLN+$%`lFTY2J$3YjwP{EfUDq~~ zY19_U!j^{Lq%1EW8kH21x6>vYi~Njlb7*8McOeweR)XAQd+&0KH@Il7w>l+zRN z`S!?DDFAg*G`gEn8uA39l4a@0Q1y?^YOwOm-$~w*Wb)X?NJ&Pc9_(v+wO1b46g~B= zxwz^o9*l}@(k(puSHIu;Ky8X`_}yq%cL-e@k}*or_yGXHfi@V^M#`YHO$3A+nBMrF zQD4n8MwZ?3)X7ws0Q**TAN}FWfz=LyvWe4R7r_WEWg=A-FX3*pIWnrK!)B)Ea}$FJ z6t1y*;FntY*iZZ&?mB4tj@XJgs<{Ipkd-i(Y){P4O=IC(-^EemHd?b*#Ec#sVY8%Z zizqB>)q8de(hk;r7HQfBK8@dDD?hNm@Cb7~VMG@G)P~rE_J>|nN67cD-(5JU&;%cf zqa0@&)V>cIWCL27wN0C8NBn#kbzofpfAd@M6uZ zD0{2p@8GZB<&hl0)o^Zju)<8RJRIU09;XDQXD|#W<(<4)d2e)grJqLZg+%hCm(~Wx z4akp&)iO*Z!zsvkluyGrce4g?^3Seg=)09JJ99^nqP2Abvj2bqJlxl#j5DDSm5&)R+k!5i0H zc(9(g`~}57Q{Dej<|tVb^Tdi)sq=u+!;kD+o2f*<={3dSU1+E{$gR_W2`m-rtf`SN zwvG#R_p=I5i==m>7Nh|zH{54!6TvqC;!KCG?x?}t zH$R&IO~^ICtAbLZp2HtepN?N6de}u4n}Tc_Qb9)G8d#|Jj?wt(Fz#lI^YGBV&3jun zSFA3*%;1z(U5lYtTV03)Qw5;2BI}`!8k5RY9O)Z#Ve$fZ#2P@@TS>Bq3U>4k$9WW# zD!$)06AuYd?w>w#-q+vzyD0c;qNe~LXHCK+r!;slQ^7#B)!e^AXSF4S(^=G9qOkbp z!`ix^@l6l#?-sYZKiqb{DDA;_n_hh=tur{xDvdQp5hN%U^5kFB5GcW#1=NbtJCnVm zmO96Zo-soLt<#K(@bMN8t3sr98wgltyM>>ZYVk-E^F!d|9V#(D<0qGY{Ty|!M^CRQ z(STgLj4=N;iM@@S^LzpbRT46MuiR;_a#klpMCkjVROZbP=N0r=JhZ7N@UlX!^_maQz%W=(T<3Vd0NI?-?=Nxu1onu>LKyM;JXAR2FU$ z#aey0w}bD%ado9}AGa2@utnn={UV?F9PaCzJ!z(=d|Ik~dVQV+wzSjtKrAJ|Dba{e z-JT-yDNOZS#H{;{S3QV7p`+5wS4DFw&(k${~+G-LOS8PTryn!3mCk13+K8+kAv*0c&nrq&*~m6se5^F1e>*Z zCP<;ie2@aSrU_xlN>>v}xHjQDJV`B3NmcJPQCHTNd*@FQo0bh6Dg`;l`()cel(guM z&^lxto`grZj&WWN2BBYK1`J`hUM#bM94u}6Zxr%hn|XTCQ>@e=P-U9A9qrhE7rt<9a$g(3ix+B21D14E0 z@fasj9xts>HJe1G_zzzXyTJj)5Q`{zIqQ3MJr`Gpk z=H@p*=HJei{%y`!VP5sKYGLu_265ASsY!8Uy;QL z?M+xiVRG1|j6>GbC_h<}#7p=W$b-nnncn0^dZNXSBN!apjmsD&_`i&d!PE(c?_MA` zqS|Csbx}P6Fou%IIIVdxdQjI)R?Z@%_^H?{goLFP#apUgb$xy z?_*4Cv$4ouPNxCqx?MA^b56Mq(~;OUs-HVGguX@i9h=;M&{J-^lndG> zlFXu(y%4WuGgefg7$Z@ASUQ^RLZZ;lXn0g-B`)d@C&5&olfze4#)-3t2=X4z<msuC~}Nl*P%^mVsJTzRAP! z&KdkYP*&dvhvb~AIg4Oj+Z-bb--P$lOXtt1UJ5DqM0Wab zhVox>vLGRvUfeXPCeCG&5=kw-oXhEAyF3|*vpSoN*m7;AEbFOYWQ^I&Ux(sryQ8Db z>d31(%wXbm4e> zp(H<({4e-Q9XP8vC!SGbfx+H%NtHEKanB2-F5oI$?)3aR@IsYCsc$&!7rdTgbT~iJ zM)%iI=mVNjuGUY9^V{?I;)XGyw}4hre$woqF+s=o^IDTstNYAQAqt2)wCW zcDd837g-yt0@`@Wrp8N#2)@a`&?zC4z{kBzwcZ@4vH{xo!TPVgcRA-Wh%DTk+x$VF z1t4}VA;=EPEJn`n89*oXkYj&^zWa*k@uiS{ z;*czg?na-;5EXUL;RS55^6T8iWsRxFFt|pBueb;`-$yEUb5?iTqG6h0x zEPH=y`-gucF}X9hc9i-DzJp$SGtn?Tj_ZI9ARP-6XWrdX7}FFU6`|!`#VC=}C@eS? zue4`MMqOqL0-b4&Jc^=KxGQ-->)+pYS+&Uqj|6%(I$}*q9R2xr(ZuurXioalt7{SL zp#)-EAA=62_FM}hYD)Z|YO>PCdARs*E0bq=Y@0YJA_>@pFCv%fJ^(WfG8-le0Dv+# z#{3G%f19!YkDfx~=j4A61G-fXgAr?$?pB%Q zbXBJ7Owx~u|JzL{2>kyI`ukT1+W%(++JB?|{#E7wpY2)y1^v;BkhUQJDF1vv0cELy zvhWbxsEf^yIM0DyFp}u`tZ*2e9j$YrGM@aVSZ{=6=Cw)7u%94H8jF!15;s zjpNKIWKvWa&bdyKBk0VwQnJeefP=-;Ybv;J47u|-le4oY9`lh#1&)OW848R?kO`0rCy(yaV(4r)X61h$BYoB##O#E> z2r_bPdZJEKl8i<Nv>s1+TAbx;opddLdim3ril$2`2xRxB&a?@+4?qT}0@*U;a*IAyAN9`wEWiHSqWN6*GE+a_745-A5X``Oq1)gSZ=*@As)Y4oVhH9bJgc z&tq%Sd30YWwotETt6D=B6Zy071BfvJB*PdYF?TBqH4S>64EfIlGRaL&FZ%MvF9&;# z7En0qKp3t%KhQ>43(6`iNG?L&>)L16$nr>!No4En4m0K)o! zSP;6_TMc{dK!Ne!4Tcx_*lzpw*-M7^!Qs{+1zJtsf z3z`-ma30+B_s;dG2MPwl1!!%7y0-+ddj$Y<;DySnO1Jk$nQVPJ>O4Z651qA0hk5IFQ*O6Tr}jlmZ~Uh$CkT;vnvJ{@I?~fNlRY2eyWhCu%2v3{)1xa z8_zmR>jFPyk}KqO$%9!WEanp0J7u)%0r9FCWQcynrzlL&J(*{Oksh=iMmjSg{LVax z>n}XAwHOAH@o#8{ln)=bt(Co-XE0jxe)@UFX(4O_3R2N5-29c(_!re(>7mP>>A&p1 zKA3x;6>3+6FDhHx0^Z&##ZbGGWQnhc!%Bd7NSpY3I74ePsiJMPwHnvNICr%-PlAQq%!e+&mO?kZsL*|dm2+S3gLXE2V(+pvlmL3`O~|Z27fmBH3=PZl0R%V-uRCwmc(>+p2shbE1bl|Ha@;uKaDZW8JOiMj-2TarLE%EU zX5F)&2)=7Rhw9taL;w^$c`p&(*92~(gTN56U*)sVh0S>&BC+K$JM zV-CP4tFU2pYZ_O6<*C2wr z5zK6~F7;ojAL8NIVn#L-HZhCh7cSr6KLHKFc-BX0e(bWPQ-jc zS}A`H%XnetLQUO?+{3R)bU3F-YeR|APrMZ8Nf2YA&&+WC~F`7MTpZSXra< zV~|m5m9!w!ee@(fr@KY^>VDh9&H+|&FIW(Z3?%YEqjuA5?Aa6ZweI>l;^>;+Qj6Z| zh&9OL1xP_Yiv0P)fxlyMC^b3KGgeXbZjkRjYOB* zu(~_eSE(NA%f=)vj6bpixZTS?I4QY33<&I74T=3AC=dHT`JV+8GY33I78==x zzq**i0^pHfBVUQJY_d!AZ$ZDR64a@LhM2JKpL3JTVR3m_%PF0(3Sk|MP`;UYmfZgs z0VH>}ug1VXlSoJ>WtxUwt9ip`zHtX0C*Gm;{=k;3l$V1dW&4XdptzH?J=_Ji(rte) z!Z*V7P$`D2k{>o+u6GO&2&~b9Uv8dGl6H$0cF}951JLMpc;FPLC;}kwz_--Q&I7nn zB*}qY4t6xpHGzJ-?Y%Eh@}5!bg+4_1$WErQR;@Kde*c*lFvE&p`jyCDa6|xpab^1w6V<|fdC(52S1-N-v4~p34_6Igscp8DkoISbF zTd%>$wVU39Bp_1L7tA1Y!nnLJRS&K`Y?ni*CFHFR$Vi z%!V9-9I4vVvM6Afa3u1)x!-u@IRen-WuI%ZwVV7INS)U|lB{N+vD$&4h$p42e z9M}?F)8sDyyq1WOLyq%TlNPkD=FDYZQ@W<3kP%O)*BUW?!<S)Xb|bQL?eAj2qo3JvNG|+pfymur2XODK6~P(L9g0bI{OSO!=uO1&Gtaw` z2LDJP)K$U_l|S^g>A1|;7><0It+1*@1`j!M+nJZz+Wi@Fy;U|n2KV&aNZ}$3RCc)~fgEXZO%Pr-|=t$#XK? z5w1;l6F^^zL~EZ0W3nnus4KJr8G13L=S$4oUb^!*39|Oo+MuY=cnaa6B_`Y}ectAJA*M1InQ0bfXYoC}Y~^NKcP4lC<7jkA=Y-9X zSoZR;;J|DuyqIuWzX0^e9JMIu2**MDNaD}fZKn+-{l#{6!s6H;B7c51p9S`*cH%** zgsvfM_h~i#8vu4DKszSk6!WHl)WSAekb%2heu2gzj<0Fy)^j17Ktnp$c_}(SE!c}^ zsc{CV8FjH|&kg_T~E?>!Jmh>$HX!tByfjwi} z7jaaLU;uU8Shg2#r`T<;%3M5*m?dBGq)qgIK!mUH&|SmcMRl-%@s3dM%#i3Yk-#`m zd%Y3c0kaJh_V4casBW|VR3~h*h>fqIECIx9!-mq%eY0O$wJ7!2MhHlg@}vRVGJW~W z2%4KiaBW+%Y*CB!YLc8t^@k@k#ohT2HpKB8SpscpEX6_T^L|T=;#PoNmZ0de>vm>K z6(usQBG9!pRChPZ60}BgP^;w&tW4FaXQBdE7MN*=Ef{AOk`wSXa(RM`szxQwS z!k3{-+3N4O4B#Yia!AC8nAXZ-x@wqH_kJj(<$C0_R$8HiO`S)`DK3}^FC3{z`Z`I{ z!Ie>UQ@DFYI0JW|6DuSQxFUoRe)}~UU?vi72@WMu`NM+fkGzS6fY!*1=|y6v^KfC|>Dj*v`+PW(#!HcO?%5IZ-G%Klm4App5yn*F)?d!Ckg zl~8JWv9nmU=lvBYR!PtUJ&Ap3{8gK_vL*X=$G-2L0N)X{p3Q4LBiwVLmV*M<3R^7G zd7&t3)9WMZ>AS$@XamtWlAC%;?ebKX@T`GU+3?&%M6F(1jdn_85dGJ0ysF6GptvQq zN1$&0Sl3Zp+s`3vIhhxs#?XAe$E@nQblNvp&B@TuO&Ed6vk)rp-TD}m0AyMP=J+S} zB0D3&FOvfBfCKPiO#tpE^VPs}ux;aL#bOvLK_ET6LQv!EOB}@MktV3&Svy1D}T zC?2s9 zD|=K%kDu4rhQ0X*8@-K4jiY!#nAZV7=mGxO-up9GeOtdrEXevx9aWKknJxk5ygg$y zQ$KWe$`{(`oEsY#>r)XJdxak$VJHGlLfG(Ru*Zm2T8JDHk2u-}Bg7;DR+62^^@Wy8 zK-asC+UfVTPkSIMi|1L4EZcJ*-f(~3XYh&H^>|{fwMl}R&+L*u)WIx|w%HG_1*=_q zu1W(4ti85=LABXQ?x2%Ma}eRR6?j<$*4CB2gNF;So(nG(aCa@v|6uPO<17if1n*n6 zZFSkUy6oz*ZQHhO+je!??y_y$-kxV?o}Jm5clOJ>-}ZjUUu49&ktZW=-i*w1&i}Ak zASP*Q57s`UaTJC9m~8$7Tq^&mpWsl7!Q49k>eUl=Pc>z210d`Nn*r5YxNWZ5 zKFSgF^UV$ltZaxarg#@>wGE(`(h9)l{@pkyYhiY8iByJ4f-s6=uxd|_3T^wsDX)iw^f&t7$*67S1eovSx^ z44_%f9sUdmmNgrLR>MHC+x7TEVsCtZ^C6CM<0_JW!XUYQQ2nnk+#8a7-#OVCCQvbC`uLo$nLBGWVwRAi{AU9Uds+ z`BE5gxF$v8pNR>m7-(lKUA0?3L7)@dTCSH+T9eLNKjkG&OFlU?^g=;NHoVbg8sBmrU$mPhIhaIqWU)Vm^;THv^Vm!xjOH`~PW5!R}8L#nEgiX!=NIP8XBF{-2J z1a>Ppp$wuXR>3-kg@n(~)pih-Y692af#Q-X=d$)8_$r;(w)2|M2)c`3biM*J;B$4? zI87I7EB0-JC{@GH;`$W9L1tj!s9)uEvTr2~bHH)`O6@etR4$A(A6U7%?3f9{n9x+B zW~zyPJwmct=B5$q1*#`Ako?IMBJtNEbq?%pJ{`)21PiEgp%uk2l1XqWN`) zsu%t~V7d%lhk+E;`YPGsafM*(yLgpyR}c$RLQ}kF+8! zVA?RvkiZ-8*UCbnRE#GaL!3Z6*I>`{kz}qMWhMMW#T&nNu1)y5e;5yvb^nC6n%at? ziB5`zv@)1^5`|%-^E_{n@8h6-spDgG(#sdQR@$p`F(*K^O-$rZCtKnVMavqsWN(5 zpczONMV4k#G&%`wPdZzcC%$Z^p^AO+<&rg0R(eyo+;Ahq7uG*F*JaH|;DK5fzG4aH z?nq7QdSsrpAtt)KLm61m{%EU?vcx)eJYUdFj{_C^t1c)1=$rdUAKwuX)=WI|^F$7n zk3yMDaM-<|zKml8oL7HBWH+7WFnD8y1h&x*1tuD_$ex705y9Wg5LP!#5}J$6z}I)_ z__ZHBF&4vo#JOl&xB>c5ropiFzCvC@7?})6ldjt>c`1QgsgT<++lffxny@`^amI2k zWzqu)7S0L{KS=ZyDzHb$0TR?Q1uWE)**tZ1+0G0wYLJ8ohRW3agy{Cs78UUD8t1O` zesVdSX->?|@)chbWATWo%P}pC(qv8R`*?gw+K&pe;IhjsIiA8O}FR?m#DVoW*`|Tb6j{f8b6Zsd(Yx6Ql6F zHxH6?dxI2{*jQ-NYl8#M5Fp@ZfsqV6t|^H*vkO!z@^j=S4T7JKbO{}A-H;Hu(fgd> z8i>muv5EZWQS1?e{IlO}7Z%1=F5It6NlzjM`7AL)nFHl`BN>M!mP$-4s83qJ7Fdas zo-@PF6IG>uC>Y$p>7W{n4qi#qojs;xbYpS*RzyNv?^-_)zKpqA0__=uwNo4tMj{JE zpfIObHDUWAvKVf?3W(8}Q&;{&4Jl`i122ID60e7orQT4pvmNyYc$BYWv>CN)ymEbk zC_ik8vo?>FMlZ>P;q_~ka0(}NE{a4hMrh$J(ad{yo+XEQ3OEV)t?4T-Kf{!Yi2|2% z5{7^ns!fVi0b8=6pzpS|6-V`2HEoNd$``-(iL<3E$CubmXV9OoPda<`18DRNfJpd$ z*ES_Y)NDXiEEL8`NwiD72#+EgRjkCBUT4Nfid?XNskTZl*D( zhpTmfrSQP&A*r&r3i4SprA_U#TXehv79pkaH?EI%VyeNg;B~vPw(ovQUZd)tj7(I> zSEB7E&tT75tztZv%P1SfWPY@AYA+?C9!p5rT34M@umf;!Go*7PM`|Daoa6Pezh1Ks zNjwU1pcy$o+HH#Oo@dkfqw+;&Xr-Pl&^Dl+-XANYy$DJO5Agc69t%~(kN{P&5o+Ii zPbaK}UsHIU+{;oKB_40JTrn+n!yS_0xAnB}{$al6_L74yvX$k718XwDM*>UF8I884 zuSBz^8pibB21EGsI3`$qo-UBwpOWkHUa`x%N**ZQ*y#(->#6j&e96Q`!HM@)4eWex z;OX>n3wab1Ctf`ba>=5a13TxQ53%eb?zy_zHlS_oa1);C!)olnjp&nv+i$7<&-U3I zA!}w1+T3W}aSUepZib?#5H~>h*HEvb-|@;>Ty7U%B*V#Gi!R>=v&=Wj*s1uLUZ4W* z)g^6qw_1w5v-FY7X3BO~y${b8Lv{~aQRuVd1lA3ro;Yla3F7(UFv!h@SzC7N>g5W} z1@!?^&=`lL`-^G`2h^Hvv6L5Uh2UZW3L5 z$sa^VRSDSYLFkSs5G1SC?t{*4e^F%Ozf=So;wxru(J|>8KC&Uz)prP|+vd5cTbO6f zvSxnsFzB4gf}L$rF7PLHsg<^%`RdnOk70_Y{Q*(%OXdl5N-edu>JuN8eMJ zc{Lv}Hsll{zPq_~Yilj^!MsHgLqAm^nz)JSRzsq2j6?sm{|NVNa3i9_dGO zc8=3w+vqcRWwEnTc7N$>UV?PJLOUmRQrck?sgjVoX8N>hDb9fcPC_1MX7}B)BW|9* zp;=3`CPR~H1TcaAn9LA6rS81xaNH<}B+LDi#J^X?%v0N0*#c;` zt)OrxiD%d^9*L3m_ZVjPHM@jGt3mKs7MKjX5ng*dj>Fq~_E?j{p-5*BZg9lPh#w++ z#ssr0k1heQ?BNl*VPK=VYz}YoUe4tXQBW@s1BPtg8+78gwERe>A(=FTS}KSrRCD5& zQxDeiL}t< zv5NLuQ?joWzGV!gN679rzVL`nz<(WO%KQsgJmT%?$C5e2CK6}5fx(Wp8XYqt%;a78 zHggMa^v(@(*`E6{^{_rI#@N7l0||}z-W)g0wMSC9Mbg<-+F_JfCr=LhjaEap@juco z1BBk$J3hM+qq$-P?^$aY1&v+EP~xk3`K3 zO^d9obdJh-%}6(3>DWkpPcEJqAx=8p{hGonTA#0H$6mbCD-BQz)Jte>@Ltv98cX0> zk3N&P(!UfHC;aFpHSQ!z@iTOLAuyGaKRSGJ>%x>>%0f3=X?7a{t-C zEP$XB8tB51=z}0q06OJ`d*+>xin%TnSU5SKzys&J*aLUk(9`q5_VjuZBz5p>X;k4j z-X98D<>i^|p{=IWc-YV6-no3=hNx~1m0%SZlt-eo zYVyU5bEmdu7BRh?FI^_Gr8Opz-xGpCg?an)$RgI|W9%KB@%7%D8XpOy)0~qxMoVnmg z55F(7*@Lp6^-*(^W$b7+1@B`B~rLH{}nZ5MAS;8=OpRK^X1xW8EiSn4DFzu%=SNbPuF2 z^S}|`-t6q9VNM9ksxBG#YzEIK|3HfOKY>O^g4?HP;=>=mET3mptv4RWx#+bhdIYNL z{YE5x2Q-DGc%o~5jlWjmm(_|6oRvkQkMz3N`TnwQJ*?}5OZ`F!dK(3OU>x=y+k|sV79Rv_KR@!Gn9wfXSk(x=@Z) zo;StArRW>9uBtgb48!1JU9w@!OhMhv)+<(w6k2ci36EgOC2eYLg9!d}<O;iAR3sO_<||h>_13k{S_c2w6PX20 zto}i#22)-vac2-Ha=C{qSJT+r-vK^HD3lMSxyVbi-y-8k)Gb)Y%@WbKewZli> zTu(;H1z_P{?_*1>P-!aMm9*( z86kN^x2vIiFBF#ft>7%}+h#CCso=aMTv&uXTTHFuLX3HbQ{2BRP*Un+RQfl-DL7N2O42V=iiav;(;re8 zBXy>qDqKr3jBDP*C)Q~jVRE&hr;ITKCdnBoRyE{oTLt7qQFtV=DfSQRq8Bjn(m|6} za(3{kBO0b!1`dWKwc`tuAclcygc6MXc=a@Ye@BI!+)GdK;Fb>Gjv!)j?c18*oQ%{o2J#!uGM!2xENCJ zK{;-h8rU~%I^Uiq6l1|OE&b`_uJ&lPRsaLVIe}#3@lqDa=+#`6h~^SD)Q-GL6}A)L@;yc3qItLAZ$aqKQa$uR}lrV=dA8euRb{# zMK&}znn6KZ{CK}DRK*}E&io{b5=9X&DA$)LS~%n1S^=nR!nwZ$rOF_jCfCrceLTmp zdC~;I>UI~%gAmT*#Av=n#)aU3N$9_uIDQVe_gfUJiB(W$j`GOcOv4&YC^+zNMFmE@ zH7f@Iv0(oQ#B*Qc?!zO=IP+qYKLKM<_mtZ5;lOW=Kcorr zvxEZK+T3t{7S{#4E88s zlc@sF`G$qzB{xz9-J%HP$S?_yy`qy?@8biBJ>mG!N%7baIDq&vD2_NYTS7hzD**4( z9hx>o-`#pJ&uwc_%qXA#qj9a0QRgwQVel6_ZKAiER*g;cq)L1h%X>BEOy!a!qqw9N zGJ=^y9%uvd^}PW(Fh5O;a;9`t5rXYBno*$vNuWE$X9LC>ipF2+{gO!L-CX28tnuxg z&vzugl;3yBw;VNEy;8B!JWp?lE@f^4gbP#tQY!eJciR_qB=4cp>`)_zWf=a~^E-oY zJ9sPs+d%X&Bm+iAKhX^BHvG;gV(IM6W`oC(5C{L(?B*I)hi#XBIDB?CSU;J#d|K_EI-`tB(q1Bg ze*8;icUEAs!@m6;-gXz!GUYZ4F>KLE){|rHPwr@ z|9VnIQp-KwUwl8;y7aSoo2R+8X*J)Dt5*a&cES+w>-JEiu+7u-&-RNuwf4m<37gZw z-r(jftYF*^iT?bQM^lowXc$N$TeQDrekFUJ3rikC)=_5)`3hYe3wJ zHTf9u`?>nuudv{rCo2qbNk&P@dGKsLvd?|Obb20Q`ZgcSzi3_@kOWde^xRr7Xl{f+ z-cM2#G7tXR)>kuwcln!nN0|*e@7{(M3%}!Na?K`qQ3q&#g#f&j2Phh25(ah&;d}99 zfK_Q2?OR<0t#B#$LEoOUQ%1-kH*xx*=dXW`ojZJDe6%Gfw@2kt;qj1Sw%6~ivHNsL zP7(ej$@khfE$O?J{wYH^xjG znEL)OX5Al3RAA;cK5PwFAEPaAX?&jfi&eWN~A!sf} zFb+0;^)&|Cnz$IxJa|zqior^?rNb6 z(13Ze!yo z6#wi_W{*=)8$c5m!baBiFj{NX`johWL#IR1I=&t^*a{uq>~S2b+BL3$6c(2VLB+ugUG}%{dc3;@7bod99N*uwXb$td2U8!rs>`_2J=mf%P&sc%`3gvN@nu)3JGa z^Z%MwvDAZE*@L4lEmi9;o&31!g)fH{G^M^XJ{1-5KfQHlt0_nO9GE$&yTHO}k{E$# z0?#OccjJUJ#t}#(kAGK5QxuorDk%ggZWCf>RI757#g5++SOlTKVOiuK-c+Bpi z`^2=Ev3kih9v>ngy1%voC-nMuB-eEsY~-wpM6yr7|4vePH;PEk2u{9;=MdZN-01=F zEK}`jJt8|W!|RW&47ZiQFRV0b_ePPEk*71*KEnC(%dovUoImXe3azCB1DKp}oO6q5h4T+n?1|Z7-!8FH_`C z%$YpifCuG=Ek(>%kx-`JEJGo9Y@d?>iwmQhb}8XAON`j|+$^Z|;>#)w5@~G;CnN?7 zYv^`Ps9?FZ+GaN#L&U@Ei$GcWbJfo?@giv2;iZ~T1DOV=`b5rhV+p|$mZ-1!eT z3DrPE1%{l&Cg}hMf82#VhB8(HP;HbpZjGa_JirzE!*$#!+Eml0i<5c9^%&L!9I>cB zmVoyA)uwI@65)z;+oOfy<~*7|AQ@0O|AghKQque4rm#y(?ya-fnx-m*Q*yqpcEK`RLA=z<8 z<-vc3Q2is&3i`_~n2QbC^BhjV+@QU4tV!jS;k<|OK-ZT}m>>34j>^S~y z=f5f-n8*B2$$x8ff?o1!J=0s6`*-y_V~m<&?in6LT3Z0lkg8Z%N@BsK{;{d(6x(=nPC^3=I9sUpNC4W8X~{ zzHpH;ej<<#$7d7FU1E5{3G+gpCGnH5G*+YwCFad<)Cw_eNzm>SOTOzWk9M*ezfRvRK~z{=SCd{n8wv`Ah;J& zX(0>*yFaI_VA~o2SIxZGly;ieW*Ah6NtG^(cz$1I$9}|&Iy0Gp^L~eV4V8cS&Kzt1 z4p%6f%?pR1+w#Y#djqoK3w101g$rq-xkvov%G;o$6TYUb_H1Q5)%w-6sfs-BV~hBG zr2Ws8?e^Bw*TRksGX*e{cuDE3^-)SUMco2yP&0>t-t%+}ch*3a-YDY%XJhCZh`Wgzov3mZM*YL=HPS>7#E^v?8w0WDg0LcLQ9Nk z4T?83fhV>%4rf;C&=PmYraHzPj+W_cw&t0xnNn_dc}YM3ji)(! z*^X`uwPMirVn(dm0L7Js71_Xsh7R&iaQ0u$>lr_RGM?M)P2Dfs@jR*9Lix&b{*gx8 z_3+J4SkP0U_M=!ZQYiBb<$QvVs~ck<0?4ugy!)O#xx-)`<#1QY(?(EDsPU2?b1VmR zooBMJf(|4rqkU-Y>4{>xHpv7ts^ojHs(^@u4*ucpuw0-j?*YNf`-pxA8L4%CLxaB# zFgpv2MDbwt6jG6D!S7%e{W(flagtHkXB^gFU}JEA}#;WE7k?9RZOQ*@{r%i<7$$*_V+! z+uH+!?bDM#?xhrKg{${l24gA~%y%l*V5m4(gF|eDMTEfpCN9|7Tt(}xRtxIU(|Bf! ztTXq0rRbV`fEUAVi`g#o<9eDKkfPQZ^l8+{6)va>01 zGFzYm=nKhuncd22XZd|`phFsU{{t;Hbgj=`3sbsz(72^ht-yI_J8E#oQmgO!>RQ8J zWVNRr`K6O>fH+_^fxk849dg#0;5lD1{_9`A1a}{;kKE0e62hl3L@ER3z^3bbff*r3d^8nPdfd>$*bMd%)$^ zn=-45E!9|=q|Wi_liQ%wb&%oLbL!A4FA)H6#xoaQ~N zc38?VLu&zJlp*HuzitvAAau};RX0Yle1YZi6e9YK<)Y|`!RB2?yo1TBI6W=tRFyav z#|pbvtn&mLx_qMr$PdNRoZY-N3O~35wt;xb{je$Nhtg~0QO5gg%66dYDzQ@3Mx^)t z<~-wfD8hXT(!$%~MXUdeMis62iS@;4RntRT^t(<$J2wW%S;tbhLBlm+SiPE4hCWDK z%4u>pZ9*!aAEak9#0gHCKRsLhu~oIrLm8VL$>J5ly*yy#;dH(zkMiHJs(&x;uv@nj z&|gEqiq%ZJ6i!cp<@Td>@7ce)y_+=?6C_3z(F$;A@Pd~0>BxjW&zs7ww)MGlluG_2 zvGkJNMCWHrsk}&{H~8x>DjJG^h!~qH7@btgGj`7TU8J%eSQg>IKTI6Lf=y`IF{K~B zKPwA3nNF>ZvU>Oadm^wSsCc<^X6*+!JQbU_La3yUav2dHH(P+f2ReVomIO^fX3U>!z91Q#4cj+Ide-N>(J29Dr`+FMTq-}J_ zdYWx_Kj3X=Zrv)v0$P zG}LDvy;uK>l6<{=Zy+Id*%S5QWoE@2>2!f<`7!77xa5L93djJ(TjWF6E@%IRczr2i zD?rJfwg2$JriKp@%4(88ih`nTasV%BiEJ{L#!0nQ{TE#Izhh|s1xl+3mI&?aB^~{Z zak#&Yd~Ld|5Dmj=P=@2iN*pJ}2vnsKxL85cyb6b?U~!%MP+=3MiQhGn%ct~TC*JsO zYX%FR->Z~NXih)_l4?^g8dqj4;zz6Ik8BaB=IkGzxXhb;Xi_L z|8=hklP!%Xhp9*1mSJ*QiiC-K3nUlOO++DMfFj=v*4^GV-}l&oo#%116^<&i0DYv^ z{&?Dv`JZVp^5TH0x8L_c3oWAqR%DSOX#r-K1Jw zL#7E#hSw${H}Ms7xbEN|QFrMmoMq#T{4dTz|z~7ZSF~d z%JZ=u@#W-by*0r!gZ_Sj3jJq-6n@O#Wcgdk-(6uMq&#XnW45OkCWCrZGaZrBC#exn z-eDrpg-7V_U^<`bd8+jn(jUl^LQUP6$DRgLGR?YVOtPvDG^MdoWHeeR6xS$*(_h)9? z&tmZ|z90FZWDzl48-njf8oC`1Ee}Fy$Pa`%B~SUQpoBjmy?9H}fkc+IVO)WnKpes? zd<|}TB3Q6$sjoLt8`%3yhym}}+i1ER!t5Jd&E8=G6mE*~LH!jpB1q(z&6$M#W= zO{s+%9%ojo21qf~=0GQ|FEGeGxP7D=x1ZkoZyd#|dbV+SwDicEOs-o+Zr9bLJ-Cnq zPkVC1s3a3v=n^ray%YHlBvg*f`9m)ujeYbsk5mH?J?Dn{K_Amb3Hd7ARDrL_^RL4% zz_Ej9b$z+qul1~;Y`810s*ikuIwhJKb#0Ls)q5W??U5#9n4kEnhA<)x&%_YKCFOSc z);t`#pFF&AL{|(VbDx2i!0_*6@}`UxBWH68z`CQ-&JfaE$?#*RpZa!@v>oR{lyk+q z*E`FyHq0>g3@7h!#Y5@9T#{NIpGOo!*8q_g#R|L3PIVgO^h}w4l-UmoILUb5CBOr}@4L2v7 zj^R(?K12%%P>G5jmv){S6=1iY%f!g@Nh0043hln|7(d#+vgb7aE2Hmul* zSJh%BD5}2kdEN#uN&P8uvJ95OpL{t`pdNq>#j5?^<8SMrIT6==^hyvK=b~*V5|fP6 z3(9Ap4%1VZZIzNR_8T_~!q_i4Wa1)__ghv%8!Dl_aZsB9ET>K4pQ*WmapQbM!VklV1$Go%C{J ze6kPv!!O|7P&l%?rp5+~1G$2K1pc1((c`t-a5^p;pw8jx1V0pR1W$O_Cvfo!!1m-a zl#;+deUoUfVu(Pk*BUQJ`XG!m^>T092tg8v!e$qs?LleqsURZjd3twT7iGKf5$)Ob4vtdQ>pcQ-9aAK|z=tC>F!(WD;cuVMOR6$#hdeRpd2ToRW zdtCMt@s2o@J%;V~P#4Ti_lk0IxYeh|O8^)3y_~)G46Xz1(X8Rs_I$eO(WV3bt7{{; z9)*^VC-zjzz?o+bCiam#7AR+w2Mq$pq$YM>_%rr`G$;%tR66!?L2#AJ0 zhZC!fojADy0|C8N!Z(OE;Yyh(jbv5ZL1X#RVe##|T1Vno_;7H9Ctm8bTHaYC{=7A8 z-ft;LKgps@{h42Gg8u8QlZs3&u)I*gGzDDypdm?w_K_gAAy;3~^3c9s4%XmYCm;t) zRGhHnNU&%r`VL>N64(ZH3(uFPvU3MIz;x=o@jn9Sd69qi)V$XHG8Ak+^)*G+iU{5& zntwJyd(0{kin$!$3YMknjqm)RsaO<~yX~r|+BU-D@u|Ahh=xPt+d*9L!^&<1u*hKK@hMuK-`;wamgdW)> zQKC>G5+GnjftS_sx(h}Vf-9k1%MOd-B9LC5%1mM8b!*B7;Lz_;h(F8(DbN*7lcpi6 zk9IHO{V%04(5(N=cBxOx=Va2&a3O$*M0;L18k#7ifb+JT?NTV*)OYFW!xe3!l{D~l zVq7_rdbf9g5eox4MnK2dfU*1xJM8S;ZLb9`*jJa;8y^43=3jitLX^nPSBjMfwp2?W#aAay zox{d>&mOh|%ukD+1gNO%v=6rBJ_qoZ2SBn3*Uyskf8X!(I^4J!bdTbWvHeSwcCQ@8n(2-Jx4m|hZRrunyZpsLJEL6f$z2v7W>Xqk!#DTN5dg&fbkOI=U#@7} zd7vf#W~#aRnFg%{W^BjQWux*uTfM7bjPnMf97}099`Kkhi#HCGYz{q-QDefD81*`e z_*^r%;4ZCmD4P4?`1Yx2!X=J)v;K{E3{_m>1*%D*x8;w8=iQ%&l;IjrX$e88=Genk!YNAM(JDvMB{o?Lt@Ls1N~#%GkFiM*jUbC(&Xd#(6T7J-weyGv^m^_p5dS>2*F8gK{$N8ctOA zmB@)UKa%TLwm6<8h&GFpA`1SA|0N+6WB+u=6m-gJ6KaW zf6VF|H4$ia`&_jHM-K(xbs?3Z_O8;;k|Bz1&?Z}XJ$!WV4@z3Q-lyG%Fff+^8)gI1 zO9^m|NQPvE<;!gNPMcZHeicoua(NpC004Xib)LCyF^;f@G3%hTzX41|^C!(C58E08 zeD#%3O#V7k3xjI#s$;io;JMTF^{W~1RjT!z`B5&IFBR;odNQ!8-yB$apuTo`2)xhf z84AvFUNav-b?uoyEzIoSg!VUJ7xdzWm`LYdm1Z+8pv}MTVwx&=9GqGZCsf@0QtrWyxkiqO3|qZhnlrd_&?N z*k-kC715ZmOU~IpyzC6TN0D+|AtO9x9u`=MN2oT%iB#--$0Dvls>yx{U@_rOxDGxK zM*}bYW%;RAfCM48p7(6>J@?q^CdZ$~P`PtVinoj1rA;Zh=4P^pDf%}bF-Cgy6=wb3 zj#KwC=*upwXM(dZI%1(r!=G+=jMtti%pL%GlMA(8?92%PsoDiOarlJC1u}^?jVIza zq%A6S?^W5@ieSxz0q{x-9D!(lOI<6xW~6`s%4DRMg^*Z-g0#Rb{!27iFA~=-1ZSm2 zimZ+qAq?wG$5)o~k!ck351%>=TAxy4f=#5-dfDZRkN}0r7R8a(EySQc)ivKGDyME2 zk+!yOL;+y|002x4Mt5ymxz5Ga{B442$&Zc53yj7ib&t%ok3z_LE#r#5k;Yimvy4&i zcRa%CJ~z%WA$Yw?;hFGM3Vj;0lwn;9ZTrFdH8h8d&mRd+;i5-=Di~$Bsz!x;>l`dOVmbSHQ?(HPU~V-%H+O#YFD3 zmu{5r;AK^1x!B9JrG6O=Ed1EtOI6MR!T550cp>5yk$1Z*_&*0Bug0)9#M*#rn7a;(Ez~dg-HUIZ!1H9JzFZN>a zzw3klMIijI$`IK9sKxyIJO1wl+4BA&@sDo*TTu|2e+a_;B{cr`JxYG920#~q3rnJ8 z8i(urPiY`LivMS11^lPa|7F9rF91NCM%sq?Pb(`G)IY4OvaNA9u74MJ8LCY?SNVe0 z_2EP544^%|Rj2le%@3{7BRNI-@xgH??8-l?mA80I-T576x^MIl?~SEofi2?wAH&Nj zN+Z$O4szEErgQ+b&$fNwO2@vQ{&cqSq-{I}465rxU}2y*IUuM=i)ujp2qJ5u_WM2^ z&tsAl$0a*RmgC#UbJMp|+P#h!BXI>zvLFk*OW~pWh6rf$5{vbS9v6*4Bs78m$B$6D zZG>fW2(t`*_}wBzaN=7~3%+UdL$#HYSZl8F=-YhC5np1I$UEoX-@NXd47%5lDB}nS z4o67M3*^_`_EDD#5xT4-mE3t=PYY_i^bjV-31StU9W@Clh`9kqB$gQ6!N5$G(M-j^4Lv-NlBfaA))Qf>J=)AQ%NOY%ISz=2BG`SxxQs~MKF(GUZI z-<6F8Pd14xqh0u0G*jxb6H3Fc`UNKb-)iAx7PYP(V*u$BU(4YVcNFCA0Mt;8HHqY{ z9IY|G7RrI?k;G#sYco%-UdKR(3^Yp|AS1+^u<*HorN>=U6F#gB9u~Bgo!qxc?=i<8 z@lK8|Nr)MlFoA>XZ@}sGFY%rY;!PYb&erA7+~14I`_y_Ik(Z#`W=}?X6A5o~Lf&%E7{X`dX7|O!!V`4oTb&WEgW$P`!`w56m8KN7OLyqw(QB9iDaXch) z2^uNEfb^PIQz)a@wev&Dq2CAido08MA_2>I`J5nJ8hD`+ufDarrAQfOyt`OjsUmol zhQ4y0B1p^mu=r}idaRKD6phZq$7B?0Z*>FQE2i)ZL!_ljri;h}Eb(%PO7;nK09JQjL12S)fo-y^srapx&*uas=&NGc2n3HDqF>gE zY;ie2;sO56{)6TkggS?4>jz9e@!MFa(~m*0arekm)UAghrf0rCmXnrAWY*G50{m=6 zohOOjm3wpKVQp%RUzy)Y+p*RUu%`y3M-|18Qze$hQHZK^N7vPtv;An6rF&&TQ_HjI z0`q5YS#N*DFJadtXnrU?KVPyt?ae+A$~*n8>{D$dTtRNX^~c8`?eRX)h|DzO0B20# zbq{lOnH?b~g&cqjSH9ie<;O5YfZ6~Q4{hjUC^4ZdRe?(nvO(U`c(S5C%)HYA_qxH>W@PP-eC zfOiaL{zzt3<`of3%t4K~ZovJ0RAM5H%d8BDbrvy=z)agGIuba+jG$RkT7+EtjUqwN z9;V@`-@*;I5v^46(!32C$Y}1Bd6bqK9Hy{8npBcbKv=tDp7J}Qjx4x{HySL`nA##f zc5*uviyZf`;83t(RjeZ_r;gmOp7A>vv(lHmbN*O$E@E*bh5O`rf$rG56hFmK>idK! z$RHS*63Sk(_WsgV^H*RP%_7Z2izp82sfepiZmG3Js*YL_TY~d0g-H9a=8685aoABuMoL#Tv11tr|hq#PeK(% z+Kl7`KIgtO)ay*5T|2Q)1G(GV4cyHMu}=vR)$gaKCYH%izKcQD$Q6tEFLgd*;a_>W zz!h;CrH)J5bCxrwE4mkCe$>R}paMCW`6JV3=Iyj5AtPJjKm>6%l6A0r#;t!=F3kff zf!#-lCR0KPj9!3MtA;_|F<+i>}@_?=&z@gn+a_&jx z7E5GBb1lk53`u|3<&?+M?Cpvq`X^WCbU|F5+)Kn;6$4%&VbF zuAXvVnZy+cbbiH2&2SQTFPNp#L0>A^E{uX>z{sNWV)A#fauzXvF&RyOa~l9>5a+ zi-rw67Y9ox05Src@WJAQ!U2IAv@DWjrj5zv=J#|w=0gLJ)I{~R3^nV7{|Q>v+wd*J z-BnOUN3@Z*J#wj`wul-}n8#-_w81 zIiGvx{>;oZGuL&^az8!p$KqM|`+-~E!e{yQnJvo;PT9M}FMmNQ-Gkg#ZvI>#>WWYq;C9y3)V!B#1i3D4rEZxHy0SayN$Uw z-?XQX^r`${@WqOzlCh~p?L-AG3sy;r*CsUjcp2Yf@V;KJi+kaRFR~b)Y1&hKfy{q) zeQ#TPk*ii!t+SGx+NrM_V|s6?Q%$?(*6Pv!H(q@sZIBc0Hr;D~Gq%~+ z-tw;5uPr#j%A&qxUlBdpQo6(#%!|!^dxk99v`CHh>#5F&f?3I z%Jz0?wq+V-2;X1YK4A8yE_CcRjU)-J^14yL5t~q#qjUr}^dc;mjusFWFI=5ioM&@l zu!@dWaC06wSHw~)Xh&?ZTqmyn@yab<8l@q8wJBS3zejaa+g7KYsJZm+&$XX!ZXu8= z@sMoEWVEy2@HrS}#=5<-bpGI2pRjDW_xORf;;CAlhcxmhBDWQpSe3?FbFBuYB2-JS zXfdwXGz8IGE#EXID*MFWTo|NcHCb8op1=Kl*%sv(!9JWer@5vuO*YG+@#mTfOUKKA zxdg@CyTr5|dzzSU)#)S($SrpI$%xWD_i@fWBk=w#P1n}2iUEcFsH2e>w{&1d;BA5^ zFPGrJD{I|C=e1d%OBZd5W#tJuQ$VQf?X3@r_*;*=&?Q%_UCFO3AZAOxMb6 zIuz3$M7){ADH8FB&&-!uZ_t@H);Ws)`z9rxp&wN7=ctyUdgE?Y2oVmPd) zJo9&>PAM^A;4#GH5J!Xvq=4Z`I^3@P$16X zsa{+jx%X_RWbfSWrh0aF84vre;_WB?uUDwTr&X5-ONCy`uT47oa6RGrMhdPCGK%(x zzTc$Hn|cz}I9iv1XDGUNX6cEFl%&KI2~BgM4Iiwnjaso-h2ul9;T*nM{|| zQd#lj5Jjs_)@YjjHrKDc(|!E@I*oHOyS1u$#o1ZA6>0p$(N5inL6-b_d}j7HypM1m zQz|@#KhJoARkT=1I!+enQRb$2Iz9teqS4cnOrsVi6wMzA4cy7wH9`W!^McGmpQahk zE!A;2#@;uWJl4DD-4IBjHfM3Tj#)b+gVlQ(+Z4(2xH8dp+G^wT+P>*yeqUZl8J9;- z8^1}wi~C?TZaJxS$y`W?tip0_R~SdU3X2hEMRSZ`8Olj==lhWb22H+yEO#u>BZbNXj#{kNP?l5^QP8@^w>@Sbl; z`l4>dZMoCFTmtT4xaoHUc{e?tHKY(-*0rlDZ_UzseWU%`oJ~N=bigIiNH3|;KDPSh zTgQC+WhBknkHo#oOYGLC@-n>oDLZh&$8_UljlySoq1b+=p7u0+*)weODH-?QurU!E z&t{b9n36}f011=2%rhn)-}bDZW0~)vx#Okng~#s`EK^rfv{iHb`rghUU3MWnwd2*B zuPyhIInbozyKj%C<Z>=}9 zuBXZQ{EG=Lle7zd56M$U26#8~E|QElEOZGyIwCMxKeK;gkSu9CFy|OEZK}I=M?rT4 zjkv6&r5u6YC96xbo^G1$qlVZ*Zz`#MaG6&URnN$HQ!^;K;W5}*5W9UaZ1G=WNG@-d zVW*nbbbQwPrIEt6a~Sl=r3Cb{S@#~!%t`6nuXZ4Ga$7QE5es7}afetb9zN2KJ2#v(D9cvZ!~yb@8!p!s2{1 z+i7QStF~{XL+V5vCL9A7*lBHFMv9$nEW@}y+dOclWxshU~}Jna!O6sw}I}Yg6q}yM|(EjOIX#|s=jL!_Om}PDl=Ufm+QIjGBFlsP`r{| z?LqafwwS<#0nLys>YN(6yDn!$aK4@A8Nz?KwdBLI*qay45tcg~V&ZBgUolgka>0%_ zX9`=eWBNw6nkCWJtHkRUgucxoDXr%j`aG>)`E}8PSqB?|y9>nTp!K0SF0SAgY9+Tb zbMDd29=ch#J3dpq-8XcymcwK}6+WDpTwUW7LK7OPM*kuVH2Sdy1d8CEUL9 z@ryI-H_roV(xDI+OE1x9;Z2=Sd!maXj2tw}2ufHFS4BLdS$dSamixXy)w_|ci5wG4 zt)(;_D{*p(Gz`OLJRHkXsSqDUS6xn8 zaI8EIl%IJlrr|}CZdTNyqF|#?vpqp}zn-)on*sg>o?1pNW7wg)5zVvSx-+tDmn1w% z$g|j9TOGR}m3bjKO)TLWEgk7BGw+e|Qn{p_S;?VD#AKd-x{MB;T}v$u}$VMG~) zl}8=z1+OE{MV05yoS1LDQpO?cJtB&~{W@{*UOq_@mX&S&)!K;d&-h1m!VJvxM7D~! zN&MB;>GsWE=so3C7WV4N5e!b$M0@i}^KwvK-ah`O*n7QCsFm3&=pw}5<;?WB%N~vE z$J4%J#373Fl``hgvdez!`MR!yp3fsQS38OAc)h}84pq&oX?%?P+OJvZNvar=+n6Ip zbxc&)DJo?JbBG#LtzB!?`q~R0<(qNugeHVp9L6#Z*4;B2AbRP9wZDnFOc+*q`}yQ2 zp?zeSw{8eu?4gVsg!|7Wi!K(OF@Wz;_oCc3|CT)$pfgxvO9Gv+(lIOa6)Sx!1;goL zCgT;s*q2+*f_ekk6$eJ5DxWky&3m1HG4&RH&Rv6gK4-lErNPFd^g7|kf;Zwm49U>S zBNOg;!8aqC72?ct_10W(O_y{}3SN16bv16Na{7b70RL5&D^Kp%XI)5JE7Zhd&ZzR? zXiM5~+2#tVd|JbUTg^m*h~0=X`M6D9#O6beoe}dOs`#GzWW;_E6@N^LOq#TF*Fedr zOT--0n8f{PPbYfPPQ!`0k$bnL^tEnzO7u+QR-X$G?wGUN;`oebw;agNKw-VH6=eS` za~6xG{kZN>nz9M4$9Q=9yZ$RIQpUpsCp!iEW-d*tou;bFBl9_@HR~=ohBV+b7jNOc z#G7n9M3ye)T}DOpNM<(gURS*2L!8^Xf!H>iPmbu!5c{c@j`ZMQ%bO_s*YTIBOphnH zEeZ`a`GoUGH!wKM^bT4T91M>3w{x zdojYoot`(y(ueBRk#}zkcpg~bCeo*vWYJ$%UVWZEQ_Th{3F(#&is4!5?|-NO;QWX(_)jJ zIn!RfZRZUixsN=-in*iJu6`qwBeM^SyLdE|;$1eE+vHwO`RU7BD(XyMAD!7^_}DlQ z@WHt2ImXNU%e&>i+7Y2g-&}f*YK-UHG}5a#2(?+)X@5p2o%TNC)y;LxaB;?52KP6@ z%rA@{Vj6FTYDcOlIJ!A#>|V5u3``BmIV7BT%g@z|?RaK*t4unDnq`*r=|gIJ2Yewx zhmdPddbFeMhR?cEIi6RfeDzTEKUkL5t@3RA;~s&gGG{p{#*iC_${9t7y&dI!8HI|S zAKD%=5*!riT&z5OoHA3!Ense_*I)ZN|NcJjJJb~I=k{Js7r>HQ-$QA*l)G6|`$4PMN5A>0tHHyu=Mn9VP%jQSQJADJC)sxeH}Ss zdWTY@;ZLJA)jF3p#SeCj9U_XKwC__@O`9!$O&pZQE^)TS3Y zuCLn<=FJE1Bj?Fv^K&LRRw`~LR2Zr2QOxv|{^Pxk;A$D|qbxnXIL`}PkbGY0m_0XB z>-IN@5&6eOzSOR~ahsqjH7-oXUq*+Daf8CMZt>a8g5b_t|I_y@^>Y`*dy0}d6sh7n zW^;63$E5ImW+J^ysd;T?cr^$SPHL{I9+J7fz~zVcu6wW8Wdfd_p796lv|fd;C-HJb z4h`DyxsQ9fmE3Hj!YPXMVs?RVIsdi<5xssRK$_hKtzEbhAD7UGDpS(F_&r&!ZV^8^N z92!jVh~&tDM9ss&OsPubc6;*fbH2UG7}DxA#=0nMw@UWm(Z1Z8@Ow{vs;_2y2k2Mp zsA8mweIjgK?#sldvPSa?#`aiR?rpbvNNU=zetOURS;sF6Sa>0wGp;0VB3TcW#iB$c zIP&88>C206UD#Y)@eh*FOYXzT4v=-~xha`a%{xqfV&c6LgAaSzxzC>mKh!XS?ppjz z%9+fzkNJaoJ{wst+KwFzq@c5q8Zzoh!yjq0*I2u8sL6`LoFk+DdG@<630T1~i{wiQ z)Bc3H;tVgk-iQx%>r7o;5t+PxjWbZCVmgub9BX9x$m{~~zV?x}w!kVk^4SHmJ5oV6 zA}jW)=ifZ5IPlJHfhBz3vWT&wd{&g8=S}={&3+QrFh@m6hQu+gy`OGb%B8=Fj-e#P zkNu=@BSE=`>Z{}7qP>}~1`D>N=T%Y`tEQ!veZmM%oSmh1lVroPY!$$FtE??qF(kA$ zEZ!JP&K?c94!~3aQo`L^)Zy@Gd7Rd0y+!Lp^{~qD=oWXje5*XWcFYU^uQ!z21yZLT zKKi;fG#S5eCVil6fXmMmBnu{M_v6#(yu{X1(E&4WX+`o6UH2j@H)N}qy53(DIB{$2 zLE;#B_d&%yN@a45Lw(u{CXprkUI;{{__rmLl2yxvU9}rAX^Wq`eMUM!pcCvlb$Rt| z?0()NDS(K~7`b&^z<*~xaHL&HL_=Tn)us}+M&%X`C2n+QPji2ynki-d>ht?|w*%@9 z7?*M&f|HJ*V91u{RBKN2tQ7(GU@OIkz{7lEHwZ2qoi=zO*X?#jOPW9bOF>~Ljtz^- z*SUmXO5yR47-uEviWJXYs5h^<|I!m22&VHS~53HFTNbG?MgE7I{lsbGKr`f<_G4Bd$H2 z3Ypd&b7GQNZnBB3K~q#VgHu>Ylz++s|dercgo6s#v|pWg@`=qQV=pgllhP z#0iy3_GU(iR5LhTy`b^7(eqQy_;l`EirD3umfm8s(vVHk(+|YGn?%a5ejOVe(u#dt zv%b72@o;rYCcNd#`6gG_WWD5Uj;FqMM2b62CkT>YI+7eopKER8K_f2*>AGS<=|HM}+&hHfB5IMRlx!xQ9Y;SMpIkjgO+LBD(^I$E-8{Kc zB_M3p&i&2%_S+>Xiu@KVmHI*_l;hgP)iW3*mBoglV>M^&Ru+(>R6K;;zUNv+O3XWP z%T_~c^~WVrS0wmWnr{?b$8Tmlu}3kXd+&hXUL}5-cI3f}TThQja$+w|Z&$zM3qM(Q zs@jpiq3;AWz1ULzV#u3C#_M(;Rfc2+%*PMt@IT?6aTeCTC4Q8TwZU3Dd0h8UOJ%|~ z61~+RQe@`DCvoM)_;nH^?2W*~dopvw&zzNd@+ql$UB_6dlnUtWBe^Nh)n8pM1+miW5HKAG}s0sAL4zcW|t_78e05&(Hn?nGv^0=T;OrPVhkXl8N)?(V~X1YAHsclRPPA zmG3D*J*N!Wh>q#|zEfv}1I9bF%80qb7`R7gj+LYg-ELT`^%9vmiu77GFc1{Bt4yzK zV^n<4el*zZpl?6^=9X*n4f2a*4^-K6gaf)0T*RVd_eD8wN+w0FREG=>s=nLCmwtzD zy51*VjG>%1jW3|O%cy^3p|-@vgLLETid?jy*S#{DpJ4v#r$wDS$V2t+w)b|@kcF|MMm90sm44FWO6)I%z2gt z5g$5VjrqouExB4n9z69{ERUBitu6MsjjK^l?aL1r@w2m4_SiWukGOaXZ;0{w13KZ? zQRS`&s(LU@pDiNu*+!lAJlizpy+}lI+H&mp2erbwe4u`km5x)I4a53&oR@(_u6VVj zKzQ+_=D>Ze!LuucCMg|@$H)8LbH=rVoljA;u0ETV& z0i_oy&pGf&%3_g{R7A&0N4C_ZJ$j6NuFyK~zGpYLwDFQQ!PR_gOw=0x71g?$lVV){ z#8a|)`O8=eSYHjVx+=Vs>-Dtx(3sh2CcH)+NkU53bB#`!guyVQTW85TolWzQCpJ|u z$5N7p)|~eM=6rv&7x4w#`=_N07o;im3KOs_w?4D}`aaMPUkCchKhpW@Jr_S}9I8Cv zZ`gl~@&y1x!dbz9eg$heXPvs!R<=KX?(r3Kz4-8Rw2OBt{`K2*gu!By#Or!4_G1$F z_!C`V*S>$(j?xHCV_TtD9BTB6yN+H-c6de>G_8tWzz&Utjiw2A=z}|SCVIt^-AR8J ziWdL%|4-8Zv1Jmr_M+d1<{ZDZfE`V<=LY;*_rMQ9H17xMA0%9Vsr*N6{^WJf9kwXP zI--)a{ok)Jrv`o*?(YouBlpiS&c9UtqcVT;T!Ci5#jQ4lO3;2=z=NPwNI{#6bKX<}-{!;nhsXX9E z@}Fbef9d>3W&T_@|FsqWsPUiu$-T=jH~&%TKiAEFeNg+O#(%b(Ujl$!)Xu>HeMqyl zbGI`4*Dhd9!h9}(z$4}EjDNX-8GpH#-Cg#_H2jbY7yea`u?@^R=W4UlCglDIze^)) z`-AzO_)9x~*DhWF0N7$X6*$b{-%-O9gSo8>dbCCZlz_^`n)b0~<#!rIoH#nIWp%+1u?jK#!@ zMajXz5>hAZP5EF!oGm+Rd-#e;K<#d~)7ahUy<$6)4cj+Q|KnosQJY|HXn8t0jd ztBEa4ceuE^d^Z(Zrquk8z9IxrnL5}y+nd^Y!dB4# zU?6uic5#F;dV_-6#MSvXdyvV)H=N)*L5|kNb;njPxl_h?$3_6~z=5JRL<1fKT3;7J z4?-Wp0OBmfIhb|E^iLTFAP37rJMY9~xiJ3jW03z}-BbQUa{jmN{)F;GPoN$!5NN>t z_v6~p+X1M1)8C5j)Peo^A%+~149j`{UJi|n0bmM)=9+dMDNJX>cHhAm4i5{KMOGkgz&to%`Mc0>J2dBRT|?g5|(SErMe z0BCi>>2(QCscHa>F2XJ0D*(>W#%?hHc=rJi#0$WcHUO?(gIyZ|AQ|dA4aPU2j&H+$ z7UTnPFA#vz^8i#pT|R^3tAT#dkOIIfB>>)B2A~hNGX?8?ssUgHj&F+{AXMQ1VTc0= z=P*Eo4gf?xA0Vo+0MUO2U4sN5UPJ&1f=fyS%#Yg-kQ7ybWcC2$HjE4D08*+9kjH`m zsX7Rd=S%=;;swYnT!3_#0i>q^Anz{%Wate*#*6?m9RrYsAZW6~09ls>$d(gyqn7}K zD-SS)N$~p`763y=4NWKv-OV0gX!8MPpESTQ1p*A)UV!0@1(*Xp0CVsf!0^I)TA!g= zCjjO?%xm=p7-$yE>N<264uIKG1(%q9z0 z7^qZqJ}zzJK!%)hrxMEk7H}bnp!WE4pZdKx!(J%DQjq>+VPYvloyfIwxSG0yKX zn#P6r&J*t9;o9{FPqvFk`}Vy&^)4PA#dn@=7f-*7XWGRx@8Z$*1?>w8j$Qosexp41 zE*{SLKgvTt{-a&NUA)jP{>Uy~bQcfT{6FMK?&4*4@p8L(bWQu-PsLq4T-*NOPwnE> zcJXI+@mjlh?OnY7F5X}l4_{jLhkSJH{jO&tn8y1p4>RzWJrDW;0-X(U{SV?RuuKL- z5yTycJcvSw+Yq-PG9j{erQyDUBtQg0!1Ww~YbyfJKRfNAZNmKnfqH-kHGrIkP=|1b zfNL*e4gtr!6WhW#00OT0h%q0I2;3i# zAPBTQ6!83pI6_!Jp!IDa{2dny&+43W4%xAy7S`veEItdgz=- zps($;`=9#A|5Oeg!~ay~zs>*4xq%D%;z1k$c%e=Opx;WuwDbc2ONUeIHJo;`0J+}^kcZO%c>>QD zHSpZfPymo-Hh{F<1jw6Bfb>TLi<|%y92vD}d*Qd4S<&gXe}3fYBfV7&uf+ zA5gBF68*;&1e-OOB}M*@92^PLzb0qgFkSa=F>HgCAST Loading cast… No cast information is available. + CAST PROFILE + Loading biography and filmography… + No biography is available. + FILMOGRAPHY + No films or series are available. + BACK · CAST + Deceased SUBTITLES TEXT SIZE GET SUBTITLES diff --git a/app/src/test/java/com/ponzischeme89/memby/data/CastMetadataTest.kt b/app/src/test/java/com/ponzischeme89/memby/data/CastMetadataTest.kt index 31e612c..dcad6b3 100644 --- a/app/src/test/java/com/ponzischeme89/memby/data/CastMetadataTest.kt +++ b/app/src/test/java/com/ponzischeme89/memby/data/CastMetadataTest.kt @@ -97,5 +97,25 @@ class CastMetadataTest { assertEquals("portrait-tag", item.cast.single().primaryImageTag) } + @Test + fun `decodes person biography and life dates`() { + val person = Json { ignoreUnknownKeys = true }.decodeFromString( + """ + { + "Id":"person-1", + "Name":"Alex Actor", + "Type":"Person", + "Overview":"A stage and screen performer.", + "PremiereDate":"1940-01-02T00:00:00.0000000Z", + "EndDate":"2020-03-04T00:00:00.0000000Z" + } + """.trimIndent(), + ) + + assertEquals("A stage and screen performer.", person.overview) + assertEquals("1940-01-02T00:00:00.0000000Z", person.premiereDate) + assertEquals("2020-03-04T00:00:00.0000000Z", person.endDate) + } + private fun actor(id: String) = EmbyPerson(id = id, name = id, type = "Actor") } diff --git a/app/src/test/java/com/ponzischeme89/memby/data/DevicePlaybackCapabilitiesTest.kt b/app/src/test/java/com/ponzischeme89/memby/data/DevicePlaybackCapabilitiesTest.kt index c3d0c55..3395534 100644 --- a/app/src/test/java/com/ponzischeme89/memby/data/DevicePlaybackCapabilitiesTest.kt +++ b/app/src/test/java/com/ponzischeme89/memby/data/DevicePlaybackCapabilitiesTest.kt @@ -3,6 +3,9 @@ package com.ponzischeme89.memby.data import com.ponzischeme89.memby.data.playback.DevicePlaybackCapabilities import com.ponzischeme89.memby.data.playback.VideoDecoderCapabilities import com.ponzischeme89.memby.data.playback.gatewayCapabilityTokens +import com.ponzischeme89.memby.data.model.DeviceProfile +import com.ponzischeme89.memby.data.model.h264TranscodeFallback +import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test @@ -45,4 +48,14 @@ class DevicePlaybackCapabilitiesTest { assertTrue("video_h264_decode" in tokens) assertTrue("video_hevc_decode" !in tokens) } + + @Test + fun decoderFallbackCannotReturnHevcStreamCopy() { + val fallback = DeviceProfile.embyAndroidTv(supportsHevc = true) + .h264TranscodeFallback() + + assertTrue(fallback.directPlayProfiles.isEmpty()) + assertEquals(listOf("h264"), fallback.transcodingProfiles.map { it.videoCodec }) + assertTrue(fallback.codecProfiles.none { it.codec.equals("hevc", ignoreCase = true) }) + } } diff --git a/app/src/test/java/com/ponzischeme89/memby/ui/ContinueWatchingResumeTest.kt b/app/src/test/java/com/ponzischeme89/memby/ui/ContinueWatchingResumeTest.kt new file mode 100644 index 0000000..9729cde --- /dev/null +++ b/app/src/test/java/com/ponzischeme89/memby/ui/ContinueWatchingResumeTest.kt @@ -0,0 +1,44 @@ +package com.ponzischeme89.memby.ui + +import com.ponzischeme89.memby.data.launchResumePositionMs +import com.ponzischeme89.memby.data.model.BaseItem +import com.ponzischeme89.memby.data.model.UserItemData +import com.ponzischeme89.memby.ui.detail.primaryActionLabel +import org.junit.Assert.assertEquals +import org.junit.Test + +class ContinueWatchingResumeTest { + @Test + fun `detail metadata keeps the continue watching playhead and resume label`() { + val card = BaseItem( + id = "episode", + name = "The Episode", + type = "Episode", + indexNumber = 3, + parentIndexNumber = 1, + userData = UserItemData(playbackPositionTicks = 36_000_000L), + ) + val detailsWithoutUserData = card.copy( + overview = "The richer record fetched after focus.", + userData = null, + ) + + val focused = focusedItemWithMetadata(card, detailsWithoutUserData) + + assertEquals(36_000_000L, focused.userData?.playbackPositionTicks) + assertEquals("Resume S01E03", primaryActionLabel(focused)) + assertEquals("The richer record fetched after focus.", focused.overview) + } + + @Test + fun `pressed card resume point outranks a prefetched zero`() { + assertEquals( + 36_000L, + launchResumePositionMs(resolvedPositionMs = 0L, requestedPositionMs = 36_000L), + ) + assertEquals( + 42_000L, + launchResumePositionMs(resolvedPositionMs = 42_000L, requestedPositionMs = 0L), + ) + } +} diff --git a/app/src/test/java/com/ponzischeme89/memby/ui/alerts/AlertsFormatTest.kt b/app/src/test/java/com/ponzischeme89/memby/ui/alerts/AlertsFormatTest.kt index 6e26d0a..7313d0b 100644 --- a/app/src/test/java/com/ponzischeme89/memby/ui/alerts/AlertsFormatTest.kt +++ b/app/src/test/java/com/ponzischeme89/memby/ui/alerts/AlertsFormatTest.kt @@ -5,6 +5,13 @@ import org.junit.Assert.assertNull import org.junit.Test class AlertsFormatTest { + @Test + fun `dismissed alert hands focus to a stable neighbour`() { + assertEquals(1, alertFocusIndexAfterRemoval(removedIndex = 1, remainingCount = 3)) + assertEquals(2, alertFocusIndexAfterRemoval(removedIndex = 3, remainingCount = 3)) + assertNull(alertFocusIndexAfterRemoval(removedIndex = 0, remainingCount = 0)) + } + @Test fun `no alerts wears no badge`() { assertNull(alertBadgeLabel(0)) diff --git a/app/src/test/java/com/ponzischeme89/memby/ui/player/CastPanelScreenshotTest.kt b/app/src/test/java/com/ponzischeme89/memby/ui/player/CastPanelScreenshotTest.kt index d714c3a..117ddf8 100644 --- a/app/src/test/java/com/ponzischeme89/memby/ui/player/CastPanelScreenshotTest.kt +++ b/app/src/test/java/com/ponzischeme89/memby/ui/player/CastPanelScreenshotTest.kt @@ -43,7 +43,11 @@ class CastPanelScreenshotTest { title = "The Lives of Others", loaded = true, members = listOf( - CastMember("Ulrich Mühe", "Hauptmann Gerd Wiesler"), + CastMember( + "Ulrich Mühe", + "Hauptmann Gerd Wiesler", + deathDate = "2007-07-22T00:00:00.0000000Z", + ), CastMember("Martina Gedeck", "Christa-Maria Sieland"), CastMember("Sebastian Koch", "Georg Dreyman"), CastMember("Ulrich Tukur", "Oberstleutnant Anton Grubitz"), @@ -94,6 +98,33 @@ class CastPanelScreenshotTest { ) } + @Test + fun `selected actor shows biography and filmography`() { + capture( + name = "cast-panel-person-profile", + state = CastPanelState( + title = "The Lives of Others", + loaded = true, + members = listOf(CastMember("Ulrich Mühe", id = "person-1")), + selectedPerson = CastPersonPanel( + id = "person-1", + name = "Ulrich Mühe", + role = "Hauptmann Gerd Wiesler", + birthDate = "1953-06-20T00:00:00.0000000Z", + deathDate = "2007-07-22T00:00:00.0000000Z", + overview = "A celebrated German actor known for quiet, exacting performances on stage and screen. His portrayal of Gerd Wiesler earned international acclaim.", + filmography = listOf( + FilmographyCredit("The Lives of Others", 2006), + FilmographyCredit("Funny Games", 1997), + FilmographyCredit("The Castle", 1997), + FilmographyCredit("Benny's Video", 1992), + ), + loaded = true, + ), + ), + ) + } + /** Still fetching. The panel opens instantly and says so rather than showing nothing. */ @Test fun `still loading`() { @@ -118,6 +149,10 @@ class CastPanelScreenshotTest { assertEquals("C", castInitials("Cher")) assertEquals("AA", castInitials(" amy adams ")) assertEquals("", castInitials(" ")) + assertEquals( + "20 June 1953 – 22 July 2007", + personLifeDates("1953-06-20T00:00:00Z", "2007-07-22T00:00:00Z"), + ) } private fun capture(name: String, state: CastPanelState, focused: Int? = null) { diff --git a/app/src/test/java/com/ponzischeme89/memby/ui/player/PlaybackRecoveryTest.kt b/app/src/test/java/com/ponzischeme89/memby/ui/player/PlaybackRecoveryTest.kt index 9d1331e..82cee08 100644 --- a/app/src/test/java/com/ponzischeme89/memby/ui/player/PlaybackRecoveryTest.kt +++ b/app/src/test/java/com/ponzischeme89/memby/ui/player/PlaybackRecoveryTest.kt @@ -53,4 +53,13 @@ class PlaybackRecoveryTest { assertEquals(3_000L, automaticRetryDelayMs(2)) assertNull(automaticRetryDelayMs(3)) } + + @Test + fun prolongedMidProgrammeRebufferGetsOneCompatibilityFallback() { + assertTrue(shouldRecoverProlongedRebuffer(true, false, false, false)) + assertFalse(shouldRecoverProlongedRebuffer(false, false, false, false)) + assertFalse(shouldRecoverProlongedRebuffer(true, true, false, false)) + assertFalse(shouldRecoverProlongedRebuffer(true, false, true, false)) + assertFalse(shouldRecoverProlongedRebuffer(true, false, false, true)) + } } diff --git a/app/src/test/java/com/ponzischeme89/memby/ui/player/PrerollSequenceTest.kt b/app/src/test/java/com/ponzischeme89/memby/ui/player/PrerollSequenceTest.kt index cdaa303..9155cb5 100644 --- a/app/src/test/java/com/ponzischeme89/memby/ui/player/PrerollSequenceTest.kt +++ b/app/src/test/java/com/ponzischeme89/memby/ui/player/PrerollSequenceTest.kt @@ -1,5 +1,7 @@ package com.ponzischeme89.memby.ui.player +import androidx.media3.common.C +import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test @@ -40,4 +42,11 @@ class PrerollSequenceTest { assertFalse(prerollCountdownAdvances(lifecycleStarted = false, playbackReady = true)) assertFalse(prerollCountdownAdvances(lifecycleStarted = true, playbackReady = false)) } + + @Test + fun `packaged clip duration replaces the configured fallback`() { + assertEquals(4_133L, effectivePrerollDurationMs(4_133L, 7_000L)) + assertEquals(7_000L, effectivePrerollDurationMs(C.TIME_UNSET, 7_000L)) + assertEquals(7_000L, effectivePrerollDurationMs(0L, 7_000L)) + } } diff --git a/bugs-0908.md b/bugs-0908.md index b567c5f..3fda2a9 100644 --- a/bugs-0908.md +++ b/bugs-0908.md @@ -18,20 +18,21 @@ focus moves predictably, and whether destructive or app-leaving actions are reco dismisses the confirmation, and Left/Right is explicitly constrained between the two choices. The setting defaults off and is stored per television. -## Open findings +## Fixed locally after 0.2.39 — not published ### NAV-002 — Recommendation onboarding has no deterministic entry focus or Back route - Severity: High - Surface: First-time recommendation onboarding - Reproduction: Sign in with a profile that has been prompted for taste onboarding. -- Finding: The screen creates focusable title/person cards and footer buttons but does not - request initial focus. It also has no `BackHandler`. Focus therefore depends on Compose's - geometric fallback, while Back can leave the activity instead of moving to a defined +- Previous behaviour: The screen created focusable title/person cards and footer buttons but did not + request initial focus. It also had no `BackHandler`. Focus therefore depended on Compose's + geometric fallback, while Back could leave the activity instead of moving to a defined previous step or presenting a skip choice. -- Recommended fix: Give the first card (or **Next** when the stage is empty) an explicit - entry requester. Make Back move to the previous stage; on the first stage, focus a clear - **Skip for now** confirmation instead of closing the app. +- Resolution: The first choice now receives focus explicitly; an empty stage focuses its + primary footer action. Back moves to the previous stage, while Back on the first stage + opens a Cancel-first **Skip for now** confirmation. Skipping returns to Home for this + visit without falsely recording onboarding as complete. ### NAV-003 — Manage users does not scale beyond one fixed row @@ -39,10 +40,11 @@ focus moves predictably, and whether destructive or app-leaving actions are reco - Surface: Manage users / profile chooser - Reproduction: Save enough profiles that the fixed 154 dp tiles plus 24 dp gaps exceed the viewport width. -- Finding: Profiles are rendered in a plain `Row`, not a `LazyRow`, and do not wrap or +- Previous behaviour: Profiles were rendered in a plain `Row`, not a `LazyRow`, and did not wrap or scroll. Profiles beyond the right edge can become invisible or unreachable by D-pad. -- Recommended fix: Use a centred `LazyRow` with content padding, stable keys and remembered - horizontal state. Keep **Add another user** as the final item. +- Resolution: Profiles now use a padded `LazyRow` with stable profile keys, so focus brings + off-screen profiles into view. **Add another user** remains the final item and has + explicit neighbours. ### NAV-004 — Profile removal targets rely on geometric focus search @@ -50,38 +52,41 @@ focus moves predictably, and whether destructive or app-leaving actions are reco - Surface: Manage users / profile chooser - Reproduction: Move around a profile tile and its small × removal control using only the D-pad. -- Finding: Each tile and its overlaid removal button are independent focus targets with no +- Previous behaviour: Each tile and its overlaid removal button were independent focus targets with no explicit direction mapping. Depending on card position and screen density, Up/Right can select a different profile or skip the removal target. The 28 dp target is also difficult to identify at TV distance. -- Recommended fix: Put profile actions in a deterministic vertical group: OK opens the - profile and a labelled **Remove user** action sits below it, with explicit Up/Down and - Left/Right neighbours. Retain the existing safe Cancel-first confirmation. +- Resolution: Every profile is now a deterministic two-action column: the profile tile and + a full-width, labelled **Remove user** action. Explicit Up/Down and Left/Right mappings + connect peer actions, **Add another user**, and **Back to Memby**. The existing + Cancel-first removal confirmation remains. ### NAV-005 — Dismissing an alert can leave focus without a stable successor - Severity: Medium - Surface: My Alerts - Reproduction: Focus an alert in the middle of the list and press OK to dismiss it. -- Finding: The focused keyed row is removed immediately, but focus is only requested when +- Previous behaviour: The focused keyed row was removed immediately, but focus was only requested when the list changes between empty and non-empty. There is no requester for the next or - previous alert after an individual removal, so focus restoration is left to framework - behaviour and can appear to vanish on some Compose/TV combinations. -- Recommended fix: Track the focused alert id/index and, after removal, request the item now - occupying that index (or the previous item). When the list becomes empty, move focus to - the alert controls explicitly. + previous alert after an individual removal, so focus restoration was left to framework + behaviour and could appear to vanish on some Compose/TV combinations. +- Resolution: The page records the dismissed row index, scrolls the remaining list to the + row now occupying that position, and requests it after composition. Removing the final + alert explicitly returns focus to the alert controls. The index rule is unit-tested. ### NAV-006 — Onboarding stage changes do not preserve a clear focus destination - Severity: Medium - Surface: Recommendation onboarding - Reproduction: Focus **Next**, advance a stage, then continue with the D-pad. -- Finding: The focused footer node is reused while the card row above is replaced. The +- Previous behaviour: The focused footer node was reused while the card row above was replaced. The viewer is left at the bottom of each new question and must navigate geometrically back to the new choices; there is no explicit Up target or per-stage return position. -- Recommended fix: On each stage change, move focus to the first choice and remember the - last focused choice per stage. Give the footer buttons explicit Up targets and the card - row an explicit Down target. +- Resolution: Every stage change now places focus on the first choice, making the new + question immediately actionable. Choice cards explicitly move Down to the primary footer + action; footer actions explicitly move Up to the choices and Left/Right between each + other. This deliberately resets to the first choice rather than leaving the remote on a + stale footer node from the previous question. ## Areas checked with no blocking issue found diff --git a/server/internal/api/api.go b/server/internal/api/api.go index 0a04e43..5f1cc3b 100644 --- a/server/internal/api/api.go +++ b/server/internal/api/api.go @@ -168,6 +168,8 @@ func (s *Server) Routes() http.Handler { v1.Handle("PUT /v1/preferences", s.authed(s.handlePreferences)) v1.Handle("GET /v1/items/{id}", s.authed(s.handleItem)) + v1.Handle("GET /v1/people/{id}", s.authed(s.handlePerson)) + v1.Handle("GET /v1/people/{id}/filmography", s.authed(s.handlePersonFilmography)) v1.Handle("GET /v1/items/{id}/ratings", s.authed(s.handleMovieRatings)) v1.Handle("GET /v1/items/{id}/season-finale", s.authed(s.handleSeasonFinale)) v1.Handle("GET /v1/items/{id}/episodes", s.authed(s.handleSeriesEpisodes)) diff --git a/server/internal/api/people.go b/server/internal/api/people.go new file mode 100644 index 0000000..a733ff2 --- /dev/null +++ b/server/internal/api/people.go @@ -0,0 +1,94 @@ +package api + +import ( + "encoding/json" + "net/http" + "net/url" + + "github.com/ponzischeme89/memby/server/internal/cache" + "github.com/ponzischeme89/memby/server/internal/store" +) + +type personFilmographyResponse struct { + Items []json.RawMessage `json:"items"` +} + +// handlePerson returns Emby's person item. PremiereDate and EndDate are the person's +// birth and death dates; Overview is their biography. +func (s *Server) handlePerson(w http.ResponseWriter, r *http.Request, sess store.Session) { + ctx := r.Context() + personID := r.PathValue("id") + if personID == "" { + writeError(w, http.StatusBadRequest, "person id is required") + return + } + key := cache.UserKey(sess.EmbyUserID, "person:v1:"+personID) + if raw, err := s.cache.Get(ctx, key); err == nil { + w.Header().Set("X-Memby-Cache", "hit") + writeRaw(w, http.StatusOK, raw) + return + } + person, err := s.emby.Item( + ctx, + credentials(sess), + personID, + "Overview,Genres,PrimaryImageAspectRatio", + ) + if err != nil { + s.writeUpstreamError(ctx, w, err, "could not load the person") + return + } + if err := s.cache.Set(ctx, key, person, s.cfg.ItemTTL); err != nil { + s.loggerFor(ctx).Warn("person cache write failed", "error", err) + } + w.Header().Set("X-Memby-Cache", "miss") + writeRaw(w, http.StatusOK, person) +} + +// Filmography is kept separate from the biography so life dates can decorate the cast +// row without also loading every cast member's credits. +func (s *Server) handlePersonFilmography(w http.ResponseWriter, r *http.Request, sess store.Session) { + ctx := r.Context() + personID := r.PathValue("id") + if personID == "" { + writeError(w, http.StatusBadRequest, "person id is required") + return + } + key := cache.UserKey(sess.EmbyUserID, "person-filmography:v1:"+personID) + if raw, err := s.cache.Get(ctx, key); err == nil { + w.Header().Set("X-Memby-Cache", "hit") + writeRaw(w, http.StatusOK, raw) + return + } + result, err := s.emby.Items(ctx, credentials(sess), url.Values{ + "PersonIds": {personID}, + "IncludeItemTypes": {"Movie,Series"}, + "Recursive": {"true"}, + "SortBy": {"ProductionYear,SortName"}, + "SortOrder": {"Descending"}, + "Limit": {"60"}, + "Fields": {"Overview,ProductionYear,PrimaryImageAspectRatio"}, + "EnableImages": {"true"}, + "EnableImageTypes": {"Primary"}, + "ImageTypeLimit": {"1"}, + "EnableUserData": {"true"}, + }) + if err != nil { + s.writeUpstreamError(ctx, w, err, "could not load the person's filmography") + return + } + items := result.Items + if items == nil { + items = []json.RawMessage{} + } + body, err := json.Marshal(personFilmographyResponse{Items: items}) + if err != nil { + writeError(w, http.StatusInternalServerError, "could not encode the filmography") + return + } + if err := s.cache.Set(ctx, key, body, s.cfg.ItemTTL); err != nil { + s.loggerFor(ctx).Warn("person filmography cache write failed", "error", err) + } + w.Header().Set("X-Memby-Cache", "miss") + writeRaw(w, http.StatusOK, body) +} diff --git a/server/internal/emby/client.go b/server/internal/emby/client.go index 9643bb1..0796b49 100644 --- a/server/internal/emby/client.go +++ b/server/internal/emby/client.go @@ -297,12 +297,16 @@ func (c *Client) PlaybackInfo( "UserId": {cred.UserID}, "IsPlayback": {"true"}, } + profile := androidTVDeviceProfile(capabilities) + if forceTranscode { + forceH264TranscodeProfile(profile) + } body, err := json.Marshal(map[string]any{ "Id": itemID, "UserId": cred.UserID, "IsPlayback": true, "StartTimeTicks": startTicks, "EnableDirectPlay": !forceTranscode, "EnableDirectStream": !forceTranscode, "EnableTranscoding": true, - "AllowVideoStreamCopy": true, "AllowAudioStreamCopy": true, - "DeviceProfile": androidTVDeviceProfile(capabilities), + "AllowVideoStreamCopy": !forceTranscode, "AllowAudioStreamCopy": true, + "DeviceProfile": profile, }) var requestBody map[string]any if err == nil { @@ -314,10 +318,6 @@ func (c *Client) PlaybackInfo( if currentPlaySessionID != "" { requestBody["CurrentPlaySessionId"] = currentPlaySessionID } - if forceTranscode { - profile := requestBody["DeviceProfile"].(map[string]any) - profile["DirectPlayProfiles"] = []map[string]string{} - } if err == nil { body, err = json.Marshal(requestBody) } diff --git a/server/internal/emby/client_playback_test.go b/server/internal/emby/client_playback_test.go index 24ef8a0..e1a272c 100644 --- a/server/internal/emby/client_playback_test.go +++ b/server/internal/emby/client_playback_test.go @@ -32,3 +32,23 @@ func TestAndroidTVProfileConstrainsCodecLevelAndResolution(t *testing.T) { t.Fatalf("codec profiles = %#v", codecProfiles) } } + +func TestForcedTranscodeProfileCannotStreamCopyHEVC(t *testing.T) { + profile := androidTVDeviceProfile(PlaybackCapabilities{ + HEVC: true, HEVCMain: true, HEVCMain10: true, + }) + forceH264TranscodeProfile(profile) + + if direct := profile["DirectPlayProfiles"].([]map[string]string); len(direct) != 0 { + t.Fatalf("direct profiles = %#v", direct) + } + transcode := profile["TranscodingProfiles"].([]map[string]string) + if len(transcode) != 1 || transcode[0]["VideoCodec"] != "h264" { + t.Fatalf("transcoding profiles = %#v", transcode) + } + for _, codecProfile := range profile["CodecProfiles"].([]map[string]any) { + if codecProfile["Codec"] == "hevc" { + t.Fatalf("HEVC constraint survived fallback: %#v", codecProfile) + } + } +} diff --git a/server/internal/emby/device_profile.go b/server/internal/emby/device_profile.go index f9aa379..901a865 100644 --- a/server/internal/emby/device_profile.go +++ b/server/internal/emby/device_profile.go @@ -53,6 +53,27 @@ func androidTVDeviceProfile(capabilities PlaybackCapabilities) map[string]any { } } +// forceH264TranscodeProfile turns decoder recovery into an actual codec change. Merely +// removing DirectPlayProfiles is insufficient: Emby may otherwise stream-copy HEVC into +// the ordinary HLS transcoding profile and hand the failing decoder the same video again. +func forceH264TranscodeProfile(profile map[string]any) { + profile["DirectPlayProfiles"] = []map[string]string{} + profile["TranscodingProfiles"] = []map[string]string{ + { + "Container": "ts", "VideoCodec": "h264", "AudioCodec": "aac", + "Protocol": "hls", "Type": "Video", "Context": "Streaming", + }, + } + profiles, _ := profile["CodecProfiles"].([]map[string]any) + h264Profiles := make([]map[string]any, 0, len(profiles)) + for _, codecProfile := range profiles { + if codec, _ := codecProfile["Codec"].(string); codec == "h264" { + h264Profiles = append(h264Profiles, codecProfile) + } + } + profile["CodecProfiles"] = h264Profiles +} + func androidTVSubtitleProfiles() []map[string]string { return []map[string]string{ {"Format": "srt", "Method": "External"},