diff --git a/.flutter b/.flutter index 2663184aa..603104015 160000 --- a/.flutter +++ b/.flutter @@ -1 +1 @@ -Subproject commit 2663184aa79047d0a33a14a3b607954f8fdd8730 +Subproject commit 603104015dd692ea3403755b55d07813d5cf8965 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 7b0990bcf..955b3b3fb 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -22,6 +22,6 @@ jobs: egress-policy: audit - name: 'Checkout Repository' - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: 'Dependency Review' - uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4 + uses: actions/dependency-review-action@4081bf99e2866ebe428fc0477b69eb4fcda7220a # v4.4.0 diff --git a/.github/workflows/quality-check.yml b/.github/workflows/quality-check.yml index 7265b74c5..3971f6bd7 100644 --- a/.github/workflows/quality-check.yml +++ b/.github/workflows/quality-check.yml @@ -23,7 +23,7 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Get Flutter packages run: scripts/pub_get_all.sh @@ -59,17 +59,17 @@ jobs: # Building relies on the Android Gradle plugin, # which requires a modern Java version (not the default one). - name: Set up JDK for Android Gradle plugin - uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0 + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 with: distribution: 'temurin' java-version: '21' - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -83,6 +83,6 @@ jobs: ./flutterw build apk --profile -t lib/main_play.dart --flavor play - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 68c6ea2d4..657445e42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,9 @@ jobs: name: GitHub release runs-on: ubuntu-latest permissions: + attestations: write contents: write + id-token: write steps: - name: Harden Runner uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 @@ -23,13 +25,13 @@ jobs: # Building relies on the Android Gradle plugin, # which requires a modern Java version (not the default one). - name: Set up JDK for Android Gradle plugin - uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0 + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0 with: distribution: 'temurin' java-version: '21' - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Get Flutter packages run: scripts/pub_get_all.sh @@ -72,6 +74,11 @@ jobs: AVES_KEY_PASSWORD: ${{ secrets.AVES_KEY_PASSWORD }} AVES_GOOGLE_API_KEY: ${{ secrets.AVES_GOOGLE_API_KEY }} + - name: Generate artifact attestation + uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3 + with: + subject-path: 'outputs/*' + - name: Create GitHub release uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 with: @@ -96,7 +103,7 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Get appbundle from artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 6e2676543..a88b1673d 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -36,7 +36,7 @@ jobs: egress-policy: audit - name: "Checkout code" - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false @@ -71,6 +71,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 with: sarif_file: results.sarif diff --git a/CHANGELOG.md b/CHANGELOG.md index cf952d08f..7b290ba4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [v1.11.17] - 2024-10-30 + +### Added + +- Map: create shortcut to custom region and filters +- Video: frame stepping forward/backward +- Video: custom playback buttons +- English (Shavian) translation (thanks Paranoid Android) + +### Changed + +- upgraded Flutter to stable v3.24.4 + +### Fixed + +- crash when loading large collection +- Viewer: copying content URI item +- Albums: creating album with same name as existing empty directory +- Privacy: tagging while vaults are unlocked does not yield recent tags visible when vaults are locked + ## [v1.11.16] - 2024-10-10 ### Fixed diff --git a/android/app/build.gradle b/android/app/build.gradle index 2bf34a887..eb8171a4a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,5 +1,3 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { id 'com.android.application' id 'com.google.devtools.ksp' @@ -30,16 +28,15 @@ if (keystorePropertiesFile.exists()) { keystoreProperties["googleApiKey"] = System.getenv("AVES_GOOGLE_API_KEY") ?: "" } +kotlin { + jvmToolchain 17 +} + android { namespace 'deckers.thibault.aves' compileSdk 35 // cf https://developer.android.com/studio/projects/install-ndk#default-ndk-per-agp - ndkVersion '26.1.10909125' - - compileOptions { - sourceCompatibility JavaVersion.VERSION_21 - targetCompatibility JavaVersion.VERSION_21 - } + ndkVersion '27.0.12077973' defaultConfig { applicationId packageName @@ -133,15 +130,6 @@ android { } } -tasks.withType(KotlinCompile).configureEach { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 -} - -kotlin { - jvmToolchain(21) -} - flutter { source '../..' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 58947842a..c12dd891b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -332,6 +332,9 @@ >("uris") - try { - if (!pickedUris.isNullOrEmpty()) { - val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(this@MainActivity, Uri.parse(uriString)) } - val intent = Intent().apply { - val firstUri = toUri(pickedUris.first()) - if (pickedUris.size == 1) { - data = firstUri - } else { - clipData = ClipData.newUri(contentResolver, null, firstUri).apply { - pickedUris.drop(1).forEach { - addItem(ClipData.Item(toUri(it))) - } - } + if (pickedUris.isNullOrEmpty()) { + setResult(RESULT_CANCELED) + // move code triggering `Binder` call off the main thread + defaultScope.launch { finish() } + return + } + + val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(this@MainActivity, Uri.parse(uriString)) } + val intent = Intent().apply { + val firstUri = toUri(pickedUris.first()) + if (pickedUris.size == 1) { + data = firstUri + } else { + clipData = ClipData.newUri(contentResolver, null, firstUri).apply { + pickedUris.drop(1).forEach { + addItem(ClipData.Item(toUri(it))) } - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } - setResult(RESULT_OK, intent) - } else { - setResult(RESULT_CANCELED) } - // move code triggering `Binder` call off the main thread - defaultScope.launch { finish() } + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + } + // move code triggering `Binder` call off the main thread + defaultScope.launch { + submitPickedItemsIntent(intent, result) + } + } + + private fun submitPickedItemsIntent(intent: Intent, result: MethodChannel.Result) { + try { + setResult(RESULT_OK, intent) + finish() } catch (e: Exception) { - if (e is TransactionTooLargeException || e.cause is TransactionTooLargeException) { - result.error("submitPickedItems-large", "transaction too large with ${pickedUris?.size} URIs", e) + setResult(RESULT_CANCELED) + if (e is SecurityException && intent.flags and Intent.FLAG_GRANT_WRITE_URI_PERMISSION != 0) { + // in some environments, providing the write flag yields a `SecurityException`: + // "UID XXXX does not have permission to content://XXXX" + // so we retry without it + Log.i(LOG_TAG, "retry submitting picked items without FLAG_GRANT_WRITE_URI_PERMISSION") + intent.flags = intent.flags and Intent.FLAG_GRANT_WRITE_URI_PERMISSION.inv() + submitPickedItemsIntent(intent, result) + } else if (e.anyCauseIs()) { + result.error("submitPickedItems-large", "transaction too large with ${intent.clipData?.itemCount} URIs", e) } else { - result.error("submitPickedItems-exception", "failed to pick ${pickedUris?.size} URIs", e) + result.error("submitPickedItems-exception", "failed to pick ${intent.clipData?.itemCount} URIs", e) } } } @@ -552,6 +572,7 @@ open class MainActivity : FlutterFragmentActivity() { const val INTENT_DATA_KEY_EXPLORER_PATH = "explorerPath" const val INTENT_DATA_KEY_FILTERS = "filters" const val INTENT_DATA_KEY_MIME_TYPE = "mimeType" + const val INTENT_DATA_KEY_MIME_TYPES = "mimeTypes" const val INTENT_DATA_KEY_PAGE = "page" const val INTENT_DATA_KEY_QUERY = "query" const val INTENT_DATA_KEY_SECURE_URIS = "secureUris" @@ -566,6 +587,8 @@ open class MainActivity : FlutterFragmentActivity() { // dart page routes const val COLLECTION_PAGE_ROUTE_NAME = "/collection" + const val ENTRY_VIEWER_PAGE_ROUTE_NAME = "/viewer" + const val EXPLORER_PAGE_ROUTE_NAME = "/explorer" const val MAP_PAGE_ROUTE_NAME = "/map" const val SEARCH_PAGE_ROUTE_NAME = "/search" diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AnalysisHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AnalysisHandler.kt index 6c940da75..0ee874058 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AnalysisHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AnalysisHandler.kt @@ -1,5 +1,6 @@ package deckers.thibault.aves.channel.calls +import android.app.ActivityManager import android.content.Context import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequestBuilder @@ -51,6 +52,17 @@ class AnalysisHandler(private val activity: FlutterFragmentActivity, private val return } + val activityManager: ActivityManager = activity.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val runningAppProcesses = activityManager.runningAppProcesses + if (runningAppProcesses != null) { + val importance = runningAppProcesses[0].importance + if (importance < ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + // the app is in the background + result.error("startAnalysis-background", "app is in the background (process importance=$importance)", null) + return + } + } + // can be null or empty val allEntryIds = call.argument>("entryIds") diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt index 65c3f38a3..7825e084f 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt @@ -23,11 +23,15 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.DecodeFormat import com.bumptech.glide.request.RequestOptions import deckers.thibault.aves.MainActivity +import deckers.thibault.aves.MainActivity.Companion.COLLECTION_PAGE_ROUTE_NAME +import deckers.thibault.aves.MainActivity.Companion.ENTRY_VIEWER_PAGE_ROUTE_NAME +import deckers.thibault.aves.MainActivity.Companion.EXPLORER_PAGE_ROUTE_NAME import deckers.thibault.aves.MainActivity.Companion.EXTRA_KEY_EXPLORER_PATH import deckers.thibault.aves.MainActivity.Companion.EXTRA_KEY_FILTERS_ARRAY import deckers.thibault.aves.MainActivity.Companion.EXTRA_KEY_FILTERS_STRING import deckers.thibault.aves.MainActivity.Companion.EXTRA_KEY_PAGE import deckers.thibault.aves.MainActivity.Companion.EXTRA_STRING_ARRAY_SEPARATOR +import deckers.thibault.aves.MainActivity.Companion.MAP_PAGE_ROUTE_NAME import deckers.thibault.aves.R import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import deckers.thibault.aves.channel.calls.Coresult.Companion.safeSuspend @@ -35,6 +39,7 @@ import deckers.thibault.aves.model.FieldMap import deckers.thibault.aves.utils.BitmapUtils import deckers.thibault.aves.utils.BitmapUtils.getBytes import deckers.thibault.aves.utils.LogUtils +import deckers.thibault.aves.utils.anyCauseIs import deckers.thibault.aves.utils.getApplicationInfoCompat import deckers.thibault.aves.utils.queryIntentActivitiesCompat import io.flutter.plugin.common.MethodCall @@ -303,7 +308,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler { val started = safeStartActivityChooser(title, intent) result.success(started) } catch (e: Exception) { - if (e is TransactionTooLargeException || e.cause is TransactionTooLargeException) { + if (e.anyCauseIs()) { result.error("share-large", "transaction too large with ${uriList.size} URIs", e) } else { result.error("share-exception", "failed to share ${uriList.size} URIs", e) @@ -354,12 +359,17 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler { // shortcuts private fun pinShortcut(call: MethodCall, result: MethodChannel.Result) { + // common arguments val label = call.argument("label") val iconBytes = call.argument("iconBytes") + val route = call.argument("route") + // route dependent arguments val filters = call.argument>("filters") - val explorerPath = call.argument("explorerPath") - val uri = call.argument("uri")?.let { Uri.parse(it) } - if (label == null) { + val explorerPath = call.argument("path") + val viewUri = call.argument("viewUri")?.let { Uri.parse(it) } + val geoUri = call.argument("geoUri")?.let { Uri.parse(it) } + + if (label == null || route == null) { result.error("pin-args", "missing arguments", null) return } @@ -383,24 +393,60 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler { // so that foreground is rendered at the intended scale val supportAdaptiveIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - icon = IconCompat.createWithResource(context, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_collection else R.drawable.ic_shortcut_collection) + val resId = when (route) { + MAP_PAGE_ROUTE_NAME -> if (supportAdaptiveIcon) R.mipmap.ic_shortcut_map else R.drawable.ic_shortcut_map + else -> if (supportAdaptiveIcon) R.mipmap.ic_shortcut_collection else R.drawable.ic_shortcut_collection + } + icon = IconCompat.createWithResource(context, resId) } - val intent = when { - filters != null -> Intent(Intent.ACTION_MAIN, null, context, MainActivity::class.java) - .putExtra(EXTRA_KEY_PAGE, "/collection") - .putExtra(EXTRA_KEY_FILTERS_ARRAY, filters.toTypedArray()) - // on API 25, `String[]` or `ArrayList` extras are null when using the shortcut - // so we use a joined `String` as fallback - .putExtra(EXTRA_KEY_FILTERS_STRING, filters.joinToString(EXTRA_STRING_ARRAY_SEPARATOR)) + val intent: Intent = when (route) { + COLLECTION_PAGE_ROUTE_NAME -> { + if (filters == null) { + result.error("pin-filters", "collection shortcut requires filters", null) + return + } + Intent(Intent.ACTION_MAIN, null, context, MainActivity::class.java) + .putExtra(EXTRA_KEY_PAGE, route) + .putExtra(EXTRA_KEY_FILTERS_ARRAY, filters.toTypedArray()) + // on API 25, `String[]` or `ArrayList` extras are null when using the shortcut + // so we use a joined `String` as fallback + .putExtra(EXTRA_KEY_FILTERS_STRING, filters.joinToString(EXTRA_STRING_ARRAY_SEPARATOR)) + } - explorerPath != null -> Intent(Intent.ACTION_MAIN, null, context, MainActivity::class.java) - .putExtra(EXTRA_KEY_PAGE, "/explorer") - .putExtra(EXTRA_KEY_EXPLORER_PATH, explorerPath) + ENTRY_VIEWER_PAGE_ROUTE_NAME -> { + if (viewUri == null) { + result.error("pin-viewUri", "viewer shortcut requires URI", null) + return + } + Intent(Intent.ACTION_VIEW, viewUri, context, MainActivity::class.java) + } + + EXPLORER_PAGE_ROUTE_NAME -> { + Intent(Intent.ACTION_MAIN, null, context, MainActivity::class.java) + .putExtra(EXTRA_KEY_PAGE, route) + .putExtra(EXTRA_KEY_EXPLORER_PATH, explorerPath) + } + + MAP_PAGE_ROUTE_NAME -> { + if (geoUri == null) { + result.error("pin-geoUri", "map shortcut requires URI", null) + return + } + Intent(Intent.ACTION_VIEW, geoUri, context, MainActivity::class.java).apply { + putExtra(EXTRA_KEY_PAGE, route) + // filters are optional + filters?.let { + putExtra(EXTRA_KEY_FILTERS_ARRAY, it.toTypedArray()) + // on API 25, `String[]` or `ArrayList` extras are null when using the shortcut + // so we use a joined `String` as fallback + putExtra(EXTRA_KEY_FILTERS_STRING, it.joinToString(EXTRA_STRING_ARRAY_SEPARATOR)) + } + } + } - uri != null -> Intent(Intent.ACTION_VIEW, uri, context, MainActivity::class.java) else -> { - result.error("pin-intent", "failed to build intent", null) + result.error("pin-route", "unsupported shortcut route=$route", null) return } } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchBytesHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchBytesHandler.kt index 232fa09cd..8012e7d7a 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchBytesHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchBytesHandler.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch +import java.util.Date import kotlin.math.roundToInt class MediaFetchBytesHandler(private val context: Context) : MethodCallHandler { @@ -44,7 +45,7 @@ class MediaFetchBytesHandler(private val context: Context) : MethodCallHandler { val defaultSizeDip = call.argument("defaultSizeDip")?.toDouble() val quality = call.argument("quality") - if (uri == null || mimeType == null || dateModifiedSecs == null || rotationDegrees == null || isFlipped == null || widthDip == null || heightDip == null || defaultSizeDip == null || quality == null) { + if (uri == null || mimeType == null || rotationDegrees == null || isFlipped == null || widthDip == null || heightDip == null || defaultSizeDip == null || quality == null) { result.error("getThumbnail-args", "missing arguments", null) return } @@ -54,7 +55,7 @@ class MediaFetchBytesHandler(private val context: Context) : MethodCallHandler { context = context, uri = uri, mimeType = mimeType, - dateModifiedSecs = dateModifiedSecs, + dateModifiedSecs = dateModifiedSecs ?: (Date().time / 1000), rotationDegrees = rotationDegrees, isFlipped = isFlipped, width = (widthDip * density).roundToInt(), diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt index baee7c5e8..9b2893745 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt @@ -29,6 +29,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler { when (call.method) { "getDataUsage" -> ioScope.launch { safe(call, result, ::getDataUsage) } "getStorageVolumes" -> ioScope.launch { safe(call, result, ::getStorageVolumes) } + "getCacheDirectory" -> ioScope.launch { safe(call, result, ::getCacheDirectory) } "getUntrackedTrashPaths" -> ioScope.launch { safe(call, result, ::getUntrackedTrashPaths) } "getUntrackedVaultPaths" -> ioScope.launch { safe(call, result, ::getUntrackedVaultPaths) } "getVaultRoot" -> ioScope.launch { safe(call, result, ::getVaultRoot) } @@ -122,6 +123,18 @@ class StorageHandler(private val context: Context) : MethodCallHandler { result.success(volumes) } + private fun getCacheDirectory(call: MethodCall, result: MethodChannel.Result) { + val external = call.argument("external") + if (external == null) { + result.error("getCacheDirectory-args", "missing arguments", null) + return + } + + val dir = (if (external) context.externalCacheDir else context.cacheDir) + result.success(dir!!.path) + } + + private fun getUntrackedTrashPaths(call: MethodCall, result: MethodChannel.Result) { val knownPaths = call.argument>("knownPaths") if (knownPaths == null) { diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt index 1d6d6a60a..9734cd497 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt @@ -137,8 +137,7 @@ abstract class ImageProvider { "success" to false, ) - // prevent naming with a `.` prefix as it would hide the file and remove it from the Media Store - if (sourcePath != null && !desiredName.startsWith('.')) { + if (sourcePath != null) { try { var newFields: FieldMap = skippedFieldMap if (!isCancelledOp()) { @@ -570,6 +569,20 @@ abstract class ImageProvider { } } + fun createTimeStampFileName() = Date().time.toString() + + private fun sanitizeDesiredFileName(desiredName: String): String { + var name = desiredName + // prevent creating hidden files + while (name.isNotEmpty() && name.startsWith(".")) { + name = name.substring(1) + } + if (name.isEmpty()) { + name = createTimeStampFileName() + } + return name + } + // returns available name to use, or `null` to skip it suspend fun resolveTargetFileNameWithoutExtension( contextWrapper: ContextWrapper, @@ -578,18 +591,19 @@ abstract class ImageProvider { mimeType: String, conflictStrategy: NameConflictStrategy, ): NameConflictResolution { - var resolvedName: String? = desiredNameWithoutExtension + val sanitizedNameWithoutExtension = sanitizeDesiredFileName(desiredNameWithoutExtension) + var resolvedName: String? = sanitizedNameWithoutExtension var replacementFile: File? = null val extension = extensionFor(mimeType) - val targetFile = File(dir, "$desiredNameWithoutExtension$extension") + val targetFile = File(dir, "$sanitizedNameWithoutExtension$extension") when (conflictStrategy) { NameConflictStrategy.RENAME -> { - var nameWithoutExtension = desiredNameWithoutExtension + var nameWithoutExtension = sanitizedNameWithoutExtension var i = 0 while (File(dir, "$nameWithoutExtension$extension").exists()) { i++ - nameWithoutExtension = "$desiredNameWithoutExtension ($i)" + nameWithoutExtension = "$sanitizedNameWithoutExtension ($i)" } resolvedName = nameWithoutExtension } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt index f05e6991f..fc921de8c 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt @@ -40,6 +40,7 @@ import java.io.FileOutputStream import java.io.IOException import java.io.OutputStream import java.io.SyncFailedException +import java.util.Date import java.util.Locale import java.util.concurrent.CompletableFuture import kotlin.coroutines.Continuation @@ -478,64 +479,62 @@ class MediaStoreImageProvider : ImageProvider() { "success" to false, ) - if (sourcePath != null) { - // on API 30 we cannot get access granted directly to a volume root from its document tree, - // but it is still less constraining to use tree document files than to rely on the Media Store - // - // Relying on `DocumentFile`, we can create an item via `DocumentFile.createFile()`, but: - // - we need to scan the file to get the Media Store content URI - // - the underlying document provider controls the new file name - // - // Relying on the Media Store, we can create an item via `ContentResolver.insert()` - // with a path, and retrieve its content URI, but: - // - the Media Store isolates content by storage volume (e.g. `MediaStore.Images.Media.getContentUri(volumeName)`) - // - the volume name should be lower case, not exactly as the `StorageVolume` UUID - // cf new method in API 30 `StorageVolume.getMediaStoreVolumeName()` - // - inserting on a removable volume works on API 29, but not on API 25 nor 26 (on which API/devices does it work?) - // - there is no documentation regarding support for usage with removable storage - // - the Media Store only allows inserting in specific primary directories ("DCIM", "Pictures") when using scoped storage - try { - val appDir = when { - toBin -> StorageUtils.trashDirFor(activity, sourcePath) - toVault -> File(targetDir) - else -> null - } - if (appDir != null) { - effectiveTargetDir = ensureTrailingSeparator(appDir.path) - targetDirDocFile = DocumentFileCompat.fromFile(appDir) + // on API 30 we cannot get access granted directly to a volume root from its document tree, + // but it is still less constraining to use tree document files than to rely on the Media Store + // + // Relying on `DocumentFile`, we can create an item via `DocumentFile.createFile()`, but: + // - we need to scan the file to get the Media Store content URI + // - the underlying document provider controls the new file name + // + // Relying on the Media Store, we can create an item via `ContentResolver.insert()` + // with a path, and retrieve its content URI, but: + // - the Media Store isolates content by storage volume (e.g. `MediaStore.Images.Media.getContentUri(volumeName)`) + // - the volume name should be lower case, not exactly as the `StorageVolume` UUID + // cf new method in API 30 `StorageVolume.getMediaStoreVolumeName()` + // - inserting on a removable volume works on API 29, but not on API 25 nor 26 (on which API/devices does it work?) + // - there is no documentation regarding support for usage with removable storage + // - the Media Store only allows inserting in specific primary directories ("DCIM", "Pictures") when using scoped storage + try { + val appDir = when { + toBin -> StorageUtils.trashDirFor(activity, sourcePath ?: StorageUtils.getPrimaryVolumePath(activity)) + toVault -> File(targetDir) + else -> null + } + if (appDir != null) { + effectiveTargetDir = ensureTrailingSeparator(appDir.path) + targetDirDocFile = DocumentFileCompat.fromFile(appDir) - if (toVault) { - appDir.mkdirs() - } + if (toVault) { + appDir.mkdirs() } + } - if (effectiveTargetDir != null) { - val newFields = if (isCancelledOp()) skippedFieldMap else { - val sourceFile = File(sourcePath) - if (!sourceFile.exists() && toBin) { - delete(activity, sourceUri, sourcePath, mimeType = mimeType) - deletedFieldMap - } else { - moveSingle( - activity = activity, - sourceFile = sourceFile, - sourceUri = sourceUri, - targetDir = effectiveTargetDir, - targetDirDocFile = targetDirDocFile, - desiredName = desiredName ?: sourceFile.name, - nameConflictStrategy = nameConflictStrategy, - mimeType = mimeType, - copy = copy, - toBin = toBin, - ) - } + if (effectiveTargetDir != null) { + val newFields = if (isCancelledOp()) skippedFieldMap else { + val sourceFile = if (sourcePath != null) File(sourcePath) else null + if (sourceFile != null && !sourceFile.exists() && toBin) { + delete(activity, sourceUri, sourcePath, mimeType = mimeType) + deletedFieldMap + } else { + moveSingle( + activity = activity, + sourceFile = sourceFile, + sourceUri = sourceUri, + targetDir = effectiveTargetDir, + targetDirDocFile = targetDirDocFile, + desiredName = desiredName ?: sourceFile?.name ?: sourceUri.lastPathSegment ?: createTimeStampFileName(), + nameConflictStrategy = nameConflictStrategy, + mimeType = mimeType, + copy = copy, + toBin = toBin, + ) } - result["newFields"] = newFields - result["success"] = true } - } catch (e: Exception) { - Log.w(LOG_TAG, "failed to move to targetDir=$targetDir entry with sourcePath=$sourcePath", e) + result["newFields"] = newFields + result["success"] = true } + } catch (e: Exception) { + Log.w(LOG_TAG, "failed to move to targetDir=$targetDir entry with sourcePath=$sourcePath", e) } callback.onSuccess(result) } @@ -544,7 +543,7 @@ class MediaStoreImageProvider : ImageProvider() { private suspend fun moveSingle( activity: Activity, - sourceFile: File, + sourceFile: File?, sourceUri: Uri, targetDir: String, targetDirDocFile: DocumentFileCompat?, @@ -554,8 +553,8 @@ class MediaStoreImageProvider : ImageProvider() { copy: Boolean, toBin: Boolean, ): FieldMap { - val sourcePath = sourceFile.path - val sourceDir = sourceFile.parent?.let { ensureTrailingSeparator(it) } + val sourcePath = sourceFile?.path + val sourceDir = sourceFile?.parent?.let { ensureTrailingSeparator(it) } if (sourceDir == targetDir && !(copy && nameConflictStrategy == NameConflictStrategy.RENAME)) { // nothing to do unless it's a renamed copy return skippedFieldMap diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/ExceptionUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/ExceptionUtils.kt new file mode 100644 index 000000000..f164c9760 --- /dev/null +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/ExceptionUtils.kt @@ -0,0 +1,10 @@ +package deckers.thibault.aves.utils + +inline fun Exception.anyCauseIs(): Boolean { + var cause: Throwable? = this + while (cause != null) { + if (cause is T) return true + cause = cause.cause + } + return false +} diff --git a/android/app/src/main/res/values-ar/strings.xml b/android/app/src/main/res/values-ar/strings.xml index 958d8d391..478e4f4a8 100644 --- a/android/app/src/main/res/values-ar/strings.xml +++ b/android/app/src/main/res/values-ar/strings.xml @@ -8,4 +8,5 @@ يتم فحص الوسائط إيقاف Aves + خريطة \ No newline at end of file diff --git a/android/app/src/main/res/values-b+en+Shaw/strings.xml b/android/app/src/main/res/values-b+en+Shaw/strings.xml new file mode 100644 index 000000000..f7bb920c6 --- /dev/null +++ b/android/app/src/main/res/values-b+en+Shaw/strings.xml @@ -0,0 +1,12 @@ + + + 𐑱𐑝𐑰𐑟 + 𐑓𐑴𐑑𐑴 𐑓𐑮𐑱𐑥 + 𐑢𐑷𐑤𐑐𐑱𐑐𐑼 + 𐑥𐑨𐑐 + 𐑕𐑻𐑗 + 𐑝𐑦𐑛𐑦𐑴𐑟 + 𐑥𐑰𐑛𐑾 𐑕𐑒𐑨𐑯 + 𐑕𐑒𐑨𐑯𐑦𐑙 𐑥𐑰𐑛𐑾 + 𐑕𐑑𐑪𐑐 + \ No newline at end of file diff --git a/android/app/src/main/res/values-de/strings.xml b/android/app/src/main/res/values-de/strings.xml index f2f20076c..2c7396d91 100644 --- a/android/app/src/main/res/values-de/strings.xml +++ b/android/app/src/main/res/values-de/strings.xml @@ -8,4 +8,5 @@ Analyse von Medien Medien scannen Abbrechen + Karte \ No newline at end of file diff --git a/android/app/src/main/res/values-fi/strings.xml b/android/app/src/main/res/values-fi/strings.xml index 27295a07f..c9aaf8a47 100644 --- a/android/app/src/main/res/values-fi/strings.xml +++ b/android/app/src/main/res/values-fi/strings.xml @@ -8,4 +8,5 @@ Aves Median skannaus Hae + Kartta \ No newline at end of file diff --git a/android/app/src/main/res/values-pt/strings.xml b/android/app/src/main/res/values-pt/strings.xml index 0e887b1bb..a793c012d 100644 --- a/android/app/src/main/res/values-pt/strings.xml +++ b/android/app/src/main/res/values-pt/strings.xml @@ -8,4 +8,5 @@ Digitalização de mídia Digitalizando mídia Pare + Mapa \ No newline at end of file diff --git a/android/app/src/main/res/values-sk/strings.xml b/android/app/src/main/res/values-sk/strings.xml index 9a8ac434a..a6c505626 100644 --- a/android/app/src/main/res/values-sk/strings.xml +++ b/android/app/src/main/res/values-sk/strings.xml @@ -8,4 +8,5 @@ Zastaviť Skenovanie médií Skenovanie média + Mapa \ No newline at end of file diff --git a/android/app/src/main/res/values-tr/strings.xml b/android/app/src/main/res/values-tr/strings.xml index e516cc30b..c115ff9dc 100644 --- a/android/app/src/main/res/values-tr/strings.xml +++ b/android/app/src/main/res/values-tr/strings.xml @@ -8,4 +8,5 @@ Medya tarama Medya taranıyor Durdur + Harita \ No newline at end of file diff --git a/android/app/src/main/res/values-uk/strings.xml b/android/app/src/main/res/values-uk/strings.xml index 5d33f2d3b..60717a3ee 100644 --- a/android/app/src/main/res/values-uk/strings.xml +++ b/android/app/src/main/res/values-uk/strings.xml @@ -8,4 +8,5 @@ Стоп Фоторамка Сканування медіа + Мапа \ No newline at end of file diff --git a/android/app/src/main/res/values-vi/strings.xml b/android/app/src/main/res/values-vi/strings.xml index 367ab8dc2..96db6a481 100644 --- a/android/app/src/main/res/values-vi/strings.xml +++ b/android/app/src/main/res/values-vi/strings.xml @@ -8,4 +8,5 @@ Aves Quét phương tiện Tìm kiếm + Bản đồ \ No newline at end of file diff --git a/android/exifinterface/build.gradle b/android/exifinterface/build.gradle index d2e62e0d5..46bfa54d8 100644 --- a/android/exifinterface/build.gradle +++ b/android/exifinterface/build.gradle @@ -20,8 +20,8 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_21 - targetCompatibility JavaVersion.VERSION_21 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 45181329e..81a4301fc 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip diff --git a/android/settings.gradle b/android/settings.gradle index e262ed8f1..76ed740ec 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -10,7 +10,7 @@ pluginManagement { settings.ext.kotlin_version = '1.9.24' settings.ext.ksp_version = "$kotlin_version-1.0.20" - settings.ext.agp_version = '8.6.1' + settings.ext.agp_version = '8.7.0' includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") diff --git a/fastlane/metadata/android/en-Shaw/full_description.txt b/fastlane/metadata/android/en-Shaw/full_description.txt new file mode 100644 index 000000000..443fb2726 --- /dev/null +++ b/fastlane/metadata/android/en-Shaw/full_description.txt @@ -0,0 +1,5 @@ +¡·𐑱𐑝𐑰𐑟 𐑒𐑨𐑯 𐑣𐑨𐑯𐑛𐑩𐑤 𐑷𐑤 𐑕𐑹𐑑𐑕 𐑝 𐑦𐑥𐑦𐑡𐑩𐑟 𐑯 𐑝𐑦𐑛𐑦𐑴𐑟, 𐑦𐑯𐑒𐑤𐑵𐑛𐑦𐑙 𐑘𐑹 𐑑𐑦𐑐𐑦𐑒𐑩𐑤 ⸰𐑡𐑓𐑧𐑜'𐑟 𐑯 ⸰𐑥𐑐4'𐑟, 𐑚𐑳𐑑 𐑷𐑤𐑕𐑴 𐑥𐑹 𐑦𐑜𐑟𐑪𐑑𐑦𐑒 𐑔𐑦𐑙𐑟 𐑤𐑲𐑒 𐑥𐑳𐑤𐑑𐑦-𐑐𐑱𐑡 ⸰𐑑𐑦𐑓𐑓'𐑕, ⸰𐑕𐑝𐑜'𐑟, 𐑴𐑤𐑛 ⸰𐑷𐑝𐑦'𐑟 𐑯 𐑥𐑹! 𐑦𐑑 𐑕𐑒𐑨𐑯𐑟 𐑘𐑹 𐑥𐑰𐑛𐑾 𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯 𐑑 𐑲𐑛𐑧𐑯𐑑𐑦𐑓𐑲 𐑥𐑴𐑖𐑩𐑯 𐑓𐑴𐑑𐑴𐑟, 𐑐𐑨𐑯𐑼𐑭𐑥𐑩𐑟 (⸰𐑷𐑯𐑨 𐑓𐑴𐑑𐑴 𐑕𐑓𐑽𐑟), 360° 𐑝𐑦𐑛𐑦𐑴𐑟, 𐑨𐑟 𐑢𐑧𐑤 𐑨𐑟 ⸰𐑡𐑰𐑴𐑑𐑦𐑓𐑓 𐑓𐑲𐑤𐑟. + +𐑯𐑨𐑝𐑦𐑜𐑱𐑖𐑩𐑯 𐑯 𐑕𐑻𐑗 𐑦𐑟 𐑩𐑯 𐑦𐑥𐑐𐑹𐑑𐑩𐑯𐑑 𐑐𐑸𐑑 𐑝 ·𐑱𐑝𐑰𐑟. 𐑞 𐑜𐑴𐑤 𐑦𐑟 𐑓 𐑿𐑟𐑼𐑟 𐑑 𐑰𐑟𐑦𐑤𐑦 𐑓𐑤𐑴 𐑓𐑮𐑪𐑥 𐑨𐑤𐑚𐑩𐑥𐑟 𐑑 𐑓𐑴𐑑𐑴𐑟 𐑑 𐑑𐑨𐑜𐑟 𐑑 𐑥𐑨𐑐𐑕, 𐑯𐑯𐑯. + +·𐑱𐑝𐑰𐑟 𐑦𐑯𐑑𐑦𐑜𐑮𐑱𐑑𐑕 𐑢𐑦𐑞 ·𐑨𐑯𐑛𐑮𐑶𐑛 (𐑓𐑮𐑪𐑥 ·𐑒𐑦𐑑𐑒𐑨𐑑 𐑑 ·𐑨𐑯𐑛𐑮𐑶𐑛 14, 𐑦𐑯𐑒𐑤𐑵𐑛𐑦𐑙 ·𐑨𐑯𐑛𐑮𐑶𐑛 ⸰𐑑𐑝) 𐑢𐑦𐑞 𐑓𐑰𐑗𐑼𐑟 𐑕𐑳𐑗 𐑨𐑟 𐑢𐑦𐑡𐑩𐑑𐑕, 𐑨𐑐 𐑖𐑹𐑑𐑒𐑳𐑑𐑕, 𐑕𐑒𐑮𐑰𐑯 𐑕𐑱𐑝𐑼 𐑯 𐑜𐑤𐑴𐑚𐑩𐑤 𐑕𐑻𐑗 𐑣𐑨𐑯𐑛𐑤𐑦𐑙. 𐑦𐑑 𐑷𐑤𐑕𐑴 𐑢𐑻𐑒𐑕 𐑨𐑟 𐑩 𐑥𐑰𐑛𐑾 𐑝𐑿𐑼 𐑯 𐑐𐑦𐑒𐑼. diff --git a/fastlane/metadata/android/en-Shaw/images/featureGraphic.png b/fastlane/metadata/android/en-Shaw/images/featureGraphic.png new file mode 100644 index 000000000..c12acf8f6 Binary files /dev/null and b/fastlane/metadata/android/en-Shaw/images/featureGraphic.png differ diff --git a/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/1.png new file mode 100644 index 000000000..4970dc5fa Binary files /dev/null and b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/2.png new file mode 100644 index 000000000..4a1e4b281 Binary files /dev/null and b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/3.png new file mode 100644 index 000000000..942667170 Binary files /dev/null and b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/4.png b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/4.png new file mode 100644 index 000000000..d8a82ad50 Binary files /dev/null and b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/5.png b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/5.png new file mode 100644 index 000000000..f4c597fca Binary files /dev/null and b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/6.png b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/6.png new file mode 100644 index 000000000..7db91fac3 Binary files /dev/null and b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/7.png b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/7.png new file mode 100644 index 000000000..636ae49e7 Binary files /dev/null and b/fastlane/metadata/android/en-Shaw/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/en-Shaw/short_description.txt b/fastlane/metadata/android/en-Shaw/short_description.txt new file mode 100644 index 000000000..9c2b68f1a --- /dev/null +++ b/fastlane/metadata/android/en-Shaw/short_description.txt @@ -0,0 +1 @@ +𐑜𐑨𐑤𐑼𐑦 𐑯 𐑥𐑧𐑑𐑩𐑛𐑱𐑑𐑩 𐑦𐑒𐑕𐑐𐑤𐑹𐑼 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/136.txt b/fastlane/metadata/android/en-US/changelogs/136.txt new file mode 100644 index 000000000..10f3ff7c4 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/136.txt @@ -0,0 +1,5 @@ +In v1.11.17: +- peruse your videos frame by frame +- create map shortcuts to filtered collections +- enjoy the app in Shavian +Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/13601.txt b/fastlane/metadata/android/en-US/changelogs/13601.txt new file mode 100644 index 000000000..10f3ff7c4 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/13601.txt @@ -0,0 +1,5 @@ +In v1.11.17: +- peruse your videos frame by frame +- create map shortcuts to filtered collections +- enjoy the app in Shavian +Full changelog available on GitHub \ No newline at end of file diff --git a/lib/geo/countries.dart b/lib/geo/countries.dart index f2b0c32b6..eeb1db079 100644 --- a/lib/geo/countries.dart +++ b/lib/geo/countries.dart @@ -41,7 +41,7 @@ class CountryTopology { return Map.fromEntries(numericMap.entries.map((kv) { final code = _countryOfNumeric(kv.key); return code != null ? MapEntry(code, kv.value) : null; - }).whereNotNull()); + }).nonNulls); } // returns a map of the given positions by the ISO 3166-1 numeric code of the country containing them diff --git a/lib/geo/topojson.dart b/lib/geo/topojson.dart index c03f7d553..94bf6bb9a 100644 --- a/lib/geo/topojson.dart +++ b/lib/geo/topojson.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:isolate'; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; // cf https://github.com/topojson/topojson-specification @@ -60,7 +59,7 @@ class Topology extends TopologyJsonObject { final name = kv.key; final geometry = Geometry.build(kv.value); return geometry != null ? MapEntry(name, geometry) : null; - }).whereNotNull()), + }).nonNulls), arcs = (data['arcs'] as List).cast().map((arc) => arc.cast().map((position) => position.cast()).toList()).toList(), transform = data.containsKey('transform') ? Transform.parse((data['transform'] as Map).cast()) : null, super.parse(); @@ -238,7 +237,7 @@ class GeometryCollection extends Geometry { final List geometries; GeometryCollection.parse(super.data) - : geometries = (data['geometries'] as List).cast>().map(Geometry.build).whereNotNull().toList(), + : geometries = (data['geometries'] as List).cast>().map(Geometry.build).nonNulls.toList(), super.parse(); @override diff --git a/lib/geo/uri.dart b/lib/geo/uri.dart index 5374bc9cd..3241d0a39 100644 --- a/lib/geo/uri.dart +++ b/lib/geo/uri.dart @@ -1,3 +1,4 @@ +import 'package:aves/utils/math_utils.dart'; import 'package:latlong2/latlong.dart'; // e.g. `geo:44.4361283,26.1027248?z=4.0(Bucharest)` @@ -24,3 +25,13 @@ import 'package:latlong2/latlong.dart'; } return null; } + +String toGeoUri(LatLng latLng, {double? zoom}) { + final latitude = roundToPrecision(latLng.latitude, decimals: 6); + final longitude = roundToPrecision(latLng.longitude, decimals: 6); + var uri = 'geo:$latitude,$longitude?q=$latitude,$longitude'; + if (zoom != null) { + uri += '&z=$zoom'; + } + return uri; +} diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index b59897d27..f55426b8b 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -541,8 +541,6 @@ "@settingsEnableBin": {}, "entryActionViewMotionPhotoVideo": "فتح الفيديو", "@entryActionViewMotionPhotoVideo": {}, - "videoControlsNone": "لا شيء", - "@videoControlsNone": {}, "otherDirectoryDescription": "دليل «{name}»", "@otherDirectoryDescription": { "placeholders": { @@ -757,8 +755,6 @@ "@drawerCollectionFavourites": {}, "filterTypeRawLabel": "خام", "@filterTypeRawLabel": {}, - "videoControlsPlaySeek": "تشغيل وتقدم للأمام/ للخلف", - "@videoControlsPlaySeek": {}, "settingsSubtitleThemeTextAlignmentCenter": "وسط", "@settingsSubtitleThemeTextAlignmentCenter": {}, "keepScreenOnVideoPlayback": "أثناء تشغيل الفيديو", @@ -1031,8 +1027,6 @@ "@viewerErrorDoesNotExist": {}, "albumCamera": "الكاميرا", "@albumCamera": {}, - "videoControlsPlay": "تشغيل", - "@videoControlsPlay": {}, "settingsNavigationSectionTitle": "التنقل", "@settingsNavigationSectionTitle": {}, "settingsDisplayRefreshRateModeDialogTitle": "معدل التحديث", @@ -1544,5 +1538,11 @@ "mapStyleOpenTopoMap": "الخريطة الطبوغرافية المفتوحة", "@mapStyleOpenTopoMap": {}, "mapAttributionOsmData": "بيانات الخريطة © [OpenStreetMap](https://www.openstreetmap.org/copyright) المساهمين", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | البلاط بواسطة [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", + "@mapAttributionOpenTopoMap": {}, + "mapStyleOsmLiberty": "حرية خرائط OSM", + "@mapStyleOsmLiberty": {}, + "mapAttributionOsmLiberty": "البلاط بواسطة [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • مُستضاف بواسطة [OSM Americana](https://tile.ourmap.us)", + "@mapAttributionOsmLiberty": {} } diff --git a/lib/l10n/app_be.arb b/lib/l10n/app_be.arb index fdb22ec83..c30d4cce9 100644 --- a/lib/l10n/app_be.arb +++ b/lib/l10n/app_be.arb @@ -257,8 +257,6 @@ "@coordinateFormatDecimal": {}, "subtitlePositionBottom": "Ніз", "@subtitlePositionBottom": {}, - "videoControlsPlaySeek": "Прайграванне і пераход на пазіцыю", - "@videoControlsPlaySeek": {}, "nameConflictStrategyReplace": "Замяніць", "@nameConflictStrategyReplace": {}, "filterAspectRatioLandscapeLabel": "Ландшафтныя", @@ -361,8 +359,6 @@ "@settingsVideoEnablePip": {}, "videoControlsPlayOutside": "Адкрыць у іншым прайгравальніку", "@videoControlsPlayOutside": {}, - "videoControlsPlay": "Прайграванне", - "@videoControlsPlay": {}, "videoLoopModeNever": "Ніколі", "@videoLoopModeNever": {}, "videoLoopModeShortOnly": "Толькі для кароткіх відэа", @@ -593,8 +589,6 @@ "@viewerInfoSearchSuggestionResolution": {}, "viewerInfoSearchSuggestionDimensions": "Памеры", "@viewerInfoSearchSuggestionDimensions": {}, - "videoControlsNone": "Нічога", - "@videoControlsNone": {}, "viewerErrorUnknown": "Ой!", "@viewerErrorUnknown": {}, "viewerSetWallpaperButtonLabel": "УСТАНАВІЦЬ ШПАЛЕРЫ", diff --git a/lib/l10n/app_ca.arb b/lib/l10n/app_ca.arb index 057b0c748..fdf55eba1 100644 --- a/lib/l10n/app_ca.arb +++ b/lib/l10n/app_ca.arb @@ -240,12 +240,6 @@ "@vaultLockTypePassword": {}, "settingsVideoEnablePip": "Imatge-en-imatge", "@settingsVideoEnablePip": {}, - "videoControlsPlay": "Reproduir", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Reproduir i retrocedeix/avança", - "@videoControlsPlaySeek": {}, - "videoControlsNone": "Cap", - "@videoControlsNone": {}, "viewerTransitionZoomIn": "Ampliar", "@viewerTransitionZoomIn": {}, "viewerTransitionNone": "Cap", diff --git a/lib/l10n/app_ckb.arb b/lib/l10n/app_ckb.arb index 5ffd61aa5..e3ab45d5c 100644 --- a/lib/l10n/app_ckb.arb +++ b/lib/l10n/app_ckb.arb @@ -218,8 +218,6 @@ "@albumTierApps": {}, "entryActionViewMotionPhotoVideo": "کردنەوەی ڤیدیۆ", "@entryActionViewMotionPhotoVideo": {}, - "videoControlsNone": "هیچیان", - "@videoControlsNone": {}, "filterNoTitleLabel": "بێ سەرناو", "@filterNoTitleLabel": {}, "videoPlaybackMuted": "بەبێ دەنگی لێبدە", @@ -244,8 +242,6 @@ "@addShortcutButtonLabel": {}, "entryActionOpenMap": "لە نەرمەواڵەی نەخشە پیشانی بدە", "@entryActionOpenMap": {}, - "videoControlsPlaySeek": "لێدان و بردنە پێش/پاش", - "@videoControlsPlaySeek": {}, "entryActionRotateCCW": "سوڕاندن بە پێچەوانەی میلی کاتژمێر", "@entryActionRotateCCW": {}, "viewerActionSettings": "ڕێکخستنەکان", @@ -274,8 +270,6 @@ "@themeBrightnessBlack": {}, "videoPlaybackSkip": "بازدان", "@videoPlaybackSkip": {}, - "videoControlsPlay": "لێدان", - "@videoControlsPlay": {}, "entryActionShareVideoOnly": "بە تەنیا ڤیدیۆ هاوبەش بکە", "@entryActionShareVideoOnly": {}, "applyTooltip": "جێبەجێ کردن", diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index f8e370116..7d2ae8a5a 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -250,14 +250,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Vždy", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Přehrát", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Přehrávat a vyhledávat vzad/vpřed", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Otevřít jiným přehrávačem", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Žádný", - "@videoControlsNone": {}, "coordinateFormatDms": "Stupně, minuty, vteřiny", "@coordinateFormatDms": {}, "coordinateFormatDecimal": "Stupně s desetinnými místy", diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index abbae4e53..55cff8978 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -101,9 +101,9 @@ "@entryActionRename": {}, "entryActionRestore": "Wiederherstellen", "@entryActionRestore": {}, - "entryActionRotateCCW": "Drehen gegen den Uhrzeigersinn", + "entryActionRotateCCW": "Gegen den Uhrzeigersinn drehen", "@entryActionRotateCCW": {}, - "entryActionRotateCW": "Drehen im Uhrzeigersinn", + "entryActionRotateCW": "Im Uhrzeigersinn drehen", "@entryActionRotateCW": {}, "entryActionFlip": "Horizontal spiegeln", "@entryActionFlip": {}, @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Immer", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Abspielen/Pausieren", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Abspielen/Pausieren & Sprung-Schaltflächen", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Mit anderem Video-Player öffnen", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Keine Schaltflächen", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (Hybrid)", @@ -1361,7 +1355,7 @@ "@videoActionABRepeat": {}, "videoRepeatActionSetStart": "Start festlegen", "@videoRepeatActionSetStart": {}, - "stopTooltip": "Stop", + "stopTooltip": "Stoppen", "@stopTooltip": {}, "videoRepeatActionSetEnd": "Ende festlegen", "@videoRepeatActionSetEnd": {}, diff --git a/lib/l10n/app_el.arb b/lib/l10n/app_el.arb index 987c39666..9e61624e3 100644 --- a/lib/l10n/app_el.arb +++ b/lib/l10n/app_el.arb @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Πάντα", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Αναπαραγωγή", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Αναπαραγωγή & πίσω/μπροστά", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Άνοιγμα με άλλη εφαρμογή", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Καμία επιλογή", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (Hybrid)", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index d3f890e78..95df01843 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -138,6 +138,8 @@ "videoActionPlay": "Play", "videoActionReplay10": "Seek backward 10 seconds", "videoActionSkip10": "Seek forward 10 seconds", + "videoActionShowPreviousFrame": "Show previous frame", + "videoActionShowNextFrame": "Show next frame", "videoActionSelectStreams": "Select tracks", "videoActionSetSpeed": "Playback speed", "videoActionABRepeat": "A-B repeat", @@ -270,10 +272,7 @@ "settingsVideoEnablePip": "Picture-in-picture", - "videoControlsPlay": "Play", - "videoControlsPlaySeek": "Play & seek backward/forward", "videoControlsPlayOutside": "Open with other player", - "videoControlsNone": "None", "videoLoopModeNever": "Never", "videoLoopModeShortOnly": "Short videos only", diff --git a/lib/l10n/app_en_Shaw.arb b/lib/l10n/app_en_Shaw.arb new file mode 100644 index 000000000..65a149a47 --- /dev/null +++ b/lib/l10n/app_en_Shaw.arb @@ -0,0 +1,1593 @@ +{ + "@@locale": "en_Shaw", + "appName": "𐑱𐑝𐑰𐑟", + "@appName": {}, + "welcomeOptional": "𐑪𐑐𐑖𐑩𐑯𐑩𐑤", + "@welcomeOptional": {}, + "welcomeTermsToggle": "𐑲 𐑩𐑜𐑮𐑰 𐑑 𐑞 𐑑𐑻𐑥𐑟 𐑯 𐑒𐑩𐑯𐑛𐑦𐑖𐑩𐑯𐑟", + "@welcomeTermsToggle": {}, + "welcomeMessage": "𐑢𐑧𐑤𐑒𐑩𐑥 𐑑 ·𐑱𐑝𐑰𐑟", + "@welcomeMessage": {}, + "timeSeconds": "{count, plural, =1{{count} 𐑕𐑧𐑒𐑩𐑯𐑛} other{{count} 𐑕𐑧𐑒𐑩𐑯𐑛𐑟}}", + "@timeSeconds": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "itemCount": "{count, plural, =1{{count} 𐑲𐑑𐑩𐑥} other{{count} 𐑲𐑑𐑩𐑥𐑟}}", + "@itemCount": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "columnCount": "{count, plural, =1{{count} 𐑒𐑪𐑤𐑩𐑥} other{{count} 𐑒𐑪𐑤𐑩𐑥𐑟}}", + "@columnCount": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "timeMinutes": "{count, plural, =1{{count} 𐑥𐑦𐑯𐑦𐑑} other{{count} 𐑥𐑦𐑯𐑦𐑑𐑕}}", + "@timeMinutes": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "timeDays": "{count, plural, =1{{count} 𐑛𐑱} other{{count} 𐑛𐑱𐑟}}", + "@timeDays": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "focalLength": "{length} 𐑥𐑥", + "@focalLength": { + "placeholders": { + "length": { + "type": "String", + "example": "5.4" + } + } + }, + "applyButtonLabel": "𐑩𐑐𐑤𐑲", + "@applyButtonLabel": {}, + "deleteButtonLabel": "𐑛𐑦𐑤𐑰𐑑", + "@deleteButtonLabel": {}, + "nextButtonLabel": "𐑯𐑧𐑒𐑕𐑑", + "@nextButtonLabel": {}, + "showButtonLabel": "𐑖𐑴", + "@showButtonLabel": {}, + "hideButtonLabel": "𐑣𐑲𐑛", + "@hideButtonLabel": {}, + "continueButtonLabel": "𐑒𐑩𐑯𐑑𐑦𐑯𐑿", + "@continueButtonLabel": {}, + "saveCopyButtonLabel": "𐑕𐑱𐑝 𐑒𐑪𐑐𐑦", + "@saveCopyButtonLabel": {}, + "applyTooltip": "𐑩𐑐𐑤𐑲", + "@applyTooltip": {}, + "cancelTooltip": "𐑒𐑨𐑯𐑕𐑩𐑤", + "@cancelTooltip": {}, + "changeTooltip": "𐑗𐑱𐑯𐑡", + "@changeTooltip": {}, + "clearTooltip": "𐑒𐑤𐑽", + "@clearTooltip": {}, + "previousTooltip": "𐑐𐑮𐑰𐑝𐑾𐑕", + "@previousTooltip": {}, + "nextTooltip": "𐑯𐑧𐑒𐑕𐑑", + "@nextTooltip": {}, + "showTooltip": "𐑖𐑴", + "@showTooltip": {}, + "hideTooltip": "𐑣𐑲𐑛", + "@hideTooltip": {}, + "actionRemove": "𐑮𐑦𐑥𐑵𐑝", + "@actionRemove": {}, + "resetTooltip": "𐑮𐑰𐑕𐑧𐑑", + "@resetTooltip": {}, + "saveTooltip": "𐑕𐑱𐑝", + "@saveTooltip": {}, + "stopTooltip": "𐑕𐑑𐑪𐑐", + "@stopTooltip": {}, + "pickTooltip": "𐑐𐑦𐑒", + "@pickTooltip": {}, + "doubleBackExitMessage": "𐑑𐑨𐑐 «𐑚𐑨𐑒» 𐑩𐑜𐑧𐑯 𐑑 𐑧𐑒𐑕𐑦𐑑.", + "@doubleBackExitMessage": {}, + "chipActionGoToAlbumPage": "𐑖𐑴 𐑦𐑯 ·𐑨𐑤𐑚𐑩𐑥𐑟", + "@chipActionGoToAlbumPage": {}, + "chipActionShowCollection": "𐑖𐑴 𐑦𐑯 ·𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯", + "@chipActionShowCollection": {}, + "videoActionPlay": "𐑐𐑤𐑱", + "@videoActionPlay": {}, + "viewerActionSettings": "𐑕𐑧𐑑𐑦𐑙𐑟", + "@viewerActionSettings": {}, + "slideshowActionResume": "𐑮𐑦𐑟𐑿𐑥", + "@slideshowActionResume": {}, + "entryInfoActionEditLocation": "𐑧𐑛𐑦𐑑 𐑤𐑴𐑒𐑱𐑖𐑩𐑯", + "@entryInfoActionEditLocation": {}, + "entryInfoActionEditTitleDescription": "𐑧𐑛𐑦𐑑 𐑑𐑲𐑑𐑩𐑤 𐑯 𐑛𐑦𐑕𐑒𐑮𐑦𐑐𐑖𐑩𐑯", + "@entryInfoActionEditTitleDescription": {}, + "doNotAskAgain": "𐑛𐑵 𐑯𐑪𐑑 𐑭𐑕𐑒 𐑩𐑜𐑧𐑯", + "@doNotAskAgain": {}, + "sourceStateLoading": "𐑤𐑴𐑛𐑦𐑙", + "@sourceStateLoading": {}, + "sourceStateCataloguing": "𐑒𐑨𐑑𐑩𐑤𐑪𐑜𐑦𐑙", + "@sourceStateCataloguing": {}, + "sourceStateLocatingCountries": "𐑤𐑴𐑒𐑱𐑑𐑦𐑙 𐑒𐑳𐑯𐑑𐑮𐑦𐑟", + "@sourceStateLocatingCountries": {}, + "sourceStateLocatingPlaces": "𐑤𐑴𐑒𐑱𐑑𐑦𐑙 𐑐𐑤𐑱𐑕𐑩𐑟", + "@sourceStateLocatingPlaces": {}, + "chipActionDelete": "𐑛𐑦𐑤𐑰𐑑", + "@chipActionDelete": {}, + "chipActionGoToExplorerPage": "𐑖𐑴 𐑦𐑯 ·𐑦𐑒𐑕𐑐𐑤𐑹𐑼", + "@chipActionGoToExplorerPage": {}, + "chipActionGoToTagPage": "𐑖𐑴 𐑦𐑯 ·𐑑𐑨𐑜𐑟", + "@chipActionGoToTagPage": {}, + "chipActionGoToPlacePage": "𐑖𐑴 𐑦𐑯 ·𐑐𐑤𐑱𐑕𐑩𐑟", + "@chipActionGoToPlacePage": {}, + "chipActionGoToCountryPage": "𐑖𐑴 𐑦𐑯 ·𐑒𐑳𐑯𐑑𐑮𐑦𐑟", + "@chipActionGoToCountryPage": {}, + "chipActionFilterOut": "𐑓𐑦𐑤𐑑𐑼 𐑬𐑑", + "@chipActionFilterOut": {}, + "chipActionFilterIn": "𐑓𐑦𐑤𐑑𐑼 𐑦𐑯", + "@chipActionFilterIn": {}, + "chipActionHide": "𐑣𐑲𐑛", + "@chipActionHide": {}, + "chipActionLock": "𐑤𐑪𐑒", + "@chipActionLock": {}, + "chipActionPin": "𐑐𐑦𐑯 𐑑 𐑑𐑪𐑐", + "@chipActionPin": {}, + "chipActionUnpin": "𐑳𐑯𐑐𐑦𐑯 𐑑 𐑑𐑪𐑐", + "@chipActionUnpin": {}, + "chipActionRename": "𐑮𐑰𐑯𐑱𐑥", + "@chipActionRename": {}, + "chipActionSetCover": "𐑕𐑧𐑑 𐑒𐑳𐑝𐑼", + "@chipActionSetCover": {}, + "chipActionShowCountryStates": "𐑖𐑴 𐑕𐑑𐑱𐑑𐑕", + "@chipActionShowCountryStates": {}, + "chipActionCreateAlbum": "𐑒𐑮𐑦𐑱𐑑 𐑨𐑤𐑚𐑩𐑥", + "@chipActionCreateAlbum": {}, + "chipActionCreateVault": "𐑒𐑮𐑦𐑱𐑑 𐑝𐑷𐑤𐑑", + "@chipActionCreateVault": {}, + "chipActionConfigureVault": "𐑒𐑩𐑯𐑓𐑦𐑜𐑼 𐑝𐑷𐑤𐑑", + "@chipActionConfigureVault": {}, + "entryActionCopyToClipboard": "𐑒𐑪𐑐𐑦 𐑑 𐑒𐑤𐑦𐑐𐑚𐑹𐑛", + "@entryActionCopyToClipboard": {}, + "entryActionDelete": "𐑛𐑦𐑤𐑰𐑑", + "@entryActionDelete": {}, + "entryActionConvert": "𐑒𐑪𐑯𐑝𐑻𐑑", + "@entryActionConvert": {}, + "entryActionExport": "𐑦𐑒𐑕𐑐𐑹𐑑", + "@entryActionExport": {}, + "entryActionInfo": "𐑦𐑯𐑓𐑴", + "@entryActionInfo": {}, + "entryActionRename": "𐑮𐑰𐑯𐑱𐑥", + "@entryActionRename": {}, + "entryActionRestore": "𐑮𐑦𐑕𐑑𐑹", + "@entryActionRestore": {}, + "entryActionRotateCCW": "𐑮𐑴𐑑𐑱𐑑 𐑒𐑬𐑯𐑑𐑼𐑒𐑤𐑪𐑒𐑢𐑲𐑟", + "@entryActionRotateCCW": {}, + "entryActionRotateCW": "𐑮𐑴𐑑𐑱𐑑 𐑒𐑤𐑪𐑒𐑢𐑲𐑟", + "@entryActionRotateCW": {}, + "entryActionFlip": "𐑓𐑤𐑦𐑐 𐑣𐑪𐑮𐑦𐑟𐑪𐑯𐑑𐑩𐑤𐑦", + "@entryActionFlip": {}, + "entryActionPrint": "𐑐𐑮𐑦𐑯𐑑", + "@entryActionPrint": {}, + "entryActionShare": "𐑖𐑺", + "@entryActionShare": {}, + "entryActionShareImageOnly": "𐑖𐑺 𐑦𐑥𐑦𐑡 𐑴𐑯𐑤𐑦", + "@entryActionShareImageOnly": {}, + "entryActionShareVideoOnly": "𐑖𐑺 𐑝𐑦𐑛𐑦𐑴 𐑴𐑯𐑤𐑦", + "@entryActionShareVideoOnly": {}, + "entryActionViewSource": "𐑝𐑿 𐑕𐑹𐑕", + "@entryActionViewSource": {}, + "entryActionShowGeoTiffOnMap": "𐑖𐑴 𐑨𐑟 𐑥𐑨𐑐 𐑴𐑝𐑼𐑤𐑱", + "@entryActionShowGeoTiffOnMap": {}, + "entryActionConvertMotionPhotoToStillImage": "𐑒𐑪𐑯𐑝𐑻𐑑 𐑑 𐑕𐑑𐑦𐑤 𐑦𐑥𐑦𐑡", + "@entryActionConvertMotionPhotoToStillImage": {}, + "entryActionViewMotionPhotoVideo": "𐑴𐑐𐑩𐑯 𐑝𐑦𐑛𐑦𐑴", + "@entryActionViewMotionPhotoVideo": {}, + "entryActionEdit": "𐑧𐑛𐑦𐑑", + "@entryActionEdit": {}, + "entryActionOpen": "𐑴𐑐𐑩𐑯 𐑢𐑦𐑞", + "@entryActionOpen": {}, + "entryActionSetAs": "𐑕𐑧𐑑 𐑨𐑟", + "@entryActionSetAs": {}, + "entryActionCast": "𐑒𐑭𐑕𐑑", + "@entryActionCast": {}, + "entryActionOpenMap": "𐑖𐑴 𐑦𐑯 𐑥𐑨𐑐 𐑨𐑐", + "@entryActionOpenMap": {}, + "entryActionRotateScreen": "𐑮𐑴𐑑𐑱𐑑 𐑕𐑒𐑮𐑰𐑯", + "@entryActionRotateScreen": {}, + "entryActionAddFavourite": "𐑨𐑛 𐑑 𐑓𐑱𐑝𐑼𐑦𐑑𐑕", + "@entryActionAddFavourite": {}, + "entryActionRemoveFavourite": "𐑮𐑦𐑥𐑵𐑝 𐑓𐑮𐑪𐑥 𐑓𐑱𐑝𐑼𐑦𐑑𐑕", + "@entryActionRemoveFavourite": {}, + "videoActionCaptureFrame": "𐑒𐑨𐑐𐑗𐑼 𐑓𐑮𐑱𐑥", + "@videoActionCaptureFrame": {}, + "videoActionMute": "𐑥𐑿𐑑", + "@videoActionMute": {}, + "videoActionUnmute": "𐑳𐑯𐑥𐑿𐑑", + "@videoActionUnmute": {}, + "videoActionPause": "𐑐𐑷𐑟", + "@videoActionPause": {}, + "videoActionReplay10": "𐑕𐑰𐑒 𐑚𐑨𐑒𐑢𐑼𐑛 10 𐑕𐑧𐑒𐑩𐑯𐑛𐑟", + "@videoActionReplay10": {}, + "videoActionSkip10": "𐑕𐑰𐑒 𐑓𐑹𐑢𐑼𐑛 10 𐑕𐑧𐑒𐑩𐑯𐑛𐑟", + "@videoActionSkip10": {}, + "videoActionSelectStreams": "𐑕𐑦𐑤𐑧𐑒𐑑 𐑑𐑮𐑨𐑒𐑕", + "@videoActionSelectStreams": {}, + "videoActionSetSpeed": "𐑐𐑤𐑱𐑚𐑨𐑒 𐑕𐑐𐑰𐑛", + "@videoActionSetSpeed": {}, + "videoActionABRepeat": "𐑐–𐑚 𐑮𐑦𐑐𐑰𐑑", + "@videoActionABRepeat": {}, + "videoRepeatActionSetStart": "𐑕𐑧𐑑 𐑕𐑑𐑸𐑑", + "@videoRepeatActionSetStart": {}, + "videoRepeatActionSetEnd": "𐑕𐑧𐑑 𐑧𐑯𐑛", + "@videoRepeatActionSetEnd": {}, + "viewerActionLock": "𐑤𐑪𐑒 𐑝𐑿𐑼", + "@viewerActionLock": {}, + "viewerActionUnlock": "𐑳𐑯𐑤𐑪𐑒 𐑝𐑿𐑼", + "@viewerActionUnlock": {}, + "slideshowActionShowInCollection": "𐑖𐑴 𐑦𐑯 ·𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯", + "@slideshowActionShowInCollection": {}, + "entryInfoActionEditDate": "𐑧𐑛𐑦𐑑 𐑛𐑱𐑑 𐑯 𐑑𐑲𐑥", + "@entryInfoActionEditDate": {}, + "entryInfoActionEditRating": "𐑧𐑛𐑦𐑑 𐑮𐑱𐑑𐑦𐑙", + "@entryInfoActionEditRating": {}, + "entryInfoActionEditTags": "𐑧𐑛𐑦𐑑 𐑑𐑨𐑜𐑟", + "@entryInfoActionEditTags": {}, + "entryInfoActionRemoveMetadata": "𐑮𐑦𐑥𐑵𐑝 𐑥𐑧𐑑𐑩𐑛𐑱𐑑𐑩", + "@entryInfoActionRemoveMetadata": {}, + "entryInfoActionExportMetadata": "𐑦𐑒𐑕𐑐𐑹𐑑 𐑥𐑧𐑑𐑩𐑛𐑱𐑑𐑩", + "@entryInfoActionExportMetadata": {}, + "entryInfoActionRemoveLocation": "𐑮𐑦𐑥𐑵𐑝 𐑤𐑴𐑒𐑱𐑖𐑩𐑯", + "@entryInfoActionRemoveLocation": {}, + "editorActionTransform": "𐑑𐑮𐑨𐑯𐑕𐑓𐑹𐑥", + "@editorActionTransform": {}, + "editorTransformCrop": "𐑒𐑮𐑪𐑐", + "@editorTransformCrop": {}, + "editorTransformRotate": "𐑮𐑴𐑑𐑱𐑑", + "@editorTransformRotate": {}, + "cropAspectRatioFree": "𐑓𐑮𐑰", + "@cropAspectRatioFree": {}, + "cropAspectRatioOriginal": "𐑼𐑦𐑡𐑦𐑯𐑩𐑤", + "@cropAspectRatioOriginal": {}, + "cropAspectRatioSquare": "𐑕𐑒𐑢𐑺", + "@cropAspectRatioSquare": {}, + "filterAspectRatioLandscapeLabel": "𐑤𐑨𐑯𐑛𐑕𐑒𐑱𐑐", + "@filterAspectRatioLandscapeLabel": {}, + "filterAspectRatioPortraitLabel": "𐑐𐑹𐑑𐑮𐑦𐑑", + "@filterAspectRatioPortraitLabel": {}, + "filterBinLabel": "𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯", + "@filterBinLabel": {}, + "filterFavouriteLabel": "𐑓𐑱𐑝𐑼𐑦𐑑", + "@filterFavouriteLabel": {}, + "filterNoDateLabel": "𐑳𐑯𐑛𐑱𐑑𐑩𐑛", + "@filterNoDateLabel": {}, + "filterNoAddressLabel": "𐑯𐑴 𐑩𐑛𐑮𐑧𐑕", + "@filterNoAddressLabel": {}, + "filterLocatedLabel": "𐑤𐑴𐑒𐑱𐑑𐑩𐑛", + "@filterLocatedLabel": {}, + "filterNoLocationLabel": "𐑳𐑯𐑤𐑴𐑒𐑱𐑑𐑩𐑛", + "@filterNoLocationLabel": {}, + "filterNoRatingLabel": "𐑳𐑯𐑮𐑱𐑑𐑩𐑛", + "@filterNoRatingLabel": {}, + "filterTaggedLabel": "𐑑𐑨𐑜𐑛", + "@filterTaggedLabel": {}, + "filterNoTagLabel": "𐑳𐑯𐑑𐑨𐑜𐑛", + "@filterNoTagLabel": {}, + "filterNoTitleLabel": "𐑳𐑯𐑑𐑲𐑑𐑩𐑤𐑛", + "@filterNoTitleLabel": {}, + "filterOnThisDayLabel": "𐑪𐑯 𐑞𐑦𐑕 𐑛𐑱", + "@filterOnThisDayLabel": {}, + "filterRecentlyAddedLabel": "𐑮𐑰𐑕𐑩𐑯𐑑𐑤𐑦 𐑨𐑛𐑩𐑛", + "@filterRecentlyAddedLabel": {}, + "filterRatingRejectedLabel": "𐑮𐑦𐑡𐑧𐑒𐑑𐑩𐑛", + "@filterRatingRejectedLabel": {}, + "filterTypeAnimatedLabel": "𐑨𐑯𐑦𐑥𐑱𐑑𐑩𐑛", + "@filterTypeAnimatedLabel": {}, + "filterTypeMotionPhotoLabel": "𐑥𐑴𐑖𐑩𐑯 𐑓𐑴𐑑𐑴", + "@filterTypeMotionPhotoLabel": {}, + "filterTypePanoramaLabel": "𐑐𐑨𐑯𐑼𐑭𐑥𐑩", + "@filterTypePanoramaLabel": {}, + "filterTypeRawLabel": "𐑮𐑷", + "@filterTypeRawLabel": {}, + "filterTypeSphericalVideoLabel": "360° 𐑝𐑦𐑛𐑦𐑴", + "@filterTypeSphericalVideoLabel": {}, + "filterTypeGeotiffLabel": "⸰𐑡𐑰𐑴𐑑𐑦𐑓𐑓", + "@filterTypeGeotiffLabel": {}, + "filterMimeImageLabel": "𐑦𐑥𐑦𐑡", + "@filterMimeImageLabel": {}, + "filterMimeVideoLabel": "𐑝𐑦𐑛𐑦𐑴", + "@filterMimeVideoLabel": {}, + "accessibilityAnimationsRemove": "𐑐𐑮𐑦𐑝𐑧𐑯𐑑 𐑕𐑒𐑮𐑰𐑯 𐑦𐑓𐑧𐑒𐑑𐑕", + "@accessibilityAnimationsRemove": {}, + "accessibilityAnimationsKeep": "𐑒𐑰𐑐 𐑕𐑒𐑮𐑰𐑯 𐑦𐑓𐑧𐑒𐑑𐑕", + "@accessibilityAnimationsKeep": {}, + "albumTierNew": "𐑯𐑿", + "@albumTierNew": {}, + "albumTierPinned": "𐑐𐑦𐑯𐑛", + "@albumTierPinned": {}, + "albumTierSpecial": "𐑒𐑪𐑥𐑩𐑯", + "@albumTierSpecial": {}, + "albumTierApps": "𐑨𐑐𐑕", + "@albumTierApps": {}, + "albumTierVaults": "𐑝𐑷𐑤𐑑𐑕", + "@albumTierVaults": {}, + "albumTierRegular": "𐑳𐑞𐑼𐑟", + "@albumTierRegular": {}, + "coordinateFormatDms": "⸰𐑛𐑥𐑕", + "@coordinateFormatDms": {}, + "coordinateFormatDecimal": "𐑛𐑧𐑕𐑦𐑥𐑩𐑤 𐑛𐑦𐑜𐑮𐑰𐑟", + "@coordinateFormatDecimal": {}, + "coordinateDms": "{coordinate} {direction}", + "@coordinateDms": { + "placeholders": { + "coordinate": { + "type": "String", + "example": "38° 41′ 47.72″" + }, + "direction": { + "type": "String", + "example": "S" + } + } + }, + "coordinateDmsNorth": "𐑯", + "@coordinateDmsNorth": {}, + "coordinateDmsSouth": "𐑕", + "@coordinateDmsSouth": {}, + "coordinateDmsEast": "𐑰", + "@coordinateDmsEast": {}, + "coordinateDmsWest": "𐑢", + "@coordinateDmsWest": {}, + "displayRefreshRatePreferHighest": "𐑣𐑲𐑩𐑕𐑑 𐑮𐑱𐑑", + "@displayRefreshRatePreferHighest": {}, + "keepScreenOnVideoPlayback": "𐑛𐑘𐑫𐑼𐑦𐑙 𐑝𐑦𐑛𐑦𐑴 𐑐𐑤𐑱𐑚𐑨𐑒", + "@keepScreenOnVideoPlayback": {}, + "keepScreenOnViewerOnly": "𐑝𐑿𐑼 𐑐𐑱𐑡 𐑴𐑯𐑤𐑦", + "@keepScreenOnViewerOnly": {}, + "lengthUnitPixel": "𐑐𐑒𐑕", + "@lengthUnitPixel": {}, + "lengthUnitPercent": "%", + "@lengthUnitPercent": {}, + "mapStyleGoogleNormal": "·𐑜𐑵𐑜𐑩𐑤 𐑥𐑨𐑐𐑕", + "@mapStyleGoogleNormal": {}, + "mapStyleGoogleHybrid": "·𐑜𐑵𐑜𐑩𐑤 𐑥𐑨𐑐𐑕 (𐑣𐑲𐑚𐑮𐑦𐑛)", + "@mapStyleGoogleHybrid": {}, + "mapStyleGoogleTerrain": "·𐑜𐑵𐑜𐑩𐑤 𐑥𐑨𐑐𐑕 (𐑑𐑼𐑱𐑯)", + "@mapStyleGoogleTerrain": {}, + "mapStyleOsmLiberty": "⸰𐑴𐑕𐑥 ·𐑤𐑦𐑚𐑼𐑑𐑦", + "@mapStyleOsmLiberty": {}, + "mapStyleOpenTopoMap": "·𐑴𐑐𐑩𐑯𐑑𐑪𐑐𐑩𐑥𐑨𐑐", + "@mapStyleOpenTopoMap": {}, + "mapStyleOsmHot": "·𐑣𐑿𐑥𐑨𐑯𐑦𐑑𐑺𐑾𐑯 ⸰𐑴𐑕𐑥", + "@mapStyleOsmHot": {}, + "maxBrightnessAlways": "𐑷𐑤𐑢𐑱𐑟", + "@maxBrightnessAlways": {}, + "mapStyleStamenWatercolor": "·𐑕𐑑𐑱𐑥𐑩𐑯 𐑢𐑷𐑑𐑼𐑒𐑳𐑤𐑼", + "@mapStyleStamenWatercolor": {}, + "maxBrightnessNever": "𐑯𐑧𐑝𐑼", + "@maxBrightnessNever": {}, + "nameConflictStrategyRename": "𐑮𐑰𐑯𐑱𐑥", + "@nameConflictStrategyRename": {}, + "nameConflictStrategyReplace": "𐑮𐑦𐑐𐑤𐑱𐑕", + "@nameConflictStrategyReplace": {}, + "nameConflictStrategySkip": "𐑕𐑒𐑦𐑐", + "@nameConflictStrategySkip": {}, + "overlayHistogramNone": "𐑯𐑳𐑯", + "@overlayHistogramNone": {}, + "overlayHistogramRGB": "⸰𐑮𐑜𐑚", + "@overlayHistogramRGB": {}, + "overlayHistogramLuminance": "𐑤𐑵𐑥𐑦𐑯𐑩𐑯𐑕", + "@overlayHistogramLuminance": {}, + "subtitlePositionTop": "𐑑𐑪𐑐", + "@subtitlePositionTop": {}, + "subtitlePositionBottom": "𐑚𐑪𐑑𐑩𐑥", + "@subtitlePositionBottom": {}, + "themeBrightnessLight": "𐑤𐑲𐑑", + "@themeBrightnessLight": {}, + "themeBrightnessDark": "𐑛𐑸𐑒", + "@themeBrightnessDark": {}, + "themeBrightnessBlack": "𐑚𐑤𐑨𐑒", + "@themeBrightnessBlack": {}, + "unitSystemMetric": "𐑥𐑧𐑑𐑮𐑦𐑒", + "@unitSystemMetric": {}, + "unitSystemImperial": "𐑦𐑥𐑐𐑽𐑾𐑤", + "@unitSystemImperial": {}, + "vaultLockTypePattern": "𐑐𐑨𐑑𐑼𐑯", + "@vaultLockTypePattern": {}, + "vaultLockTypePin": "⸰𐑐𐑲𐑯", + "@vaultLockTypePin": {}, + "vaultLockTypePassword": "𐑐𐑭𐑕𐑢𐑻𐑛", + "@vaultLockTypePassword": {}, + "settingsVideoEnablePip": "𐑐𐑦𐑒𐑗𐑼-𐑦𐑯-𐑐𐑦𐑒𐑗𐑼", + "@settingsVideoEnablePip": {}, + "videoControlsPlayOutside": "𐑴𐑐𐑩𐑯 𐑢𐑦𐑞 𐑳𐑞𐑼 𐑐𐑤𐑱𐑼", + "@videoControlsPlayOutside": {}, + "videoLoopModeNever": "𐑯𐑧𐑝𐑼", + "@videoLoopModeNever": {}, + "videoLoopModeShortOnly": "𐑖𐑹𐑑 𐑝𐑦𐑛𐑦𐑴𐑟 𐑴𐑯𐑤𐑦", + "@videoLoopModeShortOnly": {}, + "videoLoopModeAlways": "𐑷𐑤𐑢𐑱𐑟", + "@videoLoopModeAlways": {}, + "videoPlaybackSkip": "𐑕𐑒𐑦𐑐", + "@videoPlaybackSkip": {}, + "videoPlaybackMuted": "𐑐𐑤𐑱 𐑥𐑿𐑑𐑩𐑛", + "@videoPlaybackMuted": {}, + "videoPlaybackWithSound": "𐑐𐑤𐑱 𐑢𐑦𐑞 𐑕𐑬𐑯𐑛", + "@videoPlaybackWithSound": {}, + "videoResumptionModeNever": "𐑯𐑧𐑝𐑼", + "@videoResumptionModeNever": {}, + "videoResumptionModeAlways": "𐑷𐑤𐑢𐑱𐑟", + "@videoResumptionModeAlways": {}, + "viewerTransitionSlide": "𐑕𐑤𐑲𐑛", + "@viewerTransitionSlide": {}, + "viewerTransitionParallax": "𐑐𐑨𐑮𐑩𐑤𐑨𐑒𐑕", + "@viewerTransitionParallax": {}, + "viewerTransitionFade": "𐑓𐑱𐑛", + "@viewerTransitionFade": {}, + "viewerTransitionZoomIn": "𐑟𐑵𐑥 𐑦𐑯", + "@viewerTransitionZoomIn": {}, + "viewerTransitionNone": "𐑯𐑳𐑯", + "@viewerTransitionNone": {}, + "wallpaperTargetHome": "𐑣𐑴𐑥 𐑕𐑒𐑮𐑰𐑯", + "@wallpaperTargetHome": {}, + "wallpaperTargetLock": "𐑤𐑪𐑒 𐑕𐑒𐑮𐑰𐑯", + "@wallpaperTargetLock": {}, + "wallpaperTargetHomeLock": "𐑣𐑴𐑥 𐑯 𐑤𐑪𐑒 𐑕𐑒𐑮𐑰𐑯𐑟", + "@wallpaperTargetHomeLock": {}, + "widgetDisplayedItemRandom": "𐑮𐑨𐑯𐑛𐑩𐑥", + "@widgetDisplayedItemRandom": {}, + "widgetDisplayedItemMostRecent": "𐑥𐑴𐑕𐑑 𐑮𐑰𐑕𐑩𐑯𐑑", + "@widgetDisplayedItemMostRecent": {}, + "widgetOpenPageHome": "𐑴𐑐𐑩𐑯 𐑣𐑴𐑥", + "@widgetOpenPageHome": {}, + "widgetOpenPageCollection": "𐑴𐑐𐑩𐑯 𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯", + "@widgetOpenPageCollection": {}, + "widgetOpenPageViewer": "𐑴𐑐𐑩𐑯 𐑝𐑿𐑼", + "@widgetOpenPageViewer": {}, + "widgetTapUpdateWidget": "𐑳𐑐𐑛𐑱𐑑 𐑢𐑦𐑡𐑩𐑑", + "@widgetTapUpdateWidget": {}, + "storageVolumeDescriptionFallbackPrimary": "𐑦𐑯𐑑𐑻𐑯𐑩𐑤 𐑕𐑑𐑹𐑦𐑡", + "@storageVolumeDescriptionFallbackPrimary": {}, + "storageVolumeDescriptionFallbackNonPrimary": "⸰𐑕𐑛 𐑒𐑸𐑛", + "@storageVolumeDescriptionFallbackNonPrimary": {}, + "rootDirectoryDescription": "𐑮𐑵𐑑 𐑛𐑦𐑮𐑧𐑒𐑑𐑼𐑦", + "@rootDirectoryDescription": {}, + "otherDirectoryDescription": "«{name}» 𐑛𐑦𐑮𐑧𐑒𐑑𐑼𐑦", + "@otherDirectoryDescription": { + "placeholders": { + "name": { + "type": "String", + "example": "Pictures", + "description": "the name of a specific directory" + } + } + }, + "storageAccessDialogMessage": "𐑐𐑤𐑰𐑟 𐑕𐑦𐑤𐑧𐑒𐑑 𐑞 {directory} 𐑝 «{volume}» 𐑦𐑯 𐑞 𐑯𐑧𐑒𐑕𐑑 𐑕𐑒𐑮𐑰𐑯 𐑑 𐑜𐑦𐑝 𐑞𐑦𐑕 𐑨𐑐 𐑨𐑒𐑕𐑧𐑕 𐑑 𐑦𐑑.", + "@storageAccessDialogMessage": { + "placeholders": { + "directory": { + "type": "String", + "description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "restrictedAccessDialogMessage": "𐑞𐑦𐑕 𐑨𐑐 𐑦𐑟 𐑯𐑪𐑑 𐑩𐑤𐑬𐑛 𐑑 𐑥𐑪𐑛𐑦𐑓𐑲 𐑓𐑲𐑤𐑟 𐑦𐑯 𐑞 {directory} 𐑝 «{volume}».\n\n𐑐𐑤𐑰𐑟 𐑿𐑟 𐑩 𐑐𐑮𐑰-𐑦𐑯𐑕𐑑𐑷𐑤𐑛 𐑓𐑲𐑤 𐑥𐑨𐑯𐑦𐑡𐑼 𐑹 𐑜𐑨𐑤𐑼𐑦 𐑨𐑐 𐑑 𐑥𐑵𐑝 𐑞 𐑲𐑑𐑩𐑥𐑟 𐑑 𐑩𐑯𐑳𐑞𐑼 𐑛𐑦𐑮𐑧𐑒𐑑𐑼𐑦.", + "@restrictedAccessDialogMessage": { + "placeholders": { + "directory": { + "type": "String", + "description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "keepScreenOnNever": "𐑯𐑧𐑝𐑼", + "@keepScreenOnNever": {}, + "displayRefreshRatePreferLowest": "𐑤𐑴𐑩𐑕𐑑 𐑮𐑱𐑑", + "@displayRefreshRatePreferLowest": {}, + "keepScreenOnAlways": "𐑷𐑤𐑢𐑱𐑟", + "@keepScreenOnAlways": {}, + "notEnoughSpaceDialogMessage": "𐑞𐑦𐑕 𐑪𐑐𐑼𐑱𐑖𐑩𐑯 𐑯𐑰𐑛𐑟 {neededSize} 𐑝 𐑓𐑮𐑰 𐑕𐑐𐑱𐑕 𐑪𐑯 «{volume}» 𐑑 𐑒𐑩𐑥𐑐𐑤𐑰𐑑, 𐑚𐑳𐑑 𐑞𐑺 𐑦𐑟 𐑴𐑯𐑤𐑦 {freeSize} 𐑤𐑧𐑓𐑑.", + "@notEnoughSpaceDialogMessage": { + "placeholders": { + "neededSize": { + "type": "String", + "example": "314 MB" + }, + "freeSize": { + "type": "String", + "example": "123 MB" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "missingSystemFilePickerDialogMessage": "𐑞 𐑕𐑦𐑕𐑑𐑩𐑥 𐑓𐑲𐑤 𐑐𐑦𐑒𐑼 𐑦𐑟 𐑥𐑦𐑕𐑦𐑙 𐑹 𐑛𐑦𐑕𐑱𐑚𐑩𐑤𐑛. 𐑐𐑤𐑰𐑟 𐑦𐑯𐑱𐑚𐑩𐑤 𐑦𐑑 𐑯 𐑑𐑮𐑲 𐑩𐑜𐑧𐑯.", + "@missingSystemFilePickerDialogMessage": {}, + "nameConflictDialogSingleSourceMessage": "𐑕𐑳𐑥 𐑓𐑲𐑤𐑟 𐑦𐑯 𐑞 𐑛𐑧𐑕𐑑𐑦𐑯𐑱𐑖𐑩𐑯 𐑓𐑴𐑤𐑛𐑼 𐑣𐑨𐑝 𐑞 𐑕𐑱𐑥 𐑯𐑱𐑥.", + "@nameConflictDialogSingleSourceMessage": {}, + "nameConflictDialogMultipleSourceMessage": "𐑕𐑳𐑥 𐑓𐑲𐑤𐑟 𐑣𐑨𐑝 𐑞 𐑕𐑱𐑥 𐑯𐑱𐑥.", + "@nameConflictDialogMultipleSourceMessage": {}, + "addShortcutDialogLabel": "𐑖𐑹𐑑𐑒𐑳𐑑 𐑤𐑱𐑚𐑩𐑤", + "@addShortcutDialogLabel": {}, + "addShortcutButtonLabel": "𐑨𐑛", + "@addShortcutButtonLabel": {}, + "noMatchingAppDialogMessage": "𐑞𐑺 𐑸 𐑯𐑴 𐑨𐑐𐑕 𐑞𐑨𐑑 𐑒𐑨𐑯 𐑣𐑨𐑯𐑛𐑩𐑤 𐑞𐑦𐑕.", + "@noMatchingAppDialogMessage": {}, + "binEntriesConfirmationDialogMessage": "{count, plural, =1{¿𐑥𐑵𐑝 𐑞𐑦𐑕 𐑲𐑑𐑩𐑥 𐑑 𐑞 𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯?} other{¿𐑥𐑵𐑝 𐑞𐑰𐑟 {count} 𐑲𐑑𐑩𐑥𐑟 𐑑 𐑞 𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯?}}", + "@binEntriesConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "deleteEntriesConfirmationDialogMessage": "{count, plural, =1{¿𐑛𐑦𐑤𐑰𐑑 𐑞𐑦𐑕 𐑲𐑑𐑩𐑥?} other{¿𐑛𐑦𐑤𐑰𐑑 𐑞𐑰𐑟 {count} 𐑲𐑑𐑩𐑥𐑟?}}", + "@deleteEntriesConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "moveUndatedConfirmationDialogMessage": "¿𐑕𐑱𐑝 𐑲𐑑𐑩𐑥 𐑛𐑱𐑑𐑕 𐑚𐑦𐑓𐑹 𐑐𐑮𐑩𐑕𐑰𐑛𐑦𐑙?", + "@moveUndatedConfirmationDialogMessage": {}, + "moveUndatedConfirmationDialogSetDate": "𐑕𐑱𐑝 𐑛𐑱𐑑𐑕", + "@moveUndatedConfirmationDialogSetDate": {}, + "videoResumeDialogMessage": "¿𐑛𐑵 𐑿 𐑢𐑪𐑯𐑑 𐑑 𐑮𐑦𐑟𐑿𐑥 𐑨𐑑 {time}?", + "@videoResumeDialogMessage": { + "placeholders": { + "time": { + "type": "String", + "example": "13:37" + } + } + }, + "videoStartOverButtonLabel": "𐑕𐑑𐑸𐑑 𐑴𐑝𐑼", + "@videoStartOverButtonLabel": {}, + "videoResumeButtonLabel": "𐑮𐑦𐑟𐑿𐑥", + "@videoResumeButtonLabel": {}, + "setCoverDialogLatest": "𐑤𐑱𐑑𐑩𐑕𐑑 𐑲𐑑𐑩𐑥", + "@setCoverDialogLatest": {}, + "setCoverDialogAuto": "𐑷𐑑𐑴", + "@setCoverDialogAuto": {}, + "setCoverDialogCustom": "𐑒𐑳𐑕𐑑𐑩𐑥", + "@setCoverDialogCustom": {}, + "hideFilterConfirmationDialogMessage": "𐑥𐑨𐑗𐑦𐑙 𐑓𐑴𐑑𐑴𐑟 𐑯 𐑝𐑦𐑛𐑦𐑴𐑟 𐑢𐑦𐑤 𐑚𐑰 𐑣𐑦𐑛𐑩𐑯 𐑓𐑮𐑪𐑥 𐑘𐑹 𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯. 𐑿 𐑒𐑨𐑯 𐑖𐑴 𐑞𐑧𐑥 𐑩𐑜𐑧𐑯 𐑓𐑮𐑪𐑥 𐑞 «𐑐𐑮𐑦𐑝𐑩𐑕𐑦» 𐑕𐑧𐑑𐑦𐑙𐑟.\n\n¿𐑸 𐑿 𐑖𐑫𐑼 𐑿 𐑢𐑪𐑯𐑑 𐑑 𐑣𐑲𐑛 𐑞𐑧𐑥?", + "@hideFilterConfirmationDialogMessage": {}, + "newAlbumDialogTitle": "𐑯𐑿 𐑨𐑤𐑚𐑩𐑥", + "@newAlbumDialogTitle": {}, + "newAlbumDialogNameLabel": "𐑨𐑤𐑚𐑩𐑥 𐑯𐑱𐑥", + "@newAlbumDialogNameLabel": {}, + "unsupportedTypeDialogMessage": "{count, plural, =1{𐑞𐑦𐑕 𐑪𐑐𐑼𐑱𐑖𐑩𐑯 𐑦𐑟 𐑯𐑪𐑑 𐑕𐑩𐑐𐑹𐑑𐑩𐑛 𐑓 𐑲𐑑𐑩𐑥𐑟 𐑝 𐑞 𐑓𐑪𐑤𐑴𐑦𐑙 𐑑𐑲𐑐: {types}.} other{𐑞𐑦𐑕 𐑪𐑐𐑼𐑱𐑖𐑩𐑯 𐑦𐑟 𐑯𐑪𐑑 𐑕𐑩𐑐𐑹𐑑𐑩𐑛 𐑓 𐑲𐑑𐑩𐑥𐑟 𐑝 𐑞 𐑓𐑪𐑤𐑴𐑦𐑙 𐑑𐑲𐑐𐑕: {types}.}}", + "@unsupportedTypeDialogMessage": { + "placeholders": { + "count": {}, + "types": { + "type": "String", + "example": "GIF, TIFF, MP4", + "description": "a list of unsupported types" + } + } + }, + "newAlbumDialogNameLabelAlreadyExistsHelper": "𐑛𐑦𐑮𐑧𐑒𐑑𐑼𐑦 𐑷𐑤𐑮𐑧𐑛𐑦 𐑦𐑜𐑟𐑦𐑕𐑑𐑕", + "@newAlbumDialogNameLabelAlreadyExistsHelper": {}, + "newAlbumDialogStorageLabel": "𐑕𐑑𐑹𐑦𐑡:", + "@newAlbumDialogStorageLabel": {}, + "newVaultWarningDialogMessage": "𐑲𐑑𐑩𐑥𐑟 𐑦𐑯 𐑝𐑷𐑤𐑑𐑕 𐑸 𐑴𐑯𐑤𐑦 𐑩𐑝𐑱𐑤𐑩𐑚𐑩𐑤 𐑑 𐑞𐑦𐑕 𐑨𐑐 𐑯 𐑯𐑴 𐑳𐑞𐑼𐑟.\n\n𐑦𐑓 𐑿 𐑳𐑯𐑦𐑯𐑕𐑑𐑷𐑤 𐑞𐑦𐑕 𐑨𐑐, 𐑹 𐑒𐑤𐑽 𐑞𐑦𐑕 𐑨𐑐 𐑛𐑱𐑑𐑩, 𐑿 𐑢𐑦𐑤 𐑤𐑵𐑟 𐑷𐑤 𐑞𐑰𐑟 𐑲𐑑𐑩𐑥𐑟.", + "@newVaultWarningDialogMessage": {}, + "newVaultDialogTitle": "𐑯𐑿 𐑝𐑷𐑤𐑑", + "@newVaultDialogTitle": {}, + "configureVaultDialogTitle": "𐑒𐑩𐑯𐑓𐑦𐑜𐑼 𐑝𐑷𐑤𐑑", + "@configureVaultDialogTitle": {}, + "exportEntryDialogQuality": "𐑒𐑢𐑪𐑤𐑦𐑑𐑦", + "@exportEntryDialogQuality": {}, + "exportEntryDialogHeight": "𐑣𐑲𐑑", + "@exportEntryDialogHeight": {}, + "durationDialogSeconds": "𐑕𐑧𐑒𐑩𐑯𐑛𐑟", + "@durationDialogSeconds": {}, + "editEntryLocationDialogChooseOnMap": "𐑗𐑵𐑟 𐑪𐑯 𐑥𐑨𐑐", + "@editEntryLocationDialogChooseOnMap": {}, + "editEntryLocationDialogLatitude": "𐑤𐑨𐑑𐑦𐑑𐑿𐑛", + "@editEntryLocationDialogLatitude": {}, + "editEntryLocationDialogLongitude": "𐑤𐑪𐑯𐑡𐑦𐑑𐑿𐑛", + "@editEntryLocationDialogLongitude": {}, + "videoStreamSelectionDialogAudio": "𐑷𐑛𐑦𐑴", + "@videoStreamSelectionDialogAudio": {}, + "videoStreamSelectionDialogText": "𐑕𐑳𐑚𐑑𐑲𐑑𐑩𐑤𐑟", + "@videoStreamSelectionDialogText": {}, + "aboutDataUsageExternal": "𐑦𐑒𐑕𐑑𐑻𐑯𐑩𐑤", + "@aboutDataUsageExternal": {}, + "aboutTranslatorsSectionTitle": "𐑑𐑮𐑨𐑯𐑟𐑤𐑱𐑑𐑼𐑟", + "@aboutTranslatorsSectionTitle": {}, + "aboutLicensesSectionTitle": "𐑴𐑐𐑩𐑯-𐑕𐑹𐑕 𐑤𐑲𐑕𐑩𐑯𐑕𐑩𐑟", + "@aboutLicensesSectionTitle": {}, + "policyPageTitle": "𐑐𐑮𐑦𐑝𐑩𐑕𐑦 𐑐𐑪𐑤𐑩𐑕𐑦", + "@policyPageTitle": {}, + "collectionPageTitle": "𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯", + "@collectionPageTitle": {}, + "collectionActionEmptyBin": "𐑧𐑥𐑐𐑑𐑦 𐑚𐑦𐑯", + "@collectionActionEmptyBin": {}, + "collectionGroupAlbum": "𐑚𐑲 𐑨𐑤𐑚𐑩𐑥", + "@collectionGroupAlbum": {}, + "collectionActionEdit": "𐑧𐑛𐑦𐑑", + "@collectionActionEdit": {}, + "collectionDeleteFailureFeedback": "{count, plural, =1{𐑓𐑱𐑤𐑛 𐑑 𐑛𐑦𐑤𐑰𐑑 1 𐑲𐑑𐑩𐑥} other{𐑓𐑱𐑤𐑛 𐑑 𐑛𐑦𐑤𐑰𐑑 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionDeleteFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionMoveSuccessFeedback": "{count, plural, =1{𐑥𐑵𐑝𐑛 1 𐑲𐑑𐑩𐑥} other{𐑥𐑵𐑝𐑛 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionMoveSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "searchCollectionFieldHint": "𐑕𐑻𐑗 𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯", + "@searchCollectionFieldHint": {}, + "searchDateSectionTitle": "𐑛𐑱𐑑", + "@searchDateSectionTitle": {}, + "searchAlbumsSectionTitle": "𐑨𐑤𐑚𐑩𐑥𐑟", + "@searchAlbumsSectionTitle": {}, + "searchStatesSectionTitle": "𐑕𐑑𐑱𐑑𐑕", + "@searchStatesSectionTitle": {}, + "searchRatingSectionTitle": "𐑮𐑱𐑑𐑦𐑙𐑟", + "@searchRatingSectionTitle": {}, + "settingsActionExport": "𐑦𐑒𐑕𐑐𐑹𐑑", + "@settingsActionExport": {}, + "appExportFavourites": "𐑓𐑱𐑝𐑼𐑦𐑑𐑕", + "@appExportFavourites": {}, + "setHomeCustom": "𐑒𐑳𐑕𐑑𐑩𐑥", + "@setHomeCustom": {}, + "settingsKeepScreenOnDialogTitle": "𐑒𐑰𐑐 𐑕𐑒𐑮𐑰𐑯 𐑪𐑯", + "@settingsKeepScreenOnDialogTitle": {}, + "settingsConfirmationDialogTitle": "𐑒𐑪𐑯𐑓𐑼𐑥𐑱𐑖𐑩𐑯 𐑛𐑲𐑩𐑤𐑪𐑜𐑟", + "@settingsConfirmationDialogTitle": {}, + "settingsConfirmationAfterMoveToBinItems": "𐑖𐑴 𐑥𐑧𐑕𐑦𐑡 𐑭𐑓𐑑𐑼 𐑥𐑵𐑝𐑦𐑙 𐑲𐑑𐑩𐑥𐑟 𐑑 𐑞 𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯", + "@settingsConfirmationAfterMoveToBinItems": {}, + "settingsNavigationDrawerEditorPageTitle": "𐑯𐑨𐑝𐑦𐑜𐑱𐑖𐑩𐑯 𐑥𐑧𐑯𐑿", + "@settingsNavigationDrawerEditorPageTitle": {}, + "settingsThumbnailShowMotionPhotoIcon": "𐑖𐑴 𐑥𐑴𐑖𐑩𐑯 𐑓𐑴𐑑𐑴 𐑲𐑒𐑪𐑯", + "@settingsThumbnailShowMotionPhotoIcon": {}, + "settingsThumbnailOverlayTile": "𐑴𐑝𐑼𐑤𐑱", + "@settingsThumbnailOverlayTile": {}, + "vaultDialogLockModeWhenScreenOff": "𐑤𐑪𐑒 𐑢𐑧𐑯 𐑕𐑒𐑮𐑰𐑯 𐑑𐑻𐑯𐑟 𐑪𐑓", + "@vaultDialogLockModeWhenScreenOff": {}, + "vaultDialogLockTypeLabel": "𐑤𐑪𐑒 𐑑𐑲𐑐", + "@vaultDialogLockTypeLabel": {}, + "patternDialogEnter": "𐑧𐑯𐑑𐑼 𐑐𐑨𐑑𐑼𐑯", + "@patternDialogEnter": {}, + "patternDialogConfirm": "𐑒𐑩𐑯𐑓𐑻𐑥 𐑐𐑨𐑑𐑼𐑯", + "@patternDialogConfirm": {}, + "pinDialogEnter": "𐑧𐑯𐑑𐑼 ⸰𐑐𐑲𐑯", + "@pinDialogEnter": {}, + "pinDialogConfirm": "𐑒𐑩𐑯𐑓𐑻𐑥 ⸰𐑐𐑲𐑯", + "@pinDialogConfirm": {}, + "passwordDialogEnter": "𐑧𐑯𐑑𐑼 𐑐𐑭𐑕𐑢𐑻𐑛", + "@passwordDialogEnter": {}, + "passwordDialogConfirm": "𐑒𐑩𐑯𐑓𐑻𐑥 𐑐𐑭𐑕𐑢𐑻𐑛", + "@passwordDialogConfirm": {}, + "authenticateToConfigureVault": "𐑷𐑔𐑧𐑯𐑑𐑦𐑒𐑱𐑑 𐑑 𐑒𐑩𐑯𐑓𐑦𐑜𐑼 𐑝𐑷𐑤𐑑", + "@authenticateToConfigureVault": {}, + "authenticateToUnlockVault": "𐑷𐑔𐑧𐑯𐑑𐑦𐑒𐑱𐑑 𐑑 𐑳𐑯𐑤𐑪𐑒 𐑝𐑷𐑤𐑑", + "@authenticateToUnlockVault": {}, + "vaultBinUsageDialogMessage": "𐑕𐑳𐑥 𐑝𐑷𐑤𐑑𐑕 𐑸 𐑿𐑟𐑦𐑙 𐑞 𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯.", + "@vaultBinUsageDialogMessage": {}, + "renameAlbumDialogLabel": "𐑯𐑿 𐑯𐑱𐑥", + "@renameAlbumDialogLabel": {}, + "renameAlbumDialogLabelAlreadyExistsHelper": "𐑛𐑦𐑮𐑧𐑒𐑑𐑼𐑦 𐑷𐑤𐑮𐑧𐑛𐑦 𐑦𐑜𐑟𐑦𐑕𐑑𐑕", + "@renameAlbumDialogLabelAlreadyExistsHelper": {}, + "renameEntrySetPageTitle": "𐑮𐑰𐑯𐑱𐑥", + "@renameEntrySetPageTitle": {}, + "renameEntrySetPagePatternFieldLabel": "𐑯𐑱𐑥𐑦𐑙 𐑐𐑨𐑑𐑼𐑯", + "@renameEntrySetPagePatternFieldLabel": {}, + "renameEntrySetPageInsertTooltip": "𐑦𐑯𐑕𐑻𐑑 𐑓𐑰𐑤𐑛", + "@renameEntrySetPageInsertTooltip": {}, + "renameEntrySetPagePreviewSectionTitle": "𐑐𐑮𐑰𐑝𐑿", + "@renameEntrySetPagePreviewSectionTitle": {}, + "renameProcessorCounter": "𐑒𐑬𐑯𐑑𐑼", + "@renameProcessorCounter": {}, + "renameProcessorHash": "𐑣𐑨𐑖", + "@renameProcessorHash": {}, + "renameProcessorName": "𐑯𐑱𐑥", + "@renameProcessorName": {}, + "deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{¿𐑛𐑦𐑤𐑰𐑑 𐑞𐑦𐑕 𐑨𐑤𐑚𐑩𐑥 𐑯 𐑞 𐑲𐑑𐑩𐑥 𐑦𐑯 𐑦𐑑?} other{¿𐑛𐑦𐑤𐑰𐑑 𐑞𐑦𐑕 𐑨𐑤𐑚𐑩𐑥 𐑯 𐑞 {count} 𐑲𐑑𐑩𐑥𐑟 𐑦𐑯 𐑦𐑑?}}", + "@deleteSingleAlbumConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{¿𐑛𐑦𐑤𐑰𐑑 𐑞𐑰𐑟 𐑨𐑤𐑚𐑩𐑥𐑟 𐑯 𐑞 𐑲𐑑𐑩𐑥 𐑦𐑯 𐑞𐑧𐑥?} other{¿𐑛𐑦𐑤𐑰𐑑 𐑞𐑰𐑟 𐑨𐑤𐑚𐑩𐑥𐑟 𐑯 𐑞 {count} 𐑲𐑑𐑩𐑥𐑟 𐑦𐑯 𐑞𐑧𐑥?}}", + "@deleteMultiAlbumConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "exportEntryDialogFormat": "𐑓𐑹𐑥𐑨𐑑:", + "@exportEntryDialogFormat": {}, + "exportEntryDialogWidth": "𐑢𐑦𐑛𐑔", + "@exportEntryDialogWidth": {}, + "exportEntryDialogWriteMetadata": "𐑮𐑲𐑑 𐑥𐑧𐑑𐑩𐑛𐑱𐑑𐑩", + "@exportEntryDialogWriteMetadata": {}, + "renameEntryDialogLabel": "𐑯𐑿 𐑯𐑱𐑥", + "@renameEntryDialogLabel": {}, + "editEntryDialogCopyFromItem": "𐑒𐑪𐑐𐑦 𐑓𐑮𐑪𐑥 𐑳𐑞𐑼 𐑲𐑑𐑩𐑥", + "@editEntryDialogCopyFromItem": {}, + "editEntryDialogTargetFieldsHeader": "𐑓𐑰𐑤𐑛𐑟 𐑑 𐑥𐑪𐑛𐑦𐑓𐑲", + "@editEntryDialogTargetFieldsHeader": {}, + "editEntryDateDialogTitle": "𐑛𐑱𐑑 𐑯 𐑑𐑲𐑥", + "@editEntryDateDialogTitle": {}, + "editEntryDateDialogSetCustom": "𐑕𐑧𐑑 𐑒𐑳𐑕𐑑𐑩𐑥 𐑛𐑱𐑑", + "@editEntryDateDialogSetCustom": {}, + "editEntryDateDialogCopyField": "𐑒𐑪𐑐𐑦 𐑓𐑮𐑪𐑥 𐑳𐑞𐑼 𐑛𐑱𐑑", + "@editEntryDateDialogCopyField": {}, + "editEntryDateDialogExtractFromTitle": "𐑦𐑒𐑕𐑑𐑮𐑨𐑒𐑑 𐑓𐑮𐑪𐑥 𐑑𐑲𐑑𐑩𐑤", + "@editEntryDateDialogExtractFromTitle": {}, + "editEntryDateDialogShift": "𐑖𐑦𐑓𐑑", + "@editEntryDateDialogShift": {}, + "editEntryDateDialogSourceFileModifiedDate": "𐑓𐑲𐑤 𐑥𐑪𐑛𐑦𐑓𐑲𐑛 𐑛𐑱𐑑", + "@editEntryDateDialogSourceFileModifiedDate": {}, + "durationDialogHours": "𐑬𐑼𐑟", + "@durationDialogHours": {}, + "durationDialogMinutes": "𐑥𐑦𐑯𐑦𐑑𐑕", + "@durationDialogMinutes": {}, + "editEntryLocationDialogTitle": "𐑤𐑴𐑒𐑱𐑖𐑩𐑯", + "@editEntryLocationDialogTitle": {}, + "editEntryLocationDialogSetCustom": "𐑕𐑧𐑑 𐑒𐑳𐑕𐑑𐑩𐑥 𐑤𐑴𐑒𐑱𐑖𐑩𐑯", + "@editEntryLocationDialogSetCustom": {}, + "locationPickerUseThisLocationButton": "𐑿𐑟 𐑞𐑦𐑕 𐑤𐑴𐑒𐑱𐑖𐑩𐑯", + "@locationPickerUseThisLocationButton": {}, + "editEntryRatingDialogTitle": "𐑮𐑱𐑑𐑦𐑙", + "@editEntryRatingDialogTitle": {}, + "removeEntryMetadataDialogTitle": "𐑥𐑧𐑑𐑩𐑛𐑱𐑑𐑩 𐑮𐑦𐑥𐑵𐑝𐑩𐑤", + "@removeEntryMetadataDialogTitle": {}, + "removeEntryMetadataDialogMore": "𐑥𐑹", + "@removeEntryMetadataDialogMore": {}, + "removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "⸰𐑦𐑥𐑐 𐑦𐑟 𐑮𐑦𐑒𐑢𐑲𐑼𐑛 𐑑 𐑐𐑤𐑱 𐑞 𐑝𐑦𐑛𐑦𐑴 𐑦𐑯𐑕𐑲𐑛 𐑩 𐑥𐑴𐑖𐑩𐑯 𐑓𐑴𐑑𐑴.\n\n¿𐑸 𐑿 𐑖𐑫𐑼 𐑿 𐑢𐑪𐑯𐑑 𐑑 𐑮𐑦𐑥𐑵𐑝 𐑦𐑑?", + "@removeEntryMetadataMotionPhotoXmpWarningDialogMessage": {}, + "videoSpeedDialogLabel": "𐑐𐑤𐑱𐑚𐑨𐑒 𐑕𐑐𐑰𐑛", + "@videoSpeedDialogLabel": {}, + "videoStreamSelectionDialogVideo": "𐑝𐑦𐑛𐑦𐑴", + "@videoStreamSelectionDialogVideo": {}, + "videoStreamSelectionDialogOff": "𐑪𐑓", + "@videoStreamSelectionDialogOff": {}, + "videoStreamSelectionDialogTrack": "𐑑𐑮𐑨𐑒", + "@videoStreamSelectionDialogTrack": {}, + "videoStreamSelectionDialogNoSelection": "𐑞𐑺 𐑸 𐑯𐑴 𐑳𐑞𐑼 𐑑𐑮𐑨𐑒𐑕.", + "@videoStreamSelectionDialogNoSelection": {}, + "genericSuccessFeedback": "¡𐑛𐑳𐑯!", + "@genericSuccessFeedback": {}, + "genericFailureFeedback": "𐑓𐑱𐑤𐑛", + "@genericFailureFeedback": {}, + "genericDangerWarningDialogMessage": "¿𐑸 𐑿 𐑖𐑫𐑼?", + "@genericDangerWarningDialogMessage": {}, + "tooManyItemsErrorDialogMessage": "𐑑𐑮𐑲 𐑩𐑜𐑧𐑯 𐑢𐑦𐑞 𐑓𐑿𐑼 𐑲𐑑𐑩𐑥𐑟.", + "@tooManyItemsErrorDialogMessage": {}, + "menuActionConfigureView": "𐑝𐑿", + "@menuActionConfigureView": {}, + "menuActionSelect": "𐑕𐑦𐑤𐑧𐑒𐑑", + "@menuActionSelect": {}, + "menuActionSelectAll": "𐑕𐑦𐑤𐑧𐑒𐑑 𐑷𐑤", + "@menuActionSelectAll": {}, + "menuActionSelectNone": "𐑕𐑦𐑤𐑧𐑒𐑑 𐑯𐑳𐑯", + "@menuActionSelectNone": {}, + "menuActionMap": "𐑥𐑨𐑐", + "@menuActionMap": {}, + "menuActionSlideshow": "𐑕𐑤𐑲𐑛𐑖𐑴", + "@menuActionSlideshow": {}, + "menuActionStats": "𐑕𐑑𐑨𐑑𐑕", + "@menuActionStats": {}, + "viewDialogSortSectionTitle": "𐑕𐑹𐑑", + "@viewDialogSortSectionTitle": {}, + "viewDialogGroupSectionTitle": "𐑜𐑮𐑵𐑐", + "@viewDialogGroupSectionTitle": {}, + "viewDialogLayoutSectionTitle": "𐑤𐑱𐑬𐑑", + "@viewDialogLayoutSectionTitle": {}, + "viewDialogReverseSortOrder": "𐑮𐑦𐑝𐑻𐑕 𐑕𐑹𐑑 𐑹𐑛𐑼", + "@viewDialogReverseSortOrder": {}, + "tileLayoutMosaic": "𐑥𐑴𐑟𐑱𐑦𐑒", + "@tileLayoutMosaic": {}, + "tileLayoutGrid": "𐑜𐑮𐑦𐑛", + "@tileLayoutGrid": {}, + "tileLayoutList": "𐑤𐑦𐑕𐑑", + "@tileLayoutList": {}, + "castDialogTitle": "𐑒𐑭𐑕𐑑 𐑛𐑦𐑝𐑲𐑕𐑩𐑟", + "@castDialogTitle": {}, + "coverDialogTabCover": "𐑒𐑳𐑝𐑼", + "@coverDialogTabCover": {}, + "coverDialogTabApp": "𐑨𐑐", + "@coverDialogTabApp": {}, + "coverDialogTabColor": "𐑒𐑳𐑤𐑼", + "@coverDialogTabColor": {}, + "appPickDialogTitle": "𐑐𐑦𐑒 𐑨𐑐", + "@appPickDialogTitle": {}, + "appPickDialogNone": "𐑯𐑳𐑯", + "@appPickDialogNone": {}, + "aboutPageTitle": "𐑩𐑚𐑬𐑑", + "@aboutPageTitle": {}, + "aboutLinkLicense": "𐑤𐑲𐑕𐑩𐑯𐑕", + "@aboutLinkLicense": {}, + "aboutLinkPolicy": "𐑐𐑮𐑦𐑝𐑩𐑕𐑦 𐑐𐑪𐑤𐑩𐑕𐑦", + "@aboutLinkPolicy": {}, + "aboutBugSectionTitle": "𐑚𐑳𐑜 𐑮𐑦𐑐𐑹𐑑", + "@aboutBugSectionTitle": {}, + "aboutBugSaveLogInstruction": "𐑕𐑱𐑝 𐑨𐑐 𐑤𐑪𐑜𐑟 𐑑 𐑩 𐑓𐑲𐑤", + "@aboutBugSaveLogInstruction": {}, + "aboutBugCopyInfoInstruction": "𐑒𐑪𐑐𐑦 𐑕𐑦𐑕𐑑𐑩𐑥 𐑦𐑯𐑓𐑼𐑥𐑱𐑖𐑩𐑯", + "@aboutBugCopyInfoInstruction": {}, + "aboutBugCopyInfoButton": "𐑒𐑪𐑐𐑦", + "@aboutBugCopyInfoButton": {}, + "aboutBugReportInstruction": "𐑮𐑦𐑐𐑹𐑑 𐑪𐑯 ·𐑜𐑦𐑑𐑣𐑳𐑚 𐑢𐑦𐑞 𐑞 𐑤𐑪𐑜𐑟 𐑯 𐑕𐑦𐑕𐑑𐑩𐑥 𐑦𐑯𐑓𐑼𐑥𐑱𐑖𐑩𐑯", + "@aboutBugReportInstruction": {}, + "aboutBugReportButton": "𐑮𐑦𐑐𐑹𐑑", + "@aboutBugReportButton": {}, + "aboutDataUsageSectionTitle": "𐑛𐑱𐑑𐑩 𐑿𐑕𐑦𐑡", + "@aboutDataUsageSectionTitle": {}, + "aboutDataUsageData": "𐑛𐑱𐑑𐑩", + "@aboutDataUsageData": {}, + "aboutDataUsageCache": "𐑒𐑨𐑖", + "@aboutDataUsageCache": {}, + "aboutDataUsageDatabase": "𐑛𐑱𐑑𐑩𐑚𐑱𐑕", + "@aboutDataUsageDatabase": {}, + "aboutDataUsageMisc": "𐑥𐑦𐑕𐑤.", + "@aboutDataUsageMisc": {}, + "aboutDataUsageInternal": "𐑦𐑯𐑑𐑻𐑯𐑩𐑤", + "@aboutDataUsageInternal": {}, + "aboutDataUsageClearCache": "𐑒𐑤𐑽 𐑒𐑨𐑖", + "@aboutDataUsageClearCache": {}, + "aboutCreditsSectionTitle": "𐑒𐑮𐑧𐑛𐑦𐑑𐑕", + "@aboutCreditsSectionTitle": {}, + "aboutCreditsWorldAtlas1": "𐑞𐑦𐑕 𐑨𐑐 𐑿𐑟𐑩𐑟 𐑩 ⸰𐑑𐑪𐑐𐑩𐑡𐑕𐑪𐑯 𐑓𐑲𐑤 𐑓𐑮𐑪𐑥", + "@aboutCreditsWorldAtlas1": {}, + "aboutCreditsWorldAtlas2": "𐑳𐑯𐑛𐑼 ⸰𐑦𐑕𐑒 𐑤𐑲𐑕𐑩𐑯𐑕.", + "@aboutCreditsWorldAtlas2": {}, + "aboutLicensesBanner": "𐑞𐑦𐑕 𐑨𐑐 𐑿𐑟𐑩𐑟 𐑞 𐑓𐑪𐑤𐑴𐑦𐑙 𐑴𐑐𐑩𐑯-𐑕𐑹𐑕 𐑐𐑨𐑒𐑦𐑡𐑩𐑟 𐑯 𐑤𐑲𐑚𐑮𐑼𐑦𐑟.", + "@aboutLicensesBanner": {}, + "aboutLicensesAndroidLibrariesSectionTitle": "·𐑨𐑯𐑛𐑮𐑶𐑛 𐑤𐑲𐑚𐑮𐑼𐑦𐑟", + "@aboutLicensesAndroidLibrariesSectionTitle": {}, + "aboutLicensesFlutterPluginsSectionTitle": "·𐑓𐑤𐑳𐑑𐑼 𐑐𐑤𐑳𐑜𐑦𐑯𐑟", + "@aboutLicensesFlutterPluginsSectionTitle": {}, + "aboutLicensesFlutterPackagesSectionTitle": "·𐑓𐑤𐑳𐑑𐑼 𐑐𐑨𐑒𐑦𐑡𐑩𐑟", + "@aboutLicensesFlutterPackagesSectionTitle": {}, + "aboutLicensesDartPackagesSectionTitle": "·𐑛𐑸𐑑 𐑐𐑨𐑒𐑦𐑡𐑩𐑟", + "@aboutLicensesDartPackagesSectionTitle": {}, + "aboutLicensesShowAllButtonLabel": "𐑖𐑴 𐑷𐑤 𐑤𐑲𐑕𐑩𐑯𐑕𐑩𐑟", + "@aboutLicensesShowAllButtonLabel": {}, + "collectionPickPageTitle": "𐑐𐑦𐑒", + "@collectionPickPageTitle": {}, + "collectionSelectPageTitle": "𐑕𐑦𐑤𐑧𐑒𐑑 𐑲𐑑𐑩𐑥𐑟", + "@collectionSelectPageTitle": {}, + "collectionActionShowTitleSearch": "𐑖𐑴 𐑑𐑲𐑑𐑩𐑤 𐑓𐑦𐑤𐑑𐑼", + "@collectionActionShowTitleSearch": {}, + "collectionActionHideTitleSearch": "𐑣𐑲𐑛 𐑑𐑲𐑑𐑩𐑤 𐑓𐑦𐑤𐑑𐑼", + "@collectionActionHideTitleSearch": {}, + "collectionActionAddShortcut": "𐑨𐑛 𐑖𐑹𐑑𐑒𐑳𐑑", + "@collectionActionAddShortcut": {}, + "collectionActionSetHome": "𐑕𐑧𐑑 𐑨𐑟 𐑣𐑴𐑥", + "@collectionActionSetHome": {}, + "collectionActionCopy": "𐑒𐑪𐑐𐑦 𐑑 𐑨𐑤𐑚𐑩𐑥", + "@collectionActionCopy": {}, + "collectionActionMove": "𐑥𐑵𐑝 𐑑 𐑨𐑤𐑚𐑩𐑥", + "@collectionActionMove": {}, + "collectionActionRescan": "𐑮𐑰𐑕𐑒𐑨𐑯", + "@collectionActionRescan": {}, + "collectionSearchTitlesHintText": "𐑕𐑻𐑗 𐑑𐑲𐑑𐑩𐑤𐑟", + "@collectionSearchTitlesHintText": {}, + "collectionGroupMonth": "𐑚𐑲 𐑥𐑳𐑯𐑔", + "@collectionGroupMonth": {}, + "collectionGroupDay": "𐑚𐑲 𐑛𐑱", + "@collectionGroupDay": {}, + "collectionGroupNone": "𐑛𐑵 𐑯𐑪𐑑 𐑜𐑮𐑵𐑐", + "@collectionGroupNone": {}, + "sectionUnknown": "𐑳𐑯𐑯𐑴𐑯", + "@sectionUnknown": {}, + "dateToday": "𐑑𐑫𐑛𐑱", + "@dateToday": {}, + "dateYesterday": "𐑘𐑧𐑕𐑑𐑼𐑛𐑱", + "@dateYesterday": {}, + "dateThisMonth": "𐑞𐑦𐑕 𐑥𐑳𐑯𐑔", + "@dateThisMonth": {}, + "collectionCopyFailureFeedback": "{count, plural, =1{𐑓𐑱𐑤𐑛 𐑑 𐑒𐑪𐑐𐑦 1 𐑲𐑑𐑩𐑥} other{𐑓𐑱𐑤𐑛 𐑑 𐑒𐑪𐑐𐑦 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionCopyFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionMoveFailureFeedback": "{count, plural, =1{𐑓𐑱𐑤𐑛 𐑑 𐑥𐑵𐑝 1 𐑲𐑑𐑩𐑥} other{𐑓𐑱𐑤𐑛 𐑑 𐑥𐑵𐑝 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionMoveFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionRenameFailureFeedback": "{count, plural, =1{𐑓𐑱𐑤𐑛 𐑑 𐑮𐑰𐑯𐑱𐑥 1 𐑲𐑑𐑩𐑥} other{𐑓𐑱𐑤𐑛 𐑑 𐑮𐑰𐑯𐑱𐑥 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionRenameFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEditFailureFeedback": "{count, plural, =1{𐑓𐑱𐑤𐑛 𐑑 𐑧𐑛𐑦𐑑 1 𐑲𐑑𐑩𐑥} other{𐑓𐑱𐑤𐑛 𐑑 𐑧𐑛𐑦𐑑 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionEditFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionExportFailureFeedback": "{count, plural, =1{𐑓𐑱𐑤𐑛 𐑑 𐑦𐑒𐑕𐑐𐑹𐑑 1 𐑐𐑱𐑡} other{𐑓𐑱𐑤𐑛 𐑑 𐑦𐑒𐑕𐑐𐑹𐑑 {count} 𐑐𐑱𐑡𐑩𐑟}}", + "@collectionExportFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionCopySuccessFeedback": "{count, plural, =1{𐑒𐑪𐑐𐑦𐑛 1 𐑲𐑑𐑩𐑥} other{𐑒𐑪𐑐𐑦𐑛 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionCopySuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionRenameSuccessFeedback": "{count, plural, =1{𐑮𐑰𐑯𐑱𐑥𐑛 1 𐑲𐑑𐑩𐑥} other{𐑮𐑰𐑯𐑱𐑥𐑛 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionRenameSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEditSuccessFeedback": "{count, plural, =1{𐑧𐑛𐑦𐑑𐑩𐑛 1 𐑲𐑑𐑩𐑥} other{𐑧𐑛𐑦𐑑𐑩𐑛 {count} 𐑲𐑑𐑩𐑥𐑟}}", + "@collectionEditSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEmptyFavourites": "𐑯𐑴 𐑓𐑱𐑝𐑼𐑦𐑑𐑕", + "@collectionEmptyFavourites": {}, + "collectionEmptyVideos": "𐑯𐑴 𐑝𐑦𐑛𐑦𐑴𐑟", + "@collectionEmptyVideos": {}, + "collectionEmptyImages": "𐑯𐑴 𐑦𐑥𐑦𐑡𐑩𐑟", + "@collectionEmptyImages": {}, + "collectionEmptyGrantAccessButtonLabel": "𐑜𐑮𐑭𐑯𐑑 𐑨𐑒𐑕𐑧𐑕", + "@collectionEmptyGrantAccessButtonLabel": {}, + "collectionSelectSectionTooltip": "𐑕𐑦𐑤𐑧𐑒𐑑 𐑕𐑧𐑒𐑖𐑩𐑯", + "@collectionSelectSectionTooltip": {}, + "collectionDeselectSectionTooltip": "𐑛𐑰𐑕𐑦𐑤𐑧𐑒𐑑 𐑕𐑧𐑒𐑖𐑩𐑯", + "@collectionDeselectSectionTooltip": {}, + "drawerAboutButton": "𐑩𐑚𐑬𐑑", + "@drawerAboutButton": {}, + "drawerSettingsButton": "𐑕𐑧𐑑𐑦𐑙𐑟", + "@drawerSettingsButton": {}, + "drawerCollectionAll": "𐑷𐑤 𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯", + "@drawerCollectionAll": {}, + "drawerCollectionFavourites": "𐑓𐑱𐑝𐑼𐑦𐑑𐑕", + "@drawerCollectionFavourites": {}, + "drawerCollectionImages": "𐑦𐑥𐑦𐑡𐑩𐑟", + "@drawerCollectionImages": {}, + "drawerCollectionVideos": "𐑝𐑦𐑛𐑦𐑴𐑟", + "@drawerCollectionVideos": {}, + "drawerCollectionAnimated": "𐑨𐑯𐑦𐑥𐑱𐑑𐑩𐑛", + "@drawerCollectionAnimated": {}, + "drawerCollectionMotionPhotos": "𐑥𐑴𐑖𐑩𐑯 𐑓𐑴𐑑𐑴𐑟", + "@drawerCollectionMotionPhotos": {}, + "drawerCollectionPanoramas": "𐑐𐑨𐑯𐑼𐑭𐑥𐑩𐑟", + "@drawerCollectionPanoramas": {}, + "drawerCollectionRaws": "𐑮𐑷 𐑓𐑴𐑑𐑴𐑟", + "@drawerCollectionRaws": {}, + "drawerCollectionSphericalVideos": "360° 𐑝𐑦𐑛𐑦𐑴𐑟", + "@drawerCollectionSphericalVideos": {}, + "drawerAlbumPage": "𐑨𐑤𐑚𐑩𐑥𐑟", + "@drawerAlbumPage": {}, + "drawerCountryPage": "𐑒𐑳𐑯𐑑𐑮𐑦𐑟", + "@drawerCountryPage": {}, + "drawerPlacePage": "𐑐𐑤𐑱𐑕𐑩𐑟", + "@drawerPlacePage": {}, + "drawerTagPage": "𐑑𐑨𐑜𐑟", + "@drawerTagPage": {}, + "sortByDate": "𐑚𐑲 𐑛𐑱𐑑", + "@sortByDate": {}, + "sortByName": "𐑚𐑲 𐑯𐑱𐑥", + "@sortByName": {}, + "sortByItemCount": "𐑚𐑲 𐑲𐑑𐑩𐑥 𐑒𐑬𐑯𐑑", + "@sortByItemCount": {}, + "sortBySize": "𐑚𐑲 𐑕𐑲𐑟", + "@sortBySize": {}, + "sortByAlbumFileName": "𐑚𐑲 𐑨𐑤𐑚𐑩𐑥 𐑯 𐑓𐑲𐑤 𐑯𐑱𐑥", + "@sortByAlbumFileName": {}, + "sortByRating": "𐑚𐑲 𐑮𐑱𐑑𐑦𐑙", + "@sortByRating": {}, + "sortByDuration": "𐑚𐑲 𐑛𐑘𐑫𐑼𐑱𐑖𐑩𐑯", + "@sortByDuration": {}, + "sortOrderNewestFirst": "𐑯𐑿𐑩𐑕𐑑 𐑓𐑻𐑕𐑑", + "@sortOrderNewestFirst": {}, + "sortOrderOldestFirst": "𐑴𐑤𐑛𐑩𐑕𐑑 𐑓𐑻𐑕𐑑", + "@sortOrderOldestFirst": {}, + "sortOrderAtoZ": "·𐑐 𐑑 ·𐑿", + "@sortOrderAtoZ": {}, + "sortOrderZtoA": "·𐑿 𐑑 ·𐑐", + "@sortOrderZtoA": {}, + "sortOrderHighestFirst": "𐑣𐑲𐑩𐑕𐑑 𐑓𐑻𐑕𐑑", + "@sortOrderHighestFirst": {}, + "sortOrderLowestFirst": "𐑤𐑴𐑩𐑕𐑑 𐑓𐑻𐑕𐑑", + "@sortOrderLowestFirst": {}, + "sortOrderLargestFirst": "𐑤𐑸𐑡𐑩𐑕𐑑 𐑓𐑻𐑕𐑑", + "@sortOrderLargestFirst": {}, + "sortOrderSmallestFirst": "𐑕𐑥𐑷𐑤𐑩𐑕𐑑 𐑓𐑻𐑕𐑑", + "@sortOrderSmallestFirst": {}, + "sortOrderShortestFirst": "𐑖𐑹𐑑𐑩𐑕𐑑 𐑓𐑻𐑕𐑑", + "@sortOrderShortestFirst": {}, + "sortOrderLongestFirst": "𐑤𐑪𐑙𐑜𐑩𐑕𐑑 𐑓𐑻𐑕𐑑", + "@sortOrderLongestFirst": {}, + "albumGroupTier": "𐑚𐑲 𐑑𐑽", + "@albumGroupTier": {}, + "albumGroupType": "𐑚𐑲 𐑑𐑲𐑐", + "@albumGroupType": {}, + "albumGroupVolume": "𐑚𐑲 𐑕𐑑𐑹𐑦𐑡 𐑝𐑪𐑤𐑿𐑥", + "@albumGroupVolume": {}, + "albumGroupNone": "𐑛𐑵 𐑯𐑪𐑑 𐑜𐑮𐑵𐑐", + "@albumGroupNone": {}, + "albumMimeTypeMixed": "𐑥𐑦𐑒𐑕𐑑", + "@albumMimeTypeMixed": {}, + "albumPickPageTitleCopy": "𐑒𐑪𐑐𐑦 𐑑 𐑨𐑤𐑚𐑩𐑥", + "@albumPickPageTitleCopy": {}, + "albumPickPageTitleExport": "𐑦𐑒𐑕𐑐𐑹𐑑 𐑑 𐑨𐑤𐑚𐑩𐑥", + "@albumPickPageTitleExport": {}, + "albumPickPageTitleMove": "𐑥𐑵𐑝 𐑑 𐑨𐑤𐑚𐑩𐑥", + "@albumPickPageTitleMove": {}, + "albumPickPageTitlePick": "𐑐𐑦𐑒 𐑨𐑤𐑚𐑩𐑥", + "@albumPickPageTitlePick": {}, + "albumCamera": "𐑒𐑨𐑥𐑼𐑩", + "@albumCamera": {}, + "albumDownload": "𐑛𐑬𐑯𐑤𐑴𐑛", + "@albumDownload": {}, + "albumScreenshots": "𐑕𐑒𐑮𐑰𐑯𐑖𐑪𐑑𐑕", + "@albumScreenshots": {}, + "albumScreenRecordings": "𐑕𐑒𐑮𐑰𐑯 𐑮𐑦𐑒𐑹𐑛𐑦𐑙𐑟", + "@albumScreenRecordings": {}, + "albumVideoCaptures": "𐑝𐑦𐑛𐑦𐑴 𐑒𐑨𐑐𐑗𐑼𐑟", + "@albumVideoCaptures": {}, + "albumPageTitle": "𐑨𐑤𐑚𐑩𐑥𐑟", + "@albumPageTitle": {}, + "albumEmpty": "𐑯𐑴 𐑨𐑤𐑚𐑩𐑥𐑟", + "@albumEmpty": {}, + "createAlbumButtonLabel": "𐑒𐑮𐑦𐑱𐑑", + "@createAlbumButtonLabel": {}, + "newFilterBanner": "𐑯𐑿", + "@newFilterBanner": {}, + "countryPageTitle": "𐑒𐑳𐑯𐑑𐑮𐑦𐑟", + "@countryPageTitle": {}, + "countryEmpty": "𐑯𐑴 𐑒𐑳𐑯𐑑𐑮𐑦𐑟", + "@countryEmpty": {}, + "statePageTitle": "𐑕𐑑𐑱𐑑𐑕", + "@statePageTitle": {}, + "stateEmpty": "𐑯𐑴 𐑕𐑑𐑱𐑑𐑕", + "@stateEmpty": {}, + "placePageTitle": "𐑐𐑤𐑱𐑕𐑩𐑟", + "@placePageTitle": {}, + "placeEmpty": "𐑯𐑴 𐑐𐑤𐑱𐑕𐑩𐑟", + "@placeEmpty": {}, + "tagPageTitle": "𐑑𐑨𐑜𐑟", + "@tagPageTitle": {}, + "tagEmpty": "𐑯𐑴 𐑑𐑨𐑜𐑟", + "@tagEmpty": {}, + "binPageTitle": "𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯", + "@binPageTitle": {}, + "explorerPageTitle": "𐑦𐑒𐑕𐑐𐑤𐑹𐑼", + "@explorerPageTitle": {}, + "explorerActionSelectStorageVolume": "𐑕𐑦𐑤𐑧𐑒𐑑 𐑕𐑑𐑹𐑦𐑡", + "@explorerActionSelectStorageVolume": {}, + "selectStorageVolumeDialogTitle": "𐑕𐑦𐑤𐑧𐑒𐑑 𐑕𐑑𐑹𐑦𐑡", + "@selectStorageVolumeDialogTitle": {}, + "searchRecentSectionTitle": "𐑮𐑰𐑕𐑩𐑯𐑑", + "@searchRecentSectionTitle": {}, + "searchCountriesSectionTitle": "𐑒𐑳𐑯𐑑𐑮𐑦𐑟", + "@searchCountriesSectionTitle": {}, + "searchPlacesSectionTitle": "𐑐𐑤𐑱𐑕𐑩𐑟", + "@searchPlacesSectionTitle": {}, + "searchTagsSectionTitle": "𐑑𐑨𐑜𐑟", + "@searchTagsSectionTitle": {}, + "searchMetadataSectionTitle": "𐑥𐑧𐑑𐑩𐑛𐑱𐑑𐑩", + "@searchMetadataSectionTitle": {}, + "settingsPageTitle": "𐑕𐑧𐑑𐑦𐑙𐑟", + "@settingsPageTitle": {}, + "settingsSystemDefault": "𐑕𐑦𐑕𐑑𐑩𐑥 𐑛𐑦𐑓𐑷𐑤𐑑", + "@settingsSystemDefault": {}, + "settingsDefault": "𐑛𐑦𐑓𐑷𐑤𐑑", + "@settingsDefault": {}, + "settingsDisabled": "𐑛𐑦𐑕𐑱𐑚𐑩𐑤𐑛", + "@settingsDisabled": {}, + "settingsAskEverytime": "𐑭𐑕𐑒 𐑧𐑝𐑮𐑦𐑑𐑲𐑥", + "@settingsAskEverytime": {}, + "settingsModificationWarningDialogMessage": "𐑳𐑞𐑼 𐑕𐑧𐑑𐑦𐑙𐑟 𐑢𐑦𐑤 𐑚𐑰 𐑥𐑪𐑛𐑦𐑓𐑲𐑛.", + "@settingsModificationWarningDialogMessage": {}, + "settingsSearchFieldLabel": "𐑕𐑻𐑗 𐑕𐑧𐑑𐑦𐑙𐑟", + "@settingsSearchFieldLabel": {}, + "settingsSearchEmpty": "𐑯𐑴 𐑥𐑨𐑗𐑦𐑙 𐑕𐑧𐑑𐑦𐑙", + "@settingsSearchEmpty": {}, + "settingsActionExportDialogTitle": "𐑦𐑒𐑕𐑐𐑹𐑑", + "@settingsActionExportDialogTitle": {}, + "settingsActionImport": "𐑦𐑥𐑐𐑹𐑑", + "@settingsActionImport": {}, + "settingsActionImportDialogTitle": "𐑦𐑥𐑐𐑹𐑑", + "@settingsActionImportDialogTitle": {}, + "appExportCovers": "𐑒𐑳𐑝𐑼𐑟", + "@appExportCovers": {}, + "appExportSettings": "𐑕𐑧𐑑𐑦𐑙𐑟", + "@appExportSettings": {}, + "settingsNavigationSectionTitle": "𐑯𐑨𐑝𐑦𐑜𐑱𐑖𐑩𐑯", + "@settingsNavigationSectionTitle": {}, + "settingsHomeTile": "𐑣𐑴𐑥", + "@settingsHomeTile": {}, + "settingsHomeDialogTitle": "𐑣𐑴𐑥", + "@settingsHomeDialogTitle": {}, + "settingsShowBottomNavigationBar": "𐑖𐑴 𐑚𐑳𐑑𐑩𐑯 𐑯𐑨𐑝𐑦𐑜𐑱𐑖𐑩𐑯 𐑚𐑸", + "@settingsShowBottomNavigationBar": {}, + "settingsKeepScreenOnTile": "𐑒𐑰𐑐 𐑕𐑒𐑮𐑰𐑯 𐑪𐑯", + "@settingsKeepScreenOnTile": {}, + "settingsDoubleBackExit": "𐑑𐑨𐑐 «𐑚𐑨𐑒» 𐑑𐑢𐑲𐑕 𐑑 𐑧𐑒𐑕𐑦𐑑", + "@settingsDoubleBackExit": {}, + "settingsConfirmationTile": "𐑒𐑪𐑯𐑓𐑼𐑥𐑱𐑖𐑩𐑯 𐑛𐑲𐑩𐑤𐑪𐑜𐑟", + "@settingsConfirmationTile": {}, + "settingsConfirmationBeforeDeleteItems": "𐑭𐑕𐑒 𐑚𐑦𐑓𐑹 𐑛𐑦𐑤𐑰𐑑𐑦𐑙 𐑲𐑑𐑩𐑥𐑟 𐑓𐑼𐑧𐑝𐑼", + "@settingsConfirmationBeforeDeleteItems": {}, + "settingsConfirmationBeforeMoveToBinItems": "𐑭𐑕𐑒 𐑚𐑦𐑓𐑹 𐑥𐑵𐑝𐑦𐑙 𐑲𐑑𐑩𐑥𐑟 𐑑 𐑞 𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯", + "@settingsConfirmationBeforeMoveToBinItems": {}, + "settingsConfirmationBeforeMoveUndatedItems": "𐑭𐑕𐑒 𐑚𐑦𐑓𐑹 𐑥𐑵𐑝𐑦𐑙 𐑳𐑯𐑛𐑱𐑑𐑩𐑛 𐑲𐑑𐑩𐑥𐑟", + "@settingsConfirmationBeforeMoveUndatedItems": {}, + "settingsConfirmationVaultDataLoss": "𐑖𐑴 𐑝𐑷𐑤𐑑 𐑛𐑱𐑑𐑩 𐑤𐑪𐑕 𐑢𐑹𐑯𐑦𐑙", + "@settingsConfirmationVaultDataLoss": {}, + "settingsNavigationDrawerTile": "𐑯𐑨𐑝𐑦𐑜𐑱𐑖𐑩𐑯 𐑥𐑧𐑯𐑿", + "@settingsNavigationDrawerTile": {}, + "settingsNavigationDrawerBanner": "𐑑𐑳𐑗 𐑯 𐑣𐑴𐑤𐑛 𐑑 𐑥𐑵𐑝 𐑯 𐑮𐑰𐑹𐑛𐑼 𐑥𐑧𐑯𐑿 𐑲𐑑𐑩𐑥𐑟.", + "@settingsNavigationDrawerBanner": {}, + "settingsNavigationDrawerTabTypes": "𐑑𐑲𐑐𐑕", + "@settingsNavigationDrawerTabTypes": {}, + "settingsNavigationDrawerTabAlbums": "𐑨𐑤𐑚𐑩𐑥𐑟", + "@settingsNavigationDrawerTabAlbums": {}, + "settingsNavigationDrawerTabPages": "𐑐𐑱𐑡𐑩𐑟", + "@settingsNavigationDrawerTabPages": {}, + "settingsNavigationDrawerAddAlbum": "𐑨𐑛 𐑨𐑤𐑚𐑩𐑥", + "@settingsNavigationDrawerAddAlbum": {}, + "settingsThumbnailSectionTitle": "𐑔𐑳𐑥𐑯𐑱𐑤𐑟", + "@settingsThumbnailSectionTitle": {}, + "settingsThumbnailOverlayPageTitle": "𐑴𐑝𐑼𐑤𐑱", + "@settingsThumbnailOverlayPageTitle": {}, + "settingsThumbnailShowHdrIcon": "𐑖𐑴 ⸰𐑣𐑛𐑮 𐑲𐑒𐑪𐑯", + "@settingsThumbnailShowHdrIcon": {}, + "settingsThumbnailShowFavouriteIcon": "𐑖𐑴 𐑓𐑱𐑝𐑼𐑦𐑑 𐑲𐑒𐑪𐑯", + "@settingsThumbnailShowFavouriteIcon": {}, + "settingsThumbnailShowTagIcon": "𐑖𐑴 𐑑𐑨𐑜 𐑲𐑒𐑪𐑯", + "@settingsThumbnailShowTagIcon": {}, + "settingsThumbnailShowLocationIcon": "𐑖𐑴 𐑤𐑴𐑒𐑱𐑖𐑩𐑯 𐑲𐑒𐑪𐑯", + "@settingsThumbnailShowLocationIcon": {}, + "settingsThumbnailShowRating": "𐑖𐑴 𐑮𐑱𐑑𐑦𐑙", + "@settingsThumbnailShowRating": {}, + "settingsThumbnailShowRawIcon": "𐑖𐑴 𐑮𐑷 𐑲𐑒𐑪𐑯", + "@settingsThumbnailShowRawIcon": {}, + "settingsCollectionQuickActionEditorPageTitle": "𐑒𐑢𐑦𐑒 𐑨𐑒𐑖𐑩𐑯𐑟", + "@settingsCollectionQuickActionEditorPageTitle": {}, + "settingsCollectionQuickActionTabBrowsing": "𐑚𐑮𐑬𐑟𐑦𐑙", + "@settingsCollectionQuickActionTabBrowsing": {}, + "settingsCollectionQuickActionTabSelecting": "𐑕𐑦𐑤𐑧𐑒𐑑𐑦𐑙", + "@settingsCollectionQuickActionTabSelecting": {}, + "settingsCollectionBrowsingQuickActionEditorBanner": "𐑑𐑳𐑗 𐑯 𐑣𐑴𐑤𐑛 𐑑 𐑥𐑵𐑝 𐑚𐑳𐑑𐑩𐑯𐑟 𐑯 𐑕𐑦𐑤𐑧𐑒𐑑 𐑢𐑦𐑗 𐑨𐑒𐑖𐑩𐑯𐑟 𐑸 𐑛𐑦𐑕𐑐𐑤𐑱𐑛 𐑢𐑧𐑯 𐑚𐑮𐑬𐑟𐑦𐑙 𐑲𐑑𐑩𐑥𐑟.", + "@settingsCollectionBrowsingQuickActionEditorBanner": {}, + "settingsThumbnailShowVideoDuration": "𐑖𐑴 𐑝𐑦𐑛𐑦𐑴 𐑛𐑘𐑫𐑼𐑱𐑖𐑩𐑯", + "@settingsThumbnailShowVideoDuration": {}, + "settingsCollectionQuickActionsTile": "𐑒𐑢𐑦𐑒 𐑨𐑒𐑖𐑩𐑯𐑟", + "@settingsCollectionQuickActionsTile": {}, + "settingsCollectionSelectionQuickActionEditorBanner": "𐑑𐑳𐑗 𐑯 𐑣𐑴𐑤𐑛 𐑑 𐑥𐑵𐑝 𐑚𐑳𐑑𐑩𐑯𐑟 𐑯 𐑕𐑦𐑤𐑧𐑒𐑑 𐑢𐑦𐑗 𐑨𐑒𐑖𐑩𐑯𐑟 𐑸 𐑛𐑦𐑕𐑐𐑤𐑱𐑛 𐑢𐑧𐑯 𐑕𐑦𐑤𐑧𐑒𐑑𐑦𐑙 𐑲𐑑𐑩𐑥𐑟.", + "@settingsCollectionSelectionQuickActionEditorBanner": {}, + "settingsCollectionBurstPatternsTile": "𐑚𐑻𐑕𐑑 𐑐𐑨𐑑𐑼𐑯𐑟", + "@settingsCollectionBurstPatternsTile": {}, + "settingsCollectionBurstPatternsNone": "𐑯𐑳𐑯", + "@settingsCollectionBurstPatternsNone": {}, + "settingsViewerSectionTitle": "𐑝𐑿𐑼", + "@settingsViewerSectionTitle": {}, + "settingsViewerGestureSideTapNext": "𐑑𐑨𐑐 𐑪𐑯 𐑕𐑒𐑮𐑰𐑯 𐑧𐑡𐑩𐑟 𐑑 𐑖𐑴 𐑐𐑮𐑰𐑝𐑾𐑕/𐑯𐑧𐑒𐑕𐑑 𐑲𐑑𐑩𐑥", + "@settingsViewerGestureSideTapNext": {}, + "settingsViewerUseCutout": "𐑿𐑟 𐑒𐑳𐑑𐑬𐑑 𐑺𐑾", + "@settingsViewerUseCutout": {}, + "settingsViewerMaximumBrightness": "𐑥𐑨𐑒𐑕𐑦𐑥𐑩𐑥 𐑚𐑮𐑲𐑑𐑯𐑩𐑕", + "@settingsViewerMaximumBrightness": {}, + "settingsMotionPhotoAutoPlay": "𐑷𐑑𐑴 𐑐𐑤𐑱 𐑥𐑴𐑖𐑩𐑯 𐑓𐑴𐑑𐑴𐑟", + "@settingsMotionPhotoAutoPlay": {}, + "settingsImageBackground": "𐑦𐑥𐑦𐑡 𐑚𐑨𐑒𐑜𐑮𐑬𐑯𐑛", + "@settingsImageBackground": {}, + "settingsViewerQuickActionEditorBanner": "𐑑𐑳𐑗 𐑯 𐑣𐑴𐑤𐑛 𐑑 𐑥𐑵𐑝 𐑚𐑳𐑑𐑩𐑯𐑟 𐑯 𐑕𐑦𐑤𐑧𐑒𐑑 𐑢𐑦𐑗 𐑨𐑒𐑖𐑩𐑯𐑟 𐑸 𐑛𐑦𐑕𐑐𐑤𐑱𐑛 𐑦𐑯 𐑞 𐑝𐑿𐑼.", + "@settingsViewerQuickActionEditorBanner": {}, + "settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": "𐑛𐑦𐑕𐑐𐑤𐑱𐑛 𐑚𐑳𐑑𐑩𐑯𐑟", + "@settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": {}, + "settingsViewerQuickActionsTile": "𐑒𐑢𐑦𐑒 𐑨𐑒𐑖𐑩𐑯𐑟", + "@settingsViewerQuickActionsTile": {}, + "settingsViewerQuickActionEditorPageTitle": "𐑒𐑢𐑦𐑒 𐑨𐑒𐑖𐑩𐑯𐑟", + "@settingsViewerQuickActionEditorPageTitle": {}, + "settingsViewerQuickActionEditorAvailableButtonsSectionTitle": "𐑩𐑝𐑱𐑤𐑩𐑚𐑩𐑤 𐑚𐑳𐑑𐑩𐑯𐑟", + "@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {}, + "settingsViewerQuickActionEmpty": "𐑯𐑴 𐑚𐑳𐑑𐑩𐑯𐑟", + "@settingsViewerQuickActionEmpty": {}, + "settingsViewerOverlayTile": "𐑴𐑝𐑼𐑤𐑱", + "@settingsViewerOverlayTile": {}, + "settingsViewerOverlayPageTitle": "𐑴𐑝𐑼𐑤𐑱", + "@settingsViewerOverlayPageTitle": {}, + "settingsViewerShowOverlayOnOpening": "𐑖𐑴 𐑪𐑯 𐑴𐑐𐑩𐑯𐑦𐑙", + "@settingsViewerShowOverlayOnOpening": {}, + "settingsViewerShowHistogram": "𐑖𐑴 𐑣𐑦𐑕𐑑𐑩𐑜𐑮𐑨𐑥", + "@settingsViewerShowHistogram": {}, + "settingsViewerShowMinimap": "𐑖𐑴 𐑥𐑦𐑯𐑦𐑥𐑨𐑐", + "@settingsViewerShowMinimap": {}, + "settingsViewerShowInformation": "𐑖𐑴 𐑦𐑯𐑓𐑼𐑥𐑱𐑖𐑩𐑯", + "@settingsViewerShowInformation": {}, + "filePickerUseThisFolder": "𐑿𐑟 𐑞𐑦𐑕 𐑓𐑴𐑤𐑛𐑼", + "@filePickerUseThisFolder": {}, + "settingsViewerShowInformationSubtitle": "𐑖𐑴 𐑑𐑲𐑑𐑩𐑤, 𐑛𐑱𐑑, 𐑤𐑴𐑒𐑱𐑖𐑩𐑯, 𐑯𐑯𐑯", + "@settingsViewerShowInformationSubtitle": {}, + "settingsViewerShowOverlayThumbnails": "𐑖𐑴 𐑔𐑳𐑥𐑯𐑱𐑤𐑟", + "@settingsViewerShowOverlayThumbnails": {}, + "settingsViewerEnableOverlayBlurEffect": "𐑚𐑤𐑻 𐑦𐑓𐑧𐑒𐑑", + "@settingsViewerEnableOverlayBlurEffect": {}, + "statsTopStatesSectionTitle": "𐑑𐑪𐑐 𐑕𐑑𐑱𐑑𐑕", + "@statsTopStatesSectionTitle": {}, + "statsTopTagsSectionTitle": "𐑑𐑪𐑐 𐑑𐑨𐑜𐑟", + "@statsTopTagsSectionTitle": {}, + "viewerErrorDoesNotExist": "𐑞 𐑓𐑲𐑤 𐑯𐑴 𐑤𐑪𐑙𐑜𐑼 𐑦𐑜𐑟𐑦𐑕𐑑𐑕.", + "@viewerErrorDoesNotExist": {}, + "statsTopAlbumsSectionTitle": "𐑑𐑪𐑐 𐑨𐑤𐑚𐑩𐑥𐑟", + "@statsTopAlbumsSectionTitle": {}, + "viewerOpenPanoramaButtonLabel": "𐑴𐑐𐑩𐑯 𐑐𐑨𐑯𐑼𐑭𐑥𐑩", + "@viewerOpenPanoramaButtonLabel": {}, + "viewerSetWallpaperButtonLabel": "𐑕𐑧𐑑 𐑢𐑷𐑤𐑐𐑱𐑐𐑼", + "@viewerSetWallpaperButtonLabel": {}, + "viewerInfoPageTitle": "𐑦𐑯𐑓𐑴", + "@viewerInfoPageTitle": {}, + "mapAttributionOpenTopoMap": "[⸰𐑖𐑮𐑑𐑥](https://www.earthdata.nasa.gov/sensors/srtm) | 𐑑𐑲𐑤𐑟 𐑚𐑲 [·𐑴𐑐𐑩𐑯𐑑𐑪𐑐𐑩𐑥𐑨𐑐](https://opentopomap.org), [⸰𐑒𐑒 𐑚𐑲-𐑖𐑩](https://creativecommons.org/licenses/by-sa/3.0)", + "@mapAttributionOpenTopoMap": {}, + "mapAttributionOsmHot": "𐑑𐑲𐑤𐑟 𐑚𐑲 [⸰𐑣𐑴𐑑](https://www.hotosm.org) • 𐑣𐑴𐑕𐑑𐑩𐑛 𐑚𐑲 [⸰𐑴𐑕𐑥 ·𐑓𐑮𐑭𐑯𐑕](https://openstreetmap.fr)", + "@mapAttributionOsmHot": {}, + "mapAttributionStamen": "𐑑𐑲𐑤𐑟 𐑚𐑲 [·𐑕𐑑𐑱𐑥𐑩𐑯 𐑛𐑦𐑟𐑲𐑯](https://stamen.com), [⸰𐑒𐑒 𐑚𐑲 3.0](https://creativecommons.org/licenses/by/3.0)", + "@mapAttributionStamen": {}, + "settingsViewerShowShootingDetails": "𐑖𐑴 𐑖𐑵𐑑𐑦𐑙 𐑛𐑰𐑑𐑱𐑤𐑟", + "@settingsViewerShowShootingDetails": {}, + "settingsViewerShowDescription": "𐑖𐑴 𐑛𐑦𐑕𐑒𐑮𐑦𐑐𐑖𐑩𐑯", + "@settingsViewerShowDescription": {}, + "settingsViewerSlideshowTile": "𐑕𐑤𐑲𐑛𐑖𐑴", + "@settingsViewerSlideshowTile": {}, + "settingsViewerSlideshowPageTitle": "𐑕𐑤𐑲𐑛𐑖𐑴", + "@settingsViewerSlideshowPageTitle": {}, + "settingsViewerShowRatingTags": "𐑖𐑴 𐑮𐑱𐑑𐑦𐑙 𐑯 𐑑𐑨𐑜𐑟", + "@settingsViewerShowRatingTags": {}, + "settingsSlideshowRepeat": "𐑮𐑦𐑐𐑰𐑑", + "@settingsSlideshowRepeat": {}, + "settingsSlideshowShuffle": "𐑖𐑳𐑓𐑩𐑤", + "@settingsSlideshowShuffle": {}, + "settingsSlideshowFillScreen": "𐑓𐑦𐑤 𐑕𐑒𐑮𐑰𐑯", + "@settingsSlideshowFillScreen": {}, + "settingsSlideshowAnimatedZoomEffect": "𐑨𐑯𐑦𐑥𐑱𐑑𐑩𐑛 𐑟𐑵𐑥 𐑦𐑓𐑧𐑒𐑑", + "@settingsSlideshowAnimatedZoomEffect": {}, + "settingsSlideshowTransitionTile": "𐑑𐑮𐑨𐑯𐑟𐑦𐑖𐑩𐑯", + "@settingsSlideshowTransitionTile": {}, + "settingsSlideshowIntervalTile": "𐑦𐑯𐑑𐑼𐑝𐑩𐑤", + "@settingsSlideshowIntervalTile": {}, + "settingsSlideshowVideoPlaybackTile": "𐑝𐑦𐑛𐑦𐑴 𐑐𐑤𐑱𐑚𐑨𐑒", + "@settingsSlideshowVideoPlaybackTile": {}, + "settingsSlideshowVideoPlaybackDialogTitle": "𐑝𐑦𐑛𐑦𐑴 𐑐𐑤𐑱𐑚𐑨𐑒", + "@settingsSlideshowVideoPlaybackDialogTitle": {}, + "settingsVideoPageTitle": "𐑝𐑦𐑛𐑦𐑴 𐑕𐑧𐑑𐑦𐑙𐑟", + "@settingsVideoPageTitle": {}, + "settingsVideoSectionTitle": "𐑝𐑦𐑛𐑦𐑴", + "@settingsVideoSectionTitle": {}, + "settingsVideoShowVideos": "𐑖𐑴 𐑝𐑦𐑛𐑦𐑴𐑟", + "@settingsVideoShowVideos": {}, + "settingsVideoPlaybackTile": "𐑐𐑤𐑱𐑚𐑨𐑒", + "@settingsVideoPlaybackTile": {}, + "settingsVideoPlaybackPageTitle": "𐑐𐑤𐑱𐑚𐑨𐑒", + "@settingsVideoPlaybackPageTitle": {}, + "settingsVideoEnableHardwareAcceleration": "𐑣𐑸𐑛𐑢𐑺 𐑩𐑒𐑕𐑧𐑤𐑼𐑱𐑖𐑩𐑯", + "@settingsVideoEnableHardwareAcceleration": {}, + "settingsVideoAutoPlay": "𐑷𐑑𐑴 𐑐𐑤𐑱", + "@settingsVideoAutoPlay": {}, + "settingsVideoLoopModeTile": "𐑤𐑵𐑐 𐑥𐑴𐑛", + "@settingsVideoLoopModeTile": {}, + "settingsVideoLoopModeDialogTitle": "𐑤𐑵𐑐 𐑥𐑴𐑛", + "@settingsVideoLoopModeDialogTitle": {}, + "settingsVideoResumptionModeTile": "𐑮𐑦𐑟𐑿𐑥 𐑐𐑤𐑱𐑚𐑨𐑒", + "@settingsVideoResumptionModeTile": {}, + "settingsVideoResumptionModeDialogTitle": "𐑮𐑦𐑟𐑿𐑥 𐑐𐑤𐑱𐑚𐑨𐑒", + "@settingsVideoResumptionModeDialogTitle": {}, + "settingsVideoBackgroundMode": "𐑚𐑨𐑒𐑜𐑮𐑬𐑯𐑛 𐑥𐑴𐑛", + "@settingsVideoBackgroundMode": {}, + "settingsVideoBackgroundModeDialogTitle": "𐑚𐑨𐑒𐑜𐑮𐑬𐑯𐑛 𐑥𐑴𐑛", + "@settingsVideoBackgroundModeDialogTitle": {}, + "settingsVideoControlsTile": "𐑒𐑩𐑯𐑑𐑮𐑴𐑤𐑟", + "@settingsVideoControlsTile": {}, + "settingsVideoControlsPageTitle": "𐑒𐑩𐑯𐑑𐑮𐑴𐑤𐑟", + "@settingsVideoControlsPageTitle": {}, + "settingsVideoButtonsTile": "𐑚𐑳𐑑𐑩𐑯𐑟", + "@settingsVideoButtonsTile": {}, + "settingsVideoGestureDoubleTapTogglePlay": "𐑛𐑳𐑚𐑩𐑤 𐑑𐑨𐑐 𐑑 𐑐𐑤𐑱/𐑐𐑷𐑟", + "@settingsVideoGestureDoubleTapTogglePlay": {}, + "settingsVideoGestureSideDoubleTapSeek": "𐑛𐑳𐑚𐑩𐑤 𐑑𐑨𐑐 𐑪𐑯 𐑕𐑒𐑮𐑰𐑯 𐑧𐑡𐑩𐑟 𐑑 𐑕𐑰𐑒 𐑚𐑨𐑒𐑢𐑼𐑛/𐑓𐑹𐑢𐑼𐑛", + "@settingsVideoGestureSideDoubleTapSeek": {}, + "settingsVideoGestureVerticalDragBrightnessVolume": "𐑕𐑢𐑲𐑐 𐑳𐑐 𐑹 𐑛𐑬𐑯 𐑑 𐑩𐑡𐑳𐑕𐑑 𐑚𐑮𐑲𐑑𐑯𐑩𐑕/𐑝𐑪𐑤𐑿𐑥", + "@settingsVideoGestureVerticalDragBrightnessVolume": {}, + "settingsSubtitleThemeTile": "𐑕𐑳𐑚𐑑𐑲𐑑𐑩𐑤𐑟", + "@settingsSubtitleThemeTile": {}, + "settingsSubtitleThemePageTitle": "𐑕𐑳𐑚𐑑𐑲𐑑𐑩𐑤𐑟", + "@settingsSubtitleThemePageTitle": {}, + "settingsSubtitleThemeSample": "𐑞𐑦𐑕 𐑦𐑟 𐑩 𐑕𐑭𐑥𐑐𐑩𐑤.", + "@settingsSubtitleThemeSample": {}, + "settingsSubtitleThemeTextAlignmentTile": "𐑑𐑧𐑒𐑕𐑑 𐑩𐑤𐑲𐑯𐑥𐑩𐑯𐑑", + "@settingsSubtitleThemeTextAlignmentTile": {}, + "settingsSubtitleThemeTextAlignmentDialogTitle": "𐑑𐑧𐑒𐑕𐑑 𐑩𐑤𐑲𐑯𐑥𐑩𐑯𐑑", + "@settingsSubtitleThemeTextAlignmentDialogTitle": {}, + "settingsSubtitleThemeTextPositionTile": "𐑑𐑧𐑒𐑕𐑑 𐑐𐑩𐑟𐑦𐑖𐑩𐑯", + "@settingsSubtitleThemeTextPositionTile": {}, + "settingsSubtitleThemeTextPositionDialogTitle": "𐑑𐑧𐑒𐑕𐑑 𐑐𐑩𐑟𐑦𐑖𐑩𐑯", + "@settingsSubtitleThemeTextPositionDialogTitle": {}, + "settingsSubtitleThemeTextSize": "𐑑𐑧𐑒𐑕𐑑 𐑕𐑲𐑟", + "@settingsSubtitleThemeTextSize": {}, + "settingsSubtitleThemeShowOutline": "𐑖𐑴 𐑬𐑑𐑤𐑲𐑯 𐑯 𐑖𐑨𐑛𐑴", + "@settingsSubtitleThemeShowOutline": {}, + "settingsSubtitleThemeTextColor": "𐑑𐑧𐑒𐑕𐑑 𐑒𐑳𐑤𐑼", + "@settingsSubtitleThemeTextColor": {}, + "settingsSubtitleThemeTextOpacity": "𐑑𐑧𐑒𐑕𐑑 𐑴𐑐𐑨𐑕𐑦𐑑𐑦", + "@settingsSubtitleThemeTextOpacity": {}, + "settingsSubtitleThemeBackgroundColor": "𐑚𐑨𐑒𐑜𐑮𐑬𐑯𐑛 𐑒𐑳𐑤𐑼", + "@settingsSubtitleThemeBackgroundColor": {}, + "settingsSubtitleThemeBackgroundOpacity": "𐑚𐑨𐑒𐑜𐑮𐑬𐑯𐑛 𐑴𐑐𐑨𐑕𐑦𐑑𐑦", + "@settingsSubtitleThemeBackgroundOpacity": {}, + "settingsSubtitleThemeTextAlignmentLeft": "𐑤𐑧𐑓𐑑", + "@settingsSubtitleThemeTextAlignmentLeft": {}, + "settingsSubtitleThemeTextAlignmentCenter": "𐑕𐑧𐑯𐑑𐑼", + "@settingsSubtitleThemeTextAlignmentCenter": {}, + "settingsSubtitleThemeTextAlignmentRight": "𐑮𐑲𐑑", + "@settingsSubtitleThemeTextAlignmentRight": {}, + "settingsPrivacySectionTitle": "𐑐𐑮𐑦𐑝𐑩𐑕𐑦", + "@settingsPrivacySectionTitle": {}, + "settingsAllowInstalledAppAccess": "𐑩𐑤𐑬 𐑨𐑒𐑕𐑧𐑕 𐑑 𐑨𐑐 𐑦𐑯𐑝𐑩𐑯𐑑𐑼𐑦", + "@settingsAllowInstalledAppAccess": {}, + "settingsAllowInstalledAppAccessSubtitle": "𐑿𐑟𐑛 𐑑 𐑦𐑥𐑐𐑮𐑵𐑝 𐑨𐑤𐑚𐑩𐑥 𐑛𐑦𐑕𐑐𐑤𐑱", + "@settingsAllowInstalledAppAccessSubtitle": {}, + "settingsAllowErrorReporting": "𐑩𐑤𐑬 𐑩𐑯𐑪𐑯𐑦𐑥𐑩𐑕 𐑧𐑮𐑼 𐑮𐑦𐑐𐑹𐑑𐑦𐑙", + "@settingsAllowErrorReporting": {}, + "settingsSaveSearchHistory": "𐑕𐑱𐑝 𐑕𐑻𐑗 𐑣𐑦𐑕𐑑𐑼𐑦", + "@settingsSaveSearchHistory": {}, + "settingsEnableBin": "𐑿𐑟 𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯", + "@settingsEnableBin": {}, + "settingsEnableBinSubtitle": "𐑒𐑰𐑐 𐑛𐑦𐑤𐑰𐑑𐑩𐑛 𐑲𐑑𐑩𐑥𐑟 𐑓 30 𐑛𐑱𐑟", + "@settingsEnableBinSubtitle": {}, + "settingsDisablingBinWarningDialogMessage": "𐑲𐑑𐑩𐑥𐑟 𐑦𐑯 𐑞 𐑮𐑰𐑕𐑲𐑒𐑩𐑤 𐑚𐑦𐑯 𐑢𐑦𐑤 𐑚𐑰 𐑛𐑦𐑤𐑰𐑑𐑩𐑛 𐑓𐑼𐑧𐑝𐑼.", + "@settingsDisablingBinWarningDialogMessage": {}, + "settingsAllowMediaManagement": "𐑩𐑤𐑬 𐑥𐑰𐑛𐑾 𐑥𐑨𐑯𐑦𐑡𐑥𐑩𐑯𐑑", + "@settingsAllowMediaManagement": {}, + "settingsHiddenItemsTile": "𐑣𐑦𐑛𐑩𐑯 𐑲𐑑𐑩𐑥𐑟", + "@settingsHiddenItemsTile": {}, + "settingsHiddenItemsPageTitle": "𐑣𐑦𐑛𐑩𐑯 𐑲𐑑𐑩𐑥𐑟", + "@settingsHiddenItemsPageTitle": {}, + "settingsHiddenFiltersBanner": "𐑓𐑴𐑑𐑴𐑟 𐑯 𐑝𐑦𐑛𐑦𐑴𐑟 𐑥𐑨𐑗𐑦𐑙 𐑣𐑦𐑛𐑩𐑯 𐑓𐑦𐑤𐑑𐑼𐑟 𐑢𐑦𐑤 𐑯𐑪𐑑 𐑩𐑐𐑽 𐑦𐑯 𐑘𐑹 𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯.", + "@settingsHiddenFiltersBanner": {}, + "settingsHiddenFiltersEmpty": "𐑯𐑴 𐑣𐑦𐑛𐑩𐑯 𐑓𐑦𐑤𐑑𐑼𐑟", + "@settingsHiddenFiltersEmpty": {}, + "settingsStorageAccessTile": "𐑕𐑑𐑹𐑦𐑡 𐑨𐑒𐑕𐑧𐑕", + "@settingsStorageAccessTile": {}, + "settingsStorageAccessPageTitle": "𐑕𐑑𐑹𐑦𐑡 𐑨𐑒𐑕𐑧𐑕", + "@settingsStorageAccessPageTitle": {}, + "settingsStorageAccessEmpty": "𐑯𐑴 𐑨𐑒𐑕𐑧𐑕 𐑜𐑮𐑭𐑯𐑑𐑕", + "@settingsStorageAccessEmpty": {}, + "settingsStorageAccessRevokeTooltip": "𐑮𐑦𐑝𐑴𐑒", + "@settingsStorageAccessRevokeTooltip": {}, + "settingsAccessibilitySectionTitle": "𐑩𐑒𐑕𐑧𐑕𐑩𐑚𐑦𐑤𐑦𐑑𐑦", + "@settingsAccessibilitySectionTitle": {}, + "settingsRemoveAnimationsTile": "𐑮𐑦𐑥𐑵𐑝 𐑨𐑯𐑦𐑥𐑱𐑖𐑩𐑯𐑟", + "@settingsRemoveAnimationsTile": {}, + "settingsStorageAccessBanner": "𐑕𐑳𐑥 𐑛𐑦𐑮𐑧𐑒𐑑𐑼𐑦𐑟 𐑮𐑦𐑒𐑢𐑲𐑼 𐑩𐑯 𐑦𐑒𐑕𐑐𐑤𐑦𐑕𐑦𐑑 𐑨𐑒𐑕𐑧𐑕 𐑜𐑮𐑭𐑯𐑑 𐑑 𐑥𐑪𐑛𐑦𐑓𐑲 𐑓𐑲𐑤𐑟 𐑦𐑯 𐑞𐑧𐑥. 𐑿 𐑒𐑨𐑯 𐑮𐑦𐑝𐑿 𐑣𐑽 𐑛𐑦𐑮𐑧𐑒𐑑𐑼𐑦𐑟 𐑑 𐑢𐑦𐑗 𐑿 𐑐𐑮𐑰𐑝𐑾𐑕𐑤𐑦 𐑜𐑱𐑝 𐑨𐑒𐑕𐑧𐑕.", + "@settingsStorageAccessBanner": {}, + "settingsRemoveAnimationsDialogTitle": "𐑮𐑦𐑥𐑵𐑝 𐑨𐑯𐑦𐑥𐑱𐑖𐑩𐑯𐑟", + "@settingsRemoveAnimationsDialogTitle": {}, + "settingsTimeToTakeActionTile": "𐑑𐑲𐑥 𐑑 𐑑𐑱𐑒 𐑨𐑒𐑖𐑩𐑯", + "@settingsTimeToTakeActionTile": {}, + "settingsAccessibilityShowPinchGestureAlternatives": "𐑖𐑴 𐑥𐑳𐑤𐑑𐑦-𐑑𐑳𐑗 𐑡𐑧𐑕𐑗𐑼 𐑷𐑤𐑑𐑻𐑯𐑩𐑑𐑦𐑝𐑟", + "@settingsAccessibilityShowPinchGestureAlternatives": {}, + "settingsDisplaySectionTitle": "𐑛𐑦𐑕𐑐𐑤𐑱", + "@settingsDisplaySectionTitle": {}, + "settingsThemeBrightnessTile": "𐑔𐑰𐑥", + "@settingsThemeBrightnessTile": {}, + "settingsThemeBrightnessDialogTitle": "𐑔𐑰𐑥", + "@settingsThemeBrightnessDialogTitle": {}, + "settingsThemeColorHighlights": "𐑒𐑳𐑤𐑼 𐑣𐑲𐑤𐑲𐑑𐑕", + "@settingsThemeColorHighlights": {}, + "settingsThemeEnableDynamicColor": "𐑛𐑲𐑯𐑨𐑥𐑦𐑒 𐑒𐑳𐑤𐑼", + "@settingsThemeEnableDynamicColor": {}, + "settingsDisplayRefreshRateModeTile": "𐑛𐑦𐑕𐑐𐑤𐑱 𐑮𐑦𐑓𐑮𐑧𐑖 𐑮𐑱𐑑", + "@settingsDisplayRefreshRateModeTile": {}, + "settingsDisplayRefreshRateModeDialogTitle": "𐑮𐑦𐑓𐑮𐑧𐑖 𐑮𐑱𐑑", + "@settingsDisplayRefreshRateModeDialogTitle": {}, + "settingsDisplayUseTvInterface": "·𐑨𐑯𐑛𐑮𐑶𐑛 ⸰𐑑𐑝 𐑦𐑯𐑑𐑼𐑓𐑱𐑕", + "@settingsDisplayUseTvInterface": {}, + "settingsLanguageTile": "𐑤𐑨𐑙𐑜𐑢𐑦𐑡", + "@settingsLanguageTile": {}, + "settingsLanguagePageTitle": "𐑤𐑨𐑙𐑜𐑢𐑦𐑡", + "@settingsLanguagePageTitle": {}, + "settingsCoordinateFormatTile": "𐑒𐑴𐑹𐑛𐑦𐑯𐑩𐑑 𐑓𐑹𐑥𐑨𐑑", + "@settingsCoordinateFormatTile": {}, + "settingsCoordinateFormatDialogTitle": "𐑒𐑴𐑹𐑛𐑦𐑯𐑩𐑑 𐑓𐑹𐑥𐑨𐑑", + "@settingsCoordinateFormatDialogTitle": {}, + "settingsUnitSystemTile": "𐑿𐑯𐑦𐑑𐑕", + "@settingsUnitSystemTile": {}, + "settingsUnitSystemDialogTitle": "𐑿𐑯𐑦𐑑𐑕", + "@settingsUnitSystemDialogTitle": {}, + "settingsForceWesternArabicNumeralsTile": "𐑓𐑹𐑕 ·𐑨𐑮𐑩𐑚𐑦𐑒 𐑯𐑿𐑥𐑼𐑩𐑤𐑟", + "@settingsForceWesternArabicNumeralsTile": {}, + "settingsScreenSaverPageTitle": "𐑕𐑒𐑮𐑰𐑯 𐑕𐑱𐑝𐑼", + "@settingsScreenSaverPageTitle": {}, + "settingsLanguageSectionTitle": "𐑤𐑨𐑙𐑜𐑢𐑦𐑡 𐑯 𐑓𐑹𐑥𐑨𐑑𐑕", + "@settingsLanguageSectionTitle": {}, + "settingsWidgetPageTitle": "𐑓𐑴𐑑𐑴 𐑓𐑮𐑱𐑥", + "@settingsWidgetPageTitle": {}, + "settingsWidgetShowOutline": "𐑬𐑑𐑤𐑲𐑯", + "@settingsWidgetShowOutline": {}, + "settingsWidgetOpenPage": "𐑢𐑧𐑯 𐑑𐑨𐑐𐑦𐑙 𐑪𐑯 𐑞 𐑢𐑦𐑡𐑩𐑑", + "@settingsWidgetOpenPage": {}, + "settingsWidgetDisplayedItem": "𐑛𐑦𐑕𐑐𐑤𐑱𐑛 𐑲𐑑𐑩𐑥", + "@settingsWidgetDisplayedItem": {}, + "settingsCollectionTile": "𐑒𐑩𐑤𐑧𐑒𐑖𐑩𐑯", + "@settingsCollectionTile": {}, + "statsPageTitle": "𐑕𐑑𐑨𐑑𐑕", + "@statsPageTitle": {}, + "statsWithGps": "{count, plural, =1{1 𐑲𐑑𐑩𐑥 𐑢𐑦𐑞 𐑤𐑴𐑒𐑱𐑖𐑩𐑯} other{{count} 𐑲𐑑𐑩𐑥𐑟 𐑢𐑦𐑞 𐑤𐑴𐑒𐑱𐑖𐑩𐑯}}", + "@statsWithGps": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "statsTopCountriesSectionTitle": "𐑑𐑪𐑐 𐑒𐑳𐑯𐑑𐑮𐑦𐑟", + "@statsTopCountriesSectionTitle": {}, + "statsTopPlacesSectionTitle": "𐑑𐑪𐑐 𐑐𐑤𐑱𐑕𐑩𐑟", + "@statsTopPlacesSectionTitle": {}, + "viewerErrorUnknown": "¡𐑵𐑐𐑕!", + "@viewerErrorUnknown": {}, + "viewerInfoBackToViewerTooltip": "𐑚𐑨𐑒 𐑑 𐑝𐑿𐑼", + "@viewerInfoBackToViewerTooltip": {}, + "viewerInfoUnknown": "𐑳𐑯𐑯𐑴𐑯", + "@viewerInfoUnknown": {}, + "viewerInfoLabelDescription": "𐑛𐑦𐑕𐑒𐑮𐑦𐑐𐑖𐑩𐑯", + "@viewerInfoLabelDescription": {}, + "viewerInfoLabelTitle": "𐑑𐑲𐑑𐑩𐑤", + "@viewerInfoLabelTitle": {}, + "viewerInfoLabelDate": "𐑛𐑱𐑑", + "@viewerInfoLabelDate": {}, + "viewerInfoLabelResolution": "𐑮𐑧𐑟𐑩𐑤𐑵𐑖𐑩𐑯", + "@viewerInfoLabelResolution": {}, + "viewerInfoLabelSize": "𐑕𐑲𐑟", + "@viewerInfoLabelSize": {}, + "viewerInfoLabelUri": "⸰𐑿𐑮𐑲", + "@viewerInfoLabelUri": {}, + "viewerInfoLabelPath": "𐑐𐑭𐑔", + "@viewerInfoLabelPath": {}, + "viewerInfoLabelDuration": "𐑛𐑘𐑫𐑼𐑱𐑖𐑩𐑯", + "@viewerInfoLabelDuration": {}, + "viewerInfoLabelOwner": "𐑴𐑯𐑼", + "@viewerInfoLabelOwner": {}, + "viewerInfoLabelCoordinates": "𐑒𐑴𐑹𐑛𐑦𐑯𐑩𐑑𐑕", + "@viewerInfoLabelCoordinates": {}, + "viewerInfoLabelAddress": "𐑩𐑛𐑮𐑧𐑕", + "@viewerInfoLabelAddress": {}, + "mapStyleDialogTitle": "𐑥𐑨𐑐 𐑕𐑑𐑲𐑤", + "@mapStyleDialogTitle": {}, + "mapStyleTooltip": "𐑕𐑦𐑤𐑧𐑒𐑑 𐑥𐑨𐑐 𐑕𐑑𐑲𐑤", + "@mapStyleTooltip": {}, + "mapZoomInTooltip": "𐑟𐑵𐑥 𐑦𐑯", + "@mapZoomInTooltip": {}, + "mapZoomOutTooltip": "𐑟𐑵𐑥 𐑬𐑑", + "@mapZoomOutTooltip": {}, + "mapPointNorthUpTooltip": "𐑐𐑶𐑯𐑑 𐑯𐑹𐑔 𐑳𐑐", + "@mapPointNorthUpTooltip": {}, + "mapAttributionOsmData": "𐑥𐑨𐑐 𐑛𐑱𐑑𐑩 © [·𐑴𐑐𐑩𐑯𐑕𐑑𐑮𐑰𐑑𐑥𐑨𐑐](https://www.openstreetmap.org/copyright) 𐑒𐑩𐑯𐑑𐑮𐑦𐑚𐑘𐑩𐑑𐑼𐑟", + "@mapAttributionOsmData": {}, + "mapAttributionOsmLiberty": "𐑑𐑲𐑤𐑟 𐑚𐑲 [·𐑴𐑐𐑩𐑯𐑥𐑨𐑐𐑑𐑲𐑤𐑟](https://www.openmaptiles.org), [⸰𐑒𐑒 𐑚𐑲](https://creativecommons.org/licenses/by/4.0) • 𐑣𐑴𐑕𐑑𐑩𐑛 𐑚𐑲 [⸰𐑴𐑕𐑥 ·𐑩𐑥𐑧𐑮𐑦𐑒𐑭𐑯𐑩](https://tile.ourmap.us)", + "@mapAttributionOsmLiberty": {}, + "openMapPageTooltip": "𐑝𐑿 𐑪𐑯 ·𐑥𐑨𐑐 𐑐𐑱𐑡", + "@openMapPageTooltip": {}, + "mapEmptyRegion": "𐑯𐑴 𐑦𐑥𐑦𐑡𐑩𐑟 𐑦𐑯 𐑞𐑦𐑕 𐑮𐑰𐑡𐑩𐑯", + "@mapEmptyRegion": {}, + "viewerInfoOpenLinkText": "𐑴𐑐𐑩𐑯", + "@viewerInfoOpenLinkText": {}, + "viewerInfoViewXmlLinkText": "𐑝𐑿 ⸰𐑦𐑥𐑤", + "@viewerInfoViewXmlLinkText": {}, + "viewerInfoSearchFieldLabel": "𐑕𐑻𐑗 𐑥𐑧𐑑𐑩𐑛𐑱𐑑𐑩", + "@viewerInfoSearchFieldLabel": {}, + "viewerInfoOpenEmbeddedFailureFeedback": "𐑓𐑱𐑤𐑛 𐑑 𐑦𐑒𐑕𐑑𐑮𐑨𐑒𐑑 𐑦𐑥𐑚𐑧𐑛𐑩𐑛 𐑛𐑱𐑑𐑩", + "@viewerInfoOpenEmbeddedFailureFeedback": {}, + "viewerInfoSearchEmpty": "𐑯𐑴 𐑥𐑨𐑗𐑦𐑙 𐑒𐑰𐑟", + "@viewerInfoSearchEmpty": {}, + "viewerInfoSearchSuggestionDescription": "𐑛𐑦𐑕𐑒𐑮𐑦𐑐𐑖𐑩𐑯", + "@viewerInfoSearchSuggestionDescription": {}, + "viewerInfoSearchSuggestionDimensions": "𐑛𐑦𐑥𐑧𐑯𐑖𐑩𐑯𐑟", + "@viewerInfoSearchSuggestionDimensions": {}, + "viewerInfoSearchSuggestionResolution": "𐑮𐑧𐑟𐑩𐑤𐑵𐑖𐑩𐑯", + "@viewerInfoSearchSuggestionResolution": {}, + "viewerInfoSearchSuggestionDate": "𐑛𐑱𐑑 𐑯 𐑑𐑲𐑥", + "@viewerInfoSearchSuggestionDate": {}, + "viewerInfoSearchSuggestionRights": "𐑮𐑲𐑑𐑕", + "@viewerInfoSearchSuggestionRights": {}, + "wallpaperUseScrollEffect": "𐑿𐑟 𐑕𐑒𐑮𐑴𐑤 𐑦𐑓𐑧𐑒𐑑 𐑪𐑯 𐑣𐑴𐑥 𐑕𐑒𐑮𐑰𐑯", + "@wallpaperUseScrollEffect": {}, + "tagEditorPageTitle": "𐑧𐑛𐑦𐑑 𐑑𐑨𐑜𐑟", + "@tagEditorPageTitle": {}, + "tagEditorPageNewTagFieldLabel": "𐑯𐑿 𐑑𐑨𐑜", + "@tagEditorPageNewTagFieldLabel": {}, + "tagEditorPageAddTagTooltip": "𐑨𐑛 𐑑𐑨𐑜", + "@tagEditorPageAddTagTooltip": {}, + "tagEditorSectionRecent": "𐑮𐑰𐑕𐑩𐑯𐑑", + "@tagEditorSectionRecent": {}, + "tagEditorSectionPlaceholders": "𐑐𐑤𐑱𐑕𐑣𐑴𐑤𐑛𐑼𐑟", + "@tagEditorSectionPlaceholders": {}, + "tagEditorDiscardDialogMessage": "¿𐑛𐑵 𐑿 𐑢𐑪𐑯𐑑 𐑑 𐑛𐑦𐑕𐑒𐑸𐑛 𐑗𐑱𐑯𐑡𐑩𐑟?", + "@tagEditorDiscardDialogMessage": {}, + "tagPlaceholderCountry": "𐑒𐑳𐑯𐑑𐑮𐑦", + "@tagPlaceholderCountry": {}, + "tagPlaceholderState": "𐑕𐑑𐑱𐑑", + "@tagPlaceholderState": {}, + "tagPlaceholderPlace": "𐑐𐑤𐑱𐑕", + "@tagPlaceholderPlace": {}, + "panoramaEnableSensorControl": "𐑦𐑯𐑱𐑚𐑩𐑤 𐑕𐑧𐑯𐑕𐑼 𐑒𐑩𐑯𐑑𐑮𐑴𐑤", + "@panoramaEnableSensorControl": {}, + "panoramaDisableSensorControl": "𐑛𐑦𐑕𐑱𐑚𐑩𐑤 𐑕𐑧𐑯𐑕𐑼 𐑒𐑩𐑯𐑑𐑮𐑴𐑤", + "@panoramaDisableSensorControl": {}, + "sourceViewerPageTitle": "𐑕𐑹𐑕", + "@sourceViewerPageTitle": {}, + "filePickerShowHiddenFiles": "𐑖𐑴 𐑣𐑦𐑛𐑩𐑯 𐑓𐑲𐑤𐑟", + "@filePickerShowHiddenFiles": {}, + "filePickerDoNotShowHiddenFiles": "𐑛𐑴𐑯'𐑑 𐑖𐑴 𐑣𐑦𐑛𐑩𐑯 𐑓𐑲𐑤𐑟", + "@filePickerDoNotShowHiddenFiles": {}, + "filePickerOpenFrom": "𐑴𐑐𐑩𐑯 𐑓𐑮𐑪𐑥", + "@filePickerOpenFrom": {}, + "filePickerNoItems": "𐑯𐑴 𐑲𐑑𐑩𐑥𐑟", + "@filePickerNoItems": {}, + "videoActionShowPreviousFrame": "𐑖𐑴 𐑐𐑮𐑰𐑝𐑾𐑕 𐑓𐑮𐑱𐑥", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "𐑖𐑴 𐑯𐑧𐑒𐑕𐑑 𐑓𐑮𐑱𐑥", + "@videoActionShowNextFrame": {} +} diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 8567db5ab..af2c69363 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -217,12 +217,6 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Siempre", "@videoLoopModeAlways": {}, - "videoControlsNone": "Ninguno", - "@videoControlsNone": {}, - "videoControlsPlay": "Reproducir", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Reproducir y buscar", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Reproducir externamente", "@videoControlsPlayOutside": {}, "mapStyleGoogleNormal": "Google Maps", @@ -1392,5 +1386,9 @@ "mapAttributionOsmLiberty": "Mosaicos por [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Alojado por [OSM Americana](https://tile.ourmap.us)", "@mapAttributionOsmLiberty": {}, "mapStyleOsmLiberty": "OSM Liberty", - "@mapStyleOsmLiberty": {} + "@mapStyleOsmLiberty": {}, + "videoActionShowPreviousFrame": "Mostrar fotograma anterior", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "Mostrar fotograma siguiente", + "@videoActionShowNextFrame": {} } diff --git a/lib/l10n/app_eu.arb b/lib/l10n/app_eu.arb index d69479c8e..30a34e155 100644 --- a/lib/l10n/app_eu.arb +++ b/lib/l10n/app_eu.arb @@ -236,8 +236,6 @@ "@entryActionViewSource": {}, "unitSystemMetric": "Metriko", "@unitSystemMetric": {}, - "videoControlsPlay": "Erreproduzitu", - "@videoControlsPlay": {}, "entryActionShowGeoTiffOnMap": "Erakutsi gainjarritako mapa bezala", "@entryActionShowGeoTiffOnMap": {}, "coordinateFormatDms": "DMS (Dokumentuak kudeatzeko sistema)", @@ -271,12 +269,8 @@ "@videoLoopModeNever": {}, "videoLoopModeShortOnly": "Bideo laburrak soilik", "@videoLoopModeShortOnly": {}, - "videoControlsPlaySeek": "Erreproduzitu eta aurrera edo atzera egin", - "@videoControlsPlaySeek": {}, "nameConflictStrategySkip": "Jauzi", "@nameConflictStrategySkip": {}, - "videoControlsNone": "Bat ere ez", - "@videoControlsNone": {}, "keepScreenOnNever": "Inoiz", "@keepScreenOnNever": {}, "nameConflictStrategyReplace": "Ordezkatu", diff --git a/lib/l10n/app_fa.arb b/lib/l10n/app_fa.arb index d3b38f5ef..e3e097cf1 100644 --- a/lib/l10n/app_fa.arb +++ b/lib/l10n/app_fa.arb @@ -232,8 +232,6 @@ "@filterTypeMotionPhotoLabel": {}, "unitSystemImperial": "مایلی", "@unitSystemImperial": {}, - "videoControlsPlaySeek": "پخش، برگشت به عقب، جلو رفتن", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "باز کردن با برنامه‌ای دیگر", "@videoControlsPlayOutside": {}, "videoLoopModeShortOnly": "فقط برای ویدئو های کوتاه", @@ -252,8 +250,6 @@ "@menuActionStats": {}, "exportEntryDialogWidth": "عرض", "@exportEntryDialogWidth": {}, - "videoControlsPlay": "پخش", - "@videoControlsPlay": {}, "mapZoomInTooltip": "بزرگ نمایی", "@mapZoomInTooltip": {}, "chipActionFilterOut": "پاک‌کردن از پالایش", @@ -417,8 +413,6 @@ "@filterAspectRatioPortraitLabel": {}, "filterTypeGeotiffLabel": "GeoTIFF", "@filterTypeGeotiffLabel": {}, - "videoControlsNone": "هیچ‌کدام", - "@videoControlsNone": {}, "otherDirectoryDescription": "شاخهٔ «{name}»", "@otherDirectoryDescription": { "placeholders": { diff --git a/lib/l10n/app_fi.arb b/lib/l10n/app_fi.arb index 8e7910a83..c86a03d1a 100644 --- a/lib/l10n/app_fi.arb +++ b/lib/l10n/app_fi.arb @@ -376,14 +376,8 @@ "@vaultLockTypePassword": {}, "settingsVideoEnablePip": "Kuva kuvassa", "@settingsVideoEnablePip": {}, - "videoControlsPlay": "Toista", - "@videoControlsPlay": {}, "videoControlsPlayOutside": "Avaa toisella soittimella", "@videoControlsPlayOutside": {}, - "videoControlsPlaySeek": "Toista & selaa eteen/taakse", - "@videoControlsPlaySeek": {}, - "videoControlsNone": "Ei mitään", - "@videoControlsNone": {}, "videoLoopModeNever": "Ei koskaan", "@videoLoopModeNever": {}, "videoLoopModeShortOnly": "Vain lyhyissä videoissa", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index c6602e0ae..8380f1809 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Toujours", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Lecture", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Lecture & déplacement", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Ouvrir avec un autre lecteur", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Aucun", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (Satellite)", @@ -1392,5 +1386,9 @@ "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Fond de carte par [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", "@mapAttributionOpenTopoMap": {}, "mapAttributionOsmData": "Données © les contributeurs d’[OpenStreetMap](https://www.openstreetmap.org/copyright)", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "videoActionShowNextFrame": "Montrer l’image suivante", + "@videoActionShowNextFrame": {}, + "videoActionShowPreviousFrame": "Montrer l’image précédente", + "@videoActionShowPreviousFrame": {} } diff --git a/lib/l10n/app_gl.arb b/lib/l10n/app_gl.arb index c1a34ee9c..936b628cb 100644 --- a/lib/l10n/app_gl.arb +++ b/lib/l10n/app_gl.arb @@ -273,18 +273,12 @@ "@unitSystemMetric": {}, "unitSystemImperial": "Imperial", "@unitSystemImperial": {}, - "videoControlsPlay": "Reproducir", - "@videoControlsPlay": {}, "videoLoopModeNever": "Nunca", "@videoLoopModeNever": {}, "videoLoopModeAlways": "Sempre", "@videoLoopModeAlways": {}, - "videoControlsPlaySeek": "Reprroduce e busca cara atrás/adelante", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Abrir con outro xogador", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Ningún", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "videoLoopModeShortOnly": "Só vídeos curtos", diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb index fee20d0aa..c027bc9ca 100644 --- a/lib/l10n/app_hi.arb +++ b/lib/l10n/app_hi.arb @@ -266,8 +266,6 @@ "@themeBrightnessDark": {}, "themeBrightnessBlack": "काला", "@themeBrightnessBlack": {}, - "videoControlsPlaySeek": "पिछड़े / आगे की तलाश करें", - "@videoControlsPlaySeek": {}, "mapStyleOsmHot": "Humanitarian OSM", "@mapStyleOsmHot": {}, "filterAspectRatioPortraitLabel": "पोर्ट्रेट", @@ -439,12 +437,8 @@ "@authenticateToUnlockVault": {}, "settingsVideoEnablePip": "पिक्चर-इन-पिक्चर", "@settingsVideoEnablePip": {}, - "videoControlsPlay": "चलाएं", - "@videoControlsPlay": {}, "videoControlsPlayOutside": "अन्य प्लेयर के साथ खोलें", "@videoControlsPlayOutside": {}, - "videoControlsNone": "कोई नहीं", - "@videoControlsNone": {}, "videoLoopModeShortOnly": "केवल लघु वीडियो", "@videoLoopModeShortOnly": {}, "videoPlaybackWithSound": "ध्वनि के साथ चलाए", diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb index b6534f246..313641c08 100644 --- a/lib/l10n/app_hu.arb +++ b/lib/l10n/app_hu.arb @@ -67,10 +67,6 @@ "@themeBrightnessDark": {}, "vaultLockTypePassword": "Jelszó", "@vaultLockTypePassword": {}, - "videoControlsPlay": "Lejátszás", - "@videoControlsPlay": {}, - "videoControlsNone": "Nincs", - "@videoControlsNone": {}, "videoLoopModeAlways": "Mindig", "@videoLoopModeAlways": {}, "viewerTransitionNone": "Nincs", @@ -913,8 +909,6 @@ "@accessibilityAnimationsKeep": {}, "keepScreenOnViewerOnly": "Csak a megtekintőnél", "@keepScreenOnViewerOnly": {}, - "videoControlsPlaySeek": "Lejátszás és ugrás vissza/előre", - "@videoControlsPlaySeek": {}, "viewerTransitionSlide": "Csúsztatás", "@viewerTransitionSlide": {}, "viewerTransitionFade": "Áttünés", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 22b5ae442..38d90b999 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -225,14 +225,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Selalu", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Putar", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Putar dan cari", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Buka dengan pemutar lain", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Tidak ada", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (Hybrid)", @@ -1392,5 +1386,9 @@ "mapStyleOsmLiberty": "OSM Liberty", "@mapStyleOsmLiberty": {}, "mapAttributionOsmLiberty": "Ubin oleh [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Disediakan oleh [OSM Americana](https://tile.ourmap.us)", - "@mapAttributionOsmLiberty": {} + "@mapAttributionOsmLiberty": {}, + "videoActionShowPreviousFrame": "Tampilkan bingkai sebelumnya", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "Tampilkan bingkai berikutnya", + "@videoActionShowNextFrame": {} } diff --git a/lib/l10n/app_is.arb b/lib/l10n/app_is.arb index 991b394c4..ed5136344 100644 --- a/lib/l10n/app_is.arb +++ b/lib/l10n/app_is.arb @@ -463,8 +463,6 @@ "@settingsEnableBin": {}, "entryActionViewMotionPhotoVideo": "Opna myndskeið", "@entryActionViewMotionPhotoVideo": {}, - "videoControlsNone": "Ekkert", - "@videoControlsNone": {}, "otherDirectoryDescription": "„{name}“ möppu", "@otherDirectoryDescription": { "placeholders": { @@ -673,8 +671,6 @@ "@drawerCollectionFavourites": {}, "filterTypeRawLabel": "RAW", "@filterTypeRawLabel": {}, - "videoControlsPlaySeek": "Spila og leita afturábak/áfram", - "@videoControlsPlaySeek": {}, "settingsSubtitleThemeTextAlignmentCenter": "Miðjað", "@settingsSubtitleThemeTextAlignmentCenter": {}, "keepScreenOnVideoPlayback": "Á meðan myndskeið er í spilun", @@ -918,8 +914,6 @@ "@viewerErrorDoesNotExist": {}, "albumCamera": "Myndavél", "@albumCamera": {}, - "videoControlsPlay": "Afspilun", - "@videoControlsPlay": {}, "settingsNavigationSectionTitle": "Flakk", "@settingsNavigationSectionTitle": {}, "settingsDisplayRefreshRateModeDialogTitle": "Uppfærslutíðni", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 57aacabfd..563943a31 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Sempre", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Riproduci", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Riproduci e cerca avanti/indietro", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Apri con un altro lettore", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Nessuno", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (Ibrido)", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 574fbca5b..38065cee7 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -217,14 +217,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "常にループ再生", "@videoLoopModeAlways": {}, - "videoControlsPlay": "再生", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "再生&早送り/早戻し", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "他のプレイヤーで開く", "@videoControlsPlayOutside": {}, - "videoControlsNone": "なし", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google マップ", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google マップ(ハイブリッド)", diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index b424a0834..b046e0674 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "항상 반복", "@videoLoopModeAlways": {}, - "videoControlsPlay": "재생", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "재생 및 앞뒤로 가기", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "다른 앱에서 열기", "@videoControlsPlayOutside": {}, - "videoControlsNone": "없음", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google 지도", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google 지도 (위성)", @@ -1392,5 +1386,9 @@ "mapAttributionOsmLiberty": "타일 [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • 호스팅 [OSM Americana](https://tile.ourmap.us)", "@mapAttributionOsmLiberty": {}, "mapStyleOsmLiberty": "OSM Liberty", - "@mapStyleOsmLiberty": {} + "@mapStyleOsmLiberty": {}, + "videoActionShowNextFrame": "다음 프레임 보기", + "@videoActionShowNextFrame": {}, + "videoActionShowPreviousFrame": "이전 프레임 보기", + "@videoActionShowPreviousFrame": {} } diff --git a/lib/l10n/app_lt.arb b/lib/l10n/app_lt.arb index f46f64c72..fb053e921 100644 --- a/lib/l10n/app_lt.arb +++ b/lib/l10n/app_lt.arb @@ -139,8 +139,6 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Visada", "@videoLoopModeAlways": {}, - "videoControlsNone": "Jokie", - "@videoControlsNone": {}, "keepScreenOnNever": "Niekada", "@keepScreenOnNever": {}, "keepScreenOnAlways": "Visada", @@ -935,10 +933,6 @@ "@filterNoLocationLabel": {}, "filterMimeImageLabel": "Paveikslėlis", "@filterMimeImageLabel": {}, - "videoControlsPlay": "Groti", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Groti ir peršokti pirmyn/atgal", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Atidaryti su kitu grotuvu", "@videoControlsPlayOutside": {}, "mapStyleGoogleNormal": "Google Maps", diff --git a/lib/l10n/app_my.arb b/lib/l10n/app_my.arb index 6dd6f843d..a57bb2fd6 100644 --- a/lib/l10n/app_my.arb +++ b/lib/l10n/app_my.arb @@ -464,8 +464,6 @@ "@vaultLockTypePattern": {}, "videoControlsPlayOutside": "တခြား player နဲ့ဖွင့်တဲ့ခလုတ်", "@videoControlsPlayOutside": {}, - "videoControlsNone": "ဘာမှမထား", - "@videoControlsNone": {}, "videoLoopModeNever": "ဘယ်တော့မှမလုပ်ပါနှင့်", "@videoLoopModeNever": {}, "videoLoopModeShortOnly": "ဗီဒီယိုအတိုလေးတွေတွင်သာ", @@ -494,10 +492,6 @@ "@keepScreenOnNever": {}, "keepScreenOnVideoPlayback": "ဗီဒီယိုကြည့်နေသည့်အချိန်အတွင်း", "@keepScreenOnVideoPlayback": {}, - "videoControlsPlay": "ဖွင့်တဲ့ခလုတ်", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "ဖွင့်တဲ့ခလုတ်နဲ့ အရှေ့/အနောက်သွားတဲ့ ခလုတ်", - "@videoControlsPlaySeek": {}, "videoPlaybackSkip": "ကျော်လိုက်ပါ", "@videoPlaybackSkip": {}, "viewerTransitionFade": "မှိန်သွားခြင်း", diff --git a/lib/l10n/app_nb.arb b/lib/l10n/app_nb.arb index 6b6ad73ce..0229a61aa 100644 --- a/lib/l10n/app_nb.arb +++ b/lib/l10n/app_nb.arb @@ -457,14 +457,8 @@ "@albumTierApps": {}, "videoLoopModeShortOnly": "Kun korte videoer", "@videoLoopModeShortOnly": {}, - "videoControlsPlay": "Spill", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Spill og blafre forover/bakover", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Åpne med annen avspiller", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Ingen", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "videoPlaybackSkip": "Hopp over", diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index f39770a5c..dc244329c 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Altijd", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Afspelen", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Speel & zoek terug/vooruit", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Met andere speler openen", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Geen", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (Hybride)", @@ -1001,7 +995,7 @@ "@settingsUnitSystemDialogTitle": {}, "settingsScreenSaverPageTitle": "Schermbeveiliging", "@settingsScreenSaverPageTitle": {}, - "settingsWidgetPageTitle": "Foto Lijstje", + "settingsWidgetPageTitle": "Fotolijst", "@settingsWidgetPageTitle": {}, "settingsWidgetShowOutline": "Contour", "@settingsWidgetShowOutline": {}, @@ -1394,5 +1388,9 @@ "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Tegels van [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", "@mapAttributionOpenTopoMap": {}, "mapAttributionOsmData": "Kaartgegevens © [OpenStreetMap](https://www.openstreetmap.org/copyright) bijdragers", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "videoActionShowNextFrame": "Volgend frame weergeven", + "@videoActionShowNextFrame": {}, + "videoActionShowPreviousFrame": "Vorig frame weergeven", + "@videoActionShowPreviousFrame": {} } diff --git a/lib/l10n/app_nn.arb b/lib/l10n/app_nn.arb index 61d158406..087ee16d3 100644 --- a/lib/l10n/app_nn.arb +++ b/lib/l10n/app_nn.arb @@ -236,12 +236,8 @@ "@videoLoopModeNever": {}, "videoLoopModeAlways": "På", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Spel av", - "@videoControlsPlay": {}, "videoControlsPlayOutside": "Opne med ein ytre avspelar", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Ingen", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (hybrid)", @@ -329,8 +325,6 @@ }, "unitSystemMetric": "Metrisk", "@unitSystemMetric": {}, - "videoControlsPlaySeek": "Spel av, spol framover/bakover", - "@videoControlsPlaySeek": {}, "mapStyleStamenWatercolor": "Stamen Watercolor (vassfarge)", "@mapStyleStamenWatercolor": {}, "viewerTransitionNone": "Ingen", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index a37a1d5ae..292b5637a 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -211,8 +211,6 @@ "@filterAspectRatioPortraitLabel": {}, "filterNoAddressLabel": "Brak adresu", "@filterNoAddressLabel": {}, - "videoControlsPlaySeek": "Odtwórz oraz przeszukuj", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Odtwórz innym odtwarzaczem", "@videoControlsPlayOutside": {}, "mapStyleGoogleNormal": "Mapy Google", @@ -325,8 +323,6 @@ "@unitSystemImperial": {}, "videoLoopModeNever": "Nigdy", "@videoLoopModeNever": {}, - "videoControlsNone": "Brak", - "@videoControlsNone": {}, "accessibilityAnimationsRemove": "Zapobiegaj efektom ekranu", "@accessibilityAnimationsRemove": {}, "accessibilityAnimationsKeep": "Zachowaj efekty ekranu", @@ -355,8 +351,6 @@ "@coordinateDmsWest": {}, "unitSystemMetric": "Metryczne", "@unitSystemMetric": {}, - "videoControlsPlay": "Odtwórz", - "@videoControlsPlay": {}, "albumTierPinned": "Przypięty", "@albumTierPinned": {}, "videoResumeButtonLabel": "WZNÓW", @@ -1550,5 +1544,9 @@ "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Kafelki od [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", "@mapAttributionOpenTopoMap": {}, "mapAttributionOsmData": "Dane mapy © [OpenStreetMap](https://www.openstreetmap.org/copyright) współtwórcy", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "videoActionShowPreviousFrame": "Pokaż poprzednią klatkę", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "Pokaż kolejną klatkę", + "@videoActionShowNextFrame": {} } diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index c582cccb5..2814a863d 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Sempre", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Começar", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Começar e procurar", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Abrir com outro player", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Nenhum", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (Híbrido)", @@ -993,9 +987,9 @@ "@settingsDisplayRefreshRateModeDialogTitle": {}, "settingsLanguageSectionTitle": "Idioma e Formatos", "@settingsLanguageSectionTitle": {}, - "settingsLanguageTile": "Língua", + "settingsLanguageTile": "Idioma", "@settingsLanguageTile": {}, - "settingsLanguagePageTitle": "Língua", + "settingsLanguagePageTitle": "Idioma", "@settingsLanguagePageTitle": {}, "settingsCoordinateFormatTile": "Formato de coordenadas", "@settingsCoordinateFormatTile": {}, @@ -1384,5 +1378,13 @@ "setHomeCustom": "Personalizada", "@setHomeCustom": {}, "mapAttributionOsmData": "Dados do mapa © [OpenStreetMap](https://www.openstreetmap.org/copyright) colaboradores", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "mapStyleOpenTopoMap": "OpenTopoMap", + "@mapStyleOpenTopoMap": {}, + "mapStyleOsmLiberty": "OSM Liberty", + "@mapStyleOsmLiberty": {}, + "mapAttributionOsmLiberty": "Blocos por [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Hospedado por [OSM Americana](https://tile.ourmap.us)", + "@mapAttributionOsmLiberty": {}, + "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Blocos por [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", + "@mapAttributionOpenTopoMap": {} } diff --git a/lib/l10n/app_ro.arb b/lib/l10n/app_ro.arb index 91f029a15..eebc0111b 100644 --- a/lib/l10n/app_ro.arb +++ b/lib/l10n/app_ro.arb @@ -237,14 +237,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Mereu", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Redă", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Redați și căutați înapoi/înainte", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Deschide cu alt player", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Nici unul", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Hărți Google", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Hărți Google (hibrid)", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 76766e645..ce3230d0f 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Всегда", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Воспроизведение", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Воспроизведение и переход на позицию", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Открыть в другом видеоплеере", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Ничего", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Карты", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Карты (Гибридный)", @@ -1388,5 +1382,13 @@ "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Плитки от [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", "@mapAttributionOpenTopoMap": {}, "mapAttributionOsmData": "Данные карты © [openstreetmap](https://www.openstreetmap.org/copyright) участники", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "videoActionShowPreviousFrame": "Показать предыдущий кадр", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "Показать следующий кадр", + "@videoActionShowNextFrame": {}, + "mapStyleOsmLiberty": "OSM Liberty", + "@mapStyleOsmLiberty": {}, + "mapAttributionOsmLiberty": "Tiles by [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Hosted by [OSM Americana](https://tile.ourmap.us)", + "@mapAttributionOsmLiberty": {} } diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index e512e0adb..1aa743de2 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -270,10 +270,6 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Vždy", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Spustiť", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Spustiť & pretočiť dozadu/dopredu", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Otvoriť v inom prehrávači", "@videoControlsPlayOutside": {}, "mapStyleGoogleNormal": "Google mapy", @@ -286,8 +282,6 @@ "@mapStyleStamenWatercolor": {}, "nameConflictStrategyRename": "Premenovať", "@nameConflictStrategyRename": {}, - "videoControlsNone": "Žiadne", - "@videoControlsNone": {}, "nameConflictStrategyReplace": "Nahradiť", "@nameConflictStrategyReplace": {}, "keepScreenOnNever": "Nikdy", @@ -1544,5 +1538,11 @@ "explorerPageTitle": "Prieskumník", "@explorerPageTitle": {}, "mapAttributionOsmData": "Údaje máp © [OpenStreetMap](https://www.openstreetmap.org/copyright) prispievatelia", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) Dlaždice podľa [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", + "@mapAttributionOpenTopoMap": {}, + "mapStyleOsmLiberty": "OSM Slobody", + "@mapStyleOsmLiberty": {}, + "mapAttributionOsmLiberty": "Dlaždice podľa [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Hostovaný [OSM Americana](https://tile.ourmap.us)", + "@mapAttributionOsmLiberty": {} } diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index 5ff46b075..05d0693a5 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -375,14 +375,8 @@ "@vaultLockTypePassword": {}, "settingsVideoEnablePip": "Bild-i-bild", "@settingsVideoEnablePip": {}, - "videoControlsPlay": "Spela", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Spela & spola bakåt/framåt", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Öppna med annan spelare", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Inga", - "@videoControlsNone": {}, "videoLoopModeNever": "Aldrig", "@videoLoopModeNever": {}, "videoLoopModeShortOnly": "Bara korta videor", @@ -1572,5 +1566,9 @@ "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Brickor av [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", "@mapAttributionOpenTopoMap": {}, "mapAttributionOsmData": "Kartdata © [OpenStreetMap](https://www.openstreetmap.org/copyright) bidragsgivare", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "videoActionShowPreviousFrame": "Visa föregående bildruta", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "Visa nästa bildruta", + "@videoActionShowNextFrame": {} } diff --git a/lib/l10n/app_th.arb b/lib/l10n/app_th.arb index 0de204c5d..74d639b8c 100644 --- a/lib/l10n/app_th.arb +++ b/lib/l10n/app_th.arb @@ -167,14 +167,8 @@ "@unitSystemImperial": {}, "videoLoopModeShortOnly": "เฉพาะคลิปสั้น", "@videoLoopModeShortOnly": {}, - "videoControlsPlay": "เล่น", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "เล่นและกรอหลัง/หน้า", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "เปิดด้วยตัวเล่นอื่น", "@videoControlsPlayOutside": {}, - "videoControlsNone": "ไม่มี", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Maps", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Maps (Hybrid)", diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb index 5857ccc49..22b9df042 100644 --- a/lib/l10n/app_tr.arb +++ b/lib/l10n/app_tr.arb @@ -211,14 +211,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "Her zaman", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Oynat", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "Oynat ve ileri/geri git", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "Başka bir oynatıcı ile aç", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Hiçbiri", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Haritalar", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Haritalar (Hibrit)", diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index ec55d158a..faa04d9d2 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -209,12 +209,8 @@ "@videoLoopModeNever": {}, "videoLoopModeAlways": "Завжди", "@videoLoopModeAlways": {}, - "videoControlsPlay": "Відтворити", - "@videoControlsPlay": {}, "videoControlsPlayOutside": "Відкрити в іншому відеоплеєрі", "@videoControlsPlayOutside": {}, - "videoControlsNone": "Нічого", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google Карти", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google Карти (Гібрид)", @@ -715,8 +711,6 @@ "@filterTypeAnimatedLabel": {}, "videoLoopModeShortOnly": "Тільки короткі відео", "@videoLoopModeShortOnly": {}, - "videoControlsPlaySeek": "Відтворити та перемотати назад/вперед", - "@videoControlsPlaySeek": {}, "mapStyleStamenWatercolor": "Stamen Watercolor", "@mapStyleStamenWatercolor": {}, "widgetOpenPageCollection": "Відкрити колекцію", @@ -1542,5 +1536,17 @@ "sortOrderLongestFirst": "Спершу найдовше", "@sortOrderLongestFirst": {}, "mapAttributionOsmData": "Картографічні дані © [OpenStreetMap](https://www.openstreetmap.org/copyright) помічники", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "mapStyleOsmLiberty": "OSM Liberty", + "@mapStyleOsmLiberty": {}, + "mapStyleOpenTopoMap": "OpenTopoMap", + "@mapStyleOpenTopoMap": {}, + "mapAttributionOsmLiberty": "Плитки від [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Розміщено на [OSM Americana](https://tile.ourmap.us)", + "@mapAttributionOsmLiberty": {}, + "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Плитки від [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", + "@mapAttributionOpenTopoMap": {}, + "videoActionShowPreviousFrame": "Показати попередній кадр", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "Показати наступний кадр", + "@videoActionShowNextFrame": {} } diff --git a/lib/l10n/app_vi.arb b/lib/l10n/app_vi.arb index cb9f13382..ec82c7aef 100644 --- a/lib/l10n/app_vi.arb +++ b/lib/l10n/app_vi.arb @@ -633,8 +633,6 @@ "@patternDialogEnter": {}, "settingsEnableBin": "Dùng thùng rác", "@settingsEnableBin": {}, - "videoControlsNone": "Không có", - "@videoControlsNone": {}, "otherDirectoryDescription": "“{name}” thư mục", "@otherDirectoryDescription": { "placeholders": { @@ -833,8 +831,6 @@ "@drawerCollectionFavourites": {}, "filterTypeRawLabel": "RAW", "@filterTypeRawLabel": {}, - "videoControlsPlaySeek": "Phát & kéo lùi lại/tiến tới", - "@videoControlsPlaySeek": {}, "settingsSubtitleThemeTextAlignmentCenter": "Trung tâm", "@settingsSubtitleThemeTextAlignmentCenter": {}, "keepScreenOnVideoPlayback": "Khi phát lại video", @@ -1077,8 +1073,6 @@ "@viewerErrorDoesNotExist": {}, "albumCamera": "Máy Ảnh", "@albumCamera": {}, - "videoControlsPlay": "Phát", - "@videoControlsPlay": {}, "settingsNavigationSectionTitle": "Điều hướng", "@settingsNavigationSectionTitle": {}, "settingsDisplayRefreshRateModeDialogTitle": "Tốc độ làm mới", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index bedeeccb5..59046c3c6 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -229,14 +229,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "始终", "@videoLoopModeAlways": {}, - "videoControlsPlay": "播放", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "播放和步进/步退", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "用其他播放器打开", "@videoControlsPlayOutside": {}, - "videoControlsNone": "无", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google 地图", "@mapStyleGoogleNormal": {}, "mapStyleGoogleHybrid": "Google 地图 (卫星图像)", diff --git a/lib/l10n/app_zh_Hant.arb b/lib/l10n/app_zh_Hant.arb index e63949190..a700f4080 100644 --- a/lib/l10n/app_zh_Hant.arb +++ b/lib/l10n/app_zh_Hant.arb @@ -205,14 +205,8 @@ "@videoLoopModeShortOnly": {}, "videoLoopModeAlways": "總是", "@videoLoopModeAlways": {}, - "videoControlsPlay": "播放", - "@videoControlsPlay": {}, - "videoControlsPlaySeek": "播放和後退/前進", - "@videoControlsPlaySeek": {}, "videoControlsPlayOutside": "用其他播放器打開", "@videoControlsPlayOutside": {}, - "videoControlsNone": "無", - "@videoControlsNone": {}, "mapStyleGoogleNormal": "Google 地圖", "@mapStyleGoogleNormal": {}, "mapStyleGoogleTerrain": "Google 地圖 (地形)", diff --git a/lib/model/app/contributors.dart b/lib/model/app/contributors.dart index de0461704..191d22cbf 100644 --- a/lib/model/app/contributors.dart +++ b/lib/model/app/contributors.dart @@ -69,7 +69,7 @@ class Contributors { Contributor('Henning Bunk', 'henningtbunk@gmail.com'), Contributor('Samirah Ail', 'samiratalzahrani@gmail.com'), Contributor('Salih Ail', 'rrrfff444@gmail.com'), - Contributor('nasreddineloukriz', 'nasreddineloukriz@gmail.com'), + Contributor('Nasreddine Loukriz', 'nasreddineloukriz@gmail.com'), Contributor('Mohamed Zeroug', 'mzeroug19@gmail.com'), Contributor('ssantos', 'ssantos@web.de'), Contributor('Сергій', 'sergiy.goncharuk.1@gmail.com'), @@ -102,8 +102,14 @@ class Contributors { Contributor('Taufan', 'taufanxxx@gmail.com'), Contributor('Leo Aaua Felix', 'g00g7el@gmail.com'), Contributor('-J-', 'heyj0e@tuta.io'), - Contributor('bittin1ddc447d824349b2', 'bittin@reimu.nl'), + Contributor('bittin', 'bittin@reimu.nl'), Contributor('splice11', 'trenchedgrandpa@protonmail.com'), + Contributor('Ihor Hordiichuk', 'igor_ck@outlook.com'), + Contributor('João Palmeiro', 'joaommpalmeiro@gmail.com'), + Contributor('Whoever4976', 'wolffjonas47@gmail.com'), + Contributor('Your Average Code', 'neumeiersi91358@th-nuernberg.de'), + Contributor('Paranoid Android', 'f.cherdzhiev@innopolis.university'), + Contributor('Noah Kenzie Rodriguez-Beus', 'noahbeus@protonmail.com'), // Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali // Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese // Contributor('Khant', 'khant@users.noreply.hosted.weblate.org'), // Burmese diff --git a/lib/model/app_inventory.dart b/lib/model/app_inventory.dart index 22705a135..273216cd8 100644 --- a/lib/model/app_inventory.dart +++ b/lib/model/app_inventory.dart @@ -71,7 +71,7 @@ class Package { currentLabel, englishLabel, ...ownedDirs, - ].whereNotNull().map(normalizePotentialDir).toSet(); + ].nonNulls.map(normalizePotentialDir).toSet(); static String normalizePotentialDir(String dir) { return dir.replaceAll('_', ' ').trim().toLowerCase(); diff --git a/lib/model/covers.dart b/lib/model/covers.dart index 288a19f0a..231d8effc 100644 --- a/lib/model/covers.dart +++ b/lib/model/covers.dart @@ -146,7 +146,7 @@ class Covers { if (colorValue != null) 'color': colorValue, }; }) - .whereNotNull() + .nonNulls .toList(); return jsonList.isNotEmpty ? jsonList : null; } diff --git a/lib/model/db/db.dart b/lib/model/db/db.dart index 2fa2b0b17..0798e9e6f 100644 --- a/lib/model/db/db.dart +++ b/lib/model/db/db.dart @@ -115,7 +115,7 @@ abstract class LocalMediaDb { Future> loadAllVideoPlayback(); - Future loadVideoPlayback(int? id); + Future loadVideoPlayback(int id); Future addVideoPlayback(Set rows); diff --git a/lib/model/db/db_sqflite.dart b/lib/model/db/db_sqflite.dart index 77bc690be..5bfb18d3a 100644 --- a/lib/model/db/db_sqflite.dart +++ b/lib/model/db/db_sqflite.dart @@ -31,6 +31,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb { static const trashTable = 'trash'; static const videoPlaybackTable = 'videoPlayback'; + static const _queryCursorBufferSize = 1000; static int _lastId = 0; @override @@ -180,6 +181,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb { whereArgs.add(origin); } + final entries = {}; if (directory != null) { final separator = pContext.separator; if (!directory.endsWith(separator)) { @@ -188,21 +190,25 @@ class SqfliteLocalMediaDb implements LocalMediaDb { where = '${where != null ? '$where AND ' : ''}path LIKE ?'; whereArgs.add('$directory%'); - final rows = await _db.query(entryTable, where: where, whereArgs: whereArgs); + final cursor = await _db.queryCursor(entryTable, where: where, whereArgs: whereArgs, bufferSize: _queryCursorBufferSize); final dirLength = directory.length; - return rows - .whereNot((row) { - // skip entries in subfolders - final path = row['path'] as String?; - return path == null || path.substring(dirLength).contains(separator); - }) - .map(AvesEntry.fromMap) - .toSet(); + while (await cursor.moveNext()) { + final row = cursor.current; + // skip entries in subfolders + final path = row['path'] as String?; + if (path != null && !path.substring(dirLength).contains(separator)) { + entries.add(AvesEntry.fromMap(row)); + } + } + } else { + final cursor = await _db.queryCursor(entryTable, where: where, whereArgs: whereArgs, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + entries.add(AvesEntry.fromMap(cursor.current)); + } } - final rows = await _db.query(entryTable, where: where, whereArgs: whereArgs); - return rows.map(AvesEntry.fromMap).toSet(); + return entries; } @override @@ -278,8 +284,13 @@ class SqfliteLocalMediaDb implements LocalMediaDb { @override Future> loadDates() async { - final rows = await _db.query(dateTakenTable); - return Map.fromEntries(rows.map((map) => MapEntry(map['id'] as int, (map['dateMillis'] ?? 0) as int))); + final result = {}; + final cursor = await _db.queryCursor(dateTakenTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + final row = cursor.current; + result[row['id'] as int] = row['dateMillis'] as int? ?? 0; + } + return result; } // catalog metadata @@ -292,8 +303,12 @@ class SqfliteLocalMediaDb implements LocalMediaDb { @override Future> loadCatalogMetadata() async { - final rows = await _db.query(metadataTable); - return rows.map(CatalogMetadata.fromMap).toSet(); + final result = {}; + final cursor = await _db.queryCursor(metadataTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + result.add(CatalogMetadata.fromMap(cursor.current)); + } + return result; } @override @@ -351,8 +366,12 @@ class SqfliteLocalMediaDb implements LocalMediaDb { @override Future> loadAddresses() async { - final rows = await _db.query(addressTable); - return rows.map(AddressDetails.fromMap).toSet(); + final result = {}; + final cursor = await _db.queryCursor(addressTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + result.add(AddressDetails.fromMap(cursor.current)); + } + return result; } @override @@ -395,8 +414,12 @@ class SqfliteLocalMediaDb implements LocalMediaDb { @override Future> loadAllVaults() async { - final rows = await _db.query(vaultTable); - return rows.map(VaultDetails.fromMap).toSet(); + final result = {}; + final cursor = await _db.queryCursor(vaultTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + result.add(VaultDetails.fromMap(cursor.current)); + } + return result; } @override @@ -443,8 +466,12 @@ class SqfliteLocalMediaDb implements LocalMediaDb { @override Future> loadAllTrashDetails() async { - final rows = await _db.query(trashTable); - return rows.map(TrashDetails.fromMap).toSet(); + final result = {}; + final cursor = await _db.queryCursor(trashTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + result.add(TrashDetails.fromMap(cursor.current)); + } + return result; } @override @@ -474,8 +501,12 @@ class SqfliteLocalMediaDb implements LocalMediaDb { @override Future> loadAllFavourites() async { - final rows = await _db.query(favouriteTable); - return rows.map(FavouriteRow.fromMap).toSet(); + final result = {}; + final cursor = await _db.queryCursor(favouriteTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + result.add(FavouriteRow.fromMap(cursor.current)); + } + return result; } @override @@ -524,8 +555,15 @@ class SqfliteLocalMediaDb implements LocalMediaDb { @override Future> loadAllCovers() async { - final rows = await _db.query(coverTable); - return rows.map(CoverRow.fromMap).whereNotNull().toSet(); + final result = {}; + final cursor = await _db.queryCursor(coverTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + final row = CoverRow.fromMap(cursor.current); + if (row != null) { + result.add(row); + } + } + return result; } @override @@ -587,14 +625,19 @@ class SqfliteLocalMediaDb implements LocalMediaDb { @override Future> loadAllVideoPlayback() async { - final rows = await _db.query(videoPlaybackTable); - return rows.map(VideoPlaybackRow.fromMap).whereNotNull().toSet(); + final result = {}; + final cursor = await _db.queryCursor(videoPlaybackTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + final row = VideoPlaybackRow.fromMap(cursor.current); + if (row != null) { + result.add(row); + } + } + return result; } @override - Future loadVideoPlayback(int? id) async { - if (id == null) return null; - + Future loadVideoPlayback(int id) async { final rows = await _db.query(videoPlaybackTable, where: 'id = ?', whereArgs: [id]); if (rows.isEmpty) return null; @@ -631,11 +674,13 @@ class SqfliteLocalMediaDb implements LocalMediaDb { // convenience methods Future> _getByIds(Set ids, String table, T Function(Map row) mapRow) async { - if (ids.isEmpty) return {}; - final rows = await _db.query( - table, - where: 'id IN (${ids.join(',')})', - ); - return rows.map(mapRow).toSet(); + final result = {}; + if (ids.isNotEmpty) { + final cursor = await _db.queryCursor(table, where: 'id IN (${ids.join(',')})', bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + result.add(mapRow(cursor.current)); + } + } + return result; } } diff --git a/lib/model/db/db_sqflite_upgrade.dart b/lib/model/db/db_sqflite_upgrade.dart index 69c4055b8..d9b902698 100644 --- a/lib/model/db/db_sqflite_upgrade.dart +++ b/lib/model/db/db_sqflite_upgrade.dart @@ -1,5 +1,4 @@ import 'package:aves/model/db/db_sqflite.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:sqflite/sqflite.dart'; @@ -338,11 +337,11 @@ class LocalMediaDbUpgrader { // clean duplicates introduced before Aves v1.7.1 final duplicatedContentIdRows = await db.query(entryTable, columns: ['contentId'], groupBy: 'contentId', having: 'COUNT(id) > 1 AND contentId IS NOT NULL'); - final duplicatedContentIds = duplicatedContentIdRows.map((row) => row['contentId'] as int?).whereNotNull().toSet(); + final duplicatedContentIds = duplicatedContentIdRows.map((row) => row['contentId'] as int?).nonNulls.toSet(); final duplicateIds = {}; await Future.forEach(duplicatedContentIds, (contentId) async { final rows = await db.query(entryTable, columns: ['id'], where: 'contentId = ?', whereArgs: [contentId]); - final ids = rows.map((row) => row['id'] as int?).whereNotNull().toList()..sort(); + final ids = rows.map((row) => row['id'] as int?).nonNulls.toList()..sort(); if (ids.length > 1) { ids.removeAt(0); duplicateIds.addAll(ids); diff --git a/lib/model/entry/entry.dart b/lib/model/entry/entry.dart index 8d1e3c3d2..0a3f6ab98 100644 --- a/lib/model/entry/entry.dart +++ b/lib/model/entry/entry.dart @@ -12,8 +12,8 @@ import 'package:aves/theme/format.dart'; import 'package:aves/utils/time_utils.dart'; import 'package:aves_model/aves_model.dart'; import 'package:aves_utils/aves_utils.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; enum EntryDataType { basic, aspectRatio, catalog, address, references } @@ -73,7 +73,7 @@ class AvesEntry with AvesEntryBase { this.stackedEntries, }) : id = id ?? 0 { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$AvesEntry', object: this, @@ -189,7 +189,7 @@ class AvesEntry with AvesEntryBase { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } visualChangeNotifier.dispose(); metadataChangeNotifier.dispose(); @@ -392,7 +392,7 @@ class AvesEntry with AvesEntryBase { _addressDetails?.countryName, _addressDetails?.adminArea, _addressDetails?.locality, - }.whereNotNull().where((v) => v.isNotEmpty).join(', '); + }.nonNulls.where((v) => v.isNotEmpty).join(', '); } Future applyNewFields(Map newFields, {required bool persist}) async { @@ -461,17 +461,17 @@ class AvesEntry with AvesEntryBase { } Future delete() { - final completer = Completer(); + final opCompleter = Completer(); mediaEditService.delete(entries: {this}).listen( - (event) => completer.complete(event.success && !event.skipped), - onError: completer.completeError, + (event) => opCompleter.complete(event.success && !event.skipped), + onError: opCompleter.completeError, onDone: () { - if (!completer.isCompleted) { - completer.complete(false); + if (!opCompleter.isCompleted) { + opCompleter.complete(false); } }, ); - return completer.future; + return opCompleter.future; } // when the MIME type or the image itself changed (e.g. after rotation) diff --git a/lib/model/entry/extensions/info.dart b/lib/model/entry/extensions/info.dart index 0b2f9deda..5c001d865 100644 --- a/lib/model/entry/extensions/info.dart +++ b/lib/model/entry/extensions/info.dart @@ -141,7 +141,7 @@ extension ExtraAvesEntryInfo on AvesEntry { final rawTags = formatCount.map((key, value) { final count = value.length; // remove duplicate names, so number of displayed names may not match displayed count - final names = value.whereNotNull().toSet().toList()..sort(compareAsciiUpperCase); + final names = value.nonNulls.toSet().toList()..sort(compareAsciiUpperCase); return MapEntry(key, '$count items: ${names.join(', ')}'); }); directories.add(MetadataDirectory('Attachments', _toSortedTags(rawTags))); @@ -157,7 +157,7 @@ extension ExtraAvesEntryInfo on AvesEntry { if (value.isEmpty) return null; final tagName = tagKV.key as String; return MapEntry(tagName, value); - }).whereNotNull())); + }).nonNulls)); return tags; } } diff --git a/lib/model/favourites.dart b/lib/model/favourites.dart index 19c85c78c..b54bff4ef 100644 --- a/lib/model/favourites.dart +++ b/lib/model/favourites.dart @@ -12,7 +12,9 @@ final Favourites favourites = Favourites._private(); class Favourites with ChangeNotifier { Set _rows = {}; - Favourites._private(); + Favourites._private() { + if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this); + } Future init() async { _rows = await localMediaDb.loadAllFavourites(); @@ -58,7 +60,7 @@ class Favourites with ChangeNotifier { Map>? export(CollectionSource source) { final visibleEntries = source.visibleEntries; final ids = favourites.all; - final paths = visibleEntries.where((entry) => ids.contains(entry.id)).map((entry) => entry.path).whereNotNull().toSet(); + final paths = visibleEntries.where((entry) => ids.contains(entry.id)).map((entry) => entry.path).nonNulls.toSet(); final byVolume = groupBy(paths, androidFileUtils.getStorageVolume); final jsonMap = Map.fromEntries(byVolume.entries.map((kv) { final volume = kv.key?.path; @@ -66,7 +68,7 @@ class Favourites with ChangeNotifier { final rootLength = volume.length; final relativePaths = kv.value.map((v) => v.substring(rootLength)).toList(); return MapEntry(volume, relativePaths); - }).whereNotNull()); + }).nonNulls); return jsonMap.isNotEmpty ? jsonMap : null; } diff --git a/lib/model/filters/or.dart b/lib/model/filters/or.dart index 2b6c8d83f..ea4876e3d 100644 --- a/lib/model/filters/or.dart +++ b/lib/model/filters/or.dart @@ -35,7 +35,7 @@ class OrFilter extends CollectionFilter { factory OrFilter.fromMap(Map json) { return OrFilter( - (json['filters'] as List).cast().map(CollectionFilter.fromJson).whereNotNull().toSet(), + (json['filters'] as List).cast().map(CollectionFilter.fromJson).nonNulls.toSet(), reversed: json['reversed'] ?? false, ); } diff --git a/lib/model/highlight.dart b/lib/model/highlight.dart index 7a9c94de0..2d372eb4a 100644 --- a/lib/model/highlight.dart +++ b/lib/model/highlight.dart @@ -5,6 +5,10 @@ import 'package:flutter/painting.dart'; class HighlightInfo extends ChangeNotifier { final EventBus eventBus = EventBus(); + HighlightInfo() { + if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this); + } + void trackItem( T? item, { TrackPredicate? predicate, diff --git a/lib/model/multipage.dart b/lib/model/multipage.dart index b8dcbb0aa..ceec4592b 100644 --- a/lib/model/multipage.dart +++ b/lib/model/multipage.dart @@ -4,6 +4,7 @@ import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/services.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class MultiPageInfo { final AvesEntry mainEntry; @@ -18,7 +19,7 @@ class MultiPageInfo { required List pages, }) : _pages = pages { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$MultiPageInfo', object: this, @@ -44,7 +45,7 @@ class MultiPageInfo { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _transientEntries.forEach((entry) => entry.dispose()); } diff --git a/lib/model/naming_pattern.dart b/lib/model/naming_pattern.dart index 2305f94c2..24882eeb9 100644 --- a/lib/model/naming_pattern.dart +++ b/lib/model/naming_pattern.dart @@ -210,7 +210,7 @@ class MetadataFieldNamingProcessor extends NamingProcessor { } @override - Set getRequiredFields() => {field}.whereNotNull().toSet(); + Set getRequiredFields() => {field}.nonNulls.toSet(); @override String? process(AvesEntry entry, int index, Map fieldValues) { @@ -268,7 +268,7 @@ class HashNamingProcessor extends NamingProcessor { } @override - Set getRequiredFields() => {function}.whereNotNull().toSet(); + Set getRequiredFields() => {function}.nonNulls.toSet(); @override String? process(AvesEntry entry, int index, Map fieldValues) { diff --git a/lib/model/query.dart b/lib/model/query.dart index 518d6941f..fa468f129 100644 --- a/lib/model/query.dart +++ b/lib/model/query.dart @@ -9,6 +9,7 @@ class Query extends ChangeNotifier { final StreamController _enabledStreamController = StreamController.broadcast(); Query({required bool enabled, required String? initialValue}) { + if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this); _enabled = enabled; if (initialValue != null && initialValue.isNotEmpty) { _enabled = true; diff --git a/lib/model/selection.dart b/lib/model/selection.dart index b49ffdc0d..fbb6014b7 100644 --- a/lib/model/selection.dart +++ b/lib/model/selection.dart @@ -9,6 +9,10 @@ class Selection extends ChangeNotifier { Set get selectedItems => _selectedItems; + Selection() { + if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this); + } + void browse() { if (!_isSelecting) return; _isSelecting = false; diff --git a/lib/model/settings/modules/app.dart b/lib/model/settings/modules/app.dart index 60f968d21..2b9caa141 100644 --- a/lib/model/settings/modules/app.dart +++ b/lib/model/settings/modules/app.dart @@ -1,13 +1,17 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/defaults.dart'; +import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/widgets/aves_app.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; mixin AppSettings on SettingsAccess { static const int recentFilterHistoryMax = 20; + void initAppSettings() { + vaults.addListener(_onVaultsChanged); + } + bool get hasAcceptedTerms => getBool(SettingKeys.hasAcceptedTermsKey) ?? SettingsDefaults.hasAcceptedTerms; set hasAcceptedTerms(bool newValue) => set(SettingKeys.hasAcceptedTermsKey, newValue); @@ -99,15 +103,32 @@ mixin AppSettings on SettingsAccess { set entryRenamingPattern(String newValue) => set(SettingKeys.entryRenamingPatternKey, newValue); - List? get topEntryIds => getStringList(SettingKeys.topEntryIdsKey)?.map(int.tryParse).whereNotNull().toList(); + List? get topEntryIds => getStringList(SettingKeys.topEntryIdsKey)?.map(int.tryParse).nonNulls.toList(); - set topEntryIds(List? newValue) => set(SettingKeys.topEntryIdsKey, newValue?.map((id) => id.toString()).whereNotNull().toList()); + set topEntryIds(List? newValue) => set(SettingKeys.topEntryIdsKey, newValue?.map((id) => id.toString()).nonNulls.toList()); List get recentDestinationAlbums => getStringList(SettingKeys.recentDestinationAlbumsKey) ?? []; set recentDestinationAlbums(List newValue) => set(SettingKeys.recentDestinationAlbumsKey, newValue.take(recentFilterHistoryMax).toList()); - List get recentTags => (getStringList(SettingKeys.recentTagsKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toList(); + // recent tags + + List get _recentTags => (getStringList(SettingKeys.recentTagsKey) ?? []).map(CollectionFilter.fromJson).nonNulls.toList(); + + set _recentTags(List newValue) => set(SettingKeys.recentTagsKey, newValue.take(recentFilterHistoryMax).map((filter) => filter.toJson()).toList()); + + // when vaults are unlocked, recent tags are transient and not persisted + List? _protectedRecentTags; + + List get recentTags => vaults.needProtection ? _protectedRecentTags ?? List.of(_recentTags) : _recentTags; + + set recentTags(List newValue) { + if (vaults.needProtection) { + _protectedRecentTags = newValue; + } else { + _recentTags = newValue; + } + } - set recentTags(List newValue) => set(SettingKeys.recentTagsKey, newValue.take(recentFilterHistoryMax).map((filter) => filter.toJson()).toList()); + void _onVaultsChanged() => _protectedRecentTags = null; } diff --git a/lib/model/settings/modules/filter_grids.dart b/lib/model/settings/modules/filter_grids.dart index 4fa6482c5..d2a7064f7 100644 --- a/lib/model/settings/modules/filter_grids.dart +++ b/lib/model/settings/modules/filter_grids.dart @@ -1,7 +1,6 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/defaults.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; mixin FilterGridsSettings on SettingsAccess { AlbumChipGroupFactor get albumGroupFactor => getEnumOrDefault(SettingKeys.albumGroupFactorKey, SettingsDefaults.albumGroupFactor, AlbumChipGroupFactor.values); @@ -48,7 +47,7 @@ mixin FilterGridsSettings on SettingsAccess { set tagSortReverse(bool newValue) => set(SettingKeys.tagSortReverseKey, newValue); - Set get pinnedFilters => (getStringList(SettingKeys.pinnedFiltersKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet(); + Set get pinnedFilters => (getStringList(SettingKeys.pinnedFiltersKey) ?? []).map(CollectionFilter.fromJson).nonNulls.toSet(); set pinnedFilters(Set newValue) => set(SettingKeys.pinnedFiltersKey, newValue.map((filter) => filter.toJson()).toList()); diff --git a/lib/model/settings/modules/navigation.dart b/lib/model/settings/modules/navigation.dart index a2c6969a5..2f6eca490 100644 --- a/lib/model/settings/modules/navigation.dart +++ b/lib/model/settings/modules/navigation.dart @@ -1,7 +1,6 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/defaults.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; mixin NavigationSettings on SettingsAccess { bool get mustBackTwiceToExit => getBool(SettingKeys.mustBackTwiceToExitKey) ?? SettingsDefaults.mustBackTwiceToExit; @@ -14,7 +13,7 @@ mixin NavigationSettings on SettingsAccess { HomePageSetting get homePage => getEnumOrDefault(SettingKeys.homePageKey, SettingsDefaults.homePage, HomePageSetting.values); - Set get homeCustomCollection => (getStringList(SettingKeys.homeCustomCollectionKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet(); + Set get homeCustomCollection => (getStringList(SettingKeys.homeCustomCollectionKey) ?? []).map(CollectionFilter.fromJson).nonNulls.toSet(); String? get homeCustomExplorerPath => getString(SettingKeys.homeCustomExplorerPathKey); diff --git a/lib/model/settings/modules/privacy.dart b/lib/model/settings/modules/privacy.dart index c9c2ff926..a6408b826 100644 --- a/lib/model/settings/modules/privacy.dart +++ b/lib/model/settings/modules/privacy.dart @@ -1,10 +1,9 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/modules/search.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; mixin PrivacySettings on SettingsAccess, SearchSettings { - Set get hiddenFilters => (getStringList(SettingKeys.hiddenFiltersKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet(); + Set get hiddenFilters => (getStringList(SettingKeys.hiddenFiltersKey) ?? []).map(CollectionFilter.fromJson).nonNulls.toSet(); set hiddenFilters(Set newValue) => set(SettingKeys.hiddenFiltersKey, newValue.map((filter) => filter.toJson()).toList()); @@ -24,7 +23,7 @@ mixin PrivacySettings on SettingsAccess, SearchSettings { hiddenFilters = _hiddenFilters; } - Set get deactivatedHiddenFilters => (getStringList(SettingKeys.deactivatedHiddenFiltersKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet(); + Set get deactivatedHiddenFilters => (getStringList(SettingKeys.deactivatedHiddenFiltersKey) ?? []).map(CollectionFilter.fromJson).nonNulls.toSet(); set deactivatedHiddenFilters(Set newValue) => set(SettingKeys.deactivatedHiddenFiltersKey, newValue.map((filter) => filter.toJson()).toList()); diff --git a/lib/model/settings/modules/search.dart b/lib/model/settings/modules/search.dart index a6dba9bee..3a5ff94cb 100644 --- a/lib/model/settings/modules/search.dart +++ b/lib/model/settings/modules/search.dart @@ -1,14 +1,13 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/defaults.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; mixin SearchSettings on SettingsAccess { bool get saveSearchHistory => getBool(SettingKeys.saveSearchHistoryKey) ?? SettingsDefaults.saveSearchHistory; set saveSearchHistory(bool newValue) => set(SettingKeys.saveSearchHistoryKey, newValue); - List get searchHistory => (getStringList(SettingKeys.searchHistoryKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toList(); + List get searchHistory => (getStringList(SettingKeys.searchHistoryKey) ?? []).map(CollectionFilter.fromJson).nonNulls.toList(); set searchHistory(List newValue) => set(SettingKeys.searchHistoryKey, newValue.map((filter) => filter.toJson()).toList()); } diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index e9e1ff4fa..40804ec4f 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -33,8 +33,8 @@ import 'package:aves_utils/aves_utils.dart'; import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:latlong2/latlong.dart'; final Settings settings = Settings._private(); @@ -56,7 +56,9 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings @override SettingsStore get store => settingsStore; - Settings._private(); + Settings._private() { + if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this); + } Future init({required bool monitorPlatformSettings}) async { await store.init(); @@ -67,6 +69,7 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings ..clear(); _subscriptions.add(_platformSettingsChangeChannel.receiveBroadcastStream().listen((event) => _onPlatformSettingsChanged(event as Map?))); } + initAppSettings(); } Future reload() => store.reload(); @@ -130,7 +133,7 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings viewerGestureSideTapNext = false; viewerUseCutout = true; videoBackgroundMode = VideoBackgroundMode.disabled; - videoControls = VideoControls.none; + videoControlActions = []; videoGestureDoubleTapTogglePlay = false; videoGestureSideDoubleTapSeek = false; enableBin = false; @@ -243,7 +246,7 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings set screenSaverInterval(int newValue) => set(SettingKeys.screenSaverIntervalKey, newValue); - Set get screenSaverCollectionFilters => (getStringList(SettingKeys.screenSaverCollectionFiltersKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet(); + Set get screenSaverCollectionFilters => (getStringList(SettingKeys.screenSaverCollectionFiltersKey) ?? []).map(CollectionFilter.fromJson).nonNulls.toSet(); set screenSaverCollectionFilters(Set newValue) => set(SettingKeys.screenSaverCollectionFiltersKey, newValue.map((filter) => filter.toJson()).toList()); @@ -287,7 +290,7 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings void setWidgetShape(int widgetId, WidgetShape newValue) => set('${SettingKeys.widgetShapePrefixKey}$widgetId', newValue.toString()); - Set getWidgetCollectionFilters(int widgetId) => (getStringList('${SettingKeys.widgetCollectionFiltersPrefixKey}$widgetId') ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet(); + Set getWidgetCollectionFilters(int widgetId) => (getStringList('${SettingKeys.widgetCollectionFiltersPrefixKey}$widgetId') ?? []).map(CollectionFilter.fromJson).nonNulls.toSet(); void setWidgetCollectionFilters(int widgetId, Set newValue) => set('${SettingKeys.widgetCollectionFiltersPrefixKey}$widgetId', newValue.map((filter) => filter.toJson()).toList()); @@ -456,7 +459,6 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings case SettingKeys.videoBackgroundModeKey: case SettingKeys.videoLoopModeKey: case SettingKeys.videoResumptionModeKey: - case SettingKeys.videoControlsKey: case SettingKeys.subtitleTextAlignmentKey: case SettingKeys.subtitleTextPositionKey: case SettingKeys.tagEditorExpandedSectionKey: @@ -487,6 +489,7 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings case SettingKeys.collectionBrowsingQuickActionsKey: case SettingKeys.collectionSelectionQuickActionsKey: case SettingKeys.viewerQuickActionsKey: + case SettingKeys.videoControlActionsKey: case SettingKeys.screenSaverCollectionFiltersKey: if (newValue is List) { store.setStringList(key, newValue.cast()); diff --git a/lib/model/source/album.dart b/lib/model/source/album.dart index cf9bbc565..859d4ad5c 100644 --- a/lib/model/source/album.dart +++ b/lib/model/source/album.dart @@ -71,7 +71,7 @@ mixin AlbumMixin on SourceBase { void addDirectories({required Set albums, bool notify = true}) { if (!_directories.containsAll(albums)) { - _directories.addAll(albums.whereNotNull()); + _directories.addAll(albums.nonNulls); _onAlbumChanged(notify: notify); } } @@ -119,7 +119,7 @@ mixin AlbumMixin on SourceBase { } else { directories ??= {}; if (entries != null) { - directories.addAll(entries.map((entry) => entry.directory).whereNotNull()); + directories.addAll(entries.map((entry) => entry.directory).nonNulls); } directories.forEach((directory) { _filterEntryCountMap.remove(directory); @@ -202,7 +202,7 @@ mixin AlbumMixin on SourceBase { return dirPath; } - final otherAlbumsOnDevice = _directories.whereNotNull().where((item) => item != dirPath).map((v) => v.toLowerCase()).toSet(); + final otherAlbumsOnDevice = _directories.nonNulls.where((item) => item != dirPath).map((v) => v.toLowerCase()).toSet(); final uniqueNameInDevice = unique(dirPath, otherAlbumsOnDevice); if (uniqueNameInDevice.length <= relativeDir.length) { return uniqueNameInDevice; diff --git a/lib/model/source/analysis_controller.dart b/lib/model/source/analysis_controller.dart index cf7288180..1efde196f 100644 --- a/lib/model/source/analysis_controller.dart +++ b/lib/model/source/analysis_controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class AnalysisController { final bool canStartService, force; @@ -15,7 +16,7 @@ class AnalysisController { this.progressOffset = 0, }) { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$AnalysisController', object: this, @@ -25,7 +26,7 @@ class AnalysisController { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _stopSignal.dispose(); } diff --git a/lib/model/source/collection_lens.dart b/lib/model/source/collection_lens.dart index d0f8d88f0..f7976bdca 100644 --- a/lib/model/source/collection_lens.dart +++ b/lib/model/source/collection_lens.dart @@ -54,11 +54,12 @@ class CollectionLens with ChangeNotifier { this.stackDevelopedRaws = true, this.fixedSort = false, this.fixedSelection, - }) : filters = (filters ?? {}).whereNotNull().toSet(), + }) : filters = (filters ?? {}).nonNulls.toSet(), burstPatterns = settings.collectionBurstPatterns, sectionFactor = settings.collectionSectionFactor, sortFactor = settings.collectionSortFactor, sortReverse = settings.collectionSortReverse { + if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this); id ??= hashCode; if (listenToSource) { final sourceEvents = source.eventBus; diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart index 54650baa3..6ea63b1b2 100644 --- a/lib/model/source/collection_source.dart +++ b/lib/model/source/collection_source.dart @@ -30,8 +30,9 @@ import 'package:aves_model/aves_model.dart'; import 'package:collection/collection.dart'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; -enum SourceScope { none, album, full } +typedef SourceScope = Set?; mixin SourceBase { EventBus get eventBus; @@ -62,9 +63,11 @@ mixin SourceBase { } abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, PlaceMixin, StateMixin, LocationMixin, TagMixin, TrashMixin { + static const fullScope = {}; + CollectionSource() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$CollectionSource', object: this, @@ -74,22 +77,20 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place settings.updateStream.where((event) => event.key == SettingKeys.hiddenFiltersKey).listen((event) { final oldValue = event.oldValue; if (oldValue is List?) { - final oldHiddenFilters = (oldValue ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet(); + final oldHiddenFilters = (oldValue ?? []).map(CollectionFilter.fromJson).nonNulls.toSet(); final newlyVisibleFilters = oldHiddenFilters.whereNot(settings.hiddenFilters.contains).toSet(); _onFilterVisibilityChanged(newlyVisibleFilters); } }); - vaults.addListener(() { - final newlyVisibleFilters = vaults.vaultDirectories.whereNot(vaults.isLocked).map((v) => AlbumFilter(v, null)).toSet(); - _onFilterVisibilityChanged(newlyVisibleFilters); - }); + vaults.addListener(_onVaultsChanged); } @mustCallSuper void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } + vaults.removeListener(_onVaultsChanged); _rawEntries.forEach((v) => v.dispose()); } @@ -337,7 +338,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place final existingEntry = _rawEntries.firstWhereOrNull((entry) => entry.path == targetPath && !entry.trashed); return existingEntry?.uri; }) - .whereNotNull() + .nonNulls .toSet(); await removeEntries(replacedUris, includeTrash: false); @@ -367,8 +368,8 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place } }); await localMediaDb.insertEntries(movedEntries); - await localMediaDb.saveCatalogMetadata(movedEntries.map((entry) => entry.catalogMetadata).whereNotNull().toSet()); - await localMediaDb.saveAddresses(movedEntries.map((entry) => entry.addressDetails).whereNotNull().toSet()); + await localMediaDb.saveCatalogMetadata(movedEntries.map((entry) => entry.catalogMetadata).nonNulls.toSet()); + await localMediaDb.saveAddresses(movedEntries.map((entry) => entry.addressDetails).nonNulls.toSet()); } else { await Future.forEach(movedOps, (movedOp) async { final newFields = movedOp.newFields; @@ -393,7 +394,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place addEntries(movedEntries); case MoveType.move: case MoveType.export: - cleanEmptyAlbums(fromAlbums.whereNotNull().toSet()); + cleanEmptyAlbums(fromAlbums.nonNulls.toSet()); addDirectories(albums: destinationAlbums); case MoveType.toBin: case MoveType.fromBin: @@ -427,11 +428,13 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place eventBus.fire(EntryMovedEvent(MoveType.move, movedEntries)); } - SourceScope get scope => SourceScope.none; + SourceScope get loadedScope; + + SourceScope get targetScope; Future init({ + required SourceScope scope, AnalysisController? analysisController, - AlbumFilter? albumFilter, bool loadTopEntriesFirst = false, }); @@ -593,6 +596,11 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place analyze(null, entries: candidateEntries); } } + + void _onVaultsChanged() { + final newlyVisibleFilters = vaults.vaultDirectories.whereNot(vaults.isLocked).map((v) => AlbumFilter(v, null)).toSet(); + _onFilterVisibilityChanged(newlyVisibleFilters); + } } class AspectRatioChangedEvent {} diff --git a/lib/model/source/location/country.dart b/lib/model/source/location/country.dart index a8128dc43..bf1b7264a 100644 --- a/lib/model/source/location/country.dart +++ b/lib/model/source/location/country.dart @@ -23,7 +23,7 @@ mixin CountryMixin on SourceBase { } else { countryCodes ??= {}; if (entries != null) { - countryCodes.addAll(entries.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.countryCode).whereNotNull()); + countryCodes.addAll(entries.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.countryCode).nonNulls); } countryCodes.forEach((countryCode) { _filterEntryCountMap.remove(countryCode); diff --git a/lib/model/source/location/location.dart b/lib/model/source/location/location.dart index bfa00b1fb..d2af8780c 100644 --- a/lib/model/source/location/location.dart +++ b/lib/model/source/location/location.dart @@ -152,9 +152,9 @@ mixin LocationMixin on CountryMixin, StateMixin { } void updateLocations() { - final locations = visibleEntries.map((entry) => entry.addressDetails).whereNotNull().toList(); + final locations = visibleEntries.map((entry) => entry.addressDetails).nonNulls.toList(); - final updatedPlaces = locations.map((address) => address.place).whereNotNull().where((v) => v.isNotEmpty).toSet().toList()..sort(compareAsciiUpperCase); + final updatedPlaces = locations.map((address) => address.place).nonNulls.where((v) => v.isNotEmpty).toSet().toList()..sort(compareAsciiUpperCase); if (!listEquals(updatedPlaces, sortedPlaces)) { sortedPlaces = List.unmodifiable(updatedPlaces); eventBus.fire(PlacesChangedEvent()); @@ -195,7 +195,7 @@ mixin LocationMixin on CountryMixin, StateMixin { final code = getCode(address); if (code == null || code.isEmpty) return null; return MapEntry(code, getName(address)); - }).whereNotNull()); + }).nonNulls); return namesByCode.entries.map((kv) { final code = kv.key; final name = kv.value; diff --git a/lib/model/source/location/place.dart b/lib/model/source/location/place.dart index aad3f9824..3b518a182 100644 --- a/lib/model/source/location/place.dart +++ b/lib/model/source/location/place.dart @@ -23,7 +23,7 @@ mixin PlaceMixin on SourceBase { } else { places ??= {}; if (entries != null) { - places.addAll(entries.map((entry) => entry.addressDetails?.place).whereNotNull()); + places.addAll(entries.map((entry) => entry.addressDetails?.place).nonNulls); } places.forEach((place) { _filterEntryCountMap.remove(place); diff --git a/lib/model/source/location/state.dart b/lib/model/source/location/state.dart index 67ce98bda..bfadc9568 100644 --- a/lib/model/source/location/state.dart +++ b/lib/model/source/location/state.dart @@ -23,7 +23,7 @@ mixin StateMixin on SourceBase { } else { stateCodes ??= {}; if (entries != null) { - stateCodes.addAll(entries.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.stateCode).whereNotNull()); + stateCodes.addAll(entries.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.stateCode).nonNulls); } stateCodes.forEach((stateCode) { _filterEntryCountMap.remove(stateCode); diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index 4b3f12e08..88acbcaed 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -21,33 +21,33 @@ class MediaStoreSource extends CollectionSource { final Debouncer _changeDebouncer = Debouncer(delay: ADurations.mediaContentChangeDebounceDelay); final Set _changedUris = {}; int? _lastGeneration; - SourceScope _scope = SourceScope.none; + SourceScope _loadedScope, _targetScope; bool _canAnalyze = true; + Future? _essentialLoader; @override set canAnalyze(bool enabled) => _canAnalyze = enabled; @override - SourceScope get scope => _scope; + SourceScope get loadedScope => _loadedScope; + + @override + SourceScope get targetScope => _targetScope; @override Future init({ + required SourceScope scope, AnalysisController? analysisController, - AlbumFilter? albumFilter, bool loadTopEntriesFirst = false, }) async { - await reportService.log('$runtimeType init album=${albumFilter?.album}'); - if (_scope == SourceScope.none) { - await _loadEssentials(); - } - if (_scope != SourceScope.full) { - _scope = albumFilter != null ? SourceScope.album : SourceScope.full; - } + _targetScope = scope; + await reportService.log('$runtimeType init target scope=$scope'); + _essentialLoader ??= _loadEssentials(); + await _essentialLoader; addDirectories(albums: settings.pinnedFilters.whereType().map((v) => v.album).toSet()); await updateGeneration(); unawaited(_loadEntries( analysisController: analysisController, - directory: albumFilter?.album, loadTopEntriesFirst: loadTopEntriesFirst, )); } @@ -63,8 +63,7 @@ class MediaStoreSource extends CollectionSource { if (currentTimeZoneOffset != null) { final catalogTimeZoneOffset = settings.catalogTimeZoneRawOffsetMillis; if (currentTimeZoneOffset != catalogTimeZoneOffset) { - // clear catalog metadata to get correct date/times when moving to a different time zone - debugPrint('$runtimeType clear catalog metadata to get correct date/times'); + unawaited(reportService.log('Time zone offset change: $currentTimeZoneOffset -> $catalogTimeZoneOffset. Clear catalog metadata to get correct date/times.')); await localMediaDb.clearDates(); await localMediaDb.clearCatalogMetadata(); settings.catalogTimeZoneRawOffsetMillis = currentTimeZoneOffset; @@ -76,14 +75,16 @@ class MediaStoreSource extends CollectionSource { Future _loadEntries({ AnalysisController? analysisController, - String? directory, required bool loadTopEntriesFirst, }) async { - unawaited(reportService.log('$runtimeType load start')); + unawaited(reportService.log('$runtimeType load (known) start')); final stopwatch = Stopwatch()..start(); state = SourceState.loading; clearEntries(); + final scopeAlbumFilters = _targetScope?.whereType(); + final scopeDirectory = scopeAlbumFilters != null && scopeAlbumFilters.length == 1 ? scopeAlbumFilters.first.album : null; + final Set topEntries = {}; if (loadTopEntriesFirst) { final topIds = settings.topEntryIds?.toSet(); @@ -95,7 +96,7 @@ class MediaStoreSource extends CollectionSource { } debugPrint('$runtimeType load ${stopwatch.elapsed} fetch known entries'); - final knownEntries = await localMediaDb.loadEntries(origin: EntryOrigins.mediaStoreContent, directory: directory); + final knownEntries = await localMediaDb.loadEntries(origin: EntryOrigins.mediaStoreContent, directory: scopeDirectory); final knownLiveEntries = knownEntries.where((entry) => !entry.trashed).toSet(); debugPrint('$runtimeType load ${stopwatch.elapsed} check obsolete entries'); @@ -114,18 +115,20 @@ class MediaStoreSource extends CollectionSource { // add entries without notifying, so that the collection is not refreshed // with items that may be hidden right away because of their metadata addEntries(knownEntries, notify: false); + // but use album notification without waiting for cataloguing + // so that it is more reactive when picking an album in view mode + notifyAlbumsChanged(); - await _loadVaultEntries(directory); + await _loadVaultEntries(scopeDirectory); debugPrint('$runtimeType load ${stopwatch.elapsed} load metadata'); - if (directory != null) { + if (scopeDirectory != null) { final ids = knownLiveEntries.map((entry) => entry.id).toSet(); await loadCatalogMetadata(ids: ids); await loadAddresses(ids: ids); } else { await loadCatalogMetadata(); await loadAddresses(); - updateDerivedFilters(); // trash await loadTrashDetails(); @@ -139,6 +142,7 @@ class MediaStoreSource extends CollectionSource { onError: (error) => debugPrint('failed to evict expired trash error=$error'), )); } + updateDerivedFilters(); // clean up obsolete entries if (removedEntries.isNotEmpty) { @@ -146,20 +150,30 @@ class MediaStoreSource extends CollectionSource { await localMediaDb.removeIds(removedEntries.map((entry) => entry.id).toSet()); } - // verify paths because some apps move files without updating their `last modified date` - debugPrint('$runtimeType load ${stopwatch.elapsed} check obsolete paths'); - final knownPathByContentId = Map.fromEntries(knownLiveEntries.map((entry) => MapEntry(entry.contentId, entry.path))); - final movedContentIds = (await mediaStoreService.checkObsoletePaths(knownPathByContentId)).toSet(); - movedContentIds.forEach((contentId) { - // make obsolete by resetting its modified date - knownDateByContentId[contentId] = 0; - }); + _loadedScope = _targetScope; + unawaited(reportService.log('$runtimeType load (known) done in ${stopwatch.elapsed.inSeconds}s for ${knownEntries.length} known, ${removedEntries.length} removed')); - if (!_canAnalyze) { + if (_canAnalyze) { // it can discover new entries only if it can analyze them + await _loadNewEntries( + analysisController: analysisController, + directory: scopeDirectory, + knownLiveEntries: knownLiveEntries, + knownDateByContentId: knownDateByContentId, + ); + } else { state = SourceState.ready; - return; } + } + + Future _loadNewEntries({ + required AnalysisController? analysisController, + required String? directory, + required Set knownLiveEntries, + required Map knownDateByContentId, + }) async { + unawaited(reportService.log('$runtimeType load (new) start')); + final stopwatch = Stopwatch()..start(); // items to add to the collection final newEntries = {}; @@ -170,8 +184,18 @@ class MediaStoreSource extends CollectionSource { newEntries.addAll(await recoverUntrackedTrashItems()); } + // verify paths because some apps move files without updating their `last modified date` + debugPrint('$runtimeType load ${stopwatch.elapsed} check obsolete paths'); + final knownPathByContentId = Map.fromEntries(knownLiveEntries.map((entry) => MapEntry(entry.contentId, entry.path))); + final movedContentIds = (await mediaStoreService.checkObsoletePaths(knownPathByContentId)).toSet(); + movedContentIds.forEach((contentId) { + // make obsolete by resetting its modified date + knownDateByContentId[contentId] = 0; + }); + // fetch new & modified entries debugPrint('$runtimeType load ${stopwatch.elapsed} fetch new entries'); + final knownContentIds = knownDateByContentId.keys.toSet(); mediaStoreService.getEntries(knownDateByContentId, directory: directory).listen( (entry) { // when discovering modified entry with known content ID, @@ -220,7 +244,7 @@ class MediaStoreSource extends CollectionSource { // so we manually notify change for potential home screen filters notifyAlbumsChanged(); - unawaited(reportService.log('$runtimeType load done in ${stopwatch.elapsed.inSeconds}s for ${knownEntries.length} known, ${newEntries.length} new, ${removedEntries.length} removed')); + unawaited(reportService.log('$runtimeType load (new) done in ${stopwatch.elapsed.inSeconds}s for ${newEntries.length} new entries')); }, onError: (error) => debugPrint('$runtimeType stream error=$error'), ); @@ -233,7 +257,7 @@ class MediaStoreSource extends CollectionSource { // sometimes yields an entry with its temporary path: `/data/sec/camera/!@#$%^..._temp.jpg` @override Future> refreshUris(Set changedUris, {AnalysisController? analysisController}) async { - if (_scope == SourceScope.none || !canRefresh || !isReady) return changedUris; + if (!canRefresh || _essentialLoader == null || !isReady) return changedUris; state = SourceState.loading; @@ -246,11 +270,11 @@ class MediaStoreSource extends CollectionSource { final contentId = int.tryParse(idString); if (contentId == null) return null; return MapEntry(contentId, uri); - }).whereNotNull()); + }).nonNulls); // clean up obsolete entries final obsoleteContentIds = (await mediaStoreService.checkObsoleteContentIds(changedUriByContentId.keys.toList())).toSet(); - final obsoleteUris = obsoleteContentIds.map((contentId) => changedUriByContentId[contentId]).whereNotNull().toSet(); + final obsoleteUris = obsoleteContentIds.map((contentId) => changedUriByContentId[contentId]).nonNulls.toSet(); await removeEntries(obsoleteUris, includeTrash: false); obsoleteContentIds.forEach(changedUriByContentId.remove); diff --git a/lib/model/source/trash.dart b/lib/model/source/trash.dart index 03062d80d..f3107cbf5 100644 --- a/lib/model/source/trash.dart +++ b/lib/model/source/trash.dart @@ -24,24 +24,24 @@ mixin TrashMixin on SourceBase { if (expiredEntries.isEmpty) return {}; final processed = {}; - final completer = Completer>(); + final opCompleter = Completer>(); mediaEditService.delete(entries: expiredEntries).listen( processed.add, - onError: completer.completeError, + onError: opCompleter.completeError, onDone: () async { final successOps = processed.where((e) => e.success).toSet(); final deletedOps = successOps.where((e) => !e.skipped).toSet(); final deletedUris = deletedOps.map((event) => event.uri).toSet(); - completer.complete(deletedUris); + opCompleter.complete(deletedUris); }, ); - return await completer.future; + return await opCompleter.future; } Future> recoverUntrackedTrashItems() async { final newEntries = {}; - final knownPaths = allEntries.map((v) => v.trashDetails?.path).whereNotNull().toSet(); + final knownPaths = allEntries.map((v) => v.trashDetails?.path).nonNulls.toSet(); final untrackedPaths = await storageService.getUntrackedTrashPaths(knownPaths); if (untrackedPaths.isNotEmpty) { debugPrint('Recovering ${untrackedPaths.length} untracked bin items'); diff --git a/lib/model/vaults/vaults.dart b/lib/model/vaults/vaults.dart index 651f08614..a621c2952 100644 --- a/lib/model/vaults/vaults.dart +++ b/lib/model/vaults/vaults.dart @@ -8,6 +8,7 @@ import 'package:aves/model/vaults/details.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves_screen_state/aves_screen_state.dart'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; @@ -20,7 +21,9 @@ class Vaults extends ChangeNotifier { static const _fileScheme = 'file'; - Vaults._private(); + Vaults._private() { + if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this); + } Future init() async { _rows = await localMediaDb.loadAllVaults(); @@ -53,7 +56,7 @@ class Vaults extends ChangeNotifier { } Future remove(Set dirPaths) async { - final details = dirPaths.map(detailsForPath).whereNotNull().toSet(); + final details = dirPaths.map(detailsForPath).nonNulls.toSet(); if (details.isEmpty) return; await localMediaDb.removeVaults(details); @@ -160,7 +163,7 @@ class Vaults extends ChangeNotifier { final vaultName = detailsForPath(dirPath)?.name; if (vaultName == null) return newEntries; - final knownPaths = source.allEntries.where((v) => v.origin == EntryOrigins.vault && v.directory == dirPath).map((v) => v.path).whereNotNull().toSet(); + final knownPaths = source.allEntries.where((v) => v.origin == EntryOrigins.vault && v.directory == dirPath).map((v) => v.path).nonNulls.toSet(); final untrackedPaths = await storageService.getUntrackedVaultPaths(vaultName, knownPaths); if (untrackedPaths.isNotEmpty) { debugPrint('Recovering ${untrackedPaths.length} untracked vault items'); @@ -181,8 +184,10 @@ class Vaults extends ChangeNotifier { void _onScreenOff() => lock(all.where((v) => v.autoLockScreenOff).map((v) => v.path).toSet()); + bool get needProtection => _unlockedDirPaths.isNotEmpty; + void _onLockStateChanged() { - windowService.secureScreen(_unlockedDirPaths.isNotEmpty); + windowService.secureScreen(needProtection); notifyListeners(); } } diff --git a/lib/ref/metadata/xmp.dart b/lib/ref/metadata/xmp.dart index 3e8df1d77..311d7a4f7 100644 --- a/lib/ref/metadata/xmp.dart +++ b/lib/ref/metadata/xmp.dart @@ -38,6 +38,7 @@ class XmpNamespaces { static const gFocus = 'http://ns.google.com/photos/1.0/focus/'; static const gImage = 'http://ns.google.com/photos/1.0/image/'; static const gPano = 'http://ns.google.com/photos/1.0/panorama/'; + static const gPhotoScan = 'http://ns.google.com/photos/1.0/photoscan/'; static const gSpherical = 'http://ns.google.com/videos/1.0/spherical/'; static const gettyImagesGift = 'http://xmp.gettyimages.com/gift/1.0/'; static const gimp210 = 'http://www.gimp.org/ns/2.10/'; diff --git a/lib/services/analysis_service.dart b/lib/services/analysis_service.dart index f06263480..693199a96 100644 --- a/lib/services/analysis_service.dart +++ b/lib/services/analysis_service.dart @@ -5,6 +5,7 @@ import 'package:aves/l10n/l10n.dart'; import 'package:aves/model/device.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/analysis_controller.dart'; +import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/media_store_source.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; @@ -13,6 +14,7 @@ import 'package:aves_model/aves_model.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:leak_tracker/leak_tracker.dart'; import 'package:permission_handler/permission_handler.dart'; class AnalysisService { @@ -99,7 +101,7 @@ class Analyzer with WidgetsBindingObserver { Analyzer() { debugPrint('$runtimeType create'); if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$Analyzer', object: this, @@ -113,7 +115,7 @@ class Analyzer with WidgetsBindingObserver { void dispose() { debugPrint('$runtimeType dispose'); if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _stopUpdateTimer(); _controller?.dispose(); @@ -147,7 +149,7 @@ class Analyzer with WidgetsBindingObserver { settings.systemLocalesFallback = await deviceService.getLocales(); _l10n = await AppLocalizations.delegate.load(settings.appliedLocale); _serviceStateNotifier.value = AnalyzerState.running; - await _source.init(analysisController: _controller); + await _source.init(scope: CollectionSource.fullScope, analysisController: _controller); _notificationUpdateTimer = Timer.periodic(notificationUpdateInterval, (_) async { if (!isRunning) return; diff --git a/lib/services/app_service.dart b/lib/services/app_service.dart index 705cc9b2d..9bfb04dc2 100644 --- a/lib/services/app_service.dart +++ b/lib/services/app_service.dart @@ -1,11 +1,11 @@ import 'dart:async'; +import 'package:aves/geo/uri.dart'; import 'package:aves/model/app_inventory.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/services/common/services.dart'; -import 'package:aves/utils/math_utils.dart'; import 'package:collection/collection.dart'; import 'package:flutter/services.dart'; import 'package:latlong2/latlong.dart'; @@ -30,7 +30,15 @@ abstract class AppService { Future shareSingle(String uri, String mimeType); - Future pinToHomeScreen(String label, AvesEntry? coverEntry, {Set? filters, String? explorerPath, String? uri}); + Future pinToHomeScreen( + String label, + AvesEntry? coverEntry, { + required String route, + Set? filters, + String? path, + String? viewUri, + String? geoUri, + }); } class PlatformAppService implements AppService { @@ -96,21 +104,21 @@ class PlatformAppService implements AppService { @override Future> edit(String uri, String mimeType) async { try { - final completer = Completer(); + final opCompleter = Completer(); _stream.receiveBroadcastStream({ 'op': 'edit', 'uri': uri, 'mimeType': mimeType, }).listen( - (data) => completer.complete(data as Map?), - onError: completer.completeError, + (data) => opCompleter.complete(data as Map?), + onError: opCompleter.completeError, onDone: () { - if (!completer.isCompleted) completer.complete({'error': 'cancelled'}); + if (!opCompleter.isCompleted) opCompleter.complete({'error': 'cancelled'}); }, cancelOnError: true, ); // `await` here, so that `completeError` will be caught below - final result = await completer.future; + final result = await opCompleter.future; if (result == null) return {'error': 'cancelled'}; return result.cast(); } on PlatformException catch (e, stack) { @@ -138,13 +146,9 @@ class PlatformAppService implements AppService { @override Future openMap(LatLng latLng) async { - final latitude = roundToPrecision(latLng.latitude, decimals: 6); - final longitude = roundToPrecision(latLng.longitude, decimals: 6); - final geoUri = 'geo:$latitude,$longitude?q=$latitude,$longitude'; - try { final result = await _platform.invokeMethod('openMap', { - 'geoUri': geoUri, + 'geoUri': toGeoUri(latLng), }); if (result != null) return result as bool; } on PlatformException catch (e, stack) { @@ -203,7 +207,15 @@ class PlatformAppService implements AppService { // app shortcuts @override - Future pinToHomeScreen(String label, AvesEntry? coverEntry, {Set? filters, String? explorerPath, String? uri}) async { + Future pinToHomeScreen( + String label, + AvesEntry? coverEntry, { + required String route, + Set? filters, + String? path, + String? viewUri, + String? geoUri, + }) async { Uint8List? iconBytes; if (coverEntry != null) { final size = coverEntry.isVideo ? 0.0 : 256.0; @@ -221,9 +233,11 @@ class PlatformAppService implements AppService { await _platform.invokeMethod('pinShortcut', { 'label': label, 'iconBytes': iconBytes, + 'route': route, 'filters': filters?.map((filter) => filter.toJson()).toList(), - 'explorerPath': explorerPath, - 'uri': uri, + 'path': path, + 'viewUri': viewUri, + 'geoUri': geoUri, }); } on PlatformException catch (e, stack) { await reportService.recordError(e, stack); diff --git a/lib/services/common/service_policy.dart b/lib/services/common/service_policy.dart index e2535533d..1b53ccc75 100644 --- a/lib/services/common/service_policy.dart +++ b/lib/services/common/service_policy.dart @@ -24,32 +24,32 @@ class ServicePolicy { int priority = ServiceCallPriority.normal, Object? key, }) { - Completer completer; + Completer taskCompleter; _Task task; key ??= platformCall.hashCode; final toResume = _paused.remove(key); if (toResume != null) { priority = toResume.$1; task = toResume.$2 as _Task; - completer = task.completer; + taskCompleter = task.completer; } else { - completer = Completer(); + taskCompleter = Completer(); task = _Task( () async { try { - completer.complete(await platformCall()); + taskCompleter.complete(await platformCall()); } catch (error, stack) { - completer.completeError(error, stack); + taskCompleter.completeError(error, stack); } _runningQueue.remove(key); _pickNext(); }, - completer, + taskCompleter, ); } _getQueue(priority)[key] = task; _pickNext(); - return completer.future; + return taskCompleter.future; } Future? resume(Object key) { diff --git a/lib/services/intent_service.dart b/lib/services/intent_service.dart index 51f18940a..9a750d668 100644 --- a/lib/services/intent_service.dart +++ b/lib/services/intent_service.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/services/app_service.dart'; import 'package:aves/services/common/services.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/services.dart'; import 'package:streams_channel/streams_channel.dart'; @@ -48,23 +47,23 @@ class IntentService { static Future?> pickCollectionFilters(Set? initialFilters) async { try { - final completer = Completer?>(); + final opCompleter = Completer?>(); _stream.receiveBroadcastStream({ 'op': 'pickCollectionFilters', 'initialFilters': initialFilters?.map((filter) => filter.toJson()).toList(), }).listen( (data) { - final result = (data as List?)?.cast().map(CollectionFilter.fromJson).whereNotNull().toSet(); - completer.complete(result); + final result = (data as List?)?.cast().map(CollectionFilter.fromJson).nonNulls.toSet(); + opCompleter.complete(result); }, - onError: completer.completeError, + onError: opCompleter.completeError, onDone: () { - if (!completer.isCompleted) completer.complete(null); + if (!opCompleter.isCompleted) opCompleter.complete(null); }, cancelOnError: true, ); // `await` here, so that `completeError` will be caught below - return await completer.future; + return await opCompleter.future; } on PlatformException catch (e, stack) { await reportService.recordError(e, stack); } diff --git a/lib/services/media/media_fetch_service.dart b/lib/services/media/media_fetch_service.dart index 756e1d58e..bdbcca44a 100644 --- a/lib/services/media/media_fetch_service.dart +++ b/lib/services/media/media_fetch_service.dart @@ -120,7 +120,7 @@ class PlatformMediaFetchService implements MediaFetchService { BytesReceivedCallback? onBytesReceived, }) async { try { - final completer = Completer.sync(); + final opCompleter = Completer(); final sink = OutputBuffer(); var bytesReceived = 0; _byteStream.receiveBroadcastStream({ @@ -139,20 +139,20 @@ class PlatformMediaFetchService implements MediaFetchService { try { onBytesReceived(bytesReceived, sizeBytes); } catch (error, stack) { - completer.completeError(error, stack); + opCompleter.completeError(error, stack); return; } } }, - onError: completer.completeError, + onError: opCompleter.completeError, onDone: () { sink.close(); - completer.complete(sink.bytes); + opCompleter.complete(sink.bytes); }, cancelOnError: true, ); // `await` here, so that `completeError` will be caught below - return await completer.future; + return await opCompleter.future; } on PlatformException catch (e, stack) { if (_isUnknownVisual(mimeType)) { await reportService.recordError(e, stack); diff --git a/lib/services/metadata/metadata_edit_service.dart b/lib/services/metadata/metadata_edit_service.dart index 463698ac2..7317e55e8 100644 --- a/lib/services/metadata/metadata_edit_service.dart +++ b/lib/services/metadata/metadata_edit_service.dart @@ -7,7 +7,6 @@ import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves_model/aves_model.dart'; import 'package:aves_report/aves_report.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:stack_trace/stack_trace.dart'; @@ -65,7 +64,7 @@ class PlatformMetadataEditService implements MetadataEditService { 'entry': entry.toPlatformEntryMap(), 'dateMillis': modifier.setDateTime?.millisecondsSinceEpoch, 'shiftSeconds': modifier.shiftSeconds, - 'fields': modifier.fields.where((v) => v.type == MetadataType.exif).map((v) => v.toPlatform).whereNotNull().toList(), + 'fields': modifier.fields.where((v) => v.type == MetadataType.exif).map((v) => v.toPlatform).nonNulls.toList(), }); if (result != null) return (result as Map).cast(); } on PlatformException catch (e, stack) { diff --git a/lib/services/metadata/svg_metadata_service.dart b/lib/services/metadata/svg_metadata_service.dart index 0567b1908..f7055f98f 100644 --- a/lib/services/metadata/svg_metadata_service.dart +++ b/lib/services/metadata/svg_metadata_service.dart @@ -82,7 +82,7 @@ class SvgMetadataService { ..._textElements.map((name) { final value = root.getElement(name)?.innerText; return value != null ? MapEntry(formatKey(name), value) : null; - }).whereNotNull(), + }).nonNulls, ]); final metadata = root.getElement(_metadataElement); diff --git a/lib/services/storage_service.dart b/lib/services/storage_service.dart index c97206cda..62589161b 100644 --- a/lib/services/storage_service.dart +++ b/lib/services/storage_service.dart @@ -12,6 +12,8 @@ abstract class StorageService { Future> getStorageVolumes(); + Future getExternalCacheDirectory(); + Future> getUntrackedTrashPaths(Iterable knownPaths); Future> getUntrackedVaultPaths(String vaultName, Iterable knownPaths); @@ -79,6 +81,19 @@ class PlatformStorageService implements StorageService { return {}; } + @override + Future getExternalCacheDirectory() async { + try { + final result = await _platform.invokeMethod('getCacheDirectory', { + 'external': true, + }); + return result as String; + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + return ''; + } + @override Future> getUntrackedTrashPaths(Iterable knownPaths) async { try { @@ -250,20 +265,20 @@ class PlatformStorageService implements StorageService { @override Future requestDirectoryAccess(String path) async { try { - final completer = Completer(); + final opCompleter = Completer(); _stream.receiveBroadcastStream({ 'op': 'requestDirectoryAccess', 'path': path, }).listen( - (data) => completer.complete(data as bool), - onError: completer.completeError, + (data) => opCompleter.complete(data as bool), + onError: opCompleter.completeError, onDone: () { - if (!completer.isCompleted) completer.complete(false); + if (!opCompleter.isCompleted) opCompleter.complete(false); }, cancelOnError: true, ); // `await` here, so that `completeError` will be caught below - return await completer.future; + return await opCompleter.future; } on PlatformException catch (e, stack) { await reportService.recordError(e, stack); } @@ -274,21 +289,21 @@ class PlatformStorageService implements StorageService { @override Future requestMediaFileAccess(List uris, List mimeTypes) async { try { - final completer = Completer(); + final opCompleter = Completer(); _stream.receiveBroadcastStream({ 'op': 'requestMediaFileAccess', 'uris': uris, 'mimeTypes': mimeTypes, }).listen( - (data) => completer.complete(data as bool), - onError: completer.completeError, + (data) => opCompleter.complete(data as bool), + onError: opCompleter.completeError, onDone: () { - if (!completer.isCompleted) completer.complete(false); + if (!opCompleter.isCompleted) opCompleter.complete(false); }, cancelOnError: true, ); // `await` here, so that `completeError` will be caught below - return await completer.future; + return await opCompleter.future; } on PlatformException catch (e, stack) { final message = e.message; // mute issue in the specific case when an item: @@ -305,22 +320,22 @@ class PlatformStorageService implements StorageService { @override Future createFile(String name, String mimeType, Uint8List bytes) async { try { - final completer = Completer(); + final opCompleter = Completer(); _stream.receiveBroadcastStream({ 'op': 'createFile', 'name': name, 'mimeType': mimeType, 'bytes': bytes, }).listen( - (data) => completer.complete(data as bool?), - onError: completer.completeError, + (data) => opCompleter.complete(data as bool?), + onError: opCompleter.completeError, onDone: () { - if (!completer.isCompleted) completer.complete(false); + if (!opCompleter.isCompleted) opCompleter.complete(false); }, cancelOnError: true, ); // `await` here, so that `completeError` will be caught below - return await completer.future; + return await opCompleter.future; } on PlatformException catch (e, stack) { await reportService.recordError(e, stack); } @@ -330,7 +345,7 @@ class PlatformStorageService implements StorageService { @override Future openFile([String? mimeType]) async { try { - final completer = Completer.sync(); + final opCompleter = Completer(); final sink = OutputBuffer(); _stream.receiveBroadcastStream({ 'op': 'openFile', @@ -340,15 +355,15 @@ class PlatformStorageService implements StorageService { final chunk = data as Uint8List; sink.add(chunk); }, - onError: completer.completeError, + onError: opCompleter.completeError, onDone: () { sink.close(); - completer.complete(sink.bytes); + opCompleter.complete(sink.bytes); }, cancelOnError: true, ); // `await` here, so that `completeError` will be caught below - return await completer.future; + return await opCompleter.future; } on PlatformException catch (e, stack) { await reportService.recordError(e, stack); } diff --git a/lib/theme/icons.dart b/lib/theme/icons.dart index 485ffa8e2..f947b079a 100644 --- a/lib/theme/icons.dart +++ b/lib/theme/icons.dart @@ -19,11 +19,6 @@ class AIcons { static const checked = Icons.done_outlined; static final count = MdiIcons.counter; static const counter = Icons.plus_one_outlined; - static const date = Icons.calendar_today_outlined; - static const dateByDay = Icons.today_outlined; - static const dateByMonth = Icons.calendar_month_outlined; - static const dateRecent = Icons.today_outlined; - static const dateUndated = Icons.event_busy_outlined; static const description = Icons.description_outlined; static const descriptionUntitled = Icons.comments_disabled_outlined; static const disc = Icons.fiber_manual_record; @@ -32,7 +27,6 @@ class AIcons { static const error = Icons.error_outline; static const explorer = Icons.account_tree_outlined; static const folder = Icons.folder_outlined; - static const geoBounds = Icons.public_outlined; static final github = MdiIcons.github; static const home = Icons.home_outlined; @@ -43,11 +37,6 @@ class AIcons { static const language = Icons.translate_outlined; static final legal = MdiIcons.scaleBalance; - static const location = Icons.place_outlined; - static const locationUnlocated = Icons.location_off_outlined; - static const country = Icons.flag_outlined; - static const state = Icons.flag_outlined; - static const place = Icons.place_outlined; static const mimeType = Icons.code_outlined; static const name = Icons.abc_outlined; static const newTier = Icons.fiber_new_outlined; @@ -76,6 +65,19 @@ class AIcons { static const volumeMin = Icons.volume_mute_outlined; static const volumeMax = Icons.volume_up_outlined; + // time/space + static const date = Icons.calendar_today_outlined; + static const dateByDay = Icons.today_outlined; + static const dateByMonth = Icons.calendar_month_outlined; + static const dateRecent = Icons.today_outlined; + static const dateUndated = Icons.event_busy_outlined; + static const geoBounds = Icons.public_outlined; + static const location = Icons.place_outlined; + static const locationUnlocated = Icons.location_off_outlined; + static const country = Icons.flag_outlined; + static const state = Icons.flag_outlined; + static const place = Icons.place_outlined; + // view static const group = Icons.group_work_outlined; static const layout = Icons.grid_view_outlined; @@ -91,7 +93,6 @@ class AIcons { static const add = Icons.add_circle_outline; static const addShortcut = Icons.add_to_home_screen_outlined; static const cancel = Icons.cancel_outlined; - static const captureFrame = Icons.screenshot_outlined; static const cast = Icons.cast_outlined; static const clear = Icons.clear_outlined; static const clipboard = Icons.content_copy_outlined; @@ -117,23 +118,16 @@ class AIcons { static const map = Icons.map_outlined; static const more = Icons.more_horiz_outlined; static final move = MdiIcons.fileMoveOutline; - static const mute = Icons.volume_off_outlined; - static const unmute = Icons.volume_up_outlined; static const rename = Icons.abc_outlined; static const openOutside = Icons.open_in_new_outlined; - static final openVideo = MdiIcons.moviePlayOutline; + static final openVideoPlayer = MdiIcons.openInApp; + static final openVideoPart = MdiIcons.moviePlayOutline; static const pin = Icons.push_pin_outlined; static final unpin = MdiIcons.pinOffOutline; - static const play = Icons.play_arrow; - static const pause = Icons.pause; static const print = Icons.print_outlined; static const refresh = Icons.refresh_outlined; - static const repeat = Icons.repeat_outlined; - static final repeatOff = MdiIcons.repeatOff; - static const replay10 = Icons.replay_10_outlined; static final resetBounds = MdiIcons.rayStartEnd; static const reverse = Icons.invert_colors_outlined; - static const skip10 = Icons.forward_10_outlined; static const reset = Icons.restart_alt_outlined; static const restore = Icons.restore_outlined; static const rotateLeft = Icons.rotate_left_outlined; @@ -141,23 +135,21 @@ class AIcons { static const rotateScreen = Icons.screen_rotation_outlined; static const search = Icons.search_outlined; static const select = Icons.select_all_outlined; - static const selectStreams = Icons.translate_outlined; static const setAs = Icons.wallpaper_outlined; static final setBoundEnd = MdiIcons.rayEnd; static final setBoundStart = MdiIcons.rayStart; static final setCover = MdiIcons.imageEditOutline; static const share = Icons.share_outlined; static const show = Icons.visibility_outlined; - static final showFullscreen = MdiIcons.arrowExpand; + static final showFullscreenArrows = MdiIcons.arrowExpand; + static const showFullscreenCorners = Icons.fullscreen_outlined; static const slideshow = Icons.slideshow_outlined; - static const speed = Icons.speed_outlined; static const stats = Icons.donut_small_outlined; - static const vaultLock = Icons.lock_outline; + static const vaultLock = Icons.lock_outlined; static const vaultAdd = Icons.enhanced_encryption_outlined; static final vaultConfigure = MdiIcons.shieldLockOutline; - static const videoSettings = Icons.video_settings_outlined; static const view = Icons.grid_view_outlined; - static const viewerLock = Icons.lock_outline; + static const viewerLock = Icons.lock_outlined; static const viewerUnlock = Icons.lock_open_outlined; static const zoomIn = Icons.add_outlined; static const zoomOut = Icons.remove_outlined; @@ -168,6 +160,23 @@ class AIcons { static const previous = Icons.chevron_left_outlined; static const next = Icons.chevron_right_outlined; + // video actions + // `play` and `pause` icon should be consistent with `AnimatedIcons.play_pause` + static const play = Icons.play_arrow; + static const pause = Icons.pause; + static const previousFrame = Icons.skip_previous_outlined; + static const nextFrame = Icons.skip_next_outlined; + static const replay10 = Icons.replay_10_outlined; + static const skip10 = Icons.forward_10_outlined; + static const mute = Icons.volume_off_outlined; + static const unmute = Icons.volume_up_outlined; + static const captureFrame = Icons.screenshot_outlined; + static const repeat = Icons.repeat_outlined; + static final repeatOff = MdiIcons.repeatOff; + static const selectStreams = Icons.translate_outlined; + static const setSpeed = Icons.speed_outlined; + static const videoSettings = Icons.video_settings_outlined; + // editor static const transform = Icons.crop_rotate_outlined; static const aspectRatioFree = Icons.crop_free_outlined; @@ -182,7 +191,7 @@ class AIcons { static const downloadAlbum = Icons.file_download; static const screenshotAlbum = Icons.screenshot_outlined; static const recordingAlbum = Icons.smartphone_outlined; - static const locked = Icons.lock_outline; + static const locked = Icons.lock_outlined; static const unlocked = Icons.lock_open_outlined; // thumbnail overlay @@ -193,8 +202,8 @@ class AIcons { static const multiPage = Icons.burst_mode_outlined; static const panorama = Icons.vrpano_outlined; static const sphericalVideo = Icons.threesixty_outlined; - static const videoThumb = Icons.play_circle_outline; - static const selected = Icons.check_circle_outline; + static const videoThumb = Icons.play_circle_outlined; + static const selected = Icons.check_circle_outlined; static const unselected = Icons.radio_button_unchecked; // Material Icons references to make constant instances of `IconData` diff --git a/lib/theme/themes.dart b/lib/theme/themes.dart index f1d14873b..843fad424 100644 --- a/lib/theme/themes.dart +++ b/lib/theme/themes.dart @@ -41,11 +41,11 @@ class Themes { static Color _schemeThirdLayer(ColorScheme colors) => _isDarkTheme(colors) ? colors.surfaceContainerHighest : colors.surfaceContainerHigh; - static Color _unselectedWidgetColor(ColorScheme colors) => colors.onSurface.withOpacity(0.6); + static Color _unselectedWidgetColor(ColorScheme colors) => colors.onSurface.withAlpha((255.0 * .6).round()); static Color backgroundTextColor(BuildContext context) { final colors = Theme.of(context).colorScheme; - return Color.alphaBlend(colors.surfaceTint, colors.onSurface).withOpacity(.5); + return Color.alphaBlend(colors.surfaceTint, colors.onSurface).withAlpha((255.0 * .5).round()); } static final _typography = Typography.material2021(platform: TargetPlatform.android); @@ -98,7 +98,7 @@ class Themes { // adapted from M3 defaults final TextStyle style = textTheme.labelLarge!; if (states.contains(WidgetState.disabled)) { - return style.apply(color: colors.onSurface.withOpacity(0.38)); + return style.apply(color: colors.onSurface.withAlpha((255.0 * .38).round())); } return style.apply(color: colors.onSurface); }), @@ -118,12 +118,12 @@ class Themes { fillColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) { if (states.contains(WidgetState.disabled)) { - return colors.onSurface.withOpacity(0.38); + return colors.onSurface.withAlpha((255.0 * .38).round()); } return colors.primary; } if (states.contains(WidgetState.disabled)) { - return colors.onSurface.withOpacity(0.38); + return colors.onSurface.withAlpha((255.0 * .38).round()); } if (states.contains(WidgetState.pressed)) { return colors.onSurface; @@ -139,7 +139,7 @@ class Themes { ); static SliderThemeData _sliderTheme(ColorScheme colors) => SliderThemeData( - inactiveTrackColor: colors.primary.withOpacity(0.24), + inactiveTrackColor: colors.primary.withAlpha((255.0 * .24).round()), ); static SnackBarThemeData _snackBarTheme(ColorScheme colors) => SnackBarThemeData( diff --git a/lib/utils/android_file_utils.dart b/lib/utils/android_file_utils.dart index b11f2f1d7..12b277132 100644 --- a/lib/utils/android_file_utils.dart +++ b/lib/utils/android_file_utils.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:aves/model/app_inventory.dart'; import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/services/common/services.dart'; @@ -7,8 +9,6 @@ import 'package:flutter/foundation.dart'; final AndroidFileUtils androidFileUtils = AndroidFileUtils._private(); -enum _State { uninitialized, initializing, initialized } - class AndroidFileUtils { // cf https://developer.android.com/reference/android/content/ContentResolver#SCHEME_CONTENT static const contentScheme = 'content'; @@ -29,16 +29,13 @@ class AndroidFileUtils { late final String dcimPath, downloadPath, moviesPath, picturesPath, avesVideoCapturesPath; late final Set videoCapturesPaths; Set storageVolumes = {}; - _State _initialized = _State.uninitialized; + Future? _loader; AndroidFileUtils._private(); Future init() async { - if (_initialized == _State.uninitialized) { - _initialized = _State.initializing; - await _doInit(); - _initialized = _State.initialized; - } + _loader ??= _doInit(); + await _loader; } Future _doInit() async { diff --git a/lib/utils/collection_utils.dart b/lib/utils/collection_utils.dart index 88c7b2ca1..0da1d0a58 100644 --- a/lib/utils/collection_utils.dart +++ b/lib/utils/collection_utils.dart @@ -1,4 +1,4 @@ -import 'package:collection/collection.dart'; + extension ExtraList on List { bool replace(E old, E newItem) { @@ -11,7 +11,7 @@ extension ExtraList on List { } extension ExtraMapNullableKey on Map { - Map whereNotNullKey() => {for (var v in keys.whereNotNull()) v: this[v] as V}; + Map whereNotNullKey() => {for (var v in keys.nonNulls) v: this[v] as V}; } extension ExtraMapNullableValue on Map { @@ -19,7 +19,7 @@ extension ExtraMapNullableValue on Map { } extension ExtraMapNullableKeyValue on Map { - Map whereNotNullKey() => {for (var v in keys.whereNotNull()) v: this[v]}; + Map whereNotNullKey() => {for (var v in keys.nonNulls) v: this[v]}; Map whereNotNullValue() => {for (var kv in entries.where((kv) => kv.value != null)) kv.key: kv.value as V}; } diff --git a/lib/view/src/actions/entry.dart b/lib/view/src/actions/entry.dart index 1c6809511..edf094590 100644 --- a/lib/view/src/actions/entry.dart +++ b/lib/view/src/actions/entry.dart @@ -43,9 +43,12 @@ extension ExtraEntryActionView on EntryAction { l10n.videoActionPlay, EntryAction.videoReplay10 => l10n.videoActionReplay10, EntryAction.videoSkip10 => l10n.videoActionSkip10, + EntryAction.videoShowPreviousFrame => l10n.videoActionShowPreviousFrame, + EntryAction.videoShowNextFrame => l10n.videoActionShowNextFrame, // external EntryAction.edit => l10n.entryActionEdit, - EntryAction.open || EntryAction.openVideo => l10n.entryActionOpen, + EntryAction.open => l10n.entryActionOpen, + EntryAction.openVideoPlayer => l10n.videoControlsPlayOutside, EntryAction.openMap => l10n.entryActionOpenMap, EntryAction.setAs => l10n.entryActionSetAs, EntryAction.cast => l10n.entryActionCast, @@ -110,7 +113,7 @@ extension ExtraEntryActionView on EntryAction { // different data depending on toggle state AIcons.mute, EntryAction.videoSelectStreams => AIcons.selectStreams, - EntryAction.videoSetSpeed => AIcons.speed, + EntryAction.videoSetSpeed => AIcons.setSpeed, EntryAction.videoABRepeat => AIcons.repeat, EntryAction.videoSettings => AIcons.videoSettings, EntryAction.videoTogglePlay => @@ -118,9 +121,12 @@ extension ExtraEntryActionView on EntryAction { AIcons.play, EntryAction.videoReplay10 => AIcons.replay10, EntryAction.videoSkip10 => AIcons.skip10, + EntryAction.videoShowPreviousFrame => AIcons.previousFrame, + EntryAction.videoShowNextFrame => AIcons.nextFrame, // external EntryAction.edit => AIcons.edit, - EntryAction.open || EntryAction.openVideo => AIcons.openOutside, + EntryAction.open => AIcons.openOutside, + EntryAction.openVideoPlayer => AIcons.openVideoPlayer, EntryAction.openMap => AIcons.map, EntryAction.setAs => AIcons.setAs, EntryAction.cast => AIcons.cast, @@ -138,7 +144,7 @@ extension ExtraEntryActionView on EntryAction { EntryAction.showGeoTiffOnMap => AIcons.map, // metadata / motion photo EntryAction.convertMotionPhotoToStillImage => AIcons.convertToStillImage, - EntryAction.viewMotionPhotoVideo => AIcons.openVideo, + EntryAction.viewMotionPhotoVideo => AIcons.openVideoPart, // debug EntryAction.debug => AIcons.debug, }; diff --git a/lib/view/src/actions/map.dart b/lib/view/src/actions/map.dart index c4d22a684..5e521a09f 100644 --- a/lib/view/src/actions/map.dart +++ b/lib/view/src/actions/map.dart @@ -11,6 +11,7 @@ extension ExtraMapActionView on MapAction { MapAction.openMapApp => l10n.entryActionOpenMap, MapAction.zoomIn => l10n.mapZoomInTooltip, MapAction.zoomOut => l10n.mapZoomOutTooltip, + MapAction.addShortcut => l10n.collectionActionAddShortcut, }; } @@ -22,6 +23,7 @@ extension ExtraMapActionView on MapAction { MapAction.openMapApp => AIcons.openOutside, MapAction.zoomIn => AIcons.zoomIn, MapAction.zoomOut => AIcons.zoomOut, + MapAction.addShortcut => AIcons.addShortcut, }; } } diff --git a/lib/view/src/settings/enums.dart b/lib/view/src/settings/enums.dart index 64c41c1d5..10854be4c 100644 --- a/lib/view/src/settings/enums.dart +++ b/lib/view/src/settings/enums.dart @@ -174,18 +174,6 @@ extension ExtraVideoBackgroundModeView on VideoBackgroundMode { } } -extension ExtraVideoControlsView on VideoControls { - String getName(BuildContext context) { - final l10n = context.l10n; - return switch (this) { - VideoControls.play => l10n.videoControlsPlay, - VideoControls.playSeek => l10n.videoControlsPlaySeek, - VideoControls.playOutside => l10n.videoControlsPlayOutside, - VideoControls.none => l10n.videoControlsNone, - }; - } -} - extension ExtraVideoLoopModeView on VideoLoopMode { String getName(BuildContext context) { final l10n = context.l10n; diff --git a/lib/view/src/xmp.dart b/lib/view/src/xmp.dart index 447abb097..46129e320 100644 --- a/lib/view/src/xmp.dart +++ b/lib/view/src/xmp.dart @@ -35,6 +35,7 @@ class XmpNamespaceView { XmpNamespaces.gImage: 'Google Image', XmpNamespaces.gPano: 'Google Panorama', XmpNamespaces.gSpherical: 'Google Spherical', + XmpNamespaces.gPhotoScan: 'Google PhotoScan', XmpNamespaces.gettyImagesGift: 'Getty Images', XmpNamespaces.gimp210: 'GIMP 2.10', XmpNamespaces.gimpXmp: 'GIMP', diff --git a/lib/widget_common.dart b/lib/widget_common.dart index f9b357c85..c915fecd9 100644 --- a/lib/widget_common.dart +++ b/lib/widget_common.dart @@ -97,7 +97,7 @@ Future _getWidgetEntry(int widgetId, bool reuseEntry) async { } }); source.canAnalyze = false; - await source.init(); + await source.init(scope: filters); await readyCompleter.future; final entries = CollectionLens(source: source, filters: filters).sortedEntries; diff --git a/lib/widgets/about/about_tv_page.dart b/lib/widgets/about/about_tv_page.dart index 4e01d8e43..d61590307 100644 --- a/lib/widgets/about/about_tv_page.dart +++ b/lib/widgets/about/about_tv_page.dart @@ -103,7 +103,7 @@ class _ContentState extends State<_Content> { return ListTile( title: DefaultTextStyle( style: theme.textTheme.bodyLarge!.copyWith( - color: isSelected ? colors.primary : colors.onSurface.withOpacity(0.64), + color: isSelected ? colors.primary : colors.onSurface.withAlpha((255.0 * .64).round()), ), child: _getTitle(_Section.values[index]), ), diff --git a/lib/widgets/about/translators.dart b/lib/widgets/about/translators.dart index 37e4641bf..9bfe976e2 100644 --- a/lib/widgets/about/translators.dart +++ b/lib/widgets/about/translators.dart @@ -62,19 +62,19 @@ class _RandomTextSpanHighlighterState extends State<_RandomTextSpanHighlighter> final color = widget.color; _baseStyle = TextStyle( - color: color.withOpacity(.7), + color: color.withAlpha((255.0 * .7).round()), shadows: [ Shadow( - color: color.withOpacity(0), + color: color.withAlpha(0), blurRadius: 0, ) ], ); final highlightStyle = TextStyle( - color: color.withOpacity(1), + color: color.withAlpha(255), shadows: [ Shadow( - color: color.withOpacity(1), + color: color.withAlpha(255), blurRadius: 3, ) ], diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index c0c6903c9..3b15aab04 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -49,7 +49,7 @@ import 'package:event_bus/event_bus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_localization_nn/flutter_localization_nn.dart'; +import 'package:flutter_localizations_plus/flutter_localizations_plus.dart'; import 'package:intl/intl.dart'; import 'package:latlong2/latlong.dart'; import 'package:overlay_support/overlay_support.dart'; @@ -305,8 +305,10 @@ class _AvesAppState extends State with WidgetsBindingObserver { themeMode: themeBrightness.appThemeMode, locale: settingsLocale, localizationsDelegates: const [ - ...AppLocalizations.localizationsDelegates, + // order matters for resolution of sublocales (e.g. `en_Shaw` before `en`) + ...LocalizationsEnShaw.delegates, ...LocalizationsNn.delegates, + ...AppLocalizations.localizationsDelegates, ], supportedLocales: AvesApp.supportedLocales, scrollBehavior: AvesScrollBehavior(), @@ -564,9 +566,9 @@ class _AvesAppState extends State with WidgetsBindingObserver { switch (settings.maxBrightness) { case MaxBrightness.never: case MaxBrightness.viewerOnly: - AvesApp.screenBrightness?.resetScreenBrightness(); + AvesApp.screenBrightness?.resetApplicationScreenBrightness(); case MaxBrightness.always: - AvesApp.screenBrightness?.setScreenBrightness(1); + AvesApp.screenBrightness?.setApplicationScreenBrightness(1); } } @@ -655,7 +657,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { final shouldReset = _exitedMainByPop; _exitedMainByPop = false; - if (!shouldReset && (intentData ?? {}).values.whereNotNull().isEmpty) { + if (!shouldReset && (intentData ?? {}).values.nonNulls.isEmpty) { reportService.log('Relaunch'); return; } @@ -683,11 +685,9 @@ class _AvesAppState extends State with WidgetsBindingObserver { Future _onAnalysisCompletion() async { debugPrint('Analysis completed'); - if (_mediaStoreSource.scope != SourceScope.none) { - await _mediaStoreSource.loadCatalogMetadata(); - await _mediaStoreSource.loadAddresses(); - _mediaStoreSource.updateDerivedFilters(); - } + await _mediaStoreSource.loadCatalogMetadata(); + await _mediaStoreSource.loadAddresses(); + _mediaStoreSource.updateDerivedFilters(); } void _onError(String? error) => reportService.recordError(error, null); diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index 31d53e01c..388ac7b7a 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -36,7 +36,6 @@ import 'package:aves/widgets/dialogs/tile_view_dialog.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip.dart'; import 'package:aves/widgets/search/search_delegate.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:provider/provider.dart'; @@ -357,7 +356,7 @@ class _CollectionAppBarState extends State with SingleTickerPr return [ ...EntrySetActions.general, ...isSelecting ? EntrySetActions.pageSelection : EntrySetActions.pageBrowsing, - ].whereNotNull().where(isVisible).map((action) { + ].nonNulls.where(isVisible).map((action) { final enabled = canApply(action); return CaptionedButton( iconButtonBuilder: (context, focusNode) => _buildButtonIcon( diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index 352d61a93..874f9004a 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -249,6 +249,11 @@ class _CollectionGridContentState extends State<_CollectionGridContent> { Future _goToViewer(CollectionLens collection, AvesEntry entry) async { // track viewer entry for dynamic hero placeholder final viewerEntryNotifier = context.read(); + + // prevent navigating again to the same entry until fully back, + // as a workaround for the hero pop/push diversion animation issue + // (cf `ThumbnailImage` `Hero` usage) + if (viewerEntryNotifier.value == entry) return; WidgetsBinding.instance.addPostFrameCallback((_) => viewerEntryNotifier.value = entry); final selection = context.read>(); @@ -421,7 +426,7 @@ class _CollectionScaler extends StatelessWidget { ), mosaicItemBuilder: (index, targetExtent) => DecoratedBox( decoration: BoxDecoration( - color: ThumbnailImage.computeLoadingBackgroundColor(index * 10, brightness).withOpacity(.9), + color: ThumbnailImage.computeLoadingBackgroundColor(index * 10, brightness).withAlpha((255.0 * .9).round()), border: Border.all( color: borderColor, width: borderWidth, @@ -498,7 +503,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge _checkingStoragePermission = false; _isStoragePermissionGranted.then((granted) { if (granted) { - widget.collection.source.init(); + widget.collection.source.init(scope: CollectionSource.fullScope); } }); } diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index e9cf454bd..72fe2cf4a 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -26,6 +26,7 @@ import 'package:aves/theme/durations.dart'; import 'package:aves/theme/themes.dart'; import 'package:aves/utils/collection_utils.dart'; import 'package:aves/utils/mime_utils.dart'; +import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/action_mixins/entry_editor.dart'; import 'package:aves/widgets/common/action_mixins/entry_storage.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; @@ -168,7 +169,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware } void onActionSelected(BuildContext context, EntrySetAction action) { - reportService.log('$action'); + reportService.log('$runtimeType handles $action'); switch (action) { // general case EntrySetAction.configureView: @@ -301,7 +302,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware final l10n = context.l10n; final source = context.read(); - final storageDirs = entries.map((e) => e.storageDirectory).whereNotNull().toSet(); + final storageDirs = entries.map((e) => e.storageDirectory).nonNulls.toSet(); final todoCount = entries.length; if (!await showSkippableConfirmationDialog( @@ -309,7 +310,9 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware type: ConfirmationDialog.deleteForever, message: l10n.deleteEntriesConfirmationDialogMessage(todoCount), confirmationButtonLabel: l10n.deleteButtonLabel, - )) return; + )) { + return; + } if (!await checkStoragePermissionForAlbums(context, storageDirs, entries: entries)) return; @@ -407,14 +410,14 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware Future> Function(AvesEntry entry) op, { bool showResult = true, }) async { - final selectionDirs = todoItems.map((e) => e.directory).whereNotNull().toSet(); + final selectionDirs = todoItems.map((e) => e.directory).nonNulls.toSet(); final todoCount = todoItems.length; if (!await checkStoragePermissionForAlbums(context, selectionDirs, entries: todoItems)) return; Set obsoleteTags = todoItems.expand((entry) => entry.tags).toSet(); - Set obsoleteCountryCodes = todoItems.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.countryCode).whereNotNull().toSet(); - Set obsoleteStateCodes = todoItems.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.stateCode).whereNotNull().toSet(); + Set obsoleteCountryCodes = todoItems.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.countryCode).nonNulls.toSet(); + Set obsoleteStateCodes = todoItems.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.stateCode).nonNulls.toSet(); final dataTypes = {}; final source = context.read(); @@ -746,7 +749,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware final (coverEntry, name) = result; if (name.isEmpty) return; - await appService.pinToHomeScreen(name, coverEntry, filters: filters); + await appService.pinToHomeScreen(name, coverEntry, route: CollectionPage.routeName, filters: filters); if (!device.showPinShortcutFeedback) { showFeedback(context, FeedbackType.info, context.l10n.genericSuccessFeedback); } diff --git a/lib/widgets/common/action_controls/quick_choosers/common/button.dart b/lib/widgets/common/action_controls/quick_choosers/common/button.dart index 5925099b7..8ada58a26 100644 --- a/lib/widgets/common/action_controls/quick_choosers/common/button.dart +++ b/lib/widgets/common/action_controls/quick_choosers/common/button.dart @@ -99,12 +99,10 @@ abstract class ChooserQuickButtonState, U> exten } void _clearChooserOverlayEntry() { - final overlayEntry = _chooserOverlayEntry; + _chooserOverlayEntry + ?..remove() + ..dispose(); _chooserOverlayEntry = null; - if (overlayEntry != null) { - overlayEntry.remove(); - overlayEntry.dispose(); - } } void _showChooser(LongPressStartDetails details) { diff --git a/lib/widgets/common/action_controls/quick_choosers/common/menu.dart b/lib/widgets/common/action_controls/quick_choosers/common/menu.dart index b0c1a8d36..a70fbee60 100644 --- a/lib/widgets/common/action_controls/quick_choosers/common/menu.dart +++ b/lib/widgets/common/action_controls/quick_choosers/common/menu.dart @@ -223,12 +223,14 @@ class _MenuQuickChooserState extends State> { bool get canGoUp { if (!_scrollController.hasClients) return false; final position = _scrollController.position; + if (!position.hasContentDimensions) return false; return reversed ? position.pixels < position.maxScrollExtent : 0 < position.pixels; } bool get canGoDown { if (!_scrollController.hasClients) return false; final position = _scrollController.position; + if (!position.hasContentDimensions) return false; return reversed ? 0 < position.pixels : position.pixels < position.maxScrollExtent; } diff --git a/lib/widgets/common/action_mixins/entry_editor.dart b/lib/widgets/common/action_mixins/entry_editor.dart index 4d3ad4bcc..02cf9e9c8 100644 --- a/lib/widgets/common/action_mixins/entry_editor.dart +++ b/lib/widgets/common/action_mixins/entry_editor.dart @@ -109,7 +109,7 @@ mixin EntryEditorMixin { Future> getTagsFromFilters(Set filters, AvesEntry entry) async { final tags = filters.whereType().map((v) => v.tag).toSet(); final placeholderTags = await Future.wait(filters.whereType().map((v) => v.toTag(entry))); - tags.addAll(placeholderTags.whereNotNull().where((v) => v.isNotEmpty)); + tags.addAll(placeholderTags.nonNulls.where((v) => v.isNotEmpty)); return tags; } diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index 958b273ff..3406ee72f 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -106,7 +106,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { onDone: (processed) async { final successOps = processed.where((e) => e.success).toSet(); final exportedOps = successOps.where((e) => !e.skipped).toSet(); - final newUris = exportedOps.map((v) => v.newFields['uri'] as String?).whereNotNull().toSet(); + final newUris = exportedOps.map((v) => v.newFields['uri'] as String?).nonNulls.toSet(); final isMainMode = context.read>().value == AppMode.main; source.resumeMonitoring(); @@ -176,7 +176,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { if (!await checkStoragePermissionForAlbums(context, destinationAlbums)) return; // permission for modification at origins - final originAlbums = entries.map((e) => e.directory).whereNotNull().toSet(); + final originAlbums = entries.map((e) => e.directory).nonNulls.toSet(); if ({MoveType.move, MoveType.toBin}.contains(moveType) && !await checkStoragePermissionForAlbums(context, originAlbums, entries: entries)) return; final hasEnoughSpaceByDestination = await Future.wait(destinationAlbums.map((destinationAlbum) { @@ -232,7 +232,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { // move final movedOps = successOps.where((v) => !v.skipped && !v.deleted).toSet(); - final movedEntries = movedOps.map((v) => v.uri).map((uri) => entries.firstWhereOrNull((entry) => entry.uri == uri)).whereNotNull().toSet(); + final movedEntries = movedOps.map((v) => v.uri).map((uri) => entries.firstWhereOrNull((entry) => entry.uri == uri)).nonNulls.toSet(); await source.updateAfterMove( todoEntries: entries, moveType: moveType, @@ -327,7 +327,9 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { type: ConfirmationDialog.moveToBin, message: l10n.binEntriesConfirmationDialogMessage(entries.length), confirmationButtonLabel: l10n.deleteButtonLabel, - )) return; + )) { + return; + } } final entriesByDestination = >{}; diff --git a/lib/widgets/common/action_mixins/feedback.dart b/lib/widgets/common/action_mixins/feedback.dart index 6eaa01a99..10de21fbb 100644 --- a/lib/widgets/common/action_mixins/feedback.dart +++ b/lib/widgets/common/action_mixins/feedback.dart @@ -138,7 +138,7 @@ mixin FeedbackMixin { VoidCallback? onCancel, Future Function(Set processed)? onDone, }) async { - final completer = Completer(); + final opCompleter = Completer(); await showDialog( context: context, barrierDismissible: false, @@ -149,12 +149,12 @@ mixin FeedbackMixin { onDone: (processed) async { Navigator.maybeOf(context)?.pop(); await onDone?.call(processed); - completer.complete(); + opCompleter.complete(); }, ), routeSettings: const RouteSettings(name: ReportOverlay.routeName), ); - return completer.future; + return opCompleter.future; } } @@ -257,7 +257,7 @@ class _ReportOverlayState extends State> with SingleTickerPr percent: percent, lineWidth: strokeWidth, radius: diameter / 2, - backgroundColor: colorScheme.onSurface.withOpacity(.2), + backgroundColor: colorScheme.onSurface.withAlpha((255.0 * .2).round()), progressColor: progressColor, animation: animate, center: total != null @@ -314,7 +314,7 @@ class ReportProgressIndicator extends StatelessWidget { height: diameter, padding: const EdgeInsets.all(strokeWidth / 2), child: CircularProgressIndicator( - color: progressColor.withOpacity(opacity), + color: progressColor.withAlpha((255.0 * opacity).round()), strokeWidth: strokeWidth, ), ); @@ -422,7 +422,7 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro style: contentTextStyle.copyWith( shadows: [ Shadow( - color: timerChangeShadowColor.withOpacity(0), + color: timerChangeShadowColor.withAlpha(0), blurRadius: 0, ) ], diff --git a/lib/widgets/common/action_mixins/permission_aware.dart b/lib/widgets/common/action_mixins/permission_aware.dart index 6aa7384b4..c553fd9a2 100644 --- a/lib/widgets/common/action_mixins/permission_aware.dart +++ b/lib/widgets/common/action_mixins/permission_aware.dart @@ -7,12 +7,11 @@ import 'package:aves/view/view.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; mixin PermissionAwareMixin { Future checkStoragePermission(BuildContext context, Set entries) { - final storageDirs = entries.map((e) => e.storageDirectory).whereNotNull().toSet(); + final storageDirs = entries.map((e) => e.storageDirectory).nonNulls.toSet(); return checkStoragePermissionForAlbums(context, storageDirs, entries: entries); } diff --git a/lib/widgets/common/app_bar/sliver_app_bar_title.dart b/lib/widgets/common/app_bar/sliver_app_bar_title.dart index 2db5f9bb6..40fb64145 100644 --- a/lib/widgets/common/app_bar/sliver_app_bar_title.dart +++ b/lib/widgets/common/app_bar/sliver_app_bar_title.dart @@ -16,7 +16,7 @@ class SliverAppBarTitleWrapper extends StatelessWidget { final toolbarOpacity = context.dependOnInheritedWidgetOfExactType()!.toolbarOpacity; final baseColor = (DefaultTextStyle.of(context).style.color ?? Theme.of(context).textTheme.titleLarge!.color!); return DefaultTextStyle.merge( - style: TextStyle(color: baseColor.withOpacity(toolbarOpacity)), + style: TextStyle(color: baseColor.withAlpha((255.0 * toolbarOpacity).round())), child: child, ); } diff --git a/lib/widgets/common/basic/wheel.dart b/lib/widgets/common/basic/wheel.dart index 668f12f87..5cc7d9a34 100644 --- a/lib/widgets/common/basic/wheel.dart +++ b/lib/widgets/common/basic/wheel.dart @@ -81,7 +81,7 @@ class _WheelSelectorState extends State> { height: itemSize.height, duration: transitionDuration, decoration: BoxDecoration( - color: foreground.withOpacity(focused ? .2 : 0), + color: foreground.withAlpha((255.0 * (focused ? .2 : 0)).round()), borderRadius: const BorderRadius.all(Radius.circular(8)), ), ); diff --git a/lib/widgets/common/fx/blurred.dart b/lib/widgets/common/fx/blurred.dart index bb20869b0..c20fd1d4f 100644 --- a/lib/widgets/common/fx/blurred.dart +++ b/lib/widgets/common/fx/blurred.dart @@ -7,6 +7,8 @@ final _filter = ImageFilter.blur(sigmaX: 4, sigmaY: 4); // as it yields performance issues when there are other layers on top final _identity = ImageFilter.matrix(Matrix4.identity().storage); +// TODO TLAD [impeller] use `BackdropKey` + class BlurredRect extends StatelessWidget { final bool enabled; final Widget child; diff --git a/lib/widgets/common/fx/transition_image.dart b/lib/widgets/common/fx/transition_image.dart index abd417506..044b708a8 100644 --- a/lib/widgets/common/fx/transition_image.dart +++ b/lib/widgets/common/fx/transition_image.dart @@ -3,7 +3,6 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter/semantics.dart'; // adapted from Flutter `_ImageState` in `/widgets/image.dart` // and `DecorationImagePainter` in `/painting/decoration_image.dart` @@ -15,13 +14,6 @@ class TransitionImage extends StatefulWidget { final ImageProvider image; final ValueListenable animation; final BoxFit thumbnailFit, viewerFit; - final ImageFrameBuilder? frameBuilder; - final ImageLoadingBuilder? loadingBuilder; - final ImageErrorWidgetBuilder? errorBuilder; - final String? semanticLabel; - final bool excludeFromSemantics; - final double? width, height; - final bool gaplessPlayback = false; final Color? background; const TransitionImage({ @@ -30,13 +22,6 @@ class TransitionImage extends StatefulWidget { required this.animation, required this.thumbnailFit, required this.viewerFit, - this.frameBuilder, - this.loadingBuilder, - this.errorBuilder, - this.semanticLabel, - this.excludeFromSemantics = false, - this.width, - this.height, this.background, }); @@ -47,13 +32,9 @@ class TransitionImage extends StatefulWidget { class _TransitionImageState extends State with WidgetsBindingObserver { ImageStream? _imageStream; ImageInfo? _imageInfo; - ImageChunkEvent? _loadingProgress; bool _isListeningToStream = false; - int? _frameNumber; bool _wasSynchronouslyLoaded = false; late DisposableBuildContext> _scrollAwareContext; - Object? _lastException; - StackTrace? _lastStack; ImageStreamCompleterHandle? _completerHandle; @override @@ -90,11 +71,6 @@ class _TransitionImageState extends State with WidgetsBindingOb @override void didUpdateWidget(TransitionImage oldWidget) { super.didUpdateWidget(oldWidget); - if (_isListeningToStream && (widget.loadingBuilder == null) != (oldWidget.loadingBuilder == null)) { - final ImageStreamListener oldListener = _getListener(); - _imageStream!.addListener(_getListener(recreateListener: true)); - _imageStream!.removeListener(oldListener); - } if (widget.image != oldWidget.image) { _resolveImage(); } @@ -111,10 +87,7 @@ class _TransitionImageState extends State with WidgetsBindingOb context: _scrollAwareContext, imageProvider: widget.image, ); - final ImageStream newStream = provider.resolve(createLocalImageConfiguration( - context, - size: widget.width != null && widget.height != null ? Size(widget.width!, widget.height!) : null, - )); + final ImageStream newStream = provider.resolve(createLocalImageConfiguration(context)); _updateSourceStream(newStream); } @@ -122,27 +95,7 @@ class _TransitionImageState extends State with WidgetsBindingOb ImageStreamListener _getListener({bool recreateListener = false}) { if (_imageStreamListener == null || recreateListener) { - _lastException = null; - _lastStack = null; - _imageStreamListener = ImageStreamListener( - _handleImageFrame, - onChunk: widget.loadingBuilder == null ? null : _handleImageChunk, - onError: widget.errorBuilder != null || kDebugMode - ? (error, stackTrace) { - setState(() { - _lastException = error; - _lastStack = stackTrace; - }); - assert(() { - if (widget.errorBuilder == null) { - // ignore: only_throw_errors, since we're just proxying the error. - throw error; // Ensures the error message is printed to the console. - } - return true; - }()); - } - : null, - ); + _imageStreamListener = ImageStreamListener(_handleImageFrame); } return _imageStreamListener!; } @@ -150,23 +103,10 @@ class _TransitionImageState extends State with WidgetsBindingOb void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { setState(() { _replaceImage(info: imageInfo); - _loadingProgress = null; - _lastException = null; - _lastStack = null; - _frameNumber = _frameNumber == null ? 0 : _frameNumber! + 1; _wasSynchronouslyLoaded = _wasSynchronouslyLoaded | synchronousCall; }); } - void _handleImageChunk(ImageChunkEvent event) { - assert(widget.loadingBuilder != null); - setState(() { - _loadingProgress = event; - _lastException = null; - _lastStack = null; - }); - } - void _replaceImage({required ImageInfo? info}) { final ImageInfo? oldImageInfo = _imageInfo; SchedulerBinding.instance.addPostFrameCallback((_) => oldImageInfo?.dispose()); @@ -185,15 +125,8 @@ class _TransitionImageState extends State with WidgetsBindingOb _imageStream!.removeListener(_getListener()); } - if (!widget.gaplessPlayback) { - setState(() { - _replaceImage(info: null); - }); - } - setState(() { - _loadingProgress = null; - _frameNumber = null; + _replaceImage(info: null); _wasSynchronouslyLoaded = false; }); @@ -235,46 +168,9 @@ class _TransitionImageState extends State with WidgetsBindingOb _isListeningToStream = false; } - Widget _debugBuildErrorWidget(BuildContext context, Object error) { - return Stack( - alignment: Alignment.center, - children: [ - const Positioned.fill( - child: Placeholder( - color: Color(0xCF8D021F), - ), - ), - Padding( - padding: const EdgeInsets.all(4.0), - child: FittedBox( - child: Text( - '$error', - textAlign: TextAlign.center, - textDirection: TextDirection.ltr, - style: const TextStyle( - shadows: [ - Shadow(blurRadius: 1.0), - ], - ), - ), - ), - ), - ], - ); - } - @override Widget build(BuildContext context) { - if (_lastException != null) { - if (widget.errorBuilder != null) { - return widget.errorBuilder!(context, _lastException!, _lastStack); - } - if (kDebugMode) { - return _debugBuildErrorWidget(context, _lastException!); - } - } - - Widget result = ValueListenableBuilder( + return ValueListenableBuilder( valueListenable: widget.animation, builder: (context, t, child) => CustomPaint( painter: _TransitionImagePainter( @@ -287,35 +183,6 @@ class _TransitionImageState extends State with WidgetsBindingOb ), ), ); - - if (!widget.excludeFromSemantics) { - result = Semantics( - container: widget.semanticLabel != null, - image: true, - label: widget.semanticLabel ?? '', - child: result, - ); - } - - if (widget.frameBuilder != null) { - result = widget.frameBuilder!(context, result, _frameNumber, _wasSynchronouslyLoaded); - } - - if (widget.loadingBuilder != null) { - result = widget.loadingBuilder!(context, result, _loadingProgress); - } - - return result; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder description) { - super.debugFillProperties(description); - description.add(DiagnosticsProperty('stream', _imageStream)); - description.add(DiagnosticsProperty('pixels', _imageInfo)); - description.add(DiagnosticsProperty('loadingProgress', _loadingProgress)); - description.add(DiagnosticsProperty('frameNumber', _frameNumber)); - description.add(DiagnosticsProperty('wasSynchronouslyLoaded', _wasSynchronouslyLoaded)); } } @@ -325,6 +192,11 @@ class _TransitionImagePainter extends CustomPainter { final Color? background; final BoxFit thumbnailFit, viewerFit; + static final _paint = Paint() + ..isAntiAlias = false + ..filterQuality = FilterQuality.low; + static const _alignment = Alignment.center; + const _TransitionImagePainter({ required this.image, required this.scale, @@ -336,20 +208,15 @@ class _TransitionImagePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { - if (image == null) return; - - final paint = Paint() - ..isAntiAlias = false - ..filterQuality = FilterQuality.low; - const alignment = Alignment.center; + final _image = image; + if (_image == null) return; - final rect = Offset.zero & size; - if (rect.isEmpty) { + if (size.isEmpty) { return; } - final outputSize = rect.size; - final inputSize = Size(image!.width.toDouble(), image!.height.toDouble()); + final outputSize = size; + final inputSize = Size(_image.width.toDouble(), _image.height.toDouble()); final thumbnailSizes = applyBoxFit(thumbnailFit, inputSize / scale, size); final viewerSizes = applyBoxFit(viewerFit, inputSize / scale, size); @@ -358,11 +225,12 @@ class _TransitionImagePainter extends CustomPainter { final halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0; final halfHeightDelta = (outputSize.height - destinationSize.height) / 2.0; - final dx = halfWidthDelta + alignment.x * halfWidthDelta; - final dy = halfHeightDelta + alignment.y * halfHeightDelta; - final destinationPosition = rect.topLeft.translate(dx, dy); + final dx = halfWidthDelta + _alignment.x * halfWidthDelta; + final dy = halfHeightDelta + _alignment.y * halfHeightDelta; + final destinationPosition = Offset(dx, dy); + final destinationRect = destinationPosition & destinationSize; - final sourceRect = alignment.inscribe( + final sourceRect = _alignment.inscribe( sourceSize, Offset.zero & inputSize, ); @@ -370,7 +238,7 @@ class _TransitionImagePainter extends CustomPainter { // deflate to avoid background artifact around opaque image canvas.drawRect(destinationRect.deflate(1), Paint()..color = background!); } - canvas.drawImageRect(image!, sourceRect, destinationRect, paint); + canvas.drawImageRect(_image, sourceRect, destinationRect, _paint); } @override diff --git a/lib/widgets/common/grid/overlay.dart b/lib/widgets/common/grid/overlay.dart index ed53d9a12..e897cbcb0 100644 --- a/lib/widgets/common/grid/overlay.dart +++ b/lib/widgets/common/grid/overlay.dart @@ -31,7 +31,7 @@ class GridItemSelectionOverlay extends StatelessWidget { alignment: AlignmentDirectional.topEnd, padding: padding, decoration: BoxDecoration( - color: isSelected ? Theme.of(context).colorScheme.primary.withOpacity(.6) : Colors.transparent, + color: isSelected ? Theme.of(context).colorScheme.primary.withAlpha((255.0 * .6).round()) : Colors.transparent, borderRadius: borderRadius, ), duration: duration, diff --git a/lib/widgets/common/grid/scaling.dart b/lib/widgets/common/grid/scaling.dart index 87d2cb98c..fe29b4bf3 100644 --- a/lib/widgets/common/grid/scaling.dart +++ b/lib/widgets/common/grid/scaling.dart @@ -196,12 +196,10 @@ class _GridScaleGestureDetectorState extends State(); diff --git a/lib/widgets/common/grid/sections/fixed/scale_grid.dart b/lib/widgets/common/grid/sections/fixed/scale_grid.dart index 965ede2ed..ed89640d8 100644 --- a/lib/widgets/common/grid/sections/fixed/scale_grid.dart +++ b/lib/widgets/common/grid/sections/fixed/scale_grid.dart @@ -77,7 +77,7 @@ class FixedExtentGridPainter extends CustomPainter { ..shader = strokeShader; final fillPaint = Paint() ..style = PaintingStyle.fill - ..color = color.withOpacity(.25); + ..color = color.withAlpha((255.0 * .25).round()); final chipWidth = chipSize.width; final chipHeight = chipSize.height; diff --git a/lib/widgets/common/grid/sections/mosaic/scale_overlay.dart b/lib/widgets/common/grid/sections/mosaic/scale_overlay.dart index cb8defc1c..6fb215e2c 100644 --- a/lib/widgets/common/grid/sections/mosaic/scale_overlay.dart +++ b/lib/widgets/common/grid/sections/mosaic/scale_overlay.dart @@ -51,7 +51,7 @@ class MosaicScaleOverlay extends StatelessWidget { child: Stack( alignment: Alignment.center, children: [ - _buildBar(extentMax, colorScheme.onSurface.withOpacity(.2)), + _buildBar(extentMax, colorScheme.onSurface.withAlpha((255.0 * .2).round())), _buildBar(scaledSize.width, colorScheme.primary), ], ), diff --git a/lib/widgets/common/identity/aves_app_bar.dart b/lib/widgets/common/identity/aves_app_bar.dart index 38c19167d..f66653fc2 100644 --- a/lib/widgets/common/identity/aves_app_bar.dart +++ b/lib/widgets/common/identity/aves_app_bar.dart @@ -142,28 +142,28 @@ class AvesAppBar extends StatelessWidget { static Widget _flightShuttleBuilder( BuildContext flightContext, Animation animation, - HeroFlightDirection direction, - BuildContext fromHero, - BuildContext toHero, + HeroFlightDirection flightDirection, + BuildContext fromHeroContext, + BuildContext toHeroContext, ) { - final pushing = direction == HeroFlightDirection.push; + final pushing = flightDirection == HeroFlightDirection.push; Widget popBuilder(context, child) => Opacity(opacity: 1 - animation.value, child: child); Widget pushBuilder(context, child) => Opacity(opacity: animation.value, child: child); return Material( type: MaterialType.transparency, child: DefaultTextStyle( - style: DefaultTextStyle.of(toHero).style, + style: DefaultTextStyle.of(toHeroContext).style, child: Stack( children: [ AnimatedBuilder( animation: animation, builder: pushing ? popBuilder : pushBuilder, - child: fromHero.widget, + child: fromHeroContext.widget, ), AnimatedBuilder( animation: animation, builder: pushing ? pushBuilder : popBuilder, - child: toHero.widget, + child: toHeroContext.widget, ), ], ), @@ -237,7 +237,9 @@ class _AvesFloatingBarState extends State with RouteAware { void didPopNext() { // post to prevent single frame flash during hero WidgetsBinding.instance.addPostFrameCallback((_) { - _isBlurAllowedNotifier.value = true; + if (mounted) { + _isBlurAllowedNotifier.value = true; + } }); } @@ -245,7 +247,9 @@ class _AvesFloatingBarState extends State with RouteAware { void didPushNext() { // post to prevent single frame flash during hero WidgetsBinding.instance.addPostFrameCallback((_) { - _isBlurAllowedNotifier.value = false; + if (mounted) { + _isBlurAllowedNotifier.value = false; + } }); } @@ -270,7 +274,7 @@ class _AvesFloatingBarState extends State with RouteAware { borderRadius: AvesFloatingBar.borderRadius, child: widget.builder( context, - blurred ? backgroundColor.withOpacity(.85) : backgroundColor, + blurred ? backgroundColor.withAlpha((255.0 * .85).round()) : backgroundColor, widget.child, ), ), diff --git a/lib/widgets/common/identity/aves_caption.dart b/lib/widgets/common/identity/aves_caption.dart index 5a53ad17f..a47c1ae56 100644 --- a/lib/widgets/common/identity/aves_caption.dart +++ b/lib/widgets/common/identity/aves_caption.dart @@ -23,7 +23,7 @@ class AvesCaption extends StatelessWidget { style: subtitleStyle.copyWith( shadows: [ Shadow( - color: subtitleChangeShadowColor.withOpacity(0), + color: subtitleChangeShadowColor.withAlpha(0), blurRadius: 0, ) ], diff --git a/lib/widgets/common/identity/aves_filter_chip.dart b/lib/widgets/common/identity/aves_filter_chip.dart index 9f0108004..357a3b759 100644 --- a/lib/widgets/common/identity/aves_filter_chip.dart +++ b/lib/widgets/common/identity/aves_filter_chip.dart @@ -423,17 +423,20 @@ class _AvesFilterChipState extends State { ); final animate = context.select((v) => v.animate); - if (animate && (widget.heroType == HeroType.always || widget.heroType == HeroType.onTap && _tapped)) { - chip = Hero( - tag: filter, - transitionOnUserGestures: true, - child: MediaQueryDataProvider( - child: DefaultTextStyle( - style: const TextStyle(), - child: chip, + if (animate) { + final heroType = widget.heroType; + if (heroType == HeroType.always || (heroType == HeroType.onTap && _tapped)) { + chip = Hero( + tag: filter, + transitionOnUserGestures: true, + child: MediaQueryDataProvider( + child: DefaultTextStyle( + style: const TextStyle(), + child: chip, + ), ), - ), - ); + ); + } } return chip; } diff --git a/lib/widgets/common/identity/buttons/captioned_button.dart b/lib/widgets/common/identity/buttons/captioned_button.dart index a3cda2caf..e98ca2b5c 100644 --- a/lib/widgets/common/identity/buttons/captioned_button.dart +++ b/lib/widgets/common/identity/buttons/captioned_button.dart @@ -164,7 +164,7 @@ class CaptionedButtonText extends StatelessWidget { Widget build(BuildContext context) { var style = DefaultTextStyle.of(context).style; if (!enabled) { - style = style.copyWith(color: style.color!.withOpacity(.2)); + style = style.copyWith(color: style.color!.withAlpha((255.0 * .2).round())); } return Text( diff --git a/lib/widgets/common/identity/buttons/overlay_button.dart b/lib/widgets/common/identity/buttons/overlay_button.dart index 839a5513f..4fca3ccf0 100644 --- a/lib/widgets/common/identity/buttons/overlay_button.dart +++ b/lib/widgets/common/identity/buttons/overlay_button.dart @@ -175,7 +175,7 @@ class OverlayTextButton extends StatelessWidget { style: ButtonStyle( backgroundColor: WidgetStateProperty.all(Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred)), foregroundColor: WidgetStateProperty.all(theme.colorScheme.onSurface), - overlayColor: theme.isDark ? WidgetStateProperty.all(Colors.white.withOpacity(0.12)) : null, + overlayColor: theme.isDark ? WidgetStateProperty.all(Colors.white.withAlpha((255.0 * .12).round())) : null, minimumSize: _minSize, side: WidgetStateProperty.all(AvesBorder.curvedSide(context)), shape: WidgetStateProperty.all(const RoundedRectangleBorder( diff --git a/lib/widgets/common/identity/empty.dart b/lib/widgets/common/identity/empty.dart index ccefa90f7..43c51bd68 100644 --- a/lib/widgets/common/identity/empty.dart +++ b/lib/widgets/common/identity/empty.dart @@ -25,7 +25,7 @@ class EmptyContent extends StatelessWidget { @override Widget build(BuildContext context) { - final color = Theme.of(context).colorScheme.primary.withOpacity(.5); + final color = Theme.of(context).colorScheme.primary.withAlpha((255.0 * .5).round()); final durations = context.watch(); return Padding( padding: safeBottom diff --git a/lib/widgets/common/map/attribution.dart b/lib/widgets/common/map/attribution.dart index 067623085..bfe8c7aee 100644 --- a/lib/widgets/common/map/attribution.dart +++ b/lib/widgets/common/map/attribution.dart @@ -1,3 +1,4 @@ +import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/text.dart'; import 'package:aves/widgets/aves_app.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; @@ -5,6 +6,7 @@ import 'package:aves/widgets/viewer/info/common.dart'; import 'package:aves_map/aves_map.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:provider/provider.dart'; class Attribution extends StatelessWidget { final EntryMapStyle? style; @@ -33,17 +35,40 @@ class Attribution extends StatelessWidget { Widget _buildOsmAttributionMarkdown(BuildContext context, String data) { final theme = Theme.of(context); + Widget child = MarkdownBody( + data: '${context.l10n.mapAttributionOsmData}${AText.separator}$data', + selectable: true, + styleSheet: MarkdownStyleSheet( + a: TextStyle(color: theme.colorScheme.primary), + p: theme.textTheme.bodySmall!.merge(const TextStyle(fontSize: InfoRowGroup.fontSize)), + ), + onTapLink: (text, href, title) => AvesApp.launchUrl(href), + ); + + final animate = context.select((v) => v.animate); + if (animate) { + child = Hero( + tag: 'map-attribution', + flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) { + return DefaultTextStyle( + style: DefaultTextStyle.of(toHeroContext).style, + child: MediaQuery.removeViewPadding( + context: context, + removeLeft: true, + removeTop: true, + removeRight: true, + removeBottom: true, + child: toHeroContext.widget, + ), + ); + }, + child: child, + ); + } + return Padding( padding: const EdgeInsets.only(top: 4), - child: MarkdownBody( - data: '${context.l10n.mapAttributionOsmData}${AText.separator}$data', - selectable: true, - styleSheet: MarkdownStyleSheet( - a: TextStyle(color: theme.colorScheme.primary), - p: theme.textTheme.bodySmall!.merge(const TextStyle(fontSize: InfoRowGroup.fontSize)), - ), - onTapLink: (text, href, title) => AvesApp.launchUrl(href), - ), + child: child, ); } } diff --git a/lib/widgets/common/map/buttons/button.dart b/lib/widgets/common/map/buttons/button.dart index f7f383522..0d3f6da7a 100644 --- a/lib/widgets/common/map/buttons/button.dart +++ b/lib/widgets/common/map/buttons/button.dart @@ -1,68 +1,48 @@ -import 'package:aves/model/settings/settings.dart'; -import 'package:aves/theme/themes.dart'; -import 'package:aves/widgets/common/fx/blurred.dart'; -import 'package:aves/widgets/common/fx/borders.dart'; +import 'package:aves/widgets/common/identity/buttons/overlay_button.dart'; import 'package:aves_map/aves_map.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class MapOverlayButton extends StatelessWidget { - final Key? buttonKey; - final Widget icon; - final String tooltip; - final VoidCallback? onPressed; + final ValueWidgetBuilder builder; const MapOverlayButton({ super.key, - this.buttonKey, - required this.icon, - required this.tooltip, - required this.onPressed, + required this.builder, }); + factory MapOverlayButton.icon({ + Key? buttonKey, + required Widget icon, + required String tooltip, + VoidCallback? onPressed, + }) { + return MapOverlayButton( + builder: (context, visualDensity, child) => IconButton( + key: buttonKey, + iconSize: iconSize(visualDensity), + visualDensity: visualDensity, + icon: icon, + onPressed: onPressed, + tooltip: tooltip, + ), + ); + } + @override Widget build(BuildContext context) { - final theme = Theme.of(context); - final colorScheme = theme.colorScheme; - final blurred = settings.enableBlurEffect; - - return Theme( - data: theme.copyWith( - colorScheme: colorScheme.copyWith( - onSurfaceVariant: colorScheme.onSurface, - ), + return Selector>( + selector: (context, v) => v.scale, + builder: (context, scale, child) => OverlayButton( + scale: scale, + child: child!, ), - child: Selector>( - selector: (context, v) => v.scale, - builder: (context, scale, child) => ScaleTransition( - scale: scale, - child: child, - ), - child: BlurredOval( - enabled: blurred, - child: Material( - type: MaterialType.circle, - color: Themes.overlayBackgroundColor(brightness: Theme.of(context).brightness, blurred: blurred), - child: Ink( - decoration: BoxDecoration( - border: AvesBorder.border(context), - shape: BoxShape.circle, - ), - child: Selector( - selector: (context, v) => v.visualDensity, - builder: (context, visualDensity, child) => IconButton( - key: buttonKey, - iconSize: 20, - visualDensity: visualDensity, - icon: icon, - onPressed: onPressed, - tooltip: tooltip, - ), - ), - ), - ), - ), + child: Selector( + selector: (context, v) => v.visualDensity, + builder: builder, ), ); } + + static double iconSize(VisualDensity visualDensity) => 20 + 1.5 * visualDensity.horizontal; } diff --git a/lib/widgets/common/map/buttons/panel.dart b/lib/widgets/common/map/buttons/panel.dart index 29340beb9..c05419415 100644 --- a/lib/widgets/common/map/buttons/panel.dart +++ b/lib/widgets/common/map/buttons/panel.dart @@ -1,69 +1,64 @@ +import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/view/view.dart'; +import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/map/buttons/button.dart'; import 'package:aves/widgets/common/map/buttons/coordinate_filter.dart'; import 'package:aves/widgets/common/map/compass.dart'; import 'package:aves/widgets/common/map/map_action_delegate.dart'; +import 'package:aves/widgets/common/providers/map_theme_provider.dart'; import 'package:aves_map/aves_map.dart'; import 'package:aves_model/aves_model.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:latlong2/latlong.dart'; import 'package:provider/provider.dart'; -class MapButtonPanel extends StatelessWidget { - final AvesMapController? controller; +class MapButtonPanel extends StatefulWidget { + final AvesMapController controller; final ValueNotifier boundsNotifier; final void Function(BuildContext context)? openMapPage; - final VoidCallback? resetRotation; const MapButtonPanel({ super.key, required this.controller, required this.boundsNotifier, this.openMapPage, - this.resetRotation, }); @override - Widget build(BuildContext context) { - final iconTheme = IconTheme.of(context); - final iconSize = Size.square(iconTheme.size!); + State createState() => _MapButtonPanelState(); +} - Widget? navigationButton; - switch (context.select((v) => v.navigationButton)) { - case MapNavigationButton.back: - if (!settings.useTvLayout) { - navigationButton = MapOverlayButton( - icon: const BackButtonIcon(), - onPressed: () => Navigator.maybeOf(context)?.pop(), - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - ); - } - case MapNavigationButton.close: - navigationButton = MapOverlayButton( - icon: const CloseButtonIcon(), - onPressed: SystemNavigator.pop, - tooltip: MaterialLocalizations.of(context).closeButtonTooltip, - ); - case MapNavigationButton.map: - if (openMapPage != null) { - navigationButton = MapOverlayButton( - icon: const Icon(AIcons.map), - onPressed: () => openMapPage?.call(context), - tooltip: context.l10n.openMapPageTooltip, - ); - } - case MapNavigationButton.none: - break; - } +class _MapButtonPanelState extends State { + late MapActionDelegate _actionDelegate; + + @override + void initState() { + super.initState(); + _updateDelegate(); + } + + @override + void didUpdateWidget(covariant MapButtonPanel oldWidget) { + super.didUpdateWidget(oldWidget); + _updateDelegate(); + } + + void _updateDelegate() => _actionDelegate = MapActionDelegate(widget.controller); + @override + Widget build(BuildContext context) { final showCoordinateFilter = context.select((v) => v.showCoordinateFilter); - final visualDensity = context.select((v) => v.visualDensity); - final double padding = visualDensity == VisualDensity.compact ? 4 : 8; + final visualDensity = context.select((v) => v.visualDensity); + final double padding = 8 + visualDensity.horizontal * 2; + + Widget? topLeftButton = _buildNavigationButton(context); + Widget? topRightButton = _buildTopRightButton(context); return Positioned.fill( child: TooltipTheme( @@ -85,45 +80,18 @@ class MapButtonPanel extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - if (navigationButton != null) ...[ - navigationButton, + if (topLeftButton != null) ...[ + topLeftButton, SizedBox(height: padding), ], - ValueListenableBuilder( - valueListenable: boundsNotifier, - builder: (context, bounds, child) { - final degrees = bounds.rotation; - final opacity = degrees == 0 ? .0 : 1.0; - return IgnorePointer( - ignoring: opacity == 0, - child: AnimatedOpacity( - opacity: opacity, - duration: context.select((v) => v.viewerOverlayAnimation), - child: MapOverlayButton( - icon: Transform( - origin: iconSize.center(Offset.zero), - transform: Matrix4.rotationZ(degToRadian(degrees)), - child: CustomPaint( - painter: CompassPainter( - color: iconTheme.color!, - ), - size: iconSize, - ), - ), - onPressed: () => resetRotation?.call(), - tooltip: context.l10n.mapPointNorthUpTooltip, - ), - ), - ); - }, - ), + _buildCompass(context), ], ), ), showCoordinateFilter ? Expanded( child: OverlayCoordinateFilterChip( - boundsNotifier: boundsNotifier, + boundsNotifier: widget.boundsNotifier, padding: padding, ), ) @@ -133,9 +101,12 @@ class MapButtonPanel extends StatelessWidget { // key is expected by test driver child: Column( children: [ - _buildButton(context, MapAction.selectStyle, buttonKey: const Key('map-menu-layers')), - SizedBox(height: padding), - _buildButton(context, MapAction.openMapApp), + if (topRightButton != null) ...[ + topRightButton, + SizedBox(height: padding), + ], + // key is expected by test driver + _buildActionButton(context, MapAction.selectStyle, buttonKey: const Key('map-menu-layers')), ], ), ), @@ -148,9 +119,9 @@ class MapButtonPanel extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - _buildButton(context, MapAction.zoomIn), + _buildActionButton(context, MapAction.zoomIn), SizedBox(height: padding), - _buildButton(context, MapAction.zoomOut), + _buildActionButton(context, MapAction.zoomOut), ], ), ), @@ -161,10 +132,133 @@ class MapButtonPanel extends StatelessWidget { ); } - Widget _buildButton(BuildContext context, MapAction action, {Key? buttonKey}) => MapOverlayButton( - buttonKey: buttonKey, - icon: action.getIcon(), - onPressed: () => MapActionDelegate(controller).onActionSelected(context, action), - tooltip: action.getText(context), - ); + Widget? _buildNavigationButton(BuildContext context) { + Widget? child; + switch (context.select((v) => v.navigationButton)) { + case MapNavigationButton.back: + if (!settings.useTvLayout) { + child = MapOverlayButton.icon( + icon: const BackButtonIcon(), + onPressed: () => Navigator.maybeOf(context)?.pop(), + tooltip: MaterialLocalizations.of(context).backButtonTooltip, + ); + } + case MapNavigationButton.close: + child = MapOverlayButton.icon( + icon: const CloseButtonIcon(), + onPressed: SystemNavigator.pop, + tooltip: MaterialLocalizations.of(context).closeButtonTooltip, + ); + case MapNavigationButton.map: + final _openMapPage = widget.openMapPage; + if (_openMapPage != null) { + child = MapOverlayButton.icon( + icon: const Icon(AIcons.showFullscreenCorners), + onPressed: () => _openMapPage.call(context), + tooltip: context.l10n.openMapPageTooltip, + ); + } + case MapNavigationButton.none: + break; + } + if (child != null) { + child = _heroify(context, 'top-left', child); + } + return child; + } + + Widget? _buildTopRightButton(BuildContext context) { + const heroTag = 'top-right'; + + final actions = [ + MapAction.openMapApp, + MapAction.addShortcut, + ].where((action) => _actionDelegate.isVisible(context, action)).toList(); + + Widget? child; + if (actions.length == 1) { + child = _buildActionButton(context, actions.first, heroTag: heroTag); + } else if (actions.length > 1) { + child = MapOverlayButton(builder: (context, visualDensity, child) { + final animations = context.read().accessibilityAnimations; + return PopupMenuButton( + itemBuilder: (context) => actions + .map((action) => PopupMenuItem( + value: action, + child: MenuRow( + text: action.getText(context), + icon: action.getIcon(), + ), + )) + .toList(), + onSelected: (action) async { + // wait for the popup menu to hide before proceeding with the action + await Future.delayed(animations.popUpAnimationDelay * timeDilation); + _actionDelegate.onActionSelected(context, action); + }, + iconSize: MapOverlayButton.iconSize(visualDensity), + popUpAnimationStyle: animations.popUpAnimationStyle, + ); + }); + child = _heroify(context, heroTag, child); + } + return child; + } + + Widget _buildCompass(BuildContext context) { + final iconTheme = IconTheme.of(context); + final iconSize = Size.square(iconTheme.size!); + return ValueListenableBuilder( + valueListenable: widget.boundsNotifier, + builder: (context, bounds, child) { + final degrees = bounds.rotation; + final opacity = degrees == 0 ? .0 : 1.0; + return IgnorePointer( + ignoring: opacity == 0, + child: AnimatedOpacity( + opacity: opacity, + duration: context.select((v) => v.viewerOverlayAnimation), + child: MapOverlayButton.icon( + icon: Transform( + origin: iconSize.center(Offset.zero), + transform: Matrix4.rotationZ(degToRadian(degrees)), + child: CustomPaint( + painter: CompassPainter( + color: iconTheme.color!, + ), + size: iconSize, + ), + ), + onPressed: widget.controller.resetRotation, + tooltip: context.l10n.mapPointNorthUpTooltip, + ), + ), + ); + }, + ); + } + + Widget _buildActionButton(BuildContext context, MapAction action, {Key? buttonKey, String? heroTag}) { + final child = MapOverlayButton.icon( + buttonKey: buttonKey, + icon: action.getIcon(), + onPressed: () => _actionDelegate.onActionSelected(context, action), + tooltip: action.getText(context), + ); + return _heroify(context, heroTag ?? action.name, child); + } + + Widget _heroify(BuildContext context, String? tag, Widget child) { + if (tag != null) { + final animate = context.select((v) => v.animate); + if (animate) { + return Hero( + tag: 'map-button-$tag', + flightShuttleBuilder: MapTheme.heroFlightShuttleBuilder, + child: child, + ); + } + } + return child; + } } diff --git a/lib/widgets/common/map/compass.dart b/lib/widgets/common/map/compass.dart index 1cbdd2f91..da4b33652 100644 --- a/lib/widgets/common/map/compass.dart +++ b/lib/widgets/common/map/compass.dart @@ -26,7 +26,7 @@ class CompassPainter extends CustomPainter { final fillPaint = Paint() ..style = PaintingStyle.fill - ..color = color.withOpacity(.6); + ..color = color.withAlpha((255.0 * .6).round()); final strokePaint = Paint() ..style = PaintingStyle.stroke ..color = color diff --git a/lib/widgets/common/map/decorator.dart b/lib/widgets/common/map/decorator.dart index 7969420ae..3de7a72d6 100644 --- a/lib/widgets/common/map/decorator.dart +++ b/lib/widgets/common/map/decorator.dart @@ -1,10 +1,12 @@ +import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/fx/borders.dart'; +import 'package:aves/widgets/common/providers/map_theme_provider.dart'; import 'package:aves_map/aves_map.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class MapDecorator extends StatelessWidget { - final Widget? child; + final Widget child; static const mapBorderRadius = BorderRadius.all(Radius.circular(24)); // to match button circles static const mapBackground = Color(0xFFDBD5D3); @@ -12,11 +14,45 @@ class MapDecorator extends StatelessWidget { const MapDecorator({ super.key, - this.child, + required this.child, }); @override Widget build(BuildContext context) { + Widget _child = ClipRRect( + borderRadius: mapBorderRadius, + child: Container( + color: mapBackground, + foregroundDecoration: BoxDecoration( + border: AvesBorder.border(context), + borderRadius: mapBorderRadius, + ), + child: Stack( + children: [ + const GridPaper( + color: mapLoadingGrid, + interval: 10, + divisions: 1, + subdivisions: 1, + child: CustomPaint( + size: Size.infinite, + ), + ), + child, + ], + ), + ), + ); + + final animate = context.select((v) => v.animate); + if (animate) { + _child = Hero( + tag: 'map-canvas', + flightShuttleBuilder: MapTheme.heroFlightShuttleBuilder, + child: _child, + ); + } + final interactive = context.select((v) => v.interactive); return GestureDetector( onScaleStart: interactive @@ -25,30 +61,7 @@ class MapDecorator extends StatelessWidget { // absorb scale gesture here to prevent scrolling // and triggering by mistake a move to the image page above }, - child: ClipRRect( - borderRadius: mapBorderRadius, - child: Container( - color: mapBackground, - foregroundDecoration: BoxDecoration( - border: AvesBorder.border(context), - borderRadius: mapBorderRadius, - ), - child: Stack( - children: [ - const GridPaper( - color: mapLoadingGrid, - interval: 10, - divisions: 1, - subdivisions: 1, - child: CustomPaint( - size: Size.infinite, - ), - ), - if (child != null) child!, - ], - ), - ), - ), + child: _child, ); } } diff --git a/lib/widgets/common/map/geo_map.dart b/lib/widgets/common/map/geo_map.dart index 5e21bbaeb..bb0ba1d36 100644 --- a/lib/widgets/common/map/geo_map.dart +++ b/lib/widgets/common/map/geo_map.dart @@ -32,7 +32,7 @@ import 'package:latlong2/latlong.dart'; import 'package:provider/provider.dart'; class GeoMap extends StatefulWidget { - final AvesMapController? controller; + final AvesMapController controller; final CollectionLens? collection; final List? entries; final Size availableSize; @@ -59,7 +59,7 @@ class GeoMap extends StatefulWidget { const GeoMap({ super.key, - this.controller, + required this.controller, this.collection, this.entries, required this.availableSize, @@ -123,10 +123,7 @@ class _GeoMapState extends State { void _registerWidget(GeoMap widget) { widget.collection?.addListener(_onCollectionChanged); - final controller = widget.controller; - if (controller != null) { - _subscriptions.add(controller.markerLocationChanges.listen((event) => _onCollectionChanged())); - } + _subscriptions.add(widget.controller.markerLocationChanges.listen((event) => _onCollectionChanged())); } void _unregisterWidget(GeoMap widget) { @@ -163,7 +160,6 @@ class _GeoMapState extends State { ); bool _isMarkerImageReady(MarkerKey key) => key.entry.isThumbnailReady(extent: MapThemeData.markerImageExtent); - final controller = widget.controller; Widget child = const SizedBox(); if (mapStyle != null) { switch (mapStyle) { @@ -171,7 +167,7 @@ class _GeoMapState extends State { case EntryMapStyle.googleHybrid: case EntryMapStyle.googleTerrain: child = mobileServices.buildMap( - controller: controller, + controller: widget.controller, clusterListenable: _clusterChangeNotifier, boundsNotifier: _boundsNotifier, style: mapStyle, @@ -193,7 +189,7 @@ class _GeoMapState extends State { case EntryMapStyle.osmHot: case EntryMapStyle.stamenWatercolor: child = EntryLeafletMap( - controller: controller, + controller: widget.controller, clusterListenable: _clusterChangeNotifier, boundsNotifier: _boundsNotifier, minZoom: 2, @@ -258,7 +254,10 @@ class _GeoMapState extends State { SafeArea( top: false, bottom: false, - child: Attribution(style: mapStyle), + child: Padding( + padding: context.select((v) => v.attributionPadding), + child: Attribution(style: mapStyle), + ), ), ], ); @@ -275,12 +274,10 @@ class _GeoMapState extends State { } Widget replacement = Stack( children: [ - const MapDecorator(), - MapButtonPanel( - controller: controller, - boundsNotifier: _boundsNotifier, - openMapPage: widget.openMapPage, + const MapDecorator( + child: SizedBox(), ), + _buildButtonPanel(context), ], ); if (mapHeight != null) { @@ -401,7 +398,7 @@ class _GeoMapState extends State { ) : null; }) - .whereNotNull() + .nonNulls .toList(); return Fluster>( @@ -510,15 +507,14 @@ class _GeoMapState extends State { ); } - Widget _decorateMap(BuildContext context, Widget? child) => MapDecorator(child: child); + Widget _decorateMap(BuildContext context, Widget? child) => MapDecorator(child: child!); - Widget _buildButtonPanel(VoidCallback resetRotation) { + Widget _buildButtonPanel(BuildContext context) { if (settings.useTvLayout) return const SizedBox(); return MapButtonPanel( controller: widget.controller, boundsNotifier: _boundsNotifier, openMapPage: widget.openMapPage, - resetRotation: resetRotation, ); } } diff --git a/lib/widgets/common/map/leaflet/map.dart b/lib/widgets/common/map/leaflet/map.dart index 96f775e2b..10800040f 100644 --- a/lib/widgets/common/map/leaflet/map.dart +++ b/lib/widgets/common/map/leaflet/map.dart @@ -14,13 +14,13 @@ import 'package:latlong2/latlong.dart'; import 'package:provider/provider.dart'; class EntryLeafletMap extends StatefulWidget { - final AvesMapController? controller; + final AvesMapController controller; final Listenable clusterListenable; final ValueNotifier boundsNotifier; final double minZoom, maxZoom; final EntryMapStyle style; final TransitionBuilder decoratorBuilder; - final ButtonPanelBuilder buttonPanelBuilder; + final WidgetBuilder buttonPanelBuilder; final MarkerClusterBuilder markerClusterBuilder; final MarkerWidgetBuilder markerWidgetBuilder; final ValueNotifier? dotLocationNotifier; @@ -34,7 +34,7 @@ class EntryLeafletMap extends StatefulWidget { const EntryLeafletMap({ super.key, - this.controller, + required this.controller, required this.clusterListenable, required this.boundsNotifier, this.minZoom = 0, @@ -94,11 +94,10 @@ class _EntryLeafletMapState extends State> with TickerProv void _registerWidget(EntryLeafletMap widget) { final avesMapController = widget.controller; - if (avesMapController != null) { - _subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(event.latLng))); - _subscriptions.add(avesMapController.zoomCommands.listen((event) => _zoomBy(event.delta))); - } - _subscriptions.add(_leafletMapController.mapEventStream.listen((event) => _updateVisibleRegion())); + _subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(event.latLng))); + _subscriptions.add(avesMapController.zoomCommands.listen((event) => _zoomBy(event.delta))); + _subscriptions.add(avesMapController.rotationResetCommands.listen((_) => _resetRotation())); + _subscriptions.add(_leafletMapController.mapEventStream.listen((_) => _updateVisibleRegion())); widget.clusterListenable.addListener(_updateMarkers); widget.boundsNotifier.addListener(_onBoundsChanged); } @@ -116,7 +115,7 @@ class _EntryLeafletMapState extends State> with TickerProv return Stack( children: [ widget.decoratorBuilder(context, _buildMap()), - widget.buttonPanelBuilder(_resetRotation), + widget.buttonPanelBuilder(context), ], ); } @@ -240,7 +239,7 @@ class _EntryLeafletMapState extends State> with TickerProv void _onIdle() { if (!mounted) return; - widget.controller?.notifyIdle(bounds); + widget.controller.notifyIdle(bounds); _updateMarkers(); } diff --git a/lib/widgets/common/map/leaflet/tile_layers.dart b/lib/widgets/common/map/leaflet/tile_layers.dart index 417947ff8..fd7d5085c 100644 --- a/lib/widgets/common/map/leaflet/tile_layers.dart +++ b/lib/widgets/common/map/leaflet/tile_layers.dart @@ -1,4 +1,7 @@ +import 'dart:io'; + import 'package:aves/model/device.dart'; +import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/common/map/leaflet/vector_style_reader_extra.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; @@ -115,6 +118,10 @@ class _OsmLibertyLayerState extends State { sprites: style.sprites, // `vector` is higher quality and follows map orientation, but it is slower layerMode: VectorTileLayerMode.raster, + cacheFolder: () async { + final cacheRoot = await storageService.getExternalCacheDirectory(); + return Directory(pContext.join(cacheRoot, 'map_vector_tiles')); + }, ); }, ); diff --git a/lib/widgets/common/map/map_action_delegate.dart b/lib/widgets/common/map/map_action_delegate.dart index 5c3728e05..b2c1499a6 100644 --- a/lib/widgets/common/map/map_action_delegate.dart +++ b/lib/widgets/common/map/map_action_delegate.dart @@ -1,37 +1,94 @@ +import 'package:aves/geo/uri.dart'; +import 'package:aves/model/device.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/settings/settings.dart'; +import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/view/view.dart'; +import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/dialogs/add_shortcut_dialog.dart'; import 'package:aves/widgets/dialogs/selection_dialogs/common.dart'; import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart'; +import 'package:aves/widgets/map/map_page.dart'; import 'package:aves_map/aves_map.dart'; import 'package:aves_model/aves_model.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; -class MapActionDelegate { - final AvesMapController? controller; +class MapActionDelegate with FeedbackMixin { + final AvesMapController controller; const MapActionDelegate(this.controller); + bool isVisible(BuildContext context, MapAction action) { + switch (action) { + case MapAction.selectStyle: + case MapAction.openMapApp: + case MapAction.zoomIn: + case MapAction.zoomOut: + return true; + case MapAction.addShortcut: + return device.canPinShortcut && context.currentRouteName == MapPage.routeName; + } + } + void onActionSelected(BuildContext context, MapAction action) { switch (action) { case MapAction.selectStyle: - showSelectionDialog( - context: context, - builder: (context) => AvesSingleSelectionDialog( - initialValue: settings.mapStyle, - options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))), - title: context.l10n.mapStyleDialogTitle, - ), - onSelection: (v) => settings.mapStyle = v, - ); + _selectStyle(context); case MapAction.openMapApp: OpenMapAppNotification().dispatch(context); case MapAction.zoomIn: - controller?.zoomBy(1); + controller.zoomBy(1); case MapAction.zoomOut: - controller?.zoomBy(-1); + controller.zoomBy(-1); + case MapAction.addShortcut: + _addShortcut(context); + } + } + + Future _selectStyle(BuildContext context) => showSelectionDialog( + context: context, + builder: (context) => AvesSingleSelectionDialog( + initialValue: settings.mapStyle, + options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))), + title: context.l10n.mapStyleDialogTitle, + ), + onSelection: (v) => settings.mapStyle = v, + ); + + Future _addShortcut(BuildContext context) async { + final idleBounds = controller.idleBounds; + if (idleBounds == null) { + showFeedback(context, FeedbackType.warn, context.l10n.genericFailureFeedback); + return; + } + + final collection = context.read(); + final result = await showDialog<(AvesEntry?, String)>( + context: context, + builder: (context) => AddShortcutDialog( + defaultName: '', + collection: collection, + ), + routeSettings: const RouteSettings(name: AddShortcutDialog.routeName), + ); + if (result == null) return; + + final (coverEntry, name) = result; + if (name.isEmpty) return; + + final geoUri = toGeoUri(idleBounds.projectedCenter, zoom: idleBounds.zoom); + await appService.pinToHomeScreen( + name, + coverEntry, + route: MapPage.routeName, + filters: collection.filters, + geoUri: geoUri, + ); + if (!device.showPinShortcutFeedback) { + showFeedback(context, FeedbackType.info, context.l10n.genericSuccessFeedback); } } } diff --git a/lib/widgets/common/providers/map_theme_provider.dart b/lib/widgets/common/providers/map_theme_provider.dart index 96a5d785a..919837d66 100644 --- a/lib/widgets/common/providers/map_theme_provider.dart +++ b/lib/widgets/common/providers/map_theme_provider.dart @@ -7,8 +7,9 @@ class MapTheme extends StatelessWidget { final bool interactive, showCoordinateFilter; final MapNavigationButton navigationButton; final Animation scale; - final VisualDensity? visualDensity; + final VisualDensity visualDensity; final double? mapHeight; + final EdgeInsets attributionPadding; final Widget child; const MapTheme({ @@ -17,8 +18,9 @@ class MapTheme extends StatelessWidget { required this.showCoordinateFilter, required this.navigationButton, this.scale = kAlwaysCompleteAnimation, - this.visualDensity, + this.visualDensity = VisualDensity.standard, this.mapHeight, + this.attributionPadding = EdgeInsets.zero, required this.child, }); @@ -33,9 +35,56 @@ class MapTheme extends StatelessWidget { scale: scale, visualDensity: visualDensity, mapHeight: mapHeight, + attributionPadding: attributionPadding, ); }, child: child, ); } + + static Widget heroFlightShuttleBuilder( + BuildContext flightContext, + Animation animation, + HeroFlightDirection flightDirection, + BuildContext fromHeroContext, + BuildContext toHeroContext, + ) { + final pushing = flightDirection == HeroFlightDirection.push; + final fromMediaQuery = MediaQuery.of(fromHeroContext); + final toMediaQuery = MediaQuery.of(toHeroContext); + final fromRenderBox = fromHeroContext.findRenderObject()! as RenderBox; + final toRenderBox = toHeroContext.findRenderObject()! as RenderBox; + final fromTheme = fromHeroContext.read(); + final toTheme = toHeroContext.read(); + + return DefaultTextStyle( + style: DefaultTextStyle.of(toHeroContext).style, + child: AnimatedBuilder( + animation: animation, + builder: (context, child) { + final t = pushing ? animation.value : 1 - animation.value; + return MapTheme( + interactive: false, + showCoordinateFilter: false, + navigationButton: toTheme.navigationButton, + visualDensity: VisualDensity.lerp(fromTheme.visualDensity, toTheme.visualDensity, t), + child: MediaQuery( + data: toMediaQuery.copyWith( + padding: EdgeInsets.lerp(fromMediaQuery.padding, toMediaQuery.padding, t), + viewPadding: EdgeInsets.lerp(fromMediaQuery.viewPadding, toMediaQuery.viewPadding, t), + ), + child: Align( + alignment: Alignment.topCenter, + child: SizedBox.fromSize( + size: Size.lerp(fromRenderBox.size, toRenderBox.size, t), + child: child, + ), + ), + ), + ); + }, + child: toHeroContext.widget, + ), + ); + } } diff --git a/lib/widgets/common/search/delegate.dart b/lib/widgets/common/search/delegate.dart index 14d6a5a81..da4176455 100644 --- a/lib/widgets/common/search/delegate.dart +++ b/lib/widgets/common/search/delegate.dart @@ -5,6 +5,7 @@ import 'package:aves/widgets/common/search/route.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:leak_tracker/leak_tracker.dart'; import 'package:provider/provider.dart'; abstract class AvesSearchDelegate extends SearchDelegate { @@ -21,7 +22,7 @@ abstract class AvesSearchDelegate extends SearchDelegate { required super.searchFieldStyle, }) { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$AvesSearchDelegate', object: this, @@ -137,7 +138,7 @@ abstract class AvesSearchDelegate extends SearchDelegate { @mustCallSuper void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } currentBodyNotifier.dispose(); queryTextController.dispose(); diff --git a/lib/widgets/common/thumbnail/image.dart b/lib/widgets/common/thumbnail/image.dart index 87154a755..904d779f1 100644 --- a/lib/widgets/common/thumbnail/image.dart +++ b/lib/widgets/common/thumbnail/image.dart @@ -269,7 +269,10 @@ class _ThumbnailImageState extends State { final backgroundColor = background.isColor ? background.color : null; image = Hero( tag: heroTag, - flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) { + flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) { + // as of Flutter v3.27.0-0.1.pre, the flight `animation` is incorrect when diverting a pop: + // - diverting a push (t = 0 -> 1) with a pop (t = 1 -> 0) works as expected (t = 0 -> [0,1] -> 0) + // - diverting a pop (t = 1 -> 0) with a push (t = 0 -> 1) finishes the pop (t = 1 -> [0,1] -> 0) instead of diverting (t = 1 -> [0,1] -> 1) Widget child = TransitionImage( image: entry.bestCachedThumbnail, animation: animation, @@ -304,11 +307,11 @@ class _ThumbnailImageState extends State { if (animate && heroTag != null) { child = Hero( tag: heroTag, - flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) { + flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) { return MediaQueryDataProvider( child: DefaultTextStyle( - style: DefaultTextStyle.of(toHero).style, - child: toHero.widget, + style: DefaultTextStyle.of(toHeroContext).style, + child: toHeroContext.widget, ), ); }, diff --git a/lib/widgets/common/thumbnail/overlay.dart b/lib/widgets/common/thumbnail/overlay.dart index 28050d13f..44ffe840f 100644 --- a/lib/widgets/common/thumbnail/overlay.dart +++ b/lib/widgets/common/thumbnail/overlay.dart @@ -108,7 +108,7 @@ class ThumbnailZoomOverlay extends StatelessWidget { width: interactiveDimension, height: interactiveDimension, child: Icon( - AIcons.showFullscreen, + AIcons.showFullscreenArrows, size: context.select((t) => t.iconSize), color: Colors.white70, ), diff --git a/lib/widgets/common/tile_extent_controller.dart b/lib/widgets/common/tile_extent_controller.dart index ff4b60aab..f328f12a4 100644 --- a/lib/widgets/common/tile_extent_controller.dart +++ b/lib/widgets/common/tile_extent_controller.dart @@ -5,6 +5,7 @@ import 'package:aves/model/settings/settings.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/widgets.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class TileExtentController { final String settingsRouteKey; @@ -28,7 +29,7 @@ class TileExtentController { required this.horizontalPadding, }) { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$TileExtentController', object: this, @@ -42,7 +43,7 @@ class TileExtentController { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _subscriptions ..forEach((sub) => sub.cancel()) diff --git a/lib/widgets/debug/general.dart b/lib/widgets/debug/general.dart index 87ed6aee4..704dd6be5 100644 --- a/lib/widgets/debug/general.dart +++ b/lib/widgets/debug/general.dart @@ -47,12 +47,10 @@ class _DebugGeneralSectionState extends State with Automati SwitchListTile( value: _taskQueueOverlayEntry != null, onChanged: (v) { - final overlayEntry = _taskQueueOverlayEntry; + _taskQueueOverlayEntry + ?..remove() + ..dispose(); _taskQueueOverlayEntry = null; - if (overlayEntry != null) { - overlayEntry.remove(); - overlayEntry.dispose(); - } if (v) { _taskQueueOverlayEntry = OverlayEntry( builder: (context) => const DebugTaskQueueOverlay(), @@ -63,6 +61,20 @@ class _DebugGeneralSectionState extends State with Automati }, title: const Text('Show tasks overlay'), ), + ElevatedButton( + onPressed: () => LeakTracking.collectLeaks().then((leaks) { + const config = LeakDiagnosticConfig( + collectRetainingPathForNotGCed: true, + collectStackTraceOnStart: true, + collectStackTraceOnDisposal: true, + ); + LeakTracking.phase = const PhaseSettings( + leakDiagnosticConfig: config, + ); + debugPrint('Setup leak tracking phase with config=$config'); + }), + child: const Text('Setup leak tracking phase'), + ), ElevatedButton( onPressed: () => LeakTracking.collectLeaks().then((leaks) { leaks.byType.forEach((type, reports) { diff --git a/lib/widgets/dialogs/add_shortcut_dialog.dart b/lib/widgets/dialogs/add_shortcut_dialog.dart index 461e79a18..0154b4409 100644 --- a/lib/widgets/dialogs/add_shortcut_dialog.dart +++ b/lib/widgets/dialogs/add_shortcut_dialog.dart @@ -39,7 +39,7 @@ class _AddShortcutDialogState extends State { if (_collection != null) { final entries = _collection.sortedEntries; if (entries.isNotEmpty) { - final coverEntries = _collection.filters.map((filter) => covers.of(filter)?.$1).whereNotNull().map((id) => entries.firstWhereOrNull((entry) => entry.id == id)).whereNotNull(); + final coverEntries = _collection.filters.map((filter) => covers.of(filter)?.$1).nonNulls.map((id) => entries.firstWhereOrNull((entry) => entry.id == id)).nonNulls; _coverEntry = coverEntries.firstOrNull ?? entries.first; } } diff --git a/lib/widgets/dialogs/convert_entry_dialog.dart b/lib/widgets/dialogs/convert_entry_dialog.dart index 885116cd3..e24d5a05d 100644 --- a/lib/widgets/dialogs/convert_entry_dialog.dart +++ b/lib/widgets/dialogs/convert_entry_dialog.dart @@ -188,7 +188,7 @@ class _ConvertEntryDialogState extends State { // used by the drop down to match input decoration final textFieldDecorationBorder = Border( bottom: BorderSide( - color: colorScheme.onSurface.withOpacity(0.38), + color: colorScheme.onSurface.withAlpha((255.0 * .38).round()), width: 1.0, ), ); @@ -309,7 +309,7 @@ class _ConvertEntryDialogState extends State { style: trailingStyle.copyWith( shadows: [ Shadow( - color: trailingChangeShadowColor.withOpacity(0), + color: trailingChangeShadowColor.withAlpha(0), blurRadius: 0, ) ], diff --git a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart b/lib/widgets/dialogs/filter_editors/create_album_dialog.dart index 9943eab7c..9af1c8ef5 100644 --- a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/create_album_dialog.dart @@ -147,23 +147,43 @@ class _CreateAlbumDialogState extends State { ); } - String _buildAlbumPath(String name) { + String _sanitize(String input) => input.trim(); + + String? _buildAlbumPath(String name) { final selectedVolume = _selectedVolume; - if (selectedVolume == null || name.isEmpty) return ''; + if (selectedVolume == null || name.isEmpty) return null; return pContext.join(selectedVolume.path, 'Pictures', name); } Future _validate() async { - final newName = _nameController.text; + final newName = _sanitize(_nameController.text); final path = _buildAlbumPath(newName); - final exists = newName.isNotEmpty && await Directory(path).exists(); + // this check ignores case + final exists = path != null && await Directory(path).exists(); _existsNotifier.value = exists; - _isValidNotifier.value = newName.isNotEmpty && !exists; + _isValidNotifier.value = path != null && newName.isNotEmpty; } - void _submit(BuildContext context) { - if (_isValidNotifier.value) { - Navigator.maybeOf(context)?.pop(_buildAlbumPath(_nameController.text)); + Future _submit(BuildContext context) async { + if (!_isValidNotifier.value) return; + + final newName = _sanitize(_nameController.text); + final albumPath = _buildAlbumPath(newName); + final volumePath = _selectedVolume?.path; + if (albumPath == null || volumePath == null) return; + + // uses resolved directory name case if it exists + var resolvedPath = volumePath; + final relativePathSegments = pContext.split(pContext.relative(albumPath, from: volumePath)); + for (final targetSegment in relativePathSegments) { + String? resolvedSegment; + final directory = Directory(resolvedPath); + if (await directory.exists()) { + final lowerTargetSegment = targetSegment.toLowerCase(); + resolvedSegment = directory.listSync().map((v) => pContext.basename(v.path)).firstWhereOrNull((v) => v.toLowerCase() == lowerTargetSegment); + } + resolvedPath = pContext.join(resolvedPath, resolvedSegment ?? targetSegment); } + Navigator.maybeOf(context)?.pop(resolvedPath); } } diff --git a/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart b/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart index 4c2a88fae..a3f363bf3 100644 --- a/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart @@ -134,7 +134,9 @@ class _EditVaultDialogState extends State with FeedbackMixin, V context: context, message: l10n.settingsDisablingBinWarningDialogMessage, confirmationButtonLabel: l10n.applyButtonLabel, - )) return; + )) { + return; + } } } } diff --git a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart index 75f001ad9..40b1682df 100644 --- a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart @@ -35,11 +35,11 @@ Future pickAlbum({ required MoveType? moveType, }) async { final source = context.read(); - if (source.scope != SourceScope.full) { + if (source.targetScope != CollectionSource.fullScope) { await reportService.log('Complete source initialization to pick album'); // source may not be fully initialized in view mode source.canAnalyze = true; - await source.init(); + await source.init(scope: CollectionSource.fullScope); } final filter = await Navigator.maybeOf(context)?.push( MaterialPageRoute( @@ -263,7 +263,9 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { type: ConfirmationDialog.createVault, message: l10n.newVaultWarningDialogMessage, confirmationButtonLabel: l10n.continueButtonLabel, - )) return; + )) { + return; + } final details = await showDialog( context: context, diff --git a/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart index 3d1c821b5..8dbbc5f1c 100644 --- a/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart @@ -133,6 +133,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin interactive: true, showCoordinateFilter: false, navigationButton: MapNavigationButton.back, + attributionPadding: const EdgeInsets.symmetric(horizontal: 8), child: GeoMap( controller: _mapController, collection: openingCollection, diff --git a/lib/widgets/editor/transform/controller.dart b/lib/widgets/editor/transform/controller.dart index 0bd679813..f2853fc21 100644 --- a/lib/widgets/editor/transform/controller.dart +++ b/lib/widgets/editor/transform/controller.dart @@ -5,6 +5,7 @@ import 'package:aves/widgets/editor/transform/crop_region.dart'; import 'package:aves/widgets/editor/transform/transformation.dart'; import 'package:aves_model/aves_model.dart'; import 'package:flutter/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class TransformController { ValueNotifier aspectRatioNotifier = ValueNotifier(CropAspectRatio.free); @@ -34,7 +35,7 @@ class TransformController { TransformController(this.displaySize) { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$TransformController', object: this, @@ -46,7 +47,7 @@ class TransformController { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } aspectRatioNotifier.dispose(); } diff --git a/lib/widgets/editor/transform/painter.dart b/lib/widgets/editor/transform/painter.dart index 90f82433c..770c1aa8c 100644 --- a/lib/widgets/editor/transform/painter.dart +++ b/lib/widgets/editor/transform/painter.dart @@ -19,8 +19,8 @@ class CropperPainter extends CustomPainter { static const double gridWidth = 1; static const cornerColor = Colors.white; - static final borderColor = Colors.white.withOpacity(.5); - static final gridColor = Colors.white.withOpacity(.5); + static final borderColor = Colors.white.withAlpha((255.0 * .5).round()); + static final gridColor = Colors.white.withAlpha((255.0 * .5).round()); @override void paint(Canvas canvas, Size size) { @@ -32,7 +32,7 @@ class CropperPainter extends CustomPainter { final gridPaint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = gridWidth - ..color = gridColor.withOpacity(gridColor.opacity * gridOpacity); + ..color = gridColor.withAlpha((255.0 * gridColor.opacity * gridOpacity).round()); final xLeft = rect.left; final yTop = rect.top; @@ -118,7 +118,7 @@ class ScrimPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final scrimPaint = Paint() ..style = PaintingStyle.fill - ..color = scrimColor.withOpacity(opacity); + ..color = scrimColor.withAlpha((255.0 * opacity).round()); final outside = Path() ..addRect(Rect.fromLTWH(0, 0, size.width, size.height).inflate(.5)) diff --git a/lib/widgets/explorer/explorer_action_delegate.dart b/lib/widgets/explorer/explorer_action_delegate.dart index eb98352e5..f97a8438f 100644 --- a/lib/widgets/explorer/explorer_action_delegate.dart +++ b/lib/widgets/explorer/explorer_action_delegate.dart @@ -9,6 +9,7 @@ import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/dialogs/add_shortcut_dialog.dart'; +import 'package:aves/widgets/explorer/explorer_page.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip.dart'; import 'package:aves/widgets/stats/stats_page.dart'; import 'package:aves_model/aves_model.dart'; @@ -48,7 +49,7 @@ class ExplorerActionDelegate with FeedbackMixin { } void onActionSelected(BuildContext context, ExplorerAction action) { - reportService.log('$action'); + reportService.log('$runtimeType handles $action'); switch (action) { case ExplorerAction.addShortcut: _addShortcut(context); @@ -84,7 +85,7 @@ class ExplorerActionDelegate with FeedbackMixin { final (coverEntry, name) = result; if (name.isEmpty) return; - await appService.pinToHomeScreen(name, coverEntry, explorerPath: filter.path); + await appService.pinToHomeScreen(name, coverEntry, route: ExplorerPage.routeName, path: filter.path); if (!device.showPinShortcutFeedback) { showFeedback(context, FeedbackType.info, context.l10n.genericSuccessFeedback); } diff --git a/lib/widgets/filter_grids/common/action_delegates/album_set.dart b/lib/widgets/filter_grids/common/action_delegates/album_set.dart index 12b6a72b0..aff3e0599 100644 --- a/lib/widgets/filter_grids/common/action_delegates/album_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/album_set.dart @@ -139,7 +139,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with @override void onActionSelected(BuildContext context, ChipSetAction action) { - reportService.log('$action'); + reportService.log('$runtimeType handles $action'); switch (action) { // general case ChipSetAction.createAlbum: @@ -206,7 +206,9 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with type: ConfirmationDialog.createVault, message: l10n.newVaultWarningDialogMessage, confirmationButtonLabel: l10n.continueButtonLabel, - )) return; + )) { + return; + } final details = await showDialog( context: context, @@ -282,7 +284,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with final source = context.read(); final todoEntries = source.visibleEntries.where((entry) => filters.any((f) => f.test(entry))).toSet(); final todoAlbums = filters.map((v) => v.album).toSet(); - final filledAlbums = todoEntries.map((e) => e.directory).whereNotNull().toSet(); + final filledAlbums = todoEntries.map((e) => e.directory).nonNulls.toSet(); final emptyAlbums = todoAlbums.whereNot(filledAlbums.contains).toSet(); if (enableBin && filledAlbums.isNotEmpty) { @@ -338,7 +340,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with if (todoEntries.isEmpty) return; final source = context.read(); - final filledAlbums = todoEntries.map((e) => e.directory).whereNotNull().toSet(); + final filledAlbums = todoEntries.map((e) => e.directory).nonNulls.toSet(); final l10n = context.l10n; final messenger = ScaffoldMessenger.of(context); diff --git a/lib/widgets/filter_grids/common/action_delegates/chip.dart b/lib/widgets/filter_grids/common/action_delegates/chip.dart index c0d8c4797..86b23b105 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip.dart @@ -42,7 +42,7 @@ class ChipActionDelegate with FeedbackMixin, VaultAwareMixin { } void onActionSelected(BuildContext context, CollectionFilter filter, ChipAction action) { - reportService.log('$action'); + reportService.log('$runtimeType handles $action'); switch (action) { case ChipAction.goToAlbumPage: _goTo(context, filter, AlbumListPage.routeName, (context) => const AlbumListPage()); diff --git a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart index 4fa0702a4..4d6bc1806 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart @@ -164,7 +164,7 @@ abstract class ChipSetActionDelegate with FeedbackMi } void onActionSelected(BuildContext context, ChipSetAction action) { - reportService.log('$action'); + reportService.log('$runtimeType handles $action'); switch (action) { // general case ChipSetAction.configureView: diff --git a/lib/widgets/filter_grids/common/action_delegates/country_set.dart b/lib/widgets/filter_grids/common/action_delegates/country_set.dart index 6e72aecd3..418fe1640 100644 --- a/lib/widgets/filter_grids/common/action_delegates/country_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/country_set.dart @@ -8,7 +8,6 @@ import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart' import 'package:aves/widgets/filter_grids/countries_page.dart'; import 'package:aves/widgets/filter_grids/states_page.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; class CountryChipSetActionDelegate extends ChipSetActionDelegate { @@ -81,7 +80,7 @@ class CountryChipSetActionDelegate extends ChipSetActionDelegate @override void onActionSelected(BuildContext context, ChipSetAction action) { - reportService.log('$action'); + reportService.log('$runtimeType handles $action'); switch (action) { // single/multiple filters case ChipSetAction.showCountryStates: @@ -95,7 +94,7 @@ class CountryChipSetActionDelegate extends ChipSetActionDelegate void _showStates(BuildContext context) { final filters = getSelectedFilters(context); - final countryCodes = filters.map((v) => v.code).where(GeoStates.stateCountryCodes.contains).whereNotNull().toSet(); + final countryCodes = filters.map((v) => v.code).where(GeoStates.stateCountryCodes.contains).nonNulls.toSet(); Navigator.maybeOf(context)?.push( MaterialPageRoute( settings: const RouteSettings(name: StateListPage.routeName), diff --git a/lib/widgets/filter_grids/common/action_delegates/tag_set.dart b/lib/widgets/filter_grids/common/action_delegates/tag_set.dart index 004c2f44c..251c855a4 100644 --- a/lib/widgets/filter_grids/common/action_delegates/tag_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/tag_set.dart @@ -65,7 +65,7 @@ class TagChipSetActionDelegate extends ChipSetActionDelegate { @override void onActionSelected(BuildContext context, ChipSetAction action) { - reportService.log('$action'); + reportService.log('$runtimeType handles $action'); switch (action) { // single/multiple filters case ChipSetAction.delete: diff --git a/lib/widgets/filter_grids/common/app_bar.dart b/lib/widgets/filter_grids/common/app_bar.dart index 9b3388588..d75dd86f6 100644 --- a/lib/widgets/filter_grids/common/app_bar.dart +++ b/lib/widgets/filter_grids/common/app_bar.dart @@ -22,7 +22,6 @@ import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart' import 'package:aves/widgets/filter_grids/common/query_bar.dart'; import 'package:aves/widgets/search/search_delegate.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:provider/provider.dart'; @@ -301,7 +300,7 @@ class _FilterGridAppBarState _buildButtonIcon( diff --git a/lib/widgets/filter_grids/common/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart index 9cc9fed3b..9323ceb05 100644 --- a/lib/widgets/filter_grids/common/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -592,7 +592,7 @@ class _FilterScaler extends StatelessWidget { ), mosaicItemBuilder: (index, targetExtent) => DecoratedBox( decoration: BoxDecoration( - color: ThumbnailImage.computeLoadingBackgroundColor(index * 10, brightness).withOpacity(.9), + color: ThumbnailImage.computeLoadingBackgroundColor(index * 10, brightness).withAlpha((255.0 * .9).round()), border: Border.all( color: context.read().neutral, width: AvesFilterChip.outlineWidth, diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart index 8bbe51b26..ec6478290 100644 --- a/lib/widgets/home_page.dart +++ b/lib/widgets/home_page.dart @@ -114,7 +114,7 @@ class _HomePageState extends State { unawaited(appInventory.initAppNames()); } - if (intentData.values.whereNotNull().isNotEmpty) { + if (intentData.values.nonNulls.isNotEmpty) { await reportService.log('Intent data=$intentData'); var intentUri = intentData[IntentDataKeys.uri] as String?; final intentMimeType = intentData[IntentDataKeys.mimeType] as String?; @@ -186,7 +186,7 @@ class _HomePageState extends State { } if (_initialFilters == null) { final extraFilters = (intentData[IntentDataKeys.filters] as List?)?.cast(); - _initialFilters = extraFilters?.map(CollectionFilter.fromJson).whereNotNull().toSet(); + _initialFilters = extraFilters?.map(CollectionFilter.fromJson).nonNulls.toSet(); } _initialExplorerPath = intentData[IntentDataKeys.explorerPath] as String?; @@ -222,16 +222,17 @@ class _HomePageState extends State { unawaited(GlobalSearch.registerCallback()); unawaited(AnalysisService.registerCallback()); final source = context.read(); - if (source.scope != SourceScope.full) { - await reportService.log('Initialize source (init state=${source.scope.name}) to start app with mode=$appMode'); + if (source.loadedScope != CollectionSource.fullScope) { + await reportService.log('Initialize source to start app with mode=$appMode, loaded scope=${source.loadedScope}'); final loadTopEntriesFirst = settings.homePage == HomePageSetting.collection && settings.homeCustomCollection.isEmpty; - await source.init(loadTopEntriesFirst: loadTopEntriesFirst); + source.canAnalyze = true; + await source.init(scope: CollectionSource.fullScope, loadTopEntriesFirst: loadTopEntriesFirst); } case AppMode.screenSaver: await reportService.log('Initialize source to start screen saver'); final source = context.read(); source.canAnalyze = false; - await source.init(); + await source.init(scope: settings.screenSaverCollectionFilters); case AppMode.view: if (_isViewerSourceable(_viewerEntry) && _secureUris == null) { final directory = _viewerEntry?.directory; @@ -240,7 +241,7 @@ class _HomePageState extends State { await reportService.log('Initialize source to view item in directory $directory'); final source = context.read(); source.canAnalyze = false; - await source.init(albumFilter: AlbumFilter(directory, null)); + await source.init(scope: {AlbumFilter(directory, null)}); } } else { await _initViewerEssentials(); @@ -305,38 +306,38 @@ class _HomePageState extends State { CollectionLens? collection; final source = context.read(); - if (source.scope != SourceScope.none) { - final album = viewerEntry.directory; - if (album != null) { - // wait for collection to pass the `loading` state - final completer = Completer(); - void _onSourceStateChanged() { - if (source.state != SourceState.loading) { - source.stateNotifier.removeListener(_onSourceStateChanged); - completer.complete(); - } + final album = viewerEntry.directory; + if (album != null) { + // wait for collection to pass the `loading` state + final loadingCompleter = Completer(); + final stateNotifier = source.stateNotifier; + void _onSourceStateChanged() { + if (stateNotifier.value != SourceState.loading) { + stateNotifier.removeListener(_onSourceStateChanged); + loadingCompleter.complete(); } + } - source.stateNotifier.addListener(_onSourceStateChanged); - await completer.future; + stateNotifier.addListener(_onSourceStateChanged); + _onSourceStateChanged(); + await loadingCompleter.future; - collection = CollectionLens( - source: source, - filters: {AlbumFilter(album, source.getAlbumDisplayName(context, album))}, - listenToSource: false, - // if we group bursts, opening a burst sub-entry should: - // - identify and select the containing main entry, - // - select the sub-entry in the Viewer page. - stackBursts: false, - ); - final viewerEntryPath = viewerEntry.path; - final collectionEntry = collection.sortedEntries.firstWhereOrNull((entry) => entry.path == viewerEntryPath); - if (collectionEntry != null) { - viewerEntry = collectionEntry; - } else { - debugPrint('collection does not contain viewerEntry=$viewerEntry'); - collection = null; - } + collection = CollectionLens( + source: source, + filters: {AlbumFilter(album, source.getAlbumDisplayName(context, album))}, + listenToSource: false, + // if we group bursts, opening a burst sub-entry should: + // - identify and select the containing main entry, + // - select the sub-entry in the Viewer page. + stackBursts: false, + ); + final viewerEntryPath = viewerEntry.path; + final collectionEntry = collection.sortedEntries.firstWhereOrNull((entry) => entry.path == viewerEntryPath); + if (collectionEntry != null) { + viewerEntry = collectionEntry; + } else { + debugPrint('collection does not contain viewerEntry=$viewerEntry'); + collection = null; } } @@ -377,7 +378,10 @@ class _HomePageState extends State { return buildRoute((context) { final mapCollection = CollectionLens( source: source, - filters: {LocationFilter.located}, + filters: { + LocationFilter.located, + if (filters != null) ...filters, + }, ); return MapPage( collection: mapCollection, diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index 382314cac..0639e3739 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -5,8 +5,9 @@ import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/filters/coordinate.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/media/geotiff.dart'; +import 'package:aves/model/filters/location.dart'; import 'package:aves/model/highlight.dart'; +import 'package:aves/model/media/geotiff.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/map_style.dart'; import 'package:aves/model/settings/settings.dart'; @@ -62,10 +63,15 @@ class MapPage extends StatelessWidget { @override Widget build(BuildContext context) { - // do not rely on the `HighlightInfoProvider` app level - // as the map can be stacked on top of other pages - // that catch highlight events and will not let it bubble up - return HighlightInfoProvider( + return MultiProvider( + providers: [ + // do not rely on the `HighlightInfoProvider` app level + // as the map can be stacked on top of other pages + // that catch highlight events and will not let it bubble up + HighlightInfoProvider(), + // opening collection can be used by map actions + ChangeNotifierProvider.value(value: collection), + ], child: AvesScaffold( body: SafeArea( left: false, @@ -153,6 +159,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin _selectedIndexNotifier.addListener(_onThumbnailIndexChanged); Future.delayed(ADurations.pageTransitionLoose * timeDilation + const Duration(seconds: 1), () { + if (!mounted) return; final regionEntries = regionCollection?.sortedEntries ?? []; final initialEntry = widget.initialEntry ?? regionEntries.firstOrNull; if (initialEntry != null) { @@ -268,6 +275,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin showCoordinateFilter: true, navigationButton: canPop ? MapNavigationButton.back : MapNavigationButton.close, scale: _overlayScale, + attributionPadding: const EdgeInsets.symmetric(horizontal: 8), child: GeoMap( // key is expected by test driver key: const Key('map_view'), @@ -440,10 +448,16 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin Navigator.maybeOf(context)?.pushAndRemoveUntil( MaterialPageRoute( settings: const RouteSettings(name: CollectionPage.routeName), - builder: (context) => CollectionPage( - source: openingCollection.source, - filters: {...openingCollection.filters, filter}, - ), + builder: (context) { + final filters = {...openingCollection.filters, filter}; + if (filter is CoordinateFilter) { + filters.removeWhere((v) => (v is CoordinateFilter && v != filter) || v == LocationFilter.located); + } + return CollectionPage( + source: openingCollection.source, + filters: filters, + ); + }, ), (route) => false, ); diff --git a/lib/widgets/navigation/drawer/app_drawer.dart b/lib/widgets/navigation/drawer/app_drawer.dart index d6197b44e..565694312 100644 --- a/lib/widgets/navigation/drawer/app_drawer.dart +++ b/lib/widgets/navigation/drawer/app_drawer.dart @@ -175,8 +175,8 @@ class _AppDrawerState extends State with WidgetsBindingObserver { data: OutlinedButtonThemeData( style: ButtonStyle( foregroundColor: WidgetStateProperty.all(onPrimary), - overlayColor: WidgetStateProperty.all(onPrimary.withOpacity(.12)), - side: WidgetStateProperty.all(BorderSide(width: 1, color: onPrimary.withOpacity(.24))), + overlayColor: WidgetStateProperty.all(onPrimary.withAlpha((255.0 * .12).round())), + side: WidgetStateProperty.all(BorderSide(width: 1, color: onPrimary.withAlpha((255.0 * .24).round()))), ), ), child: Column( diff --git a/lib/widgets/navigation/drawer/collection_nav_tile.dart b/lib/widgets/navigation/drawer/collection_nav_tile.dart index 18f51d3b7..f29f6b2c4 100644 --- a/lib/widgets/navigation/drawer/collection_nav_tile.dart +++ b/lib/widgets/navigation/drawer/collection_nav_tile.dart @@ -36,7 +36,7 @@ class CollectionNavTile extends StatelessWidget { trailing: trailing != null ? Builder( builder: (context) { - final trailingColor = IconTheme.of(context).color!.withOpacity(.6); + final trailingColor = IconTheme.of(context).color!.withAlpha((255.0 * .6).round()); return IconTheme.merge( data: IconThemeData(color: trailingColor), child: DefaultTextStyle.merge( diff --git a/lib/widgets/navigation/drawer/page_nav_tile.dart b/lib/widgets/navigation/drawer/page_nav_tile.dart index a6ff3f63b..3250d76eb 100644 --- a/lib/widgets/navigation/drawer/page_nav_tile.dart +++ b/lib/widgets/navigation/drawer/page_nav_tile.dart @@ -43,7 +43,7 @@ class PageNavTile extends StatelessWidget { ? Builder( builder: (context) => DefaultTextStyle.merge( style: TextStyle( - color: IconTheme.of(context).color!.withOpacity(.6), + color: IconTheme.of(context).color!.withAlpha((255.0 * .6).round()), ), child: trailing!, ), diff --git a/lib/widgets/navigation/nav_bar/nav_bar.dart b/lib/widgets/navigation/nav_bar/nav_bar.dart index 0b7891c72..7e42b2fbe 100644 --- a/lib/widgets/navigation/nav_bar/nav_bar.dart +++ b/lib/widgets/navigation/nav_bar/nav_bar.dart @@ -104,11 +104,11 @@ class _AppBottomNavBarState extends State { if (animate) { child = Hero( tag: 'nav-bar', - flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) { + flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) { return MediaQuery.removeViewInsets( context: context, removeBottom: true, - child: toHero.widget, + child: toHeroContext.widget, ); }, child: child, diff --git a/lib/widgets/search/search_delegate.dart b/lib/widgets/search/search_delegate.dart index 60ab56c67..3c92ebf23 100644 --- a/lib/widgets/search/search_delegate.dart +++ b/lib/widgets/search/search_delegate.dart @@ -29,7 +29,6 @@ import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/search/delegate.dart'; import 'package:aves/widgets/common/search/page.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -124,7 +123,7 @@ class CollectionSearchDelegate extends AvesSearchDelegate with FeedbackMixin, Va filters: [ queryFilter, ...visibleTypeFilters, - ].whereNotNull().where((f) => containQuery(f.getLabel(context))).toList(), + ].nonNulls.where((f) => containQuery(f.getLabel(context))).toList(), // usually perform hero animation only on tapped chips, // but we also need to animate the query chip when it is selected by submitting the search query heroTypeBuilder: (filter) => filter == queryFilter ? HeroType.always : HeroType.onTap, diff --git a/lib/widgets/settings/common/quick_actions/action_panel.dart b/lib/widgets/settings/common/quick_actions/action_panel.dart index e83f4186a..f1810cc33 100644 --- a/lib/widgets/settings/common/quick_actions/action_panel.dart +++ b/lib/widgets/settings/common/quick_actions/action_panel.dart @@ -15,10 +15,10 @@ class ActionPanel extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final color = highlight ? theme.colorScheme.primary : Color.alphaBlend(theme.colorScheme.surfaceTint.withOpacity(.2), Themes.secondLayerColor(context)); + final color = highlight ? theme.colorScheme.primary : Color.alphaBlend(theme.colorScheme.surfaceTint.withAlpha((255.0 * .2).round()), Themes.secondLayerColor(context)); return AnimatedContainer( foregroundDecoration: BoxDecoration( - color: color.withOpacity(.2), + color: color.withAlpha((255.0 * .2).round()), border: Border.fromBorderSide(BorderSide( color: color, width: highlight ? 2 : 1, diff --git a/lib/widgets/settings/common/quick_actions/editor_page.dart b/lib/widgets/settings/common/quick_actions/editor_page.dart index 063d17ac7..8e0086e2d 100644 --- a/lib/widgets/settings/common/quick_actions/editor_page.dart +++ b/lib/widgets/settings/common/quick_actions/editor_page.dart @@ -287,7 +287,7 @@ class _QuickActionEditorBodyState extends State { height: kMinInteractiveDimension, child: DefaultTextStyle( style: crumbStyle.copyWith( - color: crumbStyle.color!.withOpacity(.4), + color: crumbStyle.color!.withAlpha((255.0 * .4).round()), fontWeight: FontWeight.w500, ), child: CrumbLine( diff --git a/lib/widgets/settings/privacy/privacy.dart b/lib/widgets/settings/privacy/privacy.dart index 671f77ce6..d49a4813c 100644 --- a/lib/widgets/settings/privacy/privacy.dart +++ b/lib/widgets/settings/privacy/privacy.dart @@ -123,7 +123,9 @@ class SettingsTilePrivacyEnableBin extends SettingsTile { context: context, message: l10n.settingsDisablingBinWarningDialogMessage, confirmationButtonLabel: l10n.applyButtonLabel, - )) return false; + )) { + return false; + } // delete forever trashed items await EntrySetActionDelegate().doDelete( diff --git a/lib/widgets/settings/settings_mobile_page.dart b/lib/widgets/settings/settings_mobile_page.dart index 8c29b5c10..ffb754546 100644 --- a/lib/widgets/settings/settings_mobile_page.dart +++ b/lib/widgets/settings/settings_mobile_page.dart @@ -22,7 +22,6 @@ import 'package:aves/widgets/settings/app_export/selection_dialog.dart'; import 'package:aves/widgets/settings/settings_page.dart'; import 'package:aves/widgets/settings/settings_search.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -113,7 +112,7 @@ class _SettingsMobilePageState extends State with FeedbackMi final allMap = Map.fromEntries(toExport.map((v) { final jsonMap = v.export(source); return jsonMap != null ? MapEntry(v.name, jsonMap) : null; - }).whereNotNull()); + }).nonNulls); allMap[exportVersionKey] = exportVersion; final allJsonString = jsonEncode(allMap); diff --git a/lib/widgets/settings/settings_search.dart b/lib/widgets/settings/settings_search.dart index ac547b44f..a9403ea90 100644 --- a/lib/widgets/settings/settings_search.dart +++ b/lib/widgets/settings/settings_search.dart @@ -5,7 +5,6 @@ import 'package:aves/widgets/common/identity/highlight_title.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/search/delegate.dart'; import 'package:aves/widgets/settings/settings_definition.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; class SettingsSearchDelegate extends AvesSearchDelegate { @@ -64,7 +63,7 @@ class SettingsSearchDelegate extends AvesSearchDelegate { final loaders = snapshot.data; if (loaders == null) return const SizedBox(); - final children = loaders.whereNotNull().expand((builder) => builder(context)).toList(); + final children = loaders.nonNulls.expand((builder) => builder(context)).toList(); return children.isEmpty ? EmptyContent( icon: AIcons.settings, diff --git a/lib/widgets/settings/thumbnails/overlay.dart b/lib/widgets/settings/thumbnails/overlay.dart index fa839f214..6a19793c1 100644 --- a/lib/widgets/settings/thumbnails/overlay.dart +++ b/lib/widgets/settings/thumbnails/overlay.dart @@ -121,7 +121,7 @@ class ThumbnailOverlayPage extends StatelessWidget { icon, key: ValueKey(key), size: _getIconSize(context), - color: _getIconColor(context).withOpacity(disabled ? SettingsSwitchListTile.disabledOpacity : 1.0), + color: _getIconColor(context).withAlpha((255.0 * (disabled ? SettingsSwitchListTile.disabledOpacity : 1.0)).round()), ), ), ); diff --git a/lib/widgets/settings/video/control_actions.dart b/lib/widgets/settings/video/control_actions.dart new file mode 100644 index 000000000..6919bb1fe --- /dev/null +++ b/lib/widgets/settings/video/control_actions.dart @@ -0,0 +1,70 @@ +import 'package:aves/model/settings/settings.dart'; +import 'package:aves/view/view.dart'; +import 'package:aves/widgets/common/basic/scaffold.dart'; +import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/common/identity/buttons/overlay_button.dart'; +import 'package:aves/widgets/settings/common/quick_actions/action_panel.dart'; +import 'package:aves/widgets/viewer/overlay/video/controls.dart'; +import 'package:aves_model/aves_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class VideoControlButtonsPage extends StatelessWidget { + static const routeName = '/settings/video/control_buttons'; + static const _availableActions = [...EntryActions.videoPlayback, EntryAction.openVideoPlayer]; + + const VideoControlButtonsPage({super.key}); + + @override + Widget build(BuildContext context) { + return AvesScaffold( + appBar: AppBar( + automaticallyImplyLeading: !settings.useTvLayout, + title: Text(context.l10n.settingsVideoControlsPageTitle), + ), + body: SafeArea( + child: Selector>( + selector: (context, s) => s.videoControlActions, + builder: (context, selectedActionList, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ActionPanel( + child: Container( + alignment: AlignmentDirectional.center, + height: OverlayButton.getSize(context) + 48, + child: selectedActionList.isNotEmpty + ? VideoControlRow(onActionSelected: (_) {}) + : Text( + context.l10n.settingsViewerQuickActionEmpty, + style: Theme.of(context).textTheme.bodySmall, + ), + ), + ), + Expanded( + child: ListView( + children: _availableActions.map((action) { + return SwitchListTile( + value: selectedActionList.contains(action), + onChanged: (v) { + final selectedActionSet = settings.videoControlActions.toSet(); + if (v) { + selectedActionSet.add(action); + } else { + selectedActionSet.remove(action); + } + settings.videoControlActions = _availableActions.where(selectedActionSet.contains).toList(); + }, + title: Text(action.getText(context)), + ); + }).toList(), + ), + ), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/widgets/settings/video/controls.dart b/lib/widgets/settings/video/controls.dart index 509284435..da1ceeecd 100644 --- a/lib/widgets/settings/video/controls.dart +++ b/lib/widgets/settings/video/controls.dart @@ -1,9 +1,8 @@ import 'package:aves/model/settings/settings.dart'; -import 'package:aves/view/view.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/settings/common/tiles.dart'; -import 'package:aves_model/aves_model.dart'; +import 'package:aves/widgets/settings/video/control_actions.dart'; import 'package:flutter/material.dart'; class VideoControlsPage extends StatelessWidget { @@ -20,12 +19,10 @@ class VideoControlsPage extends StatelessWidget { body: SafeArea( child: ListView( children: [ - SettingsSelectionListTile( - values: VideoControls.values, - getName: (context, v) => v.getName(context), - selector: (context, s) => s.videoControls, - onSelection: (v) => settings.videoControls = v, - tileTitle: context.l10n.settingsVideoButtonsTile, + SettingsSubPageTile( + title: context.l10n.settingsVideoButtonsTile, + routeName: VideoControlButtonsPage.routeName, + builder: (context) => const VideoControlButtonsPage(), ), SettingsSwitchListTile( selector: (context, s) => s.videoGestureDoubleTapTogglePlay, diff --git a/lib/widgets/settings/video/subtitle_sample.dart b/lib/widgets/settings/video/subtitle_sample.dart index a7ffad64a..6b441ed1f 100644 --- a/lib/widgets/settings/video/subtitle_sample.dart +++ b/lib/widgets/settings/video/subtitle_sample.dart @@ -22,7 +22,7 @@ class SubtitleSample extends StatelessWidget { builder: (context, settings, child) { final textAlign = settings.subtitleTextAlignment; final textPosition = settings.subtitleTextPosition; - final outlineColor = Colors.black.withOpacity(settings.subtitleTextColor.opacity); + final outlineColor = Colors.black.withAlpha((255.0 * settings.subtitleTextColor.opacity).round()); final shadows = [ Shadow( color: outlineColor, diff --git a/lib/widgets/settings/video/subtitle_theme.dart b/lib/widgets/settings/video/subtitle_theme.dart index 6758d9437..6c846ebb8 100644 --- a/lib/widgets/settings/video/subtitle_theme.dart +++ b/lib/widgets/settings/video/subtitle_theme.dart @@ -62,23 +62,23 @@ class SubtitleThemePage extends StatelessWidget { ), ColorListTile( title: context.l10n.settingsSubtitleThemeTextColor, - value: settings.subtitleTextColor.withOpacity(1), - onChanged: (v) => settings.subtitleTextColor = v.withOpacity(settings.subtitleTextColor.opacity), + value: settings.subtitleTextColor.withAlpha(255), + onChanged: (v) => settings.subtitleTextColor = v.withAlpha((255.0 * settings.subtitleTextColor.opacity).round()), ), SliderListTile( title: context.l10n.settingsSubtitleThemeTextOpacity, value: settings.subtitleTextColor.opacity, - onChanged: (v) => settings.subtitleTextColor = settings.subtitleTextColor.withOpacity(v), + onChanged: (v) => settings.subtitleTextColor = settings.subtitleTextColor.withAlpha((255.0 * v).round()), ), ColorListTile( title: context.l10n.settingsSubtitleThemeBackgroundColor, - value: settings.subtitleBackgroundColor.withOpacity(1), - onChanged: (v) => settings.subtitleBackgroundColor = v.withOpacity(settings.subtitleBackgroundColor.opacity), + value: settings.subtitleBackgroundColor.withAlpha(255), + onChanged: (v) => settings.subtitleBackgroundColor = v.withAlpha((255.0 * settings.subtitleBackgroundColor.opacity).round()), ), SliderListTile( title: context.l10n.settingsSubtitleThemeBackgroundOpacity, value: settings.subtitleBackgroundColor.opacity, - onChanged: (v) => settings.subtitleBackgroundColor = settings.subtitleBackgroundColor.withOpacity(v), + onChanged: (v) => settings.subtitleBackgroundColor = settings.subtitleBackgroundColor.withAlpha((255.0 * v).round()), ), SettingsSwitchListTile( selector: (context, s) => s.subtitleShowOutline, diff --git a/lib/widgets/stats/date/histogram.dart b/lib/widgets/stats/date/histogram.dart index 29b949140..869b04c2e 100644 --- a/lib/widgets/stats/date/histogram.dart +++ b/lib/widgets/stats/date/histogram.dart @@ -78,7 +78,7 @@ class _HistogramState extends State with AutomaticKeepAliveClientMixi _firstDate = normalizeDate(firstDate); _lastDate = normalizeDate(lastDate); - final dates = entriesByDateDescending.map((entry) => entry.bestDate).whereNotNull(); + final dates = entriesByDateDescending.map((entry) => entry.bestDate).nonNulls; _entryCountPerDate.addAll(groupBy(dates, normalizeDate).map((k, v) => MapEntry(k, v.length))); if (_entryCountPerDate.isNotEmpty) { // discrete points @@ -198,7 +198,7 @@ class _HistogramState extends State with AutomaticKeepAliveClientMixi final colorScheme = Theme.of(context).colorScheme; final accentColor = colorScheme.primary; final axisColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onSurface : Colors.transparent); - final measureLineColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onSurface.withOpacity(.1) : Colors.transparent); + final measureLineColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onSurface.withAlpha((255.0 * .1).round()) : Colors.transparent); final histogramLineColor = charts.ColorUtil.fromDartColor(drawLine ? accentColor : Colors.white); final histogramPointStrikeColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onSurface : Colors.transparent); final histogramPointFillColor = charts.ColorUtil.fromDartColor(Themes.firstLayerColor(context)); @@ -292,8 +292,8 @@ class _HistogramState extends State with AutomaticKeepAliveClientMixi begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ - accentColor.withOpacity(0), accentColor, + accentColor.withAlpha(0), ], ).createShader, blendMode: BlendMode.srcIn, diff --git a/lib/widgets/viewer/action/entry_action_delegate.dart b/lib/widgets/viewer/action/entry_action_delegate.dart index ed0fb320c..a53ae8dd0 100644 --- a/lib/widgets/viewer/action/entry_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_action_delegate.dart @@ -34,6 +34,7 @@ import 'package:aves/widgets/viewer/action/printer.dart'; import 'package:aves/widgets/viewer/action/single_entry_editor.dart'; import 'package:aves/widgets/viewer/controls/notifications.dart'; import 'package:aves/widgets/viewer/debug/debug_page.dart'; +import 'package:aves/widgets/viewer/entry_viewer_page.dart'; import 'package:aves/widgets/viewer/multipage/conductor.dart'; import 'package:aves/widgets/viewer/source_viewer_page.dart'; import 'package:aves/widgets/viewer/video/conductor.dart'; @@ -101,7 +102,9 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.videoTogglePlay: case EntryAction.videoReplay10: case EntryAction.videoSkip10: - case EntryAction.openVideo: + case EntryAction.videoShowPreviousFrame: + case EntryAction.videoShowNextFrame: + case EntryAction.openVideoPlayer: return targetEntry.isPureVideo; case EntryAction.rotateScreen: return !settings.useTvLayout && settings.isRotationLocked; @@ -183,7 +186,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix } void onActionSelected(BuildContext context, EntryAction action) { - reportService.log('$action'); + reportService.log('$runtimeType handles $action'); final targetEntry = _getTargetEntry(context, action); switch (action) { @@ -241,7 +244,9 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.videoTogglePlay: case EntryAction.videoReplay10: case EntryAction.videoSkip10: - case EntryAction.openVideo: + case EntryAction.videoShowPreviousFrame: + case EntryAction.videoShowNextFrame: + case EntryAction.openVideoPlayer: final controller = context.read().getController(targetEntry); if (controller != null) { VideoActionNotification( @@ -395,7 +400,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix final name = result.$2; if (name.isEmpty) return; - await appService.pinToHomeScreen(name, targetEntry, uri: targetEntry.uri); + await appService.pinToHomeScreen(name, targetEntry, route: EntryViewerPage.routeName, viewUri: targetEntry.uri); if (!device.showPinShortcutFeedback) { showFeedback(context, FeedbackType.info, context.l10n.genericSuccessFeedback); } @@ -429,7 +434,9 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix type: ConfirmationDialog.deleteForever, message: l10n.deleteEntriesConfirmationDialogMessage(1), confirmationButtonLabel: l10n.deleteButtonLabel, - )) return; + )) { + return; + } if (!await checkStoragePermission(context, {targetEntry})) return; @@ -437,9 +444,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix showFeedback(context, FeedbackType.warn, l10n.genericFailureFeedback); } else { final source = context.read(); - if (source.scope != SourceScope.none) { - await source.removeEntries({targetEntry.uri}, includeTrash: true); - } + await source.removeEntries({targetEntry.uri}, includeTrash: true); EntryDeletedNotification({targetEntry}).dispatch(context); } } diff --git a/lib/widgets/viewer/action/entry_info_action_delegate.dart b/lib/widgets/viewer/action/entry_info_action_delegate.dart index 41795c046..02146ba4c 100644 --- a/lib/widgets/viewer/action/entry_info_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_info_action_delegate.dart @@ -92,7 +92,7 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi } Future onActionSelected(BuildContext context, AvesEntry targetEntry, CollectionLens? collection, EntryAction action) async { - await reportService.log('$action'); + await reportService.log('$runtimeType handles $action'); _eventStreamController.add(ActionStartedEvent(action)); switch (action) { // general diff --git a/lib/widgets/viewer/action/video_action_delegate.dart b/lib/widgets/viewer/action/video_action_delegate.dart index 65f96c45b..78375aad4 100644 --- a/lib/widgets/viewer/action/video_action_delegate.dart +++ b/lib/widgets/viewer/action/video_action_delegate.dart @@ -25,6 +25,7 @@ import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:leak_tracker/leak_tracker.dart'; import 'package:provider/provider.dart'; class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { @@ -35,7 +36,7 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix required this.collection, }) { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$VideoActionDelegate', object: this, @@ -45,7 +46,7 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } stopOverlayHidingTimer(); } @@ -74,7 +75,11 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix await controller.seekTo(max(controller.currentPosition - 10000, 0)); case EntryAction.videoSkip10: await controller.seekTo(controller.currentPosition + 10000); - case EntryAction.openVideo: + case EntryAction.videoShowPreviousFrame: + await controller.skipFrames(-1); + case EntryAction.videoShowNextFrame: + await controller.skipFrames(1); + case EntryAction.openVideoPlayer: await appService.open(entry.uri, entry.mimeTypeAnySubtype, forceChooser: false).then((success) { if (!success) showNoMatchingAppDialog(context); }); @@ -159,7 +164,7 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix context: context, builder: (context) => VideoStreamSelectionDialog( streams: Map.fromEntries(streams.map((stream) { - final selectedStream = currentSelectedStreams.whereNotNull().firstWhereOrNull((v) => v.type == stream.type); + final selectedStream = currentSelectedStreams.nonNulls.firstWhereOrNull((v) => v.type == stream.type); final selected = selectedStream != null && selectedStream.index == stream.index; return MapEntry(stream, selected); })), diff --git a/lib/widgets/viewer/controls/controller.dart b/lib/widgets/viewer/controls/controller.dart index 54bb798f7..a71cdce48 100644 --- a/lib/widgets/viewer/controls/controller.dart +++ b/lib/widgets/viewer/controls/controller.dart @@ -10,6 +10,7 @@ import 'package:aves_magnifier/aves_magnifier.dart'; import 'package:aves_model/aves_model.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class ViewerController with CastMixin { final ValueNotifier entryNotifier = ValueNotifier(null); @@ -51,7 +52,7 @@ class ViewerController with CastMixin { this.autopilotAnimatedZoom = false, }) { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$ViewerController', object: this, @@ -66,7 +67,7 @@ class ViewerController with CastMixin { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } entryNotifier.removeListener(_onEntryChanged); windowService.setHdrColorMode(false); diff --git a/lib/widgets/viewer/entry_vertical_pager.dart b/lib/widgets/viewer/entry_vertical_pager.dart index 9d7df2ad0..00aad71bd 100644 --- a/lib/widgets/viewer/entry_vertical_pager.dart +++ b/lib/widgets/viewer/entry_vertical_pager.dart @@ -178,7 +178,7 @@ class _ViewerVerticalPageViewState extends State { builder: (context, overlayOpacity, child) { final background = Theme.of(context).isDark ? Colors.black : Color.lerp(Colors.black, Colors.white, overlayOpacity)!; return Container( - color: background.withOpacity(backgroundOpacity), + color: background.withAlpha((255.0 * backgroundOpacity).round()), child: child, ); }, @@ -331,7 +331,7 @@ class _ViewerVerticalPageViewState extends State { if (settings.maxBrightness == MaxBrightness.viewerOnly) { _systemBrightness?.then((system) { final value = lerpDouble(maximumBrightness, system, ((1 - page).abs() * 2).clamp(0, 1))!; - AvesApp.screenBrightness?.setScreenBrightness(value); + AvesApp.screenBrightness?.setApplicationScreenBrightness(value); }); } diff --git a/lib/widgets/viewer/entry_viewer_stack.dart b/lib/widgets/viewer/entry_viewer_stack.dart index e56eee0fd..1ecf6f3c2 100644 --- a/lib/widgets/viewer/entry_viewer_stack.dart +++ b/lib/widgets/viewer/entry_viewer_stack.dart @@ -104,7 +104,7 @@ class _EntryViewerStackState extends State with EntryViewContr void initState() { super.initState(); if (settings.maxBrightness == MaxBrightness.viewerOnly) { - AvesApp.screenBrightness?.setScreenBrightness(1); + AvesApp.screenBrightness?.setApplicationScreenBrightness(1); } if (settings.keepScreenOn == KeepScreenOn.viewerOnly) { windowService.keepScreenOn(true); @@ -915,9 +915,9 @@ class _EntryViewerStackState extends State with EntryViewContr switch (settings.maxBrightness) { case MaxBrightness.never: case MaxBrightness.viewerOnly: - await AvesApp.screenBrightness?.resetScreenBrightness(); + await AvesApp.screenBrightness?.resetApplicationScreenBrightness(); case MaxBrightness.always: - await AvesApp.screenBrightness?.setScreenBrightness(1); + await AvesApp.screenBrightness?.setApplicationScreenBrightness(1); } if (settings.keepScreenOn == KeepScreenOn.viewerOnly) { await windowService.keepScreenOn(false); diff --git a/lib/widgets/viewer/info/metadata/xmp_card.dart b/lib/widgets/viewer/info/metadata/xmp_card.dart index 7df746d69..ba46bb176 100644 --- a/lib/widgets/viewer/info/metadata/xmp_card.dart +++ b/lib/widgets/viewer/info/metadata/xmp_card.dart @@ -7,7 +7,6 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/highlight_title.dart'; import 'package:aves/widgets/viewer/info/common.dart'; import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; typedef XmpExtractedCard = (Map, List?); @@ -28,7 +27,7 @@ class XmpCard extends StatefulWidget { }) { directStruct = structByIndex[null]; - final length = structByIndex.keys.whereNotNull().fold(0, max); + final length = structByIndex.keys.nonNulls.fold(0, max); indexedStructs = length > 0 ? [for (var i = 0; i < length; i++) structByIndex[i + 1] ?? const ({}, null)] : null; } diff --git a/lib/widgets/viewer/info/metadata/xmp_namespaces.dart b/lib/widgets/viewer/info/metadata/xmp_namespaces.dart index 57ae547d2..f16f26627 100644 --- a/lib/widgets/viewer/info/metadata/xmp_namespaces.dart +++ b/lib/widgets/viewer/info/metadata/xmp_namespaces.dart @@ -99,7 +99,7 @@ class XmpNamespace extends Equatable { cards.forEach((card) => extracted |= card.extract(prop)); return extracted ? null : prop; }) - .whereNotNull() + .nonNulls .toList() ..sort(); diff --git a/lib/widgets/viewer/info/metadata/xmp_ns/google.dart b/lib/widgets/viewer/info/metadata/xmp_ns/google.dart index 77e740170..28e4f869f 100644 --- a/lib/widgets/viewer/info/metadata/xmp_ns/google.dart +++ b/lib/widgets/viewer/info/metadata/xmp_ns/google.dart @@ -50,7 +50,7 @@ abstract class XmpGoogleNamespace extends XmpNamespace { }, )) : null; - }).whereNotNull()); + }).nonNulls); } } diff --git a/lib/widgets/viewer/multipage/conductor.dart b/lib/widgets/viewer/multipage/conductor.dart index 2c277e0a4..8c607aa02 100644 --- a/lib/widgets/viewer/multipage/conductor.dart +++ b/lib/widgets/viewer/multipage/conductor.dart @@ -4,6 +4,7 @@ import 'package:aves/model/entry/entry.dart'; import 'package:aves/widgets/viewer/multipage/controller.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class MultiPageConductor { final List _controllers = []; @@ -12,7 +13,7 @@ class MultiPageConductor { MultiPageConductor() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$MultiPageConductor', object: this, @@ -22,7 +23,7 @@ class MultiPageConductor { Future dispose() async { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } await _disposeAll(); _controllers.clear(); diff --git a/lib/widgets/viewer/multipage/controller.dart b/lib/widgets/viewer/multipage/controller.dart index b8875ba05..589cfa285 100644 --- a/lib/widgets/viewer/multipage/controller.dart +++ b/lib/widgets/viewer/multipage/controller.dart @@ -5,6 +5,7 @@ import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/multipage.dart'; import 'package:flutter/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class MultiPageController { final AvesEntry entry; @@ -25,7 +26,7 @@ class MultiPageController { MultiPageController(this.entry) { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$MultiPageController', object: this, @@ -48,7 +49,7 @@ class MultiPageController { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _info?.dispose(); _disposed = true; diff --git a/lib/widgets/viewer/overlay/histogram.dart b/lib/widgets/viewer/overlay/histogram.dart index 60ec190c3..b59d667dd 100644 --- a/lib/widgets/viewer/overlay/histogram.dart +++ b/lib/widgets/viewer/overlay/histogram.dart @@ -138,7 +138,7 @@ class _HistogramPainter extends CustomPainter { Path()..addPolygon(polyline, true), Paint() ..style = PaintingStyle.fill - ..color = color.withOpacity(.5)); + ..color = color.withAlpha((255.0 * .5).round())); } Color _getChannelColor(HistogramChannel channel) { diff --git a/lib/widgets/viewer/overlay/video/controls.dart b/lib/widgets/viewer/overlay/video/controls.dart index 3921ff045..f2929dbd0 100644 --- a/lib/widgets/viewer/overlay/video/controls.dart +++ b/lib/widgets/viewer/overlay/video/controls.dart @@ -1,4 +1,3 @@ -import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/view/view.dart'; import 'package:aves/widgets/common/action_controls/togglers/play.dart'; @@ -10,9 +9,9 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class VideoControlRow extends StatelessWidget { - final AvesEntry entry; final AvesVideoController? controller; final Animation scale; + final bool canOpenVideoPlayer; final Function(EntryAction value) onActionSelected; static const double padding = 8; @@ -20,87 +19,76 @@ class VideoControlRow extends StatelessWidget { const VideoControlRow({ super.key, - required this.entry, - required this.controller, - required this.scale, + this.controller, + this.scale = kAlwaysCompleteAnimation, + this.canOpenVideoPlayer = true, required this.onActionSelected, }); @override Widget build(BuildContext context) { - return Selector( - selector: (context, s) => s.videoControls, - builder: (context, videoControls, child) { - switch (videoControls) { - case VideoControls.play: - return Padding( - padding: const EdgeInsets.only(left: padding), - child: _buildOverlayButton( - child: PlayToggler( - controller: controller, - onPressed: () => onActionSelected(EntryAction.videoTogglePlay), - ), - ), - ); - case VideoControls.playSeek: - return Row( - mainAxisSize: MainAxisSize.min, - textDirection: ViewerBottomOverlay.actionsDirection, - children: [ - const SizedBox(width: padding), - _buildIconButton( - context, - EntryAction.videoReplay10, - borderRadius: const BorderRadius.only(topLeft: radius, bottomLeft: radius), - ), - _buildOverlayButton( - child: PlayToggler( - controller: controller, - onPressed: () => onActionSelected(EntryAction.videoTogglePlay), - ), - borderRadius: const BorderRadius.all(Radius.zero), - ), - _buildIconButton( - context, - EntryAction.videoSkip10, - borderRadius: const BorderRadius.only(topRight: radius, bottomRight: radius), - ), - ], - ); - case VideoControls.playOutside: - return Padding( - padding: const EdgeInsets.only(left: padding), - child: _buildIconButton(context, EntryAction.openVideo, enabled: !entry.trashed), - ); - case VideoControls.none: - return const SizedBox(); - } + return Selector>( + selector: (context, s) => s.videoControlActions, + builder: (context, actions, child) { + return Padding( + padding: EdgeInsets.only(left: actions.isEmpty ? 0 : padding), + child: Row( + mainAxisSize: MainAxisSize.min, + textDirection: ViewerBottomOverlay.actionsDirection, + children: actions.map((action) { + // null radius yields a circular button + BorderRadius? borderRadius; + if (actions.length > 1) { + // zero radius yields a square button + borderRadius = BorderRadius.zero; + if (action == actions.first) { + borderRadius = const BorderRadius.horizontal(left: radius); + } else if (action == actions.last) { + borderRadius = const BorderRadius.horizontal(right: radius); + } + } + return _buildOverlayButton(context, action, borderRadius); + }).toList(), + ), + ); }, ); } - Widget _buildOverlayButton({ + Widget _buildOverlayButton( + BuildContext context, + EntryAction action, BorderRadius? borderRadius, - required Widget child, - }) => - OverlayButton( - scale: scale, - borderRadius: borderRadius, - child: child, + ) { + Widget child; + if (action == EntryAction.videoTogglePlay) { + child = PlayToggler( + controller: controller, + onPressed: () => onActionSelected(action), + ); + } else { + final enabled = action == EntryAction.openVideoPlayer ? canOpenVideoPlayer : true; + child = IconButton( + icon: action.getIcon(), + onPressed: enabled ? () => onActionSelected(action) : null, + tooltip: action.getText(context), ); + } - Widget _buildIconButton( - BuildContext context, - EntryAction action, { - bool enabled = true, - BorderRadius? borderRadius, - }) => - _buildOverlayButton( - borderRadius: borderRadius, - child: IconButton( - icon: action.getIcon(), - onPressed: enabled ? () => onActionSelected(action) : null, - tooltip: action.getText(context), + if (borderRadius != null) { + child = Padding( + padding: EdgeInsets.only( + left: borderRadius.topLeft.x > 0 ? padding / 3 : 0, + right: borderRadius.topRight.x > 0 ? padding / 3 : 0, ), + child: child, ); + } + + return OverlayButton( + scale: scale, + borderRadius: borderRadius, + child: child, + ); + } } diff --git a/lib/widgets/viewer/overlay/video/progress_bar.dart b/lib/widgets/viewer/overlay/video/progress_bar.dart index 3d746c3e0..249e29d1e 100644 --- a/lib/widgets/viewer/overlay/video/progress_bar.dart +++ b/lib/widgets/viewer/overlay/video/progress_bar.dart @@ -119,7 +119,7 @@ class _VideoProgressBarState extends State { if (!progress.isFinite) progress = 0.0; return LinearProgressIndicator( value: progress, - backgroundColor: theme.colorScheme.onSurface.withOpacity(.2), + backgroundColor: theme.colorScheme.onSurface.withAlpha((255.0 * .2).round()), ); }), ), diff --git a/lib/widgets/viewer/overlay/video/video.dart b/lib/widgets/viewer/overlay/video/video.dart index eb38e103a..02ad52ae5 100644 --- a/lib/widgets/viewer/overlay/video/video.dart +++ b/lib/widgets/viewer/overlay/video/video.dart @@ -47,7 +47,7 @@ class _VideoControlOverlayState extends State with SingleTi final status = controller?.status ?? VideoStatus.idle; if (status == VideoStatus.error) { - const action = EntryAction.openVideo; + const action = EntryAction.openVideoPlayer; return Align( alignment: Alignment.centerRight, child: OverlayButton( @@ -78,9 +78,9 @@ class _VideoControlOverlayState extends State with SingleTi ), ), VideoControlRow( - entry: entry, controller: controller, scale: scale, + canOpenVideoPlayer: !entry.trashed, onActionSelected: widget.onActionSelected, ), ], diff --git a/lib/widgets/viewer/video/conductor.dart b/lib/widgets/viewer/video/conductor.dart index 0b6da17ef..250ded24f 100644 --- a/lib/widgets/viewer/video/conductor.dart +++ b/lib/widgets/viewer/video/conductor.dart @@ -10,6 +10,7 @@ import 'package:aves_model/aves_model.dart'; import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class VideoConductor { final CollectionLens? _collection; @@ -21,7 +22,7 @@ class VideoConductor { VideoConductor({CollectionLens? collection}) : _collection = collection { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$VideoConductor', object: this, @@ -31,7 +32,7 @@ class VideoConductor { Future dispose() async { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _subscriptions ..forEach((sub) => sub.cancel()) diff --git a/lib/widgets/viewer/view/conductor.dart b/lib/widgets/viewer/view/conductor.dart index 78a7def8b..529238ec6 100644 --- a/lib/widgets/viewer/view/conductor.dart +++ b/lib/widgets/viewer/view/conductor.dart @@ -5,6 +5,7 @@ import 'package:aves_magnifier/aves_magnifier.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class ViewStateConductor { final List _controllers = []; @@ -14,7 +15,7 @@ class ViewStateConductor { ViewStateConductor() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$ViewStateConductor', object: this, @@ -24,7 +25,7 @@ class ViewStateConductor { Future dispose() async { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _controllers.forEach((v) => v.dispose()); _controllers.clear(); diff --git a/lib/widgets/viewer/view/controller.dart b/lib/widgets/viewer/view/controller.dart index 725e99ec1..ca487baa9 100644 --- a/lib/widgets/viewer/view/controller.dart +++ b/lib/widgets/viewer/view/controller.dart @@ -3,6 +3,7 @@ import 'package:aves/model/viewer/view_state.dart'; import 'package:aves/widgets/viewer/view/histogram.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class ViewStateController with HistogramMixin { final AvesEntry entry; @@ -16,7 +17,7 @@ class ViewStateController with HistogramMixin { required this.viewStateNotifier, }) { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$ViewStateController', object: this, @@ -26,7 +27,7 @@ class ViewStateController with HistogramMixin { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } viewStateNotifier.dispose(); fullImageNotifier.dispose(); diff --git a/lib/widgets/viewer/view/histogram.dart b/lib/widgets/viewer/view/histogram.dart index 72f278101..3436fac9a 100644 --- a/lib/widgets/viewer/view/histogram.dart +++ b/lib/widgets/viewer/view/histogram.dart @@ -13,29 +13,28 @@ typedef HistogramLevels = Map>; mixin HistogramMixin { HistogramLevels _levels = {}; - Completer? _completer; + Future? _loader; static const int bins = 256; Future getHistogramLevels(ImageInfo info, bool forceUpdate) async { if (_levels.isEmpty || forceUpdate) { - if (_completer == null) { - _completer = Completer(); - final data = (await info.image.toByteData(format: ImageByteFormat.rawStraightRgba))!; - _levels = switch (settings.overlayHistogramStyle) { - OverlayHistogramStyle.rgb => await compute(_computeRgbLevels, data), - OverlayHistogramStyle.luminance => await compute(_computeLuminanceLevels, data), - _ => >{}, - }; - _completer?.complete(); - } else { - await _completer?.future; - _completer = null; - } + _loader ??= _getLevels(info); + await _loader; + _loader = null; } return _levels; } + Future _getLevels(ImageInfo info) async { + final data = (await info.image.toByteData(format: ImageByteFormat.rawStraightRgba))!; + _levels = switch (settings.overlayHistogramStyle) { + OverlayHistogramStyle.rgb => await compute(_computeRgbLevels, data), + OverlayHistogramStyle.luminance => await compute(_computeLuminanceLevels, data), + _ => >{}, + }; + } + static HistogramLevels _computeRgbLevels(ByteData data) { final redLevels = List.filled(bins, 0); final greenLevels = List.filled(bins, 0); diff --git a/lib/widgets/viewer/visual/entry_page_view.dart b/lib/widgets/viewer/visual/entry_page_view.dart index 6098b07ec..0f23b68be 100644 --- a/lib/widgets/viewer/visual/entry_page_view.dart +++ b/lib/widgets/viewer/visual/entry_page_view.dart @@ -310,12 +310,10 @@ class _EntryPageViewState extends State with TickerProviderStateM onScaleEnd = (details) { valueNotifier?.dispose(); - final overlayEntry = _actionFeedbackOverlayEntry; + _actionFeedbackOverlayEntry + ?..remove() + ..dispose(); _actionFeedbackOverlayEntry = null; - if (overlayEntry != null) { - overlayEntry.remove(); - overlayEntry.dispose(); - } }; } diff --git a/lib/widgets/viewer/visual/video/subtitle/ass_parser.dart b/lib/widgets/viewer/visual/video/subtitle/ass_parser.dart index 42085900b..8d9aa7fc5 100644 --- a/lib/widgets/viewer/visual/video/subtitle/ass_parser.dart +++ b/lib/widgets/viewer/visual/video/subtitle/ass_parser.dart @@ -2,7 +2,6 @@ import 'package:aves/ref/unicode.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/line.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/span.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/style.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; class AssParser { @@ -471,7 +470,7 @@ class AssParser { pathPattern.allMatches(commands).forEach((match) { if (match.groupCount == 2) { final command = match.group(1)!; - final params = match.group(2)!.trim().split(' ').map(double.tryParse).whereNotNull().map((v) => v / scale).toList(); + final params = match.group(2)!.trim().split(' ').map(double.tryParse).nonNulls.map((v) => v / scale).toList(); switch (command) { case 'b': if (path != null) { @@ -529,7 +528,7 @@ class AssParser { if (g != null) { final params = g.split(','); if (params.length == 4) { - final points = params.map(double.tryParse).whereNotNull().toList(); + final points = params.map(double.tryParse).nonNulls.toList(); if (points.length == 4) { paths = [ Path() diff --git a/lib/widgets/viewer/visual/video/subtitle/subtitle.dart b/lib/widgets/viewer/visual/video/subtitle/subtitle.dart index 06a81b904..3a06ef47a 100644 --- a/lib/widgets/viewer/visual/video/subtitle/subtitle.dart +++ b/lib/widgets/viewer/visual/video/subtitle/subtitle.dart @@ -44,7 +44,7 @@ class VideoSubtitles extends StatelessWidget { final baseTextAlign = settings.subtitleTextAlignment; final baseTextAlignY = settings.subtitleTextPosition.toTextAlignVertical(); final baseOutlineWidth = settings.subtitleShowOutline ? 1 : 0; - final baseOutlineColor = Colors.black.withOpacity(settings.subtitleTextColor.opacity); + final baseOutlineColor = Colors.black.withAlpha((255.0 * settings.subtitleTextColor.opacity).round()); final baseShadows = [ Shadow( color: baseOutlineColor, diff --git a/lib/widgets/viewer/visual/video/swipe_action.dart b/lib/widgets/viewer/visual/video/swipe_action.dart index 435f3da34..f15e9eca0 100644 --- a/lib/widgets/viewer/visual/video/swipe_action.dart +++ b/lib/widgets/viewer/visual/video/swipe_action.dart @@ -12,7 +12,7 @@ extension ExtraSwipeAction on SwipeAction { Future get() { switch (this) { case SwipeAction.brightness: - return AvesApp.screenBrightness?.current ?? Future.value(1); + return AvesApp.screenBrightness?.application ?? Future.value(1); case SwipeAction.volume: return VolumeController().getVolume(); } @@ -21,7 +21,7 @@ extension ExtraSwipeAction on SwipeAction { Future set(double value) async { switch (this) { case SwipeAction.brightness: - await AvesApp.screenBrightness?.setScreenBrightness(value); + await AvesApp.screenBrightness?.setApplicationScreenBrightness(value); case SwipeAction.volume: VolumeController().setVolume(value, showSystemUI: false); } @@ -43,9 +43,9 @@ class SwipeActionFeedback extends StatelessWidget { static const Radius radius = Radius.circular(width / 2); static const double borderWidth = 2; static const Color borderColor = Colors.white; - static final Color fillColor = Colors.white.withOpacity(.8); - static final Color backgroundColor = Colors.black.withOpacity(.2); - static final Color innerBorderColor = Colors.black.withOpacity(.5); + static final Color fillColor = Colors.white.withAlpha((255.0 * .8).round()); + static final Color backgroundColor = Colors.black.withAlpha((255.0 * .2).round()); + static final Color innerBorderColor = Colors.black.withAlpha((255.0 * .5).round()); static const Color iconColor = Colors.white; static const Color shadowColor = Colors.black; diff --git a/lib/widgets/wallpaper_page.dart b/lib/widgets/wallpaper_page.dart index 7184edff7..e86aa3e4a 100644 --- a/lib/widgets/wallpaper_page.dart +++ b/lib/widgets/wallpaper_page.dart @@ -88,7 +88,7 @@ class _EntryEditorState extends State with EntryViewControllerMixin void initState() { super.initState(); if (settings.maxBrightness == MaxBrightness.viewerOnly) { - AvesApp.screenBrightness?.setScreenBrightness(1); + AvesApp.screenBrightness?.setApplicationScreenBrightness(1); } if (settings.keepScreenOn == KeepScreenOn.viewerOnly) { windowService.keepScreenOn(true); diff --git a/plugins/aves_magnifier/lib/src/controller/controller.dart b/plugins/aves_magnifier/lib/src/controller/controller.dart index cd30162c1..2bb1b1e97 100644 --- a/plugins/aves_magnifier/lib/src/controller/controller.dart +++ b/plugins/aves_magnifier/lib/src/controller/controller.dart @@ -6,6 +6,7 @@ import 'package:aves_magnifier/src/scale/scale_level.dart'; import 'package:aves_magnifier/src/scale/state.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class AvesMagnifierController { final StreamController _stateStreamController = StreamController.broadcast(); @@ -21,7 +22,7 @@ class AvesMagnifierController { MagnifierState? initialState, }) : super() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$AvesMagnifierController', object: this, @@ -41,7 +42,7 @@ class AvesMagnifierController { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _disposed = true; _stateStreamController.close(); diff --git a/plugins/aves_magnifier/pubspec.lock b/plugins/aves_magnifier/pubspec.lock index e6dcaa66d..60fb262be 100644 --- a/plugins/aves_magnifier/pubspec.lock +++ b/plugins/aves_magnifier/pubspec.lock @@ -16,6 +16,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" collection: dependency: transitive description: @@ -45,6 +53,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + leak_tracker: + dependency: "direct main" + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" lints: dependency: transitive description: @@ -77,6 +93,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" provider: dependency: "direct main" description: @@ -98,6 +122,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" sdks: dart: ">=3.5.0 <4.0.0" flutter: ">=1.16.0" diff --git a/plugins/aves_magnifier/pubspec.yaml b/plugins/aves_magnifier/pubspec.yaml index 485a526d2..d04fb11a5 100644 --- a/plugins/aves_magnifier/pubspec.yaml +++ b/plugins/aves_magnifier/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: aves_utils: path: ../aves_utils equatable: + leak_tracker: provider: dev_dependencies: diff --git a/plugins/aves_map/lib/src/controller.dart b/plugins/aves_map/lib/src/controller.dart index 26d2b56c4..899799325 100644 --- a/plugins/aves_map/lib/src/controller.dart +++ b/plugins/aves_map/lib/src/controller.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:aves_map/src/zoomed_bounds.dart'; import 'package:flutter/foundation.dart'; import 'package:latlong2/latlong.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class AvesMapController { final StreamController _streamController = StreamController.broadcast(); @@ -16,13 +17,15 @@ class AvesMapController { Stream get zoomCommands => _events.where((event) => event is MapControllerZoomEvent).cast(); + Stream get rotationResetCommands => _events.where((event) => event is MapControllerRotationResetEvent).cast(); + Stream get idleUpdates => _events.where((event) => event is MapIdleUpdate).cast(); Stream get markerLocationChanges => _events.where((event) => event is MapMarkerLocationChangeEvent).cast(); AvesMapController() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$AvesMapController', object: this, @@ -32,7 +35,7 @@ class AvesMapController { void dispose() { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } _streamController.close(); } @@ -41,6 +44,8 @@ class AvesMapController { void zoomBy(double delta) => _streamController.add(MapControllerZoomEvent(delta)); + void resetRotation() => _streamController.add(MapControllerRotationResetEvent()); + void notifyIdle(ZoomedBounds bounds) { _idleBounds = bounds; _streamController.add(MapIdleUpdate(bounds)); @@ -61,6 +66,8 @@ class MapControllerZoomEvent { MapControllerZoomEvent(this.delta); } +class MapControllerRotationResetEvent {} + class MapIdleUpdate { final ZoomedBounds bounds; diff --git a/plugins/aves_map/lib/src/interface.dart b/plugins/aves_map/lib/src/interface.dart index 6aedd5397..3f2cc1756 100644 --- a/plugins/aves_map/lib/src/interface.dart +++ b/plugins/aves_map/lib/src/interface.dart @@ -3,7 +3,6 @@ import 'package:aves_map/src/marker/key.dart'; import 'package:flutter/material.dart'; import 'package:latlong2/latlong.dart'; -typedef ButtonPanelBuilder = Widget Function(VoidCallback resetRotation); typedef MarkerClusterBuilder = Map, GeoEntry> Function(); typedef MarkerWidgetBuilder = Widget Function(MarkerKey key); typedef MarkerImageReadyChecker = bool Function(MarkerKey key); diff --git a/plugins/aves_map/lib/src/theme.dart b/plugins/aves_map/lib/src/theme.dart index dd188c8a2..97679da69 100644 --- a/plugins/aves_map/lib/src/theme.dart +++ b/plugins/aves_map/lib/src/theme.dart @@ -6,8 +6,9 @@ class MapThemeData { final bool interactive, showCoordinateFilter; final MapNavigationButton navigationButton; final Animation scale; - final VisualDensity? visualDensity; + final VisualDensity visualDensity; final double? mapHeight; + final EdgeInsets attributionPadding; const MapThemeData({ required this.interactive, @@ -16,6 +17,7 @@ class MapThemeData { required this.scale, required this.visualDensity, required this.mapHeight, + required this.attributionPadding, }); static const double markerOuterBorderWidth = 1.5; diff --git a/plugins/aves_map/pubspec.lock b/plugins/aves_map/pubspec.lock index b9059010f..e11f3d826 100644 --- a/plugins/aves_map/pubspec.lock +++ b/plugins/aves_map/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" aves_ui: dependency: "direct main" description: @@ -125,6 +125,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.1" + leak_tracker: + dependency: "direct main" + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" lints: dependency: transitive description: @@ -185,10 +193,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" polylabel: dependency: transitive description: @@ -230,10 +238,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" term_glyph: dependency: transitive description: @@ -246,10 +254,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" unicode: dependency: transitive description: @@ -266,6 +274,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" web: dependency: transitive description: diff --git a/plugins/aves_map/pubspec.yaml b/plugins/aves_map/pubspec.yaml index c59350c35..0aad3b575 100644 --- a/plugins/aves_map/pubspec.yaml +++ b/plugins/aves_map/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: flutter_map: intl: latlong2: + leak_tracker: provider: dev_dependencies: diff --git a/plugins/aves_model/lib/src/actions/entry.dart b/plugins/aves_model/lib/src/actions/entry.dart index 3763f7dd1..a46f30076 100644 --- a/plugins/aves_model/lib/src/actions/entry.dart +++ b/plugins/aves_model/lib/src/actions/entry.dart @@ -28,10 +28,12 @@ enum EntryAction { videoTogglePlay, videoReplay10, videoSkip10, + videoShowPreviousFrame, + videoShowNextFrame, // external edit, open, - openVideo, + openVideoPlayer, openMap, setAs, cast, @@ -97,6 +99,8 @@ class EntryActions { EntryAction.videoTogglePlay, EntryAction.videoReplay10, EntryAction.videoSkip10, + EntryAction.videoShowPreviousFrame, + EntryAction.videoShowNextFrame, EntryAction.rotateCCW, EntryAction.rotateCW, EntryAction.flip, @@ -120,7 +124,9 @@ class EntryActions { static const videoPlayback = [ EntryAction.videoReplay10, + EntryAction.videoShowPreviousFrame, EntryAction.videoTogglePlay, + EntryAction.videoShowNextFrame, EntryAction.videoSkip10, ]; diff --git a/plugins/aves_model/lib/src/actions/map.dart b/plugins/aves_model/lib/src/actions/map.dart index 6d4838cf3..1c71363e9 100644 --- a/plugins/aves_model/lib/src/actions/map.dart +++ b/plugins/aves_model/lib/src/actions/map.dart @@ -1,6 +1,9 @@ enum MapAction { + // any map panel selectStyle, openMapApp, zoomIn, zoomOut, + // full page only + addShortcut, } diff --git a/plugins/aves_model/lib/src/settings/access.dart b/plugins/aves_model/lib/src/settings/access.dart index 3dbf406c8..c87fe223b 100644 --- a/plugins/aves_model/lib/src/settings/access.dart +++ b/plugins/aves_model/lib/src/settings/access.dart @@ -100,6 +100,6 @@ mixin SettingsAccess { } List getEnumListOrDefault(String key, List defaultValue, Iterable values) { - return store.getStringList(key)?.map((s) => values.firstWhereOrNull((v) => v.toString() == s)).whereNotNull().toList() ?? defaultValue; + return store.getStringList(key)?.map((s) => values.firstWhereOrNull((v) => v.toString() == s)).nonNulls.toList() ?? defaultValue; } } diff --git a/plugins/aves_model/lib/src/settings/enums.dart b/plugins/aves_model/lib/src/settings/enums.dart index fce0ea57e..196d57774 100644 --- a/plugins/aves_model/lib/src/settings/enums.dart +++ b/plugins/aves_model/lib/src/settings/enums.dart @@ -36,8 +36,6 @@ enum VideoAutoPlayMode { disabled, playMuted, playWithSound } enum VideoBackgroundMode { disabled, pip } -enum VideoControls { play, playSeek, playOutside, none } - enum VideoLoopMode { never, shortOnly, always } enum VideoResumptionMode { never, ask, always } diff --git a/plugins/aves_model/lib/src/settings/keys.dart b/plugins/aves_model/lib/src/settings/keys.dart index a0ca10939..19be577ed 100644 --- a/plugins/aves_model/lib/src/settings/keys.dart +++ b/plugins/aves_model/lib/src/settings/keys.dart @@ -109,7 +109,7 @@ class SettingKeys { static const videoAutoPlayModeKey = 'video_auto_play_mode'; static const videoLoopModeKey = 'video_loop'; static const videoResumptionModeKey = 'video_resumption_mode'; - static const videoControlsKey = 'video_controls'; + static const videoControlActionsKey = 'video_control_actions'; static const videoGestureDoubleTapTogglePlayKey = 'video_gesture_double_tap_toggle_play'; static const videoGestureSideDoubleTapSeekKey = 'video_gesture_side_double_tap_skip'; static const videoGestureVerticalDragBrightnessVolumeKey = 'video_gesture_vertical_drag_brightness_volume'; diff --git a/plugins/aves_platform_meta/android/build.gradle b/plugins/aves_platform_meta/android/build.gradle index 3a4eafe4d..c2e5a094c 100644 --- a/plugins/aves_platform_meta/android/build.gradle +++ b/plugins/aves_platform_meta/android/build.gradle @@ -4,7 +4,7 @@ version '1.0-SNAPSHOT' buildscript { ext { kotlin_version = '1.9.24' - agp_version = '8.4.1' + agp_version = '8.7.0' } repositories { @@ -28,14 +28,13 @@ allprojects { apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +kotlin { + jvmToolchain 17 +} + android { namespace 'deckers.thibault.aves.aves_platform_meta' - compileSdk 34 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_21 - targetCompatibility JavaVersion.VERSION_21 - } + compileSdk 35 lintOptions { checkAllWarnings true diff --git a/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties b/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties index 00fb30350..41f4bed65 100644 --- a/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties +++ b/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip \ No newline at end of file diff --git a/plugins/aves_report/pubspec.lock b/plugins/aves_report/pubspec.lock index 12a214ea8..8cc8b2f34 100644 --- a/plugins/aves_report/pubspec.lock +++ b/plugins/aves_report/pubspec.lock @@ -58,10 +58,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter diff --git a/plugins/aves_report_console/pubspec.lock b/plugins/aves_report_console/pubspec.lock index 1071d0024..5a171eb34 100644 --- a/plugins/aves_report_console/pubspec.lock +++ b/plugins/aves_report_console/pubspec.lock @@ -65,10 +65,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter diff --git a/plugins/aves_screen_state/android/build.gradle b/plugins/aves_screen_state/android/build.gradle index d9c4eebad..3c3c2c0eb 100644 --- a/plugins/aves_screen_state/android/build.gradle +++ b/plugins/aves_screen_state/android/build.gradle @@ -4,7 +4,7 @@ version '1.0-SNAPSHOT' buildscript { ext { kotlin_version = '1.9.24' - agp_version = '8.6.1' + agp_version = '8.7.0' } repositories { @@ -28,15 +28,14 @@ allprojects { apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +kotlin { + jvmToolchain 17 +} + android { namespace 'deckers.thibault.aves.aves_screen_state' compileSdk 35 - compileOptions { - sourceCompatibility JavaVersion.VERSION_21 - targetCompatibility JavaVersion.VERSION_21 - } - lintOptions { checkAllWarnings true warningsAsErrors true diff --git a/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties b/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties index a10c8212e..1ae95f17d 100644 --- a/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties +++ b/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip \ No newline at end of file diff --git a/plugins/aves_services/lib/aves_services.dart b/plugins/aves_services/lib/aves_services.dart index 5fa5b9381..236de5895 100644 --- a/plugins/aves_services/lib/aves_services.dart +++ b/plugins/aves_services/lib/aves_services.dart @@ -12,12 +12,12 @@ abstract class MobileServices { List get mapStyles; Widget buildMap({ - required AvesMapController? controller, + required AvesMapController controller, required Listenable clusterListenable, required ValueNotifier boundsNotifier, required EntryMapStyle style, required TransitionBuilder decoratorBuilder, - required ButtonPanelBuilder buttonPanelBuilder, + required WidgetBuilder buttonPanelBuilder, required MarkerClusterBuilder markerClusterBuilder, required MarkerWidgetBuilder markerWidgetBuilder, required MarkerImageReadyChecker markerImageReadyChecker, diff --git a/plugins/aves_services/pubspec.lock b/plugins/aves_services/pubspec.lock index 00427b67b..595e6b7ee 100644 --- a/plugins/aves_services/pubspec.lock +++ b/plugins/aves_services/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" aves_map: dependency: "direct main" description: @@ -132,6 +132,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" lints: dependency: transitive description: @@ -192,10 +200,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" polylabel: dependency: transitive description: @@ -237,10 +245,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" term_glyph: dependency: transitive description: @@ -253,10 +261,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" unicode: dependency: transitive description: @@ -273,6 +281,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" web: dependency: transitive description: diff --git a/plugins/aves_services_google/lib/aves_services_platform.dart b/plugins/aves_services_google/lib/aves_services_platform.dart index 23a5ddbfe..9e665c84f 100644 --- a/plugins/aves_services_google/lib/aves_services_platform.dart +++ b/plugins/aves_services_google/lib/aves_services_platform.dart @@ -46,12 +46,12 @@ class PlatformMobileServices extends MobileServices { @override Widget buildMap({ - required AvesMapController? controller, + required AvesMapController controller, required Listenable clusterListenable, required ValueNotifier boundsNotifier, required EntryMapStyle style, required TransitionBuilder decoratorBuilder, - required ButtonPanelBuilder buttonPanelBuilder, + required WidgetBuilder buttonPanelBuilder, required MarkerClusterBuilder markerClusterBuilder, required MarkerWidgetBuilder markerWidgetBuilder, required MarkerImageReadyChecker markerImageReadyChecker, diff --git a/plugins/aves_services_google/lib/src/map.dart b/plugins/aves_services_google/lib/src/map.dart index 26b29f345..ef3e47ad5 100644 --- a/plugins/aves_services_google/lib/src/map.dart +++ b/plugins/aves_services_google/lib/src/map.dart @@ -9,13 +9,13 @@ import 'package:latlong2/latlong.dart' as ll; import 'package:provider/provider.dart'; class EntryGoogleMap extends StatefulWidget { - final AvesMapController? controller; + final AvesMapController controller; final Listenable clusterListenable; final ValueNotifier boundsNotifier; final double? minZoom, maxZoom; final EntryMapStyle style; final TransitionBuilder decoratorBuilder; - final ButtonPanelBuilder buttonPanelBuilder; + final WidgetBuilder buttonPanelBuilder; final MarkerClusterBuilder markerClusterBuilder; final MarkerWidgetBuilder markerWidgetBuilder; final MarkerImageReadyChecker markerImageReadyChecker; @@ -29,7 +29,7 @@ class EntryGoogleMap extends StatefulWidget { const EntryGoogleMap({ super.key, - this.controller, + required this.controller, required this.clusterListenable, required this.boundsNotifier, this.minZoom, @@ -93,10 +93,9 @@ class _EntryGoogleMapState extends State> { void _registerWidget(EntryGoogleMap widget) { final avesMapController = widget.controller; - if (avesMapController != null) { - _subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(_toServiceLatLng(event.latLng)))); - _subscriptions.add(avesMapController.zoomCommands.listen((event) => _zoomBy(event.delta))); - } + _subscriptions.add(avesMapController.moveCommands.listen((event) => _moveTo(_toServiceLatLng(event.latLng)))); + _subscriptions.add(avesMapController.zoomCommands.listen((event) => _zoomBy(event.delta))); + _subscriptions.add(avesMapController.rotationResetCommands.listen((_) => _resetRotation())); widget.clusterListenable.addListener(_updateMarkers); } @@ -125,7 +124,7 @@ class _EntryGoogleMapState extends State> { }, ), widget.decoratorBuilder(context, _buildMap()), - widget.buttonPanelBuilder(_resetRotation), + widget.buttonPanelBuilder(context), ], ); } @@ -241,7 +240,7 @@ class _EntryGoogleMapState extends State> { void _onIdle() { if (!mounted) return; - widget.controller?.notifyIdle(bounds); + widget.controller.notifyIdle(bounds); _updateMarkers(); } diff --git a/plugins/aves_services_google/pubspec.lock b/plugins/aves_services_google/pubspec.lock index f61433ca1..c5a7795f3 100644 --- a/plugins/aves_services_google/pubspec.lock +++ b/plugins/aves_services_google/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" aves_map: dependency: "direct main" description: @@ -89,10 +89,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 + sha256: c4af09051b4f0508f6c1dc0a5c085bf014d5c9a4a0678ce1799c2b4d716387a0 url: "https://pub.dev" source: hosted - version: "10.1.2" + version: "11.1.0" device_info_plus_platform_interface: dependency: transitive description: @@ -211,18 +211,18 @@ packages: dependency: "direct main" description: name: google_maps_flutter_android - sha256: a591ff8d0816436e6a4d9f32bbdf10ebb30bb26f72f6db2a51ddb2426ff7d9ec + sha256: "6caec25edb8014ec7d503babc597794de2d4c1baf3e3d20b57c41bd3e439b916" url: "https://pub.dev" source: hosted - version: "2.14.8" + version: "2.14.10" google_maps_flutter_ios: dependency: transitive description: name: google_maps_flutter_ios - sha256: "3a484846fc56f15e47e3de1f5ea80a7ff2b31721d2faa88f390f3b3cf580c953" + sha256: "753ebf6a2bc24c5eba8e714c901345d858abd9694b1f878c43614fd3f06b8060" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.13.1" google_maps_flutter_platform_interface: dependency: "direct main" description: @@ -279,6 +279,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" lints: dependency: transitive description: @@ -339,10 +347,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" plugin_platform_interface: dependency: transitive description: @@ -408,10 +416,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" term_glyph: dependency: transitive description: @@ -424,10 +432,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" unicode: dependency: transitive description: @@ -444,6 +452,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" web: dependency: transitive description: @@ -456,10 +472,10 @@ packages: dependency: transitive description: name: win32 - sha256: "4d45dc9069dba4619dc0ebd93c7cec5e66d8482cb625a370ac806dcc8165f2ec" + sha256: e1d0cc62e65dc2561f5071fcbccecf58ff20c344f8f3dc7d4922df372a11df1f url: "https://pub.dev" source: hosted - version: "5.5.5" + version: "5.7.1" win32_registry: dependency: transitive description: diff --git a/plugins/aves_services_none/lib/aves_services_platform.dart b/plugins/aves_services_none/lib/aves_services_platform.dart index f7069f805..8f95b7364 100644 --- a/plugins/aves_services_none/lib/aves_services_platform.dart +++ b/plugins/aves_services_none/lib/aves_services_platform.dart @@ -18,12 +18,12 @@ class PlatformMobileServices extends MobileServices { @override Widget buildMap({ - required AvesMapController? controller, + required AvesMapController controller, required Listenable clusterListenable, required ValueNotifier boundsNotifier, required EntryMapStyle style, required TransitionBuilder decoratorBuilder, - required ButtonPanelBuilder buttonPanelBuilder, + required WidgetBuilder buttonPanelBuilder, required MarkerClusterBuilder markerClusterBuilder, required MarkerWidgetBuilder markerWidgetBuilder, required MarkerImageReadyChecker markerImageReadyChecker, diff --git a/plugins/aves_services_none/pubspec.lock b/plugins/aves_services_none/pubspec.lock index 89c52d2fc..3cddaa2bd 100644 --- a/plugins/aves_services_none/pubspec.lock +++ b/plugins/aves_services_none/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" aves_map: dependency: "direct main" description: @@ -139,6 +139,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" lints: dependency: transitive description: @@ -199,10 +207,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" polylabel: dependency: transitive description: @@ -244,10 +252,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" term_glyph: dependency: transitive description: @@ -260,10 +268,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" unicode: dependency: transitive description: @@ -280,6 +288,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" web: dependency: transitive description: diff --git a/plugins/aves_video/lib/src/controller.dart b/plugins/aves_video/lib/src/controller.dart index 8fac10917..c68f3a21f 100644 --- a/plugins/aves_video/lib/src/controller.dart +++ b/plugins/aves_video/lib/src/controller.dart @@ -4,6 +4,7 @@ import 'package:aves_model/aves_model.dart'; import 'package:aves_video/aves_video.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +import 'package:leak_tracker/leak_tracker.dart'; abstract class AvesVideoControllerFactory { void init(); @@ -31,7 +32,7 @@ abstract class AvesVideoController with ABRepeatMixin { required this.settings, }) : _entry = entry { if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: 'aves', className: '$AvesVideoController', object: this, @@ -45,7 +46,7 @@ abstract class AvesVideoController with ABRepeatMixin { assert(!_disposed); _disposed = true; if (kFlutterMemoryAllocationsEnabled) { - FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } abRepeatNotifier.dispose(); _entry.visualChangeNotifier.removeListener(onVisualChanged); @@ -69,6 +70,8 @@ abstract class AvesVideoController with ABRepeatMixin { Future seekToProgress(double progress) => seekTo((duration * progress.clamp(0, 1)).toInt()); + Future skipFrames(int frameCount); + Listenable get playCompletedListenable; VideoStatus get status; diff --git a/plugins/aves_video/lib/src/settings/defaults.dart b/plugins/aves_video/lib/src/settings/defaults.dart index fcdc9b0df..dd457cccc 100644 --- a/plugins/aves_video/lib/src/settings/defaults.dart +++ b/plugins/aves_video/lib/src/settings/defaults.dart @@ -11,7 +11,7 @@ class SettingsDefaults { static const videoLoopMode = VideoLoopMode.shortOnly; static const videoResumptionMode = VideoResumptionMode.ask; static const videoShowRawTimedText = false; - static const videoControls = VideoControls.play; + static const videoControlActions = [EntryAction.videoTogglePlay]; static const videoGestureDoubleTapTogglePlay = false; static const videoGestureSideDoubleTapSeek = true; static const videoGestureVerticalDragBrightnessVolume = false; diff --git a/plugins/aves_video/lib/src/settings/video.dart b/plugins/aves_video/lib/src/settings/video.dart index 11aa335ff..80be2351f 100644 --- a/plugins/aves_video/lib/src/settings/video.dart +++ b/plugins/aves_video/lib/src/settings/video.dart @@ -22,9 +22,9 @@ mixin VideoSettings on SettingsAccess { set videoResumptionMode(VideoResumptionMode newValue) => set(SettingKeys.videoResumptionModeKey, newValue.toString()); - VideoControls get videoControls => getEnumOrDefault(SettingKeys.videoControlsKey, SettingsDefaults.videoControls, VideoControls.values); + List get videoControlActions => getEnumListOrDefault(SettingKeys.videoControlActionsKey, SettingsDefaults.videoControlActions, EntryAction.values); - set videoControls(VideoControls newValue) => set(SettingKeys.videoControlsKey, newValue.toString()); + set videoControlActions(List newValue) => set(SettingKeys.videoControlActionsKey, newValue.map((v) => v.toString()).toList()); bool get videoGestureDoubleTapTogglePlay => getBool(SettingKeys.videoGestureDoubleTapTogglePlayKey) ?? SettingsDefaults.videoGestureDoubleTapTogglePlay; diff --git a/plugins/aves_video/pubspec.lock b/plugins/aves_video/pubspec.lock index 4f186ec74..3bd054f59 100644 --- a/plugins/aves_video/pubspec.lock +++ b/plugins/aves_video/pubspec.lock @@ -23,6 +23,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" collection: dependency: transitive description: @@ -52,6 +60,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + leak_tracker: + dependency: "direct main" + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" lints: dependency: transitive description: @@ -76,6 +92,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -89,5 +113,13 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" sdks: dart: ">=3.5.0 <4.0.0" diff --git a/plugins/aves_video/pubspec.yaml b/plugins/aves_video/pubspec.yaml index 0741f7602..791f9ebe1 100644 --- a/plugins/aves_video/pubspec.yaml +++ b/plugins/aves_video/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: path: ../aves_model aves_utils: path: ../aves_utils + leak_tracker: dev_dependencies: flutter_lints: diff --git a/plugins/aves_video_ffmpeg/pubspec.lock b/plugins/aves_video_ffmpeg/pubspec.lock index fe7b46b57..6e3b9f1a3 100644 --- a/plugins/aves_video_ffmpeg/pubspec.lock +++ b/plugins/aves_video_ffmpeg/pubspec.lock @@ -30,6 +30,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" collection: dependency: transitive description: @@ -76,6 +84,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" lints: dependency: transitive description: @@ -100,6 +116,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" plugin_platform_interface: dependency: transitive description: @@ -121,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" sdks: dart: ">=3.5.0 <4.0.0" flutter: ">=2.0.0" diff --git a/plugins/aves_video_mpv/lib/src/controller.dart b/plugins/aves_video_mpv/lib/src/controller.dart index 65265323c..621f23506 100644 --- a/plugins/aves_video_mpv/lib/src/controller.dart +++ b/plugins/aves_video_mpv/lib/src/controller.dart @@ -59,6 +59,12 @@ class MpvVideoController extends AvesVideoController { title: entry.bestTitle ?? entry.uri, libass: false, logLevel: MPVLogLevel.warn, + protocolWhitelist: [ + ...const PlayerConfiguration().protocolWhitelist, + // Android `content` URIs are considered unsafe by default, + // as they are transferred via a custom `fd` protocol + 'fd', + ], ), ); _initController(); @@ -206,6 +212,20 @@ class MpvVideoController extends AvesVideoController { await _instance.seek(Duration(milliseconds: targetMillis)); } + @override + Future skipFrames(int frameCount) async { + final platform = _instance.platform; + if (platform is NativePlayer) { + if (frameCount > 0) { + await platform.command(['frame-step']); + } else if (frameCount < 0) { + await platform.command(['frame-back-step']); + } + } else { + throw Exception('Platform player ${platform.runtimeType} does not support frame stepping'); + } + } + @override Listenable get playCompletedListenable => _completedNotifier; diff --git a/plugins/aves_video_mpv/pubspec.lock b/plugins/aves_video_mpv/pubspec.lock index 47b08e192..2cc027e8d 100644 --- a/plugins/aves_video_mpv/pubspec.lock +++ b/plugins/aves_video_mpv/pubspec.lock @@ -13,18 +13,18 @@ packages: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.6.0" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" aves_model: dependency: "direct main" description: @@ -74,10 +74,10 @@ packages: dependency: transitive description: name: crypto - sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" dbus: dependency: transitive description: @@ -106,10 +106,10 @@ packages: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -148,10 +148,10 @@ packages: dependency: transitive description: name: image - sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.3.0" js: dependency: transitive description: @@ -160,6 +160,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" lints: dependency: transitive description: @@ -179,10 +187,11 @@ packages: media_kit: dependency: "direct main" description: - name: media_kit - sha256: "1f1deee148533d75129a6f38251ff8388e33ee05fc2d20a6a80e57d6051b7b62" - url: "https://pub.dev" - source: hosted + path: media_kit + ref: "3e7812ac2db4ec19ea501a387c1bf7a9c79bb64e" + resolved-ref: "3e7812ac2db4ec19ea501a387c1bf7a9c79bb64e" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.1.11" media_kit_libs_android_video: dependency: "direct main" @@ -192,21 +201,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.6" - media_kit_native_event_loop: - dependency: "direct main" - description: - name: media_kit_native_event_loop - sha256: "7d82e3b3e9ded5c35c3146c5ba1da3118d1dd8ac3435bac7f29f458181471b40" - url: "https://pub.dev" - source: hosted - version: "1.0.9" media_kit_video: dependency: "direct main" description: - name: media_kit_video - sha256: "2cc3b966679963ba25a4ce5b771e532a521ebde7c6aa20e9802bec95d9916c8f" - url: "https://pub.dev" - source: hosted + path: media_kit_video + ref: "3e7812ac2db4ec19ea501a387c1bf7a9c79bb64e" + resolved-ref: "3e7812ac2db4ec19ea501a387c1bf7a9c79bb64e" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.2.5" meta: dependency: transitive @@ -220,10 +222,10 @@ packages: dependency: transitive description: name: package_info_plus - sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 + sha256: df3eb3e0aed5c1107bb0fdb80a8e82e778114958b1c5ac5644fb1ac9cae8a998 url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "8.1.0" package_info_plus_platform_interface: dependency: transitive description: @@ -236,10 +238,10 @@ packages: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" petitparser: dependency: transitive description: @@ -260,10 +262,10 @@ packages: dependency: transitive description: name: safe_local_storage - sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440 + sha256: e9a21b6fec7a8aa62cc2585ff4c1b127df42f3185adbd2aca66b47abe2e80236 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "2.0.1" screen_brightness: dependency: transitive description: @@ -337,10 +339,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" synchronized: dependency: transitive description: @@ -361,10 +363,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" universal_platform: dependency: transitive description: @@ -377,10 +379,10 @@ packages: dependency: transitive description: name: uri_parser - sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835" + sha256: ff4d2c720aca3f4f7d5445e23b11b2d15ef8af5ddce5164643f38ff962dcb270 url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "3.0.0" uuid: dependency: transitive description: @@ -397,6 +399,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" volume_controller: dependency: transitive description: @@ -433,10 +443,10 @@ packages: dependency: transitive description: name: win32 - sha256: "4d45dc9069dba4619dc0ebd93c7cec5e66d8482cb625a370ac806dcc8165f2ec" + sha256: e1d0cc62e65dc2561f5071fcbccecf58ff20c344f8f3dc7d4922df372a11df1f url: "https://pub.dev" source: hosted - version: "5.5.5" + version: "5.7.1" xml: dependency: transitive description: diff --git a/plugins/aves_video_mpv/pubspec.yaml b/plugins/aves_video_mpv/pubspec.yaml index eb1c6374c..73fce6ec4 100644 --- a/plugins/aves_video_mpv/pubspec.yaml +++ b/plugins/aves_video_mpv/pubspec.yaml @@ -17,10 +17,21 @@ dependencies: collection: media_kit: media_kit_libs_android_video: - media_kit_native_event_loop: media_kit_video: path: +dependency_overrides: + media_kit: + git: + url: https://github.com/media-kit/media-kit.git + ref: 18f155fe81cae93db712307411a17b2b93760eb6 + path: media_kit + media_kit_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: 18f155fe81cae93db712307411a17b2b93760eb6 + path: media_kit_video + dev_dependencies: flutter_lints: diff --git a/pubspec.lock b/pubspec.lock index f2531dd8a..aa22e10e2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,10 +42,10 @@ packages: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.6.0" async: dependency: transitive description: @@ -215,10 +215,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "2056db5241f96cdc0126bd94459fc4cdc13876753768fc7a31c425e50a7177d0" + sha256: "876849631b0c7dc20f8b471a2a03142841b482438e3b707955464f5ffca3e4c3" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.0" connectivity_plus_platform_interface: dependency: transitive description: @@ -231,10 +231,10 @@ packages: dependency: transitive description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" country_code: dependency: "direct main" description: @@ -247,18 +247,18 @@ packages: dependency: transitive description: name: coverage - sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5 + sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268" url: "https://pub.dev" source: hosted - version: "1.9.2" + version: "1.10.0" crypto: dependency: transitive description: name: crypto - sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" csslib: dependency: transitive description: @@ -303,10 +303,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 + sha256: c4af09051b4f0508f6c1dc0a5c085bf014d5c9a4a0678ce1799c2b4d716387a0 url: "https://pub.dev" source: hosted - version: "10.1.2" + version: "11.1.0" device_info_plus_platform_interface: dependency: transitive description: @@ -449,10 +449,10 @@ packages: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" flex_color_picker: dependency: "direct main" description: @@ -473,10 +473,10 @@ packages: dependency: "direct main" description: name: floating - sha256: "62924c96915823ce955da32b26766e11583ce437990a601032191d6d4b0b0764" + sha256: aaebbbf0cef0454f68476edb6d79ca9734ee0e0080a03cee75db42fcc462efb5 url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "5.0.0" fluster: dependency: "direct main" description: @@ -527,20 +527,20 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" - flutter_localization_nn: + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_localizations_plus: dependency: "direct main" description: path: "." ref: HEAD - resolved-ref: f35b88327b98a1494426cf40ff7c30281cc7f937 - url: "https://github.com/deckerst/flutter_localization_nn.git" + resolved-ref: "3ce50168ea196acc8d18e09377a3e88bb13cb125" + url: "https://github.com/deckerst/flutter_localizations_plus.git" source: git version: "0.0.1" - flutter_localizations: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" flutter_map: dependency: "direct main" description: @@ -553,10 +553,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: e17575ca576a34b46c58c91f9948891117a1bd97815d2e661813c7f90c647a78 + sha256: f0e599ba89c9946c8e051780f0ec99aba4ba15895e0380a7ab68f420046fc44e url: "https://pub.dev" source: hosted - version: "0.7.3+2" + version: "0.7.4+1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -600,10 +600,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: ff97e5e7b2e82e63c82f5658c6ba2605ea831f0f7489b0d2fb255d817ec4eb5e + sha256: "35c253f83f9e03cbac65ffa159510e41ae15f49b37291ab8c522d7a0b6f330cd" url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "8.0.1" glob: dependency: transitive description: @@ -656,18 +656,18 @@ packages: dependency: transitive description: name: google_maps_flutter_android - sha256: a591ff8d0816436e6a4d9f32bbdf10ebb30bb26f72f6db2a51ddb2426ff7d9ec + sha256: "6caec25edb8014ec7d503babc597794de2d4c1baf3e3d20b57c41bd3e439b916" url: "https://pub.dev" source: hosted - version: "2.14.8" + version: "2.14.10" google_maps_flutter_ios: dependency: transitive description: name: google_maps_flutter_ios - sha256: "3a484846fc56f15e47e3de1f5ea80a7ff2b31721d2faa88f390f3b3cf580c953" + sha256: "753ebf6a2bc24c5eba8e714c901345d858abd9694b1f878c43614fd3f06b8060" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.13.1" google_maps_flutter_platform_interface: dependency: transitive description: @@ -728,10 +728,10 @@ packages: dependency: transitive description: name: image - sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.3.0" intl: dependency: "direct main" description: @@ -752,10 +752,10 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.7.1" latlong2: dependency: "direct main" description: @@ -856,10 +856,10 @@ packages: dependency: transitive description: name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" macros: dependency: transitive description: @@ -901,12 +901,13 @@ packages: source: hosted version: "7.0.7296" media_kit: - dependency: transitive + dependency: "direct overridden" description: - name: media_kit - sha256: "1f1deee148533d75129a6f38251ff8388e33ee05fc2d20a6a80e57d6051b7b62" - url: "https://pub.dev" - source: hosted + path: media_kit + ref: "18f155fe81cae93db712307411a17b2b93760eb6" + resolved-ref: "18f155fe81cae93db712307411a17b2b93760eb6" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.1.11" media_kit_libs_android_video: dependency: transitive @@ -916,21 +917,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.6" - media_kit_native_event_loop: - dependency: transitive - description: - name: media_kit_native_event_loop - sha256: "7d82e3b3e9ded5c35c3146c5ba1da3118d1dd8ac3435bac7f29f458181471b40" - url: "https://pub.dev" - source: hosted - version: "1.0.9" media_kit_video: dependency: "direct overridden" description: - name: media_kit_video - sha256: "2cc3b966679963ba25a4ce5b771e532a521ebde7c6aa20e9802bec95d9916c8f" - url: "https://pub.dev" - source: hosted + path: media_kit_video + ref: "18f155fe81cae93db712307411a17b2b93760eb6" + resolved-ref: "18f155fe81cae93db712307411a17b2b93760eb6" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.2.5" meta: dependency: transitive @@ -977,10 +971,10 @@ packages: dependency: "direct main" description: name: network_info_plus - sha256: "6a31fa47c1f6e240f1b60de0a57d65a092ac1af7515247660f03643576984eb8" + sha256: "89bad7bf9614e78716f0f86c905fe2a850dbdcc00c377968d5260c49c2c6f2eb" url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.1.0" network_info_plus_platform_interface: dependency: transitive description: @@ -1025,10 +1019,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 + sha256: df3eb3e0aed5c1107bb0fdb80a8e82e778114958b1c5ac5644fb1ac9cae8a998 url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "8.1.0" package_info_plus_platform_interface: dependency: transitive description: @@ -1066,10 +1060,10 @@ packages: dependency: transitive description: name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + sha256: "45f7d6bba1128761de5540f39d5ca000ea8a1f22f06b76b61094a60a2997bd0e" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" path_provider: dependency: transitive description: @@ -1162,10 +1156,10 @@ packages: dependency: transitive description: name: permission_handler_android - sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa" + sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1" url: "https://pub.dev" source: hosted - version: "12.0.12" + version: "12.0.13" permission_handler_apple: dependency: transitive description: @@ -1306,10 +1300,10 @@ packages: dependency: transitive description: name: safe_local_storage - sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440 + sha256: e9a21b6fec7a8aa62cc2585ff4c1b127df42f3185adbd2aca66b47abe2e80236 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "2.0.1" sanitize_html: dependency: transitive description: @@ -1322,50 +1316,50 @@ packages: dependency: "direct main" description: name: screen_brightness - sha256: "7d4ac84ae26b37c01d6f5db7123a72db7933e1f2a2a8c369a51e08f81b3178d8" + sha256: a43fdbccd5b90044f68057412dde7715cd7499a4c24f5d5da7e01ed4cf41e0af url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0+2" screen_brightness_android: dependency: transitive description: name: screen_brightness_android - sha256: "8c69d3ac475e4d625e7fa682a3a51a69ff59abe5b4a9e57f6ec7d830a6c69bd6" + sha256: "74455f9901ab8a1a45c9097b83855dbbb7498110cc2bc249cb5a86570dd1cf7c" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0" screen_brightness_ios: dependency: transitive description: name: screen_brightness_ios - sha256: f08f70ca1ac3e30719764b5cfb8b3fe1e28163065018a41b3e6f243ab146c2f1 + sha256: caee02b34e0089b138a7aee35c461bd2d7c78446dd417f07613def192598ca08 url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0" screen_brightness_macos: dependency: transitive description: name: screen_brightness_macos - sha256: "70c2efa4534e22b927e82693488f127dd4a0f008469fccf4f0eefe9061bbdd6a" + sha256: "84fc8ffcbcf19c03d76b7673b0f2c2a2663c09aa2bc37c76ea83ab049294a97a" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0" screen_brightness_platform_interface: dependency: transitive description: name: screen_brightness_platform_interface - sha256: "9f3ebf7f22d5487e7676fe9ddaf3fc55b6ff8057707cf6dc0121c7dfda346a16" + sha256: "321e9455b0057e3647fd37700931e063739d94a8aa1b094f98133c01cb56c27b" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0" screen_brightness_windows: dependency: transitive description: name: screen_brightness_windows - sha256: c8e12a91cf6dd912a48bd41fcf749282a51afa17f536c3460d8d05702fb89ffa + sha256: fa97ae838c42f762f04d2d70adb3d957350d6a84e3598ec800e269e7c466eedd url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0" shared_preferences: dependency: "direct main" description: @@ -1640,10 +1634,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" unicode: dependency: transitive description: @@ -1664,10 +1658,10 @@ packages: dependency: transitive description: name: uri_parser - sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835" + sha256: ff4d2c720aca3f4f7d5445e23b11b2d15ef8af5ddce5164643f38ff962dcb270 url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "3.0.0" url_launcher: dependency: "direct main" description: @@ -1680,10 +1674,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "8fc3bae0b68c02c47c5c86fa8bfa74471d42687b0eded01b78de87872db745e2" + sha256: "0dea215895a4d254401730ca0ba8204b29109a34a99fb06ae559a2b60988d2de" url: "https://pub.dev" source: hosted - version: "6.3.12" + version: "6.3.13" url_launcher_ios: dependency: transitive description: @@ -1728,10 +1722,10 @@ packages: dependency: transitive description: name: url_launcher_windows - sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" uuid: dependency: transitive description: @@ -1856,10 +1850,10 @@ packages: dependency: transitive description: name: win32 - sha256: "4d45dc9069dba4619dc0ebd93c7cec5e66d8482cb625a370ac806dcc8165f2ec" + sha256: e1d0cc62e65dc2561f5071fcbccecf58ff20c344f8f3dc7d4922df372a11df1f url: "https://pub.dev" source: hosted - version: "5.5.5" + version: "5.7.1" win32_registry: dependency: transitive description: @@ -1902,4 +1896,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.5.0 <4.0.0" - flutter: ">=3.24.3" + flutter: ">=3.24.4" diff --git a/pubspec.yaml b/pubspec.yaml index 1d8cd945c..38ff93e9b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,13 +7,13 @@ repository: https://github.com/deckerst/aves # - play changelog: /whatsnew/whatsnew-en-US # - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt # - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt -version: 1.11.16+135 +version: 1.11.17+136 publish_to: none environment: # this project bundles Flutter SDK via `flutter_wrapper` # cf https://github.com/passsy/flutter_wrapper - flutter: 3.24.3 + flutter: 3.24.4 sdk: '>=3.5.0 <4.0.0' # use `scripts/apply_flavor_{flavor}.sh` to set the right dependencies for the flavor @@ -71,9 +71,9 @@ dependencies: fluster: flutter_displaymode: flutter_highlight: - flutter_localization_nn: + flutter_localizations_plus: git: - url: https://github.com/deckerst/flutter_localization_nn.git + url: https://github.com/deckerst/flutter_localizations_plus.git flutter_map: flutter_markdown: flutter_staggered_animations: @@ -119,9 +119,16 @@ dependencies: xml: dependency_overrides: - # media_kit_video v1.2.4 depends on a specific old version of screen_brightness - media_kit_video: ^1.0.0 - screen_brightness: ^1.0.0 + media_kit: + git: + url: https://github.com/media-kit/media-kit.git + ref: 18f155fe81cae93db712307411a17b2b93760eb6 + path: media_kit + media_kit_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: 18f155fe81cae93db712307411a17b2b93760eb6 + path: media_kit_video dev_dependencies: flutter_test: diff --git a/shaders.sksl.json b/shaders.sksl.json index 9a9499fda..d87aea7e6 100644 --- a/shaders.sksl.json +++ b/shaders.sksl.json @@ -1 +1 @@ -{"platform":"android","name":"XQ DE72","engineRevision":"36335019a8eab588c3c2ea783c618d90505be233","data":{"AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAEMCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","B2ABSAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABGABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1PDAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGhhbGY0IHVDb2xvcl9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGYgaW5Db3ZlcmFnZTsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gdUNvbG9yX1MwOwoJY29sb3IgPSBjb2xvciAqIGluQ292ZXJhZ2U7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8zX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzFfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAUQEAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIERlZmF1bHRHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAoAAABpblBvc2l0aW9uAAAKAAAAaW5Db3ZlcmFnZQAAAAAAAA==","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAIIBAAAABAAAAALIEAAAAAAAAAAAAAABQAMAAAABAAAAAAABBAMAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAL8CAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCkucnJycjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZJAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAABAAAAAABBAMABQAMAAAABAAAAAAABBAMAAA":"DAAAAExTS1N3AQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAApAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpLCBvdXRwdXRDb2xvcl9TMCkpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1N4AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0MiB1QXRsYXNTaXplSW52X1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gdXNob3J0MiBpblRleHR1cmVDb29yZHM7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQml0bWFwVGV4dAoJaW50IHRleElkeCA9IDA7CglmbG9hdDIgdW5vcm1UZXhDb29yZHMgPSBmbG9hdDIoaW5UZXh0dXJlQ29vcmRzLngsIGluVGV4dHVyZUNvb3Jkcy55KTsKCXZUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHMgKiB1QXRsYXNTaXplSW52X1MwOwoJdlRleEluZGV4X1MwID0gZmxvYXQodGV4SWR4KTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwMTsKfQoAAAAACwIAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZUZXhJbmRleF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABZQA6AAAEAAAAAAAMADAAAAAIAAAAAAAIIDA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAQAAAOgDAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglhbHBoYSA9IDEuMCAtIGFscGhhOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAACAB4QA4AAAEAAAAAAAMADAAAAAIAAAAAAAIID":"DAAAAExTS1N4AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0MiB1QXRsYXNTaXplSW52X1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gdXNob3J0MiBpblRleHR1cmVDb29yZHM7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQml0bWFwVGV4dAoJaW50IHRleElkeCA9IDA7CglmbG9hdDIgdW5vcm1UZXhDb29yZHMgPSBmbG9hdDIoaW5UZXh0dXJlQ29vcmRzLngsIGluVGV4dHVyZUNvb3Jkcy55KTsKCXZUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHMgKiB1QXRsYXNTaXplSW52X1MwOwoJdlRleEluZGV4X1MwID0gZmxvYXQodGV4SWR4KTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwMTsKfQoBAAAAmgMAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MxOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMTsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","AYQQ5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAANQCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmIGRpc3RhbmNlVG9Jbm5lckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqIChkIC0gY2lyY2xlRWRnZS53KSk7CgloYWxmIGlubmVyQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvSW5uZXJFZGdlKTsKCWVkZ2VBbHBoYSAqPSBpbm5lckFscGhhOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAADvAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJIBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAGIAAAAAAAIAAAAAQGIAAAAAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAJhwAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb2xvcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb21wb3NpdGVfMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jZW50ZXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9tYXhfcmFkaXVzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfbm9pc2Vfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9ub2lzZV9waGFzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUxX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlM19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24yX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uM19TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpOwp2b2lkIEZMVF9tYWluX1MxX2MwKCk7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCkgCnsKCXJldHVybiBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMC54eTsKfQpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKSAKewoJcmV0dXJuIGZsb2F0MngyKHJhZC54LCAtcmFkLnksIHJhZC55LCByYWQueCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cikgCnsKCWZsb2F0IGJsdXJfaGFsZiA9IGJsdXIgKiAwLjU7CglmbG9hdCBkID0gZGlzdGFuY2UodXYsIHh5KTsKCXJldHVybiAxLjAgLSBzbW9vdGhzdGVwKDEuMCAtIGJsdXJfaGFsZiwgMS4wICsgYmx1cl9oYWxmLCBkIC8gcmFkaXVzKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKSAKewoJZmxvYXQyIHBhcmFtID0gcm90YXRpb247CglwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAocGFyYW0pICogKHh5IC0gcCkgKyB4eTsKCXAgPSBtb2QocCwgZmxvYXQyKGNlbGxfZGlhbWV0ZXIpKSAvIHJlc29sdXRpb247CglmbG9hdCBjZWxsX3V2ID0gKGNlbGxfZGlhbWV0ZXIgLyByZXNvbHV0aW9uLnkpICogMC41OwoJZmxvYXQgciA9IDAuNjUgKiBjZWxsX3V2OwoJZmxvYXQyIHBhcmFtXzEgPSBwOwoJZmxvYXQyIHBhcmFtXzIgPSBmbG9hdDIoY2VsbF91dik7CglmbG9hdCBwYXJhbV8zID0gcjsKCWZsb2F0IHBhcmFtXzQgPSByICogNTAuMDsKCXJldHVybiBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dikgCnsKCWZsb2F0MiB1dl9zY2FsZSA9IHV2ICogZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW0gPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMiA9IHVfY2lyY2xlMV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8zID0gdV9yb3RhdGlvbjFfUzFfYzA7CglmbG9hdCBwYXJhbV80ID0gMC4xNzsKCWZsb2F0IF8zMTkgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7CglmbG9hdCBnMSA9IF8zMTk7CglmbG9hdDIgcGFyYW1fNSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzYgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV83ID0gdV9jaXJjbGUyX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzggPSB1X3JvdGF0aW9uMl9TMV9jMDsKCWZsb2F0IHBhcmFtXzkgPSAwLjI7CglmbG9hdCBfMzMxID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBnMiA9IF8zMzE7CglmbG9hdDIgcGFyYW1fMTAgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzEyID0gdV9jaXJjbGUzX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdV9yb3RhdGlvbjNfUzFfYzA7CglmbG9hdCBwYXJhbV8xNCA9IDAuMjc1OwoJZmxvYXQgXzM0NCA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIsIHBhcmFtXzEzLCBwYXJhbV8xNCk7CglmbG9hdCBnMyA9IF8zNDQ7CglmbG9hdCB2ID0gKChnMSAqIGcxICsgZzIpIC0gZzMpICogMC41OwoJcmV0dXJuIGNsYW1wKDAuNDUgKyAwLjggKiB2LCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cikgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQyIHBhcmFtXzEgPSB4eTsKCWZsb2F0IHBhcmFtXzIgPSByYWRpdXMgKyB0aGlja25lc3M7CglmbG9hdCBwYXJhbV8zID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9vdXRlciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0MiBwYXJhbV80ID0gdXY7CglmbG9hdDIgcGFyYW1fNSA9IHh5OwoJZmxvYXQgcGFyYW1fNiA9IG1heChyYWRpdXMgLSB0aGlja25lc3MsIDAuMCk7CglmbG9hdCBwYXJhbV83ID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9pbm5lciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcpOwoJcmV0dXJuIGNsYW1wKGNpcmNsZV9vdXRlciAtIGNpcmNsZV9pbm5lciwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKSAKewoJbiA9IGZyYWN0KG4gKiBmbG9hdDIoNS4zOTg3LCA1LjQ0MjEpKTsKCW4gKz0gZmxvYXQyKGRvdChuLnl4LCBuICsgZmxvYXQyKDIxLjUzNTEsIDE0LjMxMzcpKSk7CglmbG9hdCB4eSA9IG4ueCAqIG4ueTsKCXJldHVybiAoZnJhY3QoeHkgKiA5NS40MzA3KSArIGZyYWN0KHh5ICogNzUuMDQ5NjEpKSAtIDEuMDsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCkgCnsKCXJldHVybiBzdGVwKGwsIHYpICogKDEuMCAtIHN0ZXAoaCwgdikpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdCBfMjQyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAocGFyYW0pOwoJZmxvYXQgbiA9IF8yNDI7CglmbG9hdCBwYXJhbV8xID0gbjsKCWZsb2F0IHBhcmFtXzIgPSAwLjA7CglmbG9hdCBwYXJhbV8zID0gMC4wNTsKCWZsb2F0IHMgPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdCBwYXJhbV80ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjM1KSk7CglmbG9hdCBwYXJhbV81ID0gMC4xOwoJZmxvYXQgcGFyYW1fNiA9IDAuMTU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82KTsKCWZsb2F0IHBhcmFtXzcgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuNykpOwoJZmxvYXQgcGFyYW1fOCA9IDAuMjsKCWZsb2F0IHBhcmFtXzkgPSAwLjI1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBwYXJhbV8xMCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMS4wNSkpOwoJZmxvYXQgcGFyYW1fMTEgPSAwLjM7CglmbG9hdCBwYXJhbV8xMiA9IDAuMzU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyKTsKCXJldHVybiBjbGFtcChzLCAwLjAsIDEuMCkgKiAwLjU1Owp9CnZvaWQgRkxUX21haW5fUzFfYzAoKSAKewoJdV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueDsKCXVfc3BhcmtsZV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueTsKCXVfYmx1cl9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAuejsKCXVfcmFkaXVzX3NjYWxlX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC53OwoJZmxvYXQyIHAgPSBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CglmbG9hdDIgdXZfMSA9IHAgKiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CglmbG9hdDIgZGVuc2l0eV91diA9IHV2XzEgLSBtb2QocCwgdV9ub2lzZV9zY2FsZV9TMV9jMCk7CglmbG9hdCByYWRpdXMgPSB1X21heF9yYWRpdXNfUzFfYzAgKiB1X3JhZGl1c19zY2FsZV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHV2XzE7CglmbG9hdCB0dXJidWxlbmNlID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChwYXJhbV8xMyk7CglmbG9hdDIgcGFyYW1fMTQgPSBwOwoJZmxvYXQyIHBhcmFtXzE1ID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8xNiA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzE3ID0gMC4wNSAqIHVfbWF4X3JhZGl1c19TMV9jMDsKCWZsb2F0IHBhcmFtXzE4ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgcmluZyA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChwYXJhbV8xNCwgcGFyYW1fMTUsIHBhcmFtXzE2LCBwYXJhbV8xNywgcGFyYW1fMTgpOwoJZmxvYXQyIHBhcmFtXzE5ID0gZGVuc2l0eV91djsKCWZsb2F0IHBhcmFtXzIwID0gdV9ub2lzZV9waGFzZV9TMV9jMDsKCWZsb2F0IHNwYXJrbGUgPSAoKEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAocGFyYW1fMTksIHBhcmFtXzIwKSAqIHJpbmcpICogdHVyYnVsZW5jZSkgKiB1X3NwYXJrbGVfYWxwaGFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMjEgPSBwOwoJZmxvYXQyIHBhcmFtXzIyID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8yMyA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzI0ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgd2F2ZV9hbHBoYSA9IChGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8yMSwgcGFyYW1fMjIsIHBhcmFtXzIzLCBwYXJhbV8yNCkgKiB1X2FscGhhX1MxX2MwKSAqIHVfY29sb3JfUzFfYzAudzsKCWZsb2F0NCB3YXZlX2NvbG9yID0gZmxvYXQ0KHVfY29sb3JfUzFfYzAueHl6ICogd2F2ZV9hbHBoYSwgd2F2ZV9hbHBoYSk7CglmcmFnQ29sb3JfUzFfYzAgPSBtaXgod2F2ZV9jb2xvciwgZmxvYXQ0KDEuMCksIGZsb2F0NChzcGFya2xlKSk7Cn0KaGFsZjQgcnVudGltZV9zaGFkZXJfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAgPSBmbG9hdDQoX3RtcF8xX2Nvb3JkcywgMC4wLCAwLjApOwoJRkxUX21haW5fUzFfYzAoKTsKCXJldHVybiBoYWxmNChoYWxmNChmcmFnQ29sb3JfUzFfYzApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHJ1bnRpbWVfc2hhZGVyX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAA6IAMAAACAAAAAABGABAAAAAEAAAAAIBEABAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAAtR0AACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb2xvcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb21wb3NpdGVfMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jZW50ZXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9tYXhfcmFkaXVzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfbm9pc2Vfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9ub2lzZV9waGFzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUxX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlM19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24yX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uM19TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MyOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMjsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KTsKdm9pZCBGTFRfbWFpbl9TMV9jMCgpOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpIAp7CglyZXR1cm4gZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAueHk7Cn0KZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCkgCnsKCXJldHVybiBmbG9hdDJ4MihyYWQueCwgLXJhZC55LCByYWQueSwgcmFkLngpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpIAp7CglmbG9hdCBibHVyX2hhbGYgPSBibHVyICogMC41OwoJZmxvYXQgZCA9IGRpc3RhbmNlKHV2LCB4eSk7CglyZXR1cm4gMS4wIC0gc21vb3Roc3RlcCgxLjAgLSBibHVyX2hhbGYsIDEuMCArIGJsdXJfaGFsZiwgZCAvIHJhZGl1cyk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcikgCnsKCWZsb2F0MiBwYXJhbSA9IHJvdGF0aW9uOwoJcCA9IEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKHBhcmFtKSAqICh4eSAtIHApICsgeHk7CglwID0gbW9kKHAsIGZsb2F0MihjZWxsX2RpYW1ldGVyKSkgLyByZXNvbHV0aW9uOwoJZmxvYXQgY2VsbF91diA9IChjZWxsX2RpYW1ldGVyIC8gcmVzb2x1dGlvbi55KSAqIDAuNTsKCWZsb2F0IHIgPSAwLjY1ICogY2VsbF91djsKCWZsb2F0MiBwYXJhbV8xID0gcDsKCWZsb2F0MiBwYXJhbV8yID0gZmxvYXQyKGNlbGxfdXYpOwoJZmxvYXQgcGFyYW1fMyA9IHI7CglmbG9hdCBwYXJhbV80ID0gciAqIDUwLjA7CglyZXR1cm4gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpIAp7CglmbG9hdDIgdXZfc2NhbGUgPSB1diAqIGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzIgPSB1X2NpcmNsZTFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMyA9IHVfcm90YXRpb24xX1MxX2MwOwoJZmxvYXQgcGFyYW1fNCA9IDAuMTc7CglmbG9hdCBfMzE5ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwoJZmxvYXQgZzEgPSBfMzE5OwoJZmxvYXQyIHBhcmFtXzUgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV82ID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fNyA9IHVfY2lyY2xlMl9TMV9jMDsKCWZsb2F0MiBwYXJhbV84ID0gdV9yb3RhdGlvbjJfUzFfYzA7CglmbG9hdCBwYXJhbV85ID0gMC4yOwoJZmxvYXQgXzMzMSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgZzIgPSBfMzMxOwoJZmxvYXQyIHBhcmFtXzEwID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMTEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8xMiA9IHVfY2lyY2xlM19TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHVfcm90YXRpb24zX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTQgPSAwLjI3NTsKCWZsb2F0IF8zNDQgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyLCBwYXJhbV8xMywgcGFyYW1fMTQpOwoJZmxvYXQgZzMgPSBfMzQ0OwoJZmxvYXQgdiA9ICgoZzEgKiBnMSArIGcyKSAtIGczKSAqIDAuNTsKCXJldHVybiBjbGFtcCgwLjQ1ICsgMC44ICogdiwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0MiBwYXJhbV8xID0geHk7CglmbG9hdCBwYXJhbV8yID0gcmFkaXVzICsgdGhpY2tuZXNzOwoJZmxvYXQgcGFyYW1fMyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfb3V0ZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdDIgcGFyYW1fNCA9IHV2OwoJZmxvYXQyIHBhcmFtXzUgPSB4eTsKCWZsb2F0IHBhcmFtXzYgPSBtYXgocmFkaXVzIC0gdGhpY2tuZXNzLCAwLjApOwoJZmxvYXQgcGFyYW1fNyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfaW5uZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83KTsKCXJldHVybiBjbGFtcChjaXJjbGVfb3V0ZXIgLSBjaXJjbGVfaW5uZXIsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbikgCnsKCW4gPSBmcmFjdChuICogZmxvYXQyKDUuMzk4NywgNS40NDIxKSk7CgluICs9IGZsb2F0Mihkb3Qobi55eCwgbiArIGZsb2F0MigyMS41MzUxLCAxNC4zMTM3KSkpOwoJZmxvYXQgeHkgPSBuLnggKiBuLnk7CglyZXR1cm4gKGZyYWN0KHh5ICogOTUuNDMwNykgKyBmcmFjdCh4eSAqIDc1LjA0OTYxKSkgLSAxLjA7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpIAp7CglyZXR1cm4gc3RlcChsLCB2KSAqICgxLjAgLSBzdGVwKGgsIHYpKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCkgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQgXzI0MiA9IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKHBhcmFtKTsKCWZsb2F0IG4gPSBfMjQyOwoJZmxvYXQgcGFyYW1fMSA9IG47CglmbG9hdCBwYXJhbV8yID0gMC4wOwoJZmxvYXQgcGFyYW1fMyA9IDAuMDU7CglmbG9hdCBzID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQgcGFyYW1fNCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC4zNSkpOwoJZmxvYXQgcGFyYW1fNSA9IDAuMTsKCWZsb2F0IHBhcmFtXzYgPSAwLjE1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNik7CglmbG9hdCBwYXJhbV83ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjcpKTsKCWZsb2F0IHBhcmFtXzggPSAwLjI7CglmbG9hdCBwYXJhbV85ID0gMC4yNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgcGFyYW1fMTAgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDEuMDUpKTsKCWZsb2F0IHBhcmFtXzExID0gMC4zOwoJZmxvYXQgcGFyYW1fMTIgPSAwLjM1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMik7CglyZXR1cm4gY2xhbXAocywgMC4wLCAxLjApICogMC41NTsKfQp2b2lkIEZMVF9tYWluX1MxX2MwKCkgCnsKCXVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLng7Cgl1X3NwYXJrbGVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnk7Cgl1X2JsdXJfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLno7Cgl1X3JhZGl1c19zY2FsZV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAudzsKCWZsb2F0MiBwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwoJZmxvYXQyIHV2XzEgPSBwICogdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwoJZmxvYXQyIGRlbnNpdHlfdXYgPSB1dl8xIC0gbW9kKHAsIHVfbm9pc2Vfc2NhbGVfUzFfYzApOwoJZmxvYXQgcmFkaXVzID0gdV9tYXhfcmFkaXVzX1MxX2MwICogdV9yYWRpdXNfc2NhbGVfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1dl8xOwoJZmxvYXQgdHVyYnVsZW5jZSA9IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAocGFyYW1fMTMpOwoJZmxvYXQyIHBhcmFtXzE0ID0gcDsKCWZsb2F0MiBwYXJhbV8xNSA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTYgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8xNyA9IDAuMDUgKiB1X21heF9yYWRpdXNfUzFfYzA7CglmbG9hdCBwYXJhbV8xOCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHJpbmcgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAocGFyYW1fMTQsIHBhcmFtXzE1LCBwYXJhbV8xNiwgcGFyYW1fMTcsIHBhcmFtXzE4KTsKCWZsb2F0MiBwYXJhbV8xOSA9IGRlbnNpdHlfdXY7CglmbG9hdCBwYXJhbV8yMCA9IHVfbm9pc2VfcGhhc2VfUzFfYzA7CglmbG9hdCBzcGFya2xlID0gKChGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKHBhcmFtXzE5LCBwYXJhbV8yMCkgKiByaW5nKSAqIHR1cmJ1bGVuY2UpICogdV9zcGFya2xlX2FscGhhX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzIxID0gcDsKCWZsb2F0MiBwYXJhbV8yMiA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMjMgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8yNCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHdhdmVfYWxwaGEgPSAoRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMjEsIHBhcmFtXzIyLCBwYXJhbV8yMywgcGFyYW1fMjQpICogdV9hbHBoYV9TMV9jMCkgKiB1X2NvbG9yX1MxX2MwLnc7CglmbG9hdDQgd2F2ZV9jb2xvciA9IGZsb2F0NCh1X2NvbG9yX1MxX2MwLnh5eiAqIHdhdmVfYWxwaGEsIHdhdmVfYWxwaGEpOwoJZnJhZ0NvbG9yX1MxX2MwID0gbWl4KHdhdmVfY29sb3IsIGZsb2F0NCgxLjApLCBmbG9hdDQoc3BhcmtsZSkpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwID0gZmxvYXQ0KF90bXBfMV9jb29yZHMsIDAuMCwgMC4wKTsKCUZMVF9tYWluX1MxX2MwKCk7CglyZXR1cm4gaGFsZjQoaGFsZjQoZnJhZ0NvbG9yX1MxX2MwKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJaGFsZjQgb3V0cHV0X1MyOwoJb3V0cHV0X1MyID0gQ2lyY3VsYXJSUmVjdF9TMihvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0X1MyOwoJfQp9CgAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GYQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1MaAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAVwEAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAAAAAA","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBIA5HZ2CAAAAAAAEAQCAAAAASBEEAQAAAAAUCCQAQQGAABAEAAAAAAGMAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1POAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAcwQAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZCA9IGNsYW1wKHN1YnNldENvb3JkLCB1Y2xhbXBfUzFfYzBfYzAueHksIHVjbGFtcF9TMV9jMF9jMC56dyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gTWF0cml4RWZmZWN0X1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBIA5HZ2CAAAAAAAAAACAAAAASAEAAQAAAAAUCCQAQQGAAAAAAAAAAAGMAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1POAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAYAMAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKGhhbGY0IF9pbnB1dCkgCnsKCV9pbnB1dCA9IE1hdHJpeEVmZmVjdF9TMV9jMChfaW5wdXQpOwoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglyZXR1cm4gaGFsZjQoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAJIBAGMVUOTUABAAAAAAAIAAAAAWFBIEMVUOTUAAAAAAAAAIAAAACICAACAAAAACQIKACCAYAAAAAAAAAAAZQAAAAAABAAAAACAZAAAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAKkFAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKY29uc3QgaW50IGtNYXhMb29wTGltaXRfUzFfYzAgPSA4Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CnVuaWZvcm0gaGFsZjQgdW9mZnNldHNBbmRLZXJuZWxfUzFfYzBbMTRdOwp1bmlmb3JtIGhhbGYyIHVkaXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIF9jb29yZHMpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CgloYWxmNCBzdW0gPSBoYWxmNCgwLjApOwoJZm9yIChpbnQgaSA9IDA7aSA8IGtNYXhMb29wTGltaXRfUzFfYzA7ICsraSkgCgl7CgkJaGFsZjQgcyA9IHVvZmZzZXRzQW5kS2VybmVsX1MxX2MwW2ldOwoJCXN1bSArPSBzLnkgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIocy54ICogdWRpcl9TMV9jMCkpOwoJCXN1bSArPSBzLncgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIocy56ICogdWRpcl9TMV9jMCkpOwoJfQoJcmV0dXJuIGhhbGY0KHN1bSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAJIBAGMVUOTUABAAAAQCAIAAAAAWFBIEMVUOTUAAAAAAQCAIAAAACIGAQCAAAAACQIKACCAYAAEAQAAAAAAZQAAAAAABAAAAACAZAAAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAMUGAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKY29uc3QgaW50IGtNYXhMb29wTGltaXRfUzFfYzAgPSA4Owp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxNF07CnVuaWZvcm0gaGFsZjIgdWRpcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJZmxvYXQyIGluQ29vcmQgPSBfY29vcmRzOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkID0gY2xhbXAoc3Vic2V0Q29vcmQsIHVjbGFtcF9TMV9jMF9jMF9jMC54eSwgdWNsYW1wX1MxX2MwX2MwX2MwLnp3KTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWhhbGY0IHN1bSA9IGhhbGY0KDAuMCk7Cglmb3IgKGludCBpID0gMDtpIDwga01heExvb3BMaW1pdF9TMV9jMDsgKytpKSAKCXsKCQloYWxmNCBzID0gdW9mZnNldHNBbmRLZXJuZWxfUzFfYzBbaV07CgkJc3VtICs9IHMueSAqIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgX3RtcF8xX2Nvb3JkcyArIGZsb2F0MihzLnggKiB1ZGlyX1MxX2MwKSk7CgkJc3VtICs9IHMudyAqIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgX3RtcF8xX2Nvb3JkcyArIGZsb2F0MihzLnogKiB1ZGlyX1MxX2MwKSk7Cgl9CglyZXR1cm4gaGFsZjQoc3VtKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHJ1bnRpbWVfc2hhZGVyX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","G2JAAAAAAAUAADAAAIOAAAAADIIAB7X7777QGHAYAD7P7777777777YBAAAAAAQAAAAAAQQGAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1OjAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0IGNvdmVyYWdlOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cgl2Y292ZXJhZ2VfUzAgPSBjb3ZlcmFnZTsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoAAAAAADUCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2Y292ZXJhZ2VfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UKAAAAbG9jYWxDb29yZAAAAAAAAA==","GYQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1MaAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAVwEAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAAAAAA","GXQAAGAABBYAAAEIXBAAAGEAMAAAAAAAAAAAAAAAYAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1OYAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gZmxvYXQ0IGluUXVhZEVkZ2U7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0NCB2UXVhZEVkZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkRWRnZQoJdlF1YWRFZGdlX1MwID0gaW5RdWFkRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KBAAAAF4DAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdlF1YWRFZGdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZEVkZ2UKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGYgZWRnZUFscGhhOwoJaGFsZjIgZHV2ZHggPSBoYWxmMihkRmR4KHZRdWFkRWRnZV9TMC54eSkpOwoJaGFsZjIgZHV2ZHkgPSBoYWxmMihkRmR5KHZRdWFkRWRnZV9TMC54eSkpOwoJaWYgKHZRdWFkRWRnZV9TMC56ID4gMC4wICYmIHZRdWFkRWRnZV9TMC53ID4gMC4wKSAKCXsKCQllZGdlQWxwaGEgPSBoYWxmKG1pbihtaW4odlF1YWRFZGdlX1MwLnosIHZRdWFkRWRnZV9TMC53KSArIDAuNSwgMS4wKSk7Cgl9CgllbHNlIAoJewoJCWhhbGYyIGdGID0gaGFsZjIoaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHgueCAtIGR1dmR4LnkpLCAgICAgICAgICAgICAgICAgaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHkueCAtIGR1dmR5LnkpKTsKCQllZGdlQWxwaGEgPSBoYWxmKHZRdWFkRWRnZV9TMC54KnZRdWFkRWRnZV9TMC54IC0gdlF1YWRFZGdlX1MwLnkpOwoJCWVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNSAtIGVkZ2VBbHBoYSAvIGxlbmd0aChnRikpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAEAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAKAAAAaW5RdWFkRWRnZQAAAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGABZAA6IAAAAACAAAAAADGAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAAB+AwAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","FIMAAAYAAEHAAAARC4EAAAQWBQAAAAAAAAAQAAAAIBCAAAEYAEAAAAAQAAAABAEQAEAAAAA":"DAAAAExTS1OtAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gaGFsZjMgaW5TaGFkb3dQYXJhbXM7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFJSZWN0U2hhZG93Cgl2aW5TaGFkb3dQYXJhbXNfUzAgPSBpblNoYWRvd1BhcmFtczsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAFwCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFJSZWN0U2hhZG93CgloYWxmMyBzaGFkb3dQYXJhbXM7CglzaGFkb3dQYXJhbXMgPSB2aW5TaGFkb3dQYXJhbXNfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CgloYWxmIGQgPSBsZW5ndGgoc2hhZG93UGFyYW1zLnh5KTsKCWZsb2F0MiB1diA9IGZsb2F0MihzaGFkb3dQYXJhbXMueiAqICgxLjAgLSBkKSwgMC41KTsKCWhhbGYgZmFjdG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB1dikuMDAwci5hOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChmYWN0b3IpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADgAAAGluU2hhZG93UGFyYW1zAAAAAAAA","EADQAAAAAEAAAAAUAABQAAQPAAABCFYMAAKAUEAAAAAAAAABAAAAAAAAAAAJQAIAAAABAAAAACAJAAIAAAAA":"DAAAAExTS1NtAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7CmluIGZsb2F0MyBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKb3V0IGZsb2F0IHZUZXhJbmRleF9TMDsKb3V0IGZsb2F0MiB2SW50VGV4dHVyZUNvb3Jkc19TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEaXN0YW5jZUZpZWxkUGF0aAoJaW50IHRleElkeCA9IDA7CglmbG9hdDIgdW5vcm1UZXhDb29yZHMgPSBmbG9hdDIoaW5UZXh0dXJlQ29vcmRzLngsIGluVGV4dHVyZUNvb3Jkcy55KTsKCXZUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHMgKiB1QXRsYXNEaW1lbnNpb25zSW52X1MwOwoJdlRleEluZGV4X1MwID0gZmxvYXQodGV4SWR4KTsKCXZJbnRUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHM7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDMgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MHo7Cn0KAAAAAAAAAJICAABzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKaW4gZmxvYXQgdlRleEluZGV4X1MwOwppbiBmbG9hdDIgdkludFRleHR1cmVDb29yZHNfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGlzdGFuY2VGaWVsZFBhdGgKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiB1diA9IHZUZXh0dXJlQ29vcmRzX1MwOwoJaGFsZjQgdGV4Q29sb3I7Cgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHV2KS5ycnJyOwoJfQoJaGFsZiBkaXN0YW5jZSA9IDcuOTY4NzUqKHRleENvbG9yLnIgLSAwLjUwMTk2MDc4NDMxKTsKCWhhbGYgYWZ3aWR0aDsKCWFmd2lkdGggPSBhYnMoMC42NSpoYWxmKGRGZHgodkludFRleHR1cmVDb29yZHNfUzAueCkpKTsKCWhhbGYgdmFsID0gc21vb3Roc3RlcCgtYWZ3aWR0aCwgYWZ3aWR0aCwgZGlzdGFuY2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCh2YWwpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5UZXh0dXJlQ29vcmRzAAAAAAA=","BYIBQAAABQAAIAABBYAAAEIXBAAP777777777777AAAAAAAAAAAABGABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1OJAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gaW5Db2xvcjsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzNfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAFEBAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IAAAAAAA==","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAALIAAAAAGFM37JKEAAAAAMAAAAAIAAAAAAAMBCQN25XWQUAAAAAAYAAAAAAAAAABGABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAAKh8AACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKY29uc3QgaW50IGtGaWxsQUFfUzJfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzJfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzJfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1X2NvbG9yX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbXBvc2l0ZV8xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NlbnRlcl9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X21heF9yYWRpdXNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9ub2lzZV9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X25vaXNlX3BoYXNlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24zX1MxX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQ0IHVjaXJjbGVfUzJfYzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDaXJjbGVfUzJfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8yX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGQ7CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMCkgCgl7CgkJZCA9IGhhbGYoKGxlbmd0aCgodWNpcmNsZV9TMl9jMC54eSAtIHNrX0ZyYWdDb29yZC54eSkgKiB1Y2lyY2xlX1MyX2MwLncpIC0gMS4wKSAqIHVjaXJjbGVfUzJfYzAueik7Cgl9CgllbHNlIAoJewoJCWQgPSBoYWxmKCgxLjAgLSBsZW5ndGgoKHVjaXJjbGVfUzJfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMl9jMC53KSkgKiB1Y2lyY2xlX1MyX2MwLnopOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGludCgxKSA9PSBrRmlsbEFBX1MyX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMCA/IHNhdHVyYXRlKGQpIDogaGFsZihkID4gMC41KSkpOwp9CmhhbGY0IEJsZW5kX1MyKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoX3NyYywgQ2lyY2xlX1MyX2MwKF9zcmMpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","EEAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAAAZQAAAAAABAAAAACAZAAAAA":"DAAAAExTS1MoDAAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDQgcmFkaWlfc2VsZWN0b3I7CmluIGZsb2F0NCBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzOwppbiBmbG9hdDQgYWFfYmxvYXRfYW5kX2NvdmVyYWdlOwppbiBmbG9hdDQgcmFkaWlfeDsKaW4gZmxvYXQ0IHJhZGlpX3k7CmluIGZsb2F0NCBza2V3OwppbiBmbG9hdDIgdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZTsKaW4gaGFsZjQgY29sb3I7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgAAAACZAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2YXJjY29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","GYQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADEAAGAAAAAAAAAIAAAAAPAIABAAAAABAGEAAAAEAAAABNARCAIAAAAAAAAAAAAAYAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1O8AQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCXZjb2xvcl9TMCA9IGNvbG9yOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc180X1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMCkgKiBwb3NpdGlvbi54eTE7Cgl9Cn0KAAAAAI0DAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMCkuMDAwcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEZXZpY2VTcGFjZV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gTWF0cml4RWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9kc3RfaW4oRGV2aWNlU3BhY2VfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBK2VD3EOCAAAAAABUXHIMAQAAAAWSQ4HO4SAAAAAMAAAAAEBUEFLMJAIAAAQCI6KVAYAAAAAAAAAAAQAAEACUA2JT4HAIAAAADIIODXOJIBAAAGAAAAACAKAC5XEQEAAAAAEPF2SMAAAAAAAAIAAAACXUUGB3XUUAQAADAAAAAAABARO3SJCAAAAACGSZJWCAAAAAAAIAAAABLFBUEFLMJAIAAAAAAAAAAACIMVSTUFAEAAAAAIAAAAAWFBSHSVIGAAAAAAAAAAAAABEBAABAAAAADIEEQCEAAAAAAAAAAAQAMQAAAAAAAQAAAABAMQAAA":"DAAAAExTS1PaAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzBfYzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAABAAAAcQkAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MxOwp1bmlmb3JtIGhhbGYgdXJhbmdlX1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzBfYzAsIHVlbmRfUzFfYzBfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IExpbmVhckxheW91dF9TMV9jMF9jMF9jMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzNfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CglyZXR1cm4gaGFsZjQoaGFsZjQoaGFsZihfdG1wXzNfY29vcmRzLngpICsgMWUtMDUsIDEuMCwgMC4wLCAwLjApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzBfYzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIExpbmVhckxheW91dF9TMV9jMF9jMF9jMV9jMChfaW5wdXQpOwp9CmhhbGY0IENsYW1wZWRHcmFkaWVudF9TMV9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzRfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGY0IHQgPSBNYXRyaXhFZmZlY3RfUzFfYzBfYzBfYzEoX3RtcF80X2luQ29sb3IpOwoJaGFsZjQgb3V0Q29sb3I7CglpZiAoIWJvb2woaW50KDEpKSAmJiB0LnkgPCAwLjApIAoJewoJCW91dENvbG9yID0gaGFsZjQoMC4wKTsKCX0KCWVsc2UgaWYgKHQueCA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSB1bGVmdEJvcmRlckNvbG9yX1MxX2MwX2MwOwoJfQoJZWxzZSBpZiAodC54ID4gMS4wKSAKCXsKCQlvdXRDb2xvciA9IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwX2MwOwoJfQoJZWxzZSAKCXsKCQlvdXRDb2xvciA9IFNpbmdsZUludGVydmFsQ29sb3JpemVyX1MxX2MwX2MwX2MwKF90bXBfNF9pbkNvbG9yLCBmbG9hdDIoaGFsZjIodC54LCAwLjApKSk7Cgl9CglyZXR1cm4gaGFsZjQob3V0Q29sb3IpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ2xhbXBlZEdyYWRpZW50X1MxX2MwX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzFfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIF9jb29yZHMpLjAwMHI7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MxKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMV9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzEpICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IERpdGhlcl9TMShoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzZfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGY0IGNvbG9yID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMV9jMChfdG1wXzZfaW5Db2xvcik7CgloYWxmIHZhbHVlID0gTWF0cml4RWZmZWN0X1MxX2MxKF90bXBfNl9pbkNvbG9yLCBza19GcmFnQ29vcmQueHkpLncgLSAwLjU7CglyZXR1cm4gaGFsZjQoaGFsZjQoY2xhbXAoY29sb3IueHl6ICsgdmFsdWUgKiB1cmFuZ2VfUzEsIDAuMCwgY29sb3IudyksIGNvbG9yLncpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IERpdGhlcl9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAABQAAAAAAAACAAAAADYCAAIAAAAAIBRAAAABAAAAALIEIQCAAAAAAAAAAAAAGABQAAAAEAAAAAAAEEBQAAAA":"DAAAAExTS1PSAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCXZsb2NhbENvb3JkX1MwID0gbG9jYWxDb29yZDsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNF9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogcG9zaXRpb24ueHkxOwoJfQp9CgAAAAAAACUEAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwKS4wMDByOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IERldmljZVNwYWNlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX2RzdF9pbihEZXZpY2VTcGFjZV9TMV9jMChfc3JjKSwgX3NyYyk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAAJQAAAAAAAANAAAAAHQVERARJYAAAABQAAAABAAAAAA4BAQAAACAAAAASAMAAQAAAAAUCCYBCAAAAAAAAAAAAAANJABAAAMBJ4KECOAEAAAMAAAAAAAAEAABAAV6GUSECFHAAAAAGAAAAAAAAAAAAAGABQAAAAEAAAAAAAEEBQA":"DAAAAExTS1PdAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMl9jMF9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0IHZjb3ZlcmFnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzJfYzBfYzApICogcG9zaXRpb24ueHkxOwoJfQp9CgAAAAEAAADvIQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCmZsb2F0NCBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMDsKZmxvYXQ0IGZyYWdDb2xvcl9TMV9jMDsKZmxvYXQgdV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9zcGFya2xlX2FscGhhX1MxX2MwOwpmbG9hdCB1X2JsdXJfUzFfYzA7CmZsb2F0IHVfcmFkaXVzX3NjYWxlX1MxX2MwOwpjb25zdCBpbnQga0ZpbGxCV19TMl9jMV9jMCA9IDA7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxCV19TMl9jMV9jMCA9IDI7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCA9IDM7CnVuaWZvcm0gZmxvYXQ0IHVfY29sb3JfUzFfYzA7CnVuaWZvcm0gZmxvYXQ0IHVfY29tcG9zaXRlXzFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2VudGVyX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbWF4X3JhZGl1c19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X25vaXNlX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbm9pc2VfcGhhc2VfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUyX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjNfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MyX2MwX2MwOwp1bmlmb3JtIGZsb2F0NCB1cmVjdFVuaWZvcm1fUzJfYzFfYzA7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMjsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBUZXh0dXJlRWZmZWN0X1MyX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzIsIHZUcmFuc2Zvcm1lZENvb3Jkc182X1MwKS4wMDByOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMl9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMl9jMF9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IERldmljZVNwYWNlX1MyX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzJfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBSZWN0X1MyX2MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMl9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBjb3ZlcmFnZTsKCWlmIChpbnQoMSkgPT0ga0ZpbGxCV19TMl9jMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzJfYzFfYzApIAoJewoJCWNvdmVyYWdlID0gaGFsZihhbGwoZ3JlYXRlclRoYW4oZmxvYXQ0KHNrX0ZyYWdDb29yZC54eSwgdXJlY3RVbmlmb3JtX1MyX2MxX2MwLnp3KSwgZmxvYXQ0KHVyZWN0VW5pZm9ybV9TMl9jMV9jMC54eSwgc2tfRnJhZ0Nvb3JkLnh5KSkpKTsKCX0KCWVsc2UgCgl7CgkJaGFsZjQgZGlzdHM0ID0gc2F0dXJhdGUoaGFsZjQoMS4wLCAxLjAsIC0xLjAsIC0xLjApICogaGFsZjQoc2tfRnJhZ0Nvb3JkLnh5eHkgLSB1cmVjdFVuaWZvcm1fUzJfYzFfYzApKTsKCQloYWxmMiBkaXN0czIgPSAoZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3KSAtIDEuMDsKCQljb3ZlcmFnZSA9IGRpc3RzMi54ICogZGlzdHMyLnk7Cgl9CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSAxLjAgLSBjb3ZlcmFnZTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChjb3ZlcmFnZSkpOwp9CmhhbGY0IEJsZW5kX1MyX2MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoUmVjdF9TMl9jMV9jMChfc3JjKSwgX3NyYyk7Cn0KaGFsZjQgQmxlbmRfUzIoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9kc3RfaW4oRGV2aWNlU3BhY2VfUzJfYzAoX3NyYyksIEJsZW5kX1MyX2MxKF9zcmMsIF9kc3QpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","G2QACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAADAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1PqAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0IGNvdmVyYWdlOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQ0IGdlb21TdWJzZXQ7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAQAAAKQCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0NCB2Z2VvbVN1YnNldF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJZmxvYXQ0IGdlb1N1YnNldDsKCWdlb1N1YnNldCA9IHZnZW9tU3Vic2V0X1MwOwoJZmxvYXQ0IGRpc3RzNCA9IHNhdHVyYXRlKGZsb2F0NCgxLCAxLCAtMSwgLTEpICogKHNrX0ZyYWdDb29yZC54eXh5IC0gZ2VvU3Vic2V0KSk7CglmbG9hdDIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWNvdmVyYWdlID0gbWluKGNvdmVyYWdlLCBkaXN0czIueCAqIGRpc3RzMi55KTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAB3QA6AAAEAAAAAAAMAAPEAEAAABAAAAAABTAAAAAAACAAAAAEBSAAAA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAQAAAHcFAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzI7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MyOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZpbkNvbG9yX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJYWxwaGEgPSAxLjAgLSBhbHBoYTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQpoYWxmNCBDaXJjdWxhclJSZWN0X1MyKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMi5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMi5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MyLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBDaXJjdWxhclJSZWN0X1MyKG91dHB1dF9TMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzI7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAALIAAAAB4FJEIEKOAAAAAMAAAAAEAHQAACAAAAAAYCFPSVARQRJAAAAABQAAAAAAAAAAA4JAPAAACAAAAAAACABSAAAAAAACAAAAAEBSAAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAANCEAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKY29uc3QgaW50IGtGaWxsQldfUzJfYzAgPSAwOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzJfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzJfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1X2NvbG9yX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbXBvc2l0ZV8xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NlbnRlcl9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X21heF9yYWRpdXNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9ub2lzZV9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X25vaXNlX3BoYXNlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24zX1MxX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQ0IHVyZWN0VW5pZm9ybV9TMl9jMDsKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMl9jMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzJfYzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBSZWN0X1MyX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMl9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBjb3ZlcmFnZTsKCWlmIChpbnQoMSkgPT0ga0ZpbGxCV19TMl9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzJfYzApIAoJewoJCWNvdmVyYWdlID0gaGFsZihhbGwoZ3JlYXRlclRoYW4oZmxvYXQ0KHNrX0ZyYWdDb29yZC54eSwgdXJlY3RVbmlmb3JtX1MyX2MwLnp3KSwgZmxvYXQ0KHVyZWN0VW5pZm9ybV9TMl9jMC54eSwgc2tfRnJhZ0Nvb3JkLnh5KSkpKTsKCX0KCWVsc2UgCgl7CgkJaGFsZjQgZGlzdHM0ID0gc2F0dXJhdGUoaGFsZjQoMS4wLCAxLjAsIC0xLjAsIC0xLjApICogaGFsZjQoc2tfRnJhZ0Nvb3JkLnh5eHkgLSB1cmVjdFVuaWZvcm1fUzJfYzApKTsKCQloYWxmMiBkaXN0czIgPSAoZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3KSAtIDEuMDsKCQljb3ZlcmFnZSA9IGRpc3RzMi54ICogZGlzdHMyLnk7Cgl9CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMCkgCgl7CgkJY292ZXJhZ2UgPSAxLjAgLSBjb3ZlcmFnZTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChjb3ZlcmFnZSkpOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzJfYzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyX2MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyX2MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzJfYzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0KaGFsZjQgQmxlbmRfUzIoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShSZWN0X1MyX2MwKF9zcmMpLCBDaXJjdWxhclJSZWN0X1MyX2MxKF9zcmMpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAABwb3NpdGlvbggAAABjb3ZlcmFnZQUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAA4IAEAAACAAAAAABGABAAAAAEAAAAAIBEABAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAAEh4AACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb2xvcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb21wb3NpdGVfMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jZW50ZXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9tYXhfcmFkaXVzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfbm9pc2Vfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9ub2lzZV9waGFzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUxX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlM19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24yX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uM19TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MyOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMjsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KTsKdm9pZCBGTFRfbWFpbl9TMV9jMCgpOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpIAp7CglyZXR1cm4gZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAueHk7Cn0KZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCkgCnsKCXJldHVybiBmbG9hdDJ4MihyYWQueCwgLXJhZC55LCByYWQueSwgcmFkLngpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpIAp7CglmbG9hdCBibHVyX2hhbGYgPSBibHVyICogMC41OwoJZmxvYXQgZCA9IGRpc3RhbmNlKHV2LCB4eSk7CglyZXR1cm4gMS4wIC0gc21vb3Roc3RlcCgxLjAgLSBibHVyX2hhbGYsIDEuMCArIGJsdXJfaGFsZiwgZCAvIHJhZGl1cyk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcikgCnsKCWZsb2F0MiBwYXJhbSA9IHJvdGF0aW9uOwoJcCA9IEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKHBhcmFtKSAqICh4eSAtIHApICsgeHk7CglwID0gbW9kKHAsIGZsb2F0MihjZWxsX2RpYW1ldGVyKSkgLyByZXNvbHV0aW9uOwoJZmxvYXQgY2VsbF91diA9IChjZWxsX2RpYW1ldGVyIC8gcmVzb2x1dGlvbi55KSAqIDAuNTsKCWZsb2F0IHIgPSAwLjY1ICogY2VsbF91djsKCWZsb2F0MiBwYXJhbV8xID0gcDsKCWZsb2F0MiBwYXJhbV8yID0gZmxvYXQyKGNlbGxfdXYpOwoJZmxvYXQgcGFyYW1fMyA9IHI7CglmbG9hdCBwYXJhbV80ID0gciAqIDUwLjA7CglyZXR1cm4gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpIAp7CglmbG9hdDIgdXZfc2NhbGUgPSB1diAqIGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzIgPSB1X2NpcmNsZTFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMyA9IHVfcm90YXRpb24xX1MxX2MwOwoJZmxvYXQgcGFyYW1fNCA9IDAuMTc7CglmbG9hdCBfMzE5ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwoJZmxvYXQgZzEgPSBfMzE5OwoJZmxvYXQyIHBhcmFtXzUgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV82ID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fNyA9IHVfY2lyY2xlMl9TMV9jMDsKCWZsb2F0MiBwYXJhbV84ID0gdV9yb3RhdGlvbjJfUzFfYzA7CglmbG9hdCBwYXJhbV85ID0gMC4yOwoJZmxvYXQgXzMzMSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgZzIgPSBfMzMxOwoJZmxvYXQyIHBhcmFtXzEwID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMTEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8xMiA9IHVfY2lyY2xlM19TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHVfcm90YXRpb24zX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTQgPSAwLjI3NTsKCWZsb2F0IF8zNDQgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyLCBwYXJhbV8xMywgcGFyYW1fMTQpOwoJZmxvYXQgZzMgPSBfMzQ0OwoJZmxvYXQgdiA9ICgoZzEgKiBnMSArIGcyKSAtIGczKSAqIDAuNTsKCXJldHVybiBjbGFtcCgwLjQ1ICsgMC44ICogdiwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0MiBwYXJhbV8xID0geHk7CglmbG9hdCBwYXJhbV8yID0gcmFkaXVzICsgdGhpY2tuZXNzOwoJZmxvYXQgcGFyYW1fMyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfb3V0ZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdDIgcGFyYW1fNCA9IHV2OwoJZmxvYXQyIHBhcmFtXzUgPSB4eTsKCWZsb2F0IHBhcmFtXzYgPSBtYXgocmFkaXVzIC0gdGhpY2tuZXNzLCAwLjApOwoJZmxvYXQgcGFyYW1fNyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfaW5uZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83KTsKCXJldHVybiBjbGFtcChjaXJjbGVfb3V0ZXIgLSBjaXJjbGVfaW5uZXIsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbikgCnsKCW4gPSBmcmFjdChuICogZmxvYXQyKDUuMzk4NywgNS40NDIxKSk7CgluICs9IGZsb2F0Mihkb3Qobi55eCwgbiArIGZsb2F0MigyMS41MzUxLCAxNC4zMTM3KSkpOwoJZmxvYXQgeHkgPSBuLnggKiBuLnk7CglyZXR1cm4gKGZyYWN0KHh5ICogOTUuNDMwNykgKyBmcmFjdCh4eSAqIDc1LjA0OTYxKSkgLSAxLjA7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpIAp7CglyZXR1cm4gc3RlcChsLCB2KSAqICgxLjAgLSBzdGVwKGgsIHYpKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCkgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQgXzI0MiA9IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKHBhcmFtKTsKCWZsb2F0IG4gPSBfMjQyOwoJZmxvYXQgcGFyYW1fMSA9IG47CglmbG9hdCBwYXJhbV8yID0gMC4wOwoJZmxvYXQgcGFyYW1fMyA9IDAuMDU7CglmbG9hdCBzID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQgcGFyYW1fNCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC4zNSkpOwoJZmxvYXQgcGFyYW1fNSA9IDAuMTsKCWZsb2F0IHBhcmFtXzYgPSAwLjE1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNik7CglmbG9hdCBwYXJhbV83ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjcpKTsKCWZsb2F0IHBhcmFtXzggPSAwLjI7CglmbG9hdCBwYXJhbV85ID0gMC4yNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgcGFyYW1fMTAgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDEuMDUpKTsKCWZsb2F0IHBhcmFtXzExID0gMC4zOwoJZmxvYXQgcGFyYW1fMTIgPSAwLjM1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMik7CglyZXR1cm4gY2xhbXAocywgMC4wLCAxLjApICogMC41NTsKfQp2b2lkIEZMVF9tYWluX1MxX2MwKCkgCnsKCXVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLng7Cgl1X3NwYXJrbGVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnk7Cgl1X2JsdXJfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLno7Cgl1X3JhZGl1c19zY2FsZV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAudzsKCWZsb2F0MiBwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwoJZmxvYXQyIHV2XzEgPSBwICogdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwoJZmxvYXQyIGRlbnNpdHlfdXYgPSB1dl8xIC0gbW9kKHAsIHVfbm9pc2Vfc2NhbGVfUzFfYzApOwoJZmxvYXQgcmFkaXVzID0gdV9tYXhfcmFkaXVzX1MxX2MwICogdV9yYWRpdXNfc2NhbGVfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1dl8xOwoJZmxvYXQgdHVyYnVsZW5jZSA9IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAocGFyYW1fMTMpOwoJZmxvYXQyIHBhcmFtXzE0ID0gcDsKCWZsb2F0MiBwYXJhbV8xNSA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTYgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8xNyA9IDAuMDUgKiB1X21heF9yYWRpdXNfUzFfYzA7CglmbG9hdCBwYXJhbV8xOCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHJpbmcgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAocGFyYW1fMTQsIHBhcmFtXzE1LCBwYXJhbV8xNiwgcGFyYW1fMTcsIHBhcmFtXzE4KTsKCWZsb2F0MiBwYXJhbV8xOSA9IGRlbnNpdHlfdXY7CglmbG9hdCBwYXJhbV8yMCA9IHVfbm9pc2VfcGhhc2VfUzFfYzA7CglmbG9hdCBzcGFya2xlID0gKChGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKHBhcmFtXzE5LCBwYXJhbV8yMCkgKiByaW5nKSAqIHR1cmJ1bGVuY2UpICogdV9zcGFya2xlX2FscGhhX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzIxID0gcDsKCWZsb2F0MiBwYXJhbV8yMiA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMjMgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8yNCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHdhdmVfYWxwaGEgPSAoRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMjEsIHBhcmFtXzIyLCBwYXJhbV8yMywgcGFyYW1fMjQpICogdV9hbHBoYV9TMV9jMCkgKiB1X2NvbG9yX1MxX2MwLnc7CglmbG9hdDQgd2F2ZV9jb2xvciA9IGZsb2F0NCh1X2NvbG9yX1MxX2MwLnh5eiAqIHdhdmVfYWxwaGEsIHdhdmVfYWxwaGEpOwoJZnJhZ0NvbG9yX1MxX2MwID0gbWl4KHdhdmVfY29sb3IsIGZsb2F0NCgxLjApLCBmbG9hdDQoc3BhcmtsZSkpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwID0gZmxvYXQ0KF90bXBfMV9jb29yZHMsIDAuMCwgMC4wKTsKCUZMVF9tYWluX1MxX2MwKCk7CglyZXR1cm4gaGFsZjQoaGFsZjQoZnJhZ0NvbG9yX1MxX2MwKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQgZHkwID0gdWlubmVyUmVjdF9TMi5UIC0gc2tfRnJhZ0Nvb3JkLnk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzIuUkI7CglmbG9hdDIgZHh5ID0gbWF4KGZsb2F0MihkeHkxLngsIG1heChkeTAsIGR4eTEueSkpLCAwLjApOwoJaGFsZiBsZWZ0QWxwaGEgPSBoYWxmKHNhdHVyYXRlKHNrX0ZyYWdDb29yZC54IC0gdWlubmVyUmVjdF9TMi5MKSk7CgloYWxmIGFscGhhID0gbGVmdEFscGhhICogaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJaGFsZjQgb3V0cHV0X1MyOwoJb3V0cHV0X1MyID0gQ2lyY3VsYXJSUmVjdF9TMihvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0X1MyOwoJfQp9CgAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","AYAA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PbAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gZmxvYXQ0IGluQ2lyY2xlRWRnZTsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8yX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAQwIAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAMAAAAaW5DaXJjbGVFZGdlAAAAAA==","GYQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAMYAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1MaAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAABAAAA5gIAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MxOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","G2QACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAMYAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1PqAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0IGNvdmVyYWdlOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQ0IGdlb21TdWJzZXQ7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAQAAADMEAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJZmxvYXQ0IGdlb1N1YnNldDsKCWdlb1N1YnNldCA9IHZnZW9tU3Vic2V0X1MwOwoJZmxvYXQ0IGRpc3RzNCA9IHNhdHVyYXRlKGZsb2F0NCgxLCAxLCAtMSwgLTEpICogKHNrX0ZyYWdDb29yZC54eXh5IC0gZ2VvU3Vic2V0KSk7CglmbG9hdDIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWNvdmVyYWdlID0gbWluKGNvdmVyYWdlLCBkaXN0czIueCAqIGRpc3RzMi55KTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGdlb21TdWJzZXQAAAAAAAA=","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAADIAAAAIH5K3LIKAAAAAAMAAAAAIAAAAAAFMZBQP3FQWAUAABQAAAAAAAAAAAAADGAAAAAAAEAAAAAIDEAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAADzBAAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCmNvbnN0IGludCBrRmlsbEFBX1MxX2MwID0gMTsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEJXX1MxX2MwID0gMjsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEFBX1MxX2MwID0gMzsKdW5pZm9ybSBmbG9hdDQgdWNpcmNsZV9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMSkgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSkpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKF9zcmMsIENpcmNsZV9TMV9jMChfc3JjKSk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","EEAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAB5AAAAAAYFRP5FIUAQAADQAAAABAAAAAAABQEKBTLG62KRAAAAAHAAAAAAAAAAAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1MoDAAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDQgcmFkaWlfc2VsZWN0b3I7CmluIGZsb2F0NCBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzOwppbiBmbG9hdDQgYWFfYmxvYXRfYW5kX2NvdmVyYWdlOwppbiBmbG9hdDQgcmFkaWlfeDsKaW4gZmxvYXQ0IHJhZGlpX3k7CmluIGZsb2F0NCBza2V3OwppbiBmbG9hdDIgdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZTsKaW4gaGFsZjQgY29sb3I7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAACdBQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCmNvbnN0IGludCBrRmlsbEFBX1MxX2MwID0gMTsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEJXX1MxX2MwID0gMjsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEFBX1MxX2MwID0gMzsKdW5pZm9ybSBmbG9hdDQgdWNpcmNsZV9TMV9jMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZhcmNjb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgzKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMykgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSkpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKF9zcmMsIENpcmNsZV9TMV9jMChfc3JjKSk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEZpbGxSUmVjdE9wOjpQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCB4X3BsdXNfMT12YXJjY29vcmRfUzAueCwgeT12YXJjY29vcmRfUzAueTsKCWhhbGYgY292ZXJhZ2U7CglpZiAoMCA9PSB4X3BsdXNfMSkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKHkpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdCBmbiA9IHhfcGx1c18xICogKHhfcGx1c18xIC0gMik7CgkJZm4gPSBmbWEoeSx5LCBmbik7CgkJZmxvYXQgZm53aWR0aCA9IGZ3aWR0aChmbik7CgkJY292ZXJhZ2UgPSAuNSAtIGhhbGYoZm4vZm53aWR0aCk7CgkJY292ZXJhZ2UgPSBjbGFtcChjb3ZlcmFnZSwgMCwgMSk7Cgl9CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAOAAAAcmFkaWlfc2VsZWN0b3IAABkAAABjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzAAAAFQAAAGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZQAAAAcAAAByYWRpaV94AAcAAAByYWRpaV95AAQAAABza2V3GQAAAHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUAAAAFAAAAY29sb3IAAAAAAAAA","AYTRVAADQAAAOAEARAFQJAABBADAAAILBYAACCYUQD777777777767YAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PnAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwppbiBoYWxmMyBpbkNsaXBQbGFuZTsKaW4gaGFsZjMgaW5Jc2VjdFBsYW5lOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjMgdmluQ2xpcFBsYW5lX1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmMyB2aW5Jc2VjdFBsYW5lX1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5DbGlwUGxhbmVfUzAgPSBpbkNsaXBQbGFuZTsKCXZpbklzZWN0UGxhbmVfUzAgPSBpbklzZWN0UGxhbmU7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gdWxvY2FsTWF0cml4X1MwLnh6ICogaW5Qb3NpdGlvbiArIHVsb2NhbE1hdHJpeF9TMC55dzsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAFIEAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmMyB2aW5DbGlwUGxhbmVfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjMgdmluSXNlY3RQbGFuZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjMgY2xpcFBsYW5lOwoJY2xpcFBsYW5lID0gdmluQ2xpcFBsYW5lX1MwOwoJaGFsZjMgaXNlY3RQbGFuZTsKCWlzZWN0UGxhbmUgPSB2aW5Jc2VjdFBsYW5lX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmIGRpc3RhbmNlVG9Jbm5lckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqIChkIC0gY2lyY2xlRWRnZS53KSk7CgloYWxmIGlubmVyQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvSW5uZXJFZGdlKTsKCWVkZ2VBbHBoYSAqPSBpbm5lckFscGhhOwoJaGFsZiBjbGlwID0gaGFsZihzYXR1cmF0ZShjaXJjbGVFZGdlLnogKiBkb3QoY2lyY2xlRWRnZS54eSwgY2xpcFBsYW5lLnh5KSArIGNsaXBQbGFuZS56KSk7CgljbGlwICo9IGhhbGYoc2F0dXJhdGUoY2lyY2xlRWRnZS56ICogZG90KGNpcmNsZUVkZ2UueHksIGlzZWN0UGxhbmUueHkpICsgaXNlY3RQbGFuZS56KSk7CgllZGdlQWxwaGEgKj0gY2xpcDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQsAAABpbkNsaXBQbGFuZQAMAAAAaW5Jc2VjdFBsYW5lAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAEQQGABZAA6IAAAAACAAAAAADGAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAACHAwAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwpzYW1wbGVyRXh0ZXJuYWxPRVMgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","G2JQAAAAABEAADAAAIOAAAAADIIAB7X7777QGHAYAD7P7777A4QCQAAAAAAAAAQAAAAAAQQGAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1P8AQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0IGNvdmVyYWdlOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKaW4gZmxvYXQ0IHRleFN1YnNldDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdnRleFN1YnNldF9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0MiBwb3NpdGlvbiA9IHBvc2l0aW9uLnh5OwoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJdnRleFN1YnNldF9TMCA9IHRleFN1YnNldDsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAC4AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQ0IHZ0ZXhTdWJzZXRfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCWZsb2F0NCBzdWJzZXQ7CglzdWJzZXQgPSB2dGV4U3Vic2V0X1MwOwoJdGV4Q29vcmQgPSBjbGFtcCh0ZXhDb29yZCwgc3Vic2V0LkxULCBzdWJzZXQuUkIpOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UKAAAAbG9jYWxDb29yZAAACQAAAHRleFN1YnNldAAAAAAAAAA=","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBIA5HZ2CAAAAAAAAAQCAAAAASAEEAQAAAAAUCCQAQQGAAAAEAAAAAAGMAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1POAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAlgQAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZC54ID0gc3Vic2V0Q29vcmQueDsKCWNsYW1wZWRDb29yZC55ID0gY2xhbXAoc3Vic2V0Q29vcmQueSwgdWNsYW1wX1MxX2MwX2MwLnksIHVjbGFtcF9TMV9jMF9jMC53KTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBNYXRyaXhFZmZlY3RfUzFfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GYJBYAQCAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGABZAA6IAAAAACAAAAAADGAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAADEBwAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQgdVNyY1RGX1MwWzddOwp1bmlmb3JtIGZsb2F0M3gzIHVDb2xvclhmb3JtX1MwOwp1bmlmb3JtIGZsb2F0IHVEc3RURl9TMFs3XTsKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CmZsb2F0IHNyY190Zl9TMChmbG9hdCB4KSAKewoJZmxvYXQgRyA9IHVTcmNURl9TMFswXTsKCWZsb2F0IEEgPSB1U3JjVEZfUzBbMV07CglmbG9hdCBCID0gdVNyY1RGX1MwWzJdOwoJZmxvYXQgQyA9IHVTcmNURl9TMFszXTsKCWZsb2F0IEQgPSB1U3JjVEZfUzBbNF07CglmbG9hdCBFID0gdVNyY1RGX1MwWzVdOwoJZmxvYXQgRiA9IHVTcmNURl9TMFs2XTsKCWZsb2F0IHMgPSBzaWduKHgpOwoJeCA9IGFicyh4KTsKCXggPSAoeCA8IEQpID8gKEMgKiB4KSArIEYgOiBwb3coQSAqIHggKyBCLCBHKSArIEU7CglyZXR1cm4gcyAqIHg7Cn0KZmxvYXQgZHN0X3RmX1MwKGZsb2F0IHgpIAp7CglmbG9hdCBHID0gdURzdFRGX1MwWzBdOwoJZmxvYXQgQSA9IHVEc3RURl9TMFsxXTsKCWZsb2F0IEIgPSB1RHN0VEZfUzBbMl07CglmbG9hdCBDID0gdURzdFRGX1MwWzNdOwoJZmxvYXQgRCA9IHVEc3RURl9TMFs0XTsKCWZsb2F0IEUgPSB1RHN0VEZfUzBbNV07CglmbG9hdCBGID0gdURzdFRGX1MwWzZdOwoJZmxvYXQgcyA9IHNpZ24oeCk7Cgl4ID0gYWJzKHgpOwoJeCA9ICh4IDwgRCkgPyAoQyAqIHgpICsgRiA6IHBvdyhBICogeCArIEIsIEcpICsgRTsKCXJldHVybiBzICogeDsKfQpmbG9hdDQgZ2FtdXRfeGZvcm1fUzAoZmxvYXQ0IGNvbG9yKSAKewoJY29sb3IucmdiID0gKHVDb2xvclhmb3JtX1MwICogY29sb3IucmdiKTsKCXJldHVybiBjb2xvcjsKfQpoYWxmNCBjb2xvcl94Zm9ybV9TMChmbG9hdDQgY29sb3IpIAp7Cgljb2xvci5yID0gc3JjX3RmX1MwKGNvbG9yLnIpOwoJY29sb3IuZyA9IHNyY190Zl9TMChjb2xvci5nKTsKCWNvbG9yLmIgPSBzcmNfdGZfUzAoY29sb3IuYik7Cgljb2xvciA9IGdhbXV0X3hmb3JtX1MwKGNvbG9yKTsKCWNvbG9yLnIgPSBkc3RfdGZfUzAoY29sb3Iucik7Cgljb2xvci5nID0gZHN0X3RmX1MwKGNvbG9yLmcpOwoJY29sb3IuYiA9IGRzdF90Zl9TMChjb2xvci5iKTsKCXJldHVybiBoYWxmNChjb2xvcik7Cn0KaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKGNvbG9yX3hmb3JtX1MwKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAACAA6QAAAABYCTIUIE4AIAAAYAAAAAQAAAAAGARL4FIEMEKMAAAAAMAAAAAAAAAAAAAMADAAAAAIAAAAAAAIIDAAAA":"DAAAAExTS1N4AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0MiB1QXRsYXNTaXplSW52X1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gdXNob3J0MiBpblRleHR1cmVDb29yZHM7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQml0bWFwVGV4dAoJaW50IHRleElkeCA9IDA7CglmbG9hdDIgdW5vcm1UZXhDb29yZHMgPSBmbG9hdDIoaW5UZXh0dXJlQ29vcmRzLngsIGluVGV4dHVyZUNvb3Jkcy55KTsKCXZUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHMgKiB1QXRsYXNTaXplSW52X1MwOwoJdlRleEluZGV4X1MwID0gZmxvYXQodGV4SWR4KTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwMTsKfQoBAAAAnwUAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpjb25zdCBpbnQga0ZpbGxCV19TMV9jMCA9IDA7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxCV19TMV9jMCA9IDI7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxBQV9TMV9jMCA9IDM7CnVuaWZvcm0gZmxvYXQ0IHVyZWN0VW5pZm9ybV9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IFJlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGNvdmVyYWdlOwoJaWYgKGludCgxKSA9PSBrRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKGFsbChncmVhdGVyVGhhbihmbG9hdDQoc2tfRnJhZ0Nvb3JkLnh5LCB1cmVjdFVuaWZvcm1fUzFfYzAuencpLCBmbG9hdDQodXJlY3RVbmlmb3JtX1MxX2MwLnh5LCBza19GcmFnQ29vcmQueHkpKSkpOwoJfQoJZWxzZSAKCXsKCQloYWxmNCBkaXN0czQgPSBzYXR1cmF0ZShoYWxmNCgxLjAsIDEuMCwgLTEuMCwgLTEuMCkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIHVyZWN0VW5pZm9ybV9TMV9jMCkpOwoJCWhhbGYyIGRpc3RzMiA9IChkaXN0czQueHkgKyBkaXN0czQuencpIC0gMS4wOwoJCWNvdmVyYWdlID0gZGlzdHMyLnggKiBkaXN0czIueTsKCX0KCWlmIChpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEFBX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IDEuMCAtIGNvdmVyYWdlOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGNvdmVyYWdlKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShSZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZjQgdGV4Q29sb3I7Cgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHZUZXh0dXJlQ29vcmRzX1MwKS5ycnJyOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSB0ZXhDb2xvcjsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAHIBK2VD3EOCAAAAAABUXHIMAQAAAAWSQ4HO4SAAAAAMAAAAAAAAQAABANBBK3CICAAAEASHSVIGAAAAAAAAAAAEAABAAVAGSM7BYCAAAAA2CDQ53SKAIAABQAAAAAAACAAAEAUAF3OJAIAAAAAI6LVEYAAAAAAAAQAAAAFPJIMDXPJIBAAAGAAAAAAAAIAAAAAQIXNZERAAAAABDJMU3BAAAAAAAEAAAAAMQQAIAAAQGQQVNREBAAACAAAAAAQAKRC5HBSYCACAAAAAAAAAEAIRDJMU3BAAAAAAACAAAAAVTIMQ4RKBQAAAAAAAAAACAAIAIAAIAAAAC2BCEAQAAAAAAAAAAAMADAAAAAUAAAAAAAIIDA":"DAAAAExTS1NNAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfN19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzdfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwX2MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAEAAADKCgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gaGFsZjQgdXN0YXJ0X1MxX2MwX2MwX2MwX2MwOwp1bmlmb3JtIGhhbGY0IHVlbmRfUzFfYzBfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMF9jMTsKdW5pZm9ybSBoYWxmNCB1bGVmdEJvcmRlckNvbG9yX1MxX2MwX2MwOwp1bmlmb3JtIGhhbGY0IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzE7CnVuaWZvcm0gaGFsZiB1cmFuZ2VfUzE7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfN19TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzBfYzBfYzAsIHVlbmRfUzFfYzBfYzBfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IGNvbG9yX3hmb3JtX1MxX2MwX2MwX2MwKGZsb2F0NCBjb2xvcikgCnsKCWNvbG9yLnJnYiAqPSBjb2xvci5hOwoJcmV0dXJuIGhhbGY0KGNvbG9yKTsKfQpoYWxmNCBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBjb2xvcl94Zm9ybV9TMV9jMF9jMF9jMChTaW5nbGVJbnRlcnZhbENvbG9yaXplcl9TMV9jMF9jMF9jMF9jMChfaW5wdXQsIF9jb29yZHMpKTsKfQpoYWxmNCBMaW5lYXJMYXlvdXRfUzFfYzBfYzBfYzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8yX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8zX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc183X1MwOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGhhbGYoX3RtcF8zX2Nvb3Jkcy54KSArIDFlLTA1LCAxLjAsIDAuMCwgMC4wKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwX2MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBMaW5lYXJMYXlvdXRfUzFfYzBfYzBfYzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDbGFtcGVkR3JhZGllbnRfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF80X2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmNCB0ID0gTWF0cml4RWZmZWN0X1MxX2MwX2MwX2MxKF90bXBfNF9pbkNvbG9yKTsKCWhhbGY0IG91dENvbG9yOwoJaWYgKCFib29sKGludCgxKSkgJiYgdC55IDwgMC4wKSAKCXsKCQlvdXRDb2xvciA9IGhhbGY0KDAuMCk7Cgl9CgllbHNlIGlmICh0LnggPCAwLjApIAoJewoJCW91dENvbG9yID0gdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKCX0KCWVsc2UgaWYgKHQueCA+IDEuMCkgCgl7CgkJb3V0Q29sb3IgPSB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKCX0KCWVsc2UgCgl7CgkJb3V0Q29sb3IgPSBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzBfYzAoX3RtcF80X2luQ29sb3IsIGZsb2F0MihoYWxmMih0LngsIDAuMCkpKTsKCX0KCXJldHVybiBoYWxmNChvdXRDb2xvcik7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBDbGFtcGVkR3JhZGllbnRfUzFfYzBfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfNV9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0KaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMV9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3JkcykuMDAwcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzEoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MxX2MwKF9pbnB1dCwgZmxvYXQzeDIodW1hdHJpeF9TMV9jMSkgKiBfY29vcmRzLnh5MSk7Cn0KaGFsZjQgRGl0aGVyX1MxKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfNl9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjQgY29sb3IgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxX2MwKF90bXBfNl9pbkNvbG9yKTsKCWhhbGYgdmFsdWUgPSBNYXRyaXhFZmZlY3RfUzFfYzEoX3RtcF82X2luQ29sb3IsIHNrX0ZyYWdDb29yZC54eSkudyAtIDAuNTsKCXJldHVybiBoYWxmNChoYWxmNChjbGFtcChjb2xvci54eXogKyB2YWx1ZSAqIHVyYW5nZV9TMSwgMC4wLCBjb2xvci53KSwgY29sb3IudykpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGl0aGVyX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSAoaGFsZjQoMS4wKSAtIG91dHB1dF9TMSkgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GYJBYAQCAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAA1BgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQgdVNyY1RGX1MwWzddOwp1bmlmb3JtIGZsb2F0M3gzIHVDb2xvclhmb3JtX1MwOwp1bmlmb3JtIGZsb2F0IHVEc3RURl9TMFs3XTsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKZmxvYXQgc3JjX3RmX1MwKGZsb2F0IHgpIAp7CglmbG9hdCBHID0gdVNyY1RGX1MwWzBdOwoJZmxvYXQgQSA9IHVTcmNURl9TMFsxXTsKCWZsb2F0IEIgPSB1U3JjVEZfUzBbMl07CglmbG9hdCBDID0gdVNyY1RGX1MwWzNdOwoJZmxvYXQgRCA9IHVTcmNURl9TMFs0XTsKCWZsb2F0IEUgPSB1U3JjVEZfUzBbNV07CglmbG9hdCBGID0gdVNyY1RGX1MwWzZdOwoJZmxvYXQgcyA9IHNpZ24oeCk7Cgl4ID0gYWJzKHgpOwoJeCA9ICh4IDwgRCkgPyAoQyAqIHgpICsgRiA6IHBvdyhBICogeCArIEIsIEcpICsgRTsKCXJldHVybiBzICogeDsKfQpmbG9hdCBkc3RfdGZfUzAoZmxvYXQgeCkgCnsKCWZsb2F0IEcgPSB1RHN0VEZfUzBbMF07CglmbG9hdCBBID0gdURzdFRGX1MwWzFdOwoJZmxvYXQgQiA9IHVEc3RURl9TMFsyXTsKCWZsb2F0IEMgPSB1RHN0VEZfUzBbM107CglmbG9hdCBEID0gdURzdFRGX1MwWzRdOwoJZmxvYXQgRSA9IHVEc3RURl9TMFs1XTsKCWZsb2F0IEYgPSB1RHN0VEZfUzBbNl07CglmbG9hdCBzID0gc2lnbih4KTsKCXggPSBhYnMoeCk7Cgl4ID0gKHggPCBEKSA/IChDICogeCkgKyBGIDogcG93KEEgKiB4ICsgQiwgRykgKyBFOwoJcmV0dXJuIHMgKiB4Owp9CmZsb2F0NCBnYW11dF94Zm9ybV9TMChmbG9hdDQgY29sb3IpIAp7Cgljb2xvci5yZ2IgPSAodUNvbG9yWGZvcm1fUzAgKiBjb2xvci5yZ2IpOwoJcmV0dXJuIGNvbG9yOwp9CmhhbGY0IGNvbG9yX3hmb3JtX1MwKGZsb2F0NCBjb2xvcikgCnsKCWNvbG9yLnIgPSBzcmNfdGZfUzAoY29sb3Iucik7Cgljb2xvci5nID0gc3JjX3RmX1MwKGNvbG9yLmcpOwoJY29sb3IuYiA9IHNyY190Zl9TMChjb2xvci5iKTsKCWNvbG9yID0gZ2FtdXRfeGZvcm1fUzAoY29sb3IpOwoJY29sb3IuciA9IGRzdF90Zl9TMChjb2xvci5yKTsKCWNvbG9yLmcgPSBkc3RfdGZfUzAoY29sb3IuZyk7Cgljb2xvci5iID0gZHN0X3RmX1MwKGNvbG9yLmIpOwoJcmV0dXJuIGhhbGY0KGNvbG9yKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKGNvbG9yX3hmb3JtX1MwKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","DASAAAAAAAAAAAEAAFQAAIGAAEAOB77776PUEAIBAAAAAABAAAAAAABAMQAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1MpAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0MiB1QXRsYXNTaXplSW52X1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gdXNob3J0MiBpblRleHR1cmVDb29yZHM7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBCaXRtYXBUZXh0CglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc1NpemVJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAAAsAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZUZXhJbmRleF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB1Q29sb3JfUzA7CgloYWxmNCB0ZXhDb2xvcjsKCXsKCQl0ZXhDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdlRleHR1cmVDb29yZHNfUzApOwoJfQoJb3V0cHV0Q29sb3JfUzAgPSBvdXRwdXRDb2xvcl9TMCAqIHRleENvbG9yOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAJIBAGMVUOTUABAAAAQAAIAAAAAWFBIEMVUOTUAAAAAAQAAIAAAACIGAACAAAAACQIKACCAYAAEAAAAAAAAZQAAAAAABAAAAACAZAAAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAOgGAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKY29uc3QgaW50IGtNYXhMb29wTGltaXRfUzFfYzAgPSA4Owp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxNF07CnVuaWZvcm0gaGFsZjIgdWRpcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJZmxvYXQyIGluQ29vcmQgPSBfY29vcmRzOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBjbGFtcChzdWJzZXRDb29yZC54LCB1Y2xhbXBfUzFfYzBfYzBfYzAueCwgdWNsYW1wX1MxX2MwX2MwX2MwLnopOwoJY2xhbXBlZENvb3JkLnkgPSBzdWJzZXRDb29yZC55OwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBjbGFtcGVkQ29vcmQpOwoJcmV0dXJuIHRleHR1cmVDb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKF9pbnB1dCwgZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMCkgKiBfY29vcmRzLnh5MSk7Cn0KaGFsZjQgcnVudGltZV9zaGFkZXJfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJaGFsZjQgc3VtID0gaGFsZjQoMC4wKTsKCWZvciAoaW50IGkgPSAwO2kgPCBrTWF4TG9vcExpbWl0X1MxX2MwOyArK2kpIAoJewoJCWhhbGY0IHMgPSB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXTsKCQlzdW0gKz0gcy55ICogTWF0cml4RWZmZWN0X1MxX2MwX2MwKF90bXBfMF9pbkNvbG9yLCBfdG1wXzFfY29vcmRzICsgZmxvYXQyKHMueCAqIHVkaXJfUzFfYzApKTsKCQlzdW0gKz0gcy53ICogTWF0cml4RWZmZWN0X1MxX2MwX2MwKF90bXBfMF9pbkNvbG9yLCBfdG1wXzFfY29vcmRzICsgZmxvYXQyKHMueiAqIHVkaXJfUzFfYzApKTsKCX0KCXJldHVybiBoYWxmNChzdW0pOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","B2AAQAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABGABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1NZAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZiBpbkNvdmVyYWdlOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cgl2aW5Db3ZlcmFnZV9TMCA9IGluQ292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAJgBAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBoYWxmNCB1Q29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZiB2aW5Db3ZlcmFnZV9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIERlZmF1bHRHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHVDb2xvcl9TMDsKCWhhbGYgYWxwaGEgPSAxLjA7CglhbHBoYSA9IHZpbkNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChhbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAoAAABpblBvc2l0aW9uAAAKAAAAaW5Db3ZlcmFnZQAAAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAADIAAAAAJKLCRQQAAAAAAMAAAAACAAAAAIAAAAALAIQEVFRIYIAAAAAAGAAAAABAAAAAAAAAAAAAGABQAAAAEAAAAAAAEEBQA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAACEBgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCmNvbnN0IGludCBrRmlsbEJXX1MxX2MwID0gMDsKY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwp1bmlmb3JtIGZsb2F0NCB1ZWxsaXBzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdXNjYWxlX1MxX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBFbGxpcHNlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIGQgPSBza19GcmFnQ29vcmQueHkgLSB1ZWxsaXBzZV9TMV9jMC54eTsKCWlmIChib29sKGludCgwKSkpIAoJewoJCWQgKj0gdXNjYWxlX1MxX2MwLnk7Cgl9CglmbG9hdDIgWiA9IGQgKiB1ZWxsaXBzZV9TMV9jMC56dzsKCWZsb2F0IGltcGxpY2l0ID0gZG90KFosIGQpIC0gMS4wOwoJZmxvYXQgZ3JhZF9kb3QgPSA0LjAgKiBkb3QoWiwgWik7CglpZiAoYm9vbChpbnQoMCkpKSAKCXsKCQlncmFkX2RvdCA9IG1heChncmFkX2RvdCwgNi4xMDM2ZS0wNSk7Cgl9CgllbHNlIAoJewoJCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjE3NTVlLTM4KTsKCX0KCWZsb2F0IGFwcHJveF9kaXN0ID0gaW1wbGljaXQgKiBpbnZlcnNlc3FydChncmFkX2RvdCk7CglpZiAoYm9vbChpbnQoMCkpKSAKCXsKCQlhcHByb3hfZGlzdCAqPSB1c2NhbGVfUzFfYzAueDsKCX0KCWhhbGYgYWxwaGE7CglpZiAoaW50KDEpID09IGtGaWxsQldfUzFfYzApIAoJewoJCWFscGhhID0gaGFsZihhcHByb3hfZGlzdCA+IDAuMCA/IDAuMCA6IDEuMCk7Cgl9CgllbHNlIGlmIChpbnQoMSkgPT0ga0ZpbGxBQV9TMV9jMCkgCgl7CgkJYWxwaGEgPSBzYXR1cmF0ZSgwLjUgLSBoYWxmKGFwcHJveF9kaXN0KSk7Cgl9CgllbHNlIGlmIChpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzApIAoJewoJCWFscGhhID0gaGFsZihhcHByb3hfZGlzdCA+IDAuMCk7Cgl9CgllbHNlIAoJewoJCWFscGhhID0gc2F0dXJhdGUoMC41ICsgaGFsZihhcHByb3hfZGlzdCkpOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGFscGhhKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShFbGxpcHNlX1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpLCBoYWxmNCgxKSkpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","CMRQCIAABBYAAAEIXBAAACDQMAABRAFAAAAAAAAAAAAAAAGABQAAAAEAAAAAAAEEBQAAAAA":"DAAAAExTS1OZAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDIgaW5FbGxpcHNlT2Zmc2V0OwppbiBmbG9hdDQgaW5FbGxpcHNlUmFkaWk7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2RWxsaXBzZU9mZnNldHNfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0NCB2RWxsaXBzZVJhZGlpX1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRWxsaXBzZUdlb21ldHJ5UHJvY2Vzc29yCgl2RWxsaXBzZU9mZnNldHNfUzAgPSBpbkVsbGlwc2VPZmZzZXQ7Cgl2RWxsaXBzZVJhZGlpX1MwID0gaW5FbGxpcHNlUmFkaWk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gdWxvY2FsTWF0cml4X1MwLnh6ICogaW5Qb3NpdGlvbiArIHVsb2NhbE1hdHJpeF9TMC55dzsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAAAA2QMAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2RWxsaXBzZU9mZnNldHNfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQ0IHZFbGxpcHNlUmFkaWlfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiBvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHk7CglvZmZzZXQgKj0gdkVsbGlwc2VSYWRpaV9TMC54eTsKCWZsb2F0IHRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZmxvYXQyIGdyYWQgPSAyLjAqb2Zmc2V0KnZFbGxpcHNlUmFkaWlfUzAueHk7CglmbG9hdCBncmFkX2RvdCA9IGRvdChncmFkLCBncmFkKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjE3NTVlLTM4KTsKCWZsb2F0IGludmxlbiA9IGludmVyc2VzcXJ0KGdyYWRfZG90KTsKCWZsb2F0IGVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNS10ZXN0Kmludmxlbik7CglvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHkqdkVsbGlwc2VSYWRpaV9TMC56dzsKCXRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZ3JhZCA9IDIuMCpvZmZzZXQqdkVsbGlwc2VSYWRpaV9TMC56dzsKCWdyYWRfZG90ID0gZG90KGdyYWQsIGdyYWQpOwoJaW52bGVuID0gaW52ZXJzZXNxcnQoZ3JhZF9kb3QpOwoJZWRnZUFscGhhICo9IHNhdHVyYXRlKDAuNSt0ZXN0Kmludmxlbik7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGhhbGYoZWRnZUFscGhhKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5FbGxpcHNlT2Zmc2V0AA4AAABpbkVsbGlwc2VSYWRpaQAAAAAAAA==","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBIA5HZ2CAAAAAAAEAACAAAAASBEAAQAAAAAUCCQAQQGAABAAAAAAAAGMAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1POAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAlgQAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZC54ID0gY2xhbXAoc3Vic2V0Q29vcmQueCwgdWNsYW1wX1MxX2MwX2MwLngsIHVjbGFtcF9TMV9jMF9jMC56KTsKCWNsYW1wZWRDb29yZC55ID0gc3Vic2V0Q29vcmQueTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBNYXRyaXhFZmZlY3RfUzFfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","AYQQ5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACAAYQADAAAIATYVIE4AKAAAYAAAAAAAAIAAAABL4JJUKECOAEAAAMAAAAAAAAAAAAAMADAAAAAIAAAAAAAIIDAAAA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAQAAAGgGAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKY29uc3QgaW50IGtGaWxsQldfUzFfYzAgPSAwOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1cmVjdFVuaWZvcm1fUzFfYzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IFJlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGNvdmVyYWdlOwoJaWYgKGludCgxKSA9PSBrRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKGFsbChncmVhdGVyVGhhbihmbG9hdDQoc2tfRnJhZ0Nvb3JkLnh5LCB1cmVjdFVuaWZvcm1fUzFfYzAuencpLCBmbG9hdDQodXJlY3RVbmlmb3JtX1MxX2MwLnh5LCBza19GcmFnQ29vcmQueHkpKSkpOwoJfQoJZWxzZSAKCXsKCQloYWxmNCBkaXN0czQgPSBzYXR1cmF0ZShoYWxmNCgxLjAsIDEuMCwgLTEuMCwgLTEuMCkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIHVyZWN0VW5pZm9ybV9TMV9jMCkpOwoJCWhhbGYyIGRpc3RzMiA9IChkaXN0czQueHkgKyBkaXN0czQuencpIC0gMS4wOwoJCWNvdmVyYWdlID0gZGlzdHMyLnggKiBkaXN0czIueTsKCX0KCWlmIChpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEFBX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IDEuMCAtIGNvdmVyYWdlOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGNvdmVyYWdlKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShSZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGYgZGlzdGFuY2VUb0lubmVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKGQgLSBjaXJjbGVFZGdlLncpKTsKCWhhbGYgaW5uZXJBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9Jbm5lckVkZ2UpOwoJZWRnZUFscGhhICo9IGlubmVyQWxwaGE7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAALIAAAAB4FJEIEKOAAAAAMAAAAAUAAQAADAWB7EXCYCAAAGAAAAAAAACAAAQAK7DKJCBCTQAAAADAAAAAAAAAAAAYCADIAAAAAG5O33IKAAAAAAMAAAAAIAAAAAABMJDQP2FUWQUAAAQAAQAAAAAAAAAADGAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAAxiIAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKY29uc3QgaW50IGtGaWxsQldfUzJfYzAgPSAwOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzJfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzJfYzAgPSAzOwpjb25zdCBpbnQga0ZpbGxBQV9TMl9jMV9jMCA9IDE7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxCV19TMl9jMV9jMCA9IDI7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCA9IDM7CnVuaWZvcm0gZmxvYXQ0IHVfY29sb3JfUzFfYzA7CnVuaWZvcm0gZmxvYXQ0IHVfY29tcG9zaXRlXzFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2VudGVyX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbWF4X3JhZGl1c19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X25vaXNlX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbm9pc2VfcGhhc2VfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUyX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjNfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBmbG9hdDQgdXJlY3RVbmlmb3JtX1MyX2MwOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MyX2MxX2MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpOwp2b2lkIEZMVF9tYWluX1MxX2MwKCk7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCkgCnsKCXJldHVybiBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMC54eTsKfQpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKSAKewoJcmV0dXJuIGZsb2F0MngyKHJhZC54LCAtcmFkLnksIHJhZC55LCByYWQueCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cikgCnsKCWZsb2F0IGJsdXJfaGFsZiA9IGJsdXIgKiAwLjU7CglmbG9hdCBkID0gZGlzdGFuY2UodXYsIHh5KTsKCXJldHVybiAxLjAgLSBzbW9vdGhzdGVwKDEuMCAtIGJsdXJfaGFsZiwgMS4wICsgYmx1cl9oYWxmLCBkIC8gcmFkaXVzKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKSAKewoJZmxvYXQyIHBhcmFtID0gcm90YXRpb247CglwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAocGFyYW0pICogKHh5IC0gcCkgKyB4eTsKCXAgPSBtb2QocCwgZmxvYXQyKGNlbGxfZGlhbWV0ZXIpKSAvIHJlc29sdXRpb247CglmbG9hdCBjZWxsX3V2ID0gKGNlbGxfZGlhbWV0ZXIgLyByZXNvbHV0aW9uLnkpICogMC41OwoJZmxvYXQgciA9IDAuNjUgKiBjZWxsX3V2OwoJZmxvYXQyIHBhcmFtXzEgPSBwOwoJZmxvYXQyIHBhcmFtXzIgPSBmbG9hdDIoY2VsbF91dik7CglmbG9hdCBwYXJhbV8zID0gcjsKCWZsb2F0IHBhcmFtXzQgPSByICogNTAuMDsKCXJldHVybiBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dikgCnsKCWZsb2F0MiB1dl9zY2FsZSA9IHV2ICogZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW0gPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMiA9IHVfY2lyY2xlMV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8zID0gdV9yb3RhdGlvbjFfUzFfYzA7CglmbG9hdCBwYXJhbV80ID0gMC4xNzsKCWZsb2F0IF8zMTkgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7CglmbG9hdCBnMSA9IF8zMTk7CglmbG9hdDIgcGFyYW1fNSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzYgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV83ID0gdV9jaXJjbGUyX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzggPSB1X3JvdGF0aW9uMl9TMV9jMDsKCWZsb2F0IHBhcmFtXzkgPSAwLjI7CglmbG9hdCBfMzMxID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBnMiA9IF8zMzE7CglmbG9hdDIgcGFyYW1fMTAgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzEyID0gdV9jaXJjbGUzX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdV9yb3RhdGlvbjNfUzFfYzA7CglmbG9hdCBwYXJhbV8xNCA9IDAuMjc1OwoJZmxvYXQgXzM0NCA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIsIHBhcmFtXzEzLCBwYXJhbV8xNCk7CglmbG9hdCBnMyA9IF8zNDQ7CglmbG9hdCB2ID0gKChnMSAqIGcxICsgZzIpIC0gZzMpICogMC41OwoJcmV0dXJuIGNsYW1wKDAuNDUgKyAwLjggKiB2LCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cikgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQyIHBhcmFtXzEgPSB4eTsKCWZsb2F0IHBhcmFtXzIgPSByYWRpdXMgKyB0aGlja25lc3M7CglmbG9hdCBwYXJhbV8zID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9vdXRlciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0MiBwYXJhbV80ID0gdXY7CglmbG9hdDIgcGFyYW1fNSA9IHh5OwoJZmxvYXQgcGFyYW1fNiA9IG1heChyYWRpdXMgLSB0aGlja25lc3MsIDAuMCk7CglmbG9hdCBwYXJhbV83ID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9pbm5lciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcpOwoJcmV0dXJuIGNsYW1wKGNpcmNsZV9vdXRlciAtIGNpcmNsZV9pbm5lciwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKSAKewoJbiA9IGZyYWN0KG4gKiBmbG9hdDIoNS4zOTg3LCA1LjQ0MjEpKTsKCW4gKz0gZmxvYXQyKGRvdChuLnl4LCBuICsgZmxvYXQyKDIxLjUzNTEsIDE0LjMxMzcpKSk7CglmbG9hdCB4eSA9IG4ueCAqIG4ueTsKCXJldHVybiAoZnJhY3QoeHkgKiA5NS40MzA3KSArIGZyYWN0KHh5ICogNzUuMDQ5NjEpKSAtIDEuMDsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCkgCnsKCXJldHVybiBzdGVwKGwsIHYpICogKDEuMCAtIHN0ZXAoaCwgdikpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdCBfMjQyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAocGFyYW0pOwoJZmxvYXQgbiA9IF8yNDI7CglmbG9hdCBwYXJhbV8xID0gbjsKCWZsb2F0IHBhcmFtXzIgPSAwLjA7CglmbG9hdCBwYXJhbV8zID0gMC4wNTsKCWZsb2F0IHMgPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdCBwYXJhbV80ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjM1KSk7CglmbG9hdCBwYXJhbV81ID0gMC4xOwoJZmxvYXQgcGFyYW1fNiA9IDAuMTU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82KTsKCWZsb2F0IHBhcmFtXzcgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuNykpOwoJZmxvYXQgcGFyYW1fOCA9IDAuMjsKCWZsb2F0IHBhcmFtXzkgPSAwLjI1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBwYXJhbV8xMCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMS4wNSkpOwoJZmxvYXQgcGFyYW1fMTEgPSAwLjM7CglmbG9hdCBwYXJhbV8xMiA9IDAuMzU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyKTsKCXJldHVybiBjbGFtcChzLCAwLjAsIDEuMCkgKiAwLjU1Owp9CnZvaWQgRkxUX21haW5fUzFfYzAoKSAKewoJdV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueDsKCXVfc3BhcmtsZV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueTsKCXVfYmx1cl9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAuejsKCXVfcmFkaXVzX3NjYWxlX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC53OwoJZmxvYXQyIHAgPSBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CglmbG9hdDIgdXZfMSA9IHAgKiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CglmbG9hdDIgZGVuc2l0eV91diA9IHV2XzEgLSBtb2QocCwgdV9ub2lzZV9zY2FsZV9TMV9jMCk7CglmbG9hdCByYWRpdXMgPSB1X21heF9yYWRpdXNfUzFfYzAgKiB1X3JhZGl1c19zY2FsZV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHV2XzE7CglmbG9hdCB0dXJidWxlbmNlID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChwYXJhbV8xMyk7CglmbG9hdDIgcGFyYW1fMTQgPSBwOwoJZmxvYXQyIHBhcmFtXzE1ID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8xNiA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzE3ID0gMC4wNSAqIHVfbWF4X3JhZGl1c19TMV9jMDsKCWZsb2F0IHBhcmFtXzE4ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgcmluZyA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChwYXJhbV8xNCwgcGFyYW1fMTUsIHBhcmFtXzE2LCBwYXJhbV8xNywgcGFyYW1fMTgpOwoJZmxvYXQyIHBhcmFtXzE5ID0gZGVuc2l0eV91djsKCWZsb2F0IHBhcmFtXzIwID0gdV9ub2lzZV9waGFzZV9TMV9jMDsKCWZsb2F0IHNwYXJrbGUgPSAoKEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAocGFyYW1fMTksIHBhcmFtXzIwKSAqIHJpbmcpICogdHVyYnVsZW5jZSkgKiB1X3NwYXJrbGVfYWxwaGFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMjEgPSBwOwoJZmxvYXQyIHBhcmFtXzIyID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8yMyA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzI0ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgd2F2ZV9hbHBoYSA9IChGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8yMSwgcGFyYW1fMjIsIHBhcmFtXzIzLCBwYXJhbV8yNCkgKiB1X2FscGhhX1MxX2MwKSAqIHVfY29sb3JfUzFfYzAudzsKCWZsb2F0NCB3YXZlX2NvbG9yID0gZmxvYXQ0KHVfY29sb3JfUzFfYzAueHl6ICogd2F2ZV9hbHBoYSwgd2F2ZV9hbHBoYSk7CglmcmFnQ29sb3JfUzFfYzAgPSBtaXgod2F2ZV9jb2xvciwgZmxvYXQ0KDEuMCksIGZsb2F0NChzcGFya2xlKSk7Cn0KaGFsZjQgcnVudGltZV9zaGFkZXJfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAgPSBmbG9hdDQoX3RtcF8xX2Nvb3JkcywgMC4wLCAwLjApOwoJRkxUX21haW5fUzFfYzAoKTsKCXJldHVybiBoYWxmNChoYWxmNChmcmFnQ29sb3JfUzFfYzApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHJ1bnRpbWVfc2hhZGVyX1MxX2MwKF9pbnB1dCk7Cn0KaGFsZjQgUmVjdF9TMl9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGYgY292ZXJhZ2U7CglpZiAoaW50KDEpID09IGtGaWxsQldfUzJfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MwKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoYWxsKGdyZWF0ZXJUaGFuKGZsb2F0NChza19GcmFnQ29vcmQueHksIHVyZWN0VW5pZm9ybV9TMl9jMC56dyksIGZsb2F0NCh1cmVjdFVuaWZvcm1fUzJfYzAueHksIHNrX0ZyYWdDb29yZC54eSkpKSk7Cgl9CgllbHNlIAoJewoJCWhhbGY0IGRpc3RzNCA9IHNhdHVyYXRlKGhhbGY0KDEuMCwgMS4wLCAtMS4wLCAtMS4wKSAqIGhhbGY0KHNrX0ZyYWdDb29yZC54eXh5IC0gdXJlY3RVbmlmb3JtX1MyX2MwKSk7CgkJaGFsZjIgZGlzdHMyID0gKGRpc3RzNC54eSArIGRpc3RzNC56dykgLSAxLjA7CgkJY292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJfQoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMl9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzJfYzApIAoJewoJCWNvdmVyYWdlID0gMS4wIC0gY292ZXJhZ2U7Cgl9CglyZXR1cm4gaGFsZjQoaGFsZjQoY292ZXJhZ2UpKTsKfQpoYWxmNCBDaXJjbGVfUzJfYzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8zX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGQ7CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCkgCgl7CgkJZCA9IGhhbGYoKGxlbmd0aCgodWNpcmNsZV9TMl9jMV9jMC54eSAtIHNrX0ZyYWdDb29yZC54eSkgKiB1Y2lyY2xlX1MyX2MxX2MwLncpIC0gMS4wKSAqIHVjaXJjbGVfUzJfYzFfYzAueik7Cgl9CgllbHNlIAoJewoJCWQgPSBoYWxmKCgxLjAgLSBsZW5ndGgoKHVjaXJjbGVfUzJfYzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMl9jMV9jMC53KSkgKiB1Y2lyY2xlX1MyX2MxX2MwLnopOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGludCgxKSA9PSBrRmlsbEFBX1MyX2MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCA/IHNhdHVyYXRlKGQpIDogaGFsZihkID4gMC41KSkpOwp9CmhhbGY0IEJsZW5kX1MyX2MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoX3NyYywgQ2lyY2xlX1MyX2MxX2MwKF9zcmMpKTsKfQpoYWxmNCBCbGVuZF9TMihoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKFJlY3RfUzJfYzAoX3NyYyksIEJsZW5kX1MyX2MxKF9zcmMsIF9kc3QpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA="}} \ No newline at end of file +{"platform":"android","name":"XQ DE72","engineRevision":"db49896cf25ceabc44096d5f088d86414e05a7aa","data":{"AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAEMCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","B2ABSAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABGABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1PDAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGhhbGY0IHVDb2xvcl9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGYgaW5Db3ZlcmFnZTsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gdUNvbG9yX1MwOwoJY29sb3IgPSBjb2xvciAqIGluQ292ZXJhZ2U7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8zX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzFfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAUQEAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIERlZmF1bHRHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAoAAABpblBvc2l0aW9uAAAKAAAAaW5Db3ZlcmFnZQAAAAAAAA==","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAIIBAAAABAAAAALIEAAAAAAAAAAAAAABQAMAAAABAAAAAAABBAMAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAL8CAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCkucnJycjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZJAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAABAAAAAABBAMABQAMAAAABAAAAAAABBAMAAA":"DAAAAExTS1N3AQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAApAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpLCBvdXRwdXRDb2xvcl9TMCkpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1N4AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0MiB1QXRsYXNTaXplSW52X1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gdXNob3J0MiBpblRleHR1cmVDb29yZHM7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQml0bWFwVGV4dAoJaW50IHRleElkeCA9IDA7CglmbG9hdDIgdW5vcm1UZXhDb29yZHMgPSBmbG9hdDIoaW5UZXh0dXJlQ29vcmRzLngsIGluVGV4dHVyZUNvb3Jkcy55KTsKCXZUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHMgKiB1QXRsYXNTaXplSW52X1MwOwoJdlRleEluZGV4X1MwID0gZmxvYXQodGV4SWR4KTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwMTsKfQoAAAAACwIAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZUZXhJbmRleF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABZQA6AAAEAAAAAAAMADAAAAAIAAAAAAAIIDA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAQAAAOgDAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglhbHBoYSA9IDEuMCAtIGFscGhhOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAACAB4QA4AAAEAAAAAAAMADAAAAAIAAAAAAAIID":"DAAAAExTS1N4AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0MiB1QXRsYXNTaXplSW52X1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gdXNob3J0MiBpblRleHR1cmVDb29yZHM7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQml0bWFwVGV4dAoJaW50IHRleElkeCA9IDA7CglmbG9hdDIgdW5vcm1UZXhDb29yZHMgPSBmbG9hdDIoaW5UZXh0dXJlQ29vcmRzLngsIGluVGV4dHVyZUNvb3Jkcy55KTsKCXZUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHMgKiB1QXRsYXNTaXplSW52X1MwOwoJdlRleEluZGV4X1MwID0gZmxvYXQodGV4SWR4KTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwMTsKfQoBAAAAmgMAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MxOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMTsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","AYQQ5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAANQCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmIGRpc3RhbmNlVG9Jbm5lckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqIChkIC0gY2lyY2xlRWRnZS53KSk7CgloYWxmIGlubmVyQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvSW5uZXJFZGdlKTsKCWVkZ2VBbHBoYSAqPSBpbm5lckFscGhhOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAADvAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJIBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAGIAAAAAAAIAAAAAQGIAAAAAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAJhwAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb2xvcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb21wb3NpdGVfMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jZW50ZXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9tYXhfcmFkaXVzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfbm9pc2Vfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9ub2lzZV9waGFzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUxX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlM19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24yX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uM19TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpOwp2b2lkIEZMVF9tYWluX1MxX2MwKCk7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCkgCnsKCXJldHVybiBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMC54eTsKfQpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKSAKewoJcmV0dXJuIGZsb2F0MngyKHJhZC54LCAtcmFkLnksIHJhZC55LCByYWQueCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cikgCnsKCWZsb2F0IGJsdXJfaGFsZiA9IGJsdXIgKiAwLjU7CglmbG9hdCBkID0gZGlzdGFuY2UodXYsIHh5KTsKCXJldHVybiAxLjAgLSBzbW9vdGhzdGVwKDEuMCAtIGJsdXJfaGFsZiwgMS4wICsgYmx1cl9oYWxmLCBkIC8gcmFkaXVzKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKSAKewoJZmxvYXQyIHBhcmFtID0gcm90YXRpb247CglwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAocGFyYW0pICogKHh5IC0gcCkgKyB4eTsKCXAgPSBtb2QocCwgZmxvYXQyKGNlbGxfZGlhbWV0ZXIpKSAvIHJlc29sdXRpb247CglmbG9hdCBjZWxsX3V2ID0gKGNlbGxfZGlhbWV0ZXIgLyByZXNvbHV0aW9uLnkpICogMC41OwoJZmxvYXQgciA9IDAuNjUgKiBjZWxsX3V2OwoJZmxvYXQyIHBhcmFtXzEgPSBwOwoJZmxvYXQyIHBhcmFtXzIgPSBmbG9hdDIoY2VsbF91dik7CglmbG9hdCBwYXJhbV8zID0gcjsKCWZsb2F0IHBhcmFtXzQgPSByICogNTAuMDsKCXJldHVybiBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dikgCnsKCWZsb2F0MiB1dl9zY2FsZSA9IHV2ICogZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW0gPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMiA9IHVfY2lyY2xlMV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8zID0gdV9yb3RhdGlvbjFfUzFfYzA7CglmbG9hdCBwYXJhbV80ID0gMC4xNzsKCWZsb2F0IF8zMTkgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7CglmbG9hdCBnMSA9IF8zMTk7CglmbG9hdDIgcGFyYW1fNSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzYgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV83ID0gdV9jaXJjbGUyX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzggPSB1X3JvdGF0aW9uMl9TMV9jMDsKCWZsb2F0IHBhcmFtXzkgPSAwLjI7CglmbG9hdCBfMzMxID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBnMiA9IF8zMzE7CglmbG9hdDIgcGFyYW1fMTAgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzEyID0gdV9jaXJjbGUzX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdV9yb3RhdGlvbjNfUzFfYzA7CglmbG9hdCBwYXJhbV8xNCA9IDAuMjc1OwoJZmxvYXQgXzM0NCA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIsIHBhcmFtXzEzLCBwYXJhbV8xNCk7CglmbG9hdCBnMyA9IF8zNDQ7CglmbG9hdCB2ID0gKChnMSAqIGcxICsgZzIpIC0gZzMpICogMC41OwoJcmV0dXJuIGNsYW1wKDAuNDUgKyAwLjggKiB2LCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cikgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQyIHBhcmFtXzEgPSB4eTsKCWZsb2F0IHBhcmFtXzIgPSByYWRpdXMgKyB0aGlja25lc3M7CglmbG9hdCBwYXJhbV8zID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9vdXRlciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0MiBwYXJhbV80ID0gdXY7CglmbG9hdDIgcGFyYW1fNSA9IHh5OwoJZmxvYXQgcGFyYW1fNiA9IG1heChyYWRpdXMgLSB0aGlja25lc3MsIDAuMCk7CglmbG9hdCBwYXJhbV83ID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9pbm5lciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcpOwoJcmV0dXJuIGNsYW1wKGNpcmNsZV9vdXRlciAtIGNpcmNsZV9pbm5lciwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKSAKewoJbiA9IGZyYWN0KG4gKiBmbG9hdDIoNS4zOTg3LCA1LjQ0MjEpKTsKCW4gKz0gZmxvYXQyKGRvdChuLnl4LCBuICsgZmxvYXQyKDIxLjUzNTEsIDE0LjMxMzcpKSk7CglmbG9hdCB4eSA9IG4ueCAqIG4ueTsKCXJldHVybiAoZnJhY3QoeHkgKiA5NS40MzA3KSArIGZyYWN0KHh5ICogNzUuMDQ5NjEpKSAtIDEuMDsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCkgCnsKCXJldHVybiBzdGVwKGwsIHYpICogKDEuMCAtIHN0ZXAoaCwgdikpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdCBfMjQyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAocGFyYW0pOwoJZmxvYXQgbiA9IF8yNDI7CglmbG9hdCBwYXJhbV8xID0gbjsKCWZsb2F0IHBhcmFtXzIgPSAwLjA7CglmbG9hdCBwYXJhbV8zID0gMC4wNTsKCWZsb2F0IHMgPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdCBwYXJhbV80ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjM1KSk7CglmbG9hdCBwYXJhbV81ID0gMC4xOwoJZmxvYXQgcGFyYW1fNiA9IDAuMTU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82KTsKCWZsb2F0IHBhcmFtXzcgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuNykpOwoJZmxvYXQgcGFyYW1fOCA9IDAuMjsKCWZsb2F0IHBhcmFtXzkgPSAwLjI1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBwYXJhbV8xMCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMS4wNSkpOwoJZmxvYXQgcGFyYW1fMTEgPSAwLjM7CglmbG9hdCBwYXJhbV8xMiA9IDAuMzU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyKTsKCXJldHVybiBjbGFtcChzLCAwLjAsIDEuMCkgKiAwLjU1Owp9CnZvaWQgRkxUX21haW5fUzFfYzAoKSAKewoJdV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueDsKCXVfc3BhcmtsZV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueTsKCXVfYmx1cl9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAuejsKCXVfcmFkaXVzX3NjYWxlX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC53OwoJZmxvYXQyIHAgPSBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CglmbG9hdDIgdXZfMSA9IHAgKiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CglmbG9hdDIgZGVuc2l0eV91diA9IHV2XzEgLSBtb2QocCwgdV9ub2lzZV9zY2FsZV9TMV9jMCk7CglmbG9hdCByYWRpdXMgPSB1X21heF9yYWRpdXNfUzFfYzAgKiB1X3JhZGl1c19zY2FsZV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHV2XzE7CglmbG9hdCB0dXJidWxlbmNlID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChwYXJhbV8xMyk7CglmbG9hdDIgcGFyYW1fMTQgPSBwOwoJZmxvYXQyIHBhcmFtXzE1ID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8xNiA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzE3ID0gMC4wNSAqIHVfbWF4X3JhZGl1c19TMV9jMDsKCWZsb2F0IHBhcmFtXzE4ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgcmluZyA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChwYXJhbV8xNCwgcGFyYW1fMTUsIHBhcmFtXzE2LCBwYXJhbV8xNywgcGFyYW1fMTgpOwoJZmxvYXQyIHBhcmFtXzE5ID0gZGVuc2l0eV91djsKCWZsb2F0IHBhcmFtXzIwID0gdV9ub2lzZV9waGFzZV9TMV9jMDsKCWZsb2F0IHNwYXJrbGUgPSAoKEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAocGFyYW1fMTksIHBhcmFtXzIwKSAqIHJpbmcpICogdHVyYnVsZW5jZSkgKiB1X3NwYXJrbGVfYWxwaGFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMjEgPSBwOwoJZmxvYXQyIHBhcmFtXzIyID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8yMyA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzI0ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgd2F2ZV9hbHBoYSA9IChGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8yMSwgcGFyYW1fMjIsIHBhcmFtXzIzLCBwYXJhbV8yNCkgKiB1X2FscGhhX1MxX2MwKSAqIHVfY29sb3JfUzFfYzAudzsKCWZsb2F0NCB3YXZlX2NvbG9yID0gZmxvYXQ0KHVfY29sb3JfUzFfYzAueHl6ICogd2F2ZV9hbHBoYSwgd2F2ZV9hbHBoYSk7CglmcmFnQ29sb3JfUzFfYzAgPSBtaXgod2F2ZV9jb2xvciwgZmxvYXQ0KDEuMCksIGZsb2F0NChzcGFya2xlKSk7Cn0KaGFsZjQgcnVudGltZV9zaGFkZXJfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAgPSBmbG9hdDQoX3RtcF8xX2Nvb3JkcywgMC4wLCAwLjApOwoJRkxUX21haW5fUzFfYzAoKTsKCXJldHVybiBoYWxmNChoYWxmNChmcmFnQ29sb3JfUzFfYzApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHJ1bnRpbWVfc2hhZGVyX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAA6IAMAAACAAAAAABGABAAAAAEAAAAAIBEABAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAAtR0AACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb2xvcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb21wb3NpdGVfMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jZW50ZXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9tYXhfcmFkaXVzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfbm9pc2Vfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9ub2lzZV9waGFzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUxX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlM19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24yX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uM19TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MyOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMjsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KTsKdm9pZCBGTFRfbWFpbl9TMV9jMCgpOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpIAp7CglyZXR1cm4gZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAueHk7Cn0KZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCkgCnsKCXJldHVybiBmbG9hdDJ4MihyYWQueCwgLXJhZC55LCByYWQueSwgcmFkLngpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpIAp7CglmbG9hdCBibHVyX2hhbGYgPSBibHVyICogMC41OwoJZmxvYXQgZCA9IGRpc3RhbmNlKHV2LCB4eSk7CglyZXR1cm4gMS4wIC0gc21vb3Roc3RlcCgxLjAgLSBibHVyX2hhbGYsIDEuMCArIGJsdXJfaGFsZiwgZCAvIHJhZGl1cyk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcikgCnsKCWZsb2F0MiBwYXJhbSA9IHJvdGF0aW9uOwoJcCA9IEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKHBhcmFtKSAqICh4eSAtIHApICsgeHk7CglwID0gbW9kKHAsIGZsb2F0MihjZWxsX2RpYW1ldGVyKSkgLyByZXNvbHV0aW9uOwoJZmxvYXQgY2VsbF91diA9IChjZWxsX2RpYW1ldGVyIC8gcmVzb2x1dGlvbi55KSAqIDAuNTsKCWZsb2F0IHIgPSAwLjY1ICogY2VsbF91djsKCWZsb2F0MiBwYXJhbV8xID0gcDsKCWZsb2F0MiBwYXJhbV8yID0gZmxvYXQyKGNlbGxfdXYpOwoJZmxvYXQgcGFyYW1fMyA9IHI7CglmbG9hdCBwYXJhbV80ID0gciAqIDUwLjA7CglyZXR1cm4gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpIAp7CglmbG9hdDIgdXZfc2NhbGUgPSB1diAqIGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzIgPSB1X2NpcmNsZTFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMyA9IHVfcm90YXRpb24xX1MxX2MwOwoJZmxvYXQgcGFyYW1fNCA9IDAuMTc7CglmbG9hdCBfMzE5ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwoJZmxvYXQgZzEgPSBfMzE5OwoJZmxvYXQyIHBhcmFtXzUgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV82ID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fNyA9IHVfY2lyY2xlMl9TMV9jMDsKCWZsb2F0MiBwYXJhbV84ID0gdV9yb3RhdGlvbjJfUzFfYzA7CglmbG9hdCBwYXJhbV85ID0gMC4yOwoJZmxvYXQgXzMzMSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgZzIgPSBfMzMxOwoJZmxvYXQyIHBhcmFtXzEwID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMTEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8xMiA9IHVfY2lyY2xlM19TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHVfcm90YXRpb24zX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTQgPSAwLjI3NTsKCWZsb2F0IF8zNDQgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyLCBwYXJhbV8xMywgcGFyYW1fMTQpOwoJZmxvYXQgZzMgPSBfMzQ0OwoJZmxvYXQgdiA9ICgoZzEgKiBnMSArIGcyKSAtIGczKSAqIDAuNTsKCXJldHVybiBjbGFtcCgwLjQ1ICsgMC44ICogdiwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0MiBwYXJhbV8xID0geHk7CglmbG9hdCBwYXJhbV8yID0gcmFkaXVzICsgdGhpY2tuZXNzOwoJZmxvYXQgcGFyYW1fMyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfb3V0ZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdDIgcGFyYW1fNCA9IHV2OwoJZmxvYXQyIHBhcmFtXzUgPSB4eTsKCWZsb2F0IHBhcmFtXzYgPSBtYXgocmFkaXVzIC0gdGhpY2tuZXNzLCAwLjApOwoJZmxvYXQgcGFyYW1fNyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfaW5uZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83KTsKCXJldHVybiBjbGFtcChjaXJjbGVfb3V0ZXIgLSBjaXJjbGVfaW5uZXIsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbikgCnsKCW4gPSBmcmFjdChuICogZmxvYXQyKDUuMzk4NywgNS40NDIxKSk7CgluICs9IGZsb2F0Mihkb3Qobi55eCwgbiArIGZsb2F0MigyMS41MzUxLCAxNC4zMTM3KSkpOwoJZmxvYXQgeHkgPSBuLnggKiBuLnk7CglyZXR1cm4gKGZyYWN0KHh5ICogOTUuNDMwNykgKyBmcmFjdCh4eSAqIDc1LjA0OTYxKSkgLSAxLjA7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpIAp7CglyZXR1cm4gc3RlcChsLCB2KSAqICgxLjAgLSBzdGVwKGgsIHYpKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCkgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQgXzI0MiA9IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKHBhcmFtKTsKCWZsb2F0IG4gPSBfMjQyOwoJZmxvYXQgcGFyYW1fMSA9IG47CglmbG9hdCBwYXJhbV8yID0gMC4wOwoJZmxvYXQgcGFyYW1fMyA9IDAuMDU7CglmbG9hdCBzID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQgcGFyYW1fNCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC4zNSkpOwoJZmxvYXQgcGFyYW1fNSA9IDAuMTsKCWZsb2F0IHBhcmFtXzYgPSAwLjE1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNik7CglmbG9hdCBwYXJhbV83ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjcpKTsKCWZsb2F0IHBhcmFtXzggPSAwLjI7CglmbG9hdCBwYXJhbV85ID0gMC4yNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgcGFyYW1fMTAgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDEuMDUpKTsKCWZsb2F0IHBhcmFtXzExID0gMC4zOwoJZmxvYXQgcGFyYW1fMTIgPSAwLjM1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMik7CglyZXR1cm4gY2xhbXAocywgMC4wLCAxLjApICogMC41NTsKfQp2b2lkIEZMVF9tYWluX1MxX2MwKCkgCnsKCXVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLng7Cgl1X3NwYXJrbGVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnk7Cgl1X2JsdXJfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLno7Cgl1X3JhZGl1c19zY2FsZV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAudzsKCWZsb2F0MiBwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwoJZmxvYXQyIHV2XzEgPSBwICogdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwoJZmxvYXQyIGRlbnNpdHlfdXYgPSB1dl8xIC0gbW9kKHAsIHVfbm9pc2Vfc2NhbGVfUzFfYzApOwoJZmxvYXQgcmFkaXVzID0gdV9tYXhfcmFkaXVzX1MxX2MwICogdV9yYWRpdXNfc2NhbGVfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1dl8xOwoJZmxvYXQgdHVyYnVsZW5jZSA9IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAocGFyYW1fMTMpOwoJZmxvYXQyIHBhcmFtXzE0ID0gcDsKCWZsb2F0MiBwYXJhbV8xNSA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTYgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8xNyA9IDAuMDUgKiB1X21heF9yYWRpdXNfUzFfYzA7CglmbG9hdCBwYXJhbV8xOCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHJpbmcgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAocGFyYW1fMTQsIHBhcmFtXzE1LCBwYXJhbV8xNiwgcGFyYW1fMTcsIHBhcmFtXzE4KTsKCWZsb2F0MiBwYXJhbV8xOSA9IGRlbnNpdHlfdXY7CglmbG9hdCBwYXJhbV8yMCA9IHVfbm9pc2VfcGhhc2VfUzFfYzA7CglmbG9hdCBzcGFya2xlID0gKChGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKHBhcmFtXzE5LCBwYXJhbV8yMCkgKiByaW5nKSAqIHR1cmJ1bGVuY2UpICogdV9zcGFya2xlX2FscGhhX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzIxID0gcDsKCWZsb2F0MiBwYXJhbV8yMiA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMjMgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8yNCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHdhdmVfYWxwaGEgPSAoRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMjEsIHBhcmFtXzIyLCBwYXJhbV8yMywgcGFyYW1fMjQpICogdV9hbHBoYV9TMV9jMCkgKiB1X2NvbG9yX1MxX2MwLnc7CglmbG9hdDQgd2F2ZV9jb2xvciA9IGZsb2F0NCh1X2NvbG9yX1MxX2MwLnh5eiAqIHdhdmVfYWxwaGEsIHdhdmVfYWxwaGEpOwoJZnJhZ0NvbG9yX1MxX2MwID0gbWl4KHdhdmVfY29sb3IsIGZsb2F0NCgxLjApLCBmbG9hdDQoc3BhcmtsZSkpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwID0gZmxvYXQ0KF90bXBfMV9jb29yZHMsIDAuMCwgMC4wKTsKCUZMVF9tYWluX1MxX2MwKCk7CglyZXR1cm4gaGFsZjQoaGFsZjQoZnJhZ0NvbG9yX1MxX2MwKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJaGFsZjQgb3V0cHV0X1MyOwoJb3V0cHV0X1MyID0gQ2lyY3VsYXJSUmVjdF9TMihvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0X1MyOwoJfQp9CgAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GYQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1MaAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAVwEAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAAAAAA","GYQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1MaAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAVwEAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAAAAAA","BYIBQAAABQAAIAABBYAAAEIXBAAP777777777777AAAAAAAAAAAABGABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1OJAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gaW5Db2xvcjsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzNfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAFEBAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IAAAAAAA==","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBIA5HZ2CAAAAAAAEAQCAAAAASBEEAQAAAAAUCCQAQQGAABAEAAAAAAGMAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1POAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAcwQAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZCA9IGNsYW1wKHN1YnNldENvb3JkLCB1Y2xhbXBfUzFfYzBfYzAueHksIHVjbGFtcF9TMV9jMF9jMC56dyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gTWF0cml4RWZmZWN0X1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBIA5HZ2CAAAAAAAAAACAAAAASAEAAQAAAAAUCCQAQQGAAAAAAAAAAAGMAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1POAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAYAMAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKGhhbGY0IF9pbnB1dCkgCnsKCV9pbnB1dCA9IE1hdHJpeEVmZmVjdF9TMV9jMChfaW5wdXQpOwoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglyZXR1cm4gaGFsZjQoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAJIBAGMVUOTUABAAAAAAAIAAAAAWFBIEMVUOTUAAAAAAAAAIAAAACICAACAAAAACQIKACCAYAAAAAAAAAAAZQAAAAAABAAAAACAZAAAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAKkFAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKY29uc3QgaW50IGtNYXhMb29wTGltaXRfUzFfYzAgPSA4Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CnVuaWZvcm0gaGFsZjQgdW9mZnNldHNBbmRLZXJuZWxfUzFfYzBbMTRdOwp1bmlmb3JtIGhhbGYyIHVkaXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIF9jb29yZHMpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CgloYWxmNCBzdW0gPSBoYWxmNCgwLjApOwoJZm9yIChpbnQgaSA9IDA7aSA8IGtNYXhMb29wTGltaXRfUzFfYzA7ICsraSkgCgl7CgkJaGFsZjQgcyA9IHVvZmZzZXRzQW5kS2VybmVsX1MxX2MwW2ldOwoJCXN1bSArPSBzLnkgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIocy54ICogdWRpcl9TMV9jMCkpOwoJCXN1bSArPSBzLncgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIocy56ICogdWRpcl9TMV9jMCkpOwoJfQoJcmV0dXJuIGhhbGY0KHN1bSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAJIBAGMVUOTUABAAAAQCAIAAAAAWFBIEMVUOTUAAAAAAQCAIAAAACIGAQCAAAAACQIKACCAYAAEAQAAAAAAZQAAAAAABAAAAACAZAAAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAMUGAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKY29uc3QgaW50IGtNYXhMb29wTGltaXRfUzFfYzAgPSA4Owp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxNF07CnVuaWZvcm0gaGFsZjIgdWRpcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJZmxvYXQyIGluQ29vcmQgPSBfY29vcmRzOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkID0gY2xhbXAoc3Vic2V0Q29vcmQsIHVjbGFtcF9TMV9jMF9jMF9jMC54eSwgdWNsYW1wX1MxX2MwX2MwX2MwLnp3KTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWhhbGY0IHN1bSA9IGhhbGY0KDAuMCk7Cglmb3IgKGludCBpID0gMDtpIDwga01heExvb3BMaW1pdF9TMV9jMDsgKytpKSAKCXsKCQloYWxmNCBzID0gdW9mZnNldHNBbmRLZXJuZWxfUzFfYzBbaV07CgkJc3VtICs9IHMueSAqIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgX3RtcF8xX2Nvb3JkcyArIGZsb2F0MihzLnggKiB1ZGlyX1MxX2MwKSk7CgkJc3VtICs9IHMudyAqIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgX3RtcF8xX2Nvb3JkcyArIGZsb2F0MihzLnogKiB1ZGlyX1MxX2MwKSk7Cgl9CglyZXR1cm4gaGFsZjQoc3VtKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHJ1bnRpbWVfc2hhZGVyX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","G2JAAAAAAAUAADAAAIOAAAAADIIAB7X7777QGHAYAD7P7777777777YBAAAAAAQAAAAAAQQGAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1OjAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0IGNvdmVyYWdlOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cgl2Y292ZXJhZ2VfUzAgPSBjb3ZlcmFnZTsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoAAAAAADUCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2Y292ZXJhZ2VfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UKAAAAbG9jYWxDb29yZAAAAAAAAA==","GXQAAGAABBYAAAEIXBAAAGEAMAAAAAAAAAAAAAAAYAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1OYAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gZmxvYXQ0IGluUXVhZEVkZ2U7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0NCB2UXVhZEVkZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkRWRnZQoJdlF1YWRFZGdlX1MwID0gaW5RdWFkRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KBAAAAF4DAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdlF1YWRFZGdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZEVkZ2UKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGYgZWRnZUFscGhhOwoJaGFsZjIgZHV2ZHggPSBoYWxmMihkRmR4KHZRdWFkRWRnZV9TMC54eSkpOwoJaGFsZjIgZHV2ZHkgPSBoYWxmMihkRmR5KHZRdWFkRWRnZV9TMC54eSkpOwoJaWYgKHZRdWFkRWRnZV9TMC56ID4gMC4wICYmIHZRdWFkRWRnZV9TMC53ID4gMC4wKSAKCXsKCQllZGdlQWxwaGEgPSBoYWxmKG1pbihtaW4odlF1YWRFZGdlX1MwLnosIHZRdWFkRWRnZV9TMC53KSArIDAuNSwgMS4wKSk7Cgl9CgllbHNlIAoJewoJCWhhbGYyIGdGID0gaGFsZjIoaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHgueCAtIGR1dmR4LnkpLCAgICAgICAgICAgICAgICAgaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHkueCAtIGR1dmR5LnkpKTsKCQllZGdlQWxwaGEgPSBoYWxmKHZRdWFkRWRnZV9TMC54KnZRdWFkRWRnZV9TMC54IC0gdlF1YWRFZGdlX1MwLnkpOwoJCWVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNSAtIGVkZ2VBbHBoYSAvIGxlbmd0aChnRikpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAEAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAKAAAAaW5RdWFkRWRnZQAAAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGABZAA6IAAAAACAAAAAADGAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAAB+AwAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","FIMAAAYAAEHAAAARC4EAAAQWBQAAAAAAAAAQAAAAIBCAAAEYAEAAAAAQAAAABAEQAEAAAAA":"DAAAAExTS1OtAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gaGFsZjMgaW5TaGFkb3dQYXJhbXM7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFJSZWN0U2hhZG93Cgl2aW5TaGFkb3dQYXJhbXNfUzAgPSBpblNoYWRvd1BhcmFtczsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAFwCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFJSZWN0U2hhZG93CgloYWxmMyBzaGFkb3dQYXJhbXM7CglzaGFkb3dQYXJhbXMgPSB2aW5TaGFkb3dQYXJhbXNfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CgloYWxmIGQgPSBsZW5ndGgoc2hhZG93UGFyYW1zLnh5KTsKCWZsb2F0MiB1diA9IGZsb2F0MihzaGFkb3dQYXJhbXMueiAqICgxLjAgLSBkKSwgMC41KTsKCWhhbGYgZmFjdG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB1dikuMDAwci5hOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChmYWN0b3IpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADgAAAGluU2hhZG93UGFyYW1zAAAAAAAA","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAALIAAAAAGFM37JKEAAAAAMAAAAAIAAAAAAAMBCQN25XWQUAAAAAAYAAAAAAAAAABGABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAAKh8AACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKY29uc3QgaW50IGtGaWxsQUFfUzJfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzJfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzJfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1X2NvbG9yX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbXBvc2l0ZV8xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NlbnRlcl9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X21heF9yYWRpdXNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9ub2lzZV9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X25vaXNlX3BoYXNlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24zX1MxX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQ0IHVjaXJjbGVfUzJfYzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDaXJjbGVfUzJfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8yX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGQ7CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMCkgCgl7CgkJZCA9IGhhbGYoKGxlbmd0aCgodWNpcmNsZV9TMl9jMC54eSAtIHNrX0ZyYWdDb29yZC54eSkgKiB1Y2lyY2xlX1MyX2MwLncpIC0gMS4wKSAqIHVjaXJjbGVfUzJfYzAueik7Cgl9CgllbHNlIAoJewoJCWQgPSBoYWxmKCgxLjAgLSBsZW5ndGgoKHVjaXJjbGVfUzJfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMl9jMC53KSkgKiB1Y2lyY2xlX1MyX2MwLnopOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGludCgxKSA9PSBrRmlsbEFBX1MyX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMCA/IHNhdHVyYXRlKGQpIDogaGFsZihkID4gMC41KSkpOwp9CmhhbGY0IEJsZW5kX1MyKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoX3NyYywgQ2lyY2xlX1MyX2MwKF9zcmMpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","EEAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAAAZQAAAAAABAAAAACAZAAAAA":"DAAAAExTS1MoDAAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDQgcmFkaWlfc2VsZWN0b3I7CmluIGZsb2F0NCBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzOwppbiBmbG9hdDQgYWFfYmxvYXRfYW5kX2NvdmVyYWdlOwppbiBmbG9hdDQgcmFkaWlfeDsKaW4gZmxvYXQ0IHJhZGlpX3k7CmluIGZsb2F0NCBza2V3OwppbiBmbG9hdDIgdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZTsKaW4gaGFsZjQgY29sb3I7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgAAAACZAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2YXJjY29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","GYQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADEAAGAAAAAAAAAIAAAAAPAIABAAAAABAGEAAAAEAAAABNARCAIAAAAAAAAAAAAAYAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1O8AQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCXZjb2xvcl9TMCA9IGNvbG9yOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc180X1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMCkgKiBwb3NpdGlvbi54eTE7Cgl9Cn0KAAAAAI0DAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMCkuMDAwcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEZXZpY2VTcGFjZV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gTWF0cml4RWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9kc3RfaW4oRGV2aWNlU3BhY2VfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBK2VD3EOCAAAAAABUXHIMAQAAAAWSQ4HO4SAAAAAMAAAAAEBUEFLMJAIAAAQCI6KVAYAAAAAAAAAAAQAAEACUA2JT4HAIAAAADIIODXOJIBAAAGAAAAACAKAC5XEQEAAAAAEPF2SMAAAAAAAAIAAAACXUUGB3XUUAQAADAAAAAAABARO3SJCAAAAACGSZJWCAAAAAAAIAAAABLFBUEFLMJAIAAAAAAAAAAACIMVSTUFAEAAAAAIAAAAAWFBSHSVIGAAAAAAAAAAAAABEBAABAAAAADIEEQCEAAAAAAAAAAAQAMQAAAAAAAQAAAABAMQAAA":"DAAAAExTS1PaAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzBfYzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAABAAAAcQkAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MxOwp1bmlmb3JtIGhhbGYgdXJhbmdlX1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzBfYzAsIHVlbmRfUzFfYzBfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IExpbmVhckxheW91dF9TMV9jMF9jMF9jMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzNfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CglyZXR1cm4gaGFsZjQoaGFsZjQoaGFsZihfdG1wXzNfY29vcmRzLngpICsgMWUtMDUsIDEuMCwgMC4wLCAwLjApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzBfYzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIExpbmVhckxheW91dF9TMV9jMF9jMF9jMV9jMChfaW5wdXQpOwp9CmhhbGY0IENsYW1wZWRHcmFkaWVudF9TMV9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzRfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGY0IHQgPSBNYXRyaXhFZmZlY3RfUzFfYzBfYzBfYzEoX3RtcF80X2luQ29sb3IpOwoJaGFsZjQgb3V0Q29sb3I7CglpZiAoIWJvb2woaW50KDEpKSAmJiB0LnkgPCAwLjApIAoJewoJCW91dENvbG9yID0gaGFsZjQoMC4wKTsKCX0KCWVsc2UgaWYgKHQueCA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSB1bGVmdEJvcmRlckNvbG9yX1MxX2MwX2MwOwoJfQoJZWxzZSBpZiAodC54ID4gMS4wKSAKCXsKCQlvdXRDb2xvciA9IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwX2MwOwoJfQoJZWxzZSAKCXsKCQlvdXRDb2xvciA9IFNpbmdsZUludGVydmFsQ29sb3JpemVyX1MxX2MwX2MwX2MwKF90bXBfNF9pbkNvbG9yLCBmbG9hdDIoaGFsZjIodC54LCAwLjApKSk7Cgl9CglyZXR1cm4gaGFsZjQob3V0Q29sb3IpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ2xhbXBlZEdyYWRpZW50X1MxX2MwX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzFfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIF9jb29yZHMpLjAwMHI7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MxKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMV9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzEpICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IERpdGhlcl9TMShoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzZfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGY0IGNvbG9yID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMV9jMChfdG1wXzZfaW5Db2xvcik7CgloYWxmIHZhbHVlID0gTWF0cml4RWZmZWN0X1MxX2MxKF90bXBfNl9pbkNvbG9yLCBza19GcmFnQ29vcmQueHkpLncgLSAwLjU7CglyZXR1cm4gaGFsZjQoaGFsZjQoY2xhbXAoY29sb3IueHl6ICsgdmFsdWUgKiB1cmFuZ2VfUzEsIDAuMCwgY29sb3IudyksIGNvbG9yLncpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IERpdGhlcl9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAABQAAAAAAAACAAAAADYCAAIAAAAAIBRAAAABAAAAALIEIQCAAAAAAAAAAAAAGABQAAAAEAAAAAAAEEBQAAAA":"DAAAAExTS1PSAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCXZsb2NhbENvb3JkX1MwID0gbG9jYWxDb29yZDsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNF9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogcG9zaXRpb24ueHkxOwoJfQp9CgAAAAAAACUEAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwKS4wMDByOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IERldmljZVNwYWNlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX2RzdF9pbihEZXZpY2VTcGFjZV9TMV9jMChfc3JjKSwgX3NyYyk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAAJQAAAAAAAANAAAAAHQVERARJYAAAABQAAAABAAAAAA4BAQAAACAAAAASAMAAQAAAAAUCCYBCAAAAAAAAAAAAAANJABAAAMBJ4KECOAEAAAMAAAAAAAAEAABAAV6GUSECFHAAAAAGAAAAAAAAAAAAAGABQAAAAEAAAAAAAEEBQA":"DAAAAExTS1PdAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMl9jMF9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0IHZjb3ZlcmFnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzJfYzBfYzApICogcG9zaXRpb24ueHkxOwoJfQp9CgAAAAEAAADvIQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCmZsb2F0NCBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMDsKZmxvYXQ0IGZyYWdDb2xvcl9TMV9jMDsKZmxvYXQgdV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9zcGFya2xlX2FscGhhX1MxX2MwOwpmbG9hdCB1X2JsdXJfUzFfYzA7CmZsb2F0IHVfcmFkaXVzX3NjYWxlX1MxX2MwOwpjb25zdCBpbnQga0ZpbGxCV19TMl9jMV9jMCA9IDA7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxCV19TMl9jMV9jMCA9IDI7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCA9IDM7CnVuaWZvcm0gZmxvYXQ0IHVfY29sb3JfUzFfYzA7CnVuaWZvcm0gZmxvYXQ0IHVfY29tcG9zaXRlXzFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2VudGVyX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbWF4X3JhZGl1c19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X25vaXNlX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbm9pc2VfcGhhc2VfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUyX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjNfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MyX2MwX2MwOwp1bmlmb3JtIGZsb2F0NCB1cmVjdFVuaWZvcm1fUzJfYzFfYzA7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMjsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBUZXh0dXJlRWZmZWN0X1MyX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzIsIHZUcmFuc2Zvcm1lZENvb3Jkc182X1MwKS4wMDByOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMl9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMl9jMF9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IERldmljZVNwYWNlX1MyX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzJfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBSZWN0X1MyX2MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMl9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBjb3ZlcmFnZTsKCWlmIChpbnQoMSkgPT0ga0ZpbGxCV19TMl9jMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzJfYzFfYzApIAoJewoJCWNvdmVyYWdlID0gaGFsZihhbGwoZ3JlYXRlclRoYW4oZmxvYXQ0KHNrX0ZyYWdDb29yZC54eSwgdXJlY3RVbmlmb3JtX1MyX2MxX2MwLnp3KSwgZmxvYXQ0KHVyZWN0VW5pZm9ybV9TMl9jMV9jMC54eSwgc2tfRnJhZ0Nvb3JkLnh5KSkpKTsKCX0KCWVsc2UgCgl7CgkJaGFsZjQgZGlzdHM0ID0gc2F0dXJhdGUoaGFsZjQoMS4wLCAxLjAsIC0xLjAsIC0xLjApICogaGFsZjQoc2tfRnJhZ0Nvb3JkLnh5eHkgLSB1cmVjdFVuaWZvcm1fUzJfYzFfYzApKTsKCQloYWxmMiBkaXN0czIgPSAoZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3KSAtIDEuMDsKCQljb3ZlcmFnZSA9IGRpc3RzMi54ICogZGlzdHMyLnk7Cgl9CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSAxLjAgLSBjb3ZlcmFnZTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChjb3ZlcmFnZSkpOwp9CmhhbGY0IEJsZW5kX1MyX2MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoUmVjdF9TMl9jMV9jMChfc3JjKSwgX3NyYyk7Cn0KaGFsZjQgQmxlbmRfUzIoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9kc3RfaW4oRGV2aWNlU3BhY2VfUzJfYzAoX3NyYyksIEJsZW5kX1MyX2MxKF9zcmMsIF9kc3QpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","G2QACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAADAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1PqAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0IGNvdmVyYWdlOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQ0IGdlb21TdWJzZXQ7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAQAAAKQCAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0NCB2Z2VvbVN1YnNldF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJZmxvYXQ0IGdlb1N1YnNldDsKCWdlb1N1YnNldCA9IHZnZW9tU3Vic2V0X1MwOwoJZmxvYXQ0IGRpc3RzNCA9IHNhdHVyYXRlKGZsb2F0NCgxLCAxLCAtMSwgLTEpICogKHNrX0ZyYWdDb29yZC54eXh5IC0gZ2VvU3Vic2V0KSk7CglmbG9hdDIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWNvdmVyYWdlID0gbWluKGNvdmVyYWdlLCBkaXN0czIueCAqIGRpc3RzMi55KTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAB3QA6AAAEAAAAAAAMAAPEAEAAABAAAAAABTAAAAAAACAAAAAEBSAAAA":"DAAAAExTS1MlAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCgl2aW5DaXJjbGVFZGdlX1MwID0gaW5DaXJjbGVFZGdlOwoJdmluQ29sb3JfUzAgPSBpbkNvbG9yOwoJZmxvYXQyIF90bXBfMF9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IHVsb2NhbE1hdHJpeF9TMC54eiAqIGluUG9zaXRpb24gKyB1bG9jYWxNYXRyaXhfUzAueXc7Cglza19Qb3NpdGlvbiA9IF90bXBfMF9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAQAAAHcFAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzI7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MyOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZpbkNvbG9yX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJYWxwaGEgPSAxLjAgLSBhbHBoYTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQpoYWxmNCBDaXJjdWxhclJSZWN0X1MyKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMi5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMi5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MyLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBDaXJjdWxhclJSZWN0X1MyKG91dHB1dF9TMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzI7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","EADQAAAAAEAAAAAUAABQAAQPAAABCFYMAAKAUEAAAAAAAAABAAAAAAAAAAAJQAIAAAABAAAAACAJAAIAAAAA":"DAAAAExTS1NtAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7CmluIGZsb2F0MyBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKb3V0IGZsb2F0IHZUZXhJbmRleF9TMDsKb3V0IGZsb2F0MiB2SW50VGV4dHVyZUNvb3Jkc19TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEaXN0YW5jZUZpZWxkUGF0aAoJaW50IHRleElkeCA9IDA7CglmbG9hdDIgdW5vcm1UZXhDb29yZHMgPSBmbG9hdDIoaW5UZXh0dXJlQ29vcmRzLngsIGluVGV4dHVyZUNvb3Jkcy55KTsKCXZUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHMgKiB1QXRsYXNEaW1lbnNpb25zSW52X1MwOwoJdlRleEluZGV4X1MwID0gZmxvYXQodGV4SWR4KTsKCXZJbnRUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHM7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDMgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MHo7Cn0KAAAAAAAAAJICAABzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKaW4gZmxvYXQgdlRleEluZGV4X1MwOwppbiBmbG9hdDIgdkludFRleHR1cmVDb29yZHNfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGlzdGFuY2VGaWVsZFBhdGgKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiB1diA9IHZUZXh0dXJlQ29vcmRzX1MwOwoJaGFsZjQgdGV4Q29sb3I7Cgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHV2KS5ycnJyOwoJfQoJaGFsZiBkaXN0YW5jZSA9IDcuOTY4NzUqKHRleENvbG9yLnIgLSAwLjUwMTk2MDc4NDMxKTsKCWhhbGYgYWZ3aWR0aDsKCWFmd2lkdGggPSBhYnMoMC42NSpoYWxmKGRGZHgodkludFRleHR1cmVDb29yZHNfUzAueCkpKTsKCWhhbGYgdmFsID0gc21vb3Roc3RlcCgtYWZ3aWR0aCwgYWZ3aWR0aCwgZGlzdGFuY2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCh2YWwpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5UZXh0dXJlQ29vcmRzAAAAAAA=","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAALIAAAAB4FJEIEKOAAAAAMAAAAAEAHQAACAAAAAAYCFPSVARQRJAAAAABQAAAAAAAAAAA4JAPAAACAAAAAAACABSAAAAAAACAAAAAEBSAAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAANCEAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKY29uc3QgaW50IGtGaWxsQldfUzJfYzAgPSAwOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzJfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzJfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1X2NvbG9yX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbXBvc2l0ZV8xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NlbnRlcl9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X21heF9yYWRpdXNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9ub2lzZV9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X25vaXNlX3BoYXNlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24zX1MxX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQ0IHVyZWN0VW5pZm9ybV9TMl9jMDsKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMl9jMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzJfYzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBSZWN0X1MyX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMl9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBjb3ZlcmFnZTsKCWlmIChpbnQoMSkgPT0ga0ZpbGxCV19TMl9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzJfYzApIAoJewoJCWNvdmVyYWdlID0gaGFsZihhbGwoZ3JlYXRlclRoYW4oZmxvYXQ0KHNrX0ZyYWdDb29yZC54eSwgdXJlY3RVbmlmb3JtX1MyX2MwLnp3KSwgZmxvYXQ0KHVyZWN0VW5pZm9ybV9TMl9jMC54eSwgc2tfRnJhZ0Nvb3JkLnh5KSkpKTsKCX0KCWVsc2UgCgl7CgkJaGFsZjQgZGlzdHM0ID0gc2F0dXJhdGUoaGFsZjQoMS4wLCAxLjAsIC0xLjAsIC0xLjApICogaGFsZjQoc2tfRnJhZ0Nvb3JkLnh5eHkgLSB1cmVjdFVuaWZvcm1fUzJfYzApKTsKCQloYWxmMiBkaXN0czIgPSAoZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3KSAtIDEuMDsKCQljb3ZlcmFnZSA9IGRpc3RzMi54ICogZGlzdHMyLnk7Cgl9CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMCkgCgl7CgkJY292ZXJhZ2UgPSAxLjAgLSBjb3ZlcmFnZTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChjb3ZlcmFnZSkpOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzJfYzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyX2MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyX2MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzJfYzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0KaGFsZjQgQmxlbmRfUzIoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShSZWN0X1MyX2MwKF9zcmMpLCBDaXJjdWxhclJSZWN0X1MyX2MxKF9zcmMpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAABwb3NpdGlvbggAAABjb3ZlcmFnZQUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAA4IAEAAACAAAAAABGABAAAAAEAAAAAIBEABAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAAEh4AACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb2xvcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb21wb3NpdGVfMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jZW50ZXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9tYXhfcmFkaXVzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfbm9pc2Vfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9ub2lzZV9waGFzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUxX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlM19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24yX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uM19TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MyOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMjsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KTsKdm9pZCBGTFRfbWFpbl9TMV9jMCgpOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpIAp7CglyZXR1cm4gZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAueHk7Cn0KZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCkgCnsKCXJldHVybiBmbG9hdDJ4MihyYWQueCwgLXJhZC55LCByYWQueSwgcmFkLngpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpIAp7CglmbG9hdCBibHVyX2hhbGYgPSBibHVyICogMC41OwoJZmxvYXQgZCA9IGRpc3RhbmNlKHV2LCB4eSk7CglyZXR1cm4gMS4wIC0gc21vb3Roc3RlcCgxLjAgLSBibHVyX2hhbGYsIDEuMCArIGJsdXJfaGFsZiwgZCAvIHJhZGl1cyk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcikgCnsKCWZsb2F0MiBwYXJhbSA9IHJvdGF0aW9uOwoJcCA9IEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKHBhcmFtKSAqICh4eSAtIHApICsgeHk7CglwID0gbW9kKHAsIGZsb2F0MihjZWxsX2RpYW1ldGVyKSkgLyByZXNvbHV0aW9uOwoJZmxvYXQgY2VsbF91diA9IChjZWxsX2RpYW1ldGVyIC8gcmVzb2x1dGlvbi55KSAqIDAuNTsKCWZsb2F0IHIgPSAwLjY1ICogY2VsbF91djsKCWZsb2F0MiBwYXJhbV8xID0gcDsKCWZsb2F0MiBwYXJhbV8yID0gZmxvYXQyKGNlbGxfdXYpOwoJZmxvYXQgcGFyYW1fMyA9IHI7CglmbG9hdCBwYXJhbV80ID0gciAqIDUwLjA7CglyZXR1cm4gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpIAp7CglmbG9hdDIgdXZfc2NhbGUgPSB1diAqIGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzIgPSB1X2NpcmNsZTFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMyA9IHVfcm90YXRpb24xX1MxX2MwOwoJZmxvYXQgcGFyYW1fNCA9IDAuMTc7CglmbG9hdCBfMzE5ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwoJZmxvYXQgZzEgPSBfMzE5OwoJZmxvYXQyIHBhcmFtXzUgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV82ID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fNyA9IHVfY2lyY2xlMl9TMV9jMDsKCWZsb2F0MiBwYXJhbV84ID0gdV9yb3RhdGlvbjJfUzFfYzA7CglmbG9hdCBwYXJhbV85ID0gMC4yOwoJZmxvYXQgXzMzMSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgZzIgPSBfMzMxOwoJZmxvYXQyIHBhcmFtXzEwID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMTEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8xMiA9IHVfY2lyY2xlM19TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHVfcm90YXRpb24zX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTQgPSAwLjI3NTsKCWZsb2F0IF8zNDQgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyLCBwYXJhbV8xMywgcGFyYW1fMTQpOwoJZmxvYXQgZzMgPSBfMzQ0OwoJZmxvYXQgdiA9ICgoZzEgKiBnMSArIGcyKSAtIGczKSAqIDAuNTsKCXJldHVybiBjbGFtcCgwLjQ1ICsgMC44ICogdiwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0MiBwYXJhbV8xID0geHk7CglmbG9hdCBwYXJhbV8yID0gcmFkaXVzICsgdGhpY2tuZXNzOwoJZmxvYXQgcGFyYW1fMyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfb3V0ZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdDIgcGFyYW1fNCA9IHV2OwoJZmxvYXQyIHBhcmFtXzUgPSB4eTsKCWZsb2F0IHBhcmFtXzYgPSBtYXgocmFkaXVzIC0gdGhpY2tuZXNzLCAwLjApOwoJZmxvYXQgcGFyYW1fNyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfaW5uZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83KTsKCXJldHVybiBjbGFtcChjaXJjbGVfb3V0ZXIgLSBjaXJjbGVfaW5uZXIsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbikgCnsKCW4gPSBmcmFjdChuICogZmxvYXQyKDUuMzk4NywgNS40NDIxKSk7CgluICs9IGZsb2F0Mihkb3Qobi55eCwgbiArIGZsb2F0MigyMS41MzUxLCAxNC4zMTM3KSkpOwoJZmxvYXQgeHkgPSBuLnggKiBuLnk7CglyZXR1cm4gKGZyYWN0KHh5ICogOTUuNDMwNykgKyBmcmFjdCh4eSAqIDc1LjA0OTYxKSkgLSAxLjA7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpIAp7CglyZXR1cm4gc3RlcChsLCB2KSAqICgxLjAgLSBzdGVwKGgsIHYpKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCkgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQgXzI0MiA9IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKHBhcmFtKTsKCWZsb2F0IG4gPSBfMjQyOwoJZmxvYXQgcGFyYW1fMSA9IG47CglmbG9hdCBwYXJhbV8yID0gMC4wOwoJZmxvYXQgcGFyYW1fMyA9IDAuMDU7CglmbG9hdCBzID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQgcGFyYW1fNCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC4zNSkpOwoJZmxvYXQgcGFyYW1fNSA9IDAuMTsKCWZsb2F0IHBhcmFtXzYgPSAwLjE1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNik7CglmbG9hdCBwYXJhbV83ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjcpKTsKCWZsb2F0IHBhcmFtXzggPSAwLjI7CglmbG9hdCBwYXJhbV85ID0gMC4yNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgcGFyYW1fMTAgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDEuMDUpKTsKCWZsb2F0IHBhcmFtXzExID0gMC4zOwoJZmxvYXQgcGFyYW1fMTIgPSAwLjM1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMik7CglyZXR1cm4gY2xhbXAocywgMC4wLCAxLjApICogMC41NTsKfQp2b2lkIEZMVF9tYWluX1MxX2MwKCkgCnsKCXVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLng7Cgl1X3NwYXJrbGVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnk7Cgl1X2JsdXJfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLno7Cgl1X3JhZGl1c19zY2FsZV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAudzsKCWZsb2F0MiBwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwoJZmxvYXQyIHV2XzEgPSBwICogdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwoJZmxvYXQyIGRlbnNpdHlfdXYgPSB1dl8xIC0gbW9kKHAsIHVfbm9pc2Vfc2NhbGVfUzFfYzApOwoJZmxvYXQgcmFkaXVzID0gdV9tYXhfcmFkaXVzX1MxX2MwICogdV9yYWRpdXNfc2NhbGVfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1dl8xOwoJZmxvYXQgdHVyYnVsZW5jZSA9IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAocGFyYW1fMTMpOwoJZmxvYXQyIHBhcmFtXzE0ID0gcDsKCWZsb2F0MiBwYXJhbV8xNSA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTYgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8xNyA9IDAuMDUgKiB1X21heF9yYWRpdXNfUzFfYzA7CglmbG9hdCBwYXJhbV8xOCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHJpbmcgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAocGFyYW1fMTQsIHBhcmFtXzE1LCBwYXJhbV8xNiwgcGFyYW1fMTcsIHBhcmFtXzE4KTsKCWZsb2F0MiBwYXJhbV8xOSA9IGRlbnNpdHlfdXY7CglmbG9hdCBwYXJhbV8yMCA9IHVfbm9pc2VfcGhhc2VfUzFfYzA7CglmbG9hdCBzcGFya2xlID0gKChGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKHBhcmFtXzE5LCBwYXJhbV8yMCkgKiByaW5nKSAqIHR1cmJ1bGVuY2UpICogdV9zcGFya2xlX2FscGhhX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzIxID0gcDsKCWZsb2F0MiBwYXJhbV8yMiA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMjMgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8yNCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHdhdmVfYWxwaGEgPSAoRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMjEsIHBhcmFtXzIyLCBwYXJhbV8yMywgcGFyYW1fMjQpICogdV9hbHBoYV9TMV9jMCkgKiB1X2NvbG9yX1MxX2MwLnc7CglmbG9hdDQgd2F2ZV9jb2xvciA9IGZsb2F0NCh1X2NvbG9yX1MxX2MwLnh5eiAqIHdhdmVfYWxwaGEsIHdhdmVfYWxwaGEpOwoJZnJhZ0NvbG9yX1MxX2MwID0gbWl4KHdhdmVfY29sb3IsIGZsb2F0NCgxLjApLCBmbG9hdDQoc3BhcmtsZSkpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwID0gZmxvYXQ0KF90bXBfMV9jb29yZHMsIDAuMCwgMC4wKTsKCUZMVF9tYWluX1MxX2MwKCk7CglyZXR1cm4gaGFsZjQoaGFsZjQoZnJhZ0NvbG9yX1MxX2MwKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQgZHkwID0gdWlubmVyUmVjdF9TMi5UIC0gc2tfRnJhZ0Nvb3JkLnk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzIuUkI7CglmbG9hdDIgZHh5ID0gbWF4KGZsb2F0MihkeHkxLngsIG1heChkeTAsIGR4eTEueSkpLCAwLjApOwoJaGFsZiBsZWZ0QWxwaGEgPSBoYWxmKHNhdHVyYXRlKHNrX0ZyYWdDb29yZC54IC0gdWlubmVyUmVjdF9TMi5MKSk7CgloYWxmIGFscGhhID0gbGVmdEFscGhhICogaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJaGFsZjQgb3V0cHV0X1MyOwoJb3V0cHV0X1MyID0gQ2lyY3VsYXJSUmVjdF9TMihvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0X1MyOwoJfQp9CgAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","AYAA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PbAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gZmxvYXQ0IGluQ2lyY2xlRWRnZTsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8yX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAQwIAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAMAAAAaW5DaXJjbGVFZGdlAAAAAA==","GYQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAMYAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1MaAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAABAAAA5gIAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MxOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","G2QACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAMYAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1PqAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0IGNvdmVyYWdlOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQ0IGdlb21TdWJzZXQ7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAQAAADMEAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJZmxvYXQ0IGdlb1N1YnNldDsKCWdlb1N1YnNldCA9IHZnZW9tU3Vic2V0X1MwOwoJZmxvYXQ0IGRpc3RzNCA9IHNhdHVyYXRlKGZsb2F0NCgxLCAxLCAtMSwgLTEpICogKHNrX0ZyYWdDb29yZC54eXh5IC0gZ2VvU3Vic2V0KSk7CglmbG9hdDIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWNvdmVyYWdlID0gbWluKGNvdmVyYWdlLCBkaXN0czIueCAqIGRpc3RzMi55KTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGdlb21TdWJzZXQAAAAAAAA=","CMRQCIAABBYAAAEIXBAAACDQMAABRAFAAAAAAAAAAAAAAAGABQAAAAEAAAAAAAEEBQAAAAA":"DAAAAExTS1OZAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDIgaW5FbGxpcHNlT2Zmc2V0OwppbiBmbG9hdDQgaW5FbGxpcHNlUmFkaWk7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2RWxsaXBzZU9mZnNldHNfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0NCB2RWxsaXBzZVJhZGlpX1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRWxsaXBzZUdlb21ldHJ5UHJvY2Vzc29yCgl2RWxsaXBzZU9mZnNldHNfUzAgPSBpbkVsbGlwc2VPZmZzZXQ7Cgl2RWxsaXBzZVJhZGlpX1MwID0gaW5FbGxpcHNlUmFkaWk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gdWxvY2FsTWF0cml4X1MwLnh6ICogaW5Qb3NpdGlvbiArIHVsb2NhbE1hdHJpeF9TMC55dzsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAAAA2QMAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2RWxsaXBzZU9mZnNldHNfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQ0IHZFbGxpcHNlUmFkaWlfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiBvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHk7CglvZmZzZXQgKj0gdkVsbGlwc2VSYWRpaV9TMC54eTsKCWZsb2F0IHRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZmxvYXQyIGdyYWQgPSAyLjAqb2Zmc2V0KnZFbGxpcHNlUmFkaWlfUzAueHk7CglmbG9hdCBncmFkX2RvdCA9IGRvdChncmFkLCBncmFkKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjE3NTVlLTM4KTsKCWZsb2F0IGludmxlbiA9IGludmVyc2VzcXJ0KGdyYWRfZG90KTsKCWZsb2F0IGVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNS10ZXN0Kmludmxlbik7CglvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHkqdkVsbGlwc2VSYWRpaV9TMC56dzsKCXRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZ3JhZCA9IDIuMCpvZmZzZXQqdkVsbGlwc2VSYWRpaV9TMC56dzsKCWdyYWRfZG90ID0gZG90KGdyYWQsIGdyYWQpOwoJaW52bGVuID0gaW52ZXJzZXNxcnQoZ3JhZF9kb3QpOwoJZWRnZUFscGhhICo9IHNhdHVyYXRlKDAuNSt0ZXN0Kmludmxlbik7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGhhbGYoZWRnZUFscGhhKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5FbGxpcHNlT2Zmc2V0AA4AAABpbkVsbGlwc2VSYWRpaQAAAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAADIAAAAIH5K3LIKAAAAAAMAAAAAIAAAAAAFMZBQP3FQWAUAABQAAAAAAAAAAAAADGAAAAAAAEAAAAAIDEAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAADzBAAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCmNvbnN0IGludCBrRmlsbEFBX1MxX2MwID0gMTsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEJXX1MxX2MwID0gMjsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEFBX1MxX2MwID0gMzsKdW5pZm9ybSBmbG9hdDQgdWNpcmNsZV9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMSkgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSkpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKF9zcmMsIENpcmNsZV9TMV9jMChfc3JjKSk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","EEAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAB5AAAAAAYFRP5FIUAQAADQAAAABAAAAAAABQEKBTLG62KRAAAAAHAAAAAAAAAAAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1MoDAAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDQgcmFkaWlfc2VsZWN0b3I7CmluIGZsb2F0NCBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzOwppbiBmbG9hdDQgYWFfYmxvYXRfYW5kX2NvdmVyYWdlOwppbiBmbG9hdDQgcmFkaWlfeDsKaW4gZmxvYXQ0IHJhZGlpX3k7CmluIGZsb2F0NCBza2V3OwppbiBmbG9hdDIgdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZTsKaW4gaGFsZjQgY29sb3I7Cm5vcGVyc3BlY3RpdmUgb3V0IGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAACdBQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCmNvbnN0IGludCBrRmlsbEFBX1MxX2MwID0gMTsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEJXX1MxX2MwID0gMjsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEFBX1MxX2MwID0gMzsKdW5pZm9ybSBmbG9hdDQgdWNpcmNsZV9TMV9jMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZhcmNjb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgzKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMykgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSkpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKF9zcmMsIENpcmNsZV9TMV9jMChfc3JjKSk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEZpbGxSUmVjdE9wOjpQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCB4X3BsdXNfMT12YXJjY29vcmRfUzAueCwgeT12YXJjY29vcmRfUzAueTsKCWhhbGYgY292ZXJhZ2U7CglpZiAoMCA9PSB4X3BsdXNfMSkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKHkpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdCBmbiA9IHhfcGx1c18xICogKHhfcGx1c18xIC0gMik7CgkJZm4gPSBmbWEoeSx5LCBmbik7CgkJZmxvYXQgZm53aWR0aCA9IGZ3aWR0aChmbik7CgkJY292ZXJhZ2UgPSAuNSAtIGhhbGYoZm4vZm53aWR0aCk7CgkJY292ZXJhZ2UgPSBjbGFtcChjb3ZlcmFnZSwgMCwgMSk7Cgl9CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAOAAAAcmFkaWlfc2VsZWN0b3IAABkAAABjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzAAAAFQAAAGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZQAAAAcAAAByYWRpaV94AAcAAAByYWRpaV95AAQAAABza2V3GQAAAHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUAAAAFAAAAY29sb3IAAAAAAAAA","AYTRVAADQAAAOAEARAFQJAABBADAAAILBYAACCYUQD777777777767YAAAAAAAAAAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PnAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0NCB1bG9jYWxNYXRyaXhfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwppbiBoYWxmMyBpbkNsaXBQbGFuZTsKaW4gaGFsZjMgaW5Jc2VjdFBsYW5lOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjMgdmluQ2xpcFBsYW5lX1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmMyB2aW5Jc2VjdFBsYW5lX1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5DbGlwUGxhbmVfUzAgPSBpbkNsaXBQbGFuZTsKCXZpbklzZWN0UGxhbmVfUzAgPSBpbklzZWN0UGxhbmU7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gdWxvY2FsTWF0cml4X1MwLnh6ICogaW5Qb3NpdGlvbiArIHVsb2NhbE1hdHJpeF9TMC55dzsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAFIEAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmMyB2aW5DbGlwUGxhbmVfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjMgdmluSXNlY3RQbGFuZV9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjMgY2xpcFBsYW5lOwoJY2xpcFBsYW5lID0gdmluQ2xpcFBsYW5lX1MwOwoJaGFsZjMgaXNlY3RQbGFuZTsKCWlzZWN0UGxhbmUgPSB2aW5Jc2VjdFBsYW5lX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmIGRpc3RhbmNlVG9Jbm5lckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqIChkIC0gY2lyY2xlRWRnZS53KSk7CgloYWxmIGlubmVyQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvSW5uZXJFZGdlKTsKCWVkZ2VBbHBoYSAqPSBpbm5lckFscGhhOwoJaGFsZiBjbGlwID0gaGFsZihzYXR1cmF0ZShjaXJjbGVFZGdlLnogKiBkb3QoY2lyY2xlRWRnZS54eSwgY2xpcFBsYW5lLnh5KSArIGNsaXBQbGFuZS56KSk7CgljbGlwICo9IGhhbGYoc2F0dXJhdGUoY2lyY2xlRWRnZS56ICogZG90KGNpcmNsZUVkZ2UueHksIGlzZWN0UGxhbmUueHkpICsgaXNlY3RQbGFuZS56KSk7CgllZGdlQWxwaGEgKj0gY2xpcDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQsAAABpbkNsaXBQbGFuZQAMAAAAaW5Jc2VjdFBsYW5lAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAEQQGABZAA6IAAAAACAAAAAADGAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAACHAwAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwpzYW1wbGVyRXh0ZXJuYWxPRVMgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAJIBAGMVUOTUABAAAAQAAIAAAAAWFBIEMVUOTUAAAAAAQAAIAAAACIGAACAAAAACQIKACCAYAAEAAAAAAAAZQAAAAAABAAAAACAZAAAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAOgGAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKY29uc3QgaW50IGtNYXhMb29wTGltaXRfUzFfYzAgPSA4Owp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxNF07CnVuaWZvcm0gaGFsZjIgdWRpcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJZmxvYXQyIGluQ29vcmQgPSBfY29vcmRzOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBjbGFtcChzdWJzZXRDb29yZC54LCB1Y2xhbXBfUzFfYzBfYzBfYzAueCwgdWNsYW1wX1MxX2MwX2MwX2MwLnopOwoJY2xhbXBlZENvb3JkLnkgPSBzdWJzZXRDb29yZC55OwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBjbGFtcGVkQ29vcmQpOwoJcmV0dXJuIHRleHR1cmVDb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKF9pbnB1dCwgZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMCkgKiBfY29vcmRzLnh5MSk7Cn0KaGFsZjQgcnVudGltZV9zaGFkZXJfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJaGFsZjQgc3VtID0gaGFsZjQoMC4wKTsKCWZvciAoaW50IGkgPSAwO2kgPCBrTWF4TG9vcExpbWl0X1MxX2MwOyArK2kpIAoJewoJCWhhbGY0IHMgPSB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXTsKCQlzdW0gKz0gcy55ICogTWF0cml4RWZmZWN0X1MxX2MwX2MwKF90bXBfMF9pbkNvbG9yLCBfdG1wXzFfY29vcmRzICsgZmxvYXQyKHMueCAqIHVkaXJfUzFfYzApKTsKCQlzdW0gKz0gcy53ICogTWF0cml4RWZmZWN0X1MxX2MwX2MwKF90bXBfMF9pbkNvbG9yLCBfdG1wXzFfY29vcmRzICsgZmxvYXQyKHMueiAqIHVkaXJfUzFfYzApKTsKCX0KCXJldHVybiBoYWxmNChzdW0pOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBIA5HZ2CAAAAAAAAAQCAAAAASAEEAQAAAAAUCCQAQQGAAAAEAAAAAAGMAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1POAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAlgQAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZC54ID0gc3Vic2V0Q29vcmQueDsKCWNsYW1wZWRDb29yZC55ID0gY2xhbXAoc3Vic2V0Q29vcmQueSwgdWNsYW1wX1MxX2MwX2MwLnksIHVjbGFtcF9TMV9jMF9jMC53KTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBNYXRyaXhFZmZlY3RfUzFfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GYJBYAQCAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGABZAA6IAAAAACAAAAAADGAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAADEBwAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQgdVNyY1RGX1MwWzddOwp1bmlmb3JtIGZsb2F0M3gzIHVDb2xvclhmb3JtX1MwOwp1bmlmb3JtIGZsb2F0IHVEc3RURl9TMFs3XTsKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CmZsb2F0IHNyY190Zl9TMChmbG9hdCB4KSAKewoJZmxvYXQgRyA9IHVTcmNURl9TMFswXTsKCWZsb2F0IEEgPSB1U3JjVEZfUzBbMV07CglmbG9hdCBCID0gdVNyY1RGX1MwWzJdOwoJZmxvYXQgQyA9IHVTcmNURl9TMFszXTsKCWZsb2F0IEQgPSB1U3JjVEZfUzBbNF07CglmbG9hdCBFID0gdVNyY1RGX1MwWzVdOwoJZmxvYXQgRiA9IHVTcmNURl9TMFs2XTsKCWZsb2F0IHMgPSBzaWduKHgpOwoJeCA9IGFicyh4KTsKCXggPSAoeCA8IEQpID8gKEMgKiB4KSArIEYgOiBwb3coQSAqIHggKyBCLCBHKSArIEU7CglyZXR1cm4gcyAqIHg7Cn0KZmxvYXQgZHN0X3RmX1MwKGZsb2F0IHgpIAp7CglmbG9hdCBHID0gdURzdFRGX1MwWzBdOwoJZmxvYXQgQSA9IHVEc3RURl9TMFsxXTsKCWZsb2F0IEIgPSB1RHN0VEZfUzBbMl07CglmbG9hdCBDID0gdURzdFRGX1MwWzNdOwoJZmxvYXQgRCA9IHVEc3RURl9TMFs0XTsKCWZsb2F0IEUgPSB1RHN0VEZfUzBbNV07CglmbG9hdCBGID0gdURzdFRGX1MwWzZdOwoJZmxvYXQgcyA9IHNpZ24oeCk7Cgl4ID0gYWJzKHgpOwoJeCA9ICh4IDwgRCkgPyAoQyAqIHgpICsgRiA6IHBvdyhBICogeCArIEIsIEcpICsgRTsKCXJldHVybiBzICogeDsKfQpmbG9hdDQgZ2FtdXRfeGZvcm1fUzAoZmxvYXQ0IGNvbG9yKSAKewoJY29sb3IucmdiID0gKHVDb2xvclhmb3JtX1MwICogY29sb3IucmdiKTsKCXJldHVybiBjb2xvcjsKfQpoYWxmNCBjb2xvcl94Zm9ybV9TMChmbG9hdDQgY29sb3IpIAp7Cgljb2xvci5yID0gc3JjX3RmX1MwKGNvbG9yLnIpOwoJY29sb3IuZyA9IHNyY190Zl9TMChjb2xvci5nKTsKCWNvbG9yLmIgPSBzcmNfdGZfUzAoY29sb3IuYik7Cgljb2xvciA9IGdhbXV0X3hmb3JtX1MwKGNvbG9yKTsKCWNvbG9yLnIgPSBkc3RfdGZfUzAoY29sb3Iucik7Cgljb2xvci5nID0gZHN0X3RmX1MwKGNvbG9yLmcpOwoJY29sb3IuYiA9IGRzdF90Zl9TMChjb2xvci5iKTsKCXJldHVybiBoYWxmNChjb2xvcik7Cn0KaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKGNvbG9yX3hmb3JtX1MwKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAACAA6QAAAABYCTIUIE4AIAAAYAAAAAQAAAAAGARL4FIEMEKMAAAAAMAAAAAAAAAAAAAMADAAAAAIAAAAAAAIIDAAAA":"DAAAAExTS1N4AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0MiB1QXRsYXNTaXplSW52X1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZjQgaW5Db2xvcjsKaW4gdXNob3J0MiBpblRleHR1cmVDb29yZHM7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQml0bWFwVGV4dAoJaW50IHRleElkeCA9IDA7CglmbG9hdDIgdW5vcm1UZXhDb29yZHMgPSBmbG9hdDIoaW5UZXh0dXJlQ29vcmRzLngsIGluVGV4dHVyZUNvb3Jkcy55KTsKCXZUZXh0dXJlQ29vcmRzX1MwID0gdW5vcm1UZXhDb29yZHMgKiB1QXRsYXNTaXplSW52X1MwOwoJdlRleEluZGV4X1MwID0gZmxvYXQodGV4SWR4KTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwMTsKfQoBAAAAnwUAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpjb25zdCBpbnQga0ZpbGxCV19TMV9jMCA9IDA7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxCV19TMV9jMCA9IDI7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxBQV9TMV9jMCA9IDM7CnVuaWZvcm0gZmxvYXQ0IHVyZWN0VW5pZm9ybV9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IFJlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGNvdmVyYWdlOwoJaWYgKGludCgxKSA9PSBrRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKGFsbChncmVhdGVyVGhhbihmbG9hdDQoc2tfRnJhZ0Nvb3JkLnh5LCB1cmVjdFVuaWZvcm1fUzFfYzAuencpLCBmbG9hdDQodXJlY3RVbmlmb3JtX1MxX2MwLnh5LCBza19GcmFnQ29vcmQueHkpKSkpOwoJfQoJZWxzZSAKCXsKCQloYWxmNCBkaXN0czQgPSBzYXR1cmF0ZShoYWxmNCgxLjAsIDEuMCwgLTEuMCwgLTEuMCkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIHVyZWN0VW5pZm9ybV9TMV9jMCkpOwoJCWhhbGYyIGRpc3RzMiA9IChkaXN0czQueHkgKyBkaXN0czQuencpIC0gMS4wOwoJCWNvdmVyYWdlID0gZGlzdHMyLnggKiBkaXN0czIueTsKCX0KCWlmIChpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEFBX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IDEuMCAtIGNvdmVyYWdlOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGNvdmVyYWdlKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShSZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZjQgdGV4Q29sb3I7Cgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHZUZXh0dXJlQ29vcmRzX1MwKS5ycnJyOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSB0ZXhDb2xvcjsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAHIBK2VD3EOCAAAAAABUXHIMAQAAAAWSQ4HO4SAAAAAMAAAAAAAAQAABANBBK3CICAAAEASHSVIGAAAAAAAAAAAEAABAAVAGSM7BYCAAAAA2CDQ53SKAIAABQAAAAAAACAAAEAUAF3OJAIAAAAAI6LVEYAAAAAAAAQAAAAFPJIMDXPJIBAAAGAAAAAAAAIAAAAAQIXNZERAAAAABDJMU3BAAAAAAAEAAAAAMQQAIAAAQGQQVNREBAAACAAAAAAQAKRC5HBSYCACAAAAAAAAAEAIRDJMU3BAAAAAAACAAAAAVTIMQ4RKBQAAAAAAAAAACAAIAIAAIAAAAC2BCEAQAAAAAAAAAAAMADAAAAAUAAAAAAAIIDA":"DAAAAExTS1NNAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfN19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzdfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwX2MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAEAAADKCgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gaGFsZjQgdXN0YXJ0X1MxX2MwX2MwX2MwX2MwOwp1bmlmb3JtIGhhbGY0IHVlbmRfUzFfYzBfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMF9jMTsKdW5pZm9ybSBoYWxmNCB1bGVmdEJvcmRlckNvbG9yX1MxX2MwX2MwOwp1bmlmb3JtIGhhbGY0IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzE7CnVuaWZvcm0gaGFsZiB1cmFuZ2VfUzE7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfN19TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzBfYzBfYzAsIHVlbmRfUzFfYzBfYzBfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IGNvbG9yX3hmb3JtX1MxX2MwX2MwX2MwKGZsb2F0NCBjb2xvcikgCnsKCWNvbG9yLnJnYiAqPSBjb2xvci5hOwoJcmV0dXJuIGhhbGY0KGNvbG9yKTsKfQpoYWxmNCBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBjb2xvcl94Zm9ybV9TMV9jMF9jMF9jMChTaW5nbGVJbnRlcnZhbENvbG9yaXplcl9TMV9jMF9jMF9jMF9jMChfaW5wdXQsIF9jb29yZHMpKTsKfQpoYWxmNCBMaW5lYXJMYXlvdXRfUzFfYzBfYzBfYzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8yX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8zX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc183X1MwOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGhhbGYoX3RtcF8zX2Nvb3Jkcy54KSArIDFlLTA1LCAxLjAsIDAuMCwgMC4wKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwX2MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBMaW5lYXJMYXlvdXRfUzFfYzBfYzBfYzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDbGFtcGVkR3JhZGllbnRfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF80X2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmNCB0ID0gTWF0cml4RWZmZWN0X1MxX2MwX2MwX2MxKF90bXBfNF9pbkNvbG9yKTsKCWhhbGY0IG91dENvbG9yOwoJaWYgKCFib29sKGludCgxKSkgJiYgdC55IDwgMC4wKSAKCXsKCQlvdXRDb2xvciA9IGhhbGY0KDAuMCk7Cgl9CgllbHNlIGlmICh0LnggPCAwLjApIAoJewoJCW91dENvbG9yID0gdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKCX0KCWVsc2UgaWYgKHQueCA+IDEuMCkgCgl7CgkJb3V0Q29sb3IgPSB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKCX0KCWVsc2UgCgl7CgkJb3V0Q29sb3IgPSBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzBfYzAoX3RtcF80X2luQ29sb3IsIGZsb2F0MihoYWxmMih0LngsIDAuMCkpKTsKCX0KCXJldHVybiBoYWxmNChvdXRDb2xvcik7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBDbGFtcGVkR3JhZGllbnRfUzFfYzBfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfNV9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0KaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMV9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3JkcykuMDAwcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzEoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MxX2MwKF9pbnB1dCwgZmxvYXQzeDIodW1hdHJpeF9TMV9jMSkgKiBfY29vcmRzLnh5MSk7Cn0KaGFsZjQgRGl0aGVyX1MxKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfNl9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjQgY29sb3IgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxX2MwKF90bXBfNl9pbkNvbG9yKTsKCWhhbGYgdmFsdWUgPSBNYXRyaXhFZmZlY3RfUzFfYzEoX3RtcF82X2luQ29sb3IsIHNrX0ZyYWdDb29yZC54eSkudyAtIDAuNTsKCXJldHVybiBoYWxmNChoYWxmNChjbGFtcChjb2xvci54eXogKyB2YWx1ZSAqIHVyYW5nZV9TMSwgMC4wLCBjb2xvci53KSwgY29sb3IudykpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGl0aGVyX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSAoaGFsZjQoMS4wKSAtIG91dHB1dF9TMSkgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GYJBYAQCAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAA1BgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQgdVNyY1RGX1MwWzddOwp1bmlmb3JtIGZsb2F0M3gzIHVDb2xvclhmb3JtX1MwOwp1bmlmb3JtIGZsb2F0IHVEc3RURl9TMFs3XTsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKZmxvYXQgc3JjX3RmX1MwKGZsb2F0IHgpIAp7CglmbG9hdCBHID0gdVNyY1RGX1MwWzBdOwoJZmxvYXQgQSA9IHVTcmNURl9TMFsxXTsKCWZsb2F0IEIgPSB1U3JjVEZfUzBbMl07CglmbG9hdCBDID0gdVNyY1RGX1MwWzNdOwoJZmxvYXQgRCA9IHVTcmNURl9TMFs0XTsKCWZsb2F0IEUgPSB1U3JjVEZfUzBbNV07CglmbG9hdCBGID0gdVNyY1RGX1MwWzZdOwoJZmxvYXQgcyA9IHNpZ24oeCk7Cgl4ID0gYWJzKHgpOwoJeCA9ICh4IDwgRCkgPyAoQyAqIHgpICsgRiA6IHBvdyhBICogeCArIEIsIEcpICsgRTsKCXJldHVybiBzICogeDsKfQpmbG9hdCBkc3RfdGZfUzAoZmxvYXQgeCkgCnsKCWZsb2F0IEcgPSB1RHN0VEZfUzBbMF07CglmbG9hdCBBID0gdURzdFRGX1MwWzFdOwoJZmxvYXQgQiA9IHVEc3RURl9TMFsyXTsKCWZsb2F0IEMgPSB1RHN0VEZfUzBbM107CglmbG9hdCBEID0gdURzdFRGX1MwWzRdOwoJZmxvYXQgRSA9IHVEc3RURl9TMFs1XTsKCWZsb2F0IEYgPSB1RHN0VEZfUzBbNl07CglmbG9hdCBzID0gc2lnbih4KTsKCXggPSBhYnMoeCk7Cgl4ID0gKHggPCBEKSA/IChDICogeCkgKyBGIDogcG93KEEgKiB4ICsgQiwgRykgKyBFOwoJcmV0dXJuIHMgKiB4Owp9CmZsb2F0NCBnYW11dF94Zm9ybV9TMChmbG9hdDQgY29sb3IpIAp7Cgljb2xvci5yZ2IgPSAodUNvbG9yWGZvcm1fUzAgKiBjb2xvci5yZ2IpOwoJcmV0dXJuIGNvbG9yOwp9CmhhbGY0IGNvbG9yX3hmb3JtX1MwKGZsb2F0NCBjb2xvcikgCnsKCWNvbG9yLnIgPSBzcmNfdGZfUzAoY29sb3Iucik7Cgljb2xvci5nID0gc3JjX3RmX1MwKGNvbG9yLmcpOwoJY29sb3IuYiA9IHNyY190Zl9TMChjb2xvci5iKTsKCWNvbG9yID0gZ2FtdXRfeGZvcm1fUzAoY29sb3IpOwoJY29sb3IuciA9IGRzdF90Zl9TMChjb2xvci5yKTsKCWNvbG9yLmcgPSBkc3RfdGZfUzAoY29sb3IuZyk7Cgljb2xvci5iID0gZHN0X3RmX1MwKGNvbG9yLmIpOwoJcmV0dXJuIGhhbGY0KGNvbG9yKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKGNvbG9yX3hmb3JtX1MwKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","DASAAAAAAAAAAAEAAFQAAIGAAEAOB77776PUEAIBAAAAAABAAAAAAABAMQAAAMYAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1MpAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0MiB1QXRsYXNTaXplSW52X1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gdXNob3J0MiBpblRleHR1cmVDb29yZHM7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBCaXRtYXBUZXh0CglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc1NpemVJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAAAsAgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpub3BlcnNwZWN0aXZlIGluIGZsb2F0IHZUZXhJbmRleF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB1Q29sb3JfUzA7CgloYWxmNCB0ZXhDb2xvcjsKCXsKCQl0ZXhDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdlRleHR1cmVDb29yZHNfUzApOwoJfQoJb3V0cHV0Q29sb3JfUzAgPSBvdXRwdXRDb2xvcl9TMCAqIHRleENvbG9yOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","G2JQAAAAABEAADAAAIOAAAAADIIAB7X7777QGHAYAD7P7777A4QCQAAAAAAAAAQAAAAAAQQGAAYAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1P8AQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0IGNvdmVyYWdlOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKaW4gZmxvYXQ0IHRleFN1YnNldDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDQgdnRleFN1YnNldF9TMDsKbm9wZXJzcGVjdGl2ZSBvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0MiBwb3NpdGlvbiA9IHBvc2l0aW9uLnh5OwoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJdnRleFN1YnNldF9TMCA9IHRleFN1YnNldDsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAC4AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQ0IHZ0ZXhTdWJzZXRfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCWZsb2F0NCBzdWJzZXQ7CglzdWJzZXQgPSB2dGV4U3Vic2V0X1MwOwoJdGV4Q29vcmQgPSBjbGFtcCh0ZXhDb29yZCwgc3Vic2V0LkxULCBzdWJzZXQuUkIpOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UKAAAAbG9jYWxDb29yZAAACQAAAHRleFN1YnNldAAAAAAAAAA=","B2AAQAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABGABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1NZAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZiBpbkNvdmVyYWdlOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cgl2aW5Db3ZlcmFnZV9TMCA9IGluQ292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAJgBAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKdW5pZm9ybSBoYWxmNCB1Q29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gaGFsZiB2aW5Db3ZlcmFnZV9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIERlZmF1bHRHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHVDb2xvcl9TMDsKCWhhbGYgYWxwaGEgPSAxLjA7CglhbHBoYSA9IHZpbkNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChhbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAoAAABpblBvc2l0aW9uAAAKAAAAaW5Db3ZlcmFnZQAAAAAAAA==","GYJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAADIAAAAAJKLCRQQAAAAAAMAAAAACAAAAAIAAAAALAIQEVFRIYIAAAAAAGAAAAABAAAAAAAAAAAAAGABQAAAAEAAAAAAAEEBQA":"DAAAAExTS1MwAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0OwppbiBmbG9hdDIgcG9zaXRpb247CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgEAAACEBgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCmNvbnN0IGludCBrRmlsbEJXX1MxX2MwID0gMDsKY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwp1bmlmb3JtIGZsb2F0NCB1ZWxsaXBzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdXNjYWxlX1MxX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBFbGxpcHNlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIGQgPSBza19GcmFnQ29vcmQueHkgLSB1ZWxsaXBzZV9TMV9jMC54eTsKCWlmIChib29sKGludCgwKSkpIAoJewoJCWQgKj0gdXNjYWxlX1MxX2MwLnk7Cgl9CglmbG9hdDIgWiA9IGQgKiB1ZWxsaXBzZV9TMV9jMC56dzsKCWZsb2F0IGltcGxpY2l0ID0gZG90KFosIGQpIC0gMS4wOwoJZmxvYXQgZ3JhZF9kb3QgPSA0LjAgKiBkb3QoWiwgWik7CglpZiAoYm9vbChpbnQoMCkpKSAKCXsKCQlncmFkX2RvdCA9IG1heChncmFkX2RvdCwgNi4xMDM2ZS0wNSk7Cgl9CgllbHNlIAoJewoJCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjE3NTVlLTM4KTsKCX0KCWZsb2F0IGFwcHJveF9kaXN0ID0gaW1wbGljaXQgKiBpbnZlcnNlc3FydChncmFkX2RvdCk7CglpZiAoYm9vbChpbnQoMCkpKSAKCXsKCQlhcHByb3hfZGlzdCAqPSB1c2NhbGVfUzFfYzAueDsKCX0KCWhhbGYgYWxwaGE7CglpZiAoaW50KDEpID09IGtGaWxsQldfUzFfYzApIAoJewoJCWFscGhhID0gaGFsZihhcHByb3hfZGlzdCA+IDAuMCA/IDAuMCA6IDEuMCk7Cgl9CgllbHNlIGlmIChpbnQoMSkgPT0ga0ZpbGxBQV9TMV9jMCkgCgl7CgkJYWxwaGEgPSBzYXR1cmF0ZSgwLjUgLSBoYWxmKGFwcHJveF9kaXN0KSk7Cgl9CgllbHNlIGlmIChpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzApIAoJewoJCWFscGhhID0gaGFsZihhcHByb3hfZGlzdCA+IDAuMCk7Cgl9CgllbHNlIAoJewoJCWFscGhhID0gc2F0dXJhdGUoMC41ICsgaGFsZihhcHByb3hfZGlzdCkpOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGFscGhhKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShFbGxpcHNlX1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpLCBoYWxmNCgxKSkpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAJIBBKQPNFTIACAAADQOAIAAAAAWFBZKQHMFVIAAAAADQOAIAAAACI6DQCAAAAACQIKACCAYAA4DQAAAAAAZQAAAAAABAAAAACAZAAAAAAA":"DAAAAExTS1PIAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAkJAAAjZXh0ZW5zaW9uIEdMX05WX3NoYWRlcl9ub3BlcnNwZWN0aXZlX2ludGVycG9sYXRpb246IHJlcXVpcmUKY29uc3QgaW50IGtNYXhMb29wTGltaXRfUzFfYzAgPSAzOwp1bmlmb3JtIGhhbGY0IHVib3JkZXJfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQ0IHVzdWJzZXRfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQyIHVpZGltc19TMV9jMF9jMF9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwX2MwOwp1bmlmb3JtIGhhbGY0IHVrZXJuZWxfUzFfYzBbN107CnVuaWZvcm0gaGFsZjQgdW9mZnNldHNfUzFfYzBbMTRdOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKbm9wZXJzcGVjdGl2ZSBpbiBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglmbG9hdDIgaW5Db29yZCA9IF9jb29yZHM7CglmbG9hdDIgc3Vic2V0Q29vcmQ7CglzdWJzZXRDb29yZC54ID0gaW5Db29yZC54OwoJc3Vic2V0Q29vcmQueSA9IGluQ29vcmQueTsKCWZsb2F0MiBjbGFtcGVkQ29vcmQ7CgljbGFtcGVkQ29vcmQgPSBzdWJzZXRDb29yZDsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgKGNsYW1wZWRDb29yZCkgKiB1aWRpbXNfUzFfYzBfYzBfYzApOwoJZmxvYXQgc25hcHBlZFggPSBmbG9vcihpbkNvb3JkLnggKyAwLjAwMSkgKyAwLjU7CglpZiAoc25hcHBlZFggPCB1c3Vic2V0X1MxX2MwX2MwX2MwLnggfHwgc25hcHBlZFggPiB1c3Vic2V0X1MxX2MwX2MwX2MwLnopIAoJewoJCXRleHR1cmVDb2xvciA9IHVib3JkZXJfUzFfYzBfYzBfYzA7Cgl9CglmbG9hdCBzbmFwcGVkWSA9IGZsb29yKGluQ29vcmQueSArIDAuMDAxKSArIDAuNTsKCWlmIChzbmFwcGVkWSA8IHVzdWJzZXRfUzFfYzBfYzBfYzAueSB8fCBzbmFwcGVkWSA+IHVzdWJzZXRfUzFfYzBfYzBfYzAudykgCgl7CgkJdGV4dHVyZUNvbG9yID0gdWJvcmRlcl9TMV9jMF9jMF9jMDsKCX0KCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWhhbGY0IHN1bSA9IGhhbGY0KDAuMCk7Cglmb3IgKGludCBpID0gMDtpIDwga01heExvb3BMaW1pdF9TMV9jMDsgKytpKSAKCXsKCQloYWxmNCBrID0gdWtlcm5lbF9TMV9jMFtpXTsKCQloYWxmNCBvID0gdW9mZnNldHNfUzFfYzBbMiAqIGldOwoJCXN1bSArPSBrLnggKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIoby54eSkpOwoJCXN1bSArPSBrLnkgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIoby56dykpOwoJCW8gPSB1b2Zmc2V0c19TMV9jMFsyICogaSArIDFdOwoJCXN1bSArPSBrLnogKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIoby54eSkpOwoJCXN1bSArPSBrLncgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIoby56dykpOwoJfQoJcmV0dXJuIGhhbGY0KHN1bSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","GZIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBIA5HZ2CAAAAAAAEAACAAAAASBEAAQAAAAAUCCQAQQGAABAAAAAAAAGMAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1POAQAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpub3BlcnNwZWN0aXZlIG91dCBoYWxmNCB2Y29sb3JfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAlgQAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQp1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZC54ID0gY2xhbXAoc3Vic2V0Q29vcmQueCwgdWNsYW1wX1MxX2MwX2MwLngsIHVjbGFtcF9TMV9jMF9jMC56KTsKCWNsYW1wZWRDb29yZC55ID0gc3Vic2V0Q29vcmQueTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBNYXRyaXhFZmZlY3RfUzFfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","GZIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAJQBBYGQ467YIAAAAAAQAAAAAMACTRNBZN5QYBAAAAAAAAAAAIAALIAAAAB4FJEIEKOAAAAAMAAAAAUAAQAADAWB7EXCYCAAAGAAAAAAAACAAAQAK7DKJCBCTQAAAADAAAAAAAAAAAAYCADIAAAAAG5O33IKAAAAAAMAAAAAIAAAAAABMJDQP2FUWQUAAAQAAQAAAAAAAAAADGAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1M7AgAAI2V4dGVuc2lvbiBHTF9OVl9zaGFkZXJfbm9wZXJzcGVjdGl2ZV9pbnRlcnBvbGF0aW9uOiByZXF1aXJlCnVuaWZvcm0gZmxvYXQ0IHNrX1JUQWRqdXN0Owp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKbm9wZXJzcGVjdGl2ZSBvdXQgaGFsZjQgdmNvbG9yX1MwOwpub3BlcnNwZWN0aXZlIG91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJdmNvdmVyYWdlX1MwID0gY292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgABAAAAxiIAACNleHRlbnNpb24gR0xfTlZfc2hhZGVyX25vcGVyc3BlY3RpdmVfaW50ZXJwb2xhdGlvbjogcmVxdWlyZQpmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKY29uc3QgaW50IGtGaWxsQldfUzJfYzAgPSAwOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzJfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzJfYzAgPSAzOwpjb25zdCBpbnQga0ZpbGxBQV9TMl9jMV9jMCA9IDE7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxCV19TMl9jMV9jMCA9IDI7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCA9IDM7CnVuaWZvcm0gZmxvYXQ0IHVfY29sb3JfUzFfYzA7CnVuaWZvcm0gZmxvYXQ0IHVfY29tcG9zaXRlXzFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2VudGVyX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbWF4X3JhZGl1c19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X25vaXNlX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbm9pc2VfcGhhc2VfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUyX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjNfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBmbG9hdDQgdXJlY3RVbmlmb3JtX1MyX2MwOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MyX2MxX2MwOwpub3BlcnNwZWN0aXZlIGluIGhhbGY0IHZjb2xvcl9TMDsKbm9wZXJzcGVjdGl2ZSBpbiBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm5vcGVyc3BlY3RpdmUgaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpOwp2b2lkIEZMVF9tYWluX1MxX2MwKCk7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCkgCnsKCXJldHVybiBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMC54eTsKfQpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKSAKewoJcmV0dXJuIGZsb2F0MngyKHJhZC54LCAtcmFkLnksIHJhZC55LCByYWQueCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cikgCnsKCWZsb2F0IGJsdXJfaGFsZiA9IGJsdXIgKiAwLjU7CglmbG9hdCBkID0gZGlzdGFuY2UodXYsIHh5KTsKCXJldHVybiAxLjAgLSBzbW9vdGhzdGVwKDEuMCAtIGJsdXJfaGFsZiwgMS4wICsgYmx1cl9oYWxmLCBkIC8gcmFkaXVzKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKSAKewoJZmxvYXQyIHBhcmFtID0gcm90YXRpb247CglwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAocGFyYW0pICogKHh5IC0gcCkgKyB4eTsKCXAgPSBtb2QocCwgZmxvYXQyKGNlbGxfZGlhbWV0ZXIpKSAvIHJlc29sdXRpb247CglmbG9hdCBjZWxsX3V2ID0gKGNlbGxfZGlhbWV0ZXIgLyByZXNvbHV0aW9uLnkpICogMC41OwoJZmxvYXQgciA9IDAuNjUgKiBjZWxsX3V2OwoJZmxvYXQyIHBhcmFtXzEgPSBwOwoJZmxvYXQyIHBhcmFtXzIgPSBmbG9hdDIoY2VsbF91dik7CglmbG9hdCBwYXJhbV8zID0gcjsKCWZsb2F0IHBhcmFtXzQgPSByICogNTAuMDsKCXJldHVybiBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dikgCnsKCWZsb2F0MiB1dl9zY2FsZSA9IHV2ICogZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW0gPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMiA9IHVfY2lyY2xlMV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8zID0gdV9yb3RhdGlvbjFfUzFfYzA7CglmbG9hdCBwYXJhbV80ID0gMC4xNzsKCWZsb2F0IF8zMTkgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7CglmbG9hdCBnMSA9IF8zMTk7CglmbG9hdDIgcGFyYW1fNSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzYgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV83ID0gdV9jaXJjbGUyX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzggPSB1X3JvdGF0aW9uMl9TMV9jMDsKCWZsb2F0IHBhcmFtXzkgPSAwLjI7CglmbG9hdCBfMzMxID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBnMiA9IF8zMzE7CglmbG9hdDIgcGFyYW1fMTAgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzEyID0gdV9jaXJjbGUzX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdV9yb3RhdGlvbjNfUzFfYzA7CglmbG9hdCBwYXJhbV8xNCA9IDAuMjc1OwoJZmxvYXQgXzM0NCA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIsIHBhcmFtXzEzLCBwYXJhbV8xNCk7CglmbG9hdCBnMyA9IF8zNDQ7CglmbG9hdCB2ID0gKChnMSAqIGcxICsgZzIpIC0gZzMpICogMC41OwoJcmV0dXJuIGNsYW1wKDAuNDUgKyAwLjggKiB2LCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cikgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQyIHBhcmFtXzEgPSB4eTsKCWZsb2F0IHBhcmFtXzIgPSByYWRpdXMgKyB0aGlja25lc3M7CglmbG9hdCBwYXJhbV8zID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9vdXRlciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0MiBwYXJhbV80ID0gdXY7CglmbG9hdDIgcGFyYW1fNSA9IHh5OwoJZmxvYXQgcGFyYW1fNiA9IG1heChyYWRpdXMgLSB0aGlja25lc3MsIDAuMCk7CglmbG9hdCBwYXJhbV83ID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9pbm5lciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcpOwoJcmV0dXJuIGNsYW1wKGNpcmNsZV9vdXRlciAtIGNpcmNsZV9pbm5lciwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKSAKewoJbiA9IGZyYWN0KG4gKiBmbG9hdDIoNS4zOTg3LCA1LjQ0MjEpKTsKCW4gKz0gZmxvYXQyKGRvdChuLnl4LCBuICsgZmxvYXQyKDIxLjUzNTEsIDE0LjMxMzcpKSk7CglmbG9hdCB4eSA9IG4ueCAqIG4ueTsKCXJldHVybiAoZnJhY3QoeHkgKiA5NS40MzA3KSArIGZyYWN0KHh5ICogNzUuMDQ5NjEpKSAtIDEuMDsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCkgCnsKCXJldHVybiBzdGVwKGwsIHYpICogKDEuMCAtIHN0ZXAoaCwgdikpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdCBfMjQyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAocGFyYW0pOwoJZmxvYXQgbiA9IF8yNDI7CglmbG9hdCBwYXJhbV8xID0gbjsKCWZsb2F0IHBhcmFtXzIgPSAwLjA7CglmbG9hdCBwYXJhbV8zID0gMC4wNTsKCWZsb2F0IHMgPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdCBwYXJhbV80ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjM1KSk7CglmbG9hdCBwYXJhbV81ID0gMC4xOwoJZmxvYXQgcGFyYW1fNiA9IDAuMTU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82KTsKCWZsb2F0IHBhcmFtXzcgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuNykpOwoJZmxvYXQgcGFyYW1fOCA9IDAuMjsKCWZsb2F0IHBhcmFtXzkgPSAwLjI1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBwYXJhbV8xMCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMS4wNSkpOwoJZmxvYXQgcGFyYW1fMTEgPSAwLjM7CglmbG9hdCBwYXJhbV8xMiA9IDAuMzU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyKTsKCXJldHVybiBjbGFtcChzLCAwLjAsIDEuMCkgKiAwLjU1Owp9CnZvaWQgRkxUX21haW5fUzFfYzAoKSAKewoJdV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueDsKCXVfc3BhcmtsZV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueTsKCXVfYmx1cl9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAuejsKCXVfcmFkaXVzX3NjYWxlX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC53OwoJZmxvYXQyIHAgPSBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CglmbG9hdDIgdXZfMSA9IHAgKiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CglmbG9hdDIgZGVuc2l0eV91diA9IHV2XzEgLSBtb2QocCwgdV9ub2lzZV9zY2FsZV9TMV9jMCk7CglmbG9hdCByYWRpdXMgPSB1X21heF9yYWRpdXNfUzFfYzAgKiB1X3JhZGl1c19zY2FsZV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHV2XzE7CglmbG9hdCB0dXJidWxlbmNlID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChwYXJhbV8xMyk7CglmbG9hdDIgcGFyYW1fMTQgPSBwOwoJZmxvYXQyIHBhcmFtXzE1ID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8xNiA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzE3ID0gMC4wNSAqIHVfbWF4X3JhZGl1c19TMV9jMDsKCWZsb2F0IHBhcmFtXzE4ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgcmluZyA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChwYXJhbV8xNCwgcGFyYW1fMTUsIHBhcmFtXzE2LCBwYXJhbV8xNywgcGFyYW1fMTgpOwoJZmxvYXQyIHBhcmFtXzE5ID0gZGVuc2l0eV91djsKCWZsb2F0IHBhcmFtXzIwID0gdV9ub2lzZV9waGFzZV9TMV9jMDsKCWZsb2F0IHNwYXJrbGUgPSAoKEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAocGFyYW1fMTksIHBhcmFtXzIwKSAqIHJpbmcpICogdHVyYnVsZW5jZSkgKiB1X3NwYXJrbGVfYWxwaGFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMjEgPSBwOwoJZmxvYXQyIHBhcmFtXzIyID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8yMyA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzI0ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgd2F2ZV9hbHBoYSA9IChGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8yMSwgcGFyYW1fMjIsIHBhcmFtXzIzLCBwYXJhbV8yNCkgKiB1X2FscGhhX1MxX2MwKSAqIHVfY29sb3JfUzFfYzAudzsKCWZsb2F0NCB3YXZlX2NvbG9yID0gZmxvYXQ0KHVfY29sb3JfUzFfYzAueHl6ICogd2F2ZV9hbHBoYSwgd2F2ZV9hbHBoYSk7CglmcmFnQ29sb3JfUzFfYzAgPSBtaXgod2F2ZV9jb2xvciwgZmxvYXQ0KDEuMCksIGZsb2F0NChzcGFya2xlKSk7Cn0KaGFsZjQgcnVudGltZV9zaGFkZXJfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAgPSBmbG9hdDQoX3RtcF8xX2Nvb3JkcywgMC4wLCAwLjApOwoJRkxUX21haW5fUzFfYzAoKTsKCXJldHVybiBoYWxmNChoYWxmNChmcmFnQ29sb3JfUzFfYzApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHJ1bnRpbWVfc2hhZGVyX1MxX2MwKF9pbnB1dCk7Cn0KaGFsZjQgUmVjdF9TMl9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGYgY292ZXJhZ2U7CglpZiAoaW50KDEpID09IGtGaWxsQldfUzJfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MwKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoYWxsKGdyZWF0ZXJUaGFuKGZsb2F0NChza19GcmFnQ29vcmQueHksIHVyZWN0VW5pZm9ybV9TMl9jMC56dyksIGZsb2F0NCh1cmVjdFVuaWZvcm1fUzJfYzAueHksIHNrX0ZyYWdDb29yZC54eSkpKSk7Cgl9CgllbHNlIAoJewoJCWhhbGY0IGRpc3RzNCA9IHNhdHVyYXRlKGhhbGY0KDEuMCwgMS4wLCAtMS4wLCAtMS4wKSAqIGhhbGY0KHNrX0ZyYWdDb29yZC54eXh5IC0gdXJlY3RVbmlmb3JtX1MyX2MwKSk7CgkJaGFsZjIgZGlzdHMyID0gKGRpc3RzNC54eSArIGRpc3RzNC56dykgLSAxLjA7CgkJY292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJfQoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMl9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzJfYzApIAoJewoJCWNvdmVyYWdlID0gMS4wIC0gY292ZXJhZ2U7Cgl9CglyZXR1cm4gaGFsZjQoaGFsZjQoY292ZXJhZ2UpKTsKfQpoYWxmNCBDaXJjbGVfUzJfYzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8zX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGQ7CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCkgCgl7CgkJZCA9IGhhbGYoKGxlbmd0aCgodWNpcmNsZV9TMl9jMV9jMC54eSAtIHNrX0ZyYWdDb29yZC54eSkgKiB1Y2lyY2xlX1MyX2MxX2MwLncpIC0gMS4wKSAqIHVjaXJjbGVfUzJfYzFfYzAueik7Cgl9CgllbHNlIAoJewoJCWQgPSBoYWxmKCgxLjAgLSBsZW5ndGgoKHVjaXJjbGVfUzJfYzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMl9jMV9jMC53KSkgKiB1Y2lyY2xlX1MyX2MxX2MwLnopOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGludCgxKSA9PSBrRmlsbEFBX1MyX2MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMV9jMCA/IHNhdHVyYXRlKGQpIDogaGFsZihkID4gMC41KSkpOwp9CmhhbGY0IEJsZW5kX1MyX2MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoX3NyYywgQ2lyY2xlX1MyX2MxX2MwKF9zcmMpKTsKfQpoYWxmNCBCbGVuZF9TMihoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKFJlY3RfUzJfYzAoX3NyYyksIEJsZW5kX1MyX2MxKF9zcmMsIF9kc3QpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA="}} \ No newline at end of file diff --git a/test/model/collection_source_test.dart b/test/model/collection_source_test.dart index 2c21c62c9..e799a9cae 100644 --- a/test/model/collection_source_test.dart +++ b/test/model/collection_source_test.dart @@ -91,7 +91,7 @@ void main() { readyCompleter.complete(); } }); - await source.init(); + await source.init(scope: CollectionSource.fullScope); await readyCompleter.future; return source; } @@ -107,9 +107,9 @@ void main() { (mediaFetchService as FakeMediaFetchService).entries = {refreshEntry}; final source = MediaStoreSource(); - unawaited(source.init()); + unawaited(source.init(scope: CollectionSource.fullScope)); await Future.delayed(const Duration(milliseconds: 10)); - expect(source.scope, SourceScope.full); + expect(source.targetScope, CollectionSource.fullScope); await source.refreshUris({refreshEntry.uri}); await Future.delayed(const Duration(seconds: 1)); diff --git a/whatsnew/whatsnew-en-US b/whatsnew/whatsnew-en-US index 3d9256a35..10f3ff7c4 100644 --- a/whatsnew/whatsnew-en-US +++ b/whatsnew/whatsnew-en-US @@ -1,4 +1,5 @@ -In v1.11.16: -- enjoy new map layers -- share "geo" addresses to Aves and see your collection in that area +In v1.11.17: +- peruse your videos frame by frame +- create map shortcuts to filtered collections +- enjoy the app in Shavian Full changelog available on GitHub \ No newline at end of file