From bbba1bfe8cdbe9376c1f552f8cb44cbadcc0ee8a Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Thu, 4 Dec 2025 08:45:58 +0530 Subject: [PATCH] fix: use adjustment time in iOS for hash reset (#24047) * use adjustment time in iOS for hash reset * migration * fix equals check --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../app/alextran/immich/sync/Messages.g.kt | 13 ++++- .../alextran/immich/sync/MessagesImplBase.kt | 1 - .../drift_schemas/main/drift_schema_v14.json | Bin 0 -> 41190 bytes mobile/ios/Runner/Sync/Messages.g.swift | 14 ++++- .../ios/Runner/Sync/PHAssetExtensions.swift | 12 +++- .../models/asset/local_asset.model.dart | 34 ++++++++++- .../domain/services/local_sync.service.dart | 19 +++++- .../entities/local_asset.entity.dart | 9 +++ .../entities/local_asset.entity.drift.dart | Bin 33157 -> 40771 bytes .../repositories/db.repository.dart | 9 ++- .../repositories/db.repository.steps.dart | Bin 161418 -> 174748 bytes .../repositories/local_album.repository.dart | 54 +++++++++++++++++- mobile/lib/platform/native_sync_api.g.dart | Bin 20432 -> 20806 bytes .../pages/drift_asset_troubleshoot.page.dart | 10 ++++ mobile/lib/utils/migration.dart | 9 ++- mobile/pigeon/native_sync_api.dart | 7 +++ mobile/test/drift/main/generated/schema.dart | Bin 1582 -> 1672 bytes .../test/drift/main/generated/schema_v14.dart | Bin 0 -> 256358 bytes 18 files changed, 176 insertions(+), 15 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v14.json create mode 100644 mobile/test/drift/main/generated/schema_v14.dart diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index e6cf92f57..d3282f4df 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -89,7 +89,10 @@ data class PlatformAsset ( val height: Long? = null, val durationInSeconds: Long, val orientation: Long, - val isFavorite: Boolean + val isFavorite: Boolean, + val adjustmentTime: Long? = null, + val latitude: Double? = null, + val longitude: Double? = null ) { companion object { @@ -104,7 +107,10 @@ data class PlatformAsset ( val durationInSeconds = pigeonVar_list[7] as Long val orientation = pigeonVar_list[8] as Long val isFavorite = pigeonVar_list[9] as Boolean - return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite) + val adjustmentTime = pigeonVar_list[10] as Long? + val latitude = pigeonVar_list[11] as Double? + val longitude = pigeonVar_list[12] as Double? + return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite, adjustmentTime, latitude, longitude) } } fun toList(): List { @@ -119,6 +125,9 @@ data class PlatformAsset ( durationInSeconds, orientation, isFavorite, + adjustmentTime, + latitude, + longitude, ) } override fun equals(other: Any?): Boolean { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index b1e9dd7d4..b374ef50f 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.content.ContentUris import android.content.Context import android.database.Cursor -import android.net.Uri import android.os.Bundle import android.provider.MediaStore import android.util.Base64 diff --git a/mobile/drift_schemas/main/drift_schema_v14.json b/mobile/drift_schemas/main/drift_schema_v14.json new file mode 100644 index 0000000000000000000000000000000000000000..40a1af84236f5db12b259ac7e4a8024386778014 GIT binary patch literal 41190 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 Jrj35S|37B-{q_I= literal 0 HcmV?d00001 diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index bbe18e737..c1cc98014 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -140,6 +140,9 @@ struct PlatformAsset: Hashable { var durationInSeconds: Int64 var orientation: Int64 var isFavorite: Bool + var adjustmentTime: Int64? = nil + var latitude: Double? = nil + var longitude: Double? = nil // swift-format-ignore: AlwaysUseLowerCamelCase @@ -154,6 +157,9 @@ struct PlatformAsset: Hashable { let durationInSeconds = pigeonVar_list[7] as! Int64 let orientation = pigeonVar_list[8] as! Int64 let isFavorite = pigeonVar_list[9] as! Bool + let adjustmentTime: Int64? = nilOrValue(pigeonVar_list[10]) + let latitude: Double? = nilOrValue(pigeonVar_list[11]) + let longitude: Double? = nilOrValue(pigeonVar_list[12]) return PlatformAsset( id: id, @@ -165,7 +171,10 @@ struct PlatformAsset: Hashable { height: height, durationInSeconds: durationInSeconds, orientation: orientation, - isFavorite: isFavorite + isFavorite: isFavorite, + adjustmentTime: adjustmentTime, + latitude: latitude, + longitude: longitude ) } func toList() -> [Any?] { @@ -180,6 +189,9 @@ struct PlatformAsset: Hashable { durationInSeconds, orientation, isFavorite, + adjustmentTime, + latitude, + longitude, ] } static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool { diff --git a/mobile/ios/Runner/Sync/PHAssetExtensions.swift b/mobile/ios/Runner/Sync/PHAssetExtensions.swift index 2b1ef6ac8..f555d75bd 100644 --- a/mobile/ios/Runner/Sync/PHAssetExtensions.swift +++ b/mobile/ios/Runner/Sync/PHAssetExtensions.swift @@ -12,7 +12,10 @@ extension PHAsset { height: Int64(pixelHeight), durationInSeconds: Int64(duration), orientation: 0, - isFavorite: isFavorite + isFavorite: isFavorite, + adjustmentTime: adjustmentTimestamp, + latitude: location?.coordinate.latitude, + longitude: location?.coordinate.longitude ) } @@ -23,6 +26,13 @@ extension PHAsset { var filename: String? { return value(forKey: "filename") as? String } + + var adjustmentTimestamp: Int64? { + if let date = value(forKey: "adjustmentTimestamp") as? Date { + return Int64(date.timeIntervalSince1970) + } + return nil + } // This method is expected to be slow as it goes through the asset resources to fetch the originalFilename var originalFilename: String? { diff --git a/mobile/lib/domain/models/asset/local_asset.model.dart b/mobile/lib/domain/models/asset/local_asset.model.dart index 6f2f4c06b..ba64cc40b 100644 --- a/mobile/lib/domain/models/asset/local_asset.model.dart +++ b/mobile/lib/domain/models/asset/local_asset.model.dart @@ -5,6 +5,10 @@ class LocalAsset extends BaseAsset { final String? remoteAssetId; final int orientation; + final DateTime? adjustmentTime; + final double? latitude; + final double? longitude; + const LocalAsset({ required this.id, String? remoteId, @@ -19,6 +23,9 @@ class LocalAsset extends BaseAsset { super.isFavorite = false, super.livePhotoVideoId, this.orientation = 0, + this.adjustmentTime, + this.latitude, + this.longitude, }) : remoteAssetId = remoteId; @override @@ -33,6 +40,8 @@ class LocalAsset extends BaseAsset { @override String get heroTag => '${id}_${remoteId ?? checksum}'; + bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0; + @override String toString() { return '''LocalAsset { @@ -47,6 +56,9 @@ class LocalAsset extends BaseAsset { remoteId: ${remoteId ?? ""} isFavorite: $isFavorite, orientation: $orientation, + adjustmentTime: $adjustmentTime, + latitude: ${latitude ?? ""}, + longitude: ${longitude ?? ""}, }'''; } @@ -55,11 +67,23 @@ class LocalAsset extends BaseAsset { bool operator ==(Object other) { if (other is! LocalAsset) return false; if (identical(this, other)) return true; - return super == other && id == other.id && orientation == other.orientation; + return super == other && + id == other.id && + orientation == other.orientation && + adjustmentTime == other.adjustmentTime && + latitude == other.latitude && + longitude == other.longitude; } @override - int get hashCode => super.hashCode ^ id.hashCode ^ remoteId.hashCode ^ orientation.hashCode; + int get hashCode => + super.hashCode ^ + id.hashCode ^ + remoteId.hashCode ^ + orientation.hashCode ^ + adjustmentTime.hashCode ^ + latitude.hashCode ^ + longitude.hashCode; LocalAsset copyWith({ String? id, @@ -74,6 +98,9 @@ class LocalAsset extends BaseAsset { int? durationInSeconds, bool? isFavorite, int? orientation, + DateTime? adjustmentTime, + double? latitude, + double? longitude, }) { return LocalAsset( id: id ?? this.id, @@ -88,6 +115,9 @@ class LocalAsset extends BaseAsset { durationInSeconds: durationInSeconds ?? this.durationInSeconds, isFavorite: isFavorite ?? this.isFavorite, orientation: orientation ?? this.orientation, + adjustmentTime: adjustmentTime ?? this.adjustmentTime, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, ); } } diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index 04eaf0469..c49ac49cc 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -286,11 +286,23 @@ class LocalSyncService { } bool _assetsEqual(LocalAsset a, LocalAsset b) { - return a.updatedAt.isAtSameMomentAs(b.updatedAt) && + if (CurrentPlatform.isAndroid) { + return a.updatedAt.isAtSameMomentAs(b.updatedAt) && + a.createdAt.isAtSameMomentAs(b.createdAt) && + a.width == b.width && + a.height == b.height && + a.durationInSeconds == b.durationInSeconds; + } + + final firstAdjustment = a.adjustmentTime?.millisecondsSinceEpoch ?? 0; + final secondAdjustment = b.adjustmentTime?.millisecondsSinceEpoch ?? 0; + return firstAdjustment == secondAdjustment && a.createdAt.isAtSameMomentAs(b.createdAt) && a.width == b.width && a.height == b.height && - a.durationInSeconds == b.durationInSeconds; + a.durationInSeconds == b.durationInSeconds && + a.latitude == b.latitude && + a.longitude == b.longitude; } bool _albumsEqual(LocalAlbum a, LocalAlbum b) { @@ -376,5 +388,8 @@ extension PlatformToLocalAsset on PlatformAsset { durationInSeconds: durationInSeconds, isFavorite: isFavorite, orientation: orientation, + adjustmentTime: tryFromSecondsSinceEpoch(adjustmentTime, isUtc: true), + latitude: latitude, + longitude: longitude, ); } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 8b253f83a..d2455b744 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -16,6 +16,12 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { IntColumn get orientation => integer().withDefault(const Constant(0))(); + DateTimeColumn get adjustmentTime => dateTime().nullable()(); + + RealColumn get latitude => real().nullable()(); + + RealColumn get longitude => real().nullable()(); + @override Set get primaryKey => {id}; } @@ -34,5 +40,8 @@ extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData { width: width, remoteId: remoteId, orientation: orientation, + adjustmentTime: adjustmentTime, + latitude: latitude, + longitude: longitude, ); } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index d0fe742463ad87c386954620e481fe51e29133d7..22219b1e6e5d74397e7f8b928f1e624b6c7e773f 100644 GIT binary patch delta 4215 zcmcInO-vhC5SGoKA<)=>ZNU6IgTa`;fJhKEV3#N%Q7M!_pd~4xZrBhbVdRS&5-^wJy(Ql(yV>Y-JC-rL>T-S=$LASFK8GxNTg z`DWg{@y8$8zyHpD|HC|JZmgJzMo4U7X)P@;OG)`+d|B!!A?hdI-8BoE<>ss~9cEUXFn-yJ!^%k6V-`R_^pwN-j&t%koKvgka=zmngpo1=|B!NJ z#QkL<#C;y55qdJr|Ay^Qg$t_|#$qyQrBNg* zHtMzCR;SFgJh8u!0DD3YUUY!Ue%o&qaBrYdtRLxbJA&8{I ziGhBHrs^nxOm7qX&=<6{21Rxt{|s9106Igk))d<-U>o(y0)G^CK_>F3Q|t?ER$);3 zFN9IHqNEr~PkSsBGEO8oINcoLX&sZ~7!E$c3EVwb1>xa1MD3!Pt7O;+*)| zK6Ndgj3vmKm@F~&qWpcA259$b7Fx?h@y_Sc81+Ax?6wUOB43ZEyZGv80jLTj+$u5< z$}awz2VYFPEw@T7?!#$s7C*z&J`e9D#KI=zHhex^V{$~#_Mp??92p|&L%SM}(MMK) zYa^{_V1=+SOEKCkl|2jui(S0wSN?tLL0)#D_h#$cqLJ36bSil{$VVTX3t^ow8rYll zwCfeXL#VTwBY(`kO0-9%^7ZAhJQpE?Tj;sshVD5>6Ag^5OB@h00IwcGx$?OD4Q& zQrF6=l%_kFzJ3uRUV^S0Y#lSy;lmpnnJ}J43cjhKQ&iMyctpoRsaA8Nxh&5p2FM_g zK>-8B3`A|+H&Rz)iQ#lwl40Y*F~xoLNnUd{5eqRnCfrbnNHv>${e}DqM$TJ_JsTz} zI;Lkyf|sl0BBW+LWv;qL!U%_%X5kqM9uURq#4B8^w8QY7s(L>3FbQx1*xjry$H1vl z|H{N}OF#kRJ8WO4t6=>1qrH>xFzg0$p+Mtj6dJ8x%0D z&j`#^BM-`YV&4X2*?UP`7%d|`#}(86f|w6N4jc3V2a`3*O7c$|WRgX~9=wxmF;*Y? z-CS1a-Bp%Wo*wzkrB#m|CeprHo;}cviqHmr;<42&71gaBTPq0$Wv^L#IF%y zG7|p~Nv5hiQqY8*h7VTz6|DH0dLn^}*o|CkSV zZe{%u+x;Nt!cc0BADkQ2=9)lH7zO&jH33{3RhI359zDu-;M%BEyqop6|lE z*|>NW(DE$vJb(!P?B~E-_AaYyb*2Jl}k@F_~%e-j)Z9 zn}ym1m^N?d{J^+buSbk&^47_Mo6q*yF>T&HaT4R^t&^FUzy|r`^K8zawuoi3+Z+of Ks3~$xwOjy^pKo#i diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 548aa2e38..b42aa3155 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -10,7 +10,6 @@ import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; @@ -21,6 +20,7 @@ import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.d import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/stack.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.steps.dart'; @@ -95,7 +95,7 @@ class Drift extends $Drift implements IDatabaseRepository { } @override - int get schemaVersion => 13; + int get schemaVersion => 14; @override MigrationStrategy get migration => MigrationStrategy( @@ -185,6 +185,11 @@ class Drift extends $Drift implements IDatabaseRepository { await m.createIndex(v13.idxTrashedLocalAssetChecksum); await m.createIndex(v13.idxTrashedLocalAssetAlbum); }, + from13To14: (m, v14) async { + await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.adjustmentTime); + await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.latitude); + await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.longitude); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index f2d87a7f837af43fe6fd6ebbb80e1414ee9622ae..21a3db527477523d3c877db6d831a22684688bc7 100644 GIT binary patch delta 367 zcmeBr%Q@#NS3?Wq7N)|K>F2B%g_#UZCOghl15xi^=z^#Q#-i==PZ<9)Zhvgb^qZB@ zWO{-)qtxWUrKZ~->|{(}oF2u@$i-+moiUJ6Wby(L+3Bm88AG-|uw}|pU^JOr=%T`8 zWU{?5j%fq)WPiQ$lRwyVPM)6PIeCG$CRbugR%vlbZfai1=h;#B(YC^kia@wH);1|lQgl(4tKdG|L^2rw4A)L(g3J}b8o2BQ-ZsQz5M=KiAMWB;U|vyL<-IM-KMn#Nt#1L*wZS^O=AS yp8g<*DS-{5U|J@VGB5> CI}XMG diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index 59546a453..9d4c9bc49 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; @@ -244,7 +246,56 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } - Future _upsertAssets(Iterable localAssets) { + Future Function(Iterable) get _upsertAssets => + CurrentPlatform.isIOS ? _upsertAssetsDarwin : _upsertAssetsAndroid; + + Future _upsertAssetsDarwin(Iterable localAssets) async { + if (localAssets.isEmpty) { + return Future.value(); + } + + // Reset checksum if asset changed + await _db.batch((batch) async { + for (final asset in localAssets) { + final companion = LocalAssetEntityCompanion( + checksum: const Value(null), + adjustmentTime: Value(asset.adjustmentTime), + ); + batch.update( + _db.localAssetEntity, + companion, + where: (row) => row.id.equals(asset.id) & row.adjustmentTime.isNotExp(Variable(asset.adjustmentTime)), + ); + } + }); + + return _db.batch((batch) async { + for (final asset in localAssets) { + final companion = LocalAssetEntityCompanion.insert( + name: asset.name, + type: asset.type, + createdAt: Value(asset.createdAt), + updatedAt: Value(asset.updatedAt), + width: Value(asset.width), + height: Value(asset.height), + durationInSeconds: Value(asset.durationInSeconds), + id: asset.id, + orientation: Value(asset.orientation), + isFavorite: Value(asset.isFavorite), + latitude: Value(asset.latitude), + longitude: Value(asset.longitude), + adjustmentTime: Value(asset.adjustmentTime), + ); + batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( + _db.localAssetEntity, + companion.copyWith(checksum: const Value(null)), + onConflict: DoUpdate((old) => companion), + ); + } + }); + } + + Future _upsertAssetsAndroid(Iterable localAssets) async { if (localAssets.isEmpty) { return Future.value(); } @@ -260,6 +311,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { height: Value(asset.height), durationInSeconds: Value(asset.durationInSeconds), id: asset.id, + checksum: const Value(null), orientation: Value(asset.orientation), isFavorite: Value(asset.isFavorite), ); diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index 34ed7a5e2be0444c49fcde2e5878aef827d09099..1c3b4b083e194cfc889d81987f353c5fefc8b92a 100644 GIT binary patch delta 482 zcmcaGpYhlt#tn>Y)(Q$G8JWd;i78p7#U;6^c_ks4xv4r_3P1ps$Vn{8EGbPv5z5a? z2Z>Go!@AEdGq1#60adp(7f^djerZxps=WeCKdK3I)Wnk16vq;n!qS2iRDtr$l#&dXY({EkdPd3QvmBC>53sVuC@AC? zWdc12^iX~tOigC7TVfedyaa9us$XF0VBUc9VLpHeOnxomu(^Z%Fk`&}T3}cy6r~oI z=9EMm8pJ9j7At_l3vLKBY!PYYlO5T1PJSk+Z<3j!qmY-Fo2sKwQds~blZ#RlOHxxDOLP=U3sPXr z^30Ty3>}4x)XemZ5}nBx*@Yz(@{2N4^GXs+GV}9v6f%q563c)hC8?7W#T+)bavWyd Je2}Zv4FDc4DOdmi diff --git a/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart b/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart index 752ab5ba3..2b7034770 100644 --- a/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart +++ b/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; @RoutePage() @@ -129,6 +130,15 @@ class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection properties.insert(4, _PropertyItem(label: 'Orientation', value: asset.orientation.toString())); final albums = await ref.read(assetServiceProvider).getSourceAlbums(asset.id); properties.add(_PropertyItem(label: 'Album', value: albums.map((a) => a.name).join(', '))); + if (CurrentPlatform.isIOS) { + properties.add(_PropertyItem(label: 'Adjustment Time', value: asset.adjustmentTime?.toString())); + } + properties.add( + _PropertyItem( + label: 'GPS Coordinates', + value: asset.hasCoordinates ? '${asset.latitude}, ${asset.longitude}' : null, + ), + ); } Future _addRemoteAssetProperties(RemoteAsset asset) async { diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 552c9e356..35cdc7add 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -81,7 +81,7 @@ Future migrateDatabaseIfNeeded(Isar db, Drift drift) async { } if (version < 19 && Store.isBetaTimelineEnabled) { - if (!await _populateUpdatedAtTime(drift)) { + if (!await _populateLocalAssetTime(drift)) { return; } } @@ -229,7 +229,7 @@ Future _migrateDeviceAsset(Isar db) async { }); } -Future _populateUpdatedAtTime(Drift db) async { +Future _populateLocalAssetTime(Drift db) async { try { final nativeApi = NativeSyncApi(); final albums = await nativeApi.getAlbums(); @@ -240,6 +240,9 @@ Future _populateUpdatedAtTime(Drift db) async { batch.update( db.localAssetEntity, LocalAssetEntityCompanion( + longitude: Value(asset.longitude), + latitude: Value(asset.latitude), + adjustmentTime: Value(tryFromSecondsSinceEpoch(asset.adjustmentTime, isUtc: true)), updatedAt: Value(tryFromSecondsSinceEpoch(asset.updatedAt, isUtc: true) ?? DateTime.timestamp()), ), where: (t) => t.id.equals(asset.id), @@ -250,7 +253,7 @@ Future _populateUpdatedAtTime(Drift db) async { return true; } catch (error) { - dPrint(() => "[MIGRATION] Error while populating updatedAt time: $error"); + dPrint(() => "[MIGRATION] Error while populating asset time: $error"); return false; } } diff --git a/mobile/pigeon/native_sync_api.dart b/mobile/pigeon/native_sync_api.dart index 822e2eddb..ec28afb00 100644 --- a/mobile/pigeon/native_sync_api.dart +++ b/mobile/pigeon/native_sync_api.dart @@ -27,6 +27,10 @@ class PlatformAsset { final int orientation; final bool isFavorite; + final int? adjustmentTime; + final double? latitude; + final double? longitude; + const PlatformAsset({ required this.id, required this.name, @@ -38,6 +42,9 @@ class PlatformAsset { this.durationInSeconds = 0, this.orientation = 0, this.isFavorite = false, + this.adjustmentTime, + this.latitude, + this.longitude, }); } diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 69dff89fb12dfc08d1a597a918b0bcb4355885de..5e196105747470bdfe81ad3b12e4b246d40439ca 100644 GIT binary patch delta 100 zcmZ3-)4{vJmq|V|w;;c$L_xhcIU_YUF}}>uL@y<=s6<^Mu~-4f+3d~a&a9c7Se&Y0 sXkx{s00l* z|MqXg|2q2S==;NGM~{a`-#$LNKRo_+_~zSZ!_ng>&xSwzf5RUSzj^!s;nqK$onNeu zfx_|2_37&2uzmMt_0{S6Z2Oz_>o?~>eDBTj#ov}ctsb6StY2>bjsE`to89{#zxmCJ z)8%F}{IFSF9Gz{~+jqn2AKTU0$!2)Ae15tDj<&D(&nIUu&%boI`vK?ia=Y9g-v4G8 z{^$AI)y2j7WCee}T%RpZhyS%YTU{)-s}lrzzTB*a<#xM#@yZMShrvGGUaZf4Iv9?Z zr|abg%e}qD_SJgxpvJvF{DsOH^u>kH`VCHCvd z83=g`<8yII=yRDpgO(@RMWDOF@apw)eLBf8LESdX&(F_K_lH-@&36~)XgyC}gPkO- z6P#!UKp($h@Z&YY@74(hog8k5ugJuPx;I}Migp6Jp)!BL z*vHGQ@Qn7_SSc0;5#ncJttY}7_Zoq6o2@9#6VIa`zW@H{n`g(*o_u}u^x5Ip-?6x` zl_`s;rUr=nCfEBk%dK6J0k1d6$_t493J8Sjl?XZoTaDnyBU-A!qULn< z8XV6DKVa=dsC6+ZhIXjP*TQ0F9EifpqQm*pWkmK7EI8E$Q88B9=lto|pdUJRr$toSq$ zM);fq{boVZ#PIO!{P6h(#!$78hX7&|L=y{5fyR3ZYXe5Fzu?mBuK@Ss@?uS6&m+9Q zSf3C}0>yqdgTtB#DK0SsrK$*f5I^J+<@uK_WXaNQoSG17n^4KfbOU(=0UW1)>YosDLTq*nYw@nVb2Gk zH2!12xRi~J;iL1{Z}bN#;I7=oxt~l^~nR=L4x29YeX%1 ziyKX_LM%o=eKi50>}y0SD^Pue1|FP;+yo6xjn0Jyei*``9y1|TjhGUpScUOw&~DX$ z)KCDE-d12dfI~Cl(t17K{pK6UPWv^SG7wZm+72cCW+PCi#Q?&MNf-gy#5f`o_w70V zv8dEcRx^LNgzbon#hxoCY2DrzLDCN%{Cu(At`;zJd^p?#FEr%Us67BcO4I=%1pydV zlnzieqyS!(^cbke0x;{M9%>LOV1=)|t6%JW;Sze_GPdbq90~BJw8n#8yn1wg0vqJ} z!?(}>6Lx_fV3E2}9!xjEU`%!YX>gjZQE6Zu36gF^W-u-c=Wp;(3HL?6S}-TW`S#W7 z0-d}JJRCy7^zy~>lunNBYyB_3Nd1BkhIKsrLe&}m+kX=Xv}FPUp9Kx6bIJg)(*{Bx z2^EruLGf1tR>{f`>hc7&mZ;%26$x?>yy1m17>Hz%?vX&x4S#q8{(5EQBP;VC(dp~l z3qmmJ9xnvT8kA?Rj-*&u(zK~|-!%}7b>~!~hQ!%VjG|ayui>q~GUbs#21_%Bq#iw3 z(xP`E6EA{c&6$D{)tLgTRb;aDMlU8Yf<}yVjyDI~C!2U4tmzcA-tG`zuM^rM-0cW*A$f267tTRIoU}k5%*?NFQOUq{N@5v*}{PO$d&m^6V3qyK64C|Kz zNLmjezMx}#90nCV)|2$*m;xa1bB`zb5sC-G!RQ;{f-JbXTH_oAYN~_cW*rb)Y;4gf zN!4*U`fS$(kyf-)C&Q(3PFO4q7?-WKTF{y4g7Ya>buSA+(`4bQipJ2o z7m+|K%Vf7Zt{`?@SehqX?Ki3|IkF$vj_@dD6)3A;gWOBhAgA zG`}RL;FNLJaBNT<|M&RB{KWIit|;ddSy=KQ>4K%q0S) z1O)b9LIR|#lvh2jtOU~2R#mt(hY(trHPlmMMV!7`t$%t2c@d442D@UI+voM<687NW zVBpEw(-rJ;K+gCm+X{ZXK0Dqp!maj!rvaZ>GB_m8zxdnc@^vC=>5qCb0qlu5c%URN zc5`i6?7SDZ-u%b%?Ky73Cyeuqm1D+wbNo^ucjFw1%`!*G0K1;F>cH|xN~l)NJ^vZ* zRy>)$%Rq3)H-DqygGKuN(SIC4%Jie7r^6*=0Z2M5Zs~k6eEZGt_~_3^&yI#ihfg0J zK0XrtpuMvx-|BJj^c;=^^z%yj=Y)BT(ft_HR-}1cUN#d1EcBQ`UoaP{Zi*l!Op9l2 zMWD&K!gS)es>b%!A9l=gY9{9(8J#ouO#o#5_(tX*%OUqCkh z49-v21o^RekJ~3#xx<00RrYV_I`}VHUmeP<*%l^TOY?C4ljphow@w~yuF1u^v#--4 zDYe-Ke@mR?k0JnYkvU8LH18t_l`(UvYdn98$|;JBBYxi13nv$}`*IA|KUdqExOmcR zmVoEJQ}<6&2K5jCHFx*S{^*A>vO|&jp^ z_llUlJ1g`3R=pNXb0rg2a4jD;nc1C>`Cni63;)k@iFL%Z@39Rh&7R4tAYCX{n?fesmj)Q=F>KVc+G2H2d==vz*t3BSWj4N{tRC2`RUh7f zSZo3?E4)whgfb9s@5(H%Ghq*}xipJ4; zMOKbxdM9X@y7k+5WqA1I7xq1#<@ug`rF1}6QIR_JfXaoaXN5JS*@bmz>msQk&K1+t z8w2Syq;SO|sA*N9%(E5@T;Z;?dfPl@O>2cZ(+%fzMS2z==DDAaNIr#+U16uVzYseX zgml~s!|sT#nVQG8Ts;d?h1}AX&2@BIjdSqWpmxsir<*d5)M+YCvSU-;H={7!SncXM zZKgBn*tqtyrdM!4n&XbM)HX6wuN1^|FvQJvc=h>h#-QL9e5H+8DDE^9C$L#VhF>(5 zq$cV`{4~(p?R4Ikj9IsOxp~0**&qEre#&jRdFuDLtrwauE*JATEZOY`3^K*_V||Q( zv$ItM7ju`rV(x=}E;f!iJS!BeB=k+29ANKkOe(Lt{d_NBiPn~Zr58`F3icMP%d z{L?fVl)ygjrcI9)?uQu%8WiRrDr`Xa0Fe{mFv4Tzuz`R?iyT|M5#tMJKrWnrPC^yO z#;(8#w9K>TbeI*RV$gwtx$B%l!->HM8nxfeNmb0$NP(U^;+ztBYZkAA27Ga|Ida8@ zzy@sdowM)_+9;6A{gmFNAV*kqwtAU@rs=%WfVU~g2WH?jEHqa)Soe4tf+|;>uN$g+ zx^Yy4Gw-m+o-RXUf5@|OYe^hjA6_QdhR}MmK5hf+Y=TS6@YH{VOM83f#KsU5D70W} z*R-W(x@PL(2x}xQ#E$NLb@F zw9^URD5F3I;xLY|kKO!@&}@k?;N z3M)nt`)TsoFZTjRdEs6NRk+xuyM!~4LVKp_GtjK9TD?bcNf%tDW511{DDEhos=ubQ zBS`$HPO8Ja6u_|6eME<+`}lvme25?10SjC3{4hOXh}nR8J<>`T)^z)!&*cr)Dj8O0 z>hzD^J%blp`1@^qhK7n5iFF=s@6v{6K$C(3kINXb(SCSJrOujHa-Iq-wIL9wCXt&wi_KRzETCgXW&-shZcgm&;UIq)U}1bY*q;^`LMx&RXp#mMlT{mJs0{<;8G=3_XvNH# z>;^$6V?GJbjp1=({$t{Kxjd-k?*MR&8GS|$0|PwMM)9HZh@AO*fV01QufLfu2VncV z2P}U69{cW2^Xkj*@gF@}jjII)nz9};dfAcZ_|zq$0Ca-d;nv+6QZz5vYuJ3=J@sK7 zx`odH(#3a=y7?49u)ll2<}U!){_X*{zT{qA)I)W1g@*j22X3}}Pi?a&A-7fFMeH=U zi}6<>D)WR@?ev?2J~{K5mfH$>HMa)(4D%IHY$|S5mWp*7!qksTUBBpCb%;Jn4WS~} z0s8b&W`xvjhY=zu9R)!VIir6g?-m^!?v7mV1Bt2g+xZzp94dQYkUo;f2yGM%(n}I> z8)XCck_4Qzz(Dyc0b>m@IQdKf8}%`qwkAP`#rpX3lB$0OW(65FQ%?hTkC=^`=&wQR zcS5_+ZbK+b6Ew2`L-CRX+$_Yvy(9szU7%q#6$!E%nxTcF1VJp(p!GWeHGgQpRsvSr ze+I{vw`A>_8Tv!@ux4tNF$?s^;(bl@m7Z|Ob+{W|<%Dv@W62EN_hoC1>mX)oH3;rZ z%JyUyOthEP{)&!KOH=GSmc2B1vYYluL} zG`xl}Dz!G(fHat{yLGkE5rys6n~owxCkNFuR1#4s*AX>rWV0CdPw*!k+5L~49uSm! z`>}%t0yXuUZd?5RRO9ORr{EeDo#p^t{S;YM+iA$)*C(l}SDyqdbb6WxQ|@U{)A4E0 zTG^*DT=S=4)n`x3LT9uiG&!rc@y2E%<4@lD6xQ;gaq8>y*?(1Yu4@?C zJydB(hj)xEhVkg+Ko-3kAdKg7Fxjc&IMu0af>Q&>h6in^=>-n0PKT$#rb$$L3ZKy^ z3@-o#SrwMpv}N2_uxUU7EekUZkkP5bO9RQmcdcpwjFi}0AHe2Q2x32SH1jcQvORyn z89yP_#FdiYwxv0hL*XYtc?@AF(?`!}j*lINgLdu=W0`^mEGfe`cfyf-#NwHjvqawj zO#enenQJ*D7XWC~Zf2!X>A;fK#Tyf_eOwJd?0o?}r7KJQ}Z>Jh#$m(U9;4{uae2knuBnNmQfGJXpxoS;W6v#o$7 zWvmSb5V2}=3&>bizkJjlmPcZ4l!1&Fg24PE;%Q)X8B+<%xUga#ezV)qjFd5s0dOnf z_4Fp#*klmq{$r1*yJ1u4YXS|I24813%{(;qX1%c?U)BAg1fFI?|}LHZnj>qG}jhI$xhkWG`J-LB z^FpT$rzq7HOt*u_HVms&Xj62EjhftK&{m?*wcl zT9{CANNo0FuvWrU^Aq5BnaXFS*Zva*Xp4H#Uj2vxR+YEl#Ea50tMhn18gGXlR+aS7 zJJIFc4ly(E4n?WQ*j`-oROcD)4Rl&Cr_gCZ>PK2|3UAY2b|8n+p-y!r>m(A!$?384 z=@8_~grZ>OBpavHp{{Uo7F)=jm{%yf_uCP@Coffchc~%s>D>H+a)*-o!4BJ6om17? z4sCau9qMrcU9e5lheg28g)gqQL!D}ILRXMhJ5=19a^a9W^a&J;)8>L}4^C|eYyJ!y!041%E#GEr!v+DR?JXoe{? z@J}yI#ljcB+21{2iz5JRfA@eXKmZ{9+XG^s4^aF^53KRmfYFrokUu9$E%wZY=)&1CoFAz{Pd}R{rV%ni&!=ACM$vIRj5IZ9n_b9N1yoz8an_pP#Ot zoV`4U3-<n?1heAq8u+}O`EsKnk}tY5?X=k$wUPo{s@Lcrw->yKpoagKDAr_gm< z)Tb%rvwR{#pR(RyO(?~IWq0*0@eM|qn|MS#1QU!{13V$Li2FA72oAWwB|L(|nglz( z7y!S!SiM~T!!}g;x20F`d+{%}xS>n6;GF}kZuP^hk-dAN{J|cagYWHOMil-`$M*8# ztPZBS8;kR`TY8z;ZFi}|cbW3()7SrLocossBZ z$0TBI+}|@bd6brX^Sm^kvEeD-5)I}i6PUx(^>VXdv*I7R(Y>VB3~u>h)xbRY3Qe5F z4|-B{$e&ZK@L~$B^gE`Q0=1nUO%+n2j-Guf<)wyr>Se`T`tYZQ7M=KUqGc6m%1Sd( zHkh)q2ts95$tEJ--lLYiJv&G@Vn+?Z;xBP-U2GxgIEEeUh!&kn+$wc66JbPiClHZ% zjO!;3iM`fRGsIHt(qOWIqi@MoT4V38aga&_C_d)&(O;3P_>3|1s_&6`@XIiL*!a;Z zGY)$*syN%6L&C;CkPyo$9+9y}>{JVSuacF6JcmY!VSJE&d`iz>fh8B+tscJ$`GacC zhnRU3MoBPym~DV9ZE(*AY#i0#9970OZBvI;U>vC$ovcyCEkT8ZzY1xdWH3o{l8L>K9q$ebL{hnulDl^aOafWhGRySf$H8LUfUv^sX^H>`Lt&3npv+F-`+*9utzp`ClZ+N`1eQ+Q9p z#)kSPUwV+C9n|8Ym&M=>)o_Wd%E3~yv_QjIx;+L_!BIN_N?w8}p;))XjRJ67^RubU zJr}7*JU+$%FCJj+H(^*@NiXWDK zv&XJ0%f2EEYzETfXnOl3Of6L>X#I71xv~f8nKnd}lAOVn#pG}{&Ha-gM-2TlZl0(d zJvHr1vw>!2mjjto;4TCpv4aEcbRVGXoyhP#xn@~_e!u*gtY_obZNw%xks6G`bFOKs zuU(Todkt>^OuQfN>4h1ijs!l~ZhZ?_QpQ*Y5V6!uoFOXVgziF@n76X`p(&~Gvxj`UKB^a}mPX*&Xirdz;Cy@H8K(}7yr%w7CV4XxI6SPj3 zD}ur5ruND@E!fC)T9EpY6f&pqHqG`FJu;!+SaKodyKR?LKX&BGvXJ z*}IFAR0(b~AE!z!GM|$FDpwjdSN$_SQAelCS?UbMv?_h3#<)M!B^=E$AjSdYvqsDN zK%nQ#&8j&W^a&^r`N42ZxgS_ww{)%9G?;h?GZ^FW(fR4+>ofa(4wA(l<)^a;ykbVm z=Gro9Fa=ZZ8Q0uWdM2|I}P8-9m;(oU8RiCq~k$Z38dFuRXDx5_4s1_a{Kh}r_b=-$Aq$4 zlGoH&5vQEBI?gJ?+&-^OzU(;L3LY;~bkysk$XjM0a0xBpTPnpMU{=@R)J8W|jq7u{ z%B)`8m}FT2dyJ2Yc$#nP_mo!Cg^=*DQ^Gjk8R3`_FP7{iawNuxdF(L8KREU7(I1Z< z{b^YIF0lN&;mJ3{;`jH5fB)hO5xg86&C725{XDNup5pvsy*k4^jq|faropoXz@9P$ zp>T%(P7cl_O6ylxRi2-1w!@>#&G!7Yuf-O7Gka-KX;_~8ClGlJ-F>#kDH5s5&`gvJ zL6^SpvpkM^yaikl0G>rOvnMQctDrBs?NG>~3~dNBsb+FyH4>#y&MzS~4xTQ9(cE61 zWRpMv5r3qBz47#JB*V$1X4%M;aU0|3XFtv4Tp>RS>v8&jmBdiG0z+Dc@?%liLY+Tt zP0XKsCxuYt;tBn&G0(0AGPG{OsFRjbd#ZxJgW+<4D>K>wm#Heqmx>oZ?`mf1DK7bB zwGKAD-oB$O&Nx_4SKBYEmBl{2J*$UYe_Fl6^X~WS6FQQQxb+$h+`^qoQqA~NkA;eN z^QnlmhHTOgbq2F*qN>e<(JO+VT>fS6-uo)G_n&A_dr8w~UAyUK4cIHw-75Zpo5G4Skd zjTATEzP1#&PGhqtwiG0zNT)gx`3PKSQw!j%x}UHc;T71iVC-I@{X3|Zp$@^TAb9mkmTyUoswh_RvN)4| zdXvD{BD&J_N1NH&%5s}YWtG*ch4Mkd(uKa``h^M_*=Lk>Ny8~7`r9C+ekFe%L^3jA z2)LT)xzqE74^^nh1FU%-EW5p%5Io0*otYn()r&4zL!L>FjCo2NAwd`Ug`fx)C#?}! z_4C>;O<*R+Z_h4IPoJG1KHtCsZTRIE`9V@k(Rfb-D;W%Vt8dA7o0@$>wQ09gT2I%%kKLbEuQ~9c=>TsvknW2$l z@*N)briNqDu(;}B?YWNK=p7{TggFPmI{fZ8w=Ry)sRLaHJtaKu+{F^_;k8wGMQpV9?1GXA2o)fF_FNAbiD4tmJ$GjJMbtTo-d zr3-6p-5nM|fq1%P9TKDN!^;fF5LyfF<2JA^95CYyPi;M9XYZNa_7R|83Kg1ggUv9T zxruc#8fu0~V;z9J$;vJ>I6)NDFIgTSLm|*&C!z`NCiORa@MY~{$ zVQdLc&RYk=bsd_PdquTA+atIVj;h575?rEyL+AKh^A`G z(0?l`M4+WY*Zk+KC$ED(LqdK?8&ORNW!ymN|6mKZNWf5TO$znaST3ZJ2sfmz5?-;E z%`+tqqOhVu6j~|-)Hd*E6(k8ElCZLph880cni*x5V-D8thAe6@tI4hw*TCRF?0zxZ z;|Y{}{?U87ka%(^DZc}|0J&0Jy9bikxR3b!qxa%fM1<-)f?1Fht-LS|EoJk9wO{Zx zgyscG(ENa1@D4CUJV1!YbO>!II3Zy?l=Pd5K%KS%h&ERGB5AKI>^E7#by_WWS~d4_ed_b_doSz)KR-8Y zd&#}0YNsv;nYmy4Z?gMb0iG9#X4H8ey3~2VDGPcXRK3Qhcx4*U1yz+rSp7TKuMd6d zV2F<#kOwPg%xpBh88Fv2Q&%qT&KHHIj3|HAt}>1Tu~i9~%1n;@rIJF5WaiPraVd1Y z&o5+NAbVl~1#0gZ&`0w80j*2)sRsH;fNU6yoR=ivwoox}FG;{@7!8!a5-?r#%Ci#k znE*E9Z?xN*1RZZIj6Yo=3S?j!)w4lx{W9=Slr$d{&@f_If>NreVO7NmqUy1sSV0D? zYVOcl0!rV#a_EzIO`rIodLtv#=a?$-WARW1DzfJQa2;$c@Rhl`;<2PpeApWrVT-12 zAnwQHb&bx|U_1rr|ExFc_o#x^sI-?CCXsbL$t5UHU9aMSCQ4MYpn**ngRn|})#NXxA)fKG@f=nGkgCi2L zXWD1JDn$BcIb!@&!RKE_K6|=?S;oo6vxN3Evc_aH*u$w08+af&Z4T!AE?GLGRR_rR z7F8Wei}1OoVnrzwJ-RwhxXcP@?kpoKK`8@Ip%gH~S41rpHDG%MXM)vd4v~mzaX{B1 zXj%U(FjT7}ik2ac{>@0?g{a!`S(Ju2^K1OoVEpObQki&tL-YLhJk=LJ@T{5dCb5{g zQr#F*b}jHcUxw1@eeJ`V+BlN6|LP8AnV3dyOwYzVgz5b_n&E3TeyFBd$hv_CwP;BnZSD(VY*jaR=UvS5}IJc~QieZ*uI-KfA+@%CniXbh{$L{(Y#fBxZHf8^1hIP5%FMt0hfFx8bJ##tUGes$>aTD?P%D-()>^)`L2 zt(j(Ys4HBYq!cf!5%UUVrx=}%T;q#*GMDMFt;j29)ONa1I&mjn?h#`Cs;JO=$xhD%mP4Yg3$p96OLbwh=s4LG5if75D)!m;2yX(I= zyM1-}`uW*%&94GKnSPzo5c~z`ju2f4JIOGN!SxC8f?{y-9M%B8sq)3}?Ki{Yqdy-# zI~txIJsZCH;m>~-S4g)*Xd3}$4850M_2B&we^?cs*g87Hh@J_PS1#;#VR^F@1o-J# z!r0CKB2gX$J-2&2@zd9j@U_5;C9HBb59_G*fR5>@^2z4#^ylTfjc(cPg=ZLR)<5() z#($sZ?r~uTyPGGgi^R%l)yBdlkln-yRK}#KdflXUUFFd`Jx`!T32_{vFt=_C4*sl; z-^A5%of)P4ax|B&HsyW!TOr(a4DI?<*uI5n>WMs|8p zOd1lKcY0h7H=(PAg+rkJLb5|OH}QJVDW2eA5vPo%l1Lx$(s`9mggH~vXIW)=kO&)U zS#54UtE}c@C1y1{gP%RD<&lwWgpvW-^zHVZ+nRiJiC#A1Kn}C?$w#@uFqB6NtW5ih z06<oVpRF$Nhti6pC}mMbT-ZxrN~%@B^JCuA3P7JIQ_BhQ zB=$~QtjS5!{P-7foW_7iidUnJ+C>?Q^Nv`w%A#@sOip+78Ku;6@9aBDDYtRreZ_}) z&;*J{DC{jH9YlUqhfT$vun6it2Rne<3pB;=BWGN@jW7%5>jDXLaXA+?2?Gx##4K_G zr4R%(AlDi`M#UZls*wV{M$vPEY!&+4Q5B~GIWmEq1epj^5Cf9IC5!o#UIx0t3pJ$0 zg*-+d5sGR%h85tBbK0W7dP<*BRQM#7P}vos}tlfcYq+>o@8}SrUenD|En` zRCKSf;V4OoxVexM6jheZ929~W&nF4)!IB=_T!@5GaF|nB**g;k(O^y`>^kB%zyFfT zgo8!rEfNj~_9 zw74K8g-WQ_ToG*+gqN$Gx6jD+6^QDk@<9T!-CQSWccc37nw=}xc%J& zHgyGH`@08hs(epXv%_<(K|s7N>b22m2;eqlJ!DZ1$npZk$OmLRp$Zf+W;#J=rXMSM zwcNaVbdJC0wckD_p>ETCU>qb;Z65~ubV1Xn3X?t>IZ14Wm}InFq)_bG@} z>(gflrXDpwLD|KA`beGt2r@L-SeBrelnud45^x&@1NV{yTt>-&^*aHr$Ix(^iUheC zYQt`85_D31L!mEFj((z`%`8ZvbPM^1>fMBN@Z)yzkHy0Yao4!4!QuAp*s?ARZw>PJ zS{_$mHxEbrxZCz5>}*Fq;DLgAm}+il9n?+`YcO2%Ozt+D{NoD+l)uH{@UpoY%Gd&lny8x+;M!f46qsh19>X1t{#!QNr z?d|wx*un42xgM{>9xh^wuQ?#jxf@w+u+Ij_oL;kY5hw*93ttT-W2PADK*a#+k#9(m z@9XYm1d{0e%6*OZ6k=vxL7%!NP43UHJL4zpS-G9@)#xBzKM^UCl3@+w>s9Wp+{3vuK*mnf08^pbHXx%U*uECDUpW|^f>WuEgRJ5=vKh-9rxB1Oh zSG5k=pm;g;0Fc}nM5av_J55ME<;KM$YV#R^Lf#Ei?oJC*KhlCz$XgJ-@6fj(GL}`) zkxp$FohA&-!%fc$5*+(S!3LnAB}Jt;HY5ng0t?Z zfY)(*vu^wI<;@lBDB*J9(fR4+>$5v}dDEiSul6DzdhOx)^2OgS-#lHNuHbgm`5DCZ z`ra)$cbeJkG4Sz5p>O`m&y0IuPtG?_Uo9_SWr~)bbbv!2_W4V(iul zB+8Ro-Mje+*T8DlKlB>d@8?|ulg*Ifbba=>)kz4!c!iPVIy71%IJ)41)Eve>zK~mi zD0*Da>&gJ$G&)@`H>;CxY-O>&27;-Vu`u>Ck%JxnV4>*dAYASHG}zZt5h(k{&8mgvXVXlAyj#3Ys& zs6Xt=pRF8N;VQq!h3ie}Ot>^rd#;cn!H>Ioz3=q=#qzY!%so7niDG@a+J0I2{Czs) zz4zu~4PAWqr`0<==Y79Ep&Ye{ThDuNhIN-N_htO4OTv}GP2aD>jRV$|c#RAf`ZwP; zZ#1;6n<4DZ!ik(dDsgbAFz|obyZ3(e@+D3r-mbp8SiM~T%hnT9Z~o5wTOZ8VR6A?Zb9x zsg^<_y-=16ou4wkhatoNi1nV|jP0}9?=KRn!h|0t{Pyhf^z_;JA!X1Hzx<+x&}m-X zWNePREBaM5knGOtb+XL7?(A){jM+HT3-?w!_rh?lW@|E@qD>?5B+JlDm5VDM)}T63 z{WWw04=q<(of9Jc=POSYe4tcocTS2`LoZ;J_7I3Aikj0ChrX}E9|oqK5NJX<8W8#T zoHT_2m7PEui=@?`FMEL#+H$>`j1HMkK3zJQepf=wLP(Mc`^drg!oCHJH_8x2tMkT!pB!M_o99?~ix6DrgclY~Mjt zRYf5&gJ*#cwRTB=D&11zHjs8}s|GB`+iq@EVA^=Cf-22QW&6$71o(VEuxZzcN+VDP z5=|5ylA1mn8E{7u38*q-+D`+h3jgL}wOPT6^MEx?i;GbWc5ppcoKmH~sf+{D$vJnB zDop~Es-$-}s8$uaS5#?5=Q>j|W`LcP_S&|Qy(fR=qWl z586jwIPREZ*uCC+NSSKh+%+TSex(PnfPwz>W?ex*z7plM3)bSA#>ljAk}uQA z?)08F4r?_t$zIECoq2+^eN#}nTIbk--ZjbXblax(WO`YrDyKluUa+YL3T*gB?$?-a zXqKSO4V8*<9;yIE{sAw%RC5IieF2U=1Z4Xsn%*NCB4_;*XWuV>CO5Zvat2!x@Cys4 zD{&L@qkj`WBS7+mz9$)aU*s0L@p*S0hxwD)PoGl=&yC%oRY0bKj>;pU&T2*V(jZ| zwlKh`Vl#+Fy%D8$T6ohX$f8#0t~laiI@ws6yrg0}JY#buS?|`$toLB5a#My%XIrJ5 z*kBnl2a)QZ5lU2B*%5)878*_%iQba%SAWgKG)GtV)!mig35&}GW_pvqB3CI$QBtd$SBuAF5LHBlkJPD*nu;zQK~LbLqCy++Wo4femQ! zT1F`5iSK)um_$Xo-n$v0xof?kvD|F%k`1i+syxc-+5^FP&U2IR9Dao*${sM~6=z9X>vioMSq5BM&r^(JVdU*Wxny1muyWH&a9OOu0?IEV)^n+ectRy`yL58WT1T*frDLUBQ?f z#Md|jEDnkTwDyldJ)IU@JwG>${hdkD4Lp$v=`jF)%+J)VsKoODwzPrtEKD&@PsrceI~1`HYc+hxKAK4v*NgM)*;i(GA3ECHRn$T z&T{_n&LGVoi_ESbGJ^(Wk$sL#mZJ>|q~VHVD;FzinB<(f#e3Qoe7d?=!%XbIRu=~$ zug=1x3jlVlz8X$0)-Si;U&7q+_3B&tb=l1WnQl2Dv}p^Bvfj8qXq3kF6#loTk}LWu z`Ftv4f$_XK5pJ5nXGmfi>;a%96;;-f$W(Qnak6B)N>%C&Vs89E-5}GkLgt7I<8G`@ z{=f)($#F0gaMLXfQ0T&0WkpU9*k#?PJEZc>Et_V_AGQ%G-wb6cD1Yc4&D9gUdLpx3 zc4j{9o5Ap`4wOdy*Og~nI8%Bcf(V@{ifKPdQ`QQh1%qY`9?OQ16qkhJxQ9$)BGihv zC@u&IEDw-aI*24rVOZC$zQI)uQs>PQ5eaPGsBk*_Sf*q5RH-_Ere+_q9aPBxk9B`q zZHL}lz{b1rCR~t6qcqVi+*D3XCGjjVMi{k35B*MZLZmAW$~u=(mChQdcYb_Z&1{ie zw@x5`=;o1B9gLGjc7Eci)m!=8u&U;+)Ya^=)pcTRox2ejFy3PVhl;cBbtQ+wgxht` z0%)S`0@hg&8IR+s5W+P(0o?_Wg|5yzxH8Uq$3u%p3{4V{RxIQyWtu1Alb<3JGMTaL zfzZLn;DO=y*)KQS^Vc#C*{OWd!4x3+(*S@PF@CZ+byP1+`&^l)Cpi5!5wUFfSX>pu z&zh-3;3Jz1ulnKeTr%ryI!Myg8T9PUyZ^J^zRC}%3sDwU1yhvB9@nu+*{TeCYE!_m zcx&u5GiMcOIuQvmRRw8qk+~2uRe%a7-#jLHM2wSPR~C3W`kY??+APS+$E$2Q$Kc%| zScZYt+YC0)cuzA#Q3alI>4ZQ@$jv*FLw^+~xNp;=LXl06GD@mXndR6y4;1NjD2r+q zXMSX23&m^sO%{kTo?gT3#C0t{I1-J!Yf}^pt%sM}S3kgdYVJ{034ebmzuBeYVk(OH zIu57GcCPhQ3Q|X=4y5e)o#uET?C>Y&1Dn}LQRFOUoCM)%?IgPh)O*_IV>7&Zy_R_8xojuqnHvkEZ@S+ z{{p64=NE}%qO2)4at1tpfsnfyIuIMPb3I(1#rEGX&$r8`uqpcF><{m@tIas?1bw^# z@ZfE&_<}3+`4Ztj%}(nU#GCaQ|7z4Qo+txwpq(dO zPpSHq+UWG;#KxBNGfwF~?1?#w_G}jtZTX{$7hG8THZBb|%muY|gwm)$LX+i-A9ppw zVF?rS<4c&COE%H&nGMNvbmeRsXYG{45Z9)9GNSnlteQpYf-T0cIc=fkPNUVY^(i_s zn43?K;90mQCs=lbSn2K@_@s=&JSbe2C8Cqm@ZO2BQ2BD^$$$m-xI0rec^MnCVvdw>Qv2l_W+$8W^tg&~;iIrs%}f%$B=GOfO$_Q|!)7xF zXM#f`h59BHsdUM6bC61`TTVVInW31cher4@m37pj#}g!uj_|yT9}7tl?!`2PJV zy`%0sRHc>M^IG7U-x;i(kb{8z#z_L6{70ZfZ;#gKyGRw%;Ld#%+56!A;0d`@c7#WC8!bE;pa(nq-_?8>2Bb&@G z_ys*2SUE1B{lpXwYa*n~ATp9v6=6NvM{wQ|lF^OeNkne(s-chgS>TKW)S&hkwQ@u% zWw?v@xL{oborzd!RBDiGjcO^>0VF386;5cRsd%D+e!TdehFk{SBSdV4tsi8>D<~m9 znU2@`hC7>w*RmQ{AD((<9k9zQ@;2Z#Jda4WQY40Ta`nXC>|ER?>KUJGTKa+^?Q&b9 z-hk;T--XTEy!A+;LI|X41IlT+!7)oURv;M6DyK!p$7BL*pitY^B$*narWK8B9fb*d>8dYniEbtUE&Bj4PSN|X=l*kf1~AY<1y z@vxD-;n$!{AdCkrw0&?4EtD}vJys8gy2Nnnia;Qas8EPSC5c!g{pKkbbeI3oJQ+xV z))LBq(S%_sL}ZE+tsa=ccos_uWaYFAtVu+YRa1h3CEVicmZFy@z{CUkI|wHf7V-&d z3prDK>TmBz((I@vB0!wJq+%{KJDEmN^OYhiVVQ5^LQ7t0tO1GR04C2;R$#0kLt_Zi zo!Sig%{P#p_G>t0AXtvH9ZLGmMxaiM0Yn?CO-)*?i~G$ske#+`xPpObsF8zMKEXl( zI>ti;R3^mCb*LNPK>9c3AbKyoaIl%1zn^%7JS*ukshW8}MpnI=C zROSTPdR0jev9<%itc!Z6L7_1_(8Evxw5C0Lsrm=p(tYL83J%{z||^>;|ac31~GahEkU&sP)hpZc~vU zhY>csSeBsFI$(HBMGrZuN{L<6B8mrTys{em8t8F1qpgO-4k1g-~bnjyl7BR7p=#?}=h zpT(3GWFtF%6-wikokq#*$>UdD6XF^BZ_7QNP??(@JYy5XbQnaQauTO^X0%`#Mm9z+ zXaVO|IxnUtOF<*J0>{9s)Rwr8zPvJY*w8~6#r^h*S*bDL$mZNFQ;qQqYduUj2rsT? zwLx=2HF@A__o}U~l!pZtz|0HxcC`X5`r%}UtAVKXrD>E2H74D_Nd&Sz4+OA8G})P$ zx>pAZ<5%A3GMx?tU!Ed|>=5_~`?PMx48=<1VDt@eVQ3goU9yAP(I-9v$}1C^UNU?& zK7m)LsZ!*dy)zaV6KXuRb0Ag6A?dr_M~k$A1q6n;R5GNC&9sPH4}Ks(RaKgDySvs9 zE4;q7TGZQlmsbDw?woNu#%v1JAbM1F%*C)Dfr#s)08qv?1>D+8Z3-8dP;L^>l z>ChuJ?hvH9JgbM%-lo@jMcq3@EnpR#k~@OeetEMzr(3Cihdw}6fh}pMCY3i>TT7?K zBmfpon!9Y$fjVOnjIGmT5iEdCPmT7fj?=u;p#rcX+@4>(zF70*k`A z_v~Ao`$Jm>P70Qp?_uo_H=?iOzJ1%)wcfeE1d%+MzITsc_?QGw25#)~y0EkSBr8I@yL#6w!!XSepD z-i&obk1ZCQgU=$jH1z?sv`F%`>kK4$Mxs%%V;B-}juVD~F=eO(S6ogxCr)L2N8TJ3 zX%(HVa@QHeghMmF4Bix(4HeSdSvDDL5F-HzbY&1>J__Pi}^^3jSx6@Hf|p_Rv38OJ7(|8j`ndy|X?2 zMGI(kSz2rhjxnINAq?RUxG%cLp(&tJ%DhXyCzo;G#F~f#bht*xq5{{1GO+k1Q;=Y4 zWG*bw>4RGq%K{0;si6d??K%Ik(9KeDiwiFP!{y7DtBb{6M?Cqa33nUz3Z(y?JU{3m zz+gW+0MrKnp?~wHt~Gs4*(H|l(fj0ofJMvE-!EYeyM6Znb~0Z{2qBq9apI=x^`?>K zBF{3#m~Tr(lFuY*2|8v__;&%4ba;h5-z?6zuT~eVo%h4!;R_JL$Zm<58+j9*S=C8T zc`xHE>c=ASi96?WC$2>YTF$As+Y z#9E0MXXA-1%q!cx&GkSs?$WK)GnikJIT{D=bq5i+G}~KSnEoUqPnu%Pn&inn(~77tWH=2p4sH=Z)7#638KJwRoqR8FQpsRsWRwY$s4)s@tI)Y()Ms>$LgM!OH4JeV4Hej;GUTn|iKe$_f z-ye!kc6jfr`Pg-A{};_%>(2k?)$(F>@`rabcK;a^-Y!I|VC(%A9s#h0><=srd|Z9L z=za$1U$yWkLt$8-J%gVHf?tK5dV&@bzwk$d5OOz#!}eHHb%s=)sMw2&AAIDbKL{CE zzRqW7qmQ0>Y~q%EzfjiuLH>93Amt4^B*ouPbnNM`tiGTl+rM#vor7B?#Bt6 z@Fx1$fdaQB-oH6l7z!2pXxyknP55yqvpYBLKE@;yOujwt$>+Dlh|WE0DUfdYn5E!_ z>_zth@MG$PrNNHd0x|`4-S$0!qKZSWSJFm}kGk&&G_9rQEZ$xjFnC{tYq`m+-rL36 z+dFwFtu!8%$h>9Y{Up_t#g5>aJ@qkJ1`)T=|A2cn?O9? z@Z+1dT4NGrIkUwz*e2XOGRCkJ_Q>Fnr)i8e*GWSctQp&~@gko5OPf9 z5G=HMmK2&|F-Gi zmn!Td`A6@ti6K}jh9E-foKJc00Kq?UcCPiLDt0F_9v5vS>nLWl_3J&NYKEdbQQ~tW z=he=hLQ!!96%knU6^axlNG^Uu@gfjKzLDo{K$W$_+`$2{=$R0a}R`AL@G{w#Z!iXHpF_(7!{ zJ1!1l(C3C(!RSb=khp1?h5P(8%sb2U3aiYd%>c6moimip7@P~6%6O4Vm`hk{r3@>i zO0Z@TQF?~rGUs;kI2>o~u9eq*oaUX@sYP|9#sIp9M20IFf4ZOvB;KMArvAXpl`H_y z)}k24TTz8A&fCg%Sk%cW8Mfr2?X(kByJ&>eE8@3ZN!GwIqJO}|$xNqn0_PI8$q=6Y z+|AjO1^4e)ug|xuqkpVl&b_fyW85F=5_aq8T%+i(K!)e1ss?|y?e8II6S^szuZ3k z`{^_Idawx_={nu%oHmhH8@a%v4CA~cJ0mMOCaAl@@{9A!v+czUF*Dq4vkg8#ezOtQ z;`G7F~t5_$i&5Mimo9+5M`(f2my#nlQj}~PEzFMt+dbLgLm~v^@_c6>)>;e@Q`GmNq zL`}T@ygu2!8fTS2Zk1E@HmiTUIp17f@Xy#xxk&{kt>20L&Gq@qlk-csHnKmw{O0oY z^HugMq5{DtyZ7?=i~x7yn@L@tj^OX{;?IbVs|iBU8v;A=d|gUyfIg0e z6ShlPJNPj!CU@$>=899OxX&>&i;(8ivgN>Y9$X;vU$pR)LSD}Chb~=oDJ7-8=HLRJ zr1Rf23+6v{W{EkXTq4st`H<<7eECF#3p8NMmj*%pgdoFzpaYk`&@l=ycpUyK*f1BU z1@RXv9sljah@*~WcQvO{_EuJ~hxWmFm7)+$8fCrl1#9Iyl}VZLr^|Th)9%@V(He0v z4rY)(3&Fcv_-GxgP<)gEjqBz)pgk^cUD+UezWLU9zp8CswvKL|0cj}6UnNj`uHAW2 zN=mt}4hUK89ksFvn=rfcG*>}GV~LE|ZST&7#oaL*kqw+y5ELkGxN+W+Y1@vGX?GUp zj)JzvJ1i|X5*2r6B(AH+#=>?>5*{QN@6N<5ZSc*Hgaw}g?9Rn(QN+EBmKU9T+&m-N zQevN{I_LQaTxe4X8!*?}oq<_efm;Uc7~s@y_lD;hC+z?vjpJj_ACE zGJWDIw4RX9y$e(0-STyqnw`hE?$OLc{Fu5ClM#psiBD;zKgb%zQw+KMfR47?QkrSE z#WMf(3gD1VeJHWHI0bbWZ7EC+y%JRQVBk4hRY-FwFH&~qHGAo_Yx z;>8BpJ8{0tK$|AmEg(+2k=`t^A5CvmW0x{<3kj}whw(r zwFr2{1`q+NE<_+#f>5fXAB%L@Lz(Ri5p~}l;!ocrqHi4CfE$K2lAMAqi9C6$$epo5%lQ!^KG9rP7=Y>A`a{z7nPG@_XXomMQxpwM+17WKC?8gVz= z5#GyU5uNtPX-qe$9=_9>R+k#BveVib&1lr}|v8UVNc}AxdT`)$mks5KuH1%@Q(rLwkWHlI@(;hhx&S8s|ll*wS zBc9E0?RqsI=EVCBw|h-IW41YP3O9y3H@s!~Wp}e-woeB|eawS>dHa{=UUnmj=0tG+ zqAk- zA#!vyyb!5op$GUNA`L>bNGNc?=OB|~M=5KRGWN?@krSvyUZNNi6lZX;8TTR6h;c{W zwNU3@bh>)fLf7ZK+WU$;+u+&Tk0S7L+A!3|kK*V8*{~Wvo}q^@P{W%- z>_zvT5ixBDfQ~KhJ40kWl!d4<&VIWO&x=a~FT8YyW$Z;r=TBX9hHCCqSAN#2Ip++G z`=ubEso;(?Ja5hc9oszdD5^pQJafR~hzjPI=z%jF?jNp(OdjNQGgy;8EpZ&4ob5OQ zEn5tOyzD3ftwjtzy46v%0Ov_=G{a)4$>iVItbAvNXd*0OrBQ#}CYu7+ z*Sdw4o^LQ4@iO-T+`oqzFRUW^?;2F_01X5guB&?B1}DBKTqLo*P(4fj7+K~vvzB^-4-GmyAL;CE)gnw-(^ zTnQfC&Q%c5?OX*u_?xSssjr!V!*XIzGXva2Rr;3^On%=g^EbSjlt~htg>N>iZTJxr z`DV9+e&!*08@=}rzXXDX{7wM8Jwj(USi%V8Xuw0_W!-?n-*oj~ai~aI;bR48i2n$P zT2i1&AtTpLhn{vS+>ny9Rdjo_%iS>m=~hiz8uXI^Xv;se8T_r3P+D*vDwvqAu=(FW zLJA6OE@MP;W)BDYC!q0nfkj1(6dfA;(s;Pz7)TVw1*KTS1Z|-$e*`jGQc#dWLAG_a zI>3l7m8GI7pb&5$_;8?|^gq+hx=6-BDoS3##MCoB9Q3b1#)^vGXsHlTHwhH<&q%_` zN*Y?sM8I>x+a{ayfQVlO3KTFf`pt*%IPc%0snKTvk0pYDjuSBOpU?t-mpBL_{h~!S zy3l|^p9Kb%FhcO24~Os(&|_&qB~&mm^{HTzuqBK)cLEtJDrjga6JQ_vaNvIfvJ@1k zT*io{tA!tfFYdG`A@dmrH2@K|YH=wg5p0N-;F+Jcd7-~zQiEAd_P}2a3==hb&;b z1tFoaqUJ$@O3{rXgit5dv-7OGcUv zK)n_nL>tqfLo#k6Pb@8D7Q@fLkw86hT=cvMTf?AHNVwKderhwy zxs?~3YpD^_(52M$yCG*4B|R-xf)xB{aLNd1MFqNrK}@W{KuBO!H1x6p*Fz#8b5LZ+ zDl&$`65qq)Fks|U5tLDq(8ogM5aXlJ2q91f*$#gMBF+Mcgqi@N0@lbVE$OE=qr-3I zMW3})iD_V^PBzgsmKJ1Uh#jh!DSVYdQn?E~l;~2yb>vV&G550X4J6~nH?OkhKGB-S z*Zf&j3%B<)e0CmN#drx%kgnYA{SigoDQGqFx(C$hsH4 zdI%bJ?nxl&utaeK_~3O*2Ppb?fQ_HM=FR=zQ+M8>t$rKm>YrVj?y3>mzj_GjLjhX< z?1HL$L|Fa12TZQ>9=gO%r`Sc_BgfYRVMln69Ab|SJG*-{+waf`jt5u|Up<=YSpZu9 z?18G&xhHOov@R-$ zb1^m)DlY7RN>tbRqnLX{b){<4Nd7XNqfZOB9y?i47&u$~agS_B2{5|OUwG5yvvNkYH;+C0MUpW6n`aP;;;=+zZ1|}R}81A$dG+q z;CP`dL1CpcX!%S4Nh1x8zY?&htp;5bCP<>t2Cv^4sHnTcYC#6<&fDQbS%RYWV(?aw z0joJVw3Q^_U~UG8e#xJ>fS~W3`;ubt}mgbZf=_ z>-xtsaRcJDwb!6_co3pU4HFO4@;b8Oy2?mLtF3|j%!;3+tLw`{u)5Z<-59$tYJhzJ zwYeI?4zB77>mpiMzY-0rTZ;zOtqc~_g;A@i0pc-IU1yA!W`SV&^(Zrn`0Bn!y|Ke) zI|CXykJ5+vDbX~&TK8vQgcvv3DUEm{F|z6hOSki&iAR|4J?ShSL9 zPBpSka;dA786@3?rh(O%W6;0P;{uabzLiwB$t#TfZ2doI%R@b_oyoep#D1O!(&f@JeCgNB_XnjSW1*=fAq* zTM$_-SvvcNtXeF*0RZM`8*#r)t!Q9*UV@Mwi5no8*Z@S_CB@tZ(WtLNc0&Wj zlS*iz*dA$`P!k=&_kh|5(m*-_p#ii3;lA4DOJLD@iqW#c^ID9O{c5zcerdG@6i4uqBFi2aW`Yv~oOB8X{jsv0=fiU#X0z>UOqG%c7=-&|ur^wzR_yRz4 z04!&kAUh0x0+2fbfuad=0K;#DbCh>1TdHeVxwkFcpJ_CSbMjFx3i;7^^HAOY$XE1!RPr6N0h17y< zX^&hL@Z2(U!li-L=#?oI4V(3FDaS^XfLlyc<2F)Pxn0yXjrP|7Y6r1{G8j>-YcyA5 zeOR{%k=1WPncnK%QBhEfhxV&!h30BP7e&Z}gF3s{QnpBMjYQDmkdq|}Y*2+lcT%p3 zKxdeB#ANB!NmD+XbVM&x9M*Xaat0Yq!E&28?5P-nQPjyx(I|{$EY#K|hFLH(;3~MO zrFaZ;$SSCXQ@kQ@IYMn$kg*~^`QSmzptVXlHnR%B%7L5kCF!dMVV0*!IuyB7q;7te zhC{K*(2GcgcE?f4>fIEpOqyVc1{}(?cWRFwf(%-#6qM~#qmO|_vu)E^O2ajzvY1=Q zQ&Qqx%p+UPUHh*UiWAUO8=ZgSpuPEv&!*G27!>zy`WuDlNjhX{-kp*pq?{i4nhrs# z%ipG8w49_krVdf5&B-ciPPq14ofeW=e<2kO`|U$VoEJ`-6Mq*nMEzcq?illqMG|@E z*kg9+b7e^ySA+7TQHO8xupNq2mk?x}oWj#>IPg+ORcDF{-i!p)VHo zD2uo6k+#)NL7POaF{DS?!$OZZLg=EXpl`ef8u<`tIO#N@)#bn{d9CgAz(P}xxJIWA zb*=7@htSm_3^gT5k~hNVLclQBA%#^XE!ntP5K@O8&P{s+X^mHmmQf_MoFoR)4n?fW zStO*KUWm0FvH>Ee9soLW)ezlj0jp=cA=DwxfxSbTD@(pf2?Q$_N*VS$q#bDXYe7j5 zk~f25Ru!wnu9>dBxX2UfY_0J*-E|AOhqAWlq$*MX11S2pm-eV(>KUjsz{bxWiY+xh zr@Q7_N^1)cCa*C%qk}FEVDO)wpo5vEXm!s!<7`bRf)eZN0lxA*y1s2@-Y&czbK z)|1WQ>Cek|8{I3~`~K)Zj=n$o=F!npID)|+8^5VvpY3Em^=7e~sFIf%s(ga(Q{WeRO`d*^+R;{Hq_yzM|x?i}E8=!(x5%#qjMn z!{ehrA3Zx79vwb?bols4dPNCEw3v%FF!Q&`B<9|SUsxuQ-tk}F;sGV5uj>c6tm~a$ z;ySEc7`hzX-(ane8!h%sBCO1p_vI{-r%bRVil_76U94WN|AESA8?o%GRxe-T@7}!? z2vF<*uLl!w&uZ^pD1WdA*#Ucdn3?Bio@93!&uXPM%UW)?%NMU!C)lCq%gyQmngI0Y z4g6x@gNxPjL@|c`kF;aU*xSGC-Fsj4;h#K8`EPWIw!yaYO*27elrfQSobf?UEx)V% zq(Y5-Cw^F=W{T8UvzYN?-p?y!WMY1^tPn)WP&HY>wVb^Smd9k$jOz4@`}*tReB7{P z(rjGVK=g*uOpk76KNMWT%q>sZcw+?9Pq6v+$$0LQAS8>k@3jCcH5*NkQLJLajf z_cU3d^fgWawFe3Zjc3KHTfLWIS;~eio}sTn=IB4=*A2y9;9xw6w|){)^pB@c^?PL5 z*oE_MjeIgT_XjIrs`S(l#jat<-7dN%RR$PQesO5>C^C$_^wADbY}i8lBGwE2JkS`#T-0^mqOI<<=c((rMm5HX0Iahfw>b1^=S4vPP9&g- z5%SG&-sk9`KEh0R{c=zp)tI9(;unwyC=t|v^e9Vl-!?f1$=o_h?K)zf4|g7@ zZ1g`$;LA2@e}I|b4*Rr@R&3LHu{-@J(`(+P_2#a3lU7XZ^Hw8l_4rmJoUyNDXWdGYQ~D|ovDrm`AyrTb&M$Um5WrRh6t0CD7xG1>MOPGF{d`{9BJZ2$fwUysL559tu*uySFuNSvMJKcO9|HdE*xWA6ZGGH!*t>S*yR&ueJdy3_ zsuMr1uyb8fi3`zk?S;#QV&nRff|(b?*wN@?Nwbq(wvumTi_;|Eu6~&}vbm|!V{;Aa z0+#b~CVH#rL3~wqt@*y-tzdr}*`AtSQy`IrU2HqEMp|F`W7{6I!XXS#e?XMN#J z-ZDinm>Ik8rA!-B!->}e>AvnV%-=J2vm-hziJFX*(l&(dyhoYo%z{Xuv}yuWR-mg) zMXI#d6(hvXI~bL;I(rwafRI9_s@O@4<6Z)sbhiVBy}|S~_$P1hdUy53e2NteDQA@6 zjc)CiQcidFnaO&)J_BXh8l-$CM*t{a1H%vK^!Ub**%_Il&ca6a{>zTV8CS7=sxHiD zSY-szdSyr;+%qGX?CHSV*?kB@i%d5AbLu&w@IT| zHgn1D-s(U=(e1l8x3dH$Y2ivexz;mPua-WNtQMj%6Ne1mO^hz^BUF=T46&O&o7MTw zfto3aA=INb6jAQI2x{JI5TXv@^Z`q7@@isZ>GJ6VOC}sXU;#sORRkunIc59=s`w8> z9fX2Y1B6DF-ktvOOBrKgu zxQt|m3G|{@{s=1{I?rJ3&^TP5XQ-xJ@zen+vjjSXv2tShl-%M#vM9F>QLHM@M^F(M z1QcizeNb$%O^Tufaer-pxHQiO~0l_{DA!omEuq>o^{Xtz7G= z$a*^W8vG{vwj0a3CWkPxu?}+O*QI0&?BsBpY_S^@d$he5kovk^og6Q>yYYV_t#aoz z5|>-5*-*_vnZ3A+i41@v|piA3c3``1N;t6q4sl;PMUf zHBSNp`!6N|xcj)8D*=HWk$@hWz!b5)IB+Eum>NT#-t7Ww_>RVP z-FcuecfAK{I4p^^Q>3FQ$sR)NCL|y-X5FeAb4M1`Wtp%l121j~fp+6|v&GZC>tGmH z!29r~)YIkLRTcoKUie*ajyDLl8_!5-jN{gU?t1g)(I1Z<{b^YIPWk=a@Z_6e@%#J3 zzkl(C@Gh&Ld7k?BSM|)VX%;anG%@IiR|;a;(Jjr@-NzPNH>=gzEbb9-gm-QE<~?fQ z{nh!;^LPiqJI4E~^~q`$?+Ezuc#l)qnF$2sj@Kt_?=t;56c20j7Ou4*&Yuz!`2#gx zpuku1U*fCyLoJQJSa0QT(gXgR<%)mb)f@(K0~{}RPh41^G`R|2hZPN23Ey*zE9^Qu zfn!v0yUlVRc>z=13WGQoZm{X@$();QHE+5Bu|C=T1U`nwu|xR$^xsrkLi7~6 zEcH_3^tG(8+NuM%`-rQTw1(^@t6?q18s4s}C733B>HJnV1m%rX-fYy_ZdqwkWD}-e zj4|PHS&2|ghs!FT9GB%N$c$7maV}9*oPn!2mnJsZ*#romRo`Cn8qVTi{@L8JhQ~E< zFePIxPP8Jls?+|$=6w+@PKt6g8%{yPQ<~qNU7nskJ3pkK1{!|(#f?L!#??tvobcq$ zn1)C*n3E-4^La`{Ws}-pi1x%z@ysFu)BYJ2M=(^PT(npdAw610qP%Y;Di^_7)o}4z z44e;z>Y^Z}B4y?5$kXCw-wUET)c7Hy0q zQq~U5QVw~|l`aS|B~a7V=`iD|Wh|nX)$B!d3ARG?VmJ@}YB;%AzubO*3HK^qufBb= zg`XeUJdjN{C&rd4%-4*@HK7wX_BDe*GA7XZUY~YH*I~7Bq$mx?q5gtLBqos_JW0wQiE}WEdV=4VEk>gH`GX z;QXkeWEf?PwHC}aG8~t=f_YnF6k}*0G>MyQ#L5 ztuQAr&J2L#2+Z;sT+NscQ-?1ejZvnWzVKLF4Tnc!1P5jSkbz#03`cL=&xQ07&HvJ2 zF{_6p30*W9kd<)9ZG0dxT{lJz)=C)G#tI%w(P7iPDa`t|vbUHSrODb>%tT}e;dgB0 zkY2ej#+G9%{@zC;sHCCZ@#&slpO;Tb!t%4b&v4tiX3m;TH$BM>uHsYlUzcrk6B`YJrR; z+6A*E0+X+rgccW63S)yMwHTRjqMtP>D=%nisdA+zNlZ0%8A}T?(KsOuTg;QeYa5NF zC6QpLgP5pvsE@u6KX_1&%tRP2LIpFW)QsY6B|?uKXps7!@SA0|ioiuNfIp>pmv z8b5`&m^6VCyy5)K3ZCvhzktg*3x0oWIN!cnUEsWl2*4eL6HEYrpW2|r-uqhr%P&&D zAOx5xHuJ3P$`B^>!qGhbwln;hTop2r8Jw3S;3iT7_mTwML~h_-l7P#Q7_fdPpg3R# zM;{3gj->(7M*<|nY;gLWfT|K0tQ91{tRn_0pBW(eX$R!b1dz4M0QnOXEzMl_`I4R* z{XlbOEC2HA$`!{Gq91Wqtov%kK@rzHwmdOY7e7*|K}Rl~@VSf}81HLk9! zv!s%D9QGQC7R|cYsw=%~I=VdwV4FvE41h*+1b{~o0oqe18>@J~jdJw+%TKa(8ZiTNqB* zS|E!hue1moqbe9s8m<$YvAsBwkJX}~c$F9gBjSfw1?A>yEubk0{_YVFY%D`tQpQ*Y5Zlr?;w@1L-^Z282v6a}f$=abwm+=FCBn)*sb%6j@iYu%t_aWQ~dZZYy)R~vkV z;e*Q+nVEAHYhn8pjn~;RhFq^IJtWc z`)sstuKVXkFpjcs#)&m5=W!RnH`~Mt70AcDx0x1mFgzMu>w{mI=;GVqx{)raMEzW< z&V6*#EZrZi2!?Rd$@OsxZ<|Z%$g8Pprv<4;-_~itDda7f26tMJ`jHl#Lf(RyM29}r zcAo=K+?+C6OouF1l_EhOxgd<37@Ay%7*>_U#2GILDW@l<*db}rv=}yW%nQZL>8ee4 z2y^wwa2bA1o(;Z394kji3vP!nMT8zXS0jXoBuywthcMh9@^07&HsPaJDG?6!x)zP{tSO`jrPKSUT2>E~?V-CV zbTITQl5rw`>$htw26Zhd{oDQC)+RhbfwvJbj9v97Bh^4}o(s2dzdiO_UbNEIXy^J^ z5T0yp9-gk38~Hsfc;FB|bkS3!BL&1eagi+WLu#t$g7`7-Hd#d>9vI4OaLIwn&%2~t zN4Y-dswghr+crLJ{0lkw?Sa+bFV`3FItdjkwcERMM2vNpY{E8al`O1-^3j$)T%3S; zjD>IQk)R|~Z+#8>Ynn(`aw!KC|Jy2(*-+xVaCrhrCgzAGRSwS;+1(|Q7V zLetnPT|*apFJER$rS>WR6r^>#kWrPYuRwBuR3}(hLl^dgd4T-4v+Q>ul_F&W)8uN9 zT$PHL>cLfh15%0Wb{@>^bazdwbRUui`RmMFtQWS=y=dU9s-JJ#>5k~Cq}s`suFW-> zQHyEWt?9>7s2$J_2_^lkNm+S8OH0QBoH*y1Kl%7|Cd$qd-efTKqgTqNrK&wS`3PBK zDrlR}lCH#wF>XWMM7q{tHCIg1e1tj6^U?1FRHbLIS^)j2vVuaxrrKnjIt}U(NF_KiG7)ggd@8AZY>!9@L#r8uhbx>-hR)lVnJe!+oE&>!Xykauzpj0qHK;*L zx^CUHFeDY)ZSB)!z@l+)*C5L`GF0~mAH8co5E7;}wCH+9Ca+XGHQViMO>6%! z6USDrbr*C!y^9BKgbnZW>%_2H{`@-e?MQ%q&*!&&X(~Ze9tv(FZ+ff5&X%vU=Y6G` zWA7dU-L{!4!>}#0I<&do_~{J79$PH!_^HwNQa544Ri~gG6`ztgowGw0&3`Q4p5q~8 za<;@r@id5ga9ICRAa@fz65}rCjC;R7@crrV@~?8-d;E8xhwy*Fr zVcej^-w^Ea2>|cJKO#PZw{OO@-(Gkom5ej8nJ08)rJ4*v>X;keT5 zV)wJh(zeIWK&3HC#A36798fuayd!VgqA%E86wytO34&&C%?T|0F0ap|Zk~y9D(2ki z$hhy@ofn_+jEN&oteg*1jP-_{?nhulW-kd77EjPOr-_X$nki9r-#MeVIaBFBeOzECN6%&Am!*$ z>cWHQPrMRsGAw$U3N|(`<`&>i8_sdPSUYksm~LVvA3W4TxuJJsbs5De5yhmgtnw)y zB~JuD%LZ0LyXVVm;~bFag^3`2n?w-*_A)eJC$Pw*=8@cA+|sMCukmNrX^^^m-7V*1ZIr{BGOiPb+!mx5u3LST#sUh07=74 zhQi7<)-TewPLCvhM8Z1iR8{mLm73{pr-c|CVl(Ow_7_H>Xe>Fa0NKJrAn%$z`8%7+ z)3`NJCR|EPr{JJD+To|IPjgI{CMW+Gcpwqx?pfnC2?Gx##NKiqRB@OC0S(A#e{(R| zg01JlqQkeBGU`v~W&X56_Y$?Z-aH-GOia}cM%{X#w9W?8` z!CUKORi7V9g?SYG=A+4V&(*t-tfYFWT=)GhoujI1XNYP98kuxL&O7j97!;liJZfhT&S8VX5$;cLpeZ^IW^y zx7Jw=(lkt_xm6=tKepf%rV@;y6l5sNLL4nFi768ByiFJn2zR}_nqfd;;+qM2n83ue zltr92K%yKa%^X?3_I<`Zf<~!_O=#uKFN{}klB`*SDpCtF;nf2JvP$lR$#%x=X%YCu zZDjRQfVwUyh6fFyPc44EF%^B?Rk(U4DhH+zCLD4L6PEKJETs0a%<|5=lewgNu| zybAsa4G>%>WgAY8rqK<)yc7r|v;l;!{ownp07PYuuMTjrx6ImX=nn-joskkE2Xf_+ z*}4Y#ApRE7Ua-eootP<@P^z^I8VeerYGkDS$jjb3p6mH!M?ox_bw;)oBaY*7mHL@= ztAaRqOp^2Bc+GTR4tVIyF(%$=#2 zC|y(W=m5oX)9CScdwsstjh=-CN^^p|6@viGv*(fLQkjnT zIyvj@3tr)AJdQc|b0Zs_S0Lczp#cPo6R_CUK*{9`3}|X1==uV>+!eRwC6hurf=mpKnAF5kl3--AZo88Rjt>} ztJ&@=zY=N~`N{%^+w0fmseA3Ch2F3Xzn5=EGl->N3gakzECf~-NH%HzlSX6Peii)0 zx5oMJ(R3A>`l6EIRiP%nyXqaz{4_yvW`}n-GEdcXXl|ylOcNCK!~hi|R&TmK!$0J% zX5r$H#5OpE>!~|=-PEC+Yk`7{IGS83B~|yL3)6SDJgga+UT+cTV@(u9RBiKI*H)U; zDkeWfC*{__mr%Op&3Ywi(R!sqrL|s3L^*-^k%Ec2 zXbJ_(l;=hp+?v_GMc_txt;nh7fQx5q7^W!^O-XWodAi0(xa>NXn(2g>XL=zJ00m4N zk-*ZXS&$H|(HTLh7V2VHZW`h9T_YKCT`Z{X8KN)O5Igu%M_5gLIR)tGTh)m?uL%Q} zwaYb(M-7hin6-*Ed5UqVMm+OsZ-g|~&O|jmC?sm+0oUYw74`wfM~o5LLiWR9pk@R{ z>Qf@1#7x(c0_?&FzqteszAueBFe@;a+3X zjr0$UE2&euNX#@GZ08z=e+N!~&wNJK<-krSn}e5vzr^m_4P`S+tkx)F6;h$q)R z%T{PZ;0}Iez>5hS(NaP6a8y}->KtEx(tlkZy?D7=XVxzT^2<;A>G9c{!=pDZ{_FVt zlUvghchG~U$L|l`-}yI3EpklE-I&yHJjY6cH*;4#5P8anG^=oRw4x?Abc-_j|3hfIQ-&V5` zhv&IM{4iI4+rY;ZBHfu4CAikK&?}2F;YQo<4sOY_hb$s|UN+j*(v=-<3}k22?b^T9 z?Rs*%+qDl}_Btu6jHL+>4c?ri64w}{u0_%-oHN?wpTC-9oSvuKi~GhBx!_d`9{_w$ z*{5LJ$u5BJH%owIAWz_HuiBNx>7Liv(uvv7)1Uu*bN1@h-toPsr?hDue)}z?JdfNz zN_@mhV_}m)-#eq)TbBeI0c&D$V}Pgk)E151%LdQfz=l-BIxvmHWYpP#RFql}DjS&An7O9FuC0^=CvtqUOA-Yu<1`X<) zytjr-MDrtoiE?k!6%2E=`6hVf zDH~Ky=bf5^;RUDOYAgxH(JbQc$l=N+09Te8ygrl&$a7%p6LJy7V_+4nIubOoYbhRE zc)n%;$j+pAY~lHu0wBAO;<1J2Yo2ROx|0V3zFOJDuO-f*AqkNYnkg(5RCKh8bSqd^ zKinA|((}3MbWOxQ{Wn`&){K%rf{e=CqFvIF+JZ{oE+L*?s2UAU&^}u@xMDT2iKJQp z5LObK5Gr^!kPEpok4Ms$nV}3wUZgV?Y6o8y0OzM(i;8Xi0SnaL>lHx$hS12$CGS|Y ztu^TWtu+R9y)569G^`%<$?dfUC9n>2;RRf&i$Phf>p}O|8hnt=6(Xgm7NM{{m*U@Zzwp|{Kms1 zxa+OLo38bWdQs1~uos_uqW}==4kD4PuQ3vdAtI5i&LgqxA`;132PuVwF!G_jrdL<0 zY~rAT8tW_;5f(C^(I^6keTN23Zcw1f)g78#8IuD%710<}7O5>erui)>9746Mn?FAN z-{rH_aJ+iCJh?vH*^(KOOQ4&~S4YnluOx-DSJZ4DGy2hItydhN1x8jaI&#%RD`=M%!Vh{EHGUuG(x)$VWk_d*^RAR<)ayD zK!Og=QWaR&$cZmwA&l(g9Jl_5bPX;kaqELi7TWsYtaEddNyVCg)#I5;d17hTtCEi6 zAKN_H0;~60eHmQIJgz%dTWx$6bl^ck~rccR!q+u8v=u!ODU;Kk3N$TC1-R z>lT3f2IUAquw@=c+1dao)*}dHu|?meU|hVXV0`yC1><54Fy=4s!rGXqf0Rv~otk}W zLAUWd&sI02h0kjvw&w!K6>q)-fS^r^;oXRm+5||_Xjxep+8ZI7BgxWc#eAvxGqZDbo;7wx}D?N9j}|LXTH{@;yu=4bhHtBU;ih#ouL_jk65vHh0;X) z0@X3VQ;7=%WqYB8B?_)f>A}jVIk`%YNjH|Pz&uyHy}&$I#J#{gSIoVPu%!sZ8)lO- zvmGh>-PN9(ZhH)|s@s;{xjZ0^V+ub=UBuC$*XfW}S?(e98(Up3Bz0rYpx zB5?swJ(wKerMk4v2u7eiM4>XBK^(?C-*_B{5L?+{7Qj$^{q4z;v$U#}b{Kb%lOuVQ zezfLJg=Z6C@TrmJQ=Ha#r?p0!&xq=bl#1VBodT#=F4$%s1+u3aLo_m}Fq)_|E*N?b zzkjjnb+Zi=^tHcw0b*BHg7iOops9HToDY-Wszwg4Pdx;bz$%nZJH<|b8<76s@%IQN z?ox2NS?xajdPFs@@_W}iZ=N61PA>p&4Dg`ej{IGN;(8es?l8!uRJZePl+z)m>vk_# zA5O$|Kj6`hi01BHS2gKzXUJwP5g+_46uzbraN(b^U9q6Rf6&pI#TO z5e&JSs-kJv=7G+BhC&`O5T-y`$rB%VCao8r$a;(j9j>R){w#t8Tepq~P`+>t5zIss z!4KQ`LF$JnF8}t`@-=O$Plug1lqF2%sjrskIn!CHgCr)CN6OPIHS82OO!J?e1Wa*K zTUc(^6On3Kn;+c+S;Y()GC5`dn%3g9zMd)+ni2ePT=SR1;59NL#@i46PwG4r!c{|p z;wMM85?t|4WemyQ(WlF(gvGhk8I8JIL+P8?VikilHu_#NQ7z?HTP{(}6YcIyZaiDg zI23_pD3XJ#8jGlQ0C=F{>(eGdy@BtLfow*q+`3}u=I{euAb0eP?+oOkkIj#=1=wAZ zJiR*l=kkO!HU5qOVK!0F=+1D+Ke7JkjB-3)FMoWyI-*;FZrTk&Nr=^XpzD5CXD4q4 zi>lrje0Ue{4TOeDNlf_dvC4)o=8I2w(ZU4Ae(PSQB4kr*aN4lDqifku88kj=NOIzXJ{geQTt?xK; z~gmhD}J8cXQ|xK#UHnftl{mYR?49JrahMtDsM zYQR%3KH-AFArRNm>%hL7H-9}jS}iHcvvED!$x7(UZJNES-keAcFP5v}<>K_^{o_Nr zd1hS|h8TvFZuu4x`hYpd;L3%?gOm~seWi4&2U0?WG<1x})Yd9mW*$4n?y;_dyL zdxXOA>KK8VQ*WDN+<&H6e7S3@ zc|FJnG>$Cz`wST5`1W**gk#$~A-BIgd~i%j>GwEuc$+?6Rv!lUZE`3;c4?^32s(&2hE9i?(T_w5`Fg-8d_DWbWcpj{7%eE7~njrWHAWbDCW)jRx%!+_@L$Dxfn^#(w}KHbIb@DCW!9lyhU z$(&4My}5D!@guszckf_t=ZEdxy?a0Wv>{*0b1N0^HpK6Ut-%3<{X=ko<&n(F?ESiv zH|tKGcFhQDEcM)4$xJoxqp?QZYXYh;J>mR(b3HtxQX~pL?Y!AtlH4AiPEXsx502g( zoGQW>eFrzY%@bVfG>%i4>3Yp)|2RE+-4Ky- z;ttrG_@sEB$_{HS!#k&q@?#n0&z{weS$|JMeI5Sj#)VkU#MYYlCtqoP|F7Hk|2Ay? z38ws~Vdvqn`R5zMt?So~@fL_(&pZ8jJ#Qoy?)c=04z$E@G=u@n902x&A#7{Y5fiQR zadgH9<6GkX@;sLp^Y%?A#k(W@tM?oA+AEGp|5U@Tf4eme{oP1kmx&NgTpJhAiK-ex0SMPNksD6+Y?F+JNfXpOWNAH#jL3#o3eojd7?u~2EeshE3OdIm@ands}X%9`X1YZ0UA1y4uA8mG< zS02k}N}l1z4ZE5xbfw7@^4ZZCI@IjqZ(nZhr`{hvA{tYP&G_e-3hd?^QxWrW@o9vO zonnUWt#fd-JHw#%?1~4s-ST+!kp;ctiTHT9{~gbYk9aPs9_uP6ADa6bTo2VWLwNPW z$KJFt33pXjKMNlTSm8X$pGdp-G;%*Tr5UgXgL%Y~fNQ?^Ox99S7->8a*(;9@I6J;( z-8`%g`WMd0>dZ>KzK1LMVeWqC2|9RGr`rRG{P89xlepN4WebfcfvnaK#z$W`mBgc^ zzpKHIs!fez)#wH)wv^d#^TgvsYci`*gWernzp{r_0=}rSgQhuv0CI=M!6vw9M?4-N z)ReT`U6<{-*Dl**OQ4!xb_PGSQ_c!uGcM1#BbSB|4_lOdlU|NzoP^Pwg-sgOF21A% z++^f0myoCXK-$ob?qcR1F)Ws4r`tA!B(d1qJ%{`BDa;vH$xs`?#RjrrsT!BFL8 zK8z+3nYAH6nqKZ9tKA7-+&-z28{v zN8p4NXd|Y%Q0xGYT+ShnOx7b21_Vfm?zhot>XArlrXwb=T0M{FW+4zqiaAeXgaXs& zkCFM+VpWeBn9i<)XP|)W3OR)xOmkqsPrYnYQ0>(fPgo!TO4_CX0tc``K{WgKbfdOt zTJfsY9ogchbpt{dR*RCgz=bd3s{=Mws)djW7;qs&8>vdur~#L#s!A<1RXFv=F-g;) zKD;^DKp3k_A9n6A}luLKzvq>kwYb%wA`sl(%Um^7KZL8(sJv#}=ju0KifUK`LE!J-k z>AAU(Um)4EStGOx2yI{110Tu6B&NTqe=+#8tvAlyx+vEnkJ|cE;)SI2wpeXoBPZo&PJMoRCu=w2_+@y5Xf%E?(bB!OgGnR<5AAGd}ELs#cR_&Yg&H)>p3TwVs z{_#97ilOWCJIt|{CPmWK{Ki6)fTkUq^yRtv+NOx3b)^*c`h48U5m)s;%3vSC$jk!5 z64*yzI%ucfz@&phMs`~4D^iG!%L%dFB*y1QT#BZGQQm5!hxAZpV*DM;kxdE?a`g>< z?gs(@FoyWj3Aqcakm+&4whl@_>G=!~U}rS85AWcS$%wKB74k!BxygS5zp|}hSgL@Z zPzoRme~m0{1#+qg#Y0>(tTG~=YcknU(28bVi(+#tQwrs&31&w@Fq)m9shbe1bq*Q} zBA|GA8X&-?Zg!`LmwL=8A)QCn`eghXwfk#^rbRu;7*ysAfYVAz3A?mymbaiR*Xj*C z@6}C+8+N+JNp;mQ?Wsoh=7NibR+FfhHoK&Zm0RZKV5Ld(R$tXrVt=Jjbe|hA#xJr@ zm=5cbtPcdrZzDlVe<%d%j0GS%m~GN&-gw@t{&U@Wohf6{uH8}FaZM~j=eiJ9c*@Wf zz3+g89ur*r-7D`29VOuSy9aFgdrft+GrTGwku%rV0UN0hVT{^(6JhPS#wYV_K8BuL zbGh#Tx+oJh|FcIk#gT^&>Ft4oL=ikZwP$>(#Z#hf=~mK5q%;UfuD3jbrM0D#=JBFP zvs)$Survk}6(qSN&%g!NGSM+qFWzttmwJ2ogbKFGx|nBCekBi$IxS{r0LeoGC^#LE z+0p=yl?d=`X@JX01*kqXz;qP@&K?_pqa_ZUaZdw%wi?c4B&hmxgO*EBz(zv@IMpz~ zp{aoY5*QfJMiMz!kPur$k!U~ed;FICWu}cbl8qWn-DSVRkS=%E4NV)7hWEv<0VKu%9~5=J zuniWL`zMbJW9hI~QO&``N4p&7sXds3O7)+L0tmrat;x@R0X0J~H@F*uIk?e^&d)$H zqx&%gnWd}y?3?>?uQ6=6mU&o^g9%_g|LiDkh)1Vj)Ha4JFuH$?qgIkc2s#Nn{(zzo zuWO9(%|YF6lAt_NW~>v55=gM@vx2nllG7)aDY+QEO@4?#r>q-EjCQe_XP1b*b{yp$ z7;eu3$=+9XI^*KrjrLaAB0Mqk)YPbC0Gy^MXXrDJo@%?6U7Pu#8Rh{ngP!58*dKmCTM35>N4O(eHcv4iTYVTN$a8cysr|wx#`HOvCVKKC|Z!AXrl?(+{ zu8jwgDcVs!ijK~rD@>@quXWX2tSk2MRLV8f-i{&A&b4g7++vhj4Pp^I(4Gt7abGb7 z-i;O@v>)_5LgLZsLKX|M#sLVIQ&ZieDRb<3cusk=`Q*RUN_tra-PcUGBHk>Wa} z+CGG@GekaJu^psXa|~?ydIvFYa5m+B0yQsL1MSkxvqpQ6vfTOM|NWOY;*`j{A33t ztPH`$-@S-^s5=42-#uW_V*-?adqAq11f_rUz(#6C7^Aix^1s+gNQ7tkBl+p#ba`*} z%dNk}EiqBi>D_XC?+*gI`SaQGIZvUCJenz z>N{@^m+xsK`aT(SKjT!=s~dZ|9QA!sZS0=Gs?j&@Kia;xw@sz<-)%n`cD^4T{uAFqGw{JV=RZM#U!&;O zLhNBv2coTge3I#X0_`_C`x##7bn1;B|9pVsogPfS(c_0ZKR<3S%nYIPyx8#fzivO; z-h6fR^n^;vX{$2q><$kfKX`D?SAL)bI&0aPu#ehV)AMtnCb$MC9Hm(9Hdjb9<~Tzt z1Jcmp2J00H!Y9eJ^RD}<4$xlEduOYc=`Dsu`FLk2`XgdH zCb^#$Co58Pqx80Tx-%5LO?>75_WPsfg)@K#_XXKBLFis(j?H9@n%#!m@>f`M1Hclf zWC(p|ZII92CiNf4rPDS-FR6Qt4|fLan?MFHG$;jZaWc@>r1jJCgl-A<^~48SPIzo0 z3-Rw4&-~YT7Uget(>X>3(WC;mT+$V$@smA-4LwcFPWWtCb#4Rnb?V?X#|Y2(V6l4e z<^|1aCInXE>D(w^tJkJpPF8i zjZ=^tgZ#?{D1L~Mp^=8+%WD2d=5Q9@DjJ0}lb4rjYBPLJXoK#aYcMdFYGe?Ym#W2QT(`3NWz=>;hC^+O>I&>rPIS!5&scK zfV1gr6b&5;wv1@#Pu}4YEJ_ou*kF~l!4xl71WE8xiLF#vdK@Wq$+{MVyfYIRT^2r5s~W= V?w#&Y(iR=Co}BUb{{Q{Q{{zy*q_Y43 literal 0 HcmV?d00001